PluginProbe ʕ •ᴥ•ʔ
Smush – Image Optimization, Compression, Lazy Load, WebP & CDN / 2.0
Smush – Image Optimization, Compression, Lazy Load, WebP & CDN v2.0
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 / wp-smush.php
wp-smushit Last commit date
assets 11 years ago extras 11 years ago languages 11 years ago lib 11 years ago license.txt 17 years ago readme.txt 11 years ago uninstall.php 11 years ago wp-smush.php 11 years ago wp-smushit.php 11 years ago
wp-smush.php
899 lines
1 <?php
2 /*
3 Plugin Name: WP Smush
4 Plugin URI: http://wordpress.org/extend/plugins/wp-smushit/
5 Description: Reduce image file sizes and improve performance using the <a href="https://premium.wpmudev.org/">WPMU DEV</a> Smush API within WordPress.
6 Author: WPMU DEV
7 Version: 2.0
8 Author URI: http://premium.wpmudev.org/
9 Textdomain: wp_smush
10 */
11
12 /*
13 This plugin was originally developed by Alex Dunae.
14 http://dialect.ca/
15 */
16
17 /*
18 Copyright 2007-2015 Incsub (http://incsub.com)
19
20 This program is free software; you can redistribute it and/or modify
21 it under the terms of the GNU General Public License (Version 2 - GPLv2) as published by
22 the Free Software Foundation.
23
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
28
29 You should have received a copy of the GNU General Public License
30 along with this program; if not, write to the Free Software
31 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
32 */
33
34
35 /**
36 * Constants
37 */
38 define( 'WP_SMUSH_VERSON', "2.0" );
39 /**
40 * Plugin base name
41 */
42 define( 'WP_SMUSH_BASENAME', plugin_basename( __FILE__ ) );
43
44 define( 'WP_SMUSH_API', 'https://smushpro.wpmudev.org/1.0/' );
45 define( 'WP_SMUSH_DOMAIN', 'wp_smush' );
46 define( 'WP_SMUSH_UA', 'WP Smush/' . WP_SMUSH_VERSON . '; ' . network_home_url() );
47 define( 'WP_SMUSH_DIR', plugin_dir_path( __FILE__ ) );
48 define( 'WP_SMUSH_URL', plugin_dir_url( __FILE__ ) );
49 define( 'WP_SMUSH_MAX_BYTES', 1000000 );
50 define( 'WP_SMUSH_PREMIUM_MAX_BYTES', 8000000 );
51 define( 'WP_SMUSH_PREFIX', 'wp-smush-' );
52 if ( ! defined( 'WP_SMUSH_TIMEOUT' ) ) {
53 define( 'WP_SMUSH_TIMEOUT', 30 );
54 }
55
56 require_once WP_SMUSH_DIR . "/lib/class-wp-smush-migrate.php";
57
58 if ( ! class_exists( 'WpSmush' ) ) {
59
60 class WpSmush {
61
62 var $version = WP_SMUSH_VERSON;
63
64 /**
65 * Meta key for api validity
66 *
67 */
68 const VALIDITY_KEY = "wp-smush-valid";
69
70 /**
71 * Api server url to check api key validity
72 *
73 */
74 const API_SERVER = 'https://premium.wpmudev.org/wdp-un.php?action=smushit_check';
75
76 /**
77 * Meta key to save smush result to db
78 *
79 *
80 */
81 const SMUSHED_META_KEY = 'wp-smpro-smush-data';
82
83 /**
84 * Meta key to save migrated version
85 *
86 */
87 const MIGRATED_VERSION = "wp-smush-migrated-version";
88
89 /**
90 * Constructor
91 */
92 function __construct() {
93 /**
94 * Hooks
95 */
96 //Check if auto is enabled
97 $auto_smush = get_option( WP_SMUSH_PREFIX . 'auto' );
98
99 //Keep the uto smush on by default
100 if ( $auto_smush === false ) {
101 $auto_smush = 1;
102 }
103
104 if ( $auto_smush ) {
105 add_filter( 'wp_generate_attachment_metadata', array( $this, 'filter_generate_attachment_metadata' ), 10, 2 );
106 }
107 add_filter( 'manage_media_columns', array( $this, 'columns' ) );
108 add_action( 'manage_media_custom_column', array( $this, 'custom_column' ), 10, 2 );
109 add_action( 'admin_init', array( $this, 'admin_init' ) );
110 add_action( "admin_init", array( $this, "migrate" ) );
111
112 }
113
114 function admin_init() {
115 load_plugin_textdomain( WP_SMUSH_DOMAIN, false, dirname( WP_SMUSH_BASENAME ) . '/languages/' );
116 wp_enqueue_script( 'common' );
117 }
118
119 /**
120 * Process an image with Smush.
121 *
122 * Returns an array of the $file $results.
123 *
124 * @param string $file Full absolute path to the image file
125 * @param string $file_url Optional full URL to the image file
126 *
127 * @returns array
128 */
129 function do_smushit( $attachment_id, $file_path = '', $file_url = '' ) {
130 $errors = new WP_Error();
131 if ( empty( $file_path ) ) {
132 $errors->add( "empty_path", __( "File path is empty", WP_SMUSH_DOMAIN ) );
133 }
134
135 if ( empty( $file_url ) ) {
136 $errors->add( "empty_url", __( "File URL is empty", WP_SMUSH_DOMAIN ) );
137 }
138
139 // check that the file exists
140 if ( ! file_exists( $file_path ) || ! is_file( $file_path ) ) {
141 $errors->add( "file_not_found", sprintf( __( "Could not find %s", WP_SMUSH_DOMAIN ), $file_path ) );
142 }
143
144 // check that the file is writable
145 if ( ! is_writable( dirname( $file_path ) ) ) {
146 $errors->add( "not_writable", sprintf( __( "%s is not writable", WP_SMUSH_DOMAIN ), dirname( $file_path ) ) );
147 }
148
149 $file_size = filesize( $file_path );
150
151 //Check if premium user
152 $max_size = $this->is_premium() ? WP_SMUSH_PREMIUM_MAX_BYTES : WP_SMUSH_MAX_BYTES;
153
154 //Check if file exists
155 if ( $file_size == 0 ) {
156 $errors->add( "image_not_found", sprintf( __( 'Skipped (%s), image not found.', WP_SMUSH_DOMAIN ), $this->format_bytes( $file_size ) ) );
157 }
158
159 //Check size limit
160 if ( $file_size > $max_size ) {
161 $errors->add( "size_limit", sprintf( __( 'Skipped (%s), size limit exceeded.', WP_SMUSH_DOMAIN ), $this->format_bytes( $file_size ) ) );
162 }
163
164 if ( count( $errors->get_error_messages() ) ) {
165 return $errors;
166 }
167
168 /** Send image for smushing, and fetch the response */
169 $response = $this->_post( $file_path, $file_size );
170
171 if ( ! $response['success'] ) {
172 $errors->add( "false_response", $response['message'] );
173 }
174 //If there is no data
175 if ( empty( $response['data'] ) ) {
176 $errors->add( "no_data", __( 'Unknown API error', WP_SMUSH_DOMAIN ) );
177 }
178
179 if ( count( $errors->get_error_messages() ) ) {
180 return $errors;
181 }
182
183 //If there are no savings, or image returned is bigger in size
184 if ( ( ! empty( $response['data']->bytes_saved ) && intval( $response['data']->bytes_saved ) <= 0 )
185 || empty( $response['data']->image )
186 ) {
187 return $response;
188 }
189 $tempfile = $file_path . ".tmp";
190
191 //Add the file as tmp
192 file_put_contents( $tempfile, $response['data']->image );
193
194 //handle backups if enabled
195 $backup = get_option( WP_SMUSH_PREFIX . 'backup' );
196 if ( $backup && $this->is_premium() ) {
197 $path = pathinfo( $file_path );
198 $backup_name = trailingslashit( $path['dirname'] ) . $path['filename'] . ".bak." . $path['extension'];
199 @copy( $file_path, $backup_name );
200 }
201
202 //replace the file
203 $success = @rename( $tempfile, $file_path );
204
205 //if tempfile still exists, unlink it
206 if ( file_exists( $tempfile ) ) {
207 unlink( $tempfile );
208 }
209
210 //If file renaming was successful
211 if ( ! $success ) {
212 copy( $tempfile, $file_path );
213 unlink( $tempfile );
214 }
215
216 return $response;
217 }
218
219 /**
220 * Fills $placeholder array with values from $data array without creating new keys
221 *
222 * @param array $placeholders
223 * @param array $data
224 *
225 * @return array
226 */
227 private function _array_fill_placeholders( array $placeholders, array $data ) {
228 return array_merge( $placeholders, array_intersect_key( $data, $placeholders ) );
229 }
230
231 /**
232 * Returns signature for single size of the smush api message to be saved to db;
233 *
234 * @return array
235 */
236 private function _get_size_signature() {
237 return array(
238 'percent' => - 1,
239 'bytes' => - 1,
240 'size_before' => - 1,
241 'size_after' => - 1,
242 'time' => - 1
243 );
244 }
245
246 /**
247 * Read the image paths from an attachment's meta data and process each image
248 * with wp_smushit().
249 *
250 * This method also adds a `wp_smushit` meta key for use in the media library.
251 * Called after `wp_generate_attachment_metadata` is completed.
252 *
253 * @param $meta
254 * @param null $ID
255 * @param bool $force_resmush
256 *
257 * @return mixed
258 */
259 function resize_from_meta_data( $meta, $ID = null, $force_resmush = true ) {
260
261 //Flag to check, if original size image needs to be smushed or not
262 $smush_full = true;
263 $errors = new WP_Error();
264 $stats = array(
265 "stats" => array_merge( $this->_get_size_signature(), array(
266 'api_version' => - 1,
267 'lossy' => - 1
268 )
269 ),
270 'sizes' => array()
271 );
272
273 $size_before = $size_after = $compression = $total_time = $bytes_saved = 0;
274
275 if ( $ID && wp_attachment_is_image( $ID ) === false ) {
276 return $meta;
277 }
278
279 //File path and URL for original image
280 $attachment_file_path = get_attached_file( $ID );
281 $attachment_file_url = wp_get_attachment_url( $ID );
282
283 // If images has other registered size, smush them first
284 if ( ! empty( $meta['sizes'] ) ) {
285
286 foreach ( $meta['sizes'] as $size_key => $size_data ) {
287
288 //if there is a large size, then we will set a flag to leave the original untouched
289 if ( $size_key == 'large' ) {
290 $smush_full = false;
291 }
292
293 // We take the original image. The 'sizes' will all match the same URL and
294 // path. So just get the dirname and replace the filename.
295
296 $attachment_file_path_size = trailingslashit( dirname( $attachment_file_path ) ) . $size_data['file'];
297 $attachment_file_url_size = trailingslashit( dirname( $attachment_file_url ) ) . $size_data['file'];
298
299 //Store details for each size key
300 $response = $this->do_smushit( $ID, $attachment_file_path_size, $attachment_file_url_size );
301
302 if ( is_wp_error( $response ) ) {
303 return $response;
304 }
305
306 if ( ! empty( $response['data'] ) ) {
307 $stats['sizes'][ $size_key ] = (object) $this->_array_fill_placeholders( $this->_get_size_signature(), (array) $response['data'] );
308 }
309
310 //Total Stats, store all data in bytes
311 if ( isset( $response['data'] ) ) {
312 list( $size_before, $size_after, $total_time, $compression, $bytes_saved )
313 = $this->_update_stats_data( $response['data'], $size_before, $size_after, $total_time, $bytes_saved );
314 } else {
315 $errors->add( "image_size_error" . $size_key, sprintf( __( "Size '%s' not processed correctly", WP_SMUSH_DOMAIN ), $size_key ) );
316 }
317
318 if ( empty( $stats['stats']['api_version'] ) ) {
319 $stats['stats']['api_version'] = $response['data']->api_version;
320 $stats['stats']['lossy'] = $response['data']->lossy;
321 }
322 }
323 }
324
325 //If original size is supposed to be smushed
326 if ( $smush_full ) {
327
328 $full_image_response = $this->do_smushit( $ID, $attachment_file_path, $attachment_file_url );
329
330 if ( is_wp_error( $full_image_response ) ) {
331 return $full_image_response;
332 }
333
334 if ( ! empty( $full_image_response['data'] ) ) {
335 $stats['sizes']['full'] = (object) $this->_array_fill_placeholders( $this->_get_size_signature(), (array) $full_image_response['data'] );
336 } else {
337 $errors->add( "image_size_error", __( "Size 'full' not processed correctly", WP_SMUSH_DOMAIN ) );
338 }
339
340 //Update stats
341 if ( isset( $full_image_response['data'] ) ) {
342 list( $size_before, $size_after, $total_time, $compression, $bytes_saved )
343 = $this->_update_stats_data( $full_image_response['data'], $size_before, $size_after, $total_time, $bytes_saved );
344 } else {
345 $errors->add( "image_size_error", __( "Size 'full' not processed correctly", WP_SMUSH_DOMAIN ) );
346 }
347
348 }
349
350 $has_errors = (bool) count( $errors->get_error_messages() );
351
352 //Store stats
353
354 list( $stats['stats']['size_before'], $stats['stats']['size_after'], $stats['stats']['time'], $stats['stats']['percent'], $stats['stats']['bytes'] ) =
355 array( $size_before, $size_after, $total_time, $compression, $bytes_saved );
356
357
358 //Set smush status for all the images, store it in wp-smpro-smush-data
359 if ( ! $has_errors ) {
360 update_post_meta( $ID, self::SMUSHED_META_KEY, $stats );
361 }
362
363 //return stats
364 return $has_errors ? $errors : $stats['stats'];
365 }
366
367 /**
368 * Read the image paths from an attachment's meta data and process each image
369 * with wp_smushit()
370 *
371 * Filters wp_generate_attachment_metadata
372 * @param $meta
373 * @param null $ID
374 *
375 * @return mixed
376 */
377 function filter_generate_attachment_metadata( $meta, $ID = null ){
378 //Flag to check, if original size image needs to be smushed or not
379 $smush_full = true;
380 $errors = new WP_Error();
381 $stats = array(
382 "stats" => array_merge( $this->_get_size_signature(), array(
383 'api_version' => - 1,
384 'lossy' => - 1
385 )
386 ),
387 'sizes' => array()
388 );
389
390 $size_before = $size_after = $compression = $total_time = $bytes_saved = 0;
391
392 if ( $ID && wp_attachment_is_image( $ID ) === false ) {
393 return $meta;
394 }
395
396 //File path and URL for original image
397 $attachment_file_path = get_attached_file( $ID );
398 $attachment_file_url = wp_get_attachment_url( $ID );
399
400 // If images has other registered size, smush them first
401 if ( ! empty( $meta['sizes'] ) ) {
402
403 foreach ( $meta['sizes'] as $size_key => $size_data ) {
404
405 //if there is a large size, then we will set a flag to leave the original untouched
406 if ( $size_key == 'large' ) {
407 $smush_full = false;
408 }
409
410 // We take the original image. The 'sizes' will all match the same URL and
411 // path. So just get the dirname and replace the filename.
412
413 $attachment_file_path_size = trailingslashit( dirname( $attachment_file_path ) ) . $size_data['file'];
414 $attachment_file_url_size = trailingslashit( dirname( $attachment_file_url ) ) . $size_data['file'];
415
416 //Store details for each size key
417 $response = $this->do_smushit( $ID, $attachment_file_path_size, $attachment_file_url_size );
418
419 if ( is_wp_error( $response ) ) {
420 return $meta;
421 }
422
423 if ( ! empty( $response['data'] ) ) {
424 $stats['sizes'][ $size_key ] = (object) $this->_array_fill_placeholders( $this->_get_size_signature(), (array) $response['data'] );
425 }
426
427 //Total Stats, store all data in bytes
428 if ( isset( $response['data'] ) ) {
429 list( $size_before, $size_after, $total_time, $compression, $bytes_saved )
430 = $this->_update_stats_data( $response['data'], $size_before, $size_after, $total_time, $bytes_saved );
431 } else {
432 $errors->add( "image_size_error" . $size_key, sprintf( __( "Size '%s' not processed correctly", WP_SMUSH_DOMAIN ), $size_key ) );
433 }
434
435 if ( empty( $stats['stats']['api_version'] ) ) {
436 $stats['stats']['api_version'] = $response['data']->api_version;
437 $stats['stats']['lossy'] = $response['data']->lossy;
438 }
439 }
440 }
441
442 //If original size is supposed to be smushed
443 if ( $smush_full ) {
444
445 $full_image_response = $this->do_smushit( $ID, $attachment_file_path, $attachment_file_url );
446
447 if ( is_wp_error( $full_image_response ) ) {
448 return $meta;
449 }
450
451 if ( ! empty( $full_image_response['data'] ) ) {
452 $stats['sizes']['full'] = (object) $this->_array_fill_placeholders( $this->_get_size_signature(), (array) $full_image_response['data'] );
453 } else {
454 $errors->add( "image_size_error", __( "Size 'full' not processed correctly", WP_SMUSH_DOMAIN ) );
455 }
456
457 //Update stats
458 if ( isset( $full_image_response['data'] ) ) {
459 list( $size_before, $size_after, $total_time, $compression, $bytes_saved )
460 = $this->_update_stats_data( $full_image_response['data'], $size_before, $size_after, $total_time, $bytes_saved );
461 } else {
462 $errors->add( "image_size_error", __( "Size 'full' not processed correctly", WP_SMUSH_DOMAIN ) );
463 }
464
465 }
466
467 $has_errors = (bool) count( $errors->get_error_messages() );
468
469 //Store stats
470
471 list( $stats['stats']['size_before'], $stats['stats']['size_after'], $stats['stats']['time'], $stats['stats']['percent'], $stats['stats']['bytes'] ) =
472 array( $size_before, $size_after, $total_time, $compression, $bytes_saved );
473
474
475 //Set smush status for all the images, store it in wp-smpro-smush-data
476 if ( ! $has_errors ) {
477 update_post_meta( $ID, self::SMUSHED_META_KEY, $stats );
478 }
479
480 //return stats
481 return $has_errors ? $meta : $stats['stats'];
482 }
483
484 /**
485 * Post an image to Smush.
486 *
487 * @param string $file_url URL of the file to send to Smush
488 *
489 * @return array Returns array containing success status, and stats
490 */
491 function _post( $file_path, $file_size ) {
492 global $log;
493
494 $data = false;
495
496 $file = @fopen( $file_path, 'r' );
497 $file_data = fread( $file, $file_size );
498 $headers = array(
499 'accept' => 'application/json', // The API returns JSON
500 'content-type' => 'application/binary', // Set content type to binary
501 );
502
503 //Check if premium member, add API key
504 $api_key = $this->_get_api_key();
505 if ( ! empty( $api_key ) ) {
506 $headers['apikey'] = $api_key;
507 }
508
509 //Check if lossy compression allowed and add it to headers
510 $lossy = get_option( WP_SMUSH_PREFIX . 'lossy' );
511
512 if ( $lossy && $this->is_premium() ) {
513 $headers['lossy'] = 'true';
514 } else {
515 $headers['lossy'] = 'false';
516 }
517
518 $args = array(
519 'headers' => $headers,
520 'body' => $file_data,
521 'timeout' => WP_SMUSH_TIMEOUT,
522 'user-agent' => WP_SMUSH_UA
523 );
524 $result = wp_remote_post( WP_SMUSH_API, $args );
525
526 //Close file connection
527 fclose( $file );
528 unset( $file_data );//free memory
529 if ( is_wp_error( $result ) ) {
530 //Handle error
531 $data['message'] = sprintf( __( 'Error posting to API: %s', WP_SMUSH_DOMAIN ), $result->get_error_message() );
532 $data['success'] = false;
533 unset( $result ); //free memory
534 return $data;
535 } else if ( '200' != wp_remote_retrieve_response_code( $result ) ) {
536 //Handle error
537 $data['message'] = sprintf( __( 'Error posting to API: %s %s', WP_SMUSHIT_DOMAIN ), wp_remote_retrieve_response_code( $result ), wp_remote_retrieve_response_message( $result ) );
538 $data['success'] = false;
539 unset( $result ); //free memory
540
541 return $data;
542 }
543
544 //If there is a response and image was successfully optimised
545 $response = json_decode( $result['body'] );
546 if ( $response && $response->success == true ) {
547
548 //If there is any savings
549 if ( $response->data->bytes_saved > 0 ) {
550 $image = base64_decode( $response->data->image ); //base64_decode is necessary to send binary img over JSON, no security problems here!
551 $image_md5 = md5( $response->data->image );
552 if ( $response->data->image_md5 != $image_md5 ) {
553 //Handle error
554 $data['message'] = __( 'Smush data corrupted, try again.', WP_SMUSH_DOMAIN );
555 $data['success'] = false;
556 unset( $image );//free memory
557 } else {
558 $data['success'] = true;
559 $data['data'] = $response->data;
560 $data['data']->image = $image;
561 unset( $image );//free memory
562 }
563 } else {
564 //just return the data
565 $data['success'] = true;
566 $data['data'] = $response->data;
567 }
568 } else {
569 //Server side error, get message from response
570 $data['message'] = ! empty( $response->data ) ? $response->data : __( "Image couldn't be smushed", WP_SMUSH_DOMAIN );
571 $data['success'] = false;
572 }
573
574 unset( $result );//free memory
575 unset( $response );//free memory
576 return $data;
577 }
578
579
580 /**
581 * Print column header for Smush results in the media library using
582 * the `manage_media_columns` hook.
583 */
584 function columns( $defaults ) {
585 $defaults['smushit'] = 'WP Smush';
586
587 return $defaults;
588 }
589
590 /**
591 * Return the filesize in a humanly readable format.
592 * Taken from http://www.php.net/manual/en/function.filesize.php#91477
593 */
594 function format_bytes( $bytes, $precision = 2 ) {
595 $units = array( 'B', 'KB', 'MB', 'GB', 'TB' );
596 $bytes = max( $bytes, 0 );
597 $pow = floor( ( $bytes ? log( $bytes ) : 0 ) / log( 1024 ) );
598 $pow = min( $pow, count( $units ) - 1 );
599 $bytes /= pow( 1024, $pow );
600
601 return round( $bytes, $precision ) . ' ' . $units[ $pow ];
602 }
603
604 /**
605 * Print column data for Smush results in the media library using
606 * the `manage_media_custom_column` hook.
607 */
608 function custom_column( $column_name, $id ) {
609 if ( 'smushit' == $column_name ) {
610 $this->set_status( $id );
611 }
612 }
613
614 /**
615 * Check if user is premium member, check for api key
616 *
617 * @return mixed|string
618 */
619 function is_premium() {
620
621 //no api key set, always false
622 $api_key = $this->_get_api_key();
623 if ( empty( $api_key ) ) {
624 return false;
625 }
626
627 $key = "wp-smush-valid-$api_key"; //add apikey to transient key in case it changes
628
629 if ( false === ( $valid = get_site_transient( $key ) ) ) {
630 // call api
631 $url = self::API_SERVER . '&key=' . urlencode( $api_key );
632
633 $request = wp_remote_get( $url, array(
634 "timeout" => 3
635 )
636 );
637
638 if ( ! is_wp_error( $request ) && '200' == wp_remote_retrieve_response_code( $request ) ) {
639 $result = json_decode( wp_remote_retrieve_body( $request ) );
640 if ( $result && $result->success ) {
641 $valid = true;
642 set_site_transient( $key, 1, 12 * HOUR_IN_SECONDS );
643 } else {
644 $valid = false;
645 set_site_transient( $key, 0, 30 * MINUTE_IN_SECONDS ); //cache failure much shorter
646 }
647
648 } else {
649 $valid = false;
650 set_site_transient( $key, 0, 5 * MINUTE_IN_SECONDS ); //cache network failure even shorter, we don't want a request every pageload
651 }
652
653 }
654
655 return (bool) $valid;
656 }
657
658 /**
659 * Returns api key
660 *
661 * @return mixed
662 */
663 private function _get_api_key() {
664 if ( defined( 'WPMUDEV_APIKEY' ) ) {
665 $api_key = WPMUDEV_APIKEY;
666 } else {
667 $api_key = get_site_option( 'wpmudev_apikey' );
668 }
669
670 return $api_key;
671 }
672
673
674 /**
675 * Checks if image is already smushed
676 *
677 * @param int $id
678 * @param array $data
679 *
680 * @return bool|mixed
681 */
682 function is_smushed( $id, $data = null ) {
683
684 //For new images
685 $wp_is_smushed = get_post_meta( $id, 'wp-is-smushed', true );
686
687 //Not smushed, backward compatibility, check attachment metadata
688 if ( ! $wp_is_smushed && $data !== null ) {
689 if ( isset( $data['wp_smushit'] ) && ! empty( $data['wp_smushit'] ) ) {
690 $wp_is_smushed = true;
691 }
692 }
693
694 return $wp_is_smushed;
695 }
696
697 /**
698 * Returns size saved from the api call response
699 *
700 * @param string $message
701 *
702 * @return string|bool
703 */
704 function get_saved_size( $message ) {
705 if ( preg_match( '/\((.*)\)/', $message, $matches ) ) {
706 return isset( $matches[1] ) ? $matches[1] : false;
707 }
708
709 return false;
710 }
711
712 /**
713 * Set send button status
714 *
715 * @param $id
716 * @param bool $echo
717 * @param bool $text_only
718 *
719 * @return string|void
720 */
721 function set_status( $id, $echo = true, $text_only = false ) {
722 $status_txt = $button_txt = '';
723 $show_button = false;
724 $wp_smush_data = get_post_meta( $id, self::SMUSHED_META_KEY, true );
725 // if the image is smushed
726 if ( ! empty( $wp_smush_data ) ) {
727
728 $bytes = isset( $wp_smush_data['stats']['bytes'] ) ? $wp_smush_data['stats']['bytes'] : 0;
729 $bytes_readable = ! empty( $bytes ) ? $this->format_bytes( $bytes ) : '';
730 $percent = isset( $wp_smush_data['stats']['percent'] ) ? $wp_smush_data['stats']['percent'] : 0;
731 $percent = $percent < 0 ? 0 : $percent;
732
733 if ( isset( $wp_smush_data['stats']['size_before'] ) && $wp_smush_data['stats']['size_before'] == 0 ) {
734 $status_txt = __( 'Error processing request', WP_SMUSH_DOMAIN );
735 $show_button = true;
736 } else {
737 if ( $bytes == 0 || $percent == 0 ) {
738 $status_txt = __( 'Already Optimized', WP_SMUSH_DOMAIN );
739 } elseif ( ! empty( $percent ) && ! empty( $bytes_readable ) ) {
740 $status_txt = sprintf( __( "Reduced by %s ( %01.1f%% )", WP_SMUSH_DOMAIN ), $bytes_readable, number_format_i18n( $percent, 2, '.', '' ) );
741 }
742 }
743
744 // the button text
745 $button_txt = __( 'Re-smush', WP_SMUSH_DOMAIN );
746 } else {
747
748 // the status
749 $status_txt = __( 'Not processed', WP_SMUSH_DOMAIN );
750
751 // we need to show the smush button
752 $show_button = true;
753
754 // the button text
755 $button_txt = __( 'Smush Now!', WP_SMUSH_DOMAIN );
756 }
757 if ( $text_only ) {
758 return $status_txt;
759 }
760
761 $text = $this->column_html( $id, $status_txt, $button_txt, $show_button, $wp_smush_data, $echo );
762 if ( ! $echo ) {
763 return $text;
764 }
765 }
766
767 /**
768 * Print the column html
769 *
770 * @param string $id Media id
771 * @param string $status_txt Status text
772 * @param string $button_txt Button label
773 * @param boolean $show_button Whether to shoe the button
774 *
775 * @return null
776 */
777 function column_html( $id, $status_txt = "", $button_txt = "", $show_button = true, $smushed = false, $echo = true ) {
778 $allowed_images = array( 'image/jpeg', 'image/jpg', 'image/png', 'image/gif' );
779 // don't proceed if attachment is not image, or if image is not a jpg, png or gif
780 if ( ! wp_attachment_is_image( $id ) || ! in_array( get_post_mime_type( $id ), $allowed_images ) ) {
781 return;
782 }
783 $html = '
784 <p class="smush-status">' . $status_txt . '</p>';
785 // if we aren't showing the button
786 if ( ! $show_button ) {
787 if ( $echo ) {
788 echo $html;
789
790 return;
791 } else {
792 if ( ! $smushed ) {
793 $class = ' currently-smushing';
794 } else {
795 $class = ' smushed';
796 }
797
798 return '<div class="smush-wrap' . $class . '">' . $html . '</div>';
799 }
800 }
801 if ( ! $echo ) {
802 $html .= '
803 <button class="button button-primary wp-smush-send" data-id="' . $id . '">
804 <span>' . $button_txt . '</span>
805 </button>';
806 if ( ! $smushed ) {
807 $class = ' unsmushed';
808 } else {
809 $class = ' smushed';
810 }
811
812 return '<div class="smush-wrap' . $class . '">' . $html . '</div>';
813 } else {
814 $html .= '<button class="button wp-smush-send" data-id="' . $id . '">
815 <span>' . $button_txt . '</span>
816 </button>';
817 echo $html;
818 }
819 }
820
821 /**
822 * Migrates smushit api message to the latest structure
823 *
824 *
825 * @return void
826 */
827 function migrate() {
828
829 if ( ! version_compare( $this->version, "1.7.1", "lte" ) ) {
830 return;
831 }
832
833 $migrated_version = get_option( self::MIGRATED_VERSION );
834
835 if ( $migrated_version === $this->version ) {
836 return;
837 }
838
839 global $wpdb;
840
841 $q = $wpdb->prepare( "SELECT * FROM `" . $wpdb->postmeta . "` WHERE `meta_key`=%s AND `meta_value` LIKE %s ", "_wp_attachment_metadata", "%wp_smushit%" );
842 $results = $wpdb->get_results( $q );
843
844 if ( count( $results ) < 1 ) {
845 return;
846 }
847
848 $migrator = new WpSmushMigrate();
849 foreach ( $results as $attachment_meta ) {
850 $migrated_message = $migrator->migrate_api_message( maybe_unserialize( $attachment_meta->meta_value ) );
851 if ( $migrated_message !== array() ) {
852 update_post_meta( $attachment_meta->post_id, self::SMUSHED_META_KEY, $migrated_message );
853 }
854 }
855
856 update_option( self::MIGRATED_VERSION, $this->version );
857
858 }
859
860 /**
861 * @param Object $response_data
862 * @param $size_before
863 * @param $size_after
864 * @param $total_time
865 * @param $bytes_saved
866 *
867 * @return array
868 */
869 private function _update_stats_data( $response_data, $size_before, $size_after, $total_time, $bytes_saved ) {
870 $size_before += ! empty( $response_data->before_size ) ? (int) $response_data->before_size : 0;
871 $size_after += ( ! empty( $response_data->after_size ) && $response_data->after_size > 0 ) ? (int) $response_data->after_size : (int) $response_data->before_size;
872 $total_time += ! empty( $response_data->time ) ? (float) $response_data->time : 0;
873 $bytes_saved += ( ! empty( $response_data->bytes_saved ) && $response_data->bytes_saved > 0 ) ? $response_data->bytes_saved : 0;
874 $compression = ( $bytes_saved > 0 && $size_before > 0 ) ? ( ( $bytes_saved / $size_before ) * 100 ) : 0;
875
876 return array( $size_before, $size_after, $total_time, $compression, $bytes_saved );
877 }
878 }
879
880 global $WpSmush;
881 $WpSmush = new WpSmush();
882
883 }
884
885 //Include Admin classes
886 require_once( WP_SMUSH_DIR . '/lib/class-wp-smush-bulk.php' );
887 require_once( WP_SMUSH_DIR . '/lib/class-wp-smush-admin.php' );
888 //include_once( WP_SMUSH_DIR . '/extras/dash-notice/wpmudev-dash-notification.php' );
889
890 //register items for the dashboard plugin
891 global $wpmudev_notices;
892 $wpmudev_notices[] = array(
893 'id' => 912164,
894 'name' => 'WP Smush Pro',
895 'screens' => array(
896 'media_page_wp-smush-bulk',
897 'upload'
898 )
899 );