initial
This commit is contained in:
+54
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS;
|
||||
|
||||
class Admin_Bar_Compatibility {
|
||||
|
||||
/**
|
||||
* Enforces the admin bar stylesheet to load late and synchronously
|
||||
* when the admin bar is present on the page.
|
||||
*/
|
||||
public static function init() {
|
||||
|
||||
// Force the Admin Bar to render in the footer.
|
||||
remove_action( 'wp_body_open', 'wp_admin_bar_render', '0' );
|
||||
|
||||
add_filter( 'jetpack_boost_async_style', array( __CLASS__, 'enable_asynchronous_admin_bar' ), 10, 2 );
|
||||
add_action( 'wp_before_admin_bar_render', array( __CLASS__, 'force_admin_bar_stylesheet' ) );
|
||||
add_action( 'wp_head', array( __CLASS__, 'dequeue_admin_bar' ), 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the admin bar CSS synchronously.
|
||||
*
|
||||
* @param bool $is_async Whether admin bar is async.
|
||||
* @param string $handle Asset handle.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function enable_asynchronous_admin_bar( $is_async, $handle ) {
|
||||
|
||||
if ( 'admin-bar' === $handle ) {
|
||||
$is_async = false;
|
||||
}
|
||||
|
||||
return $is_async;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dequeue the admin bar stylesheet, so that it's not printed early.
|
||||
*
|
||||
* @see wp_head
|
||||
*/
|
||||
public static function dequeue_admin_bar() {
|
||||
wp_dequeue_style( 'admin-bar' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the admin bar stylesheet to print right before the admin bar markup.
|
||||
*
|
||||
* @see wp_before_admin_bar_render
|
||||
*/
|
||||
public static function force_admin_bar_stylesheet() {
|
||||
wp_print_styles( 'admin-bar' );
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* Critical CSS Invalidator
|
||||
*
|
||||
* Reset critical CSS when existing critical css values are stale.
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS;
|
||||
|
||||
use Automattic\Jetpack_Boost\Admin\Regenerate_Admin_Notice;
|
||||
use Automattic\Jetpack_Boost\Lib\Boost_Health;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Source_Providers;
|
||||
use Automattic\Jetpack_Boost\Modules\Modules_Setup;
|
||||
use Automattic\Jetpack_Boost\Modules\Optimizations\Cloud_CSS\Cloud_CSS;
|
||||
use Automattic\Jetpack_Boost\Modules\Optimizations\Cloud_CSS\Cloud_CSS_Followup;
|
||||
|
||||
/**
|
||||
* Handler for invalidating Critical CSS; both Cloud and Local. Watches relevant
|
||||
* hooks and clears data when necessary. Also sends out its own action
|
||||
* (after_critical_css_invalidate) so that the Cloud or Local implementations of
|
||||
* Critical CSS can respond to the invalidation.
|
||||
*/
|
||||
class Critical_CSS_Invalidator {
|
||||
public static function init() {
|
||||
add_action( 'jetpack_boost_deactivate', array( __CLASS__, 'reset_data' ) );
|
||||
add_action( 'jetpack_boost_environment_changed', array( __CLASS__, 'handle_environment_change' ) );
|
||||
add_filter( 'jetpack_boost_total_problem_count', array( __CLASS__, 'update_boost_problem_count' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Critical CSS data.
|
||||
* For Cloud CSS, we need to make sure there are providers in the state,
|
||||
* otherwise Cloud CSS cannot be stored after it is generated by the cloud.
|
||||
*/
|
||||
public static function reset_data() {
|
||||
$storage = new Critical_CSS_Storage();
|
||||
$storage->clear();
|
||||
|
||||
$state = new Critical_CSS_State();
|
||||
$state->clear();
|
||||
|
||||
if ( self::is_cloud_css() ) {
|
||||
$state->prepare_for_generation( ( new Source_Providers() )->get_provider_sources() );
|
||||
$state->save();
|
||||
|
||||
// Clear the regenerate flag so things are nice and tidy.
|
||||
jetpack_boost_ds_delete( 'critical_css_suggest_regenerate' );
|
||||
// Also clear the admin notice flag, so the notice doesn't show up in case
|
||||
// the user is reverted to free Boost.
|
||||
Regenerate_Admin_Notice::dismiss();
|
||||
|
||||
}
|
||||
|
||||
Cloud_CSS_Followup::unschedule();
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to environment changes; deciding whether or not to clear Critical CSS data.
|
||||
*/
|
||||
public static function handle_environment_change( $is_major_change ) {
|
||||
if ( $is_major_change ) {
|
||||
self::reset_data();
|
||||
|
||||
/**
|
||||
* Indicate that all existing critical CSS has been invalidated.
|
||||
*/
|
||||
do_action( 'jetpack_boost_critical_css_invalidated' );
|
||||
}
|
||||
}
|
||||
|
||||
public static function update_boost_problem_count( $count ) {
|
||||
$css_needs_regeneration = Boost_Health::critical_css_needs_regeneration();
|
||||
if ( $css_needs_regeneration ) {
|
||||
++$count;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
public static function is_cloud_css() {
|
||||
$optimizations = ( new Modules_Setup() )->get_status();
|
||||
return isset( $optimizations[ Cloud_CSS::get_slug() ] ) && $optimizations[ Cloud_CSS::get_slug() ];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS;
|
||||
|
||||
use WP_Error;
|
||||
|
||||
class Critical_CSS_State {
|
||||
|
||||
const GENERATION_STATES = array(
|
||||
'not_generated' => 'not_generated',
|
||||
'pending' => 'pending',
|
||||
'generated' => 'generated',
|
||||
'error' => 'error',
|
||||
);
|
||||
|
||||
const PROVIDER_STATES = array(
|
||||
'pending' => 'pending',
|
||||
'success' => 'success',
|
||||
'error' => 'error',
|
||||
);
|
||||
public $state;
|
||||
|
||||
public function __construct() {
|
||||
$this->state = jetpack_boost_ds_get( 'critical_css_state' );
|
||||
}
|
||||
|
||||
public function clear() {
|
||||
jetpack_boost_ds_delete( 'critical_css_state' );
|
||||
}
|
||||
|
||||
public function save() {
|
||||
$this->state['updated'] = microtime( true );
|
||||
jetpack_boost_ds_set( 'critical_css_state', $this->state );
|
||||
|
||||
if ( $this->is_generated() ) {
|
||||
/**
|
||||
* Fires when critical CSS has successfully been generated.
|
||||
*/
|
||||
do_action( 'jetpack_boost_critical_css_generated' );
|
||||
}
|
||||
}
|
||||
|
||||
public function set_error( $message ) {
|
||||
if ( empty( $message ) ) {
|
||||
error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
|
||||
'Critical CSS: set_error() called with empty message'
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->state['status_error'] = $message;
|
||||
$this->state['status'] = self::GENERATION_STATES['error'];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function set_provider_error_dismissed( $provider_key, $error_type, $dismissed ) {
|
||||
if ( empty( $this->state['providers'] ) ) {
|
||||
return new WP_Error( 'invalid_provider_key', 'No providers exist' );
|
||||
}
|
||||
|
||||
$provider_index = array_search( $provider_key, array_column( $this->state['providers'], 'key' ), true );
|
||||
if ( $provider_index === false ) {
|
||||
return new WP_Error( 'invalid_provider_key', 'Invalid provider key' );
|
||||
}
|
||||
|
||||
if ( ! isset( $this->state['providers'][ $provider_index ]['dismissed_errors'] ) ) {
|
||||
$this->state['providers'][ $provider_index ]['dismissed_errors'] = array();
|
||||
}
|
||||
|
||||
if ( $dismissed ) {
|
||||
if ( ! in_array( $error_type, $this->state['providers'][ $provider_index ]['dismissed_errors'], true ) ) {
|
||||
$this->state['providers'][ $provider_index ]['dismissed_errors'][] = $error_type;
|
||||
}
|
||||
} else {
|
||||
$key = array_search( $error_type, $this->state['providers'][ $provider_index ]['dismissed_errors'], true );
|
||||
if ( $key !== false ) {
|
||||
unset( $this->state['providers'][ $provider_index ]['dismissed_errors'][ $key ] );
|
||||
$this->state['providers'][ $provider_index ]['dismissed_errors'] = array_values( $this->state['providers'][ $provider_index ]['dismissed_errors'] );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a provider's state. The provider must already exist in the state to be updated.
|
||||
*
|
||||
* @param string $provider_key The provider key.
|
||||
* @param array $state An array to overlay over the current state.
|
||||
* @return bool|WP_Error True on success, WP_Error on failure.
|
||||
*/
|
||||
private function update_provider_state( $provider_key, $state ) {
|
||||
if ( empty( $this->state['providers'] ) ) {
|
||||
return new WP_Error( 'invalid_provider_key', 'No providers exist' );
|
||||
}
|
||||
|
||||
$provider_index = array_search( $provider_key, array_column( $this->state['providers'], 'key' ), true );
|
||||
if ( $provider_index === false ) {
|
||||
return new WP_Error( 'invalid_provider_key', 'Invalid provider key' );
|
||||
}
|
||||
|
||||
$this->state['providers'][ $provider_index ] = array_merge(
|
||||
$this->state['providers'][ $provider_index ],
|
||||
$state
|
||||
);
|
||||
|
||||
$this->maybe_set_generated();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a provider's state to error.
|
||||
*
|
||||
* @param string $provider_key The provider key.
|
||||
* @param array $errors A list of errors to store with this provider.
|
||||
* @return bool|WP_Error True on success, WP_Error on failure.
|
||||
*/
|
||||
public function set_provider_errors( $provider_key, $errors ) {
|
||||
return $this->update_provider_state(
|
||||
$provider_key,
|
||||
array(
|
||||
'status' => self::PROVIDER_STATES['error'],
|
||||
'errors' => $errors,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a provider's state to success.
|
||||
*
|
||||
* @param string $provider_key The provider key.
|
||||
* @return bool|WP_Error True on success, WP_Error on failure.
|
||||
*/
|
||||
public function set_provider_success( $provider_key ) {
|
||||
return $this->update_provider_state(
|
||||
$provider_key,
|
||||
array(
|
||||
'status' => self::PROVIDER_STATES['success'],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state to generated if all providers are done. Should be called wherever
|
||||
* a provider's state is updated.
|
||||
*/
|
||||
private function maybe_set_generated() {
|
||||
if ( empty( $this->state['providers'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$provider_states = array_column( $this->state['providers'], 'status' );
|
||||
$is_done = ! in_array( self::GENERATION_STATES['pending'], $provider_states, true );
|
||||
|
||||
if ( $is_done ) {
|
||||
$this->state['status'] = self::GENERATION_STATES['generated'];
|
||||
}
|
||||
}
|
||||
|
||||
public function has_errors() {
|
||||
// Check if any of the providers have errors as well.
|
||||
$any_provider_has_error = in_array(
|
||||
'error',
|
||||
array_unique(
|
||||
wp_list_pluck(
|
||||
$this->state['providers'],
|
||||
'status'
|
||||
)
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
return self::GENERATION_STATES['error'] === $this->state['status'] || $any_provider_has_error;
|
||||
}
|
||||
|
||||
public function get_error_message() {
|
||||
return $this->state['status_error'] ?? null;
|
||||
}
|
||||
|
||||
public function is_generated() {
|
||||
return self::GENERATION_STATES['generated'] === $this->state['status'];
|
||||
}
|
||||
|
||||
public function is_requesting() {
|
||||
return self::GENERATION_STATES['pending'] === $this->state['status'];
|
||||
}
|
||||
|
||||
public function prepare_request() {
|
||||
$this->state = array(
|
||||
'status' => self::GENERATION_STATES['pending'],
|
||||
'providers' => array(),
|
||||
'created' => microtime( true ),
|
||||
'updated' => microtime( true ),
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function set_pending_providers( $providers ) {
|
||||
foreach ( $providers as $key => $provider ) {
|
||||
$providers[ $key ]['status'] = self::PROVIDER_STATES['pending'];
|
||||
}
|
||||
$this->state['providers'] = $providers;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add providers to the state, sets their status to pending
|
||||
* and sets the generation status to pending.
|
||||
*
|
||||
* @param array $providers The providers to include in the state and set as pending.
|
||||
* @return $this
|
||||
*/
|
||||
public function prepare_for_generation( $providers ) {
|
||||
$this->set_pending_providers( $providers );
|
||||
$this->state['status'] = self::GENERATION_STATES['pending'];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fresh state
|
||||
*/
|
||||
public function get() {
|
||||
$this->state = jetpack_boost_ds_get( 'critical_css_state' );
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
public function has_pending_provider( $needles = array() ) {
|
||||
if ( empty( $this->state['providers'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$providers = $this->state['providers'];
|
||||
foreach ( $providers as $provider ) {
|
||||
if (
|
||||
! empty( $provider['key'] )
|
||||
&& ! empty( $provider['status'] )
|
||||
&& self::PROVIDER_STATES['pending'] === $provider['status']
|
||||
&& in_array( $provider['key'], $needles, true )
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* Critical CSS storage.
|
||||
*
|
||||
* @link https://automattic.com
|
||||
* @since 1.0.0
|
||||
* @package automattic/jetpack-boost
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS;
|
||||
|
||||
use Automattic\Jetpack_Boost\Lib\Storage_Post_Type;
|
||||
|
||||
/**
|
||||
* Critical CSS Storage class
|
||||
*/
|
||||
class Critical_CSS_Storage {
|
||||
|
||||
/**
|
||||
* Storage post type.
|
||||
*
|
||||
* @var Storage_Post_Type
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/**
|
||||
* Critical_CSS_Storage constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->storage = new Storage_Post_Type( 'css' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Store Critical CSS for a specific provider.
|
||||
*
|
||||
* @param string $key Provider key.
|
||||
* @param string $value Critical CSS.
|
||||
*/
|
||||
public function store_css( $key, $value ) {
|
||||
$this->storage->set(
|
||||
$key,
|
||||
array(
|
||||
'css' => $value,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the whole Critical CSS storage.
|
||||
*/
|
||||
public function clear() {
|
||||
$this->storage->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Critical CSS for specific provider keys.
|
||||
*
|
||||
* @param array $provider_keys Provider keys.
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public function get_css( $provider_keys ) {
|
||||
foreach ( $provider_keys as $key ) {
|
||||
$data = $this->storage->get( $key, false );
|
||||
if ( $data && $data['css'] ) {
|
||||
return array(
|
||||
'key' => $key,
|
||||
'css' => $data['css'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
/**
|
||||
* Class that's responsible for rendering
|
||||
* Critical CSS on the site front-end.
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS;
|
||||
|
||||
class Display_Critical_CSS {
|
||||
|
||||
/**
|
||||
* @var string The Critical CSS to display.
|
||||
*/
|
||||
protected $css;
|
||||
|
||||
/**
|
||||
* @param string $css
|
||||
*/
|
||||
public function __construct( $css ) {
|
||||
$this->css = $css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts existing screen CSS to be asynchronously loaded.
|
||||
*
|
||||
* @param string $html The link tag for the enqueued style.
|
||||
* @param string $handle The style's registered handle.
|
||||
* @param string $href The stylesheet's source URL.
|
||||
* @param string $media The stylesheet's media attribute.
|
||||
*
|
||||
* @return string
|
||||
* @see style_loader_tag
|
||||
*/
|
||||
public function asynchronize_stylesheets(
|
||||
$html,
|
||||
$handle,
|
||||
$href,
|
||||
$media
|
||||
) {
|
||||
// If there is no critical CSS, do not alter the stylesheet loading.
|
||||
if ( ! $this->css ) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
$supported_loading_methods = array( 'async', 'deferred' );
|
||||
|
||||
/**
|
||||
* Loading method for stylesheets.
|
||||
*
|
||||
* Filter the loading method for each stylesheet for the screen with following values:
|
||||
* async - Stylesheets are loaded asynchronously.
|
||||
* Styles are applied once the stylesheet is loaded completely without render blocking.
|
||||
* deferred - Loading of stylesheets are deferred until the window load event.
|
||||
* Styles from all the stylesheets are applied at once after the page load.
|
||||
*
|
||||
* Stylesheet loading behaviour is not altered for any other value such as false or 'default'.
|
||||
* Stylesheet loading is instant and the process blocks the page rendering.
|
||||
* Eg: add_filter( 'jetpack_boost_async_style', '__return_false' );
|
||||
*
|
||||
* @param string $handle The style's registered handle.
|
||||
* @param string $media The stylesheet's media attribute.
|
||||
*
|
||||
* @see onload_flip_stylesheets for how stylesheets loading is deferred.
|
||||
*
|
||||
* @todo Retrieve settings from database, either via auto-configuration or UI option.
|
||||
*/
|
||||
$method = apply_filters( 'jetpack_boost_async_style', 'async', $handle, $media );
|
||||
|
||||
// If the loading method is not supported, do not alter the stylesheet loading.
|
||||
if ( ! in_array( $method, $supported_loading_methods, true ) ) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
// Update the stylesheet markup for supported loading methods using WordPress HTML API.
|
||||
$processor = new \WP_HTML_Tag_Processor( $html );
|
||||
if ( ! $processor->next_tag( 'link' ) ) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
// Only process if this is a stylesheet link tag.
|
||||
if ( 'stylesheet' !== $processor->get_attribute( 'rel' ) ) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
// Set the new attributes based on the selected method.
|
||||
$processor->set_attribute( 'media', 'not all' );
|
||||
$processor->set_attribute( 'data-media', $media );
|
||||
if ( 'async' === $method ) {
|
||||
$processor->set_attribute( 'onload', "this.media=this.dataset.media; delete this.dataset.media; this.removeAttribute( 'onload' );" );
|
||||
}
|
||||
|
||||
// Prepend the original HTML stylesheet tag within the noscript tag
|
||||
// to support the rendering of the stylesheet when JavaScript is disabled.
|
||||
return '<noscript>' . $html . '</noscript>' . $processor->get_updated_html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the critical CSS to the page.
|
||||
*/
|
||||
public function display_critical_css() {
|
||||
$critical_css = $this->css;
|
||||
|
||||
if ( ! $critical_css ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
echo '<style id="jetpack-boost-critical-css">';
|
||||
|
||||
// Ensure the CSS cannot terminate the style element early.
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo self::neutralize_style_closing_tags( $critical_css );
|
||||
|
||||
echo '</style>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Neutralize any closing </style tag so CSS can be printed inside a <style>
|
||||
* element without breaking out of it.
|
||||
*
|
||||
* This is NOT a general-purpose CSS sanitizer: it does exactly one thing,
|
||||
* which is to stop the CSS from terminating the surrounding <style> element.
|
||||
* It deliberately avoids wp_strip_all_tags(), which corrupts valid CSS values
|
||||
* that contain markup - e.g. `background-image: url("data:image/svg+xml,<svg ...></svg>")`.
|
||||
*
|
||||
* Per the HTML rawtext tokenizer, a <style> element can only be terminated by
|
||||
* the literal sequence `</style` (case-insensitive). Escaping its forward slash
|
||||
* to `<\/style` defeats the tokenizer - `</` must be immediately followed by the
|
||||
* tag name, and `<\` is treated as literal text - so the markup stays inert. The
|
||||
* replacement string contains no `</style` substring, so a single left-to-right
|
||||
* pass cannot reconstruct the sequence (including from nested input like
|
||||
* `<</style/style`), and the transform is idempotent.
|
||||
*
|
||||
* Inside CSS strings and url() tokens `\/` is a valid escape for `/`, so
|
||||
* legitimate quoted values keep their meaning. (A literal `</style` outside a
|
||||
* quoted string does not occur in well-formed CSS; the security guarantee takes
|
||||
* priority there regardless.)
|
||||
*
|
||||
* @param string $css CSS to neutralize.
|
||||
* @return string CSS that cannot terminate the surrounding <style> element.
|
||||
*/
|
||||
public static function neutralize_style_closing_tags( $css ) {
|
||||
return str_ireplace( '</style', '<\/style', $css );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a small piece of JavaScript to the footer, which on load flips all
|
||||
* linked stylesheets from media="not all" to "all", and switches the
|
||||
* Critical CSS <style> block to media="not all" to deactivate it.
|
||||
*/
|
||||
public function onload_flip_stylesheets() {
|
||||
/*
|
||||
Unminified version of footer script.
|
||||
|
||||
?>
|
||||
<script>
|
||||
window.addEventListener( 'load', function() {
|
||||
|
||||
// Flip all media="not all" links to media="all".
|
||||
document.querySelectorAll( 'link' ).forEach(
|
||||
function( link ) {
|
||||
if ( link.media === 'not all' && link.dataset.media ) {
|
||||
link.media = link.dataset.media;
|
||||
delete link.dataset.media;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Turn off Critical CSS style block with media="not all".
|
||||
var element = document.getElementById( 'jetpack-boost-critical-css' );
|
||||
if ( element ) {
|
||||
element.media = 'not all';
|
||||
}
|
||||
|
||||
} );
|
||||
</script>
|
||||
<?php
|
||||
*/
|
||||
|
||||
// Minified version of footer script. See above comment for unminified version.
|
||||
?>
|
||||
<script>window.addEventListener( 'load', function() {
|
||||
document.querySelectorAll( 'link' ).forEach( function( e ) {'not all' === e.media && e.dataset.media && ( e.media = e.dataset.media, delete e.dataset.media );} );
|
||||
var e = document.getElementById( 'jetpack-boost-critical-css' );
|
||||
e && ( e.media = 'not all' );
|
||||
} );</script>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS;
|
||||
|
||||
use Automattic\Jetpack_Boost\Modules\Optimizations\Critical_CSS\CSS_Proxy;
|
||||
|
||||
class Generator {
|
||||
|
||||
const GENERATE_QUERY_ACTION = 'jb-generate-critical-css';
|
||||
|
||||
public static function init() {
|
||||
$generator = new static();
|
||||
if ( static::is_generating_critical_css() ) {
|
||||
add_action( 'wp_head', array( $generator, 'display_generate_meta' ), 0 );
|
||||
$generator->force_logged_out_render();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the current page to render as viewed by a logged out user. Useful when generating
|
||||
* Critical CSS.
|
||||
*/
|
||||
private function force_logged_out_render() {
|
||||
$current_user_id = get_current_user_id();
|
||||
|
||||
if ( 0 !== $current_user_id ) {
|
||||
// Force current user to 0 to ensure page is rendered as a non-logged-in user.
|
||||
wp_set_current_user( 0 );
|
||||
|
||||
// Turn off display of admin bar.
|
||||
add_filter( 'show_admin_bar', '__return_false', PHP_INT_MAX );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if page is loaded to generate critical CSS
|
||||
*
|
||||
* phpcs:disable WordPress.Security.NonceVerification.Recommended
|
||||
*/
|
||||
public static function is_generating_critical_css() {
|
||||
return isset( $_GET[ self::GENERATE_QUERY_ACTION ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Critical CSS status block, adding in local generation nonces (if applicable).
|
||||
* i.e.: Call this method to supply enough Critical CSS status to kick off local generation,
|
||||
* such as in response to a request-generate API call or during page initialization.
|
||||
*/
|
||||
public function get_generation_metadata() {
|
||||
$status = array();
|
||||
|
||||
// Add a user-bound nonce to use when proxying CSS for Critical CSS generation.
|
||||
$status['proxy_nonce'] = wp_create_nonce( CSS_Proxy::NONCE_ACTION );
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a <meta> tag used to verify this is a valid page to generate Critical CSS with.
|
||||
*/
|
||||
public function display_generate_meta() {
|
||||
?>
|
||||
<meta name="<?php echo esc_attr( self::GENERATE_QUERY_ACTION ); ?>" content="true"/>
|
||||
<?php
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS;
|
||||
|
||||
use Automattic\Jetpack_Boost\Admin\Regenerate_Admin_Notice;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Source_Providers;
|
||||
use Automattic\Jetpack_Boost\Modules\Modules_Setup;
|
||||
use Automattic\Jetpack_Boost\Modules\Optimizations\Cloud_CSS\Cloud_CSS;
|
||||
use Automattic\Jetpack_Boost\Modules\Optimizations\Cloud_CSS\Cloud_CSS_Followup;
|
||||
|
||||
class Regenerate {
|
||||
/** @var Critical_CSS_State */
|
||||
private $state;
|
||||
|
||||
public function is_cloud_css() {
|
||||
$optimizations = ( new Modules_Setup() )->get_status();
|
||||
return isset( $optimizations[ Cloud_CSS::get_slug() ] ) && $optimizations[ Cloud_CSS::get_slug() ];
|
||||
}
|
||||
|
||||
public function start() {
|
||||
// Get Critical CSS Source URLs
|
||||
$source_providers = new Source_Providers();
|
||||
$providers = $source_providers->get_provider_sources();
|
||||
|
||||
// Store those URLs in the Critical CSS State
|
||||
$this->state = new Critical_CSS_State();
|
||||
$this->state->prepare_request()
|
||||
->set_pending_providers( $providers )
|
||||
->save();
|
||||
|
||||
// Get the data
|
||||
$data = $this->state->get();
|
||||
|
||||
if ( $this->is_cloud_css() ) {
|
||||
// If this is a cloud CSS request, we need to trigger the generation
|
||||
// of the CSS and return the URL to the CSS file.
|
||||
$cloud_css = new Cloud_CSS();
|
||||
$cloud_css->regenerate_cloud_css( Cloud_CSS::REGENERATE_REASON_USER_REQUEST, $cloud_css->get_all_providers() );
|
||||
Cloud_CSS_Followup::schedule();
|
||||
}
|
||||
|
||||
// Clear previous Critical CSS From storage
|
||||
$storage = new Critical_CSS_Storage();
|
||||
$storage->clear();
|
||||
|
||||
// Dismiss admin notices
|
||||
Regenerate_Admin_Notice::dismiss();
|
||||
jetpack_boost_ds_delete( 'critical_css_suggest_regenerate' );
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function get_state() {
|
||||
return $this->state;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Data_Sync_Actions;
|
||||
|
||||
use Automattic\Jetpack\WP_JS_Data_Sync\Contracts\Data_Sync_Action;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Regenerate;
|
||||
|
||||
/**
|
||||
* Critical CSS Action: request regeneration.
|
||||
*/
|
||||
class Regenerate_CSS implements Data_Sync_Action {
|
||||
|
||||
/**
|
||||
* Handles the action logic.
|
||||
*
|
||||
* @param mixed $_data JSON Data passed to the action.
|
||||
* @param \WP_REST_Request $_request The request object.
|
||||
*/
|
||||
public function handle( $_data, $_request ) {
|
||||
$regenerate = new Regenerate();
|
||||
$regenerate->start();
|
||||
|
||||
$state = $regenerate->get_state();
|
||||
|
||||
return array(
|
||||
'success' => ! $state->has_errors(),
|
||||
'state' => $state->get(),
|
||||
'errors' => $state->get_error_message(),
|
||||
);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Data_Sync_Actions;
|
||||
|
||||
use Automattic\Jetpack\WP_JS_Data_Sync\Contracts\Data_Sync_Action;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Critical_CSS_State;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Critical_CSS_Storage;
|
||||
|
||||
/**
|
||||
* Critical CSS Action: Set CSS for a provider.
|
||||
*/
|
||||
class Set_Provider_CSS implements Data_Sync_Action {
|
||||
|
||||
/**
|
||||
* Handles the action logic.
|
||||
*
|
||||
* @param mixed $data JSON Data passed to the action.
|
||||
* @param \WP_REST_Request $_request The request object.
|
||||
*/
|
||||
public function handle( $data, $_request ) {
|
||||
$state = new Critical_CSS_State();
|
||||
|
||||
if ( empty( $data['key'] ) || empty( $data['css'] ) ) {
|
||||
return array(
|
||||
'success' => false,
|
||||
'state' => $state->get(),
|
||||
'error' => 'Invalid data',
|
||||
);
|
||||
}
|
||||
|
||||
$provider_key = sanitize_key( $data['key'] );
|
||||
$css = $this->unmask_content( $data['css'] );
|
||||
|
||||
$storage = new Critical_CSS_Storage();
|
||||
$storage->store_css( $provider_key, $css );
|
||||
|
||||
$state->set_provider_success( $provider_key );
|
||||
$state->save();
|
||||
|
||||
return array(
|
||||
'success' => true,
|
||||
'state' => $state->get(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmasks the content.
|
||||
*
|
||||
* @param string $content The content to unmask.
|
||||
* @return string The unmasked content.
|
||||
*/
|
||||
private function unmask_content( $content ) {
|
||||
return str_replace( '__JB_XMLNS__', 'xmlns', $content );
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Data_Sync_Actions;
|
||||
|
||||
use Automattic\Jetpack\WP_JS_Data_Sync\Contracts\Data_Sync_Action;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Critical_CSS_State;
|
||||
|
||||
/**
|
||||
* Critical CSS Action: Update whether or not to show a provider which is in an error state.
|
||||
*/
|
||||
class Set_Provider_Error_Dismissed implements Data_Sync_Action {
|
||||
/**
|
||||
* Handles the action logic.
|
||||
*
|
||||
* @param mixed $data JSON Data passed to the action.
|
||||
* @param \WP_REST_Request $_request The request object.
|
||||
*/
|
||||
public function handle( $data, $_request ) {
|
||||
$state = new Critical_CSS_State();
|
||||
|
||||
foreach ( $data as $item ) {
|
||||
if ( empty( $item['provider'] ) ) {
|
||||
return array(
|
||||
'success' => false,
|
||||
'state' => $state->get(),
|
||||
'error' => 'Invalid data',
|
||||
);
|
||||
}
|
||||
|
||||
$provider_key = sanitize_key( $item['provider'] );
|
||||
$dismissed = ! empty( $item['dismissed'] );
|
||||
|
||||
$state->set_provider_error_dismissed( $provider_key, $item['error_type'], $dismissed );
|
||||
}
|
||||
|
||||
$state->save();
|
||||
|
||||
return array(
|
||||
'success' => true,
|
||||
'state' => $state->get(),
|
||||
);
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Data_Sync_Actions;
|
||||
|
||||
use Automattic\Jetpack\WP_JS_Data_Sync\Contracts\Data_Sync_Action;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Critical_CSS_State;
|
||||
|
||||
/**
|
||||
* Critical CSS Action: Store errors for a provider.
|
||||
*/
|
||||
class Set_Provider_Errors implements Data_Sync_Action {
|
||||
/**
|
||||
* Handles the action logic.
|
||||
*
|
||||
* @param mixed $data JSON Data passed to the action.
|
||||
* @param \WP_REST_Request $_request The request object.
|
||||
*/
|
||||
public function handle( $data, $_request ) {
|
||||
$state = new Critical_CSS_State();
|
||||
|
||||
if ( empty( $data['key'] ) || empty( $data['errors'] ) ) {
|
||||
return array(
|
||||
'success' => false,
|
||||
'state' => $state->get(),
|
||||
'error' => 'Invalid data',
|
||||
);
|
||||
}
|
||||
|
||||
$provider_key = sanitize_key( $data['key'] );
|
||||
$errors = $data['errors'];
|
||||
|
||||
$state->set_provider_errors( $provider_key, $errors );
|
||||
$state->save();
|
||||
|
||||
return array(
|
||||
'success' => true,
|
||||
'state' => $state->get(),
|
||||
);
|
||||
}
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Data_Sync;
|
||||
|
||||
use Automattic\Jetpack\Schema\Schema;
|
||||
|
||||
/**
|
||||
* Registers data sync for both the Critical CSS module and the Cloud CSS module. Both of these modules cannot be available at the same time.
|
||||
*/
|
||||
class Data_Sync_Schema {
|
||||
/**
|
||||
* Represents a set of errors that can be stored for a single Provider Key in a Critical CSS state block.
|
||||
*/
|
||||
public static function critical_css_provider_error() {
|
||||
return Schema::as_array(
|
||||
Schema::as_assoc_array(
|
||||
array(
|
||||
'url' => Schema::as_string(),
|
||||
'message' => Schema::as_string(),
|
||||
'type' => Schema::as_string(),
|
||||
'meta' => Schema::any_json_data()->nullable(),
|
||||
)
|
||||
)->fallback(
|
||||
array(
|
||||
'url' => '',
|
||||
'message' => '',
|
||||
'type' => '',
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static function critical_css_state() {
|
||||
return Schema::as_assoc_array(
|
||||
array(
|
||||
'providers' => Schema::as_array(
|
||||
Schema::as_assoc_array(
|
||||
array(
|
||||
'key' => Schema::as_string(),
|
||||
'label' => Schema::as_string(),
|
||||
'urls' => Schema::as_array( Schema::as_string() ),
|
||||
'success_ratio' => Schema::as_float(),
|
||||
'status' => Schema::enum( array( 'success', 'pending', 'error', 'validation-error' ) )->fallback( 'validation-error' ),
|
||||
'dismissed_errors' => Schema::as_array( Schema::as_string() )->nullable(),
|
||||
'errors' => self::critical_css_provider_error()->nullable(),
|
||||
)
|
||||
)
|
||||
)->nullable(),
|
||||
'status' => Schema::enum( array( 'not_generated', 'generated', 'pending', 'error' ) )->fallback( 'not_generated' ),
|
||||
'created' => Schema::as_float()->nullable(),
|
||||
'updated' => Schema::as_float()->nullable(),
|
||||
'status_error' => Schema::as_string()->nullable(),
|
||||
)
|
||||
)->fallback(
|
||||
array(
|
||||
'providers' => array(),
|
||||
'status' => 'not_generated',
|
||||
'created' => null,
|
||||
'updated' => null,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static function critical_css_meta() {
|
||||
return Schema::as_assoc_array(
|
||||
array(
|
||||
'proxy_nonce' => Schema::as_string()->nullable(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static function critical_css_suggest_regenerate() {
|
||||
return Schema::enum(
|
||||
array(
|
||||
'1', // Old versions of Boost stored a boolean in the DB.
|
||||
'page_saved',
|
||||
'post_saved',
|
||||
'switched_theme',
|
||||
'plugin_change',
|
||||
'cornerstone_page_saved',
|
||||
'cornerstone_pages_list_updated',
|
||||
)
|
||||
)->nullable();
|
||||
}
|
||||
|
||||
public static function critical_css_set_provider() {
|
||||
return Schema::as_assoc_array(
|
||||
array(
|
||||
'key' => Schema::as_string(),
|
||||
'css' => Schema::as_string(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static function critical_css_set_provider_errors() {
|
||||
return Schema::as_assoc_array(
|
||||
array(
|
||||
'key' => Schema::as_string(),
|
||||
'errors' => self::critical_css_provider_error(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static function critical_css_set_provider_errors_dismissed() {
|
||||
return Schema::as_array(
|
||||
Schema::as_assoc_array(
|
||||
array(
|
||||
'provider' => Schema::as_string(),
|
||||
'error_type' => Schema::as_string(),
|
||||
'dismissed' => Schema::as_boolean(),
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers;
|
||||
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Critical_CSS_Storage;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers\Archive_Provider;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers\Cornerstone_Provider;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers\Post_ID_Provider;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers\Provider;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers\Singular_Post_Provider;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers\Taxonomy_Provider;
|
||||
use Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers\WP_Core_Provider;
|
||||
|
||||
class Source_Providers {
|
||||
|
||||
/**
|
||||
* Variable used to cache the CSS string during the page request.
|
||||
* This is here because `get_critical_css` is called multiple
|
||||
* times in `style_loader_tag` hook (on each CSS file).
|
||||
*
|
||||
* @var null|false|string
|
||||
*/
|
||||
protected $request_cached_css;
|
||||
|
||||
/**
|
||||
* Stores the Critical CSS key used for rendering the current page if any.
|
||||
*
|
||||
* @var null|string
|
||||
*/
|
||||
protected $current_critical_css_key;
|
||||
|
||||
/**
|
||||
* List of all the Critical CSS Types.
|
||||
*
|
||||
* The order is important because searching for critical CSS will stop as soon as a value is found.
|
||||
* So finding Critical CSS by post ID is attempted before searching for a common Singular Post critical CSS.
|
||||
*
|
||||
* @var Provider[]
|
||||
*/
|
||||
protected $providers = array(
|
||||
Cornerstone_Provider::class,
|
||||
Post_ID_Provider::class,
|
||||
WP_Core_Provider::class,
|
||||
Singular_Post_Provider::class,
|
||||
Archive_Provider::class,
|
||||
Taxonomy_Provider::class,
|
||||
);
|
||||
|
||||
public function get_providers() {
|
||||
return $this->providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Provider which controls a given key.
|
||||
*/
|
||||
public function get_provider_for_key( $key ) {
|
||||
foreach ( $this->providers as $provider ) {
|
||||
if ( $provider::owns_key( $key ) ) {
|
||||
return $provider;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all critical CSS storage keys that are available for the current request.
|
||||
* Caches the result.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_current_request_css_keys() {
|
||||
static $keys = null;
|
||||
if ( null !== $keys ) {
|
||||
return $keys;
|
||||
}
|
||||
|
||||
$keys = array();
|
||||
|
||||
foreach ( $this->providers as $provider ) {
|
||||
$provider_keys = $provider::get_current_storage_keys();
|
||||
if ( empty( $provider_keys ) ) {
|
||||
continue;
|
||||
}
|
||||
$keys = array_merge( $keys, $provider_keys );
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get critical CSS for the current request.
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
public function get_current_request_css() {
|
||||
if ( null !== $this->request_cached_css ) {
|
||||
return $this->request_cached_css;
|
||||
}
|
||||
|
||||
$storage = new Critical_CSS_Storage();
|
||||
$data = $storage->get_css( $this->get_current_request_css_keys() );
|
||||
if ( false === $data ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->request_cached_css = $data['css'];
|
||||
$this->current_critical_css_key = $data['key'];
|
||||
|
||||
return $this->request_cached_css;
|
||||
}
|
||||
|
||||
public function get_current_critical_css_key() {
|
||||
return $this->current_critical_css_key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get providers sources.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_provider_sources( $context_posts = array() ) {
|
||||
$sources = array();
|
||||
$flat_core_and_cornerstone_urls = array();
|
||||
|
||||
$wp_core_provider_urls = WP_Core_Provider::get_critical_source_urls( $context_posts );
|
||||
foreach ( $wp_core_provider_urls as $urls ) {
|
||||
$flat_core_and_cornerstone_urls = array_merge( $flat_core_and_cornerstone_urls, $urls );
|
||||
}
|
||||
$cornerstone_provider_urls = Cornerstone_Provider::get_critical_source_urls( $context_posts );
|
||||
foreach ( $cornerstone_provider_urls as $urls ) {
|
||||
$flat_core_and_cornerstone_urls = array_merge( $flat_core_and_cornerstone_urls, $urls );
|
||||
}
|
||||
$flat_core_and_cornerstone_urls = array_values( array_unique( $flat_core_and_cornerstone_urls ) );
|
||||
|
||||
foreach ( $this->get_providers() as $provider ) {
|
||||
$provider_name = $provider::get_provider_name();
|
||||
|
||||
// For each provider,
|
||||
// Gather a list of URLs that are going to be used as Critical CSS source.
|
||||
foreach ( $provider::get_critical_source_urls( $context_posts ) as $group => $urls ) {
|
||||
if ( empty( $urls ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This removes core and cornerstone URLs from the list of URLs,
|
||||
// so they don't belong to two separate groups.
|
||||
if ( ! in_array( $provider, array( WP_Core_Provider::class, Cornerstone_Provider::class ), true ) ) {
|
||||
$urls = array_values( array_diff( $urls, $flat_core_and_cornerstone_urls ) );
|
||||
}
|
||||
|
||||
if ( empty( $urls ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$urls = $this->make_absolute_urls( $urls );
|
||||
|
||||
$key = $provider_name . '_' . $group;
|
||||
|
||||
// For each provider
|
||||
// Track the state and errors in a state array.
|
||||
$sources[] = array(
|
||||
'key' => $key,
|
||||
'label' => $provider::describe_key( $key ),
|
||||
/**
|
||||
* Filters the URLs used by Critical CSS for each provider.
|
||||
*
|
||||
* @param array $urls The list of URLs to be used to generate critical CSS
|
||||
* @param string $provider The provider name.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
'urls' => apply_filters( 'jetpack_boost_critical_css_urls', $urls, $provider ),
|
||||
'success_ratio' => $provider::get_success_ratio(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the list of Critical CSS source providers.
|
||||
*
|
||||
* @param array $sources The list of Critical CSS source providers.
|
||||
* @since 3.6.0
|
||||
*/
|
||||
return apply_filters( 'jetpack_boost_critical_css_providers', $sources );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make URLs absolute.
|
||||
*
|
||||
* @param array $urls The list of URLs to make absolute.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function make_absolute_urls( $urls ) {
|
||||
$absolute_urls = array();
|
||||
foreach ( $urls as $url ) {
|
||||
if ( class_exists( '\WP_Http' ) && method_exists( '\WP_Http', 'make_absolute_url' ) ) {
|
||||
$absolute_urls[] = \WP_Http::make_absolute_url( $url, home_url() );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( stripos( $url, home_url() ) === 0 ) {
|
||||
$absolute_urls[] = $url;
|
||||
} else {
|
||||
$absolute_urls[] = home_url( $url );
|
||||
}
|
||||
}
|
||||
|
||||
return $absolute_urls;
|
||||
}
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/**
|
||||
* Archive provider class.
|
||||
*
|
||||
* @package automattic/jetpack-boost
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers;
|
||||
|
||||
/**
|
||||
* Class Archive_Provider
|
||||
*
|
||||
* @package Automattic\Jetpack_Boost\Modules\Critical_CSS\Providers
|
||||
*/
|
||||
class Archive_Provider extends Provider {
|
||||
|
||||
/**
|
||||
* Provider name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $name = 'archive';
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_critical_source_urls( $context_posts = array() ) {
|
||||
$links = array();
|
||||
$context_post_types = wp_list_pluck( $context_posts, 'post_type' );
|
||||
|
||||
$post_types = self::get_post_types();
|
||||
if ( ! empty( $context_post_types ) ) {
|
||||
$post_types = array_intersect( $post_types, $context_post_types );
|
||||
}
|
||||
foreach ( $post_types as $post_type ) {
|
||||
$link = get_post_type_archive_link( $post_type );
|
||||
|
||||
if ( ! empty( $link ) ) {
|
||||
$links[ $post_type ][] = $link;
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_current_storage_keys() {
|
||||
if ( ! is_archive() ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// For example: "archive_post".
|
||||
return array( self::$name . '_' . get_post_type() );
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_keys() {
|
||||
return self::get_post_types();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_edit_url( $_provider_key ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function describe_key( $provider_key ) {
|
||||
$post_type = substr( $provider_key, strlen( static::$name ) + 1 );
|
||||
|
||||
switch ( $post_type ) {
|
||||
case 'post':
|
||||
return __( 'Post archive view', 'jetpack-boost' );
|
||||
|
||||
case 'page':
|
||||
return __( 'Page archive view', 'jetpack-boost' );
|
||||
|
||||
default:
|
||||
return __( 'Archive page for custom post type', 'jetpack-boost' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post types that need Critical CSS.
|
||||
*
|
||||
* @return mixed|void
|
||||
*/
|
||||
public static function get_post_types() {
|
||||
$post_types = get_post_types(
|
||||
array(
|
||||
'public' => true,
|
||||
'has_archive' => true,
|
||||
),
|
||||
'objects'
|
||||
);
|
||||
unset( $post_types['attachment'] );
|
||||
|
||||
$post_types = array_filter( $post_types, 'is_post_type_viewable' );
|
||||
|
||||
$provider_post_types = array();
|
||||
// Generate a name => name array for backwards compatibility.
|
||||
foreach ( $post_types as $post_type ) {
|
||||
$provider_post_types[ $post_type->name ] = $post_type->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the post types used for Critical CSS
|
||||
*
|
||||
* @param array $post_types The array of post types to be used
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
return apply_filters(
|
||||
'jetpack_boost_critical_css_post_types_archives',
|
||||
apply_filters_deprecated(
|
||||
'jetpack_boost_critical_css_post_types',
|
||||
array(
|
||||
$provider_post_types,
|
||||
),
|
||||
'3.4.0',
|
||||
'jetpack_boost_critical_css_post_types_archives'
|
||||
),
|
||||
$post_types
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_success_ratio() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
/**
|
||||
* Provider for the cornerstone pages
|
||||
*
|
||||
* @package automattic/jetpack-boost
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers;
|
||||
|
||||
use Automattic\Jetpack_Boost\Lib\Cornerstone\Cornerstone_Utils;
|
||||
|
||||
/**
|
||||
* Class Cornerstone_Provider
|
||||
*
|
||||
* @package Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers
|
||||
*/
|
||||
class Cornerstone_Provider extends Provider {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected static $name = 'cornerstone';
|
||||
|
||||
/**
|
||||
* Get the providers for cornerstone pages.
|
||||
*
|
||||
* @param array $_context_posts Context posts, not used. Cornerstone pages are always available.
|
||||
* @return array
|
||||
*/
|
||||
public static function get_critical_source_urls( $_context_posts = array() ) {
|
||||
$urls = Cornerstone_Utils::get_list();
|
||||
|
||||
$groups = array();
|
||||
foreach ( $urls as $url ) {
|
||||
$groups[ self::get_hash_for_url( $url ) ] = array( $url );
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
public static function get_provider_key( $url ) {
|
||||
return self::$name . '_' . self::get_hash_for_url( $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current storage keys for cornerstone pages.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_current_storage_keys() {
|
||||
$current_url = self::get_request_url();
|
||||
return array( self::get_provider_key( $current_url ) );
|
||||
}
|
||||
|
||||
public static function get_request_url() {
|
||||
global $wp;
|
||||
|
||||
// If pretty parmalinks are enabled, use the request. Otherwise, use the query vars.
|
||||
if ( get_option( 'permalink_structure' ) ) {
|
||||
return home_url( $wp->request );
|
||||
}
|
||||
|
||||
return add_query_arg( $wp->query_vars, home_url() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the keys for cornerstone pages.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_keys() {
|
||||
$urls = Cornerstone_Utils::get_list();
|
||||
|
||||
return array_map( array( __CLASS__, 'get_hash_for_url' ), $urls );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function get_hash_for_url( $url ) {
|
||||
// Remove the home_url from the beginning of the URL.
|
||||
$home_url = home_url();
|
||||
if ( stripos( $url, $home_url ) === 0 ) {
|
||||
$url = substr( $url, strlen( $home_url ) );
|
||||
}
|
||||
|
||||
$url = ltrim( $url, '/' );
|
||||
$url = untrailingslashit( $url );
|
||||
|
||||
$hash = hash( 'md5', $url );
|
||||
|
||||
return substr( $hash, 0, 8 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function describe_key( $_key ) {
|
||||
return __( 'Cornerstone page', 'jetpack-boost' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function get_edit_url( $key ) {
|
||||
$hash = substr( $key, strlen( self::$name ) + 1 );
|
||||
|
||||
$source_urls = self::get_critical_source_urls();
|
||||
|
||||
if ( ! isset( $source_urls[ $hash ] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$post_id = url_to_postid( $source_urls[ $hash ][0] );
|
||||
|
||||
if ( ! $post_id ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return get_edit_post_link( $post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public static function get_success_ratio() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
/**
|
||||
* The Post ID provider class.
|
||||
*
|
||||
* @package automattic/jetpack-boost
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers;
|
||||
|
||||
/**
|
||||
* Class Post_ID_Provider
|
||||
*
|
||||
* @package Automattic\Jetpack_Boost\Modules\Critical_CSS\Providers
|
||||
*/
|
||||
class Post_ID_Provider extends Provider {
|
||||
|
||||
/**
|
||||
* Post Ids storage key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const STORAGE_KEY = 'jetpack_boost_critical_css_post_ids';
|
||||
|
||||
/**
|
||||
* Provider name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $name = 'post_id';
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_critical_source_urls( $context_posts = array() ) {
|
||||
$results = array();
|
||||
$query = self::get_posts();
|
||||
$context_post_ids = wp_list_pluck( $context_posts, 'ID' );
|
||||
|
||||
if ( false === $query ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
foreach ( $query->posts as $post ) {
|
||||
if ( empty( $context_post_ids ) || in_array( $post->ID, $context_post_ids, true ) ) {
|
||||
$url = get_permalink( $post );
|
||||
if ( ! empty( $url ) ) {
|
||||
$results[ $post->ID ] = array( $url );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_current_storage_keys() {
|
||||
if ( ! is_singular() ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// For example: "post_id_123".
|
||||
return array( self::$name . '_' . get_the_ID() );
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function describe_key( $provider_key ) {
|
||||
$post_id = (int) substr( $provider_key, strlen( self::$name ) + 1 );
|
||||
|
||||
$post = get_post( $post_id );
|
||||
if ( ! $post ) {
|
||||
/* translators: %d is the id of a post which cannot be found. */
|
||||
return sprintf( __( 'Post %d', 'jetpack-boost' ), $post_id );
|
||||
}
|
||||
|
||||
return $post->post_title;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_edit_url( $provider_key ) {
|
||||
$post_id = (int) substr( $provider_key, strlen( self::$name ) + 1 );
|
||||
|
||||
return get_edit_post_link( $post_id, 'link' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a key that can be used to identify the current page, if any exists.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function get_current_page_key() {
|
||||
$keys = static::get_current_storage_keys();
|
||||
|
||||
if ( count( $keys ) > 0 ) {
|
||||
return $keys[0];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post ids.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_post_ids() {
|
||||
// Store the IDs somewhere.
|
||||
return get_option( self::STORAGE_KEY, array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add post id to storage.
|
||||
*
|
||||
* @param int $post_id Post Id.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function add_post_id( $post_id ) {
|
||||
$post_ids = static::get_post_ids();
|
||||
|
||||
if ( in_array( $post_id, $post_ids, true ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$post_ids[] = (int) $post_id;
|
||||
|
||||
return update_option( self::STORAGE_KEY, $post_ids );
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_keys() {
|
||||
return self::get_post_ids();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new WP_Query to gather sample posts.
|
||||
*
|
||||
* @return false|\WP_Query
|
||||
*/
|
||||
public static function get_posts() {
|
||||
$ids = self::get_post_ids();
|
||||
|
||||
if ( ! $ids ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new \WP_Query(
|
||||
array(
|
||||
'post__in' => $ids,
|
||||
'posts_per_page' => count( $ids ),
|
||||
'post_status' => array( 'publish' ),
|
||||
'post_type' => 'any',
|
||||
'no_found_rows' => true,
|
||||
'ignore_sticky_posts' => true,
|
||||
'update_post_term_cache' => false,
|
||||
'update_post_meta_cache' => false,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_success_ratio() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* Abstract Critical CSS provider class.
|
||||
*
|
||||
* @package automattic/jetpack-boost
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers;
|
||||
|
||||
/**
|
||||
* Class Provider
|
||||
*
|
||||
* @package Automattic\Jetpack_Boost\Modules\Critical_CSS\Providers
|
||||
*/
|
||||
abstract class Provider {
|
||||
|
||||
/**
|
||||
* The name of the provider.
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @access protected
|
||||
* @var string $name The name of the provider
|
||||
*/
|
||||
protected static $name;
|
||||
|
||||
/**
|
||||
* Each provider must return a list of URLs to generate CSS from.
|
||||
*
|
||||
* @param \WP_Post[] $context_posts The posts to generate CSS from.
|
||||
* @return array
|
||||
*/
|
||||
abstract public static function get_critical_source_urls( $context_posts = array() );
|
||||
|
||||
/**
|
||||
* What key should this provider look for during the current request?
|
||||
* Used in the front-end to determine where the CSS
|
||||
* might be stored for the current request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public static function get_current_storage_keys();
|
||||
|
||||
/**
|
||||
* Returns a list of all keys that this provider can provide, regardless
|
||||
* of the current URL.
|
||||
*/
|
||||
abstract public static function get_keys();
|
||||
|
||||
/**
|
||||
* Get a human-displayable string describing the given provider key.
|
||||
*
|
||||
* @param string $provider_key the key to describe.
|
||||
*/
|
||||
abstract public static function describe_key( $provider_key );
|
||||
|
||||
/**
|
||||
* Get the URL of the edit page for the given provider key.
|
||||
*
|
||||
* @param string $provider_key the key to edit.
|
||||
*/
|
||||
abstract public static function get_edit_url( $provider_key );
|
||||
|
||||
/**
|
||||
* Returns true if the key looks like it belongs to this provider.
|
||||
*
|
||||
* @param string $key The key.
|
||||
*/
|
||||
public static function owns_key( $key ) {
|
||||
return strncmp( static::$name, $key, strlen( static::$name ) ) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the provider.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_provider_name() {
|
||||
return static::$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ratio of valid urls from the provider source urls
|
||||
* for the Critical CSS generation to be considered successful.
|
||||
*
|
||||
* @return float|int
|
||||
*/
|
||||
abstract public static function get_success_ratio();
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
/**
|
||||
* Critical CSS Provider for singular posts.
|
||||
*
|
||||
* @package automattic/jetpack-boost
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers;
|
||||
|
||||
/**
|
||||
* Class Singular_Post_Provider
|
||||
*
|
||||
* @package Automattic\Jetpack_Boost\Modules\Critical_CSS\Providers
|
||||
*/
|
||||
class Singular_Post_Provider extends Provider {
|
||||
|
||||
/**
|
||||
* Provider name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $name = 'singular';
|
||||
|
||||
/**
|
||||
* Max number of posts to query.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const MAX_URLS = 10;
|
||||
|
||||
/**
|
||||
* Minimum number of posts to have Critical CSS generated in order for the whole process to be successful.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const MIN_SUCCESS_URLS = 5;
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_critical_source_urls( $context_posts = array() ) {
|
||||
$links = array();
|
||||
$context_post_types = wp_list_pluck( $context_posts, 'post_type' );
|
||||
|
||||
$post_types = self::get_post_types();
|
||||
if ( ! empty( $context_post_types ) ) {
|
||||
$post_types = array_intersect( $post_types, $context_post_types );
|
||||
}
|
||||
foreach ( $post_types as $post_type ) {
|
||||
$query = self::post_type_query( $post_type );
|
||||
|
||||
foreach ( $query->posts as $post ) {
|
||||
$url = get_permalink( $post );
|
||||
if ( ! empty( $url ) ) {
|
||||
$links[ $post_type ][] = $url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_current_storage_keys() {
|
||||
if ( ! is_singular() ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// For example: "singular_post".
|
||||
return array( self::$name . '_' . get_post_type() );
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_keys() {
|
||||
return array_keys( self::get_post_types() );
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_edit_url( $_provider_key ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function describe_key( $provider_key ) {
|
||||
$post_type = substr( $provider_key, strlen( static::$name ) + 1 );
|
||||
|
||||
switch ( $post_type ) {
|
||||
case 'post':
|
||||
return __( 'Single post view', 'jetpack-boost' );
|
||||
|
||||
case 'page':
|
||||
return __( 'Single page view', 'jetpack-boost' );
|
||||
|
||||
case 'product':
|
||||
return __( 'Single product view', 'jetpack-boost' );
|
||||
|
||||
default:
|
||||
return __( 'Custom post type', 'jetpack-boost' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get post types that need Critical CSS.
|
||||
*
|
||||
* @return mixed|void
|
||||
*/
|
||||
public static function get_post_types() {
|
||||
$post_types = get_post_types(
|
||||
array(
|
||||
'public' => true,
|
||||
),
|
||||
'objects'
|
||||
);
|
||||
unset( $post_types['attachment'] );
|
||||
|
||||
$post_types = array_filter( $post_types, 'is_post_type_viewable' );
|
||||
|
||||
$provider_post_types = array();
|
||||
// Generate a name => name array for backwards compatibility.
|
||||
foreach ( $post_types as $post_type ) {
|
||||
$provider_post_types[ $post_type->name ] = $post_type->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the post types used for Critical CSS
|
||||
*
|
||||
* @param array $post_types The array of post types to be used
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
return apply_filters(
|
||||
'jetpack_boost_critical_css_post_types_singular',
|
||||
apply_filters_deprecated(
|
||||
'jetpack_boost_critical_css_post_types',
|
||||
array(
|
||||
$provider_post_types,
|
||||
),
|
||||
'3.4.0',
|
||||
'jetpack_boost_critical_css_post_types_singular'
|
||||
),
|
||||
$post_types
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new WP_Query to gather sample posts.
|
||||
*
|
||||
* @param string $post_type post type.
|
||||
*
|
||||
* @return \WP_Query
|
||||
*/
|
||||
public static function post_type_query( $post_type ) {
|
||||
/**
|
||||
* Filters the WP_Query parameters used to gather sample posts
|
||||
*
|
||||
* @param array $args The arguments that will be used by WP_Query
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
$args = apply_filters(
|
||||
'jetpack_boost_critical_css_post_type_query',
|
||||
array(
|
||||
'orderby' => 'ID',
|
||||
'post_type' => $post_type,
|
||||
'posts_per_page' => static::MAX_URLS,
|
||||
'post_status' => array( 'publish' ),
|
||||
'no_found_rows' => true,
|
||||
'update_post_term_cache' => false,
|
||||
'update_post_meta_cache' => false,
|
||||
)
|
||||
);
|
||||
|
||||
return new \WP_Query( $args );
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_success_ratio() {
|
||||
return static::MIN_SUCCESS_URLS / static::MAX_URLS;
|
||||
}
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
/**
|
||||
* Provides taxonomy support for critical CSS
|
||||
*
|
||||
* @package automattic/jetpack-boost
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers;
|
||||
|
||||
/**
|
||||
* Class Taxonomy_Provider
|
||||
*
|
||||
* @package Automattic\Jetpack_Boost\Modules\Critical_CSS\Providers
|
||||
*/
|
||||
class Taxonomy_Provider extends Provider {
|
||||
|
||||
/**
|
||||
* Provider name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $name = 'taxonomy';
|
||||
|
||||
/**
|
||||
* Max number of posts to query.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const MAX_URLS = 10;
|
||||
|
||||
/**
|
||||
* Minimum number of posts to have Critical CSS generated in order for the whole process to be successful.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const MIN_SUCCESS_URLS = 5;
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_critical_source_urls( $context_posts = array() ) {
|
||||
$results = array();
|
||||
|
||||
$taxonomies = self::get_available_taxonomies();
|
||||
if ( ! empty( $context_posts ) ) {
|
||||
$context_post_types = array_unique( wp_list_pluck( $context_posts, 'post_type' ) );
|
||||
$context_taxonomies = get_object_taxonomies( $context_post_types, 'names' );
|
||||
$taxonomies = array_intersect( $taxonomies, $context_taxonomies );
|
||||
}
|
||||
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
$terms = self::get_terms( $taxonomy );
|
||||
|
||||
if ( ! $terms ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $terms as $term ) {
|
||||
$url = get_term_link( $term, $taxonomy );
|
||||
if ( ! is_wp_error( $url ) && ! empty( $url ) ) {
|
||||
$results[ $taxonomy ][] = $url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_current_storage_keys() {
|
||||
if ( ! is_category() && ! is_tax() ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if ( ! get_queried_object() ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// For example: "taxonomy_category".
|
||||
return array( self::$name . '_' . get_queried_object()->taxonomy );
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_keys() {
|
||||
return array_keys(
|
||||
array_filter(
|
||||
self::get_available_taxonomies(),
|
||||
function ( $taxonomy ) {
|
||||
return ! empty( Taxonomy_Provider::get_terms( $taxonomy ) );
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function describe_key( $provider_key ) {
|
||||
$taxonomy = substr( $provider_key, strlen( static::$name ) + 1 );
|
||||
|
||||
switch ( $taxonomy ) {
|
||||
case 'category':
|
||||
return __( 'Category view', 'jetpack-boost' );
|
||||
|
||||
default:
|
||||
return __( 'View for custom taxonomy', 'jetpack-boost' );
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_edit_url( $_provider_key ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Which taxonomies should Critical CSS be generated for.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_available_taxonomies() {
|
||||
$taxonomies = get_taxonomies(
|
||||
array(
|
||||
'public' => true,
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
'objects'
|
||||
);
|
||||
|
||||
$taxonomies = array_filter( $taxonomies, 'is_taxonomy_viewable' );
|
||||
|
||||
$provider_taxonomies = array();
|
||||
// Generate a name => name array for backwards compatibility.
|
||||
foreach ( $taxonomies as $taxonomy ) {
|
||||
$provider_taxonomies[ $taxonomy->name ] = $taxonomy->name;
|
||||
}
|
||||
|
||||
return $provider_taxonomies;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a couple sample terms for a taxonomy.
|
||||
*
|
||||
* @param string $taxonomy Taxonomy.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_terms( $taxonomy ) {
|
||||
/**
|
||||
* Filters the WP_Term_Query args to get a sample of terms for a taxonomy
|
||||
*
|
||||
* @param array $args The arguments that will be used by WP_Term_Query
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
$args = apply_filters(
|
||||
'jetpack_boost_critical_css_terms_query',
|
||||
array(
|
||||
'fields' => 'ids',
|
||||
'taxonomy' => $taxonomy,
|
||||
'orderby' => 'term_order',
|
||||
'number' => static::MAX_URLS,
|
||||
'hide_empty' => true,
|
||||
'hierarchical' => false,
|
||||
'update_term_meta_cache' => false,
|
||||
)
|
||||
);
|
||||
|
||||
return ( new \WP_Term_Query( $args ) )->terms;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_success_ratio() {
|
||||
return static::MIN_SUCCESS_URLS / static::MAX_URLS;
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
/**
|
||||
* Provides core support for critical CSS
|
||||
*
|
||||
* @package automattic/jetpack-boost
|
||||
*/
|
||||
|
||||
namespace Automattic\Jetpack_Boost\Lib\Critical_CSS\Source_Providers\Providers;
|
||||
|
||||
/**
|
||||
* Class WP_Core_Provider.
|
||||
*
|
||||
* @package Automattic\Jetpack_Boost\Modules\Critical_CSS\Providers
|
||||
*/
|
||||
class WP_Core_Provider extends Provider {
|
||||
|
||||
/**
|
||||
* Provider name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $name = 'core';
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_critical_source_urls( $context_posts = array() ) {
|
||||
$urls = array();
|
||||
|
||||
$front_page = (int) get_option( 'page_on_front' );
|
||||
$posts_page = (int) get_option( 'page_for_posts' );
|
||||
|
||||
if ( ! empty( $front_page ) && empty( $context_posts ) ) {
|
||||
$permalink = get_permalink( $front_page );
|
||||
if ( ! empty( $permalink ) ) {
|
||||
$urls['front_page'] = array( $permalink );
|
||||
}
|
||||
}
|
||||
|
||||
$context_post_types = wp_list_pluck( $context_posts, 'post_type' );
|
||||
$context_post_ids = wp_list_pluck( $context_posts, 'ID' );
|
||||
|
||||
// The blog page is only in context if the context posts include a 'post' post_type.
|
||||
// Or, if the blog page itself is in context.
|
||||
if ( empty( $context_post_types ) || in_array( 'post', $context_post_types, true ) || in_array( $posts_page, $context_post_ids, true ) ) {
|
||||
if ( ! empty( $posts_page ) ) {
|
||||
$permalink = get_permalink( $posts_page );
|
||||
if ( ! empty( $permalink ) ) {
|
||||
$urls['posts_page'] = array( $permalink );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $front_page && ! isset( $urls['posts_page'] ) ) {
|
||||
$urls['posts_page'] = array( home_url( '/' ) );
|
||||
}
|
||||
|
||||
return $urls;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_keys() {
|
||||
$keys = array( 'posts_page' );
|
||||
|
||||
if ( ! empty( get_option( 'page_on_front' ) ) ) {
|
||||
$keys[] = 'front_page';
|
||||
}
|
||||
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_current_storage_keys() {
|
||||
if ( is_home() ) {
|
||||
$key = 'posts_page';
|
||||
} elseif ( is_front_page() ) {
|
||||
$key = 'front_page';
|
||||
}
|
||||
|
||||
if ( ! isset( $key ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// For example: "core_posts_page".
|
||||
return array( self::$name . '_' . $key );
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_edit_url( $provider_key ) {
|
||||
if ( $provider_key === 'core_front_page' ) {
|
||||
$front_page_id = get_option( 'page_on_front' );
|
||||
if ( ! empty( $front_page_id ) ) {
|
||||
return get_edit_post_link( $front_page_id, 'link' );
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function describe_key( $provider_key ) {
|
||||
$page = substr( $provider_key, strlen( static::$name ) + 1 );
|
||||
|
||||
switch ( $page ) {
|
||||
case 'posts_page':
|
||||
return __( 'Posts page', 'jetpack-boost' );
|
||||
|
||||
case 'front_page':
|
||||
return __( 'Front page', 'jetpack-boost' );
|
||||
|
||||
default:
|
||||
return $provider_key;
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function get_success_ratio() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user