Skip to content

Commit ec573e6

Browse files
author
David Cramer
authored
Merge pull request #105 from cloudinary/feature/public-id-sync
Feature/public id sync
2 parents 373d871 + e124f2d commit ec573e6

File tree

5 files changed

+121
-36
lines changed

5 files changed

+121
-36
lines changed

cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/class-sync.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class Sync implements Setup, Assets {
4848
'transformation' => '_transformations',
4949
'sync_error' => '_sync_error',
5050
'cloudinary' => '_cloudinary_v2',
51+
'folder_sync' => '_folder_sync',
5152
);
5253

5354
/**
@@ -100,8 +101,9 @@ public function is_active() {
100101
*/
101102
public function is_synced( $post_id ) {
102103
$return = false;
103-
$signature = $this->plugin->components['media']->get_post_meta( $post_id, self::META_KEYS['signature'], true );
104-
if ( ! empty( $signature ) && $this->generate_signature( $post_id ) === $signature ) {
104+
$signature = $this->get_signature( $post_id );
105+
$expecting = $this->generate_signature( $post_id );
106+
if ( ! empty( $signature ) && $expecting === $signature ) {
105107
$return = $signature;
106108
}
107109

@@ -133,6 +135,31 @@ function ( $item ) {
133135
return $return;
134136
}
135137

138+
/**
139+
* Get the current sync signature of an asset.
140+
*
141+
* @param int $post_id The post ID.
142+
*
143+
* @return array|bool
144+
*/
145+
public function get_signature( $post_id ) {
146+
static $signatures = array(); // Cache signatures already fetched.
147+
148+
$return = false;
149+
if ( ! empty( $signatures[ $post_id ] ) ) {
150+
$return = $signatures[ $post_id ];
151+
} else {
152+
$signature = $this->plugin->components['media']->get_post_meta( $post_id, self::META_KEYS['signature'], true );
153+
if ( ! empty( $signature ) ) {
154+
$base_signatures = $this->generate_signature( $post_id );
155+
$signatures[ $post_id ] = wp_parse_args( $signature, $base_signatures );
156+
$return = $signatures[ $post_id ];
157+
}
158+
}
159+
160+
return $return;
161+
}
162+
136163
/**
137164
* Additional component setup.
138165
*/

cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/media/class-upgrade.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,10 @@ public function __construct( \Cloudinary\Media $media ) {
4646
public function check_cloudinary_version( $cloudinary_id, $attachment_id ) {
4747
if ( false === $cloudinary_id ) {
4848
// Backwards compat.
49-
$meta = wp_get_attachment_metadata( $attachment_id );
49+
$meta = wp_get_attachment_metadata( $attachment_id );
50+
if ( ! empty( $meta[ Sync::META_KEYS['cloudinary'] ] ) ) {
51+
return $cloudinary_id; // Current version.
52+
}
5053
$public_id = $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true );
5154

5255
/*
@@ -59,6 +62,24 @@ public function check_cloudinary_version( $cloudinary_id, $attachment_id ) {
5962
// Has public ID, but not fully down synced.
6063
$cloudinary_id = $public_id;
6164
}
65+
} else {
66+
// Backwards compat.
67+
$folder_sync = $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true );
68+
if ( 0 === strlen( $folder_sync ) ) {
69+
// Does not exist, add it to be compatible with v1.2.2.
70+
$public_id = $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true );
71+
// Set the folder sync to 0 to flag it by default as not synced.
72+
$this->media->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], '0' );
73+
if ( false !== strpos( $public_id, '/' ) ) {
74+
$path = pathinfo( $public_id );
75+
$asset_folder = trailingslashit( $path['dirname'] );
76+
$cloudinary_folder = trailingslashit( $this->media->plugin->config['settings']['sync_media']['cloudinary_folder'] );
77+
if ( $asset_folder === $cloudinary_folder ) {
78+
// The asset folder matches the defined cloudinary folder, flag it as being in a folder sync.
79+
$this->media->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], '1' );
80+
}
81+
}
82+
}
6283
}
6384

6485
return $cloudinary_id;

cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-download-sync.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,16 @@ function ( $val ) use ( $media ) {
157157
$public_id = strstr( $public_id, '.' . $path['extension'], true );
158158
// Save public ID.
159159
$media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $public_id );
160+
// Check if the asset is in the same folder as the defined Cloudinary folder.
161+
if ( false !== strpos( $public_id, '/' ) ) {
162+
$path = pathinfo( $public_id );
163+
$asset_folder = trailingslashit( $path['dirname'] );
164+
$cloudinary_folder = trailingslashit( $this->plugin->config['settings']['sync_media']['cloudinary_folder'] );
165+
if ( $asset_folder === $cloudinary_folder ) {
166+
// The asset folder matches the defined cloudinary folder, flag it as being in a folder sync.
167+
$media->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true );
168+
}
169+
}
160170

161171
return $this->download_asset( $attachment_id, $file, basename( $file ), $media->get_transformations_from_string( $file ) );
162172
}

cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-push-sync.php

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,12 @@ public function __construct( \Cloudinary\Plugin $plugin ) {
5757

5858
// Define the sync types and their option keys.
5959
$sync_types = array(
60+
'cloud_name' => 'upload',
61+
'folder' => 'upload',
6062
'file' => 'upload',
63+
'public_id' => 'rename',
6164
'breakpoints' => 'explicit',
6265
'options' => 'context',
63-
'folder' => 'upload',
64-
'cloud_name' => 'upload',
6566
);
6667
$this->sync_types = apply_filters( 'cloudinary_sync_types', $sync_types );
6768

@@ -303,15 +304,15 @@ private function get_sync_type( $attachment ) {
303304

304305
$type = 'upload';
305306
// Check for explicit (has public_id, but no breakpoints).
306-
$attachment_signature = $attachment->{Sync::META_KEYS['signature']};
307+
$attachment_signature = $this->plugin->components['sync']->get_signature( $attachment->ID );
307308
if ( empty( $attachment_signature ) ) {
308309
if ( ! empty( $attachment->{Sync::META_KEYS['public_id']} ) ) {
309310
// Has a public id but no signature, explicit update to complete download.
310311
$type = 'explicit';
311312
}
312313
// fallback to upload.
313314
} else {
314-
// Has signature. Compare and find if different.
315+
// Has signature find differences and use specific sync method.
315316
$required_signature = $this->plugin->components['sync']->generate_signature( $attachment->ID );
316317
foreach ( $required_signature as $key => $signature ) {
317318
if ( ( ! isset( $attachment_signature[ $key ] ) || $attachment_signature[ $key ] !== $signature ) && isset( $this->sync_types[ $key ] ) ) {
@@ -350,6 +351,9 @@ public function prepare_upload( $post, $down_sync = false ) {
350351
return new \WP_Error( 'attachment_post_expected', __( 'An attachment post was expected.', 'cloudinary' ) );
351352
}
352353

354+
// Get the media component.
355+
$media = $this->plugin->components['media'];
356+
353357
// First check if this has a file and it can be uploaded.
354358
$file = get_attached_file( $post->ID );
355359
$file_size = 0;
@@ -358,7 +362,7 @@ public function prepare_upload( $post, $down_sync = false ) {
358362
} elseif ( ! file_exists( $file ) ) {
359363
// May be an old upload type.
360364
$src = get_post_meta( $post->ID, '_wp_attached_file', true );
361-
if ( $this->plugin->components['media']->is_cloudinary_url( $src ) ) {
365+
if ( $media->is_cloudinary_url( $src ) ) {
362366
// Download first maybe.
363367
if ( true === $down_sync ) {
364368
$download = $this->plugin->components['sync']->managers['download']->down_sync( $post->ID );
@@ -383,36 +387,44 @@ public function prepare_upload( $post, $down_sync = false ) {
383387

384388
// translators: variable is file size.
385389
$error = sprintf( __( 'File size exceeds the maximum of %s. This media asset will be served from WordPress.', 'cloudinary' ), $max_size_hr );
386-
$this->plugin->components['media']->delete_post_meta( $post->ID, Sync::META_KEYS['pending'] ); // Remove Flag.
390+
$media->delete_post_meta( $post->ID, Sync::META_KEYS['pending'] ); // Remove Flag.
387391

388392
return new \WP_Error( 'upload_error', $error );
389393
}
390394

391395
// If it's got a public ID, then this is an explicit update.
396+
$settings = $this->plugin->config['settings'];
392397
$public_id = $post->{Sync::META_KEYS['public_id']}; // use the __get method on the \WP_Post to get post_meta.
393-
$dirs = wp_get_upload_dir();
394-
$cld_folder = false;
395-
$folder = trailingslashit( $dirs['cloudinary_folder'] );
396-
if ( '/' === $dirs['cloudinary_folder'] ) {
397-
$folder = '';
398-
}
398+
$cld_folder = trailingslashit( $settings['sync_media']['cloudinary_folder'] );
399399
if ( empty( $public_id ) ) {
400400
$file_info = pathinfo( $file );
401-
$public_id = $folder . $file_info['filename'];
402-
}
403-
404-
// Check if cloudinary folder is in public_id.
405-
$parts = explode( '/', $public_id );
406-
if ( untrailingslashit( $dirs['cloudinary_folder'] ) === $parts[0] ) {
407-
$cld_folder = $dirs['cloudinary_folder'];
401+
$public_id = $cld_folder . $file_info['filename'];
408402
}
409403

404+
// Assume that the public_id is a root item.
405+
$public_id_folder = '';
406+
$public_id_file = $public_id;
410407

408+
// Check if in a lower level.
409+
if ( false !== strpos( $public_id, '/' ) ) {
410+
// Split the public_id into path and filename to allow filtering just the ID and not giving access to the path.
411+
$public_id_info = pathinfo( $public_id );
412+
$public_id_folder = trailingslashit( $public_id_info['dirname'] );
413+
$public_id_file = $public_id_info['filename'];
414+
}
415+
// Check if this asset is a folder sync.
416+
$folder_sync = $media->get_post_meta( $post->ID, Sync::META_KEYS['folder_sync'], true );
417+
if ( ! empty( $folder_sync ) ) {
418+
$public_id_folder = $cld_folder; // Ensure the public ID folder is constant.
419+
} else {
420+
// Not folder synced, so set the folder to the folder that the asset originally came from.
421+
$cld_folder = $public_id_folder;
422+
}
411423
// Prepare upload options.
412424
$options = array(
413425
'unique_filename' => false,
414426
'resource_type' => $resource_type,
415-
'public_id' => $public_id,
427+
'public_id' => $public_id_file,
416428
'context' => array(
417429
'caption' => esc_attr( $post->post_title ),
418430
'alt' => $post->_wp_attachment_image_alt,
@@ -434,17 +446,17 @@ public function prepare_upload( $post, $down_sync = false ) {
434446
$imagesize = getimagesize( $file );
435447
$meta['width'] = $imagesize[0];
436448
}
437-
$max_width = $this->plugin->components['media']->get_max_width();
449+
$max_width = $media->get_max_width();
438450
// Add breakpoints request options.
439-
if ( ! empty( $this->plugin->config['settings']['global_transformations']['enable_breakpoints'] ) ) {
451+
if ( ! empty( $settings['global_transformations']['enable_breakpoints'] ) ) {
440452
$options['responsive_breakpoints'] = array(
441453
'create_derived' => true,
442-
'bytes_step' => $this->plugin->config['settings']['global_transformations']['bytes_step'],
443-
'max_images' => $this->plugin->config['settings']['global_transformations']['breakpoints'],
454+
'bytes_step' => $settings['global_transformations']['bytes_step'],
455+
'max_images' => $settings['global_transformations']['breakpoints'],
444456
'max_width' => $meta['width'] < $max_width ? $meta['width'] : $max_width,
445-
'min_width' => $this->plugin->config['settings']['global_transformations']['min_width'],
457+
'min_width' => $settings['global_transformations']['min_width'],
446458
);
447-
$transformations = $this->plugin->components['media']->get_transformation_from_meta( $post->ID );
459+
$transformations = $media->get_transformation_from_meta( $post->ID );
448460
if ( ! empty( $transformations ) ) {
449461
$options['responsive_breakpoints']['transformation'] = Api::generate_transformation_string( $transformations );
450462
}
@@ -474,14 +486,19 @@ public function prepare_upload( $post, $down_sync = false ) {
474486
$breakpoints['context'] = http_build_query( $breakpoints['context'], null, '|' );
475487
}
476488

477-
$return = array(
489+
// Restructure the path to the filename to allow correct placement in Cloudinary.
490+
$public_id = $public_id_folder . $options['public_id'];
491+
$return = array(
478492
'file' => $file,
479493
'folder' => $cld_folder,
494+
'public_id' => $public_id,
480495
'breakpoints' => array(),
481496
'options' => $options,
482497
);
498+
$return['options']['public_id'] = $public_id;
483499
if ( ! empty( $breakpoints ) ) {
484-
$return['breakpoints'] = $breakpoints;
500+
$return['breakpoints'] = $breakpoints;
501+
$return['breakpoints']['public_id'] = $public_id; // Stage public ID to folder for breakpoints.
485502
}
486503
$this->upload_options[ $post->ID ] = $return;
487504

@@ -519,6 +536,8 @@ public function push_attachments( $attachments ) {
519536
'total' => count( $attachments ),
520537
'processed' => 0,
521538
);
539+
// Get media component.
540+
$media = $this->plugin->components['media'];
522541

523542
// Go over each attachment.
524543
foreach ( $attachments as $attachment ) {
@@ -560,7 +579,7 @@ public function push_attachments( $attachments ) {
560579
if ( 'explicit' === $sync_type ) {
561580
// Explicit update.
562581
$args = array(
563-
'public_id' => $upload['options']['public_id'],
582+
'public_id' => $upload['public_id'],
564583
'type' => 'upload',
565584
);
566585
if ( ! empty( $upload['options']['responsive_breakpoints'] ) ) {
@@ -570,6 +589,13 @@ public function push_attachments( $attachments ) {
570589
$args['context'] = $upload['options']['context'];
571590
}
572591
$result = $this->plugin->components['connect']->api->explicit( $args );
592+
} elseif ( 'rename' === $sync_type ) {
593+
// Rename an asset.
594+
$args = array(
595+
'from_public_id' => $media->get_post_meta( $attachment->ID, Sync::META_KEYS['public_id'] ),
596+
'to_public_id' => $upload['public_id'],
597+
);
598+
$result = $this->plugin->components['connect']->api->{$upload['options']['resource_type']}( 'rename', 'POST', $args );
573599
} else {
574600
// dynamic sync type..
575601
$result = $this->plugin->components['connect']->api->{$sync_type}( $upload['file'], $upload['options'] );
@@ -583,7 +609,7 @@ public function push_attachments( $attachments ) {
583609
if ( is_wp_error( $result ) ) {
584610
$error = $result->get_error_message();
585611
$stats['fail'][] = $error;
586-
$this->plugin->components['media']->update_post_meta( $attachment->ID, Sync::META_KEYS['sync_error'], $error );
612+
$media->update_post_meta( $attachment->ID, Sync::META_KEYS['sync_error'], $error );
587613
continue;
588614
}
589615

@@ -597,8 +623,8 @@ public function push_attachments( $attachments ) {
597623
if ( ! empty( $result['version'] ) ) {
598624
$meta_data[ Sync::META_KEYS['version'] ] = $result['version'];
599625
}
600-
$this->plugin->components['media']->delete_post_meta( $attachment->ID, Sync::META_KEYS['pending'] );
601-
$this->plugin->components['media']->delete_post_meta( $attachment->ID, Sync::META_KEYS['sync_error'], false );
626+
$media->delete_post_meta( $attachment->ID, Sync::META_KEYS['pending'] );
627+
$media->delete_post_meta( $attachment->ID, Sync::META_KEYS['sync_error'], false );
602628
if ( ! empty( $this->plugin->config['settings']['global_transformations']['enable_breakpoints'] ) ) {
603629
if ( ! empty( $result['responsive_breakpoints'] ) ) { // Images only.
604630
$meta_data[ Sync::META_KEYS['breakpoints'] ] = $result['responsive_breakpoints'][0]['breakpoints'];
@@ -619,7 +645,7 @@ public function push_attachments( $attachments ) {
619645
$meta = wp_get_attachment_metadata( $attachment->ID, true );
620646
$meta[ Sync::META_KEYS['cloudinary'] ] = $meta_data;
621647
wp_update_attachment_metadata( $attachment->ID, $meta );
622-
$this->plugin->components['media']->update_post_meta( $attachment->ID, Sync::META_KEYS['public_id'], $upload['options']['public_id'] );
648+
$media->update_post_meta( $attachment->ID, Sync::META_KEYS['public_id'], $upload['options']['public_id'] );
623649
// Search and update link references in content.
624650
$content_search = new \WP_Query( array( 's' => 'wp-image-' . $attachment->ID, 'fields' => 'ids', 'posts_per_page' => 1000 ) );
625651
if ( ! empty( $content_search->found_posts ) ) {

cloudinary-image-management-and-manipulation-in-the-cloud-cdn/php/sync/class-upload-sync.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ public function add_to_sync( $attachment_id ) {
261261
if ( ! in_array( $attachment_id, $this->to_sync, true ) ) {
262262
// Flag image as pending to prevent duplicate upload.
263263
update_post_meta( $attachment_id, Sync::META_KEYS['pending'], time() );
264+
$this->plugin->components['media']->update_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'], true );
264265
$this->to_sync[] = $attachment_id;
265266
}
266267
}

0 commit comments

Comments
 (0)