should_exclude($post); if ($exclusion_reason) { return new WP_Error('excluded', $exclusion_reason); } // Get the permalink $url = get_permalink($post_id); if (!$url) { return new WP_Error('no_permalink', __('Could not get permalink for post', 'wp-to-html')); } // Fetch the rendered page $response = wp_remote_get($url, array( 'timeout' => 30, 'sslverify' => false, 'cookies' => array(), // No cookies to get the logged-out version )); if (is_wp_error($response)) { return $response; } $html = wp_remote_retrieve_body($response); $status_code = wp_remote_retrieve_response_code($response); if ($status_code !== 200) { return new WP_Error('bad_response', sprintf(__('Received HTTP %d response', 'wp-to-html'), $status_code)); } if (empty($html)) { return new WP_Error('empty_response', __('Received empty response', 'wp-to-html')); } // Get the cache path for this URL $cache_path = $this->get_cache_path($url); // Create directory if it doesn't exist $cache_dir = dirname($cache_path); if (!file_exists($cache_dir)) { wp_mkdir_p($cache_dir); } // Bundle CSS and JS assets if enabled $assets = WP_To_HTML_Assets::get_instance(); $html = $assets->bundle_assets($html, $post_id, $url); // Add cache generation comment to HTML $timestamp = current_time('mysql'); $cache_comment = "\n"; $html = preg_replace('/<\/html>/i', $cache_comment . "\n", $html); // Save the HTML file $result = file_put_contents($cache_path, $html); if ($result === false) { return new WP_Error('write_failed', __('Failed to write cache file', 'wp-to-html')); } // Save metadata $this->save_metadata($post_id, $url, $cache_path); return true; } /** * Check if a post should be excluded from static generation * * @param WP_Post $post The post object * @return string|false Exclusion reason or false if not excluded */ public function should_exclude($post) { // Check if post is published if ($post->post_status !== 'publish') { return __('Post is not published', 'wp-to-html'); } // Check manual exclusion meta $manual_exclude = get_post_meta($post->ID, '_wp_to_html_exclude', true); if ($manual_exclude === '1' || $manual_exclude === 'yes') { return __('Manually excluded via post meta', 'wp-to-html'); } // Check if comments are open if (comments_open($post->ID)) { return __('Comments are enabled on this post', 'wp-to-html'); } // Check for excluded shortcodes $settings = WP_To_HTML::get_settings(); $excluded_shortcodes = array_map('trim', explode(',', $settings['excluded_shortcodes'])); foreach ($excluded_shortcodes as $shortcode) { if (!empty($shortcode) && has_shortcode($post->post_content, $shortcode)) { return sprintf(__('Contains excluded shortcode: %s', 'wp-to-html'), $shortcode); } } // Check for excluded blocks (Gutenberg) $excluded_block = $this->has_excluded_block($post, $settings); if ($excluded_block) { return sprintf(__('Contains excluded block: %s', 'wp-to-html'), $excluded_block); } // Check for WooCommerce if ($this->is_woocommerce_page($post)) { return __('WooCommerce page (dynamic content required)', 'wp-to-html'); } return false; } /** * Check if a post contains any excluded Gutenberg blocks * * @param WP_Post $post The post object * @param array $settings Plugin settings * @return string|false Block name if found, false otherwise */ private function has_excluded_block($post, $settings) { // Get excluded blocks from settings $excluded_blocks_setting = isset($settings['excluded_blocks']) ? $settings['excluded_blocks'] : ''; $excluded_blocks = array_filter(array_map('trim', explode(',', $excluded_blocks_setting))); if (empty($excluded_blocks)) { return false; } // Parse blocks from content if (!function_exists('parse_blocks')) { return false; // Gutenberg not available } $blocks = parse_blocks($post->post_content); // Recursively check all blocks return $this->find_excluded_block($blocks, $excluded_blocks); } /** * Recursively search for excluded blocks * * @param array $blocks Array of parsed blocks * @param array $excluded_blocks Array of excluded block names * @return string|false Block name if found, false otherwise */ private function find_excluded_block($blocks, $excluded_blocks) { foreach ($blocks as $block) { $block_name = isset($block['blockName']) ? $block['blockName'] : ''; // Check if this block is excluded foreach ($excluded_blocks as $excluded) { if (!empty($excluded) && !empty($block_name)) { // Support both full block names (core/form) and partial matches (wpforms) if (stripos($block_name, $excluded) !== false) { return $block_name; } } } // Check inner blocks recursively if (!empty($block['innerBlocks'])) { $found = $this->find_excluded_block($block['innerBlocks'], $excluded_blocks); if ($found) { return $found; } } } return false; } /** * Check if a post is a WooCommerce page that should be excluded * * @param WP_Post $post The post object * @return bool */ private function is_woocommerce_page($post) { // Check if WooCommerce is active if (!class_exists('WooCommerce')) { return false; } // Check if it's a product if ($post->post_type === 'product') { return true; } // Check WooCommerce core pages $woo_pages = array( wc_get_page_id('cart'), wc_get_page_id('checkout'), wc_get_page_id('myaccount'), wc_get_page_id('shop'), ); if (in_array($post->ID, $woo_pages)) { return true; } return false; } /** * Get the cache file path for a URL * * @param string $url The page URL * @return string The cache file path */ public function get_cache_path($url) { $parsed = parse_url($url); $path = isset($parsed['path']) ? $parsed['path'] : '/'; // Remove leading and trailing slashes $path = trim($path, '/'); // Handle homepage if (empty($path)) { return WP_TO_HTML_CACHE_DIR . 'index.html'; } // Create path with index.html return WP_TO_HTML_CACHE_DIR . $path . '/index.html'; } /** * Save metadata for a cached file * * @param int $post_id The post ID * @param string $url The original URL * @param string $cache_path The cache file path */ private function save_metadata($post_id, $url, $cache_path) { $metadata_dir = WP_TO_HTML_CACHE_DIR . '.metadata/'; if (!file_exists($metadata_dir)) { wp_mkdir_p($metadata_dir); } $metadata = array( 'post_id' => $post_id, 'url' => $url, 'cache_path' => $cache_path, 'generated_at' => current_time('mysql'), 'generated_timestamp' => time(), ); $metadata_file = $metadata_dir . $post_id . '.json'; file_put_contents($metadata_file, json_encode($metadata, JSON_PRETTY_PRINT)); } /** * Delete cached file for a post * * @param int $post_id The post ID * @return bool */ public function delete_cache($post_id) { $post = get_post($post_id); if (!$post) { return false; } $url = get_permalink($post_id); $cache_path = $this->get_cache_path($url); // Delete the HTML file if (file_exists($cache_path)) { unlink($cache_path); } // Delete bundled assets for this post $assets = WP_To_HTML_Assets::get_instance(); $assets->delete_post_assets($post_id); // Delete metadata $metadata_file = WP_TO_HTML_CACHE_DIR . '.metadata/' . $post_id . '.json'; if (file_exists($metadata_file)) { unlink($metadata_file); } // Try to remove empty directories $this->cleanup_empty_dirs(dirname($cache_path)); return true; } /** * Clear entire cache * * @return bool */ public function clear_all_cache() { if (!file_exists(WP_TO_HTML_CACHE_DIR)) { return true; } $this->recursive_delete(WP_TO_HTML_CACHE_DIR, true); // Recreate the cache directory structure wp_mkdir_p(WP_TO_HTML_CACHE_DIR); wp_mkdir_p(WP_TO_HTML_CACHE_DIR . '.metadata/'); wp_mkdir_p(WP_TO_HTML_CACHE_DIR . 'assets/'); // Add index.php for security file_put_contents(WP_TO_HTML_CACHE_DIR . 'index.php', "recursive_delete($path); } else { unlink($path); } } if (!$keep_root) { rmdir($dir); } } /** * Cleanup empty directories * * @param string $dir Directory path */ private function cleanup_empty_dirs($dir) { // Don't delete the main cache directory if ($dir === WP_TO_HTML_CACHE_DIR || $dir === rtrim(WP_TO_HTML_CACHE_DIR, '/')) { return; } if (is_dir($dir) && count(glob($dir . '/*')) === 0) { rmdir($dir); $this->cleanup_empty_dirs(dirname($dir)); } } /** * Get stats about the cache * * @return array Cache statistics */ public function get_cache_stats() { $stats = array( 'total_files' => 0, 'total_size' => 0, 'oldest_file' => null, 'newest_file' => null, ); if (!file_exists(WP_TO_HTML_CACHE_DIR)) { return $stats; } $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator(WP_TO_HTML_CACHE_DIR, RecursiveDirectoryIterator::SKIP_DOTS) ); foreach ($iterator as $file) { if ($file->isFile() && $file->getExtension() === 'html') { $stats['total_files']++; $stats['total_size'] += $file->getSize(); $mtime = $file->getMTime(); if ($stats['oldest_file'] === null || $mtime < $stats['oldest_file']) { $stats['oldest_file'] = $mtime; } if ($stats['newest_file'] === null || $mtime > $stats['newest_file']) { $stats['newest_file'] = $mtime; } } } return $stats; } /** * Check if a post has a cached version * * @param int $post_id The post ID * @return bool */ public function has_cache($post_id) { $post = get_post($post_id); if (!$post) { return false; } $url = get_permalink($post_id); $cache_path = $this->get_cache_path($url); return file_exists($cache_path); } }