Completed
Push — master ( 7f657a...f0ed00 )
by Stephanie
02:26
created

FrmDb::maybe_remove_prefix()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
class FrmDb {
4
	public $fields;
5
	public $forms;
6
	public $entries;
7
	public $entry_metas;
8
9
	public function __construct() {
10
		if ( ! defined( 'ABSPATH' ) ) {
11
			die( 'You are not allowed to call this page directly.' );
12
		}
13
14
		_deprecated_function( __METHOD__, '2.05.06', 'FrmMigrate' );
15
		global $wpdb;
16
		$this->fields      = $wpdb->prefix . 'frm_fields';
17
		$this->forms       = $wpdb->prefix . 'frm_forms';
18
		$this->entries     = $wpdb->prefix . 'frm_items';
19
		$this->entry_metas = $wpdb->prefix . 'frm_item_metas';
20
	}
21
22
	/**
23
	 * Change array into format $wpdb->prepare can use
24
	 *
25
	 * @param array $args
26
	 * @param string $starts_with
27
	 */
28
	public static function get_where_clause_and_values( &$args, $starts_with = ' WHERE ' ) {
29
		if ( empty( $args ) ) {
30
			// add an arg to prevent prepare from failing
31
			$args = array(
32
				'where'  => $starts_with . '1=%d',
33
				'values' => array( 1 ),
34
			);
35
36
			return;
37
		}
38
39
		$where  = '';
40
		$values = array();
41
42
		if ( is_array( $args ) ) {
43
			$base_where = $starts_with;
44
			self::parse_where_from_array( $args, $base_where, $where, $values );
45
		}
46
47
		$args = compact( 'where', 'values' );
48
	}
49
50
	/**
51
	 * @param array $args
52
	 * @param string $base_where
53
	 * @param string $where
54
	 * @param array $values
55
	 */
56
	public static function parse_where_from_array( $args, $base_where, &$where, &$values ) {
57
		$condition = ' AND';
58
		if ( isset( $args['or'] ) ) {
59
			$condition = ' OR';
60
			unset( $args['or'] );
61
		}
62
63
		foreach ( $args as $key => $value ) {
64
			$where          .= empty( $where ) ? $base_where : $condition;
65
			$array_inc_null = ( ! is_numeric( $key ) && is_array( $value ) && in_array( null, $value ) );
66
			if ( is_numeric( $key ) || $array_inc_null ) {
67
				$where        .= ' ( ';
68
				$nested_where = '';
69
				if ( $array_inc_null ) {
70
					foreach ( $value as $val ) {
71
						$parse_where = array(
72
							$key => $val,
73
							'or' => 1,
74
						);
75
						self::parse_where_from_array( $parse_where, '', $nested_where, $values );
76
					}
77
				} else {
78
					self::parse_where_from_array( $value, '', $nested_where, $values );
79
				}
80
				$where .= $nested_where;
81
				$where .= ' ) ';
82
			} else {
83
				self::interpret_array_to_sql( $key, $value, $where, $values );
84
			}
85
		}
86
	}
87
88
	/**
89
	 * @param string $key
90
	 * @param string|array $value
91
	 * @param string $where
92
	 * @param array $values
93
	 */
94
	private static function interpret_array_to_sql( $key, $value, &$where, &$values ) {
95
		$key = trim( $key );
96
97
		if ( strpos( $key, 'created_at' ) !== false || strpos( $key, 'updated_at' ) !== false ) {
98
			$k        = explode( ' ', $key );
99
			$where    .= ' DATE_FORMAT(' . reset( $k ) . ', %s) ' . str_replace( reset( $k ), '', $key );
100
			$values[] = '%Y-%m-%d %H:%i:%s';
101
		} else {
102
			$where .= ' ' . $key;
103
		}
104
105
		$lowercase_key = explode( ' ', strtolower( $key ) );
106
		$lowercase_key = end( $lowercase_key );
107
108
		if ( is_array( $value ) ) {
109
			// translate array of values to "in"
110
			if ( strpos( $lowercase_key, 'like' ) !== false ) {
111
				$where = preg_replace( '/' . $key . '$/', '', $where );
112
				$where .= '(';
113
				$start = true;
114
				foreach ( $value as $v ) {
115
					if ( ! $start ) {
116
						$where .= ' OR ';
117
					}
118
					$start    = false;
119
					$where    .= $key . ' %s';
120
					$values[] = '%' . self::esc_like( $v ) . '%';
121
				}
122
				$where .= ')';
123
			} elseif ( ! empty( $value ) ) {
124
				$where  .= ' in (' . self::prepare_array_values( $value, '%s' ) . ')';
125
				$values = array_merge( $values, $value );
126
			}
127
		} elseif ( strpos( $lowercase_key, 'like' ) !== false ) {
128
			/**
129
			 * Allow string to start or end with the value
130
			 * If the key is like% then skip the first % for starts with
131
			 * If the key is %like then skip the last % for ends with
132
			 */
133
			$start = '%';
134
			$end   = '%';
135
			if ( $lowercase_key == 'like%' ) {
136
				$start = '';
137
				$where = rtrim( $where, '%' );
138
			} elseif ( $lowercase_key == '%like' ) {
139
				$end   = '';
140
				$where = rtrim( rtrim( $where, '%like' ), '%LIKE' );
141
				$where .= 'like';
142
			}
143
144
			$where    .= ' %s';
145
			$values[] = $start . self::esc_like( $value ) . $end;
146
147
		} elseif ( $value === null ) {
148
			$where .= ' IS NULL';
149
		} else {
150
			// allow a - to prevent = from being added
151
			if ( substr( $key, - 1 ) == '-' ) {
152
				$where = rtrim( $where, '-' );
153
			} else {
154
				$where .= '=';
155
			}
156
157
			self::add_query_placeholder( $key, $value, $where );
158
159
			$values[] = $value;
160
		}
161
	}
162
163
	/**
164
	 * Add %d, or %s to query
165
	 *
166
	 * @since 2.02.05
167
	 *
168
	 * @param string $key
169
	 * @param int|string $value
170
	 * @param string $where
171
	 */
172
	private static function add_query_placeholder( $key, $value, &$where ) {
173
		if ( is_numeric( $value ) && ( strpos( $key, 'meta_value' ) === false || strpos( $key, '+0' ) !== false ) ) {
174
			$value = $value + 0; // switch string to number
175
			$where .= is_float( $value ) ? '%f' : '%d';
176
		} else {
177
			$where .= '%s';
178
		}
179
	}
180
181
	/**
182
	 * @param string $table
183
	 * @param array $where
184
	 * @param array $args
185
	 *
186
	 * @return int
187
	 */
188
	public static function get_count( $table, $where = array(), $args = array() ) {
189
		$count = self::get_var( $table, $where, 'COUNT(*)', $args );
190
191
		return $count;
192
	}
193
194
	/**
195
	 * @param string $table
196
	 * @param array $where
197
	 * @param string $field
198
	 * @param array $args
199
	 * @param string $limit
200
	 * @param string $type
201
	 *
202
	 * @return array|null|string|object
203
	 */
204
	public static function get_var( $table, $where = array(), $field = 'id', $args = array(), $limit = '', $type = 'var' ) {
205
		$group = '';
206
		self::get_group_and_table_name( $table, $group );
207
		self::convert_options_to_array( $args, '', $limit );
208
		if ( $type === 'var' && ! isset( $args['limit'] ) ) {
209
			$args['limit'] = 1;
210
		}
211
212
		$query = self::generate_query_string_from_pieces( $field, $table, $where, $args );
213
214
		$cache_key = self::generate_cache_key( $where, $args, $field, $type );
215
		$results   = self::check_cache( $cache_key, $group, $query, 'get_' . $type );
216
217
		return $results;
218
	}
219
220
	/**
221
	 * Generate a cache key from the where query, field, type, and other arguments
222
	 *
223
	 * @since 2.03.07
224
	 *
225
	 * @param array $where
226
	 * @param array $args
227
	 * @param string $field
228
	 * @param string $type
229
	 *
230
	 * @return string
231
	 */
232
	private static function generate_cache_key( $where, $args, $field, $type ) {
233
		$cache_key = '';
234
		$where     = FrmAppHelper::array_flatten( $where );
235
		foreach ( $where as $key => $value ) {
236
			$cache_key .= $key . '_' . $value;
237
		}
238
		$cache_key .= implode( '_', $args ) . $field . '_' . $type;
239
		$cache_key = str_replace( array( ' ', ',' ), '_', $cache_key );
240
241
		return $cache_key;
242
	}
243
244
	/**
245
	 * @param string $table
246
	 * @param array $where
247
	 * @param string $field
248
	 * @param array $args
249
	 * @param string $limit
250
	 *
251
	 * @return mixed
252
	 */
253
	public static function get_col( $table, $where = array(), $field = 'id', $args = array(), $limit = '' ) {
254
		return self::get_var( $table, $where, $field, $args, $limit, 'col' );
255
	}
256
257
	/**
258
	 * @since 2.0
259
	 *
260
	 * @param string $table
261
	 * @param array $where
262
	 * @param string $fields
263
	 * @param array $args
264
	 *
265
	 * @return mixed
266
	 */
267
	public static function get_row( $table, $where = array(), $fields = '*', $args = array() ) {
268
		$args['limit'] = 1;
269
270
		return self::get_var( $table, $where, $fields, $args, '', 'row' );
271
	}
272
273
	/**
274
	 * Prepare a key/value array before DB call
275
	 *
276
	 * @since 2.0
277
	 *
278
	 * @param string $table
279
	 * @param array $where
280
	 * @param string $fields
281
	 * @param array $args
282
	 *
283
	 * @return mixed
284
	 */
285
	public static function get_results( $table, $where = array(), $fields = '*', $args = array() ) {
286
		return self::get_var( $table, $where, $fields, $args, '', 'results' );
287
	}
288
289
	/**
290
	 * Check for like, not like, in, not in, =, !=, >, <, <=, >=
291
	 * Return a value to append to the where array key
292
	 *
293
	 * @param string $where_is
294
	 *
295
	 * @return string
296
	 */
297
	public static function append_where_is( $where_is ) {
298
		$switch_to = array(
299
			'='        => '',
300
			'!='       => '!',
301
			'<='       => '<',
302
			'>='       => '>',
303
			'like'     => 'like',
304
			'not like' => 'not like',
305
			'in'       => '',
306
			'not in'   => 'not',
307
			'like%'    => 'like%',
308
			'%like'    => '%like',
309
		);
310
311
		$where_is = strtolower( $where_is );
312
		if ( isset( $switch_to[ $where_is ] ) ) {
313
			return ' ' . $switch_to[ $where_is ];
314
		}
315
316
		// > and < need a little more work since we don't want them switched to >= and <=
317
		if ( $where_is == '>' || $where_is == '<' ) {
318
			return ' ' . $where_is . '-'; // the - indicates that the = should not be added later
319
		}
320
321
		// fallback to = if the query is none of these
322
		return '';
323
	}
324
325
	/**
326
	 * Get 'frm_forms' from wp_frm_forms or a longer table param that includes a join
327
	 * Also add the wpdb->prefix to the table if it's missing
328
	 *
329
	 * @param string $table
330
	 * @param string $group
331
	 */
332
	private static function get_group_and_table_name( &$table, &$group ) {
333
		global $wpdb, $wpmuBaseTablePrefix;
334
335
		$table_parts = explode( ' ', $table );
336
		$group       = reset( $table_parts );
337
		self::maybe_remove_prefix( $wpdb->prefix, $group );
338
339
		$prefix = $wpmuBaseTablePrefix ? $wpmuBaseTablePrefix : $wpdb->base_prefix;
340
		self::maybe_remove_prefix( $prefix, $group );
341
342
		if ( $group == $table ) {
343
			$table = $wpdb->prefix . $table;
344
		}
345
346
		// switch to singular group name
347
		$group = rtrim( $group, 's' );
348
	}
349
350
	/**
351
	 * Only remove the db prefix when at the beginning.
352
	 *
353
	 * @since 4.04.02
354
	 */
355
	private static function maybe_remove_prefix( $prefix, &$name ) {
356
		if ( substr( $name, 0, strlen( $prefix ) ) === $prefix ) {
357
		    $name = substr( $name, strlen( $prefix ) );
358
		}
359
	}
360
361
	private static function convert_options_to_array( &$args, $order_by = '', $limit = '' ) {
362
		if ( ! is_array( $args ) ) {
363
			$args = array( 'order_by' => $args );
364
		}
365
366
		if ( ! empty( $order_by ) ) {
367
			$args['order_by'] = $order_by;
368
		}
369
370
		if ( ! empty( $limit ) ) {
371
			$args['limit'] = $limit;
372
		}
373
374
		$temp_args = $args;
375
		foreach ( $temp_args as $k => $v ) {
376
			if ( $v == '' ) {
377
				unset( $args[ $k ] );
378
				continue;
379
			}
380
381
			$db_name = strtoupper( str_replace( '_', ' ', $k ) );
382
			if ( strpos( $v, $db_name ) === false ) {
383
				$args[ $k ] = $db_name . ' ' . $v;
384
			}
385
		}
386
387
		// Make sure LIMIT is the last argument
388
		if ( isset( $args['order_by'] ) && isset( $args['limit'] ) ) {
389
			$temp_limit = $args['limit'];
390
			unset( $args['limit'] );
391
			$args['limit'] = $temp_limit;
392
		}
393
	}
394
395
	/**
396
	 * Get the associative array results for the given columns, table, and where query
397
	 *
398
	 * @since 2.02.05
399
	 *
400
	 * @param string $columns
401
	 * @param string $table
402
	 * @param array $where
403
	 *
404
	 * @return mixed
405
	 */
406
	public static function get_associative_array_results( $columns, $table, $where ) {
407
		$group = '';
408
		self::get_group_and_table_name( $table, $group );
409
410
		$query = self::generate_query_string_from_pieces( $columns, $table, $where );
411
412
		$cache_key = str_replace( array( ' ', ',' ), '_', trim( implode( '_', FrmAppHelper::array_flatten( $where ) ) . $columns . '_results_ARRAY_A', ' WHERE' ) );
413
		$results   = self::check_cache( $cache_key, $group, $query, 'get_associative_results' );
414
415
		return $results;
416
	}
417
418
	/**
419
	 * Combine the pieces of a query to form a full, prepared query
420
	 *
421
	 * @since 2.02.05
422
	 *
423
	 * @param string $columns
424
	 * @param string $table
425
	 * @param mixed $where
426
	 * @param array $args
427
	 *
428
	 * @return string
429
	 */
430
	private static function generate_query_string_from_pieces( $columns, $table, $where, $args = array() ) {
431
		$query = 'SELECT ' . $columns . ' FROM ' . $table;
432
433
		self::esc_query_args( $args );
434
435
		if ( is_array( $where ) || empty( $where ) ) {
436
			self::get_where_clause_and_values( $where );
437
			global $wpdb;
438
			$query = $wpdb->prepare( $query . $where['where'] . ' ' . implode( ' ', $args ), $where['values'] ); // WPCS: unprepared SQL ok.
439
		} else {
440
			/**
441
			 * Allow the $where to be prepared before we recieve it here.
442
			 * This is a fallback for reverse compatibility, but is not recommended
443
			 */
444
			_deprecated_argument( 'where', '2.0', esc_html__( 'Use the query in an array format so it can be properly prepared.', 'formidable' ) );
445
			$query .= $where . ' ' . implode( ' ', $args );
446
		}
447
448
		return $query;
449
	}
450
451
	/**
452
	 * @since 2.05.07
453
	 */
454
	private static function esc_query_args( &$args ) {
455
		foreach ( $args as $param => $value ) {
456
			if ( $param == 'order_by' ) {
457
				$args[ $param ] = self::esc_order( $value );
458
			} elseif ( $param == 'limit' ) {
459
				$args[ $param ] = self::esc_limit( $value );
460
			}
461
462
			if ( $args[ $param ] == '' ) {
463
				unset( $args[ $param ] );
464
			}
465
		}
466
	}
467
468
	/**
469
	 * Added for < WP 4.0 compatability
470
	 *
471
	 * @since 2.05.06
472
	 *
473
	 * @param string $term The value to escape
474
	 *
475
	 * @return string The escaped value
476
	 */
477
	public static function esc_like( $term ) {
478
		global $wpdb;
479
480
		return $wpdb->esc_like( $term );
481
	}
482
483
	/**
484
	 * @since 2.05.06
485
	 *
486
	 * @param string $order_query
487
	 */
488
	public static function esc_order( $order_query ) {
489
		if ( empty( $order_query ) ) {
490
			return '';
491
		}
492
493
		// remove ORDER BY before santizing
494
		$order_query = strtolower( $order_query );
495
		if ( strpos( $order_query, 'order by' ) !== false ) {
496
			$order_query = str_replace( 'order by', '', $order_query );
497
		}
498
499
		$order_query = explode( ' ', trim( $order_query ) );
500
501
		$order      = trim( reset( $order_query ) );
502
		$safe_order = array( 'count(*)' );
503
		if ( ! in_array( strtolower( $order ), $safe_order ) ) {
504
			$order = preg_replace( '/[^a-zA-Z0-9\-\_\.\+]/', '', $order );
505
		}
506
507
		$order_by = '';
508
		if ( count( $order_query ) > 1 ) {
509
			$order_by = end( $order_query );
510
			self::esc_order_by( $order_by );
511
		}
512
513
		return ' ORDER BY ' . $order . ' ' . $order_by;
514
	}
515
516
	/**
517
	 * Make sure this is ordering by either ASC or DESC
518
	 *
519
	 * @since 2.05.06
520
	 */
521
	public static function esc_order_by( &$order_by ) {
522
		$sort_options = array( 'asc', 'desc' );
523
		if ( ! in_array( strtolower( $order_by ), $sort_options ) ) {
524
			$order_by = 'asc';
525
		}
526
	}
527
528
	/**
529
	 * @param string $limit
530
	 *
531
	 * @since 2.05.06
532
	 */
533
	public static function esc_limit( $limit ) {
534
		if ( empty( $limit ) ) {
535
			return '';
536
		}
537
538
		$limit = trim( str_replace( 'limit ', '', strtolower( $limit ) ) );
539
		if ( is_numeric( $limit ) ) {
540
			return ' LIMIT ' . $limit;
541
		}
542
543
		$limit = explode( ',', trim( $limit ) );
544
		foreach ( $limit as $k => $l ) {
545
			if ( is_numeric( $l ) ) {
546
				$limit[ $k ] = $l;
547
			}
548
		}
549
550
		$limit = implode( ',', $limit );
551
552
		return ' LIMIT ' . $limit;
553
	}
554
555
	/**
556
	 * Get an array of values ready to go through $wpdb->prepare
557
	 *
558
	 * @since 2.05.06
559
	 */
560
	public static function prepare_array_values( $array, $type = '%s' ) {
561
		$placeholders = array_fill( 0, count( $array ), $type );
562
563
		return implode( ', ', $placeholders );
564
	}
565
566
	/**
567
	 * @since 2.05.06
568
	 */
569
	public static function prepend_and_or_where( $starts_with = ' WHERE ', $where = '' ) {
570
		if ( empty( $where ) ) {
571
			return '';
572
		}
573
574
		if ( is_array( $where ) ) {
575
			global $wpdb;
576
			self::get_where_clause_and_values( $where, $starts_with );
577
			$where = $wpdb->prepare( $where['where'], $where['values'] ); // WPCS: unprepared SQL ok.
578
		} else {
579
			$where = $starts_with . $where;
580
		}
581
582
		return $where;
583
	}
584
585
	/**
586
	 * Prepare and save settings in styles and actions
587
	 *
588
	 * @param array $settings
589
	 * @param string $group
590
	 *
591
	 * @since 2.05.06
592
	 */
593
	public static function save_settings( $settings, $group ) {
594
		$settings                 = (array) $settings;
595
		$settings['post_content'] = FrmAppHelper::prepare_and_encode( $settings['post_content'] );
596
597
		if ( empty( $settings['ID'] ) ) {
598
			unset( $settings['ID'] );
599
		}
600
601
		// delete all caches for this group
602
		self::cache_delete_group( $group );
603
604
		return self::save_json_post( $settings );
605
	}
606
607
	/**
608
	 * Since actions are JSON encoded, we don't want any filters messing with it.
609
	 * Remove the filters and then add them back in case any posts or views are
610
	 * also being imported.
611
	 *
612
	 * Used when saving form actions and styles
613
	 *
614
	 * @since 2.05.06
615
	 */
616
	public static function save_json_post( $settings ) {
617
		global $wp_filter;
618
		$filters = $wp_filter['content_save_pre'];
619
620
		// Remove the balanceTags filter in case WordPress is trying to validate the XHTML
621
		remove_all_filters( 'content_save_pre' );
622
623
		$post = wp_insert_post( $settings );
624
625
		// add the content filters back for views or posts
626
		$wp_filter['content_save_pre'] = $filters;
627
628
		return $post;
629
	}
630
631
	/**
632
	 * Check cache before fetching values and saving to cache
633
	 *
634
	 * @since 2.05.06
635
	 *
636
	 * @param string $cache_key The unique name for this cache
637
	 * @param string $group The name of the cache group
638
	 * @param string $query If blank, don't run a db call
639
	 * @param string $type The wpdb function to use with this query
640
	 *
641
	 * @return mixed $results The cache or query results
642
	 */
643
	public static function check_cache( $cache_key, $group = '', $query = '', $type = 'get_var', $time = 300 ) {
644
		$results = wp_cache_get( $cache_key, $group );
645
		if ( ! FrmAppHelper::is_empty_value( $results, false ) || empty( $query ) ) {
646
			return $results;
647
		}
648
649
		if ( 'get_posts' == $type ) {
650
			$results = get_posts( $query );
651
		} elseif ( 'get_associative_results' == $type ) {
652
			global $wpdb;
653
			$results = $wpdb->get_results( $query, OBJECT_K ); // WPCS: unprepared SQL ok.
654
		} else {
655
			global $wpdb;
656
			$results = $wpdb->{$type}( $query );
657
		}
658
659
		self::set_cache( $cache_key, $results, $group, $time );
660
661
		return $results;
662
	}
663
664
	/**
665
	 * @since 2.05.06
666
	 */
667
	public static function set_cache( $cache_key, $results, $group = '', $time = 300 ) {
668
		if ( ! FrmAppHelper::prevent_caching() ) {
669
			self::add_key_to_group_cache( $cache_key, $group );
670
			wp_cache_set( $cache_key, $results, $group, $time );
671
		}
672
	}
673
674
	/**
675
	 * Keep track of the keys cached in each group so they can be deleted
676
	 * in Redis and Memcache
677
	 *
678
	 * @since 2.05.06
679
	 */
680
	public static function add_key_to_group_cache( $key, $group ) {
681
		$cached         = self::get_group_cached_keys( $group );
682
		$cached[ $key ] = $key;
683
		wp_cache_set( 'cached_keys', $cached, $group, 300 );
684
	}
685
686
	/**
687
	 * @since 2.05.06
688
	 */
689
	public static function get_group_cached_keys( $group ) {
690
		$cached = wp_cache_get( 'cached_keys', $group );
691
		if ( ! $cached || ! is_array( $cached ) ) {
692
			$cached = array();
693
		}
694
695
		return $cached;
696
	}
697
698
	/**
699
	 * @since 2.05.06
700
	 *
701
	 * @param string $cache_key
702
	 */
703
	public static function delete_cache_and_transient( $cache_key, $group = 'default' ) {
704
		delete_transient( $cache_key );
705
		wp_cache_delete( $cache_key, $group );
706
	}
707
708
	/**
709
	 * Delete all caching in a single group
710
	 *
711
	 * @since 2.05.06
712
	 *
713
	 * @param string $group The name of the cache group
714
	 */
715
	public static function cache_delete_group( $group ) {
716
		$cached_keys = self::get_group_cached_keys( $group );
717
718
		if ( ! empty( $cached_keys ) ) {
719
			foreach ( $cached_keys as $key ) {
720
				wp_cache_delete( $key, $group );
721
			}
722
723
			wp_cache_delete( 'cached_keys', $group );
724
		}
725
	}
726
727
	/**
728
	 * @deprecated 2.05.06
729
	 * @codeCoverageIgnore
730
	 */
731
	public function upgrade() {
732
		FrmDeprecated::upgrade();
733
	}
734
735
	/**
736
	 * @deprecated 2.05.06
737
	 * @codeCoverageIgnore
738
	 */
739
	public function collation() {
740
		return FrmDeprecated::collation();
741
	}
742
743
	/**
744
	 * @deprecated 2.05.06
745
	 * @codeCoverageIgnore
746
	 */
747
	public function uninstall() {
748
		FrmDeprecated::uninstall();
749
	}
750
}
751