hook_suffixes[] = $main_hook;
$pages = array(
'wpsco-dashboard' => array( __( 'Dashboard', 'speednest' ), array( $this, 'render_dashboard' ) ),
'wpsco-cache' => array( __( 'Cache Control', 'speednest' ), array( $this, 'render_cache' ) ),
'wpsco-css' => array( __( 'CSS Optimization', 'speednest' ), array( $this, 'render_css' ) ),
'wpsco-javascript' => array( __( 'JS Optimization', 'speednest' ), array( $this, 'render_javascript' ) ),
'wpsco-html' => array( __( 'HTML Optimization', 'speednest' ), array( $this, 'render_html' ) ),
'wpsco-images' => array( __( 'Image Optimization', 'speednest' ), array( $this, 'render_images' ) ),
'wpsco-fonts' => array( __( 'Font Optimization', 'speednest' ), array( $this, 'render_fonts' ) ),
'wpsco-database' => array( __( 'Database Cleaner', 'speednest' ), array( $this, 'render_database' ) ),
'wpsco-wordpress' => array( __( 'WP Tweaks', 'speednest' ), array( $this, 'render_wordpress' ) ),
'wpsco-woocommerce' => array( __( 'WooCommerce', 'speednest' ), array( $this, 'render_woocommerce' ) ),
'wpsco-cloudflare' => array( __( 'Cloudflare & CDN', 'speednest' ), array( $this, 'render_cloudflare' ) ),
'wpsco-diagnostics' => array( __( 'Diagnostics', 'speednest' ), array( $this, 'render_diagnostics' ) ),
'wpsco-tools' => array( __( 'Tools & Logs', 'speednest' ), array( $this, 'render_tools' ) ),
'wpsco-settings' => array( __( 'Settings', 'speednest' ), array( $this, 'render_settings' ) ),
);
foreach ( $pages as $slug => $data ) {
$hook = add_submenu_page(
'wpsco-dashboard',
$data[0] . ' ‹ ' . __( 'SpeedNest', 'speednest' ),
$data[0],
$cap,
$slug,
$data[1]
);
$this->hook_suffixes[] = $hook;
}
}
/**
* Enqueue admin assets only on plugin pages.
*
* @param string $hook Current admin page hook.
*/
public function enqueue_assets( $hook ) {
if ( strpos( $hook, 'wpsco' ) === false && ! in_array( $hook, $this->hook_suffixes, true ) ) {
return;
}
$ver = WPSCO_VERSION . '.' . time();
wp_enqueue_style(
'wpsco-admin-css',
WPSCO_PLUGIN_URL . 'assets/css/admin.css',
array( 'dashicons' ),
$ver
);
wp_enqueue_script(
'wpsco-admin-js',
WPSCO_PLUGIN_URL . 'assets/js/admin.js',
array( 'jquery' ),
$ver,
true
);
wp_localize_script( 'wpsco-admin-js', 'wpsco_admin', array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'wpsco_admin_nonce' ),
'strings' => array(
'purging' => __( 'Purging caches across all layers...', 'speednest' ),
'purge_success' => __( 'Cache purged successfully.', 'speednest' ),
'purge_partial' => __( 'Partial cache purge completed.', 'speednest' ),
'optimizing' => __( 'Applying safe optimization settings...', 'speednest' ),
'optimize_done' => __( 'Safe optimization configured and caches cleared!', 'speednest' ),
'confirm_nuclear' => __( 'Are you sure you want to run a Nuclear Purge? This clears all cached HTML, CSS, JS, images, fonts, transients, object cache, and resets OPcache.', 'speednest' ),
'confirm_clean' => __( 'Are you sure you want to clean selected database items? WooCommerce orders and users are always protected.', 'speednest' ),
'confirm_reset' => __( 'Are you sure you want to reset all plugin settings to factory defaults?', 'speednest' ),
'testing' => __( 'Testing connection...', 'speednest' ),
'running_diag' => __( 'Analyzing performance metrics...', 'speednest' ),
),
) );
}
/**
* Render global conflict & emergency notices.
*/
public function render_admin_notices() {
if ( ! wpsco_current_user_can() ) {
return;
}
$screen = get_current_screen();
if ( ! $screen || strpos( $screen->id, 'wpsco' ) === false ) {
return;
}
// Cache action success message.
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only notice message from redirect.
if ( isset( $_GET['wpsco_msg'] ) && ! empty( $_GET['wpsco_msg'] ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only notice message from redirect.
$wpsco_msg = sanitize_text_field( wp_unslash( $_GET['wpsco_msg'] ) );
?>
detect_all();
$dismissed = (array) wpsco_get_setting( 'dismissed_conflicts', array() );
if ( ! empty( $conflicts ) ) {
foreach ( $conflicts as $plugin_file => $conflict ) {
if ( in_array( $plugin_file, $dismissed, true ) ) {
continue;
}
?>
' . esc_html( $conflict['name'] ) . ''
);
?>
purge_all();
$msg = __( 'All caches cleared successfully.', 'speednest' );
} elseif ( 'purge_everything' === $action ) {
$purger->purge_everything();
$msg = __( 'Nuclear cache purge completed across all layers.', 'speednest' );
} elseif ( 'clear_html' === $action ) {
$purger->purge_html_cache();
$msg = __( 'HTML page cache cleared.', 'speednest' );
} elseif ( 'clear_css' === $action ) {
$purger->purge_css_cache();
$msg = __( 'CSS cache cleared.', 'speednest' );
} elseif ( 'clear_js' === $action ) {
$purger->purge_js_cache();
$msg = __( 'JS cache cleared.', 'speednest' );
} elseif ( 'clear_object' === $action ) {
$purger->purge_object_cache();
$msg = __( 'Object cache flushed.', 'speednest' );
} elseif ( 'reset_opcache' === $action ) {
$purger->purge_opcache();
$msg = __( 'PHP OPcache reset.', 'speednest' );
}
$page = isset( $_GET['page'] ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : 'wpsco-dashboard';
wp_safe_redirect( add_query_arg( array( 'page' => $page, 'wpsco_msg' => rawurlencode( $msg ) ), admin_url( 'admin.php' ) ) );
exit;
}
/**
* AJAX: Clear Cache.
*/
public function ajax_clear_cache() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
$layer = isset( $_POST['layer'] ) ? sanitize_text_field( wp_unslash( $_POST['layer'] ) ) : 'all';
$purger = new WPSCO_Cache_Purger();
if ( 'css' === $layer ) {
$result = array( 'css' => $purger->purge_css_cache() );
} elseif ( 'js' === $layer ) {
$result = array( 'js' => $purger->purge_js_cache() );
} elseif ( 'html' === $layer ) {
$result = array( 'html' => $purger->purge_html_cache() );
} elseif ( 'object' === $layer ) {
$result = array( 'object' => $purger->purge_object_cache() );
} elseif ( 'opcache' === $layer ) {
$result = array( 'opcache' => $purger->purge_opcache() );
} else {
$result = $purger->purge_all();
}
$new_size_bytes = wpsco_get_dir_size( wpsco_get_cache_dir() );
$new_size_text = wpsco_format_size( $new_size_bytes );
$new_version = substr( wpsco_get_cache_version(), 0, 12 ) . '...';
wp_send_json_success( array(
'results' => $result,
'new_size_text' => $new_size_text,
'new_version' => $new_version,
'message' => __( 'Cache purge operation executed.', 'speednest' ),
) );
}
/**
* AJAX: Purge Everything Possible (Nuclear Purge).
*/
public function ajax_purge_everything() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
$purger = new WPSCO_Cache_Purger();
$results = $purger->purge_everything();
$new_size_bytes = wpsco_get_dir_size( wpsco_get_cache_dir() );
$new_size_text = wpsco_format_size( $new_size_bytes );
$new_version = substr( wpsco_get_cache_version(), 0, 12 ) . '...';
wp_send_json_success( array(
'results' => $results,
'new_size_text' => $new_size_text,
'new_version' => $new_version,
'message' => __( 'Nuclear cache purge completed across all layers.', 'speednest' ),
) );
}
/**
* AJAX: One-Click Safe Optimization.
*/
public function ajax_safe_optimize() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
WPSCO_Settings::apply_one_click_safe_optimization();
$purger = new WPSCO_Cache_Purger();
$results = $purger->purge_all();
wp_send_json_success( array(
'message' => __( 'Safe optimization activated! Caches cleared.', 'speednest' ),
'results' => $results,
) );
}
/**
* AJAX: Deactivate Conflicting Plugin safely.
*/
public function ajax_deactivate_conflict() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
$plugin_file = isset( $_POST['plugin'] ) ? sanitize_text_field( wp_unslash( $_POST['plugin'] ) ) : '';
$detector = new WPSCO_Conflict_Detector();
$result = $detector->deactivate_conflicting_plugin( $plugin_file );
if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
}
wp_send_json_success( array(
'message' => __( 'Conflicting plugin deactivated safely. Data and settings were preserved.', 'speednest' ),
) );
}
/**
* AJAX: Dismiss conflict notice.
*/
public function ajax_dismiss_conflict() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
$plugin_file = isset( $_POST['plugin'] ) ? sanitize_text_field( wp_unslash( $_POST['plugin'] ) ) : '';
if ( ! empty( $plugin_file ) ) {
$dismissed = (array) wpsco_get_setting( 'dismissed_conflicts', array() );
$dismissed[] = $plugin_file;
wpsco_update_setting( 'dismissed_conflicts', array_unique( $dismissed ) );
}
wp_send_json_success();
}
/**
* AJAX: Run Local Diagnostics.
*/
public function ajax_run_diagnostics() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
$diag = WPSCO_Diagnostics::run_local_diagnostics();
wp_send_json_success( array( 'diagnostics' => $diag ) );
}
/**
* AJAX: Run dual Google PageSpeed & GTmetrix speed audit.
*/
public function ajax_run_speed_test() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
$strategy = isset( $_POST['strategy'] ) && 'desktop' === $_POST['strategy'] ? 'desktop' : 'mobile';
$audit = WPSCO_Diagnostics::run_speed_audit( $strategy, true );
if ( is_wp_error( $audit ) ) {
wp_send_json_error( array( 'message' => $audit->get_error_message() ) );
}
wp_send_json_success( array(
'audit' => $audit,
'psi' => $audit,
) );
}
/**
* AJAX: Run Google PageSpeed Insights scan (legacy alias).
*/
public function ajax_run_pagespeed() {
$this->ajax_run_speed_test();
}
/**
* AJAX: Save Google PageSpeed API key from dashboard.
*/
public function ajax_save_api_key() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
$api_key = isset( $_POST['api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['api_key'] ) ) : '';
$settings = wpsco_get_setting();
$settings['pagespeed_api_key'] = $api_key;
update_option( 'wpsco_settings', $settings );
delete_transient( 'wpsco_speed_audit_mobile_' . md5( home_url() ) );
delete_transient( 'wpsco_speed_audit_desktop_' . md5( home_url() ) );
delete_option( 'wpsco_speed_scores' );
wp_send_json_success( array( 'message' => __( 'API key saved successfully.', 'speednest' ) ) );
}
/**
* AJAX: Clean Database items.
*/
public function ajax_clean_database() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
$type = isset( $_POST['type'] ) ? sanitize_text_field( wp_unslash( $_POST['type'] ) ) : '';
$cleaner = new WPSCO_Database_Cleaner();
$count = 0;
if ( 'revisions' === $type ) {
$count = $cleaner->clean_post_revisions();
} elseif ( 'drafts' === $type ) {
$count = $cleaner->clean_auto_drafts();
} elseif ( 'trash' === $type ) {
$count = $cleaner->clean_trash_posts();
} elseif ( 'spam' === $type ) {
$count = $cleaner->clean_spam_comments();
} elseif ( 'transients' === $type ) {
$count = $cleaner->clean_expired_transients();
}
/* translators: %d: Number of cleaned items */
$cleaned_message = sprintf( __( 'Cleaned %d item(s).', 'speednest' ), $count );
wp_send_json_success( array(
'count' => $count,
'message' => $cleaned_message,
) );
}
/**
* AJAX: Test Cloudflare credentials.
*/
public function ajax_test_cloudflare() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
$cf = new WPSCO_Cloudflare();
$result = $cf->test_connection();
if ( 'connected' === $result['status'] ) {
wp_send_json_success( $result );
} else {
wp_send_json_error( $result );
}
}
/**
* AJAX: Emergency disable toggle.
*/
public function ajax_emergency_toggle() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
$current = (bool) wpsco_get_setting( 'emergency_disable', false );
$new_val = ! $current;
wpsco_update_setting( 'emergency_disable', $new_val );
// Purge caches immediately.
$purger = new WPSCO_Cache_Purger();
$purger->purge_all();
wp_send_json_success( array(
'disabled' => $new_val,
'message' => $new_val ? __( 'Emergency Safe Mode activated. All frontend optimizations disabled.', 'speednest' ) : __( 'Optimizations re-enabled.', 'speednest' ),
) );
}
/**
* AJAX: Reset settings to default.
*/
public function ajax_reset_settings() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
WPSCO_Settings::reset_to_defaults();
wp_send_json_success( array(
'message' => __( 'All plugin settings reset to defaults and caches purged.', 'speednest' ),
) );
}
/**
* AJAX: Lossless / High Quality Bulk Image Compression.
* Compresses images in batches, reducing KB while strictly preserving original dimensions and quality.
*/
public function ajax_compress_images() {
check_ajax_referer( 'wpsco_admin_nonce' );
WPSCO_Security::enforce_capability();
$offset = isset( $_POST['offset'] ) ? max( 0, intval( $_POST['offset'] ) ) : 0;
$batch_size = 5;
$quality = (int) wpsco_get_setting( 'image_quality', 82 );
// Query image attachments.
$query_args = array(
'post_type' => 'attachment',
'post_mime_type' => array( 'image/jpeg', 'image/png', 'image/webp' ),
'post_status' => 'inherit',
'posts_per_page' => $batch_size,
'offset' => $offset,
'fields' => 'ids',
'orderby' => 'ID',
'order' => 'ASC',
);
$attachments = get_posts( $query_args );
$total_count = wp_count_attachments();
$total_images = 0;
foreach ( array( 'image/jpeg', 'image/png', 'image/webp' ) as $m ) {
if ( isset( $total_count->$m ) ) {
$total_images += $total_count->$m;
}
}
$processed = 0;
$compressed = 0;
$bytes_saved = 0;
require_once WPSCO_PLUGIN_DIR . 'includes/class-image-optimizer.php';
foreach ( $attachments as $att_id ) {
$file = get_attached_file( $att_id );
$meta = wp_get_attachment_metadata( $att_id );
$processed++;
if ( $file && file_exists( $file ) ) {
$res = WPSCO_Image_Optimizer::compress_image_file( $file, $quality );
if ( ! empty( $res['success'] ) ) {
$compressed++;
$bytes_saved += $res['saved'];
}
// Compress sub-sizes.
if ( ! empty( $meta['sizes'] ) && is_array( $meta['sizes'] ) ) {
$dir = dirname( $file );
foreach ( $meta['sizes'] as $s_info ) {
if ( ! empty( $s_info['file'] ) ) {
$sub_file = $dir . '/' . $s_info['file'];
if ( file_exists( $sub_file ) ) {
$sub_res = WPSCO_Image_Optimizer::compress_image_file( $sub_file, $quality );
if ( ! empty( $sub_res['success'] ) ) {
$bytes_saved += $sub_res['saved'];
}
}
}
}
}
}
}
$new_offset = $offset + count( $attachments );
$is_done = empty( $attachments ) || $new_offset >= $total_images;
$percent = $total_images > 0 ? min( 100, round( ( $new_offset / $total_images ) * 100 ) ) : 100;
wp_send_json_success( array(
'done' => $is_done,
'offset' => $new_offset,
'total' => $total_images,
'percent' => $percent,
'processed' => $processed,
'compressed' => $compressed,
'bytes_saved' => $bytes_saved,
'message' => sprintf(
/* translators: 1: Processed count, 2: Total count, 3: Formatted size saved */
__( 'Processed %1$d of %2$d images. Saved %3$s.', 'speednest' ),
$new_offset,
$total_images,
size_format( $bytes_saved, 2 )
),
) );
}
/**
* Render view helpers.
*/
public function render_dashboard() { $this->load_view( 'dashboard' ); }
public function render_cache() { $this->load_view( 'cache' ); }
public function render_css() { $this->load_view( 'css' ); }
public function render_javascript() { $this->load_view( 'javascript' ); }
public function render_html() { $this->load_view( 'html' ); }
public function render_images() { $this->load_view( 'images' ); }
public function render_fonts() { $this->load_view( 'fonts' ); }
public function render_database() { $this->load_view( 'database' ); }
public function render_wordpress() { $this->load_view( 'wordpress' ); }
public function render_woocommerce() { $this->load_view( 'woocommerce' ); }
public function render_cloudflare() { $this->load_view( 'cloudflare' ); }
public function render_diagnostics() { $this->load_view( 'diagnostics' ); }
public function render_tools() { $this->load_view( 'tools' ); }
public function render_settings() { $this->load_view( 'settings' ); }
/**
* Helper to load view files.
*
* @param string $view_name View file name without extension.
*/
private function load_view( $view_name ) {
$view_file = WPSCO_PLUGIN_DIR . 'admin/' . $view_name . '.php';
if ( file_exists( $view_file ) ) {
require $view_file;
} else {
/* translators: %s: View file name */
echo '' . esc_html( sprintf( __( 'View file %s.php not found.', 'speednest' ), $view_name ) ) . '
';
}
}
/**
* Filter admin footer text on plugin screens.
*
* @param string $text Original footer text.
* @return string
*/
public function filter_admin_footer( $text ) {
$screen = get_current_screen();
if ( $screen && strpos( $screen->id, 'wpsco' ) !== false ) {
return sprintf(
/* translators: 1: Heart icon, 2: Code Buddy link, 3: Website link */
__( 'Made with %1$s from %2$s (%3$s)', 'speednest' ),
'♥',
'Code Buddy\'s',
'codebuddys.in'
);
}
return $text;
}
}