This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@ -0,0 +1,175 @@
<?php
namespace ShortPixel\CriticalCSS;
use ComposePress\Core\Abstracts\Component;
use ShortPixel\CriticalCSS;
/**
* Class API
*
* @property CriticalCSS $plugin
*/
class API extends Component {
const STATUS_UNKNOWN = 'JOB_UNKNOWN';
const STATUS_QUEUED = 'JOB_QUEUED';
const STATUS_ONGOING = 'JOB_ONGOING';
const STATUS_DONE = 'JOB_DONE';
const STATUS_REMOVED = 'JOB_REMOVED';
//const BASE_URL = 'https://stgcriticalapi.shortpixel.com/';
const BASE_URL = 'https://api.criticalcss.co/';
/**
* @var
*/
protected $api_key;
/**
* CriticalCSS_API constructor.
*
* @param $api_key
*/
public function __construct( $api_key = null ) {
$this->api_key = $api_key;
}
/**
* @return bool
*/
public function ping() {
$response = $this->_send_request( 'get', 'ping' );
$response = (array) $response;
return empty( $response );
}
/**
* @param string $type
* @param string $endpoint
* @param bool $auth
* @param array $query_args
* @param array $args
*
* @return array|bool|mixed|object
*/
private function _send_request( $type, $endpoint, $auth = true, $query_args = [], $args = [] ) {
if ( empty( $this->api_key ) ) {
return false;
}
$type = strtolower( $type );
$func = "wp_remote_{$type}";
if ( ! function_exists( $func ) ) {
return false;
}
$api_url = self::BASE_URL;
$query_args = array_merge( $query_args, [
'version' => $this->plugin->get_version(),
] );
$auth_args = [];
if ( $auth ) {
$auth_args = [
'headers' => [
'Authorization' => 'JWT ' . $this->api_key,
],
];
//$api_url .= 'premium/';
}
$api_url .= $endpoint;
$all_args = array_merge_recursive( $auth_args, $args );
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_API) && FileLog::instance()->log("API " . $type . " CALL. URL: " . $api_url . " ARGS:", $all_args);
//(SPCCSS_DEBUG & FileLog::DEBUG_AREA_API) && FileLog::instance()->log("STACK:", debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
$response = $func( add_query_arg( $query_args, $api_url ), $all_args );
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_API) && FileLog::instance()->log("API CALL RESPONSE: ", $response);
if ( $response instanceof \WP_Error ) {
return $response;
}
if ( is_array( $response ) ) {
return json_decode( $response['body'] );
}
return false;
}
/**
* @param $item_id
*
* @return array|bool|mixed|object
*/
public function get_result( $item_id ) {
return $this->_send_request( 'get', 'results', true, [
'resultId' => $item_id,
] );
}
/**
* @param array $item
*
* @return array|bool|mixed|object
*/
public function generate( array $item ) {
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_API) && FileLog::instance()->log("Called Generate");
//when manually ran from api_run, it throws a fatal: "Uncaught Exception: Plugin property is equal to self. Did you forget to set the parent or create a getter?"
$force_styles = $this->plugin->settings_manager->get_setting( 'force_include_styles' );
$force_selectors = [];
if ( ! empty( $force_styles ) ) {
$lines = explode( "\n", $force_styles );
$lines = array_map( 'trim', $lines );
foreach ( $lines as $index => $line ) {
$selector = [ 'value' => $line ];
if ( preg_match( '/^\/.*?\/[gimy]*$/', $line ) ) {
$selector['type'] = 'RegExp';
}
$force_selectors[] = $selector;
}
}
$response = $this->_send_request( 'post', 'generate', true, [], [
'body' => [
'height' => 900,
'width' => 1300,
'url' => $this->plugin->get_permalink( $item ),
'aff' => 3,
'version' => $this->plugin->get_version(),
'wpCriticalCssQueueLength' => $this->plugin->api_queue->get_length(),
'forceInclude' => $force_selectors,
],
] );
return $response instanceof \WP_Error ? $response : $response->job;
}
public function get_invalid_url_regexes() {
$cache_name = CriticalCSS::LANG_DOMAIN . '_invalid_urls';
$cache = $this->plugin->cache_manager->get_store()->get_transient( $cache_name );
if ( empty( $cache ) ) {
$response = $this->_send_request( 'get', 'invalid-generate-url-rules', false );
$cache = $response->rules;
$this->plugin->cache_manager->get_store()->set_transient( $cache_name, $cache, WEEK_IN_SECONDS * 2 );
}
return $cache;
}
/**
*
*/
public function init() {
// noop
}
/**
* @param mixed $api_key
*/
public function set_api_key( $api_key ) {
$this->api_key = $api_key;
}
/**
* @return mixed
*/
public function get_api_key() {
return $this->api_key;
}
}

View File

@ -0,0 +1,171 @@
<?php
namespace ShortPixel\CriticalCSS\API\Background;
use ShortPixel\CriticalCSS\API;
use ShortPixel\CriticalCSS\FileLog;
use \ShortPixel\CriticalCSS\Background\ProcessAbstract;
/**
* Class Process
*
* @package ShortPixel\CriticalCSS\API\Background
*/
class Process extends ProcessAbstract {
protected $action = 'shortpixel_critical_css_api';
private $ping_checked = false;
/**
* @var \ShortPixel\CriticalCSS\API
*/
private $api;
/**
* Process constructor.
*
* @param \ShortPixel\CriticalCSS\API $api
*/
public function __construct( API $api ) {
$this->api = $api;
parent::__construct();
}
/**
* Task
*
* Override this method to perform any actions required on each
* queue item. Return the modified item for further processing
* in the next pass through. Or, return false to remove the
* item from the queue.
*
* @param mixed $item Queue item to iterate over
*
* @return mixed
*/
public function task( $item, $return_job_done = false ) {
if ( null === $this->api->parent ) {
$this->api->parent = shortpixel_critical_css();
}
$settings = shortpixel_critical_css()->settings_manager->settings;
if ( empty( $settings ) ) {
return false;
}
//use beta api key if it is not entered in settings
if( empty( $settings['apikey'] ) ) {
$this->api->api_key = $this->api->parent->beta_api_key;
} else {
$this->api->api_key = $settings['apikey'];
}
if ( ! empty( $item['timestamp'] ) && $item['timestamp'] + 8 >= time() ) {
return $item;
}
if ( ! $this->ping_checked ) {
if ( $this->api->ping() ) {
$this->ping_checked = true;
} else {
return false;
}
}
$item['timestamp'] = time();
$url = shortpixel_critical_css()->get_permalink( $item );
if ( empty( $url ) ) {
return false;
}
$bad_urls = $this->api->get_invalid_url_regexes();
$bad_urls = array_filter( $bad_urls, function ( $regex ) use ( $url ) {
return preg_match( "~$regex~", $url );
} );
if ( ! empty( $bad_urls ) ) {
return false;
}
if ( 2083 <= strlen( $url ) ) {
return false;
}
if ( ! empty( $item['queue_id'] ) ) {
$result = $this->api->get_result( $item['queue_id'] );
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_API) && FileLog::instance()->log(" GET RESULT RETURNED ", $result);
if ( $result instanceof \WP_Error ) {
return false;
}
if ( ! empty( $result->status ) ) {
$item['status'] = $result->status;
}
// @codingStandardsIgnoreLine
if ( ! empty( $result->resultStatus ) ) {
// @codingStandardsIgnoreLine
$item['result_status'] = $result->resultStatus;
}
if ( ! empty( $result->error ) || in_array( $result->status, [
'JOB_UNKNOWN',
'JOB_FAILED',
] ) ) {
unset( $item['queue_id'] );
return $item;
}
if ( 'JOB_QUEUED' === $result->status ) {
// @codingStandardsIgnoreLine
$item['queue_index'] = @$result->queueIndex;
return $item;
}
if ( 'JOB_ONGOING' === $result->status ) {
return $item;
}
if ( 'JOB_DONE' === $result->status ) {
$item['code'] = $result->code;
// @codingStandardsIgnoreLine
if ( 'GOOD' === $result->resultStatus && ! empty( $result->css ) ) {
shortpixel_critical_css()->integration_manager->disable_integrations();
if ( !empty( $item['template'] ) || !empty( $item['post_type'] ) ) {
$template = !empty( $item['template'] )?$item['template']:null;
$post_type = !empty( $item['post_type'] )?$item['post_type']:null;
$logs = shortpixel_critical_css()->template_log->get( $template, $post_type );
foreach ( $logs as $log ) {
$url = shortpixel_critical_css()->get_permalink( $log );
if ( ! parse_url( $url, PHP_URL_QUERY ) ) {
shortpixel_critical_css()->cache_manager->purge_page_cache( $log['type'], $log['object_id'], $url );
}
shortpixel_critical_css()->template_log->delete( $log['object_id'], $log['type'], $log['url'] );
}
shortpixel_critical_css()->cache_manager->purge_page_cache( $item['type'], $item['object_id'], shortpixel_critical_css()->get_permalink( $item ) );
} else {
shortpixel_critical_css()->cache_manager->purge_page_cache( $item['type'], $item['object_id'], shortpixel_critical_css()->get_permalink( $item ) );
}
shortpixel_critical_css()->integration_manager->enable_integrations();
shortpixel_critical_css()->data_manager->set_cache( $item, $result->css );
if ( empty( $item['template'] ) ) {
shortpixel_critical_css()->data_manager->set_css_hash( $item, $item['css_hash'] );
shortpixel_critical_css()->data_manager->set_html_hash( $item, $item['html_hash'] );
}
if( empty($item['url']) ) {
$item['url'] = preg_replace('%nocache/$%', '', $url, 1);
}
shortpixel_critical_css()->log->insert( $item );
}
if($return_job_done) {
return $result;
}
} else {
unset( $item['queue_id'] );
return $item;
}
} else {
$result = $this->api->generate( $item );
if ( $result instanceof \WP_Error || empty($result->id) ) {
return false;
}
$item['queue_id'] = $result->id;
$item['status'] = $result->status;
return $item;
}
return false;
}
}

View File

@ -0,0 +1,677 @@
<?php
namespace ShortPixel\CriticalCSS\Admin;
use ComposePress\Core\Abstracts\Component;
use ShortPixel\CriticalCSS;
use ShortPixel\CriticalCSS\Admin\UI\Post;
use ShortPixel\CriticalCSS\Admin\UI\Term;
use ShortPixel\CriticalCSS\API;
use ShortPixel\CriticalCSS\Queue\API\Table as APITable;
use ShortPixel\CriticalCSS\Queue\Log\Table as LogTable;
use ShortPixel\CriticalCSS\Queue\Web\Check\Table as WebCheckTable;
use ShortPixel\CriticalCSS\Settings\API as SettingsAPI;
/**
* Class UI
*
* @package ShortPixel\CriticalCSS\Admin
* @property \ShortPixel\CriticalCSS $plugin
*/
class UI extends Component {
/**
* @var \ShortPixel\CriticalCSS\Settings\API
*/
private $settings_ui;
/**
* @var \ShortPixel\CriticalCSS\Queue\ListTableAbstract
*/
private $api_table;
/**
* @var \ShortPixel\CriticalCSS\API
*/
private $api;
/**
* @var \ShortPixel\CriticalCSS\Queue\Web\Check\Table
*/
private $web_check_table;
/**
* @var \ShortPixel\CriticalCSS\Queue\Log\Table
*/
private $log_table;
/**
* @var \ShortPixel\CriticalCSS\Admin\UI\Post
*/
private $post_ui;
/**
* @var \ShortPixel\CriticalCSS\Admin\UI\Term
*/
private $term_ui;
/**
* UI constructor.
*
* @param \ShortPixel\CriticalCSS\API|\ShortPixel\CriticalCSS\Settings\API $settings_ui
* @param \ShortPixel\CriticalCSS\API $api
* @param APITable $api_table
* @param WebCheckTable $web_check_table
* @param \ShortPixel\CriticalCSS\Queue\Log\Table $log_table
* @param \ShortPixel\CriticalCSS\Admin\UI\Post $post_ui
* @param \ShortPixel\CriticalCSS\Admin\UI\Term $term_ui
*/
public function __construct( SettingsAPI $settings_ui, API $api, APITable $api_table, WebCheckTable $web_check_table, LogTable $log_table, Post $post_ui, Term $term_ui ) {
$this->settings_ui = $settings_ui;
$this->api = $api;
$this->api_table = $api_table;
$this->web_check_table = $web_check_table;
$this->log_table = $log_table;
$this->post_ui = $post_ui;
$this->term_ui = $term_ui;
if(!defined('DISALLOW_FILE_EDIT') || !DISALLOW_FILE_EDIT ) {
add_action('customize_register',[$this, 'spccss_customize_register']);
}
}
/**
* @return \ShortPixel\CriticalCSS\Queue\ListTableAbstract
*/
public function get_api_table() {
return $this->api_table;
}
/**
* @return \ShortPixel\CriticalCSS\API
*/
public function get_api() {
return $this->api;
}
/**
* @return \ShortPixel\CriticalCSS\Queue\Web\Check\Table
*/
public function get_web_check_table() {
return $this->web_check_table;
}
/**
* @return \ShortPixel\CriticalCSS\Queue\Log\Table
*/
public function get_log_table() {
return $this->log_table;
}
/**
*
*/
public function init() {
$this->setup_components();
$this->api_table->set_queue( $this->plugin->api_queue );
$this->log_table->set_queue( $this->plugin->log );
$this->web_check_table->set_queue( $this->plugin->web_check_queue );
if ( is_admin() ) {
add_action( 'network_admin_menu', [
$this,
'settings_init',
] );
add_action( 'admin_menu', [
$this,
'settings_init',
] );
add_action( 'pre_update_option_shortpixel_critical_css', [
$this,
'sync_options',
], 10, 2 );
add_action( 'update_option_shortpixel_critical_css_web_check_queue', [
$this,
'delete_dummy_option',
], 10, 2 );
add_action( 'update_option_shortpixel_critical_css_api_queue', [
$this,
'delete_dummy_option',
] );
add_action( 'update_option_shortpixel_critical_css_log', [
$this,
'delete_dummy_option',
] );
}
}
/**
* Build settings page configuration
*/
public function settings_init() {
wp_enqueue_script( 'sp_ccss_apikey', plugin_dir_url( dirname(__FILE__ ) ) . 'assets/js/apikey.min.js' , [], false, true );
wp_localize_script( 'sp_ccss_apikey', 'ccssApikeyLocal', [
'descriptionRegister' => __( 'API Key for ShortPixel\'s Critical CSS. Get yours for free at <a href="https://ShortPixel.com/free-sign-up" target="_blank">ShortPixel.com</a>. If you use our <a href="https://wordpress.org/plugins/shortpixel-image-optimiser/" target="_blank">ShortPixel Image Optimization</a> plugin, you can use the same API Key.', CriticalCSS::LANG_DOMAIN ),
'descriptionLogin' => __( 'API Key for ShortPixel\'s Critical CSS. Manage your key on <a href="https://ShortPixel.com/login" target="_blank">ShortPixel.com</a>', CriticalCSS::LANG_DOMAIN ),
'buttonText' => __( 'Validate & save api key' ),
] );
$api_key = $this->plugin->settings_manager->get_setting( 'apikey' );
if ( is_multisite() ) {
$hook = add_submenu_page( 'settings.php', 'ShortPixel Critical CSS Settings', 'SP Critical CSS', 'manage_network_options', CriticalCSS::OPTIONNAME, [
$this,
'settings_ui',
] );
} else {
$hook = add_options_page( 'ShortPixel Critical CSS Settings', 'SP Critical CSS', 'manage_options', CriticalCSS::OPTIONNAME, [
$this,
'settings_ui',
] );
}
add_action( "load-$hook", [
$this,
'screen_option',
] );
$this->settings_ui->add_section( [
'id' => $this->plugin->get_option_name(),
'title' => __( 'Options', CriticalCSS::LANG_DOMAIN ),
'desc' => ( $api_key === ''
? __('<h4>Thank you for installing ShortPixel Critical CSS! To start generating critical CSS for your pages, you will need an API key. You can get it for free when you register at <a href="https://www.ShortPixel.com/free-sign-up" target="_blank">ShortPixel.com</a></h4>', CriticalCSS::LANG_DOMAIN ) : ''),
] );
//api field rendered in js, there is only label and description
$this->settings_ui->add_field( $this->plugin->get_option_name(), [
'name' => 'apikey',
'label' => 'API Key',
'type' => 'hidden',
'autocomplete' => 'off',
'sanitize_callback' => [
$this,
'validate_criticalcss_apikey',
],
'desc' =>
($api_key === '')
? __( 'API Key for ShortPixel\'s Critical CSS. Get yours for free at <a href="https://ShortPixel.com/free-sign-up" target="_blank">ShortPixel.com</a>. If you use our <a href="https://wordpress.org/plugins/shortpixel-image-optimiser/" target="_blank">ShortPixel Image Optimization</a> plugin, you can use the same API Key.', CriticalCSS::LANG_DOMAIN )
: __( 'API Key for ShortPixel\'s Critical CSS. Manage your key on <a href="https://ShortPixel.com/login" target="_blank">ShortPixel.com</a>', CriticalCSS::LANG_DOMAIN ),
] );
if( $api_key === '' &&
is_null( $this->plugin->beta_api_key ) ) {
} else {
// if ('on' !== $this->plugin->settings_manager->get_setting('cache_mode')['templates']) {
// $this->settings_ui->add_field($this->plugin->get_option_name(), [
// 'name' => 'force_web_check',
// 'label' => 'Force Web Check',
// 'type' => 'checkbox',
// 'desc' => __('Force a web check on all pages for css changes. This will run for new web requests.', $this->plugin->get_lang_domain()),
// ]);
// }
if ('on' !== $this->plugin->settings_manager->get_setting('prioritize_manual_css')) {
$cacheModeOptions = [];
$cacheModeOptions['postTypes'] = __('Based on Post Types');
if( !function_exists('ct_plugin_setup') ) { //oxygen builder plugin is not compatible with templates cache
$cacheModeOptions['templates'] = __('Based on WordPress templates');
}
$cacheModeOptions['posts'] = __('Based on the individual URL');
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => 'cache_mode',
'label' => 'Cache Mode',
'type' => 'multicheck',
'options' => $cacheModeOptions,
'desc' => __('<p style="font-style: italic;">At least one option should be selected. </p>', $this->plugin->get_lang_domain()),
]);
$postTypes = $this->plugin->get_cache_mode_posttypes();
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => 'post_type_values',
'label' => 'Post Types',
'type' => 'multicheck',
'options' => $postTypes,
//'desc' => __('Cache Critical CSS based on WordPress templates and not the post, page, term, author page, or arbitrary url. <p style="font-weight: bold;">This option may be useful if your website contains lots of content, pages, posts, etc. </p>', $this->plugin->get_lang_domain()),
]);
$templates = $this->plugin->get_cache_mode_templates();
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => 'template_values',
'label' => 'Templates',
'type' => 'multicheck',
'options' => $templates,
//'desc' => __('Cache Critical CSS based on WordPress templates and not the post, page, term, author page, or arbitrary url. <p style="font-weight: bold;">This option may be useful if your website contains lots of content, pages, posts, etc. </p>', $this->plugin->get_lang_domain()),
]);
}
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => 'prioritize_manual_css',
'label' => 'Enable Manual CSS Override',
'type' => 'checkbox',
'desc' => __('Allow the CSS for a post, term, post type or taxonomy to always override the generated CSS. By default, the generated CSS takes precedence if it is present.', $this->plugin->get_lang_domain()),
]);
if (!apply_filters('shortpixel_critical_css_cache_integration', false)) {
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => 'web_check_interval',
'label' => 'Web Check Interval',
'type' => 'number',
'desc' => __('How often in seconds web pages should be checked for changes to regenerate the critical CSS for them.', $this->plugin->get_lang_domain()),
]);
}
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => 'force_include_styles',
'label' => 'Force Styles to be included',
'type' => 'textarea',
'desc' => __('A list of CSS selectors and/or regex patterns for CSS selectors', $this->plugin->get_lang_domain()),
'sanitize_callback' => [
$this,
'validate_force_include_styles',
],
]);
if(!defined('DISALLOW_FILE_EDIT') || !DISALLOW_FILE_EDIT ) {
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => 'fallback_css',
'label' => 'Fallback CSS',
'type' => 'html',
'desc' => __('<p>Global CSS to be uses as fallback when generated and manual post CSS are not available can be added via your <a href="customize.php" target="_blank"><strong>Customizer</strong></a>. In the <strong>Custom CSS</strong> section, please fill in the <strong>Fallback Critical CSS</strong> field.</p>', $this->plugin->get_lang_domain()),
]);
}
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => 'lazy_load_css_files',
'label' => 'Lazy-load CSS files',
'type' => 'checkbox',
'desc' => __('Load CSS files asynchronously to speed up the Largest Contentful Paint of your pages (Core Web Vital).', $this->plugin->get_lang_domain()),
]);
if ('on' === $this->plugin->settings_manager->get_setting('prioritize_manual_css')) {
foreach (get_post_types() as $post_type) {
$label = get_post_type_object($post_type)->labels->singular_name;
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => "single_post_type_css_{$post_type}",
'label' => 'Use Single CSS for ' . $label,
'type' => 'checkbox',
'desc' => sprintf(__('Use a single CSS for all %s items', $this->plugin->get_lang_domain()), $label),
]);
if ('on' === $this->plugin->settings_manager->get_setting("single_post_type_css_{$post_type}")) {
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => "single_post_type_css_{$post_type}_css",
'label' => $label . ' post type CSS input',
'type' => 'textarea',
'desc' => sprintf(__('Enter CSS for all %s items', $this->plugin->get_lang_domain()), $label),
]);
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => "single_post_type_css_{$post_type}_archive_css",
'label' => $label . ' post type archive CSS input',
'type' => 'textarea',
'desc' => sprintf(__('Enter CSS for %s archive listings', $this->plugin->get_lang_domain()), $label),
]);
}
}
foreach (get_taxonomies() as $taxonomy) {
$label = get_taxonomy($taxonomy)->labels->singular_name;
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => "single_taxonomy_css_{$taxonomy}",
'label' => 'Use Single CSS for ' . $label,
'type' => 'checkbox',
'desc' => sprintf(__('Use a single CSS for all %s items', $this->plugin->get_lang_domain()), $label),
]);
if ('on' === $this->plugin->settings_manager->get_setting("single_taxonomy_css_{$taxonomy}_css")) {
$this->settings_ui->add_field($this->plugin->get_option_name(), [
'name' => "single_taxonomy_css_{$taxonomy}_css",
'label' => $label . ' taxonomy CSS input',
'type' => 'textarea',
'desc' => sprintf(__('Enter CSS for all %s items', $this->plugin->get_lang_domain()), $label),
]);
}
}
}
}
$this->settings_ui->admin_init();
}
public function spccss_customize_register( $wp_customize ) {
$custom_css_setting = new \WP_Customize_Custom_CSS_Setting (
$wp_customize,
'custom_css[fallback_critical_css]',
array(
'capability' => 'edit_css',
'default' => '',
)
);
$wp_customize->add_setting( $custom_css_setting );
$wp_customize->add_control( new \WP_Customize_Code_Editor_Control (
$wp_customize,
'fallback_critical_css',
array(
'label' => __( 'Fallback Critical CSS' ),
'section' => 'custom_css',
'settings' => array( 'default' => $custom_css_setting->id ),
'code_type' => 'text/css',
'input_attrs' => array(
'aria-describedby' => 'editor-keyboard-trap-help-1 editor-keyboard-trap-help-2 editor-keyboard-trap-help-3 editor-keyboard-trap-help-4',
),
)
) );
}
/**
* Render settings page
*/
public function settings_ui() {
echo('<div class="wrap spccss-settings"><h1>' . __('ShortPixel Critical CSS Settings', $this->plugin->get_lang_domain()) . '</h1>');
require_once ABSPATH . 'wp-admin/options-head.php';
if ( ( '' !== $this->plugin->settings_manager->get_setting( 'apikey' ) ||
!is_null( $this->plugin->beta_api_key ) )
) {
$this->settings_ui->add_section( [
'id' => 'shortpixel_critical_css_web_check_queue',
'title' => 'Web Check Queue',
'form' => false,
] );
}
if ( '' !== $this->plugin->settings_manager->get_setting( 'apikey' ) ||
!is_null( $this->plugin->beta_api_key ) ) {
$this->settings_ui->add_section( [
'id' => 'shortpixel_critical_css_api_queue',
'title' => 'API Queue',
'form' => false,
] );
$this->settings_ui->add_section( [
'id' => 'shortpixel_critical_css_log',
'title' => 'Processed Log',
'form' => false,
] );
?>
<style type="text/css">
.form-table .api_queue > th, .form-table .web_check_queue > th {
display: none;
}
.no-items, .manage-column, .form-table .api_queue td, .form-table .web_check_queue td {
text-align: center !important;
}
.form-table th {
width: auto;
}
.group h2 {
display: none;
}
</style>
<?php ob_start(); ?>
<p>
<?php _e( 'This queue is designed to process your content only when the cache mode based on the URL is used. It detects changes to the content and sends them to the "API Queue" when they are found.', $this->plugin->get_lang_domain() ); ?>
</p>
<?php if( !$this->plugin->settings_manager->get_setting('loopback_available') ): ?>
<div class="error" style="text-align: left; margin: 10px 0;">
<p>
<?php _e( 'The Web check queue may not work as intended because the web loop-back call failed. Please check your server configuration to make sure <code>wp_remote_get( home_url())</code> is allowed.', $this->plugin->get_lang_domain() ); ?>
</p>
</div>
<?php endif; ?>
<form method="post">
<?php
$this->web_check_table->prepare_items();
$this->web_check_table->display();
?>
</form>
<?php
$this->settings_ui->add_field( 'shortpixel_critical_css_web_check_queue', [
'name' => 'web_check_queue',
'label' => null,
'type' => 'html',
'desc' => ob_get_clean(),
] );
ob_start(); ?>
<p>
<?php _e( 'This queue processes requests by sending them to ShortPixel Critical CSS and waiting for them to be processed. When it\'s done, the supported cache is flushed and the page gets faster. :)', $this->plugin->get_lang_domain() ); ?>
</p>
<form method="post">
<?php
$this->api_table->prepare_items();
$this->api_table->display();
?>
</form>
<?php
$this->settings_ui->add_field( 'shortpixel_critical_css_api_queue', [
'name' => 'api_queue',
'label' => null,
'type' => 'html',
'desc' => ob_get_clean(),
] );
ob_start(); ?>
<p>
<?php _e( 'This is a list of all pages, links, and/or templates that have already been processed.', $this->plugin->get_lang_domain() ); ?>
</p>
<div id="ccss_modal_background" class="spccss-modal-background">
</div>
<div id="ccss_modal" class="spccss-modal">
<span class="spccss-close">&times;</span>
<div class="spccss-modal-content">
<p class="spccss-modal-css">...</p>
</div>
</div>
<style>
.spccss-settings .nav-tab-active {
background: #fff;
border-bottom: 1px solid white;
}
.spccss-settings .metabox-holder {
background-color: #fff;
border: 1px solid lightgrey;
border-top: none;
padding: 10px;
}
/* The Modal (background) */
.spccss-modal-background {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}
.spccss-modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
left: 25%;
top: 10%;
right: 10%;
bottom: 10%;
overflow: hidden;
z-index: 2;
border: 1px solid #888;
}
/* Modal Content/Box */
.spccss-modal-content {
background-color: #fefefe;
padding: 20px;
overflow: auto;
position: relative;
}
.spccss-modal-css {
white-space: pre;
}
/* The Close Button */
.spccss-close {
color: #aaa;
font-size: 28px;
font-weight: bold;
position: absolute;
right: 30px;
top: 10px;
z-index: 3;
}
.spccss-close:hover,
.spccss-close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
</style>
<form method="post">
<?php
$this->log_table->prepare_items();
$this->log_table->display();
?>
</form>
<?php
$this->settings_ui->add_field( 'shortpixel_critical_css_log', [
'name' => 'log',
'label' => null,
'type' => 'html',
'desc' => ob_get_clean(),
] );
}
$this->settings_ui->admin_init();
$this->settings_ui->show_navigation();
$this->settings_ui->show_forms();
?>
</div>
<?php
}
/**
* Validate API key is real and error if so
*
* @param $options
*
* @return bool
*/
public function validate_criticalcss_apikey( $options ) {
$valid = true;
if ( empty( $options['apikey'] ) ) {
return '';
}
$result = CriticalCSS\Settings\ApiKeyTools::validateAPIKey($options['apikey']);
if ( ! $result['status'] ) {
add_settings_error( 'apikey', 'invalid_apikey', $result['error'] );
$valid = false;
}
$this->api->set_api_key( $options['apikey'] );
if ( ! $this->api->ping() ) {
add_settings_error( 'apikey', 'invalid_apikey', 'ShortPixel API Key is invalid' );
$valid = false;
}
return $options['apikey'];
}
/**
* @param $options
*
* @return bool|string
*/
public function validate_force_include_styles( $options ) {
$valid = true;
if ( ! empty( $options['force_include_styles'] ) ) {
$lines = explode( "\n", $options['force_include_styles'] );
$lines = array_map( 'trim', $lines );
foreach ( $lines as $index => $line ) {
if ( preg_match( '/^\/.*?\/[gimy]*$/', $line ) ) {
preg_match( $line, null );
if ( PREG_NO_ERROR !== preg_last_error() ) {
add_settings_error( 'force_include_styles', 'invalid_force_include_styles_regex', sprintf( 'Line %d is an invalid regex for a force included style', $index + 1 ) );
$valid = false;
break;
}
}
}
if ( $valid ) {
$options['force_include_styles'] = implode( "\n", $lines );
}
}
return ! $valid ? $valid : $options['force_include_styles'];
}
/**
* @return \ShortPixel\CriticalCSS\Settings\API
*/
public function get_settings_ui() {
return $this->settings_ui;
}
/**
*
*/
public function screen_option() {
$this->api_table->init();
$this->web_check_table->init();
$this->log_table->init();
}
/**
* @param $value
* @param $old_value
*
* @return array
*/
public function sync_options( $value, $old_value ) {
$original_old_value = $old_value;
if ( ! is_array( $old_value ) ) {
$old_value = [];
}
if ( is_multisite() ) {
$old_value = $this->plugin->settings_manager->get_settings();
}
$value = array_merge( $old_value, $value );
if ( isset( $value['force_web_check'] ) && 'on' === $value['force_web_check'] ) {
$value['force_web_check'] = 'off';
$this->plugin->get_cache_manager()->purge_page_cache();
}
if ( isset($old_value['web_check_interval']) && $value['web_check_interval'] != $old_value['web_check_interval'] ) {
$scheduled = wp_next_scheduled( 'shortpixel_critical_css_purge_log' );
if ( $scheduled ) {
wp_unschedule_event( $scheduled, 'shortpixel_critical_css_purge_log' );
}
}
if ( is_multisite() ) {
update_site_option( $this->plugin->get_option_name(), $value );
$value = $original_old_value;
}
return $value;
}
/**
* @param $old_value
* @param $value
* @param string $option
*/
public function delete_dummy_option( $old_value, $value, $option ) {
delete_option( $option );
}
/**
* @return \ShortPixel\CriticalCSS\Admin\UI\Post
*/
public function get_post_ui() {
return $this->post_ui;
}
/**
* @return \ShortPixel\CriticalCSS\Admin\UI\Term
*/
public function get_term_ui() {
return $this->term_ui;
}
}

View File

@ -0,0 +1,133 @@
<?php
namespace ShortPixel\CriticalCSS\Admin\UI;
use ComposePress\Core\Abstracts\Component;
/**
* Class Post
*
* @package ShortPixel\CriticalCSS\Admin\UI
* @property \ShortPixel\CriticalCSS $plugin
*/
class Post extends Component {
/**
*
*/
public function init() {
if ( is_admin() ) {
add_action( 'init', [
$this,
'init_action',
] );
}
}
public function init_action() {
add_action( 'add_meta_boxes', [ $this, 'add_meta_box' ] );
add_action( 'save_post', [
$this,
'save_manual_css_meta_box',
] );
add_action( 'save_post', [
$this,
'save_override_css_meta_box',
] );
}
public function add_meta_box() {
foreach ( get_post_types() as $post_type ) {
add_meta_box( "{$this->plugin->get_safe_slug()}_css_options", __( 'ShortPixel Critical CSS Options', $this->plugin->get_lang_domain() ), [
$this,
'render_meta_box',
], $post_type );
}
}
/**
*
*/
public function render_meta_box() {
$slug = $this->plugin->get_safe_slug();
$object_id = $this->post->ID ?>
<?php
$css = $this->plugin->data_manager->get_item_data( [
'type' => 'post',
'object_id' => $object_id,
], 'manual_css' );
$override = $this->plugin->data_manager->get_item_data( [
'type' => 'post',
'object_id' => $object_id,
], 'override_css' );
$post_type = get_post_type_object( $this->post->post_type );
$post_type_name = get_post_type_object( $this->post->post_type )->label;
$post_type_name_singular = get_post_type_object( $this->post->post_type )->labels->singular_name;
?>
<table class="form-table">
<?php if ( apply_filters( 'shortpixel_critical_css_manual_post_css', true ) ) : ?>
<tr>
<td width="20%">
<label for="<?php echo $slug ?>_manual_css"><?php _e( 'Enter your manual critical css here:', $this->plugin->get_lang_domain() ); ?></label>
</td>
<td>
<textarea name="<?php echo $slug ?>_manual_css" id="<?php echo $slug ?>_manual_css"
class="widefat" rows="10"><?php echo $css ?></textarea>
</td>
</tr>
<?php endif; ?>
<?php
if ( $post_type->hierarchical ) : ?>
<tr>
<td width="20%">
<label for="<?php echo $slug ?>_override_css"><?php _e( 'Override Child Critical CSS', $this->plugin->get_lang_domain() ); ?></label>
</td>
<td>
<input type="checkbox" name="<?php echo $slug ?>_override_css"
id="<?php echo $slug ?>_override_css"
value="1" <?php checked( $override ) ?>/>
<span style="font-weight: bold;"><?php _e( sprintf( 'This will force any child %s of this %s to use this %s\'s critical css.', $post_type_name, $post_type_name_singular, $post_type_name_singular ), $this->plugin->get_lang_domain() ); ?></span>
</td>
</tr>
<?php endif; ?>
</table>
<h2>
<?php
?>
</h2>
<?php
}
/**
* @param $post_id
*/
public function save_manual_css_meta_box( $post_id ) {
$slug = $this->plugin->get_safe_slug();
$field = "{$slug}_manual_css";
if ( apply_filters( 'shortpixel_critical_css_manual_post_css', true ) && isset($_POST[$field]) ) {
$css = sanitize_textarea_field( $_POST["{$slug}_manual_css"] );
$this->plugin->data_manager->set_item_data( [
'type' => 'post',
'object_id' => $post_id,
], 'manual_css', $css );
}
}
public function save_override_css_meta_box( $object_id ) {
$slug = $this->plugin->get_safe_slug();
$post_type = get_post_type_object( get_post_type( $object_id ) );
if ( $post_type->hierarchical ) {
$value = ! empty( $_POST["{$slug}_override_css"] ) && 1 == $_POST["{$slug}_override_css"];
$this->plugin->data_manager->set_item_data( [
'type' => 'post',
'object_id' => $object_id,
], 'override_css', $value );
}
}
}

View File

@ -0,0 +1,130 @@
<?php
namespace ShortPixel\CriticalCSS\Admin\UI;
use ComposePress\Core\Abstracts\Component;
class Term extends Component {
/**
*
*/
public function init() {
add_action( 'wp_loaded', [
$this,
'wp_loaded_action',
] );
}
/**
*
*/
public function wp_loaded_action() {
foreach ( get_taxonomies() as $tax ) {
if ( apply_filters( 'shortpixel_critical_css_manual_term_css', true ) ) {
add_action( "{$tax}_edit_form", [
$this,
'render_manual_css_form',
] );
add_action( "edit_{$tax}", [
$this,
'save_manual_css',
] );
}
if (
get_taxonomy( $tax )->hierarchical
// && 'on' !== $this->plugin->settings_manager->get_setting( 'template_cache' )
) {
add_action( "{$tax}_edit_form", [
$this,
'render_term_css_override_form',
] );
add_action( "edit_{$tax}", [
$this,
'save_css_override',
] );
}
}
}
/**
*
*/
public function render_manual_css_form() {
$slug = $this->plugin->get_safe_slug();
$object_id = $this->tag->term_id;
?>
<?php
$css = $this->plugin->data_manager->get_item_data( [
'type' => 'term',
'object_id' => $object_id,
], 'manual_css' );
?>
<table class="form-table">
<tr>
<th>
<?php
_e( 'Enter your manual critical css here:', $this->plugin->get_lang_domain() );
?>
</th>
<td>
<textarea name="<?php echo $slug ?>_manual_css" id="<?php echo $slug ?>_manual_css"
class="widefat" rows="10"><?php echo $css ?></textarea>
</td>
</tr>
</table>
<?php
}
public function render_term_css_override_form() {
$slug = $this->plugin->get_safe_slug();
$value = $this->plugin->data_manager->get_item_data( [
'type' => 'term',
'object_id' => $this->tag->term_id,
], 'override_css' );
?>
<table class="form-table">
<tr class="form-field">
<th>
<label for="<?php echo $slug ?>_override_css">
<?php _e( 'Override Child Critical CSS', $this->plugin->get_lang_domain() ) ?>
</label>
</th>
<td>
<input type="checkbox" name="<?php echo $slug ?>_override_css" id="<?php echo $slug ?>_override_css"
value="1" <?php checked( $value ) ?>/>
</td>
</tr>
</table>
<?php
}
/**
* @param $post_id
*/
public function save_manual_css( $term_id ) {
$slug = $this->plugin->get_safe_slug();
$field = "{$slug}_manual_css";
if ( ! isset( $_POST[ $field ] ) ) {
return;
}
$css = sanitize_textarea_field( $_POST[ $field ] );
$this->plugin->data_manager->set_item_data( [
'type' => 'term',
'object_id' => $term_id,
], 'manual_css', $css );
}
public function save_css_override( $term_id ) {
$slug = $this->plugin->get_safe_slug();
$value = ! empty( $_POST["{$slug}_override_css"] ) && 1 == $_POST["{$slug}_override_css"];
$this->plugin->data_manager->set_item_data( [
'type' => 'term',
'object_id' => $term_id,
], 'override_css', $value );
}
}

View File

@ -0,0 +1,332 @@
<?php
declare( ticks=1 );
namespace ShortPixel\CriticalCSS\Background;
use ShortPixel\CriticalCSS\FileLog;
abstract class ProcessAbstract extends \WP_Background_Process {
/**
* @inheritDoc
*/
public function __construct() {
parent::__construct();
add_filter( $this->cron_interval_identifier, [
$this,
'cron_interval',
] );
remove_action( 'wp_ajax_' . $this->identifier, array( $this, 'maybe_handle' ) );
remove_action( 'wp_ajax_nopriv_' . $this->identifier, array( $this, 'maybe_handle' ) );
$this->schedule_event();
if ( function_exists( 'pcntl_signal' ) ) {
pcntl_signal( SIGINT, [ $this, 'handle_interrupt' ] );
}
add_action( 'shutdown', [ $this, 'unlock_process' ] );
}
public function handle_interrupt() {
do_action( 'shutdown' );
exit;
}
/**
* @return int
*/
public function cron_interval() {
return 1;
}
/**
* @return mixed
*/
public function get_identifier() {
return $this->identifier;
}
public function create_table() {
global $wpdb;
if ( ! function_exists( 'dbDelta' ) ) {
include_once ABSPATH . 'wp-admin/includes/upgrade.php';
}
$charset_collate = $wpdb->get_charset_collate();
$table = $this->get_table_name();
$sql = "CREATE TABLE $table (
id MEDIUMINT(9) NOT NULL AUTO_INCREMENT,
template VARCHAR(255),
post_type VARCHAR(255),
object_id BIGINT(10),
type VARCHAR (10),
url TEXT,
data TEXT,
updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,";
if ( is_multisite() ) {
$sql .= "\n" . 'blog_id BIGINT(20),';
}
dbDelta( "$sql\nPRIMARY KEY (id)\n) {$charset_collate};" );
}
protected function get_table_name() {
global $wpdb;
if ( is_multisite() ) {
return "{$wpdb->base_prefix}{$this->action}_queue";
}
return "{$wpdb->prefix}{$this->action}_queue";
}
public function get_item_exists( $item ) {
global $wpdb;
$args = [];
$table = $this->get_table_name();
$sql = "SELECT *
FROM `{$table}`
WHERE ";
if ( 'url' !== $item['type'] && ! empty( $item['post_type'] ) ) {
$sql .= '`post_type` = %s';
$args[] = $item['post_type'];
} elseif ( ! empty( $item['template'] ) ) {
$sql .= '`template` = %s';
$args[] = $item['template'];
} else {
if ( 'url' === $item['type'] ) {
$sql .= '`url` = %s';
$args[] = $item['url'];
} else {
$sql .= '`object_id` = %d AND `type` = %s';
$args[] = $item['object_id'];
$args[] = $item['type'];
}
}
if ( is_multisite() ) {
$sql .= ' AND `blog_id` = %d';
$args[] = get_current_blog_id();
}
$result = $wpdb->get_row( $wpdb->prepare( $sql, $args ) );
if ( null === $result ) {
$result = false;
}
return $result;
}
public function save() {
global $wpdb;
$table = $this->get_table_name();
foreach ( $this->data as $item ) {
$data = array_merge( [], $item );
unset( $data['object_id'] );
unset( $data['type'] );
unset( $data['url'] );
unset( $data['template'] );
unset( $data['post_type'] );
if ( is_multisite() ) {
unset( $data['blog_id'] );
}
$item['data'] = maybe_serialize( $data );
$item = array_diff_key( $item, $data );
$wpdb->insert( $table, $item );
if ( class_exists( 'WPECommon' ) ) {
$wpdb->query( "DELETE q1 FROM $table q1, $table q2 WHERE q1.id > q2.id
AND (
(
q1.object_id = q2.object_id AND q1.type != 'url' AND q2.type != 'url'
) OR
(
q1.url = q1.url AND q1.type = 'url' AND q2.type = 'url'
)
)" );
}
}
$this->schedule_event();
$this->data = [];
return $this;
}
public function purge() {
global $wpdb;
if ( is_multisite() ) {
$table = "{$wpdb->base_prefix}{$this->action}_queue";
} else {
$table = "{$wpdb->prefix}{$this->action}_queue";
}
$wpdb->query( "TRUNCATE `{$table}`" );
}
public function handle_cron_healthcheck() {
if ( $this->is_process_running() ) {
// Background process already running.
return;
}
if ( $this->is_queue_empty() ) {
// No data to process.
$this->clear_scheduled_event();
return;
}
$this->handle();
if ( 'cli' !== php_sapi_name() ) {
exit;
}
}
/**
* Is queue empty
*
* @return bool
*/
protected function is_queue_empty() {
$count = $this->get_length();
return 0 == $count;
}
public function get_length() {
global $wpdb;
$table = $this->get_table_name();
$count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM `{$table}`" );
return $count;
}
protected function handle() {
//echo("HAHAHA");return;
$this->lock_process();
$batch = $this->get_batch();
$original_batch = clone $batch;
$cli = 'cli' === php_sapi_name();
do {
if ( ! $batch ) {
$batch = $this->get_batch();
$original_batch = clone $batch;
}
foreach ( $batch->data as $key => $value ) {
$task = $this->task( $value );
if ( false !== $task ) {
$batch->data[ $key ] = $task;
} else {
unset( $batch->data[ $key ] );
}
if ( ( $this->time_exceeded() || $this->memory_exceeded() ) && ! $cli ) {
// Batch limits reached.
break;
}
sleep( apply_filters( 'shortpixel_critical_css_process_delay', $task ? 5.0 : 0.5 ) );
}
// Update or delete current batch.
if ( ! empty( $batch->data ) ) {
if ( serialize( $original_batch ) !== serialize( $batch ) ) {
$this->update( $batch->key, $batch->data );
$original_batch = $batch;
}
} else {
$this->delete( $batch->key );
$original_batch = null;
$batch = null;
}
sleep( apply_filters( 'shortpixel_critical_css_process_delay', 0.5 ) );
} while ( ! $this->is_queue_empty() && $cli );
$this->unlock_process();
// Start next batch or complete process.
if ( ! $this->is_queue_empty() ) {
$this->dispatch();
} else {
$this->complete();
}
}
/**
* @inheritDoc
*/
public function get_batch() {
global $wpdb;
$batch = new \stdClass();
$batch->data = [];
$batch->key = '';
if ( ! $this->is_queue_empty() ) {
if ( is_multisite() ) {
$table = "{$wpdb->base_prefix}{$this->action}_queue";
} else {
$table = "{$wpdb->prefix}{$this->action}_queue";
}
$result = $wpdb->get_row( "
SELECT *
FROM `{$table}`
LIMIT 1
" );
$batch = new \stdClass();
$batch->key = $result->id;
unset( $result->id );
$data = maybe_unserialize( $result->data );
if ( null !== $data ) {
$result = (object) array_merge( (array) $result, $data );
}
unset( $result->data );
$batch->data = [ (array) $result ];
}
return $batch;
}
public function update( $key, $items ) {
global $wpdb;
$table = $this->get_table_name();
foreach ( $items as $item ) {
$data = array_merge( [], $item );
unset( $data['object_id'] );
unset( $data['type'] );
unset( $data['url'] );
unset( $data['updated'] );
unset( $data['template'] );
$item['data'] = maybe_serialize( $data );
$item = array_diff_key( $item, $data );
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_DB) && FileLog::instance()->log("UPDATEE $table. DATA: ", $item);
if ( ! empty( $data ) ) {
$wpdb->update( $table, $item, [
'id' => $key,
] );
}
}
return $this;
}
public function delete( $key ) {
global $wpdb;
$tableName = $this->get_table_name();
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_DB) && FileLog::instance()->log("DELETE $key FROM TABLE $tableName. STACK: ");
$wpdb->delete( $tableName, [
'id' => (int) $key,
] );
}
public function unlock_process() {
return parent::unlock_process();
}
public function dispatch() {
$this->schedule_event();
}
}

View File

@ -0,0 +1,255 @@
<?php
namespace ShortPixel\CriticalCSS\Cache;
use ComposePress\Core\Abstracts\Component;
use pcfreak30\WordPress\Cache\Store;
use ShortPixel\CriticalCSS\FileLog;
/**
* Class Manager
*
* @package ShortPixel\CriticalCSS\Cache
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @property \ShortPixel\CriticalCSS $plugin
*/
class Manager extends Component {
/**
* @var \pcfreak30\WordPress\Cache\Store
*/
private $store;
/**
* Manager constructor.
*
* @param \pcfreak30\WordPress\Cache\Store $store
*/
public function __construct( Store $store ) {
$this->store = $store;
}
/**
*
*/
public function init() {
add_action(
'after_switch_theme', [
$this,
'reset_web_check_transients',
]
);
add_action(
'upgrader_process_complete', [
$this,
'reset_web_check_transients',
]
);
if ( ! ( defined( 'DOING_CRON' ) && DOING_CRON ) ) {
add_action(
'shortpixel_critical_css_purge_cache', [
$this,
'reset_web_check_transients',
]
);
}
if (
$this->plugin->settings_manager->get_setting( 'cache_mode' )
&& isset( $this->plugin->settings_manager->get_setting( 'cache_mode' )['posts'] )
) {
add_action(
'post_updated', [
$this,
'reset_web_check_post_transient',
]
);
add_action(
'edited_term', [
$this,
'reset_web_check_term_transient',
]
);
}
$this->store->set_prefix( $this->plugin->get_transient_prefix() );
$interval = 0;
if ( function_exists( 'get_rocket_purge_cron_interval' ) ) {
$interval = get_rocket_purge_cron_interval();
}
$this->store->set_expire( apply_filters( 'shortpixel_critical_css_cache_expire_period', $interval ) );
$this->store->set_max_branch_length( apply_filters( 'shortpixel_critical_css_max_branch_length', 50 ) );
}
/**
* @param array $path
*
* @return bool|mixed
*/
public function delete_cache_branch( $path = [] ) {
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_CACHE) && FileLog::instance()->log("CCSS CACHE delete branch", $path);
return $this->store->delete_cache_branch( $path );
}
/**
* @param array $path
*
* @return bool
*/
public function delete_cache_leaf( $path = [] ) {
return $this->store->delete_cache_leaf( $path );
}
/**
* @param $path
*
* @return mixed
*/
public function get_cache_fragment( $path ) {
return $this->store->get_cache_fragment( $path );
}
/**
* @param $path
* @param $value
*/
public function update_cache_fragment( $path, $value ) {
return $this->store->update_cache_fragment( $path, $value );
}
/**
* @param $type
* @param $object_id
* @param $url
*/
public function purge_page_cache( $type = null, $object_id = null, $url = null ) {
$url = preg_replace( '#nocache/$#', '', $url );
do_action( 'shortpixel_critical_css_purge_cache', $type, $object_id, $url );
}
/**
*
*/
public function reset_web_check_transients($type = null, $object_id = null, $url = null) {
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_DB) && FileLog::instance()->log("reset_web_check_transients. STACK: ", debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
switch($type) {
case 'post':
$this->reset_web_check_post_transient($object_id);
break;
case 'term':
$this->reset_web_check_term_transient($object_id);
break;
}
//if called with any params, avoid overall clear
if(is_null($type)) {
$this->store->delete_cache_branch();
}
}
/**
* Remove post meta cache
* @return void
*/
public function reset_post_meta_cache() {
$table = $this->plugin->log->get_table_name();
$wpdb = $this->plugin->wpdb;
$name = "{$this->plugin->get_safe_slug()}_cache";
//$res = $wpdb->get_results( $wpdb->prepare( "SELECT object_id, type FROM {$table} WHERE type IN ('post', 'term', 'author') AND template IS NULL AND post_type IS NULL " ), ARRAY_A );
$res = $wpdb->get_results( $wpdb->prepare( "SELECT object_id, type FROM {$table} WHERE type IN ('post', 'term', 'author')" ), ARRAY_A );
foreach($res as $row) {
switch($row['type']) {
case 'post':
delete_post_meta($row['object_id'], $name);
break;
case 'author':
delete_user_meta($row['object_id'], $name);
break;
case 'term':
delete_term_meta($row['object_id'], $name);
break;
}
}
}
/**
* @param array $path
*/
/**
* @param $post
*/
public function reset_web_check_post_transient( $post ) {
$post = get_post( $post );
$hash = $this->plugin->data_manager->get_item_hash(
[
'object_id' => $post->ID,
'type' => 'post',
]
);
$this->store->delete_cache_branch( [ 'webcheck', $hash ] );
}
/**
* @param $term
*
* @internal param \WP_Term $post
*/
public function reset_web_check_term_transient( $term ) {
$term = get_term( $term );
$hash = $this->plugin->data_manager->get_item_hash(
[
'object_id' => $term->term_id,
'type' => 'term',
]
);
$this->store->delete_cache_branch( [ 'webcheck', $hash ] );
}
/**
*
*/
/**
* @internal param \WP_Term $post
*/
public function reset_web_check_home_transient() {
$page_for_posts = get_option( 'page_for_posts' );
if ( ! empty( $page_for_posts ) ) {
$post_id = $page_for_posts;
}
if ( empty( $post_id ) || ( ! empty( $post_id ) && get_permalink( $post_id ) != site_url() ) ) {
$page_on_front = get_option( 'page_on_front' );
if ( ! empty( $page_on_front ) ) {
$post_id = $page_on_front;
} else {
$post_id = false;
}
}
if ( ! empty( $post_id ) && get_permalink( $post_id ) == site_url() ) {
$hash = $this->plugin->data_manager->get_item_hash(
[
'object_id' => $post_id,
'type' => 'post',
]
);
} else {
$hash = $this->plugin->data_manager->get_item_hash(
[
'type' => 'url',
'url' => site_url(),
]
);
}
$this->store->delete_cache_branch( [ 'webcheck', $hash ] );
}
/**
* @return \pcfreak30\WordPress\Cache\Store
*/
public function get_store() {
return $this->store;
}
}

View File

@ -0,0 +1,312 @@
<?php
namespace ShortPixel\CriticalCSS\Data;
use ComposePress\Core\Abstracts\Component;
use ShortPixel\CriticalCSS\FileLog;
/**
* Class Manager
*
* @package ShortPixel\CriticalCSS\Data
* @property \ShortPixel\CriticalCSS $plugin
*/
class Manager extends Component {
/**
* @param array $item
*
* @return string
*/
public function get_html_hash( $item = [] ) {
return $this->get_item_data( $item, 'html_hash' );
}
/**
* @param array $item
* @param $name
*
* @return mixed|null
*/
public function get_item_data( $item, $name ) {
$value = null;
if ( empty( $item ) ) {
$item = $this->plugin->request->get_current_page_type();
}
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("CCSS Data Manager get_item_data for " . $name, $item);
if ( isset($this->plugin->settings_manager->get_setting( 'cache_mode' )['postTypes'])
&& ! empty( $item['post_type'] )
&& is_array( $this->plugin->settings_manager->get_setting( 'post_type_values' ) )
&& in_array( $item['post_type'], $this->plugin->settings_manager->get_setting( 'post_type_values' ) )
) {
if ( 'cache' === $name ) {
$name = 'ccss';
}
$name = [ $name, md5( $item['post_type'] ) ];
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("CCSS Data Manager get_item_data POST TYPE for ", $name);
$value = $this->plugin->cache_manager->get_store()->get_cache_fragment( $name );
} elseif ( isset($this->plugin->settings_manager->get_setting( 'cache_mode' )['templates'] )
&& ! empty( $item['template'] )
&& is_array( $this->plugin->settings_manager->get_setting( 'template_values' ) )
&& in_array( $item['template'], $this->plugin->settings_manager->get_setting( 'template_values' ) )
) {
if ( 'cache' === $name ) {
$name = 'ccss';
}
$name = [ $name, md5( $item['template'] ) ];
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("CCSS Data Manager get_item_data TEMPLATE for ", $name);
$value = $this->plugin->cache_manager->get_store()->get_cache_fragment( $name );
} elseif ( isset($this->plugin->settings_manager->get_setting( 'cache_mode' )['posts'] ) ) {
if ( 'url' === $item['type'] ) {
if ( 'cache' === $name ) {
$name = 'ccss';
}
$name = [ $name, md5( $item['url'] ) ];
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("CCSS Data Manager get_item_data URL for ", $name);
$value = $this->plugin->get_cache_manager()->get_store()->get_cache_fragment( $name );
} else {
if ( is_multisite() && ! empty( $item['blog_id'] ) ) {
switch_to_blog( $item['blog_id'] );
}
$name = "{$this->plugin->get_safe_slug()}_{$name}";
switch ( $item['type'] ) {
case 'post':
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("CCSS Data Manager get_item_data POST for " . $name);
$value = get_post_meta( $item['object_id'], $name, true );
break;
case 'term':
$value = get_term_meta( $item['object_id'], $name, true );
break;
case 'author':
$value = get_user_meta( $item['object_id'], $name, true );
break;
}
$value = wp_unslash( $value );
if ( is_multisite() ) {
restore_current_blog();
}
}
}
return $value;
}
/**
* @param $item
* @param string $css
*
* @return void
* @internal param array $type
*/
public function set_cache( $item, $css ) {
$this->set_item_data( $item, 'cache', $css );
}
/**
* @param $item
* @param $name
* @param $value
* @param int $expires
*/
public function set_item_data( $item, $name, $value, $expires = 0 ) {
if ( isset($this->plugin->settings_manager->get_setting( 'cache_mode' )['postTypes'])
&& ! empty( $item['post_type'] )
&& is_array( $this->plugin->settings_manager->get_setting( 'post_type_values' ) )
&& in_array( $item['post_type'], $this->plugin->settings_manager->get_setting( 'post_type_values' ) )
) {
if ( 'cache' === $name ) {
$name = 'ccss';
}
$name = [ $name, md5( $item['post_type'] ) ];
$this->plugin->cache_manager->get_store()->update_cache_fragment( $name, $value );
} elseif ( isset($this->plugin->settings_manager->get_setting( 'cache_mode' )['templates'])
&& ! empty( $item['template'] )
&& in_array( $item['template'] , $this->plugin->settings_manager->get_setting( 'template_values' ) )
) {
if ( 'cache' === $name ) {
$name = 'ccss';
}
$name = [ $name, md5( $item['template'] ) ];
$this->plugin->cache_manager->get_store()->update_cache_fragment( $name, $value );
} elseif ( isset($this->plugin->settings_manager->get_setting( 'cache_mode' )['posts'] ) ) {
if ( 'url' === $item['type'] ) {
if ( 'cache' === $name ) {
$name = 'ccss';
}
$name = [ $name, md5( $item['url'] ) ];
$this->plugin->cache_manager->get_store()->update_cache_fragment( $name, $value );
} else {
if ( is_multisite() && ! empty( $item['blog_id'] ) ) {
switch_to_blog( $item['blog_id'] );
}
$name = "{$this->plugin->get_safe_slug()}_{$name}";
$value = wp_slash( $value );
switch ( $item['type'] ) {
case 'post':
update_post_meta( $item['object_id'], $name, $value );
break;
case 'term':
update_term_meta( $item['object_id'], $name, $value );
break;
case 'author':
update_user_meta( $item['object_id'], $name, $value );
break;
}
if ( is_multisite() ) {
restore_current_blog();
}
}
}
}
/**
* @param array $item
*
* @return string
*/
public function get_css_hash( $item = [] ) {
return $this->get_item_data( $item, 'css_hash' );
}
/**
* @param $item
* @param string $hash
*
* @return void
* @internal param array $type
*/
public function set_css_hash( $item, $hash ) {
$this->set_item_data( $item, 'css_hash', $hash );
}
/**
* @param $item
* @param string $hash
*
* @return void
* @internal param array $type
*/
public function set_html_hash( $item, $hash ) {
$this->set_item_data( $item, 'html_hash', $hash );
}
/**
* @param array $item
*
* @return string
*/
public function get_cache( $item = [] ) {
if ( empty( $item ) ) {
$item = $this->plugin->request->get_current_page_type();
}
$item = $this->get_item_css_override( $item );
return $this->get_item_data( $item, 'cache' );
}
protected function get_item_css_override( $item ) {
if ( 'on' === $this->plugin->settings_manager->get_setting( 'template_cache' ) ) {
return $item;
}
$tree = array_reverse( $this->get_item_parent_tree( $item ) );
$parent_item = $item;
foreach ( $tree as $leaf ) {
$parent_item['object_id'] = $leaf;
$override = (bool) $this->get_item_data( $parent_item, 'override_css' );
if ( $override ) {
return $parent_item;
}
}
return $item;
}
protected function get_item_parent_tree( $item ) {
$tree = [];
$object_id = 0;
$hierarchical = false;
switch ( $item['type'] ) {
case 'post':
$hierarchical = get_post_type_object( get_post_type( $item['object_id'] ) )->hierarchical;
break;
case 'term':
$hierarchical = get_taxonomy( get_term( $item['object_id'] )->taxonomy )->hierarchical;
break;
}
if ( ! $hierarchical ) {
return $tree;
}
do {
switch ( $item['type'] ) {
case 'post':
$object_id = wp_get_post_parent_id( $item['object_id'] );
break;
case 'term':
$object_id = get_term( $item['object_id'] )->parent;
break;
}
$tree[] = $object_id;
$item['object_id'] = $object_id;
} while ( ! empty( $object_id ) );
return array_filter( $tree );
}
/**
* @param array $item
*
* @return string
*/
public function get_manual_css( $item = [] ) {
if ( empty( $item ) ) {
$item = $this->plugin->request->get_current_page_type();
}
$item = $this->get_item_css_override( $item );
return $this->get_item_data( $item, 'manual_css' );
}
/**
* @param $item
*
* @return string
* @SuppressWarnings("unused")
*/
public function get_item_hash( $item ) {
extract( $item, EXTR_OVERWRITE );
$parts = [
'object_id',
'type',
'url',
'blog_id'
];
if( !empty($item['post_type']) ) {
$parts[] = 'post_type';
} elseif( !empty($item['template']) ) {
$parts[] = 'template';
}
$type = [];
foreach ( $parts as $var ) {
if ( isset( $item[$var] ) ) {
$type[ $var ] = $item[$var];
}
}
return md5( serialize( $type ) );
}
/**
*
*/
public function init() {
// noop
}
}

View File

@ -0,0 +1,130 @@
<?php
/**
* Created by: simon
* Date: 03.03.2022
*/
namespace ShortPixel\CriticalCSS;
class FileLog {
private static $instance;
private $logPath;
const DEBUG_AREA_INIT = 1;
const DEBUG_AREA_API = 2;
const DEBUG_AREA_DB = 4;
const DEBUG_AREA_Q = 8;
const DEBUG_AREA_CACHE = 16;
const DEBUG_AREA_PROCESS = 32;
const DEBUG_AREA_ALL = 63;
/**
* Make sure only one instance is running.
*/
public static function instance() {
if ( !isset ( self::$instance ) ) {
self::$instance = new self;
}
return self::$instance;
}
private function __construct() {
$upload_dir = wp_upload_dir();
$this->logPath = $upload_dir[ 'basedir' ] . '/sp_ccss.log';
}
public function log( $msg, $extra = false ) {
if ( SPCCSS_DEBUG ) {
$micro_date = microtime();
$date_array = explode(" ",$micro_date);
$date = date("Y-m-d H:i:s",$date_array[1]);
$date = $date . '.' . substr($date_array[0], 2, 3);
file_put_contents( $this->logPath, '[' . $date . "] $msg" . ( $extra ? json_encode( $extra, JSON_PRETTY_PRINT ) : '' ) . "\n", FILE_APPEND );
}
}
/**
* Custom (PHP) error handler
*
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param int $errline
*
* @return bool
*/
public static function errorHandler( $errno, $errstr, $errfile, $errline ) {
if ( !( error_reporting() & $errno ) ) {
// this error code doesn't contain in error_reporting,
// so let default error handler does its job
return false;
}
switch ( $errno ) {
case E_ERROR:
$type = 'Run-time ERROR';
break;
case E_WARNING:
$type = 'Run-time WARNING';
break;
case E_NOTICE:
$type = 'Run-time NOTICE';
break;
case E_DEPRECATED:
$type = 'Run-time DEPRECATED';
break;
case E_CORE_ERROR:
$type = 'PHP core ERROR';
break;
case E_CORE_WARNING:
$type = 'PHP core WARNING';
break;
case E_USER_ERROR:
$type = 'Generated ERROR';
break;
case E_USER_WARNING:
$type = 'Generated WARNING';
break;
case E_USER_NOTICE:
$type = 'Generated NOTICE';
break;
case E_USER_DEPRECATED:
$type = 'Generated DEPRECATED';
break;
default:
$type = 'OTHER';
break;
}
$logger = self::instance();
$logger->log( '--- CAUGHT:BEGIN ---' );
$logger->log( $type . ': ' . $errstr );
$logger->log( 'IN FILE: ' . $errfile );
$logger->log( 'ON LINE: ' . $errline );
// Backtrace could be uncommented if needed
// $logger->log( json_encode( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ), JSON_PRETTY_PRINT ) );
$logger->log( '--- CAUGHT:END ---' );
// don't run default error handler
return true;
}
public function clearLog() {
@unlink( $this->logPath );
}
public function getLogPath() {
return $this->logPath;
}
}

View File

@ -0,0 +1,152 @@
<?php
namespace ShortPixel\CriticalCSS;
use ComposePress\Core\Abstracts\Component;
class Frontend extends Component {
private $has_critical_css = false;
public function init() {
if ( ! is_admin() ) {
add_action(
'wp_print_styles', [
$this,
'print_styles',
], 7 );
add_action(
'wp', [
$this,
'wp_action',
]
);
add_action(
'wp_head', [
$this,
'wp_head',
]
);
if( isset( $_GET['LAZYSTYLES'] ) || 'on' === $this->plugin->settings_manager->get_setting( 'lazy_load_css_files' ) ) {
add_filter('style_loader_tag', [
$this,
'lazy_styles'
]);
}
}
}
/**
*
*/
public function wp_head() {
if ( get_query_var( 'nocache' ) ) :
?>
<meta name="robots" content="noindex, nofollow"/>
<?php
endif;
}
/**
*
*/
public function wp_action() {
set_query_var( 'nocache', $this->plugin->request->is_no_cache() );
$this->plugin->integration_manager->enable_integrations();
}
/**
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
public function print_styles() {
global $wp_styles;
if ( get_query_var( 'nocache' ) ) {
do_action( 'shortpixel_critical_css_nocache' );
}
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("CCSS FRONT print_styles");
if ( ! get_query_var( 'nocache' ) && ! is_404() ) {
//TODO extract the below into a $this->plugin->retrieve_cached_css() function - will also receive a param URL
// if present, will search for that, if not for the current page.
$css = $this->plugin->retrieve_cached_css();
$cache = $css->cache;
$manual = $css->manual;
do_action( 'shortpixel_critical_css_before_print_styles', $cache );
if ( ! empty( $cache ) ) {
$this->has_critical_css = true;
?>
<style id="criticalcss" data-no-minify="1"><?php echo $cache ?></style>
<?php
}
if(isset($_GET['STRIPSTYLES'])) {
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("CCSS STYLES:", $wp_styles->queue);
if ( is_a( $wp_styles, 'WP_Styles' ) ) foreach($wp_styles->queue as $style) {
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("CCSS STYLE:", $wp_styles->query($style));
wp_dequeue_style($style);
}
}
$type = $this->plugin->request->get_current_page_type();
if(
isset($this->plugin->settings_manager->get_setting( 'cache_mode' )['postTypes'])
&& ! empty( $type['post_type'] )
&& is_array( $this->plugin->settings_manager->get_setting( 'post_type_values' ) )
&& in_array( $type['post_type'], $this->plugin->settings_manager->get_setting( 'post_type_values' ) )
) {
unset($type['template']);
if ( empty( $cache ) ) {
if ( ! $this->plugin->api_queue->get_item_exists( $type ) ) {
$this->plugin->api_queue->push_to_queue( $type )->save();
}
$this->plugin->template_log->insert( $type );
}
} elseif (
isset($this->plugin->settings_manager->get_setting( 'cache_mode' )['templates'])
&& ! empty( $type['template'] )
&& is_array( $this->plugin->settings_manager->get_setting( 'template_values' ) )
&& in_array( $type['template'], $this->plugin->settings_manager->get_setting( 'template_values' ) )
) {
unset($type['post_type']);
if ( empty( $cache ) ) {
if ( ! $this->plugin->api_queue->get_item_exists( $type ) ) {
$this->plugin->api_queue->push_to_queue( $type )->save();
}
$this->plugin->template_log->insert( $type );
}
} elseif (
isset($this->plugin->settings_manager->get_setting( 'cache_mode' )['posts'] )
) {
unset($type['post_type'], $type['template']);
$hash = $this->plugin->data_manager->get_item_hash( $type );
$check = $this->plugin->cache_manager->get_cache_fragment( [ 'webcheck', $hash ] );
if ( ! $manual && ( empty( $check ) || ( ! empty( $check ) && empty( $cache ) && null !== $cache ) ) && ! $this->plugin->web_check_queue->get_item_exists( $type ) ) {
$this->plugin->web_check_queue->push_to_queue( $type )->save();
$this->plugin->cache_manager->update_cache_fragment( [ 'webcheck', $hash ], true );
}
}
do_action( 'shortpixel_critical_css_after_print_styles' );
}
}
public function lazy_styles($tag) {
if( !$this->has_critical_css ) return $tag;
if(!preg_match('/\bmedia\s*=/s', $tag)){
$tag = preg_replace('/\/?>\s*$/s', ' media="print" onload="this.media=\'all\'"/>', $tag);
} elseif(!preg_match('/\bmedia\s*=\s*[\'"]?\s*(print|speech)/s', $tag)) {
$tag = preg_replace('/\bmedia\s*=\s*[\'"]?\s*([-a-zA-Z]+)[\'"]?/s', 'media="print" onload="this.media=\'\\1\'"', $tag);
}
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("CCSS lazy_styles TAG: ", $tag);
return $tag;
}
}

View File

@ -0,0 +1,137 @@
<?php
namespace ShortPixel\CriticalCSS;
use ComposePress\Core\Abstracts\Component;
/**
* Class Installer
*
* @package ShortPixel\CriticalCSS
* @property \ShortPixel\CriticalCSS $plugin
*/
class Installer extends Component {
/**
*
*/
public function init() {
}
/**
*
*/
public function activate() {
$wpdb = $this->wpdb;
$settings = $this->plugin->settings_manager->get_settings();
$no_version = ( ! empty( $settings ) && empty( $settings['version'] ) ) || empty( $settings );
$version_0_3 = false;
$version_0_4 = false;
$version_0_5 = false;
$version_0_7 = false;
$version_0_7_1 = false;
if ( ! $no_version ) {
$version = $settings['version'];
$version_0_3 = version_compare( '0.3.0', $version ) === 1;
$version_0_4 = version_compare( '0.4.0', $version ) === 1;
$version_0_5 = version_compare( '0.5.0', $version ) === 1;
$version_0_7 = version_compare( '0.7.0', $version ) === 1;
$version_0_7_1 = version_compare( '0.7.1', $version ) === 1;
}
if ( $no_version || $version_0_3 || $version_0_4 ) {
remove_action(
'update_option_criticalcss', [
$this,
'after_options_updated',
]
);
if ( isset( $settings['disable_autopurge'] ) ) {
unset( $settings['disable_autopurge'] );
$this->plugin->settings_manager->update_settings( $settings );
}
if ( isset( $settings['expire'] ) ) {
unset( $settings['expire'] );
$this->plugin->settings_manager->update_settings( $settings );
}
}
if ( $no_version || $version_0_3 || $version_0_4 || $version_0_5 ) {
$wpdb->get_results( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", '_transient_criticalcss_%', '_transient_timeout_criticalcss_%' ) );
}
if ( $version_0_7 ) {
$items = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT meta_key FROM {$wpdb->postmeta} WHERE meta_key like %s ", "{$wpdb->esc_like( 'criticalcss' ) }%" ) );
foreach ( $items as $item ) {
$new_item = "wp_{$item}";
$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_key = %s WHERE meta_key = %s ", $new_item, $item ) );
}
$items = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT meta_key FROM {$wpdb->termmeta} WHERE meta_key like %s ", "{$wpdb->esc_like( 'criticalcss' ) }%" ) );
foreach ( $items as $item ) {
$new_item = "wp_{$item}";
$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->termmeta} SET meta_key = %s WHERE meta_key = %s ", $new_item, $item ) );
}
}
if ( $version_0_7_1 ) {
if ( is_multisite() ) {
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->base_prefix}{$this->plugin->get_safe_slug()}_processed_items" );
} else {
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}{$this->plugin->get_safe_slug()}" );
}
}
if ( is_multisite() ) {
foreach (
get_sites(
[
'fields' => 'ids',
'site__not_in' => [ 1 ],
]
) as $blog_id
) {
switch_to_blog( $blog_id );
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}_shortpixel_critical_css_web_check_queue" );
$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}_shortpixel_critical_css_api_queue" );
restore_current_blog();
}
}
$loopbackAvailable = true;
$result = wp_remote_get( home_url());
if( $result instanceof \WP_Error && $result->get_error_code() == 'http_request_failed' ) {
$loopbackAvailable = false;
}
$updated_settings = array_merge(
[
'web_check_interval' => DAY_IN_SECONDS,
'template_cache' => 'off',
'loopback_available' => $loopbackAvailable,
], $this->plugin->settings_manager->get_settings(), [
'version' => $this->plugin->get_version(),
]
);
if(!isset($updated_settings['cache_mode'])) {
$updated_settings['cache_mode'] = [ 'posts' => 'posts' ];
}
$this->plugin->settings_manager->update_settings(
$updated_settings
);
$this->plugin->request->add_rewrite_rules();
$this->plugin->web_check_queue->create_table();
$this->plugin->api_queue->create_table();
$this->plugin->log->create_table();
$this->plugin->template_log->create_table();
flush_rewrite_rules();
}
/**
*
*/
public function deactivate() {
flush_rewrite_rules();
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace ShortPixel\CriticalCSS\Integration;
class A3LazyLoad extends IntegrationAbstract {
/**
* @return void
*/
public function enable() {
add_action( 'shortpixel_critical_css_nocache', [ $this, 'disable_lazyload' ] );
}
/**
* @return void
*/
public function disable() {
remove_action( 'shortpixel_critical_css_nocache', [ $this, 'disable_lazyload' ] );
}
public function disable_lazyload() {
add_filter( 'a3_lazy_load_run_filter', '__return_false' );
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace ShortPixel\CriticalCSS\Integration;
use Elementor\Plugin;
class Elementor extends IntegrationAbstract {
public function init() {
if ( class_exists( '\Elementor\Plugin' ) ) {
parent::init();
}
}
/**
* @return void
*/
public function enable() {
add_action( 'wp', [ $this, 'check_preview' ] );
}
/**
* @return void
*/
public function disable() {
remove_action( 'init', [ $this, 'check_preview' ] );
}
public function check_preview() {
if ( Plugin::$instance->preview->is_preview_mode() ) {
add_filter( 'shortpixel_critical_css_print_styles_cache', '__return_empty_string', 11 );
}
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace ShortPixel\CriticalCSS\Integration;
/**
* Class IntegrationAbstract
*/
use ComposePress\Core\Abstracts\Component;
/**
* Class IntegrationAbstract
*
* @package ShortPixel\CriticalCSS\Integration
* @property \ShortPixel\CriticalCSS $plugin
*/
abstract class IntegrationAbstract extends Component {
/**
*
*/
public function init() {
add_action( 'shortpixel_critical_css_enable_integrations', [
$this,
'enable',
] );
add_action( 'shortpixel_critical_css_disable_integrations', [
$this,
'disable',
] );
}
/**
* @return void
*/
abstract public function enable();
/**
* @return void
*/
abstract public function disable();
}

View File

@ -0,0 +1,92 @@
<?php
namespace ShortPixel\CriticalCSS\Integration;
class Kinsta extends IntegrationAbstract {
private $kinsta_cache;
/**
* Kinsta constructor.
*/
public function init() {
if ( class_exists( '\Kinsta\Cache' ) ) {
add_action( 'kinsta_cache_init', [ $this, 'set_kinsta_cache' ] );
parent::init();
}
}
/**
*
*/
public function enable() {
add_action( 'shortpixel_critical_css_purge_cache', [
$this,
'purge_cache',
], 10, 3 );
if ( wp_doing_cron() ) {
add_filter( 'shortpixel_critical_css_get_permalink', [ $this, 'modify_permalink' ] );
}
}
/**
* @return void
*/
public function disable() {
}
public function modify_permalink( $url ) {
$key = 'cache_bust_' . bin2hex( random_bytes( 5 ) );
return add_query_arg( $key, '1', $url );
}
/**
* @SuppressWarnings(PHPMD)
* @param null $type
* @param null $object_id
* @param null $url
*/
public function purge_cache( $type = null, $object_id = null, $url = null ) {
if ( empty( $type ) ) {
/** @noinspection PhpUndefinedClassInspection */
$this->kinsta_cache->kinsta_cache_purge->purge_complete_caches();
} elseif ( 'post' === $type ) {
/** @noinspection PhpUndefinedClassInspection */
$this->kinsta_cache->kinsta_cache_purge->initiate_purge( $object_id, 'post' );
} else {
$this->purge_url( $url );
}
sleep( 1 );
}
private function purge_url( $url ) {
$purge = [
'single' => [ 'custom|0' => trailingslashit( $url ) ],
];
$purge = $this->kinsta_cache->kinsta_cache_purge->convert_purge_list_to_request( $purge );
wp_remote_post(
$this->kinsta_cache->config['immediate_path'],
array(
'sslverify' => false,
'timeout' => 5,
'body' => $purge,
)
);
}
public function disable_cache() {
$permalink = $this->plugin->get_permalink( $this->plugin->request->get_current_page_type() );
$this->purge_url( $permalink );
}
/**
* @param mixed $kinsta_cache
*/
public function set_kinsta_cache( $kinsta_cache ) {
$this->kinsta_cache = $kinsta_cache;
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace ShortPixel\CriticalCSS\Integration;
/**
* Class Manager
*
* @package ShortPixel\CriticalCSS\Integration
*/
class Manager extends \ComposePress\Core\Abstracts\Manager {
/**
* @var bool
*/
protected $enabled = false;
/**
* @var array
*/
protected $modules = [
'RocketAsyncCSS',
'RootRelativeURLS',
'WPRocket',
'WPEngine',
'A3LazyLoad',
'Kinsta',
'Elementor',
'WebP',
];
/**
* @return bool
*/
public function is_enabled() {
return $this->enabled;
}
/**
*
*/
public function init() {
parent::init();
$this->enable_integrations();
}
/**
*
*/
public function enable_integrations() {
do_action( 'shortpixel_critical_css_enable_integrations' );
$this->enabled = true;
}
/**
*
*/
public function disable_integrations() {
do_action( 'shortpixel_critical_css_disable_integrations' );
$this->enabled = false;
}
}

View File

@ -0,0 +1,82 @@
<?php
namespace ShortPixel\CriticalCSS\Integration;
/**
* Class RocketAsyncCSS
*/
class RocketAsyncCSS extends IntegrationAbstract {
/**
* ShortPixel_CriticalCSS_Integration_Rocket_Async_CSS constructor.
*/
public function init() {
if ( class_exists( '\Rocket_Async_Css' ) || function_exists( 'rocket_async_css_instance' ) ) {
parent::init();
}
}
/**
* @return void
*/
public function enable() {
add_action( 'shortpixel_critical_css_nocache', [
$this,
'disable_preloader',
] );
add_action( 'shortpixel_critical_css_before_print_styles', [
$this,
'maybe_disable_preloader',
] );
}
/**
* @return void
*/
public function disable() {
remove_action( 'wp', [
$this,
'wp_action',
] );
remove_action( 'shortpixel_critical_css_before_print_styles', [
$this,
'purge_cache',
] );
}
/**
* @SuppressWarnings(PHPMD.UnusedPrivateMethod)
* @param $cache
*/
public function maybe_disable_preloader( $cache ) {
if ( ! empty( $cache ) ) {
$this->disable_preloader();
}
}
public function disable_preloader() {
if ( function_exists( 'rocket_async_css_instance' ) ) {
$integration = rocket_async_css_instance()->get_integration_manager()->get_module( 'ThePreloader' );
if ( ! empty( $integration ) ) {
remove_action( 'wp_enqueue_scripts', [
$integration,
'add_window_resize_js',
] );
remove_action( 'rocket_buffer', [
$integration,
'inject_div',
] );
return;
}
}
remove_action( 'wp_enqueue_scripts', [
'Rocket_Async_Css_The_Preloader',
'add_window_resize_js',
] );
remove_action( 'rocket_buffer', [
'Rocket_Async_Css_The_Preloader',
'inject_div',
] );
}
}

View File

@ -0,0 +1,64 @@
<?php
namespace ShortPixel\CriticalCSS\Integration;
class RootRelativeURLS extends IntegrationAbstract {
public function init() {
if ( class_exists( 'MP_WP_Root_Relative_URLS' ) ) {
parent::init();
}
}
/**
* @return void
*/
public function enable() {
remove_filter( 'post_link', [
'MP_WP_Root_Relative_URLS',
'proper_root_relative_url',
], 1 );
remove_filter( 'page_link', [
'MP_WP_Root_Relative_URLS',
'proper_root_relative_url',
], 1 );
remove_filter( 'attachment_link', [
'MP_WP_Root_Relative_URLS',
'proper_root_relative_url',
], 1 );
remove_filter( 'post_type_link', [
'MP_WP_Root_Relative_URLS',
'proper_root_relative_url',
], 1 );
remove_filter( 'get_the_author_url', [
'MP_WP_Root_Relative_URLS',
'dynamic_rss_absolute_url',
], 1 );
}
/**
* @return void
*/
public function disable() {
add_filter( 'post_link', [
'MP_WP_Root_Relative_URLS',
'proper_root_relative_url',
], 1 );
add_filter( 'page_link', [
'MP_WP_Root_Relative_URLS',
'proper_root_relative_url',
], 1 );
add_filter( 'attachment_link', [
'MP_WP_Root_Relative_URLS',
'proper_root_relative_url',
], 1 );
add_filter( 'post_type_link', [
'MP_WP_Root_Relative_URLS',
'proper_root_relative_url',
], 1 );
add_filter( 'get_the_author_url', [
'MP_WP_Root_Relative_URLS',
'dynamic_rss_absolute_url',
], 1, 2 );
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace ShortPixel\CriticalCSS\Integration;
/**
* Class WPEngine
*/
class WPEngine extends IntegrationAbstract {
/**
* WP_CriticalCSS_Integration_WPEngine constructor.
*/
public function init() {
if ( class_exists( '\WPECommon' ) ) {
parent::init();
}
}
/**
*
*/
public function enable() {
add_action( 'shortpixel_critical_css_purge_cache', [
$this,
'purge_cache',
], 10, 3 );
add_action( 'shortpixel_critical_css_nocache', [
$this,
'disable_cache',
] );
}
/**
* @return void
*/
public function disable() {
}
/**
* @SuppressWarnings(PHPMD)
* @param null $type
* @param null $object_id
* @param null $url
*/
public function purge_cache( $type = null, $object_id = null, $url = null ) {
global $wpe_varnish_servers;
if ( class_exists( '\WPECommon' ) ) {
if ( empty( $type ) ) {
/** @noinspection PhpUndefinedClassInspection */
\WpeCommon::purge_varnish_cache( null, true );
} elseif ( 'post' === $type ) {
/** @noinspection PhpUndefinedClassInspection */
\WpeCommon::purge_varnish_cache( $object_id, true );
} else {
$blog_url = home_url();
// @codingStandardsIgnoreLine
$blog_url_parts = @parse_url( $blog_url );
$blog_domain = $blog_url_parts['host'];
$purge_domains = [ $blog_domain ];
$object_parts = parse_url( $url );
$object_uri = rtrim( $object_parts ['path'], '/' ) . '(.*)';
if ( ! empty( $object_parts['query'] ) ) {
$object_uri .= '?' . $object_parts['query'];
}
$paths = [ $object_uri ];
/** @noinspection PhpUndefinedClassInspection */
$purge_domains = array_unique( array_merge( $purge_domains, \WpeCommon::get_blog_domains() ) );
if ( defined( 'WPE_CLUSTER_TYPE' ) && WPE_CLUSTER_TYPE === 'pod' ) {
$wpe_varnish_servers = [ 'localhost' ];
} // End if().
elseif ( ! isset( $wpe_varnish_servers ) ) {
if ( ! defined( 'WPE_CLUSTER_ID' ) || ! WPE_CLUSTER_ID ) {
$lbmaster = 'lbmaster';
} elseif ( WPE_CLUSTER_ID >= 4 ) {
$lbmaster = 'localhost'; // so the current user sees the purge
} else {
$lbmaster = 'lbmaster-' . WPE_CLUSTER_ID;
}
$wpe_varnish_servers = [ $lbmaster ];
}
$path_regex = '(' . join( '|', $paths ) . ')';
$hostname = $purge_domains[0];
$purge_domains = array_map( 'preg_quote', $purge_domains );
$purge_domain_chunks = array_chunk( $purge_domains, 100 );
foreach ( $purge_domain_chunks as $chunk ) {
$purge_domain_regex = '^(' . join( '|', $chunk ) . ')$';
// Tell Varnish.
foreach ( $wpe_varnish_servers as $varnish ) {
$headers = [
'X-Purge-Path' => $path_regex,
'X-Purge-Host' => $purge_domain_regex,
];
/** @noinspection PhpUndefinedClassInspection */
\WpeCommon::http_request_async( 'PURGE', $varnish, 9002, $hostname, '/', $headers, 0 );
}
}
}// End if().
sleep( 1 );
}// End if().
}
public function disable_cache() {
$permalink = $this->plugin->get_permalink( $this->plugin->request->get_current_page_type() );
$this->purge_cache( $permalink['type'], $permalink['object_id'], $permalink['url'] );
}
}

View File

@ -0,0 +1,189 @@
<?php
namespace ShortPixel\CriticalCSS\Integration;
/**
* Class WPRocket
*
* @package ShortPixel\CriticalCSS\Integration
* @property \ShortPixel\CriticalCSS $plugin
*/
class WPRocket extends IntegrationAbstract {
private $doing_purge = false;
/**
*
*/
public function init() {
if ( function_exists( 'get_rocket_option' ) ) {
add_filter( 'pre_get_rocket_option_async_css', '__return_zero' );
parent::init();
}
}
/**
* @return void
*/
public function enable() {
add_action( 'after_rocket_clean_domain', [
$this->plugin->cache_manager,
'reset_web_check_transients',
] );
add_action( 'after_rocket_clean_domain', [
$this->plugin->log,
'purge',
] );
add_action( 'after_rocket_clean_post', [
$this->plugin->cache_manager,
'reset_web_check_post_transient',
] );
add_action( 'after_rocket_clean_term', [
$this->plugin->cache_manager,
'reset_web_check_term_transient',
] );
add_action( 'after_rocket_clean_home', [
$this->plugin->cache_manager,
'reset_web_check_home_transient',
] );
add_action( 'shortpixel_critical_css_nocache', [
$this,
'disable_cache',
] );
$wpeName = '\WP_Rocket\ThirdParty\Hostings\WPEngine';
if(method_exists($wpeName, 'clean_wpengine')) {
$wpe = new $wpeName();
if(!has_action( 'after_rocket_clean_domain', [$wpe, 'clean_wpengine'] ) ) {
add_action( 'after_rocket_clean_domain', [$wpe, 'clean_wpengine'] );
}
}
elseif ( function_exists( 'rocket_clean_wpengine' ) && ! has_action( 'after_rocket_clean_domain', 'rocket_clean_wpengine' ) ) {
//old WPRocket, rocket_clean_wpengine is deprecated since 3.6.1
add_action( 'after_rocket_clean_domain', 'rocket_clean_wpengine' );
}
if ( function_exists( 'rocket_clean_supercacher' ) && ! has_action( 'after_rocket_clean_domain', 'rocket_clean_supercacher' ) ) {
add_action( 'after_rocket_clean_domain', 'rocket_clean_supercacher' );
}
add_action( 'shortpixel_critical_css_purge_cache', [
$this,
'purge_cache',
], 10, 3 );
add_filter( 'shortpixel_critical_css_print_styles_cache', [
$this,
'print_styles',
] );
add_filter( 'shortpixel_critical_css_cache_integration', '__return_true' );
add_filter( 'shortpixel_critical_css_cache_expire_period', [
$this,
'get_cache_expire_period',
] );
add_filter( 'shutdown', [
$this,
'maybe_fix_preload',
], - 1 );
}
/**
* @return void
*/
public function disable() {
remove_action( 'after_rocket_clean_domain', [
$this->plugin->cache_manager,
'reset_web_check_transients',
] );
remove_action( 'after_rocket_clean_domain', [
$this->plugin->log,
'purge',
] );
remove_action( 'after_rocket_clean_post', [
$this->plugin->cache_manager,
'reset_web_check_post_transient',
] );
remove_action( 'after_rocket_clean_term', [
$this->plugin->cache_manager,
'reset_web_check_term_transient',
] );
remove_action( 'after_rocket_clean_home', [
$this->plugin->cache_manager,
'reset_web_check_home_transient',
] );
remove_action( 'after_rocket_clean_domain', 'rocket_clean_wpengine' );
remove_action( 'after_rocket_clean_domain', 'rocket_clean_supercacher' );
remove_filter( 'shortpixel_critical_css_print_styles_cache', [
$this,
'print_styles',
] );
remove_filter( 'shortpixel_critical_css_cache_integration', '_return_true' );
}
/**
* @SuppressWarnings(PHPMD.UnusedPrivateMethod)
* @param null $type
* @param null $object_id
* @param null $url
*/
public function purge_cache( $type = null, $object_id = null, $url = null ) {
if ( 'post' === $type ) {
rocket_clean_post( $object_id );
$this->doing_purge = true;
}
if ( 'term' === $type ) {
rocket_clean_term( $object_id, get_term( $object_id )->taxonomy );
$this->doing_purge = true;
}
if ( 'url' === $type ) {
rocket_clean_files( $url );
$this->doing_purge = true;
}
if ( empty( $type ) ) {
rocket_clean_domain();
$this->doing_purge = true;
}
}
/**
* @SuppressWarnings(PHPMD.UnusedPrivateMethod)
* @param $cache
*
* @return mixed
*/
public function print_styles( $cache ) {
$cache = rocket_cdn_css_properties( $cache );
return $cache;
}
/**
* @SuppressWarnings(PHPMD.UnusedPrivateMethod)
* @return int
*/
public function get_cache_expire_period() {
if ( function_exists( 'get_rocket_purge_cron_interval' ) ) {
return get_rocket_purge_cron_interval();
}
return apply_filters( 'rocket_container', '' )->get( 'expired_cache_purge_subscriber' )->get_cache_lifespan();
}
/**
*
*/
public function disable_cache() {
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
define( 'DONOTCACHEPAGE', true );
}
if ( ! defined( 'DONOTCDN' ) ) {
define( 'DONOTCDN', true );
}
add_filter( 'rocket_override_donotcachepage', '__return_false', 9999 );
}
public function maybe_fix_preload() {
if ( $this->doing_purge ) {
if ( ! defined( 'WP_ADMIN' ) ) {
define( 'WP_ADMIN', true );
}
add_filter( 'wp_doing_ajax', '__return_false' );
}
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace ShortPixel\CriticalCSS\Integration;
class WebP extends IntegrationAbstract {
/**
* @return void
*/
public function enable() {
add_action( 'shortpixel_critical_css_nocache', [ $this, 'force_webp_off' ] );
}
/**
* @return void
*/
public function disable() {
remove_action( 'shortpixel_critical_css_nocache', [ $this, 'force_webp_off' ] );
}
public function force_webp_off() {
if(isset($_SERVER['HTTP_ACCEPT'])) {
$_SERVER['HTTP_ACCEPT'] = preg_replace( '~image/webp,?~', '', $_SERVER['HTTP_ACCEPT'] );
}
}
}

View File

@ -0,0 +1,94 @@
<?php
namespace ShortPixel\CriticalCSS;
use ComposePress\Core\Abstracts\Component;
class Log extends Component {
/**
*
*/
public function init() {
add_action( 'shortpixel_critical_css_purge_log', [ $this, 'purge' ] );
}
public function create_table() {
$wpdb = $this->wpdb;
if ( ! function_exists( 'dbDelta' ) ) {
include_once ABSPATH . 'wp-admin/includes/upgrade.php';
}
$charset_collate = $wpdb->get_charset_collate();
$table = $this->get_table_name();
$sql = "CREATE TABLE $table (
id MEDIUMINT(9) NOT NULL AUTO_INCREMENT,
template VARCHAR(255),
post_type VARCHAR(255),
object_id BIGINT(10),
type VARCHAR (10),
url TEXT,
data TEXT,
updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,";
if ( is_multisite() ) {
$sql .= "\n" . 'blog_id BIGINT(20),';
}
dbDelta( "$sql\nPRIMARY KEY (id)\n) {$charset_collate};" );
}
public function insert( $item ) {
if(empty($item['url'])) {
return;
}
$wpdb = $this->wpdb;
$data = $item;
$item = [
'template' => !empty($data['template'])?$data['template']:null,
'object_id' => !empty($data['object_id'])?$data['object_id']:null,
'type' => !empty($data['type'])?$data['type']:null,
'post_type' => !empty($data['post_type'])?$data['post_type']:null,
'url' => !empty($data['url'])?$data['url']:'',
'data' => serialize([
'result_status' => !empty($data['result_status'])?$data['result_status']:'',
'queue_id' => !empty($data['queue_id'])?$data['queue_id']:'',
'updated' => !empty($data['updated'])?$data['updated']:'',
'code' => !empty($data['code'])?$data['code']:'',
'css_hash' =>!empty($data['css_hash'])?$data['css_hash']:''
])
];
if ( is_multisite() ) {
$item['blog_id'] = $data['blog_id'];
}
$tableName = $this->get_table_name();
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_Q) && FileLog::instance()->log("INSERT INTO $tableName: ", $data);
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_Q) && FileLog::instance()->log("CALLED BY: ", debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
if(!empty($data['post_type'])) {
$wpdb->query( $wpdb->prepare("DELETE FROM $tableName WHERE post_type = %s", $item['post_type']) );
} elseif(!empty($data['template'])) {
$wpdb->query( $wpdb->prepare("DELETE FROM $tableName WHERE template = %s", $item['template']) );
} else {
$wpdb->query( $wpdb->prepare("DELETE FROM $tableName WHERE url = %s", $item['url']) );
}
$wpdb->insert( $tableName, $item );
}
public function get_table_name() {
$wpdb = $this->wpdb;
if ( is_multisite() ) {
return "{$wpdb->base_prefix}{$this->plugin->get_safe_slug()}_processed_items";
}
return $table = "{$wpdb->prefix}{$this->plugin->get_safe_slug()}_processed_items";
}
public function purge() {
return; //do not purge for now.
$tableName = $this->get_table_name();
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_DB) && FileLog::instance()->log("TRUNCATE TABLE $tableName. STACK: ", debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
$this->wpdb->query( "TRUNCATE TABLE $tableName" );
}
}

View File

@ -0,0 +1,205 @@
<?php
namespace ShortPixel\CriticalCSS\Queue\API;
use ShortPixel\CriticalCSS;
use ShortPixel\CriticalCSS\Queue\ListTableAbstract;
class Table extends ListTableAbstract {
const TABLE_NAME = 'api';
const STATUS_PROCESSING = 'processing';
const STATUS_PENDING = 'pending';
/**
* @return array
*/
public function get_columns() {
$columns = [
'url' => __( 'URL', shortpixel_critical_css()->get_lang_domain() ),
'template' => __( 'Template', shortpixel_critical_css()->get_lang_domain() ),
'post_type' => __( 'Post type', shortpixel_critical_css()->get_lang_domain() ),
'status' => __( 'Status', shortpixel_critical_css()->get_lang_domain() ),
//'queue_position' => __( 'Queue Position', shortpixel_critical_css()->get_lang_domain() ),
'actions' => __( 'Actions', shortpixel_critical_css()->get_lang_domain() ),
];
if ( is_multisite() ) {
$columns = array_merge( [
'blog_id' => __( 'Blog', shortpixel_critical_css()->get_lang_domain() ),
], $columns );
}
return $columns;
}
public function __construct( array $args = [] ) {
parent::__construct( [
'singular' => __( 'Queue Item', shortpixel_critical_css()->get_lang_domain() ),
'plural' => __( 'Queue Items', shortpixel_critical_css()->get_lang_domain() ),
'ajax' => false,
] );
}
protected function do_prepare_items() {
$wpdb = shortpixel_critical_css()->wpdb;
$table = $this->get_table_name();
$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} ORDER BY LOCATE('queue_id', {$table}.data) DESC, LOCATE('queue_index', {$table}.data) DESC LIMIT %d,%d", $this->start, $this->per_page ), ARRAY_A );
usort( $this->items, [ $this, 'sort_items' ] );
}
protected function process_purge_action() {
parent::process_purge_action();
shortpixel_critical_css()->get_cache_manager()->reset_web_check_transients();
}
/**
* @param array $item
*
* @return string
*/
protected function column_blog_id( array $item ) {
if ( empty( $item['blog_id'] ) ) {
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
$details = get_blog_details( [
'blog_id' => $item['blog_id'],
] );
if ( empty( $details ) ) {
return __( 'Blog Deleted', shortpixel_critical_css()->get_lang_domain() );
}
return $details->blogname;
}
/**
* @param array $item
*
* @return string
*/
protected function column_url( array $item ) {
//$settings = shortpixel_critical_css()->get_settings_manager()->get_settings();
if ( ! empty( $item['template'] ) || ! empty( $item['post_type'] ) ) {
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
return shortpixel_critical_css()->get_permalink( $item );
}
/**
* @param array $item
*
* @return string
*/
protected function column_post_type( array $item ) {
if ( ! empty( $item['post_type'] ) ) {
return $item['post_type'];
}
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
/**
* @param array $item
*
* @return string
*/
protected function column_template( array $item ) {
$settings = shortpixel_critical_css()->get_settings_manager()->get_settings();
// if ( isset($settings[ 'cache_mode' ]['templates']) ) {
if ( ! empty( $item['template'] ) ) {
return $item['template'];
}
// }
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
/**
* @param array $item
*
* @return string
*/
protected function column_status( array $item ) {
$data = maybe_unserialize( $item['data'] );
if ( ! empty( $data ) ) {
if ( ! empty( $data['queue_id'] ) ) {
switch ( $data['status'] ) {
case CriticalCSS\API::STATUS_UNKNOWN:
return __( 'Unknown', shortpixel_critical_css()->get_lang_domain() );
break;
case CriticalCSS\API::STATUS_QUEUED:
return __( 'Queued', shortpixel_critical_css()->get_lang_domain() );
break;
case CriticalCSS\API::STATUS_ONGOING:
return __( 'In Progress', shortpixel_critical_css()->get_lang_domain() );
break;
case CriticalCSS\API::STATUS_DONE:
return __( 'Completed', shortpixel_critical_css()->get_lang_domain() );
break;
}
} else {
if ( empty( $data['status'] ) ) {
return __( 'Pending', shortpixel_critical_css()->get_lang_domain() );
}
switch ( $data['status'] ) {
case CriticalCSS\API::STATUS_UNKNOWN:
return __( 'Unknown', shortpixel_critical_css()->get_lang_domain() );
break;
default:
return __( 'Pending', shortpixel_critical_css()->get_lang_domain() );
}
}
}
return __( 'Pending', shortpixel_critical_css()->get_lang_domain() );
}
/**
* @param array $item
*
* @return string
*/
protected function column_queue_position( array $item ) {
$data = maybe_unserialize( $item['data'] );
if ( ! isset( $data['queue_id'], $data['queue_index'] ) ) {
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
return $data['queue_index'];
}
/**
* @param array $item
*
* @return string
*/
protected function column_actions( array $item ) {
return '<button class="button button-primary spccss-api-action" data-action="api-run" data-id="' . $item['id'] . '"><span class="dashicons dashicons-controls-play"style="padding-top: 4px;"></span>Check</button>&nbsp;
<button class="button button-link-delete spccss-api-action" data-action="api-remove" data-id="' . $item['id'] . '"><span class="dashicons dashicons-no"style="padding-top: 4px;"></span>Remove</button>'
;//. json_encode($item);
}
/**
* @param array $a
* @param array $b
*
* @return int
*/
private function sort_items( $a, $b ) {
$a['data'] = maybe_unserialize( $a['data'] );
$b['data'] = maybe_unserialize( $b['data'] );
if ( isset( $a['data']['queue_index'] ) ) {
if ( isset( $b['data']['queue_index'] ) ) {
return $a['data']['queue_index'] > $b['data']['queue_index'] ? 1 : - 1;
}
return 1;
}
return - 1;
}
}

View File

@ -0,0 +1,163 @@
<?php
namespace ShortPixel\CriticalCSS\Queue;
use ShortPixel\CriticalCSS\Background\ProcessAbstract;
require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
/**
* Class ListTable
*/
abstract class ListTableAbstract extends \WP_List_Table {
/**
*
*/
const TABLE_NAME = '';
/**
* @var int
*/
protected $per_page = 0;
/**
* @var int
*/
protected $start = 0;
/**
* @var int
*/
protected $total_items = 0;
/**
* @var ProcessAbstract
*/
protected $queue;
protected $args = [];
/**
* ListTable constructor.
*
* @param array $args
*/
public function __construct( $args = [] ) {
$this->args = $args;
}
public function display() {
$this->_column_headers = [ $this->get_columns() ];
parent::display();
}
public function init() {
add_screen_option( 'per_page', [
'label' => ucfirst( static::TABLE_NAME ) . ' Queue Items',
'default' => 20,
'option' => static::TABLE_NAME . '_queue_items_per_page',
] );
parent::__construct( $this->args );
}
/**
*
*/
public function no_items() {
_e( 'Nothing in the queue. Please visit your website\'s pages, the visited pages are automatically processed.', 'sp' );
}
/**
* @param ProcessAbstract $queue
*/
public function set_queue( $queue ) {
$this->queue = $queue;
}
/**
*
*/
public function prepare_items() {
$this->pre_prepare_items();
$this->do_prepare_items();
$this->post_prepare_items();
}
/**
*
*/
protected function pre_prepare_items() {
$this->_column_headers = $this->get_column_info();
$this->process_bulk_action();
$table = $this->get_table_name();
$this->total_items = shortpixel_critical_css()->wpdb->get_var( "SELECT COUNT(id) FROM {$table}" );
$this->per_page = $this->total_items;
if ( ! $this->per_page ) {
$this->per_page = 1;
}
$this->start = 0;
}
/**
* @return void
*/
protected function process_bulk_action() {
if ( 'purge' === $this->current_action() ) {
$this->process_purge_action();
}
}
protected function process_purge_action() {
$this->queue->purge();
}
/**
* @return string
*/
protected function get_table_name() {
$wpdb = shortpixel_critical_css()->wpdb;
$table_name = static::TABLE_NAME;
if ( is_multisite() ) {
return "{$wpdb->base_prefix}shortpixel_critical_css_{$table_name}_queue";
}
return "{$wpdb->prefix}shortpixel_critical_css_{$table_name}_queue";
}
/**
* @return mixed
*/
abstract protected function do_prepare_items();
/**
*
*/
protected function post_prepare_items() {
$this->set_pagination_args( [
'total_items' => $this->total_items,
'per_page' => $this->per_page,
'total_pages' => ceil( $this->total_items / $this->per_page ),
] );
}
/**
* @return array
*/
protected function get_bulk_actions() {
return [
'purge' => __( 'Purge', shortpixel_critical_css()->get_lang_domain() ),
];
}
/**
* @param $id the DB id of the item to be retrieved
* @return array|null found record (with data unserialized) or null if not found
*/
public function get_item($id) {
$wpdb = shortpixel_critical_css()->wpdb;
$table = $this->get_table_name();
$results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE id=%d", $id ), ARRAY_A );
if(count($results)) {
$result = $results[0];
return $result;
}
return null;
}
}

View File

@ -0,0 +1,155 @@
<?php
namespace ShortPixel\CriticalCSS\Queue\Log;
use ShortPixel\CriticalCSS\API;
use ShortPixel\CriticalCSS\Queue\ListTableAbstract;
class Table extends ListTableAbstract {
public function __construct( array $args = [] ) {
parent::__construct( [
'singular' => __( 'Processed Log Item', shortpixel_critical_css()->get_lang_domain() ),
'plural' => __( 'Processed Log Items', shortpixel_critical_css()->get_lang_domain() ),
'ajax' => false,
] );
}
/**
* @return array
*/
public function get_columns() {
$columns = [
'url' => __( 'URL', shortpixel_critical_css()->get_lang_domain() ),
'template' => __( 'Template', shortpixel_critical_css()->get_lang_domain() ),
'post_type' => __( 'Post type', shortpixel_critical_css()->get_lang_domain() ),
'status' => __( 'Status', shortpixel_critical_css()->get_lang_domain() ),
'updated' => __( 'Updated', shortpixel_critical_css()->get_lang_domain() ),
];
if ( is_multisite() ) {
$columns = array_merge( [
'blog_id' => __( 'Blog', shortpixel_critical_css()->get_lang_domain() ),
], $columns );
}
return $columns;
}
protected function get_bulk_actions() {
return [];
}
protected function do_prepare_items() {
$wpdb = shortpixel_critical_css()->wpdb;
$table = $this->get_table_name();
$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} ORDER BY updated DESC LIMIT %d,%d", $this->start, $this->per_page ), ARRAY_A );
}
protected function get_table_name() {
return shortpixel_critical_css()->log->get_table_name();
}
protected function column_blog_id( array $item ) {
if ( empty( $item['blog_id'] ) ) {
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
$details = get_blog_details( [
'blog_id' => $item['blog_id'],
] );
if ( empty( $details ) ) {
return __( 'Blog Deleted', shortpixel_critical_css()->get_lang_domain() );
}
return $details->blogname;
}
/**
* @param array $item
*
* @return string
*/
protected function column_url( array $item ) {
if ( ! empty( $item['template'] ) || ! empty( $item['post_type'] ) ) {
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
return shortpixel_critical_css()->get_permalink( $item );
}
/**
* @param array $item
*
* @return string
*/
protected function column_post_type( array $item ) {
if ( ! empty( $item['post_type'] ) ) {
return $item['post_type'];
}
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
/**
* @param array $item
*
* @return string
*/
protected function column_template( array $item ) {
if ( ! empty( $item['template'] ) ) {
return $item['template'];
}
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
/**
* @param array $item
*
* @return string
*/
protected function column_status( array $item ) {
if ( ! empty( $item['data'] ) ) {
$css = shortpixel_critical_css()->retrieve_cached_css($item);
if($css->cache) {
$data = unserialize($item['data']);
if(!empty($data['result_status'])) {
$ret = $data['result_status'];
if(!empty($data['code'])) {
$shotsUrl = API::BASE_URL . 'screenshot/' . $data['code'] . '/';
$ret .= ' ( <a class="thickbox spccss-get" data-id="'. $item['id'] . '" href="">CSS</a> |'
.' <a class="spccss-screenshot" href="' . $shotsUrl . 'original.png" target="_blank">Original</a> | <a class="spccss-screenshot" href="' . $shotsUrl . 'critical.png" target="_blank">Critical</a> )';
}
return $ret;
}
}
else {
return "EXPIRED";
}
}
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
/**
* @param array $item
*
* @return string
*/
protected function column_updated( array $item ) {
if ( ! empty( $item['data'] ) ) {
$data = unserialize($item['data']);
if(!empty($data['updated'])) {
$ret = $data['updated'];
return $ret;
}
}
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
}

View File

@ -0,0 +1,133 @@
<?php
namespace ShortPixel\CriticalCSS\Queue\Web\Check;
use ShortPixel\CriticalCSS\Queue\ListTableAbstract;
class Table extends ListTableAbstract {
const TABLE_NAME = 'web_check';
const STATUS_PROCESSING = 'processing';
const STATUS_PENDING = 'pending';
const STATUS_EXISTS = 'web_check_exists'; //status used only in frontend
const STATUS_DONE = 'web_check_done'; //status used only in frontend
public function __construct( array $args = [] ) {
parent::__construct( [
'singular' => __( 'Web Check Queue Item', shortpixel_critical_css()->get_lang_domain() ),
'plural' => __( 'Web Check Queue Items', shortpixel_critical_css()->get_lang_domain() ),
'ajax' => false,
] );
}
/**
* @return array
*/
public function get_columns() {
$columns = [
'url' => __( 'URL', shortpixel_critical_css()->get_lang_domain() ),
'status' => __( 'Status', shortpixel_critical_css()->get_lang_domain() ),
'actions' => __( 'Actions', shortpixel_critical_css()->get_lang_domain() ),
];
if ( is_multisite() ) {
$columns = array_merge( [
'blog_id' => __( 'Blog', shortpixel_critical_css()->get_lang_domain() ),
], $columns );
}
return $columns;
}
protected function do_prepare_items() {
$wpdb = shortpixel_critical_css()->wpdb;
$table = $this->get_table_name();
$this->items = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} ORDER BY LOCATE('status', {$table}.data) DESC LIMIT %d,%d", $this->start, $this->per_page ), ARRAY_A );
}
protected function process_purge_action() {
parent::process_purge_action();
shortpixel_critical_css()->get_cache_manager()->reset_web_check_transients();
}
/**
* @param array $item
*
* @return string
*/
protected function column_blog_id( array $item ) {
if ( empty( $item['blog_id'] ) ) {
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
$details = get_blog_details( [
'blog_id' => $item['blog_id'],
] );
if ( empty( $details ) ) {
return __( 'Blog Deleted', shortpixel_critical_css()->get_lang_domain() );
}
return $details->blogname;
}
/**
* @param array $item
*
* @return string
*/
protected function column_url( array $item ) {
return shortpixel_critical_css()->get_permalink( $item );
}
/**
* @param array $item
*
* @return string
*/
protected function column_template( array $item ) {
$settings = shortpixel_critical_css()->get_settings_manager()->get_settings();
// if ( isset($settings['cache_mode']['templates'])
// && ! empty( $item['template'] )
// && in_array( $item['template'], $settings[ 'template_values' ] )
// ) {
return $item['template'];
// }
return __( 'N/A', shortpixel_critical_css()->get_lang_domain() );
}
/**
* @param array $item
*
* @return string
*/
protected function column_status( array $item ) {
$data = maybe_unserialize( $item['data'] );
if ( ! empty( $data ) && ! empty( $data['status'] ) ) {
if ( empty( $data['status'] ) ) {
return __( 'Pending', shortpixel_critical_css()->get_lang_domain() );
}
switch ( $data['status'] ) {
case self::STATUS_PROCESSING:
return __( 'Processing', shortpixel_critical_css()->get_lang_domain() );
break;
default:
return __( 'Pending', shortpixel_critical_css()->get_lang_domain() );
}
} else {
return __( 'Pending', shortpixel_critical_css()->get_lang_domain() );
}
}
/**
* @param array $item
*
* @return string
*/
protected function column_actions( array $item ) {
return '<button class="button button-primary spccss-api-action" data-action="web-run" data-id="' . $item['id'] . '"><span class="dashicons dashicons-controls-play"style="padding-top: 4px;"></span>Check</button>&nbsp;
<button class="button button-link-delete spccss-api-action" data-action="web-remove" data-id="' . $item['id'] . '"><span class="dashicons dashicons-no"style="padding-top: 4px;"></span>Remove</button>'
;//. json_encode($item);
}
}

View File

@ -0,0 +1,438 @@
<?php
namespace ShortPixel\CriticalCSS;
use ComposePress\Core\Abstracts\Component;
/**
* Class Request
*
* @property string $template
*/
class Request extends Component {
/**
* @var
*/
protected $nocache;
/**
* @var
*/
protected $template;
protected $original_is_single;
protected $original_is_page;
/**
* ShortPixel_CriticalCSS_Request constructor.
*/
public function init() {
add_action( 'init', [
$this,
'add_rewrite_rules',
] );
add_action( 'init', [
$this,
'check_log_cron',
] );
add_filter( 'rewrite_rules_array', [
$this,
'fix_rewrites',
], 11 );
add_action( 'request', [
$this,
'update_request',
] );
//if ( isset( $this->plugin->settings_manager->get_setting( 'cache_mode' )['templates'] ) ) {
add_action( 'template_include', [
$this,
'template_include',
], PHP_INT_MAX );
//}
/*
* Prevent a 404 on homepage if a static page is set.
* Will store query_var outside \WP_Query temporarily so we don't need to do any extra routing logic and will appear as if it was not set.
*/
add_action( 'query_vars', [
$this,
'query_vars',
] );
add_action( 'parse_request', [
$this,
'parse_request',
] );
// Don't fix url or try to guess url if we are using nocache on the homepage
add_filter( 'redirect_canonical', [
$this,
'redirect_canonical',
], 0 );
add_filter( 'redirect_canonical', [
$this,
'redirect_canonical_fix_vars',
], PHP_INT_MAX );
}
/**
*
*/
public function add_rewrite_rules() {
//add_rewrite_endpoint( 'nocache', E_ALL );
add_rewrite_rule( 'nocache/?$', 'index.php?nocache=1', 'top' );
}
public function check_log_cron() {
$scheduled = wp_next_scheduled( 'shortpixel_critical_css_purge_log' );
$integration = apply_filters( 'shortpixel_critical_css_cache_integration', false );
if ( ! $scheduled && ! $integration ) {
wp_schedule_single_event( time() + (int) $this->plugin->settings_manager->get_setting( 'web_check_interval' ), 'shortpixel_critical_css_purge_log' );
}
if ( $scheduled && $integration ) {
wp_unschedule_event( $scheduled, 'shortpixel_critical_css_purge_log' );
}
}
/**
*
*/
public function fix_rewrites( $rules ) {
$post_rewrite = $this->generate_rewrite_rules( $this->wp_rewrite->permalink_structure, EP_PERMALINK );
$root_rewrite = $this->generate_rewrite_rules( $this->wp_rewrite->root . '/', EP_ROOT );
$search_rewrite = $this->generate_rewrite_rules( $this->wp_rewrite->get_search_permastruct(), EP_SEARCH );
$page_rewrite = $this->generate_rewrite_rules( $this->wp_rewrite->get_page_permastruct(), EP_PAGES, true, true, false, false );
$cpt_rules = [];
foreach ( $this->wp_rewrite->extra_permastructs as $permastructname => $struct ) {
if ( is_array( $struct ) ) {
if ( count( $struct ) == 2 ) {
$cpt_rules[ $permastructname ] = $this->generate_rewrite_rules( $struct[0], $struct[1] );
} else {
$cpt_rules[ $permastructname ] = $this->generate_rewrite_rules( $struct['struct'], $struct['ep_mask'], $struct['paged'], $struct['feed'], $struct['forcomments'], $struct['walk_dirs'], $struct['endpoints'] );
}
} else {
$cpt_rules[ $permastructname ] = $this->generate_rewrite_rules( $struct );
}
}
if ( $this->wp_rewrite->use_verbose_page_rules ) {
$rules = array_merge( call_user_func_array( 'array_merge', array_values($cpt_rules) ),$root_rewrite, $search_rewrite, $page_rewrite, $post_rewrite, $rules );
} else {
$rules = array_merge( call_user_func_array( 'array_merge', array_values($cpt_rules) ), $root_rewrite, $search_rewrite, $post_rewrite, $page_rewrite, $rules );
}
return $rules;
}
/**
* Generate rewrite rules based off of WP-Write but specific to nocache endpoints
*
* @param $permalink_structure
* @param int $ep_mask
* @param bool $paged
* @param bool $feed
* @param bool $forcomments
* @param bool $walk_dirs
* @param bool $endpoints
*
* @return array
*/
private function generate_rewrite_rules( $permalink_structure, $ep_mask = EP_NONE, $paged = true, $feed = true, $forcomments = false, $walk_dirs = true, $endpoints = true ) {
preg_match_all( '/%.+?%/', $permalink_structure, $tokens );
$num_tokens = count( $tokens[0] );
$index = $this->wp_rewrite->index; //probably 'index.php'
$queries = array();
for ( $i = 0; $i < $num_tokens; ++ $i ) {
if ( 0 < $i ) {
$queries[ $i ] = $queries[ $i - 1 ] . '&';
} else {
$queries[ $i ] = '';
}
$query_token = str_replace( $this->wp_rewrite->rewritecode, $this->wp_rewrite->queryreplace, $tokens[0][ $i ] ) . $this->wp_rewrite->preg_index( $i + 1 );
$queries[ $i ] .= $query_token;
}
$structure = $permalink_structure;
$front = substr( $permalink_structure, 0, strpos( $permalink_structure, '%' ) );
if ( '/' !== $front ) {
$structure = str_replace( $front, '', $structure );
}
$structure = trim( $structure, '/' );
$dirs = $walk_dirs ? explode( '/', $structure ) : array( $structure );
$num_dirs = count( $dirs );
// Strip slashes from the front of $front.
$front = preg_replace( '|^/+|', '', $front );
$post_rewrite = array();
$struct = $front;
for ( $j = 0; $j < $num_dirs; ++ $j ) {
// Get the struct for this dir, and trim slashes off the front.
$struct .= $dirs[ $j ] . '/'; // Accumulate. see comment near explode('/', $structure) above.
$struct = ltrim( $struct, '/' );
// Replace tags with regexes.
$match = str_replace( $this->wp_rewrite->rewritecode, $this->wp_rewrite->rewritereplace, $struct );
// Make a list of tags, and store how many there are in $num_toks.
$num_toks = preg_match_all( '/%.+?%/', $struct, $toks );
// Get the 'tagname=$matches[i]'.
$query = ( ! empty( $num_toks ) && isset( $queries[ $num_toks - 1 ] ) ) ? $queries[ $num_toks - 1 ] : '';
if ( ! empty( $query ) ) {
if ( '&' !== $query[ strlen( $query ) - 1 ] ) {
$query .= '&';
}
}
$query .= 'nocache=1';
// Start creating the array of rewrites for this dir.
$rewrite = array();
// If we've got some tags in this dir.
if ( $num_toks ) {
$post = false;
/*
* Check to see if this dir is permalink-level: i.e. the structure specifies an
* individual post. Do this by checking it contains at least one of 1) post name,
* 2) post ID, 3) page name, 4) timestamp (year, month, day, hour, second and
* minute all present). Set these flags now as we need them for the endpoints.
*/
if ( strpos( $struct, '%postname%' ) !== false
|| strpos( $struct, '%post_id%' ) !== false
|| ( strpos( $struct, '%year%' ) !== false && strpos( $struct, '%monthnum%' ) !== false && strpos( $struct, '%day%' ) !== false && strpos( $struct, '%hour%' ) !== false && strpos( $struct, '%minute%' ) !== false && strpos( $struct, '%second%' ) !== false )
) {
$post = true;
}
if ( ! $post ) {
// For custom post types, we need to add on endpoints as well.
foreach ( get_post_types( array( '_builtin' => false ) ) as $ptype ) {
if ( strpos( $struct, "%$ptype%" ) !== false ) {
$post = true;
break;
}
}
}
// If creating rules for a permalink, do all the endpoints like attachments etc.
if ( $post ) {
// Trim slashes from the end of the regex for this dir.
$match = rtrim( $match, '/' );
/*
* Post pagination, e.g. <permalink>/2/
* Previously: '(/[0-9]+)?/?$', which produced '/2' for page.
* When cast to int, returned 0.
*/
$match = $match . '(?:/([0-9]+))?/nocache/?$';
$query = $index . '?' . $query . '&page=' . $this->wp_rewrite->preg_index( $num_toks + 1 );
// Not matching a permalink so this is a lot simpler.
} else {
// Close the match and finalise the query.
$match .= 'nocache/?$';
$query = $index . '?' . $query;
}
/*
* Create the final array for this dir by joining the $rewrite array (which currently
* only contains rules/queries for trackback, pages etc) to the main regex/query for
* this dir
*/
/** @noinspection SlowArrayOperationsInLoopInspection */
$rewrite = array_merge( $rewrite, array( $match => $query ) );
}
// Add the rules for this dir to the accumulating $post_rewrite.
/** @noinspection SlowArrayOperationsInLoopInspection */
$post_rewrite = array_merge( $rewrite, $post_rewrite );
}
return $post_rewrite;
}
/**
* @param $redirect_url
*
* @return bool
*/
public function redirect_canonical( $redirect_url ) {
global $wp_query;
if ( ! array_diff( array_keys( $wp_query->query ), [ 'nocache' ] ) || get_query_var( 'nocache' ) ) {
$redirect_url = false;
$wp_query->is_404 = false;
$this->original_is_single = $wp_query->is_single;
$this->original_is_page = $wp_query->is_page;
$wp_query->is_single = false;
$wp_query->is_page = false;
}
return $redirect_url;
}
public function redirect_canonical_fix_vars( $redirect_url ) {
global $wp_query;
if ( null !== $this->original_is_single ) {
$wp_query->is_single = $this->original_is_single;
}
if ( null !== $this->original_is_page ) {
$wp_query->is_page = $this->original_is_page;
}
return $redirect_url;
}
/**
* @SuppressWarnings(PHPMD.ShortVariable)
* @param \WP $wp
*/
public function parse_request( \WP $wp ) {
if ( isset( $wp->query_vars['nocache'] ) ) {
$this->nocache = $wp->query_vars['nocache'];
unset( $wp->query_vars['nocache'] );
}
}
/**
* @param $vars
*
* @return array
*/
public function query_vars( $vars ) {
$vars[] = 'nocache';
return $vars;
}
/**
* @param $vars
*
* @return mixed
*/
public function update_request( $vars ) {
if ( isset( $vars['nocache'] ) ) {
$vars['nocache'] = true;
}
return $vars;
}
/**
* @return bool
*/
public function is_no_cache() {
return $this->nocache;
}
/**
* @param $template
*
* @return mixed
*/
public function template_include( $template ) {
//for block themes
if(function_exists('wp_is_block_theme') && wp_is_block_theme()) {
$post = get_post();
$pageTemplate = get_post_meta($post->ID, '_wp_page_template');
$pageTemplate = is_array($pageTemplate)?array_pop($pageTemplate):$pageTemplate;
$this->template = $pageTemplate;
} else {
$this->template = str_replace( trailingslashit( WP_CONTENT_DIR ), '', $template );
}
return $template;
}
/**
* @return string
*/
public function get_template() {
return $this->template;
}
/**
* @SuppressWarnings(PHPMD.ShortVariable)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @return array
*/
public function get_current_page_type() {
global $wp;
$object_id = 0;
if ( is_home() ) {
$page_for_posts = get_option( 'page_for_posts' );
if ( ! empty( $page_for_posts ) ) {
$object_id = $page_for_posts;
$type = 'post';
}
} elseif ( is_front_page() ) {
$page_on_front = get_option( 'page_on_front' );
if ( ! empty( $page_on_front ) ) {
$object_id = $page_on_front;
$type = 'post';
}
} elseif ( is_singular() ) {
$object_id = get_the_ID();
$type = 'post';
} elseif ( is_tax() || is_category() || is_tag() ) {
$object_id = get_queried_object()->term_id;
$type = 'term';
} elseif ( is_author() ) {
$object_id = get_the_author_meta( 'ID' );
$type = 'author';
}
$object_id = absint( $object_id );
if ( ! isset( $type ) ) {
$this->plugin->integration_manager->disable_integrations();
$url = site_url( $wp->request );
$vars = [];
foreach ( $this->wp->public_query_vars as $var ) {
if ( isset( $_GET[ $var ] ) ) {
$vars[ $var ] = sanitize_url($_GET[ $var ]);
}
}
$url = add_query_arg( $vars, $url );
$this->plugin->integration_manager->enable_integrations();
$type = 'url';
unset( $object_id );
}
if('url' !== $type) {
$template = $this->template;
$post_type = get_post_type();
}
if ( is_multisite() ) {
$blog_id = get_current_blog_id();
}
$compact = [];
foreach ( [ 'object_id', 'type', 'url', 'blog_id', 'template', 'post_type' ] as $var ) {
if ( isset( $$var ) ) {
$compact[ $var ] = $$var;
}
}
return $compact;
}
public function get_page_type_for_url( $url ) {
$post_id = url_to_postid($url);
if(0 < $post_id) {
return (object)['type' => 'post', 'object_id' => $post_id];
} else {
return (object)['type' => 'url', 'url' => $url];
}
}
}

View File

@ -0,0 +1,871 @@
<?php
namespace ShortPixel\CriticalCSS\Settings;
use ComposePress\Core\Abstracts\Component;
/**
* weDevs Settings API wrapper class
*
* @version 1.3 (27-Sep-2016)
*
* @author Tareq Hasan <tareq@weDevs.com>
* @link https://tareq.co Tareq Hasan
* @example example/oop-example.php How to use the class
* @SuppressWarnings(PHPMD)
* @property \ShortPixel\CriticalCSS $plugin
*/
class API extends Component {
/**
* settings sections array
*
* @var array
*/
protected $settings_sections = [];
/**
* Settings fields array
*
* @var array
*/
protected $settings_fields = [];
/**
* Enqueue scripts and styles
*/
public function admin_enqueue_scripts() {
wp_enqueue_style( 'wp-color-picker' );
wp_enqueue_media();
wp_enqueue_script( 'wp-color-picker' );
wp_enqueue_script( 'jquery' );
wp_enqueue_script( 'beautify-css', plugin_dir_url( dirname(__FILE__ ) ) . 'assets/js/beautify-css.min.js' , [], false, true );
wp_enqueue_script( 'highlightjs', plugin_dir_url( dirname(__FILE__ ) ) . 'assets/js/highlight.min.js' , [], false, true );
wp_add_inline_script( 'highlightjs', $this->inline_scripts(), 'after' );
wp_enqueue_script ( 'ccss_admin_ui', plugin_dir_url( dirname(__FILE__ ) ) . 'assets/js/ui.min.js' , [], false, true );
wp_localize_script( 'ccss_admin_ui', 'ccssUILocal', [
'forceWebCheck' => __( "The Critical CSS cache has been flushed and the Critical CSS generation will start again for each page when it's visited.", shortpixel_critical_css()->get_lang_domain() ),
] );
wp_enqueue_style ( 'ccss_admin_ui', plugin_dir_url( dirname(__FILE__ ) ) . 'assets/css/ui.min.css');
wp_enqueue_style ( 'hightlightjs-theme', plugin_dir_url( dirname(__FILE__ ) ) . 'assets/css/atelier-lakeside.min.css');
$this->enqueue_contact_form_scripts();
}
function inline_scripts() {
return "
jQuery(document).ready(function ($) {
$('.spccss_notice .dismiss-button').click( function(e){
const causer = $(this).parents('.spccss_notice').attr('data-dismissed-causer');
$.ajax( {
method : 'post',
url : 'admin-ajax.php',
data : {
action : 'shortpixel_critical_css_dismiss',
causer : causer,
},
success : function( response ) {
}
});
$(this).parents('.spccss_notice').find('.notice-dismiss').click();
});
$('#spccss_usekey').click( function(e){
$.ajax( {
method : 'post',
url : 'admin-ajax.php',
data : {
action : 'shortpixel_critical_css_usekey',
},
success : function( response ) {
location.reload();
}
});
});
});
";
}
/**
* Enqueue contct form scripts
*/
function enqueue_contact_form_scripts()
{
wp_enqueue_script( 'contact-form', plugin_dir_url( dirname(__FILE__ ) ) . 'assets/js/shortpixel-contact-form.min.js' , [], false, true );
wp_localize_script( 'contact-form', 'ccssContactFormLocal', [
'description' => __( "We understand that this might feel complicated at first. The plugin team would be more than happy to help or to receive feed-back on how the plugin could be improved. \nJust fill in the form below and hit \"Send\" to get in touch with us." ),
'name' => __( "Your Name" ),
'email' => __( "Your E-mail" ),
'texthint' => __( "Please let us know the domain name, PHP and WP version and any other information you think is relevant." ),
'send' => __( "Send" ),
'buttonTitle' => __( "Get help or send us feed-back." ),
'close' => __( "Close" ),
'buttonText' => __( "Need help?" ),
'formError' => __( "Error sending the message. Please try again." ),
'formSuccess' => __( "Your message has been sent. Thank you!" ),
] );
wp_enqueue_style ( 'contact-form-style', plugin_dir_url( dirname(__FILE__ ) ) . 'assets/css/shortpixel-contact-form.min.css');
}
/**
* Set settings sections
*
* @param array $sections setting sections array
*
* @return $this
*/
public function set_sections( $sections ) {
$this->settings_sections = $sections;
return $this;
}
/**
* Add a single section
*
* @param array $section
*
* @return $this
*/
public function add_section( $section ) {
$section = wp_parse_args( $section, [
'form' => true,
] );
$this->settings_sections[] = $section;
return $this;
}
/**
* Set settings fields
*
* @param array $fields settings fields array
*
* @return $this
*/
public function set_fields( $fields ) {
$this->settings_fields = $fields;
return $this;
}
public function add_field( $section, $field ) {
$defaults = [
'name' => '',
'label' => '',
'desc' => '',
'type' => 'text',
];
$arg = wp_parse_args( $field, $defaults );
$this->settings_fields[ $section ][] = $arg;
return $this;
}
/**
* Initialize and registers the settings sections and fileds to WordPress
*
* Usually this should be called at `admin_init` hook.
*
* This function gets the initiated settings sections and fields. Then
* registers them to WordPress and ready for use.
*/
public function admin_init() {
//register settings sections
foreach ( $this->settings_sections as $section ) {
if ( false == get_option( $section['id'] ) ) {
add_option( $section['id'] );
}
if ( isset( $section['desc'] ) && ! empty( $section['desc'] ) ) {
$section['desc'] = '<div class="inside">' . $section['desc'] . '</div>';
$callback = function() use( $section ) { echo str_replace('"', '\"', $section['desc']) ; }; //create_function( '', 'echo "' . str_replace( '"', '\"', $section['desc'] ) . '";' );
} elseif ( isset( $section['callback'] ) ) {
$callback = $section['callback'];
} else {
$callback = null;
}
add_settings_section( $section['id'], $section['title'], $callback, $section['id'] );
}
//register settings fields
foreach ( $this->settings_fields as $section => $field ) {
foreach ( $field as $option ) {
$name = $option['name'];
$type = isset( $option['type'] ) ? $option['type'] : 'text';
$label = isset( $option['label'] ) ? $option['label'] : '';
$callback = isset( $option['callback'] ) ? $option['callback'] : [
$this,
'callback_' . $type,
];
$args = [
'id' => $name,
'class' => isset( $option['class'] ) ? $option['class'] : $name,
'label_for' => "{$section}[{$name}]",
'desc' => isset( $option['desc'] ) ? $option['desc'] : '',
'name' => $label,
'section' => $section,
'size' => isset( $option['size'] ) ? $option['size'] : null,
'options' => isset( $option['options'] ) ? $option['options'] : '',
'std' => isset( $option['default'] ) ? $option['default'] : '',
'sanitize_callback' => isset( $option['sanitize_callback'] ) ? $option['sanitize_callback'] : '',
'type' => $type,
'placeholder' => isset( $option['placeholder'] ) ? $option['placeholder'] : '',
'autocomplete' => isset( $option['autocomplete'] ) ? $option['autocomplete'] : '',
'min' => isset( $option['min'] ) ? $option['min'] : '',
'max' => isset( $option['max'] ) ? $option['max'] : '',
'step' => isset( $option['step'] ) ? $option['step'] : '',
];
add_settings_field( "{$section}[{$name}]", $label, $callback, $section, $section, $args );
}
}
// creates our settings in the options table
foreach ( $this->settings_sections as $section ) {
register_setting( $section['id'], $section['id'], [
$this,
'sanitize_options',
] );
}
}
/**
* Displays a hidden field for a settings field (still may use description)
*
* @param array $args settings field args
*/
public function callback_hidden( $args ) {
$this->callback_text( $args );
}
/**
* Displays a url field for a settings field
*
* @param array $args settings field args
*/
public function callback_url( $args ) {
$this->callback_text( $args );
}
/**
* Displays a text field for a settings field
*
* @param array $args settings field args
*/
public function callback_text( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$type = isset( $args['type'] ) ? $args['type'] : 'text';
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
$autocomplete= empty( $args['autocomplete'] ) ? '' : ' autocomplete="' . $args['autocomplete'] . '"';
$text_input = sprintf( '<input type="%1$s" class="%2$s-text" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s%7$s/>', $type, $size, $args['section'], $args['id'], $value, $placeholder, $autocomplete );
$text_input .= $this->get_field_description( $args );
echo $text_input;
}
/**
* Get the value of a settings field
*
* @param string $option settings field name
* @param string $section the section name this field belongs to
* @param string $default default text if it's not found
*
* @return string
*/
public function get_option( $option, $section, $default = '' ) {
$options = $this->plugin->settings_manager->get_settings();
if ( isset( $options[ $option ] ) ) {
return $options[ $option ];
}
return $default;
}
/**
* Get field description for display
*
* @param array $args settings field args
*
* @return string
*/
public function get_field_description( $args ) {
if ( ! empty( $args['desc'] ) ) {
$desc = sprintf( '<p class="description">%s</p>', $args['desc'] );
} else {
$desc = '';
}
return $desc;
}
private function echo_option($option_html) {
echo $option_html;return;
echo wp_kses($option_html, [
'input' => ['type' => [], 'class' => [], 'id' => [], 'name' => [], 'value' => [], 'placeholder' => [], 'min' => [], 'max' => [], 'step' => [], 'checked' => [], 'autocomplete' => []],
'textarea' => ['class' => [], 'id' => [], 'name' => [], 'cols' => [], 'rows' => [], 'placeholder' => []],
'select' => ['class' => [], 'id' => [], 'name' => [], 'value' => [], 'placeholder' => []],
'option' => ['value' => [], 'selected' => [], 'style' => []],
'label' => ['for' => [], 'style' => []],
'p' => ['class' => [], 'style' => []],
'span' => ['class' => [], 'style' => []],
'strong' => ['class' => [], 'style' => []],
'a' => ['href' => [], 'target' => [], 'class' => [], 'data-id' => [], 'style' => []],
'table' => ['class' => [], 'style' => []],
'th' => ['class' => [], 'style' => []],
'tr' => ['class' => [], 'style' => []],
'td' => ['class' => [], 'style' => []],
'div' => ['class' => [], 'id' => [], 'style' => []],
'style' => ['type' => []],
]);
}
/**
* Displays a number field for a settings field
*
* @param array $args settings field args
*/
public function callback_number( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$type = isset( $args['type'] ) ? $args['type'] : 'number';
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
$min = empty( $args['min'] ) ? '' : ' min="' . $args['min'] . '"';
$max = empty( $args['max'] ) ? '' : ' max="' . $args['max'] . '"';
$step = empty( $args['max'] ) ? '' : ' step="' . $args['step'] . '"';
$number_input = sprintf( '<input type="%1$s" class="%2$s-number" id="%3$s[%4$s]" name="%3$s[%4$s]" value="%5$s"%6$s%7$s%8$s%9$s/>', $type, $size, $args['section'], $args['id'], $value, $placeholder, $min, $max, $step );
$number_input .= $this->get_field_description( $args );
$this->echo_option($number_input);
}
/**
* Displays a checkbox for a settings field
*
* @param array $args settings field args
*/
public function callback_checkbox( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$check_input = '<fieldset>';
$check_input .= sprintf( '<label for="wpuf-%1$s[%2$s]">', $args['section'], $args['id'] );
$check_input .= sprintf( '<input type="hidden" name="%1$s[%2$s]" value="off" />', $args['section'], $args['id'] );
$check_input .= sprintf( '<input type="checkbox" class="checkbox" id="wpuf-%1$s[%2$s]" name="%1$s[%2$s]" value="on" %3$s />', $args['section'], $args['id'], checked( $value, 'on', false ) );
$check_input .= sprintf( '%1$s</label>', $args['desc'] );
$check_input .= '</fieldset>';
$this->echo_option($check_input);
}
/**
* Displays a multicheckbox a settings field
*
* @param array $args settings field args
*/
public function callback_multicheck( $args ) {
$value = $this->get_option( $args['id'], $args['section'], $args['std'] );
$html = '<fieldset>';
$html .= sprintf( '<input type="hidden" name="%1$s[%2$s]" value="" />', $args['section'], $args['id'] );
foreach ( $args['options'] as $key => $label ) {
$checked = isset( $value[ $key ] ) ? $value[ $key ] : '0';
$html .= sprintf( '<label for="wpuf-%1$s[%2$s][%3$s]">', $args['section'], $args['id'], $key );
$html .= sprintf( '<input type="checkbox" class="checkbox" id="wpuf-%1$s[%2$s][%3$s]" name="%1$s[%2$s][%3$s]" value="%3$s" %4$s />', $args['section'], $args['id'], $key, checked( $checked, $key, false ) );
$html .= sprintf( '%1$s</label><br>', $label );
}
$html .= $this->get_field_description( $args );
$html .= '</fieldset>';
$this->echo_option($html);
}
/**
* Displays a multicheckbox a settings field
*
* @param array $args settings field args
*/
public function callback_radio( $args ) {
$value = $this->get_option( $args['id'], $args['section'], $args['std'] );
$html = '<fieldset>';
foreach ( $args['options'] as $key => $label ) {
$html .= sprintf( '<label for="wpuf-%1$s[%2$s][%3$s]">', $args['section'], $args['id'], $key );
$html .= sprintf( '<input type="radio" class="radio" id="wpuf-%1$s[%2$s][%3$s]" name="%1$s[%2$s]" value="%3$s" %4$s />', $args['section'], $args['id'], $key, checked( $value, $key, false ) );
$html .= sprintf( '%1$s</label><br>', $label );
}
$html .= $this->get_field_description( $args );
$html .= '</fieldset>';
$this->echo_option($html);
}
/**
* Displays a selectbox for a settings field
*
* @param array $args settings field args
*/
public function callback_select( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$html = sprintf( '<select class="%1$s" name="%2$s[%3$s]" id="%2$s[%3$s]">', $size, $args['section'], $args['id'] );
foreach ( $args['options'] as $key => $label ) {
$html .= sprintf( '<option value="%s"%s>%s</option>', $key, selected( $value, $key, false ), $label );
}
$html .= sprintf( '</select>' );
$html .= $this->get_field_description( $args );
$this->echo_option($html);
}
/**
* Displays a textarea for a settings field
*
* @param array $args settings field args
*/
public function callback_textarea( $args ) {
$value = esc_textarea( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$placeholder = empty( $args['placeholder'] ) ? '' : ' placeholder="' . $args['placeholder'] . '"';
$html = sprintf( '<textarea rows="5" cols="55" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]"%4$s>%5$s</textarea>', $size, $args['section'], $args['id'], $placeholder, $value );
$html .= $this->get_field_description( $args );
$this->echo_option($html);
}
/**
* Displays a textarea for a settings field
*
* @param array $args settings field args
*
* @return void
*/
public function callback_html( $args ) {
$this->echo_option($args['desc']);
}
/**
* Displays a rich text textarea for a settings field
*
* @param array $args settings field args
*/
public function callback_wysiwyg( $args ) {
$value = $this->get_option( $args['id'], $args['section'], $args['std'] );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : '500px';
echo '<div style="max-width: ' . $size . ';">';
$editor_settings = [
'teeny' => true,
'textarea_name' => $args['section'] . '[' . $args['id'] . ']',
'textarea_rows' => 10,
];
if ( isset( $args['options'] ) && is_array( $args['options'] ) ) {
$editor_settings = array_merge( $editor_settings, $args['options'] );
}
wp_editor( $value, $args['section'] . '-' . $args['id'], $editor_settings );
echo '</div>';
echo $this->get_field_description( $args );
}
/**
* Displays a file upload field for a settings field
*
* @param array $args settings field args
*/
public function callback_file( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$id = $args['section'] . '[' . $args['id'] . ']';
$label = isset( $args['options']['button_label'] ) ? $args['options']['button_label'] : __( 'Choose File' );
$html = sprintf( '<input type="text" class="%1$s-text wpsa-url" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>', $size, $args['section'], $args['id'], $value );
$html .= '<input type="button" class="button wpsa-browse" value="' . $label . '" />';
$html .= $this->get_field_description( $args );
$this->echo_option($html);
}
/**
* Displays a password field for a settings field
*
* @param array $args settings field args
*/
public function callback_password( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$html = sprintf( '<input type="password" class="%1$s-text" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s"/>', $size, $args['section'], $args['id'], $value );
$html .= $this->get_field_description( $args );
$this->echo_option($html);
}
/**
* Displays a color picker field for a settings field
*
* @param array $args settings field args
*/
public function callback_color( $args ) {
$value = esc_attr( $this->get_option( $args['id'], $args['section'], $args['std'] ) );
$size = isset( $args['size'] ) && ! is_null( $args['size'] ) ? $args['size'] : 'regular';
$html = sprintf( '<input type="text" class="%1$s-text wp-color-picker-field" id="%2$s[%3$s]" name="%2$s[%3$s]" value="%4$s" data-default-color="%5$s" />', $size, $args['section'], $args['id'], $value, $args['std'] );
$html .= $this->get_field_description( $args );
$this->echo_option($html);
}
public function sanitize_options( $options ) {
if ( ! $options ) {
return $options;
}
foreach ( $options as $option_slug => $option_value ) {
$sanitize_callback = $this->get_sanitize_callback( $option_slug );
// If callback is set, call it
if ( $sanitize_callback ) {
$options[ $option_slug ] = call_user_func( $sanitize_callback, $options );
continue;
}
}
return $options;
}
/**
* Get sanitization callback for given option slug
*
* @param string $slug option slug
*
* @return mixed string or bool false
*/
public function get_sanitize_callback( $slug = '' ) {
if ( empty( $slug ) ) {
return false;
}
// Iterate over registered fields and see if we can find proper callback
foreach ( $this->settings_fields as $section => $options ) {
foreach ( $options as $option ) {
if ( $option['name'] != $slug ) {
continue;
}
// Return the callback name
return isset( $option['sanitize_callback'] ) && is_callable( $option['sanitize_callback'] ) ? $option['sanitize_callback'] : false;
}
}
return false;
}
/**
* Show navigations as tab
*
* Shows all the settings section labels as tab
*/
public function show_navigation() {
$html = '<h2 class="nav-tab-wrapper">';
$count = count( $this->settings_sections );
// don't show the navigation if only one section exists
if ( 1 === $count ) {
return;
}
foreach ( $this->settings_sections as $tab ) {
$html .= sprintf( '<a href="#%1$s" class="nav-tab" id="%1$s-tab">%2$s</a>', $tab['id'], $tab['title'] );
}
$html .= '<button class="ccss_top_actions" id="ccssFlushWebCheck" title="Clear the cache and force a Web Check on all pages."><span class="ccss-icon"></span></button>';
$html .= '</h2>';
echo wp_kses($html, 'post');
}
/**
* Show the section settings forms
*
* This function displays every sections in a different form
*/
public function show_forms() {
?>
<div class="metabox-holder">
<?php foreach ( $this->settings_sections as $form ) { ?>
<div id="<?php echo $form['id']; ?>" class="group" style="display: none;">
<?php if ( $form['form'] ) : ?>
<form method="post" action="<?php if ( is_multisite() ) : ?>../<?php endif; ?>options.php">
<?php endif; ?>
<?php
do_action( 'wsa_form_top_' . $form['id'], $form );
if ( $form['form'] ) {
settings_fields( $form['id'] );
}
do_settings_sections( $form['id'] );
do_action( 'wsa_form_bottom_' . $form['id'], $form );
if ( $form['form'] && isset( $this->settings_fields[ $form['id'] ] ) ) :
?>
<div style="padding-left: 10px">
<?php submit_button(); ?>
</div>
<?php endif; ?>
<?php if ( $form['form'] ) : ?>
</form>
<?php endif; ?>
</div>
<?php } ?>
</div>
<div id="shortpixelContactFormContainer"></div>
<?php
$this->script();
}
/**
* Tabbable JavaScript codes & Initiate Color Picker
*
* This code uses localstorage for displaying active tabs
*/
public function script() {
?>
<script>
jQuery(document).ready(function ($) {
//Initiate Color Picker
$('.wp-color-picker-field').wpColorPicker();
// Switches option sections
$('.group').hide();
var activetab = '';
if (typeof(localStorage) != 'undefined') {
activetab = localStorage.getItem("activetab");
}
if (activetab != '' && $(activetab).length) {
$(activetab).fadeIn();
} else {
$('.group:first').fadeIn();
}
$('.group .collapsed').each(function () {
$(this).find('input:checked').parent().parent().parent().nextAll().each(
function () {
if ($(this).hasClass('last')) {
$(this).removeClass('hidden');
return false;
}
$(this).filter('.hidden').removeClass('hidden');
});
});
if (activetab != '' && $(activetab + '-tab').length) {
$(activetab + '-tab').addClass('nav-tab-active');
}
else {
$('.nav-tab-wrapper a:first').addClass('nav-tab-active');
}
$('.nav-tab-wrapper a').click(function (evt) {
$('.nav-tab-wrapper a').removeClass('nav-tab-active');
$(this).addClass('nav-tab-active').blur();
var clicked_group = $(this).attr('href');
if (typeof(localStorage) != 'undefined') {
localStorage.setItem("activetab", $(this).attr('href'));
}
$('.group').hide();
$(clicked_group).fadeIn();
evt.preventDefault();
});
$('.wpsa-browse').on('click', function (event) {
event.preventDefault();
var self = $(this);
// Create the media frame.
var file_frame = wp.media.frames.file_frame = wp.media({
title: self.data('uploader_title'),
button: {
text: self.data('uploader_button_text')
},
multiple: false
});
file_frame.on('select', function () {
attachment = file_frame.state().get('selection').first().toJSON();
self.prev('.wpsa-url').val(attachment.url).change();
});
// Finally, open the modal
file_frame.open();
});
$('.spccss-close, .spccss-modal-background').on('click', function(){
$('.spccss-modal')[0].style.display = "none";
$('.spccss-modal-background')[0].style.display = "none";
});
$('.spccss-get').on('click', function(event) {
event.preventDefault();
event.stopPropagation();
const link = event.target;
$.ajax( {
method : 'post',
url : 'admin-ajax.php',
data : {
action : 'shortpixel_critical_css_get',
log_id : event.target.dataset['id'],
},
success : function( response ) {
/*
* Open the Critical CSS modal, to display the CCSS that is assigned to that page.
*/
let modal = document.getElementById('ccss_modal'),
modalBackground = document.getElementById('ccss_modal_background');
modal.querySelector('.spccss-modal-css').innerHTML = '<pre>' + hljs.highlightAuto(css_beautify(response.cache)).value + '</pre>';
modal.style.display = 'flex';
modalBackground.style.display= "block";
var hideModal = function(event) {
if (event.target == modal) {
modal.style.display = "none";
window.removeEventListener('click', hideModal);
}
}
window.addEventListener('click', hideModal);
},
error : function(xhr, ajaxOptions, thrownError) {
alert(thrownError + " Please retry.");
}
} );
});
$('.spccss-api-action').on('click', function(event) {
event.preventDefault();
event.stopPropagation();
let button = event.target, span, action, part;
action = button.dataset['action'];
if( ['api-remove', 'api-run', 'web-run', 'web-remove'].indexOf(action) === -1 ) {
return;
}
part = 'shortpixel_critical_css_' + action.replace('-', '_');
if(button.classList.contains('dashicons')) {
span = $(button);
button = button.parentElement;
} else {
span = $('span.dashicons', button);
}
span.addClass('dashicons-clock');
span.removeClass('dashicons-controls-play');
button.disabled = true;
$.ajax( {
method : 'post',
url : 'admin-ajax.php',
data : {
action : part,
queue_id : button.dataset['id'],
},
success : function( response ) {
//if finalized, will change to check and clicking on it will show details.
//else (generate) will let the button call results
if(response.status == '<?= \ShortPixel\CriticalCSS\API::STATUS_DONE ?>') {
alert("Job completed, " + ( !!response.resultStatus ? "the Critical CSS is " + response.resultStatus +" and has a size of "
+ (new Number(response.size)).toLocaleString() + " bytes": "please check the Processed Log"));
window.location.reload();
} else if(response.status == '<?= \ShortPixel\CriticalCSS\Queue\Web\Check\Table::STATUS_DONE ?>' ||
response.status == '<?= \ShortPixel\CriticalCSS\Queue\Web\Check\Table::STATUS_EXISTS ?>') {
alert("Web check completed, added to the API Queue.");
window.location.reload();
} else if(response.status == '<?= \ShortPixel\CriticalCSS\API::STATUS_QUEUED ?>') {
alert("The job is queued for processing in the ShortPixel Critical CSS processing cloud (" + response.queue_id + ")");
} else if(response.status == '<?= \ShortPixel\CriticalCSS\Queue\Web\Check\Table::STATUS_PENDING ?>'||
response.status == '<?= \ShortPixel\CriticalCSS\Queue\Web\Check\Table::STATUS_PROCESSING ?>'
) {
alert("The job is pending web check before pushing to ShortPixel Critical CSS processing queue");
} else if(response.status == '<?= \ShortPixel\CriticalCSS\API::STATUS_REMOVED ?>') {
$(button).closest('tr').hide();
window.location.reload();
}
$(button).removeAttr( 'disabled' );
span.addClass('dashicons-controls-play');
span.removeClass('dashicons-clock');
},
error : function(xhr, ajaxOptions, thrownError) {
alert(thrownError + " Please retry.");
$(button).removeAttr( 'disabled' );
span.addClass('dashicons-controls-play');
span.removeClass('dashicons-clock');
}
} );
});
});
</script>
<?php
$this->_style_fix();
}
public function _style_fix() {
global $wp_version;
if ( version_compare( $wp_version, '3.8', '<=' ) ) :
?>
<style type="text/css">
/** WordPress 3.8 Fix **/
.form-table th {
padding: 20px 10px;
}
#wpbody-content .metabox-holder {
padding-top: 5px;
}
</style>
<?php
endif;
}
/**
*
*/
public function init() {
add_action( 'admin_enqueue_scripts', [
$this,
'admin_enqueue_scripts',
] );
}
}

View File

@ -0,0 +1,91 @@
<?php
namespace ShortPixel\CriticalCSS\Settings;
class ApiKeyTools {
static $API_URL = 'https://api.shortpixel.com';
/**
* Returns SPIO API key if found, null otherwise
* @return ?string
*/
static function getApiKey()
{
$settings = shortpixel_critical_css()->settings_manager->get_settings();
return !empty($settings['apikey'])?$settings['apikey']:null;
}
/**
* Returns SPIO API key if found, null otherwise
* @return ?string
*/
static function getSPIOApiKey()
{
$spioApiKey = get_option( 'wp-short-pixel-apiKey', false );
return !empty($spioApiKey)?$spioApiKey:null;
}
/**
* Validates API key using local checks and remote call. Returns array with boolean status and error string if not valid
* @param $key
*
* @return array
*/
static function validateAPIKey(string $key)
{
$ret = [
'status' => true,
];
if(strlen($key) !== 20) {
$ret = [
'status' => false,
'error' => 'Invalid API key length. Must be 20 characters'
];
} else {
$result = wp_safe_remote_get( self::$API_URL . '/v2/user-info.php?key=' . urlencode($key) , array('timeout' => 120, 'httpversion' => '1.1'));
if( is_wp_error( $result ) ) {
$ret = [
'status' => false,
'error' => $result->get_error_message()
];
} else {
if( is_array( $result['response'] ) && $result['response']['code'] == 200 ) {
$answer = json_decode( $result['body'] );
if( !empty($answer->Status) ) {
//error status
$ret = [
'status' => false,
'error' => !empty($answer->Status->Message) ? $answer->Status->Message : 'Unknown error',
];
} elseif( !empty($answer->email) ) {
//api key is valid
$ret = [
'status' => true
];
} else {
$ret = [
'status' => false,
'error' => 'Invalid API answer',
];
}
}
}
}
return $ret;
}
static function updateApiKey( $key )
{
$settings = shortpixel_critical_css()->settings_manager->get_settings();
shortpixel_critical_css()->settings_manager->update_settings(array_merge(
$settings,
[ 'apikey' => $key ]
));
return true;
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace ShortPixel\CriticalCSS\Settings;
use ComposePress\Core\Abstracts\Component;
use ShortPixel\CriticalCSS;
/**
* Class Manager
*
* @package ShortPixel\CriticalCSS\Settings
* @property CriticalCSS $plugin
*/
class Manager extends Component {
protected $settings = [
'version',
'web_check_interval',
'apikey',
'force_web_check',
//'template_cache',
'cache_mode',
'post_type_values',
'template_values',
'web_check_interval',
'prioritize_manual_css',
'force_include_styles',
'fallback_css',
];
public function get_setting( $name ) {
$settings = $this->get_settings();
if ( empty( $settings ) || ! isset( $settings[ $name ] ) ) {
return false;
}
return $settings[ $name ];
}
/**
* @return array
*/
public function get_settings() {
if ( is_multisite() ) {
return $this->get_settings_multisite();
}
$settings = get_option( $this->plugin->get_option_name(), [] );
if ( empty( $settings ) ) {
$settings = [];
}
$settings = array_merge( $this->get_defaults(), $settings );
return $settings;
}
private function get_settings_multisite() {
$settings = get_site_option( $this->plugin->get_option_name(), [] );
if ( empty( $settings ) ) {
$settings = get_option( $this->plugin->get_option_name(), [] );
}
if ( empty( $settings ) ) {
$settings = [];
}
$settings = array_merge( $this->get_defaults(), $settings );
return $settings;
}
protected function get_defaults() {
$defaults = [];
foreach ( $this->settings as $setting ) {
$defaults[ $setting ] = null;
}
return $defaults;
}
/**
* @param array $settings
*
* @return bool
*/
public function update_settings( array $settings ) {
if ( is_multisite() ) {
return update_site_option( $this->plugin->get_option_name(), $settings );
}
return update_option( $this->plugin->get_option_name(), $settings );
}
/**
*
*/
public function init() {
// TODO: Implement init() method.
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace ShortPixel\CriticalCSS\Template;
class Log extends \ShortPixel\CriticalCSS\Log {
public function get( $template = null, $post_type = null ) {
if(is_null($template) && is_null($post_type)) {
throw new \Exception("Template and post_type are not specified in query to template log");
} elseif( !is_null( $template ) ) {
$query = "SELECT `template`, `object_id`, `type`, `url`, `post_type` FROM {$this->get_table_name()} WHERE `template` = %s";
$args = [ $template ];
} else {
$query = "SELECT `template`, `object_id`, `type`, `url`, `post_type` FROM {$this->get_table_name()} WHERE `post_type` = %s";
$args = [ $post_type ];
}
if ( is_multisite() ) {
$query .= " AND `blog_id` = %d";
$args[] = get_current_blog_id();
}
$query .= " GROUP BY `object_id`, `type`, `url`";
return $this->wpdb->get_results( $this->wpdb->prepare( $query, $args ), ARRAY_A );
}
public function get_table_name() {
$wpdb = $this->wpdb;
if ( is_multisite() ) {
return "{$wpdb->base_prefix}{$this->plugin->get_safe_slug()}_template_log";
}
return $table = "{$wpdb->prefix}{$this->plugin->get_safe_slug()}_template_log";
}
public function delete( $object_id, $type, $url ) {
$query = "DELETE FROM {$this->get_table_name()} WHERE ((`object_id` = %d and `type` = %s) OR (`type` = %s and `url` = %s and `url` IS NOT NULL))";
$args = [ $object_id, $type, $type, $url ];
if ( is_multisite() ) {
$query .= " AND `blog_id` = %d";
$args[] = get_current_blog_id();
}
$this->wpdb->query( $this->wpdb->prepare( $query, $args ) );
}
}

View File

@ -0,0 +1,216 @@
<?php
namespace ShortPixel\CriticalCSS\Web\Check\Background;
use ShortPixel\CriticalCSS\Background\ProcessAbstract;
use ShortPixel\CriticalCSS\FileLog;
use ShortPixel\CriticalCSS\Queue\Web\Check\Table;
class Process extends ProcessAbstract {
protected $action = 'shortpixel_critical_css_web_check';
private $_processed_urls = [];
/**
* Task
*
* Override this method to perform any actions required on each
* queue item. Return the modified item for further processing
* in the next pass through. Or, return false to remove the
* item from the queue.
*
* @param mixed $item Queue item to iterate over
*
* @return mixed
*/
public function task( $item, $return_job = false ) {
$ret = false;
$upload_dir = wp_upload_dir();
$url = shortpixel_critical_css()->get_permalink( $item );
if ( empty( $url ) ) {
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("WEB CHECK - URL IS EMPTY FOR ITEM:", $item);
return $ret;
}
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("WEB CHECK TASK URL: " . $url);
$item = $this->set_processing();
if ( isset( $this->_processed_urls[ $url ] ) ) {
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("WEB CHECK ALREADY PROCESSED");
if($return_job) {
$ret = [
'status' => Table::STATUS_DONE,
];
}
return $ret;
}
$api_queue = shortpixel_critical_css()->get_api_queue();
if ( $api_queue->get_item_exists( $item ) ) {
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("WEB CHECK ALREADY EXISTS");
if($return_job) {
$ret = [
'status' => Table::STATUS_EXISTS, // already added to queue
];
}
return $ret;
}
$css_hash = shortpixel_critical_css()->get_data_manager()->get_css_hash( $item );
$html_hash = shortpixel_critical_css()->get_data_manager()->get_html_hash( $item );
$web_check_args = apply_filters( 'shortpixel_critical_css_web_check_request_args', [], $item );
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("WEB CHECK CALLING wp_remote_get. REQUEST ARGS: ", $web_check_args);
$result = wp_remote_get( $url, $web_check_args);
$settings = shortpixel_critical_css()->settings_manager->get_settings();
if( $result instanceof \WP_Error && $result->get_error_code() == 'http_request_failed' ) {
if($settings['loopback_available']) {
shortpixel_critical_css()->settings_manager->update_settings(array_merge(
$settings,
[ 'loopback_available' => false ]
));
}
} else {
if(!$settings['loopback_available']) {
shortpixel_critical_css()->settings_manager->update_settings(array_merge(
$settings,
[ 'loopback_available' => true ]
));
}
}
(SPCCSS_DEBUG & FileLog::DEBUG_AREA_INIT) && FileLog::instance()->log("WEB CHECK wp_remote_get RESPONSE: ",
is_string($result) ? substr($result, 0, 400) : $result);
if ( $result instanceof \WP_Error ) {
if ( empty( $item['error'] ) ) {
$item['error'] = 0;
}
if ( $return_job || $item['error'] <= apply_filters( 'shortpixel_critical_css_web_check_retries', 3 ) ) {
$item['error'] ++;
sleep( 1 );
$item = $this->set_pending();
$ret = $item;
}
return $ret;
}
$document = new \DOMDocument();
if ( ! @$document->loadHTML( $result['body'] ) ) {
return $ret;
}
$xpath = new \DOMXpath( $document );
$css = '';
$urls = [];
foreach ( $xpath->query( '((//style|//STYLE)|(//link|//LINK)[@rel="stylesheet"])' ) as $tag ) {
$name = strtolower( $tag->tagName );
$rel = $tag->getAttribute( 'rel' );
$href = $tag->getAttribute( 'href' );
if ( 'link' == $name ) {
if ( 'stylesheet' == $rel ) {
// If not a stylesheet, rocket_async_css_process_file return false, or exclude regex/file extension matches, move on
if ( 'stylesheet' !== $rel ) {
continue;
}
if ( 0 === strpos( $href, '//' ) ) {
//Handle no protocol urls
$href = 'http:' . $href;
}
$href = set_url_scheme( $href );
$urls[] = $href;
} else {
$css .= $tag->textContent;
}
}
}
if ( preg_match_all( '#loadCSS\s*\(\s*["\'](.*)["\']\s*#', $result['body'], $matches ) ) {
foreach ( $matches[1] as $match ) {
$href = $match;
if ( 0 === strpos( $href, '//' ) ) {
//Handle no protocol urls
$href = 'http:' . $match;
}
$href = set_url_scheme( $href );
$urls[] = $href;
}
}
$urls = apply_filters( 'shortpixel_critical_css_web_check_css_urls', $urls, $item );
foreach ( $urls as $url ) {
$host = parse_url( $url, PHP_URL_HOST );
if ( empty( $host ) ) {
$url = site_url( $url );
}
$file = wp_remote_get( $url, [
'sslverify' => false,
] );
// Catch Error
if ( $file instanceof \WP_Error || ( is_array( $file ) && ( empty( $file['response']['code'] ) || ! in_array( $file['response']['code'], [
200,
304,
] ) ) )
) {
continue;
}
$css .= $file['body'];
}
$changed = false;
$new_css_hash = hash( "crc32b", $css );
if ( empty( $css_hash ) || $css_hash != $new_css_hash ) {
$changed = true;
$css_hash = $new_css_hash;
}
if ( ! $changed ) {
$body = $document->getElementsByTagName( 'body' )->item( 0 );
if ( null !== $body ) {
$new_html_hash = hash( "crc32b", $document->saveHTML( $body ) );
if ( empty( $html_hash ) || $html_hash != $new_html_hash ) {
$changed = true;
$html_hash = $new_html_hash;
}
}
}
if($return_job) {
$ret = [
'status' => Table::STATUS_PENDING,
];
}
if ( $changed && ! $api_queue->get_item_exists( $item ) ) {
$item['css_hash'] = $css_hash;
$item['html_hash'] = $html_hash;
shortpixel_critical_css()->get_integration_manager()->disable_integrations();
shortpixel_critical_css()->get_cache_manager()->purge_page_cache( $item['type'], $item['object_id'], shortpixel_critical_css()->get_permalink( $item ) );
shortpixel_critical_css()->get_integration_manager()->enable_integrations();
shortpixel_critical_css()->get_data_manager()->set_cache( $item, '' );
$api_queue->push_to_queue( $item )->save();
if($return_job) {
$ret = [
'status' => Table::STATUS_DONE,
];
}
}
$this->_processed_urls[ $url ] = true;
return $ret;
}
private function set_processing() {
return $this->set_status( Table::STATUS_PROCESSING );
}
private function set_status( $status ) {
$batch = $this->get_batch();
$data = end( $batch->data );
$data['status'] = $status;
$batch->data = [ $data ];
$this->update( $batch->key, $batch->data );
return $data;
}
private function set_pending() {
return $this->set_status( Table::STATUS_PENDING );
}
}

View File

@ -0,0 +1,7 @@
/*!
Theme: Atelier Lakeside
Author: Bram de Haan (http://atelierbramdehaan.nl)
License: ~ MIT (or more permissive) [via base16-schemes-source]
Maintainer: @highlightjs/core-team
Version: 2021.09.0
*/pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#7ea2b4;background:#161b1d}.hljs ::selection,.hljs::selection{background-color:#516d7b;color:#7ea2b4}.hljs-comment{color:#5a7b8c}.hljs-tag{color:#7195a8}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#7ea2b4}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#d22d72}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#935c25}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#8a8a0f}.hljs-strong{font-weight:700;color:#8a8a0f}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#568c3b}.hljs-built_in,.hljs-doctag,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp{color:#2d8f6f}.hljs-attribute,.hljs-function .hljs-title,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#257fad}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#6b6bb8}.hljs-emphasis{color:#6b6bb8;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#b72dd2}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700}

View File

@ -0,0 +1,142 @@
#shortpixelContactForm .error{
display: none;
}
.shortpixelContactForm{
position: fixed;
right: -400px;
bottom: 90px;
width: 300px;
background: white;
padding: 20px;
background-color: #ecf9fc;
border-radius: 20px 0 0 20px;
box-shadow: 0px 0px 10px -1px rgba(0,0,0,1);
display: block;
/*opacity: 0;*/
transition: 1s;
}
.shortpixelContactForm.opened{
display: block;
right: 0px;
opacity: 1;
transition: 1s;
}
.shortpixelContactForm input, .shortpixelContactForm textarea{
margin: 5px 0;
}
.shortpixelContactForm input, .shortpixelContactForm textarea {
width: 100%;
}
.shortpixelContactForm input[type=submit] {
background: red;
color: white;
border: none;
border-radius: 5px;
padding: 5px 20px;
width: 100%;
}
.shortpixelContactForm.sent input[type=submit] {
background: green;
}
.shortpixelContactFormWidgetButton{
position: fixed;
right: 0;
bottom: 40px;
width: 120px;
height: 40px;
vertical-align: center;
box-shadow: 0px 0px 10px -1px rgba(0,0,0,1);
border-radius: 20px 0 0 20px;
transition: box-shadow 0.3s ease-in-out;
transition: width 0.3s ease-in-out;
display: flex;
background: #ecf9fc;
cursor: pointer;
padding: 0 0 0 10px;
}
.shortpixelContactFormWidgetButton.hovered{
box-shadow: 0px 0px 16px -1px rgba(0,0,0,1);
}
.shortpixelContactFormWidgetButton.opened{
width: 40px;
}
.shortpixelContactFormWidgetButton.opened p{
visibility: hidden;
opacity: 0;
transition: visibility 0s, opacity 0.5s linear;
}
.shortpixelContactFormWidgetButton:before{
display: inline-block;
content: '\00D7';
width: 0;
visibility: hidden;
opacity: 0;
padding-top: 7px;
}
.shortpixelContactFormWidgetButton.opened:before{
display: inline-block;
content: '\00D7';
width: auto;
font-size: 40px;
visibility: visible;
opacity: 1;
transition: visibility 0s, opacity 0.5s linear;
}
.shortpixelContactFormWidgetButton p{
padding: 0;
margin: 0;
white-space: nowrap;
margin: 10px 10px 0 10px;
visibility: visible;
opacity: 1;
transition: visibility 0s, opacity 0.5s linear;
}
.shortpixelContactFormWidgetButton:after{
display: inline-block;
content: ' ';
background: transparent url('../img/blob.png') center right no-repeat;
background-size: contain;
width: 40px;
height: 40px;
}
#shortpixelContactFormWidgetButton i:before {
display: none;
}
#shortpixelContactFormWidgetButton.hovered:not(.opened) i:before {
content: "\00D7";
color: #fff;
text-align: center;
padding-top: 1px;
display: block;
}
#shortpixelContactFormWidgetButton.hovered:not(.opened) i {
display: block;
border: 0px solid;
border-radius: 15px;
background: gray;
font-size: 15px;
font-style: normal;
line-height: 10px;
height: 15px;
width: 15px;
position: absolute;
top: -10px;
right: 5px;
}
#shortpixelContactForm input:disabled {
opacity: 0.5;
}
#shortpixelContactForm .form-messages {
margin: 0;
}
#shortpixelContactForm.sent form {
visibility: hidden;
}
#shortpixelContactForm.sent .form-messages {
position: absolute;
top: 20px;
width: 170px;
padding: 10px;
}

View File

@ -0,0 +1 @@
#shortpixelContactForm .error{display:none}.shortpixelContactForm{position:fixed;right:-400px;bottom:90px;width:300px;background:#fff;padding:20px;background-color:#ecf9fc;border-radius:20px 0 0 20px;box-shadow:0 0 10px -1px rgba(0,0,0,1);display:block;transition:1s}.shortpixelContactForm.opened{display:block;right:0;opacity:1;transition:1s}.shortpixelContactForm input,.shortpixelContactForm textarea{margin:5px 0}.shortpixelContactForm input,.shortpixelContactForm textarea{width:100%}.shortpixelContactForm input[type=submit]{background:red;color:#fff;border:none;border-radius:5px;padding:5px 20px;width:100%}.shortpixelContactForm.sent input[type=submit]{background:green}.shortpixelContactFormWidgetButton{position:fixed;right:0;bottom:40px;width:120px;height:40px;vertical-align:center;box-shadow:0 0 10px -1px rgba(0,0,0,1);border-radius:20px 0 0 20px;transition:box-shadow 0.3s ease-in-out;transition:width 0.3s ease-in-out;display:flex;background:#ecf9fc;cursor:pointer;padding:0 0 0 10px}.shortpixelContactFormWidgetButton.hovered{box-shadow:0 0 16px -1px rgba(0,0,0,1)}.shortpixelContactFormWidgetButton.opened{width:40px}.shortpixelContactFormWidgetButton.opened p{visibility:hidden;opacity:0;transition:visibility 0s,opacity 0.5s linear}.shortpixelContactFormWidgetButton:before{display:inline-block;content:'\00D7';width:0;visibility:hidden;opacity:0;padding-top:7px}.shortpixelContactFormWidgetButton.opened:before{display:inline-block;content:'\00D7';width:auto;font-size:40px;visibility:visible;opacity:1;transition:visibility 0s,opacity 0.5s linear}.shortpixelContactFormWidgetButton p{padding:0;margin:0;white-space:nowrap;margin:10px 10px 0 10px;visibility:visible;opacity:1;transition:visibility 0s,opacity 0.5s linear}.shortpixelContactFormWidgetButton:after{display:inline-block;content:' ';background:transparent url(../img/blob.png) center right no-repeat;background-size:contain;width:40px;height:40px}#shortpixelContactFormWidgetButton i:before{display:none}#shortpixelContactFormWidgetButton.hovered:not(.opened) i:before{content:"\00D7";color:#fff;text-align:center;padding-top:1px;display:block}#shortpixelContactFormWidgetButton.hovered:not(.opened) i{display:block;border:0 solid;border-radius:15px;background:gray;font-size:15px;font-style:normal;line-height:10px;height:15px;width:15px;position:absolute;top:-10px;right:5px}#shortpixelContactForm input:disabled{opacity:.5}#shortpixelContactForm .form-messages{margin:0}#shortpixelContactForm.sent form{visibility:hidden}#shortpixelContactForm.sent .form-messages{position:absolute;top:20px;width:170px;padding:10px}

View File

@ -0,0 +1,47 @@
.spCcssTemplates, .spCcssPostTypes{
background: #f0f0f1;
padding: 10px;
border-radius: 5px;
}
#shortpixel_critical_css .form-table td{
width: 85%;
}
.form-table td fieldset .spCcssTemplates label, .form-table td fieldset .spCcssPostTypes label {
margin: 0 10px !important;
vertical-align: top;
width: 30%;
padding: 4px 0;
}
.post_type_values,.template_values{
display:none;
}
.ccss_top_actions {
cursor: pointer;
border: 1px solid #135e96;
border-bottom-color: rgb(31, 190, 201);
border-bottom-style: solid;
border-bottom-width: 1px;
border-bottom: none;
margin-left: 1.5em;
padding: 5px 5px;
font-size: 5px;
line-height: 1.71428571;
font-weight: 600;
background: #135e96;
color: #fff;
text-decoration: none;
white-space: nowrap;
outline: 0;
display: inline-block;
}
.ccss-icon {
background: url(../img/clearcss-white.svg) top left no-repeat;
background-size: auto;
background-size: cover;
display: inline-block;
height: 22px;
width: 22px;
}
.ccss_top_actions.disabled {
background: #dcdcde;
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
viewBox="0 0 700 700">
<g
style="fill:#ffffff;fill-opacity:1"
transform="matrix(1.3111934,0,0,1.3091353,-309.16918,-307.23381)"><path
d="m 677.1,463.2 c -22.2,-10.3 -31.2,-8.8 -51.5,-19.8 -20.4,-11.1 -32.8,-6.3 -34.9,-5.3 -2.6,1.6 -11.6,7.4 -14,10.8 l 120.2,53.8 c 4.2,-20.7 0.2,-30.2 -19.8,-39.5 z"
id="path4"
style="fill:#ffffff;fill-opacity:1"
/><path
d="m 281.6,343.8 c -7.2,0 -13,5.8 -13,12.9 0,7.1 5.9,13 12.9,13 h 0.1 c 7.1,-0.1 12.9,-5.8 12.9,-12.9 0,-3.4 -1.3,-6.7 -3.7,-9.2 -2.5,-2.4 -5.7,-3.7 -9.2,-3.8 z"
id="path6"
style="fill:#ffffff;fill-opacity:1"
/><path
d="m 727.9,268.3 c -4.2,-2.1 -9.2,-0.5 -11.4,3.7 l -1.1,2.3 -60.8,115 32.6,15.7 c 0,0 31.1,-82.3 44.6,-125.8 1.7,-4.2 0.1,-8.9 -3.9,-10.9 z"
id="path8"
style="fill:#ffffff;fill-opacity:1"
/><path
d="m 357.3,494.7 h 0.7 c 12.4,0 23.1,-10.8 23.4,-23.5 0.2,-6.1 -2.2,-12 -6.6,-16.6 -4.5,-4.7 -10.6,-7.4 -16.9,-7.5 v 0 c -13,0 -23.8,10.7 -24,23.4 -0.3,12.9 10.2,23.8 23.4,24.2 z"
id="path10"
style="fill:#ffffff;fill-opacity:1"
/><path
d="m 500,990 c -333.33333,6.66667 -166.66667,3.33333 0,0 z M 358.5,412.7 c 15.4,0.3 30.4,6.9 41.2,18.2 10.8,11.3 16.5,25.8 16.2,41 -0.8,31.6 -26.6,57.3 -57.6,57.3 h -1.7 c -32.1,-1 -57.7,-27.6 -57,-59.4 0.6,-30.9 27.2,-57.1 57.9,-57.1 z m -77,-16.8 c -21.2,0 -39,-17.9 -39,-39.2 0,-21.5 17.6,-39 39.1,-39 10.6,0 20.4,4.2 27.8,11.6 7.4,7.5 11.4,17.3 11.3,27.7 -0.2,21.2 -17.6,38.7 -38.9,38.8 z m 431.8,178.8 c -1.5,4.3 -2.9,8.5 -4.3,12.6 -20.4,56.5 -44.1,104.6 -72.7,146.8 -1.3,1.9 -2.7,3.8 -4.1,5.7 -1.3,1.7 -2.6,3.5 -3.8,5.3 -6.7,10.1 -16.4,15.2 -28.9,15.2 -1.5,0 -3.1,-0.1 -4.7,-0.2 -39.1,-3.7 -77.9,-12.7 -115.2,-26.8 -70.2,-26.4 -128.2,-67.1 -172.5,-121 -8.2,-10 -9.9,-21.6 -4.9,-32.7 4.9,-10.7 14.9,-17 27.4,-17.1 31.8,-0.4 64,-6.7 95.8,-18.7 31.6,-11.9 54.2,-26.9 71.3,-47 3.7,-4.3 6.9,-9.2 10.4,-14.3 1.7,-2.5 3.4,-5.1 5.3,-7.6 l 5.5,-7.8 39.3,18 -6.6,9.6 c -1.9,2.8 -3.7,5.5 -5.6,8.3 -4,6 -8.2,12.3 -12.8,18.2 -15.3,19.4 -27.8,33.2 -53.3,47.4 -27,15 -80.3,34.4 -114.8,41.5 9,7.3 33.3,22.3 41.5,28.3 18.8,-4.3 46.2,-15.5 58.2,-26.6 l 23.6,-21.9 -6,31.6 c -1.8,9.3 -17.5,37.5 -27.3,49.3 6.6,3.1 13.3,6.4 19.7,10.2 8.4,4.9 16.2,4.8 28.6,-0.6 21.5,-9.3 43.6,-36 58.7,-48.9 1.8,-1.6 3.6,-3.2 5.6,-4.9 l 29,-25.5 -8.3,33.1 c -2.8,11 -23.6,45.7 -42.7,74.2 0.9,0.2 1.9,0.4 2.8,0.5 5.4,1 19.4,1.9 24.9,2.7 4.9,0.8 18.3,0.4 23.2,1.2 20.4,-30.5 43.9,-76 59.5,-114.3 4,-9.7 7.6,-19.9 11.2,-29.7 1.7,-4.6 3.3,-9.2 5,-13.8 l 5.3,-14.5 42.2,18 z m 18.6,-48.4 c -0.5,1.8 -1.2,3.5 -1.9,5.4 l -4.7,13 -201,-89.4 4.2,-9.6 c 5.8,-13.6 15.2,-24.2 27.8,-31.6 20.6,-12.1 42.8,-13.5 64.7,-4 23.2,10.1 48,21 72.4,32.3 32.8,15.1 48.6,49.7 38.5,83.9 z m 24.1,-245 c -12.8,35.2 -56.4,158.8 -56.4,158.8 L 618.4,401.8 697.9,257 c 5.1,-9.6 12.4,-14.3 22.3,-14.3 1.7,0 3.6,0.1 5.6,0.4 10.7,1.6 19.5,6.4 26.1,14.3 5.8,7 7.2,15.3 4.1,23.9 z"
id="path12"
style="fill:#ffffff;fill-opacity:1" /></g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -0,0 +1,51 @@
;( function( $, d, w ) {
$(function(){
let data = {
action : 'shortpixel_critical_css_getapikey',
};
$.ajax( {
url : 'admin-ajax.php',
method : 'post',
data : data,
success : function( data ) {
const apiKey = data.key || '';
$('tr.apikey td').html('<input type="hidden" id="ccssApiKeyDefault" value=""><input type="text" class="regular-text" id="ccssApiKey" value="" autocomplete="off">\n' +
'<button id="ccssApikeyUpdate" class="button button-primary" style="display: none;"></button>\n' +
'<p class="notice notice-error hidden" style="margin-left: 0;"></p>' +
'<p class="description"></p>')
$('#ccssApiKeyDefault').val(apiKey);
$('#ccssApiKey').val(apiKey);
$('#ccssApikeyUpdate').text( ccssApikeyLocal.buttonText );
$('tr.apikey td .description').html( apiKey=='' ? ccssApikeyLocal.descriptionRegister : ccssApikeyLocal.descriptionLogin );
}
} );
});
$('body').on('keyup','#ccssApiKey',function(e){
if( $('#ccssApiKeyDefault').val() == $('#ccssApiKey').val() ) {
$('#ccssApikeyUpdate').hide();
} else {
$('#ccssApikeyUpdate').show();
}
});
$('body').on('click','#ccssApikeyUpdate',function(e){
e.preventDefault();
let data = {
action : 'shortpixel_critical_css_updateapikey',
key : $('#ccssApiKey').val(),
};
$.ajax( {
url : 'admin-ajax.php',
method : 'post',
data : data,
success : function( data ) {
if(data.status == 'error') {
$('tr.apikey td .notice-error').text(data.error).show();
} else {
location.reload();
}
}
} );
})
} )( jQuery, document, window );

View File

@ -0,0 +1 @@
(function(a){a(function(){a.ajax({url:"admin-ajax.php",method:"post",data:{action:"shortpixel_critical_css_getapikey"},success:function(b){const c=b.key||"";a("tr.apikey td").html("<input type=\"hidden\" id=\"ccssApiKeyDefault\" value=\"\"><input type=\"text\" class=\"regular-text\" id=\"ccssApiKey\" value=\"\" autocomplete=\"off\">\n<button id=\"ccssApikeyUpdate\" class=\"button button-primary\" style=\"display: none;\"></button>\n<p class=\"notice notice-error hidden\" style=\"margin-left: 0;\"></p><p class=\"description\"></p>"),a("#ccssApiKeyDefault").val(c),a("#ccssApiKey").val(c),a("#ccssApikeyUpdate").text(ccssApikeyLocal.buttonText),a("tr.apikey td .description").html(""==c?ccssApikeyLocal.descriptionRegister:ccssApikeyLocal.descriptionLogin)}})}),a("body").on("keyup","#ccssApiKey",function(){a("#ccssApiKeyDefault").val()==a("#ccssApiKey").val()?a("#ccssApikeyUpdate").hide():a("#ccssApikeyUpdate").show()}),a("body").on("click","#ccssApikeyUpdate",function(b){b.preventDefault();let c={action:"shortpixel_critical_css_updateapikey",key:a("#ccssApiKey").val()};a.ajax({url:"admin-ajax.php",method:"post",data:c,success:function(b){"error"==b.status?a("tr.apikey td .notice-error").text(b.error).show():location.reload()}})})})(jQuery,document,window);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,102 @@
;( function( $, d, w ) {
const formHtml = `<div id="shortpixelContactForm" class="shortpixelContactForm">
<div class="form-messages"></div>
<form action="/admin-ajax.php" method="POST">
<input type="hidden" name="source" value="">
<input type="hidden" name="quriobot_answers" value="">
<p>${ccssContactFormLocal.description}</p>
<input type="text" name="name" value="" placeholder="${ccssContactFormLocal.name}" required="required">
<input type="email" name="email" value="" placeholder="${ccssContactFormLocal.email}" required="required">
<textarea rows="6" name="message" value=""
placeholder="${ccssContactFormLocal.texthint}" required="required"
></textarea>
<input type="submit" name="submit" value="${ccssContactFormLocal.send}">
</form>
</div>`;
const buttonHtml = `
<div id="shortpixelContactFormWidgetButton" class="shortpixelContactFormWidgetButton" tabindex="0" role="button"
title="${ccssContactFormLocal.buttonTitle}">
<i class="glyphicon glyphicon-remove" title="${ccssContactFormLocal.close}"></i>
<p>${ccssContactFormLocal.buttonText}</p>
</div>
`;
$('#shortpixelContactFormContainer').html(formHtml + buttonHtml);
$('body').on('mouseover', '#shortpixelContactFormWidgetButton', function(){
$('#shortpixelContactFormWidgetButton').addClass('hovered');
});
$('body').on('mouseout', '#shortpixelContactFormWidgetButton', function(){
$('#shortpixelContactFormWidgetButton').removeClass('hovered');
});
$('body').on('click', '#shortpixelContactFormWidgetButton', function(){
if($('#shortpixelContactFormWidgetButton').hasClass('opened')) {
$('#shortpixelContactFormWidgetButton').removeClass('opened');
$('#shortpixelContactForm').removeClass('opened');
} else {
$('#shortpixelContactForm .form-messages').hide();
$('#shortpixelContactForm input[type=submit]').prop('disabled', false);
$('#shortpixelContactFormWidgetButton').addClass('opened');
$('#shortpixelContactForm').addClass('opened').removeClass('sent');
}
});
$('body').on('click', '#shortpixelContactFormWidgetButton.hovered i', function(e){
e.preventDefault();
$('#shortpixelContactFormWidgetButton').fadeOut();
return false;
});
$('body').on('submit', '#shortpixelContactForm form', function(e) {
e.preventDefault();
$('#shortpixelContactForm div.form-messages').hide().removeClass('error').removeClass('success');
const form = $(this);
let data = form.serializeArray()
data = data.concat({
name: 'action',
value: 'shortpixel_critical_css_contact',
},{
name: 'submit',
value: 'Send',
},
);
$.ajax( {
url : 'admin-ajax.php',
method : 'post',
data : data,
beforeSend : function() {
form.find('input[type=submit]').prop('disabled', true);
},
error : function( error ) {
$('#shortpixelContactForm div.form-messages')
.addClass('error')
.text( ccssContactFormLocal.formError )
.fadeIn();
form.find('input[type=submit]').prop('disabled', false);
},
success : function( data ) {
if(data && data.response && data.response.search('input_error') != -1) {
form.find('input[type=submit]').prop('disabled', false);
$('#shortpixelContactForm div.form-messages')
.addClass('error')
.text( ccssContactFormLocal.formError )
.fadeIn();
} else {
$('#shortpixelContactForm').addClass('sent');
$('#shortpixelContactForm form')[0].reset();
$('#shortpixelContactForm div.form-messages')
.addClass('updated')
.text( ccssContactFormLocal.formSuccess )
.fadeIn();
}
},
complete : function(jqxr, textStatus) {
}
} );
})
} )( jQuery, document, window );

View File

@ -0,0 +1,20 @@
(function(a){const b=`<div id="shortpixelContactForm" class="shortpixelContactForm">
<div class="form-messages"></div>
<form action="/admin-ajax.php" method="POST">
<input type="hidden" name="source" value="">
<input type="hidden" name="quriobot_answers" value="">
<p>${ccssContactFormLocal.description}</p>
<input type="text" name="name" value="" placeholder="${ccssContactFormLocal.name}" required="required">
<input type="email" name="email" value="" placeholder="${ccssContactFormLocal.email}" required="required">
<textarea rows="6" name="message" value=""
placeholder="${ccssContactFormLocal.texthint}" required="required"
></textarea>
<input type="submit" name="submit" value="${ccssContactFormLocal.send}">
</form>
</div>`,c=`
<div id="shortpixelContactFormWidgetButton" class="shortpixelContactFormWidgetButton" tabindex="0" role="button"
title="${ccssContactFormLocal.buttonTitle}">
<i class="glyphicon glyphicon-remove" title="${ccssContactFormLocal.close}"></i>
<p>${ccssContactFormLocal.buttonText}</p>
</div>
`;a("#shortpixelContactFormContainer").html(b+c),a("body").on("mouseover","#shortpixelContactFormWidgetButton",function(){a("#shortpixelContactFormWidgetButton").addClass("hovered")}),a("body").on("mouseout","#shortpixelContactFormWidgetButton",function(){a("#shortpixelContactFormWidgetButton").removeClass("hovered")}),a("body").on("click","#shortpixelContactFormWidgetButton",function(){a("#shortpixelContactFormWidgetButton").hasClass("opened")?(a("#shortpixelContactFormWidgetButton").removeClass("opened"),a("#shortpixelContactForm").removeClass("opened")):(a("#shortpixelContactForm .form-messages").hide(),a("#shortpixelContactForm input[type=submit]").prop("disabled",!1),a("#shortpixelContactFormWidgetButton").addClass("opened"),a("#shortpixelContactForm").addClass("opened").removeClass("sent"))}),a("body").on("click","#shortpixelContactFormWidgetButton.hovered i",function(b){return b.preventDefault(),a("#shortpixelContactFormWidgetButton").fadeOut(),!1}),a("body").on("submit","#shortpixelContactForm form",function(b){b.preventDefault(),a("#shortpixelContactForm div.form-messages").hide().removeClass("error").removeClass("success");const c=a(this);let d=c.serializeArray();d=d.concat({name:"action",value:"shortpixel_critical_css_contact"},{name:"submit",value:"Send"}),a.ajax({url:"admin-ajax.php",method:"post",data:d,beforeSend:function(){c.find("input[type=submit]").prop("disabled",!0)},error:function(){a("#shortpixelContactForm div.form-messages").addClass("error").text(ccssContactFormLocal.formError).fadeIn(),c.find("input[type=submit]").prop("disabled",!1)},success:function(b){b&&b.response&&-1!=b.response.search("input_error")?(c.find("input[type=submit]").prop("disabled",!1),a("#shortpixelContactForm div.form-messages").addClass("error").text(ccssContactFormLocal.formError).fadeIn()):(a("#shortpixelContactForm").addClass("sent"),a("#shortpixelContactForm form")[0].reset(),a("#shortpixelContactForm div.form-messages").addClass("updated").text(ccssContactFormLocal.formSuccess).fadeIn())},complete:function(){}})})})(jQuery,document,window);

View File

@ -0,0 +1,72 @@
;( function( $, d, w ) {
$(function(){
$('#ccssFlushWebCheck').click(function(e){
e.preventDefault();
$.ajax( {
url : 'admin-ajax.php',
method : 'post',
data : {
action : 'shortpixel_critical_css_force_web_check'
},
beforeSend : function() {
$('#ccssFlushWebCheck').prop('disabled', true).addClass('disabled');
},
error : function( error ) {
},
success : function( data ) {
if(data && data.status && data.status == 'ok') {
$('h2.nav-tab-wrapper').before(`<div id="setting-error-settings_updated" class="notice notice-success settings-error is-dismissible"><p><strong>${ccssUILocal.forceWebCheck}</strong></p> </div>`);
}
},
complete : function(){
$('#ccssFlushWebCheck').prop('disabled', false).removeClass('disabled');
}
} );
});
$('input[name="shortpixel_critical_css[cache_mode][postTypes]"]').parent().after($('<div>').addClass('spCcssPostTypes'));
$('input[name="shortpixel_critical_css[cache_mode][templates]"]').parent().after($('<div>').addClass('spCcssTemplates'));
$('.post_type_values fieldset input[type=hidden], .post_type_values fieldset label').each(function(index, element){
$(element).detach();
$('.spCcssPostTypes').append(element);
});
$('.template_values fieldset input[type=hidden], .template_values fieldset label').each(function(index, element){
$(element).detach();
$('.spCcssTemplates').append(element);
});
$('body').on('change', '#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[postTypes\\]', function(e){
switchSuboptionsBlock(this, $('.spCcssPostTypes'));
});
$('body').on('change', '#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[templates\\]', function(e){
switchSuboptionsBlock(this, $('.spCcssTemplates'));
});
switchSuboptionsBlock('#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[postTypes\\]', $('.spCcssPostTypes'));
switchSuboptionsBlock('#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[templates\\]', $('.spCcssTemplates'));
//disable to putt off all three modes, one should be always on
$('#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[postTypes\\],#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[templates\\],#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[posts\\]').change(function(){
if( $(this).parents('fieldset').find('>label>input[type=checkbox]:checked').length < 1 ){
$(this).prop('checked', true);
}
})
});
function switchSuboptionsBlock(option, block)
{
if($(option).is(':checked')){
$(block).css('display', 'block');
//activate all values if nothing checked
if($(block).find('input:checked').length < 1) {
$(block).find('input[type=checkbox]').prop('checked', true);
}
} else {
$(block).css('display', 'none');
}
}
} )( jQuery, document, window );

View File

@ -0,0 +1 @@
(function(a){function b(b,c){a(b).is(":checked")?(a(c).css("display","block"),1>a(c).find("input:checked").length&&a(c).find("input[type=checkbox]").prop("checked",!0)):a(c).css("display","none")}a(function(){a("#ccssFlushWebCheck").click(function(b){b.preventDefault(),a.ajax({url:"admin-ajax.php",method:"post",data:{action:"shortpixel_critical_css_force_web_check"},beforeSend:function(){a("#ccssFlushWebCheck").prop("disabled",!0).addClass("disabled")},error:function(){},success:function(b){b&&b.status&&"ok"==b.status&&a("h2.nav-tab-wrapper").before(`<div id="setting-error-settings_updated" class="notice notice-success settings-error is-dismissible"><p><strong>${ccssUILocal.forceWebCheck}</strong></p> </div>`)},complete:function(){a("#ccssFlushWebCheck").prop("disabled",!1).removeClass("disabled")}})}),a("input[name=\"shortpixel_critical_css[cache_mode][postTypes]\"]").parent().after(a("<div>").addClass("spCcssPostTypes")),a("input[name=\"shortpixel_critical_css[cache_mode][templates]\"]").parent().after(a("<div>").addClass("spCcssTemplates")),a(".post_type_values fieldset input[type=hidden], .post_type_values fieldset label").each(function(b,c){a(c).detach(),a(".spCcssPostTypes").append(c)}),a(".template_values fieldset input[type=hidden], .template_values fieldset label").each(function(b,c){a(c).detach(),a(".spCcssTemplates").append(c)}),a("body").on("change","#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[postTypes\\]",function(){b(this,a(".spCcssPostTypes"))}),a("body").on("change","#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[templates\\]",function(){b(this,a(".spCcssTemplates"))}),b("#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[postTypes\\]",a(".spCcssPostTypes")),b("#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[templates\\]",a(".spCcssTemplates")),a("#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[postTypes\\],#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[templates\\],#wpuf-shortpixel_critical_css\\[cache_mode\\]\\[posts\\]").change(function(){1>a(this).parents("fieldset").find(">label>input[type=checkbox]:checked").length&&a(this).prop("checked",!0)})})})(jQuery,document,window);