PluginProbe
WP-Stateless – Google Cloud Storage / 2.3.0
WP-Stateless – Google Cloud Storage v2.3.0
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 2.3.0, at lib/classes/compatibility/shortpixel.php

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