Completed
Pull Request — master (#1)
by
unknown
03:05
created

Dismissible_Notices_Handler::init()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 35
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 2
Metric Value
c 3
b 0
f 2
dl 0
loc 35
rs 8.8571
cc 3
eloc 17
nc 3
nop 0
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
						__( '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
						__( '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
				__( '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
			);
260
261
			return apply_filters( 'dnh_notice_types', $types );
262
263
		}
264
265
		/**
266
		 * Get the default arguments for a notice
267
		 *
268
		 * @since 1.0
269
		 * @return array
270
		 */
271
		private function default_args() {
272
273
			$args = array(
274
				'screen' => '', // Coming soon
275
				'scope'  => 'user', // Scope of the dismissal. Either user or global
276
				'cap'    => '', // Required user capability
277
				'class'  => '', // Additional class to add to the notice
278
			);
279
280
			return apply_filters( 'dnh_default_args', $args );
281
282
		}
283
284
		/**
285
		 * Register a new notice
286
		 *
287
		 * @since 1.0
288
		 *
289
		 * @param string $id      Notice ID, used to identify it
290
		 * @param string $type    Type of notice to display
291
		 * @param string $content Notice content
292
		 * @param array  $args    Additional parameters
293
		 *
294
		 * @return bool
295
		 */
296
		public function register_notice( $id, $type, $content, $args = array() ) {
297
298
			if ( is_null( self::$instance->notices ) ) {
299
				self::$instance->notices = array();
300
			}
301
302
			$id      = self::$instance->get_id( $id );
303
			$type    = in_array( $t = sanitize_text_field( $type ), self::$instance->get_types() ) ? $t : 'updated';
304
			$content = wp_kses_post( $content );
305
			$args    = wp_parse_args( $args, self::$instance->default_args() );
306
307
			if ( array_key_exists( $id, self::$instance->notices ) ) {
308
309
				self::$instance->spit_error(
310
					sprintf(
311
						/* translators: %s: required php version */
312
						__( 'A notice with the ID %s has already been registered.', 'wp-dismissible-notices-handler' ),
313
						"<code>$id</code>"
314
					)
315
				);
316
317
				return false;
318
			}
319
320
			$notice = array(
321
				'type'    => $type,
322
				'content' => $content,
323
			);
324
325
			$notice = array_merge( $notice, $args );
326
327
			self::$instance->notices[ $id ] = $notice;
328
329
			return true;
330
331
		}
332
333
		/**
334
		 * Notice dismissal triggered by Ajax
335
		 *
336
		 * @since 1.0
337
		 * @return void
338
		 */
339
		public function dismiss_notice_ajax() {
340
341
			if ( ! isset( $_POST['id'] ) ) {
342
				echo 0;
343
				exit;
344
			}
345
346
			if ( empty( $_POST['id'] ) || false === strpos( $_POST['id'], 'dnh-' ) ) {
347
				echo 0;
348
				exit;
349
			}
350
351
			$id = self::$instance->get_id( str_replace( 'dnh-', '', $_POST['id'] ) );
352
353
			echo self::$instance->dismiss_notice( $id );
354
			exit;
355
356
		}
357
358
		/**
359
		 * Dismiss a notice
360
		 *
361
		 * @since 1.0
362
		 *
363
		 * @param string $id ID of the notice to dismiss
364
		 *
365
		 * @return bool
366
		 */
367
		public function dismiss_notice( $id ) {
368
369
			$notice = self::$instance->get_notice( self::$instance->get_id( $id ) );
370
371
			if ( false === $notice ) {
372
				return false;
373
			}
374
375
			if ( self::$instance->is_dismissed( $id ) ) {
376
				return false;
377
			}
378
379
			return 'user' === $notice['scope'] ? self::$instance->dismiss_user( $id ) : self::$instance->dismiss_global( $id );
380
381
		}
382
383
		/**
384
		 * Dismiss notice for the current user
385
		 *
386
		 * @since 1.0
387
		 *
388
		 * @param string $id Notice ID
389
		 *
390
		 * @return int|bool
391
		 */
392
		private function dismiss_user( $id ) {
393
394
			$dismissed = self::$instance->dismissed_user();
395
396
			if ( in_array( $id, $dismissed ) ) {
397
				return false;
398
			}
399
400
			array_push( $dismissed, $id );
401
402
			return update_user_meta( get_current_user_id(), 'dnh_dismissed_notices', $dismissed );
403
404
		}
405
406
		/**
407
		 * Dismiss notice globally on the site
408
		 *
409
		 * @since 1.0
410
		 *
411
		 * @param string $id Notice ID
412
		 *
413
		 * @return bool
414
		 */
415
		private function dismiss_global( $id ) {
416
417
			$dismissed = self::$instance->dismissed_global();
418
419
			if ( in_array( $id, $dismissed ) ) {
420
				return false;
421
			}
422
423
			array_push( $dismissed, $id );
424
425
			return update_option( 'dnh_dismissed_notices', $dismissed );
426
427
		}
428
429
		/**
430
		 * Restore a dismissed notice
431
		 *
432
		 * @since 1.0
433
		 *
434
		 * @param string $id ID of the notice to restore
435
		 *
436
		 * @return bool
437
		 */
438
		public function restore_notice( $id ) {
439
440
			$id     = self::$instance->get_id( $id );
441
			$notice = self::$instance->get_notice( $id );
442
443
			if ( false === $notice ) {
444
				return false;
445
			}
446
447
			return 'user' === $notice['scope'] ? self::$instance->restore_user( $id ) : self::$instance->restore_global( $id );
448
449
		}
450
451
		/**
452
		 * Restore a notice dismissed by the current user
453
		 *
454
		 * @since 1.0
455
		 *
456
		 * @param string $id ID of the notice to restore
457
		 *
458
		 * @return bool
459
		 */
460 View Code Duplication
		private function restore_user( $id ) {
0 ignored issues
show
Duplication introduced by
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...
461
462
			$id     = self::$instance->get_id( $id );
463
			$notice = self::$instance->get_notice( $id );
464
465
			if ( false === $notice ) {
466
				return false;
467
			}
468
469
			$dismissed = self::$instance->dismissed_user();
470
471
			if ( ! in_array( $id, $dismissed ) ) {
472
				return false;
473
			}
474
475
			$flip = array_flip( $dismissed );
476
			$key  = $flip[ $id ];
477
478
			unset( $dismissed[ $key ] );
479
480
			return update_user_meta( get_current_user_id(), 'dnh_dismissed_notices', $dismissed );
481
482
		}
483
484
		/**
485
		 * Restore a notice dismissed globally
486
		 *
487
		 * @since 1.0
488
		 *
489
		 * @param string $id ID of the notice to restore
490
		 *
491
		 * @return bool
492
		 */
493 View Code Duplication
		private function restore_global( $id ) {
0 ignored issues
show
Duplication introduced by
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...
494
495
			$id     = self::$instance->get_id( $id );
496
			$notice = self::$instance->get_notice( $id );
497
498
			if ( false === $notice ) {
499
				return false;
500
			}
501
502
			$dismissed = self::$instance->dismissed_global();
503
504
			if ( ! in_array( $id, $dismissed ) ) {
505
				return false;
506
			}
507
508
			$flip = array_flip( $dismissed );
509
			$key  = $flip[ $id ];
510
511
			unset( $dismissed[ $key ] );
512
513
			return update_option( 'dnh_dismissed_notices', $dismissed );
514
515
		}
516
517
		/**
518
		 * Get all dismissed notices
519
		 *
520
		 * This includes notices dismissed globally or per user.
521
		 *
522
		 * @since 1.0
523
		 * @return array
524
		 */
525
		public function dismissed_notices() {
526
527
			$user   = self::$instance->dismissed_user();
528
			$global = self::$instance->dismissed_global();
529
530
			return array_merge( $user, $global );
531
532
		}
533
534
		/**
535
		 * Get user dismissed notices
536
		 *
537
		 * @since 1.0
538
		 * @return array
539
		 */
540
		private function dismissed_user() {
541
542
			$dismissed = get_user_meta( get_current_user_id(), 'dnh_dismissed_notices', true );
543
544
			if ( '' === $dismissed ) {
545
				$dismissed = array();
546
			}
547
548
			return $dismissed;
549
550
		}
551
552
		/**
553
		 * Get globally dismissed notices
554
		 *
555
		 * @since 1.0
556
		 * @return array
557
		 */
558
		private function dismissed_global() {
559
			return get_option( 'dnh_dismissed_notices', array() );
560
		}
561
562
		/**
563
		 * Check if a notice has been dismissed
564
		 *
565
		 * @since 1.0
566
		 *
567
		 * @param string $id Notice ID
568
		 *
569
		 * @return bool
570
		 */
571
		public function is_dismissed( $id ) {
572
573
			$dismissed = self::$instance->dismissed_notices();
574
575
			if ( ! in_array( self::$instance->get_id( $id ), $dismissed ) ) {
576
				return false;
577
			}
578
579
			return true;
580
581
		}
582
583
		/**
584
		 * Get all the registered notices
585
		 *
586
		 * @since 1.0
587
		 * @return array|null
588
		 */
589
		public function get_notices() {
590
			return self::$instance->notices;
591
		}
592
593
		/**
594
		 * Return a specific notice
595
		 *
596
		 * @since 1.0
597
		 *
598
		 * @param string $id Notice ID
599
		 *
600
		 * @return array|false
601
		 */
602
		public function get_notice( $id ) {
603
604
			$id = self::$instance->get_id( $id );
605
606
			if ( ! is_array( self::$instance->notices ) || ! array_key_exists( $id, self::$instance->notices ) ) {
607
				return false;
608
			}
609
610
			return self::$instance->notices[ $id ];
611
612
		}
613
614
	}
615
616
	/**
617
	 * The main function responsible for returning the unique Dismissible Notices Handler instance
618
	 *
619
	 * Use this function like you would a global variable, except without needing
620
	 * to declare the global.
621
	 *
622
	 * @since 1.0
623
	 * @return object Dismissible_Notices_Handler
624
	 */
625
	function DNH() {
626
		return Dismissible_Notices_Handler::instance();
627
	}
628
629
	/**
630
	 * Get the library running
631
	 */
632
	DNH();
633
634
}
635