PluginProbe
WP-Stateless – Google Cloud Storage / trunk
WP-Stateless – Google Cloud Storage vtrunk
4.4.3 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.3.0 2.3.1 2.3.2 3.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 All 62 releases
wp-stateless / lib / classes / compatibility / shortpixel.php

shortpixel.php in WP-Stateless – Google Cloud Storage trunk, at lib/classes/compatibility/shortpixel.php

489 lines 20.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Compatibility Plugin Name: ShortPixel Image Optimizer
5 * Compatibility Plugin URI: https://wordpress.org/plugins/shortpixel-image-optimiser/
6 *
7 * Compatibility Description: Ensures compatibility with ShortPixel Image Optimizer.
8 *
9 * We don't need to download image to server from GCS to optimize image. As it can directly use GCS URL.
10 *
11 * @todo Use backup version of image on Regenerate and Sync with GCS.
12 * @todo Restore image now download from backup dir then upload to regular GCS path again. We need to fix that.
13 * https://cloud.google.com/storage/docs/renaming-copying-moving-objects#copy
14 * @todo implement a customized version of ShortPixelMetaFacade::getURLsAndPATHs() function.
15 * @todo convert GCS URL from http://storage.googleapis.com/udx-ci-develop-alim/ to http://udx-ci-develop-alim.storage.googleapis.com
16 */
17
18 namespace wpCloud\StatelessMedia {
19
20 if (!class_exists('wpCloud\StatelessMedia\ShortPixel')) {
21
22 class ShortPixel extends Compatibility {
23
24 protected $id = 'shortpixel';
25 protected $title = 'ShortPixel Image Optimizer';
26 protected $constant = 'WP_STATELESS_COMPATIBILITY_SHORTPIXEL';
27 protected $description = 'Ensures compatibility with ShortPixel Image Optimizer.';
28 protected $plugin_file = 'shortpixel-image-optimiser/wp-shortpixel.php';
29 protected $sm_mode_not_supported = ['stateless'];
30 protected $enabled = false;
31 protected $is_internal = true;
32
33 /**
34 * @param $sm
35 */
36 public function module_init($sm) {
37 add_action('shortpixel_image_optimised', array($this, 'shortpixel_image_optimised'));
38 add_filter('shortpixel_image_exists', array($this, 'shortpixel_image_exists'), 10, 3);
39 // A way to disable the URL conversion to subdomain format.
40 if (!defined('WP_STATELESS_MEDIA_SHORTPIXEL_DISABLE_SUBDOMAIN_LINK') || !WP_STATELESS_MEDIA_SHORTPIXEL_DISABLE_SUBDOMAIN_LINK) {
41 // URL conversion to subdomain format {bucket}.storage.googleapis.com instead of storage.googleapis.com/{bucket}
42 // So that ShortPixel don't count all Stateless request as one domain.
43 add_filter('shortpixel_image_urls', array($this, 'shortpixel_image_urls'), 10, 2);
44 }
45 add_filter('shortpixel_skip_backup', array($this, 'shortpixel_skip_backup'), 10, 3);
46 add_filter('wp_update_attachment_metadata', array($this, 'wp_update_attachment_metadata'), 10, 2);
47 // Remove backup and webp version of image.
48 add_filter('shortpixel_skip_delete_backups_and_webps', array($this, 'shortpixel_skip_delete_backups_and_webps'), 10, 2);
49 add_filter('shortpixel_backup_folder', array($this, 'getBackupFolderAny'), 10, 3);
50 add_filter('wp_stateless_add_media_args', array($this, 'wp_stateless_add_media_args'));
51 add_filter('shortpixel_webp_image_base', array($this, 'shortpixel_webp_image_base'), 10, 2);
52 // add_filter( 'shortpixel_skip_restore_image', array( $this, 'shortpixel_skip_restore_image' ), 10, 2 );
53
54 add_action('shortpixel_before_restore_image', array($this, 'shortpixel_before_restore_image'));
55 add_action('shortpixel_after_restore_image', array($this, 'handleRestoreBackup'));
56 add_action('admin_enqueue_scripts', array($this, 'shortPixelJS'));
57 // Sync from sync tab
58 add_action('sm:synced::image', array($this, 'sync_backup_file'), 10, 2);
59 add_action('sm:synced::image', array($this, 'sync_webp_file'), 10, 2);
60 }
61
62 public function shortPixelJS() {
63 $upload_dir = wp_upload_dir();
64
65 wp_enqueue_script('stateless-short-pixel', ud_get_stateless_media()->path('lib/classes/compatibility/js/shortpixel.js', 'url'), array('shortpixel'), '', true);
66
67 $image_host = ud_get_stateless_media()->get_gs_host();
68 $bucketLink = apply_filters('wp_stateless_bucket_link', $image_host);
69
70 wp_localize_script('stateless-short-pixel', '_stateless_short_pixel', array('baseurl' => $upload_dir['baseurl'], 'bucketLink' => $bucketLink,));
71 }
72
73 /**
74 * Return Path of ShortPixel backup path.
75 * Bypass the checking whether a backup file exist when returning backup path.
76 * @todo check on GCS if backup really exist. Maybe we can implement transient caching for performance.
77 *
78 * @param $ret
79 * @param $file
80 * @param $thumbs
81 * @return string
82 */
83 public function getBackupFolderAny($ret, $file, $thumbs) {
84 if ($ret == false) {
85 $fullSubDir = $this->returnSubDir($file);
86 $ret = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir;
87 }
88 return $ret;
89 }
90
91 /**
92 * Check whether image exist on GCS.
93 * Only when image is not available on server.
94 *
95 * @param $return
96 * @param $path
97 * @param null $id
98 * @return bool
99 */
100 public function shortpixel_image_exists($return, $path, $id = null) {
101 if ($return) return $return;
102
103 $key = "stateless_url_to_postid_" . md5($path);
104 $return = get_transient($key);
105 // echo "\npath: $path \nKey: $key\nReturn: $return\nID: $id\n ";
106 if (!$return) {
107 // Checking by matching file name in gs_name and $path.
108 if (!empty($id)) {
109 $metadata = wp_get_attachment_metadata($id);
110 $basename = basename($path);
111 if (!empty($metadata['gs_name'])) {
112 $gs_basename = basename($metadata['gs_name']);
113 if ($gs_basename == $basename) {
114 $return = true;
115 }
116
117 if (is_array($metadata['sizes'])) {
118 foreach ($metadata['sizes'] as $key => &$data) {
119 if (empty($data['gs_name'])) continue;
120 $gs_basename = basename($data['gs_name']);
121 if ($gs_basename == $basename) {
122 $return = true;
123 }
124 }
125 }
126 }
127 } // Directly check on GCS if image exist.
128 else if (empty($id)) {
129 $wp_uploads_dir = wp_get_upload_dir();
130 $gs_name = str_replace(trailingslashit($wp_uploads_dir['basedir']), '', $path);
131 $gs_name = str_replace(trailingslashit($wp_uploads_dir['baseurl']), '', $gs_name);
132 $gs_name = str_replace(trailingslashit(ud_get_stateless_media()->get_gs_host()), '', $gs_name);
133 $gs_name = apply_filters('wp_stateless_file_name', $gs_name);
134 if ($media = ud_get_stateless_media()->get_client()->media_exists($gs_name)) {
135 $return = true;
136 }
137 }
138 set_transient($key, $return, 10 * MINUTE_IN_SECONDS);
139 }
140 return $return;
141 }
142
143 /**
144 * Short-circuit filter.
145 * We need to remove backup image from GCS when shortpixel tries to delete from server.
146 * We are returning true to short-circuit in ephemeral mode, because file not exist in server.
147 *
148 * @param $return
149 * @param $paths
150 * @return bool
151 */
152 public function shortpixel_skip_delete_backups_and_webps($return, $paths) {
153 if (empty($paths) || !is_array($paths)) return $return;
154
155 $sp__uploads = wp_upload_dir();
156 $fullSubDir = $this->returnSubDir($paths[0]);
157 $backup_path = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir;
158
159 foreach ($paths as $key => $path) {
160 // Removing backup
161 $name = apply_filters('wp_stateless_file_name', SHORTPIXEL_BACKUP . '/' . $fullSubDir . basename($path));
162 do_action('sm:sync::deleteFile', $name);
163
164 // Removing WebP
165 $backup_images = \WPShortPixelSettings::getOpt('wp-short-create-webp');
166 if ($backup_images) {
167 $name = str_replace($sp__uploads['basedir'], '', $path);
168 $name = apply_filters('wp_stateless_file_name', $name . '.webp');
169 do_action('sm:sync::deleteFile', $name);
170 }
171 }
172
173 if (ud_get_stateless_media()->get('sm.mode') == 'ephemeral') {
174 return true;
175 }
176 // When ephemeral mode isn't set backup files will be available in server. So no short-circuit.
177 return $return;
178 }
179
180 /**
181 * Skip the original backup process in ephemeral mode.
182 *
183 * @param $return
184 * @param $mainPath
185 * @param $PATHs
186 * @return bool
187 */
188 public function shortpixel_skip_backup($return, $mainPath, $PATHs) {
189 if (ud_get_stateless_media()->get('sm.mode') == 'ephemeral') {
190 return true;
191 }
192 return $return;
193 }
194
195 /**
196 * In ephemeral mode we need to take backup directly from original location.
197 * Because we will skip the backup process of shortpixel.
198 *
199 * @param $metadata
200 * @param $attachment_id
201 * @return mixed
202 */
203 public function wp_update_attachment_metadata($metadata, $attachment_id) {
204 if (ud_get_stateless_media()->get('sm.mode') == 'ephemeral') {
205 $backup_images = \WPShortPixelSettings::getOpt('wp-short-backup_images');
206 if ($backup_images) {
207 $this->sync_backup_file($attachment_id, $metadata, false, array('before_optimization' => true));
208 }
209 }
210 return $metadata;
211 }
212
213 /**
214 * Sync image after optimization.
215 *
216 * @param $id
217 */
218 public function shortpixel_image_optimised($id) {
219 $metadata = wp_get_attachment_metadata($id);
220 ud_get_stateless_media()->add_media($metadata, $id, true);
221 // Sync the webp to GCS
222 $create_webp = \WPShortPixelSettings::getOpt('wp-short-create-webp');
223 if ($create_webp) {
224 $this->sync_webp_file($id, $metadata);
225 }
226 // Don't needed in ephemeral mode. In ephemeral mode the back will be sync once on wp_update_attachment_metadata filter.
227 if (ud_get_stateless_media()->get('sm.mode') !== 'ephemeral') {
228 $this->sync_backup_file($id, $metadata, true);
229 }
230 }
231
232 /**
233 * Before shortpixel tries to restore from backup we need to make files available on server.
234 *
235 * @param $id
236 * @param null $metadata
237 */
238 public function shortpixel_before_restore_image($id, $metadata = null) {
239 $this->sync_backup_file($id, $metadata, true, array('download' => true));
240 }
241
242 /**
243 * Disable default shortpixel restore and directly update image on GCS from backup copy in GCS.
244 *
245 * @param $return
246 * @param null $id
247 * @return bool
248 */
249 public function shortpixel_skip_restore_image($return, $id = null) {
250 if (ud_get_stateless_media()->get('sm.mode') === 'ephemeral') {
251 $this->client = ud_get_stateless_media()->get_client();
252 $this->client->copy_media('localhost/ShortpixelBackups/wp-content/uploads/2019/04/htpps.png', 'localhost/2019/04/htpps.png');
253 return true;
254 }
255 return $return;
256 }
257
258 /**
259 * Sync backup image
260 *
261 * @param $id
262 * @param null $metadata
263 * @param bool $force
264 * @param array $args
265 * before_optimization : pass true if you want to sync directly from original path instead of backup path.
266 */
267 public function sync_backup_file($id, $metadata = null, $force = false, $args = array()) {
268 $args = wp_parse_args($args, array(
269 'download' => false, // whether to only download.
270 'before_optimization' => false, // whether to delete local file in ephemeral mode.
271 ));
272
273 /* Get metadata in case if method is called directly. */
274 if (empty($metadata)) {
275 $metadata = wp_get_attachment_metadata($id);
276 }
277 /* Now we go through all available image sizes and upload them to Google Storage */
278 if (!empty($metadata['sizes']) && is_array($metadata['sizes'])) {
279
280 // Sync backup file with GCS
281 $file_path = get_attached_file($id);
282 $fullSubDir = $this->returnSubDir($file_path);
283 $backup_path = SHORTPIXEL_BACKUP_FOLDER . '/' . $fullSubDir;
284 if ($args['before_optimization']) {
285 $upload_dir = wp_upload_dir();
286 $backup_path = $upload_dir['basedir'] . '/' . dirname($metadata['file']);
287 $args = array('ephemeral' => false);
288 }
289
290 $absolutePath = trailingslashit($backup_path) . basename($metadata['file']);
291 $name = apply_filters('wp_stateless_file_name', SHORTPIXEL_BACKUP . '/' . $fullSubDir . basename($metadata['file']));
292 do_action('sm:sync::syncFile', $name, $absolutePath, $force, $args);
293
294 foreach ((array) $metadata['sizes'] as $image_size => $data) {
295 $absolutePath = trailingslashit($backup_path) . $data['file'];
296 $name = apply_filters('wp_stateless_file_name', SHORTPIXEL_BACKUP . '/' . $fullSubDir . $data['file']);
297
298 do_action('sm:sync::syncFile', $name, $absolutePath, $force, $args);
299 }
300 }
301 }
302
303 /**
304 * Sync from sync tab
305 *
306 * @param $id
307 * @param null $metadata
308 */
309 public function sync_webp_file($id, $metadata = null) {
310 /* Get metadata in case if method is called directly. */
311 if (empty($metadata)) {
312 $metadata = wp_get_attachment_metadata($id);
313 }
314 add_filter('upload_mimes', array($this, 'add_webp_mime'), 10, 2);
315 // Sync the webp to GCS
316 ud_get_stateless_media()->add_media($metadata, $id, true, array('is_webp' => '.webp'));
317 remove_filter('upload_mimes', array($this, 'add_webp_mime'), 10);
318 }
319
320 /**
321 * return subdir for that particular attached file - if it's media library then last 3 path items, otherwise substract the uploads path
322 * Has trailing directory separator (/)
323 *
324 * @copied from shortpixel-image-optimiser\class\db\shortpixel-meta-facade.php
325 * @param type $file
326 * @return string
327 */
328 public function returnSubDir($file) {
329 $hp = wp_normalize_path(get_home_path());
330 $file = wp_normalize_path($file);
331 $sp__uploads = wp_upload_dir();
332 if (strstr($file, $hp)) {
333 $path = str_replace($hp, "", $file);
334 } elseif (strstr($file, dirname(WP_CONTENT_DIR))) { //in some situations the content dir is not inside the root, check this also (ex. single.shortpixel.com)
335 $path = str_replace(trailingslashit(dirname(WP_CONTENT_DIR)), "", $file);
336 } elseif ((strstr(realpath($file), realpath($hp)))) {
337 $path = str_replace(realpath($hp), "", realpath($file));
338 } elseif (strstr($file, trailingslashit(dirname(dirname($sp__uploads['basedir']))))) {
339 $path = str_replace(trailingslashit(dirname(dirname($sp__uploads['basedir']))), "", $file);
340 } else {
341 $path = (substr($file, 1));
342 }
343 $pathArr = explode('/', $path);
344 unset($pathArr[count($pathArr) - 1]);
345 return implode('/', $pathArr) . '/';
346 }
347
348 /**
349 * Sync images after shortpixel restore them from backup.
350 *
351 * @param $attachmentID
352 */
353 public function handleRestoreBackup($attachmentID) {
354 $metadata = wp_get_attachment_metadata($attachmentID);
355 $this->add_media($metadata, $attachmentID);
356 }
357
358 /**
359 * Customized version of wpCloud\StatelessMedia\Utility::add_media()
360 * to satisfied our need in restore backup
361 * If a image isn't restored from backup then ignore it.
362 *
363 * @param $metadata
364 * @param $attachment_id
365 */
366 public static function add_media($metadata, $attachment_id) {
367 $upload_dir = wp_upload_dir();
368
369 $client = ud_get_stateless_media()->get_client();
370
371 if (!is_wp_error($client)) {
372
373 $fullsizepath = wp_normalize_path(get_attached_file($attachment_id));
374 // Make non-images uploadable.
375 if (empty($metadata['file']) && $attachment_id) {
376 $metadata = array("file" => str_replace(trailingslashit($upload_dir['basedir']), '', get_attached_file($attachment_id)));
377 }
378
379 $file = wp_normalize_path($metadata['file']);
380 $image_host = ud_get_stateless_media()->get_gs_host();
381 $bucketLink = apply_filters('wp_stateless_bucket_link', $image_host);
382 $_cacheControl = \wpCloud\StatelessMedia\Utility::getCacheControl($attachment_id, $metadata, null);
383 $_contentDisposition = \wpCloud\StatelessMedia\Utility::getContentDisposition($attachment_id, $metadata, null);
384 $_metadata = array("width" => isset($metadata['width']) ? $metadata['width'] : null, "height" => isset($metadata['height']) ? $metadata['height'] : null, 'object-id' => $attachment_id, 'source-id' => md5($attachment_id . ud_get_stateless_media()->get('sm.bucket')), 'file-hash' => md5($metadata['file']));
385
386 if (file_exists($fullsizepath)) {
387 $file = apply_filters('wp_stateless_file_name', $file);
388
389 /* Add default image */
390 $media = $client->add_media($_mediaOptions = array_filter(array('force' => true, 'name' => $file, 'absolutePath' => wp_normalize_path(get_attached_file($attachment_id)), 'cacheControl' => $_cacheControl, 'contentDisposition' => $_contentDisposition, 'mimeType' => get_post_mime_type($attachment_id), 'metadata' => $_metadata)));
391
392 // ephemeral mode: we don't need the local version.
393 if (ud_get_stateless_media()->get('sm.mode') === 'ephemeral') {
394 unlink($fullsizepath);
395 }
396 }
397
398 /* Now we go through all available image sizes and upload them to Google Storage */
399 if (!empty($metadata['sizes']) && is_array($metadata['sizes'])) {
400
401 $path = wp_normalize_path(dirname(get_attached_file($attachment_id)));
402 $mediaPath = apply_filters('wp_stateless_file_name', trim(dirname($metadata['file']), '\/\\'));
403
404 foreach ((array) $metadata['sizes'] as $image_size => $data) {
405
406 $absolutePath = wp_normalize_path($path . '/' . $data['file']);
407
408 if (!file_exists($absolutePath)) {
409 continue;
410 }
411
412 /* Add 'image size' image */
413 $media = $client->add_media(array('force' => true, 'name' => $file_path = trim($mediaPath . '/' . $data['file'], '/'), 'absolutePath' => $absolutePath, 'cacheControl' => $_cacheControl, 'contentDisposition' => $_contentDisposition, 'mimeType' => $data['mime-type'], 'metadata' => array_merge($_metadata, array('width' => $data['width'], 'height' => $data['height'], 'child-of' => $attachment_id, 'file-hash' => md5($data['file'])))));
414
415 /* Break if we have errors. */
416 if (!is_wp_error($media)) {
417 // ephemeral mode: we don't need the local version.
418 if (ud_get_stateless_media()->get('sm.mode') === 'ephemeral') {
419 unlink($absolutePath);
420 }
421 }
422 }
423 }
424 }
425 }
426 // End add_media
427
428 /**
429 * modifying gs_name and absolutePath so that we can upload webp image using the same Utility::add_media function.
430 *
431 * @param $args
432 * @return mixed
433 */
434 public function wp_stateless_add_media_args($args) {
435 if (!empty($args['is_webp']) && $args['is_webp']) {
436 if (\file_exists($args['absolutePath'] . '.webp')) {
437 $args['name'] = $args['name'] . '.webp';
438 $args['absolutePath'] = $args['absolutePath'] . '.webp';
439 } else {
440 $pathinfo = pathinfo($args['absolutePath']);
441 $absolutePath = trailingslashit($pathinfo['dirname']) . $pathinfo['filename'] . '.webp';
442 if (file_exists($absolutePath)) {
443 $args['name'] = $args['name'] . '.webp';
444 $args['absolutePath'] = $absolutePath;
445 }
446 }
447 $args['mimeType'] = 'image/webp';
448 }
449 return $args;
450 }
451
452 /**
453 * Bypass server url check and return base url for GCS image.
454 *
455 * @param $imageBase
456 * @param $src
457 * @return mixed
458 */
459 public function shortpixel_webp_image_base($imageBase, $src) {
460 $gs_link = \ud_get_stateless_media()->convert_to_gs_link($src, true);
461 if ($gs_link) {
462 $imageBase = trailingslashit(dirname($gs_link));
463 }
464 return $imageBase;
465 }
466
467 /**
468 * @param $URLs
469 * @param $id
470 * @return mixed
471 */
472 public function shortpixel_image_urls($URLs, $id) {
473 foreach ($URLs as $key => $url) {
474 $url_parts = wp_parse_url($url);
475 if ($url_parts['host'] == 'storage.googleapis.com') {
476 if (preg_match("@(^/?.*?/)(.*)@", $url_parts['path'], $matches)) {
477 $bucket = trim($matches[1], '/');
478 $url_parts['path'] = $matches[2];
479 $url_parts['host'] = $bucket . '.' . $url_parts['host'];
480 $URLs[$key] = Utility::join_url($url_parts);
481 }
482 }
483 }
484 return $URLs;
485 }
486 }
487 }
488 }
489