PluginProbe
Performance Lab / 3.9.0
Performance Lab v3.9.0
trunk 1.0.0 1.0.0-beta.1 1.0.0-beta.2 1.0.0-beta.3 1.0.0-rc.1 1.1.0 1.2.0 1.3.0 1.4.0 1.5.0 1.6.0 1.7.0 1.8.0 1.9.0 2.0.0 2.1.0 2.2.0 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.7.0 2.8.0 All 44 releases
performance-lab / includes / admin / load.php

load.php in Performance Lab 3.9.0, at includes/admin/load.php

505 lines 14.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Admin integration file.
4 *
5 * @package performance-lab
6 */
7
8 // @codeCoverageIgnoreStart
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit; // Exit if accessed directly.
11 }
12 // @codeCoverageIgnoreEnd
13
14 /**
15 * Adds the features page to the Settings menu.
16 *
17 * @since 1.0.0
18 * @since 3.0.0 Renamed to perflab_add_features_page().
19 */
20 function perflab_add_features_page(): void {
21 $hook_suffix = add_options_page(
22 __( 'Performance Features', 'performance-lab' ),
23 __( 'Performance', 'performance-lab' ),
24 'manage_options',
25 PERFLAB_SCREEN,
26 'perflab_render_settings_page'
27 );
28
29 // Add the following hooks only if the screen was successfully added.
30 if ( false !== $hook_suffix ) {
31 add_action( "load-{$hook_suffix}", 'perflab_load_features_page', 10, 0 );
32 add_filter( 'plugin_action_links_' . plugin_basename( PERFLAB_MAIN_FILE ), 'perflab_plugin_action_links_add_settings' );
33 }
34 }
35
36 add_action( 'admin_menu', 'perflab_add_features_page' );
37
38 /**
39 * Initializes functionality for the features page.
40 *
41 * @since 1.0.0
42 * @since 3.0.0 Renamed to perflab_load_features_page(), and the
43 * $module and $hook_suffix parameters were removed.
44 */
45 function perflab_load_features_page(): void {
46 // Handle script enqueuing for settings page.
47 add_action( 'admin_enqueue_scripts', 'perflab_enqueue_features_page_scripts' );
48
49 // Handle admin notices for settings page.
50 add_action( 'admin_notices', 'perflab_plugin_admin_notices' );
51
52 // Handle style for settings page.
53 add_action( 'admin_head', 'perflab_print_features_page_style' );
54 }
55
56 /**
57 * Renders the plugin page.
58 *
59 * @since 1.0.0
60 * @since 3.0.0 Renamed to perflab_render_settings_page().
61 */
62 function perflab_render_settings_page(): void {
63 ?>
64 <div class="wrap">
65 <?php perflab_render_plugins_ui(); ?>
66 </div>
67 <?php
68 }
69
70 /**
71 * Initializes admin pointer.
72 *
73 * Handles the bootstrapping of the admin pointer.
74 * Mainly jQuery code that is self-initialising.
75 *
76 * @since 1.0.0
77 *
78 * @param string|null $hook_suffix The current admin page. Note this can be null because `iframe_header()` does not
79 * ensure that `$hook_suffix` is a string when it calls `do_action( 'admin_enqueue_scripts', $hook_suffix )`.
80 */
81 function perflab_admin_pointer( ?string $hook_suffix = '' ): void {
82 // Do not show admin pointer in multisite Network admin or User admin UI.
83 if ( is_network_admin() || is_user_admin() ) {
84 return;
85 }
86 $current_user = get_current_user_id();
87 $dismissed = array_filter( explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) );
88
89 if ( in_array( 'perflab-admin-pointer', $dismissed, true ) ) {
90 return;
91 }
92
93 if ( ! in_array( $hook_suffix, array( 'index.php', 'plugins.php' ), true ) ) {
94
95 // Do not show on the settings page and dismiss the pointer.
96 if ( isset( $_GET['page'] ) && PERFLAB_SCREEN === $_GET['page'] && ( ! in_array( 'perflab-admin-pointer', $dismissed, true ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
97 $dismissed[] = 'perflab-admin-pointer';
98 update_user_meta( $current_user, 'dismissed_wp_pointers', implode( ',', $dismissed ) );
99 }
100
101 return;
102 }
103
104 // Enqueue pointer CSS and JS.
105 wp_enqueue_style( 'wp-pointer' );
106 wp_enqueue_script( 'wp-pointer' );
107 add_action( 'admin_print_footer_scripts', 'perflab_render_pointer', 10, 0 );
108 }
109 add_action( 'admin_enqueue_scripts', 'perflab_admin_pointer' );
110
111 /**
112 * Renders the Admin Pointer.
113 *
114 * Handles the rendering of the admin pointer.
115 *
116 * @since 1.0.0
117 * @since 2.4.0 Optional arguments were added to make the function reusable for different pointers.
118 *
119 * @param string $pointer_id Optional. ID of the pointer. Default 'perflab-admin-pointer'.
120 * @param array{heading?: string, content?: string} $args Optional. Pointer arguments. Supports 'heading' and 'content' entries.
121 * Defaults are the heading and content for the 'perflab-admin-pointer'.
122 */
123 function perflab_render_pointer( string $pointer_id = 'perflab-admin-pointer', array $args = array() ): void {
124 if ( ! isset( $args['heading'] ) ) {
125 $args['heading'] = __( 'Performance Lab', 'performance-lab' );
126 }
127 if ( ! isset( $args['content'] ) ) {
128 $args['content'] = sprintf(
129 /* translators: %s: settings page link */
130 esc_html__( 'You can now test upcoming WordPress performance features. Open %s to individually toggle the performance features.', 'performance-lab' ),
131 '<a href="' . esc_url( add_query_arg( 'page', PERFLAB_SCREEN, admin_url( 'options-general.php' ) ) ) . '">' . esc_html__( 'Settings > Performance', 'performance-lab' ) . '</a>'
132 );
133 }
134
135 $wp_kses_options = array(
136 'a' => array(
137 'href' => array(),
138 ),
139 );
140
141 ?>
142 <script id="<?php echo esc_attr( $pointer_id ); ?>" type="text/javascript">
143 jQuery( function() {
144 // Pointer Options.
145 const options = {
146 content: <?php echo wp_json_encode( '<h3>' . esc_html( $args['heading'] ) . '</h3><p>' . wp_kses( $args['content'], $wp_kses_options ) . '</p>' ); ?>,
147 position: {
148 edge: 'left',
149 align: 'right',
150 },
151 pointerClass: 'wp-pointer arrow-top',
152 pointerWidth: 420,
153 close: function() {
154 jQuery.post(
155 window.ajaxurl,
156 {
157 pointer: <?php echo wp_json_encode( $pointer_id ); ?>,
158 action: 'dismiss-wp-pointer',
159 _wpnonce: <?php echo wp_json_encode( wp_create_nonce( 'dismiss_pointer' ) ); ?>,
160 }
161 );
162 }
163 };
164
165 jQuery( '#menu-settings' ).pointer( options ).pointer( 'open' );
166 } );
167 </script>
168 <?php
169 }
170
171 /**
172 * Adds a link to the features page to the plugin's entry in the plugins list table.
173 *
174 * This function is only used if the features page exists and is accessible.
175 *
176 * @since 1.0.0
177 *
178 * @see perflab_add_features_page()
179 *
180 * @param string[]|mixed $links List of plugin action links HTML.
181 * @return string[]|mixed Modified list of plugin action links HTML.
182 */
183 function perflab_plugin_action_links_add_settings( $links ) {
184 if ( ! is_array( $links ) ) {
185 return $links;
186 }
187
188 // Add link as the first plugin action link.
189 $settings_link = sprintf(
190 '<a href="%s">%s</a>',
191 esc_url( add_query_arg( 'page', PERFLAB_SCREEN, admin_url( 'options-general.php' ) ) ),
192 esc_html__( 'Settings', 'performance-lab' )
193 );
194
195 return array_merge(
196 array( 'settings' => $settings_link ),
197 $links
198 );
199 }
200
201 /**
202 * Dismisses notification pointer after verifying nonce.
203 *
204 * This function adds a nonce check before dismissing perflab-admin-pointer
205 * It runs before the dismiss-wp-pointer AJAX action is performed.
206 *
207 * @since 2.3.0
208 */
209 function perflab_dismiss_wp_pointer_wrapper(): void {
210 if ( isset( $_POST['pointer'] ) && 'perflab-admin-pointer' !== $_POST['pointer'] ) {
211 // Another plugin's pointer, do nothing.
212 return;
213 }
214 check_ajax_referer( 'dismiss_pointer' );
215 }
216 add_action( 'wp_ajax_dismiss-wp-pointer', 'perflab_dismiss_wp_pointer_wrapper', 0 );
217
218 /**
219 * Gets the path to a script or stylesheet.
220 *
221 * @since 3.7.0
222 *
223 * @param string $src_path Source path.
224 * @param string|null $min_path Minified path. If not supplied, then '.min' is injected before the file extension in the source path.
225 * @return string URL to script or stylesheet.
226 */
227 function perflab_get_asset_path( string $src_path, ?string $min_path = null ): string {
228 if ( null === $min_path ) {
229 // Note: wp_scripts_get_suffix() is not used here because we need access to both the source and minified paths.
230 $min_path = (string) preg_replace( '/(?=\.\w+$)/', '.min', $src_path );
231 }
232
233 $force_src = false;
234 if ( WP_DEBUG && ! file_exists( trailingslashit( PERFLAB_PLUGIN_DIR_PATH ) . $min_path ) ) {
235 $force_src = true;
236 wp_trigger_error(
237 __FUNCTION__,
238 sprintf(
239 /* translators: %s is the minified asset path */
240 __( 'Minified asset has not been built: %s', 'performance-lab' ),
241 $min_path
242 ),
243 E_USER_WARNING
244 );
245 }
246
247 if ( SCRIPT_DEBUG || $force_src ) {
248 return $src_path;
249 }
250
251 return $min_path;
252 }
253
254 /**
255 * Callback function to handle admin scripts.
256 *
257 * @since 2.8.0
258 * @since 3.0.0 Renamed to perflab_enqueue_features_page_scripts().
259 */
260 function perflab_enqueue_features_page_scripts(): void {
261 // These assets are needed for the "Learn more" popover.
262 wp_enqueue_script( 'thickbox' );
263 wp_enqueue_style( 'thickbox' );
264 wp_enqueue_script( 'plugin-install' );
265
266 // Enqueue plugin activate AJAX script and localize script data.
267 wp_enqueue_script(
268 'perflab-plugin-activate-ajax',
269 plugins_url( perflab_get_asset_path( 'includes/admin/plugin-activate-ajax.js' ), PERFLAB_MAIN_FILE ),
270 array( 'wp-i18n', 'wp-a11y', 'wp-api-fetch' ),
271 PERFLAB_VERSION,
272 true
273 );
274 }
275
276 /**
277 * Sanitizes a plugin slug.
278 *
279 * @since 3.1.0
280 *
281 * @param mixed $unsanitized_plugin_slug Unsanitized plugin slug.
282 * @return string|null Validated and sanitized slug or else null.
283 */
284 function perflab_sanitize_plugin_slug( $unsanitized_plugin_slug ): ?string {
285 if ( in_array( $unsanitized_plugin_slug, perflab_get_standalone_plugins(), true ) ) {
286 return $unsanitized_plugin_slug;
287 } else {
288 return null;
289 }
290 }
291
292 /**
293 * Callback for handling installation/activation of plugin.
294 *
295 * @since 3.0.0
296 */
297 function perflab_install_activate_plugin_callback(): void {
298 check_admin_referer( 'perflab_install_activate_plugin' );
299
300 require_once ABSPATH . 'wp-admin/includes/plugin.php';
301 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
302 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
303 require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php';
304
305 if ( ! isset( $_GET['slug'] ) ) {
306 wp_die( esc_html__( 'Missing required parameter.', 'performance-lab' ) );
307 }
308
309 $plugin_slug = perflab_sanitize_plugin_slug( wp_unslash( $_GET['slug'] ) );
310 if ( null === $plugin_slug ) {
311 wp_die( esc_html__( 'Invalid plugin.', 'performance-lab' ) );
312 }
313
314 // Install and activate the plugin and its dependencies.
315 $result = perflab_install_and_activate_plugin( $plugin_slug );
316 if ( $result instanceof WP_Error ) {
317 wp_die( wp_kses_post( $result->get_error_message() ) );
318 }
319
320 $url = add_query_arg(
321 array(
322 'page' => PERFLAB_SCREEN,
323 'activate' => $plugin_slug,
324 ),
325 admin_url( 'options-general.php' )
326 );
327
328 if ( wp_safe_redirect( $url ) ) {
329 exit;
330 }
331 }
332 add_action( 'admin_action_perflab_install_activate_plugin', 'perflab_install_activate_plugin_callback' );
333
334 /**
335 * Callback function to handle admin inline style.
336 *
337 * @since 3.0.0
338 */
339 function perflab_print_features_page_style(): void {
340 ?>
341 <style>
342 .plugin-card .name,
343 .plugin-card .desc, /* For WP <6.5 versions */
344 .plugin-card .desc > p {
345 margin-left: 0;
346 }
347 .plugin-card-top {
348 /* This is required to ensure the Settings link does not extend below the bottom of a plugin card on a wide screen. */
349 min-height: 90px;
350 }
351 @media screen and (max-width: 782px) {
352 .plugin-card-top {
353 /* Same reason as above, but now the button is taller to make it easier to tap on touch screens. */
354 min-height: 110px;
355 }
356 }
357 .plugin-card .perflab-plugin-experimental {
358 font-size: 80%;
359 font-weight: normal;
360 }
361
362 @media screen and (max-width: 1100px) and (min-width: 782px), (max-width: 480px) {
363 .plugin-card .action-links {
364 margin-left: auto;
365 }
366 /* Make sure the settings link gets spaced out from the Learn more link. */
367 .plugin-card .plugin-action-buttons > li:nth-child(3) {
368 margin-left: 2ex;
369 border-left: solid 1px;
370 padding-left: 2ex;
371 }
372 }
373 </style>
374 <?php
375 }
376
377 /**
378 * Callback function hooked to admin_notices to render admin notices on the plugin's screen.
379 *
380 * @since 2.8.0
381 */
382 function perflab_plugin_admin_notices(): void {
383 if ( ! current_user_can( 'install_plugins' ) ) {
384 $are_all_plugins_installed = true;
385 $installed_plugin_slugs = array_map(
386 static function ( $name ) {
387 return strtok( $name, '/' );
388 },
389 array_keys( get_plugins() )
390 );
391 foreach ( perflab_get_standalone_plugin_version_constants() as $plugin_slug => $constant_name ) {
392 if ( ! in_array( $plugin_slug, $installed_plugin_slugs, true ) ) {
393 $are_all_plugins_installed = false;
394 break;
395 }
396 }
397
398 if ( ! $are_all_plugins_installed ) {
399 wp_admin_notice(
400 esc_html__( 'Due to your site\'s configuration, you may not be able to activate the performance features, unless the underlying plugin is already installed. Please install the relevant plugins manually.', 'performance-lab' ),
401 array(
402 'type' => 'warning',
403 )
404 );
405 return;
406 }
407 }
408
409 $activated_plugin_slug = null;
410 if ( isset( $_GET['activate'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
411 $activated_plugin_slug = perflab_sanitize_plugin_slug( wp_unslash( $_GET['activate'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
412 }
413
414 if ( null !== $activated_plugin_slug ) {
415 $message = __( 'Feature activated.', 'performance-lab' );
416
417 $plugin_settings_url = perflab_get_plugin_settings_url( $activated_plugin_slug );
418 if ( null !== $plugin_settings_url ) {
419 /* translators: %s is the settings URL */
420 $message .= ' ' . sprintf( __( 'Review <a href="%s">settings</a>.', 'performance-lab' ), esc_url( $plugin_settings_url ) );
421 }
422
423 wp_admin_notice(
424 wp_kses(
425 $message,
426 array(
427 'a' => array(
428 'href' => array(),
429 ),
430 )
431 ),
432 array(
433 'type' => 'success',
434 'dismissible' => true,
435 )
436 );
437 }
438 }
439
440 /**
441 * Gets the URL to the plugin settings screen if one exists.
442 *
443 * @since 3.1.0
444 *
445 * @param string $plugin_slug Plugin slug passed to generate the settings link.
446 * @return string|null Either the plugin settings URL or null if not available.
447 */
448 function perflab_get_plugin_settings_url( string $plugin_slug ): ?string {
449 $plugin_file = null;
450
451 foreach ( array_keys( get_plugins() ) as $file ) {
452 if ( strtok( $file, '/' ) === $plugin_slug ) {
453 $plugin_file = $file;
454 break;
455 }
456 }
457
458 if ( null === $plugin_file ) {
459 return null;
460 }
461
462 /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
463 $plugin_links = apply_filters( "plugin_action_links_{$plugin_file}", array() );
464
465 if ( ! is_array( $plugin_links ) || ! array_key_exists( 'settings', $plugin_links ) ) {
466 return null;
467 }
468
469 $p = new WP_HTML_Tag_Processor( $plugin_links['settings'] );
470 if ( ! $p->next_tag( array( 'tag_name' => 'A' ) ) ) {
471 return null;
472 }
473 $href = $p->get_attribute( 'href' );
474 if ( is_string( $href ) && '' !== $href ) {
475 return $href;
476 }
477
478 return null;
479 }
480
481 /**
482 * Prints the Performance Lab install notice after each feature plugin's row meta.
483 *
484 * @since 3.2.0
485 *
486 * @param string $plugin_file Plugin file.
487 */
488 function perflab_print_row_meta_install_notice( string $plugin_file ): void {
489 if ( ! in_array( strtok( $plugin_file, '/' ), perflab_get_standalone_plugins(), true ) ) {
490 return;
491 }
492
493 $message = sprintf(
494 /* translators: %s: link to Performance Lab settings screen */
495 __( 'This plugin is installed by <a href="%s">Performance Lab</a>.', 'performance-lab' ),
496 esc_url( add_query_arg( 'page', PERFLAB_SCREEN, admin_url( 'options-general.php' ) ) )
497 );
498
499 printf(
500 '<div class="requires"><p>%1$s</p></div>',
501 wp_kses( $message, array( 'a' => array( 'href' => array() ) ) )
502 );
503 }
504 add_action( 'after_plugin_row_meta', 'perflab_print_row_meta_install_notice' );
505