PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.3.1
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.3.1
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / inc / classes / class-imagify-views.php

class-imagify-views.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 2.3.1, at inc/classes/class-imagify-views.php

742 lines 19.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use Imagify\User\User;
4 use Imagify\Dependencies\WPMedia\PluginFamily\Model\PluginFamily;
5 use Imagify\Traits\InstanceGetterTrait;
6
7 /**
8 * Class that handles templates and menus.
9 *
10 * @since 1.7
11 */
12 class Imagify_Views {
13 use InstanceGetterTrait;
14
15 /**
16 * Class version.
17 *
18 * @var string
19 * @since 1.7
20 */
21 const VERSION = '1.1';
22
23 /**
24 * Slug used for the settings page URL.
25 *
26 * @var string
27 * @since 1.7
28 */
29 protected $slug_settings;
30
31 /**
32 * Slug used for the bulk optimization page URL.
33 *
34 * @var string
35 * @since 1.7
36 */
37 protected $slug_bulk;
38
39 /**
40 * Slug used for the "custom folders" page URL.
41 *
42 * @var string
43 * @since 1.7
44 */
45 protected $slug_files;
46
47 /**
48 * A list of JS templates to print at the end of the page.
49 *
50 * @var array
51 * @since 1.9
52 */
53 protected $templates_in_footer = [];
54
55 /**
56 * Stores the "custom folders" files list instance.
57 *
58 * @var Imagify_Files_List_Table
59 * @since 1.7
60 */
61 protected $list_table;
62
63 /**
64 * Filesystem object.
65 *
66 * @var Imagify_Filesystem
67 * @since 1.7.1
68 */
69 protected $filesystem;
70
71 /**
72 * Imagify admin bar menu.
73 *
74 * @var bool
75 */
76 private $admin_menu_is_present = false;
77
78
79 /** ----------------------------------------------------------------------------------------- */
80 /** INSTANCE/INIT =========================================================================== */
81 /** ----------------------------------------------------------------------------------------- */
82
83 /**
84 * The constructor.
85 *
86 * @since 1.7
87 */
88 protected function __construct() {
89 $this->slug_settings = IMAGIFY_SLUG;
90 $this->slug_bulk = IMAGIFY_SLUG . '-bulk-optimization';
91 $this->slug_files = IMAGIFY_SLUG . '-files';
92 $this->filesystem = Imagify_Filesystem::get_instance();
93 }
94
95 /**
96 * Launch the hooks.
97 *
98 * @since 1.7
99 */
100 public function init() {
101 // Menu items.
102 add_action( 'admin_menu', [ $this, 'add_site_menus' ] );
103
104 if ( imagify_is_active_for_network() ) {
105 add_action( 'network_admin_menu', [ $this, 'add_network_menus' ] );
106 }
107
108 // Save the "per page" option value from the files list screen.
109 add_filter( 'set-screen-option', [ 'Imagify_Files_List_Table', 'save_screen_options' ], 10, 3 );
110
111 // JS templates in footer.
112 add_action( 'admin_print_footer_scripts', [ $this, 'print_js_templates' ] );
113 add_action( 'admin_footer', [ $this, 'print_modal_payment' ] );
114 add_action( 'wp_before_admin_bar_render', [ $this, 'maybe_print_modal_payment' ] );
115 }
116
117
118 /** ----------------------------------------------------------------------------------------- */
119 /** MENU ITEMS ============================================================================== */
120 /** ----------------------------------------------------------------------------------------- */
121
122 /**
123 * Add sub-menus for all sites.
124 *
125 * @since 1.7
126 */
127 public function add_site_menus() {
128 $wp_context = imagify_get_context( 'wp' );
129
130 // Sub-menu item: bulk optimization.
131 add_media_page( __( 'Bulk Optimization', 'imagify' ), __( 'Bulk Optimization', 'imagify' ), $wp_context->get_capacity( 'bulk-optimize' ), $this->get_bulk_page_slug(), [ $this, 'display_bulk_page' ] );
132
133 if ( imagify_is_active_for_network() ) {
134 return;
135 }
136
137 /**
138 * Plugin is not network activated.
139 */
140 if ( imagify_can_optimize_custom_folders() ) {
141 // Sub-menu item: custom folders list.
142 $cf_context = imagify_get_context( 'custom-folders' );
143 $screen_id = add_media_page( __( 'Other Media optimized by Imagify', 'imagify' ), __( 'Other Media', 'imagify' ), $cf_context->get_capacity( 'optimize' ), $this->get_files_page_slug(), [ $this, 'display_files_list' ] );
144
145 if ( $screen_id ) {
146 // Load the data for this page.
147 add_action( 'load-' . $screen_id, [ $this, 'load_files_list' ] );
148 }
149 }
150
151 // Sub-menu item: settings.
152 add_options_page( 'Imagify', 'Imagify', $wp_context->get_capacity( 'manage' ), $this->get_settings_page_slug(), [ $this, 'display_settings_page' ] );
153 }
154
155 /**
156 * Add menu and sub-menus in the network admin when Imagify is network-activated.
157 *
158 * @since 1.7
159 */
160 public function add_network_menus() {
161 global $submenu;
162
163 $wp_context = imagify_get_context( 'wp' );
164
165 if ( ! imagify_can_optimize_custom_folders() ) {
166 // Main item: settings (edge case).
167 add_menu_page( 'Imagify', 'Imagify', $wp_context->get_capacity( 'manage' ), $this->get_settings_page_slug(), [ $this, 'display_settings_page' ] );
168 return;
169 }
170
171 $cf_context = imagify_get_context( 'custom-folders' );
172
173 // Main item: bulk optimization (custom folders).
174 add_menu_page( __( 'Bulk Optimization', 'imagify' ), 'Imagify', $cf_context->current_user_can( 'bulk-optimize' ), $this->get_bulk_page_slug(), [ $this, 'display_bulk_page' ] );
175
176 // Sub-menu item: custom folders list.
177 $screen_id = add_submenu_page( $this->get_bulk_page_slug(), __( 'Other Media optimized by Imagify', 'imagify' ), __( 'Other Media', 'imagify' ), $cf_context->current_user_can( 'bulk-optimize' ), $this->get_files_page_slug(), [ $this, 'display_files_list' ] );
178
179 // Sub-menu item: settings.
180 add_submenu_page( $this->get_bulk_page_slug(), 'Imagify', __( 'Settings', 'imagify' ), $wp_context->get_capacity( 'manage' ), $this->get_settings_page_slug(), [ $this, 'display_settings_page' ] );
181
182 // Change the sub-menu label.
183 if ( ! empty( $submenu[ $this->get_bulk_page_slug() ] ) ) {
184 $submenu[ $this->get_bulk_page_slug() ][0][0] = __( 'Bulk Optimization', 'imagify' ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
185 }
186
187 if ( $screen_id ) {
188 // On the "Other Media optimized by Imagify" page, load the data.
189 add_action( 'load-' . $screen_id, [ $this, 'load_files_list' ] );
190 }
191 }
192
193 /** ----------------------------------------------------------------------------------------- */
194 /** MAIN PAGE TEMPLATES ===================================================================== */
195 /** ----------------------------------------------------------------------------------------- */
196
197 /**
198 * The main settings page.
199 *
200 * @since 1.7
201 */
202 public function display_settings_page() {
203 $plugin_family = new PluginFamily();
204 $plugins_array = $plugin_family->get_filtered_plugins( 'imagify/imagify' );
205
206 $data = [
207 'hide_plugin_family' => wpm_apply_filters_typed( 'boolean', 'imagify_hide_plugin_family', false ),
208 'plugin_family' => $plugins_array['uncategorized'],
209 ];
210
211 $this->print_template( 'page-settings', $data );
212 }
213
214 /**
215 * The bulk optimization page.
216 *
217 * @since 1.7
218 */
219 public function display_bulk_page() {
220 $types = [];
221 $data = [
222 // Limits.
223 'unoptimized_attachment_limit' => 0,
224 // What to optimize.
225 'icon' => 'images-alt2',
226 'title' => __( 'Optimize your media files', 'imagify' ),
227 'groups' => [],
228 ];
229
230 if ( imagify_is_screen( 'bulk' ) ) {
231 if ( ! is_network_admin() ) {
232 /**
233 * Library: in each site.
234 */
235 $types['library|wp'] = 1;
236 }
237
238 if (
239 imagify_can_optimize_custom_folders()
240 &&
241 (
242 ( imagify_is_active_for_network() && is_network_admin() )
243 ||
244 ! imagify_is_active_for_network()
245 )
246 ) {
247 /**
248 * Custom folders: in network admin only if network activated, in each site otherwise.
249 */
250 $types['custom-folders|custom-folders'] = 1;
251 }
252 }
253
254 /**
255 * Filter the types to display in the bulk optimization page.
256 *
257 * @since 1.7.1
258 * @author Grégory Viguier
259 *
260 * @param array $types The folder types displayed on the page. If a folder type is "library", the context should be suffixed after a pipe character. They are passed as array keys.
261 */
262 $types = apply_filters( 'imagify_bulk_page_types', $types );
263 $types = array_filter( (array) $types );
264
265 if ( isset( $types['library|wp'] ) ) {
266 // Limits.
267 $data['unoptimized_attachment_limit'] += imagify_get_unoptimized_attachment_limit();
268 // Group.
269 $data['groups']['library'] = [
270 /**
271 * The group_id corresponds to the file names like 'part-bulk-optimization-results-row-{$group_id}'.
272 * It is also used in get_imagify_localize_script_translations().
273 */
274 'group_id' => 'library',
275 'context' => 'wp',
276 'title' => __( 'Media Library', 'imagify' ),
277 /* translators: 1 is the opening of a link, 2 is the closing of this link. */
278 'footer' => sprintf( __( 'You can also re-optimize your media files from your %1$sMedia Library%2$s screen.', 'imagify' ), '<a href="' . esc_url( admin_url( 'upload.php' ) ) . '">', '</a>' ),
279 ];
280 }
281
282 if ( isset( $types['custom-folders|custom-folders'] ) ) {
283 if ( ! Imagify_Folders_DB::get_instance()->has_items() ) {
284 $data['no-custom-folders'] = true;
285 } elseif ( Imagify_Folders_DB::get_instance()->has_active_folders() ) {
286 // Group.
287 $data['groups']['custom-folders'] = [
288 'group_id' => 'custom-folders',
289 'context' => 'custom-folders',
290 'title' => __( 'Custom folders', 'imagify' ),
291 /* translators: 1 is the opening of a link, 2 is the closing of this link. */
292 'footer' => sprintf( __( 'You can re-optimize your media files more finely directly in the %1$smedia management%2$s.', 'imagify' ), '<a href="' . esc_url( get_imagify_admin_url( 'files-list' ) ) . '">', '</a>' ),
293 ];
294 }
295 }
296
297 // Add generic stats.
298 $data = array_merge(
299 $data,
300 imagify_get_bulk_stats(
301 $types,
302 [
303 'fullset' => true,
304 ]
305 )
306 );
307
308 /**
309 * Filter the data to use on the bulk optimization page.
310 *
311 * @since 1.7
312 * @since 1.7.1 Added the $types parameter.
313 * @author Grégory Viguier
314 *
315 * @param array $data The data to use.
316 * @param array $types The folder types displayed on the page. They are passed as array keys.
317 */
318 $data = apply_filters( 'imagify_bulk_page_data', $data, $types );
319
320 $this->print_template( 'page-bulk', $data );
321 }
322
323 /**
324 * The page displaying the "custom folders" files.
325 *
326 * @since 1.7
327 */
328 public function display_files_list() {
329 $this->print_template( 'page-files-list' );
330 }
331
332 /**
333 * Initiate the "custom folders" list table data.
334 *
335 * @since 1.7
336 */
337 public function load_files_list() {
338 // Instantiate the list.
339 $this->list_table = new Imagify_Files_List_Table(
340 [
341 'screen' => 'imagify-files',
342 ]
343 );
344
345 // Query the Items.
346 $this->list_table->prepare_items();
347 }
348
349
350 /** ----------------------------------------------------------------------------------------- */
351 /** GETTERS ================================================================================= */
352 /** ----------------------------------------------------------------------------------------- */
353
354 /**
355 * Get the settings page slug.
356 *
357 * @since 1.7
358 *
359 * @return string
360 */
361 public function get_settings_page_slug() {
362 return $this->slug_settings;
363 }
364
365 /**
366 * Get the bulk optimization page slug.
367 *
368 * @since 1.7
369 *
370 * @return string
371 */
372 public function get_bulk_page_slug() {
373 return $this->slug_bulk;
374 }
375
376 /**
377 * Get the "custom folders" files page slug.
378 *
379 * @since 1.7
380 *
381 * @return string
382 */
383 public function get_files_page_slug() {
384 return $this->slug_files;
385 }
386
387
388 /** ----------------------------------------------------------------------------------------- */
389 /** PAGE TESTS ============================================================================== */
390 /** ----------------------------------------------------------------------------------------- */
391
392 /**
393 * Tell if we’re displaying the settings page.
394 *
395 * @since 1.9
396 *
397 * @return bool
398 */
399 public function is_settings_page() {
400 global $pagenow;
401
402 if ( ! isset( $_GET['page'] ) ) {
403 return false;
404 }
405
406 $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
407
408 if ( $this->get_settings_page_slug() !== $page ) {
409 return false;
410 }
411
412 if ( imagify_is_active_for_network() ) {
413 return 'admin.php' === $pagenow;
414 }
415
416 return 'options-general.php' === $pagenow;
417 }
418
419 /**
420 * Tell if we’re displaying the bulk optimization page.
421 *
422 * @since 1.9
423 *
424 * @return bool
425 */
426 public function is_bulk_page() {
427 global $pagenow;
428
429 if ( ! isset( $_GET['page'] ) ) {
430 return false;
431 }
432
433 $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
434
435 return 'upload.php' === $pagenow && $this->get_bulk_page_slug() === $page;
436 }
437
438 /**
439 * Tell if we’re displaying the custom files list page.
440 *
441 * @since 1.9
442 *
443 * @return bool
444 */
445 public function is_files_page() {
446 global $pagenow;
447
448 if ( ! isset( $_GET['page'] ) ) {
449 return false;
450 }
451
452 $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
453
454 return 'upload.php' === $pagenow && $this->get_files_page_slug() === $page;
455 }
456
457 /**
458 * Tell if we’re displaying the WP media library page.
459 *
460 * @since 1.9
461 *
462 * @return bool
463 */
464 public function is_wp_library_page() {
465 global $pagenow;
466
467 return 'upload.php' === $pagenow && ! isset( $_GET['page'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
468 }
469
470 /**
471 * Tell if we’re displaying a media page.
472 *
473 * @since 1.9
474 *
475 * @return bool
476 */
477 public function is_media_page() {
478 global $pagenow, $typenow;
479
480 return 'post.php' === $pagenow && 'attachment' === $typenow;
481 }
482
483
484 /** ----------------------------------------------------------------------------------------- */
485 /** QUOTA =================================================================================== */
486 /** ----------------------------------------------------------------------------------------- */
487
488 /**
489 * Get the remaining quota in percent.
490 *
491 * @since 1.8.1
492 *
493 * @return int
494 */
495 public function get_quota_percent() {
496 static $quota;
497
498 if ( isset( $quota ) ) {
499 return $quota;
500 }
501
502 $user = new User();
503 $quota = $user->get_percent_unconsumed_quota();
504
505 return $quota;
506 }
507
508 /**
509 * Get the HTML class used for the quota (to change the color when out of quota for example).
510 *
511 * @since 1.8.1
512 *
513 * @return string
514 */
515 public function get_quota_class() {
516 static $class;
517
518 if ( isset( $class ) ) {
519 return $class;
520 }
521
522 $quota = $this->get_quota_percent();
523 $class = 'imagify-bar-';
524
525 if ( $quota <= 20 ) {
526 $class .= 'negative';
527 } elseif ( $quota <= 50 ) {
528 $class .= 'neutral';
529 } else {
530 $class .= 'positive';
531 }
532
533 return $class;
534 }
535
536 /**
537 * Get the HTML tag used for the quota (the weather-like icon).
538 *
539 * @since 1.8.1
540 *
541 * @return string
542 */
543 public function get_quota_icon() {
544 static $icon;
545
546 if ( isset( $icon ) ) {
547 return $icon;
548 }
549
550 $quota = $this->get_quota_percent();
551
552 if ( $quota <= 20 ) {
553 $icon = '<img src="' . IMAGIFY_ASSETS_IMG_URL . 'stormy.svg" width="40" height="63" alt="" />';
554 } elseif ( $quota <= 50 ) {
555 $icon = '<img src="' . IMAGIFY_ASSETS_IMG_URL . 'cloudy-sun.svg" width="63" height="64" alt="" />';
556 } else {
557 $icon = '<img src="' . IMAGIFY_ASSETS_IMG_URL . 'sun.svg" width="63" height="64" alt="" />';
558 }
559
560 return $icon;
561 }
562
563
564 /** ----------------------------------------------------------------------------------------- */
565 /** GENERIC TEMPLATE TOOLS ================================================================== */
566 /** ----------------------------------------------------------------------------------------- */
567
568 /**
569 * Get a template contents.
570 *
571 * @since 1.7
572 *
573 * @param string $template The template name.
574 * @param mixed $data Some data to pass to the template.
575 * @return string|bool The page contents. False if the template doesn't exist.
576 */
577 public function get_template( $template, $data = [] ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
578 $path = str_replace( '_', '-', $template );
579 $path = IMAGIFY_PATH . 'views/' . $template . '.php';
580
581 if ( ! $this->filesystem->exists( $path ) ) {
582 return false;
583 }
584
585 ob_start();
586 include $path;
587 $contents = ob_get_clean();
588
589 return trim( (string) $contents );
590 }
591
592 /**
593 * Print a template.
594 *
595 * @since 1.7
596 *
597 * @param string $template The template name.
598 * @param mixed $data Some data to pass to the template.
599 */
600 public function print_template( $template, $data = [] ) {
601 echo $this->get_template( $template, $data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
602 }
603
604 /**
605 * Add a template to the list of JS templates to print at the end of the page.
606 *
607 * @since 1.7
608 *
609 * @param string $template The template name.
610 */
611 public function print_js_template_in_footer( $template ) {
612 if ( isset( $this->templates_in_footer[ $template ] ) ) {
613 return;
614 }
615
616 if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
617 return;
618 }
619
620 switch ( $template ) {
621 case 'button/processing':
622 $data = [ 'label' => '{{ data.label }}' ];
623 break;
624 default:
625 $data = [];
626 }
627
628 $this->templates_in_footer[ $template ] = $data;
629 }
630
631 /**
632 * Print the JS templates that have been added to the "queue".
633 *
634 * @since 1.9
635 */
636 public function print_js_templates() {
637 if ( ! $this->templates_in_footer ) {
638 return;
639 }
640
641 foreach ( $this->templates_in_footer as $template => $data ) {
642 $template_id = str_replace( [ '/', '_' ], '-', $template );
643
644 echo '<script type="text/html" id="tmpl-imagify-' . esc_attr( $template_id ) . '">';
645 $this->print_template( $template, $data );
646 echo '</script>';
647 }
648 }
649
650 /**
651 * Get imagify user info
652 *
653 * @return bool
654 */
655 private function get_user_info(): bool {
656 $user = new User();
657 $unconsumed_quota = $user->get_percent_unconsumed_quota();
658
659 return ( ! $user->is_infinite() && $unconsumed_quota <= 20 )
660 || ( $user->is_free() && $unconsumed_quota > 20 );
661 }
662
663 /**
664 * Start print the payment modal process.
665 */
666 public function maybe_print_modal_payment() {
667 if ( $this->get_user_info() ) {
668 global $wp_admin_bar;
669 $this->admin_menu_is_present = $wp_admin_bar && $wp_admin_bar->get_node( 'imagify' );
670
671 return;
672 }
673
674 $this->admin_menu_is_present = false;
675 }
676
677 /**
678 * Print the payment modal.
679 *
680 * @return void
681 */
682 public function print_modal_payment() {
683 if ( is_admin_bar_showing() && $this->admin_menu_is_present ) {
684 $this->print_template(
685 'modal-payment',
686 [
687 'attachments_number' => $this->get_attachments_number_modal(),
688 ]
689 );
690 }
691 }
692
693 /**
694 * Get the number of attachments to display in the payment modal.
695 *
696 * @return int
697 */
698 private function get_attachments_number_modal() {
699 $transient = get_transient( 'imagify_attachments_number_modal' );
700
701 if ( false !== $transient ) {
702 return $transient;
703 }
704
705 $attachments_number = imagify_count_attachments() + Imagify_Files_Stats::count_all_files();
706
707 set_transient( 'imagify_attachments_number_modal', $attachments_number, 1 * DAY_IN_SECONDS );
708
709 return $attachments_number;
710 }
711
712 /** ----------------------------------------------------------------------------------------- */
713 /** TOOLS =================================================================================== */
714 /** ----------------------------------------------------------------------------------------- */
715
716 /**
717 * Create HTML attributes from an array.
718 *
719 * @since 1.9
720 *
721 * @param array $attributes A list of attribute pairs.
722 * @return string HTML attributes.
723 */
724 public function build_attributes( $attributes ) {
725 if ( ! $attributes || ! is_array( $attributes ) ) {
726 return '';
727 }
728
729 $out = '';
730
731 foreach ( $attributes as $attribute => $value ) {
732 if ( '' === $value ) {
733 continue;
734 }
735
736 $out .= ' ' . $attribute . '="' . esc_attr( $value ) . '"';
737 }
738
739 return $out;
740 }
741 }
742