Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 95 additions & 34 deletions plugins/performance-lab/includes/admin/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,21 +88,38 @@ function perflab_get_dismissed_admin_pointer_ids(): array {
*
* @since n.e.x.t
*
* @return array<non-empty-string, string> Admin pointer messages with the admin pointer IDs as the keys.
* @return array<non-empty-string, array{ content: string, plugin: non-empty-string, dismiss_if_installed: bool }> Keys are the admin pointer IDs.
*/
function perflab_get_admin_pointers(): array {
$pointers = array(
'perflab-admin-pointer' => __( 'You can now test upcoming WordPress performance features.', 'performance-lab' ),
'perflab-feature-view-transitions' => __( 'New <strong>View Transitions</strong> feature now available.', 'performance-lab' ),
'perflab-feature-nocache-bfcache' => __( 'New <strong>No-cache BFCache</strong> feature now available.', 'performance-lab' ),
'perflab-admin-pointer' => array(
'content' => __( 'You can now test upcoming WordPress performance features.', 'performance-lab' ),
'plugin' => 'performance-lab',
'dismiss_if_installed' => false,
),
'perflab-feature-view-transitions' => array(
'content' => __( 'New <strong>View Transitions</strong> feature now available.', 'performance-lab' ),
'plugin' => 'view-transitions',
'dismiss_if_installed' => true,
),
'perflab-feature-nocache-bfcache' => array(
'content' => __( 'New <strong>No-cache BFCache</strong> feature now available.', 'performance-lab' ),
'plugin' => 'nocache-bfcache',
'dismiss_if_installed' => true,
),
);

$installed_plugins = get_plugins();
if (
defined( 'SPECULATION_RULES_VERSION' )
isset( $installed_plugins['speculation-rules/load.php']['Version'] )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change allows for the code to be tested without having to define the constant by loading the plugin.

&&
version_compare( SPECULATION_RULES_VERSION, '1.6.0', '>=' )
version_compare( $installed_plugins['speculation-rules/load.php']['Version'], '1.6.0', '>=' )
) {
$pointers['perflab-feature-speculation-rules-auth'] = __( '<strong>Speculative Loading</strong> now includes an opt-in setting for logged-in users.', 'performance-lab' );
$pointers['perflab-feature-speculation-rules-auth'] = array(
'content' => __( '<strong>Speculative Loading</strong> now includes an opt-in setting for logged-in users.', 'performance-lab' ),
'plugin' => 'speculative-loading',
'dismiss_if_installed' => false,
);
}

return $pointers;
Expand All @@ -120,49 +137,93 @@ function perflab_get_admin_pointers(): array {
* ensure that `$hook_suffix` is a string when it calls `do_action( 'admin_enqueue_scripts', $hook_suffix )`.
*/
function perflab_admin_pointer( ?string $hook_suffix = '' ): void {
// Do not show admin pointer in multisite Network admin or User admin UI.
if ( is_network_admin() || is_user_admin() ) {
// See get_plugin_page_hookname().
$is_performance_screen = 'settings_page_' . PERFLAB_SCREEN === $hook_suffix;

// Do not show admin pointer in multisite Network admin, User admin UI, dashboard, or plugins list table. However,
// do proceed on the Performance screen so that all pointers can be auto-dismissed.
if (
is_network_admin() ||
is_user_admin() ||
(
! in_array( $hook_suffix, array( 'index.php', 'plugins.php' ), true ) &&
! $is_performance_screen
)
) {
return;
}

$admin_pointers = perflab_get_admin_pointers();
$admin_pointer_ids = array_keys( $admin_pointers );
$dismissed_pointer_ids = perflab_get_dismissed_admin_pointer_ids();

// All pointers have been dismissed already.
if ( count( array_diff( $admin_pointer_ids, $dismissed_pointer_ids ) ) === 0 ) {
return;
// And if we're on the Performance screen, automatically dismiss all the pointers.
$auto_dismissed_pointer_ids = array();
if ( $is_performance_screen ) {
$auto_dismissed_pointer_ids = array_merge( $auto_dismissed_pointer_ids, $admin_pointer_ids );
}

// Do not show the admin pointer when not on the dashboard or plugins list table.
if ( ! in_array( $hook_suffix, array( 'index.php', 'plugins.php' ), true ) ) {

// And if we're on the Performance screen, automatically dismiss the pointers.
if ( isset( $_GET['page'] ) && PERFLAB_SCREEN === $_GET['page'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This turns out to not have been ideal. It should have used $hooks_suffix, to see if it is equal to 'settings_page_' . PERFLAB_SCREEN.

update_user_meta(
get_current_user_id(),
'dismissed_wp_pointers',
implode(
',',
array_unique( array_merge( $dismissed_pointer_ids, $admin_pointer_ids ) )
)
);
// List of pointer IDs that are tied to feature plugin slugs.
$plugin_pointers_dismissed_if_installed = array();
foreach ( $admin_pointers as $pointer_id => $admin_pointer ) {
if ( $admin_pointer['dismiss_if_installed'] ) {
$plugin_pointers_dismissed_if_installed[ $pointer_id ] = $admin_pointer['plugin'];
}
}

return;
// Preemptively dismiss plugin-specific pointers for plugins which are already installed.
$plugin_dependent_pointers_undismissed = array_diff( array_keys( $plugin_pointers_dismissed_if_installed ), $dismissed_pointer_ids );
if ( count( $plugin_dependent_pointers_undismissed ) > 0 ) {
/**
* Installed plugin slugs.
*
* @var non-empty-string[] $installed_plugin_slugs
*/
$installed_plugin_slugs = array_map(
static function ( $name ) {
return strtok( $name, '/' );
},
array_keys( get_plugins() )
);

foreach ( $plugin_dependent_pointers_undismissed as $pointer_id ) {
if (
in_array( $plugin_pointers_dismissed_if_installed[ $pointer_id ], $installed_plugin_slugs, true ) &&
! in_array( $pointer_id, $dismissed_pointer_ids, true )
) {
$auto_dismissed_pointer_ids[] = $pointer_id;
}
}
}

// Enqueue pointer CSS and JS.
wp_enqueue_style( 'wp-pointer' );
wp_enqueue_script( 'wp-pointer' );
// Persist the automatically-dismissed pointers.
if ( count( $auto_dismissed_pointer_ids ) > 0 ) {
$dismissed_pointer_ids = array_unique( array_merge( $dismissed_pointer_ids, $auto_dismissed_pointer_ids ) );
update_user_meta(
get_current_user_id(),
'dismissed_wp_pointers',
implode( ',', $dismissed_pointer_ids )
);
}

// Determine which admin pointers we need.
$new_install_pointer_id = 'perflab-admin-pointer';
if ( ! in_array( $new_install_pointer_id, $dismissed_pointer_ids, true ) ) {
$needed_pointer_ids = array( $new_install_pointer_id );
} else {
$needed_pointer_ids = array_diff( $admin_pointer_ids, $dismissed_pointer_ids );
$needed_pointer_ids = $admin_pointer_ids;
}
$needed_pointer_ids = array_diff( $needed_pointer_ids, $dismissed_pointer_ids );

// No admin pointers are needed, so abort.
if ( count( $needed_pointer_ids ) === 0 ) {
return;
}

// Enqueue pointer CSS and JS.
wp_enqueue_style( 'wp-pointer' );
wp_enqueue_script( 'wp-pointer' );

$args = array(
'heading' => __( 'Performance Lab', 'performance-lab' ),
);
Expand All @@ -171,7 +232,7 @@ function perflab_admin_pointer( ?string $hook_suffix = '' ): void {
'',
array_map(
static function ( string $needed_pointer ) use ( $admin_pointers ): string {
return '<p>' . $admin_pointers[ $needed_pointer ] . '</p>';
return '<p>' . $admin_pointers[ $needed_pointer ]['content'] . '</p>';
},
$needed_pointer_ids
)
Expand All @@ -197,8 +258,8 @@ static function ( string $needed_pointer ) use ( $admin_pointers ): string {
?>
<script>
jQuery( function() {
const pointerIdsToDismiss = <?php echo wp_json_encode( $pointer_ids_to_dismiss, JSON_OBJECT_AS_ARRAY ); ?>;
const nonce = <?php echo wp_json_encode( wp_create_nonce( 'dismiss_pointer' ) ); ?>;
const pointerIdsToDismiss = <?php echo wp_json_encode( $pointer_ids_to_dismiss, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES | JSON_OBJECT_AS_ARRAY ); ?>;
const nonce = <?php echo wp_json_encode( wp_create_nonce( 'dismiss_pointer' ), JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ); ?>;

function dismissNextPointer() {
const pointerId = pointerIdsToDismiss.shift();
Expand All @@ -218,7 +279,7 @@ function dismissNextPointer() {

// Pointer Options.
const options = {
content: <?php echo wp_json_encode( '<h3>' . esc_html( $args['heading'] ) . '</h3>' . wp_kses( $args['content'], $wp_kses_options ) ); ?>,
content: <?php echo wp_json_encode( '<h3>' . esc_html( $args['heading'] ) . '</h3>' . wp_kses( $args['content'], $wp_kses_options ), JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ); ?>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

position: {
edge: 'left',
align: 'right',
Expand Down
103 changes: 52 additions & 51 deletions plugins/performance-lab/tests/includes/admin/test-load.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,73 +118,77 @@ public function test_perflab_get_admin_pointers(): void {
}

/**
* @return array<string, array{ hook_suffix: string|null, expected: bool }>
* @return array<string, array{
* initial_wp_pointers: string,
* hook_suffix: string|null,
* expected: bool,
* dismissed_wp_pointers: string,
* }>
*/
public function data_provider_test_perflab_admin_pointer(): array {
return array(
'null' => array(
'set_up' => null,
'null' => array(
'initial_wp_pointers' => '',
'hook_suffix' => null,
'expected' => false,
'assert' => null,
'dismissed_wp_pointers' => '',
),
'edit.php' => array(
'set_up' => null,
'edit.php' => array(
'initial_wp_pointers' => '',
'hook_suffix' => 'edit.php',
'expected' => false,
'assert' => null,
'dismissed_wp_pointers' => '',
),
'dashboard_not_dismissed' => array(
'set_up' => null,
'dashboard_not_dismissed' => array(
'initial_wp_pointers' => '',
'hook_suffix' => 'index.php',
'expected' => true,
'assert' => null,
'dismissed_wp_pointers' => '',
'dismissed_wp_pointers' => 'perflab-feature-view-transitions',
),
'plugins_not_dismissed' => array(
'set_up' => null,
'plugins_not_dismissed' => array(
'initial_wp_pointers' => '',
'hook_suffix' => 'plugins.php',
'expected' => true,
'assert' => null,
'dismissed_wp_pointers' => '',
'dismissed_wp_pointers' => 'perflab-feature-view-transitions',
),
'dashboard_new_dismissed' => array(
// Note: If the No-cache BFCache plugin (not part of the monorepo) is installed, then this test will likely fail and it should be skipped.
'initial_wp_pointers' => 'perflab-admin-pointer',
'hook_suffix' => 'index.php',
'expected' => true,
'dismissed_wp_pointers' => 'perflab-admin-pointer,perflab-feature-view-transitions',
),
'dashboard_last_auto_dismissed' => array(
// Note: The No-cache BFCache plugin is not part of the monorepo, so it is not automatically installed in the dev environment.
'initial_wp_pointers' => 'perflab-admin-pointer,perflab-feature-nocache-bfcache,perflab-feature-speculation-rules-auth',
'hook_suffix' => 'index.php',
'expected' => false,
'dismissed_wp_pointers' => 'perflab-admin-pointer,perflab-feature-nocache-bfcache,perflab-feature-speculation-rules-auth,perflab-feature-view-transitions',
),
'dashboard_new_dismissed' => array(
'set_up' => static function (): void {
update_user_meta( wp_get_current_user()->ID, 'dismissed_wp_pointers', 'perflab-admin-pointer' );
},
'dashboard_one_not_auto_dismissed' => array(
// Note: The No-cache BFCache plugin is not part of the monorepo, so it is not automatically installed in the dev environment.
// Note: The Speculative Loading admin pointer 'perflab-feature-speculation-rules-auth' does not get auto-dismissed because it is for a new feature of an existing feature plugin.
'initial_wp_pointers' => 'perflab-admin-pointer,perflab-feature-nocache-bfcache',
'hook_suffix' => 'index.php',
'expected' => true,
'assert' => null,
'dismissed_wp_pointers' => 'perflab-admin-pointer',
'dismissed_wp_pointers' => 'perflab-admin-pointer,perflab-feature-nocache-bfcache,perflab-feature-view-transitions',
),
'dashboard_all_dismissed' => array(
'set_up' => static function (): void {
update_user_meta( wp_get_current_user()->ID, 'dismissed_wp_pointers', implode( ',', array_keys( perflab_get_admin_pointers() ) ) );
},
'dashboard_all_dismissed' => array(
'initial_wp_pointers' => implode( ',', array_keys( perflab_get_admin_pointers() ) ),
'hook_suffix' => 'index.php',
'expected' => false,
'assert' => null,
'dismissed_wp_pointers' => implode( ',', array_keys( perflab_get_admin_pointers() ) ),
),
'perflab_screen_first_time' => array(
'set_up' => static function (): void {
$_GET['page'] = PERFLAB_SCREEN;
},
'hook_suffix' => 'options-general.php',
'perflab_screen_first_time' => array(
'initial_wp_pointers' => '',
'hook_suffix' => 'settings_page_' . PERFLAB_SCREEN,
'expected' => false,
'assert' => null,
'dismissed_wp_pointers' => implode( ',', array_keys( perflab_get_admin_pointers() ) ),
),
'perflab_screen_second_time' => array(
'set_up' => static function (): void {
$_GET['page'] = PERFLAB_SCREEN;
update_user_meta( wp_get_current_user()->ID, 'dismissed_wp_pointers', 'perflab-admin-pointer' );
},
'hook_suffix' => 'options-general.php',
'perflab_screen_second_time' => array(
'initial_wp_pointers' => 'perflab-admin-pointer',
'hook_suffix' => 'settings_page_' . PERFLAB_SCREEN,
'expected' => false,
'assert' => null,
'dismissed_wp_pointers' => implode( ',', array_keys( perflab_get_admin_pointers() ) ),
),
);
Expand All @@ -194,18 +198,15 @@ public function data_provider_test_perflab_admin_pointer(): array {
* @covers ::perflab_admin_pointer
* @dataProvider data_provider_test_perflab_admin_pointer
*
* @param Closure|null $set_up Set up.
* @param string|null $hook_suffix Hook suffix.
* @param bool $expected Expected.
* @param Closure|null $assert Assert.
* @param string $dismissed_wp_pointers Dismissed admin pointers.
* @param string $initial_wp_pointers Set up.
* @param string|null $hook_suffix Hook suffix.
* @param bool $expected Expected.
* @param string $dismissed_wp_pointers Dismissed admin pointers.
*/
public function test_perflab_admin_pointer( ?Closure $set_up, ?string $hook_suffix, bool $expected, ?Closure $assert, string $dismissed_wp_pointers ): void {
public function test_perflab_admin_pointer( string $initial_wp_pointers, ?string $hook_suffix, bool $expected, string $dismissed_wp_pointers ): void {
$user_id = self::factory()->user->create( array( 'role' => 'administrator' ) );
wp_set_current_user( $user_id );
if ( $set_up instanceof Closure ) {
$set_up();
}
update_user_meta( wp_get_current_user()->ID, 'dismissed_wp_pointers', $initial_wp_pointers );
$this->assertFalse( is_network_admin() || is_user_admin() );
perflab_admin_pointer( $hook_suffix );

Expand All @@ -214,14 +215,14 @@ public function test_perflab_admin_pointer( ?Closure $set_up, ?string $hook_suff
if ( $script_dependency instanceof _WP_Dependency ) {
$after_script = implode( "\n", array_filter( $script_dependency->extra['after'] ?? array() ) );
}
$this->assertSame( $dismissed_wp_pointers, get_user_meta( $user_id, 'dismissed_wp_pointers', true ) );
$this->assertSame( $expected, wp_script_is( 'wp-pointer', 'enqueued' ) );
$this->assertSame( $expected, wp_style_is( 'wp-pointer', 'enqueued' ) );
if ( $expected ) {
$this->assertStringContainsString( 'pointerIdsToDismiss', $after_script );
} else {
$this->assertStringNotContainsString( 'pointerIdsToDismiss', $after_script );
}
$this->assertSame( $expected, wp_script_is( 'wp-pointer', 'enqueued' ) );
$this->assertSame( $expected, wp_style_is( 'wp-pointer', 'enqueued' ) );
$this->assertSame( $dismissed_wp_pointers, get_user_meta( $user_id, 'dismissed_wp_pointers', true ) );
}

/**
Expand Down
4 changes: 2 additions & 2 deletions plugins/speculation-rules/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Description: Enables browsers to speculatively prerender or prefetch pages to achieve near-instant loads based on user interaction.
* Requires at least: 6.6
* Requires PHP: 7.2
* Version: 1.5.0
* Version: 1.6.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This version bump was required in order to test the new admin pointer.

* Author: WordPress Performance Team
* Author URI: https://make.wordpress.org/performance/
* License: GPLv2 or later
Expand Down Expand Up @@ -65,7 +65,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi
}
)(
'plsr_pending_plugin_info',
'1.5.0',
'1.6.0',
static function ( string $version ): void {

// Define the constant.
Expand Down
4 changes: 3 additions & 1 deletion plugins/speculation-rules/readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Contributors: wordpressdotorg
Tested up to: 6.8
Stable tag: 1.5.0
Stable tag: 1.6.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Tags: performance, javascript, speculation rules, prerender, prefetch
Expand Down Expand Up @@ -122,6 +122,8 @@ Contributions are always welcome! Learn more about how to get involved in the [C

== Changelog ==

= 1.6.0 =

= 1.5.0 =

**Enhancements**
Expand Down
Loading