Completed
Push — try/jetpack-stories-block-mobi... ( 5d409e...2fea66 )
by
unknown
116:16 queued 106:28
created

Jetpack_React_Page   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 467
Duplicated Lines 1.5 %

Coupling/Cohesion

Components 1
Dependencies 15

Importance

Changes 0
Metric Value
dl 7
loc 467
rs 4.08
c 0
b 0
f 0
wmc 59
lcom 1
cbo 15

19 Methods

Rating   Name   Duplication   Size   Complexity  
A get_page_hook() 0 4 1
B add_page_actions() 0 25 6
A jetpack_add_set_up_sub_nav_item() 0 5 2
A jetpack_add_dashboard_sub_nav_item() 4 6 3
A jetpack_add_settings_sub_nav_item() 3 5 4
A add_fallback_head_meta() 0 3 1
A add_noscript_head_meta() 0 5 1
A jetpack_menu_order() 0 5 1
A page_render() 0 20 2
A get_dismissed_jetpack_notices() 0 12 1
A additional_styles() 0 3 1
A page_admin_scripts() 0 36 5
F get_initial_state() 0 149 15
A get_external_services_connect_urls() 0 8 2
A get_flattened_settings() 0 5 1
A show_setup_wizard() 0 3 1
B get_update_modal_data() 0 52 6
A allow_post_embed_iframe() 0 13 2
A get_release_post_data() 0 22 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

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

Common duplication problems, and corresponding solutions are:

Complex Class

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

Complex classes like Jetpack_React_Page often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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

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

1
<?php
2
use Automattic\Jetpack\Constants;
3
use Automattic\Jetpack\Connection\REST_Connector;
4
use Automattic\Jetpack\Licensing;
5
use Automattic\Jetpack\Status;
6
use Automattic\Jetpack\Partner;
7
8
include_once( 'class.jetpack-admin-page.php' );
9
10
// Builds the landing page and its menu
11
class Jetpack_React_Page extends Jetpack_Admin_Page {
12
13
	protected $dont_show_if_not_active = false;
14
15
	protected $is_redirecting = false;
16
17
	function get_page_hook() {
18
		// Add the main admin Jetpack menu
19
		return add_menu_page( 'Jetpack', 'Jetpack', 'jetpack_admin_page', 'jetpack', array( $this, 'render' ), 'div', 3 );
20
	}
21
22
	function add_page_actions( $hook ) {
23
		/** This action is documented in class.jetpack.php */
24
		do_action( 'jetpack_admin_menu', $hook );
25
26
		if ( ! isset( $_GET['page'] ) || 'jetpack' !== $_GET['page'] ) {
27
			return; // No need to handle the fallback redirection if we are not on the Jetpack page
28
		}
29
30
		// Adding a redirect meta tag if the REST API is disabled
31
		if ( ! $this->is_rest_api_enabled() ) {
32
			$this->is_redirecting = true;
33
			add_action( 'admin_head', array( $this, 'add_fallback_head_meta' ) );
34
		}
35
36
		// Adding a redirect meta tag wrapped in noscript tags for all browsers in case they have JavaScript disabled
37
		add_action( 'admin_head', array( $this, 'add_noscript_head_meta' ) );
38
39
		// If this is the first time the user is viewing the admin, don't show JITMs.
40
		// This filter is added just in time because this function is called on admin_menu
41
		// and JITMs are initialized on admin_init
42
		if ( Jetpack::is_active() && ! Jetpack_Options::get_option( 'first_admin_view', false ) ) {
43
			Jetpack_Options::update_option( 'first_admin_view', true );
44
			add_filter( 'jetpack_just_in_time_msgs', '__return_false' );
45
		}
46
	}
47
48
	/**
49
	 * Add Jetpack Setup sub-link for eligible users
50
	 */
51
	function jetpack_add_set_up_sub_nav_item() {
52
		if ( $this->show_setup_wizard() ) {
53
			add_submenu_page( 'jetpack', __( 'Set up', 'jetpack' ), __( 'Set up', 'jetpack' ), 'jetpack_admin_page', 'jetpack#/setup', '__return_null' );
54
		}
55
	}
56
57
	/**
58
	 * Add Jetpack Dashboard sub-link and point it to AAG if the user can view stats, manage modules or if Protect is active.
59
	 *
60
	 * Works in Dev Mode or when user is connected.
61
	 *
62
	 * @since 4.3.0
63
	 */
64
	function jetpack_add_dashboard_sub_nav_item() {
65 View Code Duplication
		if ( ( new Status() )->is_offline_mode() || Jetpack::is_active() ) {
66
			add_submenu_page( 'jetpack', __( 'Dashboard', 'jetpack' ), __( 'Dashboard', 'jetpack' ), 'jetpack_admin_page', 'jetpack#/dashboard', '__return_null' );
67
			remove_submenu_page( 'jetpack', 'jetpack' );
68
		}
69
	}
70
71
	/**
72
	 * If user is allowed to see the Jetpack Admin, add Settings sub-link.
73
	 *
74
	 * @since 4.3.0
75
	 */
76
	function jetpack_add_settings_sub_nav_item() {
77 View Code Duplication
		if ( ( ( new Status() )->is_offline_mode() || Jetpack::is_active() ) && current_user_can( 'edit_posts' ) ) {
78
			add_submenu_page( 'jetpack', __( 'Settings', 'jetpack' ), __( 'Settings', 'jetpack' ), 'jetpack_admin_page', 'jetpack#/settings', '__return_null' );
79
		}
80
	}
81
82
	function add_fallback_head_meta() {
83
		echo '<meta http-equiv="refresh" content="0; url=?page=jetpack_modules">';
84
	}
85
86
	function add_noscript_head_meta() {
87
		echo '<noscript>';
88
		$this->add_fallback_head_meta();
89
		echo '</noscript>';
90
	}
91
92
	/**
93
	 * Custom menu order.
94
	 *
95
	 * @deprecated since 9.2.0
96
	 * @param array $menu_order Menu order.
97
	 * @return array
98
	 */
99
	function jetpack_menu_order( $menu_order ) {
100
		_deprecated_function( __METHOD__, 'jetpack-9.2' );
101
102
		return $menu_order;
103
	}
104
105
	function page_render() {
106
		/** This action is already documented in views/admin/admin-page.php */
107
		do_action( 'jetpack_notices' );
108
109
		// Try fetching by patch
110
		$static_html = @file_get_contents( JETPACK__PLUGIN_DIR . '_inc/build/static.html' );
111
112
		if ( false === $static_html ) {
113
114
			// If we still have nothing, display an error
115
			echo '<p>';
116
			esc_html_e( 'Error fetching static.html. Try running: ', 'jetpack' );
117
			echo '<code>yarn distclean && yarn build</code>';
118
			echo '</p>';
119
		} else {
120
121
			// We got the static.html so let's display it
122
			echo $static_html;
123
		}
124
	}
125
126
	/**
127
	 * Gets array of any Jetpack notices that have been dismissed.
128
	 *
129
	 * @since 4.0.1
130
	 * @return mixed|void
131
	 */
132
	function get_dismissed_jetpack_notices() {
133
		$jetpack_dismissed_notices = get_option( 'jetpack_dismissed_notices', array() );
134
		/**
135
		 * Array of notices that have been dismissed.
136
		 *
137
		 * @since 4.0.1
138
		 *
139
		 * @param array $jetpack_dismissed_notices If empty, will not show any Jetpack notices.
140
		 */
141
		$dismissed_notices = apply_filters( 'jetpack_dismissed_notices', $jetpack_dismissed_notices );
142
		return $dismissed_notices;
143
	}
144
145
	function additional_styles() {
146
		Jetpack_Admin_Page::load_wrapper_styles();
147
	}
148
149
	function page_admin_scripts() {
150
		if ( $this->is_redirecting ) {
151
			return; // No need for scripts on a fallback page
152
		}
153
154
155
		$is_offline_mode     = ( new Status() )->is_offline_mode();
156
		$script_deps_path    = JETPACK__PLUGIN_DIR . '_inc/build/admin.asset.php';
157
		$script_dependencies = array( 'wp-polyfill' );
158
		if ( file_exists( $script_deps_path ) ) {
159
			$asset_manifest      = include $script_deps_path;
160
			$script_dependencies = $asset_manifest['dependencies'];
161
		}
162
163
		wp_enqueue_script(
164
			'react-plugin',
165
			plugins_url( '_inc/build/admin.js', JETPACK__PLUGIN_FILE ),
166
			$script_dependencies,
167
			JETPACK__VERSION,
168
			true
169
		);
170
171
		if ( ! $is_offline_mode && Jetpack::is_active() ) {
172
			// Required for Analytics.
173
			wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
174
		}
175
176
		wp_set_script_translations( 'react-plugin', 'jetpack' );
177
178
		// Add objects to be passed to the initial state of the app.
179
		// Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
180
		wp_add_inline_script( 'react-plugin', 'var Initial_State=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( $this->get_initial_state() ) ) . '"));', 'before' );
181
182
		// This will set the default URL of the jp_redirects lib.
183
		wp_add_inline_script( 'react-plugin', 'var jetpack_redirects = { currentSiteRawUrl: "' . Jetpack::build_raw_urls( get_home_url() ) . '" };', 'before' );
184
	}
185
186
	function get_initial_state() {
187
		global $is_safari;
188
		// Load API endpoint base classes and endpoints for getting the module list fed into the JS Admin Page
189
		require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php';
190
		require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php';
191
		$moduleListEndpoint = new Jetpack_Core_API_Module_List_Endpoint();
192
		$modules = $moduleListEndpoint->get_modules();
193
194
		// Preparing translated fields for JSON encoding by transforming all HTML entities to
195
		// respective characters.
196
		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...
197
			$modules[ $slug ]['name'] = html_entity_decode( $data['name'] );
198
			$modules[ $slug ]['description'] = html_entity_decode( $data['description'] );
199
			$modules[ $slug ]['short_description'] = html_entity_decode( $data['short_description'] );
200
			$modules[ $slug ]['long_description'] = html_entity_decode( $data['long_description'] );
201
		}
202
203
		// Collecting roles that can view site stats.
204
		$stats_roles = array();
205
		$enabled_roles = function_exists( 'stats_get_option' ) ? stats_get_option( 'roles' ) : array( 'administrator' );
206
207
		if ( ! function_exists( 'get_editable_roles' ) ) {
208
			require_once ABSPATH . 'wp-admin/includes/user.php';
209
		}
210
		foreach ( get_editable_roles() as $slug => $role ) {
211
			$stats_roles[ $slug ] = array(
212
				'name' => translate_user_role( $role['name'] ),
213
				'canView' => is_array( $enabled_roles ) ? in_array( $slug, $enabled_roles, true ) : false,
214
			);
215
		}
216
217
		// Get information about current theme.
218
		$current_theme = wp_get_theme();
219
220
		// Get all themes that Infinite Scroll provides support for natively.
221
		$inf_scr_support_themes = array();
222
		foreach ( Jetpack::glob_php( JETPACK__PLUGIN_DIR . 'modules/infinite-scroll/themes' ) as $path ) {
223
			if ( is_readable( $path ) ) {
224
				$inf_scr_support_themes[] = basename( $path, '.php' );
225
			}
226
		}
227
228
		// Get last post, to build the link to Customizer in the Related Posts module.
229
		$last_post = get_posts( array( 'posts_per_page' => 1 ) );
230
		$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...
231
			? get_permalink( $last_post[0]->ID )
232
			: get_home_url();
233
234
		$current_user_data = jetpack_current_user_data();
235
236
		/**
237
		 * Adds information to the `connectionStatus` API field that is unique to the Jetpack React dashboard.
238
		 */
239
		$connection_status = array(
240
			'isInIdentityCrisis' => Jetpack::validate_sync_error_idc_option(),
241
			'sandboxDomain'      => JETPACK__SANDBOX_DOMAIN,
242
243
			/**
244
			 * Filter to add connection errors
245
			 * Format: array( array( 'code' => '...', 'message' => '...', 'action' => '...' ), ... )
246
			 *
247
			 * @since 8.7.0
248
			 *
249
			 * @param array $errors Connection errors.
250
			 */
251
			'errors'             => apply_filters( 'react_connection_errors_initial_state', array() ),
252
		);
253
254
		$connection_status = array_merge( REST_Connector::connection_status( false ), $connection_status );
255
256
		return array(
257
			'WP_API_root'                 => esc_url_raw( rest_url() ),
258
			'WP_API_nonce'                => wp_create_nonce( 'wp_rest' ),
259
			'pluginBaseUrl'               => plugins_url( '', JETPACK__PLUGIN_FILE ),
260
			'connectionStatus'            => $connection_status,
261
			'connectUrl'                  => false == $current_user_data['isConnected'] // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
262
				? Jetpack::init()->build_connect_url( true, false, false )
263
				: '',
264
			'dismissedNotices'            => $this->get_dismissed_jetpack_notices(),
265
			'isDevVersion'                => Jetpack::is_development_version(),
266
			'currentVersion'              => JETPACK__VERSION,
267
			'is_gutenberg_available'      => true,
268
			'getModules'                  => $modules,
269
			'rawUrl'                      => Jetpack::build_raw_urls( get_home_url() ),
270
			'adminUrl'                    => esc_url( admin_url() ),
271
			'siteTitle'                   => (string) htmlspecialchars_decode( get_option( 'blogname' ), ENT_QUOTES ),
272
			'stats'                       => array(
273
				// data is populated asynchronously on page load.
274
				'data'  => array(
275
					'general' => false,
276
					'day'     => false,
277
					'week'    => false,
278
					'month'   => false,
279
				),
280
				'roles' => $stats_roles,
281
			),
282
			'aff'                         => Partner::init()->get_partner_code( Partner::AFFILIATE_CODE ),
283
			'partnerSubsidiaryId'         => Partner::init()->get_partner_code( Partner::SUBSIDIARY_CODE ),
284
			'settings'                    => $this->get_flattened_settings( $modules ),
0 ignored issues
show
Bug introduced by
It seems like $modules defined by $moduleListEndpoint->get_modules() on line 192 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...
285
			'userData'                    => array(
286
				'currentUser' => $current_user_data,
287
			),
288
			'siteData'                    => array(
289
				'icon'                       => has_site_icon()
290
					? apply_filters( 'jetpack_photon_url', get_site_icon_url(), array( 'w' => 64 ) )
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with array('w' => 64).

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...
291
					: '',
292
				'siteVisibleToSearchEngines' => '1' == get_option( 'blog_public' ), // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
293
				/**
294
				 * Whether promotions are visible or not.
295
				 *
296
				 * @since 4.8.0
297
				 *
298
				 * @param bool $are_promotions_active Status of promotions visibility. True by default.
299
				 */
300
				'showPromotions'             => apply_filters( 'jetpack_show_promotions', true ),
301
				'isAtomicSite'               => jetpack_is_atomic_site(),
302
				'plan'                       => Jetpack_Plan::get(),
303
				'showBackups'                => Jetpack::show_backups_ui(),
304
				'showSetupWizard'            => $this->show_setup_wizard(),
305
				'isMultisite'                => is_multisite(),
306
				'dateFormat'                 => get_option( 'date_format' ),
307
			),
308
			'themeData'                   => array(
309
				'name'      => $current_theme->get( 'Name' ),
310
				'hasUpdate' => (bool) get_theme_update_available( $current_theme ),
311
				'support'   => array(
312
					'infinite-scroll' => current_theme_supports( 'infinite-scroll' ) || in_array( $current_theme->get_stylesheet(), $inf_scr_support_themes, true ),
313
				),
314
			),
315
			'jetpackStateNotices'         => array(
316
				'messageCode'      => Jetpack::state( 'message' ),
317
				'errorCode'        => Jetpack::state( 'error' ),
318
				'errorDescription' => Jetpack::state( 'error_description' ),
319
				'messageContent'   => Jetpack::state( 'display_update_modal' ) ? $this->get_update_modal_data() : null,
320
			),
321
			'tracksUserData'              => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
322
			'currentIp'                   => function_exists( 'jetpack_protect_get_ip' ) ? jetpack_protect_get_ip() : false,
323
			'lastPostUrl'                 => esc_url( $last_post ),
324
			'externalServicesConnectUrls' => $this->get_external_services_connect_urls(),
325
			'calypsoEnv'                  => Jetpack::get_calypso_env(),
326
			'products'                    => Jetpack::get_products_for_purchase(),
327
			'setupWizardStatus'           => Jetpack_Options::get_option( 'setup_wizard_status', 'not-started' ),
328
			'isSafari'                    => $is_safari,
329
			'doNotUseConnectionIframe'    => Constants::is_true( 'JETPACK_SHOULD_NOT_USE_CONNECTION_IFRAME' ),
330
			'licensing'                   => array(
331
				'error' => Licensing::instance()->last_error(),
332
			),
333
		);
334
	}
335
336
	function get_external_services_connect_urls() {
337
		$connect_urls = array();
338
		jetpack_require_lib( 'class.jetpack-keyring-service-helper' );
339
		foreach ( Jetpack_Keyring_Service_Helper::$SERVICES as $service_name => $service_info ) {
340
			$connect_urls[ $service_name ] = Jetpack_Keyring_Service_Helper::connect_url( $service_name, $service_info[ 'for' ] );
341
		}
342
		return $connect_urls;
343
	}
344
345
	/**
346
	 * Returns an array of modules and settings both as first class members of the object.
347
	 *
348
	 * @param array $modules the result of an API request to get all modules.
349
	 *
350
	 * @return array flattened settings with modules.
351
	 */
352
	function get_flattened_settings( $modules ) {
353
		$core_api_endpoint = new Jetpack_Core_API_Data();
354
		$settings = $core_api_endpoint->get_all_options();
355
		return $settings->data;
356
	}
357
358
359
	/**
360
	 * Returns a boolean for whether the Setup Wizard should be displayed or not.
361
	 *
362
	 * @return bool True if the Setup Wizard should be displayed, false otherwise.
363
	 */
364
	public function show_setup_wizard() {
365
		return Jetpack_Wizard::can_be_displayed();
366
	}
367
368
	/**
369
	 * Returns the release post content and image data as an associative array.
370
	 * This data is used to create the update modal.
371
	 */
372
	public function get_update_modal_data() {
373
		$post_data = $this->get_release_post_data();
374
375
		if ( ! isset( $post_data['posts'][0] ) ) {
376
			return;
377
		}
378
379
		$post = $post_data['posts'][0];
380
381
		if ( empty( $post['content'] ) ) {
382
			return;
383
		}
384
385
		// This allows us to embed videopress videos into the release post.
386
		add_filter( 'wp_kses_allowed_html', array( $this, 'allow_post_embed_iframe' ), 10, 2 );
387
		$content = wp_kses_post( $post['content'] );
388
		remove_filter( 'wp_kses_allowed_html', array( $this, 'allow_post_embed_iframe' ), 10, 2 );
389
390
		$post_title = isset( $post['title'] ) ? $post['title'] : null;
391
		$title      = wp_kses( $post_title, array() );
392
393
		$post_thumbnail = isset( $post['post_thumbnail'] ) ? $post['post_thumbnail'] : null;
394
		if ( ! empty( $post_thumbnail ) ) {
395
			jetpack_require_lib( 'class.jetpack-photon-image' );
396
			$photon_image = new Jetpack_Photon_Image(
397
				array(
398
					'file'   => jetpack_photon_url( $post_thumbnail['URL'] ),
399
					'width'  => $post_thumbnail['width'],
400
					'height' => $post_thumbnail['height'],
401
				),
402
				$post_thumbnail['mime_type']
403
			);
404
			$photon_image->resize(
405
				array(
406
					'width'  => 600,
407
					'height' => null,
408
					'crop'   => false,
409
				)
410
			);
411
			$post_thumbnail_url = $photon_image->get_raw_filename();
412
		} else {
413
			$post_thumbnail_url = null;
414
		}
415
416
		$post_array = array(
417
			'release_post_content'        => $content,
418
			'release_post_featured_image' => $post_thumbnail_url,
419
			'release_post_title'          => $title,
420
		);
421
422
		return $post_array;
423
	}
424
425
	/**
426
	 * Temporarily allow post content to contain iframes, e.g. for videopress.
427
	 *
428
	 * @param string $tags    The tags.
429
	 * @param string $context The context.
430
	 */
431
	public function allow_post_embed_iframe( $tags, $context ) {
432
		if ( 'post' === $context ) {
433
			$tags['iframe'] = array(
434
				'src'             => true,
435
				'height'          => true,
436
				'width'           => true,
437
				'frameborder'     => true,
438
				'allowfullscreen' => true,
439
			);
440
		}
441
442
		return $tags;
443
	}
444
445
	/**
446
	 * Obtains the release post from the Jetpack release post blog. A release post will be displayed in the
447
	 * update modal when a post has a tag equal to the Jetpack version number.
448
	 *
449
	 * The response parameters for the post array can be found here:
450
	 * https://developer.wordpress.com/docs/api/1.1/get/sites/%24site/posts/%24post_ID/#apidoc-response
451
	 *
452
	 * @return array|null Returns an associative array containing the release post data at index ['posts'][0].
453
	 *                    Returns null if the release post data is not available.
454
	 */
455
	private function get_release_post_data() {
456
		if ( Constants::is_defined( 'TESTING_IN_JETPACK' ) && Constants::get_constant( 'TESTING_IN_JETPACK' ) ) {
457
			return null;
458
		}
459
460
		$release_post_src = add_query_arg(
461
			array(
462
				'order_by' => 'date',
463
				'tag'      => JETPACK__VERSION,
464
				'number'   => '1',
465
			),
466
			'https://public-api.wordpress.com/rest/v1/sites/' . JETPACK__RELEASE_POST_BLOG_SLUG . '/posts'
467
		);
468
469
		$response = wp_remote_get( $release_post_src );
470
471
		if ( ! is_array( $response ) ) {
472
			return null;
473
		}
474
475
		return json_decode( wp_remote_retrieve_body( $response ), true );
476
	}
477
}
478
479
/**
480
 * Gather data about the current user.
481
 *
482
 * @since 4.1.0
483
 *
484
 * @return array
485
 */
486
function jetpack_current_user_data() {
487
	$current_user   = wp_get_current_user();
488
	$is_master_user = $current_user->ID == Jetpack_Options::get_option( 'master_user' );
489
	$dotcom_data    = Jetpack::get_connected_user_data();
490
491
	// Add connected user gravatar to the returned dotcom_data.
492
	$dotcom_data['avatar'] = ( ! empty( $dotcom_data['email'] ) ?
493
		get_avatar_url(
494
			$dotcom_data['email'],
495
			array(
496
				'size'    => 64,
497
				'default' => 'mysteryman',
498
			)
499
		)
500
		: false );
501
502
	$current_user_data = array(
503
		'isConnected' => Jetpack::is_user_connected( $current_user->ID ),
504
		'isMaster'    => $is_master_user,
505
		'username'    => $current_user->user_login,
506
		'id'          => $current_user->ID,
507
		'wpcomUser'   => $dotcom_data,
508
		'gravatar'    => get_avatar_url( $current_user->ID, 64, 'mm', '', array( 'force_display' => true ) ),
509
		'permissions' => array(
510
			'admin_page'         => current_user_can( 'jetpack_admin_page' ),
511
			'connect'            => current_user_can( 'jetpack_connect' ),
512
			'disconnect'         => current_user_can( 'jetpack_disconnect' ),
513
			'manage_modules'     => current_user_can( 'jetpack_manage_modules' ),
514
			'network_admin'      => current_user_can( 'jetpack_network_admin_page' ),
515
			'network_sites_page' => current_user_can( 'jetpack_network_sites_page' ),
516
			'edit_posts'         => current_user_can( 'edit_posts' ),
517
			'publish_posts'      => current_user_can( 'publish_posts' ),
518
			'manage_options'     => current_user_can( 'manage_options' ),
519
			'view_stats'		 => current_user_can( 'view_stats' ),
520
			'manage_plugins'	 => current_user_can( 'install_plugins' )
521
									&& current_user_can( 'activate_plugins' )
522
									&& current_user_can( 'update_plugins' )
523
									&& current_user_can( 'delete_plugins' ),
524
		),
525
	);
526
527
	return $current_user_data;
528
}
529