WordPoints_Un_Installer_Base   F
last analyzed

Complexity

Total Complexity 165

Size/Duplication

Total Lines 1909
Duplicated Lines 4.61 %

Importance

Changes 0
Metric Value
dl 88
loc 1909
rs 0.5217
c 0
b 0
f 0
wmc 165

63 Methods

Rating   Name   Duplication   Size   Complexity  
A do_per_site_uninstall() 0 12 4
A uninstall_transient() 0 6 2
A install_site() 0 11 3
A uninstall_custom_caps() 0 3 1
B maybe_update_tables_to_utf8mb4() 0 16 5
A map_uninstall_shortcut() 0 14 4
A unset_network_installed() 0 2 1
A uninstall_network() 0 7 2
B prepare_uninstall_list_tables() 0 24 5
A add_installed_site_id() 0 17 3
B uninstall_option() 27 27 3
A install_db_schema() 0 6 2
A before_update() 0 2 1
A set_component_version() 0 13 2
A get_db_version() 0 3 1
F uninstall_() 0 49 10
A get_all_site_ids() 0 7 1
A get_updates_for() 0 3 1
C prepare_to_update() 0 25 7
B before_uninstall() 0 30 4
A load_base_dependencies() 0 11 1
A unset_option() 0 6 2
A set_network_update_skipped() 0 2 1
B __construct() 0 22 4
A uninstall_table() 0 14 2
A map_shortcuts() 0 22 3
A uninstall_single() 0 7 2
A unset_db_version() 0 3 1
A uninstall_site() 0 7 2
B get_db_schema() 9 29 4
A after_update() 0 1 1
A set_network_install_skipped() 0 2 1
A uninstall_points_hook() 0 3 1
A no_interruptions() 0 2 1
A do_per_site_install() 0 9 1
A set_db_version() 0 3 1
B uninstall_metadata() 0 29 5
A update_() 0 4 2
A validate_site_ids() 16 16 3
A do_per_site_update() 0 7 3
B get_installed_site_ids() 0 24 3
B prepare_uninstall_non_per_site_items() 0 25 5
A unset_network_update_skipped() 0 2 1
B install_on_site() 0 27 1
A delete_installed_site_ids() 0 6 2
A install_custom_caps() 0 4 2
A uninstall_meta_boxes() 0 22 3
B uninstall_network_option() 24 24 2
B install() 0 71 6
A uninstall_widget() 0 3 1
A unset_network_install_skipped() 0 2 1
A is_network_installed() 0 6 2
A set_network_installed() 0 2 1
A load_dependencies() 0 1 1
A before_install() 0 3 1
C update() 0 74 7
A skip_per_site_install() 0 5 2
B uninstall() 0 59 5
A maybe_load_custom_caps() 0 11 3
A set_option() 0 6 2
A install_network() 0 6 2
A install_single() 0 5 1
C uninstall_list_table() 9 46 8

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like WordPoints_Un_Installer_Base often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WordPoints_Un_Installer_Base, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Base un/installer class.
5
 *
6
 * @package WordPoints
7
 * @since 2.3.0
8
 */
9
10
/**
11
 * Base class to be extended for un/installing a plugin/component/module.
12
 *
13
 * @since 1.8.0
14
 * @deprecated 2.4.0 Use the new installables API instead.
15
 */
16
abstract class WordPoints_Un_Installer_Base {
17
18
	/**
19
	 * Bitmask value directing an install to be performed.
20
	 *
21
	 * @since 2.1.0
22
	 *
23
	 * @const int
24
	 */
25
	const DO_INSTALL = 0;
26
27
	/**
28
	 * Bitmask value directing an install to be skipped.
29
	 *
30
	 * @since 2.1.0
31
	 *
32
	 * @const int
33
	 */
34
	const SKIP_INSTALL = 1;
35
36
	/**
37
	 * Bitmask value indicating that manual installation is required.
38
	 *
39
	 * @since 2.1.0
40
	 *
41
	 * @const int
42
	 */
43
	const REQUIRES_MANUAL_INSTALL = 2;
44
45
	//
46
	// Protected Vars.
47
	//
48
49
	/**
50
	 * The type of entity.
51
	 *
52
	 * For example, 'module' or 'component'.
53
	 *
54
	 * Note that this is singular, even though in the 'wordpoints_data' option the
55
	 * plural forms are used for legacy reasons.
56
	 *
57
	 * @since 2.0.0
58
	 *
59
	 * @var string
60
	 */
61
	protected $type;
62
63
	/**
64
	 * The slug of this entity.
65
	 *
66
	 * @since 2.0.0
67
	 *
68
	 * @var string
69
	 */
70
	protected $slug;
71
72
	/**
73
	 * The code version of the entity.
74
	 *
75
	 * @since 2.0.0
76
	 *
77
	 * @var string
78
	 */
79
	protected $version;
80
81
	/**
82
	 * The prefix to use for the name of the options the un/installer uses.
83
	 *
84
	 * @since 1.8.0
85
	 * @deprecated 2.0.0 The $slug and $type properties are used instead.
86
	 *
87
	 * @type string $option_prefix
88
	 */
89
	protected $option_prefix;
90
91
	/**
92
	 * A list of versions of this entity with updates.
93
	 *
94
	 * @since 1.8.0
95
	 *
96
	 * @type array $updates
97
	 */
98
	protected $updates = array();
99
100
	/**
101
	 * Whether the entity is being installed network wide.
102
	 *
103
	 * @since 1.8.0
104
	 *
105
	 * @type bool $network_wide
106
	 */
107
	protected $network_wide;
108
109
	/**
110
	 * The version being updated from.
111
	 *
112
	 * @since 1.8.0
113
	 *
114
	 * @type string $updating_from
115
	 */
116
	protected $updating_from;
117
118
	/**
119
	 * The version being updated to.
120
	 *
121
	 * @since 1.8.0
122
	 *
123
	 * @type string $updating_to
124
	 */
125
	protected $updating_to;
126
127
	/**
128
	 * The action currently being performed.
129
	 *
130
	 * Possible values: 'install', 'update', 'uninstall'.
131
	 *
132
	 * @since 2.0.0
133
	 *
134
	 * @type string $action
135
	 */
136
	protected $action;
137
138
	/**
139
	 * The current context being un/installed/updated.
140
	 *
141
	 * Possible values: 'single', 'site', 'network'.
142
	 *
143
	 * @since 2.0.0
144
	 *
145
	 * @type string $context
146
	 */
147
	protected $context;
148
149
	/**
150
	 * Database schema for this entity.
151
	 *
152
	 * @since 1.0.0
153
	 *
154
	 * @var array[] $schema {
155
	 *      @type array[] $single {
156
	 *            Schema for a single site (non-multisite) install.
157
	 *
158
	 *             @type string[] $tables DB field schema for tables (i.e., the part of the
159
	 *                                    CREATE TABLE query within the main parenthesis)
160
	 *                                    indexed by table name (base DB prefix will be
161
	 *                                    prepended).
162
	 *      }
163
	 *      @type array[] $site Schema for each site in a multisite network. See
164
	 *                          $single for list of keys. Note that 'tables' will be
165
	 *                          prepended with blog prefix instead.
166
	 *      @type array[] $network Schema for a multisite network. See $single for
167
	 *                             list of keys.
168
	 *      @type array[] $local Schema for each site in a multisite network, and on
169
	 *                           a single site install. See $single for list of keys.
170
	 *      @type array[] $global Schema for a multisite network and on a single site
171
	 *                            install. See $single for list of keys.
172
	 *      @type array[] $universal Schema for $single, $site, and $network. See
173
	 *                               $single for list of keys.
174
	 * }
175
	 */
176
	protected $schema = array();
177
178
	/**
179
	 * List of things to uninstall.
180
	 *
181
	 * @since 2.0.0
182
	 * @since 2.1.0 Added support for uninstalling meta boxes.
183
	 * @since 2.1.0 Deprecated $list_tables in favor of $*['list_tables'].
184
	 * @since 2.4.0 Added support for uninstalling transients.
185
	 *
186
	 * @type array[] $uninstall {
187
	 *       Different kinds of things to uninstall.
188
	 *
189
	 *       @type array[] $list_tables Deprecated. Use the list tables key in one of
190
	 *                                  the below options instead.
191
	 *       @type array[] $single {
192
	 *             Things to be uninstalled on a single site (non-multisite) install.
193
	 *
194
	 *             @type string[] $user_meta    A list of keys for user metadata to delete.
195
	 *             @type string[] $options      A list of options to delete.
196
	 *             @type string[] $transients   A list of transients to delete.
197
	 *             @type string[] $widgets      A list of widget slugs to uninstall.
198
	 *             @type string[] $points_hooks A list of points hooks to uninstall.
199
	 *             @type string[] $tables       A list of tables to uninstall. Base
200
	 *                                          DB prefix will be prepended.
201
	 *             @type string[] $comment_meta A list of keys for comment metadata
202
	 *                                          to delete.
203
	 *             @type array[]  $meta_boxes {
204
	 *                   Admin screens with meta boxes to uninstall, keyed by screen slug.
205
	 *
206
	 *                   @type string   $parent  The slug of the parent screen.
207
	 *                                           Defaults to 'wordpoints'. Use
208
	 *                                           'toplevel' if no parent.
209
	 *                   @type string[] $options Extra meta box options provided by
210
	 *                                           this screen.
211
	 *             }
212
	 *             @type array[] $list_tables {
213
	 *                   Admin screens with list tables to uninstall, keyed by screen slug.
214
	 *
215
	 *                   @type string   $parent  The slug of the parent screen.
216
	 *                   @type string[] $options The options provided by this screen.
217
	 *                                           Defaults to [ 'per_page' ].
218
	 *             }
219
	 *       }
220
	 *       @type array[] $site Things to be uninstalled on each site in a multisite
221
	 *                           network. See $single for list of keys. Note that
222
	 *                           'tables' will be prepended with blog prefix instead.
223
	 *       @type array[] $network Things to be uninstalled on a multisite network.
224
	 *                              See $single for list of keys. $options refers to
225
	 *                              network options.
226
	 *       @type array[] $local Things to be uninstalled on each site in a multisite
227
	 *                            network, and on a single site install. See $single
228
	 *                            for list of keys.
229
	 *       @type array[] $global Things to be uninstalled on a multisite network
230
	 *                             and on a single site install. See $single for list
231
	 *                             of keys.
232
	 *       @type array[] $universal Things to be uninstalled for $single, $site,
233
	 *                                and $network. See $single for list of keys.
234
	 * }
235
	 */
236
	protected $uninstall = array();
237
238
	/**
239
	 * The function to use to get the user capabilities used by this entity.
240
	 *
241
	 * The function should return an array of capabilities of the format processed
242
	 * by {@see wordpoints_add_custom_caps()}.
243
	 *
244
	 * @since 2.0.0
245
	 *
246
	 * @type callable $custom_caps_getter
247
	 */
248
	protected $custom_caps_getter;
249
250
	/**
251
	 * The entity's capabilities.
252
	 *
253
	 * Used to hold the list of capabilities during install, update, and uninstall,
254
	 * so that they don't have to be retrieved all over again for each site (if
255
	 * multisite).
256
	 *
257
	 * The array is of the format needed by {@see wordpoints_add_custom_caps()}.
258
	 *
259
	 * @since 2.0.0
260
	 *
261
	 * @type array $custom_caps
262
	 */
263
	protected $custom_caps;
264
265
	/**
266
	 * The entity's capabilities (keys only).
267
	 *
268
	 * Used to hold the list of capabilities during install and uninstall, so that
269
	 * they don't have to be retrieved all over again for each site (if multisite).
270
	 *
271
	 * The array is of the form needed by {@see wordpoints_remove_custom_caps()}.
272
	 *
273
	 * @since 2.0.0
274
	 *
275
	 * @type array $custom_caps_keys
276
	 */
277
	protected $custom_caps_keys;
278
279
	/**
280
	 * Installable object for the entity.
281
	 *
282
	 * @since 2.4.0
283
	 *
284
	 * @var WordPoints_Installable
285
	 */
286
	protected $installable;
287
288
	//
289
	// Public Methods.
290
	//
291
292
	/**
293
	 * Constructs the un/installer with the entity's slug and version.
294
	 *
295
	 * @since 2.0.0
296
	 *
297
	 * @param string $slug    The slug of the entity.
298
	 * @param string $version The current code version of the entity.
299
	 */
300
	public function __construct( $slug = null, $version = null ) {
301
302
		_deprecated_function( __METHOD__, '2.4.0' );
0 ignored issues
show
Bug introduced by
The function _deprecated_function was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

302
		/** @scrutinizer ignore-call */ 
303
  _deprecated_function( __METHOD__, '2.4.0' );
Loading history...
303
304
		if ( ! isset( $slug ) ) {
305
			_doing_it_wrong( __METHOD__, 'The $slug parameter is required.', '2.0.0' );
0 ignored issues
show
Bug introduced by
The function _doing_it_wrong was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

305
			/** @scrutinizer ignore-call */ 
306
   _doing_it_wrong( __METHOD__, 'The $slug parameter is required.', '2.0.0' );
Loading history...
306
		}
307
308
		if ( ! isset( $version ) ) {
309
			_doing_it_wrong( __METHOD__, 'The $version parameter is required.', '2.0.0' );
310
		}
311
312
		$this->slug        = $slug;
313
		$this->version     = $version;
314
		$this->installable = new WordPoints_Installable_Basic(
315
			$this->type
316
			, $this->slug
317
			, $this->version
318
		);
319
320
		if ( isset( $this->option_prefix ) ) {
0 ignored issues
show
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

320
		if ( isset( /** @scrutinizer ignore-deprecated */ $this->option_prefix ) ) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
321
			_deprecated_argument( __METHOD__, '2.0.0', 'The $option_prefix property is deprecated.' );
0 ignored issues
show
Bug introduced by
The function _deprecated_argument was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

321
			/** @scrutinizer ignore-call */ 
322
   _deprecated_argument( __METHOD__, '2.0.0', 'The $option_prefix property is deprecated.' );
Loading history...
322
		}
323
	}
324
325
	/**
326
	 * Run the install routine.
327
	 *
328
	 * @since 1.8.0
329
	 *
330
	 * @param bool $network Whether the install should be network-wide on multisite.
331
	 */
332
	public function install( $network ) {
333
334
		$this->action = 'install';
335
336
		$this->network_wide = $network;
337
338
		$this->no_interruptions();
339
340
		$hooks      = wordpoints_hooks();
341
		$hooks_mode = $hooks->get_current_mode();
342
		$hooks->set_current_mode( 'standard' );
343
344
		$this->before_install();
345
346
		/**
347
		 * Include the upgrade script so that we can use dbDelta() to create DBs.
348
		 *
349
		 * @since 1.8.0
350
		 */
351
		require_once ABSPATH . 'wp-admin/includes/upgrade.php';
0 ignored issues
show
Bug introduced by
The constant ABSPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
352
353
		if ( is_multisite() ) {
0 ignored issues
show
Bug introduced by
The function is_multisite was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

353
		if ( /** @scrutinizer ignore-call */ is_multisite() ) {
Loading history...
354
355
			$this->context = 'network';
356
			$hooks->set_current_mode( 'network' );
357
358
			$this->install_network();
359
360
			$this->context = 'site';
361
			$hooks->set_current_mode( 'standard' );
362
363
			if ( $network ) {
364
365
				$this->set_network_installed();
366
367
				$skip_per_site_install = $this->skip_per_site_install();
368
369
				if ( ! ( $skip_per_site_install & self::SKIP_INSTALL ) ) {
370
371
					$ms_switched_state = new WordPoints_Multisite_Switched_State();
372
					$ms_switched_state->backup();
373
374
					foreach ( $this->get_all_site_ids() as $blog_id ) {
375
						switch_to_blog( $blog_id );
0 ignored issues
show
Bug introduced by
The function switch_to_blog was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

375
						/** @scrutinizer ignore-call */ 
376
      switch_to_blog( $blog_id );
Loading history...
376
						$this->install_site();
377
					}
378
379
					$ms_switched_state->restore();
380
				}
381
382
				if ( $skip_per_site_install & self::REQUIRES_MANUAL_INSTALL ) {
383
384
					// We'll check this later and let the user know that per-site
385
					// install was skipped.
386
					$this->set_network_install_skipped();
387
				}
388
389
			} else {
390
391
				$this->install_site();
392
				$this->add_installed_site_id();
393
			}
394
395
		} else {
396
397
			$this->context = 'single';
398
			$this->install_single();
399
400
		} // End if ( is_multisite() ) else.
401
402
		$hooks->set_current_mode( $hooks_mode );
403
	}
404
405
	/**
406
	 * Run the install routine on a certain site on the network.
407
	 *
408
	 * @since 2.0.0
409
	 *
410
	 * @param int $site_id The ID of the site to install on.
411
	 */
412
	public function install_on_site( $site_id ) {
413
414
		$this->action       = 'install';
415
		$this->network_wide = true;
416
417
		$this->no_interruptions();
418
419
		$hooks      = wordpoints_hooks();
420
		$hooks_mode = $hooks->get_current_mode();
421
		$hooks->set_current_mode( 'standard' );
422
423
		$this->before_install();
424
425
		/**
426
		 * Include the upgrade script so that we can use dbDelta() to create DBs.
427
		 *
428
		 * @since 1.8.0
429
		 */
430
		require_once ABSPATH . 'wp-admin/includes/upgrade.php';
0 ignored issues
show
Bug introduced by
The constant ABSPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
431
432
		$this->context = 'site';
433
434
		switch_to_blog( $site_id );
0 ignored issues
show
Bug introduced by
The function switch_to_blog was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

434
		/** @scrutinizer ignore-call */ 
435
  switch_to_blog( $site_id );
Loading history...
435
		$this->install_site();
436
		restore_current_blog();
0 ignored issues
show
Bug introduced by
The function restore_current_blog was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

436
		/** @scrutinizer ignore-call */ 
437
  restore_current_blog();
Loading history...
437
438
		$hooks->set_current_mode( $hooks_mode );
439
	}
440
441
	/**
442
	 * Run the uninstallation routine.
443
	 *
444
	 * @since 1.8.0
445
	 */
446
	public function uninstall() {
447
448
		$this->action = 'uninstall';
449
450
		$this->load_base_dependencies();
451
		$this->load_dependencies();
452
453
		$this->no_interruptions();
454
455
		$hooks      = wordpoints_hooks();
456
		$hooks_mode = $hooks->get_current_mode();
457
		$hooks->set_current_mode( 'standard' );
458
459
		$this->before_uninstall();
460
461
		if ( is_multisite() ) {
0 ignored issues
show
Bug introduced by
The function is_multisite was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

461
		if ( /** @scrutinizer ignore-call */ is_multisite() ) {
Loading history...
462
463
			if ( $this->do_per_site_uninstall() ) {
464
465
				$this->context = 'site';
466
467
				$ms_switched_state = new WordPoints_Multisite_Switched_State();
468
				$ms_switched_state->backup();
469
470
				$site_ids = $this->get_installed_site_ids();
471
472
				foreach ( $site_ids as $blog_id ) {
473
					switch_to_blog( $blog_id );
0 ignored issues
show
Bug introduced by
The function switch_to_blog was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

473
					/** @scrutinizer ignore-call */ 
474
     switch_to_blog( $blog_id );
Loading history...
474
					$this->uninstall_site();
475
				}
476
477
				$ms_switched_state->restore();
478
479
				unset( $ms_switched_state, $site_ids );
480
			}
481
482
			$this->context = 'network';
483
			$hooks->set_current_mode( 'network' );
484
485
			$this->uninstall_network();
486
487
			$this->delete_installed_site_ids();
488
489
			// If WordPoints is being uninstalled, the options will already have been
490
			// deleted, and calling these methods will actually create them again.
491
			if ( 'wordpoints' !== $this->slug ) {
492
				$this->unset_network_installed();
493
				$this->unset_network_install_skipped();
494
				$this->unset_network_update_skipped();
495
			}
496
497
		} else {
498
499
			$this->context = 'single';
500
			$this->uninstall_single();
501
502
		} // End if ( is_multisite() ) else.
503
504
		$hooks->set_current_mode( $hooks_mode );
505
	}
506
507
	/**
508
	 * Prepares to update the entity.
509
	 *
510
	 * @since 2.0.0
511
	 *
512
	 * @param string $from    The version to update from.
513
	 * @param string $to      The version to update to.
514
	 * @param bool   $network Whether the entity is network active. Defaults to the
515
	 *                        state of WordPoints itself.
516
	 */
517
	protected function prepare_to_update( $from, $to, $network ) {
518
519
		$this->action = 'update';
520
521
		if ( null === $network ) {
522
			$network = is_wordpoints_network_active();
523
		}
524
525
		$this->network_wide  = $network;
526
		$this->updating_from = ( null === $from ) ? $this->get_db_version() : $from;
527
		$this->updating_to   = ( null === $to ) ? $this->version : $to;
528
529
		$updates = array();
530
531
		foreach ( $this->updates as $version => $types ) {
532
533
			if (
534
				version_compare( $this->updating_from, $version, '<' )
535
				&& version_compare( $this->updating_to, $version, '>=' )
536
			) {
537
				$updates[ str_replace( array( '.', '-' ), '_', $version ) ] = $types;
538
			}
539
		}
540
541
		$this->updates = $updates;
542
	}
543
544
	/**
545
	 * Update the entity.
546
	 *
547
	 * @since 1.8.0
548
	 *
549
	 * @param string $from    The version to update from.
550
	 * @param string $to      The version to update to.
551
	 * @param bool   $network Whether the entity is network active. Defaults to the
552
	 *                        state of WordPoints itself.
553
	 */
554
	public function update( $from = null, $to = null, $network = null ) {
555
556
		$this->prepare_to_update( $from, $to, $network );
557
558
		if ( empty( $this->updates ) ) {
559
			return;
560
		}
561
562
		$this->no_interruptions();
563
564
		$hooks      = wordpoints_hooks();
565
		$hooks_mode = $hooks->get_current_mode();
566
		$hooks->set_current_mode( 'standard' );
567
568
		$this->before_update();
569
570
		/**
571
		 * Include the upgrade script so that we can use dbDelta() to create DBs.
572
		 *
573
		 * @since 1.8.0
574
		 */
575
		require_once ABSPATH . 'wp-admin/includes/upgrade.php';
0 ignored issues
show
Bug introduced by
The constant ABSPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
576
577
		if ( is_multisite() ) {
0 ignored issues
show
Bug introduced by
The function is_multisite was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

577
		if ( /** @scrutinizer ignore-call */ is_multisite() ) {
Loading history...
578
579
			$this->context = 'network';
580
			$hooks->set_current_mode( 'network' );
581
582
			$this->update_( 'network', $this->get_updates_for( 'network' ) );
583
584
			$this->context = 'site';
585
			$hooks->set_current_mode( 'standard' );
586
587
			if ( $this->network_wide ) {
588
589
				$updates = $this->get_updates_for( 'site' );
590
591
				if ( $updates ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $updates 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...
592
593
					if ( $this->do_per_site_update() ) {
594
595
						$ms_switched_state = new WordPoints_Multisite_Switched_State();
596
						$ms_switched_state->backup();
597
598
						foreach ( $this->get_installed_site_ids() as $blog_id ) {
599
							switch_to_blog( $blog_id );
0 ignored issues
show
Bug introduced by
The function switch_to_blog was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

599
							/** @scrutinizer ignore-call */ 
600
       switch_to_blog( $blog_id );
Loading history...
600
							$this->update_( 'site', $updates );
601
						}
602
603
						$ms_switched_state->restore();
604
605
					} else {
606
607
						// We'll check this later and let the user know that per-site
608
						// update was skipped.
609
						$this->set_network_update_skipped();
610
					}
611
				}
612
613
			} else {
614
615
				$this->update_( 'site', $this->get_updates_for( 'site' ) );
616
			}
617
618
		} else {
619
620
			$this->context = 'single';
621
			$this->update_( 'single', $this->get_updates_for( 'single' ) );
622
623
		} // End if ( is_multisite() ) else.
624
625
		$this->after_update();
626
627
		$hooks->set_current_mode( $hooks_mode );
628
	}
629
630
	//
631
	// Protected Methods.
632
	//
633
634
	/**
635
	 * Prevent any interruptions from occurring during the update.
636
	 *
637
	 * @since 2.2.0
638
	 */
639
	protected function no_interruptions() {
640
		wordpoints_prevent_interruptions();
641
	}
642
643
	/**
644
	 * Check whether we should run the install for each site in the network.
645
	 *
646
	 * On large networks we don't attempt the per-site install.
647
	 *
648
	 * @since 2.1.0
649
	 *
650
	 * @return int Whether to skip the per-site installation.
651
	 */
652
	protected function skip_per_site_install() {
653
654
		return ( wp_is_large_network() )
0 ignored issues
show
Bug introduced by
The function wp_is_large_network was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

654
		return ( /** @scrutinizer ignore-call */ wp_is_large_network() )
Loading history...
655
			? self::SKIP_INSTALL | self::REQUIRES_MANUAL_INSTALL
656
			: self::DO_INSTALL;
657
	}
658
659
	/**
660
	 * Check whether we should run the install for each site in the network.
661
	 *
662
	 * On large networks we don't attempt the per-site install.
663
	 *
664
	 * @since 1.8.0
665
	 * @deprecated 2.1.0 Use self::skip_per_site_install() instead.
666
	 *
667
	 * @return bool Whether to do the per-site installation.
668
	 */
669
	protected function do_per_site_install() {
670
671
		_deprecated_function(
0 ignored issues
show
Bug introduced by
The function _deprecated_function was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

671
		/** @scrutinizer ignore-call */ 
672
  _deprecated_function(
Loading history...
672
			__METHOD__
673
			, '2.1.0'
674
			, __CLASS__ . '::skip_per_site_install'
675
		);
676
677
		return ! wp_is_large_network();
0 ignored issues
show
Bug introduced by
The function wp_is_large_network was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

677
		return ! /** @scrutinizer ignore-call */ wp_is_large_network();
Loading history...
678
	}
679
680
	/**
681
	 * Get the IDs of all sites on the network.
682
	 *
683
	 * @since 1.8.0
684
	 *
685
	 * @return array The IDs of all sites on the network.
686
	 */
687
	protected function get_all_site_ids() {
688
689
		return get_sites(
0 ignored issues
show
Bug introduced by
The function get_sites was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

689
		return /** @scrutinizer ignore-call */ get_sites(
Loading history...
690
			array(
691
				'fields'     => 'ids',
692
				'network_id' => get_current_network_id(),
0 ignored issues
show
Bug introduced by
The function get_current_network_id was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

692
				'network_id' => /** @scrutinizer ignore-call */ get_current_network_id(),
Loading history...
693
				'number'     => 0,
694
			)
695
		);
696
	}
697
698
	/**
699
	 * Set an option in the database for this entity.
700
	 *
701
	 * @since 2.0.0
702
	 *
703
	 * @param string $option The name of the option to set.
704
	 * @param mixed  $value  The value of the option.
705
	 */
706
	private function set_option( $option, $value = true ) {
707
708
		if ( isset( $this->option_prefix ) ) {
0 ignored issues
show
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

708
		if ( isset( /** @scrutinizer ignore-deprecated */ $this->option_prefix ) ) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
709
			update_site_option( "{$this->option_prefix}{$option}", $value );
0 ignored issues
show
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

709
			update_site_option( "{/** @scrutinizer ignore-deprecated */ $this->option_prefix}{$option}", $value );

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
Bug introduced by
The function update_site_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

709
			/** @scrutinizer ignore-call */ 
710
   update_site_option( "{$this->option_prefix}{$option}", $value );
Loading history...
710
		} else {
711
			$this->installable->{"set_{$option}"}( $value );
712
		}
713
	}
714
715
	/**
716
	 * Delete an option in the database for this entity.
717
	 *
718
	 * @since 2.0.0
719
	 *
720
	 * @param string $option The name of the option to delete.
721
	 */
722
	private function unset_option( $option ) {
723
724
		if ( isset( $this->option_prefix ) ) {
0 ignored issues
show
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

724
		if ( isset( /** @scrutinizer ignore-deprecated */ $this->option_prefix ) ) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
725
			delete_site_option( "{$this->option_prefix}{$option}" );
0 ignored issues
show
Bug introduced by
The function delete_site_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

725
			/** @scrutinizer ignore-call */ 
726
   delete_site_option( "{$this->option_prefix}{$option}" );
Loading history...
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

725
			delete_site_option( "{/** @scrutinizer ignore-deprecated */ $this->option_prefix}{$option}" );

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
726
		} else {
727
			$this->installable->{"unset_{$option}"}();
728
		}
729
	}
730
731
	/**
732
	 * Check if this entity is network installed.
733
	 *
734
	 * @since 1.8.0
735
	 *
736
	 * @return bool Whether the code is network installed.
737
	 */
738
	protected function is_network_installed() {
739
740
		if ( isset( $this->option_prefix ) ) {
0 ignored issues
show
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

740
		if ( isset( /** @scrutinizer ignore-deprecated */ $this->option_prefix ) ) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
741
			return (bool) get_site_option( "{$this->option_prefix}network_installed" );
0 ignored issues
show
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

741
			return (bool) get_site_option( "{/** @scrutinizer ignore-deprecated */ $this->option_prefix}network_installed" );

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
Bug introduced by
The function get_site_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

741
			return (bool) /** @scrutinizer ignore-call */ get_site_option( "{$this->option_prefix}network_installed" );
Loading history...
742
		} else {
743
			return $this->installable->is_network_installed();
744
		}
745
	}
746
747
	/**
748
	 * Set this entity's status as network-installed in the database.
749
	 *
750
	 * @since 2.0.0
751
	 */
752
	protected function set_network_installed() {
753
		$this->set_option( 'network_installed' );
754
	}
755
756
	/**
757
	 * Delete this entity's status as network-installed.
758
	 *
759
	 * @since 2.0.0
760
	 */
761
	protected function unset_network_installed() {
762
		$this->unset_option( 'network_installed' );
763
	}
764
765
	/**
766
	 * Set that the per-site network installation has been skipped in the database.
767
	 *
768
	 * @since 2.0.0
769
	 */
770
	protected function set_network_install_skipped() {
771
		$this->set_option( 'network_install_skipped' );
772
	}
773
774
	/**
775
	 * Delete the network-install skipped flag for this entity from the database.
776
	 *
777
	 * @since 2.0.0
778
	 */
779
	protected function unset_network_install_skipped() {
780
		$this->unset_option( 'network_install_skipped' );
781
	}
782
783
	/**
784
	 * Set that per-site network-updating has been skipped in the database.
785
	 *
786
	 * @since 2.0.0
787
	 */
788
	protected function set_network_update_skipped() {
789
		$this->set_option( 'network_update_skipped', $this->updating_from );
790
	}
791
792
	/**
793
	 * Delete the network-update skipped flag for this entity from the database.
794
	 *
795
	 * @since 2.0.0
796
	 */
797
	protected function unset_network_update_skipped() {
798
		$this->unset_option( 'network_update_skipped' );
799
	}
800
801
	/**
802
	 * Check if we should run the uninstall for each site on the network.
803
	 *
804
	 * On large multisite networks we don't attempt the per-site uninstall.
805
	 *
806
	 * @since 1.8.0
807
	 *
808
	 * @return bool Whether to do the per-site uninstallation.
809
	 */
810
	protected function do_per_site_uninstall() {
811
812
		if ( wp_is_large_network() ) {
0 ignored issues
show
Bug introduced by
The function wp_is_large_network was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

812
		if ( /** @scrutinizer ignore-call */ wp_is_large_network() ) {
Loading history...
813
814
			if ( $this->is_network_installed() ) {
815
				return false;
816
			} elseif ( count( $this->get_installed_site_ids() ) > 10000 ) {
817
				return false;
818
			}
819
		}
820
821
		return true;
822
	}
823
824
	/**
825
	 * Check if we should run the update for each site on the network.
826
	 *
827
	 * On large multisite networks we don't attempt the per-site update.
828
	 *
829
	 * @since 1.8.0
830
	 *
831
	 * @return bool Whether to do the per-site update.
832
	 */
833
	protected function do_per_site_update() {
834
835
		if ( $this->is_network_installed() && wp_is_large_network() ) {
0 ignored issues
show
Bug introduced by
The function wp_is_large_network was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

835
		if ( $this->is_network_installed() && /** @scrutinizer ignore-call */ wp_is_large_network() ) {
Loading history...
836
			return false;
837
		}
838
839
		return true;
840
	}
841
842
	/**
843
	 * Get the IDs of all sites on which this is installed.
844
	 *
845
	 * @since 1.8.0
846
	 *
847
	 * @return array The IDs of the sites where this entity is installed.
848
	 */
849
	protected function get_installed_site_ids() {
850
851
		if ( $this->is_network_installed() ) {
852
853
			$sites = $this->get_all_site_ids();
854
855
		} else {
856
857
			if ( isset( $this->option_prefix ) ) {
0 ignored issues
show
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

857
			if ( isset( /** @scrutinizer ignore-deprecated */ $this->option_prefix ) ) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
858
859
				$sites = wordpoints_get_array_option(
860
					"{$this->option_prefix}installed_sites"
0 ignored issues
show
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

860
					"{/** @scrutinizer ignore-deprecated */ $this->option_prefix}installed_sites"

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
861
					, 'site'
862
				);
863
864
				$sites = $this->validate_site_ids( $sites );
865
866
			} else {
867
868
				$sites = $this->installable->get_installed_site_ids();
869
			}
870
		}
871
872
		return $sites;
873
	}
874
875
	/**
876
	 * Add a site's ID to the list of the installed sites.
877
	 *
878
	 * @since 1.8.0
879
	 *
880
	 * @param int $id The ID of the site to add. Defaults to the current site's ID.
881
	 */
882
	protected function add_installed_site_id( $id = null ) {
883
884
		if ( ! isset( $this->option_prefix ) ) {
0 ignored issues
show
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

884
		if ( ! isset( /** @scrutinizer ignore-deprecated */ $this->option_prefix ) ) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
885
			$this->installable->add_installed_site_id( $id );
886
			return;
887
		}
888
889
		if ( empty( $id ) ) {
890
			$id = get_current_blog_id();
0 ignored issues
show
Bug introduced by
The function get_current_blog_id was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

890
			$id = /** @scrutinizer ignore-call */ get_current_blog_id();
Loading history...
891
		}
892
893
		$option_name = "{$this->option_prefix}installed_sites";
0 ignored issues
show
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

893
		$option_name = "{/** @scrutinizer ignore-deprecated */ $this->option_prefix}installed_sites";

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
894
895
		$sites   = wordpoints_get_array_option( $option_name, 'site' );
896
		$sites[] = $id;
897
898
		update_site_option( $option_name, $sites );
0 ignored issues
show
Bug introduced by
The function update_site_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

898
		/** @scrutinizer ignore-call */ 
899
  update_site_option( $option_name, $sites );
Loading history...
899
	}
900
901
	/**
902
	 * Delete the list of installed sites.
903
	 *
904
	 * @since 2.0.0
905
	 */
906
	protected function delete_installed_site_ids() {
907
908
		if ( ! isset( $this->option_prefix ) ) {
0 ignored issues
show
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

908
		if ( ! isset( /** @scrutinizer ignore-deprecated */ $this->option_prefix ) ) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
909
			$this->installable->delete_installed_site_ids();
910
		} else {
911
			delete_site_option( "{$this->option_prefix}installed_sites" );
0 ignored issues
show
Bug introduced by
The function delete_site_option was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

911
			/** @scrutinizer ignore-call */ 
912
   delete_site_option( "{$this->option_prefix}installed_sites" );
Loading history...
Deprecated Code introduced by
The property WordPoints_Un_Installer_Base::$option_prefix has been deprecated: 2.0.0 The $slug and $type properties are used instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

911
			delete_site_option( "{/** @scrutinizer ignore-deprecated */ $this->option_prefix}installed_sites" );

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
912
		}
913
	}
914
915
	/**
916
	 * Validate a list of site IDs against the database.
917
	 *
918
	 * @since 1.8.0
919
	 *
920
	 * @param array $site_ids The site IDs to validate.
921
	 *
922
	 * @return array The validated site IDs.
923
	 */
924 View Code Duplication
	protected function validate_site_ids( $site_ids ) {
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...
925
926
		if ( empty( $site_ids ) || ! is_array( $site_ids ) ) {
927
			return array();
928
		}
929
930
		$site_ids = get_sites(
0 ignored issues
show
Bug introduced by
The function get_sites was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

930
		$site_ids = /** @scrutinizer ignore-call */ get_sites(
Loading history...
931
			array(
932
				'fields'     => 'ids',
933
				'network_id' => get_current_network_id(),
0 ignored issues
show
Bug introduced by
The function get_current_network_id was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

933
				'network_id' => /** @scrutinizer ignore-call */ get_current_network_id(),
Loading history...
934
				'number'     => 0,
935
				'site__in'   => $site_ids,
936
			)
937
		);
938
939
		return $site_ids;
940
	}
941
942
	/**
943
	 * Get the database version of the entity.
944
	 *
945
	 * Note that this should be used with care during uninstall, since
946
	 * `$this->network_wide` isn't set by default during uninstall.
947
	 *
948
	 * @since 2.0.0
949
	 * @since 2.3.0 Now based on `$this->network_wide` instead of `$this->context`.
950
	 *
951
	 * @return string|false The database version of the entity, or false if not set.
952
	 */
953
	protected function get_db_version() {
954
955
		return $this->installable->get_db_version( $this->network_wide );
956
	}
957
958
	/**
959
	 * Set the version of the entity in the database.
960
	 *
961
	 * @since 2.0.0
962
	 * @since 2.3.0 Now based on `$this->network_wide` instead of `$this->context`.
963
	 *
964
	 * @param string $version The version of the entity.
965
	 */
966
	protected function set_db_version( $version = null ) {
967
968
		$this->installable->set_db_version( $version, $this->network_wide );
969
	}
970
971
	/**
972
	 * Remove the version of the entity from the database.
973
	 *
974
	 * @since 2.0.0
975
	 */
976
	protected function unset_db_version() {
977
978
		$this->installable->unset_db_version( 'network' === $this->context );
979
	}
980
981
	/**
982
	 * Set a component's version.
983
	 *
984
	 * For use when installing a component.
985
	 *
986
	 * @since 1.8.0
987
	 * @deprecated 2.0.0 Use set_db_version() method instead.
988
	 *
989
	 * @param string $component The component's slug.
990
	 * @param string $version   The installed component version.
991
	 */
992
	protected function set_component_version( $component, $version ) {
993
994
		_deprecated_function( __METHOD__, '2.0.0', '::set_db_version()' );
0 ignored issues
show
Bug introduced by
The function _deprecated_function was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

994
		/** @scrutinizer ignore-call */ 
995
  _deprecated_function( __METHOD__, '2.0.0', '::set_db_version()' );
Loading history...
995
996
		$wordpoints_data = wordpoints_get_maybe_network_array_option(
997
			'wordpoints_data'
998
		);
999
1000
		if ( empty( $wordpoints_data['components'][ $component ]['version'] ) ) {
1001
			$wordpoints_data['components'][ $component ]['version'] = $version;
1002
		}
1003
1004
		wordpoints_update_maybe_network_option( 'wordpoints_data', $wordpoints_data );
1005
	}
1006
1007
	/**
1008
	 * Load the capabilities of the entity being un/install/updated, if needed.
1009
	 *
1010
	 * @since 2.0.0
1011
	 */
1012
	protected function maybe_load_custom_caps() {
1013
1014
		if ( empty( $this->custom_caps_getter ) ) {
1015
			return;
1016
		}
1017
1018
		$this->custom_caps      = call_user_func( $this->custom_caps_getter );
1019
		$this->custom_caps_keys = array_keys( $this->custom_caps );
1020
1021
		if ( 'uninstall' === $this->action ) {
1022
			$this->uninstall['local']['custom_caps'] = true;
1023
		}
1024
	}
1025
1026
	/**
1027
	 * Run before installing.
1028
	 *
1029
	 * @since 1.8.0
1030
	 */
1031
	protected function before_install() {
1032
		$this->map_shortcuts( 'schema' );
1033
		$this->maybe_load_custom_caps();
1034
	}
1035
1036
	/**
1037
	 * Install capabilities on the current site.
1038
	 *
1039
	 * @since 2.0.0
1040
	 */
1041
	protected function install_custom_caps() {
1042
1043
		if ( ! empty( $this->custom_caps ) ) {
1044
			wordpoints_add_custom_caps( $this->custom_caps );
1045
		}
1046
	}
1047
1048
	/**
1049
	 * Install the database schema for the current site.
1050
	 *
1051
	 * @since 2.0.0
1052
	 */
1053
	protected function install_db_schema() {
1054
1055
		$schema = $this->get_db_schema();
1056
1057
		if ( ! empty( $schema ) ) {
1058
			dbDelta( $schema );
0 ignored issues
show
Bug introduced by
The function dbDelta was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1058
			/** @scrutinizer ignore-call */ 
1059
   dbDelta( $schema );
Loading history...
1059
		}
1060
	}
1061
1062
	/**
1063
	 * Get the database schema for this entity.
1064
	 *
1065
	 * @since 2.0.0
1066
	 *
1067
	 * @return string The database schema for this entity.
1068
	 */
1069
	public function get_db_schema() {
1070
1071
		if ( ! isset( $this->schema[ $this->context ]['tables'] ) ) {
1072
			return '';
1073
		}
1074
1075
		global $wpdb;
1076
1077
		$schema = '';
1078
1079
		$charset_collate = $wpdb->get_charset_collate();
1080
1081
		if ( 'site' === $this->context ) {
1082
			$prefix = $wpdb->prefix;
1083
		} else {
1084
			$prefix = $wpdb->base_prefix;
1085
		}
1086
1087 View Code Duplication
		foreach ( $this->schema[ $this->context ]['tables'] as $table_name => $table_schema ) {
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...
1088
1089
			$table_name   = str_replace( '`', '``', $table_name );
1090
			$table_schema = trim( $table_schema );
1091
1092
			$schema .= "CREATE TABLE {$prefix}{$table_name} (
1093
				{$table_schema}
1094
			) {$charset_collate};";
1095
		}
1096
1097
		return $schema;
1098
	}
1099
1100
	/**
1101
	 * Run before uninstalling, but after loading dependencies.
1102
	 *
1103
	 * @since 1.8.0
1104
	 */
1105
	protected function before_uninstall() {
1106
1107
		$this->maybe_load_custom_caps();
1108
1109
		$this->prepare_uninstall_list_tables();
1110
		$this->prepare_uninstall_non_per_site_items( 'meta_boxes' );
1111
		$this->map_uninstall_shortcut( 'widgets', 'options', array( 'prefix' => 'widget_' ) );
1112
		$this->map_uninstall_shortcut( 'points_hooks', 'options', array( 'prefix' => 'wordpoints_hook-' ) );
1113
1114
		// Add any tables to uninstall based on the db schema.
1115
		foreach ( $this->schema as $context => $schema ) {
1116
1117
			if ( ! isset( $schema['tables'] ) ) {
1118
				continue;
1119
			}
1120
1121
			if ( ! isset( $this->uninstall[ $context ]['tables'] ) ) {
1122
				$this->uninstall[ $context ]['tables'] = array();
1123
			}
1124
1125
			$this->uninstall[ $context ]['tables'] = array_unique(
1126
				array_merge(
1127
					$this->uninstall[ $context ]['tables']
1128
					, array_keys( $schema['tables'] )
1129
				)
1130
			);
1131
		}
1132
1133
		// This *must* happen *after* the schema and list tables args are parsed.
1134
		$this->map_shortcuts( 'uninstall' );
1135
	}
1136
1137
	/**
1138
	 * Prepare to uninstall list tables.
1139
	 *
1140
	 * The 'list_tables' element of the {@see self::$uninstall} configuration array
1141
	 * can provide a list of screens which provide list tables. In this way it acts
1142
	 * as an easy shortcut, rather than all of the metadata keys associated with a
1143
	 * list table having to be supplied in the 'user_meta' element. Duplication is
1144
	 * thus reduced, and it is not longer necessary to mess with the complexity of
1145
	 * list table options.
1146
	 *
1147
	 * The 'list_tables' element is only a shortcut though, and this function takes
1148
	 * the values provided in it and adds the appropriate entries to the 'user_meta'
1149
	 * to uninstall.
1150
	 *
1151
	 * List tables have two main configuration options, which are both saves as user
1152
	 * metadata:
1153
	 * - Hidden Columns
1154
	 * - Screen Options
1155
	 *
1156
	 * The hidden columns metadata is removed by default, as well as the 'per_page'
1157
	 * screen options.
1158
	 *
1159
	 * A note on screen options: they are retrieved with get_user_option(), however,
1160
	 * they are saved by update_user_option() with the $global argument set to true.
1161
	 * Because of this, even on multisite, they are saved like regular user metadata,
1162
	 * which is network wide, *not* prefixed for each site.
1163
	 *
1164
	 * @since 2.0.0
1165
	 */
1166
	protected function prepare_uninstall_list_tables() {
1167
1168
		if ( ! isset( $this->uninstall['list_tables'] ) ) {
1169
			return;
1170
		}
1171
1172
		_deprecated_argument(
0 ignored issues
show
Bug introduced by
The function _deprecated_argument was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1172
		/** @scrutinizer ignore-call */ 
1173
  _deprecated_argument(
Loading history...
1173
			__METHOD__
1174
			, '2.1.0'
1175
			, '$this->uninstall["list_tables"] is deprecated, use $this->uninstall[*]["list_tables"] instead.'
1176
		);
1177
1178
		// Loop through all of the list table screens.
1179
		foreach ( $this->uninstall['list_tables'] as $screen_id => $args ) {
1180
1181
			// Back-compat for pre-2.1.0.
1182
			if ( isset( $args['parent'] ) && '_page' === substr( $args['parent'], -5 /* _page */ ) ) {
1183
				$args['parent'] = substr( $args['parent'], 0, -5 );
1184
			}
1185
1186
			$this->uninstall['universal']['list_tables'][ $screen_id ] = $args;
1187
		}
1188
1189
		$this->prepare_uninstall_non_per_site_items( 'list_tables' );
1190
	}
1191
1192
	/**
1193
	 * Prepare to uninstall items which don't need to be uninstalled per-site.
1194
	 *
1195
	 * Screen options are retrieved with get_user_option(), however, they are saved
1196
	 * by update_user_option() with the $global argument set to true. Because of
1197
	 * this, even on multisite, they are saved like regular user metadata, which is
1198
	 * network wide, *not* prefixed for each site.
1199
	 *
1200
	 * So we only need to run the meta box and list table uninstall for network and
1201
	 * single (i.e.,  "global"), even if the screen is "universal".
1202
	 *
1203
	 * @since 2.1.0
1204
	 *
1205
	 * @param string $items_key The key name of the items in the uninstall[*] arrays.
1206
	 */
1207
	protected function prepare_uninstall_non_per_site_items( $items_key ) {
1208
1209
		$map = array(
1210
			'universal' => array( 'global' ),
1211
			'site'      => array( 'network' ),
1212
			'local'     => array( 'network', 'single' ),
1213
		);
1214
1215
		foreach ( $map as $from => $to ) {
1216
1217
			if ( isset( $this->uninstall[ $from ][ $items_key ] ) ) {
1218
1219
				foreach ( $to as $context ) {
1220
1221
					if ( ! isset( $this->uninstall[ $context ][ $items_key ] ) ) {
1222
						$this->uninstall[ $context ][ $items_key ] = array();
1223
					}
1224
1225
					$this->uninstall[ $context ][ $items_key ] = array_merge(
1226
						$this->uninstall[ $context ][ $items_key ]
1227
						, $this->uninstall[ $from ][ $items_key ]
1228
					);
1229
				}
1230
1231
				unset( $this->uninstall[ $from ][ $items_key ] );
1232
			}
1233
		}
1234
	}
1235
1236
	/**
1237
	 * Map an uninstall shortcut to its actual storage location.
1238
	 *
1239
	 * For an explanation of what this function does, let's look at an example:
1240
	 *
1241
	 * Points hooks settings are currently stored in the options table. The settings
1242
	 * for each type of points hook is stored in a separate option. The option name
1243
	 * is the class name (all lowercase'd) prefixed with 'wordpoints_hook-'. Within
1244
	 * the plugin, the storage and retrieval of hook settings is handled by core
1245
	 * functions, so how they are stored is not important to extensions. It is thus
1246
	 * possible that the method of storage could change in the future. To avoid
1247
	 * breakage if this happens, the hooks to uninstall are just specified by slug,
1248
	 * and the uninstaller's bootstrap should handle the rest. We also provide a
1249
	 * uninstall_points_hook() method, which can be used if needed. However, it is
1250
	 * really just a wrapper for uninstall_option(), and in interest of performance
1251
	 * we don't use it in uninstall_(). Instead, we currently treat the list of
1252
	 * 'points_hooks' as a shortcut to the prefixed options. That way they'll be
1253
	 * handled automatically with the other options in uninstall_().
1254
	 *
1255
	 * This function is used to take a list like the list of points hooks, and
1256
	 * translate it into a list of, e.g., options, before uninstallation.
1257
	 *
1258
	 * @since 2.0.0
1259
	 *
1260
	 * @param string $shortcut The shortcut key in 'single', 'site', etc.
1261
	 * @param string $canonical The canonical key.
1262
	 * @param array  $args {
1263
	 *        Other arguments.
1264
	 *
1265
	 *        @type string $prefix The prefix to prepend the elements with before
1266
	 *                             adding to them to the canonical array.
1267
	 * }
1268
	 */
1269
	protected function map_uninstall_shortcut( $shortcut, $canonical, $args ) {
1270
1271
		$args = array_merge( array( 'prefix' => '' ), $args );
1272
1273
		$types = array( 'single', 'site', 'network', 'local', 'global', 'universal' );
1274
1275
		foreach ( $types as $type ) {
1276
1277
			if ( ! isset( $this->uninstall[ $type ][ $shortcut ] ) ) {
1278
				continue;
1279
			}
1280
1281
			foreach ( $this->uninstall[ $type ][ $shortcut ] as $slug ) {
1282
				$this->uninstall[ $type ][ $canonical ][] = $args['prefix'] . $slug;
1283
			}
1284
		}
1285
	}
1286
1287
	/**
1288
	 * Map the uninstall shortcuts to their canonical elements.
1289
	 *
1290
	 * For the list of {@see self::$unisntall} configuration arguments, some
1291
	 * shortcuts are provided. These reduce duplication across the canonical
1292
	 * elements, 'single', 'site', and 'network'. These shortcuts make it possible
1293
	 * to define, e.g., an option to be uninstalled on a single site and as a network
1294
	 * option on multisite installs in just a single location, using the 'global'
1295
	 * shortcut, rather than having to add it to both the 'single' and 'network'
1296
	 * arrays.
1297
	 *
1298
	 * @since 2.0.0
1299
	 *
1300
	 * @param string $type The type of shortcuts to map. Corresponds to a member var.
1301
	 */
1302
	protected function map_shortcuts( $type ) {
1303
1304
		// shortcut => canonicals
1305
		$map = array(
1306
			'local'     => array( 'single', 'site'  /*  -  */ ),
1307
			'global'    => array( 'single', /* - */ 'network' ),
1308
			'universal' => array( 'single', 'site', 'network' ),
1309
		);
1310
1311
		$this->$type = array_merge(
1312
			array_fill_keys(
1313
				array( 'single', 'site', 'network', 'local', 'global', 'universal' )
1314
				, array()
1315
			)
1316
			, $this->$type
1317
		);
1318
1319
		foreach ( $map as $shortcut => $canonicals ) {
1320
			foreach ( $canonicals as $canonical ) {
1321
				$this->{$type}[ $canonical ] = array_merge_recursive(
1322
					$this->{$type}[ $canonical ]
1323
					, $this->{$type}[ $shortcut ]
1324
				);
1325
			}
1326
		}
1327
	}
1328
1329
	/**
1330
	 * Run before updating.
1331
	 *
1332
	 * @since 1.8.0
1333
	 */
1334
	protected function before_update() {
1335
		$this->maybe_load_custom_caps();
1336
	}
1337
1338
	/**
1339
	 * Run after updating.
1340
	 *
1341
	 * @since 2.0.0
1342
	 */
1343
	protected function after_update() {}
1344
1345
	/**
1346
	 * Get the versions that request a given type of update.
1347
	 *
1348
	 * @since 1.8.0
1349
	 *
1350
	 * @param string $type The type of update.
1351
	 *
1352
	 * @return array The versions that request this type of update.
1353
	 */
1354
	protected function get_updates_for( $type ) {
1355
1356
		return array_keys( wp_list_filter( $this->updates, array( $type => true ) ) );
0 ignored issues
show
Bug introduced by
The function wp_list_filter was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1356
		return array_keys( /** @scrutinizer ignore-call */ wp_list_filter( $this->updates, array( $type => true ) ) );
Loading history...
1357
	}
1358
1359
	/**
1360
	 * Run an update.
1361
	 *
1362
	 * @since 1.8.0
1363
	 *
1364
	 * @param string $type     The type of update to run.
1365
	 * @param array  $versions The versions to run this type of update for.
1366
	 */
1367
	protected function update_( $type, $versions ) {
1368
1369
		foreach ( $versions as $version ) {
1370
			$this->{"update_{$type}_to_{$version}"}();
1371
		}
1372
	}
1373
1374
	/**
1375
	 * Maybe update some database tables to the utf8mb4 character set.
1376
	 *
1377
	 * @since 2.0.0
1378
	 *
1379
	 * @param string $type The type of tables to update.
1380
	 */
1381
	protected function maybe_update_tables_to_utf8mb4( $type ) {
1382
1383
		global $wpdb;
1384
1385
		if ( 'utf8mb4' !== $wpdb->charset ) {
1386
			return;
1387
		}
1388
1389
		if ( 'global' === $type || 'network' === $type ) {
1390
			$prefix = $wpdb->base_prefix;
1391
		} else {
1392
			$prefix = $wpdb->prefix;
1393
		}
1394
1395
		foreach ( $this->schema[ $type ]['tables'] as $table_name => $schema ) {
1396
			maybe_convert_table_to_utf8mb4( $prefix . $table_name );
0 ignored issues
show
Bug introduced by
The function maybe_convert_table_to_utf8mb4 was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1396
			/** @scrutinizer ignore-call */ 
1397
   maybe_convert_table_to_utf8mb4( $prefix . $table_name );
Loading history...
1397
		}
1398
	}
1399
1400
	/**
1401
	 * Run the default uninstall routine for a given context.
1402
	 *
1403
	 * @since 2.0.0
1404
	 *
1405
	 * @param string $type The type of uninstallation to perform.
1406
	 */
1407
	protected function uninstall_( $type ) {
1408
1409
		if ( empty( $this->uninstall[ $type ] ) ) {
1410
			return;
1411
		}
1412
1413
		$uninstall = array_merge(
1414
			array(
1415
				'user_meta'    => array(),
1416
				'options'      => array(),
1417
				'transients'   => array(),
1418
				'tables'       => array(),
1419
				'comment_meta' => array(),
1420
				'meta_boxes'   => array(),
1421
				'list_tables'  => array(),
1422
			)
1423
			, $this->uninstall[ $type ]
1424
		);
1425
1426
		if ( ! empty( $uninstall['custom_caps'] ) ) {
1427
			$this->uninstall_custom_caps( $this->custom_caps_keys );
1428
		}
1429
1430
		foreach ( $uninstall['user_meta'] as $meta_key ) {
1431
			$this->uninstall_metadata( 'user', $meta_key );
1432
		}
1433
1434
		foreach ( $uninstall['options'] as $option ) {
1435
			$this->uninstall_option( $option );
1436
		}
1437
1438
		foreach ( $uninstall['transients'] as $transient ) {
1439
			$this->uninstall_transient( $transient );
1440
		}
1441
1442
		foreach ( $uninstall['tables'] as $table ) {
1443
			$this->uninstall_table( $table );
1444
		}
1445
1446
		foreach ( $uninstall['comment_meta'] as $meta_key ) {
1447
			$this->uninstall_metadata( 'comment', $meta_key );
1448
		}
1449
1450
		foreach ( $uninstall['meta_boxes'] as $screen_id => $args ) {
1451
			$this->uninstall_meta_boxes( $screen_id, $args );
1452
		}
1453
1454
		foreach ( $uninstall['list_tables'] as $screen_id => $args ) {
1455
			$this->uninstall_list_table( $screen_id, $args );
1456
		}
1457
	}
1458
1459
	/**
1460
	 * Uninstall a list of capabilities.
1461
	 *
1462
	 * @since 2.0.0
1463
	 *
1464
	 * @param string[] $caps The capabilities to uninstall.
1465
	 */
1466
	protected function uninstall_custom_caps( $caps ) {
1467
1468
		wordpoints_remove_custom_caps( $caps );
1469
	}
1470
1471
	/**
1472
	 * Uninstall metadata for all objects by key.
1473
	 *
1474
	 * @since 2.0.0
1475
	 *
1476
	 * @param string $type The type of metadata to uninstall, e.g., 'user', 'post'.
1477
	 * @param string $key  The metadata key to delete.
1478
	 */
1479
	protected function uninstall_metadata( $type, $key ) {
1480
1481
		if ( 'user' === $type && 'site' === $this->context ) {
1482
			$key = $GLOBALS['wpdb']->get_blog_prefix() . $key;
1483
		}
1484
1485
		if ( false !== strpos( $key, '%' ) ) {
1486
1487
			global $wpdb;
1488
1489
			$table = wordpoints_escape_mysql_identifier( _get_meta_table( $type ) );
0 ignored issues
show
Bug introduced by
The function _get_meta_table was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1489
			$table = wordpoints_escape_mysql_identifier( /** @scrutinizer ignore-call */ _get_meta_table( $type ) );
Loading history...
1490
1491
			$keys = $wpdb->get_col( // WPCS: unprepared SQL OK.
1492
				$wpdb->prepare( // WPCS: unprepared SQL OK.
1493
					"
1494
						SELECT `meta_key`
1495
						FROM {$table}
1496
						WHERE `meta_key` LIKE %s
1497
					"
1498
					, $key
1499
				)
1500
			); // WPCS: cache pass.
1501
1502
		} else {
1503
			$keys = array( $key );
1504
		}
1505
1506
		foreach ( $keys as $key ) {
1507
			delete_metadata( $type, 0, wp_slash( $key ), '', true );
0 ignored issues
show
Bug introduced by
The function wp_slash was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1507
			delete_metadata( $type, 0, /** @scrutinizer ignore-call */ wp_slash( $key ), '', true );
Loading history...
Bug introduced by
The function delete_metadata was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1507
			/** @scrutinizer ignore-call */ 
1508
   delete_metadata( $type, 0, wp_slash( $key ), '', true );
Loading history...
1508
		}
1509
	}
1510
1511
	/**
1512
	 * Uninstall a screen with meta boxes.
1513
	 *
1514
	 * Any admin screen that uses meta boxes also will automatically have certain
1515
	 * screen options associated with it. Each user is able to configure these
1516
	 * options for themselves, and this is saved as user metadata.
1517
	 *
1518
	 * The default options currently are:
1519
	 * - Hidden meta boxes.
1520
	 * - Closed meta boxes.
1521
	 * - Reordered meta boxes.
1522
	 *
1523
	 * This function will automatically remove all of these.
1524
	 *
1525
	 * A note on screen options: they are retrieved with get_user_option(), however,
1526
	 * they are saved by update_user_option() with the $global argument set to true.
1527
	 * Because of this, even on multisite, they are saved like regular user metadata,
1528
	 * which is network wide, *not* prefixed for each site.
1529
	 *
1530
	 * @param string $screen_id The screen ID.
1531
	 * @param array  $args {
1532
	 *        Other args.
1533
	 *
1534
	 *        @type string   $parent  The parent screen slug, or 'toplevel' if none.
1535
	 *                                Defaults to 'wordpoints'.
1536
	 *        @type string[] $options The list of options to delete. Defaults to
1537
	 *                                'closedpostboxes', 'metaboxhidden',
1538
	 *                                'meta-box-order'.
1539
	 * }
1540
	 */
1541
	protected function uninstall_meta_boxes( $screen_id, $args ) {
1542
1543
		$defaults = array(
1544
			'parent'  => 'wordpoints',
1545
			'options' => array( 'closedpostboxes', 'metaboxhidden', 'meta-box-order' ),
1546
		);
1547
1548
		$args            = array_merge( $defaults, $args );
1549
		$args['options'] = array_merge( $defaults['options'], $args['options'] );
1550
1551
		// Each user gets to set the options to their liking.
1552
		foreach ( $args['options'] as $option ) {
1553
1554
			$this->uninstall_metadata(
1555
				'user'
1556
				, "{$option}_{$args['parent']}_page_{$screen_id}"
1557
			);
1558
1559
			if ( 'network' === $this->context ) {
1560
				$this->uninstall_metadata(
1561
					'user'
1562
					, "{$option}_{$args['parent']}_page_{$screen_id}-network"
1563
				);
1564
			}
1565
		}
1566
	}
1567
1568
	/**
1569
	 * Uninstall list tables.
1570
	 *
1571
	 * Any admin screen that uses list tables also will automatically have certain
1572
	 * screen options associated with it. Each user is able to configure these
1573
	 * options for themselves, and this is saved as user metadata.
1574
	 *
1575
	 * List tables have two main configuration options, which are both saves as user
1576
	 * metadata:
1577
	 * - Hidden Columns
1578
	 * - Screen Options
1579
	 *
1580
	 * The hidden columns metadata is removed by default, as well as the 'per_page'
1581
	 * screen options.
1582
	 *
1583
	 * A note on screen options: they are retrieved with get_user_option(), however,
1584
	 * they are saved by update_user_option() with the $global argument set to true.
1585
	 * Because of this, even on multisite, they are saved like regular user metadata,
1586
	 * which is network wide, *not* prefixed for each site.
1587
	 *
1588
	 * @since 2.1.0
1589
	 *
1590
	 * @param string $screen_id The screen ID.
1591
	 * @param array  $args {
1592
	 *        Other args.
1593
	 *
1594
	 *        @type string   $parent  The parent screen slug, or 'toplevel' if none.
1595
	 *                                Defaults to 'wordpoints'.
1596
	 *        @type string[] $options The list of options to delete. Defaults to
1597
	 *                                'per_page'.
1598
	 * }
1599
	 */
1600
	protected function uninstall_list_table( $screen_id, $args ) {
1601
1602
		$defaults = array(
1603
			'parent'  => 'wordpoints',
1604
			'options' => array( 'per_page' ),
1605
		);
1606
1607
		$args = array_merge( $defaults, $args );
1608
1609
		$network_parent = $args['parent'];
1610
		$parent         = $network_parent;
1611
1612
		// The parent page is usually the same on a multisite site, but we need to
1613
		// handle the special case of the extensions screen.
1614
		if (
1615
			(
1616
				'wordpoints_extensions' === $screen_id
1617
				|| 'wordpoints_modules' === $screen_id
1618
			)
1619
			&& is_multisite()
0 ignored issues
show
Bug introduced by
The function is_multisite was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1619
			&& /** @scrutinizer ignore-call */ is_multisite()
Loading history...
1620
		) {
1621
			$parent = 'toplevel';
1622
		}
1623
1624
		$meta_keys = array();
1625
1626
		// Each user can hide specific columns of the table.
1627
		$meta_keys[] = "manage{$parent}_page_{$screen_id}columnshidden";
1628
1629
		if ( 'network' === $this->context ) {
1630
			$meta_keys[] = "manage{$network_parent}_page_{$screen_id}-networkcolumnshidden";
1631
		}
1632
1633
		// Loop through each of the other options provided by this list table.
1634 View Code Duplication
		foreach ( $args['options'] as $option ) {
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...
1635
1636
			// Each user gets to set the options to their liking.
1637
			$meta_keys[] = "{$parent}_page_{$screen_id}_{$option}";
1638
1639
			if ( 'network' === $this->context ) {
1640
				$meta_keys[] = "{$network_parent}_page_{$screen_id}_network_{$option}";
1641
			}
1642
		}
1643
1644
		foreach ( $meta_keys as $meta_key ) {
1645
			$this->uninstall_metadata( 'user', $meta_key );
1646
		}
1647
	}
1648
1649
	/**
1650
	 * Uninstall an option.
1651
	 *
1652
	 * If the $option contains a % wildcard, all matching options will be retrieved
1653
	 * and deleted.
1654
	 *
1655
	 * @since 2.0.0
1656
	 * @since 2.1.0 Added support for wildcards for network options.
1657
	 *
1658
	 * @param string $option The option to uninstall.
1659
	 */
1660 View Code Duplication
	protected function uninstall_option( $option ) {
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...
1661
1662
		if ( 'network' === $this->context ) {
1663
			$this->uninstall_network_option( $option );
1664
			return;
1665
		}
1666
1667
		if ( false !== strpos( $option, '%' ) ) {
1668
1669
			global $wpdb;
1670
1671
			$options = $wpdb->get_col(
1672
				$wpdb->prepare(
1673
					"
1674
						SELECT `option_name`
1675
						FROM `{$wpdb->options}`
1676
						WHERE `option_name` LIKE %s
1677
					"
1678
					, $option
1679
				)
1680
			); // WPCS: cache pass.
1681
1682
		} else {
1683
			$options = array( $option );
1684
		}
1685
1686
		array_map( 'delete_option', $options );
1687
	}
1688
1689
	/**
1690
	 * Uninstall a network option.
1691
	 *
1692
	 * @since 2.1.0
1693
	 *
1694
	 * @see WordPoints_Un_Installer_Base::uninstall_option()
1695
	 *
1696
	 * @param string $option The network option to uninstall.
1697
	 */
1698 View Code Duplication
	protected function uninstall_network_option( $option ) {
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...
1699
1700
		if ( false !== strpos( $option, '%' ) ) {
1701
1702
			global $wpdb;
1703
1704
			$options = $wpdb->get_col(
1705
				$wpdb->prepare(
1706
					"
1707
						SELECT `meta_key`
1708
						FROM `{$wpdb->sitemeta}`
1709
						WHERE `meta_key` LIKE %s
1710
							AND `site_id` = %d
1711
					"
1712
					, $option
1713
					, $wpdb->siteid
1714
				)
1715
			); // WPCS: cache pass.
1716
1717
		} else {
1718
			$options = array( $option );
1719
		}
1720
1721
		array_map( 'delete_site_option', $options );
1722
	}
1723
1724
	/**
1725
	 * Uninstall a transient.
1726
	 *
1727
	 * @since 2.4.0
1728
	 *
1729
	 * @param string $transient The transient to uninstall.
1730
	 */
1731
	protected function uninstall_transient( $transient ) {
1732
1733
		if ( 'network' === $this->context ) {
1734
			delete_site_transient( $transient );
0 ignored issues
show
Bug introduced by
The function delete_site_transient was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1734
			/** @scrutinizer ignore-call */ 
1735
   delete_site_transient( $transient );
Loading history...
1735
		} else {
1736
			delete_transient( $transient );
0 ignored issues
show
Bug introduced by
The function delete_transient was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1736
			/** @scrutinizer ignore-call */ 
1737
   delete_transient( $transient );
Loading history...
1737
		}
1738
	}
1739
1740
	/**
1741
	 * Uninstall a widget.
1742
	 *
1743
	 * @since 2.0.0
1744
	 *
1745
	 * @param string $id_base The base ID of the widget to uninstall (class name).
1746
	 */
1747
	protected function uninstall_widget( $id_base ) {
1748
1749
		$this->uninstall_option( "widget_{$id_base}" );
1750
	}
1751
1752
	/**
1753
	 * Uninstall a points hook.
1754
	 *
1755
	 * @since 2.0.0
1756
	 *
1757
	 * @param string $id_base The base ID (class) of the points hook to uninstall.
1758
	 */
1759
	protected function uninstall_points_hook( $id_base ) {
1760
1761
		$this->uninstall_option( "wordpoints_hook-{$id_base}" );
1762
	}
1763
1764
	/**
1765
	 * Uninstall a table from the database.
1766
	 *
1767
	 * @since 2.0.0
1768
	 *
1769
	 * @param string $table The name of the table to uninstall, sans DB prefix.
1770
	 */
1771
	protected function uninstall_table( $table ) {
1772
1773
		global $wpdb;
1774
1775
		// Null will use the current blog's prefix, 0 the base prefix.
1776
		if ( 'site' === $this->context ) {
1777
			$site_id = null;
1778
		} else {
1779
			$site_id = 0;
1780
		}
1781
1782
		$table = str_replace( '`', '``', $table );
1783
1784
		$wpdb->query( 'DROP TABLE IF EXISTS `' . $wpdb->get_blog_prefix( $site_id ) . $table . '`' ); // WPCS: unprepared SQL, cache pass.
1785
	}
1786
1787
	//
1788
	// [Previously] Abstract Methods.
1789
	//
1790
1791
	/**
1792
	 * Install on the network.
1793
	 *
1794
	 * This runs on multisite to install only the things that are common to the
1795
	 * whole network. For example, it would add any "site" (network-wide) options.
1796
	 *
1797
	 * @since 1.8.0
1798
	 * @since 2.0.0 No longer abstract.
1799
	 */
1800
	protected function install_network() {
1801
1802
		$this->install_db_schema();
1803
1804
		if ( $this->network_wide ) {
1805
			$this->set_db_version();
1806
		}
1807
	}
1808
1809
	/**
1810
	 * Install on a single site on the network.
1811
	 *
1812
	 * This runs on multisite to install on a single site on the network, which
1813
	 * will be the current site when this method is called.
1814
	 *
1815
	 * @since 1.8.0
1816
	 * @since 2.0.0 No longer abstract.
1817
	 */
1818
	protected function install_site() {
1819
1820
		if ( isset( $this->schema['site'] ) ) {
1821
			$this->install_db_schema();
1822
		}
1823
1824
		if ( ! $this->network_wide ) {
1825
			$this->set_db_version();
1826
		}
1827
1828
		$this->install_custom_caps();
1829
	}
1830
1831
	/**
1832
	 * Install on a single site.
1833
	 *
1834
	 * This runs when the WordPress site is not a multisite. It should completely
1835
	 * install the entity.
1836
	 *
1837
	 * @since 1.8.0
1838
	 * @since 2.0.0 No longer abstract.
1839
	 */
1840
	protected function install_single() {
1841
1842
		$this->install_db_schema();
1843
		$this->install_custom_caps();
1844
		$this->set_db_version();
1845
	}
1846
1847
	/**
1848
	 * Load the dependencies of the main un/install routine.
1849
	 *
1850
	 * @since 2.1.0
1851
	 */
1852
	protected function load_base_dependencies() {
1853
1854
		require_once dirname( __FILE__ ) . '/../../../includes/constants.php';
1855
		require_once WORDPOINTS_DIR . '/classes/class/autoloader.php';
1856
1857
		WordPoints_Class_Autoloader::register_dir( WORDPOINTS_DIR . '/classes' );
1858
1859
		require_once WORDPOINTS_DIR . '/includes/functions.php';
1860
		require_once WORDPOINTS_DIR . '/includes/apps.php';
1861
		require_once WORDPOINTS_DIR . '/includes/hooks.php';
1862
		require_once WORDPOINTS_DIR . '/includes/filters.php';
1863
	}
1864
1865
	/**
1866
	 * Load any dependencies of the uninstall code.
1867
	 *
1868
	 * @since 1.8.0
1869
	 * @since 2.0.0 No longer abstract.
1870
	 */
1871
	protected function load_dependencies() {}
1872
1873
	/**
1874
	 * Uninstall from the network.
1875
	 *
1876
	 * This runs on multisite to uninstall only the things that are common to the
1877
	 * whole network. For example, it would delete any "site" (network-wide) options.
1878
	 *
1879
	 * @since 1.8.0
1880
	 * @since 2.0.0 No longer abstract.
1881
	 */
1882
	protected function uninstall_network() {
1883
1884
		if ( ! empty( $this->uninstall['network'] ) ) {
1885
			$this->uninstall_( 'network' );
1886
		}
1887
1888
		$this->unset_db_version();
1889
	}
1890
1891
	/**
1892
	 * Uninstall from a single site on the network.
1893
	 *
1894
	 * This runs on multisite to uninstall from a single site on the network, which
1895
	 * will be the current site when this method is called.
1896
	 *
1897
	 * @since 1.8.0
1898
	 * @since 2.0.0 No longer abstract.
1899
	 */
1900
	protected function uninstall_site() {
1901
1902
		if ( ! empty( $this->uninstall['site'] ) ) {
1903
			$this->uninstall_( 'site' );
1904
		}
1905
1906
		$this->unset_db_version();
1907
	}
1908
1909
	/**
1910
	 * Uninstall from a single site.
1911
	 *
1912
	 * This runs when the WordPress site is not a multisite. It should completely
1913
	 * uninstall the entity.
1914
	 *
1915
	 * @since 1.8.0
1916
	 * @since 2.0.0 No longer abstract.
1917
	 */
1918
	protected function uninstall_single() {
1919
1920
		if ( ! empty( $this->uninstall['single'] ) ) {
1921
			$this->uninstall_( 'single' );
1922
		}
1923
1924
		$this->unset_db_version();
1925
	}
1926
}
1927
1928
// EOF
1929