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();
?>
'na', 'items' => array() );
?>
__( '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();
?>
$y ) : ?>
>
$label ) :
$allowed = ZeroConfig_Options::feature_allowed( $k );
$live = ZeroConfig_Options::feature_on( $k );
$cls = $live ? 'on' : ( $allowed ? 'off' : 'locked' );
$why = ! $allowed ? __( 'Pro', 'zeroconfig' ) : ( ! empty( $o['disabled'][ $k ] ) ? $o['disabled'][ $k ] : '' );
// Edge features only work once the site is connected; don't show them green before that.
if ( 'cache' !== $k && $live && ! ZeroConfig_Options::is_connected() ) { $cls = 'off'; $why = __( 'waiting for the connection', 'zeroconfig' ); }
if ( 'images' === $k && $live && ! empty( $o['cdn_paused'] ) ) { $cls = 'off'; $why = __( 'paused: unusually high CDN use, we have e-mailed you', 'zeroconfig' ); }
if ( 'images' === $k && $live && isset( $quota['images_limit'] ) && $quota['images_limit'] > 0 && $quota['images_used'] >= $quota['images_limit'] ) { $cls = 'off'; $why = __( 'monthly image limit reached: new images come from your server', 'zeroconfig' ); }
?>