Source: php/media/class-upgrade.php

  1. <?php
  2. /**
  3. * Upgrades from a Legecy version of Cloudinary.
  4. *
  5. * @package Cloudinary
  6. */
  7. namespace Cloudinary\Media;
  8. use Cloudinary\Relate;
  9. use Cloudinary\Sync;
  10. use Cloudinary\Utils;
  11. /**
  12. * Class Filter.
  13. *
  14. * Handles filtering of HTML content.
  15. */
  16. class Upgrade {
  17. /**
  18. * Holds the Media instance.
  19. *
  20. * @since 0.1
  21. *
  22. * @var \Cloudinary\Media Instance of the plugin.
  23. */
  24. private $media;
  25. /**
  26. * Holds the Sync instance.
  27. *
  28. * @since 0.1
  29. *
  30. * @var \Cloudinary\Sync Instance of the plugin.
  31. */
  32. private $sync;
  33. /**
  34. * Filter constructor.
  35. *
  36. * @param \Cloudinary\Media $media The plugin.
  37. */
  38. public function __construct( \Cloudinary\Media $media ) {
  39. $this->media = $media;
  40. $this->sync = $media->plugin->components['sync'];
  41. $this->setup_hooks();
  42. }
  43. /**
  44. * Convert an image post that was created from Cloudinary v1.
  45. *
  46. * @param int $attachment_id The attachment ID to convert.
  47. *
  48. * @return string Cloudinary ID
  49. */
  50. public function convert_cloudinary_version( $attachment_id ) {
  51. if ( ! empty( get_post_meta( $attachment_id, Sync::META_KEYS['cloudinary'], true ) ) ) {
  52. // V2.5 changed the meta. if it had, theres no upgrades needed.
  53. /**
  54. * Action to trigger an upgrade on a synced asset.
  55. *
  56. * @hook cloudinary_upgrade_asset
  57. * @since 3.0.5
  58. *
  59. * @param $attachment_id {int} The attachment ID.
  60. * @param $version {string} The current plugin version.
  61. */
  62. do_action( 'cloudinary_upgrade_asset', $attachment_id, $this->media->plugin->version );
  63. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['plugin_version'], $this->media->plugin->version );
  64. $this->sync->set_signature_item( $attachment_id, 'upgrade' );
  65. return $this->media->get_public_id( $attachment_id );
  66. }
  67. $file = get_post_meta( $attachment_id, '_wp_attached_file', true );
  68. if ( ! $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true ) && wp_http_validate_url( $file ) ) {
  69. // Version 1 upgrade.
  70. $path = wp_parse_url( $file, PHP_URL_PATH );
  71. $media = $this->media;
  72. $parts = explode( '/', ltrim( $path, '/' ) );
  73. $cloud_name = null;
  74. $asset_version = 1;
  75. $asset_transformations = array();
  76. $id_parts = array();
  77. $public_id = $this->get_fetch_public_id( $path, $attachment_id );
  78. foreach ( $parts as $val ) {
  79. if ( empty( $val ) ) {
  80. continue;
  81. }
  82. if ( is_null( $cloud_name ) ) {
  83. // Cloudname will always be the first item.
  84. $cloud_name = md5( $val );
  85. continue;
  86. }
  87. if ( in_array( $val, array( 'images', 'image', 'video', 'upload', 'fetch' ), true ) ) {
  88. continue;
  89. }
  90. $transformation_maybe = $media->get_transformations_from_string( $val );
  91. if ( ! empty( $transformation_maybe ) ) {
  92. $asset_transformations = $transformation_maybe;
  93. continue;
  94. }
  95. if ( substr( $val, 0, 1 ) === 'v' && is_numeric( substr( $val, 1 ) ) ) {
  96. $asset_version = substr( $val, 1 );
  97. continue;
  98. }
  99. // Filter out file name.
  100. $path = Utils::pathinfo( $val, PATHINFO_FILENAME );
  101. if ( ! in_array( $path, $id_parts, true ) ) {
  102. $id_parts[] = Utils::pathinfo( $val, PATHINFO_FILENAME );
  103. }
  104. }
  105. // Build public_id.
  106. $parts = array_filter( $id_parts );
  107. if ( empty( $public_id ) ) {
  108. $public_id = implode( '/', $parts );
  109. }
  110. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $public_id );
  111. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['version'], $asset_version );
  112. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['upgrading'], true );
  113. if ( ! empty( $asset_transformations ) ) {
  114. Relate::update_transformations( $attachment_id, $asset_transformations );
  115. }
  116. $this->sync->set_signature_item( $attachment_id, 'cloud_name', $cloud_name );
  117. } else {
  118. // v2 upgrade.
  119. $public_id = $this->media->get_public_id( $attachment_id, true );
  120. $suffix = $this->media->get_post_meta( $attachment_id, Sync::META_KEYS['suffix'], true );
  121. if ( ! empty( $suffix ) ) {
  122. // Has suffix. Get delete and cleanup public ID.
  123. if ( false !== strpos( $public_id, $suffix ) ) {
  124. $public_id = str_replace( $suffix, '', $public_id );
  125. }
  126. $public_id .= $suffix;
  127. $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['suffix'] );
  128. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['public_id'], $public_id );
  129. }
  130. // Check folder sync in order and if it's not a URL.
  131. if ( ! wp_http_validate_url( $file ) && $this->media->is_folder_synced( $attachment_id ) ) {
  132. $public_id_folder = ltrim( dirname( $this->media->get_public_id( $attachment_id ) ) );
  133. $test_signature = md5( false );
  134. $folder_signature = md5( $public_id_folder );
  135. $signature = $this->sync->get_signature( $attachment_id );
  136. if ( $folder_signature !== $test_signature && $test_signature === $signature['folder'] ) {
  137. // The test signature is a hashed false, which is how non-folder-synced items got hashed.
  138. // Indicating this is broken link.
  139. $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['folder_sync'] );
  140. $this->media->delete_post_meta( $attachment_id, Sync::META_KEYS['sync_error'] ); // Remove any errors from upgrade. they are outdated.
  141. delete_post_meta( $attachment_id, Sync::META_KEYS['sync_error'] ); // Remove any errors from upgrade. they are outdated.
  142. $this->sync->set_signature_item( $attachment_id, 'folder' );
  143. }
  144. }
  145. }
  146. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['plugin_version'], $this->media->plugin->version );
  147. $this->sync->set_signature_item( $attachment_id, 'upgrade' );
  148. $this->sync->set_signature_item( $attachment_id, 'public_id' );
  149. $this->sync->set_signature_item( $attachment_id, 'storage' );
  150. // Update Sync keys.
  151. $sync_key = $public_id;
  152. $transformations = $this->media->get_transformation_from_meta( $attachment_id );
  153. if ( ! empty( $transformations ) ) {
  154. $sync_key .= wp_json_encode( $transformations );
  155. }
  156. update_post_meta( $attachment_id, '_' . md5( $sync_key ), true );
  157. update_post_meta( $attachment_id, '_' . md5( 'base_' . $public_id ), true );
  158. // Get a new uncached signature.
  159. $this->sync->get_signature( $attachment_id, true );
  160. return $public_id;
  161. }
  162. /**
  163. * Maybe the upgraded attachment is a fetch image.
  164. *
  165. * @param string $path The attachment path.
  166. * @param int $attachment_id The attachment ID.
  167. *
  168. * @return string
  169. */
  170. public function get_fetch_public_id( $path, $attachment_id ) {
  171. $parts = explode( '/image/fetch/', $path );
  172. if ( ! empty( $parts[1] ) ) {
  173. $this->media->update_post_meta( $attachment_id, Sync::META_KEYS['delivery'], 'fetch' );
  174. return $parts[1];
  175. }
  176. return '';
  177. }
  178. /**
  179. * Migrate legacy meta data to new meta.
  180. *
  181. * @param int $attachment_id The attachment ID to migrate.
  182. *
  183. * @return array();
  184. */
  185. public function migrate_legacy_meta( $attachment_id ) {
  186. $old_meta = wp_get_attachment_metadata( $attachment_id, true );
  187. $v2_meta = get_post_meta( $attachment_id, Sync::META_KEYS['cloudinary_legacy'], true );
  188. $v3_meta = array();
  189. // Direct from old meta to v3, create v2 to chain the upgrade path.
  190. if ( isset( $old_meta[ Sync::META_KEYS['cloudinary_legacy'] ] ) && empty( $v2_meta ) ) {
  191. $v2_meta = $old_meta[ Sync::META_KEYS['cloudinary_legacy'] ];
  192. // Add public ID.
  193. $public_id = get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true );
  194. $v2_meta[ Sync::META_KEYS['public_id'] ] = $public_id;
  195. delete_post_meta( $attachment_id, Sync::META_KEYS['public_id'] );
  196. }
  197. // Handle v2 upgrade.
  198. if ( ! empty( $v2_meta ) ) {
  199. // Migrate to v3.
  200. update_post_meta( $attachment_id, Sync::META_KEYS['cloudinary'], $v2_meta );
  201. delete_post_meta( $attachment_id, Sync::META_KEYS['cloudinary_legacy'] );
  202. $v3_meta = $v2_meta;
  203. if ( ! empty( $v3_meta[ Sync::META_KEYS['public_id'] ] ) ) {
  204. // Cleanup from v2.7.7.
  205. if ( ! empty( $v3_meta[ Sync::META_KEYS['storage'] ] ) && 'cld' === $v3_meta[ Sync::META_KEYS['storage'] ] ) {
  206. $file = get_post_meta( $attachment_id, '_wp_attached_file', true );
  207. if ( $this->media->is_cloudinary_url( $file ) ) {
  208. $file = path_join( dirname( $old_meta['file'] ), wp_basename( $file ) );
  209. update_post_meta( $attachment_id, '_wp_attached_file', $file );
  210. update_post_meta( $attachment_id, '_' . md5( $file ), $file );
  211. }
  212. }
  213. }
  214. // Remove old data style.
  215. unset( $old_meta[ Sync::META_KEYS['cloudinary_legacy'] ] );
  216. }
  217. // Attempt to update old meta, which will fail if nothing changed.
  218. update_post_meta( $attachment_id, '_wp_attachment_metadata', $old_meta );
  219. // migrate from pre v2 meta.
  220. if ( empty( $v2_meta ) && empty( $v3_meta ) ) {
  221. // Attempt old post meta.
  222. $public_id = get_post_meta( $attachment_id, Sync::META_KEYS['public_id'], true );
  223. if ( ! empty( $public_id ) ) {
  224. // Loop through all types and create new meta item.
  225. $v3_meta = array(
  226. Sync::META_KEYS['public_id'] => $public_id,
  227. );
  228. update_post_meta( $attachment_id, Sync::META_KEYS['cloudinary'], $v3_meta );
  229. foreach ( Sync::META_KEYS as $meta_key ) {
  230. if ( Sync::META_KEYS['cloudinary'] === $meta_key ) {
  231. // Dont use the root as it will be an infinite loop.
  232. continue;
  233. }
  234. $value = get_post_meta( $attachment_id, $meta_key, true );
  235. if ( ! empty( $value ) ) {
  236. $v3_meta[ $meta_key ] = $value;
  237. $this->media->update_post_meta( $attachment_id, $meta_key, $value );
  238. }
  239. }
  240. }
  241. }
  242. return $v3_meta;
  243. }
  244. /**
  245. * Setup hooks for the filters.
  246. */
  247. public function setup_hooks() {
  248. // Add filter to manage legacy items.
  249. // @todo: cleanup `convert_cloudinary_version` by v2 upgrades to here.
  250. add_filter( 'cloudinary_migrate_legacy_meta', array( $this, 'migrate_legacy_meta' ) );
  251. // Add a redirection to the new plugin settings, from the old plugin.
  252. if ( is_admin() ) {
  253. add_action(
  254. 'admin_menu',
  255. function () {
  256. global $plugin_page;
  257. if ( ! empty( $plugin_page ) && false !== strpos( $plugin_page, 'cloudinary-image-management-and-manipulation-in-the-cloud-cdn' ) ) {
  258. wp_safe_redirect( admin_url( '?page=cloudinary' ) );
  259. die;
  260. }
  261. }
  262. );
  263. }
  264. }
  265. }