Completed
Push — add/beta-plugin-code-standard ( aa4138...da180b )
by
unknown
427:59 queued 408:50
created

Jetpack_Beta::clear_autoloader_plugin_cache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Primary class file for the Jetpack Beta plugin.
4
 *
5
 * @package Jetpack Beta
6
 */
7
8
// Check that the file is not accessed directly.
9
if ( ! defined( 'ABSPATH' ) ) {
10
	exit;
11
}
12
13
/**
14
 * Class Jetpack_Beta
15
 */
16
class Jetpack_Beta {
17
18
	/**
19
	 * Singleton Jetpack_Beta class instance.
20
	 *
21
	 * @var Jetpack_Beta
22
	 */
23
	protected static $instance = null;
24
25
	/**
26
	 * WP Options string: jetpack_beta_active
27
	 *
28
	 * @var string
29
	 */
30
	protected static $option = 'jetpack_beta_active';
31
32
	/**
33
	 * WP Options string: jetpack_beta_dev_currently_installed
34
	 *
35
	 * @var string
36
	 */
37
	protected static $option_dev_installed = 'jetpack_beta_dev_currently_installed';
38
39
	/**
40
	 * WP Options string: jp_beta_autoupdate
41
	 *
42
	 * @var string
43
	 */
44
	protected static $option_autoupdate = 'jp_beta_autoupdate';
45
46
	/**
47
	 * WP Options string: jp_beta_email_notifications
48
	 *
49
	 * @var string
50
	 */
51
	protected static $option_email_notif = 'jp_beta_email_notifications';
52
53
	/**
54
	 * WP-Cron string: jetpack_beta_autoupdate_hourly_cron
55
	 *
56
	 * @var string
57
	 */
58
	protected static $auto_update_cron_hook = 'jetpack_beta_autoupdate_hourly_cron';
59
60
	/**
61
	 * Main Instance
62
	 */
63
	public static function instance() {
64
		if ( null === self::$instance ) {
65
			self::$instance = new self();
66
		}
67
68
		return self::$instance;
69
	}
70
71
	/**
72
	 * Constructor
73
	 */
74
	public function __construct() {
75
		if ( isset( $_GET['delete'] ) ) {
76
			delete_site_transient( 'update_plugins' );
77
		}
78
79
		add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'maybe_plugins_update_transient' ) );
80
		add_filter( 'upgrader_post_install', array( $this, 'upgrader_post_install' ), 10, 3 );
81
82
		add_action( 'admin_bar_menu', array( $this, 'admin_bar_menu' ) );
83
		add_action( 'deactivate_plugin', array( $this, 'plugin_deactivated' ), 10, 2 );
84
85
		add_action( 'upgrader_process_complete', array( $this, 'upgrader_process_complete' ), 10, 2 );
86
87
		add_filter( 'plugin_action_links_' . JETPACK_PLUGIN_FILE, array( $this, 'remove_activate_stable' ) );
88
		add_filter( 'plugin_action_links_' . JETPACK_DEV_PLUGIN_FILE, array( $this, 'remove_activate_dev' ) );
89
90
		add_filter( 'network_admin_plugin_action_links_' . JETPACK_PLUGIN_FILE, array( $this, 'remove_activate_stable' ) );
91
		add_filter( 'network_admin_plugin_action_links_' . JETPACK_DEV_PLUGIN_FILE, array( $this, 'remove_activate_dev' ) );
92
93
		add_filter( 'all_plugins', array( $this, 'update_all_plugins' ) );
94
95
		add_filter( 'plugins_api', array( $this, 'get_plugin_info' ), 10, 3 );
96
97
		add_action( 'jetpack_beta_autoupdate_hourly_cron', array( 'Jetpack_Beta', 'run_autoupdate' ) );
98
99
		add_filter( 'jetpack_options_whitelist', array( $this, 'add_to_options_whitelist' ) );
100
101
		if ( is_admin() ) {
102
			require JPBETA__PLUGIN_DIR . 'class-jetpack-beta-admin.php';
103
			self::maybe_schedule_autoupdate();
104
			Jetpack_Beta_Admin::init();
105
		}
106
	}
107
108
	/**
109
	 * Fired when the upgrader process is complete; sets option jetpack_beta_dev_currently_installed
110
	 *
111
	 * @param WP_Upgrader $upgrader          - An upgrader instance.
112
	 * @param array       $updates_completed - Array of bulk item update data.
113
	 */
114
	public function upgrader_process_complete( $upgrader, $updates_completed ) {
115
		if ( ! isset( $updates_completed['plugins'] ) ) {
116
			return;
117
		}
118
119
		if ( 'update' === $updates_completed['action'] &&
120
			'plugin' === $updates_completed['type'] &&
121
		in_array( JETPACK_DEV_PLUGIN_FILE, $updates_completed['plugins'], true ) ) {
122
			list( $branch, $section ) = self::get_branch_and_section_dev();
123
			if ( self::should_update_dev_to_master() ) {
124
				list( $branch, $section ) = array( 'master', 'master' );
125
			}
126
			update_option( self::$option_dev_installed, array( $branch, $section, self::get_manifest_data( $branch, $section ) ) );
127
		}
128
	}
129
130
	/**
131
	 * If Jetpack or JP Dev plugin is network activated, update active_plugins option.
132
	 */
133
	public static function is_network_enabled() {
134
		if ( self::is_network_active() ) {
135
			add_filter( 'option_active_plugins', array( 'Jetpack_Beta', 'override_active_plugins' ) );
136
		}
137
	}
138
139
	/**
140
	 * This filter is only applied if Jetpack is network activated,
141
	 * makes sure that you can't have Jetpack or Jetpack Dev plugins versions loaded.
142
	 *
143
	 * @param array $active_plugins - Currently activated plugins.
144
	 *
145
	 * @return array Updated array of active plugins.
146
	 */
147
	public static function override_active_plugins( $active_plugins ) {
148
		$new_active_plugins = array();
149
		foreach ( $active_plugins as $active_plugin ) {
150
			if ( ! self::is_jetpack_plugin( $active_plugin ) ) {
151
				$new_active_plugins[] = $active_plugin;
152
			}
153
		}
154
		return $new_active_plugins;
155
	}
156
157
	/**
158
	 * Actions taken when the Jetpack Beta plugin is deactivated.
159
	 *
160
	 * @param string $plugin       - Plugin path being deactivated.
161
	 */
162
	public function plugin_deactivated( $plugin ) {
163
		if ( ! self::is_jetpack_plugin( $plugin ) ) {
164
			return;
165
		}
166
167
		delete_option( self::$option );
168
	}
169
170
	/**
171
	 * Checks if passed plugin matches JP or JP Dev paths.
172
	 *
173
	 * @param string $plugin - A plugin path.
174
	 */
175
	public static function is_jetpack_plugin( $plugin ) {
176
		return in_array( $plugin, array( JETPACK_PLUGIN_FILE, JETPACK_DEV_PLUGIN_FILE ), true );
177
	}
178
179
	/**
180
	 * Filter JP Dev plugin action links.
181
	 *
182
	 * @param array $actions - Array of plugin action links.
183
	 */
184
	public function remove_activate_dev( $actions ) {
185 View Code Duplication
		if ( is_plugin_active( JETPACK_PLUGIN_FILE ) || self::is_network_active() ) {
186
			$actions['activate'] = __( 'Plugin Already Active', 'jetpack-beta' );
187
		}
188
		return $actions;
189
	}
190
191
	/**
192
	 * Filter JP Stable plugin action links.
193
	 *
194
	 * @param array $actions - Array of plugin action links.
195
	 */
196
	public function remove_activate_stable( $actions ) {
197 View Code Duplication
		if ( is_plugin_active( JETPACK_DEV_PLUGIN_FILE ) || self::is_network_active() ) {
198
			$actions['activate'] = __( 'Plugin Already Active', 'jetpack-beta' );
199
		}
200
		return $actions;
201
	}
202
203
	/**
204
	 * Filters plugins to list in the Plugins list table.
205
	 *
206
	 * @param array $plugins - Array of arrays of plugin data.
207
	 *
208
	 * @return array Updated array of plugin data.
209
	 */
210
	public function update_all_plugins( $plugins ) {
211
		// WP.com requests away show regular plugin.
212
		if ( defined( 'REST_API_REQUEST' ) && REST_API_REQUEST ) {
213
			// Ensure that Jetpack reports the version it's using on account of the Jetpack Beta plugin to Calypso.
214
			if ( is_plugin_active( JETPACK_DEV_PLUGIN_FILE ) ) {
215
				$plugins[ JETPACK_PLUGIN_FILE ]['Version'] = $plugins[ JETPACK_DEV_PLUGIN_FILE ]['Version'];
216
			}
217
			unset( $plugins[ JETPACK_DEV_PLUGIN_FILE ] );
218
			return $plugins;
219
		}
220
221
		if ( is_plugin_active( JETPACK_DEV_PLUGIN_FILE ) ) {
222
			unset( $plugins[ JETPACK_PLUGIN_FILE ] );
223
		} else {
224
			unset( $plugins[ JETPACK_DEV_PLUGIN_FILE ] );
225
		}
226
		return $plugins;
227
	}
228
229
	/**
230
	 * Filter WordPress.org Plugins API results.
231
	 *
232
	 * @param false|object|array $false    - The result object or array. Default false.
233
	 * @param string             $action   - The type of information being requested from the Plugin Installation API.
234
	 * @param object             $response - Plugin API arguments.
235
	 */
236
	public function get_plugin_info( $false, $action, $response ) {
237
238
		// Check if this call API is for the right plugin.
239
		if ( ! isset( $response->slug ) || JETPACK_DEV_PLUGIN_SLUG !== $response->slug ) {
240
			return false;
241
		}
242
		$update_date  = null;
243
		$download_zip = null;
244
		$dev_data     = self::get_dev_installed();
245
		if ( isset( $dev_data[2] ) ) {
246
			$update_date  = $dev_data[2]->update_date;
247
			$download_zip = $dev_data[2]->download_url;
248
		}
249
		// Update tags.
250
		$response->slug          = JETPACK_DEV_PLUGIN_SLUG;
251
		$response->plugin        = JETPACK_DEV_PLUGIN_SLUG;
252
		$response->name          = 'Jetpack | ' . self::get_jetpack_plugin_pretty_version( true );
253
		$response->plugin_name   = 'Jetpack | ' . self::get_jetpack_plugin_pretty_version( true );
254
		$response->version       = self::get_jetpack_plugin_version( true );
255
		$response->author        = 'Automattic';
256
		$response->homepage      = 'https://jetpack.com/contact-support/beta-group/';
257
		$response->downloaded    = false;
258
		$response->last_updated  = $update_date;
259
		$response->sections      = array( 'description' => Jetpack_Beta_Admin::to_test_content() );
260
		$response->download_link = $download_zip;
261
		return $response;
262
	}
263
264
	/**
265
	 * Run on activation to flush update cache.
266
	 */
267
	public static function activate() {
268
		// Don't do anyting funnly.
269
		if ( defined( 'DOING_CRON' ) ) {
270
			return;
271
		}
272
		delete_site_transient( 'update_plugins' );
273
	}
274
275
	/**
276
	 * Returns active Jetpack plugin file partial path string (jetpack/jetpack.php|jetpack-dev/jetpack.php).
277
	 */
278
	public static function get_plugin_file() {
279
		return self::get_plugin_slug() . '/jetpack.php';
280
	}
281
282
	/**
283
	 * Returns active plugin slug string (jetpack|jetpack-dev).
284
	 */
285 View Code Duplication
	public static function get_plugin_slug() {
286
		$installed = self::get_branch_and_section();
287
		if ( empty( $installed ) || 'stable' === $installed[1] || 'tags' === $installed[1] ) {
288
			return 'jetpack';
289
		}
290
		return JETPACK_DEV_PLUGIN_SLUG;
291
	}
292
293
	/**
294
	 * Handler ran for Jetpack Beta plugin deactivation hook.
295
	 */
296
	public static function deactivate() {
297
		// Don't do anyting funnly.
298
		if ( defined( 'DOING_CRON' ) ) {
299
			return;
300
		}
301
302
		self::clear_autoupdate_cron();
303
		self::delete_all_transiants();
304
		add_action( 'shutdown', array( __CLASS__, 'switch_active' ), 5 );
305
		add_action( 'shutdown', array( __CLASS__, 'remove_dev_plugin' ), 20 );
306
		delete_option( self::$option );
307
	}
308
309
	/**
310
	 * When Jetpack Beta plugin is deactivated, remove the jetpack-dev plugin directory and cleanup.
311
	 */
312
	public static function remove_dev_plugin() {
313
		if ( is_multisite() ) {
314
			return;
315
		}
316
317
		// Delete the jetpack dev plugin.
318
		require_once ABSPATH . 'wp-admin/includes/file.php';
319
		$creds = request_filesystem_credentials( site_url() . '/wp-admin/', '', false, false, array() );
320
		if ( ! WP_Filesystem( $creds ) ) {
321
			// Any problems and we exit.
322
			return;
323
		}
324
		global $wp_filesystem;
325
		if ( ! $wp_filesystem ) {
326
			return;
327
		}
328
329
		$working_dir = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . JETPACK_DEV_PLUGIN_SLUG;
330
		// Delete the folder JETPACK_BETA_PLUGIN_FOLDER.
331
		if ( $wp_filesystem->is_dir( $working_dir ) ) {
332
			$wp_filesystem->delete( $working_dir, true );
333
		}
334
		// Since we are removing this dev plugin we should also clean up this data.
335
		delete_option( self::$option_dev_installed );
336
	}
337
338
	/**
339
	 * Builds URL to the admin area for the current site and specified query param.
340
	 *
341
	 * @param string $query - Path relative to the admin URL.
342
	 */
343
	public static function admin_url( $query = '?page=jetpack-beta' ) {
344
		return ( self::is_network_active() )
345
		? network_admin_url( 'admin.php' . $query )
346
		: admin_url( 'admin.php' . $query );
347
	}
348
349
	/**
350
	 * Build the "Jetpack Beta" admin bar menu items.
351
	 */
352
	public function admin_bar_menu() {
353
		global $wp_admin_bar;
354
355
		if ( ! is_object( $wp_admin_bar ) ) {
356
			return;
357
		}
358
359
		// Nothing got activated yet.
360
		if ( ! self::get_option() ) {
361
			return;
362
		}
363
364
		$args = array(
365
			'id'     => 'jetpack-beta_admin_bar',
366
			'title'  => 'Jetpack Beta',
367
			'parent' => 'top-secondary',
368
			'href'   => current_user_can( 'update_plugins' ) ? self::admin_url() : '',
369
		);
370
		$wp_admin_bar->add_node( $args );
371
372
		// Add a child item to our parent item.
373
		$args = array(
374
			'id'     => 'jetpack-beta_version',
375
			// translators: %s: active Jetpack plugin branch/tag.
376
			'title'  => sprintf( __( 'Running %s', 'jetpack-beta' ), self::get_jetpack_plugin_pretty_version() ),
377
			'parent' => 'jetpack-beta_admin_bar',
378
		);
379
380
		$wp_admin_bar->add_node( $args );
381
382
		if ( self::get_plugin_slug() === JETPACK_DEV_PLUGIN_SLUG ) {
383
			// Highlight the menu if you are running the BETA Versions..
384
			echo sprintf( '<style>#wpadminbar #wp-admin-bar-jetpack-beta_admin_bar { background: %s; }</style>', esc_attr( JETPACK_GREEN ) );
385
		}
386
387
		$args = array(
388
			'id'     => 'jetpack-beta_report',
389
			'title'  => __( 'Report Bug', 'jetpack-beta' ),
390
			'href'   => JETPACK_BETA_REPORT_URL,
391
			'parent' => 'jetpack-beta_admin_bar',
392
		);
393
		$wp_admin_bar->add_node( $args );
394
395
		list( $branch, $section ) = self::get_branch_and_section();
396
		if ( 'pr' === $section ) {
397
			$args = array(
398
				'id'     => 'jetpack-beta_report_more_info',
399
				'title'  => __( 'More Info ', 'jetpack-beta' ),
400
				'href'   => self::get_url( $branch, $section ),
401
				'parent' => 'jetpack-beta_admin_bar',
402
			);
403
			$wp_admin_bar->add_node( $args );
404
		}
405
	}
406
407
	/**
408
	 * Filters `update_plugins` transient.
409
	 *
410
	 * @param object $transient - Plugin update data.
411
	 */
412
	public function maybe_plugins_update_transient( $transient ) {
413
		if ( ! isset( $transient->no_update ) ) {
414
			return $transient;
415
		}
416
417
		// Do not try to update things that do not exist.
418
		if ( ! file_exists( WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . JETPACK_DEV_PLUGIN_FILE ) ) {
419
			return $transient;
420
		}
421
422
		// Do not look for update if we are stable branch.
423
		if ( self::is_on_stable() ) {
424
			return $transient;
425
		}
426
427
		// Lets always grab the latest.
428
		delete_site_transient( 'jetpack_beta_manifest' );
429
430
		// Check if there is a new version.
431
		if ( self::should_update_dev_to_master() ) {
432
			// If response is false, don't alter the transient.
433
			$transient->response[ JETPACK_DEV_PLUGIN_FILE ] = self::get_jepack_dev_master_update_response();
434
			// Unset the that it doesn't need an update.
435
			unset( $transient->no_update[ JETPACK_DEV_PLUGIN_FILE ] );
436
		} elseif ( self::should_update_dev_version() ) {
437
			// If response is false, don't alter the transient.
438
			$transient->response[ JETPACK_DEV_PLUGIN_FILE ] = self::get_jepack_dev_update_response();
439
			// Unset the that it doesn't need an update.
440
			unset( $transient->no_update[ JETPACK_DEV_PLUGIN_FILE ] );
441
		} else {
442
			unset( $transient->response[ JETPACK_DEV_PLUGIN_FILE ] );
443
			if ( isset( $transient->no_update ) ) {
444
				$transient->no_update[ JETPACK_DEV_PLUGIN_FILE ] = self::get_jepack_dev_update_response();
445
			}
446
		}
447
448
		return $transient;
449
	}
450
451
	/**
452
	 * Determine if JP dev version should be updated.
453
	 */
454
	public static function should_update_dev_version() {
455
		return version_compare( self::get_new_jetpack_version( true ), self::get_jetpack_plugin_version( true ), '>' );
456
	}
457
458
	/**
459
	 * Build plugin update data response for dev plugin.
460
	 */
461
	public static function get_jepack_dev_update_response() {
462
		$response              = new stdClass();
463
		$response->id          = JETPACK_DEV_PLUGIN_SLUG;
464
		$response->plugin      = JETPACK_DEV_PLUGIN_SLUG;
465
		$response->new_version = self::get_new_jetpack_version( true );
466
		$response->slug        = JETPACK_DEV_PLUGIN_SLUG;
467
		$response->url         = self::get_url_dev();
468
		$response->package     = self::get_install_url_dev();
469
		return $response;
470
	}
471
472
	/**
473
	 * Build plugin update data response for JP dev master.
474
	 */
475
	public static function get_jepack_dev_master_update_response() {
476
		$response = self::get_jepack_dev_update_response();
477
478
		$master_manifest       = self::get_manifest_data( 'master', 'master' );
479
		$response->new_version = $master_manifest->version;
480
		$response->url         = self::get_url( 'master', 'master' );
481
		$response->package     = $master_manifest->download_url;
482
		return $response;
483
	}
484
485
	/**
486
	 * Moves the newly downloaded folder into jetpack-dev.
487
	 *
488
	 * @param bool  $worked      - Installation response.
489
	 * @param array $hook_extras - Extra args passed to hooked filters.
490
	 * @param array $result      - Installation result data.
491
	 *
492
	 * @return WP_Error
493
	 */
494
	public function upgrader_post_install( $worked, $hook_extras, $result ) {
495
		global $wp_filesystem;
496
497
		if (
498
		! isset( $hook_extras['plugin'] )
499
		|| JETPACK_DEV_PLUGIN_FILE !== $hook_extras['plugin']
500
		) {
501
			return $worked;
502
		}
503
504
		if ( $wp_filesystem->move( $result['destination'], WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . JETPACK_DEV_PLUGIN_SLUG, true ) ) {
505
			return $worked;
506
		} else {
507
			return new WP_Error();
508
		}
509
	}
510
511
	/**
512
	 * Get the active JP or JP Dev plugin version.
513
	 *
514
	 * @param bool $is_dev_version - If dev plugin version is being queried.
515
	 *
516
	 * @return string|0 Plugin version.
0 ignored issues
show
Documentation introduced by
The doc-type string|0 could not be parsed: Unknown type name "0" at position 7. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
517
	 */
518
	public static function get_jetpack_plugin_version( $is_dev_version = false ) {
519
		if ( $is_dev_version ) {
520
			$info = self::get_jetpack_plugin_info_dev();
521
		} else {
522
			$info = self::get_jetpack_plugin_info();
523
		}
524
525
		return isset( $info['Version'] ) ? $info['Version'] : 0;
526
	}
527
528
	/**
529
	 * Get WP Option: jetpack_beta_active
530
	 */
531
	public static function get_option() {
532
		return get_option( self::$option );
533
	}
534
535
	/**
536
	 * Get WP Option: jetpack_beta_dev_currently_installed
537
	 */
538
	public static function get_dev_installed() {
539
		return get_option( self::$option_dev_installed );
540
	}
541
542
	/**
543
	 * Get active Jetpack branch/section.
544
	 */
545
	public static function get_branch_and_section() {
546
		$option = (array) self::get_option();
547
		if ( false === $option[0] ) {
548
			// See if the Jetpack plugin is enabled.
549
			if ( is_plugin_active( JETPACK_PLUGIN_FILE ) ) {
550
				return array( 'stable', 'stable' );
551
			}
552
			return array( false, false );
553
		}
554
		// Branch and section.
555
		return $option;
556
	}
557
558
	/**
559
	 * Check if Jetpack version is 'stable' version.
560
	 */
561 View Code Duplication
	public static function is_on_stable() {
562
		$branch_and_section = self::get_branch_and_section();
563
		if ( empty( $branch_and_section[0] ) || 'stable' === $branch_and_section[0] ) {
564
			return true;
565
		}
566
		return false;
567
	}
568
569
	/**
570
	 * Check if Jetpack active version is a tag version.
571
	 */
572
	public static function is_on_tag() {
573
		$option = (array) self::get_option();
574
		if ( isset( $option[1] ) && 'tags' === $option[1] ) {
575
			return true;
576
		}
577
		return false;
578
	}
579
580
	/**
581
	 * Get active Jetpack Dev branch/section.
582
	 */
583
	public static function get_branch_and_section_dev() {
584
		$option = (array) self::get_dev_installed();
585
		if ( false !== $option[0] && isset( $option[1] ) ) {
586
			return array( $option[0], $option[1] );
587
		}
588
		if ( is_plugin_active( JETPACK_DEV_PLUGIN_FILE ) ) {
589
			return array( 'stable', 'stable' );
590
		}
591
		return array( false, false );
592
	}
593
594
	/**
595
	 * Massage JP plugin version string.
596
	 *
597
	 * @param bool $is_dev_version - If JP Dev version is being queried.
598
	 */
599
	public static function get_jetpack_plugin_pretty_version( $is_dev_version = false ) {
600
		if ( $is_dev_version ) {
601
			list( $branch, $section ) = self::get_branch_and_section_dev();
602
		} else {
603
			list( $branch, $section ) = self::get_branch_and_section();
604
		}
605
606
		if ( ! $section ) {
607
			return '';
608
		}
609
610
		if ( 'master' === $section ) {
611
			return 'Bleeding Edge';
612
		}
613
614
		if ( 'stable' === $section ) {
615
			return 'Latest Stable';
616
		}
617
618
		if ( 'tags' === $section ) {
619
			return sprintf(
620
				// translators: %1$s: a tagged Jetpack plugin version.
621
				__( 'Public release (<a href="https://plugins.trac.wordpress.org/browser/jetpack/tags/%1$s" target="_blank" rel="noopener noreferrer">available on WordPress.org</a>)', 'jetpack-beta' ),
622
				esc_attr( $branch )
623
			);
624
		}
625
626
		if ( 'rc' === $section ) {
627
			return 'Release Candidate';
628
		}
629
630
		if ( 'pr' === $section ) {
631
			$branch = str_replace( '-', ' ', $branch );
632
			return 'Feature Branch: ' . str_replace( '_', ' / ', $branch );
633
		}
634
635
		return self::get_jetpack_plugin_version();
636
	}
637
638
	/**
639
	 * Fetch latest Jetpack version.
640
	 *
641
	 * @param bool $is_dev_version - If JP Dev version is being queried.
642
	 */
643
	public static function get_new_jetpack_version( $is_dev_version = false ) {
644
		$manifest = self::get_beta_manifest();
645
		if ( $is_dev_version ) {
646
			list( $branch, $section ) = self::get_branch_and_section_dev();
647
		} else {
648
			list( $branch, $section ) = self::get_branch_and_section();
649
		}
650
651
		if ( 'master' === $section && isset( $manifest->{$section}->version ) ) {
652
			return $manifest->{$section}->version;
653
		}
654
655
		if ( 'rc' === $section && isset( $manifest->{$section}->version ) ) {
656
			return $manifest->{$section}->version;
657
		}
658
659
		if ( isset( $manifest->{$section} ) &&
660
		isset( $manifest->{$section}->{$branch} ) &&
661
		isset( $manifest->{$section}->{$branch}->version )
662
		) {
663
			return $manifest->{$section}->{$branch}->version;
664
		}
665
		return 0;
666
	}
667
668
	/**
669
	 * Get JP Dev plugin repo URL.
670
	 */
671
	public static function get_url_dev() {
672
		list( $branch, $section ) = self::get_branch_and_section_dev();
673
		return self::get_url( $branch, $section );
674
	}
675
676
	/**
677
	 * Get JP plugin repo URL.
678
	 *
679
	 * @param string $branch  - Branch.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $branch not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
680
	 * @param string $section - Section.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $section not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
681
	 */
682
	public static function get_url( $branch = null, $section = null ) {
683
		if ( is_null( $section ) ) {
684
			list( $branch, $section ) = self::get_branch_and_section();
685
		}
686
687
		if ( 'master' === $section ) {
688
			return JETPACK_GITHUB_URL . '/tree/master-build';
689
		}
690
691
		if ( 'rc' === $section ) {
692
			return JETPACK_GITHUB_URL . '/tree/' . $section . '-build';
693
		}
694
695
		if ( 'pr' === $section ) {
696
			$manifest = self::get_beta_manifest();
697
			return isset( $manifest->{$section}->{$branch}->pr )
698
			? JETPACK_GITHUB_URL . '/pull/' . $manifest->{$section}->{$branch}->pr
699
			: JETPACK_DEFAULT_URL;
700
		}
701
		return JETPACK_DEFAULT_URL;
702
	}
703
704
	/**
705
	 * Get install URL for JP dev.
706
	 */
707
	public static function get_install_url_dev() {
708
		list( $branch, $section ) = self::get_branch_and_section_dev();
709
		return self::get_install_url( $branch, $section );
710
	}
711
712
	/**
713
	 * Get install URL for JP.
714
	 *
715
	 * @param string $branch  - Branch.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $branch not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
716
	 * @param string $section - Section.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $section not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
717
	 */
718
	public static function get_install_url( $branch = null, $section = null ) {
719
		if ( is_null( $section ) ) {
720
			list( $branch, $section ) = self::get_branch_and_section();
721
		}
722
723
		if ( 'stable' === $section ) {
724
			$org_data = self::get_org_data();
725
			return $org_data->download_link;
726
		} elseif ( 'tags' === $section ) {
727
			$org_data = self::get_org_data();
728
			return $org_data->versions->{$branch} ? $org_data->versions->{$branch} : false;
729
		}
730
		$manifest = self::get_beta_manifest( true );
731
732
		if ( 'master' === $section && isset( $manifest->{$section}->download_url ) ) {
733
			return $manifest->{$section}->download_url;
734
		}
735
736
		if ( 'rc' === $section ) {
737
			if ( isset( $manifest->{$section}->download_url ) ) {
738
				return $manifest->{$section}->download_url;
739
			}
740
			$branches = array_keys( (array) $manifest->{$section} );
741
			foreach ( $branches as $branch ) {
742
				if ( isset( $manifest->{$section}->{$branch}->download_url ) ) {
743
					return $manifest->{$section}->{$branch}->download_url;
744
				}
745
			}
746
			return null;
747
		}
748
749
		if ( isset( $manifest->{$section}->{$branch}->download_url ) ) {
750
			return $manifest->{$section}->{$branch}->download_url;
751
		}
752
		return null;
753
	}
754
755
	/**
756
	 * Get stable JP version plugin data.
757
	 */
758
	public static function get_jetpack_plugin_info_stable() {
759
		return self::get_jetpack_plugin_info( JETPACK_PLUGIN_FILE );
760
	}
761
762
	/**
763
	 * Get dev JP version plugin data.
764
	 */
765
	public static function get_jetpack_plugin_info_dev() {
766
		return self::get_jetpack_plugin_info( JETPACK_DEV_PLUGIN_FILE );
767
	}
768
769
	/**
770
	 * Get JP plugin data.
771
	 *
772
	 * @param mixed $plugin_file - JP or JP Dev plugin path.
773
	 */
774
	public static function get_jetpack_plugin_info( $plugin_file = null ) {
775
776
		if ( is_null( $plugin_file ) ) {
777
			$plugin_file = self::get_plugin_file();
778
		}
779
780
		if ( ! function_exists( 'get_plugin_data' ) ) {
781
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
782
		}
783
		$plugin_file_path = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $plugin_file;
784
785
		if ( file_exists( $plugin_file_path ) ) {
786
			return get_plugin_data( WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $plugin_file );
787
		}
788
789
		return null;
790
	}
791
792
	/**
793
	 * Switch active JP plugin version when JP Beta plugin is deactivated.
794
	 * This needs to happen on `shutdown`, otherwise it doesn't work.
795
	 */
796
	public static function switch_active() {
797
		self::replace_active_plugin( JETPACK_DEV_PLUGIN_FILE, JETPACK_PLUGIN_FILE );
798
	}
799
800
	/**
801
	 * Fetch the Jetpack beta manifest.
802
	 *
803
	 * @param bool $force_refresh - Whether to bypass cached response.
804
	 */
805
	public static function get_beta_manifest( $force_refresh = false ) {
806
		return self::get_remote_data( JETPACK_BETA_MANIFEST_URL, 'manifest', $force_refresh );
807
	}
808
809
	/**
810
	 * Fetch WordPress.org Jetpack plugin info.
811
	 */
812
	public static function get_org_data() {
813
		return self::get_remote_data( JETPACK_ORG_API_URL, 'org_data' );
814
	}
815
816
	/**
817
	 * Helper function used to fetch remote data from WordPress.org, GitHub, and betadownload.jetpack.me
818
	 *
819
	 * @param string $url       - Url being fetched.
820
	 * @param string $transient - Transient name (manifest|org_data|github_commits_).
821
	 * @param bool   $bypass    - Whether to bypass cached response.
822
	 */
823
	public static function get_remote_data( $url, $transient, $bypass = false ) {
824
		$prefix = 'jetpack_beta_';
825
		$cache  = get_site_transient( $prefix . $transient );
826
		if ( $cache && ! $bypass ) {
827
			return $cache;
828
		}
829
830
		$remote_manifest = wp_remote_get( $url );
831
832
		if ( is_wp_error( $remote_manifest ) ) {
833
			return false;
834
		}
835
836
		$cache = json_decode( wp_remote_retrieve_body( $remote_manifest ) );
837
		set_site_transient( $prefix . $transient, $cache, MINUTE_IN_SECONDS * 15 );
838
839
		return $cache;
840
	}
841
842
	/**
843
	 * Delete set transients when plugin is deactivated.
844
	 */
845
	public static function delete_all_transiants() {
846
		$prefix = 'jetpack_beta_';
847
848
		delete_site_transient( $prefix . 'org_data' );
849
		delete_site_transient( $prefix . 'manifest' );
850
851
		delete_site_transient( Jetpack_Beta_Autoupdate_Self::TRANSIENT_NAME );
852
853
	}
854
855
	/**
856
	 * Install & activate JP for the given branch/section.
857
	 *
858
	 * @param string $branch  - Branch.
859
	 * @param string $section - Section.
860
	 */
861
	public static function install_and_activate( $branch, $section ) {
862
		// Cleanup previous version of the beta plugin.
863
		if ( file_exists( WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'jetpack-pressable-beta' ) ) {
864
			// Delete the Jetpack dev plugin.
865
			require_once ABSPATH . 'wp-admin/includes/file.php';
866
			$creds = request_filesystem_credentials( site_url() . '/wp-admin/', '', false, false, array() );
867
			if ( ! WP_Filesystem( $creds ) ) {
868
				// Any problems and we exit.
869
				return new WP_error( 'Filesystem Problem' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'Filesystem Problem'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
870
			}
871
			global $wp_filesystem;
872
			if ( ! $wp_filesystem ) {
873
				return new WP_error( '$wp_filesystem is not global' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with '$wp_filesystem is not global'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
874
			}
875
876
			$working_dir = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'jetpack-pressable-beta';
877
			// Delete the folder `JETPACK_BETA_PLUGIN_FOLDER`.
878
			if ( $wp_filesystem->is_dir( $working_dir ) ) {
879
				$wp_filesystem->delete( $working_dir, true );
880
			}
881
			// Deactivate the plugin.
882
			self::replace_active_plugin( 'jetpack-pressable-beta/jetpack.php' );
883
		}
884
885 View Code Duplication
		if ( 'stable' === $section &&
886
		file_exists( WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . JETPACK_PLUGIN_FILE ) ) {
887
			self::replace_active_plugin( JETPACK_DEV_PLUGIN_FILE, JETPACK_PLUGIN_FILE, true );
888
			self::update_option( $branch, $section );
889
			return;
890
		}
891
892 View Code Duplication
		if ( self::get_branch_and_section_dev() === array( $branch, $section )
893
		&& file_exists( WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . JETPACK_DEV_PLUGIN_FILE ) ) {
894
			self::replace_active_plugin( JETPACK_PLUGIN_FILE, JETPACK_DEV_PLUGIN_FILE, true );
895
			self::update_option( $branch, $section );
896
			return;
897
		}
898
899
		self::proceed_to_install_and_activate(
900
			self::get_install_url( $branch, $section ),
901
			self::get_plugin_slug( $section ),
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Beta::get_plugin_slug() has too many arguments starting with $section.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
902
			$section
903
		);
904
		self::update_option( $branch, $section );
905
	}
906
907
	/**
908
	 * Update to the latest version.
909
	 *
910
	 * @param string $branch  - Branch.
911
	 * @param string $section - Section.
912
	 */
913
	public static function update_plugin( $branch, $section ) {
914
		self::proceed_to_install(
915
			self::get_install_url( $branch, $section ),
916
			self::get_plugin_slug( $section ),
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Beta::get_plugin_slug() has too many arguments starting with $section.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
917
			$section
918
		);
919
920 View Code Duplication
		if ( 'stable' !== $section ) {
921
			update_option( self::$option_dev_installed, array( $branch, $section, self::get_manifest_data( $branch, $section ) ) );
922
		}
923
	}
924
925
	/**
926
	 * Helper function to update installed version option.
927
	 *
928
	 * @param string $branch  - Branch.
929
	 * @param string $section - Section.
930
	 */
931
	public static function update_option( $branch, $section ) {
932 View Code Duplication
		if ( 'stable' !== $section ) {
933
			update_option( self::$option_dev_installed, array( $branch, $section, self::get_manifest_data( $branch, $section ) ) );
934
		}
935
		update_option( self::$option, array( $branch, $section ) );
936
	}
937
938
	/**
939
	 * Return manifest info for specififed branch/section.
940
	 *
941
	 * @param string $branch  - Branch.
942
	 * @param string $section - Section.
943
	 */
944
	public static function get_manifest_data( $branch, $section ) {
945
		$installed             = get_option( self::$option_dev_installed );
946
		$current_manifest_data = isset( $installed[2] ) ? $installed[2] : false;
947
948
		$manifest_data = self::get_beta_manifest();
949
950
		if ( ! isset( $manifest_data->{$section} ) ) {
951
			return $current_manifest_data;
952
		}
953
954
		if ( 'master' === $section ) {
955
			return $manifest_data->{$section};
956
		}
957
958
		if ( 'rc' === $section ) {
959
			return $manifest_data->{$section};
960
		}
961
962
		if ( isset( $manifest_data->{$section}->{$branch} ) ) {
963
			return $manifest_data->{$section}->{$branch};
964
		}
965
966
		return $current_manifest_data;
967
	}
968
969
	/**
970
	 * Install specified plugin version.
971
	 *
972
	 * @param string $url           - Url for plugin version.
973
	 * @param string $plugin_folder - Path JP or JP Dev plugin folder.
974
	 * @param string $section       - Section.
975
	 */
976
	public static function proceed_to_install_and_activate( $url, $plugin_folder = JETPACK_DEV_PLUGIN_SLUG, $section ) {
977
		self::proceed_to_install( $url, $plugin_folder, $section );
978
979
		if ( 'stable' === $section || 'tags' === $section ) {
980
			self::replace_active_plugin( JETPACK_DEV_PLUGIN_FILE, JETPACK_PLUGIN_FILE, true );
981
		} else {
982
			self::replace_active_plugin( JETPACK_PLUGIN_FILE, JETPACK_DEV_PLUGIN_FILE, true );
983
		}
984
	}
985
986
	/**
987
	 * Download plugin files.
988
	 *
989
	 * @param string $url           - Url for plugin version.
990
	 * @param string $plugin_folder - Path JP or JP Dev plugin folder.
991
	 * @param string $section       - Section.
992
	 */
993
	public static function proceed_to_install( $url, $plugin_folder = JETPACK_DEV_PLUGIN_SLUG, $section ) {
994
		$temp_path = download_url( $url );
995
996 View Code Duplication
		if ( is_wp_error( $temp_path ) ) {
997
			// translators: %1$s: download url, %2$s: error message.
998
			wp_die( wp_kses_post( sprintf( __( 'Error Downloading: <a href="%1$s">%1$s</a> - Error: %2$s', 'jetpack-beta' ), $url, $temp_path->get_error_message() ) ) );
999
		}
1000
		require_once ABSPATH . 'wp-admin/includes/file.php';
1001
		$creds = request_filesystem_credentials( site_url() . '/wp-admin/', '', false, false, array() );
1002
		/* initialize the API */
1003
		if ( ! WP_Filesystem( $creds ) ) {
1004
			/* any problems and we exit */
1005
			wp_die( esc_html( __( 'Jetpack Beta: No File System access', 'jetpack-beta' ) ) );
1006
		}
1007
1008
		global $wp_filesystem;
1009
		if ( 'stable' === $section || 'tags' === $section ) {
1010
			$plugin_path = WP_PLUGIN_DIR;
1011
		} else {
1012
			$plugin_path = str_replace( ABSPATH, $wp_filesystem->abspath(), WP_PLUGIN_DIR );
1013
		}
1014
1015
		$result = unzip_file( $temp_path, $plugin_path );
1016
1017 View Code Duplication
		if ( is_wp_error( $result ) ) {
1018
			// translators: %1$s: error message.
1019
			wp_die( esc_html( sprintf( __( 'Error Unziping file: Error: %1$s', 'jetpack-beta' ), $result->get_error_message() ) ) );
1020
		}
1021
	}
1022
1023
	/**
1024
	 * Check if plugin is network activated.
1025
	 */
1026
	public static function is_network_active() {
1027
		if ( ! is_multisite() ) {
1028
			return false;
1029
		}
1030
1031
		if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
1032
			return false;
1033
		}
1034
1035
		if ( is_plugin_active_for_network( JETPACK_PLUGIN_FILE ) || is_plugin_active_for_network( JETPACK_DEV_PLUGIN_FILE ) ) {
1036
			return true;
1037
		}
1038
1039
		return false;
1040
	}
1041
1042
	/**
1043
	 * Swap plugin files.
1044
	 *
1045
	 * @param string $current_plugin      - Current plugin path.
1046
	 * @param string $replace_with_plugin - Plugin path to replace with.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $replace_with_plugin not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1047
	 * @param bool   $force_activate      - Whether to force activate plguin.
1048
	 */
1049
	public static function replace_active_plugin( $current_plugin, $replace_with_plugin = null, $force_activate = false ) {
1050
		// The autoloader sets the cache in a shutdown hook. Clear it after the autoloader sets it.
1051
		add_action( 'shutdown', array( __CLASS__, 'clear_autoloader_plugin_cache' ), 99 );
1052
1053
		if ( self::is_network_active() ) {
1054
			$new_active_plugins     = array();
1055
			$network_active_plugins = get_site_option( 'active_sitewide_plugins' );
1056
			foreach ( $network_active_plugins as $plugin => $date ) {
1057
				$key                        = ( $plugin === $current_plugin ? $replace_with_plugin : $plugin );
1058
				$new_active_plugins[ $key ] = $date;
1059
			}
1060
			update_site_option( 'active_sitewide_plugins', $new_active_plugins );
1061
			return;
1062
		}
1063
1064
		$active_plugins     = (array) get_option( 'active_plugins', array() );
1065
		$new_active_plugins = array();
1066
1067
		if ( empty( $replace_with_plugin ) ) {
1068
			$new_active_plugins = array_diff( $active_plugins, array( $current_plugin ) );
1069
		} else {
1070
			foreach ( $active_plugins as $plugin ) {
1071
				$new_active_plugins[] = ( $plugin === $current_plugin ? $replace_with_plugin : $plugin );
1072
			}
1073
		}
1074
1075
		if ( $force_activate && ! in_array( $replace_with_plugin, $new_active_plugins, true ) ) {
1076
			$new_active_plugins[] = $replace_with_plugin;
1077
		}
1078
		update_option( 'active_plugins', $new_active_plugins );
1079
	}
1080
1081
	/**
1082
	 * Check if `stable` should be updated.
1083
	 *
1084
	 * @return bool
1085
	 */
1086
	public static function should_update_stable_version() {
1087
		// Pressable Jetpack version is manage via Pressable.
1088
		if ( defined( 'IS_PRESSABLE' ) && IS_PRESSABLE ) {
1089
			return false;
1090
		}
1091
		// Check if we are Jetpack plugin is installed via git.
1092
		if ( file_exists( WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . 'jetpack/.git' ) ) {
1093
			return false;
1094
		}
1095
1096
		// Check if running a tag directly from svn.
1097
		if ( self::is_on_tag() ) {
1098
			return false;
1099
		}
1100
1101
		$updates = get_site_transient( 'update_plugins' );
1102
1103
		if ( isset( $updates->response, $updates->response[ JETPACK_PLUGIN_FILE ] ) ) {
1104
			return true;
1105
		}
1106
		$org_data    = self::get_org_data();
1107
		$plugin_data = self::get_jetpack_plugin_info_stable();
1108
1109
		return ( isset( $plugin_data['Version'], $org_data->version )
1110
			&& $org_data->version !== $plugin_data['Version'] );
1111
	}
1112
1113
	/**
1114
	 * Here we are checking if the DEV branch that we are currenly on is not something that is available in the manifest
1115
	 * Meaning that the DEV branch was merged into master and so we need to update it.
1116
	 *
1117
	 * @return bool
1118
	 */
1119
	public static function should_update_dev_to_master() {
1120
		list( $branch, $section ) = self::get_branch_and_section_dev();
1121
1122
		if ( false === $branch || 'master' === $section || 'rc' === $section || 'tags' === $section ) {
1123
			return false;
1124
		}
1125
		$manifest = self::get_beta_manifest();
1126
		return ! isset( $manifest->{$section}->{$branch} );
1127
	}
1128
1129
	/**
1130
	 * Get WP Option: jp_beta_autoupdate
1131
	 */
1132
	public static function is_set_to_autoupdate() {
1133
		return get_option( self::$option_autoupdate, false );
1134
	}
1135
1136
	/**
1137
	 * Get WP Option: jp_beta_email_notifications
1138
	 */
1139
	public static function is_set_to_email_notifications() {
1140
		return get_option( self::$option_email_notif, true );
1141
	}
1142
1143
	/**
1144
	 * Clear scheduled WP-Cron jobs on plugin deactivation.
1145
	 */
1146
	public static function clear_autoupdate_cron() {
1147
		if ( ! is_main_site() ) {
1148
			return;
1149
		}
1150
		wp_clear_scheduled_hook( self::$auto_update_cron_hook );
1151
1152
		if ( function_exists( 'wp_unschedule_hook' ) ) { // New in WP `4.9`.
1153
			wp_unschedule_hook( self::$auto_update_cron_hook );
1154
		}
1155
	}
1156
1157
	/**
1158
	 * Schedule plugin update jobs.
1159
	 */
1160
	public static function schedule_hourly_autoupdate() {
1161
		wp_clear_scheduled_hook( self::$auto_update_cron_hook );
1162
		wp_schedule_event( time(), 'hourly', self::$auto_update_cron_hook );
1163
	}
1164
1165
	/**
1166
	 * Determine if plugin update jobs should be scheduled.
1167
	 */
1168
	public static function maybe_schedule_autoupdate() {
1169
		if ( ! self::is_set_to_autoupdate() ) {
1170
			return;
1171
		}
1172
1173
		if ( ! is_main_site() ) {
1174
			return;
1175
		}
1176
		$has_schedule_already = wp_get_schedule( self::$auto_update_cron_hook );
1177
		if ( ! $has_schedule_already ) {
1178
			self::schedule_hourly_autoupdate();
1179
		}
1180
	}
1181
1182
	/**
1183
	 * Get "What changed" info for display.
1184
	 *
1185
	 * @return string|false
1186
	 */
1187
	public static function what_changed() {
1188
		$commit = self::get_version_commit();
1189
		if ( $commit ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $commit of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false 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...
1190
			$html        = '';
1191
			$commit_data = self::get_commit_data_from_github( $commit );
1192
			if ( isset( $commit_data->commit->message ) ) {
1193
				$html .= sprintf(
1194
					__( "\n %1\$s \n\n[Commit](%2\$s)", 'jetpack-beta' ),
1195
					esc_html( $commit_data->commit->message ),
1196
					esc_url( $commit_data->html_url )
1197
				);
1198
				"\n\n";
1199
			}
1200
			if ( ! empty( $commit_data->files ) ) {
1201
				$html .= "\n\n";
1202
				// translators: %d: number of files changed.
1203
				$html .= sprintf( _n( '%d file changed ', '%d files changed', count( $commit_data->files ), 'jetpack-beta' ) );
1204
				$html .= "\n";
1205
				foreach ( $commit_data->files as $file ) {
1206
					$added_deleted_changed = array();
1207
					if ( $file->additions ) {
1208
						$added_deleted_changed[] = '+' . $file->additions;
1209
					}
1210
					if ( $file->deletions ) {
1211
						$added_deleted_changed[] = '-' . $file->deletions;
1212
					}
1213
					$html .= sprintf( "- %s ... (%s %s) \n", esc_html( $file->filename ), esc_html( $file->status ), implode( ' ', $added_deleted_changed ) );
1214
				}
1215
				$html .= "\n\n";
1216
			}
1217
			if ( ! empty( $html ) ) {
1218
				return $html;
1219
			}
1220
		}
1221
		return false;
1222
	}
1223
1224
	/**
1225
	 * Get version commit if available.
1226
	 *
1227
	 * @return string|false
1228
	 */
1229
	public static function get_version_commit() {
1230
		$split_version = explode( '-', self::get_jetpack_plugin_version() );
1231
		if ( isset( $split_version[3] ) ) {
1232
			return $split_version[3];
1233
		}
1234
		return false;
1235
	}
1236
1237
	/**
1238
	 * Fetch commit data from GitHub.
1239
	 *
1240
	 * @param string $commit - The commit to fetch.
1241
	 */
1242
	public static function get_commit_data_from_github( $commit ) {
1243
		return self::get_remote_data( JETPACK_GITHUB_API_URL . 'commits/' . $commit, 'github_commits_' . $commit );
1244
	}
1245
1246
	/**
1247
	 * The jetpack_beta_autoupdate_hourly_cron job.
1248
	 */
1249
	public static function run_autoupdate() {
1250
		if ( ! self::is_set_to_autoupdate() ) {
1251
			return;
1252
		}
1253
1254
		if ( ! is_main_site() ) {
1255
			return;
1256
		}
1257
1258
		require_once ABSPATH . 'wp-admin/includes/file.php';
1259
		require_once ABSPATH . 'wp-admin/includes/plugin.php';
1260
		require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1261
		wp_clean_plugins_cache();
1262
		ob_start();
1263
		wp_update_plugins(); // Check for Plugin updates.
1264
		ob_end_clean();
1265
		$plugins = array();
1266
		if (
1267
		! self::is_on_stable() &&
1268
		( self::should_update_dev_to_master() || self::should_update_dev_version() )
1269
		) {
1270
			add_filter( 'upgrader_source_selection', array( 'Jetpack_Beta', 'check_for_main_files' ), 10, 2 );
1271
1272
			// If response is false, don't alter the transient.
1273
			$plugins[] = JETPACK_DEV_PLUGIN_FILE;
1274
		}
1275
		$autupdate = Jetpack_Beta_Autoupdate_Self::instance();
1276
		if ( $autupdate->has_never_version() ) {
1277
			$plugins[] = JPBETA__PLUGIN_FOLDER . '/jetpack-beta.php';
1278
		}
1279
1280
		if ( empty( $plugins ) ) {
1281
			return;
1282
		}
1283
1284
		// Unhook this functions that output things before we send our response header.
1285
		remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
1286
		remove_action( 'upgrader_process_complete', 'wp_version_check' );
1287
		remove_action( 'upgrader_process_complete', 'wp_update_themes' );
1288
1289
		$skin = new WP_Ajax_Upgrader_Skin();
1290
		// The Automatic_Upgrader_Skin skin shouldn't output anything.
1291
		$upgrader = new Plugin_Upgrader( $skin );
1292
		$upgrader->init();
1293
		// This avoids the plugin to be deactivated.
1294
		// Using bulk upgrade puts the site into maintenance mode during the upgrades.
1295
		$result = $upgrader->bulk_upgrade( $plugins );
1296
		$errors = $upgrader->skin->get_errors();
1297
		$log    = $upgrader->skin->get_upgrade_messages();
1298
1299
		if ( is_wp_error( $errors ) && $errors->get_error_code() ) {
1300
			return $errors;
1301
		}
1302
1303
		if ( $result && ! defined( 'JETPACK_BETA_SKIP_EMAIL' ) && self::is_set_to_email_notifications() ) {
1304
			self::send_autoupdate_email( $plugins, $log );
1305
		}
1306
	}
1307
1308
	/**
1309
	 * Builds and sends an email about succesfull plugin autoupdate.
1310
	 *
1311
	 * @param Array  $plugins - List of plugins that were updated.
1312
	 * @param String $log     - Upgrade message from core's plugin upgrader.
1313
	 */
1314
	private static function send_autoupdate_email( $plugins, $log ) {
1315
		$admin_email = get_site_option( 'admin_email' );
1316
1317
		if ( empty( $admin_email ) ) {
1318
			return;
1319
		}
1320
1321
		// In case the code is called in a scope different from wp-admin.
1322
		require_once JPBETA__PLUGIN_DIR . 'class-jetpack-beta-admin.php';
1323
1324
		// Calling empty() on a function return value crashes in PHP < 5.5.
1325
		// Thus we assign the return value explicitly and then check with empty().
1326
		$bloginfo_name = get_bloginfo( 'name' );
1327
		$site_title    = ! empty( $bloginfo_name ) ? get_bloginfo( 'name' ) : get_site_url();
1328
		$what_updated  = 'Jetpack Beta Tester Plugin';
1329
		// translators: %s: The site title.
1330
		$subject = sprintf( __( '[%s] Autoupdated Jetpack Beta Tester', 'jetpack-beta' ), $site_title );
1331
1332
		if ( in_array( JETPACK_DEV_PLUGIN_FILE, $plugins, true ) ) {
1333
			$subject = sprintf(
1334
				// translators: %1$s: site title, %2$s: pretty plugin version (eg 9.3).
1335
				__( '[%1$s] Autoupdated Jetpack %2$s ', 'jetpack-beta' ),
1336
				$site_title,
1337
				self::get_jetpack_plugin_pretty_version()
1338
			);
1339
1340
			$what_updated = sprintf(
1341
				// translators: $1$s: pretty plugin version, $2$s: raw plugin version (eg 9.3.2-beta).
1342
				__( 'Jetpack %1$s (%2$s)', 'jetpack-beta' ),
1343
				self::get_jetpack_plugin_pretty_version(),
1344
				self::get_jetpack_plugin_version()
1345
			);
1346
1347
			if ( count( $plugins ) > 1 ) {
1348
				$subject = sprintf(
1349
					// translators: %1$s: site title, %2$s: pretty plugin version.
1350
					__( '[%1$s] Autoupdated Jetpack %2$s and the Jetpack Beta Tester', 'jetpack-beta' ),
1351
					$site_title,
1352
					self::get_jetpack_plugin_pretty_version()
1353
				);
1354
1355
				$what_updated = sprintf(
1356
					// translators: $1$s: pretty plugin version, $2$s: raw plugin version.
1357
					__( 'Jetpack %1$s (%2$s) and the Jetpack Beta Tester', 'jetpack-beta' ),
1358
					self::get_jetpack_plugin_pretty_version(),
1359
					self::get_jetpack_plugin_version()
1360
				);
1361
			}
1362
		}
1363
1364
		$message = sprintf(
1365
			// translators: %1$s: site url, $2$s: text of what has updated.
1366
			__( 'Howdy! Your site at %1$s has autoupdated %2$s.', 'jetpack-beta' ),
1367
			home_url(),
1368
			$what_updated
1369
		);
1370
		$message .= "\n\n";
1371
1372
		$what_changed = self::what_changed();
1373
		if ( $what_changed ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $what_changed of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false 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...
1374
			$message .= __( 'What changed?', 'jetpack-beta' );
1375
			$message .= wp_strip_all_tags( $what_changed );
1376
		}
1377
1378
		$message .= __( 'During the autoupdate the following happened:', 'jetpack-beta' );
1379
		$message .= "\n\n";
1380
		// Can only reference the About screen if their update was successful.
1381
		$log      = array_map( 'html_entity_decode', $log );
1382
		$message .= ' - ' . implode( "\n - ", $log );
1383
		$message .= "\n\n";
1384
1385
		// Adds To test section. for PR's it's a PR description, for master/RC - it's a to_test.md file contents.
1386
		$message .= Jetpack_Beta_Admin::to_test_content();
1387
		$message .= "\n\n";
1388
1389
		wp_mail( $admin_email, $subject, $message );
1390
	}
1391
1392
	/**
1393
	 * This checks intends to fix errors in our build server when Jetpack.
1394
	 *
1395
	 * @param string $source        - Source path.
1396
	 * @param string $remote_source - Remote path.
1397
	 *
1398
	 * @return WP_Error
1399
	 */
1400
	public static function check_for_main_files( $source, $remote_source ) {
1401
		if ( $source === $remote_source . '/jetpack-dev/' ) {
1402
			if ( ! file_exists( $source . 'jetpack.php' ) ) {
1403
				return new WP_Error( 'plugin_file_does_not_exist', __( 'Main Plugin File does not exist', 'jetpack-beta' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'plugin_file_does_not_exist'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1404
			}
1405
			if ( ! file_exists( $source . '_inc/build/static.html' ) ) {
1406
				return new WP_Error( 'static_admin_page_does_not_exist', __( 'Static Admin Page File does not exist', 'jetpack-beta' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'static_admin_page_does_not_exist'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1407
			}
1408
			if ( ! file_exists( $source . '_inc/build/admin.js' ) ) {
1409
				return new WP_Error( 'admin_page_does_not_exist', __( 'Admin Page File does not exist', 'jetpack-beta' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'admin_page_does_not_exist'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1410
			}
1411
			// It has happened that sometimes a generated bundle from the master branch ends up with an empty
1412
			// vendor directory. Used to be a problem in the beta building process.
1413
			if ( self::is_dir_empty( $source . 'vendor' ) ) {
1414
				return new WP_Error( 'vendor_dir_is_empty', __( 'The dependencies dir (vendor) is empty', 'jetpack-beta' ) );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'vendor_dir_is_empty'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1415
			}
1416
		}
1417
1418
		return $source;
1419
	}
1420
1421
	/**
1422
	 * Checks if a dir is empty.
1423
	 *
1424
	 * @param [type] $dir The absolute directory path to check.
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1425
	 * @return boolean
1426
	 */
1427
	public static function is_dir_empty( $dir ) {
1428
		return ( count( scandir( $dir ) ) === 2 );
1429
	}
1430
1431
	/**
1432
	 * Callback function to include Jetpack beta options into Jetpack sync whitelist.
1433
	 *
1434
	 * @param Array $whitelist List of whitelisted options to sync.
1435
	 */
1436
	public function add_to_options_whitelist( $whitelist ) {
1437
		$whitelist[] = self::$option;
1438
		$whitelist[] = self::$option_dev_installed;
1439
		$whitelist[] = self::$option_autoupdate;
1440
		$whitelist[] = self::$option_email_notif;
1441
		return $whitelist;
1442
	}
1443
1444
	/**
1445
	 * Custom error handler to intercept errors and log them using Jetpack's own logger.
1446
	 *
1447
	 * @param int    $errno   - Error code.
1448
	 * @param string $errstr  - Error message.
1449
	 * @param string $errfile - File name where the error happened.
1450
	 * @param int    $errline - Line in the code.
1451
	 *
1452
	 * @return bool Whether to make the default handler handle the error as well.
1453
	 */
1454
	public static function custom_error_handler( $errno, $errstr, $errfile, $errline ) {
1455
1456
		if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'log' ) ) {
1457
			$error_string = sprintf( '%s, %s:%d', $errstr, $errfile, $errline );
1458
1459
			// Only adding to log if the message is related to Jetpack.
1460
			if ( false !== stripos( $error_string, 'jetpack' ) ) {
1461
				Jetpack::log( $errno, $error_string );
1462
			}
1463
		}
1464
1465
		/**
1466
		 * The error_reporting call returns current error reporting level as an integer. Bitwise
1467
		 * AND lets us determine whether the current error is included in the current error
1468
		 * reporting level
1469
		 */
1470
		if ( ! ( error_reporting() & $errno ) ) {
1471
1472
			// If this error is not being reported in the current settings, stop reporting here by returning true.
1473
			return true;
1474
		}
1475
1476
		// Returning false makes the error go through the standard error handler as well.
1477
		return false;
1478
	}
1479
1480
	/**
1481
	 * Clears the autoloader transient.
1482
	 */
1483
	public static function clear_autoloader_plugin_cache() {
1484
		delete_transient( 'jetpack_autoloader_plugin_paths' );
1485
	}
1486
}
1487