PluginProbe ʕ •ᴥ•ʔ
TinyPNG – JPEG, PNG & WebP image compression / trunk
TinyPNG – JPEG, PNG & WebP image compression vtrunk
3.7.0 3.6.14 trunk 1.0.0 1.1.0 1.2.0 1.2.1 1.3.0 1.3.1 1.3.2 1.4.0 1.5.0 1.6.0 1.7.0 1.7.1 1.7.2 2.0.0 2.0.1 2.0.2 2.1.0 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 3.0.0 3.0.1 3.1.0 3.2.0 3.2.1 3.3 3.4 3.4.1 3.4.2 3.4.4 3.4.5 3.4.6 3.5.0 3.5.1 3.5.2 3.6.0 3.6.1 3.6.10 3.6.11 3.6.12 3.6.13 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.6.9
tiny-compress-images / src / class-tiny-plugin.php
tiny-compress-images / src Last commit date
compatibility 2 days ago config 2 days ago css 5 months ago data 3 years ago images 3 years ago js 2 days ago vendor 4 months ago views 2 days ago class-tiny-apache-rewrite.php 2 days ago class-tiny-bulk-optimization.php 2 days ago class-tiny-cli.php 2 days ago class-tiny-compress-client.php 2 days ago class-tiny-compress-fopen.php 2 days ago class-tiny-compress.php 2 days ago class-tiny-conversion.php 2 months ago class-tiny-diagnostics.php 5 months ago class-tiny-exception.php 5 months ago class-tiny-helpers.php 2 days ago class-tiny-image-size.php 2 days ago class-tiny-image.php 2 days ago class-tiny-logger.php 2 days ago class-tiny-migrate.php 2 days ago class-tiny-notices.php 2 days ago class-tiny-php.php 2 days ago class-tiny-picture.php 2 days ago class-tiny-plugin.php 2 days ago class-tiny-settings.php 2 days ago class-tiny-source-base.php 2 months ago class-tiny-source-image.php 5 months ago class-tiny-source-picture.php 5 months ago class-tiny-wp-base.php 2 days ago
class-tiny-plugin.php
932 lines
1 <?php
2 /*
3 * Tiny Compress Images - WordPress plugin.
4 * Copyright (C) 2015-2023 Tinify B.V.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc., 51
18 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 class Tiny_Plugin extends Tiny_WP_Base {
21 const VERSION = '3.7.0';
22 const MEDIA_COLUMN = self::NAME;
23 const DATETIME_FORMAT = 'Y-m-d G:i:s';
24
25 private static $version;
26
27 private $settings;
28 private $twig;
29
30 public static function jpeg_quality() {
31 return 85;
32 }
33
34 public static function version() {
35 /*
36 Avoid using get_plugin_data() because it is not loaded early enough
37 in xmlrpc.php. */
38 return self::VERSION;
39 }
40
41 public function __construct() {
42 parent::__construct();
43 $this->settings = new Tiny_Settings();
44 new Tiny_Conversion( $this->settings );
45 }
46
47 public function set_compressor( $compressor ) {
48 $this->settings->set_compressor( $compressor );
49 }
50
51 public function init() {
52 add_filter(
53 'jpeg_quality',
54 $this->get_static_method( 'jpeg_quality' )
55 );
56
57 add_filter(
58 'wp_editor_set_quality',
59 $this->get_static_method( 'jpeg_quality' )
60 );
61
62 add_filter(
63 'wp_generate_attachment_metadata',
64 $this->get_method( 'process_attachment' ),
65 10,
66 2
67 );
68
69 add_action( 'delete_attachment', $this->get_method( 'clean_attachment' ), 10, 2 );
70
71 load_plugin_textdomain(
72 self::NAME,
73 false,
74 dirname( plugin_basename( __FILE__ ) ) . '/languages'
75 );
76
77 $this->tiny_compatibility();
78 }
79
80 public function cli_init() {
81 Tiny_CLI::register_command( $this->settings );
82 }
83
84 public function ajax_init() {
85 add_filter(
86 'wp_ajax_tiny_async_optimize_upload_new_media',
87 $this->get_method( 'compress_on_upload' )
88 );
89
90 add_action(
91 'wp_ajax_tiny_compress_image_from_library',
92 $this->get_method( 'compress_image_from_library' )
93 );
94
95 add_action(
96 'wp_ajax_tiny_compress_image_for_bulk',
97 $this->get_method( 'compress_image_for_bulk' )
98 );
99
100 add_action(
101 'wp_ajax_tiny_get_optimization_statistics',
102 $this->get_method( 'ajax_optimization_statistics' )
103 );
104
105 add_action(
106 'wp_ajax_tiny_get_compression_status',
107 $this->get_method( 'ajax_compression_status' )
108 );
109
110 add_action(
111 'wp_ajax_tiny_mark_image_as_compressed',
112 $this->get_method( 'mark_image_as_compressed' )
113 );
114
115 /*
116 When touching any functionality linked to image compressions when
117 uploading images make sure it also works with XML-RPC. See README. */
118 add_filter(
119 'wp_ajax_nopriv_tiny_rpc',
120 $this->get_method( 'process_rpc_request' )
121 );
122
123 if ( $this->settings->compress_wr2x_images() ) {
124 add_action(
125 'wr2x_upload_retina',
126 $this->get_method( 'compress_original_retina_image' ),
127 10,
128 2
129 );
130
131 add_action(
132 'wr2x_retina_file_added',
133 $this->get_method( 'compress_retina_image' ),
134 10,
135 3
136 );
137
138 add_action(
139 'wr2x_retina_file_removed',
140 $this->get_method( 'remove_retina_image' ),
141 10,
142 2
143 );
144 }
145 }
146
147 public function admin_init() {
148 add_action(
149 'wp_dashboard_setup',
150 $this->get_method( 'add_dashboard_widget' )
151 );
152
153 add_action(
154 'admin_enqueue_scripts',
155 $this->get_method( 'enqueue_scripts' )
156 );
157
158 add_action(
159 'admin_action_tiny_bulk_action',
160 $this->get_method( 'media_library_bulk_action' )
161 );
162
163 add_action(
164 'admin_action_-1',
165 $this->get_method( 'media_library_bulk_action' )
166 );
167
168 add_action(
169 'admin_action_tiny_bulk_mark_compressed',
170 $this->get_method( 'media_library_bulk_action' )
171 );
172
173 add_filter(
174 'manage_media_columns',
175 $this->get_method( 'add_media_columns' )
176 );
177
178 add_action(
179 'manage_media_custom_column',
180 $this->get_method( 'render_media_column' ),
181 10,
182 2
183 );
184
185 add_action(
186 'attachment_submitbox_misc_actions',
187 $this->get_method( 'show_media_info' )
188 );
189
190 $plugin = plugin_basename(
191 dirname( __DIR__ ) . '/tiny-compress-images.php'
192 );
193
194 add_filter(
195 "plugin_action_links_$plugin",
196 $this->get_method( 'add_plugin_links' )
197 );
198
199 $this->tiny_compatibility();
200
201 add_thickbox();
202 Tiny_Logger::init();
203 }
204
205 public function admin_menu() {
206 add_media_page(
207 __( 'Bulk Optimization', 'tiny-compress-images' ),
208 esc_html__( 'Bulk TinyPNG', 'tiny-compress-images' ),
209 'upload_files',
210 'tiny-bulk-optimization',
211 $this->get_method( 'render_bulk_optimization_page' )
212 );
213 }
214
215 public function add_plugin_links( $current_links ) {
216 $additional = array(
217 'settings' => sprintf(
218 '<a href="options-general.php?page=tinify">%s</a>',
219 esc_html__( 'Settings', 'tiny-compress-images' )
220 ),
221 'bulk' => sprintf(
222 '<a href="upload.php?page=tiny-bulk-optimization">%s</a>',
223 esc_html__( 'Bulk TinyPNG', 'tiny-compress-images' )
224 ),
225 );
226 return array_merge( $additional, $current_links );
227 }
228
229 public function tiny_compatibility() {
230 if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
231 new Tiny_WPML();
232 }
233
234 if ( Tiny_AS3CF::is_active() ) {
235 new Tiny_AS3CF( $this->settings );
236 }
237
238 new Tiny_WooCommerce();
239 }
240
241 public function compress_original_retina_image( $attachment_id, $path ) {
242 $tiny_image = new Tiny_Image( $this->settings, $attachment_id );
243 $tiny_image->compress_retina( 'original_wr2x', $path );
244 }
245
246 public function compress_retina_image( $attachment_id, $path, $size_name ) {
247 $tiny_image = new Tiny_Image( $this->settings, $attachment_id );
248 $tiny_image->compress_retina( $size_name . '_wr2x', $path );
249 }
250
251 public function remove_retina_image( $attachment_id, $path ) {
252 $tiny_image = new Tiny_Image( $this->settings, $attachment_id );
253 $tiny_image->remove_retina_metadata();
254 }
255
256 public function enqueue_scripts( $hook ) {
257 wp_enqueue_style(
258 self::NAME . '_admin',
259 plugins_url( '/css/admin.css', __FILE__ ),
260 array(),
261 self::version()
262 );
263
264 wp_enqueue_style(
265 self::NAME . '_chart',
266 plugins_url( '/css/optimization-chart.css', __FILE__ ),
267 array(),
268 self::version()
269 );
270
271 wp_register_script(
272 self::NAME . '_admin',
273 plugins_url( '/js/admin.js', __FILE__ ),
274 array(),
275 self::version(),
276 true
277 );
278
279 // WordPress < 3.3 does not handle multidimensional arrays
280 wp_localize_script(
281 self::NAME . '_admin',
282 'tinyCompress',
283 array(
284 'nonce' => wp_create_nonce( 'tiny-compress' ),
285 'wpVersion' => self::wp_version(),
286 'pluginVersion' => self::version(),
287 'L10nAllDone' => __(
288 'All images are processed',
289 'tiny-compress-images'
290 ),
291 'L10nNoActionTaken' => __(
292 'No action taken',
293 'tiny-compress-images'
294 ),
295 'L10nDuplicate' => __(
296 'Image was already processed',
297 'tiny-compress-images'
298 ),
299 'L10nBulkAction' => __( 'Compress Images', 'tiny-compress-images' ),
300 'L10nBulkMarkCompressed' => __(
301 'Mark as Compressed',
302 'tiny-compress-images'
303 ),
304 'L10nCancelled' => __( 'Cancelled', 'tiny-compress-images' ),
305 'L10nCompressing' => __( 'Compressing', 'tiny-compress-images' ),
306 'L10nCompressed' => __( 'compressed', 'tiny-compress-images' ),
307 'L10nConverted' => __( 'converted', 'tiny-compress-images' ),
308 'L10nFile' => __( 'File', 'tiny-compress-images' ),
309 'L10nSizesOptimized' => __(
310 'Sizes optimized',
311 'tiny-compress-images'
312 ),
313 'L10nInitialSize' => __( 'Initial size', 'tiny-compress-images' ),
314 'L10nCurrentSize' => __( 'Current size', 'tiny-compress-images' ),
315 'L10nSavings' => __( 'Savings', 'tiny-compress-images' ),
316 'L10nStatus' => __( 'Status', 'tiny-compress-images' ),
317 'L10nShowMoreDetails' => __(
318 'Show more details',
319 'tiny-compress-images'
320 ),
321 'L10nError' => __( 'Error', 'tiny-compress-images' ),
322 'L10nLatestError' => __( 'Latest error', 'tiny-compress-images' ),
323 'L10nInternalError' => __( 'Internal error', 'tiny-compress-images' ),
324 'L10nOutOf' => __( 'out of', 'tiny-compress-images' ),
325 'L10nWaiting' => __( 'Waiting', 'tiny-compress-images' ),
326 )
327 );
328
329 wp_enqueue_script( self::NAME . '_admin' );
330
331 if ( 'media_page_tiny-bulk-optimization' == $hook ) {
332 wp_enqueue_style(
333 self::NAME . '_tiny_bulk_optimization',
334 plugins_url( '/css/bulk-optimization.css', __FILE__ ),
335 array(),
336 self::version()
337 );
338
339 wp_enqueue_style(
340 self::NAME . '_chart',
341 plugins_url( '/css/optimization-chart.css', __FILE__ ),
342 array(),
343 self::version()
344 );
345
346 wp_register_script(
347 self::NAME . '_tiny_bulk_optimization',
348 plugins_url( '/js/bulk-optimization.js', __FILE__ ),
349 array(),
350 self::version(),
351 true
352 );
353
354 wp_enqueue_script( self::NAME . '_tiny_bulk_optimization' );
355 }
356 }
357
358 public function process_attachment( $metadata, $attachment_id ) {
359 if ( $this->settings->auto_compress_enabled() ) {
360 if (
361 $this->settings->background_compress_enabled()
362 ) {
363 $this->async_compress_on_upload( $metadata, $attachment_id );
364 } else {
365 return $this->blocking_compress_on_upload( $metadata, $attachment_id );
366 }
367 }
368
369 return $metadata;
370 }
371
372 public function blocking_compress_on_upload( $metadata, $attachment_id ) {
373 if ( ! empty( $metadata ) ) {
374 $tiny_image = new Tiny_Image( $this->settings, $attachment_id, $metadata );
375
376 Tiny_Logger::debug(
377 'blocking compress on upload',
378 array(
379 'image_id' => $attachment_id,
380 )
381 );
382
383 $result = $tiny_image->compress();
384 return $tiny_image->get_wp_metadata();
385 } else {
386 return $metadata;
387 }
388 }
389
390 public function async_compress_on_upload( $metadata, $attachment_id ) {
391 $context = 'wp';
392 $action = 'tiny_async_optimize_upload_new_media';
393 $_ajax_nonce = wp_create_nonce( 'new_media-' . $attachment_id );
394 $body = compact( 'action', '_ajax_nonce', 'metadata', 'attachment_id', 'context' );
395
396 $args = array(
397 'timeout' => 0.01,
398 'blocking' => false,
399 'body' => $body,
400 'cookies' => isset( $_COOKIE ) && is_array( $_COOKIE ) ? $_COOKIE : array(),
401 'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
402 );
403
404 if ( defined( 'XMLRPC_REQUEST' ) && get_current_user_id() ) {
405 /* We generate a hash to be used for the transient we use to store the current user. */
406 $rpc_hash = md5( maybe_serialize( $body ) );
407
408 $args['body']['tiny_rpc_action'] = $args['body']['action'];
409 /* We set a different action to make sure that all RPC requests are first validated. */
410 $args['body']['action'] = 'tiny_rpc';
411 $args['body']['tiny_rpc_hash'] = $rpc_hash;
412 $args['body']['tiny_rpc_nonce'] = wp_create_nonce( 'tiny_rpc_' . $rpc_hash );
413
414 /*
415 We can't use cookies here, so we save the user id in a transient
416 so that we can retrieve it again when processing the RPC request.
417 We should be able to use a relatively short timeout, as the request
418 should be processed directly afterwards.
419 */
420 set_transient( 'tiny_rpc_' . $rpc_hash, get_current_user_id(), 10 );
421 }
422
423 Tiny_Logger::debug(
424 'remote post',
425 array(
426 'image_id' => $attachment_id,
427 )
428 );
429
430 if ( getenv( 'WORDPRESS_HOST' ) !== false ) {
431 wp_remote_post( getenv( 'WORDPRESS_HOST' ) . '/wp-admin/admin-ajax.php', $args );
432 } else {
433 wp_remote_post( admin_url( 'admin-ajax.php' ), $args );
434 }
435 }
436
437 public function process_rpc_request() {
438 if (
439 empty( $_POST['tiny_rpc_action'] ) ||
440 empty( $_POST['tiny_rpc_hash'] )
441 ) {
442 exit();
443 }
444
445 $rpc_hash = sanitize_key( wp_unslash( $_POST['tiny_rpc_hash'] ) );
446 if ( 32 !== strlen( $rpc_hash ) ) {
447 exit();
448 }
449
450 $user_id = absint( get_transient( 'tiny_rpc_' . $rpc_hash ) );
451 $user = $user_id ? get_userdata( $user_id ) : false;
452
453 /* We no longer need the transient. */
454 delete_transient( 'tiny_rpc_' . $rpc_hash );
455
456 if ( ! $user || ! $user->exists() ) {
457 exit();
458 }
459 wp_set_current_user( $user_id );
460
461 if ( ! check_ajax_referer( 'tiny_rpc_' . $rpc_hash, 'tiny_rpc_nonce', false ) ) {
462 exit();
463 }
464
465 /* Now that everything is checked, perform the actual action. */
466 $action = sanitize_key( wp_unslash( $_POST['tiny_rpc_action'] ) );
467 unset(
468 $_POST['action'],
469 $_POST['tiny_rpc_action'],
470 $_POST['tiny_rpc_id'],
471 $_POST['tiny_rpc_nonce']
472 );
473 do_action( 'wp_ajax_' . $action );
474 }
475
476 public function compress_on_upload() {
477 $nonce = isset( $_POST['_ajax_nonce'] ) ?
478 sanitize_key( wp_unslash( $_POST['_ajax_nonce'] ) ) : '';
479 $attachment_id = isset( $_POST['attachment_id'] ) ?
480 intval( wp_unslash( $_POST['attachment_id'] ) ) : 0;
481
482 if ( ! wp_verify_nonce( $nonce, 'new_media-' . $attachment_id ) ) {
483 exit;
484 }
485 if ( current_user_can( 'upload_files' ) ) {
486 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
487 $metadata = isset( $_POST['metadata'] ) ? wp_unslash( $_POST['metadata'] ) : array();
488 if ( is_array( $metadata ) ) {
489 $tiny_image = new Tiny_Image( $this->settings, $attachment_id, $metadata );
490
491 Tiny_Logger::debug(
492 'compress on upload',
493 array(
494 'image_id' => $attachment_id,
495 )
496 );
497
498 $result = $tiny_image->compress();
499 // The wp_update_attachment_metadata call is thrown because the
500 // dimensions of the original image can change. This will then
501 // trigger other plugins and can result in unexpected behaviour and
502 // further changes to the image. This may require another approach.
503 // Note that as of WP 5.3 it is advised to not hook into this filter
504 // anymore, so other plugins are less likely to be triggered.
505 wp_update_attachment_metadata( $attachment_id, $tiny_image->get_wp_metadata() );
506 }
507 }
508 exit();
509 }
510
511 /**
512 * Validates AJAX request for attachment operations.
513 *
514 * @since 3.0.0
515 *
516 * @return array Either error array ['error' => 'message']
517 * or success array ['data' => [$id, $metadata]]
518 */
519 private function validate_ajax_attachment_request() {
520 check_ajax_referer( 'tiny-compress', '_nonce' );
521
522 if ( ! current_user_can( 'upload_files' ) ) {
523 return array(
524 'error' => esc_html__(
525 "You don't have permission to upload files.",
526 'tiny-compress-images'
527 ),
528 );
529 }
530 if ( empty( $_POST['id'] ) ) {
531 return array(
532 'error' => esc_html__(
533 'Not a valid media file.',
534 'tiny-compress-images'
535 ),
536 );
537 }
538 $id = intval( $_POST['id'] );
539 $metadata = wp_get_attachment_metadata( $id );
540 if ( ! is_array( $metadata ) ) {
541 return array(
542 'error' => esc_html__(
543 'Could not find metadata of media file.',
544 'tiny-compress-images'
545 ),
546 );
547 }
548
549 return array(
550 'data' => array( $id, $metadata ),
551 );
552 }
553
554 public function compress_image_from_library() {
555 $response = $this->validate_ajax_attachment_request();
556 if ( isset( $response['error'] ) ) {
557 echo esc_html( $response['error'] );
558 exit();
559 }
560 list($id, $metadata) = $response['data'];
561
562 Tiny_Logger::debug(
563 'compress from library',
564 array(
565 'image_id' => $id,
566 )
567 );
568
569 $tiny_image = new Tiny_Image( $this->settings, $id, $metadata );
570 $result = $tiny_image->compress();
571
572 // The wp_update_attachment_metadata call is thrown because the
573 // dimensions of the original image can change. This will then
574 // trigger other plugins and can result in unexpected behaviour and
575 // further changes to the image. This may require another approach.
576 // Note that as of WP 5.3 it is advised to not hook into this filter
577 // anymore, so other plugins are less likely to be triggered.
578 wp_update_attachment_metadata( $id, $tiny_image->get_wp_metadata() );
579
580 $this->render_compress_details( $tiny_image );
581
582 exit();
583 }
584
585 public function compress_image_for_bulk() {
586 $response = $this->validate_ajax_attachment_request();
587 if ( isset( $response['error'] ) ) {
588 echo json_encode( $response );
589 exit();
590 }
591
592 list($id, $metadata) = $response['data'];
593 $tiny_image_before = new Tiny_Image( $this->settings, $id, $metadata );
594 $image_statistics_before = $tiny_image_before->get_statistics(
595 $this->settings->get_sizes(),
596 $this->settings->get_active_tinify_sizes()
597 );
598 $size_before = $image_statistics_before['compressed_total_size'];
599
600 $tiny_image = new Tiny_Image( $this->settings, $id, $metadata );
601
602 Tiny_Logger::debug(
603 'compress from bulk',
604 array(
605 'image_id' => $id,
606 )
607 );
608
609 $result = $tiny_image->compress();
610 $image_statistics = $tiny_image->get_statistics(
611 $this->settings->get_sizes(),
612 $this->settings->get_active_tinify_sizes()
613 );
614 wp_update_attachment_metadata( $id, $tiny_image->get_wp_metadata() );
615
616 // Nonce verified in validate_ajax_attachment_request().
617 // phpcs:disable WordPress.Security.NonceVerification.Missing
618 $current_library_size = isset( $_POST['current_size'] ) ?
619 intval( wp_unslash( $_POST['current_size'] ) )
620 : 0;
621 // phpcs:enable WordPress.Security.NonceVerification.Missing
622 $size_after = $image_statistics['compressed_total_size'];
623 $new_library_size = $current_library_size + $size_after - $size_before;
624
625 $result['message'] = $tiny_image->get_latest_error();
626 $result['image_sizes_compressed'] = $image_statistics['image_sizes_compressed'];
627 $result['image_sizes_converted'] = $image_statistics['image_sizes_converted'];
628 $result['image_sizes_optimized'] = $image_statistics['image_sizes_optimized'];
629
630 $result['initial_total_size'] = size_format(
631 $image_statistics['initial_total_size'],
632 1
633 );
634
635 $result['optimized_total_size'] = size_format(
636 $image_statistics['compressed_total_size'],
637 1
638 );
639
640 $result['savings'] = $tiny_image->get_savings( $image_statistics );
641 $result['status'] = $this->settings->get_status();
642 $result['thumbnail'] = wp_get_attachment_image(
643 $id,
644 array( '30', '30' ),
645 true,
646 array(
647 'class' => 'pinkynail',
648 'alt' => '',
649 )
650 );
651 $result['size_change'] = $size_after - $size_before;
652 $result['human_readable_library_size'] = size_format( $new_library_size, 2 );
653
654 echo json_encode( $result );
655
656 exit();
657 }
658
659 public function ajax_optimization_statistics() {
660 if ( check_ajax_referer( 'tiny-compress', '_nonce', false ) &&
661 current_user_can( 'upload_files' ) ) {
662 $stats = Tiny_Bulk_Optimization::get_optimization_statistics( $this->settings );
663 echo json_encode( $stats );
664 }
665 exit();
666 }
667
668 public function ajax_compression_status() {
669 $response = $this->validate_ajax_attachment_request();
670
671 if ( isset( $response['error'] ) ) {
672 echo esc_html( $response['error'] );
673 exit();
674 }
675 list($id, $metadata) = $response['data'];
676
677 $tiny_image = new Tiny_Image( $this->settings, $id, $metadata );
678
679 $this->render_compress_details( $tiny_image );
680
681 exit();
682 }
683
684 public function media_library_bulk_action() {
685 $valid_actions = array( 'tiny_bulk_action', 'tiny_bulk_mark_compressed' );
686 $action = isset( $_REQUEST['action'] ) ?
687 sanitize_key( wp_unslash( $_REQUEST['action'] ) ) : '';
688 $action2 = isset( $_REQUEST['action2'] ) ?
689 sanitize_key( wp_unslash( $_REQUEST['action2'] ) ) : '';
690
691 if (
692 ! in_array( $action, $valid_actions, true ) &&
693 ! in_array( $action2, $valid_actions, true )
694 ) {
695 return;
696 }
697 $media = isset( $_REQUEST['media'] ) ?
698 array_map( 'intval', wp_unslash( (array) $_REQUEST['media'] ) )
699 : array();
700 if ( empty( $media ) ) {
701 $_REQUEST['action'] = '';
702 return;
703 }
704 check_admin_referer( 'bulk-media' );
705 $ids = implode( '-', $media );
706 $location = 'upload.php?mode=list&ids=' . $ids;
707
708 $location = add_query_arg( 'action', $action, $location );
709 $location = add_query_arg( '_tiny_nonce', wp_create_nonce( 'tiny-bulk-ids' ), $location );
710
711 if ( ! empty( $_REQUEST['paged'] ) ) {
712 $location = add_query_arg( 'paged', absint( $_REQUEST['paged'] ), $location );
713 }
714 if ( ! empty( $_REQUEST['s'] ) ) {
715 $location = add_query_arg(
716 's',
717 sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ),
718 $location
719 );
720 }
721 if ( ! empty( $_REQUEST['m'] ) ) {
722 $location = add_query_arg(
723 'm',
724 sanitize_text_field( wp_unslash( $_REQUEST['m'] ) ),
725 $location
726 );
727 }
728
729 wp_safe_redirect( admin_url( $location ) );
730 exit();
731 }
732
733 public function add_media_columns( $columns ) {
734 $columns[ self::MEDIA_COLUMN ] = esc_html__( 'Compression', 'tiny-compress-images' );
735 return $columns;
736 }
737
738 public function render_media_column( $column, $id ) {
739 if ( self::MEDIA_COLUMN === $column ) {
740 $tiny_image = new Tiny_Image( $this->settings, $id );
741 if ( $tiny_image->file_type_allowed() ) {
742 echo '<div class="tiny-ajax-container">';
743 $this->render_compress_details( $tiny_image );
744 echo '</div>';
745 }
746 }
747 }
748
749 public function show_media_info() {
750 global $post;
751 $tiny_image = new Tiny_Image( $this->settings, $post->ID );
752 if ( $tiny_image->file_type_allowed() ) {
753 echo '<div class="misc-pub-section tiny-compress-images">';
754 echo '<h4>';
755 esc_html_e( 'JPEG, PNG, & WebP optimization', 'tiny-compress-images' );
756 echo '</h4>';
757 echo '<div class="tiny-ajax-container">';
758 $this->render_compress_details( $tiny_image );
759 echo '</div>';
760 echo '</div>';
761 }
762 }
763
764 private function render_compress_details( $tiny_image ) {
765 $images_to_compress = array();
766
767 if ( ! empty( $_GET['ids'] ) ) {
768 $nonce = isset( $_GET['_tiny_nonce'] ) ?
769 sanitize_key( wp_unslash( $_GET['_tiny_nonce'] ) ) : '';
770
771 if ( $nonce && wp_verify_nonce( $nonce, 'tiny-bulk-ids' ) ) {
772 $request_ids = sanitize_text_field( wp_unslash( $_GET['ids'] ) );
773 $images_to_compress = array_map( 'intval', explode( '-', $request_ids ) );
774 }
775 }
776
777 $in_progress = $tiny_image->filter_image_sizes( 'in_progress' );
778 if ( count( $in_progress ) > 0 ) {
779 include __DIR__ . '/views/compress-details-processing.php';
780 } else {
781 include __DIR__ . '/views/compress-details.php';
782 }
783 }
784
785 public function get_estimated_bulk_cost( $estimated_credit_use ) {
786 return Tiny_Compress::estimate_cost(
787 $estimated_credit_use,
788 $this->settings->get_compression_count()
789 );
790 }
791
792 public function render_bulk_optimization_page() {
793 $stats = Tiny_Bulk_Optimization::get_optimization_statistics( $this->settings );
794
795 $estimated_costs = $this->get_estimated_bulk_cost( $stats['estimated_credit_use'] );
796 $admin_colors = self::retrieve_admin_colors();
797
798 /* This makes sure that up to date information is retrieved from the API. */
799 $this->settings->get_compressor()->get_status();
800
801 $active_tinify_sizes = $this->settings->get_active_tinify_sizes();
802 $remaining_credits = $this->settings->get_remaining_credits();
803 $is_on_free_plan = $this->settings->is_on_free_plan();
804 $email_address = $this->settings->get_email_address();
805
806 include __DIR__ . '/views/bulk-optimization.php';
807 }
808
809 public function add_dashboard_widget() {
810 wp_enqueue_style(
811 self::NAME . '_chart',
812 plugins_url( '/css/optimization-chart.css', __FILE__ ),
813 array(),
814 self::version()
815 );
816
817 wp_enqueue_style(
818 self::NAME . '_dashboard_widget',
819 plugins_url( '/css/dashboard-widget.css', __FILE__ ),
820 array(),
821 self::version()
822 );
823
824 wp_register_script(
825 self::NAME . '_dashboard_widget',
826 plugins_url( '/js/dashboard-widget.js', __FILE__ ),
827 array(),
828 self::version(),
829 true
830 );
831
832 /*
833 This might be deduplicated with the admin script localization, but
834 the order of including scripts is sometimes different. So in that
835 case we need to make sure that the order of inclusion is correct. */
836 wp_localize_script(
837 self::NAME . '_dashboard_widget',
838 'tinyCompressDashboard',
839 array(
840 'nonce' => wp_create_nonce( 'tiny-compress' ),
841 )
842 );
843
844 wp_enqueue_script( self::NAME . '_dashboard_widget' );
845
846 wp_add_dashboard_widget(
847 $this->get_prefixed_name( 'dashboard_widget' ),
848 esc_html__( 'TinyPNG - JPEG, PNG & WebP image compression', 'tiny-compress-images' ),
849 $this->get_method( 'add_widget_view' )
850 );
851 }
852
853 public function add_widget_view() {
854 $admin_colors = self::retrieve_admin_colors();
855 include __DIR__ . '/views/dashboard-widget.php';
856 }
857
858 private static function retrieve_admin_colors() {
859 global $_wp_admin_css_colors;
860 $admin_colour_scheme = get_user_option( 'admin_color', get_current_user_id() );
861 $admin_colors = array( '#0074aa', '#1685b5', '#78ca44', '#0086ba' ); // default
862 if ( isset( $_wp_admin_css_colors[ $admin_colour_scheme ] ) ) {
863 if ( isset( $_wp_admin_css_colors[ $admin_colour_scheme ]->colors ) ) {
864 $admin_colors = $_wp_admin_css_colors[ $admin_colour_scheme ]->colors;
865 }
866 }
867 if ( '#e5e5e5' == $admin_colors[0] && '#999' == $admin_colors[1] ) {
868 $admin_colors[0] = '#bbb';
869 }
870 if ( '#5589aa' == $admin_colors[0] && '#cfdfe9' == $admin_colors[1] ) {
871 $admin_colors[1] = '#85aec5';
872 }
873 if ( '#7c7976' == $admin_colors[0] && '#c6c6c6' == $admin_colors[1] ) {
874 $admin_colors[1] = '#adaba9';
875 $admin_colors[2] = '#adaba9';
876 }
877 if ( self::wp_version() > 3.7 ) {
878 if ( 'fresh' == $admin_colour_scheme ) {
879 $admin_colors = array( '#0074aa', '#1685b5', '#78ca44', '#0086ba' ); // better
880 }
881 }
882 return $admin_colors;
883 }
884
885 public function friendly_user_name() {
886 $user = wp_get_current_user();
887 $name = ucfirst( empty( $user->first_name ) ? $user->display_name : $user->first_name );
888 return $name;
889 }
890
891 /**
892 * Will clean up converted files (if any) when the original is deleted
893 *
894 * Hooked to the `delete_attachment` action.
895 *
896 * @see https://developer.wordpress.org/reference/hooks/deleted_post/
897 *
898 * @param [int] $post_id
899 *
900 * @return void
901 */
902 public function clean_attachment( $post_id ) {
903 $tiny_image = new Tiny_Image( $this->settings, $post_id );
904 $tiny_image->delete_converted_image();
905 }
906
907 /**
908 * Runs on uninstall
909 *
910 * @return void
911 */
912 public static function uninstall() {
913 Tiny_Apache_Rewrite::uninstall_rules();
914 }
915
916 public function mark_image_as_compressed() {
917 $response = $this->validate_ajax_attachment_request();
918 if ( isset( $response['error'] ) ) {
919 echo esc_html( $response['error'] );
920 exit();
921 }
922
923 list($id, $metadata) = $response['data'];
924 $tiny_image = new Tiny_Image( $this->settings, $id, $metadata );
925 $tiny_image->mark_as_compressed();
926
927 $this->render_compress_details( $tiny_image );
928
929 exit();
930 }
931 }
932