Skip to content

Commit

Permalink
Add button into Site Health to reenable CSS transient caching (#4522)
Browse files Browse the repository at this point in the history
* Add button into Site Health to reenable CSS transient caching

* Use wp_jsonencode() instead of addslashes()

Co-Authored-By: Weston Ruter <westonruter@google.com>

* Remove unneeded PHPCS exception

* Use wp.ajax and heredoc for JS script

* Update styling

* Add capability check

* Use current_user_can()

* Add CSS transient caching option to options manager validation

* Fix code style issues

* Only read Option::DISABLE_CSS_TRANSIENT_CACHING if exists

* Remove <script> tags from JS code passed to wp_add_inline_script()

* Remove pesky 2nd blank line

Co-Authored-By: Weston Ruter <westonruter@google.com>

* Rename AjaxAction to ReenableCssTransientCachingAjaxAction

* Remove $access and default to authenticated users only

* Remove $scope and default to admin backend only

* Remove $action and hardcode as const

* Remove $callback and move method into AjaxAction

* Remove $selector and hardcode as const

* Only register AJAX action on site-health.php screen

* Use tabs instead of spaces to indent CSS

* Escape button label translations

* Add nonce verification

* Disable button after click

* Use DOMContentLoaded instead of a timeout

* Indent JS code with tabs instead of spaces

* Document $hook_suffix argument

* Flesh out guidance for CSS transient caching

* Avoid string interpolation for JS injection to better ensure late-escaping

* Use nowdoc instead of heredoc

Co-Authored-By: Alain Schlesser <alain.schlesser@gmail.com>

Co-authored-by: Weston Ruter <westonruter@google.com>
  • Loading branch information
schlessera and westonruter authored Apr 10, 2020
1 parent 1868ea2 commit d1ea42f
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 10 deletions.
8 changes: 8 additions & 0 deletions includes/options/class-amp-options-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* @package AMP
*/

use AmpProject\AmpWP\Option;

/**
* Class AMP_Options_Manager
*/
Expand Down Expand Up @@ -262,6 +264,12 @@ public static function validate_options( $new_options ) {
}
}

if ( array_key_exists( Option::DISABLE_CSS_TRANSIENT_CACHING, $new_options ) && true === $new_options[ Option::DISABLE_CSS_TRANSIENT_CACHING ] ) {
$options[ Option::DISABLE_CSS_TRANSIENT_CACHING ] = true;
} else {
unset( $options[ Option::DISABLE_CSS_TRANSIENT_CACHING ] );
}

// Store the current version with the options so we know the format.
$options['version'] = AMP__VERSION;

Expand Down
126 changes: 126 additions & 0 deletions src/Admin/ReenableCssTransientCachingAjaxAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<?php
/**
* Class ReenableCssTransientCachingAjaxAction.
*
* @package AmpProject\AmpWP
*/

namespace AmpProject\AmpWP\Admin;

use AMP_Options_Manager;
use AmpProject\AmpWP\Option;

/**
* Base class to define a new AJAX action.
*
* @package AmpProject\AmpWP
*/
final class ReenableCssTransientCachingAjaxAction {

/**
* Action to use for enqueueing the JS logic at the backend.
*
* @var string
*/
const BACKEND_ENQUEUE_ACTION = 'admin_enqueue_scripts';

/**
* AJAX action name to use.
*
* @var string
*/
const AJAX_ACTION = 'amp_reenable_css_transient_caching';

/**
* Selector to attach the click handler to.
*
* @var string
*/
const SELECTOR = 'a.reenable-css-transient-caching';

/**
* Register the AJAX action with the WordPress system.
*/
public function register() {
add_action( static::BACKEND_ENQUEUE_ACTION, [ $this, 'register_ajax_script' ] );
add_action( 'wp_ajax_' . self::AJAX_ACTION, [ $this, 'reenable_css_transient_caching' ] );
}

/**
* Register the AJAX logic.
*
* @param string $hook_suffix Hook suffix to identify from what admin page the call is coming from.
*/
public function register_ajax_script( $hook_suffix ) {
if ( 'site-health.php' !== $hook_suffix ) {
return;
}

$script = <<< 'JS_SCRIPT'
;( function () {
window.addEventListener( 'DOMContentLoaded', ( event ) => {
var selector = SELECTOR;
( document.querySelectorAll( selector ) || [] )
.forEach( ( element ) => {
element.addEventListener( 'click', function ( event ) {
event.preventDefault();
if ( element.classList.contains( 'disabled' ) ) {
return;
}
wp.ajax.post( ACTION, ARGUMENTS )
.done( function () {
element.classList.remove( 'ajax-failure' );
element.classList.add( 'ajax-success' )
element.classList.add( 'disabled' )
} )
.fail( function () {
element.classList.remove( 'ajax-success' );
element.classList.add( 'ajax-failure' )
element.classList.add( 'disabled' )
} );
} );
} );
} );
} )();
JS_SCRIPT;

$replacements = array_map(
'wp_json_encode',
[
'SELECTOR' => self::SELECTOR,
'ACTION' => self::AJAX_ACTION,
'ARGUMENTS' => [ 'nonce' => wp_create_nonce( self::AJAX_ACTION ) ],
]
);

$script = str_replace(
array_keys( $replacements ),
array_values( $replacements ),
$script
);

wp_enqueue_script( 'wp-util' );
wp_add_inline_script( 'wp-util', $script );
}

/**
* Re-enable the CSS Transient caching.
*
* This is triggered via an AJAX call from the Site Health panel.
*/
public function reenable_css_transient_caching() {
check_ajax_referer( self::AJAX_ACTION, 'nonce' );

if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( 'Unauthorized.', 401 );
}

$result = AMP_Options_Manager::update_option( Option::DISABLE_CSS_TRANSIENT_CACHING, false );

if ( false === $result ) {
wp_send_json_error( 'CSS transient caching could not be re-enabled.', 500 );
}

wp_send_json_success( 'CSS transient caching was re-enabled.', 200 );
}
}
95 changes: 85 additions & 10 deletions src/Admin/SiteHealth.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public function init() {
add_filter( 'site_status_tests', [ $this, 'add_tests' ] );
add_filter( 'debug_information', [ $this, 'add_debug_information' ] );
add_filter( 'site_status_test_php_modules', [ $this, 'add_extensions' ] );
add_action( 'admin_print_styles', [ $this, 'add_styles' ] );

( new ReenableCssTransientCachingAjaxAction() )->register();
}

/**
Expand Down Expand Up @@ -62,6 +65,21 @@ public function add_tests( $tests ) {
return $tests;
}

/**
* Get action HTML for the link to learn more about persistent object caching.
*
* @return string HTML.
*/
private function get_persistent_object_cache_learn_more_action() {
return sprintf(
'<p><a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s <span class="screen-reader-text">%3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>',
esc_url( 'https://make.wordpress.org/hosting/handbook/handbook/performance/#object-cache' ),
esc_html__( 'Learn more about persistent object caching', 'amp' ),
/* translators: The accessibility text. */
esc_html__( '(opens in a new tab)', 'amp' )
);
}

/**
* Gets the test result data for whether there is a persistent object cache.
*
Expand All @@ -75,13 +93,7 @@ public function persistent_object_cache() {
'color' => $is_using_object_cache ? 'green' : 'orange',
],
'description' => esc_html__( 'The AMP plugin performs at its best when persistent object cache is enabled. Object caching is used to more effectively store image dimensions and parsed CSS.', 'amp' ),
'actions' => sprintf(
'<p><a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s <span class="screen-reader-text">%3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></p>',
'https://codex.wordpress.org/Class_Reference/WP_Object_Cache#Persistent_Caching',
esc_html__( 'Learn more about persistent object caching', 'amp' ),
/* translators: The accessibility text. */
esc_html__( '(opens in a new tab)', 'amp' )
),
'actions' => $this->get_persistent_object_cache_learn_more_action(),
'test' => 'amp_persistent_object_cache',
];

Expand Down Expand Up @@ -240,11 +252,13 @@ public function icu_version() {
* @return array The test data.
*/
public function css_transient_caching() {
$disabled = AMP_Options_Manager::get_option( Option::DISABLE_CSS_TRANSIENT_CACHING, false );

if ( wp_using_ext_object_cache() ) {
$status = 'good';
$color = 'blue';
$label = __( 'Transient caching of parsed stylesheets is not used due to external object cache', 'amp' );
} elseif ( AMP_Options_Manager::get_option( Option::DISABLE_CSS_TRANSIENT_CACHING, false ) ) {
} elseif ( $disabled ) {
$status = 'recommended';
$color = 'orange';
$label = __( 'Transient caching of parsed stylesheets is disabled', 'amp' );
Expand All @@ -254,16 +268,40 @@ public function css_transient_caching() {
$label = __( 'Transient caching of parsed stylesheets is enabled', 'amp' );
}

return [
$data = [
'badge' => [
'label' => esc_html__( 'AMP', 'amp' ),
'color' => $color,
],
'description' => esc_html__( 'On sites which have highly variable CSS and are not using a persistent object cache, the transient caching of parsed stylesheets may be automatically disabled in order to prevent a site from filling up its wp_options table with too many transients.', 'amp' ),
'description' => wp_kses(
sprintf(
/* translators: %1$s is wp_options table, %2$s is the amp_css_transient_monitoring_threshold filter, and %3$s is the amp_css_transient_monitoring_sampling_range filter */
__( 'On sites which have highly variable CSS and are not using a persistent object cache, the transient caching of parsed stylesheets may be automatically disabled in order to prevent a site from filling up its <code>%1$s</code> table with too many transients. Examples of highly variable CSS include dynamically-generated style rules with selectors referring to user IDs or elements being given randomized background colors. There are two filters which may be used to configure the CSS transient monitoring: <code>%2$s</code> and <code>%3$s</code>.', 'amp' ),
'wp_options',
'amp_css_transient_monitoring_threshold',
'amp_css_transient_monitoring_sampling_range'
),
[
'code' => [],
]
),
'test' => 'amp_css_transient_caching',
'status' => $status,
'label' => esc_html( $label ),
];

if ( $disabled ) {
$data['description'] .= ' ' . esc_html__( 'If you have identified and eliminated the cause of the variable CSS, please re-enable transient caching to reduce the amount of CSS processing required to generate AMP pages.', 'amp' );
$data['actions'] = sprintf(
'<p><a class="button reenable-css-transient-caching" href="#">%s</a><span class="dashicons dashicons-yes success-icon"></span><span class="dashicons dashicons-no failure-icon"></span><span class="success-text">%s</span><span class="failure-text">%s</span></p>',
esc_html__( 'Re-enable transient caching', 'amp' ),
esc_html__( 'Reload the page to refresh the diagnostic check.', 'amp' ),
esc_html__( 'The operation failed, please reload the page and try again.', 'amp' )
);
$data['actions'] .= $this->get_persistent_object_cache_learn_more_action();
}

return $data;
}

/**
Expand Down Expand Up @@ -502,4 +540,41 @@ public function add_extensions( $extensions ) {
]
);
}

/**
* Add needed styles for the Site Health integration.
*/
public function add_styles() {
echo '
<style>
.wp-core-ui .button.reenable-css-transient-caching ~ .success-icon,
.wp-core-ui .button.reenable-css-transient-caching ~ .success-text,
.wp-core-ui .button.reenable-css-transient-caching ~ .failure-icon,
.wp-core-ui .button.reenable-css-transient-caching ~ .failure-text {
display: none;
}
.wp-core-ui .button.reenable-css-transient-caching ~ .success-icon,
.wp-core-ui .button.reenable-css-transient-caching ~ .failure-icon {
font-size: xx-large;
padding-right: 1rem;
}
.wp-core-ui .button.reenable-css-transient-caching.ajax-success ~ .success-icon,
.wp-core-ui .button.reenable-css-transient-caching.ajax-success ~ .success-text,
.wp-core-ui .button.reenable-css-transient-caching.ajax-failure ~ .failure-icon,
.wp-core-ui .button.reenable-css-transient-caching.ajax-failure ~ .failure-text {
display: inline-block;
}
.wp-core-ui .button.reenable-css-transient-caching.ajax-success ~ .success-icon {
color: #46b450;
}
.wp-core-ui .button.reenable-css-transient-caching.ajax-failure ~ .failure-icon {
color: #dc3232;
}
</style>
';
}
}

0 comments on commit d1ea42f

Please sign in to comment.