array( 'CTPS_Settings', 'sanitize' ) ) ); } public static function assets( $hook ) { if ( $hook !== self::$hook ) { return; } wp_enqueue_style( 'ctps-admin', CTPS_URL . 'assets/admin.css', array(), CTPS_VERSION ); wp_enqueue_script( 'ctps-critical', CTPS_URL . 'assets/critical.js', array(), CTPS_VERSION, true ); wp_enqueue_script( 'ctps-used-css', CTPS_URL . 'assets/used-css.js', array( 'ctps-critical' ), CTPS_VERSION, true ); if ( isset( $_GET['setup'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification wp_enqueue_script( 'ctps-wizard', CTPS_URL . 'assets/wizard.js', array( 'ctps-critical' ), CTPS_VERSION, true ); $handle = 'ctps-wizard'; } else { wp_enqueue_script( 'ctps-admin', CTPS_URL . 'assets/admin.js', array( 'ctps-critical' ), CTPS_VERSION, true ); $handle = 'ctps-admin'; } $last = get_option( CTPS_PSI::LAST, array() ); $history = get_option( CTPS_PSI::HISTORY, array() ); list( $webp_done, $webp_total ) = CTPS_Webp::stats(); wp_localize_script( $handle, 'CTPS', array( 'ajax' => admin_url( 'admin-ajax.php' ), 'nonce' => wp_create_nonce( 'ctps_admin' ), 'home' => home_url( '/' ), 'last' => is_array( $last ) ? $last : array(), 'history' => is_array( $history ) ? array_values( $history ) : array(), 'webp' => array( 'done' => $webp_done, 'total' => $webp_total, 'supported' => CTPS_Webp::supported() ), 'wr' => array( 'remaining' => CTPS_Webp_Replace::remaining(), 'stats' => CTPS_Webp_Replace::public_stats(), 'supported' => CTPS_Webp_Replace::supported(), 'bg' => CTPS_Webp_Replace::bg_on() ), 'backupRecent' => CTPS_DB_Backup::recent(), 'backupPlugin' => CTPS_Safety::backup_plugin(), 'criticalUrls' => CTPS_Safety::check_urls(), 'rum' => array( 'mobile' => CTPS_RUM::summary( 0 ), 'desktop' => CTPS_RUM::summary( 1 ) ), 'usedCssUrls' => CTPS_Used_CSS::sample_urls(), 'safelist' => CTPS_Compat::css_safelist(), 'assetsNonce' => wp_create_nonce( 'ctps_assets' ), 'i18n' => CTPS_I18n::js(), 'locale' => str_replace( '_', '-', get_user_locale() ), ) ); } public static function action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Settings', 'celero-adaptive-speed' ) . '' ); return $links; } public static function admin_bar( $bar ) { if ( ! current_user_can( 'manage_options' ) ) { return; } $last = get_option( CTPS_PSI::LAST, array() ); $score = isset( $last['mobile']['scores']['performance']['score'] ) ? (int) $last['mobile']['scores']['performance']['score'] : null; $title = 'Adaptive Speed' . ( null !== $score ? ' ' . $score : '' ); $bar->add_node( array( 'id' => 'ctps', 'title' => $title, 'href' => self::page_url() ) ); $bar->add_node( array( 'parent' => 'ctps', 'id' => 'ctps-purge', 'title' => esc_html__( 'Clear cache', 'celero-adaptive-speed' ), 'href' => wp_nonce_url( admin_url( 'admin-post.php?action=ctps_purge' ), 'ctps_purge' ), ) ); } /* --------------------------------------------------------------------- * Actions * ------------------------------------------------------------------ */ private static function guard( $nonce_action ) { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Permission denied.', 'celero-adaptive-speed' ), 403 ); } check_admin_referer( $nonce_action ); } private static function back( $arg ) { $back = wp_get_referer() ? wp_get_referer() : self::page_url(); wp_safe_redirect( add_query_arg( 'ctps_msg', $arg, remove_query_arg( 'ctps_msg', $back ) ) ); exit; } public static function handle_purge() { self::guard( 'ctps_purge' ); CTPS_Page_Cache::purge_all(); self::back( 'purged' ); } public static function handle_profile() { self::guard( 'ctps_profile' ); $id = isset( $_GET['profile'] ) ? sanitize_key( $_GET['profile'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce verified in guard() self::back( CTPS_Settings::apply_profile( $id ) ? 'profile' : 'error' ); } public static function handle_restore() { self::guard( 'ctps_restore' ); self::back( CTPS_Settings::restore_backup() ? 'restored' : 'error' ); } private static function ajax_guard() { if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( __( 'Permission denied.', 'celero-adaptive-speed' ), 403 ); } check_ajax_referer( 'ctps_admin' ); } public static function ajax_psi() { self::ajax_guard(); $url = isset( $_POST['url'] ) ? esc_url_raw( wp_unslash( $_POST['url'] ) ) : home_url( '/' ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in ajax_guard() $strategy = isset( $_POST['strategy'] ) ? sanitize_key( $_POST['strategy'] ) : 'mobile'; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in ajax_guard() $result = CTPS_PSI::run( $url ? $url : home_url( '/' ), $strategy, true ); if ( is_wp_error( $result ) ) { wp_send_json_error( $result->get_error_message() ); } wp_send_json_success( array( 'result' => $result, 'history' => array_values( (array) get_option( CTPS_PSI::HISTORY, array() ) ), ) ); } public static function ajax_webp() { self::ajax_guard(); if ( CTPS_Webp::paused() ) { /* translators: %s: name of another image plugin */ wp_send_json_error( sprintf( __( '%s already creates the WebP images on this site: the conversion is paused so the same work is not done twice.', 'celero-adaptive-speed' ), CTPS_Webp::other_provider() ) ); } if ( ! CTPS_Webp::supported() ) { wp_send_json_error( __( 'The server does not support WebP conversion (GD or Imagick with WebP support is required).', 'celero-adaptive-speed' ) ); } $batch = CTPS_Webp::convert_batch(); list( $done, $total ) = CTPS_Webp::stats(); if ( $batch['processed'] > 0 ) { CTPS_Page_Cache::purge_all(); } wp_send_json_success( array_merge( $batch, array( 'done' => $done, 'total' => $total ) ) ); } private static function action_url( $action, $args = array() ) { return wp_nonce_url( add_query_arg( array_merge( array( 'action' => 'ctps_' . $action ), $args ), admin_url( 'admin-post.php' ) ), 'ctps_' . $action ); } public static function handle_cf_test() { self::guard( 'ctps_cf_test' ); $r = CTPS_CDN::cf_test(); set_transient( 'ctps_admin_msg', true === $r ? __( 'Cloudflare connected: the CDN cache will be cleared together with the plugin cache.', 'celero-adaptive-speed' ) /* translators: %s: Cloudflare error message */ : sprintf( __( 'Cloudflare: %s', 'celero-adaptive-speed' ), $r ), 60 ); self::back( true === $r ? 'custom_ok' : 'custom_error' ); } public static function handle_report_test() { self::guard( 'ctps_report_test' ); $to = CTPS_Report::recipients(); if ( ! $to ) { $to = array( wp_get_current_user()->user_email ); } self::back( CTPS_Report::send( $to ) ? 'report_sent' : 'mail_error' ); } public static function handle_snapshot_restore() { self::guard( 'ctps_snapshot_restore' ); $id = isset( $_GET['id'] ) ? sanitize_key( $_GET['id'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- nonce verified in guard() self::back( CTPS_Safety::restore( $id ) ? 'restored' : 'error' ); } public static function handle_emergency_on() { self::guard( 'ctps_emergency_on' ); CTPS_Safety::enter_emergency(); self::back( 'emergency_on' ); } public static function handle_emergency_off() { self::guard( 'ctps_emergency_off' ); CTPS_Safety::exit_emergency(); self::back( CTPS_Safety::is_disabled() ? 'still_disabled' : 'emergency_off' ); } public static function handle_key_regen() { self::guard( 'ctps_key_regen' ); CTPS_Safety::regenerate_key(); self::back( 'key' ); } public static function handle_key_mail() { self::guard( 'ctps_key_mail' ); self::back( CTPS_Safety::mail_recovery_link() ? 'mailed' : 'mail_error' ); } public static function handle_purge_all() { self::guard( 'ctps_purge_all' ); CTPS_Page_Cache::purge_all(); CTPS_Assets::remove_all(); self::back( 'purged_all' ); } public static function handle_oc_enable() { self::guard( 'ctps_oc_enable' ); $r = CTPS_Object_Cache::enable(); if ( is_wp_error( $r ) ) { set_transient( 'ctps_admin_msg', $r->get_error_message(), 60 ); self::back( 'custom_error' ); } CTPS_Log::add( 'cache', 'Redis object cache enabled' ); self::back( 'oc_enabled' ); } public static function handle_oc_disable() { self::guard( 'ctps_oc_disable' ); $r = CTPS_Object_Cache::disable(); if ( is_wp_error( $r ) ) { set_transient( 'ctps_admin_msg', $r->get_error_message(), 60 ); self::back( 'custom_error' ); } delete_option( 'ctps_oc_resume' ); CTPS_Log::add( 'cache', 'Redis object cache disabled' ); self::back( 'oc_disabled' ); } public static function handle_log_clear() { self::guard( 'ctps_log_clear' ); CTPS_Log::clear(); self::back( 'log_cleared' ); } public static function handle_log_download() { self::guard( 'ctps_log_download' ); $file = CTPS_Log::file(); nocache_headers(); header( 'Content-Type: text/plain; charset=utf-8' ); header( 'Content-Disposition: attachment; filename="ctps-debug-' . gmdate( 'Y-m-d' ) . '.log"' ); header( 'X-Content-Type-Options: nosniff' ); if ( file_exists( $file . '.1' ) ) { readfile( $file . '.1' ); // phpcs:ignore } if ( file_exists( $file ) ) { readfile( $file ); // phpcs:ignore } exit; } public static function handle_used_css_remove() { self::guard( 'ctps_used_css_remove' ); $s = CTPS_Settings::all(); CTPS_Used_CSS::remove(); if ( ! empty( $s['used_css'] ) ) { $s['used_css'] = 0; update_option( CTPS_Settings::OPTION, $s ); } else { CTPS_Page_Cache::purge_all(); } self::back( 'used_css_removed' ); } public static function handle_db_backup() { self::guard( 'ctps_db_backup' ); CTPS_DB_Backup::stream(); } /** * Settings as JSON (without the API key, which is a secret). */ public static function handle_export() { self::guard( 'ctps_export' ); $settings = CTPS_Settings::all(); // Secrets and update settings do not travel in exported files. foreach ( self::PRIVATE_KEYS as $k ) { unset( $settings[ $k ] ); } $data = array( 'plugin' => 'celero-adaptive-speed', 'version' => CTPS_VERSION, 'site' => home_url(), 'exported' => gmdate( 'c' ), 'settings' => $settings, ); $host = preg_replace( '/[^a-z0-9.-]/i', '', (string) wp_parse_url( home_url(), PHP_URL_HOST ) ); nocache_headers(); header( 'Content-Type: application/json; charset=utf-8' ); header( 'Content-Disposition: attachment; filename="pagespeed-' . $host . '-' . wp_date( 'Ymd-His' ) . '.json"' ); echo wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); // phpcs:ignore exit; } public static function ajax_import() { self::ajax_guard(); $raw = isset( $_POST['json'] ) && is_string( $_POST['json'] ) ? wp_unslash( $_POST['json'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing -- nonce checked in ajax_guard(); JSON document: decoded below, then every setting is sanitized by CTPS_Settings::sanitize() $data = json_decode( (string) $raw, true ); if ( ! is_array( $data ) || ! in_array( ( $data['plugin'] ?? '' ), array( 'celero-adaptive-speed', 'adaptive-speed', 'wp-adaptive' ), true ) || ! isset( $data['settings'] ) || ! is_array( $data['settings'] ) ) { wp_send_json_error( __( 'This file is not a Adaptive Speed export.', 'celero-adaptive-speed' ) ); } // Only known settings, each sanitized by its own rule (CTPS_Settings::sanitize()). $import = CTPS_Settings::sanitize( array_merge( CTPS_Settings::all(), array_intersect_key( $data['settings'], CTPS_Settings::defaults() ) ) ); $new = array_merge( CTPS_Settings::all(), $import ); // An imported file cannot change API keys or tokens. foreach ( self::PRIVATE_KEYS as $k ) { $new[ $k ] = CTPS_Settings::get( $k ); } $new = CTPS_Settings::sanitize( $new ); /* translators: %s: source site URL */ CTPS_Safety::label( sprintf( __( 'Before import from %s', 'celero-adaptive-speed' ), sanitize_text_field( (string) ( $data['site'] ?? 'file' ) ) ) ); update_option( CTPS_Settings::OPTION, $new ); $health = get_option( CTPS_Safety::HEALTH ); wp_send_json_success( array( 'rolled_back' => ! empty( $health['rolled_back'] ) && $health['time'] >= time() - 60 ) ); } public static function ajax_wr_step() { self::ajax_guard(); if ( empty( $_POST['backup'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in ajax_guard() wp_send_json_error( __( 'Confirm that the site has a recent backup.', 'celero-adaptive-speed' ) ); } if ( ! CTPS_Webp_Replace::supported() ) { wp_send_json_error( __( 'The server cannot create or read WebP images (GD with WebP support is required).', 'celero-adaptive-speed' ) ); } $r = CTPS_Webp_Replace::step( 0, CTPS_Webp_Replace::budget( 20 ) ); if ( is_wp_error( $r ) ) { wp_send_json_error( $r->get_error_message() ); } wp_send_json_success( $r ); } /** Starts or stops the background replacement (WP-Cron, every minute). */ public static function ajax_wr_bg() { self::ajax_guard(); $mode = isset( $_POST['mode'] ) ? sanitize_key( wp_unslash( $_POST['mode'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in ajax_guard() if ( 'start' === $mode ) { if ( empty( $_POST['backup'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in ajax_guard() wp_send_json_error( __( 'Confirm that the site has a recent backup.', 'celero-adaptive-speed' ) ); } if ( ! CTPS_Webp_Replace::supported() ) { wp_send_json_error( __( 'The server cannot create or read WebP images (GD with WebP support is required).', 'celero-adaptive-speed' ) ); } CTPS_Webp_Replace::start_bg(); } else { CTPS_Webp_Replace::stop_bg(); } wp_send_json_success( array( 'bg' => CTPS_Webp_Replace::bg_on(), 'remaining' => CTPS_Webp_Replace::remaining(), 'stats' => CTPS_Webp_Replace::public_stats() ) ); } public static function ajax_wr_retry() { self::ajax_guard(); CTPS_Webp_Replace::retry_skipped(); wp_send_json_success( array( 'remaining' => CTPS_Webp_Replace::remaining(), 'stats' => CTPS_Webp_Replace::public_stats() ) ); } public static function ajax_webp_remove() { self::ajax_guard(); $r = CTPS_Webp::remove_batch(); if ( 0 === $r['left'] ) { CTPS_Page_Cache::purge_all(); } list( $done, $total ) = CTPS_Webp::stats(); wp_send_json_success( array_merge( $r, array( 'done' => $done, 'total' => $total ) ) ); } public static function ajax_db() { self::ajax_guard(); $type = isset( $_POST['type'] ) ? sanitize_key( $_POST['type'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in ajax_guard() if ( ! isset( CTPS_Database::items()[ $type ] ) ) { wp_send_json_error( __( 'Invalid type.', 'celero-adaptive-speed' ) ); } $deleted = CTPS_Database::clean( $type ); wp_send_json_success( array( 'deleted' => $deleted, 'counts' => CTPS_Database::counts() ) ); } /* --------------------------------------------------------------------- * Components * ------------------------------------------------------------------ */ private static function name( $key ) { return CTPS_Settings::OPTION . '[' . $key . ']'; } /** * Card with a toggle (real checkbox, styled). */ private static function toggle( $key, $title, $desc, $impact = '', $metric = '' ) { static $n = 0; $id = 'ctps-' . $key . '-' . ( ++$n ); $impacts = array( 'alto' => __( 'High impact', 'celero-adaptive-speed' ), 'medio' => __( 'Medium impact', 'celero-adaptive-speed' ), 'basso' => __( 'Low impact', 'celero-adaptive-speed' ), ); ?>
$st['after'] ) { /* translators: %s: disk space, e.g. "1.2 GB" */ echo ' · ' . esc_html( sprintf( __( 'space freed: %s', 'celero-adaptive-speed' ), size_format( $st['before'] - $st['after'], 1 ) ) ); } ?>
location ~* ^(.+)\.(jpe?g|png)$ { try_files $uri $1.webp =404; }
define( \'CTPS_VARNISH_IP\', \'127.0.0.1\' );' ); ?>
get_error_message() ) . ' ';
/* translators: %s: PHP constants */
printf( esc_html__( 'Your hosting must provide Redis and the PHP "redis" extension. Connection settings go in wp-config.php: %s.', 'celero-adaptive-speed' ), 'CTPS_REDIS_HOST, CTPS_REDIS_PORT, CTPS_REDIS_PASSWORD, CTPS_REDIS_DB' );
?>
| LCP | TBT | CLS | |||||||
|---|---|---|---|---|---|---|---|---|---|
| = 90 ? 'is-good' : ( $d['score'] >= 50 ? 'is-avg' : 'is-bad' ) ); ?> | |||||||||
', '' ); ?>