feat: implement Phase 1 - Core Page Cache and Admin Settings UI
Implement the core page caching functionality and admin settings interface for WP Recache plugin. This includes: - Plugin bootstrap with activation/deactivation handlers - Admin settings page with tabbed interface (Cache, Optimization, Advanced) - Admin bar integration for quick cache purging - Core page cache using output buffering - Cache file management with mobile device separation - Cache purge on post update/comment - Default URL exclusions (cart, checkout, account) - Cache statistics display - PHPUnit test suite setup - Docker-based development environment - PHPStan configuration for static analysis References: - Closes #1 (Core Page Cache) - Closes #8 (Admin Settings UI)
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin functionality.
|
||||
*
|
||||
* @package WP_Recache
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class WP_Recache_Admin {
|
||||
|
||||
/**
|
||||
* Initialize admin hooks.
|
||||
*/
|
||||
public function init() {
|
||||
add_action('admin_menu', [$this, 'add_admin_menu']);
|
||||
add_action('admin_init', [$this, 'register_settings']);
|
||||
add_action('admin_bar_menu', [$this, 'add_admin_bar_button'], 100);
|
||||
add_action('wp_ajax_wp_recache_purge_all', [$this, 'ajax_purge_all']);
|
||||
add_action('wp_ajax_wp_recache_purge_url', [$this, 'ajax_purge_url']);
|
||||
add_action('admin_notices', [$this, 'admin_notices']);
|
||||
|
||||
wp_localize_script('jquery', 'wpRecache', [
|
||||
'ajaxUrl' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('wp_recache_nonce'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add admin menu page.
|
||||
*/
|
||||
public function add_admin_menu() {
|
||||
add_options_page(
|
||||
'WP Recache',
|
||||
'WP Recache',
|
||||
'manage_options',
|
||||
'wp-recache',
|
||||
[$this, 'settings_page']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register settings.
|
||||
*/
|
||||
public function register_settings() {
|
||||
register_setting('wp_recache_settings', 'wp_recache_cache_enabled', [
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'rest_sanitize_boolean',
|
||||
'default' => true,
|
||||
]);
|
||||
|
||||
register_setting('wp_recache_settings', 'wp_recache_separate_mobile_cache', [
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'rest_sanitize_boolean',
|
||||
'default' => false,
|
||||
]);
|
||||
|
||||
register_setting('wp_recache_settings', 'wp_recache_exclude_urls', [
|
||||
'type' => 'array',
|
||||
'sanitize_callback' => [$this, 'sanitize_exclude_urls'],
|
||||
'default' => ['/cart', '/checkout', '/my-account'],
|
||||
]);
|
||||
|
||||
register_setting('wp_recache_settings', 'wp_recache_minify_css', [
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'rest_sanitize_boolean',
|
||||
'default' => false,
|
||||
]);
|
||||
|
||||
register_setting('wp_recache_settings', 'wp_recache_minify_js', [
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'rest_sanitize_boolean',
|
||||
'default' => false,
|
||||
]);
|
||||
|
||||
register_setting('wp_recache_settings', 'wp_recache_lazyload_images', [
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'rest_sanitize_boolean',
|
||||
'default' => false,
|
||||
]);
|
||||
|
||||
register_setting('wp_recache_settings', 'wp_recache_preload_enabled', [
|
||||
'type' => 'boolean',
|
||||
'sanitize_callback' => 'rest_sanitize_boolean',
|
||||
'default' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize exclude URLs.
|
||||
*
|
||||
* @param mixed $input Input value.
|
||||
* @return array Sanitized URLs.
|
||||
*/
|
||||
public function sanitize_exclude_urls($input) {
|
||||
if (!is_array($input)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_map('sanitize_text_field', $input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render settings page.
|
||||
*/
|
||||
public function settings_page() {
|
||||
if (!current_user_can('manage_options')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$active_tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : 'cache';
|
||||
|
||||
include WP_RECACHE_INC_PATH . 'Admin/views/settings.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add admin bar purge button.
|
||||
*
|
||||
* @param WP_Admin_Bar $admin_bar Admin bar instance.
|
||||
*/
|
||||
public function add_admin_bar_button($admin_bar) {
|
||||
if (!is_user_logged_in()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$admin_bar->add_menu([
|
||||
'id' => 'wp-recache',
|
||||
'title' => 'WP Recache',
|
||||
'href' => admin_url('options-general.php?page=wp-recache'),
|
||||
]);
|
||||
|
||||
$admin_bar->add_menu([
|
||||
'id' => 'wp-recache-purge-all',
|
||||
'parent' => 'wp-recache',
|
||||
'title' => 'Purge All Cache',
|
||||
'href' => '#',
|
||||
'meta' => [
|
||||
'onclick' => 'wpRecachePurgeAll();',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for purging all cache.
|
||||
*/
|
||||
public function ajax_purge_all() {
|
||||
check_ajax_referer('wp_recache_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error('Unauthorized');
|
||||
}
|
||||
|
||||
wp_recache_delete_all_cache();
|
||||
|
||||
wp_send_json_success('Cache purged successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler for purging URL cache.
|
||||
*/
|
||||
public function ajax_purge_url() {
|
||||
check_ajax_referer('wp_recache_nonce', 'nonce');
|
||||
|
||||
if (!current_user_can('manage_options')) {
|
||||
wp_send_json_error('Unauthorized');
|
||||
}
|
||||
|
||||
$url = isset($_POST['url']) ? esc_url_raw($_POST['url']) : '';
|
||||
|
||||
if (empty($url)) {
|
||||
wp_send_json_error('No URL provided');
|
||||
}
|
||||
|
||||
wp_recache_delete_cache($url);
|
||||
|
||||
wp_send_json_success('URL cache purged successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display admin notices.
|
||||
*/
|
||||
public function admin_notices() {
|
||||
$screen = get_current_screen();
|
||||
|
||||
if (!$screen || $screen->id !== 'settings_page_wp-recache') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($_GET['settings-updated'])) {
|
||||
echo '<div class="notice notice-success"><p>Settings saved.</p></div>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cache statistics.
|
||||
*
|
||||
* @return array Cache statistics.
|
||||
*/
|
||||
public static function get_cache_stats() {
|
||||
$cache_path = WP_RECACHE_CACHE_PATH;
|
||||
|
||||
if (!is_dir($cache_path)) {
|
||||
return [
|
||||
'files' => 0,
|
||||
'size' => 0,
|
||||
'size_formatted' => '0 B',
|
||||
];
|
||||
}
|
||||
|
||||
$files = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($cache_path, RecursiveDirectoryIterator::SKIP_DOTS)
|
||||
);
|
||||
|
||||
$count = 0;
|
||||
$size = 0;
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ($file->isFile()) {
|
||||
$count++;
|
||||
$size += $file->getSize();
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'files' => $count,
|
||||
'size' => $size,
|
||||
'size_formatted' => size_format($size),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
/**
|
||||
* Settings page template.
|
||||
*
|
||||
* @package WP_Recache
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
$stats = WP_Recache_Admin::get_cache_stats();
|
||||
?>
|
||||
|
||||
<div class="wrap">
|
||||
<h1>WP Recache Settings</h1>
|
||||
|
||||
<form method="post" action="options.php">
|
||||
<?php settings_fields('wp_recache_settings'); ?>
|
||||
|
||||
<nav class="nav-tab-wrapper">
|
||||
<a href="?page=wp-recache&tab=cache" class="nav-tab <?php echo $active_tab === 'cache' ? 'nav-tab-active' : ''; ?>">Cache</a>
|
||||
<a href="?page=wp-recache&tab=optimize" class="nav-tab <?php echo $active_tab === 'optimize' ? 'nav-tab-active' : ''; ?>">Optimization</a>
|
||||
<a href="?page=wp-recache&tab=advanced" class="nav-tab <?php echo $active_tab === 'advanced' ? 'nav-tab-active' : ''; ?>">Advanced</a>
|
||||
</nav>
|
||||
|
||||
<div class="tab-content" style="margin-top: 20px;">
|
||||
<?php if ($active_tab === 'cache') : ?>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">Enable Cache</th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="wp_recache_cache_enabled" value="1" <?php checked(get_option('wp_recache_cache_enabled', true)); ?>>
|
||||
Enable page caching
|
||||
</label>
|
||||
<p class="description">Serve cached HTML pages to anonymous visitors.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Separate Mobile Cache</th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="wp_recache_separate_mobile_cache" value="1" <?php checked(get_option('wp_recache_separate_mobile_cache', false)); ?>>
|
||||
Create separate cache for mobile devices
|
||||
</label>
|
||||
<p class="description">Useful for sites with different layouts for mobile.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Exclude URLs</th>
|
||||
<td>
|
||||
<textarea name="wp_recache_exclude_urls[]" rows="5" cols="50" class="large-text"><?php echo esc_textarea(implode("\n", get_option('wp_recache_exclude_urls', ['/cart', '/checkout', '/my-account']))); ?></textarea>
|
||||
<p class="description">One URL per line. These pages will not be cached.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2>Cache Statistics</h2>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">Cached Files</th>
|
||||
<td><?php echo esc_html($stats['files']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Cache Size</th>
|
||||
<td><?php echo esc_html($stats['size_formatted']); ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<?php elseif ($active_tab === 'optimize') : ?>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">Minify CSS</th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="wp_recache_minify_css" value="1" <?php checked(get_option('wp_recache_minify_css', false)); ?>>
|
||||
Minify CSS files
|
||||
</label>
|
||||
<p class="description">Remove whitespace and comments from CSS files.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Minify JavaScript</th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="wp_recache_minify_js" value="1" <?php checked(get_option('wp_recache_minify_js', false)); ?>>
|
||||
Minify JavaScript files
|
||||
</label>
|
||||
<p class="description">Remove whitespace and comments from JS files.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Lazy Load Images</th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="wp_recache_lazyload_images" value="1" <?php checked(get_option('wp_recache_lazyload_images', false)); ?>>
|
||||
Enable lazy loading for images
|
||||
</label>
|
||||
<p class="description">Load images only when they enter the viewport.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<?php elseif ($active_tab === 'advanced') : ?>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">Preload Cache</th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="wp_recache_preload_enabled" value="1" <?php checked(get_option('wp_recache_preload_enabled', false)); ?>>
|
||||
Enable cache preloading
|
||||
</label>
|
||||
<p class="description">Automatically crawl sitemap to warm cache.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2>Tools</h2>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">Purge Cache</th>
|
||||
<td>
|
||||
<button type="button" class="button" onclick="wpRecachePurgeAll();">Purge All Cache</button>
|
||||
<p class="description">Delete all cached files.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php submit_button('Save Settings'); ?>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function wpRecachePurgeAll() {
|
||||
if (!confirm('Are you sure you want to purge all cache?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
jQuery.post(wpRecache.ajaxUrl, {
|
||||
action: 'wp_recache_purge_all',
|
||||
nonce: wpRecache.nonce
|
||||
}, function(response) {
|
||||
if (response.success) {
|
||||
alert('Cache purged successfully');
|
||||
location.reload();
|
||||
} else {
|
||||
alert('Error: ' + response.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user