Completed
Push — try/add-jetpack-purchase-token ( b55274...037411 )
by
unknown
122:25 queued 111:35
created

Jetpack_React_Page   F

Complexity

Total Complexity 69

Size/Duplication

Total Lines 551
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 0
Metric Value
wmc 69
lcom 1
cbo 18
dl 0
loc 551
rs 2.88
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A get_page_hook() 0 4 1
B add_page_actions() 0 25 6
A jetpack_add_dashboard_sub_nav_item() 0 6 3
B can_access_settings() 0 55 10
A jetpack_add_settings_sub_nav_item() 0 5 2
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 37 5
A get_purchase_token() 0 14 3
A generate_purchase_token() 0 3 1
F get_initial_state() 0 152 16
A get_external_services_connect_urls() 0 8 2
A get_flattened_settings() 0 5 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   Complexity   

Complex Class

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\Manager as Connection_Manager;
4
use Automattic\Jetpack\Connection\REST_Connector;
5
use Automattic\Jetpack\Device_Detection\User_Agent_Info;
6
use Automattic\Jetpack\Licensing;
7
use Automattic\Jetpack\Partner;
8
use Automattic\Jetpack\Status;
9
10
include_once( 'class.jetpack-admin-page.php' );
11
12
// Builds the landing page and its menu
13
class Jetpack_React_Page extends Jetpack_Admin_Page {
14
15
	protected $dont_show_if_not_active = false;
16
17
	protected $is_redirecting = false;
18
19
	function get_page_hook() {
20
		// Add the main admin Jetpack menu
21
		return add_menu_page( 'Jetpack', 'Jetpack', 'jetpack_admin_page', 'jetpack', array( $this, 'render' ), 'div', 3 );
22
	}
23
24
	function add_page_actions( $hook ) {
25
		/** This action is documented in class.jetpack.php */
26
		do_action( 'jetpack_admin_menu', $hook );
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_connection_ready() && ! 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 Dashboard sub-link and point it to AAG if the user can view stats, manage modules or if Protect is active.
52
	 *
53
	 * Works in Dev Mode or when user is connected.
54
	 *
55
	 * @since 4.3.0
56
	 */
57
	function jetpack_add_dashboard_sub_nav_item() {
58
		if ( ( new Status() )->is_offline_mode() || Jetpack::is_connection_ready() ) {
59
			add_submenu_page( 'jetpack', __( 'Dashboard', 'jetpack' ), __( 'Dashboard', 'jetpack' ), 'jetpack_admin_page', 'jetpack#/dashboard', '__return_null' );
60
			remove_submenu_page( 'jetpack', 'jetpack' );
61
		}
62
	}
63
64
	/**
65
	 * Determine whether a user can access the Jetpack Settings page.
66
	 *
67
	 * Rules are:
68
	 * - user is allowed to see the Jetpack Admin
69
	 * - site is connected or in offline mode
70
	 * - non-admins only need access to the settings when there are modules they can manage.
71
	 *
72
	 * @return bool $can_access_settings Can the user access settings.
73
	 */
74
	private function can_access_settings() {
75
		$connection = new Connection_Manager( 'jetpack' );
76
		$status     = new Status();
77
78
		// User must have the necessary permissions to see the Jetpack settings pages.
79
		if ( ! current_user_can( 'edit_posts' ) ) {
80
			return false;
81
		}
82
83
		// In offline mode, allow access to admins.
84
		if ( $status->is_offline_mode() && current_user_can( 'manage_options' ) ) {
85
			return true;
86
		}
87
88
		// If not in offline mode but site is not connected, bail.
89
		if ( ! Jetpack::is_connection_ready() ) {
90
			return false;
91
		}
92
93
		/*
94
		 * Additional checks for non-admins.
95
		*/
96
		if ( ! current_user_can( 'manage_options' ) ) {
97
			// If the site isn't connected at all, bail.
98
			if ( ! $connection->has_connected_owner() ) {
99
				return false;
100
			}
101
102
			/*
103
			 * If they haven't connected their own account yet,
104
			 * they have no use for the settings page.
105
			 * They will not be able to manage any settings.
106
			 */
107
			if ( ! $connection->is_user_connected() ) {
108
				return false;
109
			}
110
111
			/*
112
			 * Non-admins only have access to settings
113
			 * for the following modules:
114
			 * - Publicize
115
			 * - Post By Email
116
			 * If those modules are not available, bail.
117
			 */
118
			if (
119
				! Jetpack::is_module_active( 'post-by-email' )
120
				&& ! Jetpack::is_module_active( 'publicize' )
121
			) {
122
				return false;
123
			}
124
		}
125
126
		// fallback.
127
		return true;
128
	}
129
130
	/**
131
	 * Jetpack Settings sub-link.
132
	 *
133
	 * @since 4.3.0
134
	 * @since 9.7.0 If Connection does not have an owner, restrict it to admins
135
	 */
136
	function jetpack_add_settings_sub_nav_item() {
137
		if ( $this->can_access_settings() ) {
138
			add_submenu_page( 'jetpack', __( 'Settings', 'jetpack' ), __( 'Settings', 'jetpack' ), 'jetpack_admin_page', 'jetpack#/settings', '__return_null' );
139
		}
140
	}
141
142
	function add_fallback_head_meta() {
143
		echo '<meta http-equiv="refresh" content="0; url=?page=jetpack_modules">';
144
	}
145
146
	function add_noscript_head_meta() {
147
		echo '<noscript>';
148
		$this->add_fallback_head_meta();
149
		echo '</noscript>';
150
	}
151
152
	/**
153
	 * Custom menu order.
154
	 *
155
	 * @deprecated since 9.2.0
156
	 * @param array $menu_order Menu order.
157
	 * @return array
158
	 */
159
	function jetpack_menu_order( $menu_order ) {
160
		_deprecated_function( __METHOD__, 'jetpack-9.2' );
161
162
		return $menu_order;
163
	}
164
165
	function page_render() {
166
		/** This action is already documented in views/admin/admin-page.php */
167
		do_action( 'jetpack_notices' );
168
169
		// Try fetching by patch
170
		$static_html = @file_get_contents( JETPACK__PLUGIN_DIR . '_inc/build/static.html' );
171
172
		if ( false === $static_html ) {
173
174
			// If we still have nothing, display an error
175
			echo '<p>';
176
			esc_html_e( 'Error fetching static.html. Try running: ', 'jetpack' );
177
			echo '<code>yarn distclean && yarn build</code>';
178
			echo '</p>';
179
		} else {
180
181
			// We got the static.html so let's display it
182
			echo $static_html;
183
		}
184
	}
185
186
	/**
187
	 * Gets array of any Jetpack notices that have been dismissed.
188
	 *
189
	 * @since 4.0.1
190
	 * @return mixed|void
191
	 */
192
	function get_dismissed_jetpack_notices() {
193
		$jetpack_dismissed_notices = get_option( 'jetpack_dismissed_notices', array() );
194
		/**
195
		 * Array of notices that have been dismissed.
196
		 *
197
		 * @since 4.0.1
198
		 *
199
		 * @param array $jetpack_dismissed_notices If empty, will not show any Jetpack notices.
200
		 */
201
		$dismissed_notices = apply_filters( 'jetpack_dismissed_notices', $jetpack_dismissed_notices );
202
		return $dismissed_notices;
203
	}
204
205
	function additional_styles() {
206
		Jetpack_Admin_Page::load_wrapper_styles();
207
	}
208
209
	function page_admin_scripts() {
210
		if ( $this->is_redirecting ) {
211
			return; // No need for scripts on a fallback page
212
		}
213
214
		$status              = new Status();
215
		$is_offline_mode     = $status->is_offline_mode();
216
		$site_suffix         = $status->get_site_suffix();
217
		$script_deps_path    = JETPACK__PLUGIN_DIR . '_inc/build/admin.asset.php';
218
		$script_dependencies = array( 'wp-polyfill' );
219
		if ( file_exists( $script_deps_path ) ) {
220
			$asset_manifest      = include $script_deps_path;
221
			$script_dependencies = $asset_manifest['dependencies'];
222
		}
223
224
		wp_enqueue_script(
225
			'react-plugin',
226
			plugins_url( '_inc/build/admin.js', JETPACK__PLUGIN_FILE ),
227
			$script_dependencies,
228
			JETPACK__VERSION,
229
			true
230
		);
231
232
		if ( ! $is_offline_mode && Jetpack::is_connection_ready() ) {
233
			// Required for Analytics.
234
			wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
235
		}
236
237
		wp_set_script_translations( 'react-plugin', 'jetpack' );
238
239
		// Add objects to be passed to the initial state of the app.
240
		// Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
241
		wp_add_inline_script( 'react-plugin', 'var Initial_State=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( $this->get_initial_state() ) ) . '"));', 'before' );
242
243
		// This will set the default URL of the jp_redirects lib.
244
		wp_add_inline_script( 'react-plugin', 'var jetpack_redirects = { currentSiteRawUrl: "' . $site_suffix . '" };', 'before' );
245
	}
246
247
	/**
248
	 * Gets a purchase token that is used for Jetpack logged out visitor checkout.
249
	 * The purchase token should be appended to all CTA url's that lead to checkout.
250
	 *
251
	 * @since 9.8.0
252
	 * @return string|boolean
253
	 */
254
	public function get_purchase_token() {
255
		if ( ! Jetpack::current_user_can_purchase() ) {
256
			return false;
257
		}
258
259
		$purchase_token = Jetpack_Options::get_option( 'purchase_token', false );
260
261
		if ( $purchase_token ) {
262
			return $purchase_token;
263
		}
264
		// If the purchase token is not saved in the options table yet, then add it.
265
		Jetpack_Options::update_option( 'purchase_token', $this->generate_purchase_token(), true );
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
266
		return Jetpack_Options::get_option( 'purchase_token', false );
267
	}
268
269
	/**
270
	 * Generates a purchase token that is used for Jetpack logged out visitor checkout.
271
	 *
272
	 * @since 9.8.0
273
	 * @return string
274
	 */
275
	public function generate_purchase_token() {
276
		return wp_generate_password( 12, false );
277
	}
278
279
	function get_initial_state() {
280
		global $is_safari;
281
282
		// Load API endpoint base classes and endpoints for getting the module list fed into the JS Admin Page
283
		require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php';
284
		require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php';
285
		$moduleListEndpoint = new Jetpack_Core_API_Module_List_Endpoint();
286
		$modules = $moduleListEndpoint->get_modules();
287
288
		// Preparing translated fields for JSON encoding by transforming all HTML entities to
289
		// respective characters.
290
		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...
291
			$modules[ $slug ]['name'] = html_entity_decode( $data['name'] );
292
			$modules[ $slug ]['description'] = html_entity_decode( $data['description'] );
293
			$modules[ $slug ]['short_description'] = html_entity_decode( $data['short_description'] );
294
			$modules[ $slug ]['long_description'] = html_entity_decode( $data['long_description'] );
295
		}
296
297
		// Collecting roles that can view site stats.
298
		$stats_roles = array();
299
		$enabled_roles = function_exists( 'stats_get_option' ) ? stats_get_option( 'roles' ) : array( 'administrator' );
300
301
		if ( ! function_exists( 'get_editable_roles' ) ) {
302
			require_once ABSPATH . 'wp-admin/includes/user.php';
303
		}
304
		foreach ( get_editable_roles() as $slug => $role ) {
305
			$stats_roles[ $slug ] = array(
306
				'name' => translate_user_role( $role['name'] ),
307
				'canView' => is_array( $enabled_roles ) ? in_array( $slug, $enabled_roles, true ) : false,
308
			);
309
		}
310
311
		// Get information about current theme.
312
		$current_theme = wp_get_theme();
313
314
		// Get all themes that Infinite Scroll provides support for natively.
315
		$inf_scr_support_themes = array();
316
		foreach ( Jetpack::glob_php( JETPACK__PLUGIN_DIR . 'modules/infinite-scroll/themes' ) as $path ) {
317
			if ( is_readable( $path ) ) {
318
				$inf_scr_support_themes[] = basename( $path, '.php' );
319
			}
320
		}
321
322
		// Get last post, to build the link to Customizer in the Related Posts module.
323
		$last_post = get_posts( array( 'posts_per_page' => 1 ) );
324
		$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...
325
			? get_permalink( $last_post[0]->ID )
326
			: get_home_url();
327
328
		$current_user_data = jetpack_current_user_data();
329
330
		/**
331
		 * Adds information to the `connectionStatus` API field that is unique to the Jetpack React dashboard.
332
		 */
333
		$connection_status = array(
334
			'isInIdentityCrisis' => Jetpack::validate_sync_error_idc_option(),
335
			'sandboxDomain'      => JETPACK__SANDBOX_DOMAIN,
336
337
			/**
338
			 * Filter to add connection errors
339
			 * Format: array( array( 'code' => '...', 'message' => '...', 'action' => '...' ), ... )
340
			 *
341
			 * @since 8.7.0
342
			 *
343
			 * @param array $errors Connection errors.
344
			 */
345
			'errors'             => apply_filters( 'react_connection_errors_initial_state', array() ),
346
		);
347
348
		$connection_status = array_merge( REST_Connector::connection_status( false ), $connection_status );
349
350
		return array(
351
			'WP_API_root'                 => esc_url_raw( rest_url() ),
352
			'WP_API_nonce'                => wp_create_nonce( 'wp_rest' ),
353
			'purchaseToken'               => $this->get_purchase_token(),
354
			'pluginBaseUrl'               => plugins_url( '', JETPACK__PLUGIN_FILE ),
355
			'connectionStatus'            => $connection_status,
356
			'connectUrl'                  => false == $current_user_data['isConnected'] // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
357
				? Jetpack::init()->build_connect_url( true, false, false )
358
				: '',
359
			'dismissedNotices'            => $this->get_dismissed_jetpack_notices(),
360
			'isDevVersion'                => Jetpack::is_development_version(),
361
			'currentVersion'              => JETPACK__VERSION,
362
			'is_gutenberg_available'      => true,
363
			'getModules'                  => $modules,
364
			'rawUrl'                      => ( new Status() )->get_site_suffix(),
365
			'adminUrl'                    => esc_url( admin_url() ),
366
			'siteTitle'                   => (string) htmlspecialchars_decode( get_option( 'blogname' ), ENT_QUOTES ),
367
			'stats'                       => array(
368
				// data is populated asynchronously on page load.
369
				'data'  => array(
370
					'general' => false,
371
					'day'     => false,
372
					'week'    => false,
373
					'month'   => false,
374
				),
375
				'roles' => $stats_roles,
376
			),
377
			'aff'                         => Partner::init()->get_partner_code( Partner::AFFILIATE_CODE ),
378
			'partnerSubsidiaryId'         => Partner::init()->get_partner_code( Partner::SUBSIDIARY_CODE ),
379
			'settings'                    => $this->get_flattened_settings( $modules ),
0 ignored issues
show
Bug introduced by
It seems like $modules defined by $moduleListEndpoint->get_modules() on line 286 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...
380
			'userData'                    => array(
381
				'currentUser' => $current_user_data,
382
			),
383
			'siteData'                    => array(
384
				'icon'                       => has_site_icon()
385
					? 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...
386
					: '',
387
				'siteVisibleToSearchEngines' => '1' == get_option( 'blog_public' ), // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
388
				/**
389
				 * Whether promotions are visible or not.
390
				 *
391
				 * @since 4.8.0
392
				 *
393
				 * @param bool $are_promotions_active Status of promotions visibility. True by default.
394
				 */
395
				'showPromotions'             => apply_filters( 'jetpack_show_promotions', true ),
396
				'isAtomicSite'               => jetpack_is_atomic_site(),
397
				'plan'                       => Jetpack_Plan::get(),
398
				'showBackups'                => Jetpack::show_backups_ui(),
399
				'showRecommendations'        => Jetpack_Recommendations::is_enabled(),
400
				'isMultisite'                => is_multisite(),
401
				'dateFormat'                 => get_option( 'date_format' ),
402
			),
403
			'themeData'                   => array(
404
				'name'      => $current_theme->get( 'Name' ),
405
				'hasUpdate' => (bool) get_theme_update_available( $current_theme ),
406
				'support'   => array(
407
					'infinite-scroll' => current_theme_supports( 'infinite-scroll' ) || in_array( $current_theme->get_stylesheet(), $inf_scr_support_themes, true ),
408
				),
409
			),
410
			'jetpackStateNotices'         => array(
411
				'messageCode'      => Jetpack::state( 'message' ),
412
				'errorCode'        => Jetpack::state( 'error' ),
413
				'errorDescription' => Jetpack::state( 'error_description' ),
414
				'messageContent'   => Jetpack::state( 'display_update_modal' ) ? $this->get_update_modal_data() : null,
415
			),
416
			'tracksUserData'              => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
417
			'currentIp'                   => function_exists( 'jetpack_protect_get_ip' ) ? jetpack_protect_get_ip() : false,
418
			'lastPostUrl'                 => esc_url( $last_post ),
419
			'externalServicesConnectUrls' => $this->get_external_services_connect_urls(),
420
			'calypsoEnv'                  => Jetpack::get_calypso_env(),
421
			'products'                    => Jetpack::get_products_for_purchase(),
422
			'recommendationsStep'         => Jetpack_Core_Json_Api_Endpoints::get_recommendations_step()['step'],
423
			'isSafari'                    => $is_safari || User_Agent_Info::is_opera_desktop(), // @todo Rename isSafari everywhere.
424
			'doNotUseConnectionIframe'    => Constants::is_true( 'JETPACK_SHOULD_NOT_USE_CONNECTION_IFRAME' ),
425
			'licensing'                   => array(
426
				'error'           => Licensing::instance()->last_error(),
427
				'showLicensingUi' => Licensing::instance()->is_licensing_input_enabled(),
428
			),
429
		);
430
	}
431
432
	function get_external_services_connect_urls() {
433
		$connect_urls = array();
434
		jetpack_require_lib( 'class.jetpack-keyring-service-helper' );
435
		foreach ( Jetpack_Keyring_Service_Helper::$SERVICES as $service_name => $service_info ) {
436
			$connect_urls[ $service_name ] = Jetpack_Keyring_Service_Helper::connect_url( $service_name, $service_info[ 'for' ] );
437
		}
438
		return $connect_urls;
439
	}
440
441
	/**
442
	 * Returns an array of modules and settings both as first class members of the object.
443
	 *
444
	 * @param array $modules the result of an API request to get all modules.
445
	 *
446
	 * @return array flattened settings with modules.
447
	 */
448
	function get_flattened_settings( $modules ) {
449
		$core_api_endpoint = new Jetpack_Core_API_Data();
450
		$settings = $core_api_endpoint->get_all_options();
451
		return $settings->data;
452
	}
453
454
	/**
455
	 * Returns the release post content and image data as an associative array.
456
	 * This data is used to create the update modal.
457
	 */
458
	public function get_update_modal_data() {
459
		$post_data = $this->get_release_post_data();
460
461
		if ( ! isset( $post_data['posts'][0] ) ) {
462
			return;
463
		}
464
465
		$post = $post_data['posts'][0];
466
467
		if ( empty( $post['content'] ) ) {
468
			return;
469
		}
470
471
		// This allows us to embed videopress videos into the release post.
472
		add_filter( 'wp_kses_allowed_html', array( $this, 'allow_post_embed_iframe' ), 10, 2 );
473
		$content = wp_kses_post( $post['content'] );
474
		remove_filter( 'wp_kses_allowed_html', array( $this, 'allow_post_embed_iframe' ), 10, 2 );
0 ignored issues
show
Unused Code introduced by
The call to remove_filter() has too many arguments starting with 2.

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...
475
476
		$post_title = isset( $post['title'] ) ? $post['title'] : null;
477
		$title      = wp_kses( $post_title, array() );
478
479
		$post_thumbnail = isset( $post['post_thumbnail'] ) ? $post['post_thumbnail'] : null;
480
		if ( ! empty( $post_thumbnail ) ) {
481
			jetpack_require_lib( 'class.jetpack-photon-image' );
482
			$photon_image = new Jetpack_Photon_Image(
483
				array(
484
					'file'   => jetpack_photon_url( $post_thumbnail['URL'] ),
485
					'width'  => $post_thumbnail['width'],
486
					'height' => $post_thumbnail['height'],
487
				),
488
				$post_thumbnail['mime_type']
489
			);
490
			$photon_image->resize(
491
				array(
492
					'width'  => 600,
493
					'height' => null,
494
					'crop'   => false,
495
				)
496
			);
497
			$post_thumbnail_url = $photon_image->get_raw_filename();
498
		} else {
499
			$post_thumbnail_url = null;
500
		}
501
502
		$post_array = array(
503
			'release_post_content'        => $content,
504
			'release_post_featured_image' => $post_thumbnail_url,
505
			'release_post_title'          => $title,
506
		);
507
508
		return $post_array;
509
	}
510
511
	/**
512
	 * Temporarily allow post content to contain iframes, e.g. for videopress.
513
	 *
514
	 * @param string $tags    The tags.
515
	 * @param string $context The context.
516
	 */
517
	public function allow_post_embed_iframe( $tags, $context ) {
518
		if ( 'post' === $context ) {
519
			$tags['iframe'] = array(
520
				'src'             => true,
521
				'height'          => true,
522
				'width'           => true,
523
				'frameborder'     => true,
524
				'allowfullscreen' => true,
525
			);
526
		}
527
528
		return $tags;
529
	}
530
531
	/**
532
	 * Obtains the release post from the Jetpack release post blog. A release post will be displayed in the
533
	 * update modal when a post has a tag equal to the Jetpack version number.
534
	 *
535
	 * The response parameters for the post array can be found here:
536
	 * https://developer.wordpress.com/docs/api/1.1/get/sites/%24site/posts/%24post_ID/#apidoc-response
537
	 *
538
	 * @return array|null Returns an associative array containing the release post data at index ['posts'][0].
539
	 *                    Returns null if the release post data is not available.
540
	 */
541
	private function get_release_post_data() {
542
		if ( Constants::is_defined( 'TESTING_IN_JETPACK' ) && Constants::get_constant( 'TESTING_IN_JETPACK' ) ) {
543
			return null;
544
		}
545
546
		$release_post_src = add_query_arg(
547
			array(
548
				'order_by' => 'date',
549
				'tag'      => JETPACK__VERSION,
550
				'number'   => '1',
551
			),
552
			'https://public-api.wordpress.com/rest/v1/sites/' . JETPACK__RELEASE_POST_BLOG_SLUG . '/posts'
553
		);
554
555
		$response = wp_remote_get( $release_post_src );
556
557
		if ( ! is_array( $response ) ) {
558
			return null;
559
		}
560
561
		return json_decode( wp_remote_retrieve_body( $response ), true );
562
	}
563
}
564
565
/**
566
 * Gather data about the current user.
567
 *
568
 * @since 4.1.0
569
 *
570
 * @return array
571
 */
572
function jetpack_current_user_data() {
573
	$jetpack_connection = new Connection_Manager( 'jetpack' );
574
575
	$current_user      = wp_get_current_user();
576
	$is_user_connected = $jetpack_connection->is_user_connected( $current_user->ID );
577
	$is_master_user    = $is_user_connected && (int) $current_user->ID && (int) Jetpack_Options::get_option( 'master_user' ) === (int) $current_user->ID;
578
	$dotcom_data       = $jetpack_connection->get_connected_user_data();
579
580
	// Add connected user gravatar to the returned dotcom_data.
581
	$dotcom_data['avatar'] = ( ! empty( $dotcom_data['email'] ) ?
582
		get_avatar_url(
583
			$dotcom_data['email'],
584
			array(
585
				'size'    => 64,
586
				'default' => 'mysteryman',
587
			)
588
		)
589
		: false );
590
591
	$current_user_data = array(
592
		'isConnected' => $is_user_connected,
593
		'isMaster'    => $is_master_user,
594
		'username'    => $current_user->user_login,
595
		'id'          => $current_user->ID,
596
		'wpcomUser'   => $dotcom_data,
597
		'gravatar'    => get_avatar_url( $current_user->ID, 64, 'mm', '', array( 'force_display' => true ) ),
598
		'permissions' => array(
599
			'admin_page'         => current_user_can( 'jetpack_admin_page' ),
600
			'connect'            => current_user_can( 'jetpack_connect' ),
601
			'connect_user'       => current_user_can( 'jetpack_connect_user' ),
602
			'disconnect'         => current_user_can( 'jetpack_disconnect' ),
603
			'manage_modules'     => current_user_can( 'jetpack_manage_modules' ),
604
			'network_admin'      => current_user_can( 'jetpack_network_admin_page' ),
605
			'network_sites_page' => current_user_can( 'jetpack_network_sites_page' ),
606
			'edit_posts'         => current_user_can( 'edit_posts' ),
607
			'publish_posts'      => current_user_can( 'publish_posts' ),
608
			'manage_options'     => current_user_can( 'manage_options' ),
609
			'view_stats'         => current_user_can( 'view_stats' ),
610
			'manage_plugins'     => current_user_can( 'install_plugins' )
611
									&& current_user_can( 'activate_plugins' )
612
									&& current_user_can( 'update_plugins' )
613
									&& current_user_can( 'delete_plugins' ),
614
		),
615
	);
616
617
	return $current_user_data;
618
}
619