Skip to content

Commit

Permalink
Introduce od_store_url_metric_now meta cap which maps to manage_optio…
Browse files Browse the repository at this point in the history
…ns by default
  • Loading branch information
westonruter committed Jan 29, 2025
1 parent 803d0b5 commit 85ce02b
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 8 deletions.
2 changes: 1 addition & 1 deletion plugins/optimization-detective/docs/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ add_filter( 'od_metrics_storage_lock_ttl', function ( int $ttl ): int {
} );
```

By default, the TTL is zero (0) for administrator users and sixty (60) for everyone else.
By default, the TTL is zero (0) for administrator users and sixty (60) for everyone else. Whether the current user is an administrator is determined by whether the user has the `od_store_url_metric_now` capability. This meta capability by default maps to the `manage_options` capability via the `map_meta_cap` filter.

During development this is useful to set to zero so you can quickly collect new URL Metrics by reloading the page without having to wait for the storage lock to release:

Expand Down
1 change: 1 addition & 0 deletions plugins/optimization-detective/hooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
add_action( 'init', 'od_initialize_extensions', PHP_INT_MAX );
add_filter( 'template_include', 'od_buffer_output', PHP_INT_MAX );
OD_URL_Metrics_Post_Type::add_hooks();
OD_Storage_Lock::add_hooks();
add_action( 'wp', 'od_maybe_add_template_output_buffer_filter' );
add_action( 'wp_head', 'od_render_generator_meta_tag' );
add_filter( 'site_status_tests', 'od_add_rest_api_availability_test' );
Expand Down
49 changes: 47 additions & 2 deletions plugins/optimization-detective/storage/class-od-storage-lock.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,49 @@
*/
final class OD_Storage_Lock {

/**
* Capability for being able to store a URL Metric now.
*
* @since n.e.x.t
* @access private
* @var string
*/
const STORE_URL_METRIC_NOW_CAPABILITY = 'od_store_url_metric_now';

/**
* Adds hooks.
*
* @since n.e.x.t
* @access private
*/
public static function add_hooks(): void {
add_filter( 'map_meta_cap', array( __CLASS__, 'filter_map_meta_cap' ), 10, 3 );
}

/**
* Filters map_meta_cap to grant the `od_store_url_metric_now` capability to administrators by default.
*
* @since n.e.x.t
* @access private
*
* @param string[]|mixed $caps Capabilities.
* @param string $cap Capability.
* @param int $user_id User ID.
* @return string[] Granted capabilities.
*/
public static function filter_map_meta_cap( $caps, string $cap, int $user_id ): array {
if ( ! is_array( $caps ) ) {
$caps = array();

Check warning on line 55 in plugins/optimization-detective/storage/class-od-storage-lock.php

View check run for this annotation

Codecov / codecov/patch

plugins/optimization-detective/storage/class-od-storage-lock.php#L55

Added line #L55 was not covered by tests
}

$primitive_cap = 'manage_options';
if ( 'od_store_url_metric_now' === $cap && user_can( $user_id, $primitive_cap ) ) {
$caps = array( $primitive_cap );

Check warning on line 60 in plugins/optimization-detective/storage/class-od-storage-lock.php

View check run for this annotation

Codecov / codecov/patch

plugins/optimization-detective/storage/class-od-storage-lock.php#L60

Added line #L60 was not covered by tests
}

return $caps;
}

/**
* Gets the TTL (in seconds) for the URL Metric storage lock.
*
Expand All @@ -29,7 +72,7 @@ final class OD_Storage_Lock {
* @return int<0, max> TTL in seconds, greater than or equal to zero. A value of zero means that the storage lock should be disabled and thus that transients must not be used.
*/
public static function get_ttl(): int {
$ttl = current_user_can( 'manage_options' ) ? 0 : MINUTE_IN_SECONDS;
$ttl = current_user_can( self::STORE_URL_METRIC_NOW_CAPABILITY ) ? 0 : MINUTE_IN_SECONDS;

/**
* Filters how long the current IP is locked from submitting another URL metric storage REST API request.
Expand All @@ -41,7 +84,9 @@ public static function get_ttl(): int {
* return is_user_logged_in() ? 0 : $ttl;
* } );
*
* By default, the TTL is zero (0) for administrator users and sixty (60) for everyone else.
* By default, the TTL is zero (0) for administrator users and sixty (60) for everyone else. Whether the current
* user is an administrator is determined by whether the user has the `od_store_url_metric_now` capability. This
* meta capability by default maps to the `manage_options` capability via the `map_meta_cap` filter.
*
* @since 0.1.0
* @since 1.0.0 This now defaults to zero (0) for administrator users.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,22 @@ public function tear_down(): void {
parent::tear_down();
}

/**
* Test add_hooks().
*
* @covers ::add_hooks
*/
public function test_add_hooks(): void {
remove_all_filters( 'map_meta_cap' );

OD_Storage_Lock::add_hooks();

$this->assertSame(
10,
has_filter( 'map_meta_cap', array( OD_Storage_Lock::class, 'filter_map_meta_cap' ) )
);
}

/**
* Data provider.
*
Expand Down
64 changes: 59 additions & 5 deletions plugins/optimization-detective/tests/test-detection.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,27 @@ public function test_od_get_cache_purge_post_id( Closure $set_up, bool $expected
*/
public function data_provider_od_get_detection_script(): array {
return array(
'unfiltered' => array(
'unfiltered' => array(
'set_up' => static function (): void {},
'expected_exports' => array(
'storageLockTTL' => MINUTE_IN_SECONDS,
'extensionModuleUrls' => array(),
'cachePurgePostId' => null,
),
'expected_standard_build' => true,
),
'filtered' => array(
'unfiltered_admin' => array(
'set_up' => static function (): void {
wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
},
'expected_exports' => array(
'storageLockTTL' => 0,
'extensionModuleUrls' => array(),
'cachePurgePostId' => null,
),
'expected_standard_build' => true,
),
'filtered' => array(
'set_up' => static function (): void {
add_filter(
'od_url_metric_storage_lock_ttl',
Expand All @@ -107,11 +119,31 @@ static function ( array $urls ): array {
return $urls;
}
);
add_filter(
'od_minimum_viewport_aspect_ratio',
static function () {
return 0;
}
);
add_filter(
'od_maximum_viewport_aspect_ratio',
static function () {
return 2;
}
);
add_filter( 'od_use_web_vitals_attribution_build', '__return_true' );
add_filter(
'od_url_metric_storage_lock_ttl',
static function () {
return DAY_IN_SECONDS;
}
);
},
'expected_exports' => array(
'storageLockTTL' => HOUR_IN_SECONDS,
'extensionModuleUrls' => array( home_url( '/my-extension.js', 'https' ) ),
'storageLockTTL' => DAY_IN_SECONDS,
'extensionModuleUrls' => array( home_url( '/my-extension.js', 'https' ) ),
'minViewportAspectRatio' => 0,
'maxViewportAspectRatio' => 2,
),
'expected_standard_build' => false,
),
Expand All @@ -123,6 +155,15 @@ static function ( array $urls ): array {
*
* @covers ::od_get_detection_script
* @covers ::od_get_asset_path
* @covers OD_Storage_Lock::get_ttl
* @covers ::od_get_cache_purge_post_id
* @covers ::od_get_minimum_viewport_aspect_ratio
* @covers ::od_get_maximum_viewport_aspect_ratio
* @covers ::od_get_current_url
* @covers ::od_get_url_metrics_storage_hmac
* @covers OD_URL_Metric_Group::get_minimum_viewport_width
* @covers OD_URL_Metric_Group::is_complete
* @covers OD_URL_Metric_Group_Collection::get_current_etag
*
* @dataProvider data_provider_od_get_detection_script
*
Expand All @@ -132,9 +173,21 @@ static function ( array $urls ): array {
*/
public function test_od_get_detection_script_returns_script( Closure $set_up, array $expected_exports, bool $expected_standard_build ): void {
$set_up();
$slug = od_get_url_metrics_slug( array( 'p' => '1' ) );
$slug = od_get_url_metrics_slug( array() );
$current_etag = md5( '' );

$expected_exports = array_merge(
array(
'minViewportAspectRatio' => od_get_minimum_viewport_aspect_ratio(),
'maxViewportAspectRatio' => od_get_maximum_viewport_aspect_ratio(),
'isDebug' => WP_DEBUG,
'currentUrl' => od_get_current_url(),
'urlMetricSlug' => $slug,
'cachePurgePostId' => od_get_cache_purge_post_id(),
),
$expected_exports
);

$breakpoints = array( 480, 600, 782 );
$group_collection = new OD_URL_Metric_Group_Collection( array(), $current_etag, $breakpoints, 3, HOUR_IN_SECONDS );

Expand All @@ -145,6 +198,7 @@ public function test_od_get_detection_script_returns_script( Closure $set_up, ar
foreach ( $expected_exports as $key => $value ) {
$this->assertStringContainsString( sprintf( '%s:%s', wp_json_encode( $key ), wp_json_encode( $value ) ), $script );
}
$this->assertStringContainsString( '"urlMetricHMAC":', $script );
$this->assertSame( 1, preg_match( '/"webVitalsLibrarySrc":("[^"]+?")/', $script, $matches ) );
$web_vitals_library_src = json_decode( $matches[1] );
$this->assertStringContainsString(
Expand Down

0 comments on commit 85ce02b

Please sign in to comment.