PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.0.1
Defender Security – Malware Scanner, Login Security & Firewall v3.0.1
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / extra / free-dashboard / classes / class-handler.php

class-handler.php in Defender Security – Malware Scanner, Login Security & Firewall 3.0.1, at extra/free-dashboard/classes/class-handler.php

745 lines 18.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin notices handler.
4 *
5 * This class will take care of registering, queuing and showing different
6 * notices across WP pages.
7 *
8 * @since 2.0
9 * @author Incsub (Joel James)
10 * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
11 * @copyright Copyright (c) 2022, Incsub
12 * @package WPMUDEV\Notices
13 * @subpackage Handler
14 */
15
16 namespace WPMUDEV\Notices;
17
18 // If this file is called directly, abort.
19 defined( 'WPINC' ) || die;
20
21 if ( ! class_exists( __NAMESPACE__ . '\\Handler' ) ) {
22 /**
23 * Class Module
24 *
25 * @since 2.0
26 * @package WPMUDEV\Notices
27 */
28 final class Handler {
29
30 /**
31 * Current version.
32 *
33 * @var string $version
34 *
35 * @since 2.0
36 */
37 public $version = '2.0.2';
38
39 /**
40 * Option name to store data.
41 *
42 * @var string $option_name
43 *
44 * @since 2.0
45 */
46 protected $option_name = 'wpmudev_notices';
47
48 /**
49 * Registered plugins for the opt-ins.
50 *
51 * @var array[] $plugins
52 *
53 * @since 2.0
54 */
55 private $plugins = array();
56
57 /**
58 * WordPress screen IDs to show notices on.
59 *
60 * @var array[] $screens
61 *
62 * @since 2.0
63 */
64 private $screens = array();
65
66 /**
67 * Disabled notice types.
68 *
69 * @var string[] $disabled
70 *
71 * @since 2.0
72 */
73 private $disabled = array( 'email' );
74
75 /**
76 * Registered plugin notices data from db.
77 *
78 * @var array|null $queue
79 *
80 * @since 2.0
81 */
82 private $stored = null;
83
84 /**
85 * Notice types that are shown on WP Dashboard.
86 *
87 * @var array $wp_notices
88 *
89 * @since 2.0
90 */
91 private $wp_notices = array(
92 'email' => '\WPMUDEV\Notices\Notices\Email',
93 'rate' => '\WPMUDEV\Notices\Notices\Rating',
94 );
95
96 /**
97 * Notice type that are shown within plugin pages.
98 *
99 * @var array $plugin_notices
100 *
101 * @since 2.0
102 */
103 private $plugin_notices = array(
104 'giveaway' => '\WPMUDEV\Notices\Notices\Giveaway',
105 );
106
107 /**
108 * Construct handler class.
109 *
110 * @since 2.0
111 */
112 protected function __construct() {
113 // Register plugins.
114 add_action( 'wpmudev_register_notices', array( $this, 'register' ), 10, 2 );
115
116 // Always setup ajax actions.
117 add_action( 'wp_ajax_wpmudev_notices_action', array( $this, 'process_action' ) );
118
119 // Render admin notices.
120 add_action( 'load-index.php', array( $this, 'admin_notice' ) );
121 }
122
123 /**
124 * Initializes and returns the singleton instance.
125 *
126 * @since 2.0
127 *
128 * @return static
129 */
130 public static function instance() {
131 static $instance = null;
132
133 if ( null === $instance ) {
134 $instance = new self();
135 }
136
137 return $instance;
138 }
139
140 /**
141 * Register an active plugin for notices.
142 *
143 * ```php
144 * do_action(
145 * 'wpmudev_register_notices',
146 * 'beehive', // Plugin id.
147 * array(
148 * 'basename' => plugin_basename( BEEHIVE_PLUGIN_FILE ), // Required: Plugin basename (for backward compat).
149 * 'title' => 'Beehive', // Required: Plugin title.
150 * 'wp_slug' => 'beehive-analytics', // Required: wp.org slug of the plugin.
151 * 'cta_email' => __( 'Get Fast!', 'ga_trans' ), // Email button CTA.
152 * 'mc_list_id' => 'xxxxxxxx', // Optional: Mailchimp list id for the plugin - e.g. 4b14b58816 is list id for Smush.
153 * 'installed_on' => time(), // Optional: Plugin activated time.
154 * 'screens' => array( // Required: Plugin screen ids.
155 * 'toplevel_page_beehive',
156 * 'dashboard_page_beehive-accounts',
157 * 'dashboard_page_beehive-settings',
158 * 'dashboard_page_beehive-tutorials',
159 * 'dashboard_page_beehive-google-analytics',
160 * 'dashboard_page_beehive-google-tag-manager',
161 * ),
162 * )
163 * );
164 * ```
165 *
166 * @param string $plugin_id Plugin ID.
167 * @param array $options Options.
168 *
169 * @since 2.0
170 */
171 public function register( $plugin_id, array $options = array() ) {
172 // Plugin ID can't be empty.
173 if ( empty( $plugin_id ) ) {
174 return;
175 }
176
177 // Add to the plugins list.
178 $this->plugins[ $plugin_id ] = $options;
179
180 // Setup screens.
181 if ( ! empty( $options['screens'] ) ) {
182 $this->add_to_screens( $plugin_id, $options['screens'] );
183 }
184 }
185
186 /**
187 * Show an admin notice on WP page (not plugin's SUI pages).
188 *
189 * @since 2.0
190 *
191 * @return void
192 */
193 public function admin_notice() {
194 if ( is_super_admin() ) {
195 add_action( 'all_admin_notices', array( $this, 'render_admin_notice' ) );
196 }
197 }
198
199 /**
200 * Show a notice on current plugin page.
201 *
202 * @since 2.0
203 *
204 * @return void
205 */
206 public function plugin_notice() {
207 if ( is_super_admin() ) {
208 add_action( 'all_admin_notices', array( $this, 'render_plugin_notice' ) );
209 }
210 }
211
212 /**
213 * Render admin notice content.
214 *
215 * @since 2.0
216 *
217 * @return void
218 */
219 public function render_admin_notice() {
220 $this->render( false, array_keys( $this->wp_notices ) );
221 }
222
223 /**
224 * Render a plugin notice content.
225 *
226 * @since 2.0
227 *
228 * @return void
229 */
230 public function render_plugin_notice() {
231 // Get current screen.
232 $screen = get_current_screen();
233
234 // Continue only if registered screen.
235 if ( empty( $screen->id ) || empty( $this->screens[ $screen->id ] ) ) {
236 return;
237 }
238
239 $this->render(
240 $this->screens[ $screen->id ],
241 array_keys( $this->plugin_notices )
242 );
243 }
244
245 /**
246 * Process a notice action.
247 *
248 * All ajax requests from the notice are processed here.
249 * After nonce verification the action will be processed if a matching
250 * method is already defined.
251 *
252 * @since 2.0
253 */
254 public function process_action() {
255 // Check required fields.
256 if ( ! isset( $_POST['plugin_id'], $_POST['notice_action'], $_POST['notice_type'], $_POST['nonce'] ) ) {
257 wp_die( esc_html__( 'Required fields are missing.', 'wdev_frash' ) );
258 }
259
260 // Only admins can do this.
261 if ( ! is_super_admin() ) {
262 wp_die( esc_html__( 'Access check failed.', 'wdev_frash' ) );
263 }
264
265 // Get request data.
266 $plugin = sanitize_text_field( wp_unslash( $_POST['plugin_id'] ) );
267 $action = sanitize_text_field( wp_unslash( $_POST['notice_action'] ) );
268 $type = sanitize_text_field( wp_unslash( $_POST['notice_type'] ) );
269 $nonce = sanitize_text_field( wp_unslash( $_POST['nonce'] ) );
270
271 // Verify nonce.
272 if ( ! wp_verify_nonce( $nonce, 'wpmudev_notices_action' ) ) {
273 wp_die( esc_html__( 'Nonce verification failed.', 'wdev_frash' ) );
274 }
275
276 // Initialize the options.
277 $this->init_option();
278
279 // Get the notice class.
280 $notice = $this->get_notice( $plugin, $type );
281
282 // Process action if defined on this class.
283 if ( method_exists( $this, $action ) ) {
284 call_user_func( array( $this, $action ), $plugin, $type );
285 } elseif ( is_object( $notice ) && method_exists( $notice, $action ) ) {
286 // Process action if defined on the notice class.
287 call_user_func( array( $notice, $action ), $plugin );
288 }
289
290 /**
291 * Action hook to do something after a notice action is performed.
292 *
293 * @param string $action Action.
294 * @param string $plugin Plugin ID.
295 * @param string $type Notice type.
296 *
297 * @since 2.0
298 */
299 do_action( 'wpmudev_notices_after_notice_action', $action, $plugin, $type );
300
301 wp_die();
302 }
303
304 /**
305 * Remove a notice from the queue.
306 *
307 * @param string $plugin Plugin ID.
308 * @param string $type Notice type.
309 *
310 * @since 2.0
311 *
312 * @return void
313 */
314 public function dismiss_notice( $plugin, $type ) {
315 // Remove from the queue.
316 if ( isset( $this->stored['queue'][ $plugin ][ $type ] ) ) {
317 unset( $this->stored['queue'][ $plugin ][ $type ] );
318 }
319
320 // Setup done list.
321 if ( ! isset( $this->stored['done'][ $plugin ] ) ) {
322 $this->stored['done'][ $plugin ] = array();
323 }
324
325 // Mark as done.
326 $this->stored['done'][ $plugin ][ $type ] = time();
327
328 // Update the queue.
329 $this->update_option();
330 }
331
332 /**
333 * Getter for the queue data.
334 *
335 * @since 2.0
336 *
337 * @return array
338 */
339 public function get_option() {
340 $this->init_option();
341
342 return $this->stored;
343 }
344
345 /**
346 * Update the notices stored data in db.
347 *
348 * @param array $data Option data (optional).
349 *
350 * @since 2.0
351 *
352 * @return bool
353 */
354 public function update_option( $data = false ) {
355 // If new data is provided use it.
356 if ( ! empty( $data ) ) {
357 $this->stored = $data;
358 }
359
360 // Update the data.
361 return update_site_option( $this->option_name, $this->stored );
362 }
363
364 /**
365 * Render notice for the current screen.
366 *
367 * @param string|false $plugin_id Plugin id (false to check all plugins).
368 * @param array $types Notice types to render.
369 *
370 * @since 2.0
371 *
372 * @return void|string
373 */
374 protected function render( $plugin_id = false, $types = array() ) {
375 // Setup queue when required.
376 $this->setup_queue();
377
378 if ( empty( $plugin_id ) ) {
379 // Get a random notice.
380 $notice = $this->get_random_notice( $types, $plugin_id );
381 } else {
382 // Get a plugin's notice.
383 $notice = $this->get_plugin_notice( $plugin_id, $types );
384 }
385
386 // Render if notice found.
387 if ( ! empty( $notice ) && method_exists( $notice, 'render' ) ) {
388 return call_user_func( array( $notice, 'render' ), $plugin_id );
389 }
390 }
391
392 /**
393 * Set screen IDs for the notices.
394 *
395 * NOTE: Only one plugin can use one screen id.
396 *
397 * @param string $plugin_id Plugin ID.
398 * @param array $screens Screen IDs.
399 *
400 * @since 2.0
401 *
402 * @return void
403 */
404 protected function add_to_screens( $plugin_id, array $screens ) {
405 // Set the screens.
406 if ( ! empty( $screens ) ) {
407 foreach ( $screens as $screen_id ) {
408 $this->screens[ $screen_id ] = $plugin_id;
409
410 // Remove network suffix for page hook.
411 if ( is_multisite() ) {
412 $screen_id = str_replace( '-network', '', $screen_id );
413 }
414
415 // Register screen notice.
416 add_action( "load-$screen_id", array( $this, 'plugin_notice' ) );
417 }
418 }
419 }
420
421 /**
422 * Setup the notices queue when ready.
423 *
424 * To avoid calling db queries we need to do this only before
425 * a notice is being rendered.
426 *
427 * @since 2.0
428 *
429 * @return void
430 */
431 protected function setup_queue() {
432 // Initialize data.
433 $this->init_option();
434
435 // Setup all registered plugins to in queue.
436 foreach ( $this->plugins as $plugin_id => $options ) {
437 $this->add_to_queue( $plugin_id, $options );
438 }
439 }
440
441 /**
442 * Set the queue for the plugin if required.
443 *
444 * We should always schedule all notice types even if they
445 * are disabled. Then only we can enable it later easily.
446 * Disabled notices won't be considered when taken from the queue.
447 *
448 * @param string $plugin_id Plugin ID.
449 * @param array $options Options.
450 *
451 * @since 2.0
452 *
453 * @return void
454 */
455 protected function add_to_queue( $plugin_id, array $options ) {
456 // Store to notice queue if not saved already.
457 if ( ! isset( $this->stored['plugins'][ $plugin_id ] ) ) {
458 // Register plugin.
459 $this->stored['plugins'][ $plugin_id ] = time();
460 $this->stored['queue'][ $plugin_id ] = array();
461
462 // Add notices to queue.
463 foreach ( $this->get_types() as $type => $class_name ) {
464 // Notice class.
465 $notice = $this->get_notice( $plugin_id, $type );
466 // Schedule notice.
467 if ( ! empty( $notice ) ) {
468 $this->stored['queue'][ $plugin_id ][ $type ] = $notice->get_next_schedule( $options['installed_on'] );
469 }
470 }
471
472 // Upgrade if required.
473 if ( ! empty( $options['basename'] ) ) {
474 $this->maybe_upgrade( $plugin_id, $options['basename'] );
475 }
476
477 // Update the stored data.
478 $this->update_option();
479 }
480 }
481
482 /**
483 * Init the notices stored data.
484 *
485 * Get from the db only if not already initialized.
486 *
487 * @since 2.0
488 */
489 protected function init_option() {
490 if ( null === $this->stored ) {
491 $queue = (array) get_site_option( $this->option_name, array() );
492
493 $this->stored = wp_parse_args(
494 $queue,
495 array(
496 'plugins' => array(),
497 'queue' => array(),
498 'done' => array(),
499 )
500 );
501 }
502 }
503
504 /**
505 * Get a notice object from the entire due list.
506 *
507 * This is usually used for a common WP page where all plugins'
508 * notices are shown. Eg: WP Dashboard page.
509 *
510 * @param array $types Notice types.
511 * @param string|bool $plugin_id Plugin ID.
512 *
513 * @since 2.0
514 *
515 * @return object|false
516 */
517 protected function get_random_notice( array $types = array(), &$plugin_id = false ) {
518 if ( ! empty( $this->stored['queue'] ) ) {
519 // Check all due items.
520 foreach ( $this->stored['queue'] as $plugin => $notices ) {
521 if ( ! empty( $notices ) ) {
522 // Chose one with priority.
523 $notice = $this->choose_notice( $plugin, $notices, $types );
524 // Return only if a valid notice is selected.
525 if ( ! empty( $notice ) ) {
526 // Set the plugin id.
527 $plugin_id = $plugin;
528
529 return $notice;
530 }
531 }
532 }
533 }
534
535 return false;
536 }
537
538 /**
539 * Get a notice object for the plugin.
540 *
541 * Select one with priority from the due list for the plugin.
542 *
543 * @param string $plugin_id Plugin ID.
544 * @param array $types Notice types.
545 *
546 * @since 2.0
547 *
548 * @return object|false
549 */
550 protected function get_plugin_notice( $plugin_id, array $types = array() ) {
551 // Choose one notice from the due list.
552 if ( ! empty( $this->stored['queue'][ $plugin_id ] ) ) {
553 return $this->choose_notice(
554 $plugin_id,
555 $this->stored['queue'][ $plugin_id ],
556 $types
557 );
558 }
559
560 return false;
561 }
562
563 /**
564 * Choose a notice from the due list.
565 *
566 * Notice will be selected based on the order it's defined
567 * in the $types property of this class.
568 *
569 * @param string $plugin_id Plugin ID.
570 * @param array $notices Notices array.
571 * @param array $types Notice types.
572 *
573 * @since 2.0
574 *
575 * @return object|false
576 */
577 protected function choose_notice( $plugin_id, array $notices, array $types = array() ) {
578 foreach ( $this->get_types() as $type => $class ) {
579 // Not in the list.
580 if ( ! isset( $notices[ $type ] ) ) {
581 continue;
582 }
583
584 // Not a desired type, skip.
585 if ( ! empty( $types ) && ! in_array( $type, $types, true ) ) {
586 continue;
587 }
588
589 // Disabled type, skip.
590 if ( in_array( $type, $this->disabled, true ) ) {
591 continue;
592 }
593
594 // Due time reached or passed.
595 if ( $this->get_current_time() >= (int) $notices[ $type ] ) {
596 // Get the notice class instance.
597 $notice = $this->get_notice( $plugin_id, $type );
598
599 // Return the notice object.
600 if ( ! empty( $notice ) && $notice->can_show( $plugin_id ) ) {
601 return $notice;
602 }
603 }
604 }
605
606 return false;
607 }
608
609 /**
610 * Get the notice type class instance.
611 *
612 * @param string $plugin_id Plugin ID.
613 * @param string $type Notice type.
614 *
615 * @since 2.0
616 *
617 * @return bool|object
618 */
619 protected function get_notice( $plugin_id, $type ) {
620 $types = $this->get_types();
621
622 // If a valid class found for the type.
623 if (
624 isset( $types[ $type ] )
625 && isset( $this->plugins[ $plugin_id ] )
626 && class_exists( $types[ $type ] )
627 ) {
628 /**
629 * Notice class name.
630 *
631 * @var Notices\Notice $class_name
632 */
633 $class_name = $types[ $type ];
634
635 return $class_name::instance( $this->plugins[ $plugin_id ] );
636 }
637
638 return false;
639 }
640
641 /**
642 * Get available notice types and classes.
643 *
644 * @since 2.0
645 *
646 * @return array
647 */
648 protected function get_types() {
649 return array_merge(
650 $this->wp_notices,
651 $this->plugin_notices
652 );
653 }
654
655 /**
656 * Get current time to use for due date check.
657 *
658 * This is used to enable custom time fot testing.
659 *
660 * @since 2.0
661 *
662 * @return int
663 */
664 protected function get_current_time() {
665 // Get custom time.
666 $time = filter_input( INPUT_GET, 'wpmudev_notice_time', FILTER_SANITIZE_SPECIAL_CHARS );
667
668 return empty( $time ) ? time() : (int) $time;
669 }
670
671 /**
672 * Optional upgrade from old version (WDEV Frash).
673 *
674 * If old data exist, we need to use it before registering new time.
675 * Used only when registering for the first time.
676 *
677 * @param string $plugin_id Plugin ID.
678 * @param string $basename Plugin basename (used in old plugins).
679 *
680 * @since 2.0
681 * @deprecated 2.0 We may remove this in future.
682 *
683 * @return void
684 */
685 protected function maybe_upgrade( $plugin_id, $basename ) {
686 // Old settings data.
687 $deprecated = get_site_option( 'wdev-frash' );
688
689 // Old notice exists, upgrade it.
690 if ( ! empty( $deprecated ) ) {
691 $deprecated = wp_parse_args(
692 (array) $deprecated,
693 array(
694 'plugins' => array(),
695 'queue' => array(),
696 'done' => array(),
697 )
698 );
699
700 // Not found in old settings.
701 if ( ! isset( $deprecated['plugins'][ $basename ] ) ) {
702 return;
703 }
704
705 // Use old registered time.
706 $this->stored['plugins'][ $plugin_id ] = $deprecated['plugins'][ $basename ];
707 // Existing plugin, so show giveaway right away.
708 $this->stored['queue'][ $plugin_id ]['giveaway'] = time();
709
710 // Only email and rate types.
711 foreach ( array( 'email', 'rate' ) as $type ) {
712 // Old key hash.
713 $hash = md5( $basename . '-' . $type );
714
715 if ( isset( $deprecated['queue'][ $hash ]['show_at'] ) ) {
716 // Use the existing time.
717 $this->stored['queue'][ $plugin_id ][ $type ] = $deprecated['queue'][ $hash ]['show_at'];
718 // Remove from old settings.
719 unset( $deprecated['queue'][ $hash ] );
720 } else {
721 // Check if notice type found in dismissed list.
722 $dismissed = array_filter(
723 $deprecated['done'],
724 function ( $item ) use ( $basename, $type ) {
725 return $item['plugin'] === $basename && $item['type'] === $type;
726 }
727 );
728
729 // Already shown and dismissed, remove it.
730 if ( ! empty( $dismissed[0]['handled_at'] ) ) {
731 // Remove from queue.
732 unset( $this->stored['queue'][ $plugin_id ][ $type ] );
733 // Move to done list.
734 $this->stored['done'][ $plugin_id ][ $type ] = $dismissed[0]['handled_at'];
735 }
736 }
737 }
738
739 // Do not delete it yet for backward compatibility.
740 update_site_option( 'wdev-frash', $deprecated );
741 }
742 }
743 }
744 }
745