generate_sitemap(); // Cache for 12 hours set_transient('shadowsync_seo_sitemap_cache', $xml, 12 * HOUR_IN_SECONDS); header('Content-Type: application/xml; charset=utf-8'); // See note above: every dynamic value is escaped in build_url(). echo $xml; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped exit; } private function generate_sitemap() { $posts = get_posts([ 'numberposts' => -1, 'post_status' => 'publish', 'post_type' => ['post', 'page'], ]); $xml = '' . "\n"; $xml .= '' . "\n"; // Homepage $xml .= $this->build_url(home_url('/'), '1.0', 'daily'); foreach ($posts as $post) { $url = get_permalink($post->ID); $modified = get_post_modified_time('c', true, $post->ID); $priority = $post->post_type === 'page' ? '0.8' : '0.6'; $xml .= $this->build_url($url, $priority, 'weekly', $modified); } // Add public taxonomy archives $categories = get_categories(['hide_empty' => true]); foreach ($categories as $cat) { $xml .= $this->build_url(get_category_link($cat->term_id), '0.5', 'weekly'); } $xml .= ''; return $xml; } private function build_url($url, $priority = '0.5', $changefreq = 'weekly', $lastmod = '') { $out = " \n"; $out .= " " . esc_url($url) . "\n"; if ($lastmod) { $out .= " " . esc_html($lastmod) . "\n"; } $out .= " " . esc_html($changefreq) . "\n"; $out .= " " . esc_html($priority) . "\n"; $out .= " \n"; return $out; } public function clear_sitemap_cache() { delete_transient('shadowsync_seo_sitemap_cache'); } public function flush_rewrite_rules() { check_ajax_referer( 'shadowsync_seo_scan_nonce', 'nonce' ); ShadowSync_SEO_Security::require_admin(); flush_rewrite_rules(); wp_send_json_success(['message' => 'Sitemap rewrite rules flushed.']); } }