Completed
Push — renovate/octokit-rest-18.x ( fdf192...f7756d )
by
unknown
42:56 queued 32:41
created

Jetpack_Redux_State_Helper::get_initial_state()   F

Complexity

Conditions 16
Paths 18432

Size

Total Lines 152

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
nc 18432
nop 0
dl 0
loc 152
rs 1.1198
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * A utility class that generates the initial state for Redux in wp-admin.
4
 * Modularized from `class.jetpack-react-page.php`.
5
 *
6
 * @package automattic/jetpack
7
 */
8
9
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
10
use Automattic\Jetpack\Connection\REST_Connector;
11
use Automattic\Jetpack\Constants;
12
use Automattic\Jetpack\Device_Detection\User_Agent_Info;
13
use Automattic\Jetpack\Identity_Crisis;
14
use Automattic\Jetpack\Licensing;
15
use Automattic\Jetpack\Partner;
16
use Automattic\Jetpack\Status;
17
18
/**
19
 * Responsible for populating the initial Redux state.
20
 */
21
class Jetpack_Redux_State_Helper {
22
	/**
23
	 * Generate the initial state array to be used by the Redux store.
24
	 */
25
	public static function get_initial_state() {
26
		global $is_safari;
27
28
		// Load API endpoint base classes and endpoints for getting the module list fed into the JS Admin Page.
29
		require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php';
30
		require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php';
31
		$module_list_endpoint = new Jetpack_Core_API_Module_List_Endpoint();
32
		$modules              = $module_list_endpoint->get_modules();
33
34
		// Preparing translated fields for JSON encoding by transforming all HTML entities to
35
		// respective characters.
36
		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...
37
			$modules[ $slug ]['name']              = html_entity_decode( $data['name'] );
38
			$modules[ $slug ]['description']       = html_entity_decode( $data['description'] );
39
			$modules[ $slug ]['short_description'] = html_entity_decode( $data['short_description'] );
40
			$modules[ $slug ]['long_description']  = html_entity_decode( $data['long_description'] );
41
		}
42
43
		// Collecting roles that can view site stats.
44
		$stats_roles   = array();
45
		$enabled_roles = function_exists( 'stats_get_option' ) ? stats_get_option( 'roles' ) : array( 'administrator' );
46
47
		if ( ! function_exists( 'get_editable_roles' ) ) {
48
			require_once ABSPATH . 'wp-admin/includes/user.php';
49
		}
50
		foreach ( get_editable_roles() as $slug => $role ) {
51
			$stats_roles[ $slug ] = array(
52
				'name'    => translate_user_role( $role['name'] ),
53
				'canView' => is_array( $enabled_roles ) ? in_array( $slug, $enabled_roles, true ) : false,
54
			);
55
		}
56
57
		// Get information about current theme.
58
		$current_theme = wp_get_theme();
59
60
		// Get all themes that Infinite Scroll provides support for natively.
61
		$inf_scr_support_themes = array();
62
		foreach ( Jetpack::glob_php( JETPACK__PLUGIN_DIR . 'modules/infinite-scroll/themes' ) as $path ) {
63
			if ( is_readable( $path ) ) {
64
				$inf_scr_support_themes[] = basename( $path, '.php' );
65
			}
66
		}
67
68
		// Get last post, to build the link to Customizer in the Related Posts module.
69
		$last_post = get_posts( array( 'posts_per_page' => 1 ) );
70
		$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...
71
			? get_permalink( $last_post[0]->ID )
72
			: get_home_url();
73
74
		$current_user_data = jetpack_current_user_data();
75
76
		/**
77
		 * Adds information to the `connectionStatus` API field that is unique to the Jetpack React dashboard.
78
		 */
79
		$connection_status = array(
80
			'isInIdentityCrisis' => Identity_Crisis::validate_sync_error_idc_option(),
81
			'sandboxDomain'      => JETPACK__SANDBOX_DOMAIN,
82
83
			/**
84
			 * Filter to add connection errors
85
			 * Format: array( array( 'code' => '...', 'message' => '...', 'action' => '...' ), ... )
86
			 *
87
			 * @since 8.7.0
88
			 *
89
			 * @param array $errors Connection errors.
90
			 */
91
			'errors'             => apply_filters( 'react_connection_errors_initial_state', array() ),
92
		);
93
94
		$connection_status = array_merge( REST_Connector::connection_status( false ), $connection_status );
95
96
		return array(
97
			'WP_API_root'                 => esc_url_raw( rest_url() ),
98
			'WP_API_nonce'                => wp_create_nonce( 'wp_rest' ),
99
			'purchaseToken'               => self::get_purchase_token(),
100
			'pluginBaseUrl'               => plugins_url( '', JETPACK__PLUGIN_FILE ),
101
			'connectionStatus'            => $connection_status,
102
			'connectUrl'                  => false == $current_user_data['isConnected'] // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
103
				? Jetpack::init()->build_connect_url( true, false, false )
104
				: '',
105
			'dismissedNotices'            => self::get_dismissed_jetpack_notices(),
106
			'isDevVersion'                => Jetpack::is_development_version(),
107
			'currentVersion'              => JETPACK__VERSION,
108
			'is_gutenberg_available'      => true,
109
			'getModules'                  => $modules,
110
			'rawUrl'                      => ( new Status() )->get_site_suffix(),
111
			'adminUrl'                    => esc_url( admin_url() ),
112
			'siteTitle'                   => (string) htmlspecialchars_decode( get_option( 'blogname' ), ENT_QUOTES ),
113
			'stats'                       => array(
114
				// data is populated asynchronously on page load.
115
				'data'  => array(
116
					'general' => false,
117
					'day'     => false,
118
					'week'    => false,
119
					'month'   => false,
120
				),
121
				'roles' => $stats_roles,
122
			),
123
			'aff'                         => Partner::init()->get_partner_code( Partner::AFFILIATE_CODE ),
124
			'partnerSubsidiaryId'         => Partner::init()->get_partner_code( Partner::SUBSIDIARY_CODE ),
125
			'settings'                    => self::get_flattened_settings( $modules ),
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Redux_State_Help...et_flattened_settings() has too many arguments starting with $modules.

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...
126
			'userData'                    => array(
127
				'currentUser' => $current_user_data,
128
			),
129
			'siteData'                    => array(
130
				'icon'                       => has_site_icon()
131
					? 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...
132
					: '',
133
				'siteVisibleToSearchEngines' => '1' == get_option( 'blog_public' ), // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
134
				/**
135
				 * Whether promotions are visible or not.
136
				 *
137
				 * @since 4.8.0
138
				 *
139
				 * @param bool $are_promotions_active Status of promotions visibility. True by default.
140
				 */
141
				'showPromotions'             => apply_filters( 'jetpack_show_promotions', true ),
142
				'isAtomicSite'               => jetpack_is_atomic_site(),
143
				'plan'                       => Jetpack_Plan::get(),
144
				'showBackups'                => Jetpack::show_backups_ui(),
145
				'showRecommendations'        => Jetpack_Recommendations::is_enabled(),
146
				'isMultisite'                => is_multisite(),
147
				'dateFormat'                 => get_option( 'date_format' ),
148
			),
149
			'themeData'                   => array(
150
				'name'      => $current_theme->get( 'Name' ),
151
				'hasUpdate' => (bool) get_theme_update_available( $current_theme ),
152
				'support'   => array(
153
					'infinite-scroll' => current_theme_supports( 'infinite-scroll' ) || in_array( $current_theme->get_stylesheet(), $inf_scr_support_themes, true ),
154
				),
155
			),
156
			'jetpackStateNotices'         => array(
157
				'messageCode'      => Jetpack::state( 'message' ),
158
				'errorCode'        => Jetpack::state( 'error' ),
159
				'errorDescription' => Jetpack::state( 'error_description' ),
160
				'messageContent'   => Jetpack::state( 'display_update_modal' ) ? self::get_update_modal_data() : null,
161
			),
162
			'tracksUserData'              => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
163
			'currentIp'                   => function_exists( 'jetpack_protect_get_ip' ) ? jetpack_protect_get_ip() : false,
164
			'lastPostUrl'                 => esc_url( $last_post ),
165
			'externalServicesConnectUrls' => self::get_external_services_connect_urls(),
166
			'calypsoEnv'                  => Jetpack::get_calypso_env(),
167
			'products'                    => Jetpack::get_products_for_purchase(),
168
			'recommendationsStep'         => Jetpack_Core_Json_Api_Endpoints::get_recommendations_step()['step'],
169
			'isSafari'                    => $is_safari || User_Agent_Info::is_opera_desktop(), // @todo Rename isSafari everywhere.
170
			'doNotUseConnectionIframe'    => Constants::is_true( 'JETPACK_SHOULD_NOT_USE_CONNECTION_IFRAME' ),
171
			'licensing'                   => array(
172
				'error'           => Licensing::instance()->last_error(),
173
				'showLicensingUi' => Licensing::instance()->is_licensing_input_enabled(),
174
			),
175
		);
176
	}
177
178
	/**
179
	 * Gets array of any Jetpack notices that have been dismissed.
180
	 *
181
	 * @return mixed|void
182
	 */
183
	public static function get_dismissed_jetpack_notices() {
184
		$jetpack_dismissed_notices = get_option( 'jetpack_dismissed_notices', array() );
185
		/**
186
		 * Array of notices that have been dismissed.
187
		 *
188
		 * @param array $jetpack_dismissed_notices If empty, will not show any Jetpack notices.
189
		 */
190
		$dismissed_notices = apply_filters( 'jetpack_dismissed_notices', $jetpack_dismissed_notices );
191
		return $dismissed_notices;
192
	}
193
194
	/**
195
	 * Returns an array of modules and settings both as first class members of the object.
196
	 *
197
	 * @return array flattened settings with modules.
198
	 */
199
	public static function get_flattened_settings() {
200
		$core_api_endpoint = new Jetpack_Core_API_Data();
201
		$settings          = $core_api_endpoint->get_all_options();
202
		return $settings->data;
203
	}
204
205
	/**
206
	 * Returns the release post content and image data as an associative array.
207
	 * This data is used to create the update modal.
208
	 */
209
	public static function get_update_modal_data() {
210
		$post_data = self::get_release_post_data();
211
212
		if ( ! isset( $post_data['posts'][0] ) ) {
213
			return;
214
		}
215
216
		$post = $post_data['posts'][0];
217
218
		if ( empty( $post['content'] ) ) {
219
			return;
220
		}
221
222
		// This allows us to embed videopress videos into the release post.
223
		add_filter( 'wp_kses_allowed_html', array( __CLASS__, 'allow_post_embed_iframe' ), 10, 2 );
224
		$content = wp_kses_post( $post['content'] );
225
		remove_filter( 'wp_kses_allowed_html', array( __CLASS__, '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...
226
227
		$post_title = isset( $post['title'] ) ? $post['title'] : null;
228
		$title      = wp_kses( $post_title, array() );
229
230
		$post_thumbnail = isset( $post['post_thumbnail'] ) ? $post['post_thumbnail'] : null;
231
		if ( ! empty( $post_thumbnail ) ) {
232
			jetpack_require_lib( 'class.jetpack-photon-image' );
233
			$photon_image = new Jetpack_Photon_Image(
234
				array(
235
					'file'   => jetpack_photon_url( $post_thumbnail['URL'] ),
236
					'width'  => $post_thumbnail['width'],
237
					'height' => $post_thumbnail['height'],
238
				),
239
				$post_thumbnail['mime_type']
240
			);
241
			$photon_image->resize(
242
				array(
243
					'width'  => 600,
244
					'height' => null,
245
					'crop'   => false,
246
				)
247
			);
248
			$post_thumbnail_url = $photon_image->get_raw_filename();
249
		} else {
250
			$post_thumbnail_url = null;
251
		}
252
253
		$post_array = array(
254
			'release_post_content'        => $content,
255
			'release_post_featured_image' => $post_thumbnail_url,
256
			'release_post_title'          => $title,
257
		);
258
259
		return $post_array;
260
	}
261
262
	/**
263
	 * Temporarily allow post content to contain iframes, e.g. for videopress.
264
	 *
265
	 * @param string $tags    The tags.
266
	 * @param string $context The context.
267
	 */
268
	public static function allow_post_embed_iframe( $tags, $context ) {
269
		if ( 'post' === $context ) {
270
			$tags['iframe'] = array(
271
				'src'             => true,
272
				'height'          => true,
273
				'width'           => true,
274
				'frameborder'     => true,
275
				'allowfullscreen' => true,
276
			);
277
		}
278
279
		return $tags;
280
	}
281
282
	/**
283
	 * Obtains the release post from the Jetpack release post blog. A release post will be displayed in the
284
	 * update modal when a post has a tag equal to the Jetpack version number.
285
	 *
286
	 * The response parameters for the post array can be found here:
287
	 * https://developer.wordpress.com/docs/api/1.1/get/sites/%24site/posts/%24post_ID/#apidoc-response
288
	 *
289
	 * @return array|null Returns an associative array containing the release post data at index ['posts'][0].
290
	 *                    Returns null if the release post data is not available.
291
	 */
292
	public static function get_release_post_data() {
293
		if ( Constants::is_defined( 'TESTING_IN_JETPACK' ) && Constants::get_constant( 'TESTING_IN_JETPACK' ) ) {
294
			return null;
295
		}
296
297
		$release_post_src = add_query_arg(
298
			array(
299
				'order_by' => 'date',
300
				'tag'      => JETPACK__VERSION,
301
				'number'   => '1',
302
			),
303
			'https://public-api.wordpress.com/rest/v1/sites/' . JETPACK__RELEASE_POST_BLOG_SLUG . '/posts'
304
		);
305
306
		$response = wp_remote_get( $release_post_src );
307
308
		if ( ! is_array( $response ) ) {
309
			return null;
310
		}
311
312
		return json_decode( wp_remote_retrieve_body( $response ), true );
313
	}
314
315
	/**
316
	 * Get external services connect URLs.
317
	 */
318
	public static function get_external_services_connect_urls() {
319
		$connect_urls = array();
320
		jetpack_require_lib( 'class.jetpack-keyring-service-helper' );
321
		// phpcs:disable
322
		foreach ( Jetpack_Keyring_Service_Helper::$SERVICES as $service_name => $service_info ) {
323
			// phpcs:enable
324
			$connect_urls[ $service_name ] = Jetpack_Keyring_Service_Helper::connect_url( $service_name, $service_info['for'] );
325
		}
326
		return $connect_urls;
327
	}
328
329
	/**
330
	 * Gets a purchase token that is used for Jetpack logged out visitor checkout.
331
	 * The purchase token should be appended to all CTA url's that lead to checkout.
332
	 *
333
	 * @since 9.8.0
334
	 * @return string|boolean
335
	 */
336
	public static function get_purchase_token() {
337
		if ( ! Jetpack::current_user_can_purchase() ) {
338
			return false;
339
		}
340
341
		$purchase_token = Jetpack_Options::get_option( 'purchase_token', false );
342
343
		if ( $purchase_token ) {
344
			return $purchase_token;
345
		}
346
		// If the purchase token is not saved in the options table yet, then add it.
347
		Jetpack_Options::update_option( 'purchase_token', self::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...
348
		return Jetpack_Options::get_option( 'purchase_token', false );
349
	}
350
351
	/**
352
	 * Generates a purchase token that is used for Jetpack logged out visitor checkout.
353
	 *
354
	 * @since 9.8.0
355
	 * @return string
356
	 */
357
	public static function generate_purchase_token() {
358
		return wp_generate_password( 12, false );
359
	}
360
}
361
362
/**
363
 * Gather data about the current user.
364
 *
365
 * @since 4.1.0
366
 *
367
 * @return array
368
 */
369
function jetpack_current_user_data() {
370
	$jetpack_connection = new Connection_Manager( 'jetpack' );
371
372
	$current_user      = wp_get_current_user();
373
	$is_user_connected = $jetpack_connection->is_user_connected( $current_user->ID );
374
	$is_master_user    = $is_user_connected && (int) $current_user->ID && (int) Jetpack_Options::get_option( 'master_user' ) === (int) $current_user->ID;
375
	$dotcom_data       = $jetpack_connection->get_connected_user_data();
376
377
	// Add connected user gravatar to the returned dotcom_data.
378
	$dotcom_data['avatar'] = ( ! empty( $dotcom_data['email'] ) ?
379
		get_avatar_url(
380
			$dotcom_data['email'],
381
			array(
382
				'size'    => 64,
383
				'default' => 'mysteryman',
384
			)
385
		)
386
		: false );
387
388
	$current_user_data = array(
389
		'isConnected' => $is_user_connected,
390
		'isMaster'    => $is_master_user,
391
		'username'    => $current_user->user_login,
392
		'id'          => $current_user->ID,
393
		'wpcomUser'   => $dotcom_data,
394
		'gravatar'    => get_avatar_url( $current_user->ID, 64, 'mm', '', array( 'force_display' => true ) ),
395
		'permissions' => array(
396
			'admin_page'         => current_user_can( 'jetpack_admin_page' ),
397
			'connect'            => current_user_can( 'jetpack_connect' ),
398
			'connect_user'       => current_user_can( 'jetpack_connect_user' ),
399
			'disconnect'         => current_user_can( 'jetpack_disconnect' ),
400
			'manage_modules'     => current_user_can( 'jetpack_manage_modules' ),
401
			'network_admin'      => current_user_can( 'jetpack_network_admin_page' ),
402
			'network_sites_page' => current_user_can( 'jetpack_network_sites_page' ),
403
			'edit_posts'         => current_user_can( 'edit_posts' ),
404
			'publish_posts'      => current_user_can( 'publish_posts' ),
405
			'manage_options'     => current_user_can( 'manage_options' ),
406
			'view_stats'         => current_user_can( 'view_stats' ),
407
			'manage_plugins'     => current_user_can( 'install_plugins' )
408
									&& current_user_can( 'activate_plugins' )
409
									&& current_user_can( 'update_plugins' )
410
									&& current_user_can( 'delete_plugins' ),
411
		),
412
	);
413
414
	return $current_user_data;
415
}
416