diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-sync.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-sync.php index a2bcd3745..44968e555 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-sync.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-sync.php @@ -48,6 +48,7 @@ class Sync implements Setup, Assets { 'transformation' => '_transformations', 'sync_error' => '_sync_error', 'cloudinary' => '_cloudinary_v2', + 'folder_sync' => '_folder_sync', ); /** @@ -100,8 +101,9 @@ public function is_active() { */ public function is_synced( $post_id ) { $return = false; - $signature = $this->plugin->components['media']->get_post_meta( $post_id, self::META_KEYS['signature'], true ); - if ( ! empty( $signature ) && $this->generate_signature( $post_id ) === $signature ) { + $signature = $this->get_signature( $post_id ); + $expecting = $this->generate_signature( $post_id ); + if ( ! empty( $signature ) && $expecting === $signature ) { $return = $signature; } @@ -133,6 +135,31 @@ function ( $item ) { return $return; } + /** + * Get the current sync signature of an asset. + * + * @param int $post_id The post ID. + * + * @return array|bool + */ + public function get_signature( $post_id ) { + static $signatures = array(); // Cache signatures already fetched. + + $return = false; + if ( ! empty( $signatures[ $post_id ] ) ) { + $return = $signatures[ $post_id ]; + } else { + $signature = $this->plugin->components['media']->get_post_meta( $post_id, self::META_KEYS['signature'], true ); + if ( ! empty( $signature ) ) { + $base_signatures = $this->generate_signature( $post_id ); + $signatures[ $post_id ] = wp_parse_args( $signature, $base_signatures ); + $return = $signatures[ $post_id ]; + } + } + + return $return; + } + /** * Additional component setup. */ diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-upgrade.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-upgrade.php index 55517a0db..1051bc668 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-upgrade.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-upgrade.php @@ -46,7 +46,10 @@ public function __construct( \Cloudinary\Media $media ) { public function check_cloudinary_version( $cloudinary_id, $attachment_id ) { if ( false === $cloudinary_id ) { // Backwards compat. - $meta = wp_get_attachment_metadata( $attachment_id ); + $meta = wp_get_attachment_metadata( $attachment_id ); + if ( ! empty( $meta[ Sync::META_KEYS['cloudinary'] ] ) ) { + return $cloudinary_id; // Current version. + } $public_id = $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true ); /* @@ -59,6 +62,24 @@ public function check_cloudinary_version( $cloudinary_id, $attachment_id ) { // Has public ID, but not fully down synced. $cloudinary_id = $public_id; } + } else { + // Backwards compat. + $folder_sync = $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true ); + if ( 0 === strlen( $folder_sync ) ) { + // Does not exist, add it to be compatible with v1.2.2. + $public_id = $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true ); + // Set the folder sync to 0 to flag it by default as not synced. + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], '0' ); + if ( false !== strpos( $public_id, '/' ) ) { + $path = pathinfo( $public_id ); + $asset_folder = trailingslashit( $path['dirname'] ); + $cloudinary_folder = trailingslashit( $this->media->plugin->config['settings']['sync_media']['cloudinary_folder'] ); + if ( $asset_folder === $cloudinary_folder ) { + // The asset folder matches the defined cloudinary folder, flag it as being in a folder sync. + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], '1' ); + } + } + } } return $cloudinary_id; diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-download-sync.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-download-sync.php index cb4c9e6c6..cf7c65a99 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-download-sync.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-download-sync.php @@ -157,6 +157,16 @@ function ( $val ) use ( $media ) { $public_id = strstr( $public_id, '.' . $path['extension'], true ); // Save public ID. $media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $public_id ); + // Check if the asset is in the same folder as the defined Cloudinary folder. + if ( false !== strpos( $public_id, '/' ) ) { + $path = pathinfo( $public_id ); + $asset_folder = trailingslashit( $path['dirname'] ); + $cloudinary_folder = trailingslashit( $this->plugin->config['settings']['sync_media']['cloudinary_folder'] ); + if ( $asset_folder === $cloudinary_folder ) { + // The asset folder matches the defined cloudinary folder, flag it as being in a folder sync. + $media->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true ); + } + } return $this->download_asset( $attachment_id, $file, basename( $file ), $media->get_transformations_from_string( $file ) ); } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-push-sync.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-push-sync.php index 892e86a50..da9238bae 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-push-sync.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-push-sync.php @@ -57,11 +57,12 @@ public function __construct( \Cloudinary\Plugin $plugin ) { // Define the sync types and their option keys. $sync_types = array( + 'cloud_name' => 'upload', + 'folder' => 'upload', 'file' => 'upload', + 'public_id' => 'rename', 'breakpoints' => 'explicit', 'options' => 'context', - 'folder' => 'upload', - 'cloud_name' => 'upload', ); $this->sync_types = apply_filters( 'cloudinary_sync_types', $sync_types ); @@ -303,7 +304,7 @@ private function get_sync_type( $attachment ) { $type = 'upload'; // Check for explicit (has public_id, but no breakpoints). - $attachment_signature = $attachment->{Sync::META_KEYS['signature']}; + $attachment_signature = $this->plugin->components['sync']->get_signature( $attachment->ID ); if ( empty( $attachment_signature ) ) { if ( ! empty( $attachment->{Sync::META_KEYS['public_id']} ) ) { // Has a public id but no signature, explicit update to complete download. @@ -311,7 +312,7 @@ private function get_sync_type( $attachment ) { } // fallback to upload. } else { - // Has signature. Compare and find if different. + // Has signature find differences and use specific sync method. $required_signature = $this->plugin->components['sync']->generate_signature( $attachment->ID ); foreach ( $required_signature as $key => $signature ) { if ( ( ! isset( $attachment_signature[ $key ] ) || $attachment_signature[ $key ] !== $signature ) && isset( $this->sync_types[ $key ] ) ) { @@ -350,6 +351,9 @@ public function prepare_upload( $post, $down_sync = false ) { return new \WP_Error( 'attachment_post_expected', __( 'An attachment post was expected.', 'cloudinary' ) ); } + // Get the media component. + $media = $this->plugin->components['media']; + // First check if this has a file and it can be uploaded. $file = get_attached_file( $post->ID ); $file_size = 0; @@ -358,7 +362,7 @@ public function prepare_upload( $post, $down_sync = false ) { } elseif ( ! file_exists( $file ) ) { // May be an old upload type. $src = get_post_meta( $post->ID, '_wp_attached_file', true ); - if ( $this->plugin->components['media']->is_cloudinary_url( $src ) ) { + if ( $media->is_cloudinary_url( $src ) ) { // Download first maybe. if ( true === $down_sync ) { $download = $this->plugin->components['sync']->managers['download']->down_sync( $post->ID ); @@ -383,36 +387,44 @@ public function prepare_upload( $post, $down_sync = false ) { // translators: variable is file size. $error = sprintf( __( 'File size exceeds the maximum of %s. This media asset will be served from WordPress.', 'cloudinary' ), $max_size_hr ); - $this->plugin->components['media']->delete_post_meta( $post->ID, Sync::META_KEYS['pending'] ); // Remove Flag. + $media->delete_post_meta( $post->ID, Sync::META_KEYS['pending'] ); // Remove Flag. return new \WP_Error( 'upload_error', $error ); } // If it's got a public ID, then this is an explicit update. + $settings = $this->plugin->config['settings']; $public_id = $post->{Sync::META_KEYS['public_id']}; // use the __get method on the \WP_Post to get post_meta. - $dirs = wp_get_upload_dir(); - $cld_folder = false; - $folder = trailingslashit( $dirs['cloudinary_folder'] ); - if ( '/' === $dirs['cloudinary_folder'] ) { - $folder = ''; - } + $cld_folder = trailingslashit( $settings['sync_media']['cloudinary_folder'] ); if ( empty( $public_id ) ) { $file_info = pathinfo( $file ); - $public_id = $folder . $file_info['filename']; - } - - // Check if cloudinary folder is in public_id. - $parts = explode( '/', $public_id ); - if ( untrailingslashit( $dirs['cloudinary_folder'] ) === $parts[0] ) { - $cld_folder = $dirs['cloudinary_folder']; + $public_id = $cld_folder . $file_info['filename']; } + // Assume that the public_id is a root item. + $public_id_folder = ''; + $public_id_file = $public_id; + // Check if in a lower level. + if ( false !== strpos( $public_id, '/' ) ) { + // Split the public_id into path and filename to allow filtering just the ID and not giving access to the path. + $public_id_info = pathinfo( $public_id ); + $public_id_folder = trailingslashit( $public_id_info['dirname'] ); + $public_id_file = $public_id_info['filename']; + } + // Check if this asset is a folder sync. + $folder_sync = $media->get_post_meta( $post->ID, Sync::META_KEYS['folder_sync'], true ); + if ( ! empty( $folder_sync ) ) { + $public_id_folder = $cld_folder; // Ensure the public ID folder is constant. + } else { + // Not folder synced, so set the folder to the folder that the asset originally came from. + $cld_folder = $public_id_folder; + } // Prepare upload options. $options = array( 'unique_filename' => false, 'resource_type' => $resource_type, - 'public_id' => $public_id, + 'public_id' => $public_id_file, 'context' => array( 'caption' => esc_attr( $post->post_title ), 'alt' => $post->_wp_attachment_image_alt, @@ -434,17 +446,17 @@ public function prepare_upload( $post, $down_sync = false ) { $imagesize = getimagesize( $file ); $meta['width'] = $imagesize[0]; } - $max_width = $this->plugin->components['media']->get_max_width(); + $max_width = $media->get_max_width(); // Add breakpoints request options. - if ( ! empty( $this->plugin->config['settings']['global_transformations']['enable_breakpoints'] ) ) { + if ( ! empty( $settings['global_transformations']['enable_breakpoints'] ) ) { $options['responsive_breakpoints'] = array( 'create_derived' => true, - 'bytes_step' => $this->plugin->config['settings']['global_transformations']['bytes_step'], - 'max_images' => $this->plugin->config['settings']['global_transformations']['breakpoints'], + 'bytes_step' => $settings['global_transformations']['bytes_step'], + 'max_images' => $settings['global_transformations']['breakpoints'], 'max_width' => $meta['width'] < $max_width ? $meta['width'] : $max_width, - 'min_width' => $this->plugin->config['settings']['global_transformations']['min_width'], + 'min_width' => $settings['global_transformations']['min_width'], ); - $transformations = $this->plugin->components['media']->get_transformation_from_meta( $post->ID ); + $transformations = $media->get_transformation_from_meta( $post->ID ); if ( ! empty( $transformations ) ) { $options['responsive_breakpoints']['transformation'] = Api::generate_transformation_string( $transformations ); } @@ -474,14 +486,19 @@ public function prepare_upload( $post, $down_sync = false ) { $breakpoints['context'] = http_build_query( $breakpoints['context'], null, '|' ); } - $return = array( + // Restructure the path to the filename to allow correct placement in Cloudinary. + $public_id = $public_id_folder . $options['public_id']; + $return = array( 'file' => $file, 'folder' => $cld_folder, + 'public_id' => $public_id, 'breakpoints' => array(), 'options' => $options, ); + $return['options']['public_id'] = $public_id; if ( ! empty( $breakpoints ) ) { - $return['breakpoints'] = $breakpoints; + $return['breakpoints'] = $breakpoints; + $return['breakpoints']['public_id'] = $public_id; // Stage public ID to folder for breakpoints. } $this->upload_options[ $post->ID ] = $return; @@ -519,6 +536,8 @@ public function push_attachments( $attachments ) { 'total' => count( $attachments ), 'processed' => 0, ); + // Get media component. + $media = $this->plugin->components['media']; // Go over each attachment. foreach ( $attachments as $attachment ) { @@ -560,7 +579,7 @@ public function push_attachments( $attachments ) { if ( 'explicit' === $sync_type ) { // Explicit update. $args = array( - 'public_id' => $upload['options']['public_id'], + 'public_id' => $upload['public_id'], 'type' => 'upload', ); if ( ! empty( $upload['options']['responsive_breakpoints'] ) ) { @@ -570,6 +589,13 @@ public function push_attachments( $attachments ) { $args['context'] = $upload['options']['context']; } $result = $this->plugin->components['connect']->api->explicit( $args ); + } elseif ( 'rename' === $sync_type ) { + // Rename an asset. + $args = array( + 'from_public_id' => $media->get_post_meta( $attachment->ID, Sync::META_KEYS['public_id'] ), + 'to_public_id' => $upload['public_id'], + ); + $result = $this->plugin->components['connect']->api->{$upload['options']['resource_type']}( 'rename', 'POST', $args ); } else { // dynamic sync type.. $result = $this->plugin->components['connect']->api->{$sync_type}( $upload['file'], $upload['options'] ); @@ -583,7 +609,7 @@ public function push_attachments( $attachments ) { if ( is_wp_error( $result ) ) { $error = $result->get_error_message(); $stats['fail'][] = $error; - $this->plugin->components['media']->update_post_meta( $attachment->ID, Sync::META_KEYS['sync_error'], $error ); + $media->update_post_meta( $attachment->ID, Sync::META_KEYS['sync_error'], $error ); continue; } @@ -597,8 +623,8 @@ public function push_attachments( $attachments ) { if ( ! empty( $result['version'] ) ) { $meta_data[ Sync::META_KEYS['version'] ] = $result['version']; } - $this->plugin->components['media']->delete_post_meta( $attachment->ID, Sync::META_KEYS['pending'] ); - $this->plugin->components['media']->delete_post_meta( $attachment->ID, Sync::META_KEYS['sync_error'], false ); + $media->delete_post_meta( $attachment->ID, Sync::META_KEYS['pending'] ); + $media->delete_post_meta( $attachment->ID, Sync::META_KEYS['sync_error'], false ); if ( ! empty( $this->plugin->config['settings']['global_transformations']['enable_breakpoints'] ) ) { if ( ! empty( $result['responsive_breakpoints'] ) ) { // Images only. $meta_data[ Sync::META_KEYS['breakpoints'] ] = $result['responsive_breakpoints'][0]['breakpoints']; @@ -619,7 +645,7 @@ public function push_attachments( $attachments ) { $meta = wp_get_attachment_metadata( $attachment->ID, true ); $meta[ Sync::META_KEYS['cloudinary'] ] = $meta_data; wp_update_attachment_metadata( $attachment->ID, $meta ); - $this->plugin->components['media']->update_post_meta( $attachment->ID, Sync::META_KEYS['public_id'], $upload['options']['public_id'] ); + $media->update_post_meta( $attachment->ID, Sync::META_KEYS['public_id'], $upload['options']['public_id'] ); // Search and update link references in content. $content_search = new \WP_Query( array( 's' => 'wp-image-' . $attachment->ID, 'fields' => 'ids', 'posts_per_page' => 1000 ) ); if ( ! empty( $content_search->found_posts ) ) { diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-sync.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-sync.php index cd9272c2d..f3a8bfcce 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-sync.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-sync.php @@ -261,6 +261,7 @@ public function add_to_sync( $attachment_id ) { if ( ! in_array( $attachment_id, $this->to_sync, true ) ) { // Flag image as pending to prevent duplicate upload. update_post_meta( $attachment_id, Sync::META_KEYS['pending'], time() ); + $this->plugin->components['media']->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true ); $this->to_sync[] = $attachment_id; } }