setup_cache_directory();
// Listen for cache invalidation
add_action('jltma/cache/invalidate_post', [$this, 'invalidate_post_cache']);
add_action('jltma/cache/clear_all', [$this, 'clear_all_cache']);
// Hook into asset loading when caching enabled
if ($this->is_enabled()) {
add_action('wp_enqueue_scripts', [$this, 'maybe_serve_cached_bundle'], 99);
}
// Clear cache on theme/plugin updates
add_action('switch_theme', [$this, 'clear_all_cache']);
add_action('upgrader_process_complete', [$this, 'on_upgrade_complete'], 10, 2);
// AJAX handlers for admin
add_action('wp_ajax_jltma_clear_cache', [$this, 'ajax_clear_cache']);
add_action('wp_ajax_jltma_regenerate_cache', [$this, 'ajax_regenerate_cache']);
add_action('wp_ajax_jltma_get_cache_stats', [$this, 'ajax_get_cache_stats']);
add_action('wp_ajax_jltma_clear_single_cache', [$this, 'ajax_clear_single_cache']);
add_action('wp_ajax_jltma_regenerate_single_cache', [$this, 'ajax_regenerate_single_cache']);
add_action('wp_ajax_jltma_save_performance_settings', [$this, 'ajax_save_performance_settings']);
}
/**
* Setup cache directory in uploads
*/
private function setup_cache_directory()
{
$upload_dir = wp_upload_dir();
if (!empty($upload_dir['error']) || empty($upload_dir['basedir'])) {
return;
}
$this->cache_path = $upload_dir['basedir'] . '/' . self::CACHE_DIR;
$this->cache_url = $upload_dir['baseurl'] . '/' . self::CACHE_DIR;
// Create directory if it doesn't exist
if (!file_exists($this->cache_path)) {
wp_mkdir_p($this->cache_path);
// Add index.php for security
file_put_contents(
$this->cache_path . '/index.php',
'
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE text/javascript
# Serve pre-compressed .gz files if they exist
RewriteEngine On
# Check if browser accepts gzip
RewriteCond %{HTTP:Accept-Encoding} gzip
# Serve .css.gz for .css requests
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.+)\.(css|js)$ $1.$2.gz [L]
# Set correct content types for .gz files
AddType text/css .css.gz
AddType application/javascript .js.gz
AddEncoding gzip .gz
# Cache control headers
Header set Cache-Control "max-age=31536000, public"
# Vary header for gzip
Header append Vary Accept-Encoding
# Content-Encoding for .gz files
Header set Content-Encoding gzip
HTACCESS;
file_put_contents($this->cache_path . '/.htaccess', $htaccess);
}
}
/**
* Check if caching is enabled
*/
public function is_enabled()
{
return (bool) get_option(self::OPTION_KEY, false);
}
/**
* Enable caching
*/
public static function enable()
{
update_option(self::OPTION_KEY, true);
}
/**
* Disable caching
*/
public static function disable()
{
update_option(self::OPTION_KEY, false);
}
/**
* Maybe serve cached bundle for current post
*/
public function maybe_serve_cached_bundle()
{
// Skip in admin or editor
if (is_admin() || $this->is_elementor_editor()) {
return;
}
$post_id = get_the_ID();
if (!$post_id) {
return;
}
// Check for existing valid cache
$cache_info = $this->get_post_cache_info($post_id);
if ($cache_info && $this->is_cache_valid($cache_info)) {
$this->serve_cached_bundle($cache_info);
return;
}
// Generate new cache on shutdown (non-blocking)
add_action('shutdown', function () use ($post_id) {
$this->generate_post_cache($post_id);
});
}
/**
* Generate bundled CSS/JS for a post
*/
public function generate_post_cache($post_id)
{
$assets_loader = Assets_Loader::get_instance();
$widgets = $assets_loader->detect_page_widgets($post_id);
if (empty($widgets)) {
return false;
}
// Generate unique hash based on widgets used
$hash = $this->generate_cache_hash($widgets);
// Bundle CSS
$css_content = $this->bundle_css_files($widgets);
$css_filename = "post-{$post_id}-{$hash}.css";
// Bundle JS
$js_content = $this->bundle_js_files($widgets);
$js_filename = "post-{$post_id}-{$hash}.js";
// Write files with gzip compression
$css_result = ['written' => false, 'gzip_written' => false, 'gzip_size' => 0];
$js_result = ['written' => false, 'gzip_written' => false, 'gzip_size' => 0];
if (!empty($css_content)) {
$css_result = $this->write_with_gzip(
$this->cache_path . '/' . $css_filename,
$css_content
);
}
if (!empty($js_content)) {
$js_result = $this->write_with_gzip(
$this->cache_path . '/' . $js_filename,
$js_content
);
}
if (!$css_result['written'] && !$js_result['written']) {
// File write failed - store in database fallback
$this->store_in_database($post_id, $css_content, $js_content);
return false;
}
// Store cache metadata
$cache_info = [
'hash' => $hash,
'css_file' => $css_result['written'] ? $css_filename : null,
'js_file' => $js_result['written'] ? $js_filename : null,
'widgets' => $widgets,
'created' => time(),
'size_css' => strlen($css_content),
'size_js' => strlen($js_content),
'gzip_size_css' => $css_result['gzip_size'],
'gzip_size_js' => $js_result['gzip_size'],
'gzip_enabled' => $css_result['gzip_written'] || $js_result['gzip_written'],
'is_rtl' => is_rtl(),
];
set_transient(
self::POST_CACHE_PREFIX . $post_id,
$cache_info,
WEEK_IN_SECONDS
);
$this->update_global_cache_meta($post_id, $cache_info);
return true;
}
/**
* Bundle multiple CSS files into one
* Handles array format from JLTMA_Config
*/
private function bundle_css_files($widgets)
{
$assets_loader = Assets_Loader::get_instance();
$widget_assets = $assets_loader->get_widget_assets();
$is_rtl = is_rtl();
$bundled_css = "/* Master Addons Bundled CSS - " . gmdate('Y-m-d H:i:s') . " */\n";
$processed = [];
foreach ($widgets as $widget_name) {
if (!isset($widget_assets[$widget_name])) {
continue;
}
// CSS is now an array
$css_files = $widget_assets[$widget_name]['css'] ?? [];
if (empty($css_files)) {
continue;
}
foreach ((array) $css_files as $css_slug) {
if (isset($processed[$css_slug])) {
continue;
}
$css_file = JLTMA_PATH . "assets/css/addons/{$css_slug}.css";
// Use RTL file if site is RTL
if ($is_rtl) {
$rtl_file = JLTMA_PATH . "assets/css/addons/{$css_slug}.rtl.css";
if (file_exists($rtl_file)) {
$css_file = $rtl_file;
}
}
if (file_exists($css_file)) {
$bundled_css .= "/* Widget: {$widget_name} ({$css_slug}) */\n";
$bundled_css .= file_get_contents($css_file) . "\n";
$processed[$css_slug] = true;
}
}
// Also bundle vendor CSS
$vendor_css = $widget_assets[$widget_name]['vendor']['css'] ?? [];
foreach ((array) $vendor_css as $vendor_slug) {
if (isset($processed['vendor-' . $vendor_slug])) {
continue;
}
$vendor_file = JLTMA_PATH . "assets/vendor/{$vendor_slug}/{$vendor_slug}.css";
if (file_exists($vendor_file)) {
$bundled_css .= "/* Vendor: {$vendor_slug} */\n";
$bundled_css .= file_get_contents($vendor_file) . "\n";
$processed['vendor-' . $vendor_slug] = true;
}
}
}
// Add common swiper styles if needed
$swiper_widgets = ['ma-logo-slider', 'ma-team-members-slider', 'ma-image-carousel', 'ma-twitter-slider', 'ma-blog', 'ma-timeline'];
if (array_intersect($widgets, $swiper_widgets)) {
$swiper_file = JLTMA_PATH . 'assets/css/common/swiper-carousel.css';
if (file_exists($swiper_file) && !isset($processed['common-swiper-carousel'])) {
$bundled_css .= "/* Common: Swiper */\n";
$bundled_css .= file_get_contents($swiper_file) . "\n";
}
}
return $this->minify_css($bundled_css);
}
/**
* Bundle multiple JS files into one
* Handles array format from JLTMA_Config
*/
private function bundle_js_files($widgets)
{
$assets_loader = Assets_Loader::get_instance();
$widget_assets = $assets_loader->get_widget_assets();
$bundled_js = "/* Master Addons Bundled JS - " . gmdate('Y-m-d H:i:s') . " */\n";
$bundled_js .= "(function($){\n'use strict';\n";
$has_content = false;
$processed = [];
foreach ($widgets as $widget_name) {
if (!isset($widget_assets[$widget_name])) {
continue;
}
// JS is now an array
$js_files = $widget_assets[$widget_name]['js'] ?? [];
foreach ((array) $js_files as $js_slug) {
if (empty($js_slug) || isset($processed[$js_slug])) {
continue;
}
$js_file = JLTMA_PATH . "assets/js/addons/{$js_slug}.js";
if (file_exists($js_file)) {
$bundled_js .= "/* Widget: {$widget_name} ({$js_slug}) */\n";
$bundled_js .= file_get_contents($js_file) . "\n";
$processed[$js_slug] = true;
$has_content = true;
}
}
// Also bundle vendor JS
$vendor_js = $widget_assets[$widget_name]['vendor']['js'] ?? [];
foreach ((array) $vendor_js as $vendor_slug) {
if (isset($processed['vendor-' . $vendor_slug])) {
continue;
}
$vendor_file = JLTMA_PATH . "assets/vendor/{$vendor_slug}/{$vendor_slug}.js";
if (file_exists($vendor_file)) {
$bundled_js .= "/* Vendor: {$vendor_slug} */\n";
$bundled_js .= file_get_contents($vendor_file) . "\n";
$processed['vendor-' . $vendor_slug] = true;
$has_content = true;
}
}
}
$bundled_js .= "})(jQuery);";
return $has_content ? $bundled_js : '';
}
/**
* Serve cached bundle instead of individual files
* Handles array format from JLTMA_Config
*/
private function serve_cached_bundle($cache_info)
{
$assets_loader = Assets_Loader::get_instance();
$widget_assets = $assets_loader->get_widget_assets();
// Dequeue individual addon assets
foreach ($cache_info['widgets'] as $widget_name) {
if (!isset($widget_assets[$widget_name])) {
continue;
}
// CSS is now an array
$css_files = $widget_assets[$widget_name]['css'] ?? [];
foreach ((array) $css_files as $css_slug) {
wp_dequeue_style('jltma-' . $css_slug);
wp_dequeue_style('jltma-' . $css_slug . '-rtl');
}
// JS is now an array
$js_files = $widget_assets[$widget_name]['js'] ?? [];
foreach ((array) $js_files as $js_slug) {
wp_dequeue_script('jltma-' . $js_slug);
}
// Also dequeue vendor assets
$vendor_css = $widget_assets[$widget_name]['vendor']['css'] ?? [];
foreach ((array) $vendor_css as $vendor_slug) {
wp_dequeue_style('jltma-vendor-' . $vendor_slug);
}
$vendor_js = $widget_assets[$widget_name]['vendor']['js'] ?? [];
foreach ((array) $vendor_js as $vendor_slug) {
wp_dequeue_script('jltma-vendor-' . $vendor_slug);
}
}
// Also dequeue common swiper if cached
wp_dequeue_style('jltma-swiper-carousel');
// Enqueue bundled CSS
if (!empty($cache_info['css_file'])) {
wp_enqueue_style(
'jltma-bundled-' . $cache_info['hash'],
$this->cache_url . '/' . $cache_info['css_file'],
[],
JLTMA_VER
);
}
// Enqueue bundled JS
if (!empty($cache_info['js_file'])) {
wp_enqueue_script(
'jltma-bundled-' . $cache_info['hash'],
$this->cache_url . '/' . $cache_info['js_file'],
['jquery'],
JLTMA_VER,
true
);
}
}
/**
* Generate hash from widget list
*/
private function generate_cache_hash($widgets)
{
sort($widgets); // Consistent ordering
$rtl_suffix = is_rtl() ? '-rtl' : '';
return substr(md5(implode('|', $widgets) . JLTMA_VER . $rtl_suffix), 0, 8);
}
/**
* Check if cache is still valid
*/
private function is_cache_valid($cache_info)
{
// Check if RTL setting changed
if (isset($cache_info['is_rtl']) && $cache_info['is_rtl'] !== is_rtl()) {
return false;
}
// Check if files exist
if (!empty($cache_info['css_file'])) {
if (!file_exists($this->cache_path . '/' . $cache_info['css_file'])) {
return false;
}
}
if (!empty($cache_info['js_file'])) {
if (!file_exists($this->cache_path . '/' . $cache_info['js_file'])) {
return false;
}
}
// Check if plugin version changed (hash includes version)
$current_hash = $this->generate_cache_hash($cache_info['widgets']);
if ($current_hash !== $cache_info['hash']) {
return false;
}
return true;
}
/**
* Invalidate cache for a specific post
*/
public function invalidate_post_cache($post_id)
{
$cache_info = $this->get_post_cache_info($post_id);
if ($cache_info) {
// Delete cached files (including gzipped versions)
if (!empty($cache_info['css_file'])) {
wp_delete_file($this->cache_path . '/' . $cache_info['css_file']);
wp_delete_file($this->cache_path . '/' . $cache_info['css_file'] . '.gz');
}
if (!empty($cache_info['js_file'])) {
wp_delete_file($this->cache_path . '/' . $cache_info['js_file']);
wp_delete_file($this->cache_path . '/' . $cache_info['js_file'] . '.gz');
}
// Clear transient
delete_transient(self::POST_CACHE_PREFIX . $post_id);
// Clear database fallback
delete_post_meta($post_id, '_jltma_cached_css');
delete_post_meta($post_id, '_jltma_cached_js');
delete_post_meta($post_id, '_jltma_cache_in_db');
// Update global meta
$this->remove_from_global_cache_meta($post_id);
}
}
/**
* Clear all cache files and metadata
*/
public function clear_all_cache()
{
// Delete all cache files (including gzipped versions)
$files = glob($this->cache_path . '/*.{css,js,css.gz,js.gz}', GLOB_BRACE);
if ($files) {
foreach ($files as $file) {
wp_delete_file($file);
}
}
// Clear all transients (using global meta to find them)
$global_meta = get_option(self::CACHE_META_OPTION, []);
if (!empty($global_meta['posts'])) {
foreach (array_keys($global_meta['posts']) as $post_id) {
delete_transient(self::POST_CACHE_PREFIX . $post_id);
delete_post_meta($post_id, '_jltma_cached_css');
delete_post_meta($post_id, '_jltma_cached_js');
delete_post_meta($post_id, '_jltma_cache_in_db');
}
}
// Reset global meta
update_option(self::CACHE_META_OPTION, [
'posts' => [],
'total_size' => 0,
'file_count' => 0,
'last_cleared' => time(),
]);
return true;
}
/**
* Update global cache metadata for dashboard
*/
private function update_global_cache_meta($post_id, $cache_info)
{
$global_meta = get_option(self::CACHE_META_OPTION, [
'posts' => [],
'total_size' => 0,
'file_count' => 0,
'last_cleared' => null,
]);
// Remove old entry size if updating
if (isset($global_meta['posts'][$post_id])) {
$old = $global_meta['posts'][$post_id];
$global_meta['total_size'] -= ($old['size_css'] ?? 0) + ($old['size_js'] ?? 0);
$global_meta['file_count'] -= 2;
}
// Add new entry
$global_meta['posts'][$post_id] = [
'hash' => $cache_info['hash'],
'size_css' => $cache_info['size_css'],
'size_js' => $cache_info['size_js'],
'widgets' => $cache_info['widgets'],
'created' => $cache_info['created'],
'title' => get_the_title($post_id),
];
$global_meta['total_size'] += $cache_info['size_css'] + $cache_info['size_js'];
$global_meta['file_count'] += 2;
update_option(self::CACHE_META_OPTION, $global_meta);
}
/**
* Remove post from global cache meta
*/
private function remove_from_global_cache_meta($post_id)
{
$global_meta = get_option(self::CACHE_META_OPTION, []);
if (isset($global_meta['posts'][$post_id])) {
$entry = $global_meta['posts'][$post_id];
$global_meta['total_size'] -= ($entry['size_css'] ?? 0) + ($entry['size_js'] ?? 0);
$global_meta['file_count'] -= 2;
unset($global_meta['posts'][$post_id]);
update_option(self::CACHE_META_OPTION, $global_meta);
}
}
/**
* Get post cache info from transient
*/
public function get_post_cache_info($post_id)
{
return get_transient(self::POST_CACHE_PREFIX . $post_id);
}
/**
* Get cache statistics for dashboard
*/
public function get_cache_stats()
{
$global_meta = get_option(self::CACHE_META_OPTION, []);
// Verify actual files match metadata
$actual_files = glob($this->cache_path . '/*.{css,js}', GLOB_BRACE);
$actual_count = $actual_files ? count($actual_files) : 0;
// Calculate actual size
$actual_size = 0;
if ($actual_files) {
foreach ($actual_files as $file) {
$actual_size += filesize($file);
}
}
// Get cached files list with details
$cached_files = [];
if (!empty($global_meta['posts'])) {
foreach ($global_meta['posts'] as $post_id => $info) {
$css_file = $this->cache_path . "/post-{$post_id}-{$info['hash']}.css";
$file_size = 0;
$file_modified = 0;
if (file_exists($css_file)) {
$file_size = filesize($css_file);
$file_modified = filemtime($css_file);
}
$cached_files[] = [
'post_id' => $post_id,
'post_title' => $info['title'] ?? get_the_title($post_id),
'filename' => "post-{$post_id}-{$info['hash']}.css",
'size' => $file_size,
'size_formatted' => size_format($file_size),
'modified' => $file_modified,
'modified_formatted' => $file_modified ? human_time_diff($file_modified) . ' ' . __('ago', 'master-addons') : __('N/A', 'master-addons'),
'widgets' => $info['widgets'] ?? [],
];
}
}
// Format last cleared time
$last_cleared = $global_meta['last_cleared'] ?? null;
$last_cleared_formatted = $last_cleared
? human_time_diff($last_cleared) . ' ' . __('ago', 'master-addons')
: __('Never', 'master-addons');
return [
'enabled' => $this->is_enabled(),
'total_size' => $actual_size,
'total_size_formatted' => size_format($actual_size),
'total_size_hr' => size_format($actual_size),
'file_count' => $actual_count,
'cached_pages' => count($global_meta['posts'] ?? []),
'post_count' => count($global_meta['posts'] ?? []),
'last_cleared' => $last_cleared,
'last_cleared_formatted' => $last_cleared_formatted,
'cache_path' => $this->cache_path,
'cache_url' => $this->cache_url,
'cache_directory' => str_replace(ABSPATH, '', $this->cache_path),
'posts' => $global_meta['posts'] ?? [],
'cached_files' => $cached_files,
];
}
/**
* Regenerate cache for all posts with Elementor content
*/
public function regenerate_all_cache()
{
// Clear existing first
$this->clear_all_cache();
// Find all posts with Elementor data
$posts = get_posts([
'post_type' => ['page', 'post', 'elementor_library'],
'posts_per_page' => -1,
'meta_key' => '_elementor_data',
'fields' => 'ids',
'post_status' => 'publish',
]);
$count = 0;
foreach ($posts as $post_id) {
if ($this->generate_post_cache($post_id)) {
$count++;
}
}
return $count;
}
/**
* Database fallback when file writes fail
*/
private function store_in_database($post_id, $css_content, $js_content)
{
if (!empty($css_content)) {
update_post_meta($post_id, '_jltma_cached_css', $css_content);
}
if (!empty($js_content)) {
update_post_meta($post_id, '_jltma_cached_js', $js_content);
}
update_post_meta($post_id, '_jltma_cache_in_db', true);
}
/**
* Simple CSS minification
*/
private function minify_css($css)
{
// Remove comments
$css = preg_replace('/\/\*[^*]*\*+([^\/][^*]*\*+)*\//', '', $css);
// Remove whitespace
$css = preg_replace('/\s+/', ' ', $css);
// Remove space around selectors
$css = preg_replace('/\s*([\{\}\;\:\,])\s*/', '$1', $css);
return trim($css);
}
/**
* Compress content with gzip
*
* @param string $content Content to compress
* @param int $level Compression level (1-9, default 9)
* @return string|false Compressed content or false on failure
*/
private function gzip_content($content, $level = 9)
{
if (!function_exists('gzencode')) {
return false;
}
return gzencode($content, $level);
}
/**
* Write file with optional gzip version
*
* @param string $filepath Full path to file
* @param string $content File content
* @return array ['written' => bool, 'gzip_written' => bool, 'gzip_size' => int]
*/
private function write_with_gzip($filepath, $content)
{
$result = [
'written' => false,
'gzip_written' => false,
'gzip_size' => 0,
];
// Write original file
$result['written'] = (bool) file_put_contents($filepath, $content);
if (!$result['written']) {
return $result;
}
// Write gzipped version
$gzipped = $this->gzip_content($content);
if ($gzipped !== false) {
$gzip_path = $filepath . '.gz';
$result['gzip_written'] = (bool) file_put_contents($gzip_path, $gzipped);
if ($result['gzip_written']) {
$result['gzip_size'] = strlen($gzipped);
}
}
return $result;
}
/**
* Check if we're in Elementor editor
*/
private function is_elementor_editor()
{
if (!class_exists('\Elementor\Plugin')) {
return false;
}
$elementor = \Elementor\Plugin::$instance;
if (!$elementor || !isset($elementor->editor) || !isset($elementor->preview)) {
return false;
}
return $elementor->editor->is_edit_mode() || $elementor->preview->is_preview_mode();
}
/**
* Handle plugin/theme upgrades
*/
public function on_upgrade_complete($upgrader, $options)
{
// Clear cache when Master Addons is updated
if (
$options['action'] === 'update' &&
$options['type'] === 'plugin' &&
isset($options['plugins']) &&
in_array('master-addons/master-addons.php', $options['plugins'])
) {
$this->clear_all_cache();
}
}
/**
* AJAX: Clear all cache
*/
public function ajax_clear_cache()
{
check_ajax_referer('jltma_admin_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('Permission denied', 'master-addons')]);
}
$this->clear_all_cache();
wp_send_json_success([
'message' => __('Cache cleared successfully', 'master-addons'),
'stats' => $this->get_cache_stats(),
]);
}
/**
* AJAX: Regenerate all cache
*/
public function ajax_regenerate_cache()
{
check_ajax_referer('jltma_admin_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('Permission denied', 'master-addons')]);
}
$count = $this->regenerate_all_cache();
wp_send_json_success([
/* translators: %d: number of pages */
'message' => sprintf(__('Regenerated cache for %d pages', 'master-addons'), $count),
'stats' => $this->get_cache_stats(),
]);
}
/**
* AJAX: Get cache stats
*/
public function ajax_get_cache_stats()
{
check_ajax_referer('jltma_admin_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('Permission denied', 'master-addons')]);
}
wp_send_json_success($this->get_cache_stats());
}
/**
* Get cache directory path
*/
public function get_cache_path()
{
return $this->cache_path;
}
/**
* Get cache directory URL
*/
public function get_cache_url()
{
return $this->cache_url;
}
/**
* AJAX: Clear single post cache
*/
public function ajax_clear_single_cache()
{
check_ajax_referer('jltma_admin_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('Permission denied', 'master-addons')]);
}
$post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
if (!$post_id) {
wp_send_json_error(['message' => __('Invalid post ID', 'master-addons')]);
}
$this->invalidate_post_cache($post_id);
wp_send_json_success([
/* translators: %d: post ID number */
'message' => sprintf(__('Cache cleared for post #%d', 'master-addons'), $post_id),
'stats' => $this->get_cache_stats(),
]);
}
/**
* AJAX: Regenerate single post cache
*/
public function ajax_regenerate_single_cache()
{
check_ajax_referer('jltma_admin_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('Permission denied', 'master-addons')]);
}
$post_id = isset($_POST['post_id']) ? absint($_POST['post_id']) : 0;
if (!$post_id) {
wp_send_json_error(['message' => __('Invalid post ID', 'master-addons')]);
}
// Clear existing cache first
$this->invalidate_post_cache($post_id);
// Regenerate
$result = $this->generate_post_cache($post_id);
if ($result) {
wp_send_json_success([
/* translators: %d: post ID number */
'message' => sprintf(__('Cache regenerated for post #%d', 'master-addons'), $post_id),
'stats' => $this->get_cache_stats(),
]);
} else {
wp_send_json_error([
/* translators: %d: post ID number */
'message' => sprintf(__('Failed to regenerate cache for post #%d', 'master-addons'), $post_id),
]);
}
}
/**
* AJAX: Save performance settings
*/
public function ajax_save_performance_settings()
{
check_ajax_referer('jltma_performance_settings_nonce_action', '_wpnonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('Permission denied', 'master-addons')]);
}
// Save settings
$dynamic_assets = isset($_POST['jltma_dynamic_assets_enabled']) ? true : false;
$cache_enabled = isset($_POST['jltma_cache_enabled']) ? true : false;
$cache_minify = isset($_POST['jltma_cache_minify']) ? true : false;
$cache_debug = isset($_POST['jltma_cache_debug']) ? true : false;
update_option('jltma_dynamic_assets_enabled', $dynamic_assets);
update_option('jltma_cache_enabled', $cache_enabled);
update_option('jltma_cache_minify', $cache_minify);
update_option('jltma_cache_debug', $cache_debug);
// Clear cache if caching was disabled
if (!$cache_enabled) {
$this->clear_all_cache();
}
wp_send_json_success([
'message' => __('Performance settings saved', 'master-addons'),
]);
}
}