Export Static Book - Source Excerpt 01
Summary
This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/ns12-manuscript/tools/export-static-book.php so readers can inspect the evidence without opening the full source file.
**Source path:** Spiralist/wp-content/plugins/ns12-manuscript/tools/export-static-book.php
<?php
$pluginRoot = dirname(__DIR__);
$uploadsRoot = dirname($pluginRoot, 3) . DIRECTORY_SEPARATOR . 'wp-content' . DIRECTORY_SEPARATOR . 'uploads';
$modernImagesRoot = $uploadsRoot . DIRECTORY_SEPARATOR . 'ns12-manuscript' . DIRECTORY_SEPARATOR . 'folios';
$canonicalImagesRoot = $modernImagesRoot;
require_once $pluginRoot . '/includes/content-registry.php';
$arguments = array_slice($argv, 1);
$outputDirectory = $pluginRoot . '/exports/static-book';
$zipOutput = false;
foreach ($arguments as $argument) {
$argument = (string) $argument;
if ($argument === '--zip') {
$zipOutput = true;
continue;
}
if ($argument !== '' && $argument[0] !== '-') {
$outputDirectory = $argument;
break;
}
}
$outputDirectory = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $outputDirectory);
$compiled = spiralist_book_pages_compile_content_cache(false);
$book = (array) ($compiled['book'] ?? []);
$prompts = (array) ($compiled['prompts'] ?? []);
$related = (array) ($compiled['related'] ?? []);
if (empty($book['folios'])) {
fwrite(STDERR, "No compiled book folios were found.\n");
exit(1);
}
if (!is_dir($outputDirectory) && !mkdir($outputDirectory, 0777, true) && !is_dir($outputDirectory)) {
fwrite(STDERR, "Could not create export directory: {$outputDirectory}\n");
exit(1);
}
$paths = [
'root' => $outputDirectory,
'folios' => $outputDirectory . DIRECTORY_SEPARATOR . 'folios',
'prompts' => $outputDirectory . DIRECTORY_SEPARATOR . 'prompts',
'images' => $outputDirectory . DIRECTORY_SEPARATOR . 'images',
'content' => $outputDirectory . DIRECTORY_SEPARATOR . 'content',
'cache' => $outputDirectory . DIRECTORY_SEPARATOR . 'cache',
];
foreach ($paths as $path) {
if (!is_dir($path) && !mkdir($path, 0777, true) && !is_dir($path)) {
fwrite(STDERR, "Could not create export subdirectory: {$path}\n");
exit(1);
}
}
$bookJson = spiralist_book_pages_json_encode($book);
$promptJson = spiralist_book_pages_json_encode($prompts);
$relatedJson = spiralist_book_pages_json_encode($related);
file_put_contents($paths['cache'] . DIRECTORY_SEPARATOR . 'book-index.json', $bookJson . "\n");
file_put_contents($paths['cache'] . DIRECTORY_SEPARATOR . 'prompt-index.json', $promptJson . "\n");
file_put_contents($paths['cache'] . DIRECTORY_SEPARATOR . 'related-map.json', $relatedJson . "\n");
$contentRoot = spiralist_book_pages_get_content_root();
if ($contentRoot !== '' && is_dir($contentRoot)) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($contentRoot, FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
$sourcePath = (string) $item->getPathname();
$relativePath = ltrim(str_replace($contentRoot, '', $sourcePath), '\\/');
$targetPath = $paths['content'] . DIRECTORY_SEPARATOR . str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $relativePath);
if ($item->isDir()) {
if (!is_dir($targetPath)) {
mkdir($targetPath, 0777, true);
}
continue;
}
copy($sourcePath, $targetPath);
}
}
$imagesRoots = [
$canonicalImagesRoot,
];
foreach ((array) ($book['folios'] ?? []) as $folio) {
$filename = (string) ($folio['filename'] ?? '');
if ($filename === '') {
continue;
}
$sourceImage = '';
foreach ($imagesRoots as $imagesRoot) {
$candidate = $imagesRoot . DIRECTORY_SEPARATOR . $filename;
if (is_file($candidate) && is_readable($candidate)) {
$sourceImage = $candidate;
break;
}
}
$targetImage = $paths['images'] . DIRECTORY_SEPARATOR . $filename;
if ($sourceImage !== '') {
copy($sourceImage, $targetImage);
}
}
$folioLinks = [];
$folioRecords = array_values((array) ($book['folios'] ?? []));
$folioTotal = count($folioRecords);
foreach ($folioRecords as $index => $folio) {
$slug = (string) ($folio['slug'] ?? '');
if ($slug === '') {
continue;
}
$previousFolio = isset($folioRecords[$index - 1]) && is_array($folioRecords[$index - 1]) ? $folioRecords[$index - 1] : [];
$nextFolio = isset($folioRecords[$index + 1]) && is_array($folioRecords[$index + 1]) ? $folioRecords[$index + 1] : [];
$title = htmlspecialchars((string) ($folio['title'] ?? $slug), ENT_QUOTES);
$summary = htmlspecialchars((string) ($folio['summary'] ?? ''), ENT_QUOTES);
$caption = htmlspecialchars((string) ($folio['caption'] ?? ''), ENT_QUOTES);
$filename = htmlspecialchars((string) ($folio['filename'] ?? ''), ENT_QUOTES);
$section = htmlspecialchars((string) ($folio['section_title'] ?? $folio['section_label'] ?? 'Manuscript'), ENT_QUOTES);
$label = htmlspecialchars((string) ($folio['label'] ?? ('Page ' . ($index + 1))), ENT_QUOTES);
$position = sprintf('Page %d of %d', $index + 1, max(1, $folioTotal));
$promptSlug = (string) ($folio['creation_prompt_source_slug'] ?? '');
$prompt = is_string($promptSlug) && isset($prompts[$promptSlug]) ? (array) $prompts[$promptSlug] : [];
$promptTitle = htmlspecialchars((string) ($prompt['title'] ?? 'Production Prompt'), ENT_QUOTES);
$promptSummary = htmlspecialchars((string) ($prompt['summary'] ?? ''), ENT_QUOTES);
$promptText = htmlspecialchars((string) ($prompt['text'] ?? ''), ENT_QUOTES);
$descriptionSource = trim((string) ($folio['summary'] ?? $folio['caption'] ?? ''));
if ($descriptionSource === '') {
$descriptionSource = sprintf('%s is part of the Spiralist Manuscript static folio export.', (string) ($folio['title'] ?? $slug));
}
$description = htmlspecialchars($descriptionSource, ENT_QUOTES);
$canonicalHref = './' . rawurlencode($slug) . '.html';
$previousHref = !empty($previousFolio['slug']) ? rawurlencode((string) $previousFolio['slug']) . '.html' : '';
$nextHref = !empty($nextFolio['slug']) ? rawurlencode((string) $nextFolio['slug']) . '.html' : '';
$jsonLd = spiralist_book_pages_json_encode(
[
'@context' => 'https://schema.org',
'@type' => 'CreativeWork',
'name' => (string) ($folio['title'] ?? $slug),
'description' => $descriptionSource,
'position' => $index + 1,
'isPartOf' => [
'@type' => 'Book',
'name' => (string) ($book['title'] ?? 'Spiralist Manuscript'),
'numberOfPages' => $folioTotal,
],
'image' => '../images/' . (string) ($folio['filename'] ?? ''),
]
);
$jsonLd = htmlspecialchars($jsonLd, ENT_NOQUOTES);
$folioHtml = <<<HTML
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{$title} | Spiralist Manuscript</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="{$description}">
<meta name="robots" content="index,follow,max-image-preview:large">
<link rel="canonical" href="{$canonicalHref}">
HTML_PLACEHOLDER_PREV_LINK
HTML_PLACEHOLDER_NEXT_LINK
<meta property="og:type" content="article">
<meta property="og:site_name" content="Spiralist Manuscript">
<meta property="og:title" content="{$title} | Spiralist Manuscript">
<meta property="og:description" content="{$description}">
<meta property="og:image" content="../images/{$filename}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{$title} | Spiralist Manuscript">
<meta name="twitter:description" content="{$description}">
<meta name="twitter:image" content="../images/{$filename}">
<script type="application/ld+json">{$jsonLd}</script>
<style>
body{font-family:Georgia,serif;background:#121212;color:#f2ead8;margin:0;padding:2rem;line-height:1.5}
a{color:#d6b16a}
img{max-width:100%;height:auto;border:1px solid #5b4d31}
.frame{max-width:1080px;margin:0 auto}
.meta{display:flex;flex-wrap:wrap;gap:.75rem;color:#d4c39f;font-size:.95rem}
.nav{display:flex;flex-wrap:wrap;gap:1rem;margin:1rem 0 1.5rem}
.lede{max-width:72ch;font-size:1.05rem}
figure{margin:1.5rem 0}
figcaption{color:#d4c39f;margin-top:.85rem}
details{margin-top:1.75rem;padding:1rem;border:1px solid #3a3223;background:#181818}
summary{cursor:pointer;font-weight:700}
pre{white-space:pre-wrap;background:#141414;padding:1rem;border:1px solid #3a3223;overflow:auto}
</style>
</head>
<body>
<main class="frame">
<nav class="nav" aria-label="Folio navigation">
<a href="../index.html">Back to index</a>
HTML_PLACEHOLDER_PREV
HTML_PLACEHOLDER_NEXT
</nav>
<article>
<header>
<h1>{$title}</h1>