PluginProbe
Performance Lab / 3.6.1
Performance Lab v3.6.1
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.6.1, at includes/admin/load.php

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