Sitemaps - Part 01
**Source path:** Spiralist/wp-content/plugins/ns12-manuscript/includes/sitemaps.php
<?php
if (!defined('ABSPATH')) {
exit;
}
/*
* NS12 SiteMap is intentionally filter-first. Other Spiralist plugins can add
* entries through providers or filters without becoming hard dependencies of
* the manuscript plugin.
*/
function spiralist_book_pages_register_sitemap_provider(string $id, callable $callback, array $args = []): void
{
global $spiralist_book_pages_sitemap_providers;
$id = sanitize_key($id);
if ($id === '') {
return;
}
if (!is_array($spiralist_book_pages_sitemap_providers ?? null)) {
$spiralist_book_pages_sitemap_providers = [];
}
$spiralist_book_pages_sitemap_providers[$id] = [
'id' => $id,
'label' => (string) ($args['label'] ?? ucwords(str_replace(['-', '_'], ' ', $id))),
'callback' => $callback,
'priority' => (int) ($args['priority'] ?? 50),
];
}
function spiralist_book_pages_register_default_sitemap_providers(): void
{
static $registered = false;
if ($registered) {
return;
}
$registered = true;
spiralist_book_pages_register_sitemap_provider(
'core',
'spiralist_book_pages_sitemap_core_entries',
['label' => 'Core site routes', 'priority' => 10]
);
spiralist_book_pages_register_sitemap_provider(
'public_pages',
'spiralist_book_pages_sitemap_public_page_entries',
['label' => 'Published WordPress pages', 'priority' => 20]
);
spiralist_book_pages_register_sitemap_provider(
'manuscript',
'spiralist_book_pages_sitemap_manuscript_entries',
['label' => 'NS12 manuscript routes', 'priority' => 30]
);
spiralist_book_pages_register_sitemap_provider(
'companion_links',
'spiralist_book_pages_sitemap_companion_link_entries',
['label' => 'Companion plugin routes', 'priority' => 40]
);
do_action('spiralist_book_pages_register_sitemap_providers');
}
function spiralist_book_pages_sitemap_providers(): array
{
global $spiralist_book_pages_sitemap_providers;
spiralist_book_pages_register_default_sitemap_providers();
$providers = is_array($spiralist_book_pages_sitemap_providers ?? null)
? $spiralist_book_pages_sitemap_providers
: [];
$providers = (array) apply_filters('spiralist_book_pages_sitemap_providers', $providers);
uasort(
$providers,
static function (array $a, array $b): int {
return ((int) ($a['priority'] ?? 50)) <=> ((int) ($b['priority'] ?? 50));
}
);
return $providers;
}
function spiralist_book_pages_sitemap_languages(): array
{
$languages = [];
if (function_exists('spiralist_get_enabled_locale_records')) {
foreach (spiralist_get_enabled_locale_records() as $record) {
$url_tag = sanitize_key((string) ($record['urlTag'] ?? ''));
$hreflang = trim(str_replace('_', '-', (string) ($record['tag'] ?? '')));
if ($url_tag === '' || $hreflang === '') {
continue;
}
$languages[$url_tag] = [
'label' => (string) ($record['nativeName'] ?? $record['language'] ?? strtoupper($url_tag)),
'hreflang' => $hreflang,
];
}
}
if (empty($languages)) {
$languages['en-us'] = [
'label' => 'English',
'hreflang' => 'en-US',
];
}
return (array) apply_filters('spiralist_book_pages_sitemap_languages', $languages);
}
function spiralist_book_pages_sitemap_xml_escape(string $value): string
{
if (function_exists('esc_xml')) {
return esc_xml($value);
}
return htmlspecialchars($value, ENT_XML1 | ENT_COMPAT, 'UTF-8');
}
function spiralist_book_pages_sitemap_timestamp(int $timestamp): string
{
return $timestamp > 0 ? gmdate('c', $timestamp) : '';
}
function spiralist_book_pages_sitemap_post_lastmod(WP_Post $post): string
{
return spiralist_book_pages_sitemap_timestamp((int) get_post_modified_time('U', true, $post));
}
function spiralist_book_pages_sitemap_file_lastmod(array $record): string
{
$path = (string) ($record['path'] ?? '');
if ($path === '' || !is_file($path)) {
return '';
}
return spiralist_book_pages_sitemap_timestamp((int) (@filemtime($path) ?: 0));
}
function spiralist_book_pages_sitemap_records_lastmod(array $records): string
{
$latest = 0;
foreach ($records as $record) {
if (!is_array($record)) {
continue;
}
$path = (string) ($record['path'] ?? '');
if ($path !== '' && is_file($path)) {
$latest = max($latest, (int) (@filemtime($path) ?: 0));
}
}
return spiralist_book_pages_sitemap_timestamp($latest);
}
function spiralist_book_pages_sitemap_language_urls(string $base_url): array
{
$urls = [];
foreach (array_keys(spiralist_book_pages_sitemap_languages()) as $language) {
$urls[$language] = function_exists('spiralist_book_pages_get_language_prefixed_url')
? spiralist_book_pages_get_language_prefixed_url($base_url, $language)
: home_url('/' . $language . '/' . ltrim((string) wp_parse_url($base_url, PHP_URL_PATH), '/'));
}
return (array) apply_filters('spiralist_book_pages_sitemap_language_urls', $urls, $base_url);
}
function spiralist_book_pages_sitemap_localized_route_url(string $path, string $locale_tag = ''): string
{
$url = home_url('/' . ltrim($path, '/'));
$locale_tag = sanitize_key($locale_tag);
if ($locale_tag === '' && function_exists('spiralist_book_pages_get_current_locale_tag')) {
$locale_tag = sanitize_key((string) spiralist_book_pages_get_current_locale_tag());
}
if ($locale_tag !== '' && function_exists('spiralist_book_pages_get_language_prefixed_url')) {
return spiralist_book_pages_get_language_prefixed_url($url, $locale_tag);
}
return $url;
}
function spiralist_book_pages_sitemap_request_locale(array $languages): string
{
$locale_tag = sanitize_key((string) get_query_var('spiralist_book_pages_sitemap_locale', ''));
if ($locale_tag === '' && function_exists('spiralist_book_pages_get_current_locale_tag')) {
$locale_tag = sanitize_key((string) spiralist_book_pages_get_current_locale_tag());
}
if ($locale_tag === '' || !isset($languages[$locale_tag])) {
$locale_tag = (string) array_key_first($languages);
}
return $locale_tag;
}
function spiralist_book_pages_sitemap_entry(
string $id,
string $group,
string $title,
string $base_url,
array $args = []
): array {
$description = trim((string) ($args['description'] ?? ''));
$entry = [
'id' => sanitize_key($id),
'group' => $group,
'title' => $title,
'description' => $description,
'urls' => spiralist_book_pages_sitemap_language_urls($base_url),
'lastmod' => (string) ($args['lastmod'] ?? ''),
'changefreq' => (string) ($args['changefreq'] ?? 'weekly'),
'priority' => (string) ($args['priority'] ?? '0.70'),
'image' => is_array($args['image'] ?? null) ? $args['image'] : [],
];
return (array) apply_filters('spiralist_book_pages_sitemap_entry', $entry, $id, $group, $title, $base_url, $args);
}
function spiralist_book_pages_sitemap_manuscript_route_url(string $type, string $slug): string
{
$slug = sanitize_title($slug);
if ($slug === '') {
return spiralist_book_pages_get_manuscript_page_url();
}
$segment = $type === 'section' ? 'section' : 'folio';
return trailingslashit(spiralist_book_pages_get_manuscript_page_url()) . $segment . '/' . rawurlencode($slug) . '/';
}
function spiralist_book_pages_sitemap_public_page_entries(): array
{
if (!function_exists('get_pages')) {
return [];
}
$excluded = function_exists('spiralist_get_publication_excluded_slugs')
? spiralist_get_publication_excluded_slugs()
: ['login', 'sign-up', 'register', 'dashboard', 'account', 'spiralist'];
$excluded = array_map('sanitize_title', array_map('strval', $excluded));
$skip_as_manuscript_routes = ['manuscript'];
$entries = [];
foreach (get_pages(['post_status' => 'publish', 'sort_column' => 'menu_order,post_title']) as $page) {
if (!$page instanceof WP_Post) {
continue;
}
$slug = sanitize_title((string) $page->post_name);
if ($slug === '' || in_array($slug, $excluded, true) || in_array($slug, $skip_as_manuscript_routes, true)) {
continue;
}
$permalink = get_permalink($page);
if (!is_string($permalink) || $permalink === '') {
continue;
}