add wp-rocket

This commit is contained in:
nguyen dung
2022-02-18 19:09:35 +07:00
parent 39b8cb3612
commit 3110d00ee7
927 changed files with 271703 additions and 2 deletions

View File

@@ -0,0 +1,121 @@
<?php
namespace WP_Rocket\Subscriber\Third_Party\Hostings;
use WP_Rocket\Logger\Logger;
use WP_Rocket\Event_Management\Subscriber_Interface;
/**
* Subscriber for compatibility with Litespeed
*
* @since 3.4.1
* @author Soponar Cristina
*/
class Litespeed_Subscriber implements Subscriber_Interface {
/**
* Subscribed events for Litespeed.
*
* @since 3.4.1
* @author Soponar Cristina
* @inheritDoc
*/
public static function get_subscribed_events() {
if ( ! isset( $_SERVER['HTTP_X_LSCACHE'] ) ) {
return [];
}
return [
'before_rocket_clean_domain' => 'litespeed_clean_domain',
'before_rocket_clean_file' => 'litespeed_clean_file',
'before_rocket_clean_home' => [ 'litespeed_clean_home', 10, 2 ],
];
}
/**
* Purge Litespeed all domain.
*
* @since 3.4.1
* @author Soponar Cristina
*/
public function litespeed_clean_domain() {
$this->litespeed_header_purge_all();
}
/**
* Purge a specific page
*
* @since 3.4.1
* @author Soponar Cristina
*
* @param string $url The url to purge.
*/
public function litespeed_clean_file( $url ) {
$this->litespeed_header_purge_url( trailingslashit( $url ) );
}
/**
* Purge the homepage and its pagination
*
* @since 3.4.1
* @author Soponar Cristina
*
* @param string $root The path of home cache file.
* @param string $lang The current lang to purge.
*/
public function litespeed_clean_home( $root, $lang ) {
$home_url = trailingslashit( get_rocket_i18n_home_url( $lang ) );
$home_pagination_url = $home_url . trailingslashit( $GLOBALS['wp_rewrite']->pagination_base );
$this->litespeed_header_purge_url( $home_url );
$this->litespeed_header_purge_url( $home_pagination_url );
}
/**
* Purge Litespeed URL
*
* @since 3.4.1
* @author Soponar Cristina
*
* @param string $url The URL to purge.
* @return void
*/
public function litespeed_header_purge_url( $url ) {
if ( headers_sent() ) {
Logger::debug(
'X-LiteSpeed Headers already sent',
[ 'headers_sent' ]
);
return;
}
$parse_url = get_rocket_parse_url( $url );
$path = rtrim( $parse_url['path'], '/' );
$private_prefix = 'X-LiteSpeed-Purge: ' . $path;
Logger::debug(
'X-LiteSpeed',
[
'litespeed_header_purge_url',
'path' => $private_prefix,
]
);
@header( $private_prefix );
}
/**
* Purge Litespeed Cache
*
* @since 3.4.1
* @author Soponar Cristina
*
* @return void
*/
public function litespeed_header_purge_all() {
if ( headers_sent() ) {
return;
}
$private_prefix = 'X-LiteSpeed-Purge: *';
@header( $private_prefix );
}
}

View File

@@ -0,0 +1,390 @@
<?php
namespace WP_Rocket\Subscriber\Third_Party\Plugins\Images\Webp;
use WP_Rocket\Admin\Options_Data;
use WP_Rocket\Event_Management\Subscriber_Interface;
/**
* Subscriber for the WebP support with EWWW.
*
* @since 3.4
* @author Grégory Viguier
*/
class EWWW_Subscriber implements Webp_Interface, Subscriber_Interface {
use Webp_Common;
/**
* Options_Data instance.
*
* @var Options_Data
* @access private
* @author Remy Perona
*/
private $options;
/**
* EWWW basename.
*
* @var string
* @access private
* @author Grégory Viguier
*/
private $plugin_basename;
/**
* Constructor.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param Options_Data $options Options instance.
*/
public function __construct( Options_Data $options ) {
$this->options = $options;
}
/**
* {@inheritdoc}
*/
public static function get_subscribed_events() {
return [
'rocket_webp_plugins' => 'register',
'wp_rocket_loaded' => 'load_hooks',
];
}
/** ----------------------------------------------------------------------------------------- */
/** HOOKS =================================================================================== */
/** ----------------------------------------------------------------------------------------- */
/**
* Launch filters.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*/
public function load_hooks() {
if ( ! $this->options->get( 'cache_webp' ) ) {
return;
}
/**
* Every time EWWW is (de)activated, we must "sync" our webp cache option.
*/
if ( did_action( 'activate_' . $this->get_basename() ) ) {
$this->plugin_activation();
}
if ( did_action( 'deactivate_' . $this->get_basename() ) ) {
$this->plugin_deactivation();
}
add_action( 'activate_' . $this->get_basename(), [ $this, 'plugin_activation' ], 20 );
add_action( 'deactivate_' . $this->get_basename(), [ $this, 'plugin_deactivation' ], 20 );
if ( ! function_exists( 'ewww_image_optimizer_get_option' ) ) {
return;
}
/**
* Since Rocket already updates the config file after updating its options, there is no need to do it again if the CDN or zone options change.
* Sadly, we cant monitor EWWW options accurately to update our config file.
*/
add_filter( 'rocket_cdn_cnames', [ $this, 'maybe_remove_images_cnames' ], 1000, 2 );
add_filter( 'rocket_allow_cdn_images', [ $this, 'maybe_remove_images_from_cdn_dropdown' ] );
$option_names = [
'ewww_image_optimizer_exactdn',
'ewww_image_optimizer_webp_for_cdn',
];
foreach ( $option_names as $option_name ) {
if ( $this->is_active_for_network() ) {
add_filter( 'add_site_option_' . $option_name, [ $this, 'trigger_webp_change' ] );
add_filter( 'update_site_option_' . $option_name, [ $this, 'trigger_webp_change' ] );
add_filter( 'delete_site_option_' . $option_name, [ $this, 'trigger_webp_change' ] );
} else {
add_filter( 'add_option_' . $option_name, [ $this, 'trigger_webp_change' ] );
add_filter( 'update_option_' . $option_name, [ $this, 'trigger_webp_change' ] );
add_filter( 'delete_option_' . $option_name, [ $this, 'trigger_webp_change' ] );
}
}
}
/**
* Remove CDN hosts for images if EWWW uses ExactDN.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param array $hosts List of CDN URLs.
* @param array $zones List of zones. Default is [ 'all' ].
* @return array
*/
public function maybe_remove_images_cnames( $hosts, $zones ) {
if ( ! $hosts ) {
return $hosts;
}
if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) {
return $hosts;
}
// EWWW uses ExactDN: WPR CDN should be disabled for images.
if ( ! in_array( 'images', $zones, true ) ) {
// Not asking for images.
return $hosts;
}
if ( ! array_diff( $zones, [ 'all', 'images' ] ) ) {
// This is clearly for images: return an empty list of hosts.
return [];
}
// We also want other things, like js and css: let's only remove the hosts for 'images'.
$cdn_urls = $this->options->get( 'cdn_cnames', [] );
if ( ! $cdn_urls ) {
return $hosts;
}
// Separate image hosts from the other ones.
$image_hosts = [];
$other_hosts = [];
$cdn_zones = $this->options->get( 'cdn_zone', [] );
foreach ( $cdn_urls as $k => $urls ) {
if ( ! in_array( $cdn_zones[ $k ], $zones, true ) ) {
continue;
}
$urls = explode( ',', $urls );
$urls = array_map( 'trim', $urls );
if ( 'images' === $cdn_zones[ $k ] ) {
foreach ( $urls as $url ) {
$image_hosts[] = $url;
}
} else {
foreach ( $urls as $url ) {
$other_hosts[] = $url;
}
}
}
// Make sure the image hosts are not also used for other things (duplicate).
$image_hosts = array_diff( $image_hosts, $other_hosts );
// Then remove the remaining from the final list.
return array_diff( $hosts, $image_hosts );
}
/**
* Maybe remove the images option from the CDN dropdown.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param bool $allow true to add the option, false otherwise.
* @return bool
*/
public function maybe_remove_images_from_cdn_dropdown( $allow ) {
if ( ! $allow ) {
return $allow;
}
if ( ! ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) {
return $allow;
}
// EWWW uses ExactDN: WPR CDN should be disabled for images.
return false;
}
/** ----------------------------------------------------------------------------------------- */
/** PUBLIC TOOLS ============================================================================ */
/** ----------------------------------------------------------------------------------------- */
/**
* Get the plugin name.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return string
*/
public function get_name() {
return 'EWWW';
}
/**
* Get the plugin identifier.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return string
*/
public function get_id() {
return 'ewww';
}
/**
* Tell if the plugin converts images to webp.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_converting_to_webp() {
if ( ! function_exists( 'ewww_image_optimizer_get_option' ) ) {
// No EWWW, no webp.
return false;
}
return (bool) ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp' );
}
/**
* Tell if the plugin serves webp images on frontend.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_serving_webp() {
if ( ! function_exists( 'ewww_image_optimizer_get_option' ) ) {
// No EWWW, no webp.
return false;
}
if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) {
// EWWW uses ExactDN (WPR CDN should be disabled for images).
return true;
}
if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) ) {
// EWWW uses JS to rewrite file extensions.
return true;
}
// Decide if rewrite rules are used.
if ( ! function_exists( 'ewww_image_optimizer_webp_rewrite_verify' ) ) {
// Uh?
return false;
}
if ( ! function_exists( 'get_home_path' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
if ( ! function_exists( 'extract_from_markers' ) ) {
require_once ABSPATH . 'wp-admin/includes/misc.php';
}
/**
* This function returns null if rules are present and valid. Otherwise it returns rules to be inserted.
* Note: this also returns null if WP Fastest Cache rules for webp are found in the file.
*
* @see ewww_image_optimizer_wpfc_webp_enabled()
*/
$use_rewrite_rules = ! ewww_image_optimizer_webp_rewrite_verify();
/**
* Filter wether EWW is using rewrite rules for webp.
*
* @since 3.4
* @author Grégory Viguier
*
* @param bool $use_rewrite_rules True when EWWW uses rewrite rules. False otherwise.
*/
return (bool) apply_filters( 'rocket_webp_ewww_use_rewrite_rules', $use_rewrite_rules );
}
/**
* Tell if the plugin uses a CDN-compatible technique to serve webp images on frontend.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_serving_webp_compatible_with_cdn() {
if ( ! function_exists( 'ewww_image_optimizer_get_option' ) ) {
// No EWWW, no webp.
return false;
}
if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_exactdn' ) ) {
// EWWW uses ExactDN.
return true;
}
if ( ewww_image_optimizer_get_option( 'ewww_image_optimizer_webp_for_cdn' ) ) {
// EWWW uses JS to rewrite file extensions.
return true;
}
// At this point, the plugin is using rewrite rules or nothing.
return false;
}
/**
* Get the plugin basename.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function get_basename() {
if ( empty( $this->plugin_basename ) ) {
$this->plugin_basename = rocket_has_constant( 'EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE' )
? plugin_basename( rocket_get_constant( 'EWWW_IMAGE_OPTIMIZER_PLUGIN_FILE' ) )
: 'ewww-image-optimizer/ewww-image-optimizer.php';
}
return $this->plugin_basename;
}
/** ----------------------------------------------------------------------------------------- */
/** PRIVATE TOOLS =========================================================================== */
/** ----------------------------------------------------------------------------------------- */
/**
* Tell if EWWW is active for network.
*
* @since 3.4
* @access private
* @author Grégory Viguier
*
* @return bool
*/
private function is_active_for_network() {
static $is;
if ( isset( $is ) ) {
return $is;
}
if ( ! is_multisite() ) {
$is = false;
return $is;
}
if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$is = is_plugin_active_for_network( $this->get_basename() ) && ! get_site_option( 'ewww_image_optimizer_allow_multisite_override' );
return $is;
}
}

View File

@@ -0,0 +1,435 @@
<?php
namespace WP_Rocket\Subscriber\Third_Party\Plugins\Images\Webp;
use WP_Rocket\Admin\Options_Data;
use WP_Rocket\Event_Management\Subscriber_Interface;
/**
* Subscriber for the WebP support with Imagify.
*
* @since 3.4
* @author Grégory Viguier
*/
class Imagify_Subscriber implements Webp_Interface, Subscriber_Interface {
use Webp_Common;
/**
* Options_Data instance.
*
* @var Options_Data
* @access private
* @author Remy Perona
*/
private $options;
/**
* Imagify basename.
*
* @var string
* @access private
* @author Grégory Viguier
*/
private $plugin_basename;
/**
* Imagifys "serve webp" option name.
*
* @var string
* @access private
* @author Grégory Viguier
*/
private $plugin_option_name_to_serve_webp;
/**
* Temporarily store the result of $this->is_serving_webp().
*
* @var bool
* @access private
* @author Grégory Viguier
*/
private $tmp_is_serving_webp;
/**
* Constructor.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param Options_Data $options Options instance.
*/
public function __construct( Options_Data $options ) {
$this->options = $options;
}
/**
* Returns an array of events that this subscriber wants to listen to.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return array
*/
public static function get_subscribed_events() {
return [
'rocket_webp_plugins' => 'register',
'wp_rocket_loaded' => 'load_hooks',
];
}
/** ----------------------------------------------------------------------------------------- */
/** HOOKS =================================================================================== */
/** ----------------------------------------------------------------------------------------- */
/**
* Launch filters.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*/
public function load_hooks() {
if ( ! $this->options->get( 'cache_webp' ) ) {
return;
}
/**
* Every time Imagify is (de)activated, we must "sync" our webp cache option.
*/
add_action( 'imagify_activation', [ $this, 'plugin_activation' ], 20 );
add_action( 'imagify_deactivation', [ $this, 'plugin_deactivation' ], 20 );
if ( ! rocket_has_constant( 'IMAGIFY_VERSION' ) ) {
return;
}
/**
* Since Rocket already updates the config file after updating its options, there is no need to do it again if the CDN or zone options change.
*/
/**
* Every time Imagifys option changes, we must "sync" our webp cache option.
*/
$option_name = $this->get_option_name_to_serve_webp();
if ( $this->is_active_for_network() ) {
add_filter( 'add_site_option_' . $option_name, [ $this, 'sync_on_network_option_add' ], 10, 3 );
add_filter( 'update_site_option_' . $option_name, [ $this, 'sync_on_network_option_update' ], 10, 4 );
add_filter( 'pre_delete_site_option_' . $option_name, [ $this, 'store_option_value_before_network_delete' ], 10, 2 );
add_filter( 'delete_site_option_' . $option_name, [ $this, 'sync_on_network_option_delete' ], 10, 2 );
return;
}
add_filter( 'add_option_' . $option_name, [ $this, 'sync_on_option_add' ], 10, 2 );
add_filter( 'update_option_' . $option_name, [ $this, 'sync_on_option_update' ], 10, 2 );
add_filter( 'delete_option', [ $this, 'store_option_value_before_delete' ] );
add_filter( 'delete_option_' . $option_name, [ $this, 'sync_on_option_delete' ] );
}
/**
* Maybe deactivate webp cache after Imagify network option has been successfully added.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param string $option Name of the network option.
* @param mixed $value Value of the network option.
* @param int $network_id ID of the network.
*/
public function sync_on_network_option_add( $option, $value, $network_id ) {
if ( get_current_network_id() === $network_id && ! empty( $value['display_webp'] ) ) {
$this->trigger_webp_change();
}
}
/**
* Maybe activate or deactivate webp cache after Imagify network option has been modified.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param string $option Name of the network option.
* @param mixed $value Current value of the network option.
* @param mixed $old_value Old value of the network option.
* @param int $network_id ID of the network.
*/
public function sync_on_network_option_update( $option, $value, $old_value, $network_id ) {
if ( get_current_network_id() === $network_id ) {
$this->sync_on_option_update( $old_value, $value );
}
}
/**
* Store the Imagify network option value before it is deleted.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param string $option Option name.
* @param int $network_id ID of the network.
*/
public function store_option_value_before_network_delete( $option, $network_id ) {
if ( get_current_network_id() === $network_id ) {
$this->tmp_is_serving_webp = $this->is_serving_webp();
}
}
/**
* Maybe activate webp cache after Imagify network option has been deleted.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param string $option Name of the network option.
* @param int $network_id ID of the network.
*/
public function sync_on_network_option_delete( $option, $network_id ) {
if ( get_current_network_id() === $network_id && false !== $this->tmp_is_serving_webp ) {
$this->trigger_webp_change();
}
}
/**
* Maybe deactivate webp cache after Imagify option has been successfully added.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param string $option Name of the option to add.
* @param mixed $value Value of the option.
*/
public function sync_on_option_add( $option, $value ) {
if ( ! empty( $value['display_webp'] ) ) {
$this->trigger_webp_change();
}
}
/**
* Maybe activate or deactivate webp cache after Imagify option has been modified.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param mixed $old_value The old option value.
* @param mixed $value The new option value.
*/
public function sync_on_option_update( $old_value, $value ) {
$old_display = ! empty( $old_value['display_webp'] );
$display = ! empty( $value['display_webp'] );
if ( $old_display !== $display || $old_value['display_webp_method'] !== $value['display_webp_method'] ) {
$this->trigger_webp_change();
}
}
/**
* Store the Imagify option value before it is deleted.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param string $option Name of the option to delete.
*/
public function store_option_value_before_delete( $option ) {
if ( $this->get_option_name_to_serve_webp() === $option ) {
$this->tmp_is_serving_webp = $this->is_serving_webp();
}
}
/**
* Maybe activate webp cache after Imagify option has been deleted.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param string $option Name of the deleted option.
*/
public function sync_on_option_delete( $option ) {
if ( false !== $this->tmp_is_serving_webp ) {
$this->trigger_webp_change();
}
}
/** ----------------------------------------------------------------------------------------- */
/** PUBLIC TOOLS ============================================================================ */
/** ----------------------------------------------------------------------------------------- */
/**
* Get the plugin name.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return string
*/
public function get_name() {
return 'Imagify';
}
/**
* Get the plugin identifier.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return string
*/
public function get_id() {
return 'imagify';
}
/**
* Tell if the plugin converts images to webp.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_converting_to_webp() {
if ( ! function_exists( 'get_imagify_option' ) ) {
// No Imagify, no webp.
return false;
}
return (bool) get_imagify_option( 'convert_to_webp' );
}
/**
* Tell if the plugin serves webp images on frontend.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_serving_webp() {
if ( ! function_exists( 'get_imagify_option' ) ) {
// No Imagify, no webp.
return false;
}
return (bool) get_imagify_option( 'display_webp' );
}
/**
* Tell if the plugin uses a CDN-compatible technique to serve webp images on frontend.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_serving_webp_compatible_with_cdn() {
if ( ! $this->is_serving_webp() ) {
return false;
}
return 'rewrite' !== get_imagify_option( 'display_webp_method' );
}
/**
* Get the plugin basename.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function get_basename() {
if ( empty( $this->plugin_basename ) ) {
$this->plugin_basename = rocket_has_constant( 'IMAGIFY_FILE' )
? plugin_basename( rocket_get_constant( 'IMAGIFY_FILE' ) )
: 'imagify/imagify.php';
}
return $this->plugin_basename;
}
/** ----------------------------------------------------------------------------------------- */
/** PRIVATE TOOLS =========================================================================== */
/** ----------------------------------------------------------------------------------------- */
/**
* Get the name of the Imagifys "serve webp" option.
*
* @since 3.4
* @access private
* @author Grégory Viguier
*
* @return string
*/
private function get_option_name_to_serve_webp() {
if ( ! empty( $this->plugin_option_name_to_serve_webp ) ) {
return $this->plugin_option_name_to_serve_webp;
}
$default = 'imagify_settings';
if ( ! class_exists( '\Imagify_Options' ) || ! method_exists( '\Imagify_Options', 'get_instance' ) ) {
$this->plugin_option_name_to_serve_webp = $default;
return $this->plugin_option_name_to_serve_webp;
}
$instance = \Imagify_Options::get_instance();
if ( ! method_exists( $instance, 'get_option_name' ) ) {
$this->plugin_option_name_to_serve_webp = $default;
return $this->plugin_option_name_to_serve_webp;
}
$this->plugin_option_name_to_serve_webp = $instance->get_option_name();
return $this->plugin_option_name_to_serve_webp;
}
/**
* Tell if Imagify is active for network.
*
* @since 3.4
* @access private
* @author Grégory Viguier
*
* @return bool
*/
private function is_active_for_network() {
static $is;
if ( isset( $is ) ) {
return $is;
}
if ( function_exists( 'imagify_is_active_for_network' ) ) {
$is = imagify_is_active_for_network();
return $is;
}
if ( ! is_multisite() ) {
$is = false;
return $is;
}
if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
$is = is_plugin_active_for_network( $this->get_basename() );
return $is;
}
}

View File

@@ -0,0 +1,152 @@
<?php
namespace WP_Rocket\Subscriber\Third_Party\Plugins\Images\Webp;
use WP_Rocket\Event_Management\Subscriber_Interface;
/**
* Subscriber for the WebP support with Optimus.
*
* @since 3.4
* @author Grégory Viguier
*/
class Optimus_Subscriber implements Webp_Interface, Subscriber_Interface {
/**
* Optimus basename.
*
* @var string
* @access private
* @author Grégory Viguier
*/
private $plugin_basename;
/**
* Returns an array of events that this subscriber wants to listen to.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return array
*/
public static function get_subscribed_events() {
if ( ! defined( 'OPTIMUS_FILE' ) ) {
return [];
}
return [
'rocket_webp_plugins' => 'register',
];
}
/** ----------------------------------------------------------------------------------------- */
/** HOOKS =================================================================================== */
/** ----------------------------------------------------------------------------------------- */
/**
* Register the plugin.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param array $webp_plugins An array of Webp_Interface objects.
* @return array
*/
public function register( $webp_plugins ) {
$webp_plugins[] = $this;
return $webp_plugins;
}
/** ----------------------------------------------------------------------------------------- */
/** PUBLIC TOOLS ============================================================================ */
/** ----------------------------------------------------------------------------------------- */
/**
* Get the plugin name.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return string
*/
public function get_name() {
return 'Optimus';
}
/**
* Get the plugin identifier.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return string
*/
public function get_id() {
return 'optimus';
}
/**
* Tell if the plugin converts images to webp.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_converting_to_webp() {
if ( class_exists( '\Optimus' ) && method_exists( '\Optimus', 'get_options' ) ) {
$options = \Optimus::get_options();
} else {
$options = get_option( 'optimus' );
}
return ! empty( $options['webp_convert'] );
}
/**
* Tell if the plugin serves webp images on frontend.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_serving_webp() {
return false;
}
/**
* Tell if the plugin uses a CDN-compatible technique to serve webp images on frontend.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_serving_webp_compatible_with_cdn() {
return false;
}
/**
* Get the plugin basename.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function get_basename() {
if ( empty( $this->plugin_basename ) ) {
$this->plugin_basename = defined( 'OPTIMUS_FILE' ) ? plugin_basename( OPTIMUS_FILE ) : 'optimus/optimus.php';
}
return $this->plugin_basename;
}
}

View File

@@ -0,0 +1,294 @@
<?php
namespace WP_Rocket\Subscriber\Third_Party\Plugins\Images\Webp;
use WP_Rocket\Admin\Options_Data;
use WP_Rocket\Event_Management\Subscriber_Interface;
/**
* Subscriber for the WebP support with ShortPixel.
*
* @since 3.4
* @author Grégory Viguier
*/
class ShortPixel_Subscriber implements Webp_Interface, Subscriber_Interface {
use Webp_Common;
/**
* Options_Data instance.
*
* @var Options_Data
* @access private
* @author Remy Perona
*/
private $options;
/**
* ShortPixel basename.
*
* @var string
* @access private
* @author Grégory Viguier
*/
private $plugin_basename;
/**
* ShortPixels "serve webp" option name.
*
* @var string
* @access private
* @author Grégory Viguier
*/
private $plugin_option_name_to_serve_webp = 'wp-short-pixel-create-webp-markup';
/**
* Temporarily store the result of $this->is_serving_webp().
*
* @var bool
* @access private
* @author Grégory Viguier
*/
private $tmp_is_serving_webp;
/**
* Constructor.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param Options_Data $options Options instance.
*/
public function __construct( Options_Data $options ) {
$this->options = $options;
}
/**
* Returns an array of events that this subscriber wants to listen to.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return array
*/
public static function get_subscribed_events() {
return [
'rocket_webp_plugins' => 'register',
'wp_rocket_loaded' => 'load_hooks',
];
}
/** ----------------------------------------------------------------------------------------- */
/** HOOKS =================================================================================== */
/** ----------------------------------------------------------------------------------------- */
/**
* Launch filters.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*/
public function load_hooks() {
if ( ! $this->options->get( 'cache_webp' ) ) {
return;
}
/**
* Every time ShortPixel is (de)activated, we must "sync" our webp cache option.
*/
if ( did_action( 'activate_' . $this->get_basename() ) ) {
$this->plugin_activation();
}
if ( did_action( 'deactivate_' . $this->get_basename() ) ) {
$this->plugin_deactivation();
}
add_action( 'activate_' . $this->get_basename(), [ $this, 'plugin_activation' ], 20 );
add_action( 'deactivate_' . $this->get_basename(), [ $this, 'plugin_deactivation' ], 20 );
if ( ! defined( 'SHORTPIXEL_IMAGE_OPTIMISER_VERSION' ) ) {
return;
}
/**
* Since Rocket already updates the config file after updating its options, there is no need to do it again if the CDN or zone options change.
*/
/**
* Every time ShortPixels option changes, we must "sync" our webp cache option.
*/
$option_name = $this->plugin_option_name_to_serve_webp;
add_filter( 'add_option_' . $option_name, [ $this, 'sync_on_option_add' ], 10, 2 );
add_filter( 'update_option_' . $option_name, [ $this, 'sync_on_option_update' ], 10, 2 );
add_filter( 'delete_option', [ $this, 'store_option_value_before_delete' ] );
add_filter( 'delete_option_' . $option_name, [ $this, 'sync_on_option_delete' ] );
}
/**
* Maybe deactivate webp cache after ShortPixel option has been successfully added.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param string $option Name of the option to add.
* @param mixed $value Value of the option.
*/
public function sync_on_option_add( $option, $value ) {
if ( $value ) {
$this->trigger_webp_change();
}
}
/**
* Maybe activate or deactivate webp cache after ShortPixel option has been modified.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param mixed $old_value The old option value.
* @param mixed $value The new option value.
*/
public function sync_on_option_update( $old_value, $value ) {
/**
* 0 = Dont serve webp.
* 1 = <picture> + buffer
* 2 = <picture> + hooks
* 3 = .htaccess
*/
$old_value = $old_value > 0;
$value = $value > 0;
if ( $old_value !== $value ) {
$this->trigger_webp_change();
}
}
/**
* Store the ShortPixel option value before it is deleted.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param string $option Name of the option to delete.
*/
public function store_option_value_before_delete( $option ) {
if ( $this->plugin_option_name_to_serve_webp === $option ) {
$this->tmp_is_serving_webp = $this->is_serving_webp();
}
}
/**
* Maybe activate webp cache after ShortPixel option has been deleted.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*/
public function sync_on_option_delete() {
if ( false !== $this->tmp_is_serving_webp ) {
$this->trigger_webp_change();
}
}
/** ----------------------------------------------------------------------------------------- */
/** PUBLIC TOOLS ============================================================================ */
/** ----------------------------------------------------------------------------------------- */
/**
* Get the plugin name.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return string
*/
public function get_name() {
return 'ShortPixel';
}
/**
* Get the plugin identifier.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return string
*/
public function get_id() {
return 'shortpixel';
}
/**
* Tell if the plugin converts images to webp.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_converting_to_webp() {
return (bool) get_option( 'wp-short-create-webp' );
}
/**
* Tell if the plugin serves webp images on frontend.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_serving_webp() {
return (bool) get_option( $this->plugin_option_name_to_serve_webp );
}
/**
* Tell if the plugin uses a CDN-compatible technique to serve webp images on frontend.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_serving_webp_compatible_with_cdn() {
$display = (int) get_option( $this->plugin_option_name_to_serve_webp );
if ( ! $display ) {
// The option is not enabled, no webp.
return false;
}
if ( 3 === $display ) {
// The option is set to "rewrite rules".
return false;
}
return true;
}
/**
* Get the plugin basename.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function get_basename() {
if ( empty( $this->plugin_basename ) ) {
$this->plugin_basename = defined( 'SHORTPIXEL_PLUGIN_FILE' ) ? plugin_basename( SHORTPIXEL_PLUGIN_FILE ) : 'shortpixel-image-optimiser/wp-shortpixel.php';
}
return $this->plugin_basename;
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace WP_Rocket\Subscriber\Third_Party\Plugins\Images\Webp;
/**
* Trait for webp subscribers, focussed on plugins that serve webp images on frontend.
*
* @since 3.4
* @author Grégory Viguier
*/
trait Webp_Common {
/**
* Register the plugin.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @param array $webp_plugins An array of Webp_Interface objects.
* @return array
*/
public function register( $webp_plugins ) {
$webp_plugins[] = $this;
return $webp_plugins;
}
/**
* On plugin activation, deactivate Rocket webp cache if the plugin is serving webp.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*/
public function plugin_activation() {
if ( $this->is_serving_webp() ) {
$this->trigger_webp_change();
}
}
/**
* On plugin deactivation, activate Rocket webp cache if the plugin is serving webp.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*/
public function plugin_deactivation() {
if ( $this->is_serving_webp() ) {
$this->trigger_webp_change();
}
}
/**
* Trigger an action when the webp feature is enabled/disabled in a third party plugin.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*/
public function trigger_webp_change() {
/**
* Trigger an action when the webp feature is enabled/disabled in a third party plugin.
*
* @since 3.4
* @author Grégory Viguier
*/
do_action( 'rocket_third_party_webp_change' );
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace WP_Rocket\Subscriber\Third_Party\Plugins\Images\Webp;
/**
* Interface to use for webp subscribers.
*
* @since 3.4
* @author Grégory Viguier
*/
interface Webp_Interface {
/**
* Get the plugin name.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return string
*/
public function get_name();
/**
* Get the plugin identifier.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return string
*/
public function get_id();
/**
* Tell if the plugin converts images to webp.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_converting_to_webp();
/**
* Tell if the plugin serves webp images on frontend.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_serving_webp();
/**
* Tell if the plugin uses a CDN-compatible technique to serve webp images on frontend.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function is_serving_webp_compatible_with_cdn();
/**
* Get the plugin basename.
*
* @since 3.4
* @access public
* @author Grégory Viguier
*
* @return bool
*/
public function get_basename();
}

View File

@@ -0,0 +1,396 @@
<?php
namespace WP_Rocket\Subscriber\Third_Party\Plugins;
use WP_Rocket\Event_Management\Subscriber_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Class that handles events related to plugins that add mobile themes.
*
* @since 3.2
* @author Grégory Viguier
*/
class Mobile_Subscriber implements Subscriber_Interface {
/**
* Options to activate when a mobile plugin is active.
*
* @since 3.2
* @access protected
* @author Grégory Viguier
*
* @var array
*/
protected static $options = [
'cache_mobile' => 1,
'do_caching_mobile_files' => 1,
];
/**
* Cache the value of self::is_mobile_plugin_active().
*
* @since 3.2
* @access protected
* @author Grégory Viguier
*
* @var array An array of arrays of booleans.
* First level of keys corresponds to the network ID. Second level of keys corresponds to the blog ID.
*/
protected static $is_mobile_active = [];
/**
* Returns an array of events that this subscriber wants to listen to.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*
* @return array
*/
public static function get_subscribed_events() {
// In case a mobile plugin has already been activated.
$do = [];
$undo = [];
$plugin_events = [];
if ( ! function_exists( '\is_plugin_active' ) ) {
include_once ABSPATH . 'wp-admin/includes/plugin.php';
}
foreach ( static::get_mobile_plugins() as $plugin => $plugin_data ) {
if ( \did_action( 'activate_' . $plugin ) &&
! isset( $plugin_data['is_active_callback'] ) ) {
$do[] = $plugin;
}
if ( \did_action( 'activate_' . $plugin ) &&
isset( $plugin_data['is_active_callback'] ) &&
call_user_func( $plugin_data['is_active_callback'] ) ) {
$do[] = $plugin;
}
if ( \did_action( 'deactivate_' . $plugin ) ) {
$undo[] = $plugin;
}
if ( \is_plugin_active( $plugin ) ) {
if ( isset( $plugin_data['activation_hook'] ) ) {
$plugin_events[ $plugin_data['activation_hook'] ] = 'maybe_update_mobile_cache_activation_plugin_hook';
}
if ( isset( $plugin_data['deactivation_hook'] ) ) {
$plugin_events[ $plugin_data['deactivation_hook'] ] = 'maybe_update_mobile_cache_activation_plugin_hook';
}
}
}
if ( array_diff( $do, $undo ) || array_diff( $undo, $do ) ) {
static::update_mobile_cache_activation();
}
// Register events.
$events = [
// Plugin activation/deactivation.
'add_option_active_plugins' => [ 'add_option_callback', 10, 2 ],
'update_option_active_plugins' => [ 'update_option_callback', 10, 2 ],
'delete_option_active_plugins' => 'delete_option_callback',
'add_site_option_active_sitewide_plugins' => [ 'add_site_option_callback', 10, 3 ],
'update_site_option_active_sitewide_plugins' => [ 'update_site_option_callback', 10, 4 ],
'delete_site_option_active_sitewide_plugins' => [ 'delete_site_option_callback', 10, 2 ],
// WPR settings (`get_option()`).
'option_' . WP_ROCKET_SLUG => 'mobile_options_filter',
];
foreach ( static::$options as $option => $value ) {
// WPR settings (`get_rocket_option()`).
$events[ 'pre_get_rocket_option_' . $option ] = 'is_mobile_plugin_active_callback';
}
$events = array_merge( $events, $plugin_events );
return $events;
}
/** ----------------------------------------------------------------------------------------- */
/** HOOK CALLBACKS ========================================================================== */
/** ----------------------------------------------------------------------------------------- */
/**
* Callback triggered after the option `active_plugins` is created.
* This should normally never be triggered.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*
* @param string $option Name of the option to add.
* @param mixed $value Value of the option.
*/
public function add_option_callback( $option, $value ) {
$this->maybe_update_mobile_cache_activation( $value, [] );
}
/**
* Callback triggered after the option `active_plugins` is updated.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*
* @param mixed $old_value The old option value.
* @param mixed $value Value of the option.
*/
public function update_option_callback( $old_value, $value ) {
$this->maybe_update_mobile_cache_activation( $value, $old_value );
}
/**
* Callback triggered after the option `active_plugins` is deleted.
* Very low probability to be triggered.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*/
public function delete_option_callback() {
static::update_mobile_cache_activation();
}
/**
* Callback triggered after the option `active_sitewide_plugins` is created.
* This should normally never be triggered.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*
* @param string $option Name of the option to add.
* @param mixed $value Value of the option.
* @param int $network_id ID of the network.
*/
public function add_site_option_callback( $option, $value, $network_id ) {
if ( get_current_network_id() === $network_id ) {
$this->maybe_update_mobile_cache_activation( $value, [] );
}
}
/**
* Callback triggered after the option `active_sitewide_plugins` is updated.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*
* @param string $option Name of the option to add.
* @param mixed $value Value of the option.
* @param mixed $old_value The old option value.
* @param int $network_id ID of the network.
*/
public function update_site_option_callback( $option, $value, $old_value, $network_id ) {
if ( get_current_network_id() === $network_id ) {
$this->maybe_update_mobile_cache_activation( $value, $old_value );
}
}
/**
* Callback triggered after the option `active_sitewide_plugins` is deleted.
* Very low probability to be triggered.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*
* @param string $option Name of the option to add.
* @param int $network_id ID of the network.
*/
public function delete_site_option_callback( $option, $network_id ) {
if ( get_current_network_id() === $network_id ) {
static::update_mobile_cache_activation();
}
}
/**
* Enable mobile caching when a mobile plugin is activated, or revert it back to its previous state when a mobile plugin is deactivated.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*
* @param mixed $value The new option value.
* @param mixed $old_value The old option value.
*/
public function maybe_update_mobile_cache_activation( $value, $old_value ) {
$plugins = static::get_mobile_plugins();
$plugins = array_keys( $plugins );
$value = array_intersect( $plugins, (array) $value );
$old_value = array_intersect( $plugins, (array) $old_value );
if ( $value !== $old_value ) {
static::update_mobile_cache_activation();
}
}
/**
* Enables mobile caching when a mobile plugin option is activated, or reverts it back to its previous state when a mobile plugin option is deactivated.
*
* @since 3.4.2
* @access public
* @author Soponar Cristina
*
* @return void
*/
public function maybe_update_mobile_cache_activation_plugin_hook() {
$is_mobile_plugin_active = static::is_mobile_plugin_active();
static::reset_class_cache();
$is_new_mobile_plugin_active = static::is_mobile_plugin_active();
if ( $is_mobile_plugin_active !== $is_new_mobile_plugin_active ) {
static::update_mobile_cache_activation();
}
}
/**
* Forces the values for the mobile options if a mobile plugin is active.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*
* @param array $values Option values.
* @return array
*/
public function mobile_options_filter( $values ) {
if ( static::is_mobile_plugin_active() ) {
return array_merge( (array) $values, static::$options );
}
return $values;
}
/**
* Forces the value for a mobile option if a mobile plugin is active.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*
* @param int|null $value Option value.
* @return int|null
*/
public function is_mobile_plugin_active_callback( $value ) {
if ( static::is_mobile_plugin_active() ) {
return 1;
}
return $value;
}
/** ----------------------------------------------------------------------------------------- */
/** MAIN HELPERS ============================================================================ */
/** ----------------------------------------------------------------------------------------- */
/**
* Update the config file and the advanced cache file.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*/
public static function update_mobile_cache_activation() {
// Reset class cache.
static::reset_class_cache();
// Update the config file.
rocket_generate_config_file();
// Update the advanced cache file.
rocket_generate_advanced_cache_file();
// Flush htaccess file.
flush_rocket_htaccess();
}
/**
* Reset `is_mobile_plugin_active()` cache.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*/
public static function reset_class_cache() {
// Reset class cache.
unset( static::$is_mobile_active[ get_current_network_id() ][ get_current_blog_id() ] );
}
/**
* Get the concerned plugins.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*
* @return array
*/
public static function get_mobile_plugins() {
return [
'jetpack/jetpack.php' => [
'is_active_callback' => function() {
if ( ! class_exists( 'Jetpack' ) ) {
return false;
}
return \Jetpack::is_active() && \Jetpack::is_module_active( 'minileven' );
},
'activation_hook' => 'jetpack_activate_module_minileven',
'deactivation_hook' => 'jetpack_deactivate_module_minileven',
],
'wptouch/wptouch.php' => [],
'wiziapp-create-your-own-native-iphone-app/wiziapp.php' => [],
'wordpress-mobile-pack/wordpress-mobile-pack.php' => [],
'wp-mobilizer/wp-mobilizer.php' => [],
'wp-mobile-edition/wp-mobile-edition.php' => [],
'device-theme-switcher/dts_controller.php' => [],
'wp-mobile-detect/wp-mobile-detect.php' => [],
'easy-social-share-buttons3/easy-social-share-buttons3.php' => [],
];
}
/**
* Tell if a mobile plugin is active.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*
* @return bool True if a mobile plugin in the list is active, false otherwise.
*/
public static function is_mobile_plugin_active() {
$network_id = get_current_network_id();
$blog_id = get_current_blog_id();
if ( isset( static::$is_mobile_active[ $network_id ][ $blog_id ] ) ) {
return static::$is_mobile_active[ $network_id ][ $blog_id ];
}
if ( ! function_exists( '\is_plugin_active' ) ) {
include_once ABSPATH . 'wp-admin/includes/plugin.php';
}
if ( ! isset( static::$is_mobile_active[ $network_id ] ) ) {
static::$is_mobile_active[ $network_id ] = [];
}
foreach ( static::get_mobile_plugins() as $mobile_plugin => $mobile_plugin_data ) {
if ( \is_plugin_active( $mobile_plugin ) &&
isset( $mobile_plugin_data['is_active_callback'] ) &&
call_user_func( $mobile_plugin_data['is_active_callback'] ) ) {
static::$is_mobile_active[ $network_id ][ $blog_id ] = true;
return true;
}
if ( \is_plugin_active( $mobile_plugin ) &&
! isset( $mobile_plugin_data['is_active_callback'] ) ) {
static::$is_mobile_active[ $network_id ][ $blog_id ] = true;
return true;
}
}
static::$is_mobile_active[ $network_id ][ $blog_id ] = false;
return false;
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace WP_Rocket\Subscriber\Third_Party\Plugins;
use WP_Rocket\Event_Management\Subscriber_Interface;
defined( 'ABSPATH' ) || exit;
/**
* Class that handles events related to Next Gen Gallery.
*
* @since 3.3.1
* @author Remy Perona
*/
class NGG_Subscriber implements Subscriber_Interface {
/**
* Return an array of events that this subscriber wants to listen to.
*
* @since 3.3.1
* @author Remy Perona
*
* @return array
*/
public static function get_subscribed_events() {
if ( ! class_exists( 'C_NextGEN_Bootstrap' ) ) {
return;
}
return [
'run_ngg_resource_manager' => 'deactivate_resource_manager',
];
}
/**
* Deactivate NGG Resource Manager to prevent conflict with WP Rocket output buffering
*
* @since 3.3.1
* @author Remy Perona
*
* @return bool
*/
public function deactivate_resource_manager() {
return false;
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace WP_Rocket\Subscriber\Third_Party\Plugins;
use WP_Rocket\Event_Management\Subscriber_Interface;
/**
* Compatibility class for SyntaxHighlighter plugin
*
* @since 3.3.1
* @author Remy Perona
*/
class SyntaxHighlighter_Subscriber implements Subscriber_Interface {
/**
* Return an array of events that this subscriber wants to listen to.
*
* @since 3.3.1
* @author Remy Perona
*
* @return array
*/
public static function get_subscribed_events() {
if ( ! class_exists( 'SyntaxHighlighter' ) ) {
return [];
}
return [
'rocket_exclude_defer_js' => 'exclude_defer_js_syntaxhighlighter_scripts',
'rocket_exclude_js' => 'exclude_minify_js_syntaxhighlighter_scripts',
];
}
/**
* Adds SyntaxHighlighter scripts to defer JS exclusion
*
* @since 3.3.1
* @author Remy Perona
*
* @param array $excluded_scripts Array of scripts to exclude.
* @return array
*/
public function exclude_defer_js_syntaxhighlighter_scripts( $excluded_scripts ) {
return array_merge(
$excluded_scripts,
[
'syntaxhighlighter/syntaxhighlighter3/scripts/(.*).js',
'syntaxhighlighter/syntaxhighlighter2/scripts/(.*).js',
]
);
}
/**
* Adds SyntaxHighlighter scripts to minify/combine JS exclusion
*
* @since 3.3.1
* @author Remy Perona
*
* @param array $excluded_scripts Array of scripts to exclude.
* @return array
*/
public function exclude_minify_js_syntaxhighlighter_scripts( $excluded_scripts ) {
return array_merge(
$excluded_scripts,
[
rocket_clean_exclude_file( plugins_url( 'syntaxhighlighter/syntaxhighlighter3/scripts/(.*).js' ) ),
rocket_clean_exclude_file( plugins_url( 'syntaxhighlighter/syntaxhighlighter2/scripts/(.*).js' ) ),
]
);
}
}

View File

@@ -0,0 +1,169 @@
<?php
namespace WP_Rocket\Subscriber\Third_Party\Plugins\Ecommerce;
use WP_Rocket\Event_Management\Event_Manager;
use WP_Rocket\Event_Management\Event_Manager_Aware_Subscriber_Interface;
/**
* BigCommerce compatibility subscriber
*
* @since 3.3.7
* @author Remy Perona
*/
class BigCommerce_Subscriber implements Event_Manager_Aware_Subscriber_Interface {
use \WP_Rocket\Traits\Config_Updater;
/**
* The WordPress Event Manager
*
* @var Event_Manager;
*/
protected $event_manager;
/**
* {@inheritdoc}
*
* @param Event_Manager $event_manager The WordPress Event Manager.
*/
public function set_event_manager( Event_Manager $event_manager ) {
$this->event_manager = $event_manager;
}
/**
* {@inheritdoc}
*/
public static function get_subscribed_events() {
$events = [
'activate_bigcommerce/bigcommerce.php' => [ 'activate_bigcommerce', 11 ],
'deactivate_bigcommerce/bigcommerce.php' => [ 'deactivate_bigcommerce', 11 ],
];
if ( function_exists( 'bigcommerce_init' ) ) {
$events['update_option_bigcommerce_login_page_id'] = [ 'after_update_single_option', 10, 2 ];
$events['update_option_bigcommerce_account_page_id'] = [ 'after_update_single_option', 10, 2 ];
$events['update_option_bigcommerce_address_page_id'] = [ 'after_update_single_option', 10, 2 ];
$events['update_option_bigcommerce_orders_page_id'] = [ 'after_update_single_option', 10, 2 ];
$events['update_option_bigcommerce_cart_page_id'] = [ 'after_update_single_option', 10, 2 ];
$events['update_option_bigcommerce_checkout_page_id'] = [ 'after_update_single_option', 10, 2 ];
$events['shutdown'] = 'maybe_update_config';
$events['transition_post_status'] = [ 'maybe_exclude_page', 10, 3 ];
$events['rocket_cache_reject_uri'] = [
[ 'exclude_pages' ],
];
}
return $events;
}
/**
* Add exclusions when activating the BigCommerce plugin
*
* @since 3.3.7
* @author Rémy Perona
*/
public function activate_bigcommerce() {
$this->event_manager->add_callback( 'rocket_cache_reject_uri', [ $this, 'exclude_pages' ] );
// Update .htaccess file rules.
flush_rocket_htaccess();
// Regenerate the config file.
rocket_generate_config_file();
}
/**
* Remove exclusions when deactivating the BigCommerce plugin
*
* @since 3.3.7
* @author Rémy Perona
*/
public function deactivate_woocommerce() {
$this->event_manager->remove_callback( 'rocket_cache_reject_uri', [ $this, 'exclude_pages' ] );
// Update .htaccess file rules.
flush_rocket_htaccess();
// Regenerate the config file.
rocket_generate_config_file();
}
/**
* Maybe regenerate the htaccess & config file if a BigCommerce page is published
*
* @since 3.3.7
* @author Remy Perona
*
* @param string $new_status New post status.
* @param string $old_status Old post status.
* @param WP_Post $post Post object.
* @return bool
*/
public function maybe_exclude_page( $new_status, $old_status, $post ) {
if ( 'publish' === $old_status || 'publish' !== $new_status ) {
return false;
}
if ( get_option( 'bigcommerce_login_page_id' ) !== $post->ID && get_option( 'bigcommerce_account_page_id' ) !== $post->ID && get_option( 'bigcommerce_address_page_id' ) !== $post->ID && get_option( 'bigcommerce_orders_page_id' ) !== $post->ID && get_option( 'bigcommerce_cart_page_id' ) !== $post->ID && get_option( 'bigcommerce_checkout_page_id' ) !== $post->ID ) {
return false;
}
// Update .htaccess file rules.
flush_rocket_htaccess();
// Regenerate the config file.
rocket_generate_config_file();
return true;
}
/**
* Exclude BigCommerce login, cart, checkout, account, address and orders pages from caching
*
* @since 3.3.7
*
* @param array $urls An array of excluded pages.
* @return array
*/
public function exclude_pages( $urls ) {
$checkout_urls = $this->exclude_page( get_option( 'bigcommerce_checkout_page_id' ) );
$cart_urls = $this->exclude_page( get_option( 'bigcommerce_cart_page_id' ) );
$account_urls = $this->exclude_page( get_option( 'bigcommerce_account_page_id' ) );
$login_urls = $this->exclude_page( get_option( 'bigcommerce_login_page_id' ) );
$address_urls = $this->exclude_page( get_option( 'bigcommerce_address_page_id' ) );
$orders_urls = $this->exclude_page( get_option( 'bigcommerce_orders_page_id' ) );
return array_merge( $urls, $checkout_urls, $cart_urls, $account_urls, $login_urls, $address_urls, $orders_urls );
}
/**
* Excludes BigCommerce checkout page from cache
*
* @since 3.3.7
* @author Remy Perona
*
* @param int $page_id ID of page to exclude.
* @param string $post_type Post type of the page.
* @param string $pattern Pattern to use for the exclusion.
* @return array
*/
private function exclude_page( $page_id, $post_type = 'page', $pattern = '' ) {
$urls = [];
if ( ! $page_id ) {
return $urls;
}
if ( $page_id <= 0 || (int) get_option( 'page_on_front' ) === $page_id ) {
return $urls;
}
if ( 'publish' !== get_post_status( $page_id ) ) {
return $urls;
}
$urls = get_rocket_i18n_translated_post_urls( $page_id, $post_type, $pattern );
return $urls;
}
}

View File

@@ -0,0 +1,395 @@
<?php
namespace WP_Rocket\Subscriber\Third_Party\Plugins\Security;
use WP_Rocket\Admin\Options_Data as Options;
use WP_Rocket\Event_Management\Subscriber_Interface;
use WP_Rocket\Logger\Logger;
defined( 'ABSPATH' ) || exit;
/**
* Sucuri Security compatibility.
* %s is here for the other query args.
*
* @since 3.2
* @author Grégory Viguier
*/
class Sucuri_Subscriber implements Subscriber_Interface {
/**
* URL of the API.
*
* @var string
* @since 3.2
* @author Grégory Viguier
*/
const API_URL = 'https://waf.sucuri.net/api?v2&%s';
/**
* Instance of the Option_Data class.
*
* @var Options
* @since 3.2
* @access private
* @author Grégory Viguier
*/
private $options;
/**
* Constructor.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*
* @param Options $options Instance of the Option_Data class.
*/
public function __construct( Options $options ) {
$this->options = $options;
}
/**
* {@inheritdoc}
*/
public static function get_subscribed_events() {
return [
'after_rocket_clean_domain' => 'maybe_clean_firewall_cache',
'after_rocket_clean_post' => 'maybe_clean_firewall_cache',
'after_rocket_clean_term' => 'maybe_clean_firewall_cache',
'after_rocket_clean_user' => 'maybe_clean_firewall_cache',
'after_rocket_clean_home' => 'maybe_clean_firewall_cache',
'after_rocket_clean_files' => 'maybe_clean_firewall_cache',
'admin_post_rocket_purge_sucuri' => 'do_admin_post_rocket_purge_sucuri',
'admin_notices' => 'maybe_print_notice',
];
}
/** ----------------------------------------------------------------------------------------- */
/** HOOK CALLBACKS ========================================================================== */
/** ----------------------------------------------------------------------------------------- */
/**
* Clear Sucuri firewall cache.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*/
public function maybe_clean_firewall_cache() {
static $done = false;
if ( $done ) {
return;
}
$done = true;
if ( ! $this->options->get( 'sucury_waf_cache_sync', 0 ) ) {
return;
}
$this->clean_firewall_cache();
}
/**
* Ajax callback to empty Sucury cache.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*/
public function do_admin_post_rocket_purge_sucuri() {
if ( empty( $_GET['_wpnonce'] ) || ! wp_verify_nonce( $_GET['_wpnonce'], 'rocket_purge_sucuri' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
wp_nonce_ays( '' );
}
if ( ! current_user_can( 'rocket_purge_sucuri_cache' ) ) {
wp_nonce_ays( '' );
}
$purged = $this->clean_firewall_cache();
if ( is_wp_error( $purged ) ) {
$purged_result = [
'result' => 'error',
/* translators: %s is the error message returned by the API. */
'message' => sprintf( __( 'Sucuri cache purge error: %s', 'rocket' ), $purged->get_error_message() ),
];
} else {
$purged_result = [
'result' => 'success',
'message' => __( 'The Sucuri cache is being cleared. Note that it may take up to two minutes for it to be fully flushed.', 'rocket' ),
];
}
set_transient( get_current_user_id() . '_sucuri_purge_result', $purged_result );
wp_safe_redirect( esc_url_raw( wp_get_referer() ) );
die();
}
/**
* Print an admin notice if the cache failed to be cleared.
*
* @since 3.2
* @access public
* @author Grégory Viguier
*/
public function maybe_print_notice() {
if ( ! current_user_can( 'rocket_purge_sucuri_cache' ) ) {
return;
}
if ( ! is_admin() ) {
return;
}
$user_id = get_current_user_id();
$notice = get_transient( $user_id . '_sucuri_purge_result' );
if ( ! $notice ) {
return;
}
delete_transient( $user_id . '_sucuri_purge_result' );
rocket_notice_html(
[
'status' => $notice['result'],
'message' => $notice['message'],
]
);
}
/** ----------------------------------------------------------------------------------------- */
/** TOOLS =================================================================================== */
/** ----------------------------------------------------------------------------------------- */
/**
* Tell if a API key is well formatted.
*
* @since 3.2.3
* @access public
* @author Grégory Viguier
*
* @param string $api_key An API kay.
* @return array|bool An array with the keys 'k' and 's' (required by the API) if valid. False otherwise.
*/
public static function is_api_key_valid( $api_key ) {
if ( '' !== $api_key && preg_match( '@^(?<k>[a-z0-9]{32})/(?<s>[a-z0-9]{32})$@', $api_key, $matches ) ) {
return $matches;
}
return false;
}
/**
* Clear Sucuri firewall cache.
*
* @since 3.2
* @access private
* @author Grégory Viguier
*
* @return bool|object True on success. A WP_Error object on failure.
*/
private function clean_firewall_cache() {
$api_key = $this->get_api_key();
if ( is_wp_error( $api_key ) ) {
return $api_key;
}
$response = $this->request_api(
[
'a' => 'clear_cache',
'k' => $api_key['k'],
's' => $api_key['s'],
]
);
if ( is_wp_error( $response ) ) {
return $response;
}
Logger::info(
'Sucuri firewall cache cleared.',
[
'sucuri firewall cache',
]
);
return true;
}
/**
* Get the API key.
*
* @since 3.2
* @access private
* @author Grégory Viguier
*
* @return array|object An array with the keys 'k' and 's', required by the API. A WP_Error object if no key or invalid key.
*/
private function get_api_key() {
$api_key = trim( $this->options->get( 'sucury_waf_api_key', '' ) );
if ( ! $api_key ) {
Logger::error(
'API key was not found.',
[
'sucuri firewall cache',
]
);
return new \WP_Error( 'no_sucuri_api_key', __( 'Sucuri firewall API key was not found.', 'rocket' ) );
}
$matches = self::is_api_key_valid( $api_key );
if ( ! $matches ) {
Logger::error(
'API key is invalid.',
[
'sucuri firewall cache',
]
);
return new \WP_Error( 'invalid_sucuri_api_key', __( 'Sucuri firewall API key is invalid.', 'rocket' ) );
}
return [
'k' => $matches['k'],
's' => $matches['s'],
];
}
/**
* Request against the API.
*
* @since 3.2
* @access private
* @author Grégory Viguier
*
* @param array $params Parameters to send.
* @return array|object The response data on success. A WP_Error object on failure.
*/
private function request_api( $params = [] ) {
$params['time'] = time();
$params = $this->build_query( $params );
$url = sprintf( static::API_URL, $params );
try {
/**
* Filters the arguments for the Sucuri API request
*
* @since 3.3.4
* @author Soponar Cristina
*
* @param array $args Arguments for the request.
*/
$args = apply_filters(
'rocket_sucuri_api_request_args',
[
'timeout' => 5,
'redirection' => 5,
'httpversion' => '1.1',
'blocking' => true,
/** This filter is documented in wp-includes/class-wp-http-streams.php */
'sslverify' => apply_filters( 'https_ssl_verify', true ), // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
]
);
$response = wp_remote_get( $url, $args );
} catch ( \Exception $e ) {
Logger::error(
'Error when contacting the API.',
[
'sucuri firewall cache',
'url' => $url,
'response' => $e->getMessage(),
]
);
return new \WP_Error( 'error_sucuri_api', __( 'Error when contacting Sucuri firewall API.', 'rocket' ) );
}
if ( is_wp_error( $response ) ) {
Logger::error(
'Error when contacting the API.',
[
'sucuri firewall cache',
'url' => $url,
'response' => $response->get_error_message(),
]
);
/* translators: %s is an error message. */
return new \WP_Error( 'wp_error_sucuri_api', sprintf( __( 'Error when contacting Sucuri firewall API. Error message was: %s', 'rocket' ), $response->get_error_message() ) );
}
$contents = wp_remote_retrieve_body( $response );
if ( ! $contents ) {
Logger::error(
'Could not get a response from the API.',
[
'sucuri firewall cache',
'url' => $url,
'response' => $response,
]
);
return new \WP_Error( 'sucuri_api_no_response', __( 'Could not get a response from the Sucuri firewall API.', 'rocket' ) );
}
$data = @json_decode( $contents, true );
if ( ! $data || ! is_array( $data ) ) {
Logger::error(
'Invalid response from the API.',
[
'sucuri firewall cache',
'url' => $url,
'response_body' => $contents,
]
);
return new \WP_Error( 'sucuri_api_invalid_response', __( 'Got an invalid response from the Sucuri firewall API.', 'rocket' ) );
}
if ( empty( $data['status'] ) ) {
Logger::error(
'The action failed.',
[
'sucuri firewall cache',
'url' => $url,
'response_data' => $data,
]
);
if ( empty( $data['messages'] ) || ! is_array( $data['messages'] ) ) {
return new \WP_Error( 'sucuri_api_error_status', __( 'The Sucuri firewall API returned an unknown error.', 'rocket' ) );
}
/* translators: %s is an error message. */
$message = _n( 'The Sucuri firewall API returned the following error: %s', 'The Sucuri firewall API returned the following errors: %s', count( $data['messages'] ), 'rocket' );
$message = sprintf( $message, '<br/>' . implode( '<br/>', $data['messages'] ) );
return new \WP_Error( 'sucuri_api_error_status', $message );
}
return $data;
}
/**
* An i18n-firendly alternative to the built-in PHP method `http_build_query()`.
*
* @param array|object $params An array or object containing properties.
* @return string A URL-encoded string.
*/
private function build_query( $params ) {
if ( ! $params ) {
return '';
}
$params = (array) $params;
foreach ( $params as $param => $value ) {
$params[ $param ] = $param . '=' . rawurlencode( (string) $value );
}
return implode( '&', $params );
}
}