Completed
Pull Request — master (#1854)
by Devin
10:39
created

Give_Notices::get_notice_key()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 4
nop 3
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 22 and the first side effect is on line 14.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * Admin Notices Class.
4
 *
5
 * @package     Give
6
 * @subpackage  Admin/Notices
7
 * @copyright   Copyright (c) 2016, WordImpress
8
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
9
 * @since       1.0
10
 */
11
12
// Exit if accessed directly.
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * Give_Notices Class
19
 *
20
 * @since 1.0
21
 */
22
class Give_Notices {
23
	/**
24
	 * List of notices
25
	 * @var array
26
	 * @since  1.8
27
	 * @access private
28
	 */
29
	private static $notices = array();
30
31
32
	/**
33
	 * Flag to check if any notice auto dismissible among all notices
34
	 *
35
	 * @since  1.8.9
36
	 * @access private
37
	 * @var bool
38
	 */
39
	private static $has_auto_dismissible_notice = false;
40
41
	/**
42
	 * Flag to check if any notice has dismiss interval among all notices
43
	 *
44
	 * @since  1.8.9
45
	 * @access private
46
	 * @var bool
47
	 */
48
	private static $has_dismiss_interval_notice = false;
49
50
	/**
51
	 * Get things started.
52
	 *
53
	 * @since 1.0
54
	 */
55
	public function __construct() {
56
		add_action( 'admin_notices', array( $this, 'render_admin_notices' ), 999 );
57
		add_action( 'give_dismiss_notices', array( $this, 'dismiss_notices' ) );
58
59
		add_action( 'give_frontend_notices', array( $this, 'render_frontend_notices' ), 999 );
60
		add_action( 'give_donation_form_before_personal_info', array( $this, 'render_frontend_notices' ) );
61
		add_action( 'give_ajax_donation_errors', array( $this, 'render_frontend_notices' ) );
62
	}
63
64
	/**
65
	 * Register notice.
66
	 *
67
	 * @since  1.8.9
68
	 * @access public
69
	 *
70
	 * @param $notice_args
71
	 *
72
	 * @return bool
73
	 */
74
	public function register_notice( $notice_args ) {
75
		// Bailout.
76
		if ( empty( $notice_args['id'] ) || array_key_exists( $notice_args['id'], self::$notices ) ) {
77
			return false;
78
		}
79
80
		$notice_args = wp_parse_args(
81
			$notice_args,
82
			array(
83
				'id'                    => '',
84
				'description'           => '',
85
				'auto_dismissible'      => false,
86
87
				// Value: error/warning/success/info/updated
88
				'type'                  => 'error',
89
90
				// Value: null/user/all
91
				'dismissible_type'      => null,
92
93
				// Value: shortly/permanent/null/custom
94
				'dismiss_interval'      => null,
95
96
				// Only set it when custom is defined.
97
				'dismiss_interval_time' => null,
98
99
			)
100
		);
101
102
		// Set extra dismiss links if any.
103
		if ( false !== strpos( $notice_args['description'], 'data-dismiss-interval' ) ) {
104
105
			preg_match_all( "/data-([^\"]*)=\"([^\"]*)\"/", $notice_args['description'], $extra_notice_dismiss_link );
106
107
			if ( ! empty( $extra_notice_dismiss_link ) ) {
108
				$extra_notice_dismiss_links = array_chunk( current( $extra_notice_dismiss_link ), 3 );
109
				foreach ( $extra_notice_dismiss_links as $extra_notice_dismiss_link ) {
110
					// Create array og key ==> value by parsing query string created after renaming data attributes.
111
					$data_attribute_query_str = str_replace( array( 'data-', '-', '"' ), array(
112
						'',
113
						'_',
114
						'',
115
					), implode( '&', $extra_notice_dismiss_link ) );
116
117
					$notice_args['extra_links'][] = wp_parse_args( $data_attribute_query_str );
118
				}
119
			}
120
		}
121
122
123
		self::$notices[ $notice_args['id'] ] = $notice_args;
124
125
		// Auto set show param if not already set.
126
		if ( ! isset( self::$notices[ $notice_args['id'] ]['show'] ) ) {
127
			self::$notices[ $notice_args['id'] ]['show'] = $this->is_notice_dismissed( $notice_args ) ? false : true;
128
		}
129
130
		// Auto set time interval for shortly.
131
		if ( 'shortly' === self::$notices[ $notice_args['id'] ]['dismiss_interval'] ) {
132
			self::$notices[ $notice_args['id'] ]['dismiss_interval_time'] = DAY_IN_SECONDS;
133
		}
134
135
		return true;
136
	}
137
138
	/**
139
	 * Display notice.
140
	 *
141
	 * @since 1.8.9
142
	 *
143
	 */
144
	public function render_admin_notices() {
145
		// Bailout.
146
		if ( empty( self::$notices ) ) {
147
			return;
148
		}
149
150
		$output = '';
151
152
		foreach ( self::$notices as $notice_id => $notice ) {
153
			// Check flag set to true to show notice.
154
			if ( ! $notice['show'] ) {
155
				continue;
156
			}
157
158
			// Check if notice dismissible or not.
159
			if ( ! self::$has_auto_dismissible_notice ) {
160
				self::$has_auto_dismissible_notice = $notice['auto_dismissible'];
161
			}
162
163
			// Check if notice dismissible or not.
164
			if ( ! self::$has_dismiss_interval_notice ) {
165
				self::$has_dismiss_interval_notice = $notice['dismiss_interval'];
166
			}
167
168
			$css_id = ( false === strpos( $notice['id'], 'give' ) ? "give-{$notice['id']}" : $notice['id'] );
169
170
			$css_class = "give-notice notice is-dismissible {$notice['type']} notice-{$notice['type']}";
171
			$output    .= sprintf(
172
				'<div id="%1$s" class="%2$s" data-auto-dismissible="%3$s" data-dismissible-type="%4$s" data-dismiss-interval="%5$s" data-notice-id="%6$s" data-security="%7$s" data-dismiss-interval-time="%8$s">' . " \n",
173
				$css_id,
174
				$css_class,
175
				$notice['auto_dismissible'],
176
				$notice['dismissible_type'],
177
				$notice['dismiss_interval'],
178
				$notice['id'],
179
				empty( $notice['dismissible_type'] ) ? '' : wp_create_nonce( "give_edit_{$notice_id}_notice" ),
180
				$notice['dismiss_interval_time']
181
			);
182
183
			$output .= ( 0 === strpos( $notice['description'], '<div' ) || 0 === strpos( $notice['description'], '<p' ) ? $notice['description'] : "<p>{$notice['description']}</p>" );
184
			$output .= "</div> \n";
185
		}
186
187
		echo $output;
188
189
		$this->print_js();
190
	}
191
192
193
	/**
194
	 * Render give frontend notices.
195
	 *
196
	 * @since  1.8.9
197
	 * @access public
198
	 *
199
	 * @param int $form_id
200
	 */
201
	public function render_frontend_notices( $form_id = 0 ) {
202
		$errors = give_get_errors();
203
204
		$request_form_id = isset( $_REQUEST['form-id'] ) ? intval( $_REQUEST['form-id'] ) : 0;
205
206
		// Sanity checks first: Ensure that gateway returned errors display on the appropriate form.
207
		if ( ! isset( $_POST['give_ajax'] ) && $request_form_id !== $form_id ) {
208
			return;
209
		}
210
211
		if ( $errors ) {
212
			self::print_frontend_errors( $errors );
213
214
			give_clear_errors();
215
		}
216
	}
217
218
	/**
219
	 * Print notice js.
220
	 *
221
	 * @since  1.8.9
222
	 * @access private
223
	 */
224
	private function print_js() {
225
		if ( self::$has_auto_dismissible_notice ) :
226
			?>
227
			<script>
228
				jQuery(document).ready(function () {
229
					// auto hide setting message in 5 seconds.
230
					window.setTimeout(
231
						function () {
232
							jQuery('.give-notice[data-auto-dismissible="1"]').slideUp();
233
						},
234
						5000
235
					);
236
				})
237
			</script>
238
			<?php
239
		endif;
240
241
		if ( self::$has_dismiss_interval_notice ) :
242
			?>
243
			<script>
244
				jQuery(document).ready(function () {
245
					var $body = jQuery('body');
246
247
					$body.on('click', '.give_dismiss_notice', function (e) {
248
						var $parent            = jQuery(this).parents('.give-notice'),
249
							custom_notice_data = {
250
								'dismissible_type'     : jQuery(this).data('dismissible-type'),
251
								'dismiss_interval'     : jQuery(this).data('dismiss-interval'),
252
								'dismiss_interval_time': jQuery(this).data('dismiss-interval-time')
253
							};
254
255
						$parent.find('button.notice-dismiss').trigger('click', [custom_notice_data]);
256
						return false;
257
					});
258
259
					$body.on('click', 'button.notice-dismiss', function (e, custom_notice_data) {
260
						var $parent            = jQuery(this).parents('.give-notice'),
261
							custom_notice_data = custom_notice_data || {};
262
263
						e.preventDefault();
264
265
						var data = {
266
							'give-action'          : 'dismiss_notices',
267
							'notice_id'            : $parent.data('notice-id'),
268
							'dismissible_type'     : $parent.data('dismissible-type'),
269
							'dismiss_interval'     : $parent.data('dismiss-interval'),
270
							'dismiss_interval_time': $parent.data('dismiss-interval-time'),
271
							'_wpnonce'             : $parent.data('security')
272
						};
273
274
						if (Object.keys(custom_notice_data).length) {
275
							jQuery.extend(data, custom_notice_data);
276
						}
277
278
						// Bailout.
279
						if (
280
							!data.dismiss_interval ||
281
							!data.dismissible_type
282
						) {
283
							return false;
284
						}
285
286
						jQuery.post(
287
							'<?php echo admin_url(); ?>admin-ajax.php',
288
							data,
289
							function (response) {
290
291
							})
292
					})
293
				});
294
			</script>
295
			<?php
296
		endif;
297
	}
298
299
300
	/**
301
	 * Hide notice.
302
	 *
303
	 * @since  1.8.9
304
	 * @access public
305
	 */
306
	public function dismiss_notices() {
307
		$_post     = give_clean( $_POST );
308
		$notice_id = esc_attr( $_post['notice_id'] );
309
310
		// Bailout.
311
		if (
312
			empty( $notice_id ) ||
313
			empty( $_post['dismissible_type'] ) ||
314
			empty( $_post['dismiss_interval'] ) ||
315
			! check_ajax_referer( "give_edit_{$notice_id}_notice", '_wpnonce' )
316
		) {
317
			wp_send_json_error();
318
		}
319
320
		$notice_key = Give()->notices->get_notice_key( $notice_id, $_post['dismiss_interval'] );
321
		if ( 'user' === $_post['dismissible_type'] ) {
322
			$current_user = wp_get_current_user();
323
			$notice_key   = Give()->notices->get_notice_key( $notice_id, $_post['dismiss_interval'], $current_user->ID );
324
		}
325
326
		$notice_dismiss_time = ! empty( $_post['dismiss_interval_time'] ) ? $_post['dismiss_interval_time'] : null;
327
328
		// Save option to hide notice.
329
		Give_Cache::set( $notice_key, true, $notice_dismiss_time, true );
330
331
		wp_send_json_success();
332
	}
333
334
335
	/**
336
	 * Get notice key.
337
	 *
338
	 * @since  1.8.9
339
	 * @access public
340
	 *
341
	 * @param string $notice_id
342
	 * @param string $dismiss_interval
343
	 * @param int    $user_id
344
	 *
345
	 * @return string
346
	 */
347
	public function get_notice_key( $notice_id, $dismiss_interval = null, $user_id = 0 ) {
348
		$notice_key = "_give_notice_{$notice_id}";
349
350
		if ( ! empty( $dismiss_interval ) ) {
351
			$notice_key .= "_{$dismiss_interval}";
352
		}
353
354
		if ( $user_id ) {
355
			$notice_key .= "_{$user_id}";
356
		}
357
358
		$notice_key = sanitize_key( $notice_key );
359
360
		return $notice_key;
361
	}
362
363
364
	/**
365
	 * Get notice dismiss link.
366
	 *
367
	 * @param $notice_args
368
	 *
369
	 * @return string
370
	 */
371
	public function get_dismiss_link( $notice_args ) {
372
		$notice_args = wp_parse_args(
373
			$notice_args,
374
			array(
375
				'title'                 => __( 'Click here', 'give' ),
376
				'dismissible_type'      => '',
377
				'dismiss_interval'      => '',
378
				'dismiss_interval_time' => null,
379
			)
380
		);
381
382
		return sprintf(
383
			'<a href="#" class="give_dismiss_notice" data-dismissible-type="%1$s" data-dismiss-interval="%2$s" data-dismiss-interval-time="%3$s">%4$s</a>',
384
			$notice_args['dismissible_type'],
385
			$notice_args['dismiss_interval'],
386
			$notice_args['dismiss_interval_time'],
387
			$notice_args['title']
388
		);
389
	}
390
391
392
	/**
393
	 * Check if notice dismissed or not
394
	 *
395
	 * @since  1.8.9
396
	 * @access public
397
	 *
398
	 * @param array $notice
399
	 *
400
	 * @return bool|null
401
	 */
402
	public function is_notice_dismissed( $notice ) {
403
		$notice_key          = $this->get_notice_key( $notice['id'], $notice['dismiss_interval'] );
404
		$is_notice_dismissed = false;
405
406
		if ( 'user' === $notice['dismissible_type'] ) {
407
			$current_user = wp_get_current_user();
408
			$notice_key   = Give()->notices->get_notice_key( $notice['id'], $notice['dismiss_interval'], $current_user->ID );
409
		}
410
411
		$notice_data = Give_Cache::get( $notice_key, true );
412
413
		// Find notice dismiss link status if notice has extra dismissible links.
414
		if ( ( empty( $notice_data ) || is_wp_error( $notice_data ) ) && ! empty( $notice['extra_links'] ) ) {
415
416
			foreach ( $notice['extra_links'] as $extra_link ) {
417
				$new_notice_data = wp_parse_args( $extra_link, $notice );
418
				unset( $new_notice_data['extra_links'] );
419
420
				if ( $is_notice_dismissed = $this->is_notice_dismissed( $new_notice_data ) ) {
421
					return $is_notice_dismissed;
422
				}
423
			}
424
		}
425
426
		$is_notice_dismissed = ! empty( $notice_data ) && ! is_wp_error( $notice_data );
427
428
		return $is_notice_dismissed;
429
	}
430
431
432
	/**
433
	 * Print frontend errors.
434
	 *
435
	 * @since  1.8.9
436
	 * @access public
437
	 *
438
	 * @param $errors
439
	 */
440
	static function print_frontend_errors( $errors ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
441
		if ( ! $errors ) {
442
			return;
443
		}
444
445
		$default_notice_args = array(
446
			'auto_dismissible' => false,
447
			'dismiss_interval' => 5000,
448
		);
449
450
		// Note: we will remove give_errors class in future.
451
		$classes = apply_filters( 'give_error_class', array( 'give_notices', 'give_errors' ) );
452
453
		echo sprintf( '<div class="%s">', implode( ' ', $classes ) );
454
455
			// Loop error codes and display errors.
456
			foreach ( $errors as $error_id => $error ) {
457
				// Backward compatibility v<1.8.11
458
				if ( is_string( $error ) ) {
459
					$error = array(
460
						'message'     => $error,
461
						'notice_args' => array(),
462
					);
463
				}
464
465
				$notice_args = wp_parse_args( $error['notice_args'], $default_notice_args );
466
467
				echo sprintf(
468
					'<div class="give_error give_notice" id="give_error_%1$s" data-auto-dismissible="%2$d" data-dismiss-interval="%3$d">
469
								<p><strong>%4$s</strong>: %5$s</p>
470
							</div>',
471
					$error_id,
472
					absint( $notice_args['auto_dismissible'] ),
473
					absint( $notice_args['dismiss_interval'] ),
474
					esc_html__( 'Error', 'give' ),
475
					$error['message']
476
				);
477
			}
478
479
		echo '</div>';
480
	}
481
482
	/**
483
	 * Print frontend notice.
484
	 * Notice: notice type can be success/error/warning
485
	 *
486
	 * @since  1.8.9
487
	 * @access public
488
	 *
489
	 * @param        $message
490
	 * @param bool   $echo
491
	 * @param string $notice_type
492
	 * @param array  $notice_args
493
	 *
494
	 * @return  string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
495
	 */
496
	static function print_frontend_notice( $message, $echo = true, $notice_type = 'warning', $notice_args = array() ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
497
		if ( empty( $message ) ) {
498
			return '';
499
		}
500
501
		$default_notice_args = array(
502
			'auto_dismissible' => false,
503
			'dismiss_interval' => 5000,
504
		);
505
506
		$notice_args = wp_parse_args( $notice_args, $default_notice_args );
507
508
		// Note: we will remove give_errors class in future.
509
		$error = sprintf(
510
			'<div class="give_notices give_errors" id="give_error_%1$s">
511
						<p class="give_error give_notice give_%1$s" data-auto-dismissible="%2$d" data-dismiss-interval="%3$d">
512
							%4$s
513
						</p>
514
					</div>',
515
			$notice_type,
516
			absint( $notice_args['auto_dismissible'] ),
517
			absint( $notice_args['dismiss_interval'] ),
518
			$message
519
		);
520
521
		if ( ! $echo ) {
522
			return $error;
523
		}
524
525
		echo $error;
526
	}
527
}