Completed
Pull Request — develop (#1505)
by Zack
21:14 queued 01:15
created

Plugin::__clone()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 2
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace GV;
4
5
/** If this file is called directly, abort. */
6
if ( ! defined( 'GRAVITYVIEW_DIR' ) ) {
7
	die();
8
}
9
10
/**
11
 * The GravityView WordPress plugin class.
12
 *
13
 * Contains functionality related to GravityView being
14
 * a WordPress plugin and doing WordPress pluginy things.
15
 *
16
 * Accessible via gravityview()->plugin
17
 */
18
final class Plugin {
19
20
	/**
21
	 * @since 2.0
22
	 * @api
23
	 * @var string The plugin version.
24
	 *
25
	 */
26
	public static $version = GV_PLUGIN_VERSION;
27
28
	/**
29
	 * @var string Minimum WordPress version.
30
	 *
31
	 * GravityView requires at least this version of WordPress to function properly.
32
	 */
33
	private static $min_wp_version = GV_MIN_WP_VERSION;
34
35
	/**
36
	 * @var string Minimum Gravity Forms version.
37
	 *
38
	 * GravityView requires at least this version of Gravity Forms to function properly.
39
	 */
40
	public static $min_gf_version = GV_MIN_GF_VERSION;
41
42
	/**
43
	 * @var string Minimum PHP version.
44
	 *
45
	 * GravityView requires at least this version of PHP to function properly.
46
	 */
47
	private static $min_php_version = GV_MIN_PHP_VERSION;
48
49
	/**
50
	 * @var string|bool Minimum future PHP version.
51
	 *
52
	 * GravityView will require this version of PHP soon. False if no future PHP version changes are planned.
53
	 */
54
	private static $future_min_php_version = GV_FUTURE_MIN_PHP_VERSION;
55
56
	/**
57
	 * @var string|bool Minimum future Gravity Forms version.
58
	 *
59
	 * GravityView will require this version of Gravity Forms soon. False if no future Gravity Forms version changes are planned.
60
	 */
61
	private static $future_min_gf_version = GV_FUTURE_MIN_GF_VERSION;
62
63
	/**
64
	 * @var \GV\Plugin The \GV\Plugin static instance.
65
	 */
66
	private static $__instance = null;
67
68
	/**
69
	 * @since 2.0
70
	 * @api
71
	 * @var \GV\Addon_Settings The plugin "addon" settings.
72
	 *
73
	 */
74
	public $settings;
75
76
	/**
77
	 * @var string The GFQuery functionality identifier.
78
	 */
79
	const FEATURE_GFQUERY = 'gfquery';
80
81
	/**
82
	 * @var string The joins functionality identifier.
83
	 */
84
	const FEATURE_JOINS = 'joins';
85
86
	/**
87
	 * @var string The unions functionality identifier.
88
	 */
89
	const FEATURE_UNIONS = 'unions';
90
91
	/**
92
	 * @var string The REST API functionality identifier.
93
	 */
94
	const FEATURE_REST = 'rest_api';
95
96
	/**
97
	 * Get the global instance of \GV\Plugin.
98
	 *
99
	 * @return \GV\Plugin The global instance of GravityView Plugin.
100
	 */
101
	public static function get() {
102
103
		if ( ! self::$__instance instanceof self ) {
104
			self::$__instance = new self;
105
		}
106
107
		return self::$__instance;
108
	}
109
110
	private function __construct() {
111
112
		/**
113
		 * Load translations.
114
		 */
115
		add_action( 'init', array( $this, 'load_textdomain' ) );
116
117
		/**
118
		 * Load some frontend-related legacy files.
119
		 */
120
		add_action( 'gravityview/loaded', array( $this, 'include_legacy_frontend' ) );
121
122
		/**
123
		 * GFAddOn-backed settings, licensing.
124
		 */
125
		add_action( 'plugins_loaded', array( $this, 'load_license_settings' ) );
126
	}
127
128
	public function load_license_settings() {
129
130
		require_once $this->dir( 'future/includes/class-gv-license-handler.php' );
131
		require_once $this->dir( 'future/includes/class-gv-settings-addon.php' );
132
		if ( class_exists( '\GV\Addon_Settings' ) ) {
133
			$this->settings = new Addon_Settings();
134
			include_once $this->dir( 'includes/class-gravityview-settings.php' );
135
		} else {
136
			gravityview()->log->notice( '\GV\Addon_Settings not loaded. Missing \GFAddOn.' );
137
		}
138
	}
139
140 1
	/**
141
	 * Check whether Gravity Forms is v2.5-beta or newer
142 1
	 *
143
	 * @return bool
144 1
	 * @todo add @since
145
	 *
146
	 */
147
	public function is_GF_25() {
148
149
		return version_compare( '2.5-beta', \GFForms::$version, '<=' );
150
	}
151
152
	/**
153
	 * Check whether GravityView `is network activated.
154
	 *
155
	 * @return bool Whether it's network activated or not.
156
	 */
157
	public static function is_network_activated() {
158
159
		$plugin_basename = plugin_basename( GRAVITYVIEW_FILE );
160
161
		return is_multisite() && ( function_exists( 'is_plugin_active_for_network' ) && is_plugin_active_for_network( $plugin_basename ) );
162
	}
163
164
	/**
165
	 * Include more legacy stuff.
166
	 *
167
	 * @param boolean $force Whether to force the includes.
168
	 *
169
	 * @return void
170
	 */
171
	public function include_legacy_frontend( $force = false ) {
172
173
		if ( gravityview()->request->is_admin() && ! $force ) {
174
			return;
175
		}
176
177
		include_once $this->dir( 'includes/class-gravityview-image.php' );
178
		include_once $this->dir( 'includes/class-template.php' );
179
		include_once $this->dir( 'includes/class-api.php' );
180
		include_once $this->dir( 'includes/class-frontend-views.php' );
181
		include_once $this->dir( 'includes/class-gravityview-change-entry-creator.php' );
182
183
		/**
184
		 * @action     `gravityview_include_frontend_actions` Triggered after all GravityView frontend files are loaded
185
		 *
186
		 * @deprecated Use `gravityview/loaded` along with \GV\Request::is_admin(), etc.
187
		 *
188
		 * Nice place to insert extensions' frontend stuff
189
		 */
190
		do_action( 'gravityview_include_frontend_actions' );
191
	}
192
193
	/**
194
	 * Load more legacy core files.
195
	 *
196
	 * @return void
197
	 */
198
	public function include_legacy_core() {
199
200
		if ( ! class_exists( '\GravityView_Extension' ) ) {
201
			include_once $this->dir( 'includes/class-gravityview-extension.php' );
202
		}
203
204
		if ( ! gravityview()->plugin->is_compatible() ) {
205
			return;
206
		}
207
208
		// Load fields
209
		include_once $this->dir( 'includes/fields/class-gravityview-fields.php' );
210
		include_once $this->dir( 'includes/fields/class-gravityview-field.php' );
211
212
		// Load all field files automatically
213
		foreach ( glob( $this->dir( 'includes/fields/class-gravityview-field*.php' ) ) as $gv_field_filename ) {
214
			include_once $gv_field_filename;
215
		}
216
217
		include_once $this->dir( 'includes/class-gravityview-entry-approval-status.php' );
218
		include_once $this->dir( 'includes/class-gravityview-entry-approval.php' );
219
220
		include_once $this->dir( 'includes/class-gravityview-entry-notes.php' );
221
		include_once $this->dir( 'includes/load-plugin-and-theme-hooks.php' );
222
223
		// Load Extensions
224
		// @todo: Convert to a scan of the directory or a method where this all lives
225
		include_once $this->dir( 'includes/extensions/edit-entry/class-edit-entry.php' );
226
		include_once $this->dir( 'includes/extensions/delete-entry/class-delete-entry.php' );
227
		include_once $this->dir( 'includes/extensions/duplicate-entry/class-duplicate-entry.php' );
228
		include_once $this->dir( 'includes/extensions/entry-notes/class-gravityview-field-notes.php' );
229
230
		// Load WordPress Widgets
231
		include_once $this->dir( 'includes/wordpress-widgets/register-wordpress-widgets.php' );
232
233
		// Load GravityView Widgets
234
		include_once $this->dir( 'includes/widgets/register-gravityview-widgets.php' );
235
236
		// Add oEmbed
237
		include_once $this->dir( 'includes/class-api.php' );
238
		include_once $this->dir( 'includes/class-oembed.php' );
239
240
		// Add logging
241
		include_once $this->dir( 'includes/class-gravityview-logging.php' );
242
243
		include_once $this->dir( 'includes/class-ajax.php' );
244
		include_once $this->dir( 'includes/class-gravityview-html-elements.php' );
245
		include_once $this->dir( 'includes/class-frontend-views.php' );
246
		include_once $this->dir( 'includes/class-gravityview-admin-bar.php' );
247
		include_once $this->dir( 'includes/class-gravityview-entry-list.php' );
248
		include_once $this->dir( 'includes/class-gravityview-merge-tags.php' );
249
		/** @since 1.8.4 */
250
		include_once $this->dir( 'includes/class-data.php' );
251
		include_once $this->dir( 'includes/class-gravityview-shortcode.php' );
252
		include_once $this->dir( 'includes/class-gravityview-entry-link-shortcode.php' );
253
		include_once $this->dir( 'includes/class-gvlogic-shortcode.php' );
254
		include_once $this->dir( 'includes/presets/register-default-templates.php' );
255
256
		if ( class_exists( '\GFFormsModel' ) ) {
257
			include_once $this->dir( 'includes/class-gravityview-gfformsmodel.php' );
258
		}
259
	}
260
261
	/**
262
	 * Load the translations.
263
	 *
264
	 * Order of look-ups:
265
	 *
266
	 * 1. /wp-content/languages/plugins/gravityview-{locale}.mo (loaded by WordPress Core)
267
	 * 2. /wp-content/mu-plugins/gravityview-{locale}.mo
268
	 * 3. /wp-content/mu-plugins/languages/gravityview-{locale}.mo
269
	 * 4. /wp-content/plugins/gravityview/languages/gravityview-{locale}.mo
270
	 *
271
	 * @return void
272
	 */
273
	public function load_textdomain() {
274
275
		$domain = 'gravityview';
276
277
		// 1. /wp-content/languages/plugins/gravityview-{locale}.mo (loaded by WordPress Core)
278
		if ( is_textdomain_loaded( $domain ) ) {
279
			return;
280
		}
281
282
		// 2. /wp-content/languages/plugins/gravityview-{locale}.mo
283
		// 3. /wp-content/mu-plugins/plugins/languages/gravityview-{locale}.mo
284
		$loaded = load_muplugin_textdomain( $domain, '/languages/' );
285
286
		if ( $loaded ) {
287
			return;
288
		}
289
290
		// 4. /wp-content/plugins/gravityview/languages/gravityview-{locale}.mo
291
		$loaded = load_plugin_textdomain( $domain, false, $this->relpath( '/languages/' ) );
292
293
		if ( $loaded ) {
294
			return;
295
		}
296
297
		// Pre-4.6 loading
298
		// TODO: Remove when GV minimum version is WordPress 4.6.0
299
		$locale = apply_filters( 'plugin_locale', ( ( function_exists( 'get_user_locale' ) && is_admin() ) ? get_user_locale() : get_locale() ), 'gravityview' );
300
301
		$loaded = load_textdomain( 'gravityview', sprintf( '%s/%s-%s.mo', $this->dir( 'languages' ), $domain, $locale ) );
302
303
		if ( $loaded ) {
304 1
			return;
305 1
		}
306
307 1
		gravityview()->log->error( sprintf( 'Unable to load textdomain for %s locale.', $locale ) );
308
	}
309
310
	/**
311
	 * Register hooks that are fired when the plugin is activated and deactivated.
312 1
	 *
313 1
	 * @return void
314
	 */
315
	public function register_activation_hooks() {
316 1
317 1
		register_activation_hook( $this->dir( 'gravityview.php' ), array( $this, 'activate' ) );
318
		register_deactivation_hook( $this->dir( 'gravityview.php' ), array( $this, 'deactivate' ) );
319
	}
320 1
321
	/**
322 1
	 * Plugin activation function.
323
	 *
324
	 * @return void
325 1
	 * @internal
326
	 */
327
	public function activate() {
328 1
329
		gravityview();
330 1
331 1
		if ( ! $this->is_compatible() ) {
332
			return;
333
		}
334
335
		/** Register the gravityview post type upon WordPress core init. */
336
		require_once $this->dir( 'future/includes/class-gv-view.php' );
337
		View::register_post_type();
338
339
		/** Add the entry rewrite endpoint. */
340
		require_once $this->dir( 'future/includes/class-gv-entry.php' );
341
		Entry::add_rewrite_endpoint();
342
343
		/** Flush all URL rewrites. */
344
		flush_rewrite_rules();
345
346
		update_option( 'gv_version', self::$version );
347
348
		/** Add the transient to redirect to configuration page. */
349
		set_transient( '_gv_activation_redirect', true, 60 );
350
351
		/** Clear settings transient. */
352 114
		delete_transient( 'gravityview_edd-activate_valid' );
353 114
354
		\GravityView_Roles_Capabilities::get_instance()->add_caps();
355
	}
356
357
	/**
358
	 * Plugin deactivation function.
359
	 *
360
	 * @return void
361
	 * @internal
362
	 */
363
	public function deactivate() {
364
365 1
		flush_rewrite_rules();
366
	}
367 1
368
	/**
369 1
	 * Retrieve an absolute path within the GravityView plugin directory.
370
	 *
371
	 * @since 2.0
372
	 *
373
	 * @param string $path Optional. Append this extra path component.
374
	 * @return string The absolute path to the plugin directory.
375
	 * @api
376
	 */
377
	public function dir( $path = '' ) {
378
379
		return wp_normalize_path( GRAVITYVIEW_DIR . ltrim( $path, '/' ) );
380
	}
381 1
382 1
	/**
383
	 * Retrieve a relative path to the GravityView plugin directory from the WordPress plugin directory
384
	 *
385
	 * @since 2.2.3
386
	 *
387
	 * @param string $path Optional. Append this extra path component.
388
	 * @return string The relative path to the plugin directory from the plugin directory.
389
	 * @api
390
	 */
391
	public function relpath( $path = '' ) {
392
393 5
		$dirname = trailingslashit( dirname( plugin_basename( GRAVITYVIEW_FILE ) ) );
394
395 5
		return wp_normalize_path( $dirname . ltrim( $path, '/' ) );
396 5
	}
397 5
398
	/**
399
	 * Retrieve a URL within the GravityView plugin directory.
400
	 *
401
	 * @since 2.0
402
	 *
403
	 * @param string $path Optional. Extra path appended to the URL.
404
	 * @return string The URL to this plugin, with trailing slash.
405
	 * @api
406
	 */
407
	public function url( $path = '/' ) {
408 5
409 5
		return plugins_url( $path, $this->dir( 'gravityview.php' ) );
410
	}
411
412
	/**
413
	 * Is everything compatible with this version of GravityView?
414
	 *
415
	 * @since 2.0
416
	 *
417
	 * @return bool
418
	 * @api
419
	 */
420
	public function is_compatible() {
421
422
		return
423
			$this->is_compatible_php()
424
			&& $this->is_compatible_wordpress()
425
			&& $this->is_compatible_gravityforms();
426
	}
427
428
	/**
429
	 * Is this version of GravityView compatible with the current version of PHP?
430
	 *
431
	 * @since 2.0
432
	 *
433
	 * @return bool true if compatible, false otherwise.
434 5
	 * @api
435
	 */
436 5
	public function is_compatible_php() {
437 5
438
		return version_compare( $this->get_php_version(), self::$min_php_version, '>=' );
439
	}
440 5
441
	/**
442
	 * Is this version of GravityView compatible with the future required version of PHP?
443
	 *
444
	 * @since 2.0
445
	 *
446
	 * @return bool true if compatible, false otherwise.
447
	 * @api
448
	 */
449
	public function is_compatible_future_php() {
450
451 5
		return version_compare( $this->get_php_version(), self::$future_min_php_version, '>=' );
452 5
	}
453 5
454
	/**
455
	 * Is this version of GravityView compatible with the current version of WordPress?
456
	 *
457
	 * @since 2.0
458
	 *
459
	 * @param string $version Version to check against; otherwise uses GV_MIN_WP_VERSION
460
	 *
461
	 * @return bool true if compatible, false otherwise.
462
	 * @api
463
	 */
464
	public function is_compatible_wordpress( $version = null ) {
465
466
		if ( ! $version ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $version of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
467
			$version = self::$min_wp_version;
468
		}
469
470
		return version_compare( $this->get_wordpress_version(), $version, '>=' );
471
	}
472
473
	/**
474
	 * Is this version of GravityView compatible with the current version of Gravity Forms?
475
	 *
476 4
	 * @since 2.0
477 4
	 *
478 4
	 * @return bool true if compatible, false otherwise (or not active/installed).
479
	 * @api
480
	 */
481
	public function is_compatible_gravityforms() {
482
483
		$version = $this->get_gravityforms_version();
484
485
		return $version ? version_compare( $version, self::$min_gf_version, '>=' ) : false;
486
	}
487
488 4
	/**
489 4
	 * Is this version of GravityView compatible with the future version of Gravity Forms?
490 4
	 *
491
	 * @since 2.0
492
	 *
493
	 * @return bool true if compatible, false otherwise (or not active/installed).
494
	 * @api
495
	 */
496
	public function is_compatible_future_gravityforms() {
497
498
		$version = $this->get_gravityforms_version();
499
500 4
		return $version ? version_compare( $version, self::$future_min_gf_version, '>=' ) : false;
501 4
	}
502
503
	/**
504
	 * Retrieve the current PHP version.
505
	 *
506 4
	 * Overridable with GRAVITYVIEW_TESTS_PHP_VERSION_OVERRIDE during testing.
507 4
	 *
508
	 * @return string The version of PHP.
509
	 */
510
	private function get_php_version() {
511
512
		return ! empty( $GLOBALS['GRAVITYVIEW_TESTS_PHP_VERSION_OVERRIDE'] ) ?
513
			$GLOBALS['GRAVITYVIEW_TESTS_PHP_VERSION_OVERRIDE'] : phpversion();
514
	}
515
516
	/**
517 194
	 * Retrieve the current WordPress version.
518 194
	 *
519
	 * Overridable with GRAVITYVIEW_TESTS_WP_VERSION_OVERRIDE during testing.
520
	 *
521
	 * @return string The version of WordPress.
522
	 */
523 194
	private function get_wordpress_version() {
524 74
525 194
		return ! empty( $GLOBALS['GRAVITYVIEW_TESTS_WP_VERSION_OVERRIDE'] ) ?
526 194
			$GLOBALS['GRAVITYVIEW_TESTS_WP_VERSION_OVERRIDE'] : $GLOBALS['wp_version'];
527 181
	}
528 194
529 194
	/**
530
	 * Retrieve the current Gravity Forms version.
531
	 *
532
	 * Overridable with GRAVITYVIEW_TESTS_GF_VERSION_OVERRIDE during testing.
533
	 *
534
	 * @return string|null The version of Gravity Forms or null if inactive.
535
	 */
536
	private function get_gravityforms_version() {
537
538
		if ( ! class_exists( '\GFCommon' ) || ! empty( $GLOBALS['GRAVITYVIEW_TESTS_GF_INACTIVE_OVERRIDE'] ) ) {
539
			gravityview()->log->error( 'Gravity Forms is inactive or not installed.' );
540
541
			return null;
542
		}
543
544
		return ! empty( $GLOBALS['GRAVITYVIEW_TESTS_GF_VERSION_OVERRIDE'] ) ?
545
			$GLOBALS['GRAVITYVIEW_TESTS_GF_VERSION_OVERRIDE'] : \GFCommon::$version;
546
	}
547
548
	/**
549
	 * Feature support detection.
550
	 *
551
	 * @param string $feature Feature name. Check FEATURE_* class constants.
552
	 *
553
	 * @return boolean
554
	 */
555
	public function supports( $feature ) {
556
557
		if ( ! is_null( $supports = apply_filters( "gravityview/plugin/feature/$feature", null ) ) ) {
558
			return $supports;
559
		}
560
561
		switch ( $feature ):
562
			case self::FEATURE_GFQUERY:
563
				return class_exists( '\GF_Query' );
564
			case self::FEATURE_JOINS:
565
			case self::FEATURE_UNIONS:
566
				return apply_filters( 'gravityview/query/class', false ) === '\GF_Patched_Query';
567
			case self::FEATURE_REST:
568
				return class_exists( '\WP_REST_Controller' );
569
			default:
570
				return false;
571
		endswitch;
572
	}
573
574
	/**
575
	 * Delete GravityView Views, settings, roles, caps, etc.
576
	 *
577
	 * @return void
578
	 */
579
	public function uninstall() {
580
581
		global $wpdb;
582
583
		$suppress = $wpdb->suppress_errors();
584
585
		/**
586
		 * Posts.
587
		 */
588
		$items = get_posts( array(
589
			'post_type'   => 'gravityview',
590
			'post_status' => 'any',
591
			'numberposts' => - 1,
592
			'fields'      => 'ids',
593
		) );
594
595
		foreach ( $items as $item ) {
596
			wp_delete_post( $item, true );
597
		}
598
599
		/**
600
		 * Meta.
601
		 */
602
		$tables = array();
603
604
		if ( version_compare( \GravityView_GFFormsModel::get_database_version(), '2.3-dev-1', '>=' ) ) {
605
			$tables [] = \GFFormsModel::get_entry_meta_table_name();
606
		} elseif ( ! $this->is_GF_25() ) {
607
			$tables [] = \GFFormsModel::get_lead_meta_table_name();
608
		}
609
610
		foreach ( $tables as $meta_table ) {
611
			$sql = "
612
				DELETE FROM $meta_table
613
				WHERE (
614
					`meta_key` = 'is_approved'
615
				);
616
			";
617
			$wpdb->query( $sql );
618
		}
619
620
		/**
621
		 * Notes.
622
		 */
623
		$tables = array();
624
625
		if ( version_compare( \GravityView_GFFormsModel::get_database_version(), '2.3-dev-1', '>=' ) && method_exists( 'GFFormsModel', 'get_entry_notes_table_name' ) ) {
626
			$tables[] = \GFFormsModel::get_entry_notes_table_name();
627
		} elseif ( ! $this->is_GF_25() ) {
628
			$tables[] = \GFFormsModel::get_lead_notes_table_name();
629
		}
630
631
		$disapproved = __( 'Disapproved the Entry for GravityView', 'gravityview' );
632
		$approved    = __( 'Approved the Entry for GravityView', 'gravityview' );
633
634
		$suppress = $wpdb->suppress_errors();
635
		foreach ( $tables as $notes_table ) {
636
			$sql = $wpdb->prepare( "
637
				DELETE FROM $notes_table
638
				WHERE (
639
					`note_type` = 'gravityview' OR
640
					`value` = %s OR
641
					`value` = %s
642
				);
643
			", $approved, $disapproved );
644
			$wpdb->query( $sql );
645
		}
646
647
		$wpdb->suppress_errors( $suppress );
648
649
		/**
650
		 * Capabilities.
651
		 */
652
		\GravityView_Roles_Capabilities::get_instance()->remove_caps();
653
654
		/**
655
		 * Options.
656
		 */
657
		delete_option( 'gravityview_cache_blacklist' );
658
		delete_option( 'gv_version_upgraded_from' );
659
		delete_transient( 'gravityview_edd-activate_valid' );
660
		delete_transient( 'gravityview_edd-deactivate_valid' );
661
		delete_transient( 'gravityview_dismissed_notices' );
662
		delete_site_transient( 'gravityview_related_plugins' );
663
	}
664
665
	private function __clone() {
666
	}
667
668
	private function __wakeup() {
669
	}
670
}
671