Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
166 changes: 120 additions & 46 deletions plugins/performance-lab/includes/admin/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,46 @@ function perflab_render_settings_page(): void {
<?php
}

/**
* Gets dismissed admin pointer IDs.
*
* @since n.e.x.t
*
* @return non-empty-string[] Dismissed admin pointer IDs.
*/
function perflab_get_dismissed_admin_pointer_ids(): array {
return array_filter(
explode(
',',
(string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true )
)
);
}

/**
* Gets the admin pointers.
*
* @since n.e.x.t
*
* @return array<non-empty-string, string> Admin pointer messages with the admin pointer IDs as the keys.
*/
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' ),
);

if (
defined( 'SPECULATION_RULES_VERSION' )
&&
version_compare( SPECULATION_RULES_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' );
}

return $pointers;
}

/**
* Initializes admin pointer.
*
Expand All @@ -83,19 +123,29 @@ function perflab_admin_pointer( ?string $hook_suffix = '' ): void {
if ( is_network_admin() || is_user_admin() ) {
return;
}
$current_user = get_current_user_id();
$dismissed = array_filter( explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) );

if ( in_array( 'perflab-admin-pointer', $dismissed, true ) ) {
$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;
}

// 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 ) ) {

// Do not show on the settings page and dismiss the pointer.
if ( isset( $_GET['page'] ) && PERFLAB_SCREEN === $_GET['page'] && ( ! in_array( 'perflab-admin-pointer', $dismissed, true ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$dismissed[] = 'perflab-admin-pointer';
update_user_meta( $current_user, 'dismissed_wp_pointers', implode( ',', $dismissed ) );
// 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
update_user_meta(
get_current_user_id(),
'dismissed_wp_pointers',
implode(
',',
array_unique( array_merge( $dismissed_pointer_ids, $admin_pointer_ids ) )
)
);
}

return;
Expand All @@ -104,69 +154,89 @@ function perflab_admin_pointer( ?string $hook_suffix = '' ): void {
// Enqueue pointer CSS and JS.
wp_enqueue_style( 'wp-pointer' );
wp_enqueue_script( 'wp-pointer' );
add_action( 'admin_print_footer_scripts', 'perflab_render_pointer', 10, 0 );
}
add_action( 'admin_enqueue_scripts', 'perflab_admin_pointer' );

/**
* Renders the Admin Pointer.
*
* Handles the rendering of the admin pointer.
*
* @since 1.0.0
* @since 2.4.0 Optional arguments were added to make the function reusable for different pointers.
*
* @param string $pointer_id Optional. ID of the pointer. Default 'perflab-admin-pointer'.
* @param array{heading?: string, content?: string} $args Optional. Pointer arguments. Supports 'heading' and 'content' entries.
* Defaults are the heading and content for the 'perflab-admin-pointer'.
*/
function perflab_render_pointer( string $pointer_id = 'perflab-admin-pointer', array $args = array() ): void {
if ( ! isset( $args['heading'] ) ) {
$args['heading'] = __( 'Performance Lab', 'performance-lab' );
}
if ( ! isset( $args['content'] ) ) {
$args['content'] = sprintf(
/* translators: %s: settings page link */
esc_html__( 'You can now test upcoming WordPress performance features. Open %s to individually toggle the performance features.', 'performance-lab' ),
'<a href="' . esc_url( add_query_arg( 'page', PERFLAB_SCREEN, admin_url( 'options-general.php' ) ) ) . '">' . esc_html__( 'Settings > Performance', 'performance-lab' ) . '</a>'
);
$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( array_keys( $admin_pointers ), $dismissed_pointer_ids );
}

$args = array(
'heading' => __( 'Performance Lab', 'performance-lab' ),
);

$args['content'] = implode(
'',
array_map(
static function ( string $needed_pointer ) use ( $admin_pointers ): string {
return '<p>' . $admin_pointers[ $needed_pointer ] . '</p>';
},
$needed_pointer_ids
)
);

$args['content'] .= '<p>' . sprintf(
/* translators: %s: settings page link */
esc_html__( 'Open %s to individually toggle the performance features and access their settings (if any).', 'performance-lab' ),
'<a href="' . esc_url( add_query_arg( 'page', PERFLAB_SCREEN, admin_url( 'options-general.php' ) ) ) . '">' . esc_html__( 'Settings > Performance', 'performance-lab' ) . '</a>'
) . '</p>';

$wp_kses_options = array(
'a' => array(
'a' => array(
'href' => array(),
),
'p' => array(),
'strong' => array(),
);

$pointer_ids_to_dismiss = array_values( array_diff( array_keys( $admin_pointers ), $dismissed_pointer_ids ) );

ob_start();
?>
<script id="<?php echo esc_attr( $pointer_id ); ?>" type="text/javascript">
<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' ) ); ?>;

function dismissNextPointer() {
const pointerId = pointerIdsToDismiss.shift();
if ( ! pointerId ) {
return;
}

jQuery.post(
window.ajaxurl,
{
pointer: pointerId,
action: 'dismiss-wp-pointer',
_wpnonce: nonce,
}
).then( dismissNextPointer );
}

// Pointer Options.
const options = {
content: <?php echo wp_json_encode( '<h3>' . esc_html( $args['heading'] ) . '</h3><p>' . wp_kses( $args['content'], $wp_kses_options ) . '</p>' ); ?>,
content: <?php echo wp_json_encode( '<h3>' . esc_html( $args['heading'] ) . '</h3>' . wp_kses( $args['content'], $wp_kses_options ) ); ?>,
position: {
edge: 'left',
align: 'right',
},
pointerClass: 'wp-pointer arrow-top',
pointerWidth: 420,
close: function() {
jQuery.post(
window.ajaxurl,
{
pointer: <?php echo wp_json_encode( $pointer_id ); ?>,
action: 'dismiss-wp-pointer',
_wpnonce: <?php echo wp_json_encode( wp_create_nonce( 'dismiss_pointer' ) ); ?>,
}
);
}
close: dismissNextPointer
};

jQuery( '#menu-settings' ).pointer( options ).pointer( 'open' );
} );
</script>
<?php
$processor = new WP_HTML_Tag_Processor( (string) ob_get_clean() );
if ( $processor->next_tag( array( 'tag_name' => 'SCRIPT' ) ) ) {
wp_add_inline_script( 'wp-pointer', $processor->get_modifiable_text() );
}
}
add_action( 'admin_enqueue_scripts', 'perflab_admin_pointer' );

/**
* Adds a link to the features page to the plugin's entry in the plugins list table.
Expand Down Expand Up @@ -207,7 +277,11 @@ function perflab_plugin_action_links_add_settings( $links ) {
* @since 2.3.0
*/
function perflab_dismiss_wp_pointer_wrapper(): void {
if ( isset( $_POST['pointer'] ) && 'perflab-admin-pointer' !== $_POST['pointer'] ) {
if (
isset( $_POST['pointer'] )
&&
! in_array( $_POST['pointer'], array_keys( perflab_get_admin_pointers() ), true )
) {
// Another plugin's pointer, do nothing.
return;
}
Expand Down
4 changes: 4 additions & 0 deletions plugins/performance-lab/readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,10 @@ Contributions are always welcome! Learn more about how to get involved in the [C

== Upgrade Notice ==

= n.e.x.t =

This release introduces a new feature plugin called View Transitions which adds smooth transitions between navigations on your site.

= 3.2.0 =

This release introduces a new feature plugin called Image Prioritizer which optimizes the loading of images to improve LCP.
Expand Down
60 changes: 55 additions & 5 deletions plugins/performance-lab/tests/includes/admin/test-load.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,37 @@ public function test_perflab_render_settings_page(): void {
$this->assertStringNotContainsString( "<input type='hidden' name='option_page' value='" . PERFLAB_SCREEN . "' />", $output );
}

/**
* @covers ::perflab_get_dismissed_admin_pointer_ids
*/
public function test_perflab_get_dismissed_admin_pointer_ids(): void {
$user_id = self::factory()->user->create( array( 'role' => 'administrator' ) );
wp_set_current_user( $user_id );

// No dismissed pointers.
$this->assertSame( array(), perflab_get_dismissed_admin_pointer_ids() );

// Dismiss a single pointer.
update_user_meta( $user_id, 'dismissed_wp_pointers', 'perflab-admin-pointer' );
$this->assertSame( array( 'perflab-admin-pointer' ), perflab_get_dismissed_admin_pointer_ids() );

// Dismiss multiple pointers.
update_user_meta( $user_id, 'dismissed_wp_pointers', 'perflab-admin-pointer,another-pointer' );
$this->assertSame( array( 'perflab-admin-pointer', 'another-pointer' ), perflab_get_dismissed_admin_pointer_ids() );

// Dismiss all pointers.
update_user_meta( $user_id, 'dismissed_wp_pointers', implode( ',', array_keys( perflab_get_admin_pointers() ) ) );
$this->assertSame( array_keys( perflab_get_admin_pointers() ), perflab_get_dismissed_admin_pointer_ids() );
}

/**
* @covers ::perflab_get_admin_pointers
*/
public function test_perflab_get_admin_pointers(): void {
$pointers = perflab_get_admin_pointers();
$this->assertArrayHasKey( 'perflab-admin-pointer', $pointers );
}
Comment on lines +89 to +118
Copy link
Member Author

Choose a reason for hiding this comment

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

Note: I'm happy to say that Gemini Code Assist was actually able to write most of this for me. In PhpStorm, I just did Cmd+\ and then entered /generate test perflab_get_dismissed_admin_pointer_ids and then again for perflab_get_admin_pointers(), and voilà!


/**
* @return array<string, array{ hook_suffix: string|null, expected: bool }>
*/
Expand Down Expand Up @@ -119,23 +150,32 @@ public function data_provider_test_perflab_admin_pointer(): array {
'assert' => null,
'dismissed_wp_pointers' => '',
),
'dashboard_yes_dismissed' => array(
'dashboard_new_dismissed' => array(
'set_up' => static function (): void {
update_user_meta( wp_get_current_user()->ID, 'dismissed_wp_pointers', 'perflab-admin-pointer' );
},
'hook_suffix' => 'index.php',
'expected' => false,
'expected' => true,
'assert' => null,
'dismissed_wp_pointers' => 'perflab-admin-pointer',
),
'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() ) ) );
},
'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',
'expected' => false,
'assert' => null,
'dismissed_wp_pointers' => 'perflab-admin-pointer',
'dismissed_wp_pointers' => implode( ',', array_keys( perflab_get_admin_pointers() ) ),
),
'perflab_screen_second_time' => array(
'set_up' => static function (): void {
Expand All @@ -145,7 +185,7 @@ public function data_provider_test_perflab_admin_pointer(): array {
'hook_suffix' => 'options-general.php',
'expected' => false,
'assert' => null,
'dismissed_wp_pointers' => 'perflab-admin-pointer',
'dismissed_wp_pointers' => implode( ',', array_keys( perflab_get_admin_pointers() ) ),
),
);
}
Expand All @@ -168,7 +208,17 @@ public function test_perflab_admin_pointer( ?Closure $set_up, ?string $hook_suff
}
$this->assertFalse( is_network_admin() || is_user_admin() );
perflab_admin_pointer( $hook_suffix );
$this->assertSame( $expected ? 10 : false, has_action( 'admin_print_footer_scripts', 'perflab_render_pointer' ) );

$after_script = '';
$script_dependency = wp_scripts()->query( 'wp-pointer' );
if ( $script_dependency instanceof _WP_Dependency ) {
$after_script = implode( "\n", array_filter( $script_dependency->extra['after'] ?? array() ) );
}
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
Loading