You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

358 lines
8.1 KiB
PHP

<?php
/**
* The Notification center handles notifications storage and display.
*
* @since 1.0.0
* @package RankMath
* @subpackage RankMath
* @author RankMath <support@rankmath.com>
*/
namespace RankMath\Admin\Notifications;
/**
* Notification_Center class.
*/
class Notification_Center {
/**
* Option name to store notifications in.
*
* @var string
*/
private $storage_key = '';
/**
* Notifications.
*
* @var array
*/
private $notifications = [];
/**
* Stores whether we need to clear storage or not.
*
* @var array
*/
private $should_clear_storage = true;
/**
* Stores already displayed notice texts to avoid duplication.
*
* @var array
*/
private $displayed_notifications = [];
/**
* Internal flag for whether notifications have been retrieved from storage.
*
* @var bool
*/
private $retrieved = false;
/**
* Construct
*
* @param string $storage_key Option name to store notification in.
*/
public function __construct( $storage_key = 'mythemeshop_notifications' ) {
$this->storage_key = $storage_key;
add_action( 'plugins_loaded', [ $this, 'get_from_storage' ], 5 );
add_action( 'all_admin_notices', [ $this, 'display' ] );
add_action( 'shutdown', [ $this, 'update_storage' ] );
add_action( 'admin_footer', [ $this, 'print_javascript' ] );
add_action( 'wp_ajax_wp_helpers_notice_dismissible', [ $this, 'notice_dismissible' ] );
}
/**
* Retrieve the notifications from storage
*
* @codeCoverageIgnore
*
* @return array Notification[] Notifications
*/
public function get_from_storage() {
if ( $this->retrieved ) {
return;
}
$this->retrieved = true;
$notifications = get_option( $this->storage_key );
// Check if notifications are stored.
if ( empty( $notifications ) ) {
$this->should_clear_storage = false;
return;
}
if ( is_array( $notifications ) ) {
foreach ( $notifications as $notification ) {
$this->notifications[] = new Notification(
$notification['message'],
$notification['options']
);
}
}
}
/**
* Display the notifications.
*
* @codeCoverageIgnore
*/
public function display() {
// Never display notifications for network admin.
if ( $this->is_network_admin() ) {
return;
}
$notifications = $this->get_sorted_notifications();
if ( empty( $notifications ) ) {
return;
}
foreach ( $notifications as $notification ) {
if ( $notification->can_display() && ! in_array( (string) $notification, $this->displayed_notifications, true ) ) {
echo $notification;
$this->displayed_notifications[] = (string) $notification;
}
}
}
/**
* Print JS for dismissile.
*
* @codeCoverageIgnore
*/
public function print_javascript() {
?>
<script>
;(function($) {
$( '.wp-helpers-notice.is-dismissible' ).on( 'click', '.notice-dismiss', function() {
var notice = $( this ).parent()
$.ajax({
url: ajaxurl,
type: 'POST',
dataType: 'json',
data: {
action: 'wp_helpers_notice_dismissible',
security: notice.data( 'security' ),
notificationId: notice.attr( 'id' )
}
});
});
})(jQuery);
</script>
<?php
}
/**
* Save persistent or transactional notifications to storage.
*
* We need to be able to retrieve these so they can be dismissed at any time during the execution.
*
* @codeCoverageIgnore
*/
public function update_storage() {
$notifications = $this->get_notifications();
$notifications = array_filter( $notifications, [ $this, 'remove_notification' ] );
/**
* Filter: 'wp_helpers_notifications_before_storage' - Allows developer to filter notifications before saving them.
*
* @param Notification[] $notifications
*/
$notifications = apply_filters( 'wp_helpers_notifications_before_storage', $notifications );
// No notifications to store, clear storage.
if ( empty( $notifications ) && $this->should_clear_storage ) {
delete_option( $this->storage_key );
return;
}
$notifications = array_map( [ $this, 'notification_to_array' ], $notifications );
// Save the notifications to the storage.
update_option( $this->storage_key, $notifications );
}
/**
* Dismiss persistent notice.
*
* @codeCoverageIgnore
*/
public function notice_dismissible() {
$notification_id = filter_input( INPUT_POST, 'notificationId' );
check_ajax_referer( $notification_id, 'security' );
$notification = $this->remove_by_id( $notification_id );
/**
* Filter: 'wp_helpers_notification_dismissed' - Allows developer to perform action after dismissed.
*
* @param Notification[] $notifications
*/
do_action( 'wp_helpers_notification_dismissed', $notification_id, $notification );
}
/**
* Add notification
*
* @param string $message Message string.
* @param array $options Set of options.
*/
public function add( $message, $options = [] ) {
if ( isset( $options['id'] ) && ! is_null( $this->get_notification_by_id( $options['id'] ) ) ) {
return;
}
$this->notifications[] = new Notification(
$message,
$options
);
}
/**
* Provide a way to verify present notifications
*
* @return array|Notification[] Registered notifications.
*/
public function get_notifications() {
return $this->notifications;
}
/**
* Get the notification by ID
*
* @param string $notification_id The ID of the notification to search for.
* @return null|Notification
*/
public function get_notification_by_id( $notification_id ) {
foreach ( $this->notifications as $notification ) {
if ( $notification_id === $notification->args( 'id' ) ) {
return $notification;
}
}
return null;
}
/**
* Remove the notification by ID
*
* @codeCoverageIgnore
*
* @param string $notification_id The ID of the notification to search for.
* @return Notification Instance of delete notification.
*/
public function remove_by_id( $notification_id ) {
$notification = $this->get_notification_by_id( $notification_id );
if ( ! is_null( $notification ) ) {
$notification->dismiss();
}
return $notification;
}
/**
* Remove notification after it has been displayed.
*
* @codeCoverageIgnore
*
* @param Notification $notification Notification to remove.
*/
public function remove_notification( Notification $notification ) {
if ( ! $notification->is_displayed() ) {
return true;
}
if ( $notification->is_persistent() ) {
return true;
}
return false;
}
/**
* Return the notifications sorted on type and priority
*
* @codeCoverageIgnore
*
* @return array|Notification[] Sorted Notifications
*/
private function get_sorted_notifications() {
$notifications = $this->get_notifications();
if ( empty( $notifications ) ) {
return [];
}
// Sort by severity, error first.
usort( $notifications, [ $this, 'sort_notifications' ] );
return $notifications;
}
/**
* Sort on type then priority
*
* @codeCoverageIgnore
*
* @param Notification $first Compare with B.
* @param Notification $second Compare with A.
* @return int 1, 0 or -1 for sorting offset.
*/
private function sort_notifications( Notification $first, Notification $second ) {
if ( 'error' === $first->args( 'type' ) ) {
return -1;
}
if ( 'error' === $second->args( 'type' ) ) {
return 1;
}
return 0;
}
/**
* Convert Notification to array representation
*
* @codeCoverageIgnore
*
* @param Notification $notification Notification to convert.
* @return array
*/
private function notification_to_array( Notification $notification ) {
return $notification->to_array();
}
/**
* Check if is network admin.
*
* @codeCoverageIgnore
*
* @return bool
*/
private function is_network_admin() {
return function_exists( 'is_network_admin' ) && is_network_admin();
}
/**
* Check if a notification with the given ID exists.
*
* @param string $id Notification ID.
* @return boolean
*/
public function has_notification( $id ) {
$notifications = $this->get_notifications();
foreach ( $notifications as $notification ) {
if ( isset( $notification->options['id'] ) && $notification->options['id'] === $id ) {
return true;
}
}
return false;
}
}