Completed
Push — add/php-test-for-business-hour... ( 87d7ff...7c58d5 )
by
unknown
60:32 queued 48:54
created

Identity_Crisis::get_sync_error_idc_option()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 33

Duplication

Lines 33
Ratio 100 %

Importance

Changes 0
Metric Value
cc 5
nc 8
nop 1
dl 33
loc 33
rs 9.0808
c 0
b 0
f 0
1
<?php
2
/**
3
 * Identity_Crisis package.
4
 *
5
 * @package  automattic/jetpack-identity-crisis
6
 */
7
8
namespace Automattic\Jetpack;
9
10
use Automattic\Jetpack\Assets\Logo as Jetpack_Logo;
11
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
12
use Automattic\Jetpack\Constants as Constants;
13
use Automattic\Jetpack\Status as Status;
14
use Automattic\Jetpack\Sync\Functions;
15
use Automattic\Jetpack\Tracking as Tracking;
16
use Jetpack_Options;
17
use Jetpack_Tracks_Client;
18
use WP_Error;
19
20
/**
21
 * This class will handle everything involved with fixing an Identity Crisis.
22
 *
23
 * @since 4.4.0
24
 */
25
class Identity_Crisis {
26
27
	/**
28
	 * Package Version
29
	 */
30
	const PACKAGE_VERSION = '0.1.0-alpha';
31
32
	/**
33
	 * Instance of the object.
34
	 *
35
	 * @var Identity_Crisis
36
	 **/
37
	private static $instance = null;
38
39
	/**
40
	 * The wpcom value of the home URL.
41
	 *
42
	 * @var string
43
	 */
44
	public static $wpcom_home_url;
45
46
	/**
47
	 * Has safe mode been confirmed?
48
	 *
49
	 * @var bool
50
	 */
51
	public static $is_safe_mode_confirmed;
52
53
	/**
54
	 * The current screen, which is set if the current user is a non-admin and this is an admin page.
55
	 *
56
	 * @var WP_Screen
57
	 */
58
	public static $current_screen;
59
60
	/**
61
	 * Initializer.
62
	 *
63
	 * @return object
64
	 */
65
	public static function init() {
66
		if ( is_null( self::$instance ) ) {
67
			self::$instance = new Identity_Crisis();
68
		}
69
70
		return self::$instance;
71
	}
72
73
	/**
74
	 * Class constructor.
75
	 *
76
	 * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
77
	 */
78 View Code Duplication
	private function __construct() {
79
		add_action( 'jetpack_sync_processed_actions', array( $this, 'maybe_clear_migrate_option' ) );
80
		$urls_in_crisis = self::check_identity_crisis();
81
		if ( false === $urls_in_crisis ) {
82
			return;
83
		}
84
85
		self::$wpcom_home_url = $urls_in_crisis['wpcom_home'];
86
		add_action( 'init', array( $this, 'wordpress_init' ) );
87
	}
88
89
	/**
90
	 * Gets the link to the support document used to explain Safe Mode to users.
91
	 *
92
	 * @return string
93
	 */
94
	public static function get_safe_mod_doc_url() {
95
		return Redirect::get_url( 'jetpack-support-safe-mode' );
96
	}
97
98
	/**
99
	 * This method loops through the array of processed items from sync and checks if one of the items was the
100
	 * home_url or site_url callable. If so, then we delete the jetpack_migrate_for_idc option.
101
	 *
102
	 * @param array $processed_items Array of processed items that were synced to WordPress.com.
103
	 */
104 View Code Duplication
	public function maybe_clear_migrate_option( $processed_items ) {
105
		foreach ( (array) $processed_items as $item ) {
106
107
			// First, is this item a jetpack_sync_callable action? If so, then proceed.
108
			$callable_args = ( is_array( $item ) && isset( $item[0], $item[1] ) && 'jetpack_sync_callable' === $item[0] )
109
				? $item[1]
110
				: null;
111
112
			// Second, if $callable_args is set, check if the callable was home_url or site_url. If so,
113
			// clear the migrate option.
114
			if (
115
				isset( $callable_args, $callable_args[0] )
116
				&& ( 'home_url' === $callable_args[0] || 'site_url' === $callable_args[1] )
117
			) {
118
				Jetpack_Options::delete_option( 'migrate_for_idc' );
119
				break;
120
			}
121
		}
122
	}
123
124
	/**
125
	 * WordPress init.
126
	 *
127
	 * @return void
128
	 */
129 View Code Duplication
	public function wordpress_init() {
130
		if ( ! current_user_can( 'jetpack_disconnect' ) && is_admin() ) {
131
			add_action( 'admin_notices', array( $this, 'display_non_admin_idc_notice' ) );
132
			add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_idc_notice_files' ) );
133
			add_action( 'current_screen', array( $this, 'non_admins_current_screen_check' ) );
134
135
			return;
136
		}
137
138
		if (
139
			isset( $_GET['jetpack_idc_clear_confirmation'], $_GET['_wpnonce'] ) &&
140
			wp_verify_nonce( $_GET['_wpnonce'], 'jetpack_idc_clear_confirmation' )
141
		) {
142
			Jetpack_Options::delete_option( 'safe_mode_confirmed' );
143
			self::$is_safe_mode_confirmed = false;
144
		} else {
145
			self::$is_safe_mode_confirmed = (bool) Jetpack_Options::get_option( 'safe_mode_confirmed' );
146
		}
147
148
		// 121 Priority so that it's the most inner Jetpack item in the admin bar.
149
		add_action( 'admin_bar_menu', array( $this, 'display_admin_bar_button' ), 121 );
150
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_bar_css' ) );
151
152
		if ( is_admin() && ! self::$is_safe_mode_confirmed ) {
153
			add_action( 'admin_notices', array( $this, 'display_idc_notice' ) );
154
			add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_idc_notice_files' ) );
155
		}
156
	}
157
158
	/**
159
	 * Non-admins current screen check.
160
	 *
161
	 * @param object $current_screen Current screen.
162
	 *
163
	 * @return null
164
	 */
165 View Code Duplication
	public function non_admins_current_screen_check( $current_screen ) {
166
		self::$current_screen = $current_screen;
167
		if ( isset( $current_screen->id ) && 'toplevel_page_jetpack' === $current_screen->id ) {
168
			return null;
169
		}
170
171
		// If the user has dismissed the notice, and we're not currently on a Jetpack page,
172
		// then do not show the non-admin notice.
173
		if ( isset( $_COOKIE, $_COOKIE['jetpack_idc_dismiss_notice'] ) ) {
174
			remove_action( 'admin_notices', array( $this, 'display_non_admin_idc_notice' ) );
175
			remove_action( 'admin_enqueue_scripts', array( $this, 'enqueue_idc_notice_files' ) );
176
		}
177
178
		return null;
179
	}
180
181
	/**
182
	 * Renders the admin bar button.
183
	 *
184
	 * @return void
185
	 */
186 View Code Duplication
	public function display_admin_bar_button() {
187
		global $wp_admin_bar;
188
189
		$href = is_admin()
190
			? add_query_arg( 'jetpack_idc_clear_confirmation', '1' )
191
			: add_query_arg( 'jetpack_idc_clear_confirmation', '1', admin_url() );
192
193
		$href = wp_nonce_url( $href, 'jetpack_idc_clear_confirmation' );
194
195
		$title = sprintf(
196
			'<span class="jp-idc-admin-bar">%s %s</span>',
197
			'<span class="dashicons dashicons-warning"></span>',
198
			esc_html__( 'Jetpack Safe Mode', 'jetpack' )
199
		);
200
201
		$menu = array(
202
			'id'     => 'jetpack-idc',
203
			'title'  => $title,
204
			'href'   => esc_url( $href ),
205
			'parent' => 'top-secondary',
206
		);
207
208
		if ( ! self::$is_safe_mode_confirmed ) {
209
			$menu['meta'] = array(
210
				'class' => 'hide',
211
			);
212
		}
213
214
		$wp_admin_bar->add_node( $menu );
215
	}
216
217
	/**
218
	 * Checks if the site is currently in an identity crisis.
219
	 *
220
	 * @return array|bool Array of options that are in a crisis, or false if everything is OK.
221
	 */
222
	public static function check_identity_crisis() {
223
		$connection = new Connection_Manager( 'jetpack' );
224
225
		if ( ! $connection->is_active() || ( new Status() )->is_offline_mode() || ! self::validate_sync_error_idc_option() ) {
0 ignored issues
show
Deprecated Code introduced by
The method Automattic\Jetpack\Connection\Manager::is_active() has been deprecated with message: 9.6.0

This method 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 method will be removed from the class and what other method or class to use instead.

Loading history...
226
			return false;
227
		}
228
229
		return Jetpack_Options::get_option( 'sync_error_idc' );
230
	}
231
232
	/**
233
	 * Prepare URL for display.
234
	 *
235
	 * @param string $url URL to display.
236
	 *
237
	 * @return string
238
	 */
239
	public static function prepare_url_for_display( $url ) {
240
		return untrailingslashit( self::normalize_url_protocol_agnostic( $url ) );
241
	}
242
243
	/**
244
	 * Clears all IDC specific options. This method is used on disconnect and reconnect.
245
	 *
246
	 * @return void
247
	 */
248 View Code Duplication
	public static function clear_all_idc_options() {
249
		// If the site is currently in IDC, let's also clear the VaultPress connection options.
250
		// We have to check if the site is in IDC, otherwise we'd be clearing the VaultPress
251
		// connection any time the Jetpack connection is cycled.
252
		if ( self::validate_sync_error_idc_option() ) {
253
			delete_option( 'vaultpress' );
254
			delete_option( 'vaultpress_auto_register' );
255
		}
256
257
		Jetpack_Options::delete_option(
258
			array(
259
				'sync_error_idc',
260
				'safe_mode_confirmed',
261
				'migrate_for_idc',
262
			)
263
		);
264
	}
265
266
	/**
267
	 * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup.
268
	 *
269
	 * @return bool
270
	 * @since 5.4.0 Do not call get_sync_error_idc_option() unless site is in IDC
271
	 *
272
	 * @since 4.4.0
273
	 */
274 View Code Duplication
	public static function validate_sync_error_idc_option() {
275
		$is_valid = false;
276
277
		// Is the site opted in and does the stored sync_error_idc option match what we now generate?
278
		$sync_error = Jetpack_Options::get_option( 'sync_error_idc' );
279
		if ( $sync_error && self::sync_idc_optin() ) {
280
			$local_options = self::get_sync_error_idc_option();
281
			// Ensure all values are set.
282
			if ( isset( $sync_error['home'] ) && isset( $local_options['home'] ) && isset( $sync_error['siteurl'] ) && isset( $local_options['siteurl'] ) ) {
283
				// If the WP.com expected home and siteurl match local home and siteurl it is not valid IDC.
284
				if (
285
					isset( $sync_error['wpcom_home'] ) &&
286
					isset( $sync_error['wpcom_siteurl'] ) &&
287
					$sync_error['wpcom_home'] === $local_options['home'] &&
288
					$sync_error['wpcom_siteurl'] === $local_options['siteurl']
289
				) {
290
					$is_valid = false;
291
					// Enable migrate_for_idc so that sync actions are accepted.
292
					Jetpack_Options::update_option( 'migrate_for_idc', true );
293
				} elseif ( $sync_error['home'] === $local_options['home'] && $sync_error['siteurl'] === $local_options['siteurl'] ) {
294
					$is_valid = true;
295
				}
296
			}
297
		}
298
299
		/**
300
		 * Filters whether the sync_error_idc option is valid.
301
		 *
302
		 * @param bool $is_valid If the sync_error_idc is valid or not.
303
		 *
304
		 * @since 4.4.0
305
		 */
306
		$is_valid = (bool) apply_filters( 'jetpack_sync_error_idc_validation', $is_valid );
307
308
		if ( ! $is_valid && $sync_error ) {
309
			// Since the option exists, and did not validate, delete it.
310
			Jetpack_Options::delete_option( 'sync_error_idc' );
311
		}
312
313
		return $is_valid;
314
	}
315
316
	/**
317
	 * Normalizes a url by doing three things:
318
	 *  - Strips protocol
319
	 *  - Strips www
320
	 *  - Adds a trailing slash
321
	 *
322
	 * @param string $url URL to parse.
323
	 *
324
	 * @return WP_Error|string
325
	 * @since 4.4.0
326
	 */
327 View Code Duplication
	public static function normalize_url_protocol_agnostic( $url ) {
328
		$parsed_url = wp_parse_url( trailingslashit( esc_url_raw( $url ) ) );
329
		if ( ! $parsed_url || empty( $parsed_url['host'] ) || empty( $parsed_url['path'] ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parsed_url of type string|false is loosely compared to false; 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...
330
			return new WP_Error(
331
				'cannot_parse_url',
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'cannot_parse_url'.

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...
332
				sprintf(
333
				/* translators: %s: URL to parse. */
334
					esc_html__( 'Cannot parse URL %s', 'jetpack' ),
335
					$url
336
				)
337
			);
338
		}
339
340
		// Strip www and protocols.
341
		$url = preg_replace( '/^www\./i', '', $parsed_url['host'] . $parsed_url['path'] );
342
343
		return $url;
344
	}
345
346
	/**
347
	 * Gets the value that is to be saved in the jetpack_sync_error_idc option.
348
	 *
349
	 * @param array $response HTTP response.
350
	 *
351
	 * @return array Array of the local urls, wpcom urls, and error code.
352
	 * @since 4.4.0
353
	 * @since 5.4.0 Add transient since home/siteurl retrieved directly from DB.
354
	 */
355 View Code Duplication
	public static function get_sync_error_idc_option( $response = array() ) {
356
		// Since the local options will hit the database directly, store the values
357
		// in a transient to allow for autoloading and caching on subsequent views.
358
		$local_options = get_transient( 'jetpack_idc_local' );
359
		if ( false === $local_options ) {
360
			$local_options = array(
361
				'home'    => Functions::home_url(),
362
				'siteurl' => Functions::site_url(),
363
			);
364
			set_transient( 'jetpack_idc_local', $local_options, MINUTE_IN_SECONDS );
365
		}
366
367
		$options = array_merge( $local_options, $response );
368
369
		$returned_values = array();
370
		foreach ( $options as $key => $option ) {
371
			if ( 'error_code' === $key ) {
372
				$returned_values[ $key ] = $option;
373
				continue;
374
			}
375
376
			$normalized_url = self::normalize_url_protocol_agnostic( $option );
377
			if ( is_wp_error( $normalized_url ) ) {
378
				continue;
379
			}
380
381
			$returned_values[ $key ] = $normalized_url;
382
		}
383
384
		set_transient( 'jetpack_idc_option', $returned_values, MINUTE_IN_SECONDS );
385
386
		return $returned_values;
387
	}
388
389
	/**
390
	 * Returns the value of the jetpack_sync_idc_optin filter, or constant.
391
	 * If set to true, the site will be put into staging mode.
392
	 *
393
	 * @return bool
394
	 * @since 4.3.2
395
	 */
396 View Code Duplication
	public static function sync_idc_optin() {
397
		if ( Constants::is_defined( 'JETPACK_SYNC_IDC_OPTIN' ) ) {
398
			$default = Constants::get_constant( 'JETPACK_SYNC_IDC_OPTIN' );
399
		} else {
400
			$default = ! Constants::is_defined( 'SUNRISE' ) && ! is_multisite();
401
		}
402
403
		/**
404
		 * Allows sites to opt in for IDC mitigation which blocks the site from syncing to WordPress.com when the home
405
		 * URL or site URL do not match what WordPress.com expects. The default value is either true, or the value of
406
		 * JETPACK_SYNC_IDC_OPTIN constant if set.
407
		 *
408
		 * @param bool $default Whether the site is opted in to IDC mitigation.
409
		 *
410
		 * @since 4.3.2
411
		 */
412
		return (bool) apply_filters( 'jetpack_sync_idc_optin', $default );
413
	}
414
415
	/**
416
	 * Does the current admin page have help tabs?
417
	 *
418
	 * @return bool
419
	 */
420 View Code Duplication
	public function admin_page_has_help_tabs() {
421
		if ( ! function_exists( 'get_current_screen' ) ) {
422
			return false;
423
		}
424
425
		$current_screen = get_current_screen();
426
		$tabs           = $current_screen->get_help_tabs();
427
428
		return ! empty( $tabs );
429
	}
430
431
	/**
432
	 * Renders the non-admin IDC notice.
433
	 *
434
	 * @return void
435
	 */
436
	public function display_non_admin_idc_notice() {
437
		$classes = 'jp-idc-notice inline is-non-admin notice notice-warning';
438
		if ( isset( self::$current_screen ) && 'toplevel_page_jetpack' !== self::$current_screen->id ) {
439
			$classes .= ' is-dismissible';
440
		}
441
442
		if ( $this->admin_page_has_help_tabs() ) {
443
			$classes .= ' has-help-tabs';
444
		}
445
		?>
446
447
		<div class="<?php echo esc_attr( $classes ); ?>">
448
			<?php $this->render_notice_header(); ?>
449
			<div class="jp-idc-notice__content-header">
450
				<h3 class="jp-idc-notice__content-header__lead">
451
					<?php echo esc_html( $this->get_non_admin_notice_text() ); ?>
452
				</h3>
453
454
				<p class="jp-idc-notice__content-header__explanation">
455
					<?php echo esc_html( $this->get_non_admin_contact_admin_text() ); ?>
456
				</p>
457
			</div>
458
		</div>
459
		<?php
460
	}
461
462
	/**
463
	 * First "step" of the IDC mitigation. Will provide some messaging and two options/buttons.
464
	 * "Confirm Staging" - Dismiss the notice and continue on with our lives in staging mode.
465
	 * "Fix Jetpack Connection" - Will disconnect the site and start the mitigation...
466
	 *
467
	 * @return void
468
	 */
469 View Code Duplication
	public function display_idc_notice() {
470
		$classes = 'jp-idc-notice inline notice notice-warning';
471
		if ( $this->admin_page_has_help_tabs() ) {
472
			$classes .= ' has-help-tabs';
473
		}
474
		?>
475
		<div class="<?php echo esc_attr( $classes ); ?>">
476
			<?php $this->render_notice_header(); ?>
477
			<?php $this->render_notice_first_step(); ?>
478
			<?php $this->render_notice_second_step(); ?>
479
		</div>
480
		<?php
481
	}
482
483
	/**
484
	 * Enqueue CSS for the admin bar.
485
	 *
486
	 * @return void
487
	 */
488
	public function enqueue_admin_bar_css() {
489
490
		$build_assets = require_once __DIR__ . '/../build/index.asset.php';
491
492
		wp_enqueue_style(
493
			'jetpack-idc-admin-bar-css',
494
			plugin_dir_url( __DIR__ ) . 'build/css/jetpack-idc-admin-bar.css',
495
			array( 'dashicons' ),
496
			$build_assets['version']
497
		);
498
	}
499
500
	/**
501
	 * Enqueue scripts for the notice.
502
	 *
503
	 * @return void
504
	 */
505
	public function enqueue_idc_notice_files() {
506
		$build_assets                   = require_once __DIR__ . '/../build/index.asset.php';
507
		$build_assets['dependencies'][] = 'jquery';
508
509
		wp_enqueue_script(
510
			'jetpack-idc-js',
511
			Assets::get_file_url_for_environment( 'build/index.js', 'build/index.js', plugin_dir_url( __DIR__ ) ),
512
			$build_assets['dependencies'],
513
			$build_assets['version'],
514
			true
515
		);
516
517
		wp_localize_script(
518
			'jetpack-idc-js',
519
			'idcL10n',
520
			array(
521
				'apiRoot'         => esc_url_raw( rest_url() ),
522
				'nonce'           => wp_create_nonce( 'wp_rest' ),
523
				'tracksUserData'  => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
524
				'currentUrl'      => remove_query_arg( '_wpnonce', remove_query_arg( 'jetpack_idc_clear_confirmation' ) ),
525
				'tracksEventData' => array(
526
					'isAdmin'       => current_user_can( 'jetpack_disconnect' ),
527
					'currentScreen' => self::$current_screen ? self::$current_screen->id : false,
528
				),
529
			)
530
		);
531
532
		if ( ! wp_style_is( 'jetpack-dops-style', 'registered' ) ) {
533
			wp_register_style(
534
				'jetpack-dops-style',
535
				plugin_dir_url( __DIR__ ) . 'src/_inc/admin.css', // TODO Detangle style depenedencies instead of copying whole css file.
536
				array(),
537
				self::PACKAGE_VERSION
538
			);
539
		}
540
541
		wp_enqueue_style(
542
			'jetpack-idc-admin-bar-css',
543
			plugin_dir_url( __DIR__ ) . 'build/css/jetpack-idc-admin-bar.css',
544
			array( 'jetpack-dops-style' ),
545
			self::PACKAGE_VERSION
546
		);
547
		wp_enqueue_style(
548
			'jetpack-idc-css',
549
			plugin_dir_url( __DIR__ ) . 'build/jetpack-idc.css',
550
			array( 'jetpack-dops-style' ),
551
			self::PACKAGE_VERSION
552
		);
553
554
		// Register and Enqueue jp-tracks-functions.
555
		Tracking::register_tracks_functions_scripts( true );
556
	}
557
558
	/**
559
	 * Renders the notice header.
560
	 *
561
	 * @return void
562
	 */
563
	public function render_notice_header() {
564
		?>
565
		<div class="jp-idc-notice__header">
566
			<div class="jp-idc-notice__header__emblem">
567
				<?php
568
				$jetpack_logo = new Jetpack_Logo();
569
				echo esc_html( $jetpack_logo->get_jp_emblem() );
570
				?>
571
			</div>
572
			<p class="jp-idc-notice__header__text">
573
				<?php esc_html_e( 'Jetpack Safe Mode', 'jetpack' ); ?>
574
			</p>
575
		</div>
576
577
		<div class="jp-idc-notice__separator"></div>
578
		<?php
579
	}
580
581
	/**
582
	 * Is a container for the error notices.
583
	 * Will be shown/controlled by jQuery in idc-notice.js.
584
	 *
585
	 * @return void
586
	 */
587
	public function render_error_notice() {
588
		?>
589
		<div class="jp-idc-error__notice dops-notice is-error">
590
			<svg class="gridicon gridicons-notice dops-notice__icon" height="24" width="24" viewBox="0 0 24 24">
591
				<g>
592
					<path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 15h-2v-2h2v2zm0-4h-2l-.5-6h3l-.5 6z"></path>
593
				</g>
594
			</svg>
595
			<div class="dops-notice__content">
596
				<span class="dops-notice__text">
597
					<?php esc_html_e( 'Something went wrong:', 'jetpack' ); ?>
598
					<span class="jp-idc-error__desc"></span>
599
				</span>
600
				<a class="dops-notice__action" href="javascript:void(0);">
601
					<span id="jp-idc-error__action">
602
						<?php esc_html_e( 'Try Again', 'jetpack' ); ?>
603
					</span>
604
				</a>
605
			</div>
606
		</div>
607
		<?php
608
	}
609
610
	/**
611
	 * Renders the first step notice.
612
	 *
613
	 * @return void
614
	 */
615
	public function render_notice_first_step() {
616
		?>
617
		<div class="jp-idc-notice__first-step">
618
			<div class="jp-idc-notice__content-header">
619
				<h3 class="jp-idc-notice__content-header__lead">
620
					<?php echo esc_html( $this->get_first_step_header_lead() ); ?>
621
				</h3>
622
623
				<p class="jp-idc-notice__content-header__explanation">
624
					<?php echo esc_html( $this->get_first_step_header_explanation() ); ?>
625
				</p>
626
			</div>
627
628
			<?php $this->render_error_notice(); ?>
629
630
			<div class="jp-idc-notice__actions">
631
				<div class="jp-idc-notice__action">
632
					<p class="jp-idc-notice__action__explanation">
633
						<?php echo esc_html( $this->get_confirm_safe_mode_action_explanation() ); ?>
634
					</p>
635
					<button id="jp-idc-confirm-safe-mode-action" class="dops-button">
636
						<?php echo esc_html( $this->get_confirm_safe_mode_button_text() ); ?>
637
					</button>
638
				</div>
639
640
				<div class="jp-idc-notice__action">
641
					<p class="jp-idc-notice__action__explanation">
642
						<?php echo esc_html( $this->get_first_step_fix_connection_action_explanation() ); ?>
643
					</p>
644
					<button id="jp-idc-fix-connection-action" class="dops-button">
645
						<?php echo esc_html( $this->get_first_step_fix_connection_button_text() ); ?>
646
					</button>
647
				</div>
648
			</div>
649
		</div>
650
		<?php
651
	}
652
653
	/**
654
	 * Renders the second step notice.
655
	 *
656
	 * @return void
657
	 */
658
	public function render_notice_second_step() {
659
		?>
660
		<div class="jp-idc-notice__second-step">
661
			<div class="jp-idc-notice__content-header">
662
				<h3 class="jp-idc-notice__content-header__lead">
663
					<?php echo esc_html( $this->get_second_step_header_lead() ); ?>
664
				</h3>
665
			</div>
666
667
			<?php $this->render_error_notice(); ?>
668
669
			<div class="jp-idc-notice__actions">
670
				<div class="jp-idc-notice__action">
671
					<p class="jp-idc-notice__action__explanation">
672
						<?php echo esc_html( $this->get_migrate_site_action_explanation() ); ?>
673
					</p>
674
					<button id="jp-idc-migrate-action" class="dops-button">
675
						<?php echo esc_html( $this->get_migrate_site_button_text() ); ?>
676
					</button>
677
				</div>
678
679
				<div class="jp-idc-notice__action">
680
					<p class="jp-idc-notice__action__explanation">
681
						<?php echo esc_html( $this->get_start_fresh_action_explanation() ); ?>
682
					</p>
683
					<button id="jp-idc-reconnect-site-action" class="dops-button">
684
						<?php echo esc_html( $this->get_start_fresh_button_text() ); ?>
685
					</button>
686
				</div>
687
688
			</div>
689
690
			<p class="jp-idc-notice__unsure-prompt">
691
				<?php echo esc_html( $this->get_unsure_prompt() ); ?>
692
			</p>
693
		</div>
694
		<?php
695
	}
696
697
	/**
698
	 * Returns the first step header lead.
699
	 *
700
	 * @return string
701
	 */
702
	public function get_first_step_header_lead() {
703
		$html = wp_kses(
704
			sprintf(
705
			/* translators: %s: Safe mode docs URL and site URL. */
706
				__( 'Jetpack has been placed into <a href="%1$s">Safe mode</a> because we noticed this is an exact copy of <a href="%2$s">%3$s</a>.', 'jetpack' ),
707
				esc_url( self::get_safe_mod_doc_url() ),
708
				esc_url( self::$wpcom_home_url ),
709
				self::prepare_url_for_display( esc_url_raw( self::$wpcom_home_url ) )
710
			),
711
			array( 'a' => array( 'href' => array() ) )
712
		);
713
714
		/**
715
		 * Allows overriding of the default header text in the first step of the Safe Mode notice.
716
		 *
717
		 * @param string $html The HTML to be displayed.
718
		 *
719
		 * @since 4.4.0
720
		 */
721
		return apply_filters( 'jetpack_idc_first_step_header_lead', $html );
722
	}
723
724
	/**
725
	 * Returns the first step header explanation.
726
	 *
727
	 * @return string
728
	 */
729 View Code Duplication
	public function get_first_step_header_explanation() {
730
		$html = wp_kses(
731
			sprintf(
732
			/* translators: %s: Safe mode docs URL. */
733
				__( 'Please confirm Safe Mode or fix the Jetpack connection. Select one of the options below or <a href="%1$s">learn more about Safe Mode</a>.', 'jetpack' ),
734
				esc_url( self::get_safe_mod_doc_url() )
735
			),
736
			array( 'a' => array( 'href' => array() ) )
737
		);
738
739
		/**
740
		 * Allows overriding of the default header explanation text in the first step of the Safe Mode notice.
741
		 *
742
		 * @param string $html The HTML to be displayed.
743
		 *
744
		 * @since 4.4.0
745
		 */
746
		return apply_filters( 'jetpack_idc_first_step_header_explanation', $html );
747
	}
748
749
	/**
750
	 * Returns the confirm safe mode explanation.
751
	 *
752
	 * @return string
753
	 */
754
	public function get_confirm_safe_mode_action_explanation() {
755
		$html = wp_kses(
756
			sprintf(
757
			/* translators: %s: Site URL. */
758
				__( 'Is this website a temporary duplicate of <a href="%1$s">%2$s</a> for the purposes of testing, staging or development? If so, we recommend keeping it in Safe Mode.', 'jetpack' ),
759
				esc_url( untrailingslashit( self::$wpcom_home_url ) ),
760
				self::prepare_url_for_display( esc_url( self::$wpcom_home_url ) )
761
			),
762
			array( 'a' => array( 'href' => array() ) )
763
		);
764
765
		/**
766
		 * Allows overriding of the default text used to explain the confirm safe mode action.
767
		 *
768
		 * @param string $html The HTML to be displayed.
769
		 *
770
		 * @since 4.4.0
771
		 */
772
		return apply_filters( 'jetpack_idc_confirm_safe_mode_explanation', $html );
773
	}
774
775
	/**
776
	 * Returns the confirm safe mode button text.
777
	 *
778
	 * @return string
779
	 */
780
	public function get_confirm_safe_mode_button_text() {
781
		$string = esc_html__( 'Confirm Safe Mode', 'jetpack' );
782
783
		/**
784
		 * Allows overriding of the default text used for the confirm safe mode action button.
785
		 *
786
		 * @param string $string The string to be displayed.
787
		 *
788
		 * @since 4.4.0
789
		 */
790
		return apply_filters( 'jetpack_idc_confirm_safe_mode_button_text', $string );
791
	}
792
793
	/**
794
	 * Returns the first step fix connection action explanation.
795
	 *
796
	 * @return string
797
	 */
798
	public function get_first_step_fix_connection_action_explanation() {
799
		$html = wp_kses(
800
			sprintf(
801
			/* translators: %s: Site URL. */
802
				__( 'If this is a separate and new website, or the new home of <a href="%1$s">%2$s</a>, we recommend turning Safe Mode off, and re-establishing your connection to WordPress.com.', 'jetpack' ),
803
				esc_url( untrailingslashit( self::$wpcom_home_url ) ),
804
				self::prepare_url_for_display( esc_url( self::$wpcom_home_url ) )
805
			),
806
			array( 'a' => array( 'href' => array() ) )
807
		);
808
809
		/**
810
		 * Allows overriding of the default text used to explain the fix Jetpack connection action.
811
		 *
812
		 * @param string $html The HTML to be displayed.
813
		 *
814
		 * @since 4.4.0
815
		 */
816
		return apply_filters( 'jetpack_idc_first_fix_connection_explanation', $html );
817
	}
818
819
	/**
820
	 * Returns the first step fix connection button text.
821
	 *
822
	 * @return string
823
	 */
824
	public function get_first_step_fix_connection_button_text() {
825
		$string = esc_html__( "Fix Jetpack's Connection", 'jetpack' );
826
827
		/**
828
		 * Allows overriding of the default text used for the fix Jetpack connection action button.
829
		 *
830
		 * @param string $string The string to be displayed.
831
		 *
832
		 * @since 4.4.0
833
		 */
834
		return apply_filters( 'jetpack_idc_first_step_fix_connection_button_text', $string );
835
	}
836
837
	/**
838
	 * Returns the second step header lead.
839
	 *
840
	 * @return string
841
	 */
842 View Code Duplication
	public function get_second_step_header_lead() {
843
		$string = sprintf(
844
		/* translators: %s: Site URL. */
845
			esc_html__( 'Is %1$s the new home of %2$s?', 'jetpack' ),
846
			untrailingslashit( self::normalize_url_protocol_agnostic( get_home_url() ) ),
847
			untrailingslashit( self::normalize_url_protocol_agnostic( esc_url_raw( self::$wpcom_home_url ) ) )
848
		);
849
850
		/**
851
		 * Allows overriding of the default header text in the second step of the Safe Mode notice.
852
		 *
853
		 * @param string $html The HTML to be displayed.
854
		 *
855
		 * @since 4.4.0
856
		 */
857
		return apply_filters( 'jetpack_idc_second_step_header_lead', $string );
858
	}
859
860
	/**
861
	 * Returns the site action explanation.
862
	 *
863
	 * @return string
864
	 */
865 View Code Duplication
	public function get_migrate_site_action_explanation() {
866
		$html = wp_kses(
867
			sprintf(
868
			/* translators: %s: Site URL. */
869
				__( 'Yes. <a href="%1$s">%2$s</a> is replacing <a href="%3$s">%4$s</a>. I would like to migrate my stats and subscribers from <a href="%3$s">%4$s</a> to <a href="%1$s">%2$s</a>.', 'jetpack' ),
870
				esc_url( get_home_url() ),
871
				self::prepare_url_for_display( get_home_url() ),
872
				esc_url( self::$wpcom_home_url ),
873
				untrailingslashit( self::normalize_url_protocol_agnostic( esc_url_raw( self::$wpcom_home_url ) ) )
874
			),
875
			array( 'a' => array( 'href' => array() ) )
876
		);
877
878
		/**
879
		 * Allows overriding of the default text for explaining the migrate site action.
880
		 *
881
		 * @param string $html The HTML to be displayed.
882
		 *
883
		 * @since 4.4.0
884
		 */
885
		return apply_filters( 'jetpack_idc_migrate_site_explanation', $html );
886
	}
887
888
	/**
889
	 * Returns the migrate site button text.
890
	 *
891
	 * @return string
892
	 */
893
	public function get_migrate_site_button_text() {
894
		$string = esc_html__( 'Migrate Stats &amp; Subscribers', 'jetpack' );
895
896
		/**
897
		 * Allows overriding of the default text used for the migrate site action button.
898
		 *
899
		 * @param string $string The string to be displayed.
900
		 *
901
		 * @since 4.4.0
902
		 */
903
		return apply_filters( 'jetpack_idc_migrate_site_button_text', $string );
904
	}
905
906
	/**
907
	 * Returns the start fresh explanation.
908
	 *
909
	 * @return string
910
	 */
911 View Code Duplication
	public function get_start_fresh_action_explanation() {
912
		$html = wp_kses(
913
			sprintf(
914
			/* translators: %s: Site URL. */
915
				__( 'No. <a href="%1$s">%2$s</a> is a new and different website that\'s separate from <a href="%3$s">%4$s</a>. It requires  a new connection to WordPress.com for new stats and subscribers.', 'jetpack' ),
916
				esc_url( get_home_url() ),
917
				self::prepare_url_for_display( get_home_url() ),
918
				esc_url( self::$wpcom_home_url ),
919
				untrailingslashit( self::normalize_url_protocol_agnostic( esc_url_raw( self::$wpcom_home_url ) ) )
920
			),
921
			array( 'a' => array( 'href' => array() ) )
922
		);
923
924
		/**
925
		 * Allows overriding of the default text for explaining the start fresh action.
926
		 *
927
		 * @param string $html The HTML to be displayed.
928
		 *
929
		 * @since 4.4.0
930
		 */
931
		return apply_filters( 'jetpack_idc_start_fresh_explanation', $html );
932
	}
933
934
	/**
935
	 * Returns the start fresh button text.
936
	 *
937
	 * @return string
938
	 */
939
	public function get_start_fresh_button_text() {
940
		$string = esc_html__( 'Start Fresh &amp; Create New Connection', 'jetpack' );
941
942
		/**
943
		 * Allows overriding of the default text used for the start fresh action button.
944
		 *
945
		 * @param string $string The string to be displayed.
946
		 *
947
		 * @since 4.4.0
948
		 */
949
		return apply_filters( 'jetpack_idc_start_fresh_button_text', $string );
950
	}
951
952
	/**
953
	 * Returns the unsure prompt text.
954
	 *
955
	 * @return string
956
	 */
957 View Code Duplication
	public function get_unsure_prompt() {
958
		$html = wp_kses(
959
			sprintf(
960
			/* translators: %s: Safe mode docs URL. */
961
				__( 'Unsure what to do? <a href="%1$s">Read more about Jetpack Safe Mode</a>', 'jetpack' ),
962
				esc_url( self::get_safe_mod_doc_url() )
963
			),
964
			array( 'a' => array( 'href' => array() ) )
965
		);
966
967
		/**
968
		 * Allows overriding of the default text using in the "Unsure what to do?" prompt.
969
		 *
970
		 * @param string $html The HTML to be displayed.
971
		 *
972
		 * @since 4.4.0
973
		 */
974
		return apply_filters( 'jetpack_idc_unsure_prompt', $html );
975
	}
976
977
	/**
978
	 * Returns the non-admin notice text.
979
	 *
980
	 * @return string
981
	 */
982 View Code Duplication
	public function get_non_admin_notice_text() {
983
		$html = wp_kses(
984
			sprintf(
985
			/* translators: %s: Safe mode docs URL. */
986
				__( 'Jetpack has been placed into Safe Mode. Learn more about <a href="%1$s">Safe Mode</a>.', 'jetpack' ),
987
				esc_url( self::get_safe_mod_doc_url() )
988
			),
989
			array( 'a' => array( 'href' => array() ) )
990
		);
991
992
		/**
993
		 * Allows overriding of the default text that is displayed to non-admin on the Jetpack admin page.
994
		 *
995
		 * @param string $html The HTML to be displayed.
996
		 *
997
		 * @since 4.4.0
998
		 */
999
		return apply_filters( 'jetpack_idc_non_admin_notice_text', $html );
1000
	}
1001
1002
	/**
1003
	 * Returns the non-admin contact admin text.
1004
	 *
1005
	 * @return string
1006
	 */
1007
	public function get_non_admin_contact_admin_text() {
1008
		$string = esc_html__( 'An administrator of this site can take Jetpack out of Safe Mode.', 'jetpack' );
1009
1010
		/**
1011
		 * Allows overriding of the default text that is displayed to non-admins prompting them to contact an admin.
1012
		 *
1013
		 * @param string $string The string to be displayed.
1014
		 *
1015
		 * @since 4.4.0
1016
		 */
1017
		return apply_filters( 'jetpack_idc_non_admin_contact_admin_text', $string );
1018
	}
1019
}
1020
1021
add_action( 'plugins_loaded', array( 'Identity_Crisis', 'init' ) );
1022