PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.1-a.5
16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / jetpack-scan-page / src / class-jetpack-scan.php
jetpack / jetpack_vendor / automattic / jetpack-scan-page / src Last commit date
class-initial-state.php 3 months ago class-jetpack-scan.php 2 months ago class-package-version.php 4 weeks ago class-rest-controller.php 3 months ago
class-jetpack-scan.php
330 lines
1 <?php
2 /**
3 * Primary class for the Jetpack Scan package.
4 *
5 * @package automattic/jetpack-scan-page
6 */
7
8 namespace Automattic\Jetpack\Scan_Page;
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit( 0 );
12 }
13
14 use Automattic\Jetpack\Admin_UI\Admin_Menu;
15 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
16 use Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills;
17 use function add_action;
18 use function add_filter;
19 use function apply_filters;
20 use function call_user_func;
21 use function current_user_can;
22 use function did_action;
23 use function do_action;
24 use function function_exists;
25 use function is_admin;
26 use function is_multisite;
27 use function remove_action;
28 use function remove_all_actions;
29 use function sanitize_text_field;
30 use function wp_print_inline_script_tag;
31 use function wp_scripts;
32 use function wp_unslash;
33
34 /**
35 * Class Jetpack_Scan
36 *
37 * Registers the Scan admin page and its REST routes inside the main
38 * Jetpack plugin. The page bundle is built by `@wordpress/build`
39 * (mirroring Newsletter / Forms); this class wires the wp-admin menu
40 * + the bridges that route our user-facing slug to wp-build's
41 * auto-generated enqueue / render functions.
42 */
43 class Jetpack_Scan {
44
45 /**
46 * URL-facing menu slug.
47 *
48 * @var string
49 */
50 const PAGE_SLUG = 'jetpack-scan';
51
52 /**
53 * Internal slug emitted by `@wordpress/build` (`wpPlugin.pages[0]`
54 * plus the `-wp-admin` suffix the build template appends). Used to
55 * find the auto-generated render / enqueue functions.
56 *
57 * @var string
58 */
59 const WP_BUILD_SLUG = 'jetpack-scan-wp-admin';
60
61 /**
62 * Filter name that gates the wp-build–based Scan dashboard.
63 *
64 * When this filter returns true, the new wp-admin Scan page is
65 * registered and rendered. Default false during the modernization
66 * roll-out — the package registers no admin menu and changes
67 * nothing about the existing Jetpack UI when this filter is off.
68 *
69 * @var string
70 */
71 const MODERNIZATION_FILTER = 'rsm_jetpack_ui_modernization_scan';
72
73 /**
74 * Entry point. Idempotent: safe to call from multiple bootstraps.
75 */
76 public static function initialize() {
77 if ( did_action( 'jetpack_scan_page_initialized' ) ) {
78 return;
79 }
80
81 if ( ! (bool) apply_filters( self::MODERNIZATION_FILTER, false ) ) {
82 return;
83 }
84
85 self::load_wp_build();
86 self::fix_boot_import_map_ordering();
87 self::bridge_wp_build_enqueue();
88
89 add_action( 'admin_menu', array( __CLASS__, 'add_wp_admin_submenu' ) );
90 add_action( 'rest_api_init', array( __CLASS__, 'register_rest_routes' ) );
91 add_filter( 'jetpack_package_versions', array( Package_Version::class, 'send_package_version_to_tracker' ) );
92
93 /**
94 * Fires once the Jetpack Scan package has wired its hooks.
95 *
96 * @since 0.1.0
97 */
98 do_action( 'jetpack_scan_page_initialized' );
99 }
100
101 /**
102 * Load wp-build generated registration files. Mirrors Newsletter / Forms.
103 */
104 public static function load_wp_build() {
105 // The polyfills force-replace core script handles (notably
106 // `wp-private-apis`, a stateful singleton shared by every @wordpress
107 // package on the page) during `wp_default_scripts`. Scope registration
108 // to the Scan admin page so it never runs on other admin pages such as
109 // the block editor. `$_GET['page']` is reliable this early — it's the
110 // raw query param, available well before `current_screen` exists.
111 if ( self::is_scan_admin_request() ) {
112 WP_Build_Polyfills::register(
113 'jetpack-scan',
114 array_merge( WP_Build_Polyfills::SCRIPT_HANDLES, WP_Build_Polyfills::MODULE_IDS )
115 );
116 }
117
118 $wp_build_index = dirname( __DIR__ ) . '/build/build.php';
119 if ( file_exists( $wp_build_index ) ) {
120 require_once $wp_build_index;
121 }
122
123 // `page.php` ships an `admin_init` interceptor that takes over our
124 // slug with a standalone (non-wp-admin) render. We want the
125 // wp-admin integrated experience, so unregister it as soon as it's
126 // loaded.
127 remove_action(
128 'admin_init',
129 'jetpack_scan_jetpack_scan_intercept_render'
130 );
131 }
132
133 /**
134 * Whether the current request targets the Scan admin page.
135 *
136 * Used to scope the wp-build polyfill registration (which force-replaces
137 * core script handles) to this one page, so it never affects other admin
138 * pages. Reads the menu page slug directly so it is cheap and safe to call
139 * at plugin-load time, before `current_screen` exists.
140 *
141 * @return bool True when serving the Scan page in wp-admin.
142 */
143 public static function is_scan_admin_request() {
144 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
145 if ( ! is_admin() || ! isset( $_GET['page'] ) ) {
146 return false;
147 }
148
149 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
150 return self::PAGE_SLUG === sanitize_text_field( wp_unslash( $_GET['page'] ) );
151 }
152
153 /**
154 * Bridge wp-build's auto-generated enqueue function — which checks for
155 * `?page=jetpack-scan-wp-admin` — to our user-facing slug
156 * `?page=jetpack-scan`. Hooked at priority 9 so the wp-build copy
157 * (registered at priority 10) sees the original `$_GET['page']` and
158 * skips its own enqueue.
159 */
160 public static function bridge_wp_build_enqueue() {
161 add_action(
162 'admin_enqueue_scripts',
163 static function ( $hook_suffix ) {
164 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
165 if ( ! isset( $_GET['page'] ) || self::PAGE_SLUG !== $_GET['page'] ) {
166 return;
167 }
168
169 $enqueue_fn = 'jetpack_scan_jetpack_scan_wp_admin_enqueue_scripts';
170 if ( ! function_exists( $enqueue_fn ) ) {
171 return;
172 }
173
174 // phpcs:disable WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
175 $original = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : null;
176 $_GET['page'] = self::WP_BUILD_SLUG;
177 // @phan-suppress-next-line PhanUndeclaredFunctionInCallable -- Function is generated by @wordpress/build into build/pages/jetpack-scan/page-wp-admin.php, which is outside Phan's analysis scope. The function_exists() guard above protects the call at runtime.
178 call_user_func( $enqueue_fn, $hook_suffix );
179 if ( null === $original ) {
180 unset( $_GET['page'] );
181 } else {
182 $_GET['page'] = $original;
183 }
184 // phpcs:enable WordPress.Security.NonceVerification.Recommended,WordPress.Security.ValidatedSanitizedInput.MissingUnslash,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
185 },
186 9
187 );
188 }
189
190 /**
191 * Fix import map ordering for the wp-build boot script.
192 *
193 * In wp-admin, `_wp_footer_scripts` (classic scripts) and
194 * `print_import_map` both hook into `admin_print_footer_scripts` at
195 * priority 10, but `_wp_footer_scripts` is registered first. This
196 * causes the inline `import("@wordpress/boot")` to execute before
197 * the import map exists.
198 *
199 * This fix moves the import() call from the classic inline script to
200 * a `<script type="module">` printed at priority 20 (after the import
201 * map).
202 *
203 * @todo Remove once @wordpress/build ships with the loader.js fix
204 * upstream (WordPress/gutenberg#76870) and Jetpack updates the
205 * dependency.
206 */
207 public static function fix_boot_import_map_ordering() {
208 $handle = self::WP_BUILD_SLUG . '-prerequisites';
209
210 add_action(
211 'admin_enqueue_scripts',
212 static function () use ( $handle ) {
213 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
214 if ( ! isset( $_GET['page'] ) || self::PAGE_SLUG !== $_GET['page'] ) {
215 return;
216 }
217
218 $data = wp_scripts()->get_data( $handle, 'after' );
219 if ( empty( $data ) ) {
220 return;
221 }
222
223 $boot_script = null;
224 $remaining = array();
225 foreach ( $data as $line ) {
226 if ( strpos( $line, '@wordpress/boot' ) !== false ) {
227 $boot_script = $line;
228 } else {
229 $remaining[] = $line;
230 }
231 }
232
233 if ( null === $boot_script ) {
234 return;
235 }
236
237 wp_scripts()->add_data( $handle, 'after', $remaining );
238
239 add_action(
240 'admin_print_footer_scripts',
241 static function () use ( $boot_script ) {
242 wp_print_inline_script_tag( $boot_script, array( 'type' => 'module' ) );
243 },
244 20
245 );
246 },
247 PHP_INT_MAX
248 );
249 }
250
251 /**
252 * Register the Scan submenu under Jetpack.
253 *
254 * @return string|null The resulting page's hook suffix, if registered.
255 */
256 public static function add_wp_admin_submenu() {
257 if ( ! self::is_available() ) {
258 return null;
259 }
260
261 $render_fn = 'jetpack_scan_jetpack_scan_wp_admin_render_page';
262 $render = function_exists( $render_fn )
263 ? $render_fn
264 : array( __CLASS__, 'render_page_fallback' );
265
266 $page_suffix = Admin_Menu::add_menu(
267 /** "Scan" is a product name, do not translate. */
268 'Scan',
269 'Scan',
270 'manage_options',
271 self::PAGE_SLUG,
272 $render,
273 6
274 );
275
276 if ( $page_suffix ) {
277 add_action( 'load-' . $page_suffix, array( __CLASS__, 'admin_init' ) );
278 }
279
280 return $page_suffix;
281 }
282
283 /**
284 * Whether the Scan page should be shown to the current user.
285 *
286 * @return bool
287 */
288 public static function is_available() {
289 if ( is_multisite() ) {
290 return false;
291 }
292
293 if ( ! current_user_can( 'manage_options' ) ) {
294 return false;
295 }
296
297 return ( new Connection_Manager() )->is_user_connected();
298 }
299
300 /**
301 * Fires when the admin page is loaded.
302 *
303 * Silences the standard wp-admin notice channels so JITMs and
304 * plugin-update messages don't reflow the focused Scan layout
305 * mid-scan or while a fix modal is open.
306 */
307 public static function admin_init() {
308 remove_all_actions( 'admin_notices' );
309 remove_all_actions( 'all_admin_notices' );
310 }
311
312 /**
313 * Fallback render — used only if the wp-build registration file
314 * isn't loaded (e.g. the package wasn't built yet). Renders a bare
315 * mount node so the page doesn't 500 in dev.
316 */
317 public static function render_page_fallback() {
318 ?>
319 <div id="jetpack-scan-page-root"></div>
320 <?php
321 }
322
323 /**
324 * Register the REST routes backing the Scan UI.
325 */
326 public static function register_rest_routes() {
327 REST_Controller::register_rest_routes();
328 }
329 }
330