From 8c9ee9f13929c57146dcff17d3d4ca3e583c2b5f Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 30 Jul 2020 08:22:28 +0200 Subject: [PATCH 01/64] restructure get signature based on a sync base structure. --- .../php/class-media.php | 138 ++++++++++++++++-- .../php/class-sync.php | 103 +++++++++++-- 2 files changed, 222 insertions(+), 19 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 004d4fb07..30cdbfdbf 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -8,6 +8,7 @@ namespace Cloudinary; use Cloudinary\Component\Setup; +use Cloudinary\Connect\Api; use Cloudinary\Media\Filter; use Cloudinary\Media\Upgrade; use Cloudinary\Media\Global_Transformations; @@ -120,13 +121,11 @@ public function __construct( Plugin $plugin ) { * @return bool */ public function is_media( $attachment ) { - $is_media = false; + $media_types = array( 'image', 'video' ); + $is_media = false; if ( 'attachment' === get_post_type( $attachment ) ) { - $is_media = $this->get_post_meta( $attachment, 'is_media', true ); - if ( '' === $is_media ) { - $is_media = wp_attachment_is( 'image', $attachment ) || wp_attachment_is( 'video', $attachment ); - $this->update_post_meta( $attachment, 'is_media', $is_media ); - } + $type = strstr( get_post_mime_type( $attachment ), '/', true ); + $is_media = in_array( $type, apply_filters( 'cloudinary_media_types', $media_types ) ); } return $is_media; @@ -617,12 +616,21 @@ public function cloudinary_url( $attachment_id, $size = array(), $transformation * @return array Altered array of paths. */ public function upload_dir( $dirs ) { - $folder = $this->cloudinary_folder; - $dirs['cloudinary_folder'] = trailingslashit( $folder ); + + $dirs['cloudinary_folder'] = trailingslashit( $this->get_cloudinary_folder() ); return $dirs; } + /** + * Get the setup Cloudinary Folder. + * + * @return string + */ + public function get_cloudinary_folder() { + return $this->cloudinary_folder; + } + /** * Get a public ID. * @@ -638,7 +646,7 @@ public function get_public_id( $attachment_id ) { // Build a public_id based on cloudinary folder, and filename. $file = get_attached_file( $attachment_id ); $info = pathinfo( $file ); - $cloudinary_folder = trailingslashit( $this->cloudinary_folder ); + $cloudinary_folder = trailingslashit( $this->get_cloudinary_folder() ); $public_id = $cloudinary_folder . $info['filename']; } @@ -1031,7 +1039,7 @@ public function down_sync_asset() { } $transformations = $this->get_transformations_from_string( $url ); if ( ! empty( $transformations ) ) { - $sync_key .= wp_json_encode( $transformations ); + $sync_key .= wp_json_encode( $transformations ); $asset['transformations'] = $transformations; } // Check Format and url extension. @@ -1316,6 +1324,116 @@ public function delete_post_meta( $post_id, $key ) { delete_post_meta( $post_id, $key ); } + /** + * Get the breakpoint generation options for an attachment. + * + * @param int $attachment_id The attachment ID. + * + * @return array + */ + public function get_breakpoint_options( $attachment_id ) { + // Add breakpoints if we have an image. + $breakpoints = false; + if ( wp_attachment_is_image( $attachment_id ) ) { + $meta = wp_get_attachment_metadata( $post->ID ); + // Get meta image size if non exists. + if ( empty( $meta ) ) { + $meta = array(); + $imagesize = getimagesize( get_attached_file( $attachment_id ) ); + $meta['width'] = $imagesize[0] ? $imagesize[0] : 0; + } + $max_width = $this->get_max_width(); + $settings = $this->plugin->config['settings']; + // Add breakpoints request options. + if ( ! empty( $settings['global_transformations']['enable_breakpoints'] ) ) { + $breakpoint_options = array( + 'create_derived' => true, + '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' => $settings['global_transformations']['min_width'], + ); + $transformations = $this->get_transformation_from_meta( $attachment_id ); + if ( ! empty( $transformations ) ) { + $breakpoints['transformation'] = Api::generate_transformation_string( $transformations ); + } + $breakpoints = array( + 'public_id' => $this->get_public_id( $attachment_id ), + 'type' => 'upload', + 'responsive_breakpoints' => $breakpoint_options, + 'context' => $this->get_context_options( $attachment_id ), + ); + } + } + + return $breakpoints; + } + + /** + * Get the context options for an asset. + * + * @param int $attachment_id The ID of the attachment. + * + * @return array + */ + public function get_context_options( $attachment_id ) { + + $context_options = array( + 'caption' => esc_attr( get_the_title( $attachment_id ) ), + 'alt' => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ), + ); + + // Check if this asset is a folder sync. + $folder_sync = $this->get_post_meta( $post->ID, Sync::META_KEYS['folder_sync'], true ); + if ( ! empty( $folder_sync ) ) { + $context_options['wp_id'] = $attachment_id; + } + + /** + * Filter the options to allow other plugins to add requested options for uploading. + * + * @param array $options The options array. + * @param \WP_Post $post The attachment post. + * @param \Cloudinary\Sync The sync object instance. + * + * @return array + */ + $context_options = apply_filters( 'cloudinary_context_options', $context_options, get_post( $attachment_id ), $this ); + + return http_build_query( $context_options, null, '|' );; + } + + /** + * Get the media upload options as connected to Cloudinary. + * + * @param int $attachment_id The attachment ID. + * + * @return array + */ + public function get_upload_options( $attachment_id ) { + // Prepare upload options. + $options = array( + 'unique_filename' => false, + 'resource_type' => strstr( get_post_mime_type( $attachment_id ), '/', true ), + 'public_id' => $this->get_public_id( $attachment_id ), + 'context' => $this->get_context_options( $attachment_id ), + 'responsive_breakpoints' => $this->get_breakpoint_options( $attachment_id ), + ); + + /** + * Filter the options to allow other plugins to add requested options for uploading. + * + * @param array $options The options array. + * @param \WP_Post $post The attachment post. + * @param \Cloudinary\Sync The sync object instance. + * + * @return array + */ + $options = apply_filters( 'cloudinary_upload_options', $options, get_post( $attachment_id ), $this ); + + return $options; + } + /** * Setup the hooks and base_url if configured. */ 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 35a34b1c9..a130fc8bb 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 @@ -36,6 +36,13 @@ class Sync implements Setup, Assets { */ public $managers; + /** + * Contains the sync base structure and callbacks. + * + * @var array + */ + protected $sync_base_struct; + /** * Holds the meta keys for sync meta to maintain consistency. */ @@ -120,21 +127,20 @@ public function is_synced( $post_id ) { /** * Generate a signature based on whats required for a full sync. * - * @param int $post_id The post id to generate a signature for. + * @param int $attachment_id The Attachment id to generate a signature for. * * @return string|bool */ - public function generate_signature( $post_id ) { - $upload = $this->managers['push']->prepare_upload( $post_id ); + public function generate_signature( $attachment_id ) { // Check if has an error (ususally due to file quotas). - if ( is_wp_error( $upload ) ) { - $this->plugin->components['media']->get_post_meta( $post_id, self::META_KEYS['sync_error'], $upload->get_error_message() ); + $can_sync = $this->can_sync( $attachment_id ); + if ( is_wp_error( $can_sync ) ) { + $this->plugin->components['media']->get_post_meta( $attachment_id, self::META_KEYS['sync_error'], $can_sync->get_error_message() ); return false; } - $credentials = $this->plugin->components['connect']->get_credentials(); - $upload['cloud_name'] = $credentials['cloud_name']; - $return = array_map( + $sync_base = $this->sync_base( $attachment_id ); + $return = array_map( function ( $item ) { if ( is_array( $item ) ) { $item = wp_json_encode( $item ); @@ -142,12 +148,32 @@ function ( $item ) { return md5( $item ); }, - $upload + $sync_base ); return $return; } + /** + * Check if an asset can be synced. + * + * @param int $attachment_id The attachment ID to check if it can be synced. + * + * @return bool + */ + public function can_sync( $attachment_id ) { + + /** + * Filter to allow changing if an asset is allowed to be synced. + * Return a WP Error with reason why it can't be synced. + * + * @param int $attachment_id The attachment post ID. + * + * @return bool|\WP_Error + */ + return apply_filters( 'cloudinary_can_sync_asset', true, $attachment_id ); + } + /** * Get the current sync signature of an asset. * @@ -244,6 +270,61 @@ public function add_suffix_maybe( $public_id, $attachment_id, $suffix = null ) { return $suffix; } + /** + * Get the sync_setup_base of part callbacks. + * + * @return array + */ + public function setup_sync_base_struct() { + + $base_struct = array( + 'file' => 'get_attached_file', + 'folder' => array( $this->managers['media'], 'get_cloudinary_folder' ), + 'public_id' => array( $this->managers['media'], 'get_public_id' ), + 'suffix' => array( $this, 'get_suffix_maybe' ), + 'breakpoints' => array( $this->managers['media'], 'get_breakpoint_options' ), + 'options' => array( $this->managers['media'], 'get_upload_options' ), + 'cloud_name' => array( 'Media', 'get_cloud_name' ), + ); + + return apply_filters( 'cloudinary_sync_base_struct', $base_struct ); + } + + /** + * Get the core Sync Base. + * + * @param int|\WP_Post $post The attachment to prepare. + * + * @return array|\WP_Error + */ + public function sync_base( $post ) { + + if ( ! $this->managers['media']->is_media( $post ) ) { + return new \WP_Error( 'attachment_post_expected', __( 'An attachment post was expected.', 'cloudinary' ) ); + } + + $return = array(); + foreach ( $this->sync_base_struct as $key => $callback ) { + $return[ $key ] = null; + if ( is_callable( $callback ) ) { + $return[ $key ] = call_user_func( $callback, $post ); + } + } + + /** + * Filter the sync base to allow other plugins to add requested sync components for the sync signature. + * + * @param array $options The options array. + * @param \WP_Post $post The attachment post. + * @param \Cloudinary\Sync The sync object instance. + * + * @return array + */ + $return = apply_filters( 'cloudinary_sync_base', $return, $post ); + + return $return; + } + /** * Additional component setup. */ @@ -252,6 +333,10 @@ public function setup() { $this->managers['upload']->setup(); $this->managers['delete']->setup(); $this->managers['push']->setup(); + // Setup additional components. + $this->managers['media'] = $this->plugin->components['media']; + // Setup the sync_base_structure. + $this->sync_base_struct = $this->setup_sync_base_struct(); } } } From edcb2a78298c933a1a829542f62f374bfcedbb21 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 30 Jul 2020 13:13:57 +0200 Subject: [PATCH 02/64] Rework the separate methods based on sync type --- .../php/class-connect.php | 11 +- .../php/class-media.php | 24 +- .../php/class-sync.php | 365 +++++++++++++++--- .../php/connect/class-api.php | 9 +- .../php/sync/class-delete-sync.php | 2 +- .../php/sync/class-upload-sync.php | 169 ++++---- 6 files changed, 443 insertions(+), 137 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-connect.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-connect.php index 9dcf3a68a..726e915b4 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-connect.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-connect.php @@ -256,6 +256,15 @@ public function get_credentials() { return $this->credentials; } + /** + * Get the cloud name if set. + * + * @return bool|mixed + */ + public function get_cloud_name() { + return $this->credentials['cloud_name'] ? $this->credentials['cloud_name'] : false; + } + /** * Set the config credentials from an array. * @@ -364,7 +373,7 @@ public function get_usage_stat( $type, $stat = null ) { $value = $this->usage[ $type ]['usage']; } elseif ( 'used_percent' === $stat && isset( $this->usage[ $type ]['credits_usage'] ) ) { // Calculate percentage based on credit limit and usage. - $value = round( $this->usage[ $type ]['credits_usage']/$this->usage['credits']['limit'] * 100, 2 ); + $value = round( $this->usage[ $type ]['credits_usage'] / $this->usage['credits']['limit'] * 100, 2 ); } } } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 30cdbfdbf..182dcf16a 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -617,7 +617,7 @@ public function cloudinary_url( $attachment_id, $size = array(), $transformation */ public function upload_dir( $dirs ) { - $dirs['cloudinary_folder'] = trailingslashit( $this->get_cloudinary_folder() ); + $dirs['cloudinary_folder'] = $this->get_cloudinary_folder(); return $dirs; } @@ -628,7 +628,7 @@ public function upload_dir( $dirs ) { * @return string */ public function get_cloudinary_folder() { - return $this->cloudinary_folder; + return trailingslashit( $this->cloudinary_folder ); } /** @@ -646,7 +646,7 @@ public function get_public_id( $attachment_id ) { // Build a public_id based on cloudinary folder, and filename. $file = get_attached_file( $attachment_id ); $info = pathinfo( $file ); - $cloudinary_folder = trailingslashit( $this->get_cloudinary_folder() ); + $cloudinary_folder = $this->get_cloudinary_folder(); $public_id = $cloudinary_folder . $info['filename']; } @@ -694,6 +694,8 @@ public function cloudinary_id( $attachment_id ) { $cloudinary_id = false; if ( $this->plugin->components['sync']->is_synced( $attachment_id ) ) { $cloudinary_id = $this->get_cloudinary_id( $attachment_id ); + } else { + $this->plugin->components['sync']->maybe_prepare_sync( $attachment_id ); } /** @@ -1333,8 +1335,10 @@ public function delete_post_meta( $post_id, $key ) { */ public function get_breakpoint_options( $attachment_id ) { // Add breakpoints if we have an image. - $breakpoints = false; - if ( wp_attachment_is_image( $attachment_id ) ) { + $breakpoints = array(); + $has_breakpoints = $this->plugin->config['settings']['global_transformations']['enable_breakpoints']; + + if ( 'off' !== $has_breakpoints && wp_attachment_is_image( $attachment_id ) ) { $meta = wp_get_attachment_metadata( $post->ID ); // Get meta image size if non exists. if ( empty( $meta ) ) { @@ -1411,13 +1415,13 @@ public function get_context_options( $attachment_id ) { * @return array */ public function get_upload_options( $attachment_id ) { + // Prepare upload options. $options = array( - 'unique_filename' => false, - 'resource_type' => strstr( get_post_mime_type( $attachment_id ), '/', true ), - 'public_id' => $this->get_public_id( $attachment_id ), - 'context' => $this->get_context_options( $attachment_id ), - 'responsive_breakpoints' => $this->get_breakpoint_options( $attachment_id ), + 'unique_filename' => false, + 'resource_type' => strstr( get_post_mime_type( $attachment_id ), '/', true ), + 'public_id' => $this->get_public_id( $attachment_id ), + 'context' => $this->get_context_options( $attachment_id ), ); /** 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 a130fc8bb..3c5192ff4 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 @@ -43,6 +43,20 @@ class Sync implements Setup, Assets { */ protected $sync_base_struct; + /** + * Contains the sync types and callbacks. + * + * @var array + */ + protected $sync_types; + + /** + * Holds a list of unsynced images to push on end. + * + * @var array + */ + private $to_sync = array(); + /** * Holds the meta keys for sync meta to maintain consistency. */ @@ -132,24 +146,23 @@ public function is_synced( $post_id ) { * @return string|bool */ public function generate_signature( $attachment_id ) { - // Check if has an error (ususally due to file quotas). - $can_sync = $this->can_sync( $attachment_id ); - if ( is_wp_error( $can_sync ) ) { - $this->plugin->components['media']->get_post_meta( $attachment_id, self::META_KEYS['sync_error'], $can_sync->get_error_message() ); - - return false; + static $signatures = array(); // cache signatures. + if ( ! empty( $signatures[ $attachment_id ] ) ) { + $return = $signatures[ $attachment_id ]; + } else { + $sync_base = $this->sync_base( $attachment_id ); + $return = array_map( + function ( $item ) { + if ( is_array( $item ) ) { + $item = wp_json_encode( $item ); + } + + return md5( $item ); + }, + $sync_base + ); + $signatures[ $attachment_id ] = $return; } - $sync_base = $this->sync_base( $attachment_id ); - $return = array_map( - function ( $item ) { - if ( is_array( $item ) ) { - $item = wp_json_encode( $item ); - } - - return md5( $item ); - }, - $sync_base - ); return $return; } @@ -157,11 +170,12 @@ function ( $item ) { /** * Check if an asset can be synced. * - * @param int $attachment_id The attachment ID to check if it can be synced. + * @param int $attachment_id The attachment ID to check if it can be synced. + * @param string $type The type of sync to attempt. * * @return bool */ - public function can_sync( $attachment_id ) { + public function can_sync( $attachment_id, $type = 'file' ) { /** * Filter to allow changing if an asset is allowed to be synced. @@ -171,28 +185,28 @@ public function can_sync( $attachment_id ) { * * @return bool|\WP_Error */ - return apply_filters( 'cloudinary_can_sync_asset', true, $attachment_id ); + return apply_filters( 'cloudinary_can_sync_asset', true, $attachment_id, $type ); } /** * Get the current sync signature of an asset. * - * @param int $post_id The post ID. + * @param int $attachment_id The attachment ID. * * @return array|bool */ - public function get_signature( $post_id ) { + public function get_signature( $attachment_id ) { static $signatures = array(); // Cache signatures already fetched. - $return = false; - if ( ! empty( $signatures[ $post_id ] ) ) { - $return = $signatures[ $post_id ]; + $return = array(); + if ( ! empty( $signatures[ $attachment_id ] ) ) { + $return = $signatures[ $attachment_id ]; } else { - $signature = $this->plugin->components['media']->get_post_meta( $post_id, self::META_KEYS['signature'], true ); + $signature = $this->managers['media']->get_post_meta( $attachment_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 ]; + $base_signatures = $this->generate_signature( $attachment_id ); + $signatures[ $attachment_id ] = wp_parse_args( $signature, $base_signatures ); + $return = $signatures[ $attachment_id ]; } } @@ -272,22 +286,126 @@ public function add_suffix_maybe( $public_id, $attachment_id, $suffix = null ) { /** * Get the sync_setup_base of part callbacks. - * - * @return array */ public function setup_sync_base_struct() { $base_struct = array( - 'file' => 'get_attached_file', - 'folder' => array( $this->managers['media'], 'get_cloudinary_folder' ), - 'public_id' => array( $this->managers['media'], 'get_public_id' ), - 'suffix' => array( $this, 'get_suffix_maybe' ), - 'breakpoints' => array( $this->managers['media'], 'get_breakpoint_options' ), - 'options' => array( $this->managers['media'], 'get_upload_options' ), - 'cloud_name' => array( 'Media', 'get_cloud_name' ), + 'file' => array( + 'generate' => 'get_attached_file', + 'sync' => array( + 'priority' => 0, + 'callback' => array( $this->managers['upload'], 'upload_asset' ), + ), + ), + 'folder' => array( + 'generate' => array( $this->managers['media'], 'get_cloudinary_folder' ), + 'sync' => array( + 'priority' => 10, + 'callback' => array( $this->managers['upload'], 'upload_asset' ), + ), + ), + 'public_id' => array( + 'generate' => array( $this->managers['media'], 'get_public_id' ), + 'sync' => array( + 'priority' => 20, + 'callback' => array( $this->managers['push'], 'push_attachments' ), // Rename + ), + ), + 'suffix' => array( + 'generate' => array( $this, 'get_suffix_maybe' ), + 'sync' => array( + 'priority' => 10, + 'callback' => array( $this->managers['upload'], 'upload_asset' ), + ), + ), + 'breakpoints' => array( + 'generate' => array( $this->managers['media'], 'get_breakpoint_options' ), + 'sync' => array( + 'priority' => 25, + 'callback' => array( $this->managers['upload'], 'explicit_update' ), + ), + ), + 'options' => array( + 'generate' => array( $this->managers['media'], 'get_upload_options' ), + 'sync' => array( + 'priority' => 30, + 'callback' => array( $this->managers['upload'], 'context_update' ), + ), + ), + 'cloud_name' => array( + 'generate' => array( $this->managers['connect'], 'get_cloud_name' ), + 'sync' => array( + 'priority' => 5, + 'callback' => array( $this->managers['upload'], 'upload_asset' ), + ), + ), + ); + + /** + * Filter the sync base structure to allow other plugins to sync component callbacks. + * + * @param array $base_struct The base sync structure. + * + * @return array + */ + $base_struct = apply_filters( 'cloudinary_sync_base_struct', $base_struct ); + + // Apply a default to ensure parts exist.s + $structs = array_map( + function ( $component ) { + $sync_default = array( + 'priority' => 50, + 'callback' => '__return_null', + ); + $default = array( + 'generate' => '__return_null', + 'sync' => array(), + ); + + // Ensure correct struct. + $component = wp_parse_args( $component, $default ); + $component['sync'] = wp_parse_args( $component['sync'], $sync_default ); + $component['sync']['priority'] = is_int( $component['sync']['priority'] ) ? (int) $component['sync']['priority'] : 50; + + return $component; + }, + $base_struct ); - return apply_filters( 'cloudinary_sync_base_struct', $base_struct ); + $this->sync_base_struct = $structs; + } + + /** + * Setup the sync types in priority order based on sync struct. + */ + public function setup_sync_types() { + + $sync_types = array(); + foreach ( $this->sync_base_struct as $type => $struct ) { + if ( is_callable( $struct['sync']['callback'] ) ) { + $sync_types[ $type ] = $struct['sync']['priority']; + } + } + + asort( $sync_types ); + + $this->sync_types = $sync_types; + } + + /** + * Get the method to do a sync for the specified type. + * + * @param $type + * + * @return bool|mixed + */ + public function get_sync_method( $type ) { + $method = false; + if ( isset( $this->sync_base_struct[ $type ]['sync']['callback'] ) ) { + $method = $this->sync_base_struct[ $type ]['sync']['callback']; + } + + return $method; } /** @@ -304,10 +422,12 @@ public function sync_base( $post ) { } $return = array(); - foreach ( $this->sync_base_struct as $key => $callback ) { - $return[ $key ] = null; - if ( is_callable( $callback ) ) { - $return[ $key ] = call_user_func( $callback, $post ); + foreach ( $this->sync_base_struct as $key => $struct ) { + if ( isset( $struct['generate'] ) ) { + $return[ $key ] = null; + if ( is_callable( $struct['generate'] ) ) { + $return[ $key ] = call_user_func( $struct['generate'], $post ); + } } } @@ -325,18 +445,173 @@ public function sync_base( $post ) { return $return; } + /** + * Prepare an asset to be synced, maybe. + * + * @param int $attachment_id The attachment ID. + * + * @return void + */ + public function maybe_prepare_sync( $attachment_id ) { + + $type = $this->get_sync_type( $attachment_id ); + // Check if has an error (ususally due to file quotas). + $can_sync = $this->can_sync( $attachment_id, $type ); + if ( is_wp_error( $can_sync ) ) { + $this->managers['media']->get_post_meta( $attachment_id, self::META_KEYS['sync_error'], $can_sync->get_error_message() ); + + return; + } + + $this->add_to_sync( $attachment_id ); + } + + /** + * Get the type of sync, with the lowest priority for this asset. + * + * @param $attachment_id + * + * @return mixed|string|\WP_Error + */ + public function get_sync_type( $attachment_id ) { + $attachment_signature = $this->get_signature( $attachment_id ); + // Set default sync type. + $type = array_shift( array_keys( $this->sync_types ) ); // Lowest sync type should always be a full sync. + if ( ! empty( $attachment_signature ) ) { + // Has signature find differences and use specific sync method. + $required_signature = $this->generate_signature( $attachment_id ); + if ( is_array( $required_signature ) ) { + $sync_items = array_diff( $required_signature, $attachment_signature ); + $ordered = array_intersect_key( $this->sync_types, $sync_items ); + $type = array_shift( array_keys( $ordered ) ); + } + } + + // Nothing to sync. + if ( empty( $type ) ) { + $type = new \WP_Error( 'attachment_synced', __( 'Attachment is already fully synced.', 'cloudinary' ) ); + } + + return $type; + } + + /** + * Checks the status of the media item. + * + * @param array $status Array of state and note. + * @param int $attachment_id The attachment id. + * + * @return array + */ + public function filter_status( $status, $attachment_id ) { + + if ( $this->is_pending( $attachment_id ) ) { + $status['state'] = 'warning'; + $status['note'] = __( 'Upload sync pending', 'cloudinary' ); + } + + // Check if there's an error. + $has_error = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['sync_error'], true ); + if ( ! empty( $has_error ) ) { + $status['state'] = 'error'; + $status['note'] = $has_error; + } + + return $status; + } + + /** + * Check if the attachment is pending an upload sync. + * + * @param int $attachment_id The attachment ID to check. + * + * @return bool + */ + public function is_pending( $attachment_id ) { + // Check if it's not already in the to sync array. + if ( ! in_array( $attachment_id, $this->to_sync, true ) ) { + $is_pending = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['pending'], true ); + if ( empty( $is_pending ) || $is_pending < time() - 5 * 60 ) { + // No need to delete pending meta, since it will be updated with the new timestamp anyway. + return false; + } + } + + return true; + } + + /** + * Add an attachment ID to the to_sync array. + * + * @param int $attachment_id The attachment ID to add. + */ + public function add_to_sync( $attachment_id ) { + if ( ! in_array( $attachment_id, $this->to_sync, true ) && ! $this->is_pending( $attachment_id ) ) { + // Flag image as pending to prevent duplicate upload. + $this->managers['media']->update_post_meta( $attachment_id, Sync::META_KEYS['pending'], time() ); + //$this->managers['media']->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true ); + $this->to_sync[] = $attachment_id; + } + } + + /** + * Update a part of the signature based on type. + * + * @param int $attachment_id The attachment ID. + * @param string $type The type of sync. + */ + public function update_signature( $attachment_id, $type ) { + + $current_signature = $this->get_signature( $attachment_id ); + $expecting = $this->generate_signature( $attachment_id ); + $current_sync_method = $this->sync_base_struct[ $type ]['sync']['callback']; + $meta = wp_get_attachment_metadata( $attachment_id, true ); + + // Go over all other types that share the same sync method and include them here. + foreach ( $this->sync_base_struct as $sync_type => $struct ) { + if ( $struct['sync']['callback'] === $current_sync_method ) { + $current_signature[ $sync_type ] = $expecting[ $sync_type ]; + } + } + $meta[ Sync::META_KEYS['cloudinary'] ][ Sync::META_KEYS['signature'] ] = $current_signature; + wp_update_attachment_metadata( $attachment_id, $meta ); + + } + + /** + * Initialize the background sync on requested images needing to be synced. + */ + public function init_background_upload() { + if ( ! empty( $this->to_sync ) ) { + $params = array( + 'attachment_ids' => $this->to_sync, + ); + $this->managers['api']->background_request( 'process', $params ); + } + } + /** * Additional component setup. */ public function setup() { if ( $this->plugin->config['connect'] ) { + + // Show sync status. + add_filter( 'cloudinary_media_status', array( $this, 'filter_status' ), 10, 2 ); + // Hook for on demand upload push. + add_action( 'shutdown', array( $this, 'init_background_upload' ) ); + $this->managers['upload']->setup(); $this->managers['delete']->setup(); $this->managers['push']->setup(); // Setup additional components. - $this->managers['media'] = $this->plugin->components['media']; + $this->managers['media'] = $this->plugin->components['media']; + $this->managers['connect'] = $this->plugin->components['connect']; + $this->managers['api'] = $this->plugin->components['api']; // Setup the sync_base_structure. - $this->sync_base_struct = $this->setup_sync_base_struct(); + $this->setup_sync_base_struct(); + // Setup sync types. + $this->setup_sync_types(); } } } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php index 08d80d6ec..80804d5d5 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php @@ -186,7 +186,7 @@ public static function generate_transformation_string( array $options, $type = ' $transformations = array_map( function ( $item ) use ( $transformation_index ) { $transform = array(); - if ( is_string ( $item ) ) { + if ( is_string( $item ) ) { return $item; } @@ -239,7 +239,7 @@ public function cloudinary_url( $public_id, $args = array(), $size = array(), $c $args['version'] = 'v1'; } - // Determine if we're dealing with a fetched + // Determine if we're dealing with a fetched. // ...or uploaded image and update the URL accordingly. $asset_endpoint = filter_var( $public_id, FILTER_VALIDATE_URL ) ? 'fetch' : 'upload'; @@ -448,12 +448,11 @@ public function destroy( $type, $options ) { /** * Context update of an asset. * - * @param string $file Filename of file. Ignored in this instance since it's a dynamic method call. - * @param array $args Array of options to update. + * @param array $args Array of options to update. * * @return array|\WP_Error */ - public function context( $file, $args ) { + public function context( $args ) { $url = $this->url( $args['resource_type'], 'context', true ); $options = array( diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-delete-sync.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-delete-sync.php index 0d8c4f817..9677b1197 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-delete-sync.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-delete-sync.php @@ -60,7 +60,7 @@ public function can_delete_asset( $all_caps, $caps, $args ) { if ( 'delete_post' === $request_cap && ! empty( $all_caps['delete_posts'] ) && 'attachment' === get_post_type( $post_id ) ) { // Check if is pending. - if ( ! $this->plugin->components['sync']->is_synced( $post_id ) && $this->plugin->components['sync']->managers['upload']->is_pending( $post_id ) ) { + if ( ! $this->plugin->components['sync']->is_synced( $post_id ) && $this->plugin->components['sync']->is_pending( $post_id ) ) { // Check for errors. $has_error = $this->plugin->components['media']->get_post_meta( $post_id, Sync::META_KEYS['sync_error'], true ); if ( empty( $has_error ) ) { 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 0e7c2f7ed..dfc5fdd33 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 @@ -33,18 +33,32 @@ class Upload_Sync { private $pusher; /** - * This feature is enabled. + * Holds the main Sync Class. * - * @var bool + * @var \Cloudinary\Sync */ - private $enabled; + protected $sync; + + /** + * Holds the Connect Class. + * + * @var \Cloudinary\Connect + */ + protected $connect; + + /** + * Holds the Media Class. + * + * @var \Cloudinary\Media + */ + protected $media; /** - * Holds a list of unsynced images to push on end. + * This feature is enabled. * - * @var array + * @var bool */ - private $to_sync = array(); + private $enabled; /** * Upload_Sync constructor. @@ -66,11 +80,7 @@ private function register_hooks() { // Add action to upload. add_action( 'add_attachment', array( $this, 'push_on_upload' ), 10 ); // Action Cloudinary id for on-demand upload sync. - add_action( 'cloudinary_id', array( $this, 'prep_on_demand_upload' ), 9, 2 ); - // Show sync status. - add_filter( 'cloudinary_media_status', array( $this, 'filter_status' ), 10, 2 ); - // Hook for on demand upload push. - add_action( 'shutdown', array( $this, 'init_background_upload' ) ); + // add_action( 'cloudinary_id', array( $this, 'prep_on_demand_upload' ), 9, 2 ); // Hook into auto upload sync. add_filter( 'cloudinary_on_demand_sync_enabled', array( $this, 'auto_sync_enabled' ), 10, 2 ); // Handle bulk and inline actions. @@ -202,7 +212,10 @@ public function push_on_upload( $post_id ) { */ public function setup() { if ( empty( $this->pusher ) ) { - $this->pusher = $this->plugin->components['sync']->managers['push']; + $this->pusher = $this->plugin->components['sync']->managers['push']; + $this->sync = $this->plugin->components['sync']; + $this->connect = $this->plugin->components['connect']; + $this->media = $this->plugin->components['media']; } $this->register_hooks(); } @@ -240,94 +253,100 @@ public function prep_on_demand_upload( $cloudinary_id, $attachment_id ) { } /** - * Prep an attachment for upload. + * Upload an asset to Cloudinary. * - * @param int $attachment_id The attachment ID to prep for upload. + * @param int $attachment_id The attachment ID. + * + * @return array */ - public function prep_upload( $attachment_id ) { - $max_size = ( wp_attachment_is_image( $attachment_id ) ? 'max_image_size' : 'max_video_size' ); - $file = get_attached_file( $attachment_id ); - // Get the file size to make sure it can exist in cloudinary. - if ( file_exists( $file ) && filesize( $file ) < $this->plugin->components['connect']->usage[ $max_size ] ) { - $this->add_to_sync( $attachment_id ); - } else { - // Check if the src is a url. - $file = get_post_meta( $attachment_id, '_wp_attached_file', true ); - if ( $this->plugin->components['media']->is_cloudinary_url( $file ) ) { - // Download sync. - $this->add_to_sync( $attachment_id ); - } - } + public function upload_asset( $attachment_id ) { + + $type = $this->sync->get_sync_type( $attachment_id ); + $file = get_attached_file( $attachment_id ); + $options = $this->media->get_upload_options( $attachment_id ); + $result = $this->connect->api->upload( $file, $options ); + + $this->sync->update_signature( $attachment_id, $type ); + $this->media->update_post_meta( $attachment->ID, Sync::META_KEYS['public_id'], $options['public_id'] ); + + $this->update_breakpoints( $attachment_id, $result ); + + return array(); } /** - * Add an attachment ID to the to_sync array. + * Update an assets context.. + * + * @param int $attachment_id The attachment ID. * - * @param int $attachment_id The attachment ID to add. + * @return array */ - 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; - } + public function context_update( $attachment_id ) { + // dynamic sync type.. + $type = $this->sync->get_sync_type( $attachment_id ); + $options = $this->media->get_upload_options( $attachment_id ); + $result = $this->connect->api->context( $options ); + + $this->sync->update_signature( $attachment_id, $type ); + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $options['public_id'] ); } /** - * Check if the attachment is pending an upload sync. + * Perform an explicit update to Cloudinary. * - * @param int $attachment_id The attachment ID to check. + * @param int $attachment_id The attachment ID. * - * @return bool + * @return array */ - public function is_pending( $attachment_id ) { - // Check if it's not already in the to sync array. - if ( ! in_array( $attachment_id, $this->to_sync, true ) ) { - $is_pending = $this->plugin->components['media']->get_post_meta( $attachment_id, Sync::META_KEYS['pending'], true ); - if ( empty( $is_pending ) || $is_pending < time() - 5 * 60 ) { - // No need to delete pending meta, since it will be updated with the new timestamp anyway. - return false; - } + public function explicit_update( $attachment_id ) { + // Explicit update. + $type = $this->sync->get_sync_type( $attachment_id ); + $args = $this->media->get_breakpoint_options( $attachment_id ); + if ( ! empty( $args ) ) { + $result = $this->connect->api->explicit( $args ); + $this->update_breakpoints( $attachment_id, $result ); + } else { + $this->update_breakpoints( $attachment_id, array() ); } - - return true; + $this->sync->update_signature( $attachment_id, $type ); } /** - * Checks the status of the media item. + * Update breakpoints for an asset. * - * @param array $status Array of state and note. - * @param int $attachment_id The attachment id. - * - * @return array + * @param int $attachment_id The attachment ID. + * @param array $breakpoints Structure of the breakpoints. */ - public function filter_status( $status, $attachment_id ) { + public function update_breakpoints( $attachment_id, $breakpoints ) { - if ( $this->is_pending( $attachment_id ) ) { - $status['state'] = 'warning'; - $status['note'] = __( 'Upload sync pending', 'cloudinary' ); - } - - // Check if there's an error. - $has_error = $this->plugin->components['media']->get_post_meta( $attachment_id, Sync::META_KEYS['sync_error'], true ); - if ( ! empty( $has_error ) ) { - $status['state'] = 'error'; - $status['note'] = $has_error; + if ( ! empty( $this->plugin->config['settings']['global_transformations']['enable_breakpoints'] ) ) { + if ( ! empty( $breakpoints['responsive_breakpoints'] ) ) { // Images only. + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['breakpoints'], $breakpoints['responsive_breakpoints'][0]['breakpoints'] ); + } elseif ( wp_attachment_is_image( $attachment_id ) ) { + $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['breakpoints'] ); + } } - - return $status; } /** - * Initialize the background sync on requested images needing to be synced. + * Prep an attachment for upload. + * + * @param int $attachment_id The attachment ID to prep for upload. */ - public function init_background_upload() { - if ( ! empty( $this->to_sync ) ) { - $params = array( - 'attachment_ids' => $this->to_sync, - ); - $this->plugin->components['api']->background_request( 'process', $params ); + public function prep_upload( $attachment_id ) { + $max_size = ( wp_attachment_is_image( $attachment_id ) ? 'max_image_size' : 'max_video_size' ); + $file = get_attached_file( $attachment_id ); + // Get the file size to make sure it can exist in cloudinary. + if ( file_exists( $file ) && filesize( $file ) < $this->plugin->components['connect']->usage[ $max_size ] ) { + $this->add_to_sync( $attachment_id ); + } else { + // Check if the src is a url. + $file = get_post_meta( $attachment_id, '_wp_attached_file', true ); + if ( $this->plugin->components['media']->is_cloudinary_url( $file ) ) { + // Download sync. + $this->add_to_sync( $attachment_id ); + } } } + } From 2328694cf88db91b94593e5006d31a4d7fb0e1cd Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 30 Jul 2020 13:22:26 +0200 Subject: [PATCH 03/64] cleanup background push --- .../php/sync/class-push-sync.php | 17 ++++++++-- .../php/sync/class-upload-sync.php | 32 ++++++++++++------- 2 files changed, 36 insertions(+), 13 deletions(-) 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 e95bc6e31..a67a75cd9 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 @@ -227,9 +227,22 @@ public function rest_push_attachments( \WP_REST_Request $request ) { // If not a single request, process based on queue. if ( ! empty( $attachments ) ) { + $stat = array(); // If a single specified ID, push and return response. - $ids = array_map( 'intval', $attachments ); - $stat = $this->push_attachments( $ids ); + $ids = array_map( 'intval', $attachments ); + // Handle based on Sync Type. + foreach ( $ids as $attachment_id ) { + $type = $this->sync->get_sync_type( $attachment_id ); + if ( ! is_wp_error( $type ) ) { + $callback = $this->sync->get_sync_method( $type ); + $stat[ $attachment_id ] = call_user_func( $callback, $attachment_id ); + + // remove pending. + if ( $this->sync->is_pending( $attachment_id ) ) { + $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['pending'] ); + } + } + } return rest_ensure_response( array( 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 dfc5fdd33..b78247bf7 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 @@ -257,7 +257,7 @@ public function prep_on_demand_upload( $cloudinary_id, $attachment_id ) { * * @param int $attachment_id The attachment ID. * - * @return array + * @return array|\WP_Error */ public function upload_asset( $attachment_id ) { @@ -266,12 +266,13 @@ public function upload_asset( $attachment_id ) { $options = $this->media->get_upload_options( $attachment_id ); $result = $this->connect->api->upload( $file, $options ); - $this->sync->update_signature( $attachment_id, $type ); - $this->media->update_post_meta( $attachment->ID, Sync::META_KEYS['public_id'], $options['public_id'] ); - - $this->update_breakpoints( $attachment_id, $result ); + if ( ! is_wp_error( $result ) ) { + $this->sync->update_signature( $attachment_id, $type ); + $this->media->update_post_meta( $attachment->ID, Sync::META_KEYS['public_id'], $options['public_id'] ); + $this->update_breakpoints( $attachment_id, $result ); + } - return array(); + return $result; } /** @@ -279,7 +280,7 @@ public function upload_asset( $attachment_id ) { * * @param int $attachment_id The attachment ID. * - * @return array + * @return array|\WP_Error */ public function context_update( $attachment_id ) { // dynamic sync type.. @@ -287,8 +288,12 @@ public function context_update( $attachment_id ) { $options = $this->media->get_upload_options( $attachment_id ); $result = $this->connect->api->context( $options ); - $this->sync->update_signature( $attachment_id, $type ); - $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $options['public_id'] ); + if ( ! is_wp_error( $result ) ) { + $this->sync->update_signature( $attachment_id, $type ); + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $options['public_id'] ); + } + + return $result; } /** @@ -296,7 +301,7 @@ public function context_update( $attachment_id ) { * * @param int $attachment_id The attachment ID. * - * @return array + * @return array|\WP_Error|bool */ public function explicit_update( $attachment_id ) { // Explicit update. @@ -304,11 +309,16 @@ public function explicit_update( $attachment_id ) { $args = $this->media->get_breakpoint_options( $attachment_id ); if ( ! empty( $args ) ) { $result = $this->connect->api->explicit( $args ); - $this->update_breakpoints( $attachment_id, $result ); + if ( ! is_wp_error( $result ) ) { + $this->update_breakpoints( $attachment_id, $result ); + } } else { $this->update_breakpoints( $attachment_id, array() ); + $result = true; } $this->sync->update_signature( $attachment_id, $type ); + + return $result; } /** From 832fcac59bf97a88c8f99d922343f4d2ff6a5e07 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Fri, 31 Jul 2020 05:22:06 +0200 Subject: [PATCH 04/64] use URL if possible --- .../php/class-sync.php | 5 ++- .../php/connect/class-api.php | 40 +++++++++++++------ .../php/sync/class-upload-sync.php | 17 ++++---- 3 files changed, 39 insertions(+), 23 deletions(-) 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 3c5192ff4..33da70027 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 @@ -462,8 +462,9 @@ public function maybe_prepare_sync( $attachment_id ) { return; } - - $this->add_to_sync( $attachment_id ); + if ( ! $this->is_pending( $attachment_id ) && apply_filters( 'cloudinary_on_demand_sync_enabled', $this->enabled, $attachment_id ) ) { + $this->add_to_sync( $attachment_id ); + } } /** diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php index 80804d5d5..7c516754b 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php @@ -356,13 +356,13 @@ public function upload_large( $file, $args ) { /** * Upload an asset. * - * @param string $file Path to the file. - * @param array $args Array of upload options. - * @param array $headers Additional headers to use in upload. + * @param int $attachment_id Attachment ID to upload. + * @param array $args Array of upload options. + * @param array $headers Additional headers to use in upload. * * @return array|\WP_Error */ - public function upload( $file, $args, $headers = array() ) { + public function upload( $attachment_id, $args, $headers = array() ) { $tempfile = false; if ( false !== strpos( $file, 'vip://' ) ) { $file = $this->create_local_copy( $file ); @@ -371,15 +371,22 @@ public function upload( $file, $args, $headers = array() ) { } $tempfile = true; } - $resource = ! empty( $args['resource_type'] ) ? $args['resource_type'] : 'image'; - $url = $this->url( $resource, 'upload', true ); - $args = $this->clean_args( $args ); - // Attach File. - if ( function_exists( 'curl_file_create' ) ) { - $args['file'] = curl_file_create( $file ); // phpcs:ignore - $args['file']->setPostFilename( $file ); + $resource = ! empty( $args['resource_type'] ) ? $args['resource_type'] : 'image'; + $url = $this->url( $resource, 'upload', true ); + $args = $this->clean_args( $args ); + $disable_https_fetch = get_transient( '_cld_disable_http_upload' ); + // Validate URL. + if ( empty( $disable_https_fetch ) ) { + $args['file'] = wp_get_attachment_url( $attachment_id ); } else { - $args['file'] = '@' . $file; + $file = get_attached_file( $attachment_id ); + // Attach File. + if ( function_exists( 'curl_file_create' ) ) { + $args['file'] = curl_file_create( $file ); // phpcs:ignore + $args['file']->setPostFilename( $file ); + } else { + $args['file'] = '@' . $file; + } } $call_args = array( @@ -388,6 +395,15 @@ public function upload( $file, $args, $headers = array() ) { ); $result = $this->call( $url, $call_args, 'post' ); + // Hook in flag to allow for non accessible URLS. + if ( is_wp_error( $result ) ) { + $error = $result->get_error_message(); + if ( false !== strpos( $error, 'ERR_DNS_FAIL' ) ) { + // URLS are not remotely available, try again as a file. + set_transient( '_cld_disable_http_upload', true, 300 ); + $result = $this->upload( $attachment_id, $args, $headers ); + } + } if ( true === $tempfile ) { unlink( $file ); //phpcs:ignore } 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 b78247bf7..52cbe267d 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 @@ -152,14 +152,14 @@ public function handle_bulk_actions( $location, $action, $post_ids ) { switch ( $action ) { case 'cloudinary-push' : foreach ( $post_ids as $post_id ) { - delete_post_meta( $post_id, Sync::META_KEYS['sync_error'] ); - delete_post_meta( $post_id, Sync::META_KEYS['public_id'] ); - delete_post_meta( $post_id, Sync::META_KEYS['pending'] ); - delete_post_meta( $post_id, Sync::META_KEYS['downloading'] ); - delete_post_meta( $post_id, Sync::META_KEYS['syncing'] ); + $this->media->delete_post_meta( $post_id, Sync::META_KEYS['sync_error'] ); + $this->media->delete_post_meta( $post_id, Sync::META_KEYS['public_id'] ); + $this->media->delete_post_meta( $post_id, Sync::META_KEYS['pending'] ); + $this->media->delete_post_meta( $post_id, Sync::META_KEYS['downloading'] ); + $this->media->delete_post_meta( $post_id, Sync::META_KEYS['syncing'] ); $file = get_attached_file( $post_id ); wp_generate_attachment_metadata( $post_id, $file ); - $this->prep_upload( $post_id ); + $this->sync->add_to_sync( $post_id ); } break; } @@ -262,13 +262,12 @@ public function prep_on_demand_upload( $cloudinary_id, $attachment_id ) { public function upload_asset( $attachment_id ) { $type = $this->sync->get_sync_type( $attachment_id ); - $file = get_attached_file( $attachment_id ); $options = $this->media->get_upload_options( $attachment_id ); - $result = $this->connect->api->upload( $file, $options ); + $result = $this->connect->api->upload( $attachment_id, $options ); if ( ! is_wp_error( $result ) ) { $this->sync->update_signature( $attachment_id, $type ); - $this->media->update_post_meta( $attachment->ID, Sync::META_KEYS['public_id'], $options['public_id'] ); + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $options['public_id'] ); $this->update_breakpoints( $attachment_id, $result ); } From e8947120f0625b0df33deb241b01e47ee431560f Mon Sep 17 00:00:00 2001 From: David Cramer Date: Fri, 31 Jul 2020 05:37:42 +0200 Subject: [PATCH 05/64] comment --- .../php/connect/class-api.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php index 7c516754b..cb98f180d 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php @@ -375,7 +375,8 @@ public function upload( $attachment_id, $args, $headers = array() ) { $url = $this->url( $resource, 'upload', true ); $args = $this->clean_args( $args ); $disable_https_fetch = get_transient( '_cld_disable_http_upload' ); - // Validate URL. + + // Check if we can try http file upload. if ( empty( $disable_https_fetch ) ) { $args['file'] = wp_get_attachment_url( $attachment_id ); } else { From 75a33df005a1b33811d5353aae56f849e6cfa3c5 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Fri, 31 Jul 2020 10:46:37 +0200 Subject: [PATCH 06/64] add upgrade and version sync system --- .../php/class-media.php | 56 +++-- .../php/class-settings-page.php | 14 -- .../php/class-sync.php | 233 ++++++++++++++---- .../php/media/class-upgrade.php | 79 +++--- .../php/sync/class-download-sync.php | 93 ++++++- .../php/sync/class-push-sync.php | 20 +- 6 files changed, 356 insertions(+), 139 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 182dcf16a..9643648ed 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -467,6 +467,11 @@ public function attachment_url( $url, $attachment_id ) { if ( false !== $cloudinary_id ) { $url = $this->cloudinary_url( $attachment_id ); } + // Previous v1. + $previous_url = strpos( $url, untrailingslashit( $this->base_url ) ); + if ( false !== $previous_url ) { + $url = substr( $url, $previous_url ); + } } return $url; @@ -642,12 +647,7 @@ public function get_public_id( $attachment_id ) { // Check for a public_id. $public_id = $this->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true ); if ( empty( $public_id ) ) { - // No public_id is an up-sync (WP->CLD). - // Build a public_id based on cloudinary folder, and filename. - $file = get_attached_file( $attachment_id ); - $info = pathinfo( $file ); - $cloudinary_folder = $this->get_cloudinary_folder(); - $public_id = $cloudinary_folder . $info['filename']; + $public_id = false; } return $public_id; @@ -663,16 +663,18 @@ public function get_public_id( $attachment_id ) { public function get_cloudinary_id( $attachment_id ) { // A cloudinary_id is a public_id with a file extension. - $public_id = $this->get_public_id( $attachment_id ); - $suffix_data = $this->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); - if ( is_array( $suffix_data ) && ! empty( $suffix_data['suffix'] ) && $suffix_data['public_id'] === $public_id ) { - $public_id = $public_id . $suffix_data['suffix']; + $public_id = $this->get_public_id( $attachment_id ); + if ( ! empty( $public_id ) ) { + $suffix_data = $this->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); + if ( is_array( $suffix_data ) && ! empty( $suffix_data['suffix'] ) && $suffix_data['public_id'] === $public_id ) { + $public_id = $public_id . $suffix_data['suffix']; + } + $file = get_attached_file( $attachment_id ); + $info = pathinfo( $file ); + $public_id = $public_id . '.' . $info['extension']; } - $file = get_attached_file( $attachment_id ); - $info = pathinfo( $file ); - $cloudinary_id = $public_id . '.' . $info['extension']; - return $cloudinary_id; + return $public_id; } /** @@ -688,16 +690,15 @@ public function cloudinary_id( $attachment_id ) { return false; } // Return cached ID if we've already gotten it before. - if ( ! empty( $this->cloudinary_ids[ $attachment_id ] ) ) { + if ( isset( $this->cloudinary_ids[ $attachment_id ] ) ) { return $this->cloudinary_ids[ $attachment_id ]; } - $cloudinary_id = false; - if ( $this->plugin->components['sync']->is_synced( $attachment_id ) ) { - $cloudinary_id = $this->get_cloudinary_id( $attachment_id ); - } else { + if ( ! $this->plugin->components['sync']->is_synced( $attachment_id ) && ! defined( 'REST_REQUEST' ) ) { $this->plugin->components['sync']->maybe_prepare_sync( $attachment_id ); } + $cloudinary_id = $this->get_cloudinary_id( $attachment_id ); + /** * Filter to validate the Cloudinary ID to allow extending it's availability. * @@ -716,9 +717,7 @@ public function cloudinary_id( $attachment_id ) { */ do_action( 'cloudinary_id', $cloudinary_id, $attachment_id ); // Cache ID to prevent multiple lookups. - if ( false !== $cloudinary_id ) { - $this->cloudinary_ids[ $attachment_id ] = $cloudinary_id; - } + $this->cloudinary_ids[ $attachment_id ] = $cloudinary_id; return $cloudinary_id; } @@ -1300,13 +1299,16 @@ public function build_cached_meta( $post_id, $key, $single ) { */ public function update_post_meta( $post_id, $key, $data ) { $meta_data = wp_get_attachment_metadata( $post_id, true ); - if ( is_array( $meta_data ) && isset( $meta_data[ Sync::META_KEYS['cloudinary'] ] ) && is_array( $meta_data[ Sync::META_KEYS['cloudinary'] ] ) ) { - // Only do this side if has been set before. + if ( is_array( $meta_data ) ) { + if ( ! isset( $meta_data[ Sync::META_KEYS['cloudinary'] ] ) ) { + $meta_data[ Sync::META_KEYS['cloudinary'] ] = array(); + } $meta_data[ Sync::META_KEYS['cloudinary'] ][ $key ] = $data; wp_update_attachment_metadata( $post_id, $meta_data ); + } else { + // Update core mete data for consistency. + update_post_meta( $post_id, $key, $data ); } - // Update core mete data for consistency. - update_post_meta( $post_id, $key, $data ); } /** @@ -1388,7 +1390,7 @@ public function get_context_options( $attachment_id ) { ); // Check if this asset is a folder sync. - $folder_sync = $this->get_post_meta( $post->ID, Sync::META_KEYS['folder_sync'], true ); + $folder_sync = $this->get_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true ); if ( ! empty( $folder_sync ) ) { $context_options['wp_id'] = $attachment_id; } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-settings-page.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-settings-page.php index 6a1c6200d..9ed130a89 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-settings-page.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-settings-page.php @@ -914,18 +914,4 @@ public function set_active_page( $page_slug ) { } } - /** - * Checks if auto sync feature is enabled. - * - * @return bool - */ - public function is_auto_sync_enabled() { - $settings = $this->get_config(); - - if ( ! empty( $settings['sync_media']['auto_sync'] ) && 'on' === $settings['sync_media']['auto_sync'] ) { - return true; - } - - return false; - } } 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 33da70027..0d09d4e05 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 @@ -64,6 +64,7 @@ class Sync implements Setup, Assets { 'pending' => '_cloudinary_pending', 'signature' => '_sync_signature', 'version' => '_cloudinary_version', + 'plugin_version' => '_plugin_version', 'breakpoints' => '_cloudinary_breakpoints', 'public_id' => '_public_id', 'transformation' => '_transformations', @@ -73,6 +74,7 @@ class Sync implements Setup, Assets { 'suffix' => '_suffix', 'syncing' => '_cloudinary_syncing', 'downloading' => '_cloudinary_downloading', + 'process_log' => '_process_log', ); /** @@ -106,6 +108,10 @@ public function enqueue_assets() { * Register Assets. */ public function register_assets() { + // Setup the sync_base_structure. + $this->setup_sync_base_struct(); + // Setup sync types. + $this->setup_sync_types(); } @@ -116,6 +122,21 @@ public function is_active() { return $this->plugin->components['settings']->is_active() && 'sync_media' === $this->plugin->components['settings']->active_tab(); } + /** + * Checks if an asset has been synced and up to date. + * + * @param int $attachment_id The attachment id to check. + * + * @return bool + */ + public function been_synced( $attachment_id ) { + + $public_id = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true ); + $meta = wp_get_attachment_metadata( $attachment_id ); + + return ! empty( $public_id ) || ! empty( $meta['cloudinary'] ); // From v1. + } + /** * Checks if an asset is synced and up to date. * @@ -131,23 +152,20 @@ public function is_synced( $post_id ) { return true; } - if ( $this->plugin->components['settings']->is_auto_sync_enabled() && apply_filters( 'cloudinary_flag_sync', '__return_false' ) && ! get_post_meta( $post_id, Sync::META_KEYS['downloading'], true ) ) { - update_post_meta( $post_id, Sync::META_KEYS['syncing'], true ); - } - return false; } /** * Generate a signature based on whats required for a full sync. * - * @param int $attachment_id The Attachment id to generate a signature for. + * @param int $attachment_id The Attachment id to generate a signature for. + * @param bool $cached Flag to specify if a cached signature is to be used or build a new one. * * @return string|bool */ - public function generate_signature( $attachment_id ) { + public function generate_signature( $attachment_id, $cache = true ) { static $signatures = array(); // cache signatures. - if ( ! empty( $signatures[ $attachment_id ] ) ) { + if ( ! empty( $signatures[ $attachment_id ] && true === $cache ) ) { $return = $signatures[ $attachment_id ]; } else { $sync_base = $this->sync_base( $attachment_id ); @@ -177,6 +195,8 @@ function ( $item ) { */ public function can_sync( $attachment_id, $type = 'file' ) { + $can = ( ! $this->is_pending( $attachment_id ) && $this->been_synced( $attachment_id ) ) || $this->is_auto_sync_enabled(); + /** * Filter to allow changing if an asset is allowed to be synced. * Return a WP Error with reason why it can't be synced. @@ -185,28 +205,35 @@ public function can_sync( $attachment_id, $type = 'file' ) { * * @return bool|\WP_Error */ - return apply_filters( 'cloudinary_can_sync_asset', true, $attachment_id, $type ); + return apply_filters( 'cloudinary_can_sync_asset', $can, $attachment_id, $type ); + } + + public function get_sync_version( $attachment_id ) { + $version = $this->managers['media']->get_post_meta( $attachment_id, self::META_KEYS['plugin_version'], true ); + + return $version; } /** * Get the current sync signature of an asset. * - * @param int $attachment_id The attachment ID. + * @param int $attachment_id The attachment ID. + * @param bool $cached Flag to specify if a cached signature is to be used or build a new one. * * @return array|bool */ - public function get_signature( $attachment_id ) { + public function get_signature( $attachment_id, $cached = true ) { static $signatures = array(); // Cache signatures already fetched. $return = array(); - if ( ! empty( $signatures[ $attachment_id ] ) ) { + if ( ! empty( $signatures[ $attachment_id ] ) && true === $cached ) { $return = $signatures[ $attachment_id ]; } else { $signature = $this->managers['media']->get_post_meta( $attachment_id, self::META_KEYS['signature'], true ); if ( ! empty( $signature ) ) { - $base_signatures = $this->generate_signature( $attachment_id ); - $signatures[ $attachment_id ] = wp_parse_args( $signature, $base_signatures ); - $return = $signatures[ $attachment_id ]; + //$base_signatures = $this->generate_signature( $attachment_id ); + $signatures[ $attachment_id ] = $signature; + $return = $signature; } } @@ -290,12 +317,38 @@ public function add_suffix_maybe( $public_id, $attachment_id, $suffix = null ) { public function setup_sync_base_struct() { $base_struct = array( + 'upgrade' => array( + 'generate' => array( $this, 'get_sync_version' ), + 'sync' => array( + 'priority' => 0, + 'callback' => array( $this->managers['media']->upgrade, 'convert_cloudinary_version' ), + ), + 'status' => array( + 'state' => 'info syncing', + 'note' => __( 'Upgrading from Previous version', 'cloudinary' ), + ), + ), + 'download' => array( + 'generate' => '__return_false', + 'sync' => array( + 'priority' => 1, + 'callback' => array( $this->managers['download'], 'download_asset' ), + ), + 'status' => array( + 'state' => 'info downloading', + 'note' => __( 'Downloading from Cloudinary', 'cloudinary' ), + ), + ), 'file' => array( 'generate' => 'get_attached_file', 'sync' => array( - 'priority' => 0, + 'priority' => 5, 'callback' => array( $this->managers['upload'], 'upload_asset' ), ), + 'status' => array( + 'state' => 'info syncing', + 'note' => __( 'Uploading to Cloudinary', 'cloudinary' ), + ), ), 'folder' => array( 'generate' => array( $this->managers['media'], 'get_cloudinary_folder' ), @@ -303,6 +356,12 @@ public function setup_sync_base_struct() { 'priority' => 10, 'callback' => array( $this->managers['upload'], 'upload_asset' ), ), + 'status' => array( + 'state' => 'info syncing', + 'note' => function () { + return sprintf( __( 'Moving to folder %s.', 'cloudinary' ), $this->managers['media']->get_cloudinary_folder() ); + }, + ), ), 'public_id' => array( 'generate' => array( $this->managers['media'], 'get_public_id' ), @@ -310,6 +369,10 @@ public function setup_sync_base_struct() { 'priority' => 20, 'callback' => array( $this->managers['push'], 'push_attachments' ), // Rename ), + 'status' => array( + 'state' => 'meta', + 'note' => __( 'Updating metadata', 'cloudinary' ), + ), ), 'suffix' => array( 'generate' => array( $this, 'get_suffix_maybe' ), @@ -317,6 +380,10 @@ public function setup_sync_base_struct() { 'priority' => 10, 'callback' => array( $this->managers['upload'], 'upload_asset' ), ), + 'status' => array( + 'state' => 'uploading', + 'note' => __( 'Creating unique version', 'cloudinary' ), + ), ), 'breakpoints' => array( 'generate' => array( $this->managers['media'], 'get_breakpoint_options' ), @@ -324,6 +391,10 @@ public function setup_sync_base_struct() { 'priority' => 25, 'callback' => array( $this->managers['upload'], 'explicit_update' ), ), + 'status' => array( + 'state' => 'info syncing', + 'note' => __( 'Updating breakpoints', 'cloudinary' ), + ), ), 'options' => array( 'generate' => array( $this->managers['media'], 'get_upload_options' ), @@ -331,6 +402,10 @@ public function setup_sync_base_struct() { 'priority' => 30, 'callback' => array( $this->managers['upload'], 'context_update' ), ), + 'status' => array( + 'state' => 'info syncing', + 'note' => __( 'Updating metadata', 'cloudinary' ), + ), ), 'cloud_name' => array( 'generate' => array( $this->managers['connect'], 'get_cloud_name' ), @@ -338,6 +413,10 @@ public function setup_sync_base_struct() { 'priority' => 5, 'callback' => array( $this->managers['upload'], 'upload_asset' ), ), + 'status' => array( + 'state' => 'uploading', + 'note' => __( 'Uploading to new cloud name.', 'cloudinary' ), + ), ), ); @@ -353,19 +432,26 @@ public function setup_sync_base_struct() { // Apply a default to ensure parts exist.s $structs = array_map( function ( $component ) { - $sync_default = array( + $sync_default = array( 'priority' => 50, 'callback' => '__return_null', ); - $default = array( + $status_default = array( + 'state' => 'sync', + 'note' => __( 'Syncronizing asset with Cloudinary', 'cloudinary' ), + ); + $default = array( 'generate' => '__return_null', 'sync' => array(), + 'status' => array(), ); // Ensure correct struct. $component = wp_parse_args( $component, $default ); $component['sync'] = wp_parse_args( $component['sync'], $sync_default ); $component['sync']['priority'] = is_int( $component['sync']['priority'] ) ? (int) $component['sync']['priority'] : 50; + $component['status'] = wp_parse_args( $component['status'], $status_default ); + return $component; }, @@ -455,14 +541,7 @@ public function sync_base( $post ) { public function maybe_prepare_sync( $attachment_id ) { $type = $this->get_sync_type( $attachment_id ); - // Check if has an error (ususally due to file quotas). - $can_sync = $this->can_sync( $attachment_id, $type ); - if ( is_wp_error( $can_sync ) ) { - $this->managers['media']->get_post_meta( $attachment_id, self::META_KEYS['sync_error'], $can_sync->get_error_message() ); - - return; - } - if ( ! $this->is_pending( $attachment_id ) && apply_filters( 'cloudinary_on_demand_sync_enabled', $this->enabled, $attachment_id ) ) { + if ( $this->can_sync( $attachment_id, $type ) ) { $this->add_to_sync( $attachment_id ); } } @@ -470,17 +549,19 @@ public function maybe_prepare_sync( $attachment_id ) { /** * Get the type of sync, with the lowest priority for this asset. * - * @param $attachment_id + * @param int $attachment_id The attachment ID. + * @param bool $cached Flag to specify if a cached signature is to be used or build a new one. * * @return mixed|string|\WP_Error */ - public function get_sync_type( $attachment_id ) { - $attachment_signature = $this->get_signature( $attachment_id ); + public function get_sync_type( $attachment_id, $cached = true ) { + $attachment_signature = $this->get_signature( $attachment_id, $cached ); // Set default sync type. - $type = array_shift( array_keys( $this->sync_types ) ); // Lowest sync type should always be a full sync. + $types = array_keys( $this->sync_types ); + $type = array_shift( $types ); // Lowest sync type should always be a full sync. if ( ! empty( $attachment_signature ) ) { // Has signature find differences and use specific sync method. - $required_signature = $this->generate_signature( $attachment_id ); + $required_signature = $this->generate_signature( $attachment_id, $cached ); if ( is_array( $required_signature ) ) { $sync_items = array_diff( $required_signature, $attachment_signature ); $ordered = array_intersect_key( $this->sync_types, $sync_items ); @@ -488,11 +569,6 @@ public function get_sync_type( $attachment_id ) { } } - // Nothing to sync. - if ( empty( $type ) ) { - $type = new \WP_Error( 'attachment_synced', __( 'Attachment is already fully synced.', 'cloudinary' ) ); - } - return $type; } @@ -506,21 +582,45 @@ public function get_sync_type( $attachment_id ) { */ public function filter_status( $status, $attachment_id ) { - if ( $this->is_pending( $attachment_id ) ) { - $status['state'] = 'warning'; - $status['note'] = __( 'Upload sync pending', 'cloudinary' ); - } + if ( $this->been_synced( $attachment_id ) ) { + $sync_type = $this->get_sync_type( $attachment_id ); + if ( ! empty( $sync_type ) && isset( $this->sync_base_struct[ $sync_type ] ) ) { + $status = $this->sync_base_struct[ $sync_type ]['status']; + if ( is_callable( $status['note'] ) ) { + $status['note'] = call_user_func( $status['note'] ); + } + } + - // Check if there's an error. - $has_error = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['sync_error'], true ); - if ( ! empty( $has_error ) ) { - $status['state'] = 'error'; - $status['note'] = $has_error; + // Check if there's an error. + $has_error = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['sync_error'], true ); + if ( ! empty( $has_error ) ) { + $status['state'] = 'error'; + $status['note'] = $has_error; + } } return $status; } + /** + * Add media state to display syncing info. + * + * @param array $media_states List of the states. + * @param \WP_Post $post The current attachment post. + * + * @return array + */ + public function filter_media_states( $media_states, $post ) { + + $status = apply_filters( 'cloudinary_media_status', array(), $post->ID ); + if ( ! empty( $status ) ) { + $media_states[] = $status['note']; + } + + return $media_states; + } + /** * Check if the attachment is pending an upload sync. * @@ -563,22 +663,36 @@ public function add_to_sync( $attachment_id ) { */ public function update_signature( $attachment_id, $type ) { - $current_signature = $this->get_signature( $attachment_id ); $expecting = $this->generate_signature( $attachment_id ); $current_sync_method = $this->sync_base_struct[ $type ]['sync']['callback']; - $meta = wp_get_attachment_metadata( $attachment_id, true ); // Go over all other types that share the same sync method and include them here. foreach ( $this->sync_base_struct as $sync_type => $struct ) { if ( $struct['sync']['callback'] === $current_sync_method ) { - $current_signature[ $sync_type ] = $expecting[ $sync_type ]; + $this->set_signature_item( $attachment_id, $sync_type, $expecting[ $sync_type ] ); } } - $meta[ Sync::META_KEYS['cloudinary'] ][ Sync::META_KEYS['signature'] ] = $current_signature; - wp_update_attachment_metadata( $attachment_id, $meta ); } + public function set_signature_item( $attachment_id, $type, $value = null ) { + + // Get the core meta. + $meta = wp_get_attachment_metadata( $attachment_id, true ); + + // Set the specific value. + if ( is_null( $value ) ) { + // Generate a new value based on generator. + $new_value = call_user_func( $this->sync_base_struct[ $type ]['generate'], $attachment_id ); + if ( is_array( $value ) ) { + $new_value = wp_json_encode( $new_value ); + } + $value = md5( $new_value ); + } + $meta[ Sync::META_KEYS['cloudinary'] ][ Sync::META_KEYS['signature'] ][ $type ] = $value; + wp_update_attachment_metadata( $attachment_id, $meta ); + } + /** * Initialize the background sync on requested images needing to be synced. */ @@ -591,6 +705,21 @@ public function init_background_upload() { } } + /** + * Checks if auto sync feature is enabled. + * + * @return bool + */ + public function is_auto_sync_enabled() { + $settings = $this->plugin->config['settings']; + + if ( ! empty( $settings['sync_media']['auto_sync'] ) && 'on' === $settings['sync_media']['auto_sync'] ) { + return true; + } + + return false; + } + /** * Additional component setup. */ @@ -599,20 +728,18 @@ public function setup() { // Show sync status. add_filter( 'cloudinary_media_status', array( $this, 'filter_status' ), 10, 2 ); + add_filter( 'display_media_states', array( $this, 'filter_media_states' ), 10, 2 ); // Hook for on demand upload push. add_action( 'shutdown', array( $this, 'init_background_upload' ) ); $this->managers['upload']->setup(); $this->managers['delete']->setup(); + $this->managers['download']->setup(); $this->managers['push']->setup(); // Setup additional components. $this->managers['media'] = $this->plugin->components['media']; $this->managers['connect'] = $this->plugin->components['connect']; $this->managers['api'] = $this->plugin->components['api']; - // Setup the sync_base_structure. - $this->setup_sync_base_struct(); - // Setup sync types. - $this->setup_sync_types(); } } } 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 29430a9c4..6945227ac 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 @@ -146,52 +146,59 @@ public function filter_status( $status, $attachment_id ) { */ public function convert_cloudinary_version( $attachment_id ) { - $file = get_post_meta( $attachment_id, '_wp_attached_file', true ); - $path = wp_parse_url( $file, PHP_URL_PATH ); - $media = $this->media; - $parts = explode( '/', $path ); - $parts = array_map( - function ( $val ) use ( $media ) { + $file = get_post_meta( $attachment_id, '_wp_attached_file', true ); + if ( wp_http_validate_url( $file ) ) { + // Version 1 upgrade. + $path = wp_parse_url( $file, PHP_URL_PATH ); + $media = $this->media; + $parts = explode( '/', ltrim( $path, '/' ) ); + $cloud_name = null; + $asset_version = 1; + $asset_transformations = array(); + $id_parts = array(); + foreach ( $parts as $val ) { if ( empty( $val ) ) { - return false; + continue; } - if ( $val === $media->credentials['cloud_name'] ) { - return false; + if ( is_null( $cloud_name ) ) { + // Cloudname will always be the first item. + $cloud_name = md5( $val ); + continue; } if ( in_array( $val, [ 'image', 'video', 'upload' ], true ) ) { - return false; + continue; } $transformation_maybe = $media->get_transformations_from_string( $val ); if ( ! empty( $transformation_maybe ) ) { - return false; + $asset_transformations = $transformation_maybe; + continue; } if ( substr( $val, 0, 1 ) === 'v' && is_numeric( substr( $val, 1 ) ) ) { - return false; + $asset_version = substr( $val, 1 ); + continue; } - return $val; - }, - $parts - ); - // Build public_id. - $parts = array_filter( $parts ); - $public_id = implode( '/', $parts ); - // Remove extension. - $path = pathinfo( $public_id ); - $public_id = strstr( $public_id, '.' . $path['extension'], true ); - $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $public_id ); - - // Flag the download - update_post_meta( $attachment_id, Sync::META_KEYS['downloading'], true ); - delete_post_meta( $attachment_id, Sync::META_KEYS['syncing'] ); - - if ( ! wp_next_scheduled( 'cloudinary_resume_upgrade' ) ) { - wp_schedule_single_event( time() + $this->cron_frequency, 'cloudinary_resume_upgrade' ); - } - - if ( ! defined( 'DOING_BULK_SYNC' ) ) { - $this->sync->managers['upload']->add_to_sync( $attachment_id ); // Auto sync if upgrading outside of bulk sync. + $id_parts[] = $val; + } + // Build public_id. + $parts = array_filter( $id_parts ); + $public_id = implode( '/', $parts ); + // Remove extension. + $path = pathinfo( $public_id ); + $public_id = str_replace( $path['basename'], $path['filename'], $public_id ); + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $public_id ); + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['version'], $asset_version ); + if ( ! empty( $asset_transformations ) ) { + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['transformation'], $asset_transformations ); + } + $this->sync->set_signature_item( $attachment_id, 'cloud_name', $cloud_name ); + } else { + // v2 upgrade. + $public_id = $this->media->get_public_id( $attachment_id ); } + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['plugin_version'], $this->media->plugin->version ); + $this->sync->set_signature_item( $attachment_id, 'upgrade' ); + $this->sync->set_signature_item( $attachment_id, 'public_id' ); return $public_id; } @@ -227,10 +234,10 @@ public function maybe_resume_upgrade() { * Setup hooks for the filters. */ public function setup_hooks() { - add_filter( 'validate_cloudinary_id', array( $this, 'check_cloudinary_version' ), 10, 2 ); // Priority 10, to allow prep_on_demand_upload. + //add_filter( 'validate_cloudinary_id', array( $this, 'check_cloudinary_version' ), 10, 2 ); // Priority 10, to allow prep_on_demand_upload. // Show sync status. - add_filter( 'cloudinary_media_status', array( $this, 'filter_status' ), 20, 2 ); + //add_filter( 'cloudinary_media_status', array( $this, 'filter_status' ), 20, 2 ); // Add a redirection to the new plugin settings, from the old plugin. if ( is_admin() ) { 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 cf7c65a99..d20cd687b 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 @@ -25,6 +25,20 @@ class Download_Sync { */ private $plugin; + /** + * Holds the Media component. + * + * @var \Cloudinary\Media + */ + protected $media; + + /** + * Holds the Sync component. + * + * @var \Cloudinary\Sync + */ + protected $sync; + /** * Download_Sync constructor. * @@ -171,6 +185,74 @@ function ( $val ) use ( $media ) { return $this->download_asset( $attachment_id, $file, basename( $file ), $media->get_transformations_from_string( $file ) ); } + /** + * Download an attachment source to the file system. + * + * @param int $attachment_id The attachment ID. + * + * @return array|\WP_Error + */ + public function download_asset( $attachment_id ) { + require_once ABSPATH . 'wp-admin/includes/image.php'; + require_once ABSPATH . 'wp-admin/includes/media.php'; + $source = $this->media->cloudinary_url( $attachment_id ); + $file_name = basename( $source ); + try { + // Prime a file to stream to. + $upload = wp_upload_bits( $file_name, null, 'temp' ); + if ( ! empty( $upload['error'] ) ) { + return new \WP_Error( 'download_error', $upload['error'] ); + } + // Stream file to primed file. + $response = wp_safe_remote_get( + $source, + array( + 'timeout' => 300, // phpcs:ignore + 'stream' => true, + 'filename' => $upload['file'], + ) + ); + + if ( is_wp_error( $response ) ) { + return $response; + } + if ( 200 !== $response['response']['code'] ) { + $header_error = wp_remote_retrieve_header( $response, 'x-cld-error' ); + if ( ! empty( $header_error ) ) { + $error = $header_error; + } else { + $error = __( 'Could not download the Cloudinary asset.', 'cloudinary' ); + } + + return new \WP_Error( 'download_error', $error ); + } + + // Prepare the asset. + update_attached_file( $attachment_id, $upload['file'] ); + $old_meta = wp_get_attachment_metadata( $attachment_id ); + ob_start(); // Catch possible errors in WordPress's ID3 module when setting meta for transformed videos. + $meta = wp_generate_attachment_metadata( $attachment_id, $upload['file'] ); + $captured_errors = ob_get_clean(); + $meta[ Sync::META_KEYS['cloudinary'] ] = $old_meta[ Sync::META_KEYS['cloudinary'] ]; + wp_update_attachment_metadata( $attachment_id, $meta ); + $this->sync->set_signature_item( $attachment_id, 'download' ); + $this->sync->set_signature_item( $attachment_id, 'file' ); + $this->sync->set_signature_item( $attachment_id, 'folder' ); + // Update the folder synced flag. + $public_id = $this->media->get_public_id( $attachment_id ); + $asset_folder = strpos( $public_id, '/' ) ? dirname( $public_id ) : '/'; + $cloudinary_folder = $this->media->get_cloudinary_folder(); + if ( $asset_folder === $cloudinary_folder ) { + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true ); + } + + } catch ( \Exception $e ) { + return new \WP_Error( 'download_error', $e->getMessage() ); + } + + return $source; + } + /** * Download an attachment source to the file system. * @@ -181,7 +263,7 @@ function ( $val ) use ( $media ) { * * @return array|\WP_Error */ - public function download_asset( $attachment_id, $file_path, $file_name, $transformations = null ) { + public function download_asset_old( $attachment_id, $file_path, $file_name, $transformations = null ) { // Get the image and update the attachment. require_once ABSPATH . WPINC . '/class-http.php'; @@ -257,4 +339,13 @@ public function download_asset( $attachment_id, $file_path, $file_name, $transfo return $response; } + + /** + * Setup this component. + */ + public function setup() { + // Setup components. + $this->media = $this->plugin->components['media']; + $this->sync = $this->plugin->components['sync']; + } } 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 a67a75cd9..d9e91aa7f 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 @@ -232,16 +232,20 @@ public function rest_push_attachments( \WP_REST_Request $request ) { $ids = array_map( 'intval', $attachments ); // Handle based on Sync Type. foreach ( $ids as $attachment_id ) { - $type = $this->sync->get_sync_type( $attachment_id ); - if ( ! is_wp_error( $type ) ) { - $callback = $this->sync->get_sync_method( $type ); - $stat[ $attachment_id ] = call_user_func( $callback, $attachment_id ); - - // remove pending. - if ( $this->sync->is_pending( $attachment_id ) ) { - $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['pending'] ); + while ( $type = $this->sync->get_sync_type( $attachment_id, false ) ) { + if ( isset( $stat[ $attachment_id ][ $type ] ) ) { + // Loop prevention. + break; } + $callback = $this->sync->get_sync_method( $type ); + $stat[ $attachment_id ][ $type ] = call_user_func( $callback, $attachment_id ); } + // remove pending. + if ( $this->sync->is_pending( $attachment_id ) ) { + $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['pending'] ); + } + // Record Process log. + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['process_log'], $stat[ $attachment_id ] ); } return rest_ensure_response( From 7e85e7852846f5b14b78f94c5047a7e8946a7eeb Mon Sep 17 00:00:00 2001 From: David Cramer Date: Fri, 31 Jul 2020 14:06:17 +0200 Subject: [PATCH 07/64] rework public_id checks and filters --- .../php/class-media.php | 18 ++++- .../php/class-sync.php | 74 ++++++++++++++----- .../php/connect/class-api.php | 2 +- .../php/sync/class-push-sync.php | 32 +++++++- .../php/sync/class-upload-queue.php | 16 +--- .../php/sync/class-upload-sync.php | 6 +- .../tabs/sync-media-content.php | 2 +- .../ui-definitions/tabs/sync-media-footer.php | 2 +- 8 files changed, 105 insertions(+), 47 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 9643648ed..b4e48b7a9 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -647,12 +647,23 @@ public function get_public_id( $attachment_id ) { // Check for a public_id. $public_id = $this->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true ); if ( empty( $public_id ) ) { - $public_id = false; + $public_id = $this->plugin->components['sync']->generate_public_id( $attachment_id ); } return $public_id; } + /** + * Check if an attachment has a public ID. + * + * @param int $attachment_id The Attachment ID. + * + * @return bool + */ + public function has_public_id( $attachment_id ) { + return ! empty( $this->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true ) ); + } + /** * Get a Cloudinary ID. * @@ -662,9 +673,10 @@ public function get_public_id( $attachment_id ) { */ public function get_cloudinary_id( $attachment_id ) { + $public_id = false; // A cloudinary_id is a public_id with a file extension. - $public_id = $this->get_public_id( $attachment_id ); - if ( ! empty( $public_id ) ) { + if ( $this->has_public_id( $attachment_id ) ) { + $public_id = $this->get_public_id( $attachment_id ); $suffix_data = $this->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); if ( is_array( $suffix_data ) && ! empty( $suffix_data['suffix'] ) && $suffix_data['public_id'] === $public_id ) { $public_id = $public_id . $suffix_data['suffix']; 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 0d09d4e05..21dc91b53 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 @@ -131,7 +131,7 @@ public function is_active() { */ public function been_synced( $attachment_id ) { - $public_id = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true ); + $public_id = $this->managers['media']->has_public_id( $attachment_id ); $meta = wp_get_attachment_metadata( $attachment_id ); return ! empty( $public_id ) || ! empty( $meta['cloudinary'] ); // From v1. @@ -147,7 +147,8 @@ public function been_synced( $attachment_id ) { public function is_synced( $post_id ) { $signature = $this->get_signature( $post_id ); $expecting = $this->generate_signature( $post_id ); - + ksort( $signature ); + ksort( $expecting ); if ( ! empty( $signature ) && ! empty( $expecting ) && $expecting === $signature ) { return true; } @@ -230,11 +231,11 @@ public function get_signature( $attachment_id, $cached = true ) { $return = $signatures[ $attachment_id ]; } else { $signature = $this->managers['media']->get_post_meta( $attachment_id, self::META_KEYS['signature'], true ); - if ( ! empty( $signature ) ) { - //$base_signatures = $this->generate_signature( $attachment_id ); - $signatures[ $attachment_id ] = $signature; - $return = $signature; + if ( empty( $signature ) ) { + $signature = array(); } + $signatures[ $attachment_id ] = $return; + $return = wp_parse_args( $signature, $this->sync_types ); } return $return; @@ -319,6 +320,7 @@ public function setup_sync_base_struct() { $base_struct = array( 'upgrade' => array( 'generate' => array( $this, 'get_sync_version' ), + 'validate' => array( $this, 'been_synced' ), 'sync' => array( 'priority' => 0, 'callback' => array( $this->managers['media']->upgrade, 'convert_cloudinary_version' ), @@ -330,6 +332,11 @@ public function setup_sync_base_struct() { ), 'download' => array( 'generate' => '__return_false', + 'validate' => function ( $attachment_id ) { + $file = get_attached_file( $attachment_id ); + + return ! file_exists( $file ); + }, 'sync' => array( 'priority' => 1, 'callback' => array( $this->managers['download'], 'download_asset' ), @@ -365,6 +372,10 @@ public function setup_sync_base_struct() { ), 'public_id' => array( 'generate' => array( $this->managers['media'], 'get_public_id' ), + 'validate' => function ( $attachment_id ) { + $public_id = $this->managers['media']->has_public_id( $attachment_id ); + return false === $public_id; + }, 'sync' => array( 'priority' => 20, 'callback' => array( $this->managers['push'], 'push_attachments' ), // Rename @@ -555,17 +566,28 @@ public function maybe_prepare_sync( $attachment_id ) { * @return mixed|string|\WP_Error */ public function get_sync_type( $attachment_id, $cached = true ) { + $type = false; + $required_signature = $this->generate_signature( $attachment_id, $cached ); $attachment_signature = $this->get_signature( $attachment_id, $cached ); - // Set default sync type. - $types = array_keys( $this->sync_types ); - $type = array_shift( $types ); // Lowest sync type should always be a full sync. - if ( ! empty( $attachment_signature ) ) { - // Has signature find differences and use specific sync method. - $required_signature = $this->generate_signature( $attachment_id, $cached ); - if ( is_array( $required_signature ) ) { - $sync_items = array_diff( $required_signature, $attachment_signature ); - $ordered = array_intersect_key( $this->sync_types, $sync_items ); - $type = array_shift( array_keys( $ordered ) ); + if ( is_array( $required_signature ) ) { + $sync_items = array_filter( + $attachment_signature, + function ( $item, $key ) use ( $required_signature ) { + return $item !== $required_signature[ $key ]; + }, + ARRAY_FILTER_USE_BOTH + ); + $ordered = array_intersect_key( $this->sync_types, $sync_items ); + if ( ! empty( $ordered ) ) { + $type = array_shift( array_keys( $ordered ) ); + // Validate that this sync type applied (for optional types like upgrade). + if ( isset( $this->sync_base_struct[ $type ]['validate'] ) && is_callable( $this->sync_base_struct[ $type ]['validate'] ) ) { + if ( ! call_user_func( $this->sync_base_struct[ $type ]['validate'], $attachment_id ) ) { + $this->set_signature_item( $attachment_id, $type ); + + return $this->get_sync_type( $attachment_id, $cached ); + } + } } } @@ -582,17 +604,26 @@ public function get_sync_type( $attachment_id, $cached = true ) { */ public function filter_status( $status, $attachment_id ) { - if ( $this->been_synced( $attachment_id ) ) { + if ( $this->been_synced( $attachment_id ) || $this->is_pending( $attachment_id ) ) { $sync_type = $this->get_sync_type( $attachment_id ); if ( ! empty( $sync_type ) && isset( $this->sync_base_struct[ $sync_type ] ) ) { - $status = $this->sync_base_struct[ $sync_type ]['status']; - if ( is_callable( $status['note'] ) ) { - $status['note'] = call_user_func( $status['note'] ); + // check process log in case theres an error. + $log = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['process_log'] ); + if ( ! empty( $log[ $sync_type ] ) && is_wp_error( $log[ $sync_type ] ) ) { + // Use error instead of sync note. + $status['state'] = 'error'; + $status['note'] = $log[ $sync_type ]->get_error_message(); + } else { + $status = $this->sync_base_struct[ $sync_type ]['status']; + if ( is_callable( $status['note'] ) ) { + $status['note'] = call_user_func( $status['note'] ); + } } } // Check if there's an error. + $log = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['pending'] ); $has_error = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['sync_error'], true ); if ( ! empty( $has_error ) ) { $status['state'] = 'error'; @@ -632,6 +663,9 @@ public function is_pending( $attachment_id ) { // Check if it's not already in the to sync array. if ( ! in_array( $attachment_id, $this->to_sync, true ) ) { $is_pending = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['pending'], true ); + if ( ! empty( $is_pending ) ) { + $this->get_sync_type( $attachment_id ); + } if ( empty( $is_pending ) || $is_pending < time() - 5 * 60 ) { // No need to delete pending meta, since it will be updated with the new timestamp anyway. return false; diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php index cb98f180d..72df8c8b7 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php @@ -402,7 +402,7 @@ public function upload( $attachment_id, $args, $headers = array() ) { if ( false !== strpos( $error, 'ERR_DNS_FAIL' ) ) { // URLS are not remotely available, try again as a file. set_transient( '_cld_disable_http_upload', true, 300 ); - $result = $this->upload( $attachment_id, $args, $headers ); + return $this->upload( $attachment_id, $args, $headers ); } } if ( true === $tempfile ) { 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 d9e91aa7f..2fbcf368d 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,7 +57,7 @@ class Push_Sync { /** * Holds the sync component. * - * @var \Cloudinary\Sync\ + * @var \Cloudinary\Sync */ protected $sync; @@ -222,8 +222,17 @@ public function rest_push_attachments( \WP_REST_Request $request ) { $last_result = $request->get_param( 'last_result' ); // Get attachment_id in case this is a single direct request to upload. - $attachments = $request->get_param( 'attachment_ids' ); + $attachments = $request->get_param( 'attachment_ids' ); + $background_process = false; + if ( empty( $attachments ) ) { + // No attachments posted. Pull from the queue. + $attachments = array(); + $background_process = true; + $attachments[] = $this->sync->managers['queue']->get_post(); + + $attachments = array_filter( $attachments ); + } // If not a single request, process based on queue. if ( ! empty( $attachments ) ) { @@ -246,6 +255,19 @@ public function rest_push_attachments( \WP_REST_Request $request ) { } // Record Process log. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['process_log'], $stat[ $attachment_id ] ); + + if ( true === $background_process ) { + $this->sync->managers['queue']->mark( $attachment_id, 'done' ); + } + } + + // Continue; + if ( true === $background_process ) { + // Stopped. + if ( ! $this->sync->managers['queue']->is_running() ) { + return $this->rest_get_queue_status(); + } + $this->api->background_request( 'process', array() ); } return rest_ensure_response( @@ -256,6 +278,12 @@ public function rest_push_attachments( \WP_REST_Request $request ) { ); } + return rest_ensure_response( + array( + 'success' => true, + ) + ); + // Process queue based. if ( ! empty( $last_id ) && ! empty( $last_result ) ) { $this->sync->managers['queue']->mark( $last_id, $last_result ); diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-queue.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-queue.php index 781cf782a..60db3e3e6 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-queue.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-queue.php @@ -107,7 +107,7 @@ public function set_queue( $queue ) { * @return bool */ public function get_post() { - $id = 0; + $id = null; if ( $this->is_running() ) { $queue = $this->get_queue(); if ( ! empty( $queue['pending'] ) ) { @@ -218,21 +218,9 @@ public function build_queue() { 'post_type' => 'attachment', 'post_mime_type' => array( 'image', 'video' ), 'post_status' => 'inherit', - 'posts_per_page' => 1000, // phpcs:ignore + 'posts_per_page' => -1, // phpcs:ignore 'fields' => 'ids', - 'meta_query' => array( // phpcs:ignore - 'relation' => 'AND', - array( - 'key' => Sync::META_KEYS['sync_error'], - 'compare' => 'NOT EXISTS', - ), - array( - 'key' => Sync::META_KEYS['public_id'], - 'compare' => 'NOT EXISTS', - ), - ), 'ignore_sticky_posts' => false, - 'no_found_rows' => true, ); $attachments = new \WP_Query( $args ); 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 52cbe267d..59cb877b5 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 @@ -152,11 +152,6 @@ public function handle_bulk_actions( $location, $action, $post_ids ) { switch ( $action ) { case 'cloudinary-push' : foreach ( $post_ids as $post_id ) { - $this->media->delete_post_meta( $post_id, Sync::META_KEYS['sync_error'] ); - $this->media->delete_post_meta( $post_id, Sync::META_KEYS['public_id'] ); - $this->media->delete_post_meta( $post_id, Sync::META_KEYS['pending'] ); - $this->media->delete_post_meta( $post_id, Sync::META_KEYS['downloading'] ); - $this->media->delete_post_meta( $post_id, Sync::META_KEYS['syncing'] ); $file = get_attached_file( $post_id ); wp_generate_attachment_metadata( $post_id, $file ); $this->sync->add_to_sync( $post_id ); @@ -331,6 +326,7 @@ public function update_breakpoints( $attachment_id, $breakpoints ) { if ( ! empty( $this->plugin->config['settings']['global_transformations']['enable_breakpoints'] ) ) { if ( ! empty( $breakpoints['responsive_breakpoints'] ) ) { // Images only. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['breakpoints'], $breakpoints['responsive_breakpoints'][0]['breakpoints'] ); + $this->sync->set_signature_item( $attachment_id, 'breakpoints' ); } elseif ( wp_attachment_is_image( $attachment_id ) ) { $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['breakpoints'] ); } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/ui-definitions/tabs/sync-media-content.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/ui-definitions/tabs/sync-media-content.php index a37756fc0..9abefb378 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/ui-definitions/tabs/sync-media-content.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/ui-definitions/tabs/sync-media-content.php @@ -5,7 +5,7 @@ * @package Cloudinary */ -$autosync = $this->plugin->components['settings']->is_auto_sync_enabled(); +$autosync = $this->plugin->components['sync']->is_auto_sync_enabled(); ?> plugin->config['connect'] ) ) : ?>
diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/ui-definitions/tabs/sync-media-footer.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/ui-definitions/tabs/sync-media-footer.php index 63a2da3c2..8105ee541 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/ui-definitions/tabs/sync-media-footer.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/ui-definitions/tabs/sync-media-footer.php @@ -8,7 +8,7 @@ // Reset if stopped to maintain current state. if ( ! $this->plugin->components['sync']->managers['queue']->is_running() ) { // Rebuild on load. - $this->plugin->components['sync']->managers['queue']->build_queue(); + //$this->plugin->components['sync']->managers['queue']->build_queue(); } ?> From 5b5ad841eae9b5051b2bd8fab6615a87405b4852 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Fri, 31 Jul 2020 15:51:59 +0200 Subject: [PATCH 08/64] overload concurrency. --- .../php/class-sync.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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 21dc91b53..2508fcc09 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 @@ -374,6 +374,7 @@ public function setup_sync_base_struct() { 'generate' => array( $this->managers['media'], 'get_public_id' ), 'validate' => function ( $attachment_id ) { $public_id = $this->managers['media']->has_public_id( $attachment_id ); + return false === $public_id; }, 'sync' => array( @@ -732,10 +733,12 @@ public function set_signature_item( $attachment_id, $type, $value = null ) { */ public function init_background_upload() { if ( ! empty( $this->to_sync ) ) { - $params = array( - 'attachment_ids' => $this->to_sync, - ); - $this->managers['api']->background_request( 'process', $params ); + foreach ( $this->to_sync as $id ) { + $params = array( + 'attachment_ids' => array( $id ), + ); + $this->managers['api']->background_request( 'process', $params ); + } } } From 6b420d8496b99dd30deadf0d7dee3f258ea1ba03 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Sat, 1 Aug 2020 08:11:56 +0200 Subject: [PATCH 09/64] correct public_id generation --- .../php/class-sync.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 2508fcc09..4048244e8 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 @@ -249,13 +249,13 @@ public function get_signature( $attachment_id, $cached = true ) { * @return string|null */ public function generate_public_id( $attachment_id ) { - $settings = $this->plugin->config['settings']; - $cld_folder = trailingslashit( $settings['sync_media']['cloudinary_folder'] ); + + $cld_folder = $this->managers['media']->get_cloudinary_folder(); $file = get_attached_file( $attachment_id ); $file_info = pathinfo( $file ); $public_id = $cld_folder . $file_info['filename']; - return $public_id; + return ltrim( $public_id, '/' ); } /** From 61ffb12fd3723249f423c18652c0b0523774704c Mon Sep 17 00:00:00 2001 From: David Cramer Date: Sun, 2 Aug 2020 10:17:29 +0200 Subject: [PATCH 10/64] handle video and large file uploads --- .../php/class-sync.php | 13 +++- .../php/connect/class-api.php | 70 ++++++++++++------- .../php/sync/class-upload-sync.php | 40 ++++++++--- 3 files changed, 84 insertions(+), 39 deletions(-) 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 4048244e8..005caf420 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 @@ -147,6 +147,7 @@ public function been_synced( $attachment_id ) { public function is_synced( $post_id ) { $signature = $this->get_signature( $post_id ); $expecting = $this->generate_signature( $post_id ); + // Sort to align orders for comparison. ksort( $signature ); ksort( $expecting ); if ( ! empty( $signature ) && ! empty( $expecting ) && $expecting === $signature ) { @@ -234,6 +235,9 @@ public function get_signature( $attachment_id, $cached = true ) { if ( empty( $signature ) ) { $signature = array(); } + + // Remove any old or outdated signature items. against the expected. + $signature = array_intersect_key( $signature, $this->sync_types ); $signatures[ $attachment_id ] = $return; $return = wp_parse_args( $signature, $this->sync_types ); } @@ -719,7 +723,7 @@ public function set_signature_item( $attachment_id, $type, $value = null ) { if ( is_null( $value ) ) { // Generate a new value based on generator. $new_value = call_user_func( $this->sync_base_struct[ $type ]['generate'], $attachment_id ); - if ( is_array( $value ) ) { + if ( is_array( $new_value ) ) { $new_value = wp_json_encode( $new_value ); } $value = md5( $new_value ); @@ -733,9 +737,12 @@ public function set_signature_item( $attachment_id, $type, $value = null ) { */ public function init_background_upload() { if ( ! empty( $this->to_sync ) ) { - foreach ( $this->to_sync as $id ) { + + $parts = array_chunk( $this->to_sync, 10 ); + + foreach ( $parts as $ids ) { $params = array( - 'attachment_ids' => array( $id ), + 'attachment_ids' => $ids, ); $this->managers['api']->background_request( 'process', $params ); } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php index 72df8c8b7..7453def64 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php @@ -286,17 +286,21 @@ public function get_asset_details( $public_id, $type ) { /** * Upload a large asset in chunks. * - * @param string $file Path to the file. - * @param array $args Array of upload options. + * @param int $attachment_id The attachment ID. + * @param array $args Array of upload options. * * @return array|\WP_Error */ - public function upload_large( $file, $args ) { + public function upload_large( $attachment_id, $args ) { + // Ensure we have the right file. + if ( empty( $args['file'] ) ) { + $args['file'] = get_attached_file( $attachment_id ); + } $tempfile = false; - if ( false !== strpos( $file, 'vip://' ) ) { - $file = $this->create_local_copy( $file ); - if ( is_wp_error( $file ) ) { - return $file; + if ( false !== strpos( $args['file'], 'vip://' ) ) { + $args['file'] = $this->create_local_copy( $args['file'] ); + if ( is_wp_error( $args['file'] ) ) { + return $args['file']; } $tempfile = true; } @@ -313,12 +317,12 @@ public function upload_large( $file, $args ) { } // Since WP_Filesystem doesn't have a fread, we need to do it manually. However we'll still use it for writing. - $src = fopen( $file, 'r' ); // phpcs:ignore - $temp_file_name = wp_tempnam( uniqid( time() ) . '.' . pathinfo( $file, PATHINFO_EXTENSION ) ); + $src = fopen( $args['file'], 'r' ); // phpcs:ignore + $temp_file_name = wp_tempnam( uniqid( time() ) . '.' . pathinfo( $args['file'], PATHINFO_EXTENSION ) ); $upload_id = substr( sha1( uniqid( $this->credentials['api_secret'] . wp_rand() ) ), 0, 16 ); $chunk_size = 20000000; $index = 0; - $file_size = filesize( $file ); + $file_size = filesize( $args['file'] ); while ( ! feof( $src ) ) { $current_loc = $index * $chunk_size; if ( $current_loc >= $file_size ) { @@ -336,7 +340,7 @@ public function upload_large( $file, $args ) { 'Content-Range' => $range, 'X-Unique-Upload-Id' => $upload_id, ); - + $args['file'] = $temp_file_name; $result = $this->upload( $temp_file_name, $args, $headers ); if ( is_wp_error( $result ) ) { break; @@ -346,7 +350,7 @@ public function upload_large( $file, $args ) { fclose( $src ); //phpcs:ignore unlink( $temp_file_name ); //phpcs:ignore if ( true === $tempfile ) { - unlink( $file ); //phpcs:ignore + unlink( $args['file'] ); //phpcs:ignore } return $result; @@ -363,30 +367,41 @@ public function upload_large( $file, $args ) { * @return array|\WP_Error */ public function upload( $attachment_id, $args, $headers = array() ) { - $tempfile = false; - if ( false !== strpos( $file, 'vip://' ) ) { - $file = $this->create_local_copy( $file ); - if ( is_wp_error( $file ) ) { - return $file; - } - $tempfile = true; - } + $resource = ! empty( $args['resource_type'] ) ? $args['resource_type'] : 'image'; $url = $this->url( $resource, 'upload', true ); $args = $this->clean_args( $args ); $disable_https_fetch = get_transient( '_cld_disable_http_upload' ); // Check if we can try http file upload. - if ( empty( $disable_https_fetch ) ) { + if ( empty( $headers ) && empty( $disable_https_fetch ) ) { $args['file'] = wp_get_attachment_url( $attachment_id ); } else { - $file = get_attached_file( $attachment_id ); + // We should have the file in args at this point, but if the transient was set, it will be defaulting here. + if ( empty( $args['file'] ) ) { + $args['file'] = get_attached_file( $attachment_id ); + } + // Headers indicate chunked upload. + if ( empty( $headers ) ) { + $size = filesize( $args['file'] ); + if ( 'video' === $resource || $size > 100000000 ) { + return $this->upload_large( $attachment_id, $args ); + } + } + $tempfile = false; + if ( false !== strpos( $args['file'], 'vip://' ) ) { + $args['file'] = $this->create_local_copy( $args['file'] ); + if ( is_wp_error( $args['file'] ) ) { + return $args['file']; + } + $tempfile = true; + } // Attach File. if ( function_exists( 'curl_file_create' ) ) { - $args['file'] = curl_file_create( $file ); // phpcs:ignore - $args['file']->setPostFilename( $file ); + $args['file'] = curl_file_create( $args['file'] ); // phpcs:ignore + $args['file']->setPostFilename( $args['file'] ); } else { - $args['file'] = '@' . $file; + $args['file'] = '@' . $args['file']; } } @@ -402,11 +417,12 @@ public function upload( $attachment_id, $args, $headers = array() ) { if ( false !== strpos( $error, 'ERR_DNS_FAIL' ) ) { // URLS are not remotely available, try again as a file. set_transient( '_cld_disable_http_upload', true, 300 ); - return $this->upload( $attachment_id, $args, $headers ); + + return $this->upload( $attachment_id, $args ); } } if ( true === $tempfile ) { - unlink( $file ); //phpcs:ignore + unlink( $args['file'] ); //phpcs:ignore } return $result; 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 59cb877b5..b9e986229 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 @@ -152,8 +152,7 @@ public function handle_bulk_actions( $location, $action, $post_ids ) { switch ( $action ) { case 'cloudinary-push' : foreach ( $post_ids as $post_id ) { - $file = get_attached_file( $post_id ); - wp_generate_attachment_metadata( $post_id, $file ); + $this->sync->set_signature_item( $post_id, 'file', '' ); $this->sync->add_to_sync( $post_id ); } break; @@ -256,14 +255,22 @@ public function prep_on_demand_upload( $cloudinary_id, $attachment_id ) { */ public function upload_asset( $attachment_id ) { - $type = $this->sync->get_sync_type( $attachment_id ); - $options = $this->media->get_upload_options( $attachment_id ); - $result = $this->connect->api->upload( $attachment_id, $options ); + $type = $this->sync->get_sync_type( $attachment_id ); + $options = $this->media->get_upload_options( $attachment_id ); + $public_id = $options['public_id']; + $result = $this->connect->api->upload( $attachment_id, $options ); if ( ! is_wp_error( $result ) ) { + // Set public_id. + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $public_id ); + // Update signature for all that use the same method. $this->sync->update_signature( $attachment_id, $type ); - $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $options['public_id'] ); + // Update options and public_id as well (full sync) + $this->sync->set_signature_item( $attachment_id, 'options' ); + $this->sync->set_signature_item( $attachment_id, 'public_id' ); + $this->update_breakpoints( $attachment_id, $result ); + $this->update_content( $attachment_id ); } return $result; @@ -283,7 +290,7 @@ public function context_update( $attachment_id ) { $result = $this->connect->api->context( $options ); if ( ! is_wp_error( $result ) ) { - $this->sync->update_signature( $attachment_id, $type ); + $this->sync->set_signature_item( $attachment_id, $type ); $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $options['public_id'] ); } @@ -310,7 +317,7 @@ public function explicit_update( $attachment_id ) { $this->update_breakpoints( $attachment_id, array() ); $result = true; } - $this->sync->update_signature( $attachment_id, $type ); + $this->sync->set_signature_item( $attachment_id, $type ); return $result; } @@ -326,10 +333,10 @@ public function update_breakpoints( $attachment_id, $breakpoints ) { if ( ! empty( $this->plugin->config['settings']['global_transformations']['enable_breakpoints'] ) ) { if ( ! empty( $breakpoints['responsive_breakpoints'] ) ) { // Images only. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['breakpoints'], $breakpoints['responsive_breakpoints'][0]['breakpoints'] ); - $this->sync->set_signature_item( $attachment_id, 'breakpoints' ); } elseif ( wp_attachment_is_image( $attachment_id ) ) { $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['breakpoints'] ); } + $this->sync->set_signature_item( $attachment_id, 'breakpoints' ); } } @@ -354,4 +361,19 @@ public function prep_upload( $attachment_id ) { } } + /** + * Trigger an update on content that contains the same attachment ID to allow filters to capture and process. + * + * @param int $attachment_id The attachment id to find and init an update. + */ + private function update_content( $attachment_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 ) ) { + $content_posts = array_unique( $content_search->get_posts() ); // ensure post only gets updated once. + foreach ( $content_posts as $content_id ) { + wp_update_post( array( 'ID' => $content_id ) ); // Trigger an update, internal filters will filter out remote URLS. + } + } + } } From 4b2e99fa6c0f9345d6b20082a01c8350433eb66d Mon Sep 17 00:00:00 2001 From: David Cramer Date: Mon, 3 Aug 2020 10:19:22 +0200 Subject: [PATCH 11/64] add public_id as sync state --- .../php/sync/class-push-sync.php | 3 ++- .../php/sync/class-upload-queue.php | 14 +++++++++++++- .../php/sync/class-upload-sync.php | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) 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 2fbcf368d..517ce1548 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 @@ -255,7 +255,8 @@ public function rest_push_attachments( \WP_REST_Request $request ) { } // Record Process log. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['process_log'], $stat[ $attachment_id ] ); - + // Create synced postmeta as a way to search for synced / unsynced items. + update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $this->media->get_public_id( $attachment_id ) ); if ( true === $background_process ) { $this->sync->managers['queue']->mark( $attachment_id, 'done' ); } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-queue.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-queue.php index 60db3e3e6..5d1595840 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-queue.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-queue.php @@ -218,9 +218,21 @@ public function build_queue() { 'post_type' => 'attachment', 'post_mime_type' => array( 'image', 'video' ), 'post_status' => 'inherit', - 'posts_per_page' => -1, // phpcs:ignore + 'posts_per_page' => 1000, // phpcs:ignore 'fields' => 'ids', + 'meta_query' => array( // phpcs:ignore + 'relation' => 'AND', + array( + 'key' => Sync::META_KEYS['sync_error'], + 'compare' => 'NOT EXISTS', + ), + array( + 'key' => Sync::META_KEYS['public_id'], + 'compare' => 'NOT EXISTS', + ), + ), 'ignore_sticky_posts' => false, + 'no_found_rows' => true, ); $attachments = new \WP_Query( $args ); 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 b9e986229..6306bdff6 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 @@ -152,7 +152,7 @@ public function handle_bulk_actions( $location, $action, $post_ids ) { switch ( $action ) { case 'cloudinary-push' : foreach ( $post_ids as $post_id ) { - $this->sync->set_signature_item( $post_id, 'file', '' ); + $this->media->delete_post_meta( $post_id, Sync::META_KEYS['signature'] ); $this->sync->add_to_sync( $post_id ); } break; From ea45cd45ca1935866e5f2692cf1b1d90affdb03a Mon Sep 17 00:00:00 2001 From: David Cramer Date: Mon, 3 Aug 2020 15:36:07 +0200 Subject: [PATCH 12/64] bulk sync refactor and improved --- .../php/class-sync.php | 32 ++-- .../php/connect/class-api.php | 12 +- .../php/sync/class-push-sync.php | 144 ++++++++---------- ...-upload-queue.php => class-sync-queue.php} | 62 ++++---- .../ui-definitions/tabs/sync-media-footer.php | 2 +- 5 files changed, 124 insertions(+), 128 deletions(-) rename cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/{class-upload-queue.php => class-sync-queue.php} (83%) 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 005caf420..a82e6a960 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 @@ -12,7 +12,7 @@ use Cloudinary\Sync\Delete_Sync; use Cloudinary\Sync\Download_Sync; use Cloudinary\Sync\Push_Sync; -use Cloudinary\Sync\Upload_Queue; +use Cloudinary\Sync\Sync_Queue; use Cloudinary\Sync\Upload_Sync; /** @@ -88,7 +88,7 @@ public function __construct( Plugin $plugin ) { $this->managers['upload'] = new Upload_Sync( $this->plugin ); $this->managers['download'] = new Download_Sync( $this->plugin ); $this->managers['delete'] = new Delete_Sync( $this->plugin ); - $this->managers['queue'] = new Upload_Queue( $this->plugin ); + $this->managers['queue'] = new Sync_Queue( $this->plugin ); } /** @@ -197,7 +197,13 @@ function ( $item ) { */ public function can_sync( $attachment_id, $type = 'file' ) { - $can = ( ! $this->is_pending( $attachment_id ) && $this->been_synced( $attachment_id ) ) || $this->is_auto_sync_enabled(); + $can = $this->is_auto_sync_enabled(); + + if ( $this->is_pending( $attachment_id ) ) { + $can = false; + } elseif ( $this->been_synced( $attachment_id ) ) { + $can = true; + } /** * Filter to allow changing if an asset is allowed to be synced. @@ -668,9 +674,6 @@ public function is_pending( $attachment_id ) { // Check if it's not already in the to sync array. if ( ! in_array( $attachment_id, $this->to_sync, true ) ) { $is_pending = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['pending'], true ); - if ( ! empty( $is_pending ) ) { - $this->get_sync_type( $attachment_id ); - } if ( empty( $is_pending ) || $is_pending < time() - 5 * 60 ) { // No need to delete pending meta, since it will be updated with the new timestamp anyway. return false; @@ -686,10 +689,9 @@ public function is_pending( $attachment_id ) { * @param int $attachment_id The attachment ID to add. */ public function add_to_sync( $attachment_id ) { - if ( ! in_array( $attachment_id, $this->to_sync, true ) && ! $this->is_pending( $attachment_id ) ) { + if ( ! in_array( $attachment_id, $this->to_sync, true ) ) { // Flag image as pending to prevent duplicate upload. $this->managers['media']->update_post_meta( $attachment_id, Sync::META_KEYS['pending'], time() ); - //$this->managers['media']->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true ); $this->to_sync[] = $attachment_id; } } @@ -738,14 +740,12 @@ public function set_signature_item( $attachment_id, $type, $value = null ) { public function init_background_upload() { if ( ! empty( $this->to_sync ) ) { - $parts = array_chunk( $this->to_sync, 10 ); - - foreach ( $parts as $ids ) { - $params = array( - 'attachment_ids' => $ids, - ); - $this->managers['api']->background_request( 'process', $params ); - } + $params = array( + 'attachment_ids' => $this->to_sync, + ); + $instant = microtime( true ); + wp_schedule_single_event( $instant, 'cloudinary_sync_items', $params ); + spawn_cron( $instant ); } } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php index 7453def64..7b66b11ea 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php @@ -336,12 +336,12 @@ public function upload_large( $attachment_id, $args ) { $temp_file_size = filesize( $temp_file_name ); $range = 'bytes ' . $current_loc . '-' . ( $current_loc + $temp_file_size - 1 ) . '/' . $file_size; - $headers = array( + $headers = array( 'Content-Range' => $range, 'X-Unique-Upload-Id' => $upload_id, ); $args['file'] = $temp_file_name; - $result = $this->upload( $temp_file_name, $args, $headers ); + $result = $this->upload( $temp_file_name, $args, $headers ); if ( is_wp_error( $result ) ) { break; } @@ -398,8 +398,9 @@ public function upload( $attachment_id, $args, $headers = array() ) { } // Attach File. if ( function_exists( 'curl_file_create' ) ) { - $args['file'] = curl_file_create( $args['file'] ); // phpcs:ignore - $args['file']->setPostFilename( $args['file'] ); + $file = $args['file']; + $args['file'] = curl_file_create( $file ); // phpcs:ignore + $args['file']->setPostFilename( $file ); } else { $args['file'] = '@' . $args['file']; } @@ -417,7 +418,8 @@ public function upload( $attachment_id, $args, $headers = array() ) { if ( false !== strpos( $error, 'ERR_DNS_FAIL' ) ) { // URLS are not remotely available, try again as a file. set_transient( '_cld_disable_http_upload', true, 300 ); - + // Remove URL file. + unset( $args['file'] ); return $this->upload( $attachment_id, $args ); } } 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 517ce1548..9d5287971 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 @@ -113,6 +113,8 @@ public function setup() { $this->sync = $this->plugin->components['sync']; $this->connect = $this->plugin->components['connect']; $this->api = $this->plugin->components['api']; + add_action( 'cloudinary_run_queue', array( $this, 'process_queue' ) ); + add_action( 'cloudinary_sync_items', array( $this, 'process_assets' ) ); } /** @@ -156,6 +158,7 @@ public function rest_endpoints( $endpoints ) { * @return bool */ public function rest_verify_nonce( \WP_REST_Request $request ) { + return true; $nonce = $request->get_param( 'nonce' ); return wp_verify_nonce( $nonce, 'wp_rest' ); @@ -204,9 +207,47 @@ public function rest_start_sync( \WP_REST_Request $request ) { return $this->rest_get_queue_status(); // Nothing to sync. } $this->sync->managers['queue']->start_queue(); + } + + /** + * Process asset sync. + * + * @param int|array $attachments An attachment ID or an array of ID's. + * + * @return array + */ + public function process_assets( $attachments = array() ) { + + $stat = array(); + // If a single specified ID, push and return response. + $ids = array_map( 'intval', (array) $attachments ); + // Handle based on Sync Type. + foreach ( $ids as $attachment_id ) { + // Flag attachment as being processed. + update_post_meta( $attachment_id, Sync::META_KEYS['syncing'], time() ); + while ( $type = $this->sync->get_sync_type( $attachment_id, false ) ) { + error_log( 'syncing ' . $type ); + if ( isset( $stat[ $attachment_id ][ $type ] ) ) { + // Loop prevention. + break; + } + $callback = $this->sync->get_sync_method( $type ); + $stat[ $attachment_id ][ $type ] = call_user_func( $callback, $attachment_id ); + } + // remove pending. + if ( $this->sync->is_pending( $attachment_id ) ) { + $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['pending'] ); + } + // Record Process log. + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['process_log'], $stat[ $attachment_id ] ); + // Remove processing flag. + delete_post_meta( $attachment_id, Sync::META_KEYS['syncing'] ); - return $this->call_thread(); + // Create synced post meta as a way to search for synced / unsynced items. + update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $this->media->get_public_id( $attachment_id ) ); + } + return $stat; } /** @@ -233,107 +274,56 @@ public function rest_push_attachments( \WP_REST_Request $request ) { $attachments = array_filter( $attachments ); } + $stat = array(); // If not a single request, process based on queue. if ( ! empty( $attachments ) ) { - $stat = array(); // If a single specified ID, push and return response. $ids = array_map( 'intval', $attachments ); // Handle based on Sync Type. - foreach ( $ids as $attachment_id ) { - while ( $type = $this->sync->get_sync_type( $attachment_id, false ) ) { - if ( isset( $stat[ $attachment_id ][ $type ] ) ) { - // Loop prevention. - break; - } - $callback = $this->sync->get_sync_method( $type ); - $stat[ $attachment_id ][ $type ] = call_user_func( $callback, $attachment_id ); - } - // remove pending. - if ( $this->sync->is_pending( $attachment_id ) ) { - $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['pending'] ); - } - // Record Process log. - $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['process_log'], $stat[ $attachment_id ] ); - // Create synced postmeta as a way to search for synced / unsynced items. - update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $this->media->get_public_id( $attachment_id ) ); - if ( true === $background_process ) { - $this->sync->managers['queue']->mark( $attachment_id, 'done' ); - } - } - - // Continue; - if ( true === $background_process ) { - // Stopped. - if ( ! $this->sync->managers['queue']->is_running() ) { - return $this->rest_get_queue_status(); - } - $this->api->background_request( 'process', array() ); - } + $stat = $this->process_assets( $ids ); - return rest_ensure_response( - array( - 'success' => true, - 'data' => $stat, - ) - ); } return rest_ensure_response( array( 'success' => true, + 'data' => $stat, ) ); - // Process queue based. - if ( ! empty( $last_id ) && ! empty( $last_result ) ) { - $this->sync->managers['queue']->mark( $last_id, $last_result ); - } - - if ( ! $this->sync->managers['queue']->is_running() ) { // Check it wasn't stopped. - return $this->rest_get_queue_status(); - } - - $this->post_id = $this->sync->managers['queue']->get_post(); - - // No post, end of queue. - if ( empty( $this->post_id ) ) { - $this->sync->managers['queue']->stop_queue(); - - return $this->rest_get_queue_status(); - } - - add_action( 'shutdown', array( $this, 'resume_queue' ) ); - - return $this->rest_get_queue_status(); } /** * Resume the bulk sync. * - * @return bool|\WP_REST_Response + * @return bool */ - public function resume_queue() { - // Check if there is a Cloudinary ID in case this was synced on-demand before being processed by the queue. - add_filter( 'cloudinary_on_demand_sync_enabled', '__return_false' ); // Disable the on-demand sync since we want the status. - define( 'DOING_BULK_SYNC', true ); // Define bulk sync in action. - - if ( ! $this->sync->is_synced( $this->post_id ) ) { - $stat = $this->push_attachments( array( $this->post_id ) ); - if ( ! empty( $stat['processed'] ) ) { - $result = 'done'; + public function process_queue() { + if( $this->sync->managers['queue']->is_running() ) { + $queue = $this->sync->managers['queue']->get_queue(); + if ( ! empty( $queue['pending'] ) ) { + wp_schedule_single_event( time() + 5, 'cloudinary_run_queue' ); + if ( ! empty( $queue['last_update'] ) ) { + if ( $queue['last_update'] > current_time( 'timestamp' ) - 60 ) { + error_log( 'Still running.' ); + + return; + } + } + error_log( 'syncing ' . count( $queue['pending'] ) . ' items' ); + while ( $attachment_id = $this->sync->managers['queue']->get_post() ) { + error_log( 'starting ' . $attachment_id ); + $this->process_assets( $attachment_id ); + $this->sync->managers['queue']->mark( $attachment_id, 'done' ); + } + error_log( 'Stopping.' ); } else { - $result = 'error'; + error_log( 'queue is empty. Stopping.' ); } } else { - /** - * If a Cloudinary ID was found, set as done. - * In the case of an upgrade, This would have been pushed to a background conversion. - */ - $result = 'done'; + error_log( 'Stopped.' ); } - - return $this->call_thread( $this->post_id, $result ); } /** diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-queue.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php similarity index 83% rename from cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-queue.php rename to cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php index 5d1595840..9935038b1 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-queue.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php @@ -1,6 +1,6 @@ build_queue(); @@ -98,7 +99,8 @@ public function get_queue() { * @return bool */ public function set_queue( $queue ) { - return update_option( self::$queue_key, $queue ); + + return update_option( self::$queue_key, $queue, false ); } /** @@ -107,7 +109,7 @@ public function set_queue( $queue ) { * @return bool */ public function get_post() { - $id = null; + $id = false; if ( $this->is_running() ) { $queue = $this->get_queue(); if ( ! empty( $queue['pending'] ) ) { @@ -129,8 +131,9 @@ public function get_post() { * @param string $type The type of marking to apply. */ public function mark( $id, $type = 'done' ) { - $queue = $this->get_queue(); - $key = array_search( (int) $id, $queue['processing'], true ); + $queue = $this->get_queue(); + $queue['last_update'] = current_time( 'timestamp' ); + $key = array_search( (int) $id, $queue['processing'], true ); if ( false !== $key ) { unset( $queue['processing'][ $key ] ); if ( 'error' === $type ) { @@ -147,6 +150,7 @@ public function mark( $id, $type = 'done' ) { } } } + $this->set_queue( $queue ); } @@ -156,12 +160,9 @@ public function mark( $id, $type = 'done' ) { * @return bool */ public function is_running() { - if ( false === $this->running ) { - $queue = $this->get_queue(); - $this->running = empty( $queue['started'] ) ? false : true; - } + $queue = $this->get_queue(); - return $this->running; + return empty( $queue['started'] ) ? false : true; } /** @@ -221,15 +222,15 @@ public function build_queue() { 'posts_per_page' => 1000, // phpcs:ignore 'fields' => 'ids', 'meta_query' => array( // phpcs:ignore - 'relation' => 'AND', - array( - 'key' => Sync::META_KEYS['sync_error'], - 'compare' => 'NOT EXISTS', - ), - array( - 'key' => Sync::META_KEYS['public_id'], - 'compare' => 'NOT EXISTS', - ), + 'relation' => 'AND', + array( + 'key' => Sync::META_KEYS['sync_error'], + 'compare' => 'NOT EXISTS', + ), + array( + 'key' => Sync::META_KEYS['public_id'], + 'compare' => 'NOT EXISTS', + ), ), 'ignore_sticky_posts' => false, 'no_found_rows' => true, @@ -260,11 +261,11 @@ public function stop_queue() { $queue = $this->get_queue(); if ( ! empty( $queue['started'] ) ) { unset( $queue['started'] ); + unset( $queue['last_update'] ); $this->set_queue( $queue ); } - $this->running = false; - wp_clear_scheduled_hook( 'cloudinary_resume_queue' ); + wp_unschedule_hook( 'cloudinary_run_queue' ); } /** @@ -274,13 +275,16 @@ public function stop_queue() { */ public function start_queue() { $queue = $this->get_queue(); - $queue['started'] = $queue['last_update'] = current_time( 'timestamp' ); + $queue['started'] = current_time( 'timestamp' ); + if ( ! empty( $queue['processing'] ) ) { + $queue['pending'] = array_merge( $queue['pending'], $queue['processing'] ); + } $this->set_queue( $queue ); - $this->running = true; - if ( ! wp_next_scheduled( 'cloudinary_resume_queue' ) ) { - wp_schedule_single_event( time() + $this->cron_frequency, 'cloudinary_resume_queue' ); - } + $instant = microtime( true ); + wp_unschedule_hook( 'cloudinary_run_queue' ); + wp_schedule_single_event( $instant, 'cloudinary_run_queue' ); + spawn_cron( $instant ); } /** diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/ui-definitions/tabs/sync-media-footer.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/ui-definitions/tabs/sync-media-footer.php index 8105ee541..63a2da3c2 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/ui-definitions/tabs/sync-media-footer.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/ui-definitions/tabs/sync-media-footer.php @@ -8,7 +8,7 @@ // Reset if stopped to maintain current state. if ( ! $this->plugin->components['sync']->managers['queue']->is_running() ) { // Rebuild on load. - //$this->plugin->components['sync']->managers['queue']->build_queue(); + $this->plugin->components['sync']->managers['queue']->build_queue(); } ?> From f519f49e38facef8c0580c26341c1c0c3b006e32 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Mon, 3 Aug 2020 15:37:48 +0200 Subject: [PATCH 13/64] remove debugs --- .../php/sync/class-push-sync.php | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) 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 9d5287971..2fa136d34 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 @@ -226,7 +226,6 @@ public function process_assets( $attachments = array() ) { // Flag attachment as being processed. update_post_meta( $attachment_id, Sync::META_KEYS['syncing'], time() ); while ( $type = $this->sync->get_sync_type( $attachment_id, false ) ) { - error_log( 'syncing ' . $type ); if ( isset( $stat[ $attachment_id ][ $type ] ) ) { // Loop prevention. break; @@ -297,32 +296,23 @@ public function rest_push_attachments( \WP_REST_Request $request ) { /** * Resume the bulk sync. * - * @return bool + * @return void */ public function process_queue() { - if( $this->sync->managers['queue']->is_running() ) { + if ( $this->sync->managers['queue']->is_running() ) { $queue = $this->sync->managers['queue']->get_queue(); if ( ! empty( $queue['pending'] ) ) { wp_schedule_single_event( time() + 5, 'cloudinary_run_queue' ); if ( ! empty( $queue['last_update'] ) ) { if ( $queue['last_update'] > current_time( 'timestamp' ) - 60 ) { - error_log( 'Still running.' ); - return; } } - error_log( 'syncing ' . count( $queue['pending'] ) . ' items' ); while ( $attachment_id = $this->sync->managers['queue']->get_post() ) { - error_log( 'starting ' . $attachment_id ); $this->process_assets( $attachment_id ); $this->sync->managers['queue']->mark( $attachment_id, 'done' ); } - error_log( 'Stopping.' ); - } else { - error_log( 'queue is empty. Stopping.' ); } - } else { - error_log( 'Stopped.' ); } } From d02eb3ee68e7458fa81e581cef8c0ff41dace431 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Mon, 3 Aug 2020 16:09:47 +0200 Subject: [PATCH 14/64] increase retry --- .../php/sync/class-push-sync.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2fa136d34..78031b9de 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 @@ -302,7 +302,7 @@ public function process_queue() { if ( $this->sync->managers['queue']->is_running() ) { $queue = $this->sync->managers['queue']->get_queue(); if ( ! empty( $queue['pending'] ) ) { - wp_schedule_single_event( time() + 5, 'cloudinary_run_queue' ); + wp_schedule_single_event( time() + 20, 'cloudinary_run_queue' ); if ( ! empty( $queue['last_update'] ) ) { if ( $queue['last_update'] > current_time( 'timestamp' ) - 60 ) { return; From c426e9b64c5fe3c296e3d1366f21eeadbbf8f598 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 4 Aug 2020 08:43:45 +0200 Subject: [PATCH 15/64] better cloudinary ID handling --- .../php/class-media.php | 44 ++++++++++++----- .../php/class-sync.php | 48 +++++++++++-------- 2 files changed, 60 insertions(+), 32 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index b4e48b7a9..f36d0c613 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -121,16 +121,37 @@ public function __construct( Plugin $plugin ) { * @return bool */ public function is_media( $attachment ) { - $media_types = array( 'image', 'video' ); - $is_media = false; + $is_media = false; if ( 'attachment' === get_post_type( $attachment ) ) { - $type = strstr( get_post_mime_type( $attachment ), '/', true ); - $is_media = in_array( $type, apply_filters( 'cloudinary_media_types', $media_types ) ); + /** + * Filter the default Cloudinary Media Types. + * + * @param array $types The default media types array. + * + * @return array + */ + $media_types = apply_filters( 'cloudinary_media_types', array( 'image', 'video', 'audio', 'application' ) ); + $type = $this->get_media_type( $attachment ); + $is_media = in_array( $type, $media_types ); } return $is_media; } + /** + * Get a resource type based on file.(Cloudinary v1 remove mime type in post data). + * + * @param \WP_Post|int $attachment_id The attachment ID or object. + * + * @return string + */ + public function get_media_type( $attachment_id ) { + $file = pathinfo( get_attached_file( $attachment_id ), PATHINFO_BASENAME ); + $mime = wp_check_filetype( $file ); + + return strstr( $mime['type'], '/', true ); + } + /** * Remove the crop size from a url. * @@ -567,7 +588,7 @@ public function cloudinary_url( $attachment_id, $size = array(), $transformation $transformations = $this->get_transformation_from_meta( $attachment_id ); } // Get the attachment resource type. - $resource_type = wp_attachment_is( 'image', $attachment_id ) ? 'image' : 'video'; + $resource_type = $this->get_media_type( $attachment_id ); // Setup initial args for cloudinary_url. $pre_args = array( 'secure' => is_ssl(), @@ -665,7 +686,7 @@ public function has_public_id( $attachment_id ) { } /** - * Get a Cloudinary ID. + * Get a Cloudinary ID which includes the file format extension. * * @param int $attachment_id The Attachment ID. * @@ -676,14 +697,15 @@ public function get_cloudinary_id( $attachment_id ) { $public_id = false; // A cloudinary_id is a public_id with a file extension. if ( $this->has_public_id( $attachment_id ) ) { - $public_id = $this->get_public_id( $attachment_id ); + $public_id = $this->get_public_id( $attachment_id ); $suffix_data = $this->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); if ( is_array( $suffix_data ) && ! empty( $suffix_data['suffix'] ) && $suffix_data['public_id'] === $public_id ) { $public_id = $public_id . $suffix_data['suffix']; } - $file = get_attached_file( $attachment_id ); - $info = pathinfo( $file ); - $public_id = $public_id . '.' . $info['extension']; + $file = get_attached_file( $attachment_id ); + // @todo: Make this use the globals, overrides, and application conversion. + $extension = pathinfo( $file, PATHINFO_EXTENSION ); + $public_id = $public_id . '.' . $extension; } return $public_id; @@ -1433,7 +1455,7 @@ public function get_upload_options( $attachment_id ) { // Prepare upload options. $options = array( 'unique_filename' => false, - 'resource_type' => strstr( get_post_mime_type( $attachment_id ), '/', true ), + 'resource_type' => $this->get_media_type( $attachment_id ), 'public_id' => $this->get_public_id( $attachment_id ), 'context' => $this->get_context_options( $attachment_id ), ); 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 a82e6a960..6e8e414e4 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 @@ -145,13 +145,16 @@ public function been_synced( $attachment_id ) { * @return bool */ public function is_synced( $post_id ) { - $signature = $this->get_signature( $post_id ); $expecting = $this->generate_signature( $post_id ); - // Sort to align orders for comparison. - ksort( $signature ); - ksort( $expecting ); - if ( ! empty( $signature ) && ! empty( $expecting ) && $expecting === $signature ) { - return true; + + if ( ! is_wp_error( $expecting ) ) { + $signature = $this->get_signature( $post_id ); + // Sort to align orders for comparison. + ksort( $signature ); + ksort( $expecting ); + if ( ! empty( $signature ) && ! empty( $expecting ) && $expecting === $signature ) { + return true; + } } return false; @@ -167,21 +170,23 @@ public function is_synced( $post_id ) { */ public function generate_signature( $attachment_id, $cache = true ) { static $signatures = array(); // cache signatures. - if ( ! empty( $signatures[ $attachment_id ] && true === $cache ) ) { + if ( ! empty( $signatures[ $attachment_id ] ) && true === $cache ) { $return = $signatures[ $attachment_id ]; } else { - $sync_base = $this->sync_base( $attachment_id ); - $return = array_map( - function ( $item ) { - if ( is_array( $item ) ) { - $item = wp_json_encode( $item ); - } - - return md5( $item ); - }, - $sync_base - ); - $signatures[ $attachment_id ] = $return; + $return = $this->sync_base( $attachment_id ); + if ( ! is_wp_error( $return ) ) { + $return = array_map( + function ( $item ) { + if ( is_array( $item ) ) { + $item = wp_json_encode( $item ); + } + + return md5( $item ); + }, + $return + ); + $signatures[ $attachment_id ] = $return; + } } return $return; @@ -363,7 +368,7 @@ public function setup_sync_base_struct() { 'callback' => array( $this->managers['upload'], 'upload_asset' ), ), 'status' => array( - 'state' => 'info syncing', + 'state' => 'info', 'note' => __( 'Uploading to Cloudinary', 'cloudinary' ), ), ), @@ -590,7 +595,8 @@ function ( $item, $key ) use ( $required_signature ) { ); $ordered = array_intersect_key( $this->sync_types, $sync_items ); if ( ! empty( $ordered ) ) { - $type = array_shift( array_keys( $ordered ) ); + $types = array_keys( $ordered ); + $type = array_shift( $types ); // Validate that this sync type applied (for optional types like upgrade). if ( isset( $this->sync_base_struct[ $type ]['validate'] ) && is_callable( $this->sync_base_struct[ $type ]['validate'] ) ) { if ( ! call_user_func( $this->sync_base_struct[ $type ]['validate'], $attachment_id ) ) { From 4a2dd2bf2dedab7dbdf6ab24d4802358628310a9 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 4 Aug 2020 09:05:14 +0200 Subject: [PATCH 16/64] ignore non-media items --- .../php/class-media.php | 24 +++++++++++++------ .../php/class-sync.php | 3 +++ .../php/sync/class-delete-sync.php | 2 +- .../php/sync/class-upload-sync.php | 6 ++--- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index f36d0c613..d93b210c7 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -90,6 +90,13 @@ class Media implements Setup { */ public $video; + /** + * Sync instance. + * + * @var \Cloudinary\Sync + */ + public $sync; + /** * Flag if in image_downsize function to prevent overload. * @@ -130,7 +137,7 @@ public function is_media( $attachment ) { * * @return array */ - $media_types = apply_filters( 'cloudinary_media_types', array( 'image', 'video', 'audio', 'application' ) ); + $media_types = apply_filters( 'cloudinary_media_types', array( 'image', 'video', 'audio' ) ); $type = $this->get_media_type( $attachment ); $is_media = in_array( $type, $media_types ); } @@ -668,7 +675,7 @@ public function get_public_id( $attachment_id ) { // Check for a public_id. $public_id = $this->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true ); if ( empty( $public_id ) ) { - $public_id = $this->plugin->components['sync']->generate_public_id( $attachment_id ); + $public_id = $this->sync->generate_public_id( $attachment_id ); } return $public_id; @@ -727,8 +734,8 @@ public function cloudinary_id( $attachment_id ) { if ( isset( $this->cloudinary_ids[ $attachment_id ] ) ) { return $this->cloudinary_ids[ $attachment_id ]; } - if ( ! $this->plugin->components['sync']->is_synced( $attachment_id ) && ! defined( 'REST_REQUEST' ) ) { - $this->plugin->components['sync']->maybe_prepare_sync( $attachment_id ); + if ( ! $this->sync->is_synced( $attachment_id ) && ! defined( 'REST_REQUEST' ) ) { + $this->sync->maybe_prepare_sync( $attachment_id ); } $cloudinary_id = $this->get_cloudinary_id( $attachment_id ); @@ -1480,9 +1487,12 @@ public function get_upload_options( $attachment_id ) { public function setup() { if ( $this->plugin->config['connect'] ) { - $this->base_url = $this->plugin->components['connect']->api->cloudinary_url( '/' ); - $this->credentials = $this->plugin->components['connect']->get_credentials(); - $this->cloudinary_folder = $this->plugin->config['settings']['sync_media']['cloudinary_folder'] ? $this->plugin->config['settings']['sync_media']['cloudinary_folder'] : ''; + $this->base_url = $this->plugin->components['connect']->api->cloudinary_url( '/' ); + $this->credentials = $this->plugin->components['connect']->get_credentials(); + $this->cloudinary_folder = $this->plugin->config['settings']['sync_media']['cloudinary_folder'] ? $this->plugin->config['settings']['sync_media']['cloudinary_folder'] : ''; + $this->sync = $this->plugin->components['sync']; + + // Internal components. $this->filter = new Filter( $this ); $this->upgrade = new Upgrade( $this ); $this->global_transformations = new Global_Transformations( $this ); 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 6e8e414e4..221b5239e 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 @@ -582,6 +582,9 @@ public function maybe_prepare_sync( $attachment_id ) { * @return mixed|string|\WP_Error */ public function get_sync_type( $attachment_id, $cached = true ) { + if ( ! $this->managers['media']->is_media( $attachment_id ) ) { + return false; // Ignore non media items. + } $type = false; $required_signature = $this->generate_signature( $attachment_id, $cached ); $attachment_signature = $this->get_signature( $attachment_id, $cached ); diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-delete-sync.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-delete-sync.php index 9677b1197..e9a1e641e 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-delete-sync.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-delete-sync.php @@ -57,7 +57,7 @@ public function can_delete_asset( $all_caps, $caps, $args ) { // The args are indexed, list them in named variables to better understand. list( $request_cap, , $post_id ) = $args; - if ( 'delete_post' === $request_cap && ! empty( $all_caps['delete_posts'] ) && 'attachment' === get_post_type( $post_id ) ) { + if ( $this->plugin->components['media']->is_media( $post_id ) && 'delete_post' === $request_cap && ! empty( $all_caps['delete_posts'] ) ) { // Check if is pending. if ( ! $this->plugin->components['sync']->is_synced( $post_id ) && $this->plugin->components['sync']->is_pending( $post_id ) ) { 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 6306bdff6..f9d8c13c4 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 @@ -107,7 +107,7 @@ private function register_hooks() { * @return array */ function add_inline_action( $actions, $post ) { - if ( current_user_can( 'delete_post', $post->ID ) ) { + if ( $this->media->is_media( $post->ID ) && current_user_can( 'delete_post', $post->ID ) ) { $action_url = add_query_arg( array( 'action' => 'cloudinary-push', @@ -121,7 +121,7 @@ function add_inline_action( $actions, $post ) { '%s', $action_url, /* translators: %s: Attachment title. */ - esc_attr( sprintf( __( 'Push to Cloudinary “%s”' ), 'asd' ) ), + esc_attr( __( 'Push to Cloudinary' ) ), __( 'Push to Cloudinary', 'cloudinary' ) ); } else { @@ -129,7 +129,7 @@ function add_inline_action( $actions, $post ) { '%s', $action_url, /* translators: %s: Attachment title. */ - esc_attr( sprintf( __( 'Push to Cloudinary “%s”' ), 'asd' ) ), + esc_attr( __( 'Re-sync to Cloudinary', 'cloudinary' ) ), __( 'Re-sync to Cloudinary', 'cloudinary' ) ); } From f14d315bd0547fa42fcc4ee882ac50aa711815fa Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 4 Aug 2020 09:06:16 +0200 Subject: [PATCH 17/64] remove debug --- .../php/sync/class-push-sync.php | 1 - 1 file changed, 1 deletion(-) 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 78031b9de..8f97933d6 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 @@ -158,7 +158,6 @@ public function rest_endpoints( $endpoints ) { * @return bool */ public function rest_verify_nonce( \WP_REST_Request $request ) { - return true; $nonce = $request->get_param( 'nonce' ); return wp_verify_nonce( $nonce, 'wp_rest' ); From 9f034892aea5dbb483e0bd616eb323353d1e4dd7 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 4 Aug 2020 10:30:26 +0200 Subject: [PATCH 18/64] cleanup docs --- .../php/class-plugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-plugin.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-plugin.php index 2f07275ba..4e590d899 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-plugin.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-plugin.php @@ -22,7 +22,7 @@ class Plugin { * * @since 0.1 * - * @var array + * @var Media[]|Sync[]|Settings_Page[]|REST_API[]|Connect[] */ public $components; /** From 204e4a21b98dcd42e9e06d8fa79f978656bf3966 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 4 Aug 2020 12:24:49 +0200 Subject: [PATCH 19/64] make importing work, cleanup down sync, cleanup general sync --- .../php/class-media.php | 2 +- .../php/class-sync.php | 22 +- .../php/connect/class-api.php | 6 +- .../php/media/class-filter.php | 8 +- .../php/sync/class-download-sync.php | 72 +-- .../php/sync/class-push-sync.php | 544 ++---------------- 6 files changed, 85 insertions(+), 569 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index d93b210c7..dd914e4f6 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -1447,7 +1447,7 @@ public function get_context_options( $attachment_id ) { */ $context_options = apply_filters( 'cloudinary_context_options', $context_options, get_post( $attachment_id ), $this ); - return http_build_query( $context_options, null, '|' );; + return http_build_query( $context_options, null, '|' ); } /** 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 221b5239e..653019db7 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 @@ -725,6 +725,13 @@ public function update_signature( $attachment_id, $type ) { } + /** + * Set an item to the signature set. + * + * @param int $attachment_id The attachment ID. + * @param string $type The sync type. + * @param null $value + */ public function set_signature_item( $attachment_id, $type, $value = null ) { // Get the core meta. @@ -749,12 +756,15 @@ public function set_signature_item( $attachment_id, $type, $value = null ) { public function init_background_upload() { if ( ! empty( $this->to_sync ) ) { - $params = array( - 'attachment_ids' => $this->to_sync, - ); - $instant = microtime( true ); - wp_schedule_single_event( $instant, 'cloudinary_sync_items', $params ); - spawn_cron( $instant ); + $threads = ceil( count( $this->to_sync ) / 3 ); // Max of 3 threads to prevent server overload. + $chunks = array_chunk( $this->to_sync, $threads ); + foreach ( $chunks as $thread ) { + $params = array( + 'process_key' => uniqid(), + ); + set_transient( $params['process_key'], $thread, 120 ); + $this->plugin->components['api']->background_request( 'process', $params ); + } } } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php index 7b66b11ea..bb6692add 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php @@ -231,7 +231,10 @@ public function cloudinary_url( $public_id, $args = array(), $size = array(), $c 'resource_type' => 'image', ); $args = wp_parse_args( array_filter( $args ), $defaults ); - + // Correct Audio to Video. + if ( 'audio' === $args['resource_type'] ) { + $args['resource_type'] = 'video'; + } // check for version. if ( ! empty( $args['version'] ) && is_numeric( $args['version'] ) ) { $args['version'] = 'v' . $args['version']; @@ -420,6 +423,7 @@ public function upload( $attachment_id, $args, $headers = array() ) { set_transient( '_cld_disable_http_upload', true, 300 ); // Remove URL file. unset( $args['file'] ); + return $this->upload( $attachment_id, $args ); } } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php index 4bfab0b95..5119b4152 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php @@ -379,7 +379,7 @@ public function filter_out_local( $content ) { * @return array */ public function filter_attachment_for_js( $attachment ) { - $cloudinary_id = $this->media->cloudinary_id( $attachment['id'] ); + $cloudinary_id = $this->media->get_cloudinary_id( $attachment['id'] ); if ( false !== $cloudinary_id ) { $transformations = array(); @@ -396,7 +396,7 @@ public function filter_attachment_for_js( $attachment ) { if ( empty( $attachment['transformations'] ) ) { $transformations = $this->media->get_transformation_from_meta( $attachment['id'] ); - + if ( $transformations ) { $attachment['transformations'] = $transformations; } @@ -424,11 +424,11 @@ public function filter_attachment_for_rest( $attachment ) { if ( false !== $cloudinary_id ) { $attachment->data['source_url'] = $this->media->cloudinary_url( $attachment->data['id'], false ); } - + if ( $has_transformations = ! empty( $this->media->get_transformation_from_meta( $attachment->data['id'] ) ) ) { $attachment->data['transformations'] = $has_transformations; } - + return $attachment; } 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 d20cd687b..73d9449cc 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 @@ -94,8 +94,6 @@ public function rest_can_upload_files( \WP_REST_Request $request ) { * * @param int $attachment_id The attachment ID. * @param string $error The error text to return. - * - * @return \WP_Error */ public function handle_failed_download( $attachment_id, $error ) { // @todo: Place a handler to catch the error for logging. @@ -117,10 +115,9 @@ public function rest_download_asset( \WP_REST_Request $request ) { $attachment_id = $request->get_param( 'attachment_id' ); $file_path = $request->get_param( 'src' ); - $file_name = $request->get_param( 'filename' ); $transformations = (array) $request->get_param( 'transformations' ); - $response = $this->download_asset( $attachment_id, $file_path, $file_name, $transformations ); + $response = $this->import_asset( $attachment_id, $file_path, $transformations ); if ( is_wp_error( $response ) ) { $this->handle_failed_download( $attachment_id, $response->get_error_message() ); } @@ -188,14 +185,17 @@ function ( $val ) use ( $media ) { /** * Download an attachment source to the file system. * - * @param int $attachment_id The attachment ID. + * @param int $attachment_id The attachment ID. + * @param string $source The optional source to download. * * @return array|\WP_Error */ - public function download_asset( $attachment_id ) { + public function download_asset( $attachment_id, $source = null ) { require_once ABSPATH . 'wp-admin/includes/image.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; - $source = $this->media->cloudinary_url( $attachment_id ); + if ( empty( $source ) ) { + $source = $this->media->cloudinary_url( $attachment_id ); + } $file_name = basename( $source ); try { // Prime a file to stream to. @@ -231,10 +231,13 @@ public function download_asset( $attachment_id ) { update_attached_file( $attachment_id, $upload['file'] ); $old_meta = wp_get_attachment_metadata( $attachment_id ); ob_start(); // Catch possible errors in WordPress's ID3 module when setting meta for transformed videos. - $meta = wp_generate_attachment_metadata( $attachment_id, $upload['file'] ); - $captured_errors = ob_get_clean(); - $meta[ Sync::META_KEYS['cloudinary'] ] = $old_meta[ Sync::META_KEYS['cloudinary'] ]; - wp_update_attachment_metadata( $attachment_id, $meta ); + $meta = wp_generate_attachment_metadata( $attachment_id, $upload['file'] ); + $captured_errors = ob_get_clean(); + // Be sure to record v2 meta. + if ( ! empty( $meta[ Sync::META_KEYS['cloudinary'] ] ) ) { + $meta[ Sync::META_KEYS['cloudinary'] ] = $old_meta[ Sync::META_KEYS['cloudinary'] ]; + wp_update_attachment_metadata( $attachment_id, $meta ); + } $this->sync->set_signature_item( $attachment_id, 'download' ); $this->sync->set_signature_item( $attachment_id, 'file' ); $this->sync->set_signature_item( $attachment_id, 'folder' ); @@ -250,7 +253,7 @@ public function download_asset( $attachment_id ) { return new \WP_Error( 'download_error', $e->getMessage() ); } - return $source; + return $upload; } /** @@ -258,12 +261,11 @@ public function download_asset( $attachment_id ) { * * @param int $attachment_id The attachment ID. * @param string $file_path The path of the file. - * @param string $file_name The filename. * @param array|null $transformations * * @return array|\WP_Error */ - public function download_asset_old( $attachment_id, $file_path, $file_name, $transformations = null ) { + public function import_asset( $attachment_id, $file_path, $transformations = null ) { // Get the image and update the attachment. require_once ABSPATH . WPINC . '/class-http.php'; @@ -274,43 +276,7 @@ public function download_asset_old( $attachment_id, $file_path, $file_name, $tra // Fetch the asset. try { // Prime a file to stream to. - $upload = wp_upload_bits( $file_name, null, 'temp' ); - if ( ! empty( $upload['error'] ) ) { - return new \WP_Error( 'download_error', $upload['error'] ); - } - // If the public_id of an asset includes a file extension, a derived item will have the extension duplicated, but not in the source URL. - // This creates a 404. So, instead, we get the actual file name, and use that over the file name that the source url has. - $source_path = dirname( $file_path ); - // Stream file to primed file. - $response = wp_safe_remote_get( - $source_path . '/' . $file_name, - array( - 'timeout' => 300, // phpcs:ignore - 'stream' => true, - 'filename' => $upload['file'], - ) - ); - - if ( is_wp_error( $response ) ) { - return $response; - } - if ( 200 !== $response['response']['code'] ) { - $header_error = wp_remote_retrieve_header( $response, 'x-cld-error' ); - if ( ! empty( $header_error ) ) { - $error = $header_error; - } else { - $error = __( 'Could not download the Cloudinary asset.', 'cloudinary' ); - } - - return new \WP_Error( 'download_error', $error ); - } - - // Prepare the asset. - update_attached_file( $attachment_id, $upload['file'] ); - ob_start(); // Catch possible errors in WordPress's ID3 module when setting meta for transformed videos. - $meta = wp_generate_attachment_metadata( $attachment_id, $upload['file'] ); - $captured_errors = ob_get_clean(); - wp_update_attachment_metadata( $attachment_id, $meta ); + $upload = $this->download_asset( $attachment_id, $file_path ); } catch ( \Exception $e ) { return new \WP_Error( 'download_error', $e->getMessage() ); @@ -318,10 +284,6 @@ public function download_asset_old( $attachment_id, $file_path, $file_name, $tra $attachment = wp_prepare_attachment_for_js( $attachment_id ); - // Log errors if captured. - if ( ! empty( $captured_errors ) ) { - $attachment['_captured_errors'] = $captured_errors; - } // Do transformations. if ( 'image' === $attachment['type'] ) { // Get the cloudinary_id from public_id not Media::cloudinary_id(). 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 8f97933d6..257bbe74d 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 @@ -26,20 +26,6 @@ class Push_Sync { */ public $plugin; - /** - * Holds a list of already built options array, since it can be heavy action. - * - * @var array - */ - private $upload_options = array(); - - /** - * Hold the list or available sync types. - * - * @var array - */ - private $sync_types; - /** * Holds the ID of the last attachment synced. * @@ -82,18 +68,6 @@ class Push_Sync { */ public function __construct( \Cloudinary\Plugin $plugin ) { $this->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', - ); - $this->sync_types = apply_filters( 'cloudinary_sync_types', $sync_types ); - $this->register_hooks(); } @@ -141,28 +115,14 @@ public function rest_endpoints( $endpoints ) { ); $endpoints['process'] = array( - 'method' => \WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'rest_push_attachments' ), - 'args' => array(), - 'permission_callback' => array( $this, 'rest_verify_nonce' ), + 'method' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'process_sync' ), + 'args' => array(), ); return $endpoints; } - /** - * General nonce based auth. - * - * @param \WP_REST_Request $request The request. - * - * @return bool - */ - public function rest_verify_nonce( \WP_REST_Request $request ) { - $nonce = $request->get_param( 'nonce' ); - - return wp_verify_nonce( $nonce, 'wp_rest' ); - } - /** * Admin permission callback. * @@ -205,7 +165,8 @@ public function rest_start_sync( \WP_REST_Request $request ) { return $this->rest_get_queue_status(); // Nothing to sync. } - $this->sync->managers['queue']->start_queue(); + + return $this->sync->managers['queue']->start_queue(); } /** @@ -248,6 +209,39 @@ public function process_assets( $attachments = array() ) { return $stat; } + /** + * Process assets to sync vai WP REST API. + * + * @param \WP_REST_Request $request The request. + * + * @return mixed|\WP_REST_Response + */ + public function process_sync( \WP_REST_Request $request ) { + $process_key = $request->get_param( 'process_key' ); + $note = 'no process key'; + if ( ! empty( $process_key ) ) { + $attachments = get_transient( $process_key ); + if ( ! empty( $attachments ) ) { + delete_transient( $process_key ); + + return rest_ensure_response( + array( + 'success' => true, + 'data' => $this->process_assets( $attachments ), + ) + ); + } + $note = 'no attachments'; + } + + return rest_ensure_response( + array( + 'success' => false, + 'note' => $note, + ) + ); + } + /** * Pushes attachments via WP REST API. * @@ -256,6 +250,10 @@ public function process_assets( $attachments = array() ) { * @return mixed|\WP_REST_Response */ public function rest_push_attachments( \WP_REST_Request $request ) { + + + $option_key = $request->get_param( 'synckey' ); + $data = get_transient( $option_key ); // Get thread ID. $last_id = $request->get_param( 'last_id' ); $last_result = $request->get_param( 'last_result' ); @@ -314,462 +312,4 @@ public function process_queue() { } } } - - /** - * Start a new call. - * - * @param int $last_id The last ID that was processed. - * @param string $last_result The last result. - * - * @return mixed|\WP_REST_Response. - */ - private function call_thread( $last_id = null, $last_result = null ) { - - // Add last results. - $params = array(); - if ( null !== $last_id && null !== $last_result ) { - $params['last_id'] = $last_id; - $params['last_result'] = $last_result; - } - - // Setup background call to continue the queue. - $this->api->background_request( 'process', $params ); - - return $this->rest_get_queue_status(); - } - - /** - * @param int|\WP_Post $attachment The attachment post or id to get resource type for. - * - * @return string - */ - public function get_resource_type( $attachment ) { - if ( is_numeric( $attachment ) ) { - $attachment = get_post( $attachment ); - } - - // Deal with compound mime types. - $type = explode( '/', $attachment->post_mime_type ); - - return array_shift( $type ); - } - - /** - * Get the type of sync the attachment should do. - * - * @param int|\WP_Post $attachment The attachment to get the required sync type for. - * - * @return string - */ - private function get_sync_type( $attachment ) { - if ( is_numeric( $attachment ) ) { - $attachment = get_post( $attachment ); - } - - $type = 'upload'; - // Check for explicit (has public_id, but no breakpoints). - $attachment_signature = $this->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. - $type = 'explicit'; - } - // fallback to upload. - } else { - // Has signature find differences and use specific sync method. - $required_signature = $this->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 ] ) ) { - return $this->sync_types[ $key ]; - } - } - // If it gets here, the signature is identical, so set as no update. - $type = new \WP_Error( 'attachment_synced', __( 'Attachment is already fully synced.', 'cloudinary' ) ); - } - - // Return the type. - return $type; - } - - /** - * Prepare an attachment for upload. - * - * @param int|\WP_Post $post The attachment to prepare. - * @param bool $push_sync Flag to determine if this prep is for a sync. True = build for syncing, false = build for validation. - * - * @return array|\WP_Error - */ - public function prepare_upload( $post, $push_sync = false ) { - - if ( is_numeric( $post ) ) { - $post = get_post( $post ); - } - - if ( empty( $post ) ) { - return new \WP_Error( 'attachment_post_get', __( 'Could not retrieve the attachment post.', 'cloudinary' ) ); - } - - if ( empty( $this->upload_options[ $post->ID ] ) ) { - - if ( 'attachment' !== $post->post_type ) { - return new \WP_Error( 'attachment_post_expected', __( 'An attachment post was expected.', 'cloudinary' ) ); - } - - // First check if this has a file and it can be uploaded. - $file = get_attached_file( $post->ID ); - $file_size = 0; - $downsync = false; - if ( empty( $file ) ) { - return new \WP_Error( 'attachment_no_file', __( 'Attachment did not have a file.', 'cloudinary' ) ); - } elseif ( ! file_exists( $file ) ) { - // May be an old upload type. - $src = get_post_meta( $post->ID, '_wp_attached_file', true ); - if ( $this->media->is_cloudinary_url( $src ) ) { - // Download first maybe. - if ( true === $push_sync ) { - $download = $this->sync->managers['download']->down_sync( $post->ID ); - if ( is_wp_error( $download ) ) { - update_post_meta( $post->ID, Sync::META_KEYS['sync_error'], $download->get_error_message() ); - - return new \WP_Error( 'attachment_download_error', $download->get_error_message() ); - } - $file = get_attached_file( $post->ID ); - $file_size = filesize( $file ); - $downsync = true; - } - } - } else { - $file_size = filesize( $file ); - } - - $resource_type = $this->get_resource_type( $post ); - $max_size = ( 'image' === $resource_type ? 'max_image_size' : 'max_video_size' ); - - if ( ! empty( $this->connect->usage[ $max_size ] ) && $file_size > $this->connect->usage[ $max_size ] ) { - $max_size_hr = size_format( $this->connect->usage[ $max_size ] ); - - // 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->media->delete_post_meta( $post->ID, Sync::META_KEYS['pending'] ); // Remove Flag. - - // Cleanup flags - delete_post_meta( $post->ID, Sync::META_KEYS['syncing'] ); - delete_post_meta( $post->ID, Sync::META_KEYS['downloading'] ); - - 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. - $cld_folder = trailingslashit( $settings['sync_media']['cloudinary_folder'] ); - if ( empty( $public_id ) ) { - // Create a new public_id. - $public_id = $this->sync->generate_public_id( $post->ID ); - } - - // 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 = $this->media->get_post_meta( $post->ID, Sync::META_KEYS['folder_sync'], true ); - if ( ! empty( $folder_sync ) && false === $downsync ) { - $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_file, - 'context' => array( - 'caption' => esc_attr( $post->post_title ), - 'alt' => $post->_wp_attachment_image_alt, - ), - ); - - // If in cloudinary folder, add the posts id to context. - if ( false !== $cld_folder ) { - $options['context']['wp_id'] = $post->ID; - } - - // Add breakpoints if we have an image. - $breakpoints = false; - if ( 'image' === $resource_type ) { - $meta = wp_get_attachment_metadata( $post->ID ); - // Get meta image size if non exists. - if ( empty( $meta ) ) { - $meta = array(); - $imagesize = getimagesize( $file ); - $meta['width'] = $imagesize[0]; - } - $max_width = $this->media->get_max_width(); - // Add breakpoints request options. - if ( ! empty( $settings['global_transformations']['enable_breakpoints'] ) ) { - $options['responsive_breakpoints'] = array( - 'create_derived' => true, - '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' => $settings['global_transformations']['min_width'], - ); - $transformations = $this->media->get_transformation_from_meta( $post->ID ); - if ( ! empty( $transformations ) ) { - $options['responsive_breakpoints']['transformation'] = Api::generate_transformation_string( $transformations ); - } - $breakpoints = array( - 'public_id' => $options['public_id'], - 'type' => 'upload', - 'responsive_breakpoints' => $options['responsive_breakpoints'], - 'context' => $options['context'], - ); - } - } - - /** - * Filter the options to allow other plugins to add requested options for uploading. - * - * @param array $options The options array. - * @param \WP_Post $post The attachment post. - * @param \Cloudinary\Sync The sync object instance. - * - * @return array - */ - $options = apply_filters( 'cloudinary_upload_options', $options, $post, $this ); - - // Combine context option to string. - $options['context'] = http_build_query( $options['context'], null, '|' ); - if ( ! empty( $breakpoints ) && is_array( $breakpoints['context'] ) ) { - $breakpoints['context'] = http_build_query( $breakpoints['context'], null, '|' ); - } - - // Restructure the path to the filename to allow correct placement in Cloudinary. - $public_id = ltrim( $public_id_folder . $options['public_id'], '/' ); - // If this is a push sync, make sure the ID is allowed and unique. - - // Setup suffix data for unique ids. - $suffix_defaults = array( - 'public_id' => $public_id, - 'suffix' => null, - ); - $suffix_meta = $this->media->get_post_meta( $post->ID, Sync::META_KEYS['suffix'], true ); - $suffix_data = wp_parse_args( $suffix_meta, $suffix_defaults ); - - // Prepare a uniqueness check and get a suffix if needed. - if ( true === $push_sync ) { - if ( $public_id !== $suffix_data['public_id'] || empty( $suffix_data['suffix'] ) ) { - $suffix_data['suffix'] = $this->sync->add_suffix_maybe( $public_id, $post->ID ); - if ( ! empty( $suffix_data['suffix'] ) ) { - // Only save if there is a suffix to save on metadata. - $this->media->update_post_meta( $post->ID, Sync::META_KEYS['suffix'], $suffix_data ); - } else { - // Clear meta data in case of a unique name on a rename etc. - $this->media->delete_post_meta( $post->ID, Sync::META_KEYS['suffix'] ); - } - } - } - // Add Suffix to public_id. - if ( ! empty( $suffix_data['suffix'] ) ) { - $public_id = $public_id . $suffix_data['suffix']; - } - $return = array( - 'file' => $file, - 'folder' => ltrim( $cld_folder, '/' ), - 'public_id' => $public_id, - 'suffix' => $suffix_data['suffix'], - 'breakpoints' => array(), - 'options' => $options, - ); - $return['options']['public_id'] = $public_id; - if ( ! empty( $breakpoints ) ) { - $return['breakpoints'] = $breakpoints; - $return['breakpoints']['public_id'] = $public_id; // Stage public ID to folder for breakpoints. - } - $this->upload_options[ $post->ID ] = $return; - - } - - return $this->upload_options[ $post->ID ]; - } - - /** - * Push \WP_Post items in array to Cloudinary. - * - * Provides $large param for supporting video uploads. If post_mime_type is 'video' - * then large will be assumed. - * - * @see https://cloudinary.com/documentation/php_image_and_video_upload#php_image_upload - * @see https://cloudinary.com/documentation/php_image_and_video_upload#php_video_upload - * - * @param mixed $attachments Array of \WP_Post (hopefully with `post_type='attachment'`. - * - * @return array|\WP_Error - */ - public function push_attachments( $attachments ) { - - /** - * Holds the file names for successful and failed attachments. - * - * Also includes `start` time, `end` time and `duration`. - * - * @var array $stats - */ - $stats = array( - 'success' => array(), - 'fail' => array(), - 'start' => time(), - 'total' => count( $attachments ), - 'processed' => 0, - ); - - // Go over each attachment. - foreach ( $attachments as $attachment ) { - $attachment = get_post( $attachment ); - // Clear upload option cache for this item to allow down sync. - $this->upload_options[ $attachment->ID ] = false; - - $upload = $this->prepare_upload( $attachment->ID, true ); - - // Filter out any attachments with problematic options. - if ( is_wp_error( $upload ) ) { - - /** - * Attachment is an error. - * - * @var \WP_Error $upload - */ - $stats['fail'][] = $upload->get_error_message(); - continue; - } - - - // Determine Sync type needed. - $sync_type = $this->get_sync_type( $attachment ); - - // Skip if sync type is an error. - if ( is_wp_error( $sync_type ) ) { - /** - * Sync type is an error. - * - * @var \WP_Error $sync_type - */ - $stats['fail'][] = $sync_type->get_error_message(); - continue; - } - - // 100MB restriction on normal upload. - $do_large = 'video' === $upload['options']['resource_type'] ? true : false; - - if ( ! $do_large ) { - - if ( 'explicit' === $sync_type ) { - // Explicit update. - $args = array( - 'public_id' => $upload['public_id'], - 'type' => 'upload', - ); - if ( ! empty( $upload['options']['responsive_breakpoints'] ) ) { - $args['responsive_breakpoints'] = $upload['options']['responsive_breakpoints']; - } - if ( ! empty( $upload['options']['context'] ) ) { - $args['context'] = $upload['options']['context']; - } - $result = $this->connect->api->explicit( $args ); - } elseif ( 'rename' === $sync_type ) { - // Rename an asset. - $args = array( - 'from_public_id' => $this->media->get_post_meta( $attachment->ID, Sync::META_KEYS['public_id'] ), - 'to_public_id' => $upload['public_id'], - ); - $result = $this->connect->api->{$upload['options']['resource_type']}( 'rename', 'POST', $args ); - } else { - // dynamic sync type.. - $result = $this->connect->api->{$sync_type}( $upload['file'], $upload['options'] ); - } - } else { - // Large Upload. - $result = $this->connect->api->upload_large( $upload['file'], $upload['options'] ); - } - - // Exceptions are handled by the Upload wrapper class and returned as \WP_Error, so check for it. - if ( is_wp_error( $result ) ) { - $error = $result->get_error_message(); - $stats['fail'][] = $error; - $this->media->update_post_meta( $attachment->ID, Sync::META_KEYS['sync_error'], $error ); - continue; - } - - // Successful upload, so lets update meta. - if ( is_array( $result ) && ( array_key_exists( 'public_id', $result ) || array_key_exists( 'public_ids', $result ) ) ) { - $meta_data = array( - Sync::META_KEYS['signature'] => $this->sync->generate_signature( $attachment->ID ), - ); - - // We only have a version if updating a file or an upload. - if ( ! empty( $result['version'] ) ) { - $meta_data[ Sync::META_KEYS['version'] ] = $result['version']; - } - $this->media->delete_post_meta( $attachment->ID, Sync::META_KEYS['pending'] ); - - // Cleanup flags - delete_post_meta( $attachment->ID, Sync::META_KEYS['downloading'] ); - delete_post_meta( $attachment->ID, Sync::META_KEYS['syncing'] ); - - $this->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']; - } elseif ( 'image' === $upload['options']['resource_type'] && 'explicit' === $sync_type ) { - // Remove records of breakpoints. - delete_post_meta( $attachment->ID, Sync::META_KEYS['breakpoints'] ); - } - } - - // Reset public_id without suffix. - if ( ! empty( $upload['suffix'] ) ) { - $upload['options']['public_id'] = strstr( $upload['options']['public_id'], $upload['suffix'], true ); - } - // Generate a public_id sync hash. - if ( ! empty( $upload['options']['public_id'] ) ) { - // a transformation breakpoints only ever happens on a down sync. - $sync_key = '_' . md5( $upload['options']['public_id'] ); - $meta_data['sync_key'] = true; - - // Add base ID. - update_post_meta( $attachment->ID, $sync_key, true ); - } - $stats['success'][] = $attachment->post_title; - $meta = wp_get_attachment_metadata( $attachment->ID, true ); - $meta[ Sync::META_KEYS['cloudinary'] ] = $meta_data; - wp_update_attachment_metadata( $attachment->ID, $meta ); - - $this->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 ) ) { - $content_posts = array_unique( $content_search->get_posts() ); // ensure post only gets updated once. - foreach ( $content_posts as $content_id ) { - wp_update_post( array( 'ID' => $content_id ) ); // Trigger an update, internal filters will filter out remote URLS. - } - } - } - - $stats['processed'] += 1; - } - - $stats['end'] = time(); - $stats['duration'] = (int) $stats['end'] - (int) $stats['start']; - - return $stats; - } } From cc2d23f9c5fa2476bae94b03f884cf1782c44dc9 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 5 Aug 2020 10:52:17 +0200 Subject: [PATCH 20/64] add threaded sync queue, clean up code formatting, remove some unused methods --- .../php/class-media.php | 2 +- .../php/class-settings-page.php | 8 +- .../php/class-sync.php | 3 +- .../php/connect/class-api.php | 5 +- .../php/media/class-upgrade.php | 124 ---------- .../php/sync/class-delete-sync.php | 4 +- .../php/sync/class-download-sync.php | 4 +- .../php/sync/class-push-sync.php | 103 +++------ .../php/sync/class-sync-queue.php | 215 +++++++++++++----- .../php/sync/class-upload-sync.php | 75 ------ 10 files changed, 210 insertions(+), 333 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index dd914e4f6..7dc691ceb 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -1382,7 +1382,7 @@ public function get_breakpoint_options( $attachment_id ) { $has_breakpoints = $this->plugin->config['settings']['global_transformations']['enable_breakpoints']; if ( 'off' !== $has_breakpoints && wp_attachment_is_image( $attachment_id ) ) { - $meta = wp_get_attachment_metadata( $post->ID ); + $meta = wp_get_attachment_metadata( $attachment_id ); // Get meta image size if non exists. if ( empty( $meta ) ) { $meta = array(); diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-settings-page.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-settings-page.php index 9ed130a89..84e9eab57 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-settings-page.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-settings-page.php @@ -224,8 +224,8 @@ public function render() { * * @since 0.1 * - * @param array $field The field to render. - * @param string|null $value The value to render. + * @param array $field The field to render. + * @param string|null $value The value to render. * @param bool $show_description Whether to render the description. */ public function render_field( $field, $value = null, $show_description = true ) { @@ -415,7 +415,7 @@ public function sanitize( $setting, $fields ) { if ( is_array( $field ) ) { array_walk_recursive( $field, - static function( $field_value ) { + static function ( $field_value ) { // WP 4.9 compatibility, as _sanitize_text_fields() didn't have this check yet, and this prevents an error. // @see https://github.com/WordPress/wordpress-develop/blob/b30baca3ca2feb7f44b3615262ca55fcd87ae232/src/wp-includes/formatting.php#L5307 if ( is_object( $field_value ) || is_array( $field_value ) ) { @@ -726,6 +726,8 @@ private function register_tab_assets( $tab_slug ) { * * @param array $set The array of assets to register. * @param callable $call The function to call to register asset. + * + * @return array */ public function register_tab_asset( $set, $call ) { foreach ( $set as $key => &$asset ) { 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 653019db7..1dc21332b 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 @@ -164,7 +164,7 @@ public function is_synced( $post_id ) { * Generate a signature based on whats required for a full sync. * * @param int $attachment_id The Attachment id to generate a signature for. - * @param bool $cached Flag to specify if a cached signature is to be used or build a new one. + * @param bool $cache Flag to specify if a cached signature is to be used or build a new one. * * @return string|bool */ @@ -643,7 +643,6 @@ public function filter_status( $status, $attachment_id ) { // Check if there's an error. - $log = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['pending'] ); $has_error = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['sync_error'], true ); if ( ! empty( $has_error ) ) { $status['state'] = 'error'; diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php index bb6692add..b65d30b27 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php @@ -177,7 +177,8 @@ public function url( $resource, $function = null, $endpoint = false ) { /** * Generate a transformation string. * - * @param array $options The transformation options to generate from. + * @param array $options The transformation options to generate from. + * @param string $type The asset Type. * * @return string */ @@ -283,7 +284,7 @@ public function cloudinary_url( $public_id, $args = array(), $size = array(), $c public function get_asset_details( $public_id, $type ) { $url = $this->url( 'resources', $type . '/upload/' . $public_id, true ); - return $this->call( $url, array( 'body' => $args ), 'get' ); + return $this->call( $url, array( 'body' => array() ), 'get' ); } /** 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 6945227ac..71a0beb92 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 @@ -34,20 +34,6 @@ class Upgrade { */ private $sync; - /** - * The cron frequency to ensure that the queue is progressing. - * - * @var int - */ - protected $cron_frequency; - - /** - * The cron offset since the last update. - * - * @var int - */ - protected $cron_start_offset; - /** * Filter constructor. * @@ -56,87 +42,9 @@ class Upgrade { public function __construct( \Cloudinary\Media $media ) { $this->media = $media; $this->sync = $media->plugin->components['sync']; - - $this->cron_frequency = apply_filters( 'cloudinary_cron_frequency', 600 ); - $this->cron_start_offset = apply_filters( 'cloudinary_cron_start_offset', 60 ); - $this->setup_hooks(); } - /** - * Check's if an attachment is from a previous version of Cloudinary. - * - * @param string|bool $cloudinary_id The cloudinary ID. Should be false. - * @param int $attachment_id The attachment ID to convert. - * - * @return string|bool - */ - public function check_cloudinary_version( $cloudinary_id, $attachment_id ) { - - if ( false === $cloudinary_id ) { - // Backwards compat. - $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 ); - - /* - * If we still have $meta['cloudinary'] but already have $public_id, then conversion process is already underway. - * In that case, don't call convert_cloudinary_version() again, since it would make a duplicate background_request. - */ - if ( ! empty( $meta['cloudinary'] ) && empty( $public_id ) ) { - $cloudinary_id = $this->convert_cloudinary_version( $attachment_id ); - } elseif ( ! empty( $public_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; - } - - /** - * Checks the status of the media item. - * - * @param array $status Array of state and note. - * @param int $attachment_id The attachment id. - * - * @return array - */ - public function filter_status( $status, $attachment_id ) { - - if ( get_post_meta( $attachment_id, Sync::META_KEYS['downloading'] ) ) { - $status['state'] = 'info downloading'; - $status['note'] = __( 'Downloading', 'cloudinary' ); - } - - if ( get_post_meta( $attachment_id, Sync::META_KEYS['syncing'] ) ) { - $status['state'] = 'info syncing'; - $status['note'] = __( 'Syncing metadata', 'cloudinary' ); - } - - return $status; - } - /** * Convert an image post that was created from Cloudinary v1. * @@ -203,41 +111,11 @@ public function convert_cloudinary_version( $attachment_id ) { return $public_id; } - /** - * Maybe resume the upgrading assets. - * This is a fallback mechanism to resume the upgrade when it stops unexpectedly. - * - * @return void - */ - public function maybe_resume_upgrade() { - global $wpdb; - - $assets = $wpdb->get_col( // phpcs:ignore WordPress.DB.DirectDatabaseQuery - $wpdb->prepare( - "SELECT post_id - FROM $wpdb->postmeta - WHERE meta_key = %s", - Sync::META_KEYS['downloading'] - ) - ); - - if ( ! empty( $assets ) ) { - wp_schedule_single_event( time() + $this->cron_frequency, 'cloudinary_resume_upgrade' ); - - foreach ( $assets as $asset ) { - $this->sync->managers['upload']->add_to_sync( $asset ); - } - } - } /** * Setup hooks for the filters. */ public function setup_hooks() { - //add_filter( 'validate_cloudinary_id', array( $this, 'check_cloudinary_version' ), 10, 2 ); // Priority 10, to allow prep_on_demand_upload. - - // Show sync status. - //add_filter( 'cloudinary_media_status', array( $this, 'filter_status' ), 20, 2 ); // Add a redirection to the new plugin settings, from the old plugin. if ( is_admin() ) { @@ -249,7 +127,5 @@ public function setup_hooks() { } } ); } - - add_action( 'cloudinary_resume_upgrade', array( $this, 'maybe_resume_upgrade' ) ); } } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-delete-sync.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-delete-sync.php index e9a1e641e..9881e2c78 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-delete-sync.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-delete-sync.php @@ -65,7 +65,7 @@ public function can_delete_asset( $all_caps, $caps, $args ) { $has_error = $this->plugin->components['media']->get_post_meta( $post_id, Sync::META_KEYS['sync_error'], true ); if ( empty( $has_error ) ) { $all_caps['delete_posts'] = false; - $action = filter_input( INPUT_GET, 'action', FILTER_SANITIZE_STRING ); + $action = filter_input( INPUT_GET, 'action', FILTER_SANITIZE_STRING ); if ( ! empty( $action ) && '-1' !== $action ) { wp_die( esc_html__( 'Sorry, you can’t delete an asset until it has fully synced with Cloudinary. Try again once syncing is complete.', 'cloudinary' ) ); } @@ -100,7 +100,7 @@ public function delete_asset( $post_id ) { $parts = explode( '/', $public_id ); $cloudinary_folder = $this->plugin->config['settings']['sync_media']['cloudinary_folder'] ? $this->plugin->config['settings']['sync_media']['cloudinary_folder'] : ''; if ( $cloudinary_folder === $parts[0] ) { - $type = $this->plugin->components['sync']->managers['push']->get_resource_type( $post_id ); + $type = $this->plugin->components['media']->get_media_type( $post_id ); $options = array( 'public_id' => $public_id, 'invalidate' => true, // clear from CDN cache as well. 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 73d9449cc..7b00f8df6 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 @@ -179,7 +179,7 @@ function ( $val ) use ( $media ) { } } - return $this->download_asset( $attachment_id, $file, basename( $file ), $media->get_transformations_from_string( $file ) ); + return $this->download_asset( $attachment_id, $file ); } /** @@ -232,7 +232,7 @@ public function download_asset( $attachment_id, $source = null ) { $old_meta = wp_get_attachment_metadata( $attachment_id ); ob_start(); // Catch possible errors in WordPress's ID3 module when setting meta for transformed videos. $meta = wp_generate_attachment_metadata( $attachment_id, $upload['file'] ); - $captured_errors = ob_get_clean(); + $captured_errors = ob_get_clean(); // Capture issues. // Be sure to record v2 meta. if ( ! empty( $meta[ Sync::META_KEYS['cloudinary'] ] ) ) { $meta[ Sync::META_KEYS['cloudinary'] ] = $old_meta[ Sync::META_KEYS['cloudinary'] ]; 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 257bbe74d..5c693427e 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 @@ -7,7 +7,6 @@ namespace Cloudinary\Sync; -use Cloudinary\Connect\Api; use Cloudinary\Sync; /** @@ -61,6 +60,13 @@ class Push_Sync { */ protected $api; + /** + * Holds the sync queue object. + * + * @var \Cloudinary\Sync\Sync_Queue + */ + protected $queue; + /** * Push_Sync constructor. * @@ -87,6 +93,8 @@ public function setup() { $this->sync = $this->plugin->components['sync']; $this->connect = $this->plugin->components['connect']; $this->api = $this->plugin->components['api']; + $this->queue = $this->sync->managers['queue']; + add_action( 'cloudinary_run_queue', array( $this, 'process_queue' ) ); add_action( 'cloudinary_sync_items', array( $this, 'process_assets' ) ); } @@ -120,6 +128,12 @@ public function rest_endpoints( $endpoints ) { 'args' => array(), ); + $endpoints['queue'] = array( + 'method' => \WP_REST_Server::CREATABLE, + 'callback' => array( $this, 'process_queue' ), + 'args' => array(), + ); + return $endpoints; } @@ -144,7 +158,7 @@ public function rest_get_queue_status() { return rest_ensure_response( array( 'success' => true, - 'data' => $this->sync->managers['queue']->get_queue_status(), + 'data' => $this->queue->get_queue_status(), ) ); } @@ -154,19 +168,21 @@ public function rest_get_queue_status() { * * @param \WP_REST_Request $request The request. * - * @return mixed|\WP_REST_Response + * @return \WP_REST_Response */ public function rest_start_sync( \WP_REST_Request $request ) { - $stop = $request->get_param( 'stop' ); - $queue = $this->sync->managers['queue']->get_queue(); - if ( empty( $queue['pending'] ) || ! empty( $stop ) ) { - $this->sync->managers['queue']->stop_queue(); + $stop = $request->get_param( 'stop' ); + $status = $this->queue->get_queue_status(); + if ( empty( $status['pending'] ) || ! empty( $stop ) ) { + $this->queue->stop_queue(); return $this->rest_get_queue_status(); // Nothing to sync. } - return $this->sync->managers['queue']->start_queue(); + $this->queue->start_queue(); + + return $this->rest_get_queue_status(); } /** @@ -243,73 +259,22 @@ public function process_sync( \WP_REST_Request $request ) { } /** - * Pushes attachments via WP REST API. + * Resume the bulk sync. * * @param \WP_REST_Request $request The request. * - * @return mixed|\WP_REST_Response - */ - public function rest_push_attachments( \WP_REST_Request $request ) { - - - $option_key = $request->get_param( 'synckey' ); - $data = get_transient( $option_key ); - // Get thread ID. - $last_id = $request->get_param( 'last_id' ); - $last_result = $request->get_param( 'last_result' ); - - // Get attachment_id in case this is a single direct request to upload. - $attachments = $request->get_param( 'attachment_ids' ); - $background_process = false; - - if ( empty( $attachments ) ) { - // No attachments posted. Pull from the queue. - $attachments = array(); - $background_process = true; - $attachments[] = $this->sync->managers['queue']->get_post(); - - $attachments = array_filter( $attachments ); - } - $stat = array(); - // If not a single request, process based on queue. - if ( ! empty( $attachments ) ) { - - // If a single specified ID, push and return response. - $ids = array_map( 'intval', $attachments ); - // Handle based on Sync Type. - $stat = $this->process_assets( $ids ); - - } - - return rest_ensure_response( - array( - 'success' => true, - 'data' => $stat, - ) - ); - - } - - /** - * Resume the bulk sync. - * * @return void */ - public function process_queue() { - if ( $this->sync->managers['queue']->is_running() ) { - $queue = $this->sync->managers['queue']->get_queue(); - if ( ! empty( $queue['pending'] ) ) { - wp_schedule_single_event( time() + 20, 'cloudinary_run_queue' ); - if ( ! empty( $queue['last_update'] ) ) { - if ( $queue['last_update'] > current_time( 'timestamp' ) - 60 ) { - return; - } - } - while ( $attachment_id = $this->sync->managers['queue']->get_post() ) { - $this->process_assets( $attachment_id ); - $this->sync->managers['queue']->mark( $attachment_id, 'done' ); - } + public function process_queue( \WP_REST_Request $request ) { + $thread = $request->get_param( 'thread' ); + $queue = $this->queue->get_thread_queue( $thread ); + + if ( ! empty( $queue ) && $this->queue->is_running() ) { + while ( $attachment_id = $this->queue->get_post( $thread ) ) { + $this->process_assets( $attachment_id ); + $this->queue->mark( $attachment_id, 'done' ); } + $this->queue->stop_maybe(); } } } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php index 9935038b1..cbc960748 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php @@ -32,13 +32,6 @@ class Sync_Queue { */ private static $queue_key = '_cloudinary_sync_queue'; - /** - * Holds flag to make the queue run. - * - * @var bool - */ - private $running = false; - /** * The cron frequency to ensure that the queue is progressing. * @@ -53,6 +46,13 @@ class Sync_Queue { */ protected $cron_start_offset; + /** + * Holds the thread ID's + * + * @var array + */ + public $threads; + /** * Upload_Queue constructor. * @@ -63,7 +63,7 @@ public function __construct( \Cloudinary\Plugin $plugin ) { $this->cron_frequency = apply_filters( 'cloudinary_cron_frequency', 10 * MINUTE_IN_SECONDS ); $this->cron_start_offset = apply_filters( 'cloudinary_cron_start_offset', MINUTE_IN_SECONDS ); - + $this->threads = apply_filters( 'cloudinary_queue_threads', array( 'thread_0', 'thread_1', 'thread_2' ) ); $this->load_hooks(); } @@ -73,7 +73,7 @@ public function __construct( \Cloudinary\Plugin $plugin ) { * @return void */ public function load_hooks() { - //add_action( 'cloudinary_resume_queue', array( $this, 'maybe_resume_queue' ) ); + add_action( 'cloudinary_resume_queue', array( $this, 'maybe_resume_queue' ) ); } /** @@ -106,16 +106,25 @@ public function set_queue( $queue ) { /** * Get a set of pending items. * + * @param string $thread The thread ID. + * * @return bool */ - public function get_post() { + public function get_post( $thread ) { $id = false; - if ( $this->is_running() ) { + if ( $this->is_running() && in_array( $thread, $this->threads, true ) ) { $queue = $this->get_queue(); - if ( ! empty( $queue['pending'] ) ) { - $id = array_shift( $queue['pending'] ); + if ( ! empty( $queue[ $thread ] ) ) { + $id = array_shift( $queue[ $thread ] ); $queue['processing'][] = $id; $queue['last_update'] = current_time( 'timestamp' ); + + if ( ! empty( $queue['run_status'][ $thread ]['last_update'] ) ) { + $queue['run_status'][ $thread ]['posts'][] = current_time( 'timestamp' ) - $queue['run_status'][ $thread ]['last_update']; + $queue['run_status'][ $thread ]['average'] = round( array_sum( $queue['run_status'][ $thread ]['posts'] ) / count( $queue['run_status'][ $thread ]['posts'] ), 2 ); + } + $queue['run_status'][ $thread ]['last_update'] = current_time( 'timestamp' ); + $this->set_queue( $queue ); } } @@ -123,6 +132,25 @@ public function get_post() { return $id; } + /** + * Check if a thread is running. + * + * @param string $thread Thread ID to check. + * + * @return bool + */ + protected function thread_running( $thread ) { + $running = false; + if ( in_array( $thread, $this->threads, true ) ) { + $queue = $this->get_queue(); + $now = current_time( 'timestamp' ); + if ( $this->is_running() && ! empty( $queue[ $thread ] ) && $now - $queue['run_status'][ $thread ]['last_update'] < $this->cron_start_offset ) { + $running = true; + } + } + + return $running; + } /** * Mark an id as done or error. @@ -136,19 +164,10 @@ public function mark( $id, $type = 'done' ) { $key = array_search( (int) $id, $queue['processing'], true ); if ( false !== $key ) { unset( $queue['processing'][ $key ] ); - if ( 'error' === $type ) { - $state = $this->plugin->components['sync']->managers['push']->prepare_upload( $id ); - if ( is_wp_error( $state ) ) { - $file = get_attached_file( $id ); - $queue[ $type ][] = '
' . basename( $file ) . ': ' . $state->get_error_message() . '
'; - // Add a flag that this file had an error as to not try process it again. - update_post_meta( $id, Sync::META_KEYS['sync_error'], $state->get_error_message() ); - } - } else { - if ( ! in_array( $id, $queue[ $type ], true ) ) { - $queue[ $type ][] = $id; - } + if ( ! in_array( $id, $queue[ $type ], true ) ) { + $queue[ $type ][] = $id; } + } $this->set_queue( $queue ); @@ -171,8 +190,11 @@ public function is_running() { * @return array */ public function get_queue_status() { - $queue = $this->get_queue(); - $pending = count( $queue['pending'] ); + $queue = $this->get_queue(); + $pending = 0; + foreach ( $this->threads as $thread ) { + $pending += count( $queue[ $thread ] ); + } $done = count( $queue['done'] ); $processing = count( $queue['processing'] ); $error = count( $queue['error'] ); @@ -222,15 +244,15 @@ public function build_queue() { 'posts_per_page' => 1000, // phpcs:ignore 'fields' => 'ids', 'meta_query' => array( // phpcs:ignore - 'relation' => 'AND', - array( - 'key' => Sync::META_KEYS['sync_error'], - 'compare' => 'NOT EXISTS', - ), - array( - 'key' => Sync::META_KEYS['public_id'], - 'compare' => 'NOT EXISTS', - ), + 'relation' => 'AND', + array( + 'key' => Sync::META_KEYS['sync_error'], + 'compare' => 'NOT EXISTS', + ), + array( + 'key' => Sync::META_KEYS['public_id'], + 'compare' => 'NOT EXISTS', + ), ), 'ignore_sticky_posts' => false, 'no_found_rows' => true, @@ -240,20 +262,40 @@ public function build_queue() { $ids = $attachments->get_posts(); // Transform attachments. $return = array( - 'pending' => array(), 'done' => array(), 'processing' => array(), 'error' => array(), + 'run_status' => array(), ); + foreach ( $this->threads as $thread ) { + $return[ $thread ] = array(); + $return['run_status'][ $thread ] = array(); + } // Add items to pending queue. - $return['pending'] = $ids; + if ( ! empty( $ids ) ) { + $chunk_size = ceil( count( $ids ) / count( $this->threads ) ); + $chunks = array_chunk( $ids, $chunk_size ); + foreach ( $chunks as $index => $chunk ) { + $return[ $this->threads[ $index ] ] = $chunk; + } + } $this->set_queue( $return ); return $return; } + /** + * Maybe stop the queue. + */ + public function stop_maybe() { + $status = $this->get_queue_status(); + if ( empty( $status['pending'] ) ) { + $this->stop_queue(); + } + } + /** * Stop the queue by removing the started flag. */ @@ -265,26 +307,77 @@ public function stop_queue() { $this->set_queue( $queue ); } - wp_unschedule_hook( 'cloudinary_run_queue' ); + wp_unschedule_hook( 'cloudinary_resume_queue' ); } /** * Start the queue by setting the started flag. * - * @return void + * @return array */ public function start_queue() { - $queue = $this->get_queue(); - $queue['started'] = current_time( 'timestamp' ); + $queue = $this->get_queue(); if ( ! empty( $queue['processing'] ) ) { - $queue['pending'] = array_merge( $queue['pending'], $queue['processing'] ); + // In case it stopped mid process, push back to the first thread. + $queue['thread_0'] = array_merge( $queue['thread_0'], $queue['processing'] ); + } + // Count how many are pending. + $status = $this->get_queue_status(); + if ( empty( $status['pending'] ) ) { + // Dont start if theres nothing pending. + return $status; } + // Mark as started. + $queue['started'] = current_time( 'timestamp' ); + $queue['last_update'] = current_time( 'timestamp' ); + $this->set_queue( $queue ); - $instant = microtime( true ); - wp_unschedule_hook( 'cloudinary_run_queue' ); - wp_schedule_single_event( $instant, 'cloudinary_run_queue' ); - spawn_cron( $instant ); + foreach ( $this->threads as $thread ) { + if ( ! empty( $queue[ $thread ] ) ) { + $this->start_thread( $thread ); + sleep( 2 ); // Slight pause to prevent server overload. + } + } + $this->schedule_resume(); + + return $status; + } + + /** + * Start a thread to process. + * + * @param int $thread Thread ID. + */ + public function start_thread( $thread ) { + + $this->plugin->components['api']->background_request( 'queue', array( 'thread' => $thread ) ); + } + + + /** + * Get a threads queue. + * + * @param int $thread Thread ID. + * + * @return array + */ + public function get_thread_queue( $thread ) { + $queue = $this->get_queue(); + $return = array(); + if ( in_array( $thread, $this->threads, true ) && ! empty( $queue[ $thread ] ) ) { + $return = $queue[ $thread ]; + } + + return $return; + } + + /** + * Schedule a resume queue check. + */ + protected function schedule_resume() { + $now = current_time( 'timestamp' ); + wp_schedule_single_event( $now + $this->cron_frequency, 'cloudinary_resume_queue' ); } /** @@ -294,13 +387,29 @@ public function start_queue() { * @return void */ public function maybe_resume_queue() { - $now = current_time( 'timestamp' ); - $queue = $this->get_queue(); + $stopped = array(); + if ( $this->is_running() ) { + // Check each thread. + foreach ( $this->threads as $thread ) { + if ( ! $this->thread_running( $thread ) ) { + // Possible that thread has stopped. + $stopped[] = $thread; + } + } - if ( $now - $queue['last_update'] > $this->cron_start_offset && $this->is_running() ) { - $this->plugin->components['api']->background_request( 'process', array() ); + if ( count( $stopped ) === count( $this->threads ) ) { + // All threads have stopped. Stop Queue to prevent overload in case of a slow sync. + $this->stop_queue(); + sleep( 5 ); // give it 5 seconds to allow the stop and maybe threads to catchup. + // Start a new sync. + $this->start_queue(); + } elseif ( ! empty( $stopped ) ) { + // Just start the threads that have stopped. + array_map( array( $this, 'start_thread' ), $stopped ); + $this->schedule_resume(); + } else { + $this->schedule_resume(); + } } - - wp_schedule_single_event( $now + $this->cron_frequency, 'cloudinary_resume_queue' ); } } 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 f9d8c13c4..49aee761f 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 @@ -77,10 +77,6 @@ public function __construct( \Cloudinary\Plugin $plugin, $enabled = false, $push * Register any hooks that this component needs. */ private function register_hooks() { - // Add action to upload. - add_action( 'add_attachment', array( $this, 'push_on_upload' ), 10 ); - // Action Cloudinary id for on-demand upload sync. - // add_action( 'cloudinary_id', array( $this, 'prep_on_demand_upload' ), 9, 2 ); // Hook into auto upload sync. add_filter( 'cloudinary_on_demand_sync_enabled', array( $this, 'auto_sync_enabled' ), 10, 2 ); // Handle bulk and inline actions. @@ -183,24 +179,6 @@ public function auto_sync_enabled( $enabled, $post_id ) { return $enabled; } - /** - * Push new attachment to Cloudinary on upload. - * - * @param int $post_id The post. - */ - public function push_on_upload( $post_id ) { - - // Only if this is a media file and feature is enabled. - if ( $this->plugin->components['media']->is_media( $post_id ) && apply_filters( 'cloudinary_upload_sync_enabled', $this->enabled ) ) { - // Lets do the background upload to keep the upload window as fast as possible. - update_post_meta( $post_id, '_cloudinary_pending', time() ); // Make sure it doesn't get resynced. - $params = array( - 'attachment_ids' => array( $post_id ), - ); - $this->plugin->components['api']->background_request( 'process', $params ); - } - } - /** * Setup this component. */ @@ -214,38 +192,6 @@ public function setup() { $this->register_hooks(); } - /** - * Prepare an attachment without a cloudinary id, for background, on-demand push. - * - * @param string|bool $cloudinary_id The public ID for a cloudinary asset. - * @param int $attachment_id The local attachment ID. - * - * @return string - */ - public function prep_on_demand_upload( $cloudinary_id, $attachment_id ) { - $attachment_id = intval( $attachment_id ); - if ( $attachment_id && false === $cloudinary_id ) { - // Check that this has not already been prepared for upload. - if ( ! $this->is_pending( $attachment_id ) && apply_filters( 'cloudinary_on_demand_sync_enabled', $this->enabled, $attachment_id ) ) { - $max_size = ( wp_attachment_is_image( $attachment_id ) ? 'max_image_size' : 'max_video_size' ); - $file = get_attached_file( $attachment_id ); - // Get the file size to make sure it can exist in cloudinary. - if ( ! empty( $this->plugin->components['connect']->usage[ $max_size ] ) && file_exists( $file ) && filesize( $file ) < $this->plugin->components['connect']->usage[ $max_size ] ) { - $this->add_to_sync( $attachment_id ); - } else { - // Check if the src is a url. - $file = get_post_meta( $attachment_id, '_wp_attached_file', true ); - if ( $this->plugin->components['media']->is_cloudinary_url( $file ) ) { - // Download sync. - $this->add_to_sync( $attachment_id ); - } - } - } - } - - return $cloudinary_id; - } - /** * Upload an asset to Cloudinary. * @@ -340,27 +286,6 @@ public function update_breakpoints( $attachment_id, $breakpoints ) { } } - /** - * Prep an attachment for upload. - * - * @param int $attachment_id The attachment ID to prep for upload. - */ - public function prep_upload( $attachment_id ) { - $max_size = ( wp_attachment_is_image( $attachment_id ) ? 'max_image_size' : 'max_video_size' ); - $file = get_attached_file( $attachment_id ); - // Get the file size to make sure it can exist in cloudinary. - if ( file_exists( $file ) && filesize( $file ) < $this->plugin->components['connect']->usage[ $max_size ] ) { - $this->add_to_sync( $attachment_id ); - } else { - // Check if the src is a url. - $file = get_post_meta( $attachment_id, '_wp_attached_file', true ); - if ( $this->plugin->components['media']->is_cloudinary_url( $file ) ) { - // Download sync. - $this->add_to_sync( $attachment_id ); - } - } - } - /** * Trigger an update on content that contains the same attachment ID to allow filters to capture and process. * From ced6eb8f941fa20d22723f6a539ee86f3ee55075 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 5 Aug 2020 11:04:13 +0200 Subject: [PATCH 21/64] add threading to on demand sync --- .../php/class-sync.php | 11 ++++++----- .../php/sync/class-push-sync.php | 2 +- .../php/sync/class-sync-queue.php | 18 +++++++++--------- 3 files changed, 16 insertions(+), 15 deletions(-) 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 1dc21332b..bf7d44d77 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 @@ -755,13 +755,14 @@ public function set_signature_item( $attachment_id, $type, $value = null ) { public function init_background_upload() { if ( ! empty( $this->to_sync ) ) { - $threads = ceil( count( $this->to_sync ) / 3 ); // Max of 3 threads to prevent server overload. - $chunks = array_chunk( $this->to_sync, $threads ); - foreach ( $chunks as $thread ) { + $threads = $this->managers['push']->queue->threads; + $chunk_size = ceil( count( $this->to_sync ) / count( $threads ) ); // Max of 3 threads to prevent server overload. + $chunks = array_chunk( $this->to_sync, $chunk_size ); + foreach ( $chunks as $key => $ids ) { $params = array( - 'process_key' => uniqid(), + 'process_key' => uniqid( $threads[ $key ] ), ); - set_transient( $params['process_key'], $thread, 120 ); + set_transient( $params['process_key'], $ids, 120 ); $this->plugin->components['api']->background_request( 'process', $params ); } } 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 5c693427e..a4350ad9d 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 @@ -65,7 +65,7 @@ class Push_Sync { * * @var \Cloudinary\Sync\Sync_Queue */ - protected $queue; + public $queue; /** * Push_Sync constructor. diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php index cbc960748..c4825008a 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php @@ -244,15 +244,15 @@ public function build_queue() { 'posts_per_page' => 1000, // phpcs:ignore 'fields' => 'ids', 'meta_query' => array( // phpcs:ignore - 'relation' => 'AND', - array( - 'key' => Sync::META_KEYS['sync_error'], - 'compare' => 'NOT EXISTS', - ), - array( - 'key' => Sync::META_KEYS['public_id'], - 'compare' => 'NOT EXISTS', - ), + 'relation' => 'AND', + array( + 'key' => Sync::META_KEYS['sync_error'], + 'compare' => 'NOT EXISTS', + ), + array( + 'key' => Sync::META_KEYS['public_id'], + 'compare' => 'NOT EXISTS', + ), ), 'ignore_sticky_posts' => false, 'no_found_rows' => true, From e8f0002816582e2ce75dd8504693b9b5aaab816d Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 5 Aug 2020 11:30:03 +0200 Subject: [PATCH 22/64] add correct public_id sync method --- .../php/class-sync.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 bf7d44d77..be6ab7a66 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 @@ -394,7 +394,7 @@ public function setup_sync_base_struct() { }, 'sync' => array( 'priority' => 20, - 'callback' => array( $this->managers['push'], 'push_attachments' ), // Rename + 'callback' => array( $this->managers['media']->upgrade, 'convert_cloudinary_version' ), // Rename ), 'status' => array( 'state' => 'meta', From 5b8ce68b9d1a179959e4ad7be519f50b992aa731 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 5 Aug 2020 11:45:07 +0200 Subject: [PATCH 23/64] add correct icon and better tocken for background push --- .../php/class-sync.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 be6ab7a66..6d06d2e0f 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 @@ -397,7 +397,7 @@ public function setup_sync_base_struct() { 'callback' => array( $this->managers['media']->upgrade, 'convert_cloudinary_version' ), // Rename ), 'status' => array( - 'state' => 'meta', + 'state' => 'info syncing', 'note' => __( 'Updating metadata', 'cloudinary' ), ), ), @@ -758,9 +758,10 @@ public function init_background_upload() { $threads = $this->managers['push']->queue->threads; $chunk_size = ceil( count( $this->to_sync ) / count( $threads ) ); // Max of 3 threads to prevent server overload. $chunks = array_chunk( $this->to_sync, $chunk_size ); + $token = uniqid(); foreach ( $chunks as $key => $ids ) { $params = array( - 'process_key' => uniqid( $threads[ $key ] ), + 'process_key' => $token . '-' . $threads[ $key ], ); set_transient( $params['process_key'], $ids, 120 ); $this->plugin->components['api']->background_request( 'process', $params ); From cd7ceb58d736decefe71282b3394a6bb5705580b Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 5 Aug 2020 11:49:29 +0200 Subject: [PATCH 24/64] typos and text update --- .../php/class-sync.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6d06d2e0f..755469acc 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 @@ -342,7 +342,7 @@ public function setup_sync_base_struct() { ), 'status' => array( 'state' => 'info syncing', - 'note' => __( 'Upgrading from Previous version', 'cloudinary' ), + 'note' => __( 'Upgrading from previous version', 'cloudinary' ), ), ), 'download' => array( @@ -381,7 +381,7 @@ public function setup_sync_base_struct() { 'status' => array( 'state' => 'info syncing', 'note' => function () { - return sprintf( __( 'Moving to folder %s.', 'cloudinary' ), $this->managers['media']->get_cloudinary_folder() ); + return sprintf( __( 'Copying to folder %s.', 'cloudinary' ), untrailingslashit( $this->managers['media']->get_cloudinary_folder() ) ); }, ), ), From 6eca1fb307fcdeea898566ec0a884f7db429ab67 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 5 Aug 2020 12:16:59 +0200 Subject: [PATCH 25/64] fix cookie issue and make push only hit the file --- .../php/class-rest-api.php | 25 ++++++++++--------- .../php/sync/class-upload-sync.php | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-rest-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-rest-api.php index c857cc0dd..e8663349f 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-rest-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-rest-api.php @@ -73,20 +73,21 @@ public function background_request( $endpoint, $params, $method = 'POST' ) { if ( is_user_logged_in() ) { // Setup cookie. $logged_cookie = wp_parse_auth_cookie( '', 'logged_in' ); - array_pop( $logged_cookie ); // remove the scheme. + if ( ! empty( $logged_cookie ) ) { + array_pop( $logged_cookie ); // remove the scheme. - // Add logged in cookie to request. - $args['cookies'] = array( - new \WP_Http_Cookie( - array( - 'name' => LOGGED_IN_COOKIE, - 'value' => implode( '|', $logged_cookie ), - 'expires' => '+ 1 min', // Expire after a min only. + // Add logged in cookie to request. + $args['cookies'] = array( + new \WP_Http_Cookie( + array( + 'name' => LOGGED_IN_COOKIE, + 'value' => implode( '|', $logged_cookie ), + 'expires' => '+ 1 min', // Expire after a min only. + ), + $url ), - $url - ), - ); - + ); + } } $args['headers']['X-WP-Nonce'] = $params['nonce']; 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 49aee761f..d049b186f 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 @@ -148,7 +148,7 @@ public function handle_bulk_actions( $location, $action, $post_ids ) { switch ( $action ) { case 'cloudinary-push' : foreach ( $post_ids as $post_id ) { - $this->media->delete_post_meta( $post_id, Sync::META_KEYS['signature'] ); + $this->sync->set_signature_item( $post_id, 'file', '' ); $this->sync->add_to_sync( $post_id ); } break; From 5250cdf585b3e48d9a3453d0cbb482eb20918593 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 5 Aug 2020 12:28:00 +0200 Subject: [PATCH 26/64] add docs --- .../php/class-sync.php | 7 +++++++ 1 file changed, 7 insertions(+) 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 755469acc..da3e69585 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 @@ -221,6 +221,13 @@ public function can_sync( $attachment_id, $type = 'file' ) { return apply_filters( 'cloudinary_can_sync_asset', $can, $attachment_id, $type ); } + /** + * Get the last version this asset was synced with. + * + * @param int $attachment_id The attachment ID. + * + * @return mixed + */ public function get_sync_version( $attachment_id ) { $version = $this->managers['media']->get_post_meta( $attachment_id, self::META_KEYS['plugin_version'], true ); From 9a91202082642f3dec6cae0310dfbf50b3e699b9 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 07:41:38 +0200 Subject: [PATCH 27/64] address issues --- .../php/class-connect.php | 4 +- .../php/class-media.php | 46 +++++++++---------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-connect.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-connect.php index 726e915b4..32d01eda1 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-connect.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-connect.php @@ -259,10 +259,10 @@ public function get_credentials() { /** * Get the cloud name if set. * - * @return bool|mixed + * @return string|null */ public function get_cloud_name() { - return $this->credentials['cloud_name'] ? $this->credentials['cloud_name'] : false; + return $this->credentials['cloud_name'] ? $this->credentials['cloud_name'] : null; } /** diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 7dc691ceb..8dddc9a06 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -697,11 +697,11 @@ public function has_public_id( $attachment_id ) { * * @param int $attachment_id The Attachment ID. * - * @return string A cloudinary id. + * @return string|null */ public function get_cloudinary_id( $attachment_id ) { - $public_id = false; + $public_id = null; // A cloudinary_id is a public_id with a file extension. if ( $this->has_public_id( $attachment_id ) ) { $public_id = $this->get_public_id( $attachment_id ); @@ -1378,10 +1378,10 @@ public function delete_post_meta( $post_id, $key ) { */ public function get_breakpoint_options( $attachment_id ) { // Add breakpoints if we have an image. - $breakpoints = array(); - $has_breakpoints = $this->plugin->config['settings']['global_transformations']['enable_breakpoints']; + $breakpoints = array(); + $settings = $this->plugin->config['settings']['global_transformations']; - if ( 'off' !== $has_breakpoints && wp_attachment_is_image( $attachment_id ) ) { + if ( 'off' !== $settings['enable_breakpoints'] && wp_attachment_is_image( $attachment_id ) ) { $meta = wp_get_attachment_metadata( $attachment_id ); // Get meta image size if non exists. if ( empty( $meta ) ) { @@ -1390,27 +1390,25 @@ public function get_breakpoint_options( $attachment_id ) { $meta['width'] = $imagesize[0] ? $imagesize[0] : 0; } $max_width = $this->get_max_width(); - $settings = $this->plugin->config['settings']; // Add breakpoints request options. - if ( ! empty( $settings['global_transformations']['enable_breakpoints'] ) ) { - $breakpoint_options = array( - 'create_derived' => true, - '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' => $settings['global_transformations']['min_width'], - ); - $transformations = $this->get_transformation_from_meta( $attachment_id ); - if ( ! empty( $transformations ) ) { - $breakpoints['transformation'] = Api::generate_transformation_string( $transformations ); - } - $breakpoints = array( - 'public_id' => $this->get_public_id( $attachment_id ), - 'type' => 'upload', - 'responsive_breakpoints' => $breakpoint_options, - 'context' => $this->get_context_options( $attachment_id ), - ); + $breakpoint_options = array( + 'create_derived' => true, + 'bytes_step' => $settings['bytes_step'], + 'max_images' => $settings['breakpoints'], + 'max_width' => $meta['width'] < $max_width ? $meta['width'] : $max_width, + 'min_width' => $settings['min_width'], + ); + $transformations = $this->get_transformation_from_meta( $attachment_id ); + if ( ! empty( $transformations ) ) { + $breakpoints['transformation'] = Api::generate_transformation_string( $transformations ); } + $breakpoints = array( + 'public_id' => $this->get_public_id( $attachment_id ), + 'type' => 'upload', + 'responsive_breakpoints' => $breakpoint_options, + 'context' => $this->get_context_options( $attachment_id ), + ); + } return $breakpoints; From 6981486ab41aaf8731a445fc15433a2cc8879637 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 07:42:11 +0200 Subject: [PATCH 28/64] revise and clarify sync types and add registration method --- .../php/class-sync.php | 213 ++++++++---------- 1 file changed, 98 insertions(+), 115 deletions(-) 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 da3e69585..d4cbf4706 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 @@ -175,7 +175,7 @@ public function generate_signature( $attachment_id, $cache = true ) { } else { $return = $this->sync_base( $attachment_id ); if ( ! is_wp_error( $return ) ) { - $return = array_map( + $return = array_map( function ( $item ) { if ( is_array( $item ) ) { $item = wp_json_encode( $item ); @@ -185,6 +185,7 @@ function ( $item ) { }, $return ); + // Add to signature cache. $signatures[ $attachment_id ] = $return; } } @@ -240,7 +241,7 @@ public function get_sync_version( $attachment_id ) { * @param int $attachment_id The attachment ID. * @param bool $cached Flag to specify if a cached signature is to be used or build a new one. * - * @return array|bool + * @return array */ public function get_signature( $attachment_id, $cached = true ) { static $signatures = array(); // Cache signatures already fetched. @@ -335,22 +336,61 @@ public function add_suffix_maybe( $public_id, $attachment_id, $suffix = null ) { } /** - * Get the sync_setup_base of part callbacks. + * Register a new sync type. + * + * @param string $type Sync type key. Must not exceed 20 characters and may + * only contain lowercase alphanumeric characters, dashes, + * and underscores. See sanitize_key() + * @param array $structure { + * Array of arguments for registering a sync type. + * + * @type callable $generate Callback method that generates the values to be used to sign a state. + * Returns a string or array. + * + * @type callable $validate Optional Callback method that validates the need to have the sync type applied. + * returns Bool. + * + * @type int $priority Priority in which the type takes place. Lower is higher priority. + * i.e a download should happen before an upload so download is lower in the chain. + * + * @type callable $sync Callback method that handles the sync. i.e uploads the file, adds meta data, etc.. + * + * @type string $state State class to be added to the status icon in media library. + * + * @type string|callback $note The status text displayed next to a syncing asset in the media library. + * Can be a callback if the note needs to be dynamic. see type folder. + * + * } + */ + public function register_sync_type( $type, $structure ) { + + // Apply a default to ensure parts exist. + $default = array( + 'generate' => '__return_null', + 'validate' => null, + 'priority' => 50, + 'sync' => '__return_null', + 'state' => 'sync', + 'note' => __( 'Synchronizing asset with Cloudinary', 'cloudinary' ), + ); + + $this->sync_base_struct[ $type ] = wp_parse_args( $structure, $default ); + } + + /** + * Get built-in structures that form an assets entire sync state. This holds methods for building signatures for each state of synchronization. + * These can be extended via 3rd parties by adding to the structures with custom types and generation and sync methods. */ public function setup_sync_base_struct() { $base_struct = array( 'upgrade' => array( - 'generate' => array( $this, 'get_sync_version' ), + 'generate' => array( $this, 'get_sync_version' ), // Method to generate a signature. Which 'validate' => array( $this, 'been_synced' ), - 'sync' => array( - 'priority' => 0, - 'callback' => array( $this->managers['media']->upgrade, 'convert_cloudinary_version' ), - ), - 'status' => array( - 'state' => 'info syncing', - 'note' => __( 'Upgrading from previous version', 'cloudinary' ), - ), + 'priority' => 0, + 'sync' => array( $this->managers['media']->upgrade, 'convert_cloudinary_version' ), + 'state' => 'info syncing', + 'note' => __( 'Upgrading from previous version', 'cloudinary' ), ), 'download' => array( 'generate' => '__return_false', @@ -359,38 +399,26 @@ public function setup_sync_base_struct() { return ! file_exists( $file ); }, - 'sync' => array( - 'priority' => 1, - 'callback' => array( $this->managers['download'], 'download_asset' ), - ), - 'status' => array( - 'state' => 'info downloading', - 'note' => __( 'Downloading from Cloudinary', 'cloudinary' ), - ), + 'priority' => 1, + 'sync' => array( $this->managers['download'], 'download_asset' ), + 'state' => 'info downloading', + 'note' => __( 'Downloading from Cloudinary', 'cloudinary' ), ), 'file' => array( 'generate' => 'get_attached_file', - 'sync' => array( - 'priority' => 5, - 'callback' => array( $this->managers['upload'], 'upload_asset' ), - ), - 'status' => array( - 'state' => 'info', - 'note' => __( 'Uploading to Cloudinary', 'cloudinary' ), - ), + 'priority' => 5, + 'sync' => array( $this->managers['upload'], 'upload_asset' ), + 'state' => 'info', + 'note' => __( 'Uploading to Cloudinary', 'cloudinary' ), ), 'folder' => array( 'generate' => array( $this->managers['media'], 'get_cloudinary_folder' ), - 'sync' => array( - 'priority' => 10, - 'callback' => array( $this->managers['upload'], 'upload_asset' ), - ), - 'status' => array( - 'state' => 'info syncing', - 'note' => function () { - return sprintf( __( 'Copying to folder %s.', 'cloudinary' ), untrailingslashit( $this->managers['media']->get_cloudinary_folder() ) ); - }, - ), + 'priority' => 10, + 'sync' => array( $this->managers['upload'], 'upload_asset' ), + 'state' => 'info syncing', + 'note' => function () { + return sprintf( __( 'Copying to folder %s.', 'cloudinary' ), untrailingslashit( $this->managers['media']->get_cloudinary_folder() ) ); + }, ), 'public_id' => array( 'generate' => array( $this->managers['media'], 'get_public_id' ), @@ -399,58 +427,38 @@ public function setup_sync_base_struct() { return false === $public_id; }, - 'sync' => array( - 'priority' => 20, - 'callback' => array( $this->managers['media']->upgrade, 'convert_cloudinary_version' ), // Rename - ), - 'status' => array( - 'state' => 'info syncing', - 'note' => __( 'Updating metadata', 'cloudinary' ), - ), + 'priority' => 20, + 'sync' => array( $this->managers['media']->upgrade, 'convert_cloudinary_version' ), // Rename + 'state' => 'info syncing', + 'note' => __( 'Updating metadata', 'cloudinary' ), ), 'suffix' => array( 'generate' => array( $this, 'get_suffix_maybe' ), - 'sync' => array( - 'priority' => 10, - 'callback' => array( $this->managers['upload'], 'upload_asset' ), - ), - 'status' => array( - 'state' => 'uploading', - 'note' => __( 'Creating unique version', 'cloudinary' ), - ), + 'priority' => 10, + 'sync' => array( $this->managers['upload'], 'upload_asset' ), + 'state' => 'uploading', + 'note' => __( 'Creating unique version', 'cloudinary' ), ), 'breakpoints' => array( 'generate' => array( $this->managers['media'], 'get_breakpoint_options' ), - 'sync' => array( - 'priority' => 25, - 'callback' => array( $this->managers['upload'], 'explicit_update' ), - ), - 'status' => array( - 'state' => 'info syncing', - 'note' => __( 'Updating breakpoints', 'cloudinary' ), - ), + 'priority' => 25, + 'sync' => array( $this->managers['upload'], 'explicit_update' ), + 'state' => 'info syncing', + 'note' => __( 'Updating breakpoints', 'cloudinary' ), ), 'options' => array( 'generate' => array( $this->managers['media'], 'get_upload_options' ), - 'sync' => array( - 'priority' => 30, - 'callback' => array( $this->managers['upload'], 'context_update' ), - ), - 'status' => array( - 'state' => 'info syncing', - 'note' => __( 'Updating metadata', 'cloudinary' ), - ), + 'priority' => 30, + 'sync' => array( $this->managers['upload'], 'context_update' ), + 'state' => 'info syncing', + 'note' => __( 'Updating metadata', 'cloudinary' ), ), 'cloud_name' => array( 'generate' => array( $this->managers['connect'], 'get_cloud_name' ), - 'sync' => array( - 'priority' => 5, - 'callback' => array( $this->managers['upload'], 'upload_asset' ), - ), - 'status' => array( - 'state' => 'uploading', - 'note' => __( 'Uploading to new cloud name.', 'cloudinary' ), - ), + 'priority' => 5, + 'sync' => array( $this->managers['upload'], 'upload_asset' ), + 'state' => 'uploading', + 'note' => __( 'Uploading to new cloud name.', 'cloudinary' ), ), ); @@ -463,36 +471,10 @@ public function setup_sync_base_struct() { */ $base_struct = apply_filters( 'cloudinary_sync_base_struct', $base_struct ); - // Apply a default to ensure parts exist.s - $structs = array_map( - function ( $component ) { - $sync_default = array( - 'priority' => 50, - 'callback' => '__return_null', - ); - $status_default = array( - 'state' => 'sync', - 'note' => __( 'Syncronizing asset with Cloudinary', 'cloudinary' ), - ); - $default = array( - 'generate' => '__return_null', - 'sync' => array(), - 'status' => array(), - ); - - // Ensure correct struct. - $component = wp_parse_args( $component, $default ); - $component['sync'] = wp_parse_args( $component['sync'], $sync_default ); - $component['sync']['priority'] = is_int( $component['sync']['priority'] ) ? (int) $component['sync']['priority'] : 50; - $component['status'] = wp_parse_args( $component['status'], $status_default ); - - - return $component; - }, - $base_struct - ); - - $this->sync_base_struct = $structs; + // Register each sync type. + foreach ( $base_struct as $type => $structure ) { + $this->register_sync_type( $type, $structure ); + } } /** @@ -502,8 +484,8 @@ public function setup_sync_types() { $sync_types = array(); foreach ( $this->sync_base_struct as $type => $struct ) { - if ( is_callable( $struct['sync']['callback'] ) ) { - $sync_types[ $type ] = $struct['sync']['priority']; + if ( is_callable( $struct['sync'] ) ) { + $sync_types[ $type ] = $struct['priority']; } } @@ -521,8 +503,8 @@ public function setup_sync_types() { */ public function get_sync_method( $type ) { $method = false; - if ( isset( $this->sync_base_struct[ $type ]['sync']['callback'] ) ) { - $method = $this->sync_base_struct[ $type ]['sync']['callback']; + if ( isset( $this->sync_base_struct[ $type ]['sync'] ) ) { + $method = $this->sync_base_struct[ $type ]['sync']; } return $method; @@ -641,7 +623,8 @@ public function filter_status( $status, $attachment_id ) { $status['state'] = 'error'; $status['note'] = $log[ $sync_type ]->get_error_message(); } else { - $status = $this->sync_base_struct[ $sync_type ]['status']; + $status['state'] = $this->sync_base_struct[ $sync_type ]['state']; + $status['note'] = $this->sync_base_struct[ $sync_type ]['note']; if ( is_callable( $status['note'] ) ) { $status['note'] = call_user_func( $status['note'] ); } @@ -720,11 +703,11 @@ public function add_to_sync( $attachment_id ) { public function update_signature( $attachment_id, $type ) { $expecting = $this->generate_signature( $attachment_id ); - $current_sync_method = $this->sync_base_struct[ $type ]['sync']['callback']; + $current_sync_method = $this->sync_base_struct[ $type ]['sync']; // Go over all other types that share the same sync method and include them here. foreach ( $this->sync_base_struct as $sync_type => $struct ) { - if ( $struct['sync']['callback'] === $current_sync_method ) { + if ( $struct['sync'] === $current_sync_method ) { $this->set_signature_item( $attachment_id, $sync_type, $expecting[ $sync_type ] ); } } From c8029977806c33baa3cb27f671d16056656f4f92 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 09:23:11 +0200 Subject: [PATCH 29/64] use null over false --- .../php/class-media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 8dddc9a06..0fee1e51d 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -1151,7 +1151,7 @@ public function media_column_value( $column_name, $attachment_id ) { 'note' => esc_html__( 'Not Synced', 'cloudinary' ), ); add_filter( 'cloudinary_flag_sync', '__return_true' ); - if ( false === $this->cloudinary_id( $attachment_id ) ) { + if ( ! $this->cloudinary_id( $attachment_id ) ) { // If false, lets check why by seeing if the file size is too large. $file = get_attached_file( $attachment_id ); // Get the file size to make sure it can exist in cloudinary. $max_size = ( wp_attachment_is_image( $attachment_id ) ? 'image_max_size_bytes' : 'video_max_size_bytes' ); From 538c6d399ae7f866daca6429c6f423b53d8dfc16 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 09:23:49 +0200 Subject: [PATCH 30/64] simplify methods --- .../php/class-sync.php | 151 +++++++++++------- .../php/sync/class-push-sync.php | 3 +- .../php/sync/class-upload-sync.php | 2 +- 3 files changed, 98 insertions(+), 58 deletions(-) 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 d4cbf4706..e55b3ae44 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 @@ -174,20 +174,8 @@ public function generate_signature( $attachment_id, $cache = true ) { $return = $signatures[ $attachment_id ]; } else { $return = $this->sync_base( $attachment_id ); - if ( ! is_wp_error( $return ) ) { - $return = array_map( - function ( $item ) { - if ( is_array( $item ) ) { - $item = wp_json_encode( $item ); - } - - return md5( $item ); - }, - $return - ); - // Add to signature cache. - $signatures[ $attachment_id ] = $return; - } + // Add to signature cache. + $signatures[ $attachment_id ] = $return; } return $return; @@ -391,6 +379,7 @@ public function setup_sync_base_struct() { 'sync' => array( $this->managers['media']->upgrade, 'convert_cloudinary_version' ), 'state' => 'info syncing', 'note' => __( 'Upgrading from previous version', 'cloudinary' ), + 'realtime' => true, ), 'download' => array( 'generate' => '__return_false', @@ -495,23 +484,68 @@ public function setup_sync_types() { } /** - * Get the method to do a sync for the specified type. + * Get a method from a sync type. + * + * @param string $type The sync type to get from. + * @param string $method The method to get from the sync type. + * + * @return callable|null + */ + public function get_sync_type_method( $type, $method ) { + $return = null; + if ( isset( $this->sync_base_struct[ $type ][ $method ] ) && is_callable( $this->sync_base_struct[ $type ][ $method ] ) ) { + $return = $this->sync_base_struct[ $type ][ $method ]; + } + + return $return; + } + + /** + * Run a sync method on and attachment_id. * - * @param $type + * @param string $type The sync type to run. + * @param string $method The method to run. + * @param int $attachment_id The attachment ID to run method against. * - * @return bool|mixed + * @return mixed */ - public function get_sync_method( $type ) { - $method = false; - if ( isset( $this->sync_base_struct[ $type ]['sync'] ) ) { - $method = $this->sync_base_struct[ $type ]['sync']; + public function run_sync_method( $type, $method, $attachment_id ) { + $return = null; + $run_method = $this->get_sync_type_method( $type, $method ); + if ( $run_method ) { + $return = call_user_func( $run_method, $attachment_id ); } - return $method; + return $return; } /** - * Get the core Sync Base. + * Generate a single sync type signature for an asset. + * + * @param string $type The sync type to run. + * @param int $attachment_id The attachment ID to run method against. + * + * @return mixed + */ + public function generate_type_signature( $type, $attachment_id ) { + $return = null; + $run_method = $this->get_sync_type_method( $type, 'generate' ); + if ( $run_method ) { + $value = call_user_func( $run_method, $attachment_id ); + if ( ! is_wp_error( $value ) ) { + if ( is_array( $value ) ) { + $value = wp_json_encode( $value ); + } + $return = md5( $value ); + } + } + + return $return; + } + + /** + * Prepares and asset for sync comparison by getting all sync types + * and running the generate methods for each type. * * @param int|\WP_Post $post The attachment to prepare. * @@ -524,13 +558,8 @@ public function sync_base( $post ) { } $return = array(); - foreach ( $this->sync_base_struct as $key => $struct ) { - if ( isset( $struct['generate'] ) ) { - $return[ $key ] = null; - if ( is_callable( $struct['generate'] ) ) { - $return[ $key ] = call_user_func( $struct['generate'], $post ); - } - } + foreach ( array_keys( $this->sync_types ) as $type ) { + $return[ $type ] = $this->generate_type_signature( $type, $post ); } /** @@ -557,7 +586,7 @@ public function sync_base( $post ) { public function maybe_prepare_sync( $attachment_id ) { $type = $this->get_sync_type( $attachment_id ); - if ( $this->can_sync( $attachment_id, $type ) ) { + if ( $type && $this->can_sync( $attachment_id, $type ) ) { $this->add_to_sync( $attachment_id ); } } @@ -568,13 +597,13 @@ public function maybe_prepare_sync( $attachment_id ) { * @param int $attachment_id The attachment ID. * @param bool $cached Flag to specify if a cached signature is to be used or build a new one. * - * @return mixed|string|\WP_Error + * @return string|null */ public function get_sync_type( $attachment_id, $cached = true ) { if ( ! $this->managers['media']->is_media( $attachment_id ) ) { - return false; // Ignore non media items. + return null; // Ignore non media items. } - $type = false; + $return = null; $required_signature = $this->generate_signature( $attachment_id, $cached ); $attachment_signature = $this->get_signature( $attachment_id, $cached ); if ( is_array( $required_signature ) ) { @@ -587,16 +616,35 @@ function ( $item, $key ) use ( $required_signature ) { ); $ordered = array_intersect_key( $this->sync_types, $sync_items ); if ( ! empty( $ordered ) ) { - $types = array_keys( $ordered ); - $type = array_shift( $types ); - // Validate that this sync type applied (for optional types like upgrade). - if ( isset( $this->sync_base_struct[ $type ]['validate'] ) && is_callable( $this->sync_base_struct[ $type ]['validate'] ) ) { - if ( ! call_user_func( $this->sync_base_struct[ $type ]['validate'], $attachment_id ) ) { - $this->set_signature_item( $attachment_id, $type ); - - return $this->get_sync_type( $attachment_id, $cached ); - } - } + $types = array_keys( $ordered ); + $type = array_shift( $types ); + $return = $this->validate_sync_type( $type, $attachment_id ); + } + } + + return $return; + } + + /** + * Validate the asset needs the sync type to be run, and generate a valid signature if not. + * + * @param string $type The sync type to validate. + * @param int $attachment_id The attachment ID to validate against. + * + * @return string|null + */ + public function validate_sync_type( $type, $attachment_id ) { + // Validate that this sync type applied (for optional types like upgrade). + if ( false === $this->run_sync_method( $type, 'validate', $attachment_id ) ) { + // If invalid, save the new signature + $this->set_signature_item( $attachment_id, $type ); + + $type = $this->get_sync_type( $attachment_id, false ); // Set cache to false to get the new signature. + } else { + // Check if this is a realtime process. + if ( ! empty( $this->sync_base_struct[ $type ]['realtime'] ) ) { + $this->run_sync_method( $type, 'sync', $attachment_id ); + $type = $this->get_sync_type( $attachment_id, false ); // Set cache to false to get the new signature. } } @@ -695,23 +743,20 @@ public function add_to_sync( $attachment_id ) { } /** - * Update a part of the signature based on type. + * Update signatures of types that match the specified types sync method. This prevents running the same method repeatedly. * * @param int $attachment_id The attachment ID. * @param string $type The type of sync. */ - public function update_signature( $attachment_id, $type ) { - - $expecting = $this->generate_signature( $attachment_id ); + public function sync_signature_by_type( $attachment_id, $type ) { $current_sync_method = $this->sync_base_struct[ $type ]['sync']; // Go over all other types that share the same sync method and include them here. foreach ( $this->sync_base_struct as $sync_type => $struct ) { if ( $struct['sync'] === $current_sync_method ) { - $this->set_signature_item( $attachment_id, $sync_type, $expecting[ $sync_type ] ); + $this->set_signature_item( $attachment_id, $sync_type ); } } - } /** @@ -729,11 +774,7 @@ public function set_signature_item( $attachment_id, $type, $value = null ) { // Set the specific value. if ( is_null( $value ) ) { // Generate a new value based on generator. - $new_value = call_user_func( $this->sync_base_struct[ $type ]['generate'], $attachment_id ); - if ( is_array( $new_value ) ) { - $new_value = wp_json_encode( $new_value ); - } - $value = md5( $new_value ); + $value = $this->generate_type_signature( $type, $attachment_id ); } $meta[ Sync::META_KEYS['cloudinary'] ][ Sync::META_KEYS['signature'] ][ $type ] = $value; wp_update_attachment_metadata( $attachment_id, $meta ); 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 a4350ad9d..add6cd69a 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 @@ -206,8 +206,7 @@ public function process_assets( $attachments = array() ) { // Loop prevention. break; } - $callback = $this->sync->get_sync_method( $type ); - $stat[ $attachment_id ][ $type ] = call_user_func( $callback, $attachment_id ); + $stat[ $attachment_id ][ $type ] = $this->sync->run_sync_method( $type, 'sync', $attachment_id ); } // remove pending. if ( $this->sync->is_pending( $attachment_id ) ) { 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 d049b186f..b7b9e3001 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 @@ -210,7 +210,7 @@ public function upload_asset( $attachment_id ) { // Set public_id. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $public_id ); // Update signature for all that use the same method. - $this->sync->update_signature( $attachment_id, $type ); + $this->sync->sync_signature_by_type( $attachment_id, $type ); // Update options and public_id as well (full sync) $this->sync->set_signature_item( $attachment_id, 'options' ); $this->sync->set_signature_item( $attachment_id, 'public_id' ); From 4524edfeabe39e904c4d4d0ab0b829d537a544e7 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 09:25:46 +0200 Subject: [PATCH 31/64] alignment --- .../php/class-media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 0fee1e51d..8dae48f3b 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -1081,7 +1081,7 @@ public function down_sync_asset() { } $transformations = $this->get_transformations_from_string( $url ); if ( ! empty( $transformations ) ) { - $sync_key .= wp_json_encode( $transformations ); + $sync_key .= wp_json_encode( $transformations ); $asset['transformations'] = $transformations; } // Check Format and url extension. From e79dd1707c5162d52bf2d90fe80646a8b4fe2ddb Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 09:45:20 +0200 Subject: [PATCH 32/64] address CR changes --- .../php/class-media.php | 8 ++++---- .../php/sync/class-sync-queue.php | 2 +- .../php/sync/class-upload-sync.php | 10 ++++------ 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 8dae48f3b..f9ee14d51 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -123,13 +123,13 @@ public function __construct( Plugin $plugin ) { /** * Check if the attachment is a media file. * - * @param int|\WP_Post $attachment The attachment to check. + * @param int $attachment_id The attachment ID to check. * * @return bool */ - public function is_media( $attachment ) { + public function is_media( $attachment_id ) { $is_media = false; - if ( 'attachment' === get_post_type( $attachment ) ) { + if ( 'attachment' === get_post_type( $attachment_id ) ) { /** * Filter the default Cloudinary Media Types. * @@ -138,7 +138,7 @@ public function is_media( $attachment ) { * @return array */ $media_types = apply_filters( 'cloudinary_media_types', array( 'image', 'video', 'audio' ) ); - $type = $this->get_media_type( $attachment ); + $type = $this->get_media_type( $attachment_id ); $is_media = in_array( $type, $media_types ); } diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php index c4825008a..ee65e2228 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-sync-queue.php @@ -181,7 +181,7 @@ public function mark( $id, $type = 'done' ) { public function is_running() { $queue = $this->get_queue(); - return empty( $queue['started'] ) ? false : true; + return ! empty( $queue['started'] ); } /** 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 b7b9e3001..12000bb8e 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 @@ -116,17 +116,15 @@ function add_inline_action( $actions, $post ) { $actions['cloudinary-push'] = sprintf( '%s', $action_url, - /* translators: %s: Attachment title. */ - esc_attr( __( 'Push to Cloudinary' ) ), - __( 'Push to Cloudinary', 'cloudinary' ) + esc_attr__( 'Push to Cloudinary', 'cloudinary' ), + esc_html__( 'Push to Cloudinary', 'cloudinary' ) ); } else { $actions['cloudinary-push'] = sprintf( '%s', $action_url, - /* translators: %s: Attachment title. */ - esc_attr( __( 'Re-sync to Cloudinary', 'cloudinary' ) ), - __( 'Re-sync to Cloudinary', 'cloudinary' ) + esc_attr__( 'Re-sync to Cloudinary', 'cloudinary' ), + esc_html__( 'Re-sync to Cloudinary', 'cloudinary' ) ); } } From 334e8b499007e0bd48f8ce9ea00d258c2f1bec91 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 09:51:52 +0200 Subject: [PATCH 33/64] kee nulls consistent --- .../php/class-media.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index f9ee14d51..86ffecc2b 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -585,11 +585,11 @@ public function apply_default_transformations( array $transformations, $type = ' */ public function cloudinary_url( $attachment_id, $size = array(), $transformations = array(), $cloudinary_id = null, $overwrite_transformations = false, $clean = false ) { - if ( empty( $cloudinary_id ) ) { + if ( ! ( $cloudinary_id ) ) { $cloudinary_id = $this->cloudinary_id( $attachment_id ); - } - if ( false === $cloudinary_id ) { - return false; + if ( ! $cloudinary_id ) { + return null; + } } if ( empty( $transformations ) ) { $transformations = $this->get_transformation_from_meta( $attachment_id ); @@ -723,12 +723,12 @@ public function get_cloudinary_id( $attachment_id ) { * * @param int $attachment_id The ID to get Cloudinary id for. * - * @return string|bool the ID or false if not existing. + * @return string|null the ID or false if not existing. */ public function cloudinary_id( $attachment_id ) { if ( ! $this->is_media( $attachment_id ) ) { - return false; + return null; } // Return cached ID if we've already gotten it before. if ( isset( $this->cloudinary_ids[ $attachment_id ] ) ) { From 0c766ee27dcda4d84fa596a97fc7115a8455dc38 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 10:12:27 +0200 Subject: [PATCH 34/64] keep nulls consistent --- .../php/class-media.php | 4 ++-- .../php/media/class-filter.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 86ffecc2b..5275afa29 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -492,7 +492,7 @@ public function get_transformations_from_string( $str, $type = 'image' ) { public function attachment_url( $url, $attachment_id ) { if ( ! doing_action( 'wp_insert_post_data' ) && false === $this->in_downsize ) { $cloudinary_id = $this->cloudinary_id( $attachment_id ); - if ( false !== $cloudinary_id ) { + if ( ! $cloudinary_id ) { $url = $this->cloudinary_url( $attachment_id ); } // Previous v1. @@ -782,7 +782,7 @@ public function filter_downsize( $image, $attachment_id, $size ) { $cloudinary_id = $this->cloudinary_id( $attachment_id ); - if ( false !== $cloudinary_id ) { + if ( ! $cloudinary_id ) { $this->in_downsize = true; $intermediate = image_get_intermediate_size( $attachment_id, $size ); if ( is_array( $intermediate ) ) { diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php index 5119b4152..77e27703f 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php @@ -381,7 +381,7 @@ public function filter_out_local( $content ) { public function filter_attachment_for_js( $attachment ) { $cloudinary_id = $this->media->get_cloudinary_id( $attachment['id'] ); - if ( false !== $cloudinary_id ) { + if ( ! $cloudinary_id ) { $transformations = array(); if ( ! empty( $attachment['transformations'] ) ) { @@ -421,7 +421,7 @@ public function filter_attachment_for_rest( $attachment ) { $cloudinary_id = $this->media->cloudinary_id( $attachment->data['id'] ); - if ( false !== $cloudinary_id ) { + if ( ! $cloudinary_id ) { $attachment->data['source_url'] = $this->media->cloudinary_url( $attachment->data['id'], false ); } From beeea6394bffb46fc730974a8dbda91c0891fd9c Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 10:17:18 +0200 Subject: [PATCH 35/64] keep nulls consistent --- .../php/class-media.php | 6 +++--- .../php/media/class-filter.php | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 5275afa29..db2b3e4ef 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -492,7 +492,7 @@ public function get_transformations_from_string( $str, $type = 'image' ) { public function attachment_url( $url, $attachment_id ) { if ( ! doing_action( 'wp_insert_post_data' ) && false === $this->in_downsize ) { $cloudinary_id = $this->cloudinary_id( $attachment_id ); - if ( ! $cloudinary_id ) { + if ( $cloudinary_id ) { $url = $this->cloudinary_url( $attachment_id ); } // Previous v1. @@ -782,7 +782,7 @@ public function filter_downsize( $image, $attachment_id, $size ) { $cloudinary_id = $this->cloudinary_id( $attachment_id ); - if ( ! $cloudinary_id ) { + if ( $cloudinary_id ) { $this->in_downsize = true; $intermediate = image_get_intermediate_size( $attachment_id, $size ); if ( is_array( $intermediate ) ) { @@ -833,7 +833,7 @@ public function convert_url( $url, $attachment_id, $transformations = array(), $ */ public function image_srcset( $sources, $size_array, $image_src, $image_meta, $attachment_id ) { $cloudinary_id = $this->cloudinary_id( $attachment_id ); - if ( false === $cloudinary_id ) { + if ( ! $cloudinary_id ) { return $sources; // Return WordPress default sources. } // Get transformations from URL. diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php index 77e27703f..5c7416e30 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php @@ -381,7 +381,7 @@ public function filter_out_local( $content ) { public function filter_attachment_for_js( $attachment ) { $cloudinary_id = $this->media->get_cloudinary_id( $attachment['id'] ); - if ( ! $cloudinary_id ) { + if ( $cloudinary_id ) { $transformations = array(); if ( ! empty( $attachment['transformations'] ) ) { @@ -421,7 +421,7 @@ public function filter_attachment_for_rest( $attachment ) { $cloudinary_id = $this->media->cloudinary_id( $attachment->data['id'] ); - if ( ! $cloudinary_id ) { + if ( $cloudinary_id ) { $attachment->data['source_url'] = $this->media->cloudinary_url( $attachment->data['id'], false ); } From 108b0a2499b33eb585e89a6f22119728b06fb08d Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 12:44:19 +0200 Subject: [PATCH 36/64] better handling url file type fails --- .../php/connect/class-api.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php index b65d30b27..0a6fbd6e8 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php @@ -419,9 +419,14 @@ public function upload( $attachment_id, $args, $headers = array() ) { // Hook in flag to allow for non accessible URLS. if ( is_wp_error( $result ) ) { $error = $result->get_error_message(); - if ( false !== strpos( $error, 'ERR_DNS_FAIL' ) ) { + /** + * If there's an error and the file is a URL in the error message, + * it's likely due to CURL or the location does not support URL file attachments. + * In this case, we'll flag and disable it and try again with a local file. + */ + if ( empty( $disable_https_fetch ) && false !== strpos( $error, $args['file'] ) ) { // URLS are not remotely available, try again as a file. - set_transient( '_cld_disable_http_upload', true, 300 ); + set_transient( '_cld_disable_http_upload', true, DAY_IN_SECONDS ); // Remove URL file. unset( $args['file'] ); From 56245fdf833b736b824b1896cddc5b68b134bead Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 14:08:04 +0200 Subject: [PATCH 37/64] Add prep work for suffixing --- .../php/class-media.php | 25 ++++++++++- .../php/class-sync.php | 13 +++--- .../php/sync/class-upload-sync.php | 44 +++++++++++++++++++ 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index db2b3e4ef..3de228da2 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -661,7 +661,12 @@ public function upload_dir( $dirs ) { * @return string */ public function get_cloudinary_folder() { - return trailingslashit( $this->cloudinary_folder ); + $folder = ''; + if ( ! empty( $this->cloudinary_folder ) ) { + $folder = trailingslashit( $this->cloudinary_folder ); + } + + return $folder; } /** @@ -1426,6 +1431,7 @@ public function get_context_options( $attachment_id ) { $context_options = array( 'caption' => esc_attr( get_the_title( $attachment_id ) ), 'alt' => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ), + 'guid' => md5( get_the_guid( $attachment_id ) ), ); // Check if this asset is a folder sync. @@ -1448,6 +1454,23 @@ public function get_context_options( $attachment_id ) { return http_build_query( $context_options, null, '|' ); } + /** + * Check if an asset is folder synced. + * + * @param int $attachment_id The attachment ID. + * + * @return bool + */ + public function is_folder_synced( $attachment_id ) { + + $return = true; // By default all assets in WordPress will be synced. + if ( $this->sync->been_synced( $attachment_id ) ) { + $return = ! empty( $this->get_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true ) ); + } + + return $return; + } + /** * Get the media upload options as connected to Cloudinary. * 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 e55b3ae44..fda05bb12 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 @@ -395,13 +395,14 @@ public function setup_sync_base_struct() { ), 'file' => array( 'generate' => 'get_attached_file', - 'priority' => 5, + 'priority' => 5.1, 'sync' => array( $this->managers['upload'], 'upload_asset' ), 'state' => 'info', 'note' => __( 'Uploading to Cloudinary', 'cloudinary' ), ), 'folder' => array( 'generate' => array( $this->managers['media'], 'get_cloudinary_folder' ), + 'validate' => array( $this->managers['media'], 'is_folder_synced' ), 'priority' => 10, 'sync' => array( $this->managers['upload'], 'upload_asset' ), 'state' => 'info syncing', @@ -422,9 +423,9 @@ public function setup_sync_base_struct() { 'note' => __( 'Updating metadata', 'cloudinary' ), ), 'suffix' => array( - 'generate' => array( $this, 'get_suffix_maybe' ), - 'priority' => 10, - 'sync' => array( $this->managers['upload'], 'upload_asset' ), + 'generate' => array( $this, 'get_suffix' ), + 'priority' => 5.0, + 'sync' => array( $this->managers['upload'], 'add_suffix_maybe' ), 'state' => 'uploading', 'note' => __( 'Creating unique version', 'cloudinary' ), ), @@ -444,7 +445,7 @@ public function setup_sync_base_struct() { ), 'cloud_name' => array( 'generate' => array( $this->managers['connect'], 'get_cloud_name' ), - 'priority' => 5, + 'priority' => 5.5, 'sync' => array( $this->managers['upload'], 'upload_asset' ), 'state' => 'uploading', 'note' => __( 'Uploading to new cloud name.', 'cloudinary' ), @@ -474,7 +475,7 @@ public function setup_sync_types() { $sync_types = array(); foreach ( $this->sync_base_struct as $type => $struct ) { if ( is_callable( $struct['sync'] ) ) { - $sync_types[ $type ] = $struct['priority']; + $sync_types[ $type ] = floatval( $struct['priority'] ); } } 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 12000bb8e..094a46192 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 @@ -299,4 +299,48 @@ private function update_content( $attachment_id ) { } } } + + public function add_suffix_maybe( $attachment_id, $suffix = null ) { + + // Test if asset exists by calling just the head on the asset url, to prevent API rate limits. + $options = $this->media->get_upload_options( $attachment_id ); + $public_id = $options['public_id']; + + $url = $this->connect->api->cloudinary_url( $public_id . $suffix ); + $req = wp_remote_head( $url, array( 'body' => array( 'rdm' => wp_rand( 100, 999 ) ) ) ); + $asset_error = strtolower( wp_remote_retrieve_header( $req, 'x-cld-error' ) ); + $code = wp_remote_retrieve_response_code( $req ); + + // If the request is not a 404 & does not have a cld-error header stating resource not found, it exists and should be checked that it's not a resync or generate a prefixed ID. + if ( 404 !== $code && false === strpos( $asset_error, 'resource not found' ) ) { + + // Get the attachment type. + $type = $this->media->get_media_type( $attachment_id ); + $cld_asset = $this->plugin->components['connect']->api->get_asset_details( $public_id . $suffix, $type ); + if ( ! is_wp_error( $cld_asset ) && ! empty( $cld_asset['public_id'] ) ) { + $context_guid = null; + $attachment_guid = md5( get_the_guid( $attachment_id ) ); + + // Exists, check to see if this asset originally belongs to this ID. + if ( ! empty( $cld_asset['context'] ) && ! empty( $cld_asset['context']['custom'] ) && ! empty( $cld_asset['context']['custom']['guid'] ) ) { + $context_guid = $cld_asset['context']['custom']['guid']; + } + + // Generate new ID only if context ID is not related. + if ( $context_guid !== $attachment_guid ) { + // Generate a new ID with a uniqueID prefix. + $suffix = '-' . uniqid( $attachment_id ); + + // Return new potential suffixed ID. + return $this->add_suffix_maybe( $attachment_id, $suffix ); + } + } + } + // Save defined suffix. + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['suffix'], $suffix ); + // Update Signature. + $this->sync->set_signature_item( $attachment_id, 'suffix' ); + + return $suffix; + } } From 652830049b4214152f198e89a7e6c854fbc8d9fa Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 14:09:28 +0200 Subject: [PATCH 38/64] change method --- .../php/class-sync.php | 50 ++++--------------- 1 file changed, 11 insertions(+), 39 deletions(-) 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 fda05bb12..1695b79d6 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 @@ -278,49 +278,21 @@ public function generate_public_id( $attachment_id ) { * * @return string The public ID. */ - public function add_suffix_maybe( $public_id, $attachment_id, $suffix = null ) { - - // Test if asset exists by calling just the head on the asset url, to prevent API rate limits. - $url = $this->plugin->components['connect']->api->cloudinary_url( $public_id . $suffix ); - $req = wp_remote_head( $url, array( 'body' => array( 'rdm' => wp_rand( 100, 999 ) ) ) ); - $asset_error = strtolower( wp_remote_retrieve_header( $req, 'x-cld-error' ) ); - $code = wp_remote_retrieve_response_code( $req ); - - // If the request is not a 404 & does not have a cld-error header stating resource not found, it exists and should be checked that it's not a resync or generate a prefixed ID. - if ( 404 !== $code && false === strpos( $asset_error, 'resource not found' ) ) { - - // Get the attachment type. - if ( wp_attachment_is( 'image', $attachment_id ) ) { - $type = 'image'; - } elseif ( wp_attachment_is( 'video', $attachment_id ) ) { - $type = 'video'; - } elseif ( wp_attachment_is( 'audio', $attachment_id ) ) { - $type = 'audio'; - } else { - // not supported. - return null; - } - $cld_asset = $this->plugin->components['connect']->api->get_asset_details( $public_id, $type ); - if ( ! is_wp_error( $cld_asset ) && ! empty( $cld_asset['public_id'] ) ) { - $context_id = null; - - // Exists, check to see if this asset originally belongs to this ID. - if ( ! empty( $cld_asset['context'] ) && ! empty( $cld_asset['context']['custom'] ) && ! empty( $cld_asset['context']['custom']['wp_id'] ) ) { - $context_id = (int) $cld_asset['context']['custom']['wp_id']; - } + public function get_suffix( $attachment_id ) { - // Generate new ID only if context ID is not related. - if ( $context_id !== $attachment_id ) { - // Generate a new ID with a uniqueID prefix. - $suffix = '-' . uniqid(); + $suffixed = ''; - // Return new potential suffixed ID. - return $this->add_suffix_maybe( $public_id, $attachment_id, $suffix ); - } - } + $options = $this->managers['media']->get_upload_options( $attachment_id ); // Filtered, upload options. + $cld_folder = $this->managers['media']->get_cloudinary_folder(); + $public_id = $options['public_id']; + if ( $this->managers['media']->is_folder_synced( $attachment_id ) ) { + $public_id = $cld_folder . $public_id; } + // Add suffix. + $public_id .= $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); + - return $suffix; + return $suffixed; } /** From fc584d7d040e915e413394afe45026306c248139 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 15:26:47 +0200 Subject: [PATCH 39/64] add sync signatuires on download, improve suffixing --- .../php/class-media.php | 40 ++++++++++--------- .../php/class-sync.php | 6 ++- .../php/sync/class-download-sync.php | 12 ++++-- .../php/sync/class-upload-sync.php | 16 ++++++-- 4 files changed, 46 insertions(+), 28 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 3de228da2..7a45abdc3 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -672,14 +672,22 @@ public function get_cloudinary_folder() { /** * Get a public ID. * - * @param int $attachment_id The Attachment ID. + * @param int $attachment_id The Attachment ID. + * @param bool $suffixed Flag to get suffixed version of ID. * - * @return string A cloudinary public id. + * @return string */ - public function get_public_id( $attachment_id ) { + public function get_public_id( $attachment_id, $suffixed = false ) { // Check for a public_id. - $public_id = $this->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true ); - if ( empty( $public_id ) ) { + if ( $this->has_public_id( $attachment_id ) ) { + $public_id = $this->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true ); + if ( $this->is_folder_synced( $attachment_id ) ) { + $public_id = $this->get_cloudinary_folder() . pathinfo( $public_id, PATHINFO_BASENAME ); + } + if ( true === $suffixed ) { + $public_id .= $this->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); + } + } else { $public_id = $this->sync->generate_public_id( $attachment_id ); } @@ -706,21 +714,17 @@ public function has_public_id( $attachment_id ) { */ public function get_cloudinary_id( $attachment_id ) { - $public_id = null; + $cloudinary_id = null; // A cloudinary_id is a public_id with a file extension. if ( $this->has_public_id( $attachment_id ) ) { - $public_id = $this->get_public_id( $attachment_id ); - $suffix_data = $this->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); - if ( is_array( $suffix_data ) && ! empty( $suffix_data['suffix'] ) && $suffix_data['public_id'] === $public_id ) { - $public_id = $public_id . $suffix_data['suffix']; - } - $file = get_attached_file( $attachment_id ); + $public_id = $this->get_public_id( $attachment_id, true ); + $file = get_attached_file( $attachment_id ); // @todo: Make this use the globals, overrides, and application conversion. - $extension = pathinfo( $file, PATHINFO_EXTENSION ); - $public_id = $public_id . '.' . $extension; + $extension = pathinfo( $file, PATHINFO_EXTENSION ); + $cloudinary_id = $public_id . '.' . $extension; } - return $public_id; + return $cloudinary_id; } /** @@ -740,7 +744,7 @@ public function cloudinary_id( $attachment_id ) { return $this->cloudinary_ids[ $attachment_id ]; } if ( ! $this->sync->is_synced( $attachment_id ) && ! defined( 'REST_REQUEST' ) ) { - $this->sync->maybe_prepare_sync( $attachment_id ); + return $this->sync->maybe_prepare_sync( $attachment_id ); } $cloudinary_id = $this->get_cloudinary_id( $attachment_id ); @@ -1086,7 +1090,7 @@ public function down_sync_asset() { } $transformations = $this->get_transformations_from_string( $url ); if ( ! empty( $transformations ) ) { - $sync_key .= wp_json_encode( $transformations ); + $sync_key .= wp_json_encode( $transformations ); $asset['transformations'] = $transformations; } // Check Format and url extension. @@ -1533,7 +1537,7 @@ public function setup() { // Filter live URLS. (functions that return a URL). add_filter( 'wp_calculate_image_srcset', array( $this, 'image_srcset' ), 10, 5 ); add_filter( 'wp_calculate_image_srcset_meta', array( $this, 'match_responsive_sources' ), 10, 4 ); - add_filter( 'wp_get_attachment_metadata', array( $this, 'add_cloudinary_full_size' ), 10, 2 ); + //add_filter( 'wp_get_attachment_metadata', array( $this, 'add_cloudinary_full_size' ), 10, 2 ); add_filter( 'wp_get_attachment_url', array( $this, 'attachment_url' ), 10, 2 ); add_filter( 'image_downsize', array( $this, 'filter_downsize' ), 10, 3 ); 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 1695b79d6..a7419db6c 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 @@ -270,7 +270,7 @@ public function generate_public_id( $attachment_id ) { } /** - * Maybe add a suffix to the public ID if it's not unique. + * Get suffix for the attachment. * * @param string $public_id The public ID to maybe add a suffix. * @param int $attachment_id The attachment ID. @@ -554,7 +554,7 @@ public function sync_base( $post ) { * * @param int $attachment_id The attachment ID. * - * @return void + * @return null */ public function maybe_prepare_sync( $attachment_id ) { @@ -562,6 +562,8 @@ public function maybe_prepare_sync( $attachment_id ) { if ( $type && $this->can_sync( $attachment_id, $type ) ) { $this->add_to_sync( $attachment_id ); } + + return null; } /** 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 7b00f8df6..00986e25d 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 @@ -238,16 +238,20 @@ public function download_asset( $attachment_id, $source = null ) { $meta[ Sync::META_KEYS['cloudinary'] ] = $old_meta[ Sync::META_KEYS['cloudinary'] ]; wp_update_attachment_metadata( $attachment_id, $meta ); } - $this->sync->set_signature_item( $attachment_id, 'download' ); - $this->sync->set_signature_item( $attachment_id, 'file' ); - $this->sync->set_signature_item( $attachment_id, 'folder' ); // Update the folder synced flag. $public_id = $this->media->get_public_id( $attachment_id ); $asset_folder = strpos( $public_id, '/' ) ? dirname( $public_id ) : '/'; - $cloudinary_folder = $this->media->get_cloudinary_folder(); + $cloudinary_folder = rtrim( $this->media->get_cloudinary_folder(), '/' ); if ( $asset_folder === $cloudinary_folder ) { $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true ); } + // Generate signatures. + $this->sync->set_signature_item( $attachment_id, 'options' ); + $this->sync->set_signature_item( $attachment_id, 'cloud_name' ); + $this->sync->set_signature_item( $attachment_id, 'suffix' ); + $this->sync->set_signature_item( $attachment_id, 'download' ); + $this->sync->set_signature_item( $attachment_id, 'file' ); + $this->sync->set_signature_item( $attachment_id, 'folder' ); } catch ( \Exception $e ) { return new \WP_Error( 'download_error', $e->getMessage() ); 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 094a46192..878f5843b 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 @@ -146,8 +146,9 @@ public function handle_bulk_actions( $location, $action, $post_ids ) { switch ( $action ) { case 'cloudinary-push' : foreach ( $post_ids as $post_id ) { - $this->sync->set_signature_item( $post_id, 'file', '' ); - $this->sync->add_to_sync( $post_id ); + //$this->sync->set_signature_item( $post_id, 'file', '' ); + //$this->sync->add_to_sync( $post_id ); + $this->pusher->process_assets( [ $post_id ] ); } break; } @@ -202,9 +203,16 @@ public function upload_asset( $attachment_id ) { $type = $this->sync->get_sync_type( $attachment_id ); $options = $this->media->get_upload_options( $attachment_id ); $public_id = $options['public_id']; - $result = $this->connect->api->upload( $attachment_id, $options ); + + // Add the suffix before uploading. + $options['public_id'] .= $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); + + // Run the upload Call. + $result = $this->connect->api->upload( $attachment_id, $options ); if ( ! is_wp_error( $result ) ) { + // Set folder Synced. + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], $this->media->is_folder_synced( $attachment_id ) ); // Set public_id. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $public_id ); // Update signature for all that use the same method. @@ -306,7 +314,7 @@ public function add_suffix_maybe( $attachment_id, $suffix = null ) { $options = $this->media->get_upload_options( $attachment_id ); $public_id = $options['public_id']; - $url = $this->connect->api->cloudinary_url( $public_id . $suffix ); + $url = $this->connect->api->cloudinary_url( $public_id . $suffix, array( 'transformation' => array( array( 'fetch_format' => 'auto' ) ) ) ); $req = wp_remote_head( $url, array( 'body' => array( 'rdm' => wp_rand( 100, 999 ) ) ) ); $asset_error = strtolower( wp_remote_retrieve_header( $req, 'x-cld-error' ) ); $code = wp_remote_retrieve_response_code( $req ); From 2854af954ec483fb6aa26135f51a316eb55134c9 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 15:42:35 +0200 Subject: [PATCH 40/64] correct suffixing --- .../php/class-media.php | 6 +++++- .../php/class-sync.php | 4 +--- .../php/connect/class-api.php | 2 -- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 7a45abdc3..c36160998 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -1488,7 +1488,7 @@ public function get_upload_options( $attachment_id ) { $options = array( 'unique_filename' => false, 'resource_type' => $this->get_media_type( $attachment_id ), - 'public_id' => $this->get_public_id( $attachment_id ), + 'public_id' => basename( $this->get_public_id( $attachment_id ) ), 'context' => $this->get_context_options( $attachment_id ), ); @@ -1502,6 +1502,10 @@ public function get_upload_options( $attachment_id ) { * @return array */ $options = apply_filters( 'cloudinary_upload_options', $options, get_post( $attachment_id ), $this ); + // Add folder to prevent folder contamination. + if ( $this->is_folder_synced( $attachment_id ) ) { + $options['public_id'] = $this->get_cloudinary_folder() . basename( $options['public_id'] ); + } return $options; } 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 a7419db6c..5634ab0e5 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 @@ -280,8 +280,6 @@ public function generate_public_id( $attachment_id ) { */ public function get_suffix( $attachment_id ) { - $suffixed = ''; - $options = $this->managers['media']->get_upload_options( $attachment_id ); // Filtered, upload options. $cld_folder = $this->managers['media']->get_cloudinary_folder(); $public_id = $options['public_id']; @@ -292,7 +290,7 @@ public function get_suffix( $attachment_id ) { $public_id .= $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); - return $suffixed; + return $public_id; } /** diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php index 0a6fbd6e8..5bcf67bed 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php @@ -239,8 +239,6 @@ public function cloudinary_url( $public_id, $args = array(), $size = array(), $c // check for version. if ( ! empty( $args['version'] ) && is_numeric( $args['version'] ) ) { $args['version'] = 'v' . $args['version']; - } else { - $args['version'] = 'v1'; } // Determine if we're dealing with a fetched. From b9dc4159ca363180b1e5b75ff3be25783827bcbc Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 16:04:35 +0200 Subject: [PATCH 41/64] correct background process for push/resync --- .../php/sync/class-upload-sync.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 878f5843b..3241fa592 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 @@ -146,9 +146,8 @@ public function handle_bulk_actions( $location, $action, $post_ids ) { switch ( $action ) { case 'cloudinary-push' : foreach ( $post_ids as $post_id ) { - //$this->sync->set_signature_item( $post_id, 'file', '' ); - //$this->sync->add_to_sync( $post_id ); - $this->pusher->process_assets( [ $post_id ] ); + $this->sync->set_signature_item( $post_id, 'file', '' ); + $this->sync->add_to_sync( $post_id ); } break; } From 4dc51b253447bbc5a8d0f4cf5d0036efe7636536 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Thu, 6 Aug 2020 19:10:51 +0200 Subject: [PATCH 42/64] cleanup unslashing and old methods --- .../php/class-media.php | 24 ------------------- .../php/sync/class-download-sync.php | 2 +- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index c36160998..94f769ac3 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -1272,29 +1272,6 @@ public function get_transformation_from_meta( $post_id ) { return $transformations; } - /** - * Add a full Cloudinary full size with stored file name to the sizes array. - * - * @param array $data Image meta data array. - * @param int $post_id Attachment post ID. - * - * @return mixed - */ - public function add_cloudinary_full_size( $data, $post_id ) { - // Add a full Cloudinary filename size. - if ( ! empty( $data['file'] ) && ! empty( $data['sizes'] ) ) { - $info = pathinfo( $data['file'] ); - $cld_file = $this->get_public_id( $post_id ) . '.' . $info['extension']; - $data['sizes']['_cld_full'] = array( - 'file' => basename( $cld_file ), - 'width' => $data['width'], - 'height' => $data['height'], - ); - } - - return $data; - } - /** * Get Cloudinary related Post meta. * @@ -1541,7 +1518,6 @@ public function setup() { // Filter live URLS. (functions that return a URL). add_filter( 'wp_calculate_image_srcset', array( $this, 'image_srcset' ), 10, 5 ); add_filter( 'wp_calculate_image_srcset_meta', array( $this, 'match_responsive_sources' ), 10, 4 ); - //add_filter( 'wp_get_attachment_metadata', array( $this, 'add_cloudinary_full_size' ), 10, 2 ); add_filter( 'wp_get_attachment_url', array( $this, 'attachment_url' ), 10, 2 ); add_filter( 'image_downsize', array( $this, 'filter_downsize' ), 10, 3 ); 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 00986e25d..aee1fa6d6 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 @@ -241,7 +241,7 @@ public function download_asset( $attachment_id, $source = null ) { // Update the folder synced flag. $public_id = $this->media->get_public_id( $attachment_id ); $asset_folder = strpos( $public_id, '/' ) ? dirname( $public_id ) : '/'; - $cloudinary_folder = rtrim( $this->media->get_cloudinary_folder(), '/' ); + $cloudinary_folder = untrailingslashit( $this->media->get_cloudinary_folder() ); if ( $asset_folder === $cloudinary_folder ) { $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true ); } From fdbc4286744beef0759577c670d0f25d82a2dbc7 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Fri, 7 Aug 2020 08:16:50 +0200 Subject: [PATCH 43/64] get URL on down synced upgrades and resuse old v2 meta from upgrade. --- .../php/sync/class-download-sync.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 aee1fa6d6..d2b0eaca1 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 @@ -194,7 +194,8 @@ public function download_asset( $attachment_id, $source = null ) { require_once ABSPATH . 'wp-admin/includes/image.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; if ( empty( $source ) ) { - $source = $this->media->cloudinary_url( $attachment_id ); + $cloudinary_id = $this->media->get_cloudinary_id( $attachment_id ); + $source = $this->media->cloudinary_url( $attachment_id, array(), array(), $cloudinary_id ); } $file_name = basename( $source ); try { @@ -234,10 +235,10 @@ public function download_asset( $attachment_id, $source = null ) { $meta = wp_generate_attachment_metadata( $attachment_id, $upload['file'] ); $captured_errors = ob_get_clean(); // Capture issues. // Be sure to record v2 meta. - if ( ! empty( $meta[ Sync::META_KEYS['cloudinary'] ] ) ) { + if ( ! empty( $old_meta[ Sync::META_KEYS['cloudinary'] ] ) ) { $meta[ Sync::META_KEYS['cloudinary'] ] = $old_meta[ Sync::META_KEYS['cloudinary'] ]; - wp_update_attachment_metadata( $attachment_id, $meta ); } + wp_update_attachment_metadata( $attachment_id, $meta ); // Update the folder synced flag. $public_id = $this->media->get_public_id( $attachment_id ); $asset_folder = strpos( $public_id, '/' ) ? dirname( $public_id ) : '/'; From df5fdddf9947e3c97ca10239605590a4d7780087 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Fri, 7 Aug 2020 14:38:49 +0200 Subject: [PATCH 44/64] add upgrade path for v2, to fix broken folder_syncs --- .../php/class-media.php | 9 +++++++-- .../php/class-sync.php | 6 +++--- .../php/media/class-upgrade.php | 18 ++++++++++++++++++ .../php/sync/class-upload-sync.php | 8 ++++++++ 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 94f769ac3..ea9ea4f62 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -1462,10 +1462,12 @@ public function is_folder_synced( $attachment_id ) { public function get_upload_options( $attachment_id ) { // Prepare upload options. - $options = array( + $public_id = $this->get_public_id( $attachment_id ); + $folder = ltrim( dirname( $public_id ), '.' ); + $options = array( 'unique_filename' => false, 'resource_type' => $this->get_media_type( $attachment_id ), - 'public_id' => basename( $this->get_public_id( $attachment_id ) ), + 'public_id' => basename( $public_id ), 'context' => $this->get_context_options( $attachment_id ), ); @@ -1482,6 +1484,9 @@ public function get_upload_options( $attachment_id ) { // Add folder to prevent folder contamination. if ( $this->is_folder_synced( $attachment_id ) ) { $options['public_id'] = $this->get_cloudinary_folder() . basename( $options['public_id'] ); + } elseif ( ! empty( $folder ) ) { + // add in folder if not empty (not in root). + $options['public_id'] = trailingslashit( $folder ) . basename( $options['public_id'] ); } return $options; 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 5634ab0e5..80087324a 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 @@ -396,8 +396,8 @@ public function setup_sync_base_struct() { 'generate' => array( $this, 'get_suffix' ), 'priority' => 5.0, 'sync' => array( $this->managers['upload'], 'add_suffix_maybe' ), - 'state' => 'uploading', - 'note' => __( 'Creating unique version', 'cloudinary' ), + 'state' => 'info syncing', + 'note' => __( 'Checking version', 'cloudinary' ), ), 'breakpoints' => array( 'generate' => array( $this->managers['media'], 'get_breakpoint_options' ), @@ -655,7 +655,7 @@ public function filter_status( $status, $attachment_id ) { // Check if there's an error. $has_error = $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['sync_error'], true ); - if ( ! empty( $has_error ) ) { + if ( ! empty( $has_error ) && $this->get_sync_type( $attachment_id ) ) { $status['state'] = 'error'; $status['note'] = $has_error; } 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 71a0beb92..6dafffa47 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 @@ -103,11 +103,29 @@ public function convert_cloudinary_version( $attachment_id ) { } else { // v2 upgrade. $public_id = $this->media->get_public_id( $attachment_id ); + // Check folder sync in order. + if ( $this->media->is_folder_synced( $attachment_id ) ) { + $public_id_folder = ltrim( dirname( $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true ) ), '.' ); + $test_signature = md5( false ); + $folder_signature = md5( $public_id_folder ); + $signature = $this->sync->get_signature( $attachment_id ); + if ( $folder_signature !== $test_signature && $test_signature === $signature['folder'] ) { + // The test signature is a hashed false, which is how non-folder-synced items got hashed. + // Indicating this is broken link. + $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'] ); + $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['sync_error'] ); // Remove any errors from upgrade. they are outdated. + $this->sync->set_signature_item( $attachment_id, 'folder' ); + } + // Since it had a folder sync, it was already matched with existing, so set the suffix. + $this->sync->set_signature_item( $attachment_id, 'suffix' ); + } } $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['plugin_version'], $this->media->plugin->version ); $this->sync->set_signature_item( $attachment_id, 'upgrade' ); $this->sync->set_signature_item( $attachment_id, 'public_id' ); + // Get a new uncached signature. + $this->sync->get_signature( $attachment_id, true ); return $public_id; } 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 3241fa592..688b5b70e 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 @@ -327,12 +327,20 @@ public function add_suffix_maybe( $attachment_id, $suffix = null ) { if ( ! is_wp_error( $cld_asset ) && ! empty( $cld_asset['public_id'] ) ) { $context_guid = null; $attachment_guid = md5( get_the_guid( $attachment_id ) ); + $saved_version = $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['_cloudinary_version'], true ); // Exists, check to see if this asset originally belongs to this ID. if ( ! empty( $cld_asset['context'] ) && ! empty( $cld_asset['context']['custom'] ) && ! empty( $cld_asset['context']['custom']['guid'] ) ) { $context_guid = $cld_asset['context']['custom']['guid']; } + // Check to see if this asset originally belongs to this ID using the older wp_id. + if ( ! empty( $cld_asset['context'] ) && ! empty( $cld_asset['context']['custom'] ) && ! empty( $cld_asset['context']['custom']['wp_id'] ) ) { + if( (int) $cld_asset['context']['custom']['wp_id'] === $attachment_id ) { + $context_guid = $attachment_guid; + } + } + // Generate new ID only if context ID is not related. if ( $context_guid !== $attachment_guid ) { // Generate a new ID with a uniqueID prefix. From 9d4f8ce64e99dc0ca8b4bd8a47691c5b39d31e3d Mon Sep 17 00:00:00 2001 From: David Cramer Date: Sat, 8 Aug 2020 09:45:33 +0200 Subject: [PATCH 45/64] add version back to upload. --- .../php/class-sync.php | 4 +++- .../php/sync/class-upload-sync.php | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) 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 80087324a..e73b18daa 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 @@ -743,7 +743,9 @@ public function set_signature_item( $attachment_id, $type, $value = null ) { // Get the core meta. $meta = wp_get_attachment_metadata( $attachment_id, true ); - + if ( empty( $meta[ Sync::META_KEYS['cloudinary'] ] ) ) { + $meta[ Sync::META_KEYS['cloudinary'] ] = array(); + } // Set the specific value. if ( is_null( $value ) ) { // Generate a new value based on generator. 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 688b5b70e..6f8134d14 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 @@ -214,6 +214,8 @@ public function upload_asset( $attachment_id ) { $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], $this->media->is_folder_synced( $attachment_id ) ); // Set public_id. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $public_id ); + // Set version. + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['version'], $result['version'] ); // Update signature for all that use the same method. $this->sync->sync_signature_by_type( $attachment_id, $type ); // Update options and public_id as well (full sync) From f486a7c9164c34287618d6ac86a7763ac00d43cd Mon Sep 17 00:00:00 2001 From: David Cramer Date: Sat, 8 Aug 2020 10:12:26 +0200 Subject: [PATCH 46/64] add version on import --- .../php/class-media.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index ea9ea4f62..6712b2559 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -1023,6 +1023,8 @@ private function create_attachment( $asset, $public_id ) { $sync_key = $public_id; // Capture public_id. Use core update_post_meta since this attachment data doesnt exist yet. update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $public_id ); + // Capture version number. + update_post_meta( $attachment_id, Sync::META_KEYS['version'], $asset['version'] ); if ( ! empty( $asset['transformations'] ) ) { // Save a combined key. $sync_key .= wp_json_encode( $asset['transformations'] ); From 2e8343c73cd41bf0129080f1f3fd0d2a954c37c2 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Sun, 9 Aug 2020 07:24:04 +0200 Subject: [PATCH 47/64] remove unessasary svg size filter --- .../php/media/class-filter.php | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php index 5c7416e30..b844d3dd6 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php @@ -671,45 +671,6 @@ public function filter_image_block_pre_render( $block, $source_block ) { return $block; } - /** - * Attempt to set the width and height for SVGs. - * - * @param array|false $image The image details. - * @param int $attachment_id The attachment ID. - * @param string|int[] $size The requested image size. - * - * @return array|false - */ - public function filter_svg_image_size( $image, $attachment_id, $size ) { - if ( is_array( $image ) && preg_match('/\.svg$/i', $image[0] ) && $image[1] <= 1 ) { - $image[1] = $image[2] = null; - - if ( is_array( $size ) ) { - $image[1] = $size[0]; - $image[2] = $size[1]; - } elseif ( false !== ( $xml = simplexml_load_file( $image[0] ) ) ) { - $attr = $xml->attributes(); - $viewbox = explode( ' ', $attr->viewBox ); - - // Get width - if ( isset( $attr->width ) && preg_match( '/\d+/', $attr->width, $value ) ) { - $image[1] = (int) $value[0]; - } elseif ( 4 === count( $viewbox ) ) { - $image[1] = (int) $viewbox[2]; - } - - // Get height - if ( isset( $attr->height ) && preg_match( '/\d+/', $attr->height, $value ) ) { - $image[2] = (int) $value[0]; - } elseif ( 4 === count( $viewbox ) ) { - $image[2] = (int) $viewbox[3]; - } - } - } - - return $image; - } - /** * Setup hooks for the filters. */ @@ -747,8 +708,5 @@ function ( $type ) use ( $filter ) { // Filter for block rendering. add_filter( 'render_block_data', array( $this, 'filter_image_block_pre_render' ), 10, 2 ); - - // Try to get SVGs size. - add_filter( 'wp_get_attachment_image_src', array( $this, 'filter_svg_image_size' ), 10, 3 ); } } From fb856acd07a35dd4a7624bd8be2ae355bc3f3eb1 Mon Sep 17 00:00:00 2001 From: Marco Pereirinha Date: Mon, 10 Aug 2020 09:45:38 +0100 Subject: [PATCH 48/64] Set default values --- .../php/class-rest-api.php | 9 +++++++++ .../php/connect/class-api.php | 1 + 2 files changed, 10 insertions(+) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-rest-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-rest-api.php index e8663349f..5ec043129 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-rest-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-rest-api.php @@ -36,6 +36,15 @@ public function __construct( Plugin $plugin ) { */ public function rest_api_init() { foreach ( $this->endpoints as $route => $endpoint ) { + $endpoint = wp_parse_args( + $endpoint, + array( + 'method' => 'GET', + 'callback' => null, + 'arg' => array(), + 'permission_callback' => '__return_true', + ) + ); register_rest_route( static::BASE, $route, diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php index 5bcf67bed..1bde58ca8 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php @@ -230,6 +230,7 @@ function ( $item ) use ( $transformation_index ) { public function cloudinary_url( $public_id, $args = array(), $size = array(), $clean = false ) { $defaults = array( 'resource_type' => 'image', + 'version' => '', ); $args = wp_parse_args( array_filter( $args ), $defaults ); // Correct Audio to Video. From b2bf78421e91742c93ff3eb75814f3f51ae9f4a3 Mon Sep 17 00:00:00 2001 From: Marco Pereirinha Date: Mon, 10 Aug 2020 10:23:23 +0100 Subject: [PATCH 49/64] Set default values --- .../php/connect/class-api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php index 1bde58ca8..a95b262c3 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/connect/class-api.php @@ -230,7 +230,7 @@ function ( $item ) use ( $transformation_index ) { public function cloudinary_url( $public_id, $args = array(), $size = array(), $clean = false ) { $defaults = array( 'resource_type' => 'image', - 'version' => '', + 'version' => 'v1', ); $args = wp_parse_args( array_filter( $args ), $defaults ); // Correct Audio to Video. From 1f54b189337e106875e9633b529e02f992f70d8e Mon Sep 17 00:00:00 2001 From: David Cramer Date: Mon, 10 Aug 2020 12:36:45 +0200 Subject: [PATCH 50/64] Update formatting Co-authored-by: dugajean --- .../php/sync/class-upload-sync.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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 6f8134d14..e36836bf6 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 @@ -337,10 +337,13 @@ public function add_suffix_maybe( $attachment_id, $suffix = null ) { } // Check to see if this asset originally belongs to this ID using the older wp_id. - if ( ! empty( $cld_asset['context'] ) && ! empty( $cld_asset['context']['custom'] ) && ! empty( $cld_asset['context']['custom']['wp_id'] ) ) { - if( (int) $cld_asset['context']['custom']['wp_id'] === $attachment_id ) { - $context_guid = $attachment_guid; - } + if ( + ! empty( $cld_asset['context'] ) && + ! empty( $cld_asset['context']['custom'] ) && + ! empty( $cld_asset['context']['custom']['wp_id'] ) && + (int) $cld_asset['context']['custom']['wp_id'] === $attachment_id + ) { + $context_guid = $attachment_guid; } // Generate new ID only if context ID is not related. From a4a65ee9ad73e8530e09890feae4bbcf95e7263c Mon Sep 17 00:00:00 2001 From: David Cramer Date: Mon, 10 Aug 2020 12:41:01 +0200 Subject: [PATCH 51/64] add attachment_id to note callback --- .../php/class-sync.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e73b18daa..bc511eb7b 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 @@ -647,7 +647,7 @@ public function filter_status( $status, $attachment_id ) { $status['state'] = $this->sync_base_struct[ $sync_type ]['state']; $status['note'] = $this->sync_base_struct[ $sync_type ]['note']; if ( is_callable( $status['note'] ) ) { - $status['note'] = call_user_func( $status['note'] ); + $status['note'] = call_user_func( $status['note'], $attachment_id ); } } } From 12007d8b87510e0b31fe9c5fa3abf9a08e6d4357 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Mon, 10 Aug 2020 12:48:37 +0200 Subject: [PATCH 52/64] add register sync types action --- .../php/class-sync.php | 7 +++++++ 1 file changed, 7 insertions(+) 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 bc511eb7b..97ad86c59 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 @@ -435,6 +435,13 @@ public function setup_sync_base_struct() { foreach ( $base_struct as $type => $structure ) { $this->register_sync_type( $type, $structure ); } + + /** + * Do action for setting up sync types. + * + * @param \Cloudinary\Sync $this The sync object. + */ + do_action( 'cloudinary_register_sync_types', $this ); } /** From 7f0a48bed94789d591017978cff633e88a0d8c73 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Mon, 10 Aug 2020 15:30:10 +0200 Subject: [PATCH 53/64] ensure that it's a media type with metadata --- .../php/class-media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 6712b2559..36773a56a 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -129,7 +129,7 @@ public function __construct( Plugin $plugin ) { */ public function is_media( $attachment_id ) { $is_media = false; - if ( 'attachment' === get_post_type( $attachment_id ) ) { + if ( 'attachment' === get_post_type( $attachment_id ) && wp_get_attachment_metadata( $attachment_id ) ) { /** * Filter the default Cloudinary Media Types. * From 8ecc29df7156dd7aa674ad11d7ffd32a5f1b4328 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 11 Aug 2020 07:35:54 +0200 Subject: [PATCH 54/64] add saved suffix as part of the suffixed check --- .../php/sync/class-upload-sync.php | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) 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 e36836bf6..7e9819715 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 @@ -309,6 +309,14 @@ private function update_content( $attachment_id ) { } } + /** + * Maybe add a suffix to a public_id in case it already exists. + * + * @param int $attachment_id The attachment ID to maybe suffix. + * @param null|string $suffix The proposed suffix to maybe use. + * + * @return mixed|string|null + */ public function add_suffix_maybe( $attachment_id, $suffix = null ) { // Test if asset exists by calling just the head on the asset url, to prevent API rate limits. @@ -329,7 +337,6 @@ public function add_suffix_maybe( $attachment_id, $suffix = null ) { if ( ! is_wp_error( $cld_asset ) && ! empty( $cld_asset['public_id'] ) ) { $context_guid = null; $attachment_guid = md5( get_the_guid( $attachment_id ) ); - $saved_version = $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['_cloudinary_version'], true ); // Exists, check to see if this asset originally belongs to this ID. if ( ! empty( $cld_asset['context'] ) && ! empty( $cld_asset['context']['custom'] ) && ! empty( $cld_asset['context']['custom']['guid'] ) ) { @@ -337,19 +344,25 @@ public function add_suffix_maybe( $attachment_id, $suffix = null ) { } // Check to see if this asset originally belongs to this ID using the older wp_id. - if ( - ! empty( $cld_asset['context'] ) && - ! empty( $cld_asset['context']['custom'] ) && - ! empty( $cld_asset['context']['custom']['wp_id'] ) && - (int) $cld_asset['context']['custom']['wp_id'] === $attachment_id + if ( + ! empty( $cld_asset['context'] ) && + ! empty( $cld_asset['context']['custom'] ) && + ! empty( $cld_asset['context']['custom']['wp_id'] ) && + (int) $cld_asset['context']['custom']['wp_id'] === $attachment_id ) { $context_guid = $attachment_guid; } // Generate new ID only if context ID is not related. if ( $context_guid !== $attachment_guid ) { - // Generate a new ID with a uniqueID prefix. - $suffix = '-' . uniqid( $attachment_id ); + // Check if we've saved a suffix before. If so use it, and test. This is to prevent making new suffixes if the original is removed. + $saved_suffix = $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); + if ( null === $suffix && ! empty( $saved_suffix ) ) { + $suffix = $saved_suffix; + } else { + // Generate a new ID with a uniqueID prefix. + $suffix = '-' . uniqid( $attachment_id ); + } // Return new potential suffixed ID. return $this->add_suffix_maybe( $attachment_id, $suffix ); From c44f9332b77643c58fc71dbc5d088c2eba109b13 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 11 Aug 2020 07:37:50 +0200 Subject: [PATCH 55/64] add suffix signature clear on push upload --- .../php/sync/class-upload-sync.php | 1 + 1 file changed, 1 insertion(+) 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 7e9819715..20d30e303 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 @@ -146,6 +146,7 @@ public function handle_bulk_actions( $location, $action, $post_ids ) { switch ( $action ) { case 'cloudinary-push' : foreach ( $post_ids as $post_id ) { + $this->sync->set_signature_item( $post_id, 'suffix', '' ); $this->sync->set_signature_item( $post_id, 'file', '' ); $this->sync->add_to_sync( $post_id ); } From 10fd348e967d03894495713083774a393616160d Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 11 Aug 2020 09:54:45 +0200 Subject: [PATCH 56/64] remove suffix sync, prefer no overwrite and result checking. --- .../php/class-media.php | 3 +- .../php/class-sync.php | 31 ------- .../php/sync/class-upload-sync.php | 93 +++++-------------- 3 files changed, 25 insertions(+), 102 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 36773a56a..59ba34fea 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -1467,7 +1467,8 @@ public function get_upload_options( $attachment_id ) { $public_id = $this->get_public_id( $attachment_id ); $folder = ltrim( dirname( $public_id ), '.' ); $options = array( - 'unique_filename' => false, + 'unique_filename' => true, + 'overwrite' => false, 'resource_type' => $this->get_media_type( $attachment_id ), 'public_id' => basename( $public_id ), 'context' => $this->get_context_options( $attachment_id ), 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 97ad86c59..fed2c6ab8 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 @@ -269,30 +269,6 @@ public function generate_public_id( $attachment_id ) { return ltrim( $public_id, '/' ); } - /** - * Get suffix for the attachment. - * - * @param string $public_id The public ID to maybe add a suffix. - * @param int $attachment_id The attachment ID. - * @param string|null $suffix The suffix to maybe add. - * - * @return string The public ID. - */ - public function get_suffix( $attachment_id ) { - - $options = $this->managers['media']->get_upload_options( $attachment_id ); // Filtered, upload options. - $cld_folder = $this->managers['media']->get_cloudinary_folder(); - $public_id = $options['public_id']; - if ( $this->managers['media']->is_folder_synced( $attachment_id ) ) { - $public_id = $cld_folder . $public_id; - } - // Add suffix. - $public_id .= $this->managers['media']->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); - - - return $public_id; - } - /** * Register a new sync type. * @@ -392,13 +368,6 @@ public function setup_sync_base_struct() { 'state' => 'info syncing', 'note' => __( 'Updating metadata', 'cloudinary' ), ), - 'suffix' => array( - 'generate' => array( $this, 'get_suffix' ), - 'priority' => 5.0, - 'sync' => array( $this->managers['upload'], 'add_suffix_maybe' ), - 'state' => 'info syncing', - 'note' => __( 'Checking version', 'cloudinary' ), - ), 'breakpoints' => array( 'generate' => array( $this->managers['media'], 'get_breakpoint_options' ), 'priority' => 25, 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 20d30e303..2cb4b4331 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 @@ -146,7 +146,6 @@ public function handle_bulk_actions( $location, $action, $post_ids ) { switch ( $action ) { case 'cloudinary-push' : foreach ( $post_ids as $post_id ) { - $this->sync->set_signature_item( $post_id, 'suffix', '' ); $this->sync->set_signature_item( $post_id, 'file', '' ); $this->sync->add_to_sync( $post_id ); } @@ -205,12 +204,34 @@ public function upload_asset( $attachment_id ) { $public_id = $options['public_id']; // Add the suffix before uploading. - $options['public_id'] .= $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); + if ( $this->media->get_public_id( $attachment_id ) === $public_id ) { + // Only apply the saved suffix if the public_id is the same. This is to allow filtered ID's a change to have a suffix removed. + $options['public_id'] .= $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); + } else { + // If the public_id has been changed, remove the saved suffix. + $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['suffix'] ); + } // Run the upload Call. $result = $this->connect->api->upload( $attachment_id, $options ); if ( ! is_wp_error( $result ) ) { + + // Check that this wasn't an existing. + if ( ! empty( $result['existing'] ) ) { + // Check to see if this is the same image. + $version = (int) $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['version'], true ); + if ( $version !== $result['version'] ) { + // New image with the same name. + // Add a suffix and try again. + $suffix = '_' . $attachment_id . substr( strrev( uniqid() ), 0, 5 ); + $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['suffix'], $suffix ); + + return $this->upload_asset( $attachment_id ); + } + } + + // Set folder Synced. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], $this->media->is_folder_synced( $attachment_id ) ); // Set public_id. @@ -309,72 +330,4 @@ private function update_content( $attachment_id ) { } } } - - /** - * Maybe add a suffix to a public_id in case it already exists. - * - * @param int $attachment_id The attachment ID to maybe suffix. - * @param null|string $suffix The proposed suffix to maybe use. - * - * @return mixed|string|null - */ - public function add_suffix_maybe( $attachment_id, $suffix = null ) { - - // Test if asset exists by calling just the head on the asset url, to prevent API rate limits. - $options = $this->media->get_upload_options( $attachment_id ); - $public_id = $options['public_id']; - - $url = $this->connect->api->cloudinary_url( $public_id . $suffix, array( 'transformation' => array( array( 'fetch_format' => 'auto' ) ) ) ); - $req = wp_remote_head( $url, array( 'body' => array( 'rdm' => wp_rand( 100, 999 ) ) ) ); - $asset_error = strtolower( wp_remote_retrieve_header( $req, 'x-cld-error' ) ); - $code = wp_remote_retrieve_response_code( $req ); - - // If the request is not a 404 & does not have a cld-error header stating resource not found, it exists and should be checked that it's not a resync or generate a prefixed ID. - if ( 404 !== $code && false === strpos( $asset_error, 'resource not found' ) ) { - - // Get the attachment type. - $type = $this->media->get_media_type( $attachment_id ); - $cld_asset = $this->plugin->components['connect']->api->get_asset_details( $public_id . $suffix, $type ); - if ( ! is_wp_error( $cld_asset ) && ! empty( $cld_asset['public_id'] ) ) { - $context_guid = null; - $attachment_guid = md5( get_the_guid( $attachment_id ) ); - - // Exists, check to see if this asset originally belongs to this ID. - if ( ! empty( $cld_asset['context'] ) && ! empty( $cld_asset['context']['custom'] ) && ! empty( $cld_asset['context']['custom']['guid'] ) ) { - $context_guid = $cld_asset['context']['custom']['guid']; - } - - // Check to see if this asset originally belongs to this ID using the older wp_id. - if ( - ! empty( $cld_asset['context'] ) && - ! empty( $cld_asset['context']['custom'] ) && - ! empty( $cld_asset['context']['custom']['wp_id'] ) && - (int) $cld_asset['context']['custom']['wp_id'] === $attachment_id - ) { - $context_guid = $attachment_guid; - } - - // Generate new ID only if context ID is not related. - if ( $context_guid !== $attachment_guid ) { - // Check if we've saved a suffix before. If so use it, and test. This is to prevent making new suffixes if the original is removed. - $saved_suffix = $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true ); - if ( null === $suffix && ! empty( $saved_suffix ) ) { - $suffix = $saved_suffix; - } else { - // Generate a new ID with a uniqueID prefix. - $suffix = '-' . uniqid( $attachment_id ); - } - - // Return new potential suffixed ID. - return $this->add_suffix_maybe( $attachment_id, $suffix ); - } - } - } - // Save defined suffix. - $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['suffix'], $suffix ); - // Update Signature. - $this->sync->set_signature_item( $attachment_id, 'suffix' ); - - return $suffix; - } } From e7a4ea55cff1ced530cbca8a2a1f32de3d96eb99 Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 11 Aug 2020 11:50:47 +0200 Subject: [PATCH 57/64] ensure sizes has correct URLS in metadata --- .../php/media/class-filter.php | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php index b844d3dd6..d1edde186 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-filter.php @@ -372,11 +372,11 @@ public function filter_out_local( $content ) { /** * Return a Cloudinary URL for an attachment used in JS. * - * @uses filter:wp_prepare_attachment_for_js - * - * @param array $attachment The attachment array to be used in JS. + * @param array $attachment The attachment response array. * * @return array + * @uses filter:wp_prepare_attachment_for_js + * */ public function filter_attachment_for_js( $attachment ) { $cloudinary_id = $this->media->get_cloudinary_id( $attachment['id'] ); @@ -392,13 +392,20 @@ public function filter_attachment_for_js( $attachment ) { $attachment['url'] = $this->media->cloudinary_url( $attachment['id'], false, $transformations ); $attachment['public_id'] = $attachment['type'] . '/upload/' . $this->media->get_public_id( $attachment['id'] ); - } - if ( empty( $attachment['transformations'] ) ) { - $transformations = $this->media->get_transformation_from_meta( $attachment['id'] ); + if ( empty( $attachment['transformations'] ) ) { + $transformations = $this->media->get_transformation_from_meta( $attachment['id'] ); - if ( $transformations ) { - $attachment['transformations'] = $transformations; + if ( $transformations ) { + $attachment['transformations'] = $transformations; + } + } + + // Ensure the sizes has the transformations and are converted URLS. + if ( ! empty( $attachment['sizes'] ) ) { + foreach ( $attachment['sizes'] as &$size ) { + $size['url'] = $this->media->convert_url( basename( $size['url'] ), $attachment['id'], $transformations ); + } } } @@ -680,7 +687,7 @@ public function setup_hooks() { add_filter( 'the_editor_content', array( $this, 'filter_out_local' ) ); add_filter( 'the_content', array( $this, 'filter_out_local' ), 9 ); // Early to hook before responsive srcsets. - add_filter( 'wp_prepare_attachment_for_js', array( $this, 'filter_attachment_for_js' ) ); + add_filter( 'wp_prepare_attachment_for_js', array( $this, 'filter_attachment_for_js' ), 11 ); // Add support for custom header. add_filter( 'get_header_image_tag', array( $this, 'filter_out_local' ) ); From 44a21f2644f03062a79311fe65aa0c2a36a6f59c Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 11 Aug 2020 16:37:41 +0200 Subject: [PATCH 58/64] add check that a synctype is still available --- .../php/class-sync.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fed2c6ab8..5073d94b6 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 @@ -610,7 +610,7 @@ public function validate_sync_type( $type, $attachment_id ) { */ public function filter_status( $status, $attachment_id ) { - if ( $this->been_synced( $attachment_id ) || $this->is_pending( $attachment_id ) ) { + if ( $this->been_synced( $attachment_id ) && $this->is_pending( $attachment_id ) && $this->get_sync_type( $attachment_id ) ) { $sync_type = $this->get_sync_type( $attachment_id ); if ( ! empty( $sync_type ) && isset( $this->sync_base_struct[ $sync_type ] ) ) { // check process log in case theres an error. From 80369817184e6c62de6b53f66795db63707b54fb Mon Sep 17 00:00:00 2001 From: David Cramer Date: Tue, 11 Aug 2020 16:44:55 +0200 Subject: [PATCH 59/64] show items currently syncing, but not yet fully synced. --- .../php/class-sync.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5073d94b6..215f161f8 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 @@ -610,7 +610,7 @@ public function validate_sync_type( $type, $attachment_id ) { */ public function filter_status( $status, $attachment_id ) { - if ( $this->been_synced( $attachment_id ) && $this->is_pending( $attachment_id ) && $this->get_sync_type( $attachment_id ) ) { + if ( $this->been_synced( $attachment_id ) || ( $this->is_pending( $attachment_id ) && $this->get_sync_type( $attachment_id ) ) ) { $sync_type = $this->get_sync_type( $attachment_id ); if ( ! empty( $sync_type ) && isset( $this->sync_base_struct[ $sync_type ] ) ) { // check process log in case theres an error. From d6c107870210e492aca227e1faca7d2356a969cd Mon Sep 17 00:00:00 2001 From: Marco Pereirinha Date: Tue, 11 Aug 2020 17:06:10 +0100 Subject: [PATCH 60/64] Add uploading status --- .../css/src/components/_brand.scss | 2 +- .../php/class-sync.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/css/src/components/_brand.scss b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/css/src/components/_brand.scss index caecd2e68..940b50532 100755 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/css/src/components/_brand.scss +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/css/src/components/_brand.scss @@ -40,7 +40,7 @@ } } - &.warning { + &.uploading { color : $color-orange; &:before { 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 215f161f8..20ec262e4 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 @@ -343,7 +343,7 @@ public function setup_sync_base_struct() { 'generate' => 'get_attached_file', 'priority' => 5.1, 'sync' => array( $this->managers['upload'], 'upload_asset' ), - 'state' => 'info', + 'state' => 'uploading', 'note' => __( 'Uploading to Cloudinary', 'cloudinary' ), ), 'folder' => array( From 4f9ea0b3e6b825cdf42ca0ada6242dabd3f8cdfa Mon Sep 17 00:00:00 2001 From: Marco Pereirinha Date: Tue, 11 Aug 2020 17:06:23 +0100 Subject: [PATCH 61/64] Add compiled asset --- .../css/cloudinary.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/css/cloudinary.css b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/css/cloudinary.css index 5c14e6684..c55a99c00 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/css/cloudinary.css +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/css/cloudinary.css @@ -1 +1 @@ -@font-face{font-family:cloudinary;src:url(../css/fonts/cloudinary.7b8dc57b5dd3c69880043780bb9bb133.eot);src:url(../css/fonts/cloudinary.7b8dc57b5dd3c69880043780bb9bb133.eot#iefix) format("embedded-opentype"),url(../css/fonts/cloudinary.0bf34bba6c50ef8a00885c94fc39bb81.ttf) format("truetype"),url(../css/fonts/cloudinary.130b5d626b226659422cfb0e20ce30c1.woff) format("woff"),url(../css/cloudinary.svg#cloudinary) format("svg");font-weight:400;font-style:normal}.dashicons-cloudinary{speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.dashicons-cloudinary:before{font-family:cloudinary!important;content:"\e900"}.dashicons-cloudinary.success{color:#558b2f}.dashicons-cloudinary.error{color:#dd2c00}.dashicons-cloudinary.error:before{content:"\e901"}.dashicons-cloudinary.warning{color:#fd9d2c}.dashicons-cloudinary.warning:before{content:"\e902"}.dashicons-cloudinary.info{color:#0071ba}.dashicons-cloudinary.downloading:before{content:"\e903"}.dashicons-cloudinary.syncing:before{content:"\e904"}.column-cld_status{width:5.5em}.column-cld_status .dashicons-cloudinary{display:inline-block}.column-cld_status .dashicons-cloudinary:before{font-size:1.8rem}.form-field .error-notice,.form-table .error-notice{display:none;color:#dd2c00}.form-field input.cld-field:invalid,.form-table input.cld-field:invalid{border-color:#dd2c00}.form-field input.cld-field:invalid+.error-notice,.form-table input.cld-field:invalid+.error-notice{display:inline-block}.cloudinary-welcome{background-image:url(../css/logo.svg);background-repeat:no-repeat;background-size:153px;background-position:top 12px right 20px}.cloudinary-stats{display:inline-block;margin-left:25px}.cloudinary-stat{cursor:help}.cloudinary-percent{font-size:.8em;vertical-align:top;color:#0071ba}.settings-image{max-width:100%;padding-top:5px}.settings-tabs>li{display:inline-block}.settings-tabs>li a{padding:.6em}.settings-tabs>li a.active{background-color:#fff}.settings-tab-section{padding:20px 0 0;max-width:1030px;position:relative}.settings-tab-section.cloudinary-welcome .settings-tab-section-fields-dashboard{display:flex;align-items:flex-start;align-content:flex-start;margin-top:40px}.settings-tab-section.cloudinary-welcome .settings-tab-section-fields-dashboard-description{width:55%;margin:0 auto 0 0}.settings-tab-section.cloudinary-welcome .settings-tab-section-fields-dashboard-content{width:35%;margin:0 auto}.settings-tab-section.cloudinary-welcome .settings-tab-section-fields-dashboard-content .dashicons{color:#9ea3a8}.settings-tab-section.cloudinary-welcome .settings-tab-section-card{margin-top:0}.settings-tab-section-fields .field-heading th{display:block;width:auto;color:#23282d;font-size:1.1em;margin:1em 0}.settings-tab-section-fields .field-heading td{display:none;visibility:hidden}.settings-tab-section-fields .regular-textarea{width:100%;height:60px}.settings-tab-section-fields .dashicons{text-decoration:none;vertical-align:middle}.settings-tab-section-fields a .dashicons{color:#5f5f5f}.settings-tab-section-fields-dashboard-error{font-size:1.2em;color:#5f5f5f}.settings-tab-section-fields-dashboard-error .dashicons{color:#ac0000}.settings-tab-section-fields-dashboard-error .button{font-size:1.1em;height:40px;line-height:40px;padding-right:40px;padding-left:40px}.settings-tab-section-fields-dashboard-success{font-size:1.2em;color:#23282d}.settings-tab-section-fields-dashboard-success.expanded{padding-top:40px}.settings-tab-section-fields-dashboard-success .dashicons{color:#4fb651}.settings-tab-section-fields-dashboard-success .button{font-size:1.1em;height:40px;line-height:40px;padding-right:40px;padding-left:40px}.settings-tab-section-fields-dashboard-success .description{color:#5f5f5f;font-weight:400;margin-top:12px}.settings-tab-section-card{box-sizing:border-box;border:1px solid #e5e5e5;background-color:#fff;box-shadow:0 1px 1px 0 rgba(0,0,0,.07);padding:20px 23px;margin-top:12px}.settings-tab-section-card .dashicons{font-size:1.4em}.settings-tab-section-card h2{font-size:1.8em;font-weight:400;margin-top:0}.settings-tab-section-card.pull-right{width:450px;padding:12px;float:right;position:relative;z-index:10}.settings-tab-section-card.pull-right img.settings-image{box-shadow:0 2px 4px 0 rgba(0,0,0,.5);border:1px solid #979797;margin-top:12px}.settings-tab-section-card.pull-right h3,.settings-tab-section-card.pull-right h4{margin-top:0}.settings-tab-section .field-row-cloudinary_url,.settings-tab-section .field-row-signup{display:block}.settings-tab-section .field-row-cloudinary_url td,.settings-tab-section .field-row-cloudinary_url th,.settings-tab-section .field-row-signup td,.settings-tab-section .field-row-signup th{display:block;width:auto;padding:10px 0 0}.settings-tab-section .field-row-cloudinary_url td .sign-up,.settings-tab-section .field-row-cloudinary_url th .sign-up,.settings-tab-section .field-row-signup td .sign-up,.settings-tab-section .field-row-signup th .sign-up{vertical-align:baseline}.settings-tab-section.connect .form-table{display:inline-block;width:auto;max-width:580px}.settings-valid{color:#558b2f;font-size:30px}.settings-valid-field{border-color:#558b2f!important}.settings-invalid-field{border-color:#dd2c00!important}.settings-warning{display:inline-block;padding:5px 7px;background-color:#e9faff;border:1px solid #ccd0d4;border-left:4px solid #00a0d2;box-shadow:0 1px 1px rgba(0,0,0,.04)}.sync .spinner{display:inline-block;visibility:visible;float:none;margin:0 5px 0 0}.sync-media,.sync-media-progress{display:none}.sync-media-progress-outer{height:20px;margin:20px 0 10px;width:500px;background-color:#e5e5e5;position:relative}.sync-media-progress-outer .progress-bar{width:0;height:20px;background-color:#558b2f;transition:width .25s}.sync-media-progress-notice{color:#dd2c00}.sync-media-resource{width:100px;display:inline-block}.sync-media-error{color:#dd2c00}.sync-count{font-weight:700}.sync-details{margin-top:10px}.sync .button.start-sync,.sync .button.stop-sync{display:none;padding:0 16px}.sync .button.start-sync .dashicons,.sync .button.stop-sync .dashicons{line-height:2.2em}.sync .progress-text{padding:12px 4px 12px 12px;display:inline-block;font-weight:700}.sync .completed{max-width:300px;display:none}.sync-status-disabled{color:#dd2c00}.sync-status-enabled{color:#558b2f}.sync-status-button.button{vertical-align:baseline}.cloudinary-widget{height:100%}.cloudinary-widget-wrapper{height:100%;overflow:hidden;background-image:url("data:image/svg+xml;base64,PHN2ZyBjbGFzcz0ic3Bpbm5lciIgdmlld0JveD0iLTQgLTQgMTUxIDEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48c3R5bGU+QGtleWZyYW1lcyBjb2xvcnN7MCUsdG97c3Ryb2tlOiMwMDc4ZmZ9NTAle3N0cm9rZTojMGUyZjVhfX1Aa2V5ZnJhbWVzIGRhc2h7MCUsdG97c3Ryb2tlLWRhc2hvZmZzZXQ6NTYwfTUwJXtzdHJva2UtZGFzaG9mZnNldDowfX1ALXdlYmtpdC1rZXlmcmFtZXMgY29sb3JzezAlLHRve3N0cm9rZTojMDA3OGZmfTUwJXtzdHJva2U6IzBlMmY1YX19QC13ZWJraXQta2V5ZnJhbWVzIGRhc2h7MCUsdG97c3Ryb2tlLWRhc2hvZmZzZXQ6NTYwfTUwJXtzdHJva2UtZGFzaG9mZnNldDowfX08L3N0eWxlPjxwYXRoIGQ9Ik0xMjEuNjYzIDkwLjYzOGMtMS43OTYgMC05OS4zMy0uNDk4LTEwMS40NzQtMS40NzhDOC42ODUgODMuODc3IDEuMjUgNzIuMTk2IDEuMjUgNTkuMzk2YzAtMTYuNjU2IDEyLjc5Ny0zMC42MSAyOS4wNTItMzIuMzIzIDcuNDktMTUuNzA2IDIzLjE4Ni0yNS43MDcgNDAuNzE0LTI1LjcwNyAyMC45OCAwIDM5LjIxNSAxNC43NTIgNDMuOTQ1IDM0LjkwNyAxNS4wOS4yNDUgMjcuMjkgMTIuNjMgMjcuMjkgMjcuODIyIDAgMTEuOTY4LTcuNzM4IDIyLjU1LTE5LjI1NiAyNi4zMyIgc3Ryb2tlLXdpZHRoPSI5IiBzdHJva2UtbGluZWNhcD0icm91bmQiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3R5bGU9InRyYW5zZm9ybS1vcmlnaW46Y2VudGVyOy13ZWJraXQtYW5pbWF0aW9uOmRhc2ggMnMgZWFzZS1pbi1vdXQgaW5maW5pdGUsY29sb3JzIDhzIGVhc2UtaW4tb3V0IGluZmluaXRlO2FuaW1hdGlvbjpkYXNoIDJzIGVhc2UtaW4tb3V0IGluZmluaXRlLGNvbG9ycyA4cyBlYXNlLWluLW91dCBpbmZpbml0ZSIgc3Ryb2tlLWRhc2hhcnJheT0iMjgwIi8+PC9zdmc+");background-repeat:no-repeat;background-position:50%;background-size:150px}.attachment-actions .button.edit-attachment,.attachment-info .edit-attachment{display:none}.global-transformations-preview{position:relative;max-width:600px}.global-transformations-spinner{display:none}.global-transformations-button.button-primary{display:none;position:absolute;z-index:100}.global-transformations-url{margin-bottom:5px;margin-top:5px}.global-transformations-url-transformation{max-width:100px;overflow:hidden;text-overflow:ellipsis;color:#51a3ff}.global-transformations-url-file{color:#f2d864}.global-transformations-url-link{display:block;padding:16px;background-color:#262c35;text-decoration:none;color:#fff;border-radius:6px;overflow:hidden;text-overflow:ellipsis}.global-transformations-url-link:hover{color:#888;text-decoration:underline}.cld-tax-order-list-item{border:1px solid #efefef;padding:4px;margin:0 0 -1px;background-color:#fff}.cld-tax-order-list-item.no-items{color:#888;text-align:center;display:none}.cld-tax-order-list-item.no-items:last-child{display:block}.cld-tax-order-list-item.ui-sortable-helper{box-shadow:0 2px 5px rgba(0,0,0,.2)}.cld-tax-order-list-item-placeholder{background-color:#efefef;height:45px;margin:0}.cld-tax-order-list-item-handle{cursor:grab;margin-right:4px;color:#999}.cld-tax-order-list-type{width:45%;display:inline-block;margin-right:8px}.cld-tax-order-list-type input{margin-right:4px!important}.cloudinary-media-library{position:relative;margin-left:-20px}@media screen and (max-width:782px){.cloudinary-media-library{margin-left:-10px}} \ No newline at end of file +@font-face{font-family:cloudinary;src:url(../css/fonts/cloudinary.7b8dc57b5dd3c69880043780bb9bb133.eot);src:url(../css/fonts/cloudinary.7b8dc57b5dd3c69880043780bb9bb133.eot#iefix) format("embedded-opentype"),url(../css/fonts/cloudinary.0bf34bba6c50ef8a00885c94fc39bb81.ttf) format("truetype"),url(../css/fonts/cloudinary.130b5d626b226659422cfb0e20ce30c1.woff) format("woff"),url(../css/cloudinary.svg#cloudinary) format("svg");font-weight:400;font-style:normal}.dashicons-cloudinary{speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.dashicons-cloudinary:before{font-family:cloudinary!important;content:"\e900"}.dashicons-cloudinary.success{color:#558b2f}.dashicons-cloudinary.error{color:#dd2c00}.dashicons-cloudinary.error:before{content:"\e901"}.dashicons-cloudinary.uploading{color:#fd9d2c}.dashicons-cloudinary.uploading:before{content:"\e902"}.dashicons-cloudinary.info{color:#0071ba}.dashicons-cloudinary.downloading:before{content:"\e903"}.dashicons-cloudinary.syncing:before{content:"\e904"}.column-cld_status{width:5.5em}.column-cld_status .dashicons-cloudinary{display:inline-block}.column-cld_status .dashicons-cloudinary:before{font-size:1.8rem}.form-field .error-notice,.form-table .error-notice{display:none;color:#dd2c00}.form-field input.cld-field:invalid,.form-table input.cld-field:invalid{border-color:#dd2c00}.form-field input.cld-field:invalid+.error-notice,.form-table input.cld-field:invalid+.error-notice{display:inline-block}.cloudinary-welcome{background-image:url(../css/logo.svg);background-repeat:no-repeat;background-size:153px;background-position:top 12px right 20px}.cloudinary-stats{display:inline-block;margin-left:25px}.cloudinary-stat{cursor:help}.cloudinary-percent{font-size:.8em;vertical-align:top;color:#0071ba}.settings-image{max-width:100%;padding-top:5px}.settings-tabs>li{display:inline-block}.settings-tabs>li a{padding:.6em}.settings-tabs>li a.active{background-color:#fff}.settings-tab-section{padding:20px 0 0;max-width:1030px;position:relative}.settings-tab-section.cloudinary-welcome .settings-tab-section-fields-dashboard{display:flex;align-items:flex-start;align-content:flex-start;margin-top:40px}.settings-tab-section.cloudinary-welcome .settings-tab-section-fields-dashboard-description{width:55%;margin:0 auto 0 0}.settings-tab-section.cloudinary-welcome .settings-tab-section-fields-dashboard-content{width:35%;margin:0 auto}.settings-tab-section.cloudinary-welcome .settings-tab-section-fields-dashboard-content .dashicons{color:#9ea3a8}.settings-tab-section.cloudinary-welcome .settings-tab-section-card{margin-top:0}.settings-tab-section-fields .field-heading th{display:block;width:auto;color:#23282d;font-size:1.1em;margin:1em 0}.settings-tab-section-fields .field-heading td{display:none;visibility:hidden}.settings-tab-section-fields .regular-textarea{width:100%;height:60px}.settings-tab-section-fields .dashicons{text-decoration:none;vertical-align:middle}.settings-tab-section-fields a .dashicons{color:#5f5f5f}.settings-tab-section-fields-dashboard-error{font-size:1.2em;color:#5f5f5f}.settings-tab-section-fields-dashboard-error .dashicons{color:#ac0000}.settings-tab-section-fields-dashboard-error .button{font-size:1.1em;height:40px;line-height:40px;padding-right:40px;padding-left:40px}.settings-tab-section-fields-dashboard-success{font-size:1.2em;color:#23282d}.settings-tab-section-fields-dashboard-success.expanded{padding-top:40px}.settings-tab-section-fields-dashboard-success .dashicons{color:#4fb651}.settings-tab-section-fields-dashboard-success .button{font-size:1.1em;height:40px;line-height:40px;padding-right:40px;padding-left:40px}.settings-tab-section-fields-dashboard-success .description{color:#5f5f5f;font-weight:400;margin-top:12px}.settings-tab-section-card{box-sizing:border-box;border:1px solid #e5e5e5;background-color:#fff;box-shadow:0 1px 1px 0 rgba(0,0,0,.07);padding:20px 23px;margin-top:12px}.settings-tab-section-card .dashicons{font-size:1.4em}.settings-tab-section-card h2{font-size:1.8em;font-weight:400;margin-top:0}.settings-tab-section-card.pull-right{width:450px;padding:12px;float:right;position:relative;z-index:10}.settings-tab-section-card.pull-right img.settings-image{box-shadow:0 2px 4px 0 rgba(0,0,0,.5);border:1px solid #979797;margin-top:12px}.settings-tab-section-card.pull-right h3,.settings-tab-section-card.pull-right h4{margin-top:0}.settings-tab-section .field-row-cloudinary_url,.settings-tab-section .field-row-signup{display:block}.settings-tab-section .field-row-cloudinary_url td,.settings-tab-section .field-row-cloudinary_url th,.settings-tab-section .field-row-signup td,.settings-tab-section .field-row-signup th{display:block;width:auto;padding:10px 0 0}.settings-tab-section .field-row-cloudinary_url td .sign-up,.settings-tab-section .field-row-cloudinary_url th .sign-up,.settings-tab-section .field-row-signup td .sign-up,.settings-tab-section .field-row-signup th .sign-up{vertical-align:baseline}.settings-tab-section.connect .form-table{display:inline-block;width:auto;max-width:580px}.settings-valid{color:#558b2f;font-size:30px}.settings-valid-field{border-color:#558b2f!important}.settings-invalid-field{border-color:#dd2c00!important}.settings-warning{display:inline-block;padding:5px 7px;background-color:#e9faff;border:1px solid #ccd0d4;border-left:4px solid #00a0d2;box-shadow:0 1px 1px rgba(0,0,0,.04)}.sync .spinner{display:inline-block;visibility:visible;float:none;margin:0 5px 0 0}.sync-media,.sync-media-progress{display:none}.sync-media-progress-outer{height:20px;margin:20px 0 10px;width:500px;background-color:#e5e5e5;position:relative}.sync-media-progress-outer .progress-bar{width:0;height:20px;background-color:#558b2f;transition:width .25s}.sync-media-progress-notice{color:#dd2c00}.sync-media-resource{width:100px;display:inline-block}.sync-media-error{color:#dd2c00}.sync-count{font-weight:700}.sync-details{margin-top:10px}.sync .button.start-sync,.sync .button.stop-sync{display:none;padding:0 16px}.sync .button.start-sync .dashicons,.sync .button.stop-sync .dashicons{line-height:2.2em}.sync .progress-text{padding:12px 4px 12px 12px;display:inline-block;font-weight:700}.sync .completed{max-width:300px;display:none}.sync-status-disabled{color:#dd2c00}.sync-status-enabled{color:#558b2f}.sync-status-button.button{vertical-align:baseline}.cloudinary-widget{height:100%}.cloudinary-widget-wrapper{height:100%;overflow:hidden;background-image:url("data:image/svg+xml;base64,PHN2ZyBjbGFzcz0ic3Bpbm5lciIgdmlld0JveD0iLTQgLTQgMTUxIDEwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48c3R5bGU+QGtleWZyYW1lcyBjb2xvcnN7MCUsdG97c3Ryb2tlOiMwMDc4ZmZ9NTAle3N0cm9rZTojMGUyZjVhfX1Aa2V5ZnJhbWVzIGRhc2h7MCUsdG97c3Ryb2tlLWRhc2hvZmZzZXQ6NTYwfTUwJXtzdHJva2UtZGFzaG9mZnNldDowfX1ALXdlYmtpdC1rZXlmcmFtZXMgY29sb3JzezAlLHRve3N0cm9rZTojMDA3OGZmfTUwJXtzdHJva2U6IzBlMmY1YX19QC13ZWJraXQta2V5ZnJhbWVzIGRhc2h7MCUsdG97c3Ryb2tlLWRhc2hvZmZzZXQ6NTYwfTUwJXtzdHJva2UtZGFzaG9mZnNldDowfX08L3N0eWxlPjxwYXRoIGQ9Ik0xMjEuNjYzIDkwLjYzOGMtMS43OTYgMC05OS4zMy0uNDk4LTEwMS40NzQtMS40NzhDOC42ODUgODMuODc3IDEuMjUgNzIuMTk2IDEuMjUgNTkuMzk2YzAtMTYuNjU2IDEyLjc5Ny0zMC42MSAyOS4wNTItMzIuMzIzIDcuNDktMTUuNzA2IDIzLjE4Ni0yNS43MDcgNDAuNzE0LTI1LjcwNyAyMC45OCAwIDM5LjIxNSAxNC43NTIgNDMuOTQ1IDM0LjkwNyAxNS4wOS4yNDUgMjcuMjkgMTIuNjMgMjcuMjkgMjcuODIyIDAgMTEuOTY4LTcuNzM4IDIyLjU1LTE5LjI1NiAyNi4zMyIgc3Ryb2tlLXdpZHRoPSI5IiBzdHJva2UtbGluZWNhcD0icm91bmQiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgc3R5bGU9InRyYW5zZm9ybS1vcmlnaW46Y2VudGVyOy13ZWJraXQtYW5pbWF0aW9uOmRhc2ggMnMgZWFzZS1pbi1vdXQgaW5maW5pdGUsY29sb3JzIDhzIGVhc2UtaW4tb3V0IGluZmluaXRlO2FuaW1hdGlvbjpkYXNoIDJzIGVhc2UtaW4tb3V0IGluZmluaXRlLGNvbG9ycyA4cyBlYXNlLWluLW91dCBpbmZpbml0ZSIgc3Ryb2tlLWRhc2hhcnJheT0iMjgwIi8+PC9zdmc+");background-repeat:no-repeat;background-position:50%;background-size:150px}.attachment-actions .button.edit-attachment,.attachment-info .edit-attachment{display:none}.global-transformations-preview{position:relative;max-width:600px}.global-transformations-spinner{display:none}.global-transformations-button.button-primary{display:none;position:absolute;z-index:100}.global-transformations-url{margin-bottom:5px;margin-top:5px}.global-transformations-url-transformation{max-width:100px;overflow:hidden;text-overflow:ellipsis;color:#51a3ff}.global-transformations-url-file{color:#f2d864}.global-transformations-url-link{display:block;padding:16px;background-color:#262c35;text-decoration:none;color:#fff;border-radius:6px;overflow:hidden;text-overflow:ellipsis}.global-transformations-url-link:hover{color:#888;text-decoration:underline}.cld-tax-order-list-item{border:1px solid #efefef;padding:4px;margin:0 0 -1px;background-color:#fff}.cld-tax-order-list-item.no-items{color:#888;text-align:center;display:none}.cld-tax-order-list-item.no-items:last-child{display:block}.cld-tax-order-list-item.ui-sortable-helper{box-shadow:0 2px 5px rgba(0,0,0,.2)}.cld-tax-order-list-item-placeholder{background-color:#efefef;height:45px;margin:0}.cld-tax-order-list-item-handle{cursor:grab;margin-right:4px;color:#999}.cld-tax-order-list-type{width:45%;display:inline-block;margin-right:8px}.cld-tax-order-list-type input{margin-right:4px!important}.cloudinary-media-library{position:relative;margin-left:-20px}@media screen and (max-width:782px){.cloudinary-media-library{margin-left:-10px}} \ No newline at end of file From 41f7fb17b089d18faaee6645f8946754caa45e84 Mon Sep 17 00:00:00 2001 From: Marco Pereirinha Date: Tue, 11 Aug 2020 18:25:29 +0100 Subject: [PATCH 62/64] Make sure that Cloudinary is connected before --- .../php/class-sync.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 20ec262e4..f9dc2b2f5 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 @@ -108,10 +108,12 @@ public function enqueue_assets() { * Register Assets. */ public function register_assets() { - // Setup the sync_base_structure. - $this->setup_sync_base_struct(); - // Setup sync types. - $this->setup_sync_types(); + if ( $this->plugin->config['connect'] ) { + // Setup the sync_base_structure. + $this->setup_sync_base_struct(); + // Setup sync types. + $this->setup_sync_types(); + } } From 1b7a0319760770c3d66f17937a37922fcde2303c Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 12 Aug 2020 08:48:31 +0200 Subject: [PATCH 63/64] allow some sync types to continue rendering cloudinary urls on non required sync types. --- .../php/class-media.php | 7 ++++++- .../php/class-sync.php | 19 +++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 59ba34fea..4a6f71245 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -744,7 +744,12 @@ public function cloudinary_id( $attachment_id ) { return $this->cloudinary_ids[ $attachment_id ]; } if ( ! $this->sync->is_synced( $attachment_id ) && ! defined( 'REST_REQUEST' ) ) { - return $this->sync->maybe_prepare_sync( $attachment_id ); + $sync_type = $this->sync->maybe_prepare_sync( $attachment_id ); + // Check sync type allows for continued rendering. i.e meta update, breakpoints etc, will still allow the URL to work, + // Where is type "file" will not since it's still being uploaded. + if ( ! is_null( $sync_type ) && $this->sync->is_required( $sync_type ) ) { + return null; // Return and render local URLs. + } } $cloudinary_id = $this->get_cloudinary_id( $attachment_id ); 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 f9dc2b2f5..02e0d4d57 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 @@ -162,6 +162,17 @@ public function is_synced( $post_id ) { return false; } + /** + * Check if sync type is required for rendering a Cloudinary URL. + * + * @param string $type The type to check. + * + * @return bool + */ + public function is_required( $type ) { + return ! empty( $this->sync_base_struct[ $type ]['required'] ); + } + /** * Generate a signature based on whats required for a full sync. * @@ -347,6 +358,7 @@ public function setup_sync_base_struct() { 'sync' => array( $this->managers['upload'], 'upload_asset' ), 'state' => 'uploading', 'note' => __( 'Uploading to Cloudinary', 'cloudinary' ), + 'required' => true, // Required to complete URL render flag. ), 'folder' => array( 'generate' => array( $this->managers['media'], 'get_cloudinary_folder' ), @@ -357,6 +369,7 @@ public function setup_sync_base_struct() { 'note' => function () { return sprintf( __( 'Copying to folder %s.', 'cloudinary' ), untrailingslashit( $this->managers['media']->get_cloudinary_folder() ) ); }, + 'required' => true, // Required to complete URL render flag. ), 'public_id' => array( 'generate' => array( $this->managers['media'], 'get_public_id' ), @@ -369,6 +382,7 @@ public function setup_sync_base_struct() { 'sync' => array( $this->managers['media']->upgrade, 'convert_cloudinary_version' ), // Rename 'state' => 'info syncing', 'note' => __( 'Updating metadata', 'cloudinary' ), + 'required' => true, ), 'breakpoints' => array( 'generate' => array( $this->managers['media'], 'get_breakpoint_options' ), @@ -390,6 +404,7 @@ public function setup_sync_base_struct() { 'sync' => array( $this->managers['upload'], 'upload_asset' ), 'state' => 'uploading', 'note' => __( 'Uploading to new cloud name.', 'cloudinary' ), + 'required' => true, ), ); @@ -530,7 +545,7 @@ public function sync_base( $post ) { * * @param int $attachment_id The attachment ID. * - * @return null + * @return string | null */ public function maybe_prepare_sync( $attachment_id ) { @@ -539,7 +554,7 @@ public function maybe_prepare_sync( $attachment_id ) { $this->add_to_sync( $attachment_id ); } - return null; + return $type; } /** From 2de224c7ee2776c25fc80f606fd419767256aece Mon Sep 17 00:00:00 2001 From: David Cramer Date: Wed, 12 Aug 2020 12:53:48 +0200 Subject: [PATCH 64/64] correct docblock --- .../php/class-media.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php index 4a6f71245..b1349f763 100644 --- a/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php +++ b/cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-media.php @@ -732,7 +732,7 @@ public function get_cloudinary_id( $attachment_id ) { * * @param int $attachment_id The ID to get Cloudinary id for. * - * @return string|null the ID or false if not existing. + * @return string|null the ID or null if not existing. */ public function cloudinary_id( $attachment_id ) {