Completed
Pull Request — master (#6)
by
unknown
01:28
created

handler.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Dismissible Notices Handler.
4
 *
5
 * This library is designed to handle dismissible admin notices.
6
 *
7
 * LICENSE: This program is free software; you can redistribute it and/or modify it under the terms of the GNU
8
 * General Public License as published by the Free Software Foundation; either version 3 of the License, or (at
9
 * your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY
10
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
 * General Public License for more details. You should have received a copy of the GNU General Public License along
12
 * with this program. If not, see <http://opensource.org/licenses/gpl-license.php>
13
 *
14
 * @package   Dismissible Notices Handler
15
 * @author    Julien Liabeuf <[email protected]>
16
 * @version   1.0
17
 * @license   GPL-2.0+
18
 * @link      https://julienliabeuf.com
19
 * @copyright 2016 Julien Liabeuf
20
 */
21
22
// If this file is called directly, abort.
23
if ( ! defined( 'WPINC' ) ) {
24
	die;
25
}
26
27
if ( ! class_exists( 'Dismissible_Notices_Handler' ) ) {
28
29
	final class Dismissible_Notices_Handler {
30
31
		/**
32
		 * @var Dismissible_Notices_Handler Holds the unique instance of the handler
33
		 * @since 1.0
34
		 */
35
		private static $instance;
36
37
		/**
38
		 * Library version
39
		 *
40
		 * @since 1.0
41
		 * @var string
42
		 */
43
		public $version = '1.0';
44
45
		/**
46
		 * Required version of PHP.
47
		 *
48
		 * @since 1.0
49
		 * @var string
50
		 */
51
		public $php_version_required = '5.5';
52
53
		/**
54
		 * Minimum version of WordPress required to use the library
55
		 *
56
		 * @since 1.0
57
		 * @var string
58
		 */
59
		public $wordpress_version_required = '4.2';
60
61
		/**
62
		 * @var array Holds all our registered notices
63
		 * @since 1.0
64
		 */
65
		private $notices;
66
67
		/**
68
		 * Instantiate and return the unique Dismissible_Notices_Handler object
69
		 *
70
		 * @since     1.0
71
		 * @return object Dismissible_Notices_Handler Unique instance of the handler
72
		 */
73
		public static function instance() {
74
75
			if ( ! isset( self::$instance ) && ! ( self::$instance instanceof Dismissible_Notices_Handler ) ) {
76
				self::$instance = new Dismissible_Notices_Handler;
77
				self::$instance->init();
78
			}
79
80
			return self::$instance;
81
82
		}
83
84
		/**
85
		 * Initialize the library
86
		 *
87
		 * @since 1.0
88
		 * @return void
89
		 */
90
		private function init() {
91
92
			// Make sure WordPress is compatible
93
			if ( ! self::$instance->is_wp_compatible() ) {
94
				self::$instance->spit_error(
95
					sprintf(
96
						/* translators: %s: required wordpress version */
97
						esc_html__( 'The library can not be used because your version of WordPress is too old. You need version %s at least.', 'wp-dismissible-notices-handler' ),
98
						self::$instance->wordpress_version_required
99
					)
100
				);
101
102
				return;
103
			}
104
105
			// Make sure PHP is compatible
106
			if ( ! self::$instance->is_php_compatible() ) {
107
				self::$instance->spit_error(
108
					sprintf(
109
						/* translators: %s: required php version */
110
						esc_html__( 'The library can not be used because your version of PHP is too old. You need version %s at least.', 'wp-dismissible-notices-handler' ),
111
						self::$instance->php_version_required
112
					)
113
				);
114
115
				return;
116
			}
117
118
			self::$instance->includes();
119
120
			add_action( 'admin_notices', array( self::$instance, 'display' ) );
121
			add_action( 'admin_print_scripts', array( self::$instance, 'load_script' ) );
122
			add_action( 'wp_ajax_dnh_dismiss_notice', array( self::$instance, 'dismiss_notice_ajax' ) );
123
124
		}
125
126
		/**
127
		 * Check if the current WordPress version fits the requirements
128
		 *
129
		 * @since  1.0
130
		 * @return boolean
131
		 */
132
		private function is_wp_compatible() {
133
134
			if ( version_compare( get_bloginfo( 'version' ), self::$instance->wordpress_version_required, '<' ) ) {
135
				return false;
136
			}
137
138
			return true;
139
140
		}
141
142
		/**
143
		 * Check if the version of PHP is compatible with this library
144
		 *
145
		 * @since  1.0
146
		 * @return boolean
147
		 */
148
		private function is_php_compatible() {
149
150
			if ( version_compare( phpversion(), self::$instance->php_version_required, '<' ) ) {
151
				return false;
152
			}
153
154
			return true;
155
156
		}
157
158
		/**
159
		 * Include all our files
160
		 *
161
		 * @since 1.0
162
		 * @return void
163
		 */
164
		private function includes() {
165
			require( trailingslashit( plugin_dir_path( __FILE__ ) ) . 'includes/helper-functions.php' );
166
		}
167
168
		/**
169
		 * Load the script
170
		 *
171
		 * @since 1.0
172
		 * @return void
173
		 */
174
		public function load_script() {
175
			wp_register_script( 'dnh', trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/js/main.js', array( 'jquery' ), self::$instance->version, true );
176
			wp_enqueue_script( 'dnh' );
177
		}
178
179
		/**
180
		 * Display all the registered notices
181
		 *
182
		 * @since 1.0
183
		 * @return void
184
		 */
185
		public function display() {
186
187
			if ( is_null( self::$instance->notices ) || empty( self::$instance->notices ) ) {
188
				return;
189
			}
190
191
			foreach ( self::$instance->notices as $id => $notice ) {
192
193
				$id = self::$instance->get_id( $id );
194
195
				// Check if the notice was dismissed
196
				if ( self::$instance->is_dismissed( $id ) ) {
197
					continue;
198
				}
199
200
				// Check if the current user has required capability
201
				if ( ! empty( $notice['cap'] ) && ! current_user_can( $notice['cap'] ) ) {
202
					continue;
203
				}
204
205
				$class = array(
206
					'notice',
207
					$notice['type'],
208
					'is-dismissible',
209
					$notice['class'],
210
				);
211
212
				printf( '<div id="%3$s" class="%1$s"><p>%2$s</p></div>', trim( implode( ' ', $class ) ), $notice['content'], "dnh-$id" );
213
214
			}
215
216
		}
217
218
		/**
219
		 * Spits an error message at the top of the admin screen
220
		 *
221
		 * @since 1.0
222
		 *
223
		 * @param string $error Error message to spit
224
		 *
225
		 * @return void
226
		 */
227
		protected function spit_error( $error ) {
228
			printf(
229
				'<div style="margin: 20px; text-align: center;"><strong>%1$s</strong> %2$s</pre></div>',
230
				esc_html__( 'Dismissible Notices Handler Error:', 'wp-dismissible-notices-handler' ),
231
				wp_kses_post( $error )
232
			);
233
		}
234
235
		/**
236
		 * Sanitize a notice ID and return it
237
		 *
238
		 * @since 1.0
239
		 *
240
		 * @param string $id
241
		 *
242
		 * @return string
243
		 */
244
		public function get_id( $id ) {
245
			return sanitize_key( $id );
246
		}
247
248
		/**
249
		 * Get available notice types
250
		 *
251
		 * @since 1.0
252
		 * @return array
253
		 */
254
		public function get_types() {
255
256
			$types = array(
257
				'error',
258
				'updated',
259
				'notice-warning'
260
			);
261
262
			return apply_filters( 'dnh_notice_types', $types );
263
264
		}
265
266
		/**
267
		 * Get the default arguments for a notice
268
		 *
269
		 * @since 1.0
270
		 * @return array
271
		 */
272
		private function default_args() {
273
274
			$args = array(
275
				'screen' => '', // Coming soon
276
				'scope'  => 'user', // Scope of the dismissal. Either user or global
277
				'cap'    => '', // Required user capability
278
				'class'  => '', // Additional class to add to the notice
279
			);
280
281
			return apply_filters( 'dnh_default_args', $args );
282
283
		}
284
285
		/**
286
		 * Register a new notice
287
		 *
288
		 * @since 1.0
289
		 *
290
		 * @param string $id      Notice ID, used to identify it
291
		 * @param string $type    Type of notice to display
292
		 * @param string $content Notice content
293
		 * @param array  $args    Additional parameters
294
		 *
295
		 * @return bool
296
		 */
297
		public function register_notice( $id, $type, $content, $args = array() ) {
298
299
			if ( is_null( self::$instance->notices ) ) {
300
				self::$instance->notices = array();
301
			}
302
303
			$id      = self::$instance->get_id( $id );
304
			$type    = in_array( $t = sanitize_text_field( $type ), self::$instance->get_types() ) ? $t : 'updated';
305
			$content = wp_kses_post( $content );
306
			$args    = wp_parse_args( $args, self::$instance->default_args() );
307
308
			if ( array_key_exists( $id, self::$instance->notices ) ) {
309
310
				self::$instance->spit_error(
311
					sprintf(
312
						/* translators: %s: required php version */
313
						esc_html__( 'A notice with the ID %s has already been registered.', 'wp-dismissible-notices-handler' ),
314
						"<code>$id</code>"
315
					)
316
				);
317
318
				return false;
319
			}
320
321
			$notice = array(
322
				'type'    => $type,
323
				'content' => $content,
324
			);
325
326
			$notice = array_merge( $notice, $args );
327
328
			self::$instance->notices[ $id ] = $notice;
329
330
			return true;
331
332
		}
333
334
		/**
335
		 * Notice dismissal triggered by Ajax
336
		 *
337
		 * @since 1.0
338
		 * @return void
339
		 */
340
		public function dismiss_notice_ajax() {
341
342
			if ( ! isset( $_POST['id'] ) ) {
343
				echo 0;
344
				exit;
345
			}
346
347
			if ( empty( $_POST['id'] ) || false === strpos( $_POST['id'], 'dnh-' ) ) {
348
				echo 0;
349
				exit;
350
			}
351
352
			$id = self::$instance->get_id( str_replace( 'dnh-', '', $_POST['id'] ) );
353
354
			echo self::$instance->dismiss_notice( $id );
355
			exit;
356
357
		}
358
359
		/**
360
		 * Dismiss a notice
361
		 *
362
		 * @since 1.0
363
		 *
364
		 * @param string $id ID of the notice to dismiss
365
		 *
366
		 * @return bool
367
		 */
368
		public function dismiss_notice( $id ) {
369
370
			$notice = self::$instance->get_notice( self::$instance->get_id( $id ) );
371
372
			if ( false === $notice ) {
373
				return false;
374
			}
375
376
			if ( self::$instance->is_dismissed( $id ) ) {
377
				return false;
378
			}
379
380
			return 'user' === $notice['scope'] ? self::$instance->dismiss_user( $id ) : self::$instance->dismiss_global( $id );
381
382
		}
383
384
		/**
385
		 * Dismiss notice for the current user
386
		 *
387
		 * @since 1.0
388
		 *
389
		 * @param string $id Notice ID
390
		 *
391
		 * @return int|bool
392
		 */
393
		private function dismiss_user( $id ) {
394
395
			$dismissed = self::$instance->dismissed_user();
396
397
			if ( in_array( $id, $dismissed ) ) {
398
				return false;
399
			}
400
401
			array_push( $dismissed, $id );
402
403
			return update_user_meta( get_current_user_id(), 'dnh_dismissed_notices', $dismissed );
404
405
		}
406
407
		/**
408
		 * Dismiss notice globally on the site
409
		 *
410
		 * @since 1.0
411
		 *
412
		 * @param string $id Notice ID
413
		 *
414
		 * @return bool
415
		 */
416
		private function dismiss_global( $id ) {
417
418
			$dismissed = self::$instance->dismissed_global();
419
420
			if ( in_array( $id, $dismissed ) ) {
421
				return false;
422
			}
423
424
			array_push( $dismissed, $id );
425
426
			return update_option( 'dnh_dismissed_notices', $dismissed );
427
428
		}
429
430
		/**
431
		 * Restore a dismissed notice
432
		 *
433
		 * @since 1.0
434
		 *
435
		 * @param string $id ID of the notice to restore
436
		 *
437
		 * @return bool
438
		 */
439
		public function restore_notice( $id ) {
440
441
			$id     = self::$instance->get_id( $id );
442
			$notice = self::$instance->get_notice( $id );
443
444
			if ( false === $notice ) {
445
				return false;
446
			}
447
448
			return 'user' === $notice['scope'] ? self::$instance->restore_user( $id ) : self::$instance->restore_global( $id );
449
450
		}
451
452
		/**
453
		 * Restore a notice dismissed by the current user
454
		 *
455
		 * @since 1.0
456
		 *
457
		 * @param string $id ID of the notice to restore
458
		 *
459
		 * @return bool
460
		 */
461 View Code Duplication
		private function restore_user( $id ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
462
463
			$id     = self::$instance->get_id( $id );
464
			$notice = self::$instance->get_notice( $id );
465
466
			if ( false === $notice ) {
467
				return false;
468
			}
469
470
			$dismissed = self::$instance->dismissed_user();
471
472
			if ( ! in_array( $id, $dismissed ) ) {
473
				return false;
474
			}
475
476
			$flip = array_flip( $dismissed );
477
			$key  = $flip[ $id ];
478
479
			unset( $dismissed[ $key ] );
480
481
			return update_user_meta( get_current_user_id(), 'dnh_dismissed_notices', $dismissed );
482
483
		}
484
485
		/**
486
		 * Restore a notice dismissed globally
487
		 *
488
		 * @since 1.0
489
		 *
490
		 * @param string $id ID of the notice to restore
491
		 *
492
		 * @return bool
493
		 */
494 View Code Duplication
		private function restore_global( $id ) {
0 ignored issues
show
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
495
496
			$id     = self::$instance->get_id( $id );
497
			$notice = self::$instance->get_notice( $id );
498
499
			if ( false === $notice ) {
500
				return false;
501
			}
502
503
			$dismissed = self::$instance->dismissed_global();
504
505
			if ( ! in_array( $id, $dismissed ) ) {
506
				return false;
507
			}
508
509
			$flip = array_flip( $dismissed );
510
			$key  = $flip[ $id ];
511
512
			unset( $dismissed[ $key ] );
513
514
			return update_option( 'dnh_dismissed_notices', $dismissed );
515
516
		}
517
518
		/**
519
		 * Get all dismissed notices
520
		 *
521
		 * This includes notices dismissed globally or per user.
522
		 *
523
		 * @since 1.0
524
		 * @return array
525
		 */
526
		public function dismissed_notices() {
527
528
			$user   = self::$instance->dismissed_user();
529
			$global = self::$instance->dismissed_global();
530
531
			return array_merge( $user, $global );
532
533
		}
534
535
		/**
536
		 * Get user dismissed notices
537
		 *
538
		 * @since 1.0
539
		 * @return array
540
		 */
541
		private function dismissed_user() {
542
543
			$dismissed = get_user_meta( get_current_user_id(), 'dnh_dismissed_notices', true );
544
545
			if ( '' === $dismissed ) {
546
				$dismissed = array();
547
			}
548
549
			return $dismissed;
550
551
		}
552
553
		/**
554
		 * Get globally dismissed notices
555
		 *
556
		 * @since 1.0
557
		 * @return array
558
		 */
559
		private function dismissed_global() {
560
			return get_option( 'dnh_dismissed_notices', array() );
561
		}
562
563
		/**
564
		 * Check if a notice has been dismissed
565
		 *
566
		 * @since 1.0
567
		 *
568
		 * @param string $id Notice ID
569
		 *
570
		 * @return bool
571
		 */
572
		public function is_dismissed( $id ) {
573
574
			$dismissed = self::$instance->dismissed_notices();
575
576
			if ( ! in_array( self::$instance->get_id( $id ), $dismissed ) ) {
577
				return false;
578
			}
579
580
			return true;
581
582
		}
583
584
		/**
585
		 * Get all the registered notices
586
		 *
587
		 * @since 1.0
588
		 * @return array|null
589
		 */
590
		public function get_notices() {
591
			return self::$instance->notices;
592
		}
593
594
		/**
595
		 * Return a specific notice
596
		 *
597
		 * @since 1.0
598
		 *
599
		 * @param string $id Notice ID
600
		 *
601
		 * @return array|false
602
		 */
603
		public function get_notice( $id ) {
604
605
			$id = self::$instance->get_id( $id );
606
607
			if ( ! is_array( self::$instance->notices ) || ! array_key_exists( $id, self::$instance->notices ) ) {
608
				return false;
609
			}
610
611
			return self::$instance->notices[ $id ];
612
613
		}
614
615
	}
616
617
	/**
618
	 * The main function responsible for returning the unique Dismissible Notices Handler instance
619
	 *
620
	 * Use this function like you would a global variable, except without needing
621
	 * to declare the global.
622
	 *
623
	 * @since 1.0
624
	 * @return object Dismissible_Notices_Handler
625
	 */
626
	function DNH() {
627
		return Dismissible_Notices_Handler::instance();
628
	}
629
630
	/**
631
	 * Get the library running
632
	 */
633
	DNH();
634
635
}
636