All checks were successful
Deploy / Build and Deploy to Production (push) Successful in 21s
Content .txt files: - narrative.txt (×4): Introduction → Intro - chapter.txt (×3): Text → Body - geoformat.txt: Intro → Text - map.txt: move Text content into Intro field narrative.json.php: - parseChapter: $chapter->text() → $chapter->body() - parseMarker: $marker->text() → $marker->body() - parseMap: $map->text() → $map->intro() - narrative root: $page->introduction() → $page->intro() Also commits Kirby content directory rename (1_cohesion-des-mondes → cohesion-des-mondes) and new _drafts content. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
235 lines
6.9 KiB
PHP
235 lines
6.9 KiB
PHP
<?php
|
|
/**
|
|
* JSON template to expose narrative data
|
|
* Accessible via /project/narrative.json or /project/narrative?format=json
|
|
*/
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
/**
|
|
* Résout les références de fichiers Kirby en URLs absolues
|
|
*/
|
|
function resolveFileUrl($field, $page) {
|
|
if (!$field || $field->isEmpty()) {
|
|
return null;
|
|
}
|
|
|
|
$file = $field->toFile();
|
|
if ($file) {
|
|
return $file->url();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Résout les images dans le contenu HTML
|
|
*/
|
|
function resolveImagesInHtml($html, $page) {
|
|
if (!$html) return $html;
|
|
|
|
// Remplacer les références file:// par les URLs réelles
|
|
return preg_replace_callback(
|
|
'/file:\/\/([a-z0-9]+)/i',
|
|
function($matches) use ($page) {
|
|
$uuid = $matches[1];
|
|
$file = $page->file("file://{$uuid}");
|
|
if (!$file) {
|
|
// Chercher dans les fichiers du site
|
|
$file = site()->file("file://{$uuid}");
|
|
}
|
|
return $file ? $file->url() : $matches[0];
|
|
},
|
|
$html
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Parse les blocks Kirby en structure JSON
|
|
*/
|
|
function parseBlocks($blocksField, $page) {
|
|
if (!$blocksField || $blocksField->isEmpty()) {
|
|
return [];
|
|
}
|
|
|
|
$blocks = [];
|
|
foreach ($blocksField->toBlocks() as $block) {
|
|
$blockData = [
|
|
'id' => $block->id(),
|
|
'type' => $block->type(),
|
|
'isHidden' => $block->isHidden(),
|
|
];
|
|
|
|
switch ($block->type()) {
|
|
case 'text':
|
|
$blockData['content'] = [
|
|
'text' => resolveImagesInHtml($block->text()->value(), $page)
|
|
];
|
|
break;
|
|
|
|
case 'heading':
|
|
$blockData['content'] = [
|
|
'level' => $block->level()->value() ?? 'h2',
|
|
'text' => $block->text()->value()
|
|
];
|
|
break;
|
|
|
|
case 'image':
|
|
$image = $block->image()->toFile();
|
|
$blockData['content'] = [
|
|
'url' => $image ? $image->url() : null,
|
|
'alt' => $block->alt()->value() ?? '',
|
|
'caption' => $block->caption()->value() ?? '',
|
|
'width' => $block->width()->value() ?? '100%',
|
|
'position' => $block->position()->value() ?? 'auto'
|
|
];
|
|
break;
|
|
|
|
case 'list':
|
|
$blockData['content'] = [
|
|
'text' => $block->text()->value()
|
|
];
|
|
break;
|
|
|
|
case 'quote':
|
|
$blockData['content'] = [
|
|
'text' => $block->text()->value(),
|
|
'citation' => $block->citation()->value() ?? ''
|
|
];
|
|
break;
|
|
|
|
case 'video':
|
|
$blockData['content'] = [
|
|
'url' => $block->url()->value(),
|
|
'caption' => $block->caption()->value() ?? ''
|
|
];
|
|
break;
|
|
|
|
case 'map':
|
|
$mapPage = $block->map()->toPages()->first();
|
|
if ($mapPage) {
|
|
$blockData['content'] = parseMap($mapPage);
|
|
} else {
|
|
$blockData['content'] = [
|
|
'map' => $block->map()->value()
|
|
];
|
|
}
|
|
break;
|
|
|
|
default:
|
|
$blockData['content'] = $block->content()->toArray();
|
|
}
|
|
|
|
$blocks[] = $blockData;
|
|
}
|
|
|
|
return $blocks;
|
|
}
|
|
|
|
/**
|
|
* Parse a chapter
|
|
*/
|
|
function parseChapter($chapter) {
|
|
return [
|
|
'id' => $chapter->id(),
|
|
'uuid' => $chapter->uuid()->toString(),
|
|
'template' => 'chapter',
|
|
'title' => $chapter->title()->value(),
|
|
'slug' => $chapter->slug(),
|
|
'blocks' => parseBlocks($chapter->body(), $chapter)
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Parse a marker
|
|
*/
|
|
function parseMarker($marker) {
|
|
return [
|
|
'title' => $marker->title()->value(),
|
|
'cover' => resolveFileUrl($marker->cover(), $marker),
|
|
'icon' => $marker->markerIcon()->toFile() ? $marker->markerIcon()->toFile()->url() : null,
|
|
'iconSize' => $marker->markerIconSize()->value() ?? 40,
|
|
'blocks' => parseBlocks($marker->body(), $marker)
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Parse a map
|
|
*/
|
|
function parseMap($map) {
|
|
$markers = [];
|
|
foreach ($map->children()->listed() as $child) {
|
|
if ($child->intendedTemplate()->name() === 'marker') {
|
|
$markers[] = parseMarker($child);
|
|
}
|
|
}
|
|
$staticImage = $map->file('map-static.png');
|
|
return [
|
|
'id' => $map->id(),
|
|
'uuid' => $map->uuid()->toString(),
|
|
'template' => 'carte',
|
|
'title' => $map->title()->value(),
|
|
'slug' => $map->slug(),
|
|
'tags' => $map->tags()->isNotEmpty() ? $map->tags()->split() : [],
|
|
'image' => $staticImage ? $staticImage->url() : null,
|
|
'intro' => resolveImagesInHtml($map->intro()->value(), $map),
|
|
'markers' => $markers
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Parse un geoformat
|
|
*/
|
|
function parseGeoformat($geoformat) {
|
|
$children = [];
|
|
foreach ($geoformat->children()->listed() as $child) {
|
|
$template = $child->intendedTemplate()->name();
|
|
if ($template === 'chapter') {
|
|
$children[] = parseChapter($child);
|
|
} elseif ($template === 'map') {
|
|
$children[] = parseMap($child);
|
|
}
|
|
}
|
|
|
|
return [
|
|
'id' => $geoformat->id(),
|
|
'uuid' => $geoformat->uuid()->toString(),
|
|
'template' => 'geoformat',
|
|
'title' => $geoformat->title()->value(),
|
|
'slug' => $geoformat->slug(),
|
|
'subtitle' => $geoformat->subtitle()->value() ?? '',
|
|
'tags' => $geoformat->tags()->isNotEmpty() ? $geoformat->tags()->split() : [],
|
|
'cover' => resolveFileUrl($geoformat->cover(), $geoformat),
|
|
'text' => resolveImagesInHtml($geoformat->text()->value(), $geoformat),
|
|
'children' => $children
|
|
];
|
|
}
|
|
|
|
// Build JSON response
|
|
$data = [
|
|
'id' => $page->id(),
|
|
'uuid' => $page->uuid()->toString(),
|
|
'template' => 'narrative',
|
|
'title' => $page->title()->value(),
|
|
'slug' => $page->slug(),
|
|
'author' => $page->author()->value() ?? '',
|
|
'cover' => resolveFileUrl($page->cover(), $page),
|
|
'introduction' => resolveImagesInHtml($page->intro()->value(), $page),
|
|
'customCss' => $page->customCss()->value() ?? '',
|
|
'modified' => $page->modified(),
|
|
'modifiedFormatted' => $page->modified('d/m/Y H:i'),
|
|
'children' => []
|
|
];
|
|
|
|
// Parse children (geoformats and maps)
|
|
foreach ($page->children()->listed() as $child) {
|
|
$template = $child->intendedTemplate()->name();
|
|
|
|
if ($template === 'geoformat') {
|
|
$data['children'][] = parseGeoformat($child);
|
|
} elseif ($template === 'map') {
|
|
$data['children'][] = parseMap($child);
|
|
}
|
|
}
|
|
|
|
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|