PluginProbe ʕ •ᴥ•ʔ
Smush – Image Optimization, Compression, Lazy Load, WebP & CDN / 3.15.3
Smush – Image Optimization, Compression, Lazy Load, WebP & CDN v3.15.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 / app / class-media-library.php
wp-smushit / app Last commit date
assets 2 years ago common 2 years ago modals 2 years ago pages 2 years ago views 2 years ago class-abstract-page.php 2 years ago class-abstract-summary-page.php 2 years ago class-admin.php 2 years ago class-ajax.php 2 years ago class-interface-page.php 2 years ago class-media-library.php 2 years ago
class-media-library.php
531 lines
1 <?php
2 /**
3 * Media library class.
4 *
5 * Responsible for displaying a UI (stats + action links) in the media library and the editor.
6 *
7 * @since 3.4.0
8 * @package Smush\App
9 */
10
11 namespace Smush\App;
12
13 use Smush\Core\Core;
14 use Smush\Core\Helper;
15 use Smush\Core\Media\Media_Item;
16 use Smush\Core\Media\Media_Item_Optimizer;
17 use Smush\Core\Media_Library\Media_Library_Row;
18 use Smush\Core\Modules\Abstract_Module;
19 use Smush\Core\Modules\Smush;
20 use Smush\Core\Stats\Global_Stats;
21 use WP_Post;
22 use WP_Query;
23
24 /**
25 * Class Media_Library
26 */
27 class Media_Library extends Abstract_Module {
28
29 /**
30 * Core instance.
31 *
32 * @var Core $core
33 */
34 private $core;
35 private $allowed_image_sizes;
36
37 /**
38 * Media_Library constructor.
39 *
40 * @param Core $core Core instance.
41 */
42 public function __construct( Core $core ) {
43 parent::__construct();
44 $this->core = $core;
45 }
46
47 /**
48 * Init functionality that is related to the UI.
49 */
50 public function init_ui() {
51 // Media library columns.
52 add_filter( 'manage_media_columns', array( $this, 'columns' ) );
53 add_filter( 'manage_upload_sortable_columns', array( $this, 'sortable_column' ) );
54 add_action( 'manage_media_custom_column', array( $this, 'custom_column' ), 10, 2 );
55
56 // Manage column sorting.
57 add_action( 'pre_get_posts', array( $this, 'smushit_orderby' ) );
58
59 // Smush image filter from Media Library.
60 add_filter( 'ajax_query_attachments_args', array( $this, 'filter_media_query' ) );
61 // Smush image filter from Media Library (list view).
62 add_action( 'restrict_manage_posts', array( $this, 'add_filter_dropdown' ) );
63
64 // Add pre WordPress 5.0 compatibility.
65 add_filter( 'wp_kses_allowed_html', array( $this, 'filter_html_attributes' ) );
66
67 add_action( 'admin_enqueue_scripts', array( $this, 'extend_media_modal' ), 15 );
68
69 add_filter( 'wp_prepare_attachment_for_js', array( $this, 'smush_send_status' ), 99, 3 );
70 }
71
72 /**
73 * Print column header for Smush results in the media library using the `manage_media_columns` hook.
74 *
75 * @param array $defaults Defaults array.
76 *
77 * @return array
78 */
79 public function columns( $defaults ) {
80 $defaults['smushit'] = 'Smush';
81
82 return $defaults;
83 }
84
85 /**
86 * Add the Smushit Column to sortable list
87 *
88 * @param array $columns Columns array.
89 *
90 * @return array
91 */
92 public function sortable_column( $columns ) {
93 $columns['smushit'] = 'smushit';
94
95 return $columns;
96 }
97
98 /**
99 * Print column data for Smush results in the media library using
100 * the `manage_media_custom_column` hook.
101 *
102 * @param string $column_name Column name.
103 * @param int $id Attachment ID.
104 */
105 public function custom_column( $column_name, $id ) {
106 if ( 'smushit' === $column_name ) {
107 $escaped_text = wp_kses_post( $this->generate_markup( $id ) );
108 if ( $this->is_failed_processing_page() ) {
109 $escaped_text = sprintf( '<div class="smush-failed-processing">%s</div>', $escaped_text );
110 }
111 echo $escaped_text;
112 }
113 }
114
115 /**
116 * Detect failed processing page.
117 *
118 * @since 3.12.0
119 *
120 * @return boolean
121 */
122 private function is_failed_processing_page() {
123 static $is_failed_processing_page;
124 if ( null === $is_failed_processing_page ) {
125 $filter = filter_input( INPUT_GET, 'smush-filter', FILTER_SANITIZE_SPECIAL_CHARS );
126 $is_failed_processing_page = 'failed_processing' === $filter;
127 }
128 return $is_failed_processing_page;
129 }
130
131 /**
132 * Order by query for smush columns.
133 *
134 * @param WP_Query $query Query.
135 *
136 * @return WP_Query
137 */
138 public function smushit_orderby( $query ) {
139 global $current_screen;
140
141 // Filter only media screen.
142 if (
143 ! is_admin()
144 || ( ! empty( $current_screen ) && 'upload' !== $current_screen->base )
145 || 'attachment' !== $query->get( 'post_type' )
146 ) {
147 return $query;
148 }
149
150 $filter = filter_input( INPUT_GET, 'smush-filter', FILTER_SANITIZE_SPECIAL_CHARS );
151
152 // Ignored.
153 if ( 'ignored' === $filter ) {
154 $query->set( 'meta_query', $this->query_ignored() );
155 return $query;
156 } elseif ( 'unsmushed' === $filter ) {
157 // Not processed.
158 $query->set( 'meta_query', $this->query_unsmushed() );
159 return $query;
160 } elseif ( 'failed_processing' === $filter ) {
161 // Failed processing.
162 $query->set( 'meta_query', $this->query_failed_processing() );
163 return $query;
164 }
165
166 // TODO: do we need this?
167 $orderby = $query->get( 'orderby' );
168
169 if ( isset( $orderby ) && 'smushit' === $orderby ) {
170 $query->set(
171 'meta_query',
172 array(
173 'relation' => 'OR',
174 array(
175 'key' => Smush::$smushed_meta_key,
176 'compare' => 'EXISTS',
177 ),
178 array(
179 'key' => Smush::$smushed_meta_key,
180 'compare' => 'NOT EXISTS',
181 ),
182 )
183 );
184 $query->set( 'orderby', 'meta_value_num' );
185 }
186
187 return $query;
188 }
189
190 /**
191 * Add our filter to the media query filter in Media Library.
192 *
193 * @since 2.9.0
194 *
195 * @see wp_ajax_query_attachments()
196 *
197 * @param array $query Query.
198 *
199 * @return mixed
200 */
201 public function filter_media_query( $query ) {
202 $post_query = filter_input( INPUT_POST, 'query', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_REQUIRE_ARRAY );
203
204 if ( ! isset( $post_query['stats'] ) ) {
205 return $query;
206 }
207
208 $filter_name = $post_query['stats'];
209
210 // Excluded.
211 if ( 'excluded' === $filter_name ) {
212 $query['meta_query'] = $this->query_ignored();
213 } elseif ( 'unsmushed' === $filter_name ) {
214 // Unsmushed.
215 $query['meta_query'] = $this->query_unsmushed();
216 } elseif ( 'failed_processing' === $filter_name ) {
217 // Failed processing.
218 $query['meta_query'] = $this->query_failed_processing();
219 }
220
221 return $query;
222 }
223
224 /**
225 * Meta query for images skipped from bulk smush.
226 *
227 * @return array
228 */
229 private function query_failed_processing() {
230 // Custom query to add error items.
231 add_filter( 'posts_where_request', array( $this, 'filter_query_to_add_media_item_errors' ) );
232
233 // Custom query for failed on optimization.
234 $meta_query = array(
235 'relation' => 'AND',
236 array(
237 'key' => Media_Item_Optimizer::ERROR_META_KEY,
238 'compare' => 'EXISTS',
239 ),
240 array(
241 'key' => Media_Item::IGNORED_META_KEY,
242 'compare' => 'NOT EXISTS',
243 ),
244 );
245
246 return $meta_query;
247 }
248
249
250 public function filter_query_to_add_media_item_errors( $where ) {
251 global $wpdb;
252
253 remove_filter( 'posts_where_request', array( $this, 'filter_query_to_add_media_item_errors' ) );
254
255 $media_error_ids = Global_Stats::get()->get_error_list()->get_ids();
256 if ( empty( $media_error_ids ) ) {
257 return $where;
258 }
259
260 $where .= sprintf( " OR {$wpdb->posts}.ID IN (%s)", join( ',', $media_error_ids ) );
261
262 return $where;
263 }
264
265 /**
266 * Meta query for images skipped from bulk smush.
267 *
268 * @return array
269 */
270 private function query_ignored() {
271 return array(
272 array(
273 'key' => Media_Item::IGNORED_META_KEY,
274 'compare' => 'EXISTS',
275 ),
276 );
277 }
278
279 /**
280 * Meta query for uncompressed images.
281 *
282 * @return array
283 */
284 private function query_unsmushed() {
285 return Core::get_unsmushed_meta_query();
286 }
287
288 /**
289 * Adds a search dropdown in Media Library list view to filter out images that have been
290 * ignored with bulk Smush.
291 *
292 * @since 3.2.0
293 */
294 public function add_filter_dropdown() {
295 $scr = get_current_screen();
296
297 if ( 'upload' !== $scr->base ) {
298 return;
299 }
300
301 $ignored = filter_input( INPUT_GET, 'smush-filter', FILTER_SANITIZE_SPECIAL_CHARS );
302
303 ?>
304 <label for="smush_filter" class="screen-reader-text">
305 <?php esc_html_e( 'Filter by Smush status', 'wp-smushit' ); ?>
306 </label>
307 <select class="smush-filters" name="smush-filter" id="smush_filter">
308 <option value="" <?php selected( $ignored, '' ); ?>><?php esc_html_e( 'Smush: All images', 'wp-smushit' ); ?></option>
309 <option value="unsmushed" <?php selected( $ignored, 'unsmushed' ); ?>><?php esc_html_e( 'Smush: Not processed', 'wp-smushit' ); ?></option>
310 <option value="ignored" <?php selected( $ignored, 'ignored' ); ?>><?php esc_html_e( 'Smush: Bulk ignored', 'wp-smushit' ); ?></option>
311 <option value="failed_processing" <?php selected( $ignored, 'failed_processing' ); ?>><?php esc_html_e( 'Smush: Failed Processing', 'wp-smushit' ); ?></option>
312 </select>
313 <?php
314 }
315
316 /**
317 * Data attributes are not allowed on <a> elements on WordPress before 5.0.0.
318 * Add backward compatibility.
319 *
320 * @since 3.5.0
321 * @see https://github.com/WordPress/WordPress/commit/a0309e80b6a4d805e4f230649be07b4bfb1a56a5#diff-a0e0d196dd71dde453474b0f791828fe
322 * @param array $context Context.
323 *
324 * @return mixed
325 */
326 public function filter_html_attributes( $context ) {
327 global $wp_version;
328
329 if ( version_compare( '5.0.0', $wp_version, '<' ) ) {
330 return $context;
331 }
332
333 $context['a']['data-tooltip'] = true;
334 $context['a']['data-id'] = true;
335 $context['a']['data-nonce'] = true;
336
337 return $context;
338 }
339
340 /**
341 * Load media assets.
342 *
343 * Localization also used in Gutenberg integration.
344 */
345 public function extend_media_modal() {
346 // Get current screen.
347 $current_screen = get_current_screen();
348
349 // Only run on required pages.
350 if ( ! empty( $current_screen ) && ! in_array( $current_screen->id, Core::$external_pages, true ) && empty( $current_screen->is_block_editor ) ) {
351 return;
352 }
353
354 if ( wp_script_is( 'smush-backbone-extension', 'enqueued' ) ) {
355 return;
356 }
357
358 wp_enqueue_script(
359 'smush-backbone-extension',
360 WP_SMUSH_URL . 'app/assets/js/smush-media.min.js',
361 array(
362 'jquery',
363 'media-editor', // Used in image filters.
364 'media-views',
365 'media-grid',
366 'wp-util',
367 'wp-api',
368 ),
369 WP_SMUSH_VERSION,
370 true
371 );
372
373 wp_localize_script(
374 'smush-backbone-extension',
375 'smush_vars',
376 array(
377 'strings' => array(
378 'stats_label' => esc_html__( 'Smush', 'wp-smushit' ),
379 'filter_all' => esc_html__( 'Smush: All images', 'wp-smushit' ),
380 'filter_not_processed' => esc_html__( 'Smush: Not processed', 'wp-smushit' ),
381 'filter_excl' => esc_html__( 'Smush: Bulk ignored', 'wp-smushit' ),
382 'filter_failed' => esc_html__( 'Smush: Failed Processing', 'wp-smushit' ),
383 'gb' => array(
384 'stats' => esc_html__( 'Smush Stats', 'wp-smushit' ),
385 'select_image' => esc_html__( 'Select an image to view Smush stats.', 'wp-smushit' ),
386 'size' => esc_html__( 'Image size', 'wp-smushit' ),
387 'savings' => esc_html__( 'Savings', 'wp-smushit' ),
388 ),
389 ),
390 )
391 );
392 }
393
394 /**
395 * Send smush status for attachment.
396 *
397 * @param array $response Response array.
398 * @param WP_Post $attachment Attachment object.
399 *
400 * @return mixed
401 */
402 public function smush_send_status( $response, $attachment ) {
403 if ( ! isset( $attachment->ID ) ) {
404 return $response;
405 }
406
407 // Validate nonce.
408 $status = $this->smush_status( $attachment->ID );
409 $response['smush'] = $status;
410
411 return $response;
412 }
413
414 /**
415 * Get the smush button text for attachment.
416 *
417 * @param int $id Attachment ID for which the Status has to be set.
418 *
419 * @return string
420 */
421 private function smush_status( $id ) {
422 $action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_SPECIAL_CHARS, FILTER_NULL_ON_FAILURE );
423
424 // Show Temporary Status, For Async Optimisation, No Good workaround.
425 if ( ! get_transient( 'wp-smush-restore-' . $id ) && 'upload-attachment' === $action && $this->settings->get( 'auto' ) ) {
426 $status_txt = '<p class="smush-status">' . __( 'Smushing in progress...', 'wp-smushit' ) . '</p>';
427 $button_txt = __( 'Smush Now!', 'wp-smushit' );
428
429 return $this->column_html( $id, $status_txt, $button_txt, false );
430 }
431
432 // Else return the normal status.
433 return trim( $this->generate_markup( $id ) );
434 }
435
436 /**
437 * Skip messages respective to their IDs.
438 *
439 * @param string $msg_id Message ID.
440 *
441 * TODO: Remove this method as no longer need.
442 *
443 * @return bool
444 */
445 public function skip_reason( $msg_id ) {
446 $count = count( get_intermediate_image_sizes() );
447 $smush_orgnl_txt = sprintf(
448 /* translators: %s: number of thumbnails */
449 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' ),
450 $count
451 );
452
453 $skip_msg = array(
454 'large_size' => $smush_orgnl_txt,
455 'size_limit' => esc_html__( "Image couldn't be smushed as it exceeded the 5Mb size limit, Pro users can smush images without any size restriction.", 'wp-smushit' ),
456 );
457
458 $skip_rsn = '';
459 if ( ! empty( $skip_msg[ $msg_id ] ) ) {
460 $skip_rsn = '<a href="https://wpmudev.com/project/wp-smush-pro/?utm_source=smush&utm_medium=plugin&utm_campaign=smush_medialibrary_savings" target="_blank">
461 <span class="sui-tooltip sui-tooltip-left sui-tooltip-constrained sui-tooltip-top-right-mobile" data-tooltip="' . $skip_msg[ $msg_id ] . '">
462 <span class="sui-tag sui-tag-purple sui-tag-sm">' . esc_html__( 'PRO', 'wp-smushit' ) . '</span></span></a>';
463 }
464
465 return $skip_rsn;
466 }
467
468 /**
469 * Generate HTML for image status on the media library page.
470 *
471 * @since 3.5.0 Refactored from set_status().
472 *
473 * @param int $id Attachment ID.
474 *
475 * @return string HTML content or array of results.
476 */
477 public function generate_markup( $id ) {
478 $media_lib_item = new Media_Library_Row( $id );
479 return $media_lib_item->generate_markup();
480 }
481
482 /**
483 * Print the column html.
484 *
485 * @param string $id Media id.
486 * @param string $html Status text.
487 * @param string $button_txt Button label.
488 * @param boolean $show_button Whether to shoe the button.
489 *
490 * @return string
491 */
492 private function column_html( $id, $html = '', $button_txt = '', $show_button = true ) {
493 // Don't proceed if attachment is not image, or if image is not a jpg, png or gif, or if is not found.
494 $is_smushable = Helper::is_smushable( $id );
495 if ( ! $is_smushable ) {
496 return false === $is_smushable ? esc_html__( 'Image not found!', 'wp-smushit' ) : esc_html__( 'Not processed', 'wp-smushit' );
497 }
498
499 // If we aren't showing the button.
500 if ( ! $show_button ) {
501 return $html;
502 }
503
504 if ( 'Super-Smush' === $button_txt ) {
505 $html .= ' | ';
506 }
507
508 $html .= "<a href='#' class='wp-smush-send' data-id='{$id}'>{$button_txt}</a>";
509
510 if ( get_post_meta( $id, 'wp-smush-ignore-bulk', true ) ) {
511 $nonce = wp_create_nonce( 'wp-smush-remove-skipped' );
512 $html .= " | <a href='#' class='wp-smush-remove-skipped' data-id={$id} data-nonce={$nonce}>" . esc_html__( 'Show in bulk Smush', 'wp-smushit' ) . '</a>';
513 } else {
514 $html .= " | <a href='#' class='smush-ignore-image' data-id='{$id}'>" . esc_html__( 'Ignore', 'wp-smushit' ) . '</a>';
515 }
516
517 $html .= self::progress_bar();
518
519 return $html;
520 }
521
522 /**
523 * Returns the HTML for progress bar
524 *
525 * @return string
526 */
527 public static function progress_bar() {
528 return '<span class="spinner wp-smush-progress"></span>';
529 }
530 }
531