register_hook( "TPL_METAHEADER_OUTPUT", "BEFORE", $this, "addScripts", ); $controller->register_hook( "RENDERER_CONTENT_POSTPROCESS", "BEFORE", $this, "autoLinkChronologicalDates", ); $controller->register_hook( "RENDERER_CONTENT_POSTPROCESS", "BEFORE", $this, "appendChronologicalDayEvents", ); $controller->register_hook( "RENDERER_CONTENT_POSTPROCESS", "BEFORE", $this, "appendChronologicalDayPhotos", ); $controller->register_hook( "COMMON_PAGETPL_LOAD", "BEFORE", $this, "prefillChronologicalDayTemplate", ); $controller->register_hook( "TPL_ACT_RENDER", "BEFORE", $this, "renderVirtualChronologicalDayPage", ); $controller->register_hook( "CSS_STYLES_INCLUDED", "BEFORE", $this, "addTemporaryInputStyles", ); $controller->register_hook( "AJAX_CALL_UNKNOWN", "BEFORE", $this, "handleCalendarWidgetAjax", ); $controller->register_hook( "TOOLBAR_DEFINE", "AFTER", $this, "addToolbarButton", ); } /** * Add plugin JavaScript files in a deterministic order. * * @param Event $event * @param mixed $param * @return void */ public function addScripts(Event $event, $param) { $plugin = $this->getPluginName(); $base = DOKU_BASE . "lib/plugins/$plugin/js/"; $scripts = [ "lightbox.js", "gallery-thumbnails.js", "open-service.js", "scratchpads.js", "date-fix.js", "page-link.js", "linkfavicon.js", "calendar-widget.js", "main.js", ]; foreach ($scripts as $script) { $event->data["script"][] = [ "type" => "text/javascript", "src" => $base . $script, ]; } } /** * Serve server-rendered calendar widget HTML for month navigation. * * @param Event $event * @param mixed $param * @return void */ public function handleCalendarWidgetAjax(Event $event, $param) { if ($event->data !== 'luxtools_calendar_month') return; $event->preventDefault(); $event->stopPropagation(); global $INPUT; $year = (int)$INPUT->int('year'); $month = (int)$INPUT->int('month'); $baseNs = trim((string)$INPUT->str('base')); if ($baseNs === '') { $baseNs = 'chronological'; } if (!ChronologicalCalendarWidget::isValidMonth($year, $month)) { http_status(400); echo 'Invalid month'; return; } $html = ChronologicalCalendarWidget::render($year, $month, $baseNs); if ($html === '') { http_status(500); echo 'Calendar rendering failed'; return; } header('Content-Type: text/html; charset=utf-8'); echo $html; } /** * Include temporary global input styling via css.php so @ini_* placeholders resolve. * * @param Event $event * @param mixed $param * @return void */ public function addTemporaryInputStyles(Event $event, $param) { if (!isset($event->data['mediatype']) || $event->data['mediatype'] !== 'screen') { return; } if (!isset($event->data['files']) || !is_array($event->data['files'])) { return; } $plugin = $this->getPluginName(); $event->data['files'][DOKU_PLUGIN . $plugin . '/temp-input-colors.css'] = DOKU_BASE . 'lib/plugins/' . $plugin . '/'; } /** * Auto-link strict ISO dates (YYYY-MM-DD) in rendered XHTML text nodes. * * Excludes content inside tags where links should not be altered. * * @param Event $event * @param mixed $param * @return void */ public function autoLinkChronologicalDates(Event $event, $param) { if (!is_array($event->data)) return; $mode = (string)($event->data[0] ?? ''); if ($mode !== 'xhtml') return; $doc = $event->data[1] ?? null; if (!is_string($doc) || $doc === '') return; if (!preg_match('/\d{4}-\d{2}-\d{2}/', $doc)) return; $event->data[1] = ChronologicalDateAutoLinker::linkHtml($doc); } /** * Prefill new chronological day pages with a German date headline. * * @param Event $event * @param mixed $param * @return void */ public function prefillChronologicalDayTemplate(Event $event, $param) { if (!is_array($event->data)) return; $id = (string)($event->data['id'] ?? ''); if ($id === '') return; if (function_exists('cleanID')) { $id = (string)cleanID($id); } if ($id === '') return; if (!ChronoID::isDayId($id)) return; $template = ChronologicalDayTemplate::buildForDayId($id); if ($template === null || $template === '') return; $event->data['tpl'] = $template; $event->data['tplfile'] = ''; $event->data['doreplace'] = false; } /** * Append matching date-prefixed photos to chronological day page output. * * @param Event $event * @param mixed $param * @return void */ public function appendChronologicalDayPhotos(Event $event, $param) { if (self::$internalRenderInProgress) return; if (!is_array($event->data)) return; $mode = (string)($event->data[0] ?? ''); if ($mode !== 'xhtml') return; global $ACT; if (!is_string($ACT) || $ACT !== 'show') return; $doc = $event->data[1] ?? null; if (!is_string($doc)) return; if (str_contains($doc, 'luxtools-chronological-photos')) return; global $ID; $id = is_string($ID) ? $ID : ''; if ($id === '') return; if (function_exists('cleanID')) { $id = (string)cleanID($id); } if ($id === '') return; $parts = ChronoID::parseDayId($id); if ($parts === null) return; if (!function_exists('page_exists') || !page_exists($id)) return; $basePath = trim((string)$this->getConf('image_base_path')); if ($basePath === '') return; $dateIso = sprintf('%04d-%02d-%02d', $parts['year'], $parts['month'], $parts['day']); if (!$this->hasAnyChronologicalPhotos($dateIso)) return; $photosHtml = $this->renderChronologicalPhotosMacro($dateIso); if ($photosHtml === '') return; $event->data[1] = $doc . $photosHtml; } /** * Append local calendar events to existing chronological day pages. * * @param Event $event * @param mixed $param * @return void */ public function appendChronologicalDayEvents(Event $event, $param) { static $appendInProgress = false; if ($appendInProgress) return; if (self::$internalRenderInProgress) return; if (!is_array($event->data)) return; $mode = (string)($event->data[0] ?? ''); if ($mode !== 'xhtml') return; global $ACT; if (!is_string($ACT) || $ACT !== 'show') return; $doc = $event->data[1] ?? null; if (!is_string($doc)) return; if (str_contains($doc, 'luxtools-chronological-events')) return; global $ID; $id = is_string($ID) ? $ID : ''; if ($id === '') return; if (function_exists('cleanID')) { $id = (string)cleanID($id); } if ($id === '') return; $parts = ChronoID::parseDayId($id); if ($parts === null) return; if (!function_exists('page_exists') || !page_exists($id)) return; $dateIso = sprintf('%04d-%02d-%02d', $parts['year'], $parts['month'], $parts['day']); $appendInProgress = true; try { $eventsHtml = $this->renderChronologicalEventsHtml($dateIso); } finally { $appendInProgress = false; } if ($eventsHtml === '') return; $event->data[1] = $doc . $eventsHtml; } /** * Render chronological day photos using existing {{images>...}} syntax. * * @param string $dateIso * @return string */ protected function renderChronologicalPhotosMacro(string $dateIso): string { $syntax = $this->buildChronologicalImagesSyntax($dateIso); if ($syntax === '') return ''; if (self::$internalRenderInProgress) return ''; self::$internalRenderInProgress = true; try { $info = ['cache' => false]; $instructions = p_get_instructions($syntax); $galleryHtml = (string)p_render('xhtml', $instructions, $info); } finally { self::$internalRenderInProgress = false; } if ($galleryHtml === '') return ''; $title = (string)$this->getLang('chronological_photos_title'); if ($title === '') $title = 'Photos'; return '
",
"sample" => $this->getLang("toolbar_code_sample"),
"close" => "",
"block" => false,
];
// Date Fix: normalize selected timestamp
$event->data[] = [
"type" => "LuxtoolsDatefix",
"title" => $this->getLang("toolbar_datefix_title"),
"icon" => "../../plugins/luxtools/images/date-fix.svg",
"key" => "t",
"block" => false,
];
// Date Fix All: normalize all timestamps on page
$event->data[] = [
"type" => "LuxtoolsDatefixAll",
"title" => $this->getLang("toolbar_datefix_all_title"),
"icon" => "../../plugins/luxtools/images/date-fix-all.svg",
"block" => false,
];
}
}