PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 19.0
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v19.0
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / admin / class-yoast-notification-center.php

class-yoast-notification-center.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 19.0, at admin/class-yoast-notification-center.php

913 lines 23.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WPSEO plugin file.
4 *
5 * @package WPSEO\Admin\Notifications
6 */
7
8 use Yoast\WP\SEO\Presenters\Abstract_Presenter;
9
10 /**
11 * Handles notifications storage and display.
12 */
13 class Yoast_Notification_Center {
14
15 /**
16 * Option name to store notifications on.
17 *
18 * @var string
19 */
20 const STORAGE_KEY = 'yoast_notifications';
21
22 /**
23 * The singleton instance of this object.
24 *
25 * @var \Yoast_Notification_Center
26 */
27 private static $instance = null;
28
29 /**
30 * Holds the notifications.
31 *
32 * @var \Yoast_Notification[][]
33 */
34 private $notifications = [];
35
36 /**
37 * Notifications there are newly added.
38 *
39 * @var array
40 */
41 private $new = [];
42
43 /**
44 * Notifications that were resolved this execution.
45 *
46 * @var int
47 */
48 private $resolved = 0;
49
50 /**
51 * Internal storage for transaction before notifications have been retrieved from storage.
52 *
53 * @var array
54 */
55 private $queued_transactions = [];
56
57 /**
58 * Internal flag for whether notifications have been retrieved from storage.
59 *
60 * @var bool
61 */
62 private $notifications_retrieved = false;
63
64 /**
65 * Internal flag for whether notifications need to be updated in storage.
66 *
67 * @var bool
68 */
69 private $notifications_need_storage = false;
70
71 /**
72 * Construct.
73 */
74 private function __construct() {
75
76 add_action( 'init', [ $this, 'setup_current_notifications' ], 1 );
77
78 add_action( 'all_admin_notices', [ $this, 'display_notifications' ] );
79
80 add_action( 'wp_ajax_yoast_get_notifications', [ $this, 'ajax_get_notifications' ] );
81
82 add_action( 'wpseo_deactivate', [ $this, 'deactivate_hook' ] );
83 add_action( 'shutdown', [ $this, 'update_storage' ] );
84 }
85
86 /**
87 * Singleton getter.
88 *
89 * @return Yoast_Notification_Center
90 */
91 public static function get() {
92
93 if ( self::$instance === null ) {
94 self::$instance = new self();
95 }
96
97 return self::$instance;
98 }
99
100 /**
101 * Dismiss a notification.
102 */
103 public static function ajax_dismiss_notification() {
104
105 $notification_center = self::get();
106
107 $notification_id = filter_input( INPUT_POST, 'notification' );
108 if ( empty( $notification_id ) ) {
109 die( '-1' );
110 }
111
112 $notification = $notification_center->get_notification_by_id( $notification_id );
113 if ( ( $notification instanceof Yoast_Notification ) === false ) {
114
115 // Permit legacy.
116 $options = [
117 'id' => $notification_id,
118 'dismissal_key' => $notification_id,
119 ];
120 $notification = new Yoast_Notification( '', $options );
121 }
122
123 if ( self::maybe_dismiss_notification( $notification ) ) {
124 die( '1' );
125 }
126
127 die( '-1' );
128 }
129
130 /**
131 * Check if the user has dismissed a notification.
132 *
133 * @param Yoast_Notification $notification The notification to check for dismissal.
134 * @param int|null $user_id User ID to check on.
135 *
136 * @return bool
137 */
138 public static function is_notification_dismissed( Yoast_Notification $notification, $user_id = null ) {
139
140 $user_id = self::get_user_id( $user_id );
141 $dismissal_key = $notification->get_dismissal_key();
142
143 // This checks both the site-specific user option and the meta value.
144 $current_value = get_user_option( $dismissal_key, $user_id );
145
146 // Migrate old user meta to user option on-the-fly.
147 if ( ! empty( $current_value )
148 && metadata_exists( 'user', $user_id, $dismissal_key )
149 && update_user_option( $user_id, $dismissal_key, $current_value ) ) {
150 delete_user_meta( $user_id, $dismissal_key );
151 }
152
153 return ! empty( $current_value );
154 }
155
156 /**
157 * Checks if the notification is being dismissed.
158 *
159 * @param Yoast_Notification $notification Notification to check dismissal of.
160 * @param string $meta_value Value to set the meta value to if dismissed.
161 *
162 * @return bool True if dismissed.
163 */
164 public static function maybe_dismiss_notification( Yoast_Notification $notification, $meta_value = 'seen' ) {
165
166 // Only persistent notifications are dismissible.
167 if ( ! $notification->is_persistent() ) {
168 return false;
169 }
170
171 // If notification is already dismissed, we're done.
172 if ( self::is_notification_dismissed( $notification ) ) {
173 return true;
174 }
175
176 $dismissal_key = $notification->get_dismissal_key();
177 $notification_id = $notification->get_id();
178
179 $is_dismissing = ( $dismissal_key === self::get_user_input( 'notification' ) );
180 if ( ! $is_dismissing ) {
181 $is_dismissing = ( $notification_id === self::get_user_input( 'notification' ) );
182 }
183
184 // Fallback to ?dismissal_key=1&nonce=bla when JavaScript fails.
185 if ( ! $is_dismissing ) {
186 $is_dismissing = ( self::get_user_input( $dismissal_key ) === '1' );
187 }
188
189 if ( ! $is_dismissing ) {
190 return false;
191 }
192
193 $user_nonce = self::get_user_input( 'nonce' );
194 if ( wp_verify_nonce( $user_nonce, $notification_id ) === false ) {
195 return false;
196 }
197
198 return self::dismiss_notification( $notification, $meta_value );
199 }
200
201 /**
202 * Dismisses a notification.
203 *
204 * @param Yoast_Notification $notification Notification to dismiss.
205 * @param string $meta_value Value to save in the dismissal.
206 *
207 * @return bool True if dismissed, false otherwise.
208 */
209 public static function dismiss_notification( Yoast_Notification $notification, $meta_value = 'seen' ) {
210 // Dismiss notification.
211 return update_user_option( get_current_user_id(), $notification->get_dismissal_key(), $meta_value ) !== false;
212 }
213
214 /**
215 * Restores a notification.
216 *
217 * @param Yoast_Notification $notification Notification to restore.
218 *
219 * @return bool True if restored, false otherwise.
220 */
221 public static function restore_notification( Yoast_Notification $notification ) {
222
223 $user_id = get_current_user_id();
224 $dismissal_key = $notification->get_dismissal_key();
225
226 // Restore notification.
227 $restored = delete_user_option( $user_id, $dismissal_key );
228
229 // Delete unprefixed user meta too for backward-compatibility.
230 if ( metadata_exists( 'user', $user_id, $dismissal_key ) ) {
231 $restored = delete_user_meta( $user_id, $dismissal_key ) && $restored;
232 }
233
234 return $restored;
235 }
236
237 /**
238 * Clear dismissal information for the specified Notification.
239 *
240 * When a cause is resolved, the next time it is present we want to show
241 * the message again.
242 *
243 * @param string|Yoast_Notification $notification Notification to clear the dismissal of.
244 *
245 * @return bool
246 */
247 public function clear_dismissal( $notification ) {
248
249 global $wpdb;
250
251 if ( $notification instanceof Yoast_Notification ) {
252 $dismissal_key = $notification->get_dismissal_key();
253 }
254
255 if ( is_string( $notification ) ) {
256 $dismissal_key = $notification;
257 }
258
259 if ( empty( $dismissal_key ) ) {
260 return false;
261 }
262
263 // Remove notification dismissal for all users.
264 $deleted = delete_metadata( 'user', 0, $wpdb->get_blog_prefix() . $dismissal_key, '', true );
265
266 // Delete unprefixed user meta too for backward-compatibility.
267 $deleted = delete_metadata( 'user', 0, $dismissal_key, '', true ) || $deleted;
268
269 return $deleted;
270 }
271
272 /**
273 * Retrieves notifications from the storage and merges in previous notification changes.
274 *
275 * The current user in WordPress is not loaded shortly before the 'init' hook, but the plugin
276 * sometimes needs to add or remove notifications before that. In such cases, the transactions
277 * are not actually executed, but added to a queue. That queue is then handled in this method,
278 * after notifications for the current user have been set up.
279 *
280 * @return void
281 */
282 public function setup_current_notifications() {
283 $this->retrieve_notifications_from_storage( get_current_user_id() );
284
285 foreach ( $this->queued_transactions as $transaction ) {
286 list( $callback, $args ) = $transaction;
287
288 call_user_func_array( $callback, $args );
289 }
290
291 $this->queued_transactions = [];
292 }
293
294 /**
295 * Add notification to the cookie.
296 *
297 * @param Yoast_Notification $notification Notification object instance.
298 */
299 public function add_notification( Yoast_Notification $notification ) {
300
301 $callback = [ $this, __METHOD__ ];
302 $args = func_get_args();
303 if ( $this->queue_transaction( $callback, $args ) ) {
304 return;
305 }
306
307 // Don't add if the user can't see it.
308 if ( ! $notification->display_for_current_user() ) {
309 return;
310 }
311
312 $notification_id = $notification->get_id();
313 $user_id = $notification->get_user_id();
314
315 // Empty notifications are always added.
316 if ( $notification_id !== '' ) {
317
318 // If notification ID exists in notifications, don't add again.
319 $present_notification = $this->get_notification_by_id( $notification_id, $user_id );
320 if ( ! is_null( $present_notification ) ) {
321 $this->remove_notification( $present_notification, false );
322 }
323
324 if ( is_null( $present_notification ) ) {
325 $this->new[] = $notification_id;
326 }
327 }
328
329 // Add to list.
330 $this->notifications[ $user_id ][] = $notification;
331
332 $this->notifications_need_storage = true;
333 }
334
335 /**
336 * Get the notification by ID and user ID.
337 *
338 * @param string $notification_id The ID of the notification to search for.
339 * @param int|null $user_id The ID of the user.
340 *
341 * @return Yoast_Notification|null
342 */
343 public function get_notification_by_id( $notification_id, $user_id = null ) {
344 $user_id = self::get_user_id( $user_id );
345
346 $notifications = $this->get_notifications_for_user( $user_id );
347
348 foreach ( $notifications as $notification ) {
349 if ( $notification_id === $notification->get_id() ) {
350 return $notification;
351 }
352 }
353
354 return null;
355 }
356
357 /**
358 * Display the notifications.
359 *
360 * @param bool $echo_as_json True when notifications should be printed directly.
361 *
362 * @return void
363 */
364 public function display_notifications( $echo_as_json = false ) {
365
366 // Never display notifications for network admin.
367 if ( is_network_admin() ) {
368 return;
369 }
370
371 $sorted_notifications = $this->get_sorted_notifications();
372 $notifications = array_filter( $sorted_notifications, [ $this, 'is_notification_persistent' ] );
373
374 if ( empty( $notifications ) ) {
375 return;
376 }
377
378 array_walk( $notifications, [ $this, 'remove_notification' ] );
379
380 $notifications = array_unique( $notifications );
381 if ( $echo_as_json ) {
382 $notification_json = [];
383
384 foreach ( $notifications as $notification ) {
385 $notification_json[] = $notification->render();
386 }
387
388 // phpcs:ignore WordPress.Security.EscapeOutput -- Reason: WPSEO_Utils::format_json_encode is safe.
389 echo WPSEO_Utils::format_json_encode( $notification_json );
390
391 return;
392 }
393
394 foreach ( $notifications as $notification ) {
395 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Reason: Temporarily disabled, see: https://github.com/Yoast/wordpress-seo-premium/issues/2510 and https://github.com/Yoast/wordpress-seo-premium/issues/2511.
396 echo $notification;
397 }
398 }
399
400 /**
401 * Remove notification after it has been displayed.
402 *
403 * @param Yoast_Notification $notification Notification to remove.
404 * @param bool $resolve Resolve as fixed.
405 */
406 public function remove_notification( Yoast_Notification $notification, $resolve = true ) {
407
408 $callback = [ $this, __METHOD__ ];
409 $args = func_get_args();
410 if ( $this->queue_transaction( $callback, $args ) ) {
411 return;
412 }
413
414 $index = false;
415
416 // ID of the user to show the notification for, defaults to current user id.
417 $user_id = $notification->get_user_id();
418 $notifications = $this->get_notifications_for_user( $user_id );
419
420 // Match persistent Notifications by ID, non persistent by item in the array.
421 if ( $notification->is_persistent() ) {
422 foreach ( $notifications as $current_index => $present_notification ) {
423 if ( $present_notification->get_id() === $notification->get_id() ) {
424 $index = $current_index;
425 break;
426 }
427 }
428 }
429 else {
430 $index = array_search( $notification, $notifications, true );
431 }
432
433 if ( $index === false ) {
434 return;
435 }
436
437 if ( $notification->is_persistent() && $resolve ) {
438 ++$this->resolved;
439 $this->clear_dismissal( $notification );
440 }
441
442 unset( $notifications[ $index ] );
443 $this->notifications[ $user_id ] = array_values( $notifications );
444
445 $this->notifications_need_storage = true;
446 }
447
448 /**
449 * Removes a notification by its ID.
450 *
451 * @param string $notification_id The notification id.
452 * @param bool $resolve Resolve as fixed.
453 *
454 * @return void
455 */
456 public function remove_notification_by_id( $notification_id, $resolve = true ) {
457 $notification = $this->get_notification_by_id( $notification_id );
458
459 if ( $notification === null ) {
460 return;
461 }
462
463 $this->remove_notification( $notification, $resolve );
464 $this->notifications_need_storage = true;
465 }
466
467 /**
468 * Get the notification count.
469 *
470 * @param bool $dismissed Count dismissed notifications.
471 *
472 * @return int Number of notifications
473 */
474 public function get_notification_count( $dismissed = false ) {
475
476 $notifications = $this->get_notifications_for_user( get_current_user_id() );
477 $notifications = array_filter( $notifications, [ $this, 'filter_persistent_notifications' ] );
478
479 if ( ! $dismissed ) {
480 $notifications = array_filter( $notifications, [ $this, 'filter_dismissed_notifications' ] );
481 }
482
483 return count( $notifications );
484 }
485
486 /**
487 * Get the number of notifications resolved this execution.
488 *
489 * These notifications have been resolved and should be counted when active again.
490 *
491 * @return int
492 */
493 public function get_resolved_notification_count() {
494
495 return $this->resolved;
496 }
497
498 /**
499 * Return the notifications sorted on type and priority.
500 *
501 * @return array|Yoast_Notification[] Sorted Notifications
502 */
503 public function get_sorted_notifications() {
504 $notifications = $this->get_notifications_for_user( get_current_user_id() );
505 if ( empty( $notifications ) ) {
506 return [];
507 }
508
509 // Sort by severity, error first.
510 usort( $notifications, [ $this, 'sort_notifications' ] );
511
512 return $notifications;
513 }
514
515 /**
516 * AJAX display notifications.
517 */
518 public function ajax_get_notifications() {
519 $echo = filter_input( INPUT_POST, 'version' ) === '2';
520
521 // Display the notices.
522 $this->display_notifications( $echo );
523
524 // AJAX die.
525 exit;
526 }
527
528 /**
529 * Remove storage when the plugin is deactivated.
530 */
531 public function deactivate_hook() {
532
533 $this->clear_notifications();
534 }
535
536 /**
537 * Returns the given user ID if it exists.
538 * Otherwise, this function returns the ID of the current user.
539 *
540 * @param int $user_id The user ID to check.
541 *
542 * @return int The user ID to use.
543 */
544 private static function get_user_id( $user_id ) {
545 if ( $user_id ) {
546 return $user_id;
547 }
548 return get_current_user_id();
549 }
550
551 /**
552 * Splits the notifications on user ID.
553 *
554 * In other terms, it returns an associative array,
555 * mapping user ID to a list of notifications for this user.
556 *
557 * @param array|Yoast_Notification[] $notifications The notifications to split.
558 *
559 * @return array The notifications, split on user ID.
560 */
561 private function split_on_user_id( $notifications ) {
562 $split_notifications = [];
563 foreach ( $notifications as $notification ) {
564 $split_notifications[ $notification->get_user_id() ][] = $notification;
565 }
566 return $split_notifications;
567 }
568
569 /**
570 * Save persistent notifications to storage.
571 *
572 * We need to be able to retrieve these so they can be dismissed at any time during the execution.
573 *
574 * @since 3.2
575 *
576 * @return void
577 */
578 public function update_storage() {
579
580 $notifications = $this->notifications;
581
582 /**
583 * One array of Yoast_Notifications, merged from multiple arrays.
584 *
585 * @var Yoast_Notification[] $merged_notifications
586 */
587 $merged_notifications = [];
588 if ( ! empty( $notifications ) ) {
589 $merged_notifications = array_merge( ...$notifications );
590 }
591
592 /**
593 * Filter: 'yoast_notifications_before_storage' - Allows developer to filter notifications before saving them.
594 *
595 * @api Yoast_Notification[] $notifications
596 */
597 $filtered_merged_notifications = apply_filters( 'yoast_notifications_before_storage', $merged_notifications );
598
599 // The notifications were filtered and therefore need to be stored.
600 if ( $merged_notifications !== $filtered_merged_notifications ) {
601 $merged_notifications = $filtered_merged_notifications;
602 $this->notifications_need_storage = true;
603 }
604
605 $notifications = $this->split_on_user_id( $merged_notifications );
606
607 // No notifications to store, clear storage if it was previously present.
608 if ( empty( $notifications ) ) {
609 $this->remove_storage();
610
611 return;
612 }
613
614 // Only store notifications if changes are made.
615 if ( $this->notifications_need_storage ) {
616 array_walk( $notifications, [ $this, 'store_notifications_for_user' ] );
617 }
618 }
619
620 /**
621 * Stores the notifications to its respective user's storage.
622 *
623 * @param array|Yoast_Notification[] $notifications The notifications to store.
624 * @param int $user_id The ID of the user for which to store the notifications.
625 *
626 * @return void
627 */
628 private function store_notifications_for_user( $notifications, $user_id ) {
629 $notifications_as_arrays = array_map( [ $this, 'notification_to_array' ], $notifications );
630 update_user_option( $user_id, self::STORAGE_KEY, $notifications_as_arrays );
631 }
632
633 /**
634 * Provide a way to verify present notifications.
635 *
636 * @return array|Yoast_Notification[] Registered notifications.
637 */
638 public function get_notifications() {
639 if ( ! $this->notifications ) {
640 return [];
641 }
642 return array_merge( ...$this->notifications );
643 }
644
645 /**
646 * Returns the notifications for the given user.
647 *
648 * @param int $user_id The id of the user to check.
649 *
650 * @return Yoast_Notification[] The notifications for the user with the given ID.
651 */
652 public function get_notifications_for_user( $user_id ) {
653 if ( array_key_exists( $user_id, $this->notifications ) ) {
654 return $this->notifications[ $user_id ];
655 }
656 return [];
657 }
658
659 /**
660 * Get newly added notifications.
661 *
662 * @return array
663 */
664 public function get_new_notifications() {
665
666 return array_map( [ $this, 'get_notification_by_id' ], $this->new );
667 }
668
669 /**
670 * Get information from the User input.
671 *
672 * @param string $key Key to retrieve.
673 *
674 * @return mixed value of key if set.
675 */
676 private static function get_user_input( $key ) {
677
678 $filter_input_type = INPUT_GET;
679 $request_method = isset( $_SERVER['REQUEST_METHOD'] ) ? filter_var( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : '';
680
681 if ( strtoupper( $request_method ) === 'POST' ) {
682 $filter_input_type = INPUT_POST;
683 }
684
685 return filter_input( $filter_input_type, $key );
686 }
687
688 /**
689 * Retrieve the notifications from storage and fill the relevant property.
690 *
691 * @param int $user_id The ID of the user to retrieve notifications for.
692 *
693 * @return void
694 */
695 private function retrieve_notifications_from_storage( $user_id ) {
696
697 if ( $this->notifications_retrieved ) {
698 return;
699 }
700
701 $this->notifications_retrieved = true;
702
703 $stored_notifications = get_user_option( self::STORAGE_KEY, $user_id );
704
705 // Check if notifications are stored.
706 if ( empty( $stored_notifications ) ) {
707 return;
708 }
709
710 if ( is_array( $stored_notifications ) ) {
711 $notifications = array_map( [ $this, 'array_to_notification' ], $stored_notifications );
712 // Apply array_values to ensure we get a 0-indexed array.
713 $notifications = array_values( array_filter( $notifications, [ $this, 'filter_notification_current_user' ] ) );
714
715 $this->notifications[ $user_id ] = $notifications;
716 }
717 }
718
719 /**
720 * Sort on type then priority.
721 *
722 * @param Yoast_Notification $a Compare with B.
723 * @param Yoast_Notification $b Compare with A.
724 *
725 * @return int 1, 0 or -1 for sorting offset.
726 */
727 private function sort_notifications( Yoast_Notification $a, Yoast_Notification $b ) {
728
729 $a_type = $a->get_type();
730 $b_type = $b->get_type();
731
732 if ( $a_type === $b_type ) {
733 return WPSEO_Utils::calc( $b->get_priority(), 'compare', $a->get_priority() );
734 }
735
736 if ( $a_type === 'error' ) {
737 return -1;
738 }
739
740 if ( $b_type === 'error' ) {
741 return 1;
742 }
743
744 return 0;
745 }
746
747 /**
748 * Clear local stored notifications.
749 */
750 private function clear_notifications() {
751
752 $this->notifications = [];
753 $this->notifications_retrieved = false;
754 }
755
756 /**
757 * Filter out non-persistent notifications.
758 *
759 * @since 3.2
760 *
761 * @param Yoast_Notification $notification Notification to test for persistent.
762 *
763 * @return bool
764 */
765 private function filter_persistent_notifications( Yoast_Notification $notification ) {
766
767 return $notification->is_persistent();
768 }
769
770 /**
771 * Filter out dismissed notifications.
772 *
773 * @param Yoast_Notification $notification Notification to check.
774 *
775 * @return bool
776 */
777 private function filter_dismissed_notifications( Yoast_Notification $notification ) {
778
779 return ! self::maybe_dismiss_notification( $notification );
780 }
781
782 /**
783 * Convert Notification to array representation.
784 *
785 * @since 3.2
786 *
787 * @param Yoast_Notification $notification Notification to convert.
788 *
789 * @return array
790 */
791 private function notification_to_array( Yoast_Notification $notification ) {
792
793 $notification_data = $notification->to_array();
794
795 if ( isset( $notification_data['nonce'] ) ) {
796 unset( $notification_data['nonce'] );
797 }
798
799 return $notification_data;
800 }
801
802 /**
803 * Convert stored array to Notification.
804 *
805 * @param array $notification_data Array to convert to Notification.
806 *
807 * @return Yoast_Notification
808 */
809 private function array_to_notification( $notification_data ) {
810
811 if ( isset( $notification_data['options']['nonce'] ) ) {
812 unset( $notification_data['options']['nonce'] );
813 }
814
815 if ( isset( $notification_data['message'] )
816 && \is_subclass_of( $notification_data['message'], Abstract_Presenter::class, false )
817 ) {
818 $notification_data['message'] = $notification_data['message']->present();
819 }
820
821 return new Yoast_Notification(
822 $notification_data['message'],
823 $notification_data['options']
824 );
825 }
826
827 /**
828 * Filter notifications that should not be displayed for the current user.
829 *
830 * @param Yoast_Notification $notification Notification to test.
831 *
832 * @return bool
833 */
834 private function filter_notification_current_user( Yoast_Notification $notification ) {
835 return $notification->display_for_current_user();
836 }
837
838 /**
839 * Checks if given notification is persistent.
840 *
841 * @param Yoast_Notification $notification The notification to check.
842 *
843 * @return bool True when notification is not persistent.
844 */
845 private function is_notification_persistent( Yoast_Notification $notification ) {
846 return ! $notification->is_persistent();
847 }
848
849 /**
850 * Queues a notification transaction for later execution if notifications are not yet set up.
851 *
852 * @param callable $callback Callback that performs the transaction.
853 * @param array $args Arguments to pass to the callback.
854 *
855 * @return bool True if transaction was queued, false if it can be performed immediately.
856 */
857 private function queue_transaction( $callback, $args ) {
858 if ( $this->notifications_retrieved ) {
859 return false;
860 }
861
862 $this->add_transaction_to_queue( $callback, $args );
863
864 return true;
865 }
866
867 /**
868 * Adds a notification transaction to the queue for later execution.
869 *
870 * @param callable $callback Callback that performs the transaction.
871 * @param array $args Arguments to pass to the callback.
872 */
873 private function add_transaction_to_queue( $callback, $args ) {
874 $this->queued_transactions[] = [ $callback, $args ];
875 }
876
877 /**
878 * Removes all notifications from storage.
879 *
880 * @return bool True when notifications got removed.
881 */
882 protected function remove_storage() {
883 if ( ! $this->has_stored_notifications() ) {
884 return false;
885 }
886
887 delete_user_option( get_current_user_id(), self::STORAGE_KEY );
888 return true;
889 }
890
891 /**
892 * Checks if there are stored notifications.
893 *
894 * @return bool True when there are stored notifications.
895 */
896 protected function has_stored_notifications() {
897 $stored_notifications = $this->get_stored_notifications();
898
899 return ! empty( $stored_notifications );
900 }
901
902 /**
903 * Retrieves the stored notifications.
904 *
905 * @codeCoverageIgnore
906 *
907 * @return array|false Array with notifications or false when not set.
908 */
909 protected function get_stored_notifications() {
910 return get_user_option( self::STORAGE_KEY, get_current_user_id() );
911 }
912 }
913