PluginProbe ʕ •ᴥ•ʔ
Smush – Image Optimization, Compression, Lazy Load, WebP & CDN / 2.4.3
Smush – Image Optimization, Compression, Lazy Load, WebP & CDN v2.4.3
4.1.0 4.0.3 4.0.2 2.8.1 2.9.1 3.0.0 3.0.1 3.0.2 3.1.1 3.10.1 3.10.2 3.10.3 3.11.1 3.12.3 3.12.4 3.12.5 3.12.6 3.13.0 3.13.1 3.13.2 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.15.2 3.15.3 3.15.4 3.15.5 3.16.2 3.16.4 3.16.5 3.16.6 3.17.0 3.17.1 3.18.0 3.18.1 3.2.0.1 3.2.1 3.2.2.1 3.2.4 3.20.0 3.21.1 3.22.1 3.22.3 3.23.0 3.23.1 3.23.2 3.23.3 3.23.4 3.24.0 3.24.0-beta.2 3.3.0 3.3.1 3.3.2 3.4.1 3.4.2 3.6.1 3.6.3 3.7.0 3.7.1 3.7.2 3.7.3 3.8.2 3.8.3 3.8.4 3.8.5 3.8.7 3.8.8 3.9.0 3.9.1 3.9.11 3.9.2 3.9.4 3.9.5 3.9.8 3.9.9 trunk 1.0.0 1.0.1 1.0.2 1.1 1.1.1 1.1.2 1.1.3 1.2 1.2.1 1.2.10 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.5.1 1.6.5.2 1.6.5.3 1.6.5.4 1.7 1.7.1 1.7.1.1 2.0 2.0.1 2.0.3 2.0.4 2.0.5 2.0.6 2.0.6.2 2.0.6.3 2.0.6.5 2.0.7 2.0.7.1 2.1 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.2 2.2.1 2.2.2 2.3 2.3.1 2.4 2.4.2 2.4.3 2.4.4 2.4.5 2.5.2 2.5.3 2.6.1 2.6.2 2.6.3 2.7 2.7.1 2.7.4 2.7.4.1 2.7.5 2.7.6 2.7.8 2.7.8.1 2.7.9.1 2.8.0 2.8.0.1
wp-smushit / lib / class-wp-smush.php
wp-smushit / lib Last commit date
nextgen-integration 9 years ago class-wp-smush-admin.php 9 years ago class-wp-smush-backup.php 9 years ago class-wp-smush-bulk.php 9 years ago class-wp-smush-migrate.php 10 years ago class-wp-smush-nextgen.php 9 years ago class-wp-smush-png_jpg.php 9 years ago class-wp-smush-resize.php 9 years ago class-wp-smush-share.php 9 years ago class-wp-smush-stats.php 9 years ago class-wp-smush-ui.php 9 years ago class-wp-smush.php 9 years ago
class-wp-smush.php
1954 lines
1 <?php
2 //Migration Class
3 require_once WP_SMUSH_DIR . "lib/class-wp-smush-migrate.php";
4
5 //Stats
6 require_once WP_SMUSH_DIR . "lib/class-wp-smush-stats.php";
7
8 //Include Resize class
9 require_once WP_SMUSH_DIR . 'lib/class-wp-smush-resize.php';
10
11 //Include PNG to JPG Converter
12 require_once WP_SMUSH_DIR . 'lib/class-wp-smush-png_jpg.php';
13
14 //Include Social Sharing
15 require_once WP_SMUSH_DIR . 'lib/class-wp-smush-share.php';
16
17 //Include Image backup class
18 require_once WP_SMUSH_DIR . 'lib/class-wp-smush-backup.php';
19
20 if ( ! class_exists( 'WpSmush' ) ) {
21
22 class WpSmush {
23
24 var $version = WP_SMUSH_VERSION;
25
26 /**
27 * @var Stores the value of is_pro function
28 */
29 private $is_pro;
30
31 /**
32 * Api server url to check api key validity
33 *
34 */
35 var $api_server = 'https://premium.wpmudev.org/api/smush/v1/check/';
36
37 /**
38 * Meta key to save smush result to db
39 *
40 *
41 */
42 var $smushed_meta_key = 'wp-smpro-smush-data';
43
44 /**
45 * Meta key to save migrated version
46 *
47 */
48 var $migrated_version_key = "wp-smush-migrated-version";
49
50 /**
51 * Super Smush is enabled or not
52 * @var bool
53 */
54 var $lossy_enabled = false;
55
56 /**
57 * Whether to Smush the original Image
58 * @var bool
59 */
60 var $smush_original = false;
61
62 /**
63 * Whether to Preserver the exif data or not
64 * @var bool
65 */
66 var $keep_exif = false;
67
68 /** @var Attachment id for the Image being smushed currently */
69 var $attachment_id;
70 /**
71 * Attachment type, being smushed currently
72 *
73 * @var string Possible values, "wp", "nextgen"
74 *
75 */
76 var $media_type = 'wp';
77
78 /**
79 * Constructor
80 */
81 function __construct() {
82
83 //Redirect to Settings page
84 add_action( 'activated_plugin', array( $this, 'wp_smush_redirect' ) );
85
86 /**
87 * Smush image (Auto Smush ) when `wp_update_attachment_metadata` filter is fired
88 */
89 add_filter( 'wp_update_attachment_metadata', array(
90 $this,
91 'smush_image'
92 ), 15, 2 );
93
94 //Delete Backup files
95 add_action( 'delete_attachment', array(
96 $this,
97 'delete_images'
98 ), 12 );
99
100 //Optimise WP Retina 2x images
101 add_action( 'wr2x_retina_file_added', array( $this, 'smush_retina_image' ), 20, 3 );
102
103 //Add Smush Columns
104 add_filter( 'manage_media_columns', array( $this, 'columns' ) );
105 add_action( 'manage_media_custom_column', array( $this, 'custom_column' ), 10, 2 );
106 add_filter( 'manage_upload_sortable_columns', array( $this, 'sortable_column' ) );
107 //Manage column sorting
108 add_action( 'pre_get_posts', array( $this, 'smushit_orderby' ) );
109
110 //Enqueue Scripts, And Initialize variables
111 add_action( 'admin_init', array( $this, 'admin_init' ) );
112
113 //Load Translation files
114 add_action( 'plugins_loaded', array( $this, 'i18n' ), 12 );
115
116 //Load NextGen Gallery, if hooked too late or early, auto smush doesn't works, also Load after settings have been saved on init action
117 add_action( 'plugins_loaded', array( $this, 'load_nextgen' ), 90 );
118
119 //Send Smush Stats for pro members
120 add_filter( 'wpmudev_api_project_extra_data-912164', array( $this, 'send_smush_stats') );
121
122 }
123
124 function i18n() {
125 load_plugin_textdomain( 'wp-smushit', false, WP_SMUSH_DIR . '/languages/' );
126 }
127
128 /**
129 * Initialise the setting variables
130 */
131 function initialise() {
132 //Check if Lossy enabled
133 $opt_lossy = WP_SMUSH_PREFIX . 'lossy';
134 $this->lossy_enabled = $this->validate_install() && get_option( $opt_lossy, false );
135
136 //Check if Smush Original enabled
137 $opt_original = WP_SMUSH_PREFIX . 'original';
138 $this->smush_original = $this->validate_install() && get_option( $opt_original, false );
139
140 //Check Whether to keep exif or not
141 $opt_keep_exif = WP_SMUSH_PREFIX . 'keep_exif';
142 $this->keep_exif = get_option( $opt_keep_exif, false );
143 }
144
145 function admin_init() {
146
147 //Handle Notice dismiss
148 $this->dismiss_smush_upgrade();
149
150 //Perform Migration if required
151 $this->migrate();
152
153 //Initialize variables
154 $this->initialise();
155 }
156
157 /**
158 * Process an image with Smush.
159 *
160 * Returns an array of the $file $results.
161 *
162 * @param string $file Full absolute path to the image file
163 * @param string $file_url Optional full URL to the image file
164 *
165 * @returns array
166 */
167 function do_smushit( $file_path = '' ) {
168 global $wpsmushit_admin;
169 $errors = new WP_Error();
170 $dir_name = dirname( $file_path );
171 if ( empty( $file_path ) ) {
172 $errors->add( "empty_path", __( "File path is empty", 'wp-smushit' ) );
173 }
174
175 // check that the file exists
176 if ( ! file_exists( $file_path ) || ! is_file( $file_path ) ) {
177 $errors->add( "file_not_found", sprintf( __( "Could not find %s", 'wp-smushit' ), $file_path ) );
178 }
179
180 // check that the file is writable
181 if ( ! is_writable( $dir_name ) ) {
182 $errors->add( "not_writable", sprintf( __( "%s is not writable", 'wp-smushit' ), $dir_name ) );
183 }
184
185 $file_size = file_exists( $file_path ) ? filesize( $file_path ) : '';
186
187 //Check if premium user
188 $max_size = $this->validate_install() ? WP_SMUSH_PREMIUM_MAX_BYTES : WP_SMUSH_MAX_BYTES;
189
190 //Check if file exists
191 if ( $file_size == 0 ) {
192 $errors->add( "image_not_found", '<p>' . sprintf( __( 'Skipped (%s), image not found. Attachment: %s', 'wp-smushit' ), $this->format_bytes( $file_size ), basename( $file_path ) ) . '</p>' );
193 }
194
195 //Check size limit
196 if ( $file_size > $max_size ) {
197 $errors->add( "size_limit", '<p>' . sprintf( __( 'Skipped (%s), size limit exceeded. Attachment: %s', 'wp-smushit' ), $this->format_bytes( $file_size ), basename( $file_path ) ) . '</p>' );
198 }
199
200 if ( count( $errors->get_error_messages() ) ) {
201 return $errors;
202 }
203
204 // save original file permissions
205 clearstatcache();
206 $perms = fileperms($file_path) & 0777;
207
208 /** Send image for smushing, and fetch the response */
209 $response = $this->_post( $file_path, $file_size );
210
211 if ( ! $response['success'] ) {
212 $errors->add( "false_response", $response['message'] );
213 }
214 //If there is no data
215 if ( empty( $response['data'] ) ) {
216 $errors->add( "no_data", __( 'Unknown API error', 'wp-smushit' ) );
217 }
218
219 if ( count( $errors->get_error_messages() ) ) {
220 return $errors;
221 }
222
223 //If there are no savings, or image returned is bigger in size
224 if ( ( ! empty( $response['data']->bytes_saved ) && intval( $response['data']->bytes_saved ) <= 0 )
225 || empty( $response['data']->image )
226 ) {
227 return $response;
228 }
229 $tempfile = $file_path . ".tmp";
230
231 //Add the file as tmp
232 file_put_contents( $tempfile, $response['data']->image );
233
234 //Take Backup
235 global $wpsmush_backup;
236 $wpsmush_backup->create_backup( $file_path );
237
238 //replace the file
239 $success = @rename( $tempfile, $file_path );
240
241 //if tempfile still exists, unlink it
242 if ( file_exists( $tempfile ) ) {
243 @unlink( $tempfile );
244 }
245
246 //If file renaming failed
247 if ( ! $success ) {
248 @copy( $tempfile, $file_path );
249 @unlink( $tempfile );
250 }
251
252 //Some servers are having issue with file permission, this should fix it
253 if( empty( $perms ) || !$perms ) {
254 //Source: WordPress Core
255 $stat = stat( dirname( $file_path ) );
256 $perms = $stat['mode'] & 0000666; //same permissions as parent folder, strip off the executable bits
257 }
258 @ chmod( $file_path, $perms );
259
260 return $response;
261 }
262
263 /**
264 * Fills $placeholder array with values from $data array
265 *
266 * @param array $placeholders
267 * @param array $data
268 *
269 * @return array
270 */
271 function _array_fill_placeholders( array $placeholders, array $data ) {
272 $placeholders['percent'] = $data['compression'];
273 $placeholders['bytes'] = $data['bytes_saved'];
274 $placeholders['size_before'] = $data['before_size'];
275 $placeholders['size_after'] = $data['after_size'];
276 $placeholders['time'] = $data['time'];
277
278 return $placeholders;
279 }
280
281 /**
282 * Returns signature for single size of the smush api message to be saved to db;
283 *
284 * @return array
285 */
286 function _get_size_signature() {
287 return array(
288 'percent' => 0,
289 'bytes' => 0,
290 'size_before' => 0,
291 'size_after' => 0,
292 'time' => 0
293 );
294 }
295
296 /**
297 * Optimises the image sizes
298 *
299 * Read the image paths from an attachment's meta data and process each image
300 * with wp_smushit().
301 *
302 * @param $meta
303 * @param null $ID
304 *
305 * @return mixed
306 */
307 function resize_from_meta_data( $meta, $ID = null ) {
308
309 //Flag to check, if original size image should be smushed or not
310 $original = get_option( WP_SMUSH_PREFIX . 'original' );
311 $smush_full = ( $this->validate_install() && $original == 1 ) ? true : false;
312
313 $errors = new WP_Error();
314 $stats = array(
315 "stats" => array_merge( $this->_get_size_signature(), array(
316 'api_version' => - 1,
317 'lossy' => - 1,
318 'keep_exif' => false
319 )
320 ),
321 'sizes' => array()
322 );
323
324 $size_before = $size_after = $compression = $total_time = $bytes_saved = 0;
325
326 if ( $ID && wp_attachment_is_image( $ID ) === false ) {
327 return $meta;
328 }
329
330 //Set attachment id and Media type
331 $this->attachment_id = $ID;
332 $this->media_type = "wp";
333
334 //File path and URL for original image
335 $attachment_file_path = get_attached_file( $ID );
336
337 // If images has other registered size, smush them first
338 if ( ! empty( $meta['sizes'] ) ) {
339
340 //if smush original is set to false, otherwise smush
341 //Check for large size, we will set a flag to leave the original untouched
342 if ( ! $smush_full ) {
343 if ( array_key_exists( 'large', $meta['sizes'] ) ) {
344 $smush_full = false;
345 } else {
346 $smush_full = true;
347 }
348 }
349
350 if ( class_exists( 'finfo' ) ) {
351 $finfo = new finfo( FILEINFO_MIME_TYPE );
352 } else {
353 $finfo = false;
354 }
355
356 global $wpsmushit_admin;
357 foreach ( $meta['sizes'] as $size_key => $size_data ) {
358
359 //Check if registered size is supposed to be Smushed or not
360 if( 'full' != $size_key && $wpsmushit_admin->skip_image_size( $size_key ) ) {
361 continue;
362 }
363
364 // We take the original image. The 'sizes' will all match the same URL and
365 // path. So just get the dirname and replace the filename.
366 $attachment_file_path_size = path_join( dirname( $attachment_file_path ), $size_data['file'] );
367
368 if ( $finfo ) {
369 $ext = file_exists( $attachment_file_path_size ) ? $finfo->file( $attachment_file_path_size ) : '';
370 } elseif ( function_exists( 'mime_content_type' ) ) {
371 $ext = mime_content_type( $attachment_file_path_size );
372 } else {
373 $ext = false;
374 }
375 if ( $ext ) {
376 $valid_mime = array_search(
377 $ext,
378 array(
379 'jpg' => 'image/jpeg',
380 'png' => 'image/png',
381 'gif' => 'image/gif',
382 ),
383 true
384 );
385 if ( false === $valid_mime ) {
386 continue;
387 }
388 }
389 /**
390 * Allows to skip a image from smushing
391 *
392 * @param bool , Smush image or not
393 * @$size string, Size of image being smushed
394 */
395 $smush_image = apply_filters( 'wp_smush_media_image', true, $size_key );
396 if ( ! $smush_image ) {
397 continue;
398 }
399
400 //Store details for each size key
401 $response = $this->do_smushit( $attachment_file_path_size );
402
403 if ( is_wp_error( $response ) ) {
404 return $response;
405 }
406
407 //If there are no stats
408 if( empty( $response['data'] ) ) {
409 continue;
410 }
411
412 //If the image size grew after smushing, skip it
413 if( $response['data']->after_size > $response['data']->before_size ) {
414 continue;
415 }
416
417 //All Clear, Store the stat
418 //@todo: Move the existing stats code over here, we don't need to do the stats part twice
419 $stats['sizes'][ $size_key ] = (object) $this->_array_fill_placeholders( $this->_get_size_signature(), (array) $response['data'] );
420
421 if ( empty( $stats['stats']['api_version'] ) || $stats['stats']['api_version'] == - 1 ) {
422 $stats['stats']['api_version'] = $response['data']->api_version;
423 $stats['stats']['lossy'] = $response['data']->lossy;
424 $stats['stats']['keep_exif'] = !empty( $response['data']->keep_exif ) ? $response['data']->keep_exif : 0;
425 }
426 }
427 //Upfront Integration
428 $stats = $this->smush_upfront_images( $ID, $stats );
429 } else {
430 $smush_full = true;
431 }
432
433 /**
434 * Allows to skip a image from smushing
435 *
436 * @param bool , Smush image or not
437 * @$size string, Size of image being smushed
438 */
439 $smush_full_image = apply_filters( 'wp_smush_media_image', true, 'full' );
440
441 //Whether to update the image stats or not
442 $store_stats = true;
443
444 //If original size is supposed to be smushed
445 if ( $smush_full && $smush_full_image ) {
446
447 $full_image_response = $this->do_smushit( $attachment_file_path );
448
449 if ( is_wp_error( $full_image_response ) ) {
450 return $full_image_response;
451 }
452
453 //If there are no stats
454 if( empty( $full_image_response['data'] ) ) {
455 $store_stats = false;
456 }
457
458 //If the image size grew after smushing, skip it
459 if( $full_image_response['data']->after_size > $full_image_response['data']->before_size ) {
460 $store_stats = false;
461 }
462
463 if ( $store_stats ) {
464 $stats['sizes']['full'] = (object) $this->_array_fill_placeholders( $this->_get_size_signature(), (array) $full_image_response['data'] );
465 }
466
467 //Api version and lossy, for some images, full image i skipped and for other images only full exists
468 //so have to add code again
469 if ( empty( $stats['stats']['api_version'] ) || $stats['stats']['api_version'] == - 1 ) {
470 $stats['stats']['api_version'] = $full_image_response['data']->api_version;
471 $stats['stats']['lossy'] = $full_image_response['data']->lossy;
472 $stats['stats']['keep_exif'] = !empty( $full_image_response['data']->keep_exif ) ? $full_image_response['data']->keep_exif : 0;
473 }
474
475 }
476
477 $has_errors = (bool) count( $errors->get_error_messages() );
478
479 //Set smush status for all the images, store it in wp-smpro-smush-data
480 if ( ! $has_errors ) {
481
482 $existing_stats = get_post_meta( $ID, $this->smushed_meta_key, true );
483
484 if ( ! empty( $existing_stats ) ) {
485
486 //Update stats for each size
487 if ( isset( $existing_stats['sizes'] ) && ! empty( $stats['sizes'] ) ) {
488
489 foreach ( $existing_stats['sizes'] as $size_name => $size_stats ) {
490 //if stats for a particular size doesn't exists
491 if ( empty( $stats['sizes'][ $size_name ] ) ) {
492 $stats['sizes'][ $size_name ] = $existing_stats['sizes'][ $size_name ];
493 } else {
494
495 $existing_stats_size = (object)$existing_stats['sizes'][ $size_name ];
496
497 //store the original image size
498 $stats['sizes'][ $size_name ]->size_before = ( !empty( $existing_stats_size->size_before ) && $existing_stats_size->size_before > $stats['sizes'][ $size_name ]->size_before ) ? $existing_stats_size->size_before : $stats['sizes'][ $size_name ]->size_before;
499
500 //Update compression percent and bytes saved for each size
501 $stats['sizes'][ $size_name ]->bytes = $stats['sizes'][ $size_name ]->bytes + $existing_stats_size->bytes;
502 $stats['sizes'][ $size_name ]->percent = $this->calculate_percentage( $stats['sizes'][ $size_name ], $existing_stats_size );
503 }
504 }
505 }
506 }
507
508 //Sum Up all the stats
509 $stats = $this->total_compression( $stats );
510
511 //If there was any compression and there was no error in smushing
512 if( isset( $stats['stats']['bytes'] ) && $stats['stats']['bytes'] >= 0 && !$has_errors ) {
513 /**
514 * Runs if the image smushing was successful
515 *
516 * @param int $ID Image Id
517 *
518 * @param array $stats Smush Stats for the image
519 *
520 */
521 do_action('wp_smush_image_optimised', $ID, $stats );
522 }
523 update_post_meta( $ID, $this->smushed_meta_key, $stats );
524 }
525
526 unset( $stats );
527
528 //Unset Response
529 if ( ! empty( $response ) ) {
530 unset( $response );
531 }
532
533 return $meta;
534 }
535
536 /**
537 * Read the image paths from an attachment's meta data and process each image
538 * with wp_smushit()
539 *
540 * @uses resize_from_meta_data
541 *
542 * @param $meta
543 * @param null $ID
544 *
545 * @return mixed
546 */
547 function smush_image( $meta, $ID = null ) {
548
549 //Return directly if not a image
550 if ( ! wp_attachment_is_image( $ID ) ) {
551 return $meta;
552 }
553
554 global $wpsmush_resize, $wpsmush_pngjpg, $wpsmush_backup;
555
556 //Check if auto is enabled
557 $auto_smush = $this->is_auto_smush_enabled();
558
559 //Optionally Resize Images
560 $meta = $wpsmush_resize->auto_resize( $ID, $meta );
561
562 //Auto Smush the new image
563 if ( $auto_smush ) {
564
565 //Optionally Convert PNGs to JPG
566 $meta = $wpsmush_pngjpg->png_to_jpg( $ID, $meta );
567
568 /** Fix for Hostgator */
569 //Check for use of http url, (Hostgator mostly)
570 $use_http = wp_cache_get( WP_SMUSH_PREFIX . 'use_http', 'smush' );
571 if ( ! $use_http ) {
572 $use_http = get_option( WP_SMUSH_PREFIX . 'use_http' );
573 wp_cache_add( WP_SMUSH_PREFIX . 'use_http', $use_http, 'smush' );
574 }
575 if( $use_http ) {
576 //HTTP Url
577 define( 'WP_SMUSH_API_HTTP', 'http://smushpro.wpmudev.org/1.0/' );
578 }
579
580 $this->resize_from_meta_data( $meta, $ID );
581
582 } else {
583 //remove the smush metadata
584 delete_post_meta( $ID, $this->smushed_meta_key );
585 }
586
587 return $meta;
588 }
589
590
591 /**
592 * Posts an image to Smush.
593 *
594 * @param $file_path path of file to send to Smush
595 * @param $file_size
596 *
597 * @return bool|array array containing success status, and stats
598 */
599 function _post( $file_path, $file_size ) {
600
601 $data = false;
602
603 $file = @fopen( $file_path, 'r' );
604 $file_data = fread( $file, $file_size );
605 $headers = array(
606 'accept' => 'application/json', // The API returns JSON
607 'content-type' => 'application/binary', // Set content type to binary
608 );
609
610 //Check if premium member, add API key
611 $api_key = $this->_get_api_key();
612 if ( ! empty( $api_key ) ) {
613 $headers['apikey'] = $api_key;
614 }
615
616 if ( $this->lossy_enabled && $this->validate_install() ) {
617 $headers['lossy'] = 'true';
618 } else {
619 $headers['lossy'] = 'false';
620 }
621
622 $headers['exif'] = $this->keep_exif ? 'true' : 'false';
623
624 $api_url = defined( 'WP_SMUSH_API_HTTP' ) ? WP_SMUSH_API_HTTP : WP_SMUSH_API;
625 $args = array(
626 'headers' => $headers,
627 'body' => $file_data,
628 'timeout' => WP_SMUSH_TIMEOUT,
629 'user-agent' => WP_SMUSH_UA,
630 );
631 $result = wp_remote_get( $api_url, $args );
632
633 //Close file connection
634 fclose( $file );
635 unset( $file_data );//free memory
636 if ( is_wp_error( $result ) ) {
637
638 $er_msg = $result->get_error_message();
639
640 //Hostgator Issue
641 if ( ! empty( $er_msg ) && strpos( $er_msg, 'SSL CA cert' ) !== false ) {
642 //Update DB for using http protocol
643 update_option( WP_SMUSH_PREFIX . 'use_http', 1 );
644 }
645 //Handle error
646 $data['message'] = sprintf( __( 'Error posting to API: %s', 'wp-smushit' ), $result->get_error_message() );
647 $data['success'] = false;
648 unset( $result ); //free memory
649 return $data;
650 } else if ( '200' != wp_remote_retrieve_response_code( $result ) ) {
651 //Handle error
652 $data['message'] = sprintf( __( 'Error posting to API: %s %s', 'wp-smushit' ), wp_remote_retrieve_response_code( $result ), wp_remote_retrieve_response_message( $result ) );
653 $data['success'] = false;
654 unset( $result ); //free memory
655
656 return $data;
657 }
658
659 //If there is a response and image was successfully optimised
660 $response = json_decode( $result['body'] );
661 if ( $response && $response->success == true ) {
662
663 //If there is any savings
664 if ( $response->data->bytes_saved > 0 ) {
665 $image = base64_decode( $response->data->image ); //base64_decode is necessary to send binary img over JSON, no security problems here!
666 $image_md5 = md5( $response->data->image );
667 if ( $response->data->image_md5 != $image_md5 ) {
668 //Handle error
669 $data['message'] = __( 'Smush data corrupted, try again.', 'wp-smushit' );
670 $data['success'] = false;
671 unset( $image );//free memory
672 } else {
673 $data['success'] = true;
674 $data['data'] = $response->data;
675 $data['data']->image = $image;
676 unset( $image );//free memory
677 }
678 } else {
679 //just return the data
680 $data['success'] = true;
681 $data['data'] = $response->data;
682 }
683 } else {
684 //Server side error, get message from response
685 $data['message'] = ! empty( $response->data ) ? $response->data : __( "Image couldn't be smushed", 'wp-smushit' );
686 $data['success'] = false;
687 }
688
689 unset( $result );//free memory
690 unset( $response );//free memory
691 return $data;
692 }
693
694
695 /**
696 * Print column header for Smush results in the media library using
697 * the `manage_media_columns` hook.
698 */
699 function columns( $defaults ) {
700 $defaults['smushit'] = 'WP Smush';
701
702 return $defaults;
703 }
704
705 /**
706 * Return the filesize in a humanly readable format.
707 * Taken from http://www.php.net/manual/en/function.filesize.php#91477
708 */
709 function format_bytes( $bytes, $precision = 1 ) {
710 $units = array( 'B', 'KB', 'MB', 'GB', 'TB' );
711 $bytes = max( $bytes, 0 );
712 $pow = floor( ( $bytes ? log( $bytes ) : 0 ) / log( 1024 ) );
713 $pow = min( $pow, count( $units ) - 1 );
714 $bytes /= pow( 1024, $pow );
715
716 return round( $bytes, $precision ) . $units[ $pow ];
717 }
718
719 /**
720 * Print column data for Smush results in the media library using
721 * the `manage_media_custom_column` hook.
722 */
723 function custom_column( $column_name, $id ) {
724 if ( 'smushit' == $column_name ) {
725 $this->set_status( $id );
726 }
727 }
728
729 /**
730 * Check if user is premium member, check for api key
731 *
732 * @return mixed|string
733 */
734 function validate_install() {
735
736 if ( isset( $this->is_pro ) ) {
737 return $this->is_pro;
738 }
739
740 //no api key set, always false
741 $api_key = $this->_get_api_key();
742
743 if ( empty( $api_key ) ) {
744 return false;
745 }
746
747 //Flag to check if we need to revalidate the key
748 $revalidate = false;
749
750 $api_auth = get_site_option( 'wp_smush_api_auth' );
751
752 //Check if need to revalidate
753 if ( ! $api_auth || empty( $api_auth ) || empty( $api_auth[ $api_key ] ) ) {
754 $revalidate = true;
755 } else {
756 $last_checked = $api_auth[ $api_key ]['timestamp'];
757 $valid = $api_auth[ $api_key ]['validity'];
758
759 $diff = $last_checked - current_time( 'timestamp' );
760
761 //Difference in hours
762 $diff_h = $diff / 3600;
763
764 //Difference in minutes
765 $diff_m = $diff / 60;
766
767 switch ( $valid ) {
768 case 'valid':
769 //if last checked was more than 12 hours
770 if ( $diff_h > 12 ) {
771 $revalidate = true;
772 }
773 break;
774 case 'invalid':
775 //if last checked was more than 24 hours
776 if ( $diff_h > 24 ) {
777 $revalidate = true;
778 }
779 break;
780 case 'network_failure':
781 //if last checked was more than 5 minutes
782 if ( $diff_m > 5 ) {
783 $revalidate = true;
784 }
785 break;
786 }
787 }
788 //If we are suppose to validate api, update the results in options table
789 if ( $revalidate ) {
790
791 if ( empty( $api_auth[ $api_key ] ) ) {
792 //For api key resets
793 $api_auth[ $api_key ] = array();
794
795 //Storing it as valid, unless we really get to know from api call
796 $api_auth[ $api_key ]['validity'] = 'valid';
797 }
798
799 //Aaron suggested to Update timestamp before making the api call, to avoid any concurrent calls, clever ;)
800 $api_auth[ $api_key ]['timestamp'] = current_time( 'timestamp' );
801 update_site_option( 'wp_smush_api_auth', $api_auth );
802
803 // call api
804 $url = $this->api_server . $api_key;
805
806 $request = wp_remote_get( $url, array(
807 "user-agent" => WP_SMUSH_UA,
808 "timeout" => 10
809 )
810 );
811
812 if ( ! is_wp_error( $request ) && '200' == wp_remote_retrieve_response_code( $request ) ) {
813 $result = json_decode( wp_remote_retrieve_body( $request ) );
814 if ( !empty( $result->success ) && $result->success ) {
815 $valid = 'valid';
816 } else {
817 $valid = 'invalid';
818 }
819
820 } else {
821 $valid = 'network_failure';
822 }
823
824 //Reset Value
825 $api_auth = array();
826
827 //Add a fresh Timestamp
828 $timestamp = current_time( 'timestamp' );
829 $api_auth[ $api_key ] = array( 'validity' => $valid, 'timestamp' => $timestamp );
830
831 //Update API validity
832 // update_site_option( 'wp_smush_api_auth', $api_auth );
833
834 }
835
836 $this->is_pro = ( 'valid' == $valid );
837
838 return $this->is_pro;
839 }
840
841 /**
842 * Returns api key
843 *
844 * @return mixed
845 */
846 function _get_api_key() {
847
848 if ( defined( 'WPMUDEV_APIKEY' ) && WPMUDEV_APIKEY ) {
849 $api_key = WPMUDEV_APIKEY;
850 } else {
851 $api_key = get_site_option( 'wpmudev_apikey' );
852 }
853
854 return $api_key;
855 }
856
857 /**
858 * Returns size saved from the api call response
859 *
860 * @param string $message
861 *
862 * @return string|bool
863 */
864 function get_saved_size( $message ) {
865 if ( preg_match( '/\((.*)\)/', $message, $matches ) ) {
866 return isset( $matches[1] ) ? $matches[1] : false;
867 }
868
869 return false;
870 }
871
872 /**
873 * Set send button status
874 *
875 * @param $id
876 * @param bool $echo
877 * @param bool $text_only Returns the stats text instead of button
878 * @param bool $wrapper required for `column_html`, to include the wrapper div or not
879 *
880 * @return string|void
881 */
882 function set_status( $id, $echo = true, $text_only = false, $wrapper = true ) {
883 $status_txt = $button_txt = $stats = '';
884 $show_button = $show_resmush = false;
885
886 $wp_smush_data = get_post_meta( $id, $this->smushed_meta_key, true );
887 $wp_resize_savings = get_post_meta( $id, WP_SMUSH_PREFIX . 'resize_savings', true );
888 $conversion_savings = get_post_meta( $id, WP_SMUSH_PREFIX . 'pngjpg_savings', true );
889
890 $combined_stats = $this->combined_stats( $wp_smush_data, $wp_resize_savings );
891
892 $combined_stats = $this->combine_conversion_stats( $combined_stats, $conversion_savings );
893
894 $attachment_data = wp_get_attachment_metadata( $id );
895
896 // if the image is smushed
897 if ( ! empty( $wp_smush_data ) ) {
898
899 $image_count = count( $wp_smush_data['sizes'] );
900 $bytes = isset( $combined_stats['stats']['bytes'] ) ? $combined_stats['stats']['bytes'] : 0;
901 $bytes_readable = ! empty( $bytes ) ? $this->format_bytes( $bytes ) : '';
902 $percent = isset( $combined_stats['stats']['percent'] ) ? $combined_stats['stats']['percent'] : 0;
903 $percent = $percent < 0 ? 0 : $percent;
904
905 if ( isset( $wp_smush_data['stats']['size_before'] ) && $wp_smush_data['stats']['size_before'] == 0 && ! empty( $wp_smush_data['sizes'] ) ) {
906 $status_txt = __( 'Already Optimized', 'wp-smushit' );
907 $show_button = false;
908 } else {
909 if ( $bytes == 0 || $percent == 0 ) {
910 $status_txt = __( 'Already Optimized', 'wp-smushit' );
911
912 //Show resmush link, if the settings were changed
913 $show_resmush = $this->show_resmush( $id, $wp_smush_data );
914 if ( $show_resmush ) {
915 $status_txt .= '<br />' . $this->get_resmsuh_link( $id );
916 }
917
918 } elseif ( ! empty( $percent ) && ! empty( $bytes_readable ) ) {
919 $status_txt = $image_count > 1 ? sprintf( __( "%d images reduced ", 'wp-smushit' ), $image_count ) : __( "Reduced ", 'wp-smushit' );
920
921 $stats_percent = number_format_i18n( $percent, 2, '.', '' );
922 $stats_percent = $stats_percent > 0 ? sprintf( "( %01.1f%% )", $stats_percent ) : '';
923 $status_txt .= sprintf( __( "by %s %s", 'wp-smushit' ), $bytes_readable, $stats_percent );
924
925 $file_path = get_attached_file( $id );
926 $size = file_exists( $file_path ) ? filesize( $file_path ) : 0;
927 if( $size > 0 ) {
928 $size = $this->format_bytes( $size );
929 $image_size = sprintf( __( "<br /> Image Size: %s", "wp-smushit"), $size );
930 $status_txt .= $image_size;
931 }
932
933 $show_resmush = $this->show_resmush( $id, $wp_smush_data );
934
935 if ( $show_resmush ) {
936 $status_txt .= '<br />' . $this->get_resmsuh_link( $id );
937 }
938
939 //Restore Image: Check if we need to show the restore image option
940 $show_restore = $this->show_restore_option( $id, $attachment_data );
941
942 if ( $show_restore ) {
943 if ( $show_resmush ) {
944 //Show Separator
945 $status_txt .= ' | ';
946 } else {
947 //Show the link in next line
948 $status_txt .= '<br />';
949 }
950 $status_txt .= $this->get_restore_link( $id );
951 }
952
953 //Detailed Stats: Show detailed stats if available
954 if ( ! empty( $wp_smush_data['sizes'] ) ) {
955
956 if ( $show_resmush || $show_restore ) {
957 //Show Separator
958 $status_txt .= ' | ';
959 } else {
960 //Show the link in next line
961 $status_txt .= '<br />';
962 }
963
964 //Detailed Stats Link
965 $status_txt .= sprintf( '<a href="#" class="wp-smush-action smush-stats-details wp-smush-title" tooltip="%s">%s [<span class="stats-toggle">+</span>]</a>', esc_html__( "Detailed stats for all the image sizes", "wp-smushit" ), esc_html__( "Smush stats", 'wp-smushit' ) );
966
967 //Stats
968 $stats = $this->get_detailed_stats( $id, $wp_smush_data, $attachment_data );
969
970 if ( ! $text_only ) {
971 $status_txt .= $stats;
972 }
973 }
974 }
975 }
976 /** Super Smush Button */
977 //IF current compression is lossy
978 if ( ! empty( $wp_smush_data ) && ! empty( $wp_smush_data['stats'] ) ) {
979 $lossy = ! empty( $wp_smush_data['stats']['lossy'] ) ? $wp_smush_data['stats']['lossy'] : '';
980 $is_lossy = $lossy == 1 ? true : false;
981 }
982
983 //Check image type
984 $image_type = get_post_mime_type( $id );
985
986 //Check if premium user, compression was lossless, and lossy compression is enabled
987 //If we are displaying the resmush option already, no need to show the Super Smush button
988 if ( ! $show_resmush && $this->validate_install() && ! $is_lossy && $this->lossy_enabled && $image_type != 'image/gif' ) {
989 // the button text
990 $button_txt = __( 'Super-Smush', 'wp-smushit' );
991 $show_button = true;
992 }
993
994 } else {
995
996 // the status
997 $status_txt = __( 'Not processed', 'wp-smushit' );
998
999 // we need to show the smush button
1000 $show_button = true;
1001
1002 // the button text
1003 $button_txt = __( 'Smush Now!', 'wp-smushit' );
1004 }
1005 if ( $text_only ) {
1006 //For ajax response
1007 return array(
1008 'status' => $status_txt,
1009 'stats' => $stats
1010 );
1011 }
1012
1013 //If we are not showing smush button, append progree bar, else it is already there
1014 if ( ! $show_button ) {
1015 $status_txt .= $this->progress_bar();
1016 }
1017
1018 $text = $this->column_html( $id, $status_txt, $button_txt, $show_button, $wp_smush_data, $echo, $wrapper );
1019 if ( ! $echo ) {
1020 return $text;
1021 }
1022 }
1023
1024 /**
1025 * Print the column html
1026 *
1027 * @param string $id Media id
1028 * @param string $status_txt Status text
1029 * @param string $button_txt Button label
1030 * @param boolean $show_button Whether to shoe the button
1031 * @param bool $smushed Whether image is smushed or not
1032 * @param bool $echo If true, it directly outputs the HTML
1033 * @param bool $wrapper Whether to return the button with wrapper div or not
1034 *
1035 * @return string|void
1036 */
1037 function column_html( $id, $status_txt = "", $button_txt = "", $show_button = true, $smushed = false, $echo = true, $wrapper = true ) {
1038 $allowed_images = array( 'image/jpeg', 'image/jpg', 'image/png', 'image/gif' );
1039
1040 // don't proceed if attachment is not image, or if image is not a jpg, png or gif
1041 if ( ! wp_attachment_is_image( $id ) || ! in_array( get_post_mime_type( $id ), $allowed_images ) ) {
1042 return;
1043 }
1044
1045 $class = $smushed ? '' : ' hidden';
1046 $html = '<p class="smush-status' . $class . '">' . $status_txt . '</p>';
1047 // if we aren't showing the button
1048 if ( ! $show_button ) {
1049 if ( $echo ) {
1050 echo $html;
1051
1052 return;
1053 } else {
1054 if ( ! $smushed ) {
1055 $class = ' currently-smushing';
1056 } else {
1057 $class = ' smushed';
1058 }
1059
1060 return $wrapper ? '<div class="smush-wrap' . $class . '">' . $html . '</div>' : $html;
1061 }
1062 }
1063 if ( ! $echo ) {
1064 $button_class = $wrapper ? 'button button-primary wp-smush-send' : 'button wp-smush-send';
1065 $html .= '
1066 <button class="' . $button_class . '" data-id="' . $id . '">
1067 <span>' . $button_txt . '</span>
1068 </button>';
1069 if ( ! $smushed ) {
1070 $class = ' unsmushed';
1071 } else {
1072 $class = ' smushed';
1073 }
1074
1075 $html .= $this->progress_bar();
1076 $html = $wrapper ? '<div class="smush-wrap' . $class . '">' . $html . '</div>' : $html;
1077
1078 return $html;
1079 } else {
1080 $html .= '<button class="button wp-smush-send" data-id="' . $id . '">
1081 <span>' . $button_txt . '</span>
1082 </button>';
1083 $html = $html . $this->progress_bar();
1084 echo $html;
1085 }
1086 }
1087
1088 /**
1089 * Migrates smushit api message to the latest structure
1090 *
1091 *
1092 * @return void
1093 */
1094 function migrate() {
1095
1096 if ( ! version_compare( $this->version, "1.7.1", "lte" ) ) {
1097 return;
1098 }
1099
1100 $migrated_version = get_option( $this->migrated_version_key );
1101
1102 if ( $migrated_version === $this->version ) {
1103 return;
1104 }
1105
1106 global $wpdb;
1107
1108 $q = $wpdb->prepare( "SELECT * FROM `" . $wpdb->postmeta . "` WHERE `meta_key`=%s AND `meta_value` LIKE %s ", "_wp_attachment_metadata", "%wp_smushit%" );
1109 $results = $wpdb->get_results( $q );
1110
1111 if ( count( $results ) < 1 ) {
1112 return;
1113 }
1114
1115 $migrator = new WpSmushMigrate();
1116 foreach ( $results as $attachment_meta ) {
1117 $migrated_message = $migrator->migrate_api_message( maybe_unserialize( $attachment_meta->meta_value ) );
1118 if ( $migrated_message !== array() ) {
1119 update_post_meta( $attachment_meta->post_id, $this->smushed_meta_key, $migrated_message );
1120 }
1121 }
1122
1123 update_option( $this->migrated_version_key, $this->version );
1124
1125 }
1126
1127 /**
1128 * Updates the smush stats for a single image size
1129 *
1130 * @param $id
1131 * @param $stats
1132 * @param $image_size
1133 */
1134 function update_smush_stats_single( $id, $smush_stats, $image_size = '' ) {
1135 //Return, if we don't have image id or stats for it
1136 if ( empty( $id ) || empty( $smush_stats ) || empty( $image_size ) ) {
1137 return false;
1138 }
1139 $data = $smush_stats['data'];
1140 //Get existing Stats
1141 $stats = get_post_meta( $id, $this->smushed_meta_key, true );
1142 //Update existing Stats
1143 if ( ! empty( $stats ) ) {
1144
1145 //Update stats for each size
1146 if ( isset( $stats['sizes'] ) ) {
1147
1148 //if stats for a particular size doesn't exists
1149 if ( empty( $stats['sizes'][ $image_size ] ) ) {
1150 //Update size wise details
1151 $stats['sizes'][ $image_size ] = (object) $this->_array_fill_placeholders( $this->_get_size_signature(), (array) $data );
1152 } else {
1153 //Update compression percent and bytes saved for each size
1154 $stats['sizes'][ $image_size ]->bytes = $stats['sizes'][ $image_size ]->bytes + $data->bytes_saved;
1155 $stats['sizes'][ $image_size ]->percent = $stats['sizes'][ $image_size ]->percent + $data->compression;
1156 }
1157 }
1158 } else {
1159 //Create new stats
1160 $stats = array(
1161 "stats" => array_merge( $this->_get_size_signature(), array(
1162 'api_version' => - 1,
1163 'lossy' => - 1
1164 )
1165 ),
1166 'sizes' => array()
1167 );
1168 $stats['stats']['api_version'] = $data->api_version;
1169 $stats['stats']['lossy'] = $data->lossy;
1170 $stats['stats']['keep_exif'] = ! empty( $data->keep_exif ) ? $data->keep_exif : 0;
1171
1172 //Update size wise details
1173 $stats['sizes'][ $image_size ] = (object) $this->_array_fill_placeholders( $this->_get_size_signature(), (array) $data );
1174 }
1175 //Calculate the total compression
1176 $stats = $this->total_compression( $stats );
1177
1178 update_post_meta( $id, $this->smushed_meta_key, $stats );
1179
1180 }
1181
1182 /**
1183 * Smush Retina images for WP Retina 2x, Update Stats
1184 *
1185 * @param $id
1186 * @param $retina_file
1187 * @param $image_size
1188 */
1189 function smush_retina_image( $id, $retina_file, $image_size ) {
1190
1191 //Initialize attachment id and media type
1192 $this->attachment_id = $id;
1193 $this->media_type = "wp";
1194
1195 /**
1196 * Allows to Enable/Disable WP Retina 2x Integration
1197 */
1198 $smush_retina_images = apply_filters( 'smush_retina_images', true );
1199
1200 //Check if Smush retina images is enbled
1201 if ( ! $smush_retina_images ) {
1202 return;
1203 }
1204 //Check for Empty fields
1205 if ( empty( $id ) || empty( $retina_file ) || empty( $image_size ) ) {
1206 return;
1207 }
1208
1209 /**
1210 * Allows to skip a image from smushing
1211 *
1212 * @param bool , Smush image or not
1213 * @$size string, Size of image being smushed
1214 */
1215 $smush_image = apply_filters( 'wp_smush_media_image', true, $image_size );
1216 if ( ! $smush_image ) {
1217 return;
1218 }
1219
1220 $stats = $this->do_smushit( $retina_file );
1221 //If we squeezed out something, Update stats
1222 if ( ! is_wp_error( $stats ) && ! empty( $stats['data'] ) && isset( $stats['data'] ) && $stats['data']->bytes_saved > 0 ) {
1223
1224 $image_size = $image_size . '@2x';
1225
1226 $this->update_smush_stats_single( $id, $stats, $image_size );
1227 }
1228 }
1229
1230 /**
1231 * Return a list of images not smushed and reason
1232 *
1233 * @param $image_id
1234 * @param $size_stats
1235 * @param $attachment_metadata
1236 *
1237 * @return array
1238 */
1239 function get_skipped_images( $image_id, $size_stats, $attachment_metadata ) {
1240 $skipped = array();
1241
1242 //Get a list of all the sizes, Show skipped images
1243 $media_size = get_intermediate_image_sizes();
1244
1245 //Full size
1246 $full_image = get_attached_file( $image_id );
1247
1248 //If full image was not smushed, reason 1. Large Size logic, 2. Free and greater than 1Mb
1249 if ( ! array_key_exists( 'full', $size_stats ) ) {
1250 //For free version, Check the image size
1251 if ( ! $this->validate_install() ) {
1252 //For free version, check if full size is greater than 1 Mb, show the skipped status
1253 $file_size = file_exists( $full_image ) ? filesize( $full_image ) : '';
1254 if ( ! empty( $file_size ) && ( $file_size / WP_SMUSH_MAX_BYTES ) > 1 ) {
1255 $skipped[] = array(
1256 'size' => 'full',
1257 'reason' => 'size_limit'
1258 );
1259 } else {
1260 $skipped[] = array(
1261 'size' => 'full',
1262 'reason' => 'large_size'
1263 );
1264 }
1265 } else {
1266 //Paid version, Check if we have large size
1267 if ( array_key_exists( 'large', $size_stats ) ) {
1268 $skipped[] = array(
1269 'size' => 'full',
1270 'reason' => 'large_size'
1271 );
1272 }
1273
1274 }
1275 }
1276 //For other sizes, check if the image was generated and not available in stats
1277 if ( is_array( $media_size ) ) {
1278 foreach ( $media_size as $size ) {
1279 if ( array_key_exists( $size, $attachment_metadata['sizes'] ) && ! array_key_exists( $size, $size_stats ) && ! empty( $size['file'] ) ) {
1280 //Image Path
1281 $img_path = path_join( dirname( $full_image ), $size['file'] );
1282 $image_size = file_exists( $img_path ) ? filesize( $img_path ) : '';
1283 if ( ! empty( $image_size ) && ( $image_size / WP_SMUSH_MAX_BYTES ) > 1 ) {
1284 $skipped[] = array(
1285 'size' => 'full',
1286 'reason' => 'size_limit'
1287 );
1288 }
1289 }
1290 }
1291 }
1292
1293 return $skipped;
1294 }
1295
1296 /**
1297 * Skip messages respective to their ids
1298 *
1299 * @param $msg_id
1300 *
1301 * @return bool
1302 */
1303 function skip_reason( $msg_id ) {
1304 $count = count( get_intermediate_image_sizes() );
1305 $smush_orgnl_txt = sprintf( esc_html__( "When you upload an image to WordPress it automatically creates %s thumbnail sizes that are commonly used in your pages. WordPress also stores the original full-size image, but because these are not usually embedded on your site we don’t Smush them. Pro users can override this.", 'wp_smushit' ), $count );
1306 $skip_msg = array(
1307 'large_size' => $smush_orgnl_txt,
1308 'size_limit' => esc_html__( "Image couldn't be smushed as it exceeded the 1Mb size limit, Pro users can smush images with size upto 32Mb.", "wp-smushit" )
1309 );
1310 $skip_rsn = ! empty( $skip_msg[ $msg_id ] ) ? esc_html__( " Skipped", 'wp-smushit', 'wp-smushit' ) : '';
1311 $skip_rsn = ! empty( $skip_rsn ) ? $skip_rsn . '<span tooltip="' . $skip_msg[ $msg_id ] . '"><i class="dashicons dashicons-editor-help"></i></span>' : '';
1312
1313 return $skip_rsn;
1314 }
1315
1316 /**
1317 * Shows the image size and the compression for each of them
1318 *
1319 * @param $image_id
1320 * @param $wp_smush_data
1321 *
1322 * @return string
1323 */
1324 function get_detailed_stats( $image_id, $wp_smush_data, $attachment_metadata ) {
1325
1326 global $wpsmushit_admin;
1327
1328 $stats = '<div id="smush-stats-' . $image_id . '" class="smush-stats-wrapper hidden">
1329 <table class="wp-smush-stats-holder">
1330 <thead>
1331 <tr>
1332 <th><strong>' . esc_html__( 'Image size', 'wp-smushit' ) . '</strong></th>
1333 <th><strong>' . esc_html__( 'Savings', 'wp-smushit' ) . '</strong></th>
1334 </tr>
1335 </thead>
1336 <tbody>';
1337 $size_stats = $wp_smush_data['sizes'];
1338
1339 //Reorder Sizes as per the maximum savings
1340 uasort( $size_stats, array( $this, "cmp" ) );
1341
1342 if ( ! empty( $attachment_metadata['sizes'] ) ) {
1343 //Get skipped images
1344 $skipped = $this->get_skipped_images( $image_id, $size_stats, $attachment_metadata );
1345
1346 if ( ! empty( $skipped ) ) {
1347 foreach ( $skipped as $img_data ) {
1348 $skip_class = $img_data['reason'] == 'size_limit' ? ' error' : '';
1349 $stats .= '<tr>
1350 <td>' . strtoupper( $img_data['size'] ) . '</td>
1351 <td class="smush-skipped' . $skip_class . '">' . $this->skip_reason( $img_data['reason'] ) . '</td>
1352 </tr>';
1353 }
1354
1355 }
1356 }
1357 //Show Sizes and their compression
1358 foreach ( $size_stats as $size_key => $size_value ) {
1359 $dimensions = '';
1360 //Get the dimensions for the image size if available
1361 if ( ! empty( $wpsmushit_admin->image_sizes ) && !empty( $wpsmushit_admin->image_sizes[$size_key] ) ) {
1362 $dimensions = $wpsmushit_admin->image_sizes[ $size_key ]['width'] . 'x' . $wpsmushit_admin->image_sizes[ $size_key ]['height'];
1363 }
1364 $dimensions = !empty( $dimensions ) ? sprintf( " <br /> (%s)", $dimensions ) : '';
1365 if ( $size_value->bytes > 0 ) {
1366 $percent = round( $size_value->percent, 1 );
1367 $percent = $percent > 0 ? ' ( ' . $percent . '% )' : '';
1368 $stats .= '<tr>
1369 <td>' . strtoupper( $size_key ) . $dimensions . '</td>
1370 <td>' . $this->format_bytes( $size_value->bytes ) . $percent . '</td>
1371 </tr>';
1372 }
1373 }
1374 $stats .= '</tbody>
1375 </table>
1376 </div>';
1377
1378 return $stats;
1379 }
1380
1381 /**
1382 * Compare Values
1383 *
1384 * @param $a
1385 * @param $b
1386 *
1387 * @return int
1388 */
1389 function cmp( $a, $b ) {
1390 return $a->bytes < $b->bytes;
1391 }
1392
1393 /**
1394 * Check if NextGen is active or not
1395 * Include and instantiate classes
1396 */
1397 function load_nextgen() {
1398 if ( ! class_exists( 'C_NextGEN_Bootstrap' ) || ! $this->validate_install() ) {
1399 return;
1400 }
1401 //Check if integration is Enabled or not
1402 //Smush NextGen key
1403 $opt_nextgen = WP_SMUSH_PREFIX . 'nextgen';
1404 $opt_nextgen_val = get_option( $opt_nextgen, false );
1405 if ( ! $opt_nextgen_val ) {
1406 return;
1407 }
1408
1409 require_once( WP_SMUSH_DIR . '/lib/class-wp-smush-nextgen.php' );
1410 require_once( WP_SMUSH_DIR . '/lib/nextgen-integration/class-wp-smush-nextgen-admin.php' );
1411 require_once( WP_SMUSH_DIR . '/lib/nextgen-integration/class-wp-smush-nextgen-stats.php' );
1412 require_once( WP_SMUSH_DIR . '/lib/nextgen-integration/class-wp-smush-nextgen-bulk.php' );
1413
1414 global $wpsmushnextgen, $wpsmushnextgenadmin, $wpsmushnextgenstats;
1415 //Initialize Nextgen support
1416 $wpsmushnextgen = new WpSmushNextGen();
1417 $wpsmushnextgenstats = new WpSmushNextGenStats();
1418 $wpsmushnextgenadmin = new WpSmushNextGenAdmin();
1419 new WPSmushNextGenBulk();
1420 }
1421
1422 /**
1423 * Add the Smushit Column to sortable list
1424 *
1425 * @param $columns
1426 *
1427 * @return mixed
1428 */
1429 function sortable_column( $columns ) {
1430 $columns['smushit'] = 'smushit';
1431
1432 return $columns;
1433 }
1434
1435 /**
1436 * Orderby query for smush columns
1437 */
1438 function smushit_orderby( $query ) {
1439
1440 global $current_screen;
1441
1442 //Filter only media screen
1443 if ( ! is_admin() || ( ! empty( $current_screen ) && $current_screen->base != 'upload' ) ) {
1444 return;
1445 }
1446
1447 $orderby = $query->get( 'orderby' );
1448
1449 if ( isset( $orderby ) && 'smushit' == $orderby ) {
1450 $query->set( 'meta_query', array(
1451 'relation' => 'OR',
1452 array(
1453 'key' => $this->smushed_meta_key,
1454 'compare' => 'EXISTS'
1455 ),
1456 array(
1457 'key' => $this->smushed_meta_key,
1458 'compare' => 'NOT EXISTS'
1459 )
1460 ) );
1461 $query->set( 'orderby', 'meta_value_num' );
1462 }
1463
1464 return $query;
1465
1466 }
1467
1468 /**
1469 * If any of the image size have a backup file, show the restore option
1470 *
1471 * @param $attachment_data
1472 *
1473 * @return bool
1474 */
1475 function show_restore_option( $image_id, $attachment_data ) {
1476 global $wpsmushit_admin;
1477
1478 //No Attachment data, don't go ahead
1479 if ( empty( $attachment_data ) ) {
1480 return false;
1481 }
1482
1483 //Get the image path for all sizes
1484 $file = get_attached_file( $image_id );
1485
1486 //Check backup for Full size
1487 $backup = $wpsmushit_admin->get_image_backup_path( $file );
1488
1489 //Check for backup of full image
1490 if ( file_exists( $backup ) ) {
1491 return true;
1492 }
1493
1494 //Additional Backup Check for JPEGs converted from PNG
1495 $pngjpg_savings = get_post_meta( $image_id, WP_SMUSH_PREFIX . 'pngjpg_savings', true );
1496 if ( ! empty( $pngjpg_savings ) ) {
1497 //Get the original File path and check if it exists
1498 $backup = get_post_meta( $image_id, WP_SMUSH_PREFIX . 'original_file', true );
1499 $backup = $this->original_file( $backup );
1500
1501 if ( !empty( $backup ) && file_exists( $backup ) ) {
1502 return true;
1503 }
1504 }
1505
1506 //Check for backup of image sizes
1507 foreach ( $attachment_data['sizes'] as $image_size ) {
1508 $size_path = path_join( dirname( $file ), $image_size['file'] );
1509 $size_backup_path = $wpsmushit_admin->get_image_backup_path( $size_path );
1510 if ( file_exists( $size_backup_path ) ) {
1511 return true;
1512 }
1513 }
1514
1515 return false;
1516 }
1517
1518 /**
1519 * Returns a restore link for given image id
1520 *
1521 * @param $image_id
1522 *
1523 * @return bool|string
1524 */
1525 function get_restore_link( $image_id, $type = 'wp' ) {
1526 if ( empty( $image_id ) ) {
1527 return false;
1528 }
1529
1530 $class = 'wp-smush-action wp-smush-title';
1531 $class .= 'wp' == $type ? ' wp-smush-restore' : ' wp-smush-nextgen-restore';
1532
1533 $ajax_nonce = wp_create_nonce( "wp-smush-restore-" . $image_id );
1534
1535 return sprintf( '<a href="#" tooltip="%s" data-id="%d" data-nonce="%s" class="%s">%s</a>', esc_html__( "Restore original image.", "wp-smushit" ), $image_id, $ajax_nonce, $class, esc_html__( "Restore image", "wp-smush" ) );
1536 }
1537
1538 /**
1539 * Returns the HTML for progress bar
1540 *
1541 * @return string
1542 */
1543 function progress_bar() {
1544 return '<div class="wp-smush-progress animate hidden"><span></span></div>';
1545 }
1546
1547 /**
1548 * If auto smush is set to true or not, default is true
1549 *
1550 * @return int|mixed|void
1551 */
1552 function is_auto_smush_enabled() {
1553 $auto_smush = get_option( WP_SMUSH_PREFIX . 'auto' );
1554
1555 //Keep the auto smush on by default
1556 if ( $auto_smush === false ) {
1557 $auto_smush = 1;
1558 }
1559
1560 return $auto_smush;
1561 }
1562
1563 /**
1564 * Generates a Resmush link for a image
1565 *
1566 * @param $image_id
1567 *
1568 * @return bool|string
1569 */
1570 function get_resmsuh_link( $image_id, $type = 'wp' ) {
1571
1572 if ( empty( $image_id ) ) {
1573 return false;
1574 }
1575 $class = 'wp-smush-action wp-smush-title';
1576 $class .= 'wp' == $type ? ' wp-smush-resmush' : ' wp-smush-nextgen-resmush';
1577
1578 $ajax_nonce = wp_create_nonce( "wp-smush-resmush-" . $image_id );
1579
1580 return sprintf( '<a href="#" tooltip="%s" data-id="%d" data-nonce="%s" class="%s">%s</a>', esc_html__( "Smush image including original file.", "wp-smushit" ), $image_id, $ajax_nonce, $class, esc_html__( "Resmush image", "wp-smush" ) );
1581 }
1582
1583 /**
1584 * Returns the backup path for attachment
1585 *
1586 * @param $attachment_path
1587 *
1588 * @return bool|string
1589 *
1590 */
1591 function get_image_backup_path( $attachment_path ) {
1592 //If attachment id is not available, return false
1593 if ( empty( $attachment_path ) ) {
1594 return false;
1595 }
1596 $path = pathinfo( $attachment_path );
1597 $backup_name = trailingslashit( $path['dirname'] ) . $path['filename'] . ".bak." . $path['extension'];
1598
1599 return $backup_name;
1600 }
1601
1602 /**
1603 * Deletes all the backup files when an attachment is deleted
1604 * Update Resmush List
1605 * Update Super Smush image count
1606 *
1607 * @param $image_id
1608 */
1609 function delete_images( $image_id ) {
1610 global $wpsmush_stats;
1611
1612 //Update the savings cache
1613 $wpsmush_stats->resize_savings( true );
1614
1615 //Update the savings cache
1616 $wpsmush_stats->conversion_savings( true );
1617
1618 //If no image id provided
1619 if ( empty( $image_id ) ) {
1620 return false;
1621 }
1622
1623 //Check and Update resmush list
1624 if ( $resmush_list = get_option( 'wp-smush-resmush-list' ) ) {
1625 global $wpsmushit_admin;
1626 $wpsmushit_admin->update_resmush_list( $image_id, 'wp-smush-resmush-list' );
1627 }
1628
1629 /** Delete Backups **/
1630
1631 //Check if we have any smush data for image
1632 $this->delete_backup_files( $image_id );
1633 }
1634
1635 /**
1636 * Return Global stats
1637 *
1638 * @return array|bool|mixed
1639 */
1640 function send_smush_stats() {
1641 global $wpsmushit_admin;
1642
1643 $stats = $wpsmushit_admin->global_stats_from_ids();
1644
1645 return $stats;
1646
1647 }
1648
1649 /**
1650 * Smushes the upfront images and Updates the respective stats
1651 *
1652 * @param $attachment_id
1653 * @param $stats
1654 *
1655 * @return mixed
1656 */
1657 function smush_upfront_images( $attachment_id, $stats ) {
1658 //Check if upfront is active or not
1659 if ( empty( $attachment_id ) || ! class_exists( 'Upfront' ) ) {
1660 return $stats;
1661 }
1662
1663 //Set attachment id and Media type
1664 $this->attachment_id = $attachment_id;
1665 $this->media_type = "upfront";
1666
1667 //Get post meta to check for Upfront images
1668 $upfront_images = get_post_meta( $attachment_id, 'upfront_used_image_sizes', true );
1669
1670 //If there is no upfront meta for the image
1671 if ( ! $upfront_images || empty( $upfront_images ) || ! is_array( $upfront_images ) ) {
1672 return $stats;
1673 }
1674 //Loop over all the images in upfront meta
1675 foreach ( $upfront_images as $element_id => $image ) {
1676 if ( isset( $image['is_smushed'] ) && 1 == $image['is_smushed'] ) {
1677 continue;
1678 }
1679 //Get the image path and smush it
1680 if ( isset( $image['path'] ) && file_exists( $image['path'] ) ) {
1681 $res = $this->do_smushit( $image['path'] );
1682 //If sizes key is not yet initialised
1683 if ( empty( $stats['sizes'] ) ) {
1684 $stats['sizes'] = array();
1685 }
1686
1687 //If the smushing was successful
1688 if ( ! is_wp_error( $res ) && ! empty( $res['data'] ) ) {
1689 if( $res['data']->bytes_saved > 0 ) {
1690 //Update attachment stats
1691 $stats['sizes'][ $element_id ] = (object) $this->_array_fill_placeholders( $this->_get_size_signature(), (array) $res['data'] );
1692 }
1693
1694 //Update upfront stats for the element id
1695 $upfront_images[ $element_id ]['is_smushed'] = 1;
1696 }
1697 }
1698 }
1699 //Finally Update the upfront meta key
1700 update_post_meta( $attachment_id, 'upfront_used_image_sizes', $upfront_images );
1701
1702 return $stats;
1703
1704 }
1705
1706 /**
1707 * Checks the current settings and returns the value whether to enable or not the resmush option
1708 * @param $show_resmush
1709 * @param $wp_smush_data
1710 *
1711 * @return bool
1712 */
1713 function show_resmush( $id = '', $wp_smush_data ) {
1714 //Resmush: Show resmush link, Check if user have enabled smushing the original and full image was skipped
1715 //Or: If keep exif is unchecked and the smushed image have exif
1716 //PNG To JPEG
1717 if ( $this->smush_original ) {
1718 //IF full image was not smushed
1719 if ( ! empty( $wp_smush_data ) && empty( $wp_smush_data['sizes']['full'] ) ) {
1720 return true;
1721 }
1722 }
1723
1724 //EXIF Check
1725 if ( ! $this->keep_exif ) {
1726 //If Keep Exif was set to true initially, and since it is set to false now
1727 if ( isset( $wp_smush_data['stats']['keep_exif'] ) && $wp_smush_data['stats']['keep_exif'] == 1 ) {
1728 return true;
1729 }
1730 }
1731
1732 //PNG to JPEG
1733 global $wpsmush_pngjpg;
1734 if ( $wpsmush_pngjpg->can_be_converted( $id ) ) {
1735 return true;
1736 }
1737
1738 return false;
1739
1740 }
1741
1742 /**
1743 * Calculate saving percentage from existing and current stats
1744 *
1745 * @param $stats
1746 * @param $existing_stats
1747 *
1748 * @return float
1749 */
1750 function calculate_percentage( $stats = '', $existing_stats = '' ) {
1751 if ( empty( $stats ) || empty( $existing_stats ) ) {
1752 return 0;
1753 }
1754 $size_before = ! empty( $stats->size_before ) ? $stats->size_before : $existing_stats->size_before;
1755 $size_after = ! empty( $stats->size_after ) ? $stats->size_after : $existing_stats->size_after;
1756 $savings = $size_before - $size_after;
1757 if ( $savings > 0 ) {
1758 $percentage = ( $savings / $size_before ) * 100;
1759 $percentage = $percentage > 0 ? round( $percentage, 2 ) : $percentage;
1760
1761 return $percentage;
1762 }
1763
1764 return 0;
1765 }
1766
1767 /**
1768 * Redirects the user to Plugin settings page on Plugin activation
1769 *
1770 * @param $plugin Plugin Name
1771 *
1772 * @return bool
1773 */
1774 function wp_smush_redirect( $plugin ) {
1775
1776 global $wpsmushit_admin, $wpsmush_stats;
1777
1778 //Run for only our plugin
1779 if( $plugin != WP_SMUSH_BASENAME ) {
1780 return false;
1781 }
1782
1783 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
1784 return true;
1785 }
1786
1787 //Skip if bulk activation, Or if we have to skip redirection
1788 if ( isset( $_GET['activate-multi'] ) || get_site_option('wp-smush-skip-redirect') ) {
1789 return false;
1790 }
1791
1792 //If images are already smushed
1793 if( $wpsmush_stats->smushed_count( false ) > 0 ) {
1794 return false;
1795 }
1796
1797 $url = admin_url( 'upload.php' );
1798 $url = add_query_arg(
1799 array(
1800 'page' => 'wp-smush-bulk'
1801 ),
1802 $url
1803 );
1804
1805 //Store that we need not redirect again
1806 add_site_option('wp-smush-skip-redirect', true );
1807
1808 exit( wp_redirect( $url ) );
1809 }
1810
1811 /**
1812 * Clear up all the backup files for the image, if any
1813 * @param $image_id
1814 */
1815 function delete_backup_files( $image_id ) {
1816 $smush_meta = get_post_meta( $image_id, $this->smushed_meta_key, true );
1817 if ( empty( $smush_meta ) ) {
1818 //Return if we don't have any details
1819 return;
1820 }
1821
1822 //Get the attachment details
1823 $meta = wp_get_attachment_metadata( $image_id );
1824
1825 //Attachment file path
1826 $file = get_attached_file( $image_id );
1827
1828 //Get the backup path
1829 $backup_name = $this->get_image_backup_path( $file );
1830
1831 //If file exists, corresponding to our backup path, delete it
1832 @ unlink( $backup_name );
1833
1834 //Check meta for rest of the sizes
1835 if ( ! empty( $meta ) && ! empty( $meta['sizes'] ) ) {
1836 foreach ( $meta['sizes'] as $size ) {
1837 //Get the file path
1838 if ( empty( $size['file'] ) ) {
1839 continue;
1840 }
1841
1842 //Image Path and Backup path
1843 $image_size_path = path_join( dirname( $file ), $size['file'] );
1844 $image_bckup_path = $this->get_image_backup_path( $image_size_path );
1845 @unlink( $image_bckup_path );
1846 }
1847 }
1848 }
1849
1850 /**
1851 * Manually Dismiss Smush Upgrade notice
1852 */
1853 function dismiss_smush_upgrade() {
1854 if ( isset( $_GET['remove_smush_upgrade_notice'] ) && 1 == $_GET['remove_smush_upgrade_notice'] ) {
1855 global $wpsmushit_admin;
1856 $wpsmushit_admin->dismiss_upgrade_notice( false );
1857 }
1858 }
1859
1860 /**
1861 * Iterate over all the size stats and calculate the total stats
1862 *
1863 * @param $stats
1864 *
1865 */
1866 function total_compression( $stats ) {
1867 foreach ( $stats['sizes'] as $size_stats ) {
1868 $stats['stats']['size_before'] += !empty( $size_stats->size_before ) ? $size_stats->size_before : 0;
1869 $stats['stats']['size_after'] += !empty( $size_stats->size_after) ? $size_stats->size_after : 0;
1870 $stats['stats']['time'] += !empty($size_stats->time ) ? $size_stats->time : 0;
1871 }
1872 $stats['stats']['bytes'] = ! empty( $stats['stats']['size_before'] ) && $stats['stats']['size_before'] > $stats['stats']['size_after'] ? $stats['stats']['size_before'] - $stats['stats']['size_after'] : 0;
1873 if ( ! empty( $stats['stats']['bytes'] ) && ! empty( $stats['stats']['size_before'] ) ) {
1874 $stats['stats']['percent'] = ( $stats['stats']['bytes'] / $stats['stats']['size_before'] ) * 100;
1875 }
1876
1877 return $stats;
1878 }
1879
1880 /**
1881 * Smush and Resizing Stats Combined together
1882 *
1883 * @param $smush_stats
1884 * @param $resize_savings
1885 *
1886 * @return array Array of all the stats
1887 */
1888 function combined_stats( $smush_stats, $resize_savings ) {
1889 if ( empty( $smush_stats ) || empty( $resize_savings ) ) {
1890 return $smush_stats;
1891 }
1892
1893 //Full Image
1894 if( !empty( $smush_stats['sizes']['full'] ) ) {
1895 $smush_stats['sizes']['full']->bytes = ! empty( $resize_savings['bytes'] ) ? $smush_stats['sizes']['full']->bytes + $resize_savings['bytes'] : $smush_stats['sizes']['full']->bytes;
1896 $smush_stats['sizes']['full']->size_before = ! empty( $resize_savings['size_before'] ) && ( $resize_savings['size_before'] > $smush_stats['sizes']['full']->size_before ) ? $resize_savings['size_before'] : $smush_stats['sizes']['full']->size_before;
1897 $smush_stats['sizes']['full']->percent = ! empty( $smush_stats['sizes']['full']->bytes ) && $smush_stats['sizes']['full']->size_before > 0 ? ( $smush_stats['sizes']['full']->bytes / $smush_stats['sizes']['full']->size_before ) * 100 : $smush_stats['sizes']['full']->percent;
1898
1899 $smush_stats['sizes']['full']->percent = round( $smush_stats['sizes']['full']->percent, 1 );
1900 }
1901
1902 $smush_stats = $this->total_compression( $smush_stats );
1903
1904 return $smush_stats;
1905 }
1906
1907 /**
1908 * Combine Savings from PNG to JPG conversion with smush stats
1909 *
1910 * @param $stats Savings from Smushing the image
1911 * @param $conversion_savings Savings from converting the PNG to JPG
1912 *
1913 * @return Object Total Savings
1914 */
1915 function combine_conversion_stats( $stats, $conversion_savings ) {
1916 if ( empty( $stats ) || empty( $conversion_savings ) ) {
1917 return $stats;
1918 }
1919 foreach ( $conversion_savings as $size_k => $savings ) {
1920 if ( ! empty( $stats['sizes'][ $size_k ] ) && ! empty( $savings ) ) {
1921 $stats['sizes'][ $size_k ]->bytes = $stats['sizes'][ $size_k ]->bytes + $savings['bytes'];
1922 $stats['sizes'][ $size_k ]->size_before = $stats['sizes'][ $size_k ]->size_before > $savings['size_before'] ? $stats['sizes'][ $size_k ]->size_before : $savings['size_before'];
1923 $stats['sizes'][ $size_k ]->percent = ! empty( $stats['sizes'][ $size_k ]->bytes ) && $stats['sizes'][ $size_k ]->size_before > 0 ? ( $stats['sizes'][ $size_k ]->bytes / $stats['sizes'][ $size_k ]->size_before ) * 100 : $stats['sizes'][ $size_k ]->percent;
1924 $stats['sizes'][ $size_k ]->percent = round( $stats['sizes'][ $size_k ]->percent, 1 );
1925 }
1926 }
1927
1928 $stats = $this->total_compression( $stats );
1929
1930 return $stats;
1931 }
1932
1933 /**
1934 * Original File path
1935 *
1936 * @param string $original_file
1937 *
1938 */
1939 function original_file( $original_file = '' ) {
1940 $uploads = wp_get_upload_dir();
1941 $upload_path = $uploads['basedir'];
1942
1943 return path_join( $upload_path, $original_file );
1944 }
1945 }
1946
1947 global $WpSmush;
1948 $WpSmush = new WpSmush();
1949
1950 }
1951
1952 //Include Admin classes
1953 require_once( WP_SMUSH_DIR . 'lib/class-wp-smush-bulk.php' );
1954 require_once( WP_SMUSH_DIR . 'lib/class-wp-smush-admin.php' );