You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
533 lines
18 KiB
533 lines
18 KiB
9 months ago
|
<?php
|
||
|
/**
|
||
|
* @package Freemius
|
||
|
* @copyright Copyright (c) 2015, Freemius, Inc.
|
||
|
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
||
|
* @since 1.0.7
|
||
|
*/
|
||
|
|
||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||
|
exit;
|
||
|
}
|
||
|
|
||
|
class FS_Admin_Notice_Manager {
|
||
|
/**
|
||
|
* @since 1.2.2
|
||
|
*
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $_module_unique_affix;
|
||
|
/**
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $_id;
|
||
|
/**
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $_title;
|
||
|
/**
|
||
|
* @var array[string]array
|
||
|
*/
|
||
|
private $_notices = array();
|
||
|
/**
|
||
|
* @var FS_Key_Value_Storage
|
||
|
*/
|
||
|
private $_sticky_storage;
|
||
|
/**
|
||
|
* @var FS_Logger
|
||
|
*/
|
||
|
protected $_logger;
|
||
|
/**
|
||
|
* @since 2.0.0
|
||
|
* @var int The ID of the blog that is associated with the current site level admin notices.
|
||
|
*/
|
||
|
private $_blog_id = 0;
|
||
|
/**
|
||
|
* @since 2.0.0
|
||
|
* @var bool
|
||
|
*/
|
||
|
private $_is_network_notices;
|
||
|
|
||
|
/**
|
||
|
* @var FS_Admin_Notice_Manager[]
|
||
|
*/
|
||
|
private static $_instances = array();
|
||
|
|
||
|
/**
|
||
|
* @param string $id
|
||
|
* @param string $title
|
||
|
* @param string $module_unique_affix
|
||
|
* @param bool $is_network_and_blog_admins Whether or not the message should be shown both on
|
||
|
* network and blog admin pages.
|
||
|
* @param bool $network_level_or_blog_id Since 2.0.0
|
||
|
*
|
||
|
* @return \FS_Admin_Notice_Manager
|
||
|
*/
|
||
|
static function instance(
|
||
|
$id,
|
||
|
$title = '',
|
||
|
$module_unique_affix = '',
|
||
|
$is_network_and_blog_admins = false,
|
||
|
$network_level_or_blog_id = false
|
||
|
) {
|
||
|
if ( $is_network_and_blog_admins ) {
|
||
|
$network_level_or_blog_id = true;
|
||
|
}
|
||
|
|
||
|
$key = strtolower( $id );
|
||
|
|
||
|
if ( is_multisite() ) {
|
||
|
if ( true === $network_level_or_blog_id ) {
|
||
|
$key .= ':ms';
|
||
|
} else if ( is_numeric( $network_level_or_blog_id ) && $network_level_or_blog_id > 0 ) {
|
||
|
$key .= ":{$network_level_or_blog_id}";
|
||
|
} else {
|
||
|
$network_level_or_blog_id = get_current_blog_id();
|
||
|
|
||
|
$key .= ":{$network_level_or_blog_id}";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( ! isset( self::$_instances[ $key ] ) ) {
|
||
|
self::$_instances[ $key ] = new FS_Admin_Notice_Manager(
|
||
|
$id,
|
||
|
$title,
|
||
|
$module_unique_affix,
|
||
|
$is_network_and_blog_admins,
|
||
|
$network_level_or_blog_id
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return self::$_instances[ $key ];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $id
|
||
|
* @param string $title
|
||
|
* @param string $module_unique_affix
|
||
|
* @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network and
|
||
|
* blog admin pages.
|
||
|
* @param bool|int $network_level_or_blog_id
|
||
|
*/
|
||
|
protected function __construct(
|
||
|
$id,
|
||
|
$title = '',
|
||
|
$module_unique_affix = '',
|
||
|
$is_network_and_blog_admins = false,
|
||
|
$network_level_or_blog_id = false
|
||
|
) {
|
||
|
$this->_id = $id;
|
||
|
$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $this->_id . '_data', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
|
||
|
$this->_title = ! empty( $title ) ? $title : '';
|
||
|
$this->_module_unique_affix = $module_unique_affix;
|
||
|
$this->_sticky_storage = FS_Key_Value_Storage::instance( 'admin_notices', $this->_id, $network_level_or_blog_id );
|
||
|
|
||
|
if ( is_multisite() ) {
|
||
|
$this->_is_network_notices = ( true === $network_level_or_blog_id );
|
||
|
|
||
|
if ( is_numeric( $network_level_or_blog_id ) ) {
|
||
|
$this->_blog_id = $network_level_or_blog_id;
|
||
|
}
|
||
|
} else {
|
||
|
$this->_is_network_notices = false;
|
||
|
}
|
||
|
|
||
|
$is_network_admin = fs_is_network_admin();
|
||
|
$is_blog_admin = fs_is_blog_admin();
|
||
|
|
||
|
if ( ( $this->_is_network_notices && $is_network_admin ) ||
|
||
|
( ! $this->_is_network_notices && $is_blog_admin ) ||
|
||
|
( $is_network_and_blog_admins && ( $is_network_admin || $is_blog_admin ) )
|
||
|
) {
|
||
|
if ( 0 < count( $this->_sticky_storage ) ) {
|
||
|
$ajax_action_suffix = str_replace( ':', '-', $this->_id );
|
||
|
|
||
|
// If there are sticky notices for the current slug, add a callback
|
||
|
// to the AJAX action that handles message dismiss.
|
||
|
add_action( "wp_ajax_fs_dismiss_notice_action_{$ajax_action_suffix}", array(
|
||
|
&$this,
|
||
|
'dismiss_notice_ajax_callback'
|
||
|
) );
|
||
|
|
||
|
foreach ( $this->_sticky_storage as $msg ) {
|
||
|
// Add admin notice.
|
||
|
$this->add(
|
||
|
$msg['message'],
|
||
|
$msg['title'],
|
||
|
$msg['type'],
|
||
|
true,
|
||
|
$msg['id'],
|
||
|
false,
|
||
|
isset( $msg['wp_user_id'] ) ? $msg['wp_user_id'] : null,
|
||
|
! empty( $msg['plugin'] ) ? $msg['plugin'] : null,
|
||
|
$is_network_and_blog_admins,
|
||
|
isset( $msg['dismissible'] ) ?
|
||
|
$msg['dismissible'] :
|
||
|
null
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove sticky message by ID.
|
||
|
*
|
||
|
* @author Vova Feldman (@svovaf)
|
||
|
* @since 1.0.7
|
||
|
*
|
||
|
*/
|
||
|
function dismiss_notice_ajax_callback() {
|
||
|
check_admin_referer( 'fs_dismiss_notice_action' );
|
||
|
|
||
|
if ( ! is_numeric( $_POST['message_id'] ) ) {
|
||
|
$this->_sticky_storage->remove( $_POST['message_id'] );
|
||
|
}
|
||
|
|
||
|
wp_die();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Rendered sticky message dismiss JavaScript.
|
||
|
*
|
||
|
* @author Vova Feldman (@svovaf)
|
||
|
* @since 1.0.7
|
||
|
*/
|
||
|
static function _add_sticky_dismiss_javascript() {
|
||
|
$params = array();
|
||
|
fs_require_once_template( 'sticky-admin-notice-js.php', $params );
|
||
|
}
|
||
|
|
||
|
private static $_added_sticky_javascript = false;
|
||
|
|
||
|
/**
|
||
|
* Hook to the admin_footer to add sticky message dismiss JavaScript handler.
|
||
|
*
|
||
|
* @author Vova Feldman (@svovaf)
|
||
|
* @since 1.0.7
|
||
|
*/
|
||
|
private static function has_sticky_messages() {
|
||
|
if ( ! self::$_added_sticky_javascript ) {
|
||
|
add_action( 'admin_footer', array( 'FS_Admin_Notice_Manager', '_add_sticky_dismiss_javascript' ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handle admin_notices by printing the admin messages stacked in the queue.
|
||
|
*
|
||
|
* @author Vova Feldman (@svovaf)
|
||
|
* @since 1.0.4
|
||
|
*
|
||
|
*/
|
||
|
function _admin_notices_hook() {
|
||
|
if ( function_exists( 'current_user_can' ) &&
|
||
|
! current_user_can( 'manage_options' )
|
||
|
) {
|
||
|
// Only show messages to admins.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
foreach ( $this->_notices as $id => $msg ) {
|
||
|
if ( isset( $msg['wp_user_id'] ) && is_numeric( $msg['wp_user_id'] ) ) {
|
||
|
if ( get_current_user_id() != $msg['wp_user_id'] ) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Added a filter to control the visibility of admin notices.
|
||
|
*
|
||
|
* Usage example:
|
||
|
*
|
||
|
* /**
|
||
|
* * @param bool $show
|
||
|
* * @param array $msg {
|
||
|
* * @var string $message The actual message.
|
||
|
* * @var string $title An optional message title.
|
||
|
* * @var string $type The type of the message ('success', 'update', 'warning', 'promotion').
|
||
|
* * @var string $id The unique identifier of the message.
|
||
|
* * @var string $manager_id The unique identifier of the notices manager. For plugins it would be the plugin's slug, for themes - `<slug>-theme`.
|
||
|
* * @var string $plugin The product's title.
|
||
|
* * @var string $wp_user_id An optional WP user ID that this admin notice is for.
|
||
|
* * }
|
||
|
* *
|
||
|
* * @return bool
|
||
|
* *\/
|
||
|
* function my_custom_show_admin_notice( $show, $msg ) {
|
||
|
* if ('trial_promotion' != $msg['id']) {
|
||
|
* return false;
|
||
|
* }
|
||
|
*
|
||
|
* return $show;
|
||
|
* }
|
||
|
*
|
||
|
* my_fs()->add_filter( 'show_admin_notice', 'my_custom_show_admin_notice', 10, 2 );
|
||
|
*
|
||
|
* @author Vova Feldman
|
||
|
* @since 2.2.0
|
||
|
*/
|
||
|
$show_notice = call_user_func_array( 'fs_apply_filter', array(
|
||
|
$this->_module_unique_affix,
|
||
|
'show_admin_notice',
|
||
|
$this->show_admin_notices(),
|
||
|
$msg
|
||
|
) );
|
||
|
|
||
|
if ( true !== $show_notice ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
fs_require_template( 'admin-notice.php', $msg );
|
||
|
|
||
|
if ( $msg['sticky'] ) {
|
||
|
self::has_sticky_messages();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Enqueue common stylesheet to style admin notice.
|
||
|
*
|
||
|
* @author Vova Feldman (@svovaf)
|
||
|
* @since 1.0.7
|
||
|
*/
|
||
|
function _enqueue_styles() {
|
||
|
fs_enqueue_local_style( 'fs_common', '/admin/common.css' );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if the current page is the Gutenberg block editor.
|
||
|
*
|
||
|
* @author Vova Feldman (@svovaf)
|
||
|
* @since 2.2.3
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
function is_gutenberg_page() {
|
||
|
if ( function_exists( 'is_gutenberg_page' ) &&
|
||
|
is_gutenberg_page()
|
||
|
) {
|
||
|
// The Gutenberg plugin is on.
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
$current_screen = get_current_screen();
|
||
|
|
||
|
if ( method_exists( $current_screen, 'is_block_editor' ) &&
|
||
|
$current_screen->is_block_editor()
|
||
|
) {
|
||
|
// Gutenberg page on 5+.
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if admin notices should be shown on page. E.g., we don't want to show notices in the Visual Editor.
|
||
|
*
|
||
|
* @author Xiaheng Chen (@xhchen)
|
||
|
* @since 2.4.2
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
function show_admin_notices() {
|
||
|
global $pagenow;
|
||
|
|
||
|
if ( 'about.php' === $pagenow ) {
|
||
|
// Don't show admin notices on the About page.
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( $this->is_gutenberg_page() ) {
|
||
|
// Don't show admin notices in Gutenberg (visual editor).
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add admin message to admin messages queue, and hook to admin_notices / all_admin_notices if not yet hooked.
|
||
|
*
|
||
|
* @author Vova Feldman (@svovaf)
|
||
|
* @since 1.0.4
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $title
|
||
|
* @param string $type
|
||
|
* @param bool $is_sticky
|
||
|
* @param string $id Message ID
|
||
|
* @param bool $store_if_sticky
|
||
|
* @param number|null $wp_user_id
|
||
|
* @param string|null $plugin_title
|
||
|
* @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
|
||
|
* and blog admin pages.
|
||
|
* @param bool|null $is_dismissible
|
||
|
* @param array $data
|
||
|
*
|
||
|
* @uses add_action()
|
||
|
*/
|
||
|
function add(
|
||
|
$message,
|
||
|
$title = '',
|
||
|
$type = 'success',
|
||
|
$is_sticky = false,
|
||
|
$id = '',
|
||
|
$store_if_sticky = true,
|
||
|
$wp_user_id = null,
|
||
|
$plugin_title = null,
|
||
|
$is_network_and_blog_admins = false,
|
||
|
$is_dismissible = null,
|
||
|
$data = array()
|
||
|
) {
|
||
|
$notices_type = $this->get_notices_type();
|
||
|
|
||
|
if ( empty( $this->_notices ) ) {
|
||
|
if ( ! $is_network_and_blog_admins ) {
|
||
|
add_action( $notices_type, array( &$this, "_admin_notices_hook" ) );
|
||
|
} else {
|
||
|
add_action( 'network_admin_notices', array( &$this, "_admin_notices_hook" ) );
|
||
|
add_action( 'admin_notices', array( &$this, "_admin_notices_hook" ) );
|
||
|
}
|
||
|
|
||
|
add_action( 'admin_enqueue_scripts', array( &$this, '_enqueue_styles' ) );
|
||
|
}
|
||
|
|
||
|
if ( '' === $id ) {
|
||
|
$id = md5( $title . ' ' . $message . ' ' . $type );
|
||
|
}
|
||
|
|
||
|
$message_object = array(
|
||
|
'message' => $message,
|
||
|
'title' => $title,
|
||
|
'type' => $type,
|
||
|
'sticky' => $is_sticky,
|
||
|
'id' => $id,
|
||
|
'manager_id' => $this->_id,
|
||
|
'plugin' => ( ! is_null( $plugin_title ) ? $plugin_title : $this->_title ),
|
||
|
'wp_user_id' => $wp_user_id,
|
||
|
'dismissible' => $is_dismissible,
|
||
|
'data' => $data
|
||
|
);
|
||
|
|
||
|
if ( $is_sticky && $store_if_sticky ) {
|
||
|
$this->_sticky_storage->{$id} = $message_object;
|
||
|
}
|
||
|
|
||
|
$this->_notices[ $id ] = $message_object;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @author Vova Feldman (@svovaf)
|
||
|
* @since 1.0.7
|
||
|
*
|
||
|
* @param string|string[] $ids
|
||
|
* @param bool $store
|
||
|
*/
|
||
|
function remove_sticky( $ids, $store = true ) {
|
||
|
if ( ! is_array( $ids ) ) {
|
||
|
$ids = array( $ids );
|
||
|
}
|
||
|
|
||
|
foreach ( $ids as $id ) {
|
||
|
// Remove from sticky storage.
|
||
|
$this->_sticky_storage->remove( $id, $store );
|
||
|
|
||
|
if ( isset( $this->_notices[ $id ] ) ) {
|
||
|
unset( $this->_notices[ $id ] );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if sticky message exists by id.
|
||
|
*
|
||
|
* @author Vova Feldman (@svovaf)
|
||
|
* @since 1.0.9
|
||
|
*
|
||
|
* @param $id
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
function has_sticky( $id ) {
|
||
|
return isset( $this->_sticky_storage[ $id ] );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds sticky admin notification.
|
||
|
*
|
||
|
* @author Vova Feldman (@svovaf)
|
||
|
* @since 1.0.7
|
||
|
*
|
||
|
* @param string $message
|
||
|
* @param string $id Message ID
|
||
|
* @param string $title
|
||
|
* @param string $type
|
||
|
* @param number|null $wp_user_id
|
||
|
* @param string|null $plugin_title
|
||
|
* @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
|
||
|
* and blog admin pages.
|
||
|
* @param bool $is_dimissible
|
||
|
* @param array $data
|
||
|
*/
|
||
|
function add_sticky( $message, $id, $title = '', $type = 'success', $wp_user_id = null, $plugin_title = null, $is_network_and_blog_admins = false, $is_dimissible = true, $data = array() ) {
|
||
|
if ( ! empty( $this->_module_unique_affix ) ) {
|
||
|
$message = fs_apply_filter( $this->_module_unique_affix, "sticky_message_{$id}", $message );
|
||
|
$title = fs_apply_filter( $this->_module_unique_affix, "sticky_title_{$id}", $title );
|
||
|
}
|
||
|
|
||
|
$this->add( $message, $title, $type, true, $id, true, $wp_user_id, $plugin_title, $is_network_and_blog_admins, $is_dimissible, $data );
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves the data of an sticky notice.
|
||
|
*
|
||
|
* @author Leo Fajardo (@leorw)
|
||
|
* @since 2.4.3
|
||
|
*
|
||
|
* @param string $id Message ID.
|
||
|
*
|
||
|
* @return array|null
|
||
|
*/
|
||
|
function get_sticky( $id ) {
|
||
|
return isset( $this->_sticky_storage->{$id} ) ?
|
||
|
$this->_sticky_storage->{$id} :
|
||
|
null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear all sticky messages.
|
||
|
*
|
||
|
* @author Vova Feldman (@svovaf)
|
||
|
* @since 1.0.8
|
||
|
*
|
||
|
* @param bool $is_temporary @since 2.5.1
|
||
|
*/
|
||
|
function clear_all_sticky( $is_temporary = false ) {
|
||
|
if ( $is_temporary ) {
|
||
|
$this->_notices = array();
|
||
|
} else {
|
||
|
$this->_sticky_storage->clear_all();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#--------------------------------------------------------------------------------
|
||
|
#region Helper Method
|
||
|
#--------------------------------------------------------------------------------
|
||
|
|
||
|
/**
|
||
|
* @author Vova Feldman (@svovaf)
|
||
|
* @since 2.0.0
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
private function get_notices_type() {
|
||
|
return $this->_is_network_notices ?
|
||
|
'network_admin_notices' :
|
||
|
'admin_notices';
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
}
|