Files
PMG-backoffice/plugins/json-export/json-export.php
Fabio Bonetti e56123f314 Initial commit
2026-02-11 14:57:12 +01:00

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
];
}
}