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': $blockData['content'] = [ 'map' => $block->map()->value() ]; break; default: $blockData['content'] = $block->content()->toArray(); } $blocks[] = $blockData; } return $blocks; } /** * Parse un chapitre */ function parseChapitre($chapitre) { return [ 'id' => $chapitre->id(), 'uuid' => $chapitre->uuid()->toString(), 'template' => 'chapitre', 'title' => $chapitre->title()->value(), 'slug' => $chapitre->slug(), 'blocks' => parseBlocks($chapitre->text(), $chapitre) ]; } /** * Parse une carte */ function parseCarte($carte) { return [ 'id' => $carte->id(), 'uuid' => $carte->uuid()->toString(), 'template' => 'carte', 'title' => $carte->title()->value(), 'slug' => $carte->slug(), 'tags' => $carte->tags()->isNotEmpty() ? $carte->tags()->split() : [], 'text' => resolveImagesInHtml($carte->text()->value(), $carte) ]; } /** * Parse un geoformat */ function parseGeoformat($geoformat) { $chapitres = []; foreach ($geoformat->children()->listed() as $child) { if ($child->intendedTemplate()->name() === 'chapitre') { $chapitres[] = parseChapitre($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' => $chapitres ]; } // Construction de la réponse JSON $data = [ 'id' => $page->id(), 'uuid' => $page->uuid()->toString(), 'template' => 'recit', 'title' => $page->title()->value(), 'slug' => $page->slug(), 'author' => $page->author()->value() ?? '', 'cover' => resolveFileUrl($page->cover(), $page), 'introduction' => resolveImagesInHtml($page->introduction()->value(), $page), 'children' => [] ]; // Parser les enfants (geoformats et cartes) foreach ($page->children()->listed() as $child) { $template = $child->intendedTemplate()->name(); if ($template === 'geoformat') { $data['children'][] = parseGeoformat($child); } elseif ($template === 'carte') { $data['children'][] = parseCarte($child); } } echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);