Initial commit
This commit is contained in:
340
plugins/json-export/json-export.php
Normal file
340
plugins/json-export/json-export.php
Normal file
@@ -0,0 +1,340 @@
|
||||
<?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
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user