PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.2-beta
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / _inc / lib / admin-pages / class.jetpack-react-page.php

class.jetpack-react-page.php in Jetpack – WP Security, Backup, Speed, & Growth 16.2-beta, at _inc/lib/admin-pages/class.jetpack-react-page.php

323 lines 10.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3 use Automattic\Jetpack\Admin_UI\Admin_Menu;
4 use Automattic\Jetpack\Assets\Logo;
5 use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
6 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
7 use Automattic\Jetpack\Status;
8
9 require_once __DIR__ . '/class.jetpack-admin-page.php';
10 require_once __DIR__ . '/class-jetpack-redux-state-helper.php';
11
12 /**
13 * Builds the landing page and its menu.
14 */
15 class Jetpack_React_Page extends Jetpack_Admin_Page {
16 /**
17 * Show the landing page only when Jetpack is connected.
18 *
19 * @var bool
20 */
21 protected $dont_show_if_not_active = false;
22
23 /**
24 * Used for fallback when REST API is disabled.
25 *
26 * @var bool
27 */
28 protected $is_redirecting = false;
29
30 /**
31 * Add the main admin Jetpack menu.
32 *
33 * @return string|false Return value from WordPress's `add_menu_page()`.
34 */
35 public function get_page_hook() {
36 $icon = ( new Logo() )->get_base64_logo();
37 return add_menu_page( 'Jetpack', 'Jetpack', 'jetpack_admin_page', 'jetpack', array( $this, 'render' ), $icon, 3 );
38 }
39
40 /**
41 * Add page action.
42 *
43 * @param string $hook Hook of current page.
44 * @return void
45 */
46 public function add_page_actions( $hook ) {
47 /** This action is documented in class.jetpack-admin.php */
48 do_action( 'jetpack_admin_menu', $hook );
49
50 if ( ! isset( $_GET['page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
51 return;
52 }
53 $page = sanitize_text_field( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
54 if ( 'jetpack' !== $page ) {
55 if ( strpos( $page, 'jetpack/' ) === 0 ) {
56 $section = substr( $page, 8 );
57 wp_safe_redirect( admin_url( 'admin.php?page=jetpack#/' . $section ) );
58 exit( 0 );
59 }
60 return; // No need to handle the fallback redirection if we are not on the Jetpack page.
61 }
62
63 // Adding a redirect meta tag if the REST API is disabled.
64 if ( ! $this->is_rest_api_enabled() ) {
65 $this->is_redirecting = true;
66 add_action( 'admin_head', array( $this, 'add_fallback_head_meta' ) );
67 }
68
69 // Adding a redirect meta tag wrapped in noscript tags for all browsers in case they have JavaScript disabled.
70 add_action( 'admin_head', array( $this, 'add_noscript_head_meta' ) );
71
72 // If this is the first time the user is viewing the admin, don't show JITMs.
73 // This filter is added just in time because this function is called on admin_menu
74 // and JITMs are initialized on admin_init.
75 if ( Jetpack::is_connection_ready() && ! Jetpack_Options::get_option( 'first_admin_view', false ) ) {
76 Jetpack_Options::update_option( 'first_admin_view', true );
77 add_filter( 'jetpack_just_in_time_msgs', '__return_false' );
78 }
79 }
80
81 /**
82 * Remove the main Jetpack submenu if a site is in offline mode or connected
83 * or if My Jetpack is available.
84 * At that point, admins can access the Jetpack Dashboard instead.
85 *
86 * @since 13.8
87 */
88 public function remove_jetpack_menu() {
89 $is_offline_mode = ( new Status() )->is_offline_mode();
90 $has_my_jetpack = (
91 class_exists( 'Automattic\Jetpack\My_Jetpack\Initializer' ) &&
92 method_exists( 'Automattic\Jetpack\My_Jetpack\Initializer', 'should_initialize' ) &&
93 \Automattic\Jetpack\My_Jetpack\Initializer::should_initialize()
94 );
95
96 if ( $is_offline_mode || $has_my_jetpack || Jetpack::is_connection_ready() ) {
97 remove_submenu_page( 'jetpack', 'jetpack' );
98 }
99 }
100
101 /**
102 * Determine whether a user can access the Jetpack Settings page.
103 *
104 * Rules are:
105 * - user is allowed to see the Jetpack Admin
106 * - site is connected or in offline mode
107 * - non-admins only need access to the settings when there are modules they can manage.
108 *
109 * @return bool $can_access_settings Can the user access settings.
110 */
111 private function can_access_settings() {
112 $connection = new Connection_Manager( 'jetpack' );
113 $status = new Status();
114
115 // User must have the necessary permissions to see the Jetpack settings pages.
116 if ( ! current_user_can( 'edit_posts' ) ) {
117 return false;
118 }
119
120 // In offline mode, allow access to admins.
121 if ( $status->is_offline_mode() && current_user_can( 'manage_options' ) ) {
122 return true;
123 }
124
125 // If not in offline mode but site is not connected, bail.
126 if ( ! Jetpack::is_connection_ready() ) {
127 return false;
128 }
129
130 /*
131 * Additional checks for non-admins.
132 */
133 if ( ! current_user_can( 'manage_options' ) ) {
134 // If the site isn't connected at all, bail.
135 if ( ! $connection->has_connected_owner() ) {
136 return false;
137 }
138
139 /*
140 * If they haven't connected their own account yet,
141 * they have no use for the settings page.
142 * They will not be able to manage any settings.
143 */
144 if ( ! $connection->is_user_connected() ) {
145 return false;
146 }
147
148 /*
149 * Non-admins only have access to settings
150 * for the following modules:
151 * - Publicize
152 * - Post By Email
153 * If those modules are not available, bail.
154 */
155 if (
156 ! Jetpack::is_module_active( 'post-by-email' )
157 && (
158 ! Jetpack::is_module_active( 'publicize' ) ||
159 ! current_user_can( 'publish_posts' )
160 )
161 ) {
162 return false;
163 }
164 }
165
166 // fallback.
167 return true;
168 }
169
170 /**
171 * Jetpack Settings sub-link.
172 *
173 * @since 4.3.0
174 * @since 9.7.0 If Connection does not have an owner, restrict it to admins
175 */
176 public function jetpack_add_settings_sub_nav_item() {
177 if ( $this->can_access_settings() ) {
178 Admin_Menu::add_menu(
179 __( 'Settings', 'jetpack' ),
180 __( 'Settings', 'jetpack' ),
181 'jetpack_admin_page',
182 Jetpack::admin_url( array( 'page' => 'jetpack#/settings' ) ),
183 null,
184 13
185 );
186 }
187 }
188
189 /**
190 * Fallback redirect meta tag if the REST API is disabled.
191 *
192 * @return void
193 */
194 public function add_fallback_head_meta() {
195 echo '<meta http-equiv="refresh" content="0; url=?page=jetpack_modules">';
196 }
197
198 /**
199 * Fallback meta tag wrapped in noscript tags for all browsers in case they have JavaScript disabled.
200 *
201 * @return void
202 */
203 public function add_noscript_head_meta() {
204 echo '<noscript>';
205 $this->add_fallback_head_meta();
206 echo '</noscript>';
207 }
208
209 /**
210 * Add action to render page specific HTML.
211 *
212 * @return void
213 */
214 public function page_render() {
215 /** This action is already documented in class.jetpack-admin-page.php */
216 do_action( 'jetpack_notices' );
217
218 // Fetch static.html.
219 $static_html = @file_get_contents( JETPACK__PLUGIN_DIR . '_inc/build/static.html' ); //phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents, Not fetching a remote file.
220
221 if ( false === $static_html ) {
222
223 // If we still have nothing, display an error.
224 echo '<p>';
225 esc_html_e( 'Error fetching static.html. Try running: ', 'jetpack' );
226 echo '<code>pnpm run distclean && pnpm jetpack build plugins/jetpack</code>';
227 echo '</p>';
228 } else {
229 // We got the static.html so let's display it.
230 echo $static_html; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
231 }
232 }
233 /**
234 * Allow robust deep links to React.
235 *
236 * The Jetpack dashboard requires fragments/hash values to make
237 * a deep link to it but passing fragments as part of a return URL
238 * will most often be discarded throughout the process.
239 * This logic aims to bridge this gap and reduce the chance of React
240 * specific links being broken while passing them along.
241 */
242 public function react_redirects() {
243 global $pagenow;
244
245 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
246 if ( 'admin.php' !== $pagenow || ! isset( $_GET['jp-react-redirect'] ) ) {
247 return;
248 }
249
250 $allowed_paths = array(
251 'product-purchased' => admin_url( '/admin.php?page=jetpack#/recommendations/product-purchased' ),
252 );
253
254 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
255 $target = sanitize_text_field( wp_unslash( $_GET['jp-react-redirect'] ) );
256 if ( isset( $allowed_paths[ $target ] ) ) {
257 wp_safe_redirect( $allowed_paths[ $target ] );
258 exit( 0 );
259 }
260 }
261
262 /**
263 * Load styles for static page.
264 */
265 public function additional_styles() {
266 Jetpack_Admin_Page::load_wrapper_styles();
267 }
268
269 /**
270 * Load admin page scripts.
271 */
272 public function page_admin_scripts() {
273 if ( $this->is_redirecting ) {
274 return; // No need for scripts on a fallback page.
275 }
276
277 $status = new Status();
278 $is_offline_mode = $status->is_offline_mode();
279 $site_suffix = $status->get_site_suffix();
280 $script_deps_path = JETPACK__PLUGIN_DIR . '_inc/build/admin.asset.php';
281 $script_dependencies = array( 'jquery', 'wp-polyfill' );
282 $version = JETPACK__VERSION;
283 if ( file_exists( $script_deps_path ) ) {
284 $asset_manifest = include $script_deps_path;
285 $script_dependencies = $asset_manifest['dependencies'];
286 $version = $asset_manifest['version'];
287 }
288
289 $blog_id_prop = '';
290 if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) {
291 $blog_id = Connection_Manager::get_site_id( true );
292 if ( $blog_id ) {
293 $blog_id_prop = ', currentBlogID: "' . (int) $blog_id . '"';
294 }
295 }
296
297 wp_enqueue_script(
298 'react-plugin',
299 plugins_url( '_inc/build/admin.js', JETPACK__PLUGIN_FILE ),
300 $script_dependencies,
301 $version,
302 true
303 );
304
305 if ( ! $is_offline_mode && Jetpack::is_connection_ready() ) {
306 // Required for Analytics.
307 wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
308 }
309
310 wp_set_script_translations( 'react-plugin', 'jetpack' );
311
312 // Add objects to be passed to the initial state of the app.
313 // Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
314 wp_add_inline_script( 'react-plugin', 'var Initial_State=' . wp_json_encode( Jetpack_Redux_State_Helper::get_initial_state(), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';', 'before' );
315
316 // This will set the default URL of the jp_redirects lib.
317 wp_add_inline_script( 'react-plugin', 'var jetpack_redirects = { currentSiteRawUrl: "' . $site_suffix . '"' . $blog_id_prop . ' };', 'before' );
318
319 // Adds Connection package initial state.
320 Connection_Initial_State::render_script( 'react-plugin' );
321 }
322 }
323