PluginProbe
Independent Analytics – WordPress Analytics Plugin / 2.11.4
Independent Analytics – WordPress Analytics Plugin v2.11.4
2.15.5 2.15.4 2.15.3 2.15.2 2.15.1 2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 All 120 releases
independent-analytics / freemius / includes / managers / class-fs-admin-notice-manager.php
class-fs-admin-notice-manager.php
539 lines 18.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package Freemius
4 * @copyright Copyright (c) 2015, Freemius, Inc.
5 * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6 * @since 1.0.7
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 class FS_Admin_Notice_Manager {
14 /**
15 * @since 1.2.2
16 *
17 * @var string
18 */
19 protected $_module_unique_affix;
20 /**
21 * @var string
22 */
23 protected $_id;
24 /**
25 * @var string
26 */
27 protected $_title;
28 /**
29 * @var array[string]array
30 */
31 private $_notices = array();
32 /**
33 * @var FS_Key_Value_Storage
34 */
35 private $_sticky_storage;
36 /**
37 * @var FS_Logger
38 */
39 protected $_logger;
40 /**
41 * @since 2.0.0
42 * @var int The ID of the blog that is associated with the current site level admin notices.
43 */
44 private $_blog_id = 0;
45 /**
46 * @since 2.0.0
47 * @var bool
48 */
49 private $_is_network_notices;
50
51 /**
52 * @var FS_Admin_Notice_Manager[]
53 */
54 private static $_instances = array();
55
56 /**
57 * @param string $id
58 * @param string $title
59 * @param string $module_unique_affix
60 * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on
61 * network and blog admin pages.
62 * @param bool $network_level_or_blog_id Since 2.0.0
63 *
64 * @return \FS_Admin_Notice_Manager
65 */
66 static function instance(
67 $id,
68 $title = '',
69 $module_unique_affix = '',
70 $is_network_and_blog_admins = false,
71 $network_level_or_blog_id = false
72 ) {
73 if ( $is_network_and_blog_admins ) {
74 $network_level_or_blog_id = true;
75 }
76
77 $key = strtolower( $id );
78
79 if ( is_multisite() ) {
80 if ( true === $network_level_or_blog_id ) {
81 $key .= ':ms';
82 } else if ( is_numeric( $network_level_or_blog_id ) && $network_level_or_blog_id > 0 ) {
83 $key .= ":{$network_level_or_blog_id}";
84 } else {
85 $network_level_or_blog_id = get_current_blog_id();
86
87 $key .= ":{$network_level_or_blog_id}";
88 }
89 }
90
91 if ( ! isset( self::$_instances[ $key ] ) ) {
92 self::$_instances[ $key ] = new FS_Admin_Notice_Manager(
93 $id,
94 $title,
95 $module_unique_affix,
96 $is_network_and_blog_admins,
97 $network_level_or_blog_id
98 );
99 }
100
101 return self::$_instances[ $key ];
102 }
103
104 /**
105 * @param string $id
106 * @param string $title
107 * @param string $module_unique_affix
108 * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network and
109 * blog admin pages.
110 * @param bool|int $network_level_or_blog_id
111 */
112 protected function __construct(
113 $id,
114 $title = '',
115 $module_unique_affix = '',
116 $is_network_and_blog_admins = false,
117 $network_level_or_blog_id = false
118 ) {
119 $this->_id = $id;
120 $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $this->_id . '_data', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
121 $this->_title = ! empty( $title ) ? $title : '';
122 $this->_module_unique_affix = $module_unique_affix;
123 $this->_sticky_storage = FS_Key_Value_Storage::instance( 'admin_notices', $this->_id, $network_level_or_blog_id );
124
125 if ( is_multisite() ) {
126 $this->_is_network_notices = ( true === $network_level_or_blog_id );
127
128 if ( is_numeric( $network_level_or_blog_id ) ) {
129 $this->_blog_id = $network_level_or_blog_id;
130 }
131 } else {
132 $this->_is_network_notices = false;
133 }
134
135 $is_network_admin = fs_is_network_admin();
136 $is_blog_admin = fs_is_blog_admin();
137
138 if ( ( $this->_is_network_notices && $is_network_admin ) ||
139 ( ! $this->_is_network_notices && $is_blog_admin ) ||
140 ( $is_network_and_blog_admins && ( $is_network_admin || $is_blog_admin ) )
141 ) {
142 if ( 0 < count( $this->_sticky_storage ) ) {
143 $ajax_action_suffix = str_replace( ':', '-', $this->_id );
144
145 // If there are sticky notices for the current slug, add a callback
146 // to the AJAX action that handles message dismiss.
147 add_action( "wp_ajax_fs_dismiss_notice_action_{$ajax_action_suffix}", array(
148 &$this,
149 'dismiss_notice_ajax_callback'
150 ) );
151
152 foreach ( $this->_sticky_storage as $msg ) {
153 // Add admin notice.
154 $this->add(
155 $msg['message'],
156 $msg['title'],
157 $msg['type'],
158 true,
159 $msg['id'],
160 false,
161 isset( $msg['wp_user_id'] ) ? $msg['wp_user_id'] : null,
162 ! empty( $msg['plugin'] ) ? $msg['plugin'] : null,
163 $is_network_and_blog_admins,
164 isset( $msg['dismissible'] ) ?
165 $msg['dismissible'] :
166 null
167 );
168 }
169 }
170 }
171 }
172
173 /**
174 * Remove sticky message by ID.
175 *
176 * @author Vova Feldman (@svovaf)
177 * @since 1.0.7
178 *
179 */
180 function dismiss_notice_ajax_callback() {
181 check_admin_referer( 'fs_dismiss_notice_action' );
182
183 if ( ! is_numeric( $_POST['message_id'] ) ) {
184 $this->_sticky_storage->remove( $_POST['message_id'] );
185 }
186
187 wp_die();
188 }
189
190 /**
191 * Rendered sticky message dismiss JavaScript.
192 *
193 * @author Vova Feldman (@svovaf)
194 * @since 1.0.7
195 */
196 static function _add_sticky_dismiss_javascript() {
197 $sticky_admin_notice_js_template_name = 'sticky-admin-notice-js.php';
198
199 if ( ! file_exists( fs_get_template_path( $sticky_admin_notice_js_template_name ) ) ) {
200 return;
201 }
202
203 $params = array();
204 fs_require_once_template( $sticky_admin_notice_js_template_name, $params );
205 }
206
207 private static $_added_sticky_javascript = false;
208
209 /**
210 * Hook to the admin_footer to add sticky message dismiss JavaScript handler.
211 *
212 * @author Vova Feldman (@svovaf)
213 * @since 1.0.7
214 */
215 private static function has_sticky_messages() {
216 if ( ! self::$_added_sticky_javascript ) {
217 add_action( 'admin_footer', array( 'FS_Admin_Notice_Manager', '_add_sticky_dismiss_javascript' ) );
218 }
219 }
220
221 /**
222 * Handle admin_notices by printing the admin messages stacked in the queue.
223 *
224 * @author Vova Feldman (@svovaf)
225 * @since 1.0.4
226 *
227 */
228 function _admin_notices_hook() {
229 if ( function_exists( 'current_user_can' ) &&
230 ! current_user_can( 'manage_options' )
231 ) {
232 // Only show messages to admins.
233 return;
234 }
235
236 foreach ( $this->_notices as $id => $msg ) {
237 if ( isset( $msg['wp_user_id'] ) && is_numeric( $msg['wp_user_id'] ) ) {
238 if ( get_current_user_id() != $msg['wp_user_id'] ) {
239 continue;
240 }
241 }
242
243 /**
244 * Added a filter to control the visibility of admin notices.
245 *
246 * Usage example:
247 *
248 * /**
249 * * @param bool $show
250 * * @param array $msg {
251 * * @var string $message The actual message.
252 * * @var string $title An optional message title.
253 * * @var string $type The type of the message ('success', 'update', 'warning', 'promotion').
254 * * @var string $id The unique identifier of the message.
255 * * @var string $manager_id The unique identifier of the notices manager. For plugins it would be the plugin's slug, for themes - `<slug>-theme`.
256 * * @var string $plugin The product's title.
257 * * @var string $wp_user_id An optional WP user ID that this admin notice is for.
258 * * }
259 * *
260 * * @return bool
261 * *\/
262 * function my_custom_show_admin_notice( $show, $msg ) {
263 * if ('trial_promotion' != $msg['id']) {
264 * return false;
265 * }
266 *
267 * return $show;
268 * }
269 *
270 * my_fs()->add_filter( 'show_admin_notice', 'my_custom_show_admin_notice', 10, 2 );
271 *
272 * @author Vova Feldman
273 * @since 2.2.0
274 */
275 $show_notice = call_user_func_array( 'fs_apply_filter', array(
276 $this->_module_unique_affix,
277 'show_admin_notice',
278 $this->show_admin_notices(),
279 $msg
280 ) );
281
282 if ( true !== $show_notice ) {
283 continue;
284 }
285
286 fs_require_template( 'admin-notice.php', $msg );
287
288 if ( $msg['sticky'] ) {
289 self::has_sticky_messages();
290 }
291 }
292 }
293
294 /**
295 * Enqueue common stylesheet to style admin notice.
296 *
297 * @author Vova Feldman (@svovaf)
298 * @since 1.0.7
299 */
300 function _enqueue_styles() {
301 fs_enqueue_local_style( 'fs_common', '/admin/common.css' );
302 }
303
304 /**
305 * Check if the current page is the Gutenberg block editor.
306 *
307 * @author Vova Feldman (@svovaf)
308 * @since 2.2.3
309 *
310 * @return bool
311 */
312 function is_gutenberg_page() {
313 if ( function_exists( 'is_gutenberg_page' ) &&
314 is_gutenberg_page()
315 ) {
316 // The Gutenberg plugin is on.
317 return true;
318 }
319
320 $current_screen = get_current_screen();
321
322 if ( method_exists( $current_screen, 'is_block_editor' ) &&
323 $current_screen->is_block_editor()
324 ) {
325 // Gutenberg page on 5+.
326 return true;
327 }
328
329 return false;
330 }
331
332 /**
333 * Check if admin notices should be shown on page. E.g., we don't want to show notices in the Visual Editor.
334 *
335 * @author Xiaheng Chen (@xhchen)
336 * @since 2.4.2
337 *
338 * @return bool
339 */
340 function show_admin_notices() {
341 global $pagenow;
342
343 if ( 'about.php' === $pagenow ) {
344 // Don't show admin notices on the About page.
345 return false;
346 }
347
348 if ( $this->is_gutenberg_page() ) {
349 // Don't show admin notices in Gutenberg (visual editor).
350 return false;
351 }
352
353 return true;
354 }
355
356 /**
357 * Add admin message to admin messages queue, and hook to admin_notices / all_admin_notices if not yet hooked.
358 *
359 * @author Vova Feldman (@svovaf)
360 * @since 1.0.4
361 *
362 * @param string $message
363 * @param string $title
364 * @param string $type
365 * @param bool $is_sticky
366 * @param string $id Message ID
367 * @param bool $store_if_sticky
368 * @param number|null $wp_user_id
369 * @param string|null $plugin_title
370 * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
371 * and blog admin pages.
372 * @param bool|null $is_dismissible
373 * @param array $data
374 *
375 * @uses add_action()
376 */
377 function add(
378 $message,
379 $title = '',
380 $type = 'success',
381 $is_sticky = false,
382 $id = '',
383 $store_if_sticky = true,
384 $wp_user_id = null,
385 $plugin_title = null,
386 $is_network_and_blog_admins = false,
387 $is_dismissible = null,
388 $data = array()
389 ) {
390 $notices_type = $this->get_notices_type();
391
392 if ( empty( $this->_notices ) ) {
393 if ( ! $is_network_and_blog_admins ) {
394 add_action( $notices_type, array( &$this, "_admin_notices_hook" ) );
395 } else {
396 add_action( 'network_admin_notices', array( &$this, "_admin_notices_hook" ) );
397 add_action( 'admin_notices', array( &$this, "_admin_notices_hook" ) );
398 }
399
400 add_action( 'admin_enqueue_scripts', array( &$this, '_enqueue_styles' ) );
401 }
402
403 if ( '' === $id ) {
404 $id = md5( $title . ' ' . $message . ' ' . $type );
405 }
406
407 $message_object = array(
408 'message' => $message,
409 'title' => $title,
410 'type' => $type,
411 'sticky' => $is_sticky,
412 'id' => $id,
413 'manager_id' => $this->_id,
414 'plugin' => ( ! is_null( $plugin_title ) ? $plugin_title : $this->_title ),
415 'wp_user_id' => $wp_user_id,
416 'dismissible' => $is_dismissible,
417 'data' => $data
418 );
419
420 if ( $is_sticky && $store_if_sticky ) {
421 $this->_sticky_storage->{$id} = $message_object;
422 }
423
424 $this->_notices[ $id ] = $message_object;
425 }
426
427 /**
428 * @author Vova Feldman (@svovaf)
429 * @since 1.0.7
430 *
431 * @param string|string[] $ids
432 * @param bool $store
433 */
434 function remove_sticky( $ids, $store = true ) {
435 if ( ! is_array( $ids ) ) {
436 $ids = array( $ids );
437 }
438
439 foreach ( $ids as $id ) {
440 // Remove from sticky storage.
441 $this->_sticky_storage->remove( $id, $store );
442
443 if ( isset( $this->_notices[ $id ] ) ) {
444 unset( $this->_notices[ $id ] );
445 }
446 }
447 }
448
449 /**
450 * Check if sticky message exists by id.
451 *
452 * @author Vova Feldman (@svovaf)
453 * @since 1.0.9
454 *
455 * @param $id
456 *
457 * @return bool
458 */
459 function has_sticky( $id ) {
460 return isset( $this->_sticky_storage[ $id ] );
461 }
462
463 /**
464 * Adds sticky admin notification.
465 *
466 * @author Vova Feldman (@svovaf)
467 * @since 1.0.7
468 *
469 * @param string $message
470 * @param string $id Message ID
471 * @param string $title
472 * @param string $type
473 * @param number|null $wp_user_id
474 * @param string|null $plugin_title
475 * @param bool $is_network_and_blog_admins Whether or not the message should be shown both on network
476 * and blog admin pages.
477 * @param bool $is_dimissible
478 * @param array $data
479 */
480 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() ) {
481 if ( ! empty( $this->_module_unique_affix ) ) {
482 $message = fs_apply_filter( $this->_module_unique_affix, "sticky_message_{$id}", $message );
483 $title = fs_apply_filter( $this->_module_unique_affix, "sticky_title_{$id}", $title );
484 }
485
486 $this->add( $message, $title, $type, true, $id, true, $wp_user_id, $plugin_title, $is_network_and_blog_admins, $is_dimissible, $data );
487 }
488
489 /**
490 * Retrieves the data of an sticky notice.
491 *
492 * @author Leo Fajardo (@leorw)
493 * @since 2.4.3
494 *
495 * @param string $id Message ID.
496 *
497 * @return array|null
498 */
499 function get_sticky( $id ) {
500 return isset( $this->_sticky_storage->{$id} ) ?
501 $this->_sticky_storage->{$id} :
502 null;
503 }
504
505 /**
506 * Clear all sticky messages.
507 *
508 * @author Vova Feldman (@svovaf)
509 * @since 1.0.8
510 *
511 * @param bool $is_temporary @since 2.5.1
512 */
513 function clear_all_sticky( $is_temporary = false ) {
514 if ( $is_temporary ) {
515 $this->_notices = array();
516 } else {
517 $this->_sticky_storage->clear_all();
518 }
519 }
520
521 #--------------------------------------------------------------------------------
522 #region Helper Method
523 #--------------------------------------------------------------------------------
524
525 /**
526 * @author Vova Feldman (@svovaf)
527 * @since 2.0.0
528 *
529 * @return string
530 */
531 private function get_notices_type() {
532 return $this->_is_network_notices ?
533 'network_admin_notices' :
534 'admin_notices';
535 }
536
537 #endregion
538 }
539