341 lines
12 KiB
PHP
341 lines
12 KiB
PHP
<?php
|
|
namespace Grav\Plugin;
|
|
|
|
use Grav\Common\Plugin;
|
|
use RocketTheme\Toolbox\Event\Event;
|
|
use Grav\Common\Page\Page;
|
|
use Grav\Common\Grav;
|
|
|
|
/**
|
|
* JSON Export Plugin
|
|
*
|
|
* Exports content from Grav CMS to JSON format for the React media guide app
|
|
*/
|
|
class JsonExportPlugin extends Plugin
|
|
{
|
|
public static function getSubscribedEvents()
|
|
{
|
|
return [
|
|
'onPluginsInitialized' => ['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
|
|
];
|
|
}
|
|
}
|