true ) ); } /** On our own page, finish a pending ownership check so the admin sees the result without waiting for cron. */ public static function maybe_claim() { if ( ! current_user_can( 'manage_options' ) ) { return; } $p = ZeroConfig_Options::get( 'pending_reg' ); if ( ! empty( $p['challenge'] ) && ZeroConfig_Options::get( 'engine' ) && time() - (int) $p['at'] >= 2 ) { ZeroConfig_API::claim_pending(); } } public static function action_links( $links ) { array_unshift( $links, '' . esc_html__( 'Open', 'zeroconfig' ) . '' ); return $links; } public static function url( $args = array() ) { return add_query_arg( array_merge( array( 'page' => self::SLUG ), $args ), admin_url( 'options-general.php' ) ); } /** A link that runs one action; its nonce is valid for that action only. */ public static function action_url( $action ) { return wp_nonce_url( self::url( array( 'zc_action' => $action ) ), 'zeroconfig_' . $action ); } /** The hidden fields of a form that runs one action. */ private static function action_fields( $action ) { wp_nonce_field( 'zeroconfig_' . $action ); echo ''; } public static function activation_redirect() { $who = (int) get_transient( 'zeroconfig_activation_redirect' ); if ( ! $who || get_current_user_id() !== $who || wp_doing_ajax() ) { return; } delete_transient( 'zeroconfig_activation_redirect' ); if ( current_user_can( 'manage_options' ) && ! is_network_admin() && ! isset( $_GET['activate-multi'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- core's own flag, only read wp_safe_redirect( self::url() ); exit; } } public static function admin_bar( $bar ) { if ( ! current_user_can( 'manage_options' ) || ! ZeroConfig_Options::get( 'engine' ) ) { return; } $bar->add_node( array( 'id' => 'zeroconfig', 'title' => '⚡ ' . esc_html( ZeroConfig_Options::brand() ), 'href' => self::url() ) ); $bar->add_node( array( 'parent' => 'zeroconfig', 'id' => 'zeroconfig-purge', 'title' => esc_html__( 'Clear cache', 'zeroconfig' ), 'href' => self::action_url( 'purge' ) ) ); } /* ---------- actions ---------- */ public static function handle() { if ( empty( $_REQUEST['zc_action'] ) || ! current_user_can( 'manage_options' ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- the nonce for this very action is checked next return; } $action = sanitize_key( wp_unslash( $_REQUEST['zc_action'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended check_admin_referer( 'zeroconfig_' . $action ); $msg = ''; switch ( $action ) { case 'on': ZeroConfig_Options::set( 'share_email', ! empty( $_POST['zc_share_email'] ) ); $r = ZeroConfig_Engine::turn_on(); $msg = 'on'; if ( is_wp_error( $r ) ) { // Say which part fell back, not one generic reason for all of them. $by = array( 'zc_multisite' => 'on_multisite', 'zc_dropin_foreign' => 'on_foreign', 'zc_dropin_write' => 'on_dropin_write', 'zc_htaccess' => 'on_htaccess' ); $msg = isset( $by[ $r->get_error_code() ] ) ? $by[ $r->get_error_code() ] : 'on_partial'; } break; case 'off': ZeroConfig_Engine::turn_off(); $msg = 'off'; break; case 'doctor': ZeroConfig_Doctor::run(); // time-boxed to about 10 s $msg = 'doctor'; break; case 'purge': ZeroConfig_Page_Cache::purge_all( 'manual' ); ZeroConfig_CDN_Purge::run_queue(); // Cloudflare, Varnish, edge now, so "cleared" is true when it shows $msg = 'purged'; break; case 'check': ZeroConfig_Safe_Mode::report_issue( isset( $_POST['zc_note'] ) ? sanitize_textarea_field( wp_unslash( $_POST['zc_note'] ) ) : '', isset( $_POST['zc_page'] ) ? sanitize_text_field( wp_unslash( $_POST['zc_page'] ) ) : '' ); $msg = 'check'; break; case 'recheck': ZeroConfig_Safe_Mode::schedule( 5 ); spawn_cron(); // start it now rather than on the next visitor $msg = 'recheck'; break; case 'reset': ZeroConfig_Safe_Mode::reset(); $msg = 'reset'; break; case 'reconnect': ZeroConfig_Options::update( array( 'pending_reg' => array(), 'last_error' => '' ) ); $r = ZeroConfig_API::register_site(); $msg = is_wp_error( $r ) ? 'reconnect_error' : 'reconnect'; break; case 'sync': ZeroConfig_API::heartbeat(); ZeroConfig_API::fetch_rules(); ZeroConfig_Cron::critical(); $msg = 'synced'; break; case 'cloudflare': $token = isset( $_POST['zc_cf_token'] ) ? sanitize_text_field( wp_unslash( $_POST['zc_cf_token'] ) ) : ''; if ( ! empty( $_POST['zc_cf_remove'] ) ) { ZeroConfig_CDN_Purge::cloudflare_connect( '' ); $msg = 'cf_removed'; break; } if ( '' === $token ) { $msg = ''; break; // nothing typed: the saved token stays } $r = ZeroConfig_CDN_Purge::cloudflare_connect( $token ); $msg = is_wp_error( $r ) ? 'cf_error' : 'cf_ok'; if ( is_wp_error( $r ) ) { ZeroConfig_Options::set( 'last_error', $r->get_error_message() ); } break; case 'badge': ZeroConfig_Options::set( 'badge', ! empty( $_POST['zc_badge'] ) ); if ( ZeroConfig_Options::get( 'engine' ) ) { ZeroConfig_API::heartbeat(); // limits change immediately } ZeroConfig_Page_Cache::purge_all( 'settings' ); $msg = ZeroConfig_Options::get( 'badge' ) ? 'badge_on' : 'badge_off'; break; case 'purge_token': ZeroConfig_Options::set( 'purge_token', wp_generate_password( 24, false ) ); $msg = 'token'; break; case 'license': $key = isset( $_POST['zc_license'] ) ? sanitize_text_field( wp_unslash( $_POST['zc_license'] ) ) : ''; if ( '' === $key ) { ZeroConfig_API::deactivate_license(); $msg = 'license_removed'; } else { $r = ZeroConfig_API::activate_license( $key ); $msg = is_wp_error( $r ) ? 'license_error' : ( isset( $r['status'] ) && 'saved' === $r['status'] ? 'license_saved' : 'license_ok' ); if ( 'license_ok' === $msg ) { ZeroConfig_Page_Cache::purge_all( 'settings' ); ZeroConfig_Cron::request_critical(); } } break; } wp_safe_redirect( self::url( array( 'zc_msg' => $msg ) ) ); exit; } public static function notices() { if ( ! current_user_can( 'manage_options' ) ) { return; } // Only where plugins are managed (Dashboard, Plugins), never across the whole admin. $screen = get_current_screen(); if ( ! $screen || ! in_array( $screen->id, array( 'dashboard', 'plugins' ), true ) ) { return; } $conf = ZeroConfig_Compat::conflicts(); if ( $conf && ZeroConfig_Options::get( 'engine' ) ) { printf( '

%s: %s %s. %s

', esc_html( ZeroConfig_Options::brand() ), esc_html__( 'this plugin also optimises pages or images:', 'zeroconfig' ), esc_html( implode( ', ', $conf ) ), esc_html__( 'Two plugins doing the same job can overlap. If pages look stale or images are processed twice, keep only one of them for that job.', 'zeroconfig' ) ); } } /* ---------- page ---------- */ /** "Is it working?": five rows from the last doctor run, each with its reason, owner and fix. */ private static function doctor_card() { ZeroConfig_Doctor::server_software(); // remember the web server type for checks run by cron $h = ZeroConfig_Probe::health(); $doc = isset( $h['doctor'] ) ? $h['doctor'] : array(); $icons = array( 'ok' => '✓', 'na' => '–', 'warn' => '!', 'bad' => '✕' ); $sum = ZeroConfig_Doctor::summary(); ?>

__( 'Engine is on. Within a few minutes a Safety Check loads your pages in a real browser and switches off anything that does not suit this site.', 'zeroconfig' ), 'on_partial' => __( 'Engine is on. wp-config.php is read-only, so the page cache is served by WordPress instead of the drop-in (still fast).', 'zeroconfig' ), 'on_foreign' => __( 'Engine is on. Another caching plugin owns advanced-cache.php, so ZeroConfig serves its cache through WordPress instead (still fast). Deactivate the other caching plugin for the fastest path.', 'zeroconfig' ), 'on_dropin_write' => __( 'Engine is on. wp-content is not writable, so the page cache is served by WordPress instead of the drop-in (still fast).', 'zeroconfig' ), 'on_htaccess' => __( 'Engine is on. .htaccess could not be updated, so cached pages are served by PHP instead of straight from the web server (still fast).', 'zeroconfig' ), 'off' => __( 'Engine is off. Your site is back to exactly how it was.', 'zeroconfig' ), 'purged' => __( 'Cache cleared on this server and on the edge.', 'zeroconfig' ), 'doctor' => __( 'Checked again. The results are below.', 'zeroconfig' ), 'check' => __( 'Thanks. A strict Safety Check is running now and our team received the diagnostics.', 'zeroconfig' ), 'reset' => __( 'All features re-enabled. A new Safety Check will run shortly.', 'zeroconfig' ), 'recheck' => __( 'A new Safety Check is on its way. This card shows the result when it is done.', 'zeroconfig' ), 'synced' => __( 'Synced with the edge.', 'zeroconfig' ), 'reconnect' => __( 'Verifying this site again.', 'zeroconfig' ), 'reconnect_error' => __( 'Could not reach the service.', 'zeroconfig' ) . ' ' . esc_html( (string) $o['last_error'] ), 'license_ok' => __( 'License activated. Enjoy the Pro features.', 'zeroconfig' ), 'license_saved' => __( 'License key saved on this site. It is sent to the service and activated when you press Turn on.', 'zeroconfig' ), 'on_multisite' => __( 'ZeroConfig does not support WordPress multisite networks yet, so the engine stays off.', 'zeroconfig' ), 'cf_removed' => __( 'Cloudflare token removed.', 'zeroconfig' ), 'license_error' => __( 'That license key was not accepted.', 'zeroconfig' ) . ' ' . esc_html( (string) $o['last_error'] ), 'license_removed' => __( 'License removed from this site.', 'zeroconfig' ), 'cf_ok' => __( 'Cloudflare connected. Its cache is now purged together with ours.', 'zeroconfig' ), 'cf_error' => __( 'Cloudflare did not accept that token.', 'zeroconfig' ) . ' ' . esc_html( (string) $o['last_error'] ), 'token' => __( 'New purge URL generated. The old one stopped working.', 'zeroconfig' ), 'badge_on' => __( 'Badge is on. Your free image limit is now doubled: 1,000 new images a month. Thank you!', 'zeroconfig' ), 'badge_off' => __( 'Badge removed. The limit is back to 500 new images a month.', 'zeroconfig' ), ); $features = array( 'cache' => __( 'Page cache', 'zeroconfig' ), 'images' => __( 'Image CDN (WebP/AVIF)', 'zeroconfig' ), 'assets' => __( 'CSS & JS minify on the edge', 'zeroconfig' ), 'js' => __( 'Safe JavaScript deferral', 'zeroconfig' ), 'delay' => __( 'Third-party script delay', 'zeroconfig' ), 'critical' => __( 'Automatic critical CSS', 'zeroconfig' ), ); $size = ZeroConfig_Page_Cache::size(); $score = is_array( $o['last_score'] ) ? $o['last_score'] : array(); ?>

>

0 ? min( 100, round( $u / $l * 100 ) ) : 0; ?>
/ 0 ? esc_html( number_format_i18n( $l ) ) : '∞'; ?> 0 ) : ?>
/
'queued', 'position' => 0 ) : $st ); ?>
%

1 ? esc_html( sprintf( /* translators: %d: place in the queue */ __( 'waiting in line (%d ahead), results usually within minutes.', 'zeroconfig' ), (int) $st['position'] - 1 ) ) : esc_html__( 'starting…', 'zeroconfig' ); } elseif ( 'blocked' === $st['status'] ) { esc_html_e( 'could not see your pages, so nothing was changed.', 'zeroconfig' ); if ( ! empty( $st['error'] ) ) { echo ' ' . esc_html( $st['error'] ) . '.'; } echo ' ' . esc_html__( 'If you use a firewall or bot protection (Cloudflare, Wordfence…), allow the user agent “ZeroConfigCheck” and press Re-check.', 'zeroconfig' ); } elseif ( 'timeout' === $st['status'] ) { esc_html_e( 'no result after two hours, nothing was changed. Press Re-check to try again.', 'zeroconfig' ); } elseif ( 'done' === $st['status'] ) { echo 'pass' === ( $st['verdict'] ?? 'pass' ) ? esc_html__( 'passed, all features suit this site.', 'zeroconfig' ) : esc_html__( 'adjusted: a feature that did not suit this site was switched off.', 'zeroconfig' ); } elseif ( 'error' === $st['status'] ) { esc_html_e( 'could not run, nothing was changed.', 'zeroconfig' ); if ( ! empty( $st['error'] ) ) { echo ' (' . esc_html( mb_substr( $st['error'], 0, 60 ) ) . ')'; } } else { echo esc_html( $st['status'] ); } echo ' (' . esc_html( human_time_diff( (int) ( $st['at'] ?? time() ) ) ) . ' ' . esc_html__( 'ago', 'zeroconfig' ) . ')'; ?>

—


· ·

0, 'class' => 'queued', 'label' => $ahead ? sprintf( /* translators: %d: checks ahead in the queue */ _n( 'Waiting in line: %d check ahead of yours.', 'Waiting in line: %d checks ahead of yours.', $ahead, 'zeroconfig' ), $ahead ) : __( 'Starting: opening a real browser on our servers…', 'zeroconfig' ), ); } $p = isset( $st['progress'] ) && is_array( $st['progress'] ) ? $st['progress'] : null; if ( ! $p ) { return array( 'percent' => 1, 'class' => '', 'label' => __( 'Starting: opening a real browser on our servers…', 'zeroconfig' ) ); } $names = array( '' => __( 'your page without ZeroConfig (the reference)', 'zeroconfig' ), 'images' => __( 'optimised images', 'zeroconfig' ), 'assets' => __( 'minified CSS and JavaScript', 'zeroconfig' ), 'js' => __( 'deferred JavaScript', 'zeroconfig' ), 'delay' => __( 'delayed third-party scripts', 'zeroconfig' ), 'critical' => __( 'critical CSS', 'zeroconfig' ), 'all' => __( 'every feature switched on', 'zeroconfig' ), ); $p = array_merge( array( 'url' => '', 'viewport' => '', 'feature' => '', 'done' => 0, 'total' => 1 ), $p ); $path = (string) wp_parse_url( (string) $p['url'], PHP_URL_PATH ); $page = ( '' === $path || '/' === $path ) ? __( 'home page', 'zeroconfig' ) : $path; $device = 'desktop' === $p['viewport'] ? __( 'desktop', 'zeroconfig' ) : __( 'phone', 'zeroconfig' ); $feature = $names[ $p['feature'] ] ?? $p['feature']; $percent = (int) floor( 100 * $p['done'] / max( 1, $p['total'] ) ); return array( 'percent' => max( 1, min( 99, $percent ) ), // 100 only once the result is in 'class' => '', /* translators: 1: page, 2: phone or desktop, 3: what is being compared, 4: steps done, 5: steps in total */ 'label' => sprintf( __( 'Checking %1$s on %2$s with %3$s (step %4$d of %5$d).', 'zeroconfig' ), $page, $device, $feature, min( (int) $p['total'], (int) $p['done'] + 1 ), (int) $p['total'] ), ); } /** Polled by the progress box every few seconds while a check is queued or running. */ public static function ajax_selftest_status() { check_ajax_referer( 'zeroconfig_progress' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( null, 403 ); } $job = ZeroConfig_Options::get( 'selftest_job' ); if ( ! $job ) { if ( ! empty( $_POST['wait'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- checked above // Right after Turn on the first check is still scheduled: nudge WP-Cron and keep waiting. spawn_cron(); wp_send_json_success( array( 'waiting' => true ) ); } wp_send_json_success( array( 'reload' => true ) ); } if ( ! empty( $_POST['wait'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing wp_send_json_success( array( 'started' => true ) ); } $res = ZeroConfig_API::get_selftest( $job ); if ( is_wp_error( $res ) ) { wp_send_json_success( array( 'label' => __( 'Waiting for the edge to answer…', 'zeroconfig' ) ) ); } $st = ZeroConfig_Safe_Mode::track( $res ); if ( ! $st ) { ZeroConfig_Safe_Mode::poll( 1 ); // done or failed: apply the result now, then show it wp_send_json_success( array( 'reload' => true ) ); } wp_send_json_success( self::progress_view( $st ) ); } }