PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.3.0
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.3.0
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 / classes / Notices / Notices.php

Notices.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 2.3.0, at classes/Notices/Notices.php

923 lines 23.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 declare(strict_types=1);
3
4 namespace Imagify\Notices;
5
6 use Imagify\Traits\InstanceGetterTrait;
7
8 /**
9 * Class that handles the admin notices.
10 *
11 * @since 1.6.10
12 */
13 final class Notices {
14 use InstanceGetterTrait;
15
16 /**
17 * Class version.
18 *
19 * @var string
20 */
21 const VERSION = '1.0.1';
22
23 /**
24 * Name of the transient storing temporary notices.
25 *
26 * @var string
27 */
28 const TEMPORARY_NOTICES_TRANSIENT_NAME = 'imagify_temporary_notices';
29
30 /**
31 * Name of the user meta that stores the dismissed notice IDs.
32 *
33 * @var string
34 */
35 const DISMISS_META_NAME = '_imagify_ignore_notices';
36
37 /**
38 * Action used in the nonce to dismiss a notice.
39 *
40 * @var string
41 */
42 const DISMISS_NONCE_ACTION = 'imagify-dismiss-notice';
43
44 /**
45 * Action used in the nonce to deactivate a plugin.
46 *
47 * @var string
48 */
49 const DEACTIVATE_PLUGIN_NONCE_ACTION = 'imagify-deactivate-plugin';
50
51 /**
52 * List of notice IDs.
53 * They correspond to method names and IDs stored in the "dismissed" transient.
54 * Only use "-" character, not "_".
55 *
56 * @var array
57 */
58 protected static $notice_ids = [
59 // This warning is displayed when the API key is empty. Dismissible.
60 'welcome-steps',
61 // This warning is displayed when the API key is wrong. Dismissible.
62 'wrong-api-key',
63 // This warning is displayed if some plugins are active. NOT dismissible.
64 'plugins-to-deactivate',
65 // This notice is displayed when external HTTP requests are blocked via the WP_HTTP_BLOCK_EXTERNAL constant. Dismissible.
66 'http-block-external',
67 // This warning is displayed when the grid view is active on the library. Dismissible.
68 'grid-view',
69 // This warning is displayed if the backup folder is not writable. NOT dismissible.
70 'backup-folder-not-writable',
71 // This notice is displayed to rate the plugin after 100 optimizations & 7 days after the first installation. Dismissible.
72 'rating',
73 // Add a message about WP Rocket on the "Bulk Optimization" screen. Dismissible.
74 'wp-rocket',
75 'bulk-optimization-complete',
76 'bulk-optimization-running',
77 'upsell-banner',
78 'upsell-admin-bar',
79 ];
80
81 /**
82 * List of user capabilities to use for each notice.
83 * Default value 'manage' is not listed.
84 *
85 * @var array
86 */
87 protected static $capabilities = [
88 'grid-view' => 'optimize',
89 'backup-folder-not-writable' => 'bulk-optimize',
90 'rating' => 'bulk-optimize',
91 'wp-rocket' => 'bulk-optimize',
92 'bulk-optimization-complete' => 'bulk-optimize',
93 'bulk-optimization-running' => 'bulk-optimize',
94 ];
95
96 /**
97 * List of plugins that conflict with Imagify.
98 *
99 * @var array
100 */
101 protected static $conflicting_plugins = [
102 'wp-smush' => 'wp-smushit/wp-smush.php', // WP Smush.
103 'wp-smush-pro' => 'wp-smush-pro/wp-smush.php', // WP Smush Pro.
104 'kraken' => 'kraken-image-optimizer/kraken.php', // Kraken.io.
105 'tinypng' => 'tiny-compress-images/tiny-compress-images.php', // TinyPNG.
106 'shortpixel' => 'shortpixel-image-optimiser/wp-shortpixel.php', // Shortpixel.
107 'ewww' => 'ewww-image-optimizer/ewww-image-optimizer.php', // EWWW Image Optimizer.
108 'ewww-cloud' => 'ewww-image-optimizer-cloud/ewww-image-optimizer-cloud.php', // EWWW Image Optimizer Cloud.
109 'imagerecycle' => 'imagerecycle-pdf-image-compression/wp-image-recycle.php', // ImageRecycle.
110 ];
111
112 /**
113 * The constructor.
114 *
115 * @return void
116 */
117 protected function __construct() {}
118
119
120 /** ----------------------------------------------------------------------------------------- */
121 /** INIT ==================================================================================== */
122 /** ----------------------------------------------------------------------------------------- */
123
124 /**
125 * Launch the hooks.
126 *
127 * @since 1.6.10
128 */
129 public function init() {
130 // For generic purpose.
131 add_action( 'all_admin_notices', [ $this, 'render_notices' ] );
132 add_action( 'wp_ajax_imagify_dismiss_notice', [ $this, 'admin_post_dismiss_notice' ] );
133 add_action( 'admin_post_imagify_dismiss_notice', [ $this, 'admin_post_dismiss_notice' ] );
134 // For specific notices.
135 add_action( 'imagify_dismiss_notice', [ $this, 'clear_scheduled_rating' ] );
136 add_action( 'admin_post_imagify_deactivate_plugin', [ $this, 'deactivate_plugin' ] );
137 add_action( 'imagify_not_almost_over_quota_anymore', [ $this, 'renew_almost_over_quota_notice' ] );
138 }
139
140
141 /** ----------------------------------------------------------------------------------------- */
142 /** HOOKS =================================================================================== */
143 /** ----------------------------------------------------------------------------------------- */
144
145 /**
146 * Maybe display some notices.
147 *
148 * @since 1.6.10
149 */
150 public function render_notices() {
151 foreach ( $this->get_notice_ids() as $notice_id ) {
152 // Get the name of the method that will tell if this notice should be displayed.
153 $callback = 'display_' . str_replace( '-', '_', $notice_id );
154
155 if ( ! method_exists( $this, $callback ) ) {
156 continue;
157 }
158
159 $data = call_user_func( [ $this, $callback ] );
160
161 if ( $data ) {
162 // The notice must be displayed: render the view.
163 \Imagify_Views::get_instance()->print_template( 'notice-' . $notice_id, $data );
164 }
165 }
166
167 // Temporary notices.
168 $this->render_temporary_notices();
169 }
170
171 /**
172 * Process a dismissed notice.
173 *
174 * @since 1.6.10
175 * @see _do_admin_post_imagify_dismiss_notice()
176 */
177 public function admin_post_dismiss_notice() {
178 imagify_check_nonce( self::DISMISS_NONCE_ACTION );
179
180 $notice = ! empty( $_GET['notice'] ) ? sanitize_text_field( wp_unslash( $_GET['notice'] ) ) : '';
181 $notices = $this->get_notice_ids();
182 $notices = array_flip( $notices );
183
184 if ( ! $notice || ! isset( $notices[ $notice ] ) || ! $this->user_can( $notice ) ) {
185 imagify_die();
186 }
187
188 self::dismiss_notice( $notice );
189
190 /**
191 * Fires when a notice is dismissed.
192 *
193 * @since 1.4.2
194 *
195 * @param string $notice The notice slug
196 */
197 do_action( 'imagify_dismiss_notice', $notice );
198
199 imagify_maybe_redirect();
200 wp_send_json_success();
201 }
202
203 /**
204 * Stop the rating cron when the notice is dismissed.
205 *
206 * @since 1.6.10
207 * @see _imagify_clear_scheduled_rating()
208 *
209 * @param string $notice The notice name.
210 */
211 public function clear_scheduled_rating( $notice ) {
212 if ( 'rating' === $notice ) {
213 set_site_transient( 'do_imagify_rating_cron', 'no' );
214 \Imagify_Cron_Rating::get_instance()->unschedule_event();
215 }
216 }
217
218 /**
219 * Disable a plugin which can be in conflict with Imagify.
220 *
221 * @since 1.6.10
222 * @see _imagify_deactivate_plugin()
223 */
224 public function deactivate_plugin() {
225 imagify_check_nonce( self::DEACTIVATE_PLUGIN_NONCE_ACTION );
226
227 if ( empty( $_GET['plugin'] ) || ! $this->user_can( 'plugins-to-deactivate' ) ) {
228 imagify_die();
229 }
230
231 $plugin = sanitize_text_field( wp_unslash( $_GET['plugin'] ) );
232 $plugins = $this->get_conflicting_plugins();
233 $plugins = array_flip( $plugins );
234
235 if ( empty( $plugins[ $plugin ] ) ) {
236 imagify_die();
237 }
238
239 deactivate_plugins( $plugin );
240
241 imagify_maybe_redirect();
242 wp_send_json_success();
243 }
244
245 /**
246 * Renew the "almost-over-quota" notice when the consumed quota percent decreases back below 80%.
247 *
248 * @since 1.7
249 */
250 public function renew_almost_over_quota_notice() {
251 global $wpdb;
252
253 $results = $wpdb->get_results( $wpdb->prepare( "SELECT umeta_id, user_id FROM $wpdb->usermeta WHERE meta_key = %s AND meta_value LIKE %s", self::DISMISS_META_NAME, '%upsell%' ) );
254
255 if ( ! $results ) {
256 return;
257 }
258
259 // Prevent multiple queries to the DB by caching user metas.
260 $not_cached = [];
261
262 foreach ( $results as $result ) {
263 if ( ! wp_cache_get( $result->umeta_id, 'user_meta' ) ) {
264 $not_cached[] = $result->umeta_id;
265 }
266 }
267
268 if ( $not_cached ) {
269 update_meta_cache( 'user', $not_cached );
270 }
271
272 // Renew the notice for all users.
273 foreach ( $results as $result ) {
274 self::renew_notice( 'upsell-banner', $result->user_id );
275 self::renew_notice( 'upsell-admin-bar', $result->user_id );
276 }
277 }
278
279
280 /** ----------------------------------------------------------------------------------------- */
281 /** NOTICES ================================================================================= */
282 /** ----------------------------------------------------------------------------------------- */
283
284 /**
285 * Tell if the 'welcome-steps' notice should be displayed.
286 *
287 * @since 1.6.10
288 *
289 * @return bool
290 */
291 public function display_welcome_steps() {
292 static $display;
293
294 if ( isset( $display ) ) {
295 return $display;
296 }
297
298 $display = false;
299
300 if ( ! $this->user_can( 'welcome-steps' ) ) {
301 return $display;
302 }
303
304 if ( imagify_is_screen( 'imagify-settings' ) ) {
305 return $display;
306 }
307
308 if ( self::notice_is_dismissed( 'welcome-steps' ) || get_imagify_option( 'api_key' ) ) {
309 return $display;
310 }
311
312 $display = true;
313 return $display;
314 }
315
316 /**
317 * Tell if the 'wrong-api-key' notice should be displayed.
318 *
319 * @since 1.6.10
320 *
321 * @return bool
322 */
323 public function display_wrong_api_key() {
324 static $display;
325
326 if ( isset( $display ) ) {
327 return $display;
328 }
329
330 $display = false;
331
332 if ( ! $this->user_can( 'wrong-api-key' ) ) {
333 return $display;
334 }
335
336 if ( ! imagify_is_screen( 'bulk' ) ) {
337 return $display;
338 }
339
340 if ( self::notice_is_dismissed( 'wrong-api-key' ) || ! get_imagify_option( 'api_key' ) || \Imagify_Requirements::is_api_key_valid() ) {
341 return $display;
342 }
343
344 $display = true;
345 return $display;
346 }
347
348 /**
349 * Tell if the 'plugins-to-deactivate' notice should be displayed.
350 *
351 * @since 1.6.10
352 *
353 * @return array|false An array of plugins to deactivate. false if the notice should not be displayed.
354 */
355 public function display_plugins_to_deactivate() {
356 static $display;
357
358 if ( isset( $display ) ) {
359 return $display;
360 }
361
362 if ( ! $this->user_can( 'plugins-to-deactivate' ) ) {
363 $display = false;
364 return $display;
365 }
366
367 $display = $this->get_conflicting_plugins();
368 return $display;
369 }
370
371 /**
372 * Tell if the 'plugins-to-deactivate' notice should be displayed.
373 *
374 * @since 1.6.10
375 *
376 * @return bool
377 */
378 public function display_http_block_external() {
379 static $display;
380
381 if ( isset( $display ) ) {
382 return $display;
383 }
384
385 $display = false;
386
387 if ( ! $this->user_can( 'http-block-external' ) ) {
388 return $display;
389 }
390
391 if ( imagify_is_screen( 'imagify-settings' ) ) {
392 return $display;
393 }
394
395 if ( self::notice_is_dismissed( 'http-block-external' ) || ! \Imagify_Requirements::is_imagify_blocked() ) {
396 return $display;
397 }
398
399 $display = true;
400 return $display;
401 }
402
403 /**
404 * Tell if the 'grid-view' notice should be displayed.
405 *
406 * @since 1.6.10
407 *
408 * @return bool
409 */
410 public function display_grid_view() {
411 global $wp_version;
412 static $display;
413
414 if ( isset( $display ) ) {
415 return $display;
416 }
417
418 $display = false;
419
420 if ( ! $this->user_can( 'grid-view' ) ) {
421 return $display;
422 }
423
424 if ( ! imagify_is_screen( 'library' ) ) {
425 return $display;
426 }
427
428 $media_library_mode = get_user_option( 'media_library_mode', get_current_user_id() );
429
430 if ( 'list' === $media_library_mode || self::notice_is_dismissed( 'grid-view' ) || version_compare( $wp_version, '4.0' ) < 0 ) {
431 return $display;
432 }
433
434 // Don't display the notice if the API key isn't valid.
435 if ( ! \Imagify_Requirements::is_api_key_valid() ) {
436 return $display;
437 }
438
439 $display = true;
440 return $display;
441 }
442
443 /**
444 * Tell if the 'backup-folder-not-writable' notice should be displayed.
445 *
446 * @since 1.6.10
447 *
448 * @return bool
449 */
450 public function display_backup_folder_not_writable() {
451 static $display;
452
453 if ( isset( $display ) ) {
454 return $display;
455 }
456
457 $display = false;
458
459 if ( ! $this->user_can( 'backup-folder-not-writable' ) ) {
460 return $display;
461 }
462
463 // Every places where images can be optimized, automatically or not (+ the settings page).
464 if ( ! imagify_is_screen( 'imagify-settings' ) && ! imagify_is_screen( 'library' ) && ! imagify_is_screen( 'upload' ) && ! imagify_is_screen( 'bulk' ) && ! imagify_is_screen( 'media-modal' ) ) {
465 return $display;
466 }
467
468 if ( ! get_imagify_option( 'backup' ) ) {
469 return $display;
470 }
471
472 if ( \Imagify_Requirements::attachments_backup_dir_is_writable() ) {
473 return $display;
474 }
475
476 $display = true;
477 return $display;
478 }
479
480 /**
481 * Tell if the 'rating' notice should be displayed.
482 *
483 * @since 1.6.10
484 *
485 * @return bool|int
486 */
487 public function display_rating() {
488 static $display;
489
490 if ( isset( $display ) ) {
491 return $display;
492 }
493
494 $display = false;
495
496 if ( ! $this->user_can( 'rating' ) ) {
497 return $display;
498 }
499
500 if ( ! imagify_is_screen( 'bulk' ) && ! imagify_is_screen( 'library' ) && ! imagify_is_screen( 'upload' ) ) {
501 return $display;
502 }
503
504 if ( self::notice_is_dismissed( 'rating' ) ) {
505 return $display;
506 }
507
508 $user_images_count = (int) get_site_transient( 'imagify_user_images_count' );
509
510 if ( ! $user_images_count || get_site_transient( 'imagify_seen_rating_notice' ) ) {
511 return $display;
512 }
513
514 $display = $user_images_count;
515 return $display;
516 }
517
518 /**
519 * Tell if the 'wp-rocket' notice should be displayed.
520 *
521 * @since 1.6.10
522 *
523 * @return bool
524 */
525 public function display_wp_rocket() {
526 static $display;
527
528 if ( isset( $display ) ) {
529 return $display;
530 }
531
532 $display = false;
533
534 if ( ! $this->user_can( 'wp-rocket' ) ) {
535 return $display;
536 }
537
538 if ( ! imagify_is_screen( 'bulk' ) ) {
539 return $display;
540 }
541
542 $plugins = get_plugins();
543
544 if ( isset( $plugins['wp-rocket/wp-rocket.php'] ) || self::notice_is_dismissed( 'wp-rocket' ) ) {
545 return $display;
546 }
547
548 $display = true;
549 return $display;
550 }
551
552 /**
553 * Tell if the bulk optimization complete notice should be displayed
554 *
555 * @since 2.1
556 *
557 * @return array
558 */
559 public function display_bulk_optimization_complete(): array {
560 if ( ! $this->user_can( 'bulk-optimization-complete' ) ) {
561 return [];
562 }
563
564 if ( imagify_is_screen( 'bulk' ) ) {
565 return [];
566 }
567
568 if ( self::notice_is_dismissed( 'bulk-optimization-complete' ) ) {
569 return [];
570 }
571
572 if ( false === get_transient( 'imagify_bulk_optimization_complete' ) ) {
573 return [];
574 }
575
576 $data = get_transient( 'imagify_bulk_optimization_result' );
577
578 if ( empty( $data ) ) {
579 return [];
580 }
581
582 $global_gain = $data['original_size'] - $data['optimized_size'];
583
584 $data['original_size'] = imagify_size_format( $data['original_size'], 2 );
585 $data['optimized_size'] = imagify_size_format( $global_gain, 2 );
586 $data['bulk_page_url'] = admin_url( 'upload.php?page=imagify-bulk-optimization' );
587
588 return $data;
589 }
590
591 /**
592 * Tell if the bulk optimization running notice should be displayed
593 *
594 * @since 2.1
595 *
596 * @return array
597 */
598 public function display_bulk_optimization_running(): array {
599 if ( ! $this->user_can( 'bulk-optimization-running' ) ) {
600 return [];
601 }
602
603 if ( imagify_is_screen( 'bulk' ) ) {
604 return [];
605 }
606
607 if ( self::notice_is_dismissed( 'bulk-optimization-running' ) ) {
608 return [];
609 }
610
611 $custom_folders = get_transient( 'imagify_custom-folders_optimize_running' );
612 $library_wp = get_transient( 'imagify_wp_optimize_running' );
613
614 if (
615 ! $custom_folders
616 &&
617 ! $library_wp
618 ) {
619 return [];
620 }
621
622 $data = [];
623
624 $data['bulk_page_url'] = admin_url( 'upload.php?page=imagify-bulk-optimization' );
625
626 return $data;
627 }
628
629 /** ----------------------------------------------------------------------------------------- */
630 /** TEMPORARY NOTICES ======================================================================= */
631 /** ----------------------------------------------------------------------------------------- */
632
633 /**
634 * Maybe display some notices.
635 *
636 * @since 1.7
637 */
638 protected function render_temporary_notices() {
639 if ( is_network_admin() ) {
640 $notices = $this->get_network_temporary_notices();
641 } else {
642 $notices = $this->get_site_temporary_notices();
643 }
644
645 if ( ! $notices ) {
646 return;
647 }
648
649 $views = \Imagify_Views::get_instance();
650
651 foreach ( $notices as $i => $notice_data ) {
652 $notices[ $i ]['type'] = ! empty( $notice_data['type'] ) ? $notice_data['type'] : 'error';
653 }
654
655 $views->print_template( 'notice-temporary', $notices );
656 }
657
658 /**
659 * Get temporary notices for the network.
660 *
661 * @since 1.7
662 *
663 * @return array
664 */
665 protected function get_network_temporary_notices() {
666 $notices = get_site_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
667
668 if ( false === $notices ) {
669 return [];
670 }
671
672 delete_site_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
673
674 return $notices && is_array( $notices ) ? $notices : [];
675 }
676
677 /**
678 * Create a temporary notice for the network.
679 *
680 * @since 1.7
681 *
682 * @param array|object|string $notice_data Some data, with the message to display.
683 */
684 public function add_network_temporary_notice( $notice_data ) {
685 $notices = get_site_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
686 $notices = is_array( $notices ) ? $notices : [];
687
688 if ( is_wp_error( $notice_data ) ) {
689 $notice_data = $notice_data->get_error_messages();
690 $notice_data = implode( '<br/>', $notice_data );
691 }
692
693 if ( is_string( $notice_data ) ) {
694 $notice_data = [
695 'message' => $notice_data,
696 ];
697 } elseif ( is_object( $notice_data ) ) {
698 $notice_data = (array) $notice_data;
699 }
700
701 if ( ! is_array( $notice_data ) || empty( $notice_data['message'] ) ) {
702 return;
703 }
704
705 // Deduplicate: do not add the same message twice.
706 foreach ( $notices as $existing ) {
707 if ( isset( $existing['message'] ) && $existing['message'] === $notice_data['message'] ) {
708 return;
709 }
710 }
711
712 $notices[] = $notice_data;
713
714 set_site_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME, $notices, 30 );
715 }
716
717 /**
718 * Get temporary notices for the current site.
719 *
720 * @since 1.7
721 *
722 * @return array
723 */
724 protected function get_site_temporary_notices() {
725 $notices = get_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
726
727 if ( false === $notices ) {
728 return [];
729 }
730
731 delete_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
732
733 return $notices && is_array( $notices ) ? $notices : [];
734 }
735
736 /**
737 * Create a temporary notice for the current site.
738 *
739 * @since 1.7
740 *
741 * @param array|string $notice_data Some data, with the message to display.
742 */
743 public function add_site_temporary_notice( $notice_data ) {
744 $notices = get_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME );
745 $notices = is_array( $notices ) ? $notices : [];
746
747 if ( is_string( $notice_data ) ) {
748 $notice_data = [
749 'message' => $notice_data,
750 ];
751 } elseif ( is_object( $notice_data ) ) {
752 $notice_data = (array) $notice_data;
753 }
754
755 if ( ! is_array( $notice_data ) || empty( $notice_data['message'] ) ) {
756 return;
757 }
758
759 // Deduplicate: do not add the same message twice.
760 foreach ( $notices as $existing ) {
761 if ( isset( $existing['message'] ) && $existing['message'] === $notice_data['message'] ) {
762 return;
763 }
764 }
765
766 $notices[] = $notice_data;
767
768 set_transient( self::TEMPORARY_NOTICES_TRANSIENT_NAME, $notices, 30 );
769 }
770
771
772 /** ----------------------------------------------------------------------------------------- */
773 /** PUBLIC TOOLS ============================================================================ */
774 /** ----------------------------------------------------------------------------------------- */
775
776 /**
777 * Renew a dismissed Imagify notice.
778 *
779 * @since 1.6.10
780 *
781 * @param string $notice A notice ID.
782 * @param int $user_id A user ID.
783 */
784 public static function renew_notice( $notice, $user_id = 0 ) {
785 $user_id = $user_id ? (int) $user_id : get_current_user_id();
786 $notices = get_user_meta( $user_id, self::DISMISS_META_NAME, true );
787 $notices = $notices && is_array( $notices ) ? array_flip( $notices ) : [];
788
789 if ( ! isset( $notices[ $notice ] ) ) {
790 return;
791 }
792
793 unset( $notices[ $notice ] );
794 $notices = array_flip( $notices );
795 $notices = array_filter( $notices );
796 $notices = array_values( $notices );
797
798 update_user_meta( $user_id, self::DISMISS_META_NAME, $notices );
799 }
800
801 /**
802 * Dismiss an Imagify notice.
803 *
804 * @since 1.6.10
805 * @see imagify_dismiss_notice()
806 *
807 * @param string $notice A notice ID.
808 * @param int $user_id A user ID.
809 */
810 public static function dismiss_notice( $notice, $user_id = 0 ) {
811 $user_id = $user_id ? (int) $user_id : get_current_user_id();
812 $notices = get_user_meta( $user_id, self::DISMISS_META_NAME, true );
813 $notices = $notices && is_array( $notices ) ? array_flip( $notices ) : [];
814
815 if ( isset( $notices[ $notice ] ) ) {
816 return;
817 }
818
819 $notices = array_flip( $notices );
820 $notices[] = $notice;
821 $notices = array_filter( $notices );
822 $notices = array_values( $notices );
823
824 update_user_meta( $user_id, self::DISMISS_META_NAME, $notices );
825 }
826
827 /**
828 * Tell if an Imagify notice is dismissed.
829 *
830 * @since 1.6.10
831 * @see imagify_notice_is_dismissed()
832 *
833 * @param string $notice A notice ID.
834 * @param int $user_id A user ID.
835 * @return bool
836 */
837 public static function notice_is_dismissed( $notice, $user_id = 0 ) {
838 $user_id = $user_id ? (int) $user_id : get_current_user_id();
839 $notices = get_user_meta( $user_id, self::DISMISS_META_NAME, true );
840 $notices = $notices && is_array( $notices ) ? array_flip( $notices ) : [];
841
842 return isset( $notices[ $notice ] );
843 }
844
845 /**
846 * Tell if one or more notices will be displayed later in the page.
847 *
848 * @since 1.6.10
849 *
850 * @return bool
851 */
852 public function has_notices() {
853 foreach ( self::$notice_ids as $notice_id ) {
854 $callback = 'display_' . str_replace( '-', '_', $notice_id );
855
856 if ( method_exists( $this, $callback ) && call_user_func( [ $this, $callback ] ) ) {
857 return true;
858 }
859 }
860
861 return false;
862 }
863
864
865 /** ----------------------------------------------------------------------------------------- */
866 /** INTERNAL TOOLS ========================================================================== */
867 /** ----------------------------------------------------------------------------------------- */
868
869 /**
870 * Get all notice IDs.
871 *
872 * @since 1.6.10
873 * @since 1.10 Cast return value to array.
874 *
875 * @return array The filtered notice ids.
876 */
877 protected function get_notice_ids() {
878 /**
879 * Filter the notices Imagify can display.
880 *
881 * @since 1.6.10
882 *
883 * @param array $notice_ids An array of notice "IDs".
884 */
885 return (array) wpm_apply_filters_typed( 'array', 'imagify_notices', self::$notice_ids );
886 }
887
888 /**
889 * Tell if the current user can see the notices.
890 * Notice IDs that are not listed in self::$capabilities are assumed as 'manage'.
891 *
892 * @since 1.6.10
893 *
894 * @param string $notice_id A notice ID.
895 * @return bool
896 */
897 protected function user_can( $notice_id ) {
898 $capability = isset( self::$capabilities[ $notice_id ] ) ? self::$capabilities[ $notice_id ] : 'manage';
899
900 return imagify_get_context( 'wp' )->current_user_can( $capability );
901 }
902
903 /**
904 * Get a list of plugins that can conflict with Imagify.
905 *
906 * @since 1.6.10
907 *
908 * @return array
909 */
910 protected function get_conflicting_plugins() {
911 /**
912 * Filter the recommended plugins to deactivate to prevent conflicts.
913 *
914 * @since 1.0
915 *
916 * @param array $plugins List of recommended plugins to deactivate.
917 */
918 $plugins = wpm_apply_filters_typed( 'array', 'imagify_plugins_to_deactivate', self::$conflicting_plugins );
919
920 return array_filter( $plugins, 'is_plugin_active' );
921 }
922 }
923