PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.1
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 13.9.2 14.0.1 14.1.1 All 503 releases
jetpack / _inc / lib / admin-pages / class.jetpack-react-page.php

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

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