Skip to content
wiki.fftac.org

Sitemaps - Part 01

Back to Sitemaps

**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;
        }