['onPluginsInitialized', 0] ]; } public function onPluginsInitialized() { // Check if we're in admin context if ($this->isAdmin()) { $this->enable([ 'onAdminTaskExecute' => ['onAdminTaskExecute', 0] ]); } // Enable API endpoint $this->enable([ 'onPagesInitialized' => ['onPagesInitialized', 0] ]); } public function onPagesInitialized() { $uri = $this->grav['uri']; // API endpoints if (strpos($uri->path(), '/api/') === 0) { $this->handleApiRequest($uri); } } private function handleApiRequest($uri) { $path = $uri->path(); $lang = $uri->param('lang') ?? 'en'; header('Content-Type: application/json'); header('Access-Control-Allow-Origin: *'); // GET /api/stations - List all stations if ($path === '/api/stations') { echo json_encode($this->getStationsList($lang), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); exit; } // GET /api/station/{id} - Get single station if (preg_match('#^/api/station/([a-z0-9-]+)$#', $path, $matches)) { $stationId = $matches[1]; $station = $this->getStation($stationId, $lang); if ($station) { echo json_encode($station, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); } else { http_response_code(404); echo json_encode(['error' => 'Station not found']); } exit; } // GET /api/translations - Get UI translations if ($path === '/api/translations') { echo json_encode($this->getTranslations($lang), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); exit; } // GET /api/export - Export all content to JSON files if ($path === '/api/export') { $result = $this->exportAllToJson(); echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); exit; } } private function getStationsList($lang) { $pages = $this->grav['pages']; $stations = []; foreach ($pages->all() as $page) { if ($page->template() === 'station' && $page->header()->active) { $header = $page->header(); $stations[] = [ 'id' => $header->station_id ?? $page->slug(), 'type' => $header->station_type ?? 'exhibit', 'title' => $this->getTranslatedField($header->title_translations ?? [], $lang), 'description' => $this->getTranslatedField($header->description_translations ?? [], $lang), 'order' => $header->order ?? 0, 'active' => $header->active ?? true ]; } } // Sort by order usort($stations, fn($a, $b) => $a['order'] <=> $b['order']); return $stations; } private function getStation($stationId, $lang) { $pages = $this->grav['pages']; foreach ($pages->all() as $page) { if ($page->template() === 'station') { $header = $page->header(); $id = $header->station_id ?? $page->slug(); if ($id === $stationId) { return $this->formatStationForLang($page, $lang); } } } return null; } private function formatStationForLang($page, $lang) { $header = $page->header(); $station = [ 'id' => $header->station_id ?? $page->slug(), 'type' => $header->station_type ?? 'exhibit', 'language' => $lang, 'title' => $this->getTranslatedField($header->title_translations ?? [], $lang), 'description' => $this->getTranslatedField($header->description_translations ?? [], $lang), 'components' => [] ]; // Process components if (isset($header->components) && is_array($header->components)) { foreach ($header->components as $component) { $station['components'][] = $this->formatComponent($component, $lang); } } return $station; } private function formatComponent($component, $lang) { $formatted = [ 'id' => $component['id'] ?? uniqid(), 'type' => $component['type'] ?? 'text' ]; switch ($component['type']) { case 'heading': $formatted['level'] = $component['level'] ?? 'h2'; $formatted['content'] = $this->getTranslatedField($component['content'] ?? [], $lang); break; case 'text': $formatted['content'] = $this->getTranslatedField($component['content'] ?? [], $lang); break; case 'video': $formatted['src'] = $this->getTranslatedField($component['src'] ?? [], $lang); $formatted['poster'] = $component['poster'] ?? null; $formatted['caption'] = $this->getTranslatedField($component['caption'] ?? [], $lang); break; case 'audio': $formatted['src'] = $this->getTranslatedField($component['src'] ?? [], $lang); $formatted['caption'] = $this->getTranslatedField($component['caption'] ?? [], $lang); break; case 'tabs': $formatted['tabs'] = []; if (isset($component['tabs']) && is_array($component['tabs'])) { foreach ($component['tabs'] as $tab) { $formatted['tabs'][] = [ 'title' => $this->getTranslatedField($tab['title'] ?? [], $lang), 'content' => $this->getTranslatedField($tab['content'] ?? [], $lang) ]; } } break; case 'slideshow': $formatted['slides'] = []; if (isset($component['slides']) && is_array($component['slides'])) { foreach ($component['slides'] as $slide) { $formatted['slides'][] = [ 'image' => $slide['image'] ?? '', 'caption' => $this->getTranslatedField($slide['caption'] ?? [], $lang) ]; } } break; } return $formatted; } private function getTranslatedField($field, $lang, $fallback = 'en') { if (is_string($field)) { return $field; } if (is_array($field)) { return $field[$lang] ?? $field[$fallback] ?? reset($field) ?: ''; } return ''; } private function getTranslations($lang) { // UI translations - these should be stored in config or separate files $translations = [ 'en' => [ 'appTitle' => 'EU Media Guide', 'scanQR' => 'Scan QR', 'selectLanguage' => 'Select Language', 'contentLibrary' => 'Content Library', 'noContent' => 'Scan a QR code or select content from the menu', 'errorLoading' => 'Failed to load content. Please try again.', 'playAudio' => 'Play Audio', 'pauseAudio' => 'Pause Audio', 'next' => 'Next', 'previous' => 'Previous', 'closeMenu' => 'Close Menu', 'openMenu' => 'Open Menu', 'loading' => 'Loading...' ], 'fr' => [ 'appTitle' => 'Guide Média UE', 'scanQR' => 'Scanner QR', 'selectLanguage' => 'Choisir la langue', 'contentLibrary' => 'Bibliothèque de contenu', 'noContent' => 'Scannez un code QR ou sélectionnez du contenu dans le menu', 'errorLoading' => 'Échec du chargement. Veuillez réessayer.', 'playAudio' => 'Lire l\'audio', 'pauseAudio' => 'Pause audio', 'next' => 'Suivant', 'previous' => 'Précédent', 'closeMenu' => 'Fermer le menu', 'openMenu' => 'Ouvrir le menu', 'loading' => 'Chargement...' ], 'de' => [ 'appTitle' => 'EU Media Guide', 'scanQR' => 'QR scannen', 'selectLanguage' => 'Sprache wählen', 'contentLibrary' => 'Inhaltsbibliothek', 'noContent' => 'Scannen Sie einen QR-Code oder wählen Sie Inhalte aus dem Menü', 'errorLoading' => 'Laden fehlgeschlagen. Bitte versuchen Sie es erneut.', 'playAudio' => 'Audio abspielen', 'pauseAudio' => 'Audio pausieren', 'next' => 'Weiter', 'previous' => 'Zurück', 'closeMenu' => 'Menü schließen', 'openMenu' => 'Menü öffnen', 'loading' => 'Laden...' ], 'nl' => [ 'appTitle' => 'EU Media Gids', 'scanQR' => 'QR scannen', 'selectLanguage' => 'Taal selecteren', 'contentLibrary' => 'Inhoudsbibliotheek', 'noContent' => 'Scan een QR-code of selecteer inhoud uit het menu', 'errorLoading' => 'Laden mislukt. Probeer het opnieuw.', 'playAudio' => 'Audio afspelen', 'pauseAudio' => 'Audio pauzeren', 'next' => 'Volgende', 'previous' => 'Vorige', 'closeMenu' => 'Menu sluiten', 'openMenu' => 'Menu openen', 'loading' => 'Laden...' ] // Add more languages as needed ]; return $translations[$lang] ?? $translations['en']; } private function exportAllToJson() { $exportPath = $this->grav['locator']->findResource('user://') . '/export/'; if (!is_dir($exportPath)) { mkdir($exportPath, 0755, true); } $languages = $this->grav['config']->get('system.languages.supported', ['en']); $exported = []; foreach ($languages as $lang) { // Export stations $stations = $this->getStationsList($lang); file_put_contents( $exportPath . "stations.{$lang}.json", json_encode($stations, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) ); // Export each station content foreach ($stations as $station) { $content = $this->getStation($station['id'], $lang); if ($content) { file_put_contents( $exportPath . "{$station['id']}.{$lang}.json", json_encode($content, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) ); } } // Export translations $translations = $this->getTranslations($lang); file_put_contents( $exportPath . "translations.{$lang}.json", json_encode($translations, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) ); $exported[] = $lang; } return [ 'success' => true, 'exported_languages' => $exported, 'path' => $exportPath ]; } }