Completed
Push — try/search-config-via-wpadmin ( cb094f...014825 )
by
unknown
74:32 queued 64:33
created

get_flattened_settings()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
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\REST_Connector;
10
use Automattic\Jetpack\Constants;
11
use Automattic\Jetpack\Device_Detection\User_Agent_Info;
12
use Automattic\Jetpack\Licensing;
13
use Automattic\Jetpack\Partner;
14
use Automattic\Jetpack\Status;
15
16
/**
17
 * Responsible for populating the initial Redux state.
18
 */
19
class Jetpack_Redux_State_Helper {
20
21
	/**
22
	 * Generate the initial state array to be used by the Redux store.
23
	 */
24
	public static function get_initial_state() {
25
		global $is_safari;
26
27
		// Load API endpoint base classes and endpoints for getting the module list fed into the JS Admin Page.
28
		require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-xmlrpc-consumer-endpoint.php';
29
		require_once JETPACK__PLUGIN_DIR . '_inc/lib/core-api/class.jetpack-core-api-module-endpoints.php';
30
		$module_list_endpoint = new Jetpack_Core_API_Module_List_Endpoint();
31
		$modules              = $module_list_endpoint->get_modules();
32
33
		// Preparing translated fields for JSON encoding by transforming all HTML entities to
34
		// respective characters.
35
		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...
36
			$modules[ $slug ]['name']              = html_entity_decode( $data['name'] );
37
			$modules[ $slug ]['description']       = html_entity_decode( $data['description'] );
38
			$modules[ $slug ]['short_description'] = html_entity_decode( $data['short_description'] );
39
			$modules[ $slug ]['long_description']  = html_entity_decode( $data['long_description'] );
40
		}
41
42
		// Collecting roles that can view site stats.
43
		$stats_roles   = array();
44
		$enabled_roles = function_exists( 'stats_get_option' ) ? stats_get_option( 'roles' ) : array( 'administrator' );
45
46
		if ( ! function_exists( 'get_editable_roles' ) ) {
47
			require_once ABSPATH . 'wp-admin/includes/user.php';
48
		}
49
		foreach ( get_editable_roles() as $slug => $role ) {
50
			$stats_roles[ $slug ] = array(
51
				'name'    => translate_user_role( $role['name'] ),
52
				'canView' => is_array( $enabled_roles ) ? in_array( $slug, $enabled_roles, true ) : false,
53
			);
54
		}
55
56
		// Get information about current theme.
57
		$current_theme = wp_get_theme();
58
59
		// Get all themes that Infinite Scroll provides support for natively.
60
		$inf_scr_support_themes = array();
61
		foreach ( Jetpack::glob_php( JETPACK__PLUGIN_DIR . 'modules/infinite-scroll/themes' ) as $path ) {
62
			if ( is_readable( $path ) ) {
63
				$inf_scr_support_themes[] = basename( $path, '.php' );
64
			}
65
		}
66
67
		// Get last post, to build the link to Customizer in the Related Posts module.
68
		$last_post = get_posts( array( 'posts_per_page' => 1 ) );
69
		$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...
70
			? get_permalink( $last_post[0]->ID )
71
			: get_home_url();
72
73
		$current_user_data = jetpack_current_user_data();
74
75
		/**
76
		 * Adds information to the `connectionStatus` API field that is unique to the Jetpack React dashboard.
77
		 */
78
		$connection_status = array(
79
			'isInIdentityCrisis' => Jetpack::validate_sync_error_idc_option(),
80
			'sandboxDomain'      => JETPACK__SANDBOX_DOMAIN,
81
82
			/**
83
			 * Filter to add connection errors
84
			 * Format: array( array( 'code' => '...', 'message' => '...', 'action' => '...' ), ... )
85
			 *
86
			 * @since 8.7.0
87
			 *
88
			 * @param array $errors Connection errors.
89
			 */
90
			'errors'             => apply_filters( 'react_connection_errors_initial_state', array() ),
91
		);
92
93
		$connection_status = array_merge( REST_Connector::connection_status( false ), $connection_status );
94
95
		return array(
96
			'WP_API_root'                 => esc_url_raw( rest_url() ),
97
			'WP_API_nonce'                => wp_create_nonce( 'wp_rest' ),
98
			'pluginBaseUrl'               => plugins_url( '', JETPACK__PLUGIN_FILE ),
99
			'connectionStatus'            => $connection_status,
100
			'connectUrl'                  => false == $current_user_data['isConnected'] // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
101
				? Jetpack::init()->build_connect_url( true, false, false )
102
				: '',
103
			'dismissedNotices'            => self::get_dismissed_jetpack_notices(),
104
			'isDevVersion'                => Jetpack::is_development_version(),
105
			'currentVersion'              => JETPACK__VERSION,
106
			'is_gutenberg_available'      => true,
107
			'getModules'                  => $modules,
108
			'rawUrl'                      => ( new Status() )->get_site_suffix(),
109
			'adminUrl'                    => esc_url( admin_url() ),
110
			'siteTitle'                   => (string) htmlspecialchars_decode( get_option( 'blogname' ), ENT_QUOTES ),
111
			'stats'                       => array(
112
				// data is populated asynchronously on page load.
113
				'data'  => array(
114
					'general' => false,
115
					'day'     => false,
116
					'week'    => false,
117
					'month'   => false,
118
				),
119
				'roles' => $stats_roles,
120
			),
121
			'aff'                         => Partner::init()->get_partner_code( Partner::AFFILIATE_CODE ),
122
			'partnerSubsidiaryId'         => Partner::init()->get_partner_code( Partner::SUBSIDIARY_CODE ),
123
			'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...
124
			'userData'                    => array(
125
				'currentUser' => $current_user_data,
126
			),
127
			'siteData'                    => array(
128
				'icon'                       => has_site_icon()
129
					? 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...
130
					: '',
131
				'siteVisibleToSearchEngines' => '1' == get_option( 'blog_public' ), // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
132
				/**
133
				 * Whether promotions are visible or not.
134
				 *
135
				 * @since 4.8.0
136
				 *
137
				 * @param bool $are_promotions_active Status of promotions visibility. True by default.
138
				 */
139
				'showPromotions'             => apply_filters( 'jetpack_show_promotions', true ),
140
				'isAtomicSite'               => jetpack_is_atomic_site(),
141
				'plan'                       => Jetpack_Plan::get(),
142
				'showBackups'                => Jetpack::show_backups_ui(),
143
				'showRecommendations'        => Jetpack_Recommendations::is_enabled(),
144
				'isMultisite'                => is_multisite(),
145
				'dateFormat'                 => get_option( 'date_format' ),
146
			),
147
			'themeData'                   => array(
148
				'name'      => $current_theme->get( 'Name' ),
149
				'hasUpdate' => (bool) get_theme_update_available( $current_theme ),
150
				'support'   => array(
151
					'infinite-scroll' => current_theme_supports( 'infinite-scroll' ) || in_array( $current_theme->get_stylesheet(), $inf_scr_support_themes, true ),
152
				),
153
			),
154
			'jetpackStateNotices'         => array(
155
				'messageCode'      => Jetpack::state( 'message' ),
156
				'errorCode'        => Jetpack::state( 'error' ),
157
				'errorDescription' => Jetpack::state( 'error_description' ),
158
				'messageContent'   => Jetpack::state( 'display_update_modal' ) ? self::get_update_modal_data() : null,
159
			),
160
			'tracksUserData'              => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
161
			'currentIp'                   => function_exists( 'jetpack_protect_get_ip' ) ? jetpack_protect_get_ip() : false,
162
			'lastPostUrl'                 => esc_url( $last_post ),
163
			'externalServicesConnectUrls' => self::get_external_services_connect_urls(),
164
			'calypsoEnv'                  => Jetpack::get_calypso_env(),
165
			'products'                    => Jetpack::get_products_for_purchase(),
166
			'recommendationsStep'         => Jetpack_Core_Json_Api_Endpoints::get_recommendations_step()['step'],
167
			'isSafari'                    => $is_safari || User_Agent_Info::is_opera_desktop(), // @todo Rename isSafari everywhere.
168
			'doNotUseConnectionIframe'    => Constants::is_true( 'JETPACK_SHOULD_NOT_USE_CONNECTION_IFRAME' ),
169
			'licensing'                   => array(
170
				'error'           => Licensing::instance()->last_error(),
171
				'showLicensingUi' => Licensing::instance()->is_licensing_input_enabled(),
172
			),
173
		);
174
	}
175
176
	/**
177
	 * Gets array of any Jetpack notices that have been dismissed.
178
	 *
179
	 * @since 4.0.1
180
	 * @return mixed|void
181
	 */
182
	public static function get_dismissed_jetpack_notices() {
183
		$jetpack_dismissed_notices = get_option( 'jetpack_dismissed_notices', array() );
184
		/**
185
		 * Array of notices that have been dismissed.
186
		 *
187
		 * @since 4.0.1
188
		 *
189
		 * @param array $jetpack_dismissed_notices If empty, will not show any Jetpack notices.
190
		 */
191
		$dismissed_notices = apply_filters( 'jetpack_dismissed_notices', $jetpack_dismissed_notices );
192
		return $dismissed_notices;
193
	}
194
195
	/**
196
	 * Returns an array of modules and settings both as first class members of the object.
197
	 *
198
	 * @return array flattened settings with modules.
199
	 */
200
	public static function get_flattened_settings() {
201
		$core_api_endpoint = new Jetpack_Core_API_Data();
202
		$settings          = $core_api_endpoint->get_all_options();
203
		return $settings->data;
204
	}
205
206
	/**
207
	 * Returns the release post content and image data as an associative array.
208
	 * This data is used to create the update modal.
209
	 */
210
	public static function get_update_modal_data() {
211
		$post_data = self::get_release_post_data();
212
213
		if ( ! isset( $post_data['posts'][0] ) ) {
214
			return;
215
		}
216
217
		$post = $post_data['posts'][0];
218
219
		if ( empty( $post['content'] ) ) {
220
			return;
221
		}
222
223
		// This allows us to embed videopress videos into the release post.
224
		add_filter( 'wp_kses_allowed_html', array( __CLASS__, 'allow_post_embed_iframe' ), 10, 2 );
225
		$content = wp_kses_post( $post['content'] );
226
		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...
227
228
		$post_title = isset( $post['title'] ) ? $post['title'] : null;
229
		$title      = wp_kses( $post_title, array() );
230
231
		$post_thumbnail = isset( $post['post_thumbnail'] ) ? $post['post_thumbnail'] : null;
232
		if ( ! empty( $post_thumbnail ) ) {
233
			jetpack_require_lib( 'class.jetpack-photon-image' );
234
			$photon_image = new Jetpack_Photon_Image(
235
				array(
236
					'file'   => jetpack_photon_url( $post_thumbnail['URL'] ),
237
					'width'  => $post_thumbnail['width'],
238
					'height' => $post_thumbnail['height'],
239
				),
240
				$post_thumbnail['mime_type']
241
			);
242
			$photon_image->resize(
243
				array(
244
					'width'  => 600,
245
					'height' => null,
246
					'crop'   => false,
247
				)
248
			);
249
			$post_thumbnail_url = $photon_image->get_raw_filename();
250
		} else {
251
			$post_thumbnail_url = null;
252
		}
253
254
		$post_array = array(
255
			'release_post_content'        => $content,
256
			'release_post_featured_image' => $post_thumbnail_url,
257
			'release_post_title'          => $title,
258
		);
259
260
		return $post_array;
261
	}
262
263
	/**
264
	 * Obtains the release post from the Jetpack release post blog. A release post will be displayed in the
265
	 * update modal when a post has a tag equal to the Jetpack version number.
266
	 *
267
	 * The response parameters for the post array can be found here:
268
	 * https://developer.wordpress.com/docs/api/1.1/get/sites/%24site/posts/%24post_ID/#apidoc-response
269
	 *
270
	 * @return array|null Returns an associative array containing the release post data at index ['posts'][0].
271
	 *                    Returns null if the release post data is not available.
272
	 */
273
	private static function get_release_post_data() {
274
		if ( Constants::is_defined( 'TESTING_IN_JETPACK' ) && Constants::get_constant( 'TESTING_IN_JETPACK' ) ) {
275
			return null;
276
		}
277
278
		$release_post_src = add_query_arg(
279
			array(
280
				'order_by' => 'date',
281
				'tag'      => JETPACK__VERSION,
282
				'number'   => '1',
283
			),
284
			'https://public-api.wordpress.com/rest/v1/sites/' . JETPACK__RELEASE_POST_BLOG_SLUG . '/posts'
285
		);
286
287
		$response = wp_remote_get( $release_post_src );
288
289
		if ( ! is_array( $response ) ) {
290
			return null;
291
		}
292
293
		return json_decode( wp_remote_retrieve_body( $response ), true );
294
	}
295
296
	/**
297
	 * Get external services connect URLs.
298
	 */
299
	public static function get_external_services_connect_urls() {
300
		$connect_urls = array();
301
		jetpack_require_lib( 'class.jetpack-keyring-service-helper' );
302
		// phpcs:disable
303
		foreach ( Jetpack_Keyring_Service_Helper::$SERVICES as $service_name => $service_info ) {
304
			// phpcs:enable
305
			$connect_urls[ $service_name ] = Jetpack_Keyring_Service_Helper::connect_url( $service_name, $service_info['for'] );
306
		}
307
		return $connect_urls;
308
	}
309
310
	/**
311
	 * Temporarily allow post content to contain iframes, e.g. for videopress.
312
	 *
313
	 * @param string $tags    The tags.
314
	 * @param string $context The context.
315
	 */
316
	public static function allow_post_embed_iframe( $tags, $context ) {
317
		if ( 'post' === $context ) {
318
			$tags['iframe'] = array(
319
				'src'             => true,
320
				'height'          => true,
321
				'width'           => true,
322
				'frameborder'     => true,
323
				'allowfullscreen' => true,
324
			);
325
		}
326
327
		return $tags;
328
	}
329
}
330