Completed
Push — master ( 335b91...5c3ec3 )
by Julien
02:21
created

Dismissible_Notices_Handler::dismissed_user()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
rs 9.4285
cc 2
eloc 5
nc 2
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( sprintf( 'The library can not be used because your version of WordPress is too old. You need version %s at least.', self::$instance->wordpress_version_required ) );
95
96
				return;
97
			}
98
99
			// Make sure PHP is compatible
100
			if ( ! self::$instance->is_php_compatible() ) {
101
				self::$instance->spit_error( sprintf( 'The library can not be used because your version of PHP is too old. You need version %s at least.', self::$instance->php_version_required ) );
102
103
				return;
104
			}
105
106
			self::$instance->includes();
107
108
			add_action( 'admin_notices', array( self::$instance, 'display' ) );
109
			add_action( 'admin_print_scripts', array( self::$instance, 'load_script' ) );
110
			add_action( 'wp_ajax_dnh_dismiss_notice', array( self::$instance, 'dismiss_notice_ajax' ) );
111
112
		}
113
114
		/**
115
		 * Check if the current WordPress version fits the requirements
116
		 *
117
		 * @since  1.0
118
		 * @return boolean
119
		 */
120
		private function is_wp_compatible() {
121
122
			if ( version_compare( get_bloginfo( 'version' ), self::$instance->wordpress_version_required, '<' ) ) {
123
				return false;
124
			}
125
126
			return true;
127
128
		}
129
130
		/**
131
		 * Check if the version of PHP is compatible with this library
132
		 *
133
		 * @since  1.0
134
		 * @return boolean
135
		 */
136
		private function is_php_compatible() {
137
138
			if ( version_compare( phpversion(), self::$instance->php_version_required, '<' ) ) {
139
				return false;
140
			}
141
142
			return true;
143
144
		}
145
146
		/**
147
		 * Include all our files
148
		 *
149
		 * @since 1.0
150
		 * @return void
151
		 */
152
		private function includes() {
153
			require( trailingslashit( plugin_dir_path( __FILE__ ) ) . 'includes/helper-functions.php' );
154
		}
155
156
		/**
157
		 * Load the script
158
		 *
159
		 * @since 1.0
160
		 * @return void
161
		 */
162
		public function load_script() {
163
			wp_register_script( 'dnh', trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/js/main.js', array( 'jquery' ), self::$instance->version, true );
164
			wp_enqueue_script( 'dnh' );
165
		}
166
167
		/**
168
		 * Display all the registered notices
169
		 *
170
		 * @since 1.0
171
		 * @return void
172
		 */
173
		public function display() {
174
175
			if ( is_null( self::$instance->notices ) || empty( self::$instance->notices ) ) {
176
				return;
177
			}
178
179
			foreach ( self::$instance->notices as $id => $notice ) {
180
181
				$id = self::$instance->get_id( $id );
182
183
				// Check if the notice was dismissed
184
				if ( self::$instance->is_dismissed( $id ) ) {
185
					continue;
186
				}
187
188
				// Check if the current user has required capability
189
				if ( ! empty( $notice['cap'] ) && ! current_user_can( $notice['cap'] ) ) {
190
					continue;
191
				}
192
193
				$class = array(
194
					'notice',
195
					$notice['type'],
196
					'is-dismissible',
197
					$notice['class'],
198
				);
199
200
				printf( '<div id="%3$s" class="%1$s"><p>%2$s</p></div>', trim( implode( ' ', $class ) ), $notice['content'], "dnh-$id" );
201
202
			}
203
204
		}
205
206
		/**
207
		 * Spits an error message at the top of the admin screen
208
		 *
209
		 * @since 1.0
210
		 *
211
		 * @param string $error Error message to spit
212
		 *
213
		 * @return void
214
		 */
215
		protected function spit_error( $error ) {
216
			printf( '<div style="margin: 20px; text-align: center;"><strong>%1$s</strong> %2$s</pre></div>', 'Dismissible Notices Handler Error:', wp_kses_post( $error ) );
217
		}
218
219
		/**
220
		 * Sanitize a notice ID and return it
221
		 *
222
		 * @since 1.0
223
		 *
224
		 * @param string $id
225
		 *
226
		 * @return string
227
		 */
228
		public function get_id( $id ) {
229
			return sanitize_key( $id );
230
		}
231
232
		/**
233
		 * Get available notice types
234
		 *
235
		 * @since 1.0
236
		 * @return array
237
		 */
238
		public function get_types() {
239
240
			$types = array(
241
				'error',
242
				'updated',
243
			);
244
245
			return apply_filters( 'dnh_notice_types', $types );
246
247
		}
248
249
		/**
250
		 * Get the default arguments for a notice
251
		 *
252
		 * @since 1.0
253
		 * @return array
254
		 */
255
		private function default_args() {
256
257
			$args = array(
258
				'screen' => '', // Coming soon
259
				'scope'  => 'user', // Scope of the dismissal. Either user or global
260
				'cap'    => '', // Required user capability
261
				'class'  => '', // Additional class to add to the notice
262
			);
263
264
			return apply_filters( 'dnh_default_args', $args );
265
266
		}
267
268
		/**
269
		 * Register a new notice
270
		 *
271
		 * @since 1.0
272
		 *
273
		 * @param string $id      Notice ID, used to identify it
274
		 * @param string $type    Type of notice to display
275
		 * @param string $content Notice content
276
		 * @param array  $args    Additional parameters
277
		 *
278
		 * @return bool
279
		 */
280
		public function register_notice( $id, $type, $content, $args = array() ) {
281
282
			if ( is_null( self::$instance->notices ) ) {
283
				self::$instance->notices = array();
284
			}
285
286
			$id      = self::$instance->get_id( $id );
287
			$type    = in_array( $t = sanitize_text_field( $type ), self::$instance->get_types() ) ? $t : 'updated';
288
			$content = wp_kses_post( $content );
289
			$args    = wp_parse_args( $args, self::$instance->default_args() );
290
291
			if ( array_key_exists( $id, self::$instance->notices ) ) {
292
293
				self::$instance->spit_error( sprintf( 'A notice with the ID %s has already been registered.', "<code>$id</code>" ) );
294
295
				return false;
296
			}
297
298
			$notice = array(
299
				'type'    => $type,
300
				'content' => $content,
301
			);
302
303
			$notice = array_merge( $notice, $args );
304
305
			self::$instance->notices[ $id ] = $notice;
306
307
			return true;
308
309
		}
310
311
		/**
312
		 * Notice dismissal triggered by Ajax
313
		 *
314
		 * @since 1.0
315
		 * @return void
316
		 */
317
		public function dismiss_notice_ajax() {
318
319
			if ( ! isset( $_POST['id'] ) ) {
320
				echo 0;
321
				exit;
322
			}
323
324
			if ( empty( $_POST['id'] ) || false === strpos( $_POST['id'], 'dnh-' ) ) {
325
				echo 0;
326
				exit;
327
			}
328
329
			$id = self::$instance->get_id( str_replace( 'dnh-', '', $_POST['id'] ) );
330
331
			echo self::$instance->dismiss_notice( $id );
332
			exit;
333
334
		}
335
336
		/**
337
		 * Dismiss a notice
338
		 *
339
		 * @since 1.0
340
		 *
341
		 * @param string $id ID of the notice to dismiss
342
		 *
343
		 * @return bool
344
		 */
345
		public function dismiss_notice( $id ) {
346
347
			$notice = self::$instance->get_notice( self::$instance->get_id( $id ) );
348
349
			if ( false === $notice ) {
350
				return false;
351
			}
352
353
			if ( self::$instance->is_dismissed( $id ) ) {
354
				return false;
355
			}
356
357
			return 'user' === $notice['scope'] ? self::$instance->dismiss_user( $id ) : self::$instance->dismiss_global( $id );
358
359
		}
360
361
		/**
362
		 * Dismiss notice for the current user
363
		 *
364
		 * @since 1.0
365
		 *
366
		 * @param string $id Notice ID
367
		 *
368
		 * @return int|bool
369
		 */
370
		private function dismiss_user( $id ) {
371
372
			$dismissed = self::$instance->dismissed_user();
373
374
			if ( in_array( $id, $dismissed ) ) {
375
				return false;
376
			}
377
378
			array_push( $dismissed, $id );
379
380
			return update_user_meta( get_current_user_id(), 'dnh_dismissed_notices', $dismissed );
381
382
		}
383
384
		/**
385
		 * Dismiss notice globally on the site
386
		 *
387
		 * @since 1.0
388
		 *
389
		 * @param string $id Notice ID
390
		 *
391
		 * @return bool
392
		 */
393
		private function dismiss_global( $id ) {
394
395
			$dismissed = self::$instance->dismissed_global();
396
397
			if ( in_array( $id, $dismissed ) ) {
398
				return false;
399
			}
400
401
			array_push( $dismissed, $id );
402
403
			return update_option( 'dnh_dismissed_notices', $dismissed );
404
405
		}
406
407
		/**
408
		 * Restore a dismissed notice
409
		 *
410
		 * @since 1.0
411
		 *
412
		 * @param string $id ID of the notice to restore
413
		 *
414
		 * @return bool
415
		 */
416
		public function restore_notice( $id ) {
417
418
			$id     = self::$instance->get_id( $id );
419
			$notice = self::$instance->get_notice( $id );
420
421
			if ( false === $notice ) {
422
				return false;
423
			}
424
425
			return 'user' === $notice['scope'] ? self::$instance->restore_user( $id ) : self::$instance->restore_global( $id );
426
427
		}
428
429
		/**
430
		 * Restore a notice dismissed by the current user
431
		 *
432
		 * @since 1.0
433
		 *
434
		 * @param string $id ID of the notice to restore
435
		 *
436
		 * @return bool
437
		 */
438 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...
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
			$dismissed = self::$instance->dismissed_user();
448
449
			if ( ! in_array( $id, $dismissed ) ) {
450
				return false;
451
			}
452
453
			$flip = array_flip( $dismissed );
454
			$key  = $flip[ $id ];
455
456
			unset( $dismissed[ $key ] );
457
458
			return update_user_meta( get_current_user_id(), 'dnh_dismissed_notices', $dismissed );
459
460
		}
461
462
		/**
463
		 * Restore a notice dismissed globally
464
		 *
465
		 * @since 1.0
466
		 *
467
		 * @param string $id ID of the notice to restore
468
		 *
469
		 * @return bool
470
		 */
471 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...
472
473
			$id     = self::$instance->get_id( $id );
474
			$notice = self::$instance->get_notice( $id );
475
476
			if ( false === $notice ) {
477
				return false;
478
			}
479
480
			$dismissed = self::$instance->dismissed_global();
481
482
			if ( ! in_array( $id, $dismissed ) ) {
483
				return false;
484
			}
485
486
			$flip = array_flip( $dismissed );
487
			$key  = $flip[ $id ];
488
489
			unset( $dismissed[ $key ] );
490
491
			return update_option( 'dnh_dismissed_notices', $dismissed );
492
493
		}
494
495
		/**
496
		 * Get all dismissed notices
497
		 *
498
		 * This includes notices dismissed globally or per user.
499
		 *
500
		 * @since 1.0
501
		 * @return array
502
		 */
503
		public function dismissed_notices() {
504
505
			$user   = self::$instance->dismissed_user();
506
			$global = self::$instance->dismissed_global();
507
508
			return array_merge( $user, $global );
509
510
		}
511
512
		/**
513
		 * Get user dismissed notices
514
		 *
515
		 * @since 1.0
516
		 * @return array
517
		 */
518
		private function dismissed_user() {
519
520
			$dismissed = get_user_meta( get_current_user_id(), 'dnh_dismissed_notices', true );
521
522
			if ( '' === $dismissed ) {
523
				$dismissed = array();
524
			}
525
526
			return $dismissed;
527
528
		}
529
530
		/**
531
		 * Get globally dismissed notices
532
		 *
533
		 * @since 1.0
534
		 * @return array
535
		 */
536
		private function dismissed_global() {
537
			return get_option( 'dnh_dismissed_notices', array() );
538
		}
539
540
		/**
541
		 * Check if a notice has been dismissed
542
		 *
543
		 * @since 1.0
544
		 *
545
		 * @param string $id Notice ID
546
		 *
547
		 * @return bool
548
		 */
549
		public function is_dismissed( $id ) {
550
551
			$dismissed = self::$instance->dismissed_notices();
552
553
			if ( ! in_array( self::$instance->get_id( $id ), $dismissed ) ) {
554
				return false;
555
			}
556
557
			return true;
558
559
		}
560
561
		/**
562
		 * Get all the registered notices
563
		 *
564
		 * @since 1.0
565
		 * @return array|null
566
		 */
567
		public function get_notices() {
568
			return self::$instance->notices;
569
		}
570
571
		/**
572
		 * Return a specific notice
573
		 *
574
		 * @since 1.0
575
		 *
576
		 * @param string $id Notice ID
577
		 *
578
		 * @return array|false
579
		 */
580
		public function get_notice( $id ) {
581
582
			$id = self::$instance->get_id( $id );
583
584
			if ( ! is_array( self::$instance->notices ) || ! array_key_exists( $id, self::$instance->notices ) ) {
585
				return false;
586
			}
587
588
			return self::$instance->notices[ $id ];
589
590
		}
591
592
	}
593
594
	function DNH() {
595
		return Dismissible_Notices_Handler::instance();
596
	}
597
598
}