Test Failed
Push — master ( 25adfe...70178c )
by Devin
01:46
created

Give_Notices::print_frontend_errors()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 54
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 23
nc 4
nop 1
dl 0
loc 54
rs 9.0306
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
		 * Backward compatibility for deprecated params.
65
		 *
66
		 * @since 1.8.14
67
		 */
68
		add_filter( 'give_register_notice_args', array( $this, 'bc_deprecated_params' ) );
69
		add_filter( 'give_frontend_errors_args', array( $this, 'bc_deprecated_params' ) );
70
		add_filter( 'give_frontend_notice_args', array( $this, 'bc_deprecated_params' ) );
71
	}
72
73
	/**
74
	 * Add backward compatibility to deprecated params.
75
	 *
76
	 * @since  1.8.14
77
	 * @access public
78
	 *
79
	 * @param array $args Array of notice params
80
	 *
81
	 * @return array
82
	 */
83
	public function bc_deprecated_params( $args ) {
84
		/**
85
		 *  Param: auto_dismissible
86
		 *  deprecated in 1.8.14
87
		 *
88
		 *  Check if auto_dismissible is set and it true then unset and change dismissible parameter value to auto
89
		 */
90
		if ( isset( $args['auto_dismissible'] ) ) {
91
			if ( ! empty( $args['auto_dismissible'] ) ) {
92
				$args['dismissible'] = 'auto';
93
			}
94
			// unset auto_dismissible as it has been deprecated.
95
			unset( $args['auto_dismissible'] );
96
		}
97
98
		return $args;
99
	}
100
101
	/**
102
	 * Register notice.
103
	 *
104
	 * @since  1.8.9
105
	 * @access public
106
	 *
107
	 * @param $notice_args
108
	 *
109
	 * @return bool
110
	 */
111
	public function register_notice( $notice_args ) {
112
		// Bailout.
113
		if ( empty( $notice_args['id'] ) || array_key_exists( $notice_args['id'], self::$notices ) ) {
114
			return false;
115
		}
116
117
		$notice_args = wp_parse_args(
118
			$notice_args,
119
			array(
120
				'id'                    => '',
121
				'description'           => '',
122
123
				/*
124
				 * Add New Parameter and remove the auto_dismissible parameter.
125
				 * Value: auto/true/false
126
				 *
127
				 * @since 1.8.14
128
				 */
129
				'dismissible'           => true,
130
131
				// Value: error/warning/success/info/updated
132
				'type'                  => 'error',
133
134
				// Value: null/user/all
135
				'dismissible_type'      => null,
136
137
				// Value: shortly/permanent/null/custom
138
				'dismiss_interval'      => null,
139
140
				// Only set it when custom is defined.
141
				'dismiss_interval_time' => null,
142
143
			)
144
		);
145
146
		/**
147
		 * Filter to modify Notice args before it get add
148
		 *
149
		 * @since 1.8.14
150
		 */
151
		$notice_args = apply_filters( 'give_register_notice_args', $notice_args );
152
153
		// Set extra dismiss links if any.
154
		if ( false !== strpos( $notice_args['description'], 'data-dismiss-interval' ) ) {
155
156
			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...
157
158
			if ( ! empty( $extra_notice_dismiss_link ) ) {
159
				$extra_notice_dismiss_links = array_chunk( current( $extra_notice_dismiss_link ), 3 );
160
				foreach ( $extra_notice_dismiss_links as $extra_notice_dismiss_link ) {
161
					// Create array og key ==> value by parsing query string created after renaming data attributes.
162
					$data_attribute_query_str = str_replace( array( 'data-', '-', '"' ), array(
163
						'',
164
						'_',
165
						'',
166
					), implode( '&', $extra_notice_dismiss_link ) );
167
168
					$notice_args['extra_links'][] = wp_parse_args( $data_attribute_query_str );
169
				}
170
			}
171
		}
172
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
173
174
		self::$notices[ $notice_args['id'] ] = $notice_args;
175
176
		// Auto set show param if not already set.
177
		if ( ! isset( self::$notices[ $notice_args['id'] ]['show'] ) ) {
178
			self::$notices[ $notice_args['id'] ]['show'] = $this->is_notice_dismissed( $notice_args ) ? false : true;
179
		}
180
181
		// Auto set time interval for shortly.
182
		if ( 'shortly' === self::$notices[ $notice_args['id'] ]['dismiss_interval'] ) {
183
			self::$notices[ $notice_args['id'] ]['dismiss_interval_time'] = DAY_IN_SECONDS;
184
		}
185
186
		return true;
187
	}
188
189
	/**
190
	 * Display notice.
191
	 *
192
	 * @since 1.8.9
193
	 *
194
	 */
195
	public function render_admin_notices() {
196
		// Bailout.
197
		if ( empty( self::$notices ) ) {
198
			return;
199
		}
200
201
		$output = '';
202
203
		foreach ( self::$notices as $notice_id => $notice ) {
204
			// Check flag set to true to show notice.
205
			if ( ! $notice['show'] ) {
206
				continue;
207
			}
208
209
			// Check if notice dismissible or not.
210
			if ( ! self::$has_auto_dismissible_notice ) {
211
				self::$has_auto_dismissible_notice = ( 'auto' === $notice['dismissible'] );
212
			}
213
214
			// Check if notice dismissible or not.
215
			if ( ! self::$has_dismiss_interval_notice ) {
216
				self::$has_dismiss_interval_notice = $notice['dismiss_interval'];
217
			}
218
219
			$css_id = ( false === strpos( $notice['id'], 'give' ) ? "give-{$notice['id']}" : $notice['id'] );
220
221
			$css_class = 'give-notice notice ' . ( empty( $notice['dismissible'] ) ? 'non' : 'is' ) . "-dismissible {$notice['type']} notice-{$notice['type']}";
222
			$output    .= sprintf(
223
				'<div id="%1$s" class="%2$s" data-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",
224
				$css_id,
225
				$css_class,
226
				give_clean( $notice['dismissible'] ),
227
				$notice['dismissible_type'],
228
				$notice['dismiss_interval'],
229
				$notice['id'],
230
				empty( $notice['dismissible_type'] ) ? '' : wp_create_nonce( "give_edit_{$notice_id}_notice" ),
231
				$notice['dismiss_interval_time']
232
			);
233
234
			$output .= ( 0 === strpos( $notice['description'], '<div' ) || 0 === strpos( $notice['description'], '<p' ) ? $notice['description'] : "<p>{$notice['description']}</p>" );
235
			$output .= "</div> \n";
236
		}
237
238
		echo $output;
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$output'
Loading history...
239
240
		$this->print_js();
241
	}
242
243
244
	/**
245
	 * Render give frontend notices.
246
	 *
247
	 * @since  1.8.9
248
	 * @access public
249
	 *
250
	 * @param int $form_id
251
	 */
252
	public function render_frontend_notices( $form_id = 0 ) {
253
		$errors = give_get_errors();
254
255
		$request_form_id = isset( $_REQUEST['form-id'] ) ? absint( $_REQUEST['form-id'] ) : 0;
0 ignored issues
show
introduced by
Detected access of super global var $_REQUEST, probably need manual inspection.
Loading history...
256
257
		// Sanity checks first: Ensure that gateway returned errors display on the appropriate form.
258
		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...
259
			return;
260
		}
261
262
		if ( $errors ) {
263
			self::print_frontend_errors( $errors );
0 ignored issues
show
Bug introduced by
It seems like $errors defined by give_get_errors() on line 253 can also be of type string; however, Give_Notices::print_frontend_errors() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
264
265
			give_clear_errors();
266
		}
267
	}
268
269
	/**
270
	 * Print notice js.
271
	 *
272
	 * @since  1.8.9
273
	 * @access private
274
	 */
275
	private function print_js() {
276
		if ( self::$has_auto_dismissible_notice ) :
277
			?>
278
			<script>
279
				jQuery(document).ready(function () {
280
					// auto hide setting message in 5 seconds.
281
					window.setTimeout(
282
						function () {
283
							jQuery('.give-notice[data-dismissible="auto"]').slideUp();
284
						},
285
						5000
286
					);
287
				})
288
			</script>
289
			<?php
290
		endif;
291
292
		if ( self::$has_dismiss_interval_notice ) :
293
			?>
294
			<script>
295
				jQuery(document).ready(function () {
296
					var $body = jQuery('body');
297
298
					$body.on('click', '.give_dismiss_notice', function (e) {
299
						var $parent            = jQuery(this).parents('.give-notice'),
300
							custom_notice_data = {
301
								'dismissible_type': jQuery(this).data('dismissible-type'),
302
								'dismiss_interval': jQuery(this).data('dismiss-interval'),
303
								'dismiss_interval_time': jQuery(this).data('dismiss-interval-time')
304
							};
305
306
						$parent.find('button.notice-dismiss').trigger('click', [custom_notice_data]);
307
						return false;
308
					});
309
310
					$body.on('click', 'button.notice-dismiss', function (e, custom_notice_data) {
311
						var $parent            = jQuery(this).parents('.give-notice'),
312
							custom_notice_data = custom_notice_data || {};
313
314
						e.preventDefault();
315
316
						var data = {
317
							'give-action': 'dismiss_notices',
318
							'notice_id': $parent.data('notice-id'),
319
							'dismissible_type': $parent.data('dismissible-type'),
320
							'dismiss_interval': $parent.data('dismiss-interval'),
321
							'dismiss_interval_time': $parent.data('dismiss-interval-time'),
322
							'_wpnonce': $parent.data('security')
323
						};
324
325
						if (Object.keys(custom_notice_data).length) {
326
							jQuery.extend(data, custom_notice_data);
327
						}
328
329
						// Bailout.
330
						if (
331
							!data.dismiss_interval ||
332
							!data.dismissible_type
333
						) {
334
							return false;
335
						}
336
337
						jQuery.post(
338
							'<?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...
339
							data,
340
							function (response) {
341
342
							})
343
					})
344
				});
345
			</script>
346
			<?php
347
		endif;
348
	}
349
350
351
	/**
352
	 * Hide notice.
353
	 *
354
	 * @since  1.8.9
355
	 * @access public
356
	 */
357
	public function dismiss_notices() {
358
		$_post     = give_clean( $_POST );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
359
		$notice_id = esc_attr( $_post['notice_id'] );
360
361
		// Bailout.
362
		if (
363
			empty( $notice_id ) ||
364
			empty( $_post['dismissible_type'] ) ||
365
			empty( $_post['dismiss_interval'] ) ||
366
			! check_ajax_referer( "give_edit_{$notice_id}_notice", '_wpnonce' )
367
		) {
368
			wp_send_json_error();
369
		}
370
371
		$notice_key = Give()->notices->get_notice_key( $notice_id, $_post['dismiss_interval'] );
372 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...
373
			$current_user = wp_get_current_user();
374
			$notice_key   = Give()->notices->get_notice_key( $notice_id, $_post['dismiss_interval'], $current_user->ID );
375
		}
376
377
		$notice_dismiss_time = ! empty( $_post['dismiss_interval_time'] ) ? $_post['dismiss_interval_time'] : null;
378
379
		// Save option to hide notice.
380
		Give_Cache::set( $notice_key, true, $notice_dismiss_time, true );
381
382
		wp_send_json_success();
383
	}
384
385
386
	/**
387
	 * Get notice key.
388
	 *
389
	 * @since  1.8.9
390
	 * @access public
391
	 *
392
	 * @param string $notice_id
393
	 * @param string $dismiss_interval
394
	 * @param int    $user_id
395
	 *
396
	 * @return string
397
	 */
398
	public function get_notice_key( $notice_id, $dismiss_interval = null, $user_id = 0 ) {
399
		$notice_key = "_give_notice_{$notice_id}";
400
401
		if ( ! empty( $dismiss_interval ) ) {
402
			$notice_key .= "_{$dismiss_interval}";
403
		}
404
405
		if ( $user_id ) {
406
			$notice_key .= "_{$user_id}";
407
		}
408
409
		$notice_key = sanitize_key( $notice_key );
410
411
		return $notice_key;
412
	}
413
414
415
	/**
416
	 * Get notice dismiss link.
417
	 *
418
	 * @param $notice_args
419
	 *
420
	 * @return string
421
	 */
422
	public function get_dismiss_link( $notice_args ) {
423
		$notice_args = wp_parse_args(
424
			$notice_args,
425
			array(
426
				'title'                 => __( 'Click here', 'give' ),
427
				'dismissible_type'      => '',
428
				'dismiss_interval'      => '',
429
				'dismiss_interval_time' => null,
430
			)
431
		);
432
433
		return sprintf(
434
			'<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>',
435
			$notice_args['dismissible_type'],
436
			$notice_args['dismiss_interval'],
437
			$notice_args['dismiss_interval_time'],
438
			$notice_args['title']
439
		);
440
	}
441
442
443
	/**
444
	 * Check if notice dismissed or not
445
	 *
446
	 * @since  1.8.9
447
	 * @access public
448
	 *
449
	 * @param array $notice
450
	 *
451
	 * @return bool|null
452
	 */
453
	public function is_notice_dismissed( $notice ) {
454
		$notice_key          = $this->get_notice_key( $notice['id'], $notice['dismiss_interval'] );
455
		$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...
456
457 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...
458
			$current_user = wp_get_current_user();
459
			$notice_key   = Give()->notices->get_notice_key( $notice['id'], $notice['dismiss_interval'], $current_user->ID );
460
		}
461
462
		$notice_data = Give_Cache::get( $notice_key, true );
463
464
		// Find notice dismiss link status if notice has extra dismissible links.
465
		if ( ( empty( $notice_data ) || is_wp_error( $notice_data ) ) && ! empty( $notice['extra_links'] ) ) {
466
467
			foreach ( $notice['extra_links'] as $extra_link ) {
468
				$new_notice_data = wp_parse_args( $extra_link, $notice );
469
				unset( $new_notice_data['extra_links'] );
470
471
				if ( $is_notice_dismissed = $this->is_notice_dismissed( $new_notice_data ) ) {
472
					return $is_notice_dismissed;
473
				}
474
			}
475
		}
476
477
		$is_notice_dismissed = ! empty( $notice_data ) && ! is_wp_error( $notice_data );
478
479
		return $is_notice_dismissed;
480
	}
481
482
483
	/**
484
	 * Print frontend errors.
485
	 *
486
	 * @since  1.8.9
487
	 * @access public
488
	 *
489
	 * @param array $errors
490
	 */
491
	public static function print_frontend_errors( $errors ) {
492
		// Bailout.
493
		if ( ! $errors ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $errors of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
494
			return;
495
		}
496
497
		/**
498
		 * Change auto_dismissible to dismissible and set the value to true
499
		 *
500
		 * @since 1.8.14
501
		 */
502
		$default_notice_args = array(
503
			'dismissible'      => true,
504
			'dismiss_interval' => 5000,
505
		);
506
507
		// Note: we will remove give_errors class in future.
508
		$classes = apply_filters( 'give_error_class', array( 'give_notices', 'give_errors' ) );
509
510
		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...
511
512
		// Loop error codes and display errors.
513
		foreach ( $errors as $error_id => $error ) {
514
			// Backward compatibility v<1.8.11
515
			if ( is_string( $error ) ) {
516
				$error = array(
517
					'message'     => $error,
518
					'notice_args' => array(),
519
				);
520
			}
521
522
			$notice_args = wp_parse_args( $error['notice_args'], $default_notice_args );
523
524
			/**
525
			 * Filter to modify Frontend Errors args before errors is display.
526
			 *
527
			 * @since 1.8.14
528
			 */
529
			$notice_args = apply_filters( 'give_frontend_errors_args', $notice_args );
530
531
			echo sprintf(
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'sprintf'
Loading history...
532
				'<div class="give_error give_notice" id="give_error_%1$s" data-dismissible="%2$s" data-dismiss-interval="%3$d">
533
						<p><strong>%4$s</strong>: %5$s</p>
534
					</div>',
535
				$error_id,
536
				give_clean( $notice_args['dismissible'] ),
537
				absint( $notice_args['dismiss_interval'] ),
538
				esc_html__( 'Error', 'give' ),
539
				$error['message']
540
			);
541
		}
542
543
		echo '</div>';
544
	}
545
546
	/**
547
	 * Print frontend notice.
548
	 * Notice: notice type can be success/error/warning
549
	 *
550
	 * @since  1.8.9
551
	 * @access public
552
	 *
553
	 * @param string $message
554
	 * @param bool   $echo
555
	 * @param string $notice_type
556
	 * @param array  $notice_args
557
	 *
558
	 * @return  string
559
	 */
560
	public static function print_frontend_notice( $message, $echo = true, $notice_type = 'warning', $notice_args = array() ) {
561
		if ( empty( $message ) ) {
562
			return '';
563
		}
564
565
		/**
566
		 * Change auto_dismissible to dismissible and set the value to true
567
		 *
568
		 * @since 1.8.14
569
		 */
570
		$default_notice_args = array(
571
			'dismissible'      => true,
572
			'dismiss_interval' => 5000,
573
		);
574
575
		$notice_args = wp_parse_args( $notice_args, $default_notice_args );
576
577
		/**
578
		 * Filter to modify Frontend notice args before notices is display.
579
		 *
580
		 * @since 1.8.14
581
		 */
582
		$notice_args = apply_filters( 'give_frontend_notice_args', $notice_args );
583
584
		// Note: we will remove give_errors class in future.
585
		$error = sprintf(
586
			'<div class="give_notices give_errors" id="give_error_%1$s">
587
				<p class="give_error give_notice give_%1$s" data-dismissible="%2$s" data-dismiss-interval="%3$d">
588
					%4$s
589
				</p>
590
			</div>',
591
			$notice_type,
592
			give_clean( $notice_args['dismissible'] ),
593
			absint( $notice_args['dismiss_interval'] ),
594
			$message
595
		);
596
597
		if ( ! $echo ) {
598
			return $error;
599
		}
600
601
		echo $error;
0 ignored issues
show
introduced by
Expected next thing to be a escaping function, not '$error'
Loading history...
602
	}
603
}