Test Failed
Push — issues/1925 ( 817edf )
by Ravinder
04:56
created

Give_Notices::print_frontend_errors()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 41
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 22
nc 4
nop 1
dl 0
loc 41
rs 8.5806
c 0
b 0
f 0
1
<?php
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 );
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal /data-([^\"]*)=\"([^\"]*)\"/ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
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
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
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;
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$output'
Loading history...
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;
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
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 ) {
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
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',
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'admin_url'
Loading history...
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 );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
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 View Code Duplication
		if ( 'user' === $_post['dismissible_type'] ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
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;
0 ignored issues
show
Unused Code introduced by
$is_notice_dismissed is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
405
406 View Code Duplication
		if ( 'user' === $notice['dismissible_type'] ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
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 ) );
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'sprintf'
Loading history...
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(
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'sprintf'
Loading history...
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
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;
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$error'
Loading history...
526
	}
527
}