is_current_page_cached(); if ( $is_cached ) { $status_badge = ' HIT'; } else { $status_badge = ' BYPASS'; } } $wp_admin_bar->add_node( array( 'id' => $root_id, 'title' => esc_html__( 'FastLayer', 'fastlayer' ) . $status_badge, 'href' => admin_url( 'admin.php?page=wp-fastlayer' ), 'meta' => array( 'class' => 'wp-fastlayer-admin-bar-root', ), ) ); $wp_admin_bar->add_node( array( 'id' => 'wp-fastlayer-clear-all-cache', 'title' => esc_html__( 'Clear All Cache', 'fastlayer' ), 'href' => $this->build_action_url( 'clear_all_cache', 'cache_cleared' ), 'parent' => $root_id, ) ); $wp_admin_bar->add_node( array( 'id' => 'wp-fastlayer-clear-css-js-cache', 'title' => esc_html__( 'Clear CSS/JS Cache', 'fastlayer' ), 'href' => $this->build_action_url( 'clear_css_js_cache', 'css_js_cleared' ), 'parent' => $root_id, ) ); $wp_admin_bar->add_node( array( 'id' => 'wp-fastlayer-preload-cache', 'title' => esc_html__( 'Preload Cache', 'fastlayer' ), 'href' => $this->build_action_url( 'preload_cache', 'preload_started' ), 'parent' => $root_id, ) ); } public function enqueue_toolbar_assets() { if ( ! is_user_logged_in() || ! is_admin_bar_showing() || ! current_user_can( 'manage_options' ) ) { return; } wp_enqueue_style( 'dashicons' ); wp_enqueue_style( 'fastlayer-admin', FASTLAYER_URL . 'assets/css/admin.css', array(), FASTLAYER_VERSION ); wp_enqueue_script( 'fastlayer-admin', FASTLAYER_URL . 'assets/js/admin.js', array( 'jquery' ), FASTLAYER_VERSION, true ); } public function handle_action_request() { if ( ! isset( $_GET[ $this->action_key ] ) ) { return; } $action = sanitize_key( wp_unslash( $_GET[ $this->action_key ] ) ); if ( ! in_array( $action, $this->allowed_actions, true ) ) { return; } $nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : ''; if ( ! wp_verify_nonce( $nonce, $this->get_nonce_action( $action ) ) ) { wp_die( esc_html__( 'Security check failed.', 'fastlayer' ), esc_html__( 'Error', 'fastlayer' ), array( 'response' => 403 ) ); } if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have permission to perform this action.', 'fastlayer' ), esc_html__( 'Error', 'fastlayer' ), array( 'response' => 403 ) ); } $redirect_to = isset( $_GET['redirect_to'] ) ? esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) : ''; $redirect_to = $this->sanitize_redirect_url( $redirect_to, admin_url( 'admin.php?page=wp-fastlayer' ) ); switch ( $action ) { case 'clear_all_cache': $this->process_clear_all_cache( $redirect_to ); break; case 'clear_css_js_cache': $this->process_clear_css_js_cache( $redirect_to ); break; case 'preload_cache': $this->process_preload_cache( $redirect_to ); break; } exit; } public function display_success_notice() { if ( ! current_user_can( 'manage_options' ) ) { return; } if ( ! isset( $_GET['fastlayer_admin_bar_message'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only notice/deep-link flag produced by the plugin's own wp_safe_redirect(); no state changes. return; } $message = sanitize_text_field( wp_unslash( $_GET['fastlayer_admin_bar_message'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only notice/deep-link flag produced by the plugin's own wp_safe_redirect(); no state changes. $notice = ''; $type = 'notice-success'; switch ( $message ) { case 'cache_cleared': $notice = __( 'Cache cleared successfully via FastLayer admin bar.', 'fastlayer' ); break; case 'css_js_cleared': $notice = __( 'CSS/JS cache cleared successfully via FastLayer admin bar.', 'fastlayer' ); break; case 'preload_started': // The job is still warming pages, so this is not a success // notice: it would report completion that has not happened. $type = 'notice-info'; $notice = __( 'Cache preload started in the background. You can keep working while pages are warmed.', 'fastlayer' ); break; case 'preload_busy': $type = 'notice-warning'; $notice = __( 'A cache preload job is already running, so a second one was not started.', 'fastlayer' ); break; case 'preload_disabled': $type = 'notice-warning'; $notice = __( 'Cache preloading is disabled, so nothing was queued.', 'fastlayer' ); break; case 'preload_error': $type = 'notice-error'; $notice = __( 'The cache preload job could not be started. See the Cache page for details.', 'fastlayer' ); break; } if ( $notice ) { // The message is plain text from the switch above, so a single // esc_html() at the point of output is the correct escaping. The // value is intentionally not escaped earlier to avoid escaping it // twice. printf( '

%2$s

', esc_attr( $type ), esc_html( $notice ) ); } } public function add_settings_admin_bar_node( $wp_admin_bar ) { if ( ! is_user_logged_in() || ! is_admin_bar_showing() || ! current_user_can( 'manage_options' ) ) { return; } if ( ! is_object( $wp_admin_bar ) ) { return; } $root_id = 'wp-fastlayer-admin-bar'; if ( ! $wp_admin_bar->get_node( $root_id ) ) { return; } $wp_admin_bar->add_node( array( 'id' => 'wp-fastlayer-settings', 'title' => esc_html__( 'Settings', 'fastlayer' ), 'href' => admin_url( 'admin.php?page=wp-fastlayer' ), 'parent' => $root_id, ) ); } private function build_action_url( $action, $message ) { $redirect_to = add_query_arg( array( 'page' => 'wp-fastlayer', 'fastlayer_admin_bar_message' => $message, ), admin_url( 'admin.php' ) ); $redirect_to = $this->sanitize_redirect_url( $redirect_to, admin_url( 'admin.php?page=wp-fastlayer' ) ); $url = add_query_arg( array( $this->action_key => $action, 'redirect_to' => $redirect_to, ), admin_url( 'admin-post.php' ) ); return wp_nonce_url( $url, $this->get_nonce_action( $action ) ); } private function sanitize_redirect_url( $redirect_to, $fallback ) { $redirect_to = trim( $redirect_to ); $redirect_to = esc_url_raw( $redirect_to ); $redirect_to = wp_validate_redirect( $redirect_to, $fallback ); if ( empty( $redirect_to ) ) { return $fallback; } return $redirect_to; } private function get_nonce_action( $action ) { return $this->nonce_action_prefix . '_' . $action; } private function process_clear_all_cache( $redirect_to ) { $this->clear_page_cache(); $this->clear_assets_cache(); $this->clear_preload_cache_artifacts(); if ( function_exists( 'delete_transient' ) ) { \delete_transient( 'fastlayer_cache_debug_trace' ); } wp_safe_redirect( $redirect_to ); exit; } private function is_current_page_cached() { if ( ! defined( 'FASTLAYER_CACHE_DIR' ) ) { return false; } $protocol = is_ssl() ? 'https' : 'http'; $host = isset( $_SERVER['HTTP_HOST'] ) && is_string( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : ''; $request_uri = isset( $_SERVER['REQUEST_URI'] ) && is_string( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; if ( empty( $host ) || empty( $request_uri ) ) { return false; } $url = $protocol . '://' . $host . $request_uri; $accept = isset( $_SERVER['HTTP_ACCEPT'] ) && is_string( $_SERVER['HTTP_ACCEPT'] ) ? strtolower( $_SERVER['HTTP_ACCEPT'] ) : ''; $variant = 'accept-legacy'; if ( false !== strpos( $accept, 'image/avif' ) ) { $variant = 'accept-avif'; } elseif ( false !== strpos( $accept, 'image/webp' ) ) { $variant = 'accept-webp'; } $cache_key = md5( $url . '|' . $variant ); $cache_file = trailingslashit( wp_normalize_path( FASTLAYER_CACHE_DIR ) ) . $cache_key . '.html'; return file_exists( $cache_file ); } private function process_clear_css_js_cache( $redirect_to ) { $this->clear_assets_cache(); if ( function_exists( 'delete_transient' ) ) { \delete_transient( 'fastlayer_cache_debug_trace' ); } wp_safe_redirect( $redirect_to ); exit; } private function process_preload_cache( $redirect_to ) { $status = 'error'; if ( class_exists( 'FastLayer\Page_Cache' ) && method_exists( Page_Cache::get_instance(), 'preload_cache' ) ) { // The job is queued and returns immediately, so this request is not // held open while pages are warmed. $status = Page_Cache::get_instance()->preload_cache( 'manual' ); } if ( function_exists( 'delete_transient' ) ) { \delete_transient( 'fastlayer_cache_debug_trace' ); } $status = sanitize_key( $status ); wp_safe_redirect( add_query_arg( 'fastlayer_admin_bar_message', 'preload_' . $status, $redirect_to ) ); exit; } private function clear_page_cache() { if ( class_exists( 'FastLayer\Page_Cache' ) && method_exists( Page_Cache::get_instance(), 'clear_all_cache' ) ) { Page_Cache::get_instance()->clear_all_cache(); return; } $this->delete_directory_recursive( FASTLAYER_CACHE_DIR ); } private function clear_assets_cache() { $upload_dir = wp_upload_dir(); $min_dir = trailingslashit( $upload_dir['basedir'] ) . 'wp-fastlayer-min/'; $this->delete_directory_recursive( $min_dir ); } private function clear_preload_cache_artifacts() { $upload_dir = wp_upload_dir(); $preload_dir = trailingslashit( $upload_dir['basedir'] ) . 'wp-fastlayer-preload/'; $this->delete_directory_recursive( $preload_dir ); } private function delete_directory_recursive( $directory ) { $directory = wp_normalize_path( (string) $directory ); if ( ! is_dir( $directory ) ) { return; } $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $directory, \RecursiveDirectoryIterator::SKIP_DOTS ), \RecursiveIteratorIterator::CHILD_FIRST ); foreach ( $iterator as $item ) { if ( $item->isDir() ) { Bootstrap::delete_empty_directory( $item->getPathname() ); continue; } if ( $item->isFile() || $item->isLink() ) { wp_delete_file( $item->getPathname() ); } } Bootstrap::delete_empty_directory( $directory ); } }