Completed
Push — renovate/sirbrillig-phpcs-chan... ( ecf5f1...bc80b5 )
by Jeremy
125:13 queued 118:57
created

Jetpack_React_Page::show_setup_wizard()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
1
<?php
2
use Automattic\Jetpack\Constants;
3
use Automattic\Jetpack\Status;
4
use Automattic\Jetpack\Partner;
5
6
include_once( 'class.jetpack-admin-page.php' );
7
8
// Builds the landing page and its menu
9
class Jetpack_React_Page extends Jetpack_Admin_Page {
10
11
	protected $dont_show_if_not_active = false;
12
13
	protected $is_redirecting = false;
14
15
	function get_page_hook() {
16
		// Add the main admin Jetpack menu
17
		return add_menu_page( 'Jetpack', 'Jetpack', 'jetpack_admin_page', 'jetpack', array( $this, 'render' ), 'div' );
18
	}
19
20
	function add_page_actions( $hook ) {
21
		/** This action is documented in class.jetpack.php */
22
		do_action( 'jetpack_admin_menu', $hook );
23
24
		// Place the Jetpack menu item on top and others in the order they appear
25
		add_filter( 'custom_menu_order',         '__return_true' );
26
		add_filter( 'menu_order',                array( $this, 'jetpack_menu_order' ) );
27
28
		if ( ! isset( $_GET['page'] ) || 'jetpack' !== $_GET['page'] ) {
29
			return; // No need to handle the fallback redirection if we are not on the Jetpack page
30
		}
31
32
		// Adding a redirect meta tag if the REST API is disabled
33
		if ( ! $this->is_rest_api_enabled() ) {
34
			$this->is_redirecting = true;
35
			add_action( 'admin_head', array( $this, 'add_fallback_head_meta' ) );
36
		}
37
38
		// Adding a redirect meta tag wrapped in noscript tags for all browsers in case they have JavaScript disabled
39
		add_action( 'admin_head', array( $this, 'add_noscript_head_meta' ) );
40
41
		// If this is the first time the user is viewing the admin, don't show JITMs.
42
		// This filter is added just in time because this function is called on admin_menu
43
		// and JITMs are initialized on admin_init
44
		if ( Jetpack::is_active() && ! Jetpack_Options::get_option( 'first_admin_view', false ) ) {
45
			Jetpack_Options::update_option( 'first_admin_view', true );
46
			add_filter( 'jetpack_just_in_time_msgs', '__return_false' );
47
		}
48
	}
49
50
	/**
51
	 * Add Jetpack Setup sub-link for eligible users
52
	 */
53
	function jetpack_add_set_up_sub_nav_item() {
54
		if ( $this->show_setup_wizard() ) {
55
			global $submenu;
56
			$submenu['jetpack'][] = array( __( 'Set up', 'jetpack' ), 'jetpack_admin_page', 'admin.php?page=jetpack#/setup' );
57
		}
58
	}
59
60
	/**
61
	 * Add Jetpack Dashboard sub-link and point it to AAG if the user can view stats, manage modules or if Protect is active.
62
	 *
63
	 * Works in Dev Mode or when user is connected.
64
	 *
65
	 * @since 4.3.0
66
	 */
67
	function jetpack_add_dashboard_sub_nav_item() {
68 View Code Duplication
		if ( ( new Status() )->is_development_mode() || Jetpack::is_active() ) {
69
			global $submenu;
70
			if ( current_user_can( 'jetpack_admin_page' ) ) {
71
				$submenu['jetpack'][] = array( __( 'Dashboard', 'jetpack' ), 'jetpack_admin_page', 'admin.php?page=jetpack#/dashboard' );
72
			}
73
		}
74
	}
75
76
	/**
77
	 * If user is allowed to see the Jetpack Admin, add Settings sub-link.
78
	 *
79
	 * @since 4.3.0
80
	 */
81
	function jetpack_add_settings_sub_nav_item() {
82 View Code Duplication
		if ( ( ( new Status() )->is_development_mode() || Jetpack::is_active() ) && current_user_can( 'jetpack_admin_page' ) && current_user_can( 'edit_posts' ) ) {
83
			global $submenu;
84
			$submenu['jetpack'][] = array( __( 'Settings', 'jetpack' ), 'jetpack_admin_page', 'admin.php?page=jetpack#/settings' );
85
		}
86
	}
87
88
	function add_fallback_head_meta() {
89
		echo '<meta http-equiv="refresh" content="0; url=?page=jetpack_modules">';
90
	}
91
92
	function add_noscript_head_meta() {
93
		echo '<noscript>';
94
		$this->add_fallback_head_meta();
95
		echo '</noscript>';
96
	}
97
98 View Code Duplication
	function jetpack_menu_order( $menu_order ) {
99
		$jp_menu_order = array();
100
101
		foreach ( $menu_order as $index => $item ) {
102
			if ( $item != 'jetpack' )
103
				$jp_menu_order[] = $item;
104
105
			if ( $index == 0 )
106
				$jp_menu_order[] = 'jetpack';
107
		}
108
109
		return $jp_menu_order;
110
	}
111
112
	function page_render() {
113
		/** This action is already documented in views/admin/admin-page.php */
114
		do_action( 'jetpack_notices' );
115
116
		// Try fetching by patch
117
		$static_html = @file_get_contents( JETPACK__PLUGIN_DIR . '_inc/build/static.html' );
118
119
		if ( false === $static_html ) {
120
121
			// If we still have nothing, display an error
122
			echo '<p>';
123
			esc_html_e( 'Error fetching static.html. Try running: ', 'jetpack' );
124
			echo '<code>yarn distclean && yarn build</code>';
125
			echo '</p>';
126
		} else {
127
128
			// We got the static.html so let's display it
129
			echo $static_html;
130
		}
131
	}
132
133
	/**
134
	 * Gets array of any Jetpack notices that have been dismissed.
135
	 *
136
	 * @since 4.0.1
137
	 * @return mixed|void
138
	 */
139
	function get_dismissed_jetpack_notices() {
140
		$jetpack_dismissed_notices = get_option( 'jetpack_dismissed_notices', array() );
141
		/**
142
		 * Array of notices that have been dismissed.
143
		 *
144
		 * @since 4.0.1
145
		 *
146
		 * @param array $jetpack_dismissed_notices If empty, will not show any Jetpack notices.
147
		 */
148
		$dismissed_notices = apply_filters( 'jetpack_dismissed_notices', $jetpack_dismissed_notices );
149
		return $dismissed_notices;
150
	}
151
152
	function additional_styles() {
153
		Jetpack_Admin_Page::load_wrapper_styles();
154
	}
155
156
	function page_admin_scripts() {
157
		if ( $this->is_redirecting ) {
158
			return; // No need for scripts on a fallback page
159
		}
160
161
162
		$is_development_mode = ( new Status() )->is_development_mode();
163
		$script_deps_path    = JETPACK__PLUGIN_DIR . '_inc/build/admin.asset.php';
164
		$script_dependencies = array( 'wp-polyfill' );
165
		if ( file_exists( $script_deps_path ) ) {
166
			$asset_manifest      = include $script_deps_path;
167
			$script_dependencies = $asset_manifest['dependencies'];
168
		}
169
170
		wp_enqueue_script(
171
			'react-plugin',
172
			plugins_url( '_inc/build/admin.js', JETPACK__PLUGIN_FILE ),
173
			$script_dependencies,
174
			JETPACK__VERSION,
175
			true
176
		);
177
178
		if ( ! $is_development_mode && Jetpack::is_active() ) {
179
			// Required for Analytics.
180
			wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
181
		}
182
183
		// Add objects to be passed to the initial state of the app.
184
		// Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
185
		wp_add_inline_script( 'react-plugin', 'var Initial_State=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( $this->get_initial_state() ) ) . '"));', 'before' );
186
	}
187
188
	function get_initial_state() {
189
		// Load API endpoint base classes and endpoints for getting the module list fed into the JS Admin Page
190
		require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php';
191
		require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php';
192
		$moduleListEndpoint = new Jetpack_Core_API_Module_List_Endpoint();
193
		$modules = $moduleListEndpoint->get_modules();
194
195
		// Preparing translated fields for JSON encoding by transforming all HTML entities to
196
		// respective characters.
197
		foreach( $modules as $slug => $data ) {
0 ignored issues
show
Bug introduced by
The expression $modules of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
198
			$modules[ $slug ]['name'] = html_entity_decode( $data['name'] );
199
			$modules[ $slug ]['description'] = html_entity_decode( $data['description'] );
200
			$modules[ $slug ]['short_description'] = html_entity_decode( $data['short_description'] );
201
			$modules[ $slug ]['long_description'] = html_entity_decode( $data['long_description'] );
202
		}
203
204
		// Collecting roles that can view site stats.
205
		$stats_roles = array();
206
		$enabled_roles = function_exists( 'stats_get_option' ) ? stats_get_option( 'roles' ) : array( 'administrator' );
207
208
		if ( ! function_exists( 'get_editable_roles' ) ) {
209
			require_once ABSPATH . 'wp-admin/includes/user.php';
210
		}
211
		foreach ( get_editable_roles() as $slug => $role ) {
212
			$stats_roles[ $slug ] = array(
213
				'name' => translate_user_role( $role['name'] ),
214
				'canView' => is_array( $enabled_roles ) ? in_array( $slug, $enabled_roles, true ) : false,
215
			);
216
		}
217
218
		// Get information about current theme.
219
		$current_theme = wp_get_theme();
220
221
		// Get all themes that Infinite Scroll provides support for natively.
222
		$inf_scr_support_themes = array();
223
		foreach ( Jetpack::glob_php( JETPACK__PLUGIN_DIR . 'modules/infinite-scroll/themes' ) as $path ) {
224
			if ( is_readable( $path ) ) {
225
				$inf_scr_support_themes[] = basename( $path, '.php' );
226
			}
227
		}
228
229
		// Get last post, to build the link to Customizer in the Related Posts module.
230
		$last_post = get_posts( array( 'posts_per_page' => 1 ) );
231
		$last_post = isset( $last_post[0] ) && $last_post[0] instanceof WP_Post
0 ignored issues
show
Bug introduced by
The class WP_Post does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
232
			? get_permalink( $last_post[0]->ID )
233
			: get_home_url();
234
235
		$current_user_data = jetpack_current_user_data();
236
237
		$status = new Status();
238
239
		return array(
240
			'WP_API_root'                 => esc_url_raw( rest_url() ),
241
			'WP_API_nonce'                => wp_create_nonce( 'wp_rest' ),
242
			'pluginBaseUrl'               => plugins_url( '', JETPACK__PLUGIN_FILE ),
243
			'connectionStatus'            => array(
244
				'isActive'           => Jetpack::is_active(),
245
				'isStaging'          => $status->is_staging_site(),
246
				'devMode'            => array(
247
					'isActive' => $status->is_development_mode(),
248
					'constant' => defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG,
249
					'url'      => site_url() && false === strpos( site_url(), '.' ),
250
					'filter'   => apply_filters( 'jetpack_development_mode', false ),
251
				),
252
				'isPublic'           => '1' == get_option( 'blog_public' ), // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
253
				'isInIdentityCrisis' => Jetpack::validate_sync_error_idc_option(),
254
				'sandboxDomain'      => JETPACK__SANDBOX_DOMAIN,
255
			),
256
			'connectUrl'                  => false == $current_user_data['isConnected'] // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
257
				? Jetpack::init()->build_connect_url( true, false, false )
258
				: '',
259
			'dismissedNotices'            => $this->get_dismissed_jetpack_notices(),
260
			'isDevVersion'                => Jetpack::is_development_version(),
261
			'currentVersion'              => JETPACK__VERSION,
262
			'is_gutenberg_available'      => true,
263
			'getModules'                  => $modules,
264
			'rawUrl'                      => Jetpack::build_raw_urls( get_home_url() ),
265
			'adminUrl'                    => esc_url( admin_url() ),
266
			'siteTitle'                   => (string) htmlspecialchars_decode( get_option( 'blogname' ), ENT_QUOTES ),
267
			'stats'                       => array(
268
				// data is populated asynchronously on page load.
269
				'data'  => array(
270
					'general' => false,
271
					'day'     => false,
272
					'week'    => false,
273
					'month'   => false,
274
				),
275
				'roles' => $stats_roles,
276
			),
277
			'aff'                         => Partner::init()->get_partner_code( Partner::AFFILIATE_CODE ),
278
			'partnerSubsidiaryId'         => Partner::init()->get_partner_code( Partner::SUBSIDIARY_CODE ),
279
			'settings'                    => $this->get_flattened_settings( $modules ),
0 ignored issues
show
Bug introduced by
It seems like $modules defined by $moduleListEndpoint->get_modules() on line 193 can also be of type string; however, Jetpack_React_Page::get_flattened_settings() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
280
			'userData'                    => array(
281
				'currentUser' => $current_user_data,
282
			),
283
			'siteData'                    => array(
284
				'icon'                       => has_site_icon()
285
					? apply_filters( 'jetpack_photon_url', get_site_icon_url(), array( 'w' => 64 ) )
286
					: '',
287
				'siteVisibleToSearchEngines' => '1' == get_option( 'blog_public' ), // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
288
				/**
289
				 * Whether promotions are visible or not.
290
				 *
291
				 * @since 4.8.0
292
				 *
293
				 * @param bool $are_promotions_active Status of promotions visibility. True by default.
294
				 */
295
				'showPromotions'             => apply_filters( 'jetpack_show_promotions', true ),
296
				'isAtomicSite'               => jetpack_is_atomic_site(),
297
				'plan'                       => Jetpack_Plan::get(),
298
				'showBackups'                => Jetpack::show_backups_ui(),
299
				'showSetupWizard'            => $this->show_setup_wizard(),
300
				'isMultisite'                => is_multisite(),
301
			),
302
			'themeData'                   => array(
303
				'name'      => $current_theme->get( 'Name' ),
304
				'hasUpdate' => (bool) get_theme_update_available( $current_theme ),
305
				'support'   => array(
306
					'infinite-scroll' => current_theme_supports( 'infinite-scroll' ) || in_array( $current_theme->get_stylesheet(), $inf_scr_support_themes, true ),
307
				),
308
			),
309
			'locale'                      => Jetpack::get_i18n_data_json(),
310
			'localeSlug'                  => join( '-', explode( '_', get_user_locale() ) ),
311
			'jetpackStateNotices'         => array(
312
				'messageCode'      => Jetpack::state( 'message' ),
313
				'errorCode'        => Jetpack::state( 'error' ),
314
				'errorDescription' => Jetpack::state( 'error_description' ),
315
			),
316
			'tracksUserData'              => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
317
			'currentIp'                   => function_exists( 'jetpack_protect_get_ip' ) ? jetpack_protect_get_ip() : false,
318
			'lastPostUrl'                 => esc_url( $last_post ),
319
			'externalServicesConnectUrls' => $this->get_external_services_connect_urls(),
320
			'calypsoEnv'                  => Jetpack::get_calypso_env(),
321
		);
322
	}
323
324
	function get_external_services_connect_urls() {
325
		$connect_urls = array();
326
		jetpack_require_lib( 'class.jetpack-keyring-service-helper' );
327
		foreach ( Jetpack_Keyring_Service_Helper::$SERVICES as $service_name => $service_info ) {
328
			$connect_urls[ $service_name ] = Jetpack_Keyring_Service_Helper::connect_url( $service_name, $service_info[ 'for' ] );
329
		}
330
		return $connect_urls;
331
	}
332
333
	/**
334
	 * Returns an array of modules and settings both as first class members of the object.
335
	 *
336
	 * @param array $modules the result of an API request to get all modules.
337
	 *
338
	 * @return array flattened settings with modules.
339
	 */
340
	function get_flattened_settings( $modules ) {
341
		$core_api_endpoint = new Jetpack_Core_API_Data();
342
		$settings = $core_api_endpoint->get_all_options();
343
		return $settings->data;
344
	}
345
346
347
	/**
348
	 * Returns a boolean for whether the Setup Wizard should be displayed or not.
349
	 *
350
	 * @return bool True if the Setup Wizard should be displayed, false otherwise.
351
	 */
352
	public function show_setup_wizard() {
353
		/**
354
		 * Determines if the Setup Wizard is displayed or not.
355
		 *
356
		 * @since 8.5
357
		 *
358
		 * @param array $jetpack_show_setup_wizard If true, the Setup Wizard will be displayed. Otherwise it will not display.
359
		 */
360
		return apply_filters( 'jetpack_show_setup_wizard', false ) && Jetpack::is_active();
361
	}
362
}
363
364
/**
365
 * Gather data about the current user.
366
 *
367
 * @since 4.1.0
368
 *
369
 * @return array
370
 */
371
function jetpack_current_user_data() {
372
	$current_user   = wp_get_current_user();
373
	$is_master_user = $current_user->ID == Jetpack_Options::get_option( 'master_user' );
374
	$dotcom_data    = Jetpack::get_connected_user_data();
375
376
	// Add connected user gravatar to the returned dotcom_data.
377
	$dotcom_data['avatar'] = ( ! empty( $dotcom_data['email'] ) ?
378
		get_avatar_url(
379
			$dotcom_data['email'],
380
			array(
381
				'size'    => 64,
382
				'default' => 'mysteryman',
383
			)
384
		)
385
		: false );
386
387
	$current_user_data = array(
388
		'isConnected' => Jetpack::is_user_connected( $current_user->ID ),
389
		'isMaster'    => $is_master_user,
390
		'username'    => $current_user->user_login,
391
		'id'          => $current_user->ID,
392
		'wpcomUser'   => $dotcom_data,
393
		'gravatar'    => get_avatar( $current_user->ID, 40, 'mm', '', array( 'force_display' => true ) ),
394
		'permissions' => array(
395
			'admin_page'         => current_user_can( 'jetpack_admin_page' ),
396
			'connect'            => current_user_can( 'jetpack_connect' ),
397
			'disconnect'         => current_user_can( 'jetpack_disconnect' ),
398
			'manage_modules'     => current_user_can( 'jetpack_manage_modules' ),
399
			'network_admin'      => current_user_can( 'jetpack_network_admin_page' ),
400
			'network_sites_page' => current_user_can( 'jetpack_network_sites_page' ),
401
			'edit_posts'         => current_user_can( 'edit_posts' ),
402
			'publish_posts'      => current_user_can( 'publish_posts' ),
403
			'manage_options'     => current_user_can( 'manage_options' ),
404
			'view_stats'		 => current_user_can( 'view_stats' ),
405
			'manage_plugins'	 => current_user_can( 'install_plugins' )
406
									&& current_user_can( 'activate_plugins' )
407
									&& current_user_can( 'update_plugins' )
408
									&& current_user_can( 'delete_plugins' ),
409
		),
410
	);
411
412
	return $current_user_data;
413
}
414