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(
'
',
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 );
}
}