init_hooks(); } /** * Initialize all hooks */ private function init_hooks() { // Post/page update hooks add_action('save_post', array($this, 'on_save_post'), 20, 3); add_action('delete_post', array($this, 'on_delete_post'), 10); add_action('trash_post', array($this, 'on_delete_post'), 10); add_action('transition_post_status', array($this, 'on_status_change'), 10, 3); // Comment hooks add_action('comment_post', array($this, 'on_comment_change'), 10, 2); add_action('edit_comment', array($this, 'on_comment_edit'), 10); add_action('delete_comment', array($this, 'on_comment_delete'), 10); add_action('wp_set_comment_status', array($this, 'on_comment_status_change'), 10, 2); // Global change hooks - clear entire cache add_action('switch_theme', array($this, 'on_global_change')); add_action('customize_save_after', array($this, 'on_global_change')); add_action('update_option_sidebars_widgets', array($this, 'on_global_change')); add_action('wp_update_nav_menu', array($this, 'on_global_change')); // Plugin/theme update hooks - regenerate after other plugins update, skip for this plugin add_action('upgrader_process_complete', array($this, 'on_plugin_update'), 10, 2); // WooCommerce hooks (if active) add_action('woocommerce_product_set_stock', array($this, 'on_woo_product_change')); add_action('woocommerce_variation_set_stock', array($this, 'on_woo_product_change')); // Admin bar indicator for cache status add_action('admin_bar_menu', array($this, 'add_admin_bar_indicator'), 100); add_action('wp_head', array($this, 'admin_bar_styles')); add_action('admin_head', array($this, 'admin_bar_styles')); add_action('wp_footer', array($this, 'admin_bar_scripts')); add_action('admin_footer', array($this, 'admin_bar_scripts')); // AJAX handlers for admin bar actions add_action('wp_ajax_wp_to_html_adminbar_clear', array($this, 'ajax_adminbar_clear')); add_action('wp_ajax_wp_to_html_adminbar_regenerate', array($this, 'ajax_adminbar_regenerate')); add_action('wp_ajax_wp_to_html_adminbar_regenerate_page', array($this, 'ajax_adminbar_regenerate_page')); add_action('wp_ajax_wp_to_html_adminbar_clear_regenerate', array($this, 'ajax_adminbar_clear_regenerate')); } /** * Handle post save * * @param int $post_id Post ID * @param WP_Post $post Post object * @param bool $update Whether this is an update */ public function on_save_post($post_id, $post, $update) { // Don't process revisions if (wp_is_post_revision($post_id)) { return; } // Don't process autosaves if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } // Only process pages and posts (and custom post types that are public) $allowed_types = $this->get_allowed_post_types(); if (!in_array($post->post_type, $allowed_types)) { return; } $generator = WP_To_HTML_Generator::get_instance(); // If the post is published, regenerate the cache if ($post->post_status === 'publish') { // Schedule regeneration to run after all other save operations wp_schedule_single_event(time() + 5, 'wp_to_html_regenerate_post', array($post_id)); } else { // Post is not published, delete the cache $generator->delete_cache($post_id); } } /** * Handle post deletion * * @param int $post_id Post ID */ public function on_delete_post($post_id) { $generator = WP_To_HTML_Generator::get_instance(); $generator->delete_cache($post_id); } /** * Handle post status transitions * * @param string $new_status New status * @param string $old_status Old status * @param WP_Post $post Post object */ public function on_status_change($new_status, $old_status, $post) { $generator = WP_To_HTML_Generator::get_instance(); // If unpublishing, delete cache if ($old_status === 'publish' && $new_status !== 'publish') { $generator->delete_cache($post->ID); } // If publishing, generate cache if ($new_status === 'publish' && $old_status !== 'publish') { wp_schedule_single_event(time() + 5, 'wp_to_html_regenerate_post', array($post->ID)); } } /** * Handle new comment * * @param int $comment_id Comment ID * @param int|string $comment_approved Approval status */ public function on_comment_change($comment_id, $comment_approved) { $comment = get_comment($comment_id); if ($comment) { $generator = WP_To_HTML_Generator::get_instance(); $generator->delete_cache($comment->comment_post_ID); } } /** * Handle comment edit * * @param int $comment_id Comment ID */ public function on_comment_edit($comment_id) { $comment = get_comment($comment_id); if ($comment) { $generator = WP_To_HTML_Generator::get_instance(); $generator->delete_cache($comment->comment_post_ID); } } /** * Handle comment deletion * * @param int $comment_id Comment ID */ public function on_comment_delete($comment_id) { $comment = get_comment($comment_id); if ($comment) { $generator = WP_To_HTML_Generator::get_instance(); $generator->delete_cache($comment->comment_post_ID); } } /** * Handle comment status change * * @param int $comment_id Comment ID * @param string $status New status */ public function on_comment_status_change($comment_id, $status) { $comment = get_comment($comment_id); if ($comment) { $generator = WP_To_HTML_Generator::get_instance(); $generator->delete_cache($comment->comment_post_ID); } } /** * Handle global changes that affect all pages */ public function on_global_change() { $generator = WP_To_HTML_Generator::get_instance(); $generator->clear_all_cache(); } /** * Handle plugin/theme updates * * Regenerates cache when other plugins are updated, but skips when this plugin is updated * * @param WP_Upgrader $upgrader Upgrader instance * @param array $options Update options including type and plugins */ public function on_plugin_update($upgrader, $options) { // Only handle plugin updates if (!isset($options['type']) || $options['type'] !== 'plugin') { return; } // Get the list of plugins being updated $plugins = array(); if (isset($options['plugins'])) { $plugins = (array) $options['plugins']; } elseif (isset($options['plugin'])) { $plugins = array($options['plugin']); } // Check if our plugin is in the update list $our_plugin = 'WP-to-HTML/wp-to-html.php'; $our_plugin_alt = plugin_basename(WP_TO_HTML_PLUGIN_DIR . 'wp-to-html.php'); foreach ($plugins as $plugin) { if ($plugin === $our_plugin || $plugin === $our_plugin_alt) { // This plugin is being updated - don't clear or regenerate return; } } // Other plugins are being updated - clear cache and regenerate $generator = WP_To_HTML_Generator::get_instance(); $generator->clear_all_cache(); // Set pending status for plugin update regeneration WP_To_HTML_Cron::set_regeneration_status(array( 'status' => 'pending', 'source' => 'plugin_update', 'started' => time(), 'message' => __('Waiting for plugin update to complete...', 'wp-to-html'), )); // Schedule regeneration to run after the update completes wp_schedule_single_event(time() + 10, 'wp_to_html_regenerate_all', array('plugin_update')); } /** * Handle WooCommerce product changes * * @param WC_Product $product Product object */ public function on_woo_product_change($product) { // Products are excluded by default, but clear cache just in case if (is_object($product) && method_exists($product, 'get_id')) { $generator = WP_To_HTML_Generator::get_instance(); $generator->delete_cache($product->get_id()); } } /** * Get allowed post types for static generation * * @return array List of post type slugs */ private function get_allowed_post_types() { $post_types = get_post_types(array( 'public' => true, ), 'names'); // Remove excluded types $excluded = array('attachment', 'product', 'product_variation'); return array_diff($post_types, $excluded); } /** * Add admin bar indicator for cache status * * @param WP_Admin_Bar $admin_bar Admin bar instance */ public function add_admin_bar_indicator($admin_bar) { // Only show for users who can manage options if (!current_user_can('manage_options')) { return; } $settings = WP_To_HTML::get_settings(); $generator = WP_To_HTML_Generator::get_instance(); // Determine if we're on the frontend viewing a singular page $is_frontend_singular = !is_admin() && is_singular(); if ($is_frontend_singular) { // Frontend: Show page-specific status and regenerate option $post_id = get_the_ID(); if (!$post_id) { return; } $post = get_post($post_id); // Determine cache status $exclusion_reason = $generator->should_exclude($post); $has_cache = $generator->has_cache($post_id); if (!$settings['enabled']) { $status = 'disabled'; $icon = '⏸'; $title = __('WP-to-HTML: Disabled', 'wp-to-html'); $color = '#72777c'; } elseif ($exclusion_reason) { $status = 'excluded'; $icon = '⚠'; $title = sprintf(__('WP-to-HTML: Excluded - %s', 'wp-to-html'), $exclusion_reason); $color = '#dba617'; } elseif ($has_cache) { $status = 'cached'; $icon = '⚡'; $title = __('WP-to-HTML: Cached (Static HTML)', 'wp-to-html'); $color = '#00a32a'; } else { $status = 'not-cached'; $icon = '○'; $title = __('WP-to-HTML: Not Cached Yet', 'wp-to-html'); $color = '#72777c'; } $admin_bar->add_node(array( 'id' => 'wp-to-html-status', 'title' => '' . $icon . ' ' . esc_html($title) . '', 'href' => admin_url('options-general.php?page=wp-to-html'), 'meta' => array( 'class' => 'wp-to-html-admin-bar-item wp-to-html-status-' . $status, ), )); // Add submenu items for frontend $admin_bar->add_node(array( 'id' => 'wp-to-html-settings', 'parent' => 'wp-to-html-status', 'title' => __('⚙️ Settings', 'wp-to-html'), 'href' => admin_url('options-general.php?page=wp-to-html'), )); // Only show regenerate if not excluded if (!$exclusion_reason && $settings['enabled']) { $admin_bar->add_node(array( 'id' => 'wp-to-html-regenerate-page', 'parent' => 'wp-to-html-status', 'title' => __('🔄 Regenerate This Page', 'wp-to-html'), 'href' => '#', 'meta' => array( 'class' => 'wp-to-html-action-regenerate-page', 'onclick' => 'return false;', 'data-post-id' => $post_id, ), )); } } else if (is_admin()) { // Backend: Show global actions $stats = $generator->get_cache_stats(); $icon = '⚡'; // Don't show "(0 cached)" - only show count if there are cached pages if ($stats['total_files'] > 0) { $title = sprintf(__('WP-to-HTML (%d cached)', 'wp-to-html'), $stats['total_files']); } else { $title = __('WP-to-HTML', 'wp-to-html'); } $admin_bar->add_node(array( 'id' => 'wp-to-html-status', 'title' => '' . $icon . ' ' . esc_html($title) . '', 'href' => admin_url('options-general.php?page=wp-to-html'), 'meta' => array( 'class' => 'wp-to-html-admin-bar-item', ), )); // Add submenu items for backend $admin_bar->add_node(array( 'id' => 'wp-to-html-settings', 'parent' => 'wp-to-html-status', 'title' => __('⚙️ Settings', 'wp-to-html'), 'href' => admin_url('options-general.php?page=wp-to-html'), )); $admin_bar->add_node(array( 'id' => 'wp-to-html-clear', 'parent' => 'wp-to-html-status', 'title' => __('🗑️ Clear All Cache', 'wp-to-html'), 'href' => '#', 'meta' => array( 'class' => 'wp-to-html-action-clear', 'onclick' => 'return false;', ), )); $admin_bar->add_node(array( 'id' => 'wp-to-html-regenerate', 'parent' => 'wp-to-html-status', 'title' => __('🔄 Regenerate All', 'wp-to-html'), 'href' => '#', 'meta' => array( 'class' => 'wp-to-html-action-regenerate', 'onclick' => 'return false;', ), )); $admin_bar->add_node(array( 'id' => 'wp-to-html-clear-regenerate', 'parent' => 'wp-to-html-status', 'title' => __('✨ Clear & Regenerate All', 'wp-to-html'), 'href' => '#', 'meta' => array( 'class' => 'wp-to-html-action-clear-regenerate', 'onclick' => 'return false;', ), )); } } /** * Add admin bar styles for the cache indicator */ public function admin_bar_styles() { if (!is_admin_bar_showing() || !current_user_can('manage_options')) { return; } ?> clear_all_cache(); wp_send_json_success(array( 'message' => __('Cache cleared successfully', 'wp-to-html'), )); } /** * AJAX handler for regenerating all from admin bar */ public function ajax_adminbar_regenerate() { check_ajax_referer('wp_to_html_adminbar', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(__('Permission denied', 'wp-to-html')); } // Set initial status WP_To_HTML_Cron::set_regeneration_status(array( 'status' => 'running', 'source' => 'admin_bar', 'started' => time(), 'total' => 0, 'processed' => 0, 'converted' => 0, 'skipped' => 0, 'errors' => 0, )); $bulk = WP_To_HTML_Bulk::get_instance(); $results = $bulk->convert_all(); // Set complete status WP_To_HTML_Cron::set_regeneration_status(array( 'status' => 'complete', 'source' => 'admin_bar', 'completed' => time(), 'total' => $results['total'], 'processed' => $results['total'], 'converted' => $results['converted'], 'skipped' => $results['skipped'], 'errors' => $results['errors'], )); wp_send_json_success(array( 'message' => __('Regeneration complete', 'wp-to-html'), 'converted' => $results['converted'], 'skipped' => $results['skipped'], 'errors' => $results['errors'], )); } /** * AJAX handler for regenerating a single page from admin bar */ public function ajax_adminbar_regenerate_page() { check_ajax_referer('wp_to_html_adminbar', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(__('Permission denied', 'wp-to-html')); } $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0; if (!$post_id) { wp_send_json_error(__('Invalid post ID', 'wp-to-html')); } $generator = WP_To_HTML_Generator::get_instance(); $result = $generator->generate($post_id); if (is_wp_error($result)) { wp_send_json_error($result->get_error_message()); } wp_send_json_success(array( 'message' => __('Page regenerated successfully', 'wp-to-html'), )); } /** * AJAX handler for clearing cache and regenerating all from admin bar */ public function ajax_adminbar_clear_regenerate() { check_ajax_referer('wp_to_html_adminbar', 'nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(__('Permission denied', 'wp-to-html')); } // First clear the cache $generator = WP_To_HTML_Generator::get_instance(); $generator->clear_all_cache(); // Then regenerate all $bulk = WP_To_HTML_Bulk::get_instance(); $results = $bulk->convert_all(); wp_send_json_success(array( 'message' => __('Cache cleared and regeneration complete', 'wp-to-html'), 'converted' => $results['converted'], 'skipped' => $results['skipped'], 'errors' => $results['errors'], )); } } // Register the scheduled event handlers add_action('wp_to_html_regenerate_post', function($post_id) { $generator = WP_To_HTML_Generator::get_instance(); $generator->generate($post_id); }); // Register handler for regenerating all pages after plugin updates add_action('wp_to_html_regenerate_all', function($source = 'plugin_update') { // Set running status WP_To_HTML_Cron::set_regeneration_status(array( 'status' => 'running', 'source' => $source, 'started' => time(), 'total' => 0, 'processed' => 0, 'converted' => 0, 'skipped' => 0, 'errors' => 0, )); $bulk = WP_To_HTML_Bulk::get_instance(); $results = $bulk->convert_all(); // Set complete status WP_To_HTML_Cron::set_regeneration_status(array( 'status' => 'complete', 'source' => $source, 'completed' => time(), 'total' => $results['total'], 'processed' => $results['total'], 'converted' => $results['converted'], 'skipped' => $results['skipped'], 'errors' => $results['errors'], )); });