diff --git a/action.php b/action.php
index b864ac4..f6058fb 100644
--- a/action.php
+++ b/action.php
@@ -15,7 +15,7 @@ use dokuwiki\plugin\luxtools\ChronologicalDateAutoLinker;
use dokuwiki\plugin\luxtools\ChronologicalDayTemplate;
use dokuwiki\plugin\luxtools\IcsWriter;
use dokuwiki\plugin\luxtools\MenuItem\InvalidateCache;
-require_once(__DIR__ . '/autoload.php');
+require_once __DIR__ . "/autoload.php";
/**
* luxtools action plugin: register JS assets.
@@ -34,6 +34,12 @@ class action_plugin_luxtools extends ActionPlugin
$this,
"addScripts",
);
+ $controller->register_hook(
+ "CSS_STYLES_INCLUDED",
+ "BEFORE",
+ $this,
+ "addDialogCss",
+ );
$controller->register_hook(
"DOKUWIKI_STARTED",
"AFTER",
@@ -140,6 +146,7 @@ class action_plugin_luxtools extends ActionPlugin
"page-link.js",
"linkfavicon.js",
"calendar-widget.js",
+ "dialog.js",
"event-popup.js",
"movie-import.js",
"main.js",
@@ -153,6 +160,27 @@ class action_plugin_luxtools extends ActionPlugin
}
}
+ /**
+ * Register dialog.css with DokuWiki's CSS dispatcher so that
+ * @ini_* variables are resolved and the file is properly cached.
+ */
+ public function addDialogCss(Event $event, $param)
+ {
+ if (($event->data["mediatype"] ?? "") !== "screen") {
+ return;
+ }
+
+ $plugin = $this->getPluginName();
+ $file = DOKU_PLUGIN . "$plugin/dialog.css";
+ $location = DOKU_BASE . "lib/plugins/$plugin/";
+
+ if (!file_exists($file)) {
+ return;
+ }
+
+ $event->data["files"][$file] = $location;
+ }
+
/**
* Pass plugin data to client-side JavaScript via JSINFO.
*
@@ -167,7 +195,9 @@ class action_plugin_luxtools extends ActionPlugin
{
// Intentional: the key is exposed to the browser for direct OMDb lookups.
global $JSINFO;
- $JSINFO['luxtools_omdb_apikey'] = (string)$this->getConf('omdb_apikey');
+ $JSINFO["luxtools_omdb_apikey"] = (string) $this->getConf(
+ "omdb_apikey",
+ );
}
/**
@@ -179,25 +209,31 @@ class action_plugin_luxtools extends ActionPlugin
*/
public function handleCalendarWidgetAjax(Event $event, $param)
{
- if ($event->data !== 'luxtools_calendar_month') return;
+ 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';
+ $year = (int) $INPUT->int("year");
+ $month = (int) $INPUT->int("month");
+ $baseNs = trim((string) $INPUT->str("base"));
+ if ($baseNs === "") {
+ $baseNs = "chronological";
}
- $size = ChronologicalCalendarWidget::normalizeSize((string)$INPUT->str('size'));
- $showTimes = ChronologicalCalendarWidget::normalizeShowTimes($INPUT->str('show_times'));
+ $size = ChronologicalCalendarWidget::normalizeSize(
+ (string) $INPUT->str("size"),
+ );
+ $showTimes = ChronologicalCalendarWidget::normalizeShowTimes(
+ $INPUT->str("show_times"),
+ );
if (!ChronologicalCalendarWidget::isValidMonth($year, $month)) {
http_status(400);
- echo 'Invalid month';
+ echo "Invalid month";
return;
}
@@ -207,36 +243,52 @@ class action_plugin_luxtools extends ActionPlugin
$widgetSlots = CalendarSlot::filterWidgetVisible($slots);
$indicators = [];
$dayEvents = [];
- if ($size === 'large') {
- $widgetData = CalendarService::monthWidgetData($widgetSlots, $year, $month);
- $indicators = $widgetData['indicators'];
- $dayEvents = $widgetData['events'];
+ if ($size === "large") {
+ $widgetData = CalendarService::monthWidgetData(
+ $widgetSlots,
+ $year,
+ $month,
+ );
+ $indicators = $widgetData["indicators"];
+ $dayEvents = $widgetData["events"];
} else {
- $indicators = CalendarService::monthIndicators($widgetSlots, $year, $month);
+ $indicators = CalendarService::monthIndicators(
+ $widgetSlots,
+ $year,
+ $month,
+ );
}
$slotColors = [];
$slotDisplays = [];
foreach ($widgetSlots as $slot) {
$color = $slot->getColor();
- if ($color !== '') {
+ if ($color !== "") {
$slotColors[$slot->getKey()] = $color;
}
$slotDisplays[$slot->getKey()] = $slot->getDisplay();
}
- $html = ChronologicalCalendarWidget::render($year, $month, $baseNs, $indicators, $slotColors, $slotDisplays, [
- 'size' => $size,
- 'showTimes' => $showTimes,
- 'dayEvents' => $dayEvents,
- ]);
- if ($html === '') {
+ $html = ChronologicalCalendarWidget::render(
+ $year,
+ $month,
+ $baseNs,
+ $indicators,
+ $slotColors,
+ $slotDisplays,
+ [
+ "size" => $size,
+ "showTimes" => $showTimes,
+ "dayEvents" => $dayEvents,
+ ],
+ );
+ if ($html === "") {
http_status(500);
- echo 'Calendar rendering failed';
+ echo "Calendar rendering failed";
return;
}
- header('Content-Type: text/html; charset=utf-8');
+ header("Content-Type: text/html; charset=utf-8");
echo $html;
}
@@ -251,14 +303,22 @@ class action_plugin_luxtools extends ActionPlugin
*/
public function autoLinkChronologicalDates(Event $event, $param)
{
- if (!is_array($event->data)) return;
+ if (!is_array($event->data)) {
+ return;
+ }
- $mode = (string)($event->data[0] ?? '');
- if ($mode !== 'xhtml') 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;
+ if (!is_string($doc) || $doc === "") {
+ return;
+ }
+ if (!preg_match("/\d{4}-\d{2}-\d{2}/", $doc)) {
+ return;
+ }
$event->data[1] = ChronologicalDateAutoLinker::linkHtml($doc);
}
@@ -272,23 +332,33 @@ class action_plugin_luxtools extends ActionPlugin
*/
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 (!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;
}
- if ($id === '') return;
- if (!ChronoID::isDayId($id)) return;
$template = ChronologicalDayTemplate::buildForDayId($id);
- if ($template === null || $template === '') return;
+ if ($template === null || $template === "") {
+ return;
+ }
- $event->data['tpl'] = $template;
- $event->data['tplfile'] = '';
- $event->data['doreplace'] = false;
+ $event->data["tpl"] = $template;
+ $event->data["tplfile"] = "";
+ $event->data["doreplace"] = false;
}
/**
@@ -300,40 +370,71 @@ class action_plugin_luxtools extends ActionPlugin
*/
public function appendChronologicalDayPhotos(Event $event, $param)
{
- if (self::$internalRenderInProgress) return;
- if (!is_array($event->data)) return;
+ if (self::$internalRenderInProgress) {
+ return;
+ }
+ if (!is_array($event->data)) {
+ return;
+ }
- $mode = (string)($event->data[0] ?? '');
- if ($mode !== 'xhtml') return;
+ $mode = (string) ($event->data[0] ?? "");
+ if ($mode !== "xhtml") {
+ return;
+ }
global $ACT;
- if (!is_string($ACT) || $ACT !== 'show') return;
+ 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;
+ 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);
+ $id = is_string($ID) ? $ID : "";
+ if ($id === "") {
+ return;
+ }
+ if (function_exists("cleanID")) {
+ $id = (string) cleanID($id);
+ }
+ if ($id === "") {
+ return;
}
- if ($id === '') return;
$parts = ChronoID::parseDayId($id);
- if ($parts === null) return;
+ if ($parts === null) {
+ return;
+ }
- if (!function_exists('page_exists') || !page_exists($id)) return;
+ if (!function_exists("page_exists") || !page_exists($id)) {
+ return;
+ }
- $basePath = trim((string)$this->getConf('image_base_path'));
- if ($basePath === '') 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;
+ $dateIso = sprintf(
+ "%04d-%02d-%02d",
+ $parts["year"],
+ $parts["month"],
+ $parts["day"],
+ );
+ if (!$this->hasAnyChronologicalPhotos($dateIso)) {
+ return;
+ }
$photosHtml = $this->renderChronologicalPhotosMacro($dateIso);
- if ($photosHtml === '') return;
+ if ($photosHtml === "") {
+ return;
+ }
$event->data[1] = $doc . $photosHtml;
}
@@ -348,41 +449,70 @@ class action_plugin_luxtools extends ActionPlugin
public function appendChronologicalDayEvents(Event $event, $param)
{
static $appendInProgress = false;
- if ($appendInProgress) return;
- if (self::$internalRenderInProgress) return;
+ if ($appendInProgress) {
+ return;
+ }
+ if (self::$internalRenderInProgress) {
+ return;
+ }
- if (!is_array($event->data)) return;
+ if (!is_array($event->data)) {
+ return;
+ }
- $mode = (string)($event->data[0] ?? '');
- if ($mode !== 'xhtml') return;
+ $mode = (string) ($event->data[0] ?? "");
+ if ($mode !== "xhtml") {
+ return;
+ }
global $ACT;
- if (!is_string($ACT) || $ACT !== 'show') return;
+ 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;
+ 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);
+ $id = is_string($ID) ? $ID : "";
+ if ($id === "") {
+ return;
+ }
+ if (function_exists("cleanID")) {
+ $id = (string) cleanID($id);
+ }
+ if ($id === "") {
+ return;
}
- if ($id === '') return;
$parts = ChronoID::parseDayId($id);
- if ($parts === null) return;
- if (!function_exists('page_exists') || !page_exists($id)) return;
+ if ($parts === null) {
+ return;
+ }
+ if (!function_exists("page_exists") || !page_exists($id)) {
+ return;
+ }
- $dateIso = sprintf('%04d-%02d-%02d', $parts['year'], $parts['month'], $parts['day']);
+ $dateIso = sprintf(
+ "%04d-%02d-%02d",
+ $parts["year"],
+ $parts["month"],
+ $parts["day"],
+ );
$appendInProgress = true;
try {
$eventsHtml = $this->renderChronologicalEventsHtml($dateIso);
} finally {
$appendInProgress = false;
}
- if ($eventsHtml === '') return;
+ if ($eventsHtml === "") {
+ return;
+ }
$event->data[1] = $doc . $eventsHtml;
}
@@ -396,28 +526,38 @@ class action_plugin_luxtools extends ActionPlugin
protected function renderChronologicalPhotosMacro(string $dateIso): string
{
$syntax = $this->buildChronologicalImagesSyntax($dateIso);
- if ($syntax === '') return '';
+ if ($syntax === "") {
+ return "";
+ }
- if (self::$internalRenderInProgress) return '';
+ if (self::$internalRenderInProgress) {
+ return "";
+ }
self::$internalRenderInProgress = true;
try {
- $info = ['cache' => false];
+ $info = ["cache" => false];
$instructions = p_get_instructions($syntax);
- $galleryHtml = (string)p_render('xhtml', $instructions, $info);
+ $galleryHtml = (string) p_render("xhtml", $instructions, $info);
} finally {
self::$internalRenderInProgress = false;
}
- if ($galleryHtml === '') return '';
+ if ($galleryHtml === "") {
+ return "";
+ }
- $title = (string)$this->getLang('chronological_photos_title');
- if ($title === '') $title = 'Photos';
+ $title = (string) $this->getLang("chronological_photos_title");
+ if ($title === "") {
+ $title = "Photos";
+ }
- return '
'
- . '
' . hsc($title) . '
'
- . $galleryHtml
- . '';
+ return '' .
+ "
" .
+ hsc($title) .
+ "
" .
+ $galleryHtml .
+ "";
}
/**
@@ -428,16 +568,21 @@ class action_plugin_luxtools extends ActionPlugin
*/
protected function buildChronologicalImagesSyntax(string $dateIso): string
{
- $basePath = trim((string)$this->getConf('image_base_path'));
- if ($basePath === '') return '';
+ $basePath = trim((string) $this->getConf("image_base_path"));
+ if ($basePath === "") {
+ return "";
+ }
$base = \dokuwiki\plugin\luxtools\Path::cleanPath($basePath);
- if (!is_dir($base) || !is_readable($base)) return '';
+ if (!is_dir($base) || !is_readable($base)) {
+ return "";
+ }
- $yearDir = rtrim($base, '/') . '/' . substr($dateIso, 0, 4) . '/';
- $targetDir = (is_dir($yearDir) && is_readable($yearDir)) ? $yearDir : $base;
+ $yearDir = rtrim($base, "/") . "/" . substr($dateIso, 0, 4) . "/";
+ $targetDir =
+ is_dir($yearDir) && is_readable($yearDir) ? $yearDir : $base;
- return '{{images>' . $targetDir . $dateIso . '*&recursive=0}}';
+ return "{{images>" . $targetDir . $dateIso . "*&recursive=0}}";
}
/**
@@ -451,54 +596,80 @@ class action_plugin_luxtools extends ActionPlugin
*/
public function renderVirtualChronologicalDayPage(Event $event, $param)
{
- if (!is_string($event->data) || $event->data !== 'show') return;
+ if (!is_string($event->data) || $event->data !== "show") {
+ return;
+ }
global $ID;
- $id = is_string($ID) ? $ID : '';
- if ($id === '') return;
- if (function_exists('cleanID')) {
- $id = (string)cleanID($id);
+ $id = is_string($ID) ? $ID : "";
+ if ($id === "") {
+ return;
+ }
+ if (function_exists("cleanID")) {
+ $id = (string) cleanID($id);
+ }
+ if ($id === "") {
+ return;
}
- if ($id === '') return;
- if (!ChronoID::isDayId($id)) return;
+ if (!ChronoID::isDayId($id)) {
+ return;
+ }
$this->sendNoStoreHeaders();
- if (function_exists('page_exists') && page_exists($id)) return;
+ if (function_exists("page_exists") && page_exists($id)) {
+ return;
+ }
- $wikiText = ChronologicalDayTemplate::buildForDayId($id) ?? '';
- if ($wikiText === '') return;
+ $wikiText = ChronologicalDayTemplate::buildForDayId($id) ?? "";
+ if ($wikiText === "") {
+ return;
+ }
$parts = ChronoID::parseDayId($id);
- $extraHtml = '';
+ $extraHtml = "";
if ($parts !== null) {
- $dateIso = sprintf('%04d-%02d-%02d', $parts['year'], $parts['month'], $parts['day']);
+ $dateIso = sprintf(
+ "%04d-%02d-%02d",
+ $parts["year"],
+ $parts["month"],
+ $parts["day"],
+ );
$eventsHtml = $this->renderChronologicalEventsHtml($dateIso);
- if ($eventsHtml !== '') {
+ if ($eventsHtml !== "") {
$extraHtml .= $eventsHtml;
}
if ($this->hasAnyChronologicalPhotos($dateIso)) {
$photosHtml = $this->renderChronologicalPhotosMacro($dateIso);
- if ($photosHtml !== '') {
+ if ($photosHtml !== "") {
$extraHtml .= $photosHtml;
}
}
}
- $editUrl = function_exists('wl') ? (string)wl($id, ['do' => 'edit']) : '';
- $createLinkHtml = '';
- if ($editUrl !== '') {
- $label = (string)$this->getLang('btn_create');
- if ($label === '') $label = 'Create this page';
- $createLinkHtml = '✎ ' . hsc($label) . '
';
+ $editUrl = function_exists("wl")
+ ? (string) wl($id, ["do" => "edit"])
+ : "";
+ $createLinkHtml = "";
+ if ($editUrl !== "") {
+ $label = (string) $this->getLang("btn_create");
+ if ($label === "") {
+ $label = "Create this page";
+ }
+ $createLinkHtml =
+ '✎ ' .
+ hsc($label) .
+ "
";
}
- $info = ['cache' => false];
+ $info = ["cache" => false];
$instructions = p_get_instructions($wikiText);
- $html = (string)p_render('xhtml', $instructions, $info);
+ $html = (string) p_render("xhtml", $instructions, $info);
echo $html . $createLinkHtml . $extraHtml;
$event->preventDefault();
@@ -513,23 +684,32 @@ class action_plugin_luxtools extends ActionPlugin
*/
protected function hasAnyChronologicalPhotos(string $dateIso): bool
{
- if (!ChronoID::isIsoDate($dateIso)) return false;
+ if (!ChronoID::isIsoDate($dateIso)) {
+ return false;
+ }
- $basePath = trim((string)$this->getConf('image_base_path'));
- if ($basePath === '') return false;
+ $basePath = trim((string) $this->getConf("image_base_path"));
+ if ($basePath === "") {
+ return false;
+ }
$base = \dokuwiki\plugin\luxtools\Path::cleanPath($basePath);
- if (!is_dir($base) || !is_readable($base)) return false;
+ if (!is_dir($base) || !is_readable($base)) {
+ return false;
+ }
- $yearDir = rtrim($base, '/') . '/' . substr($dateIso, 0, 4) . '/';
- $targetDir = (is_dir($yearDir) && is_readable($yearDir)) ? $yearDir : $base;
+ $yearDir = rtrim($base, "/") . "/" . substr($dateIso, 0, 4) . "/";
+ $targetDir =
+ is_dir($yearDir) && is_readable($yearDir) ? $yearDir : $base;
- $pattern = rtrim($targetDir, '/') . '/' . $dateIso . '*';
+ $pattern = rtrim($targetDir, "/") . "/" . $dateIso . "*";
$matches = glob($pattern) ?: [];
foreach ($matches as $match) {
- if (!is_file($match)) continue;
+ if (!is_file($match)) {
+ continue;
+ }
$ext = strtolower(pathinfo($match, PATHINFO_EXTENSION));
- if (in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'], true)) {
+ if (in_array($ext, ["jpg", "jpeg", "png", "gif", "webp"], true)) {
return true;
}
}
@@ -548,32 +728,52 @@ class action_plugin_luxtools extends ActionPlugin
protected function renderChronologicalEventsHtml(string $dateIso): string
{
$slots = CalendarSlot::loadEnabled($this);
- if ($slots === []) return '';
+ if ($slots === []) {
+ return "";
+ }
$grouped = CalendarService::eventsForDateGrouped($slots, $dateIso);
- if ($grouped === []) return '';
+ if ($grouped === []) {
+ return "";
+ }
- $html = '';
+ $html = "";
// Render general events
- if (isset($grouped['general'])) {
- $title = (string)$this->getLang('chronological_events_title');
- if ($title === '') $title = 'Events';
- $html .= $this->renderEventSection($grouped['general'], $title, 'general');
+ if (isset($grouped["general"])) {
+ $title = (string) $this->getLang("chronological_events_title");
+ if ($title === "") {
+ $title = "Events";
+ }
+ $html .= $this->renderEventSection(
+ $grouped["general"],
+ $title,
+ "general",
+ );
}
// Render maintenance tasks
- if (isset($grouped['maintenance'])) {
- $title = (string)$this->getLang('chronological_maintenance_title');
- if ($title === '') $title = 'Tasks';
- $html .= $this->renderMaintenanceSection($grouped['maintenance'], $title, $dateIso);
+ if (isset($grouped["maintenance"])) {
+ $title = (string) $this->getLang("chronological_maintenance_title");
+ if ($title === "") {
+ $title = "Tasks";
+ }
+ $html .= $this->renderMaintenanceSection(
+ $grouped["maintenance"],
+ $title,
+ $dateIso,
+ );
}
// Render slot3/slot4 if present
- foreach (['slot3', 'slot4'] as $slotKey) {
+ foreach (["slot3", "slot4"] as $slotKey) {
if (isset($grouped[$slotKey]) && isset($slots[$slotKey])) {
$label = $slots[$slotKey]->getLabel();
- $html .= $this->renderEventSection($grouped[$slotKey], $label, $slotKey);
+ $html .= $this->renderEventSection(
+ $grouped[$slotKey],
+ $label,
+ $slotKey,
+ );
}
}
@@ -588,18 +788,29 @@ class action_plugin_luxtools extends ActionPlugin
* @param string $slotKey
* @return string
*/
- protected function renderEventSection(array $events, string $title, string $slotKey): string
- {
- $items = '';
+ protected function renderEventSection(
+ array $events,
+ string $title,
+ string $slotKey,
+ ): string {
+ $items = "";
foreach ($events as $event) {
$items .= $this->renderEventListItem($event);
}
- if ($items === '') return '';
+ if ($items === "") {
+ return "";
+ }
- return '';
+ return '";
}
/**
@@ -610,24 +821,41 @@ class action_plugin_luxtools extends ActionPlugin
* @param string $dateIso
* @return string
*/
- protected function renderMaintenanceSection(array $events, string $title, string $dateIso): string
- {
- $items = '';
- $ajaxUrl = defined('DOKU_BASE') ? (string)DOKU_BASE . 'lib/exe/ajax.php' : 'lib/exe/ajax.php';
+ protected function renderMaintenanceSection(
+ array $events,
+ string $title,
+ string $dateIso,
+ ): string {
+ $items = "";
+ $ajaxUrl = defined("DOKU_BASE")
+ ? (string) DOKU_BASE . "lib/exe/ajax.php"
+ : "lib/exe/ajax.php";
foreach ($events as $event) {
$items .= $this->renderMaintenanceListItem($event, $ajaxUrl);
}
- if ($items === '') return '';
+ if ($items === "") {
+ return "";
+ }
- $secToken = function_exists('getSecurityToken') ? getSecurityToken() : '';
+ $secToken = function_exists("getSecurityToken")
+ ? getSecurityToken()
+ : "";
- return ''
- . '
' . hsc($title) . '
'
- . '
'
- . '
';
+ return '' .
+ "
" .
+ hsc($title) .
+ "
" .
+ "
" .
+ "
";
}
/**
@@ -644,34 +872,52 @@ class action_plugin_luxtools extends ActionPlugin
$dataAttrs = ' data-luxtools-event="1"';
$dataAttrs .= ' data-event-summary="' . hsc($event->summary) . '"';
$dataAttrs .= ' data-event-start="' . hsc($event->startIso) . '"';
- if ($event->endIso !== '') {
+ if ($event->endIso !== "") {
$dataAttrs .= ' data-event-end="' . hsc($event->endIso) . '"';
}
- if ($event->location !== '') {
- $dataAttrs .= ' data-event-location="' . hsc($event->location) . '"';
+ if ($event->location !== "") {
+ $dataAttrs .=
+ ' data-event-location="' . hsc($event->location) . '"';
}
- if ($event->description !== '') {
- $dataAttrs .= ' data-event-description="' . hsc($event->description) . '"';
+ if ($event->description !== "") {
+ $dataAttrs .=
+ ' data-event-description="' . hsc($event->description) . '"';
}
- $dataAttrs .= ' data-event-allday="' . ($event->allDay ? '1' : '0') . '"';
+ $dataAttrs .=
+ ' data-event-allday="' . ($event->allDay ? "1" : "0") . '"';
$dataAttrs .= ' data-event-slot="' . hsc($event->slotKey) . '"';
- if ($event->uid !== '') {
+ if ($event->uid !== "") {
$dataAttrs .= ' data-event-uid="' . hsc($event->uid) . '"';
}
- if ($event->recurrenceId !== '') {
- $dataAttrs .= ' data-event-recurrence="' . hsc($event->recurrenceId) . '"';
+ if ($event->recurrenceId !== "") {
+ $dataAttrs .=
+ ' data-event-recurrence="' . hsc($event->recurrenceId) . '"';
}
- if ($event->dateIso !== '') {
+ if ($event->dateIso !== "") {
$dataAttrs .= ' data-event-date="' . hsc($event->dateIso) . '"';
}
- if ($event->allDay || $event->time === '') {
- return '' . $summaryHtml . '';
+ if ($event->allDay || $event->time === "") {
+ return "' .
+ $summaryHtml .
+ "";
}
- $timeHtml = ''
- . hsc($event->time) . '';
- return '' . $timeHtml . ' - ' . $summaryHtml . '';
+ $timeHtml =
+ '' .
+ hsc($event->time) .
+ "";
+ return "" .
+ $timeHtml .
+ ' - ' .
+ $summaryHtml .
+ "";
}
/**
@@ -681,11 +927,15 @@ class action_plugin_luxtools extends ActionPlugin
* @param string $ajaxUrl
* @return string
*/
- protected function renderMaintenanceListItem(CalendarEvent $event, string $ajaxUrl): string
- {
+ protected function renderMaintenanceListItem(
+ CalendarEvent $event,
+ string $ajaxUrl,
+ ): string {
$isCompleted = $event->isCompleted();
- $classes = 'luxtools-maintenance-task';
- if ($isCompleted) $classes .= ' luxtools-task-completed';
+ $classes = "luxtools-maintenance-task";
+ if ($isCompleted) {
+ $classes .= " luxtools-task-completed";
+ }
$summaryHtml = hsc($event->summary);
@@ -693,42 +943,62 @@ class action_plugin_luxtools extends ActionPlugin
$dataAttrs = ' data-luxtools-event="1"';
$dataAttrs .= ' data-event-summary="' . hsc($event->summary) . '"';
$dataAttrs .= ' data-event-start="' . hsc($event->startIso) . '"';
- if ($event->endIso !== '') {
+ if ($event->endIso !== "") {
$dataAttrs .= ' data-event-end="' . hsc($event->endIso) . '"';
}
- if ($event->location !== '') {
- $dataAttrs .= ' data-event-location="' . hsc($event->location) . '"';
+ if ($event->location !== "") {
+ $dataAttrs .=
+ ' data-event-location="' . hsc($event->location) . '"';
}
- if ($event->description !== '') {
- $dataAttrs .= ' data-event-description="' . hsc($event->description) . '"';
+ if ($event->description !== "") {
+ $dataAttrs .=
+ ' data-event-description="' . hsc($event->description) . '"';
}
- $dataAttrs .= ' data-event-allday="' . ($event->allDay ? '1' : '0') . '"';
+ $dataAttrs .=
+ ' data-event-allday="' . ($event->allDay ? "1" : "0") . '"';
$dataAttrs .= ' data-event-slot="maintenance"';
$dataAttrs .= ' data-task-uid="' . hsc($event->uid) . '"';
$dataAttrs .= ' data-task-date="' . hsc($event->dateIso) . '"';
- $dataAttrs .= ' data-task-recurrence="' . hsc($event->recurrenceId) . '"';
+ $dataAttrs .=
+ ' data-task-recurrence="' . hsc($event->recurrenceId) . '"';
$dataAttrs .= ' data-task-status="' . hsc($event->status) . '"';
$buttonLabel = $isCompleted
- ? (string)$this->getLang('maintenance_task_reopen')
- : (string)$this->getLang('maintenance_task_complete');
- if ($buttonLabel === '') $buttonLabel = $isCompleted ? 'Reopen' : 'Complete';
- $buttonAction = $isCompleted ? 'reopen' : 'complete';
+ ? (string) $this->getLang("maintenance_task_reopen")
+ : (string) $this->getLang("maintenance_task_complete");
+ if ($buttonLabel === "") {
+ $buttonLabel = $isCompleted ? "Reopen" : "Complete";
+ }
+ $buttonAction = $isCompleted ? "reopen" : "complete";
- $buttonHtml = '';
+ $buttonHtml =
+ '";
- $timeHtml = '';
- if (!$event->allDay && $event->time !== '') {
- $timeHtml = ''
- . hsc($event->time) . ' - ';
+ $timeHtml = "";
+ if (!$event->allDay && $event->time !== "") {
+ $timeHtml =
+ '' .
+ hsc($event->time) .
+ " - ";
}
- return ''
- . $timeHtml
- . '' . $summaryHtml . ' '
- . $buttonHtml
- . '';
+ return '" .
+ $timeHtml .
+ '' .
+ $summaryHtml .
+ " " .
+ $buttonHtml .
+ "";
}
/**
@@ -740,12 +1010,14 @@ class action_plugin_luxtools extends ActionPlugin
*/
public function handleMaintenanceTaskAction(Event $event, $param)
{
- if ($event->data !== 'luxtools_maintenance_task') return;
+ if ($event->data !== "luxtools_maintenance_task") {
+ return;
+ }
$event->preventDefault();
$event->stopPropagation();
- header('Content-Type: application/json; charset=utf-8');
+ header("Content-Type: application/json; charset=utf-8");
$this->sendNoStoreHeaders();
global $INPUT;
@@ -753,46 +1025,61 @@ class action_plugin_luxtools extends ActionPlugin
// Verify security token
if (!checkSecurityToken()) {
http_status(403);
- echo json_encode(['ok' => false, 'error' => 'Security token mismatch']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "Security token mismatch",
+ ]);
return;
}
- $action = $INPUT->str('action'); // 'complete' or 'reopen'
- $uid = $INPUT->str('uid');
- $dateIso = $INPUT->str('date');
- $recurrence = $INPUT->str('recurrence');
+ $action = $INPUT->str("action"); // 'complete' or 'reopen'
+ $uid = $INPUT->str("uid");
+ $dateIso = $INPUT->str("date");
+ $recurrence = $INPUT->str("recurrence");
- if (!in_array($action, ['complete', 'reopen'], true)) {
+ if (!in_array($action, ["complete", "reopen"], true)) {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Invalid action']);
+ echo json_encode(["ok" => false, "error" => "Invalid action"]);
return;
}
- if ($uid === '' || !ChronoID::isIsoDate($dateIso)) {
+ if ($uid === "" || !ChronoID::isIsoDate($dateIso)) {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Missing uid or date']);
+ echo json_encode(["ok" => false, "error" => "Missing uid or date"]);
return;
}
$slots = CalendarSlot::loadAll($this);
- $maintenanceSlot = $slots['maintenance'] ?? null;
+ $maintenanceSlot = $slots["maintenance"] ?? null;
if ($maintenanceSlot === null || !$maintenanceSlot->isEnabled()) {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Maintenance calendar not configured']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "Maintenance calendar not configured",
+ ]);
return;
}
- $newStatus = ($action === 'complete') ? 'COMPLETED' : 'TODO';
+ $newStatus = $action === "complete" ? "COMPLETED" : "TODO";
// Update local ICS file
$localOk = false;
$file = $maintenanceSlot->getFile();
- if ($file !== '' && is_file($file)) {
- $localOk = IcsWriter::updateEventStatus($file, $uid, $recurrence, $newStatus, $dateIso);
+ if ($file !== "" && is_file($file)) {
+ $localOk = IcsWriter::updateEventStatus(
+ $file,
+ $uid,
+ $recurrence,
+ $newStatus,
+ $dateIso,
+ );
}
if (!$localOk) {
http_status(500);
- echo json_encode(['ok' => false, 'error' => $this->getLang('maintenance_complete_error')]);
+ echo json_encode([
+ "ok" => false,
+ "error" => $this->getLang("maintenance_complete_error"),
+ ]);
return;
}
@@ -801,7 +1088,7 @@ class action_plugin_luxtools extends ActionPlugin
// Remote CalDAV write-back if configured
$remoteOk = true;
- $remoteError = '';
+ $remoteError = "";
if ($maintenanceSlot->hasRemoteSource()) {
try {
$caldavResult = CalDavClient::updateEventStatus(
@@ -811,27 +1098,34 @@ class action_plugin_luxtools extends ActionPlugin
$uid,
$recurrence,
$newStatus,
- $dateIso
+ $dateIso,
);
- if ($caldavResult !== '') {
+ if ($caldavResult !== "") {
$remoteOk = false;
- $remoteError = $this->getLang('maintenance_remote_write_failed') . ': ' . $caldavResult;
+ $remoteError =
+ $this->getLang("maintenance_remote_write_failed") .
+ ": " .
+ $caldavResult;
}
} catch (Throwable $e) {
$remoteOk = false;
- $remoteError = $this->getLang('maintenance_remote_write_failed') . ': ' . $e->getMessage();
+ $remoteError =
+ $this->getLang("maintenance_remote_write_failed") .
+ ": " .
+ $e->getMessage();
}
}
- $msg = ($action === 'complete')
- ? $this->getLang('maintenance_complete_success')
- : $this->getLang('maintenance_reopen_success');
+ $msg =
+ $action === "complete"
+ ? $this->getLang("maintenance_complete_success")
+ : $this->getLang("maintenance_reopen_success");
echo json_encode([
- 'ok' => true,
- 'message' => $msg,
- 'remoteOk' => $remoteOk,
- 'remoteError' => $remoteError,
+ "ok" => true,
+ "message" => $msg,
+ "remoteOk" => $remoteOk,
+ "remoteError" => $remoteError,
]);
}
@@ -844,39 +1138,47 @@ class action_plugin_luxtools extends ActionPlugin
*/
public function handleCalendarSyncAction(Event $event, $param)
{
- if ($event->data !== 'luxtools_calendar_sync') return;
+ if ($event->data !== "luxtools_calendar_sync") {
+ return;
+ }
$event->preventDefault();
$event->stopPropagation();
- header('Content-Type: application/json; charset=utf-8');
+ header("Content-Type: application/json; charset=utf-8");
$this->sendNoStoreHeaders();
global $INPUT;
if (!checkSecurityToken()) {
http_status(403);
- echo json_encode(['ok' => false, 'error' => 'Security token mismatch']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "Security token mismatch",
+ ]);
return;
}
- if (empty($_SERVER['REMOTE_USER'])) {
+ if (empty($_SERVER["REMOTE_USER"])) {
http_status(403);
- echo json_encode(['ok' => false, 'error' => 'Authentication required']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "Authentication required",
+ ]);
return;
}
$slots = CalendarSlot::loadEnabled($this);
$result = CalendarSyncService::syncAll($slots);
- $msg = $result['ok']
- ? $this->getLang('calendar_sync_success')
- : $this->getLang('calendar_sync_partial');
+ $msg = $result["ok"]
+ ? $this->getLang("calendar_sync_success")
+ : $this->getLang("calendar_sync_partial");
echo json_encode([
- 'ok' => $result['ok'],
- 'message' => $msg,
- 'results' => $result['results'],
+ "ok" => $result["ok"],
+ "message" => $msg,
+ "results" => $result["results"],
]);
}
@@ -889,24 +1191,26 @@ class action_plugin_luxtools extends ActionPlugin
*/
public function handleCalendarSlotsAction(Event $event, $param)
{
- if ($event->data !== 'luxtools_calendar_slots') return;
+ if ($event->data !== "luxtools_calendar_slots") {
+ return;
+ }
$event->preventDefault();
$event->stopPropagation();
- header('Content-Type: application/json; charset=utf-8');
+ header("Content-Type: application/json; charset=utf-8");
$this->sendNoStoreHeaders();
$slots = CalendarSlot::loadEnabled($this);
$result = [];
foreach ($slots as $slot) {
$result[] = [
- 'key' => $slot->getKey(),
- 'label' => $slot->getLabel(),
+ "key" => $slot->getKey(),
+ "label" => $slot->getLabel(),
];
}
- echo json_encode(['ok' => true, 'slots' => $result]);
+ echo json_encode(["ok" => true, "slots" => $result]);
}
/**
@@ -918,12 +1222,14 @@ class action_plugin_luxtools extends ActionPlugin
*/
public function handleCalendarEventAction(Event $event, $param)
{
- if ($event->data !== 'luxtools_calendar_event') return;
+ if ($event->data !== "luxtools_calendar_event") {
+ return;
+ }
$event->preventDefault();
$event->stopPropagation();
- header('Content-Type: application/json; charset=utf-8');
+ header("Content-Type: application/json; charset=utf-8");
$this->sendNoStoreHeaders();
global $INPUT;
@@ -931,29 +1237,35 @@ class action_plugin_luxtools extends ActionPlugin
// Require security token
if (!checkSecurityToken()) {
http_status(403);
- echo json_encode(['ok' => false, 'error' => 'Security token mismatch']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "Security token mismatch",
+ ]);
return;
}
// Require authenticated user
- if (!isset($_SERVER['REMOTE_USER']) || $_SERVER['REMOTE_USER'] === '') {
+ if (!isset($_SERVER["REMOTE_USER"]) || $_SERVER["REMOTE_USER"] === "") {
http_status(403);
- echo json_encode(['ok' => false, 'error' => 'Authentication required']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "Authentication required",
+ ]);
return;
}
- $action = $INPUT->str('action');
- if (!in_array($action, ['create', 'edit', 'delete'], true)) {
+ $action = $INPUT->str("action");
+ if (!in_array($action, ["create", "edit", "delete"], true)) {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Invalid action']);
+ echo json_encode(["ok" => false, "error" => "Invalid action"]);
return;
}
- if ($action === 'create') {
+ if ($action === "create") {
$this->handleEventCreate($INPUT);
- } elseif ($action === 'edit') {
+ } elseif ($action === "edit") {
$this->handleEventEdit($INPUT);
- } elseif ($action === 'delete') {
+ } elseif ($action === "delete") {
$this->handleEventDelete($INPUT);
}
}
@@ -966,18 +1278,18 @@ class action_plugin_luxtools extends ActionPlugin
*/
protected function handleEventCreate($INPUT): void
{
- $slotKey = $INPUT->str('slot');
- $summary = trim($INPUT->str('summary'));
- $dateIso = $INPUT->str('date');
+ $slotKey = $INPUT->str("slot");
+ $summary = trim($INPUT->str("summary"));
+ $dateIso = $INPUT->str("date");
- if ($summary === '') {
+ if ($summary === "") {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Summary is required']);
+ echo json_encode(["ok" => false, "error" => "Summary is required"]);
return;
}
if (!ChronoID::isIsoDate($dateIso)) {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Invalid date']);
+ echo json_encode(["ok" => false, "error" => "Invalid date"]);
return;
}
@@ -985,31 +1297,40 @@ class action_plugin_luxtools extends ActionPlugin
$slot = $slots[$slotKey] ?? null;
if ($slot === null || !$slot->isEnabled()) {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Invalid calendar slot']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "Invalid calendar slot",
+ ]);
return;
}
$file = $slot->getFile();
- if ($file === '') {
+ if ($file === "") {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'No local file configured for this slot']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "No local file configured for this slot",
+ ]);
return;
}
$eventData = [
- 'summary' => $summary,
- 'date' => $dateIso,
- 'allDay' => $INPUT->bool('allday'),
- 'startTime' => $INPUT->str('start_time'),
- 'endTime' => $INPUT->str('end_time'),
- 'location' => trim($INPUT->str('location')),
- 'description' => trim($INPUT->str('description')),
+ "summary" => $summary,
+ "date" => $dateIso,
+ "allDay" => $INPUT->bool("allday"),
+ "startTime" => $INPUT->str("start_time"),
+ "endTime" => $INPUT->str("end_time"),
+ "location" => trim($INPUT->str("location")),
+ "description" => trim($INPUT->str("description")),
];
$uid = IcsWriter::createEvent($file, $eventData);
- if ($uid === '') {
+ if ($uid === "") {
http_status(500);
- echo json_encode(['ok' => false, 'error' => 'Failed to create event']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "Failed to create event",
+ ]);
return;
}
@@ -1017,20 +1338,20 @@ class action_plugin_luxtools extends ActionPlugin
// CalDAV write-back if configured
$remoteOk = true;
- $remoteError = '';
+ $remoteError = "";
if ($slot->hasRemoteSource()) {
$remoteOk = $this->pushEventToCalDav($slot, $file, $uid);
if (!$remoteOk) {
- $remoteError = 'Local event created, but CalDAV upload failed.';
+ $remoteError = "Local event created, but CalDAV upload failed.";
}
}
echo json_encode([
- 'ok' => true,
- 'message' => 'Event created.',
- 'uid' => $uid,
- 'remoteOk' => $remoteOk,
- 'remoteError' => $remoteError,
+ "ok" => true,
+ "message" => "Event created.",
+ "uid" => $uid,
+ "remoteOk" => $remoteOk,
+ "remoteError" => $remoteError,
]);
}
@@ -1042,30 +1363,30 @@ class action_plugin_luxtools extends ActionPlugin
*/
protected function handleEventEdit($INPUT): void
{
- $uid = $INPUT->str('uid');
- $recurrence = $INPUT->str('recurrence');
- $slotKey = $INPUT->str('slot');
- $summary = trim($INPUT->str('summary'));
- $dateIso = $INPUT->str('date');
- $scope = $INPUT->str('scope', 'all');
+ $uid = $INPUT->str("uid");
+ $recurrence = $INPUT->str("recurrence");
+ $slotKey = $INPUT->str("slot");
+ $summary = trim($INPUT->str("summary"));
+ $dateIso = $INPUT->str("date");
+ $scope = $INPUT->str("scope", "all");
- if (!in_array($scope, ['all', 'this', 'future'], true)) {
- $scope = 'all';
+ if (!in_array($scope, ["all", "this", "future"], true)) {
+ $scope = "all";
}
- if ($uid === '') {
+ if ($uid === "") {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Missing event UID']);
+ echo json_encode(["ok" => false, "error" => "Missing event UID"]);
return;
}
- if ($summary === '') {
+ if ($summary === "") {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Summary is required']);
+ echo json_encode(["ok" => false, "error" => "Summary is required"]);
return;
}
if (!ChronoID::isIsoDate($dateIso)) {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Invalid date']);
+ echo json_encode(["ok" => false, "error" => "Invalid date"]);
return;
}
@@ -1073,31 +1394,46 @@ class action_plugin_luxtools extends ActionPlugin
$slot = $slots[$slotKey] ?? null;
if ($slot === null || !$slot->isEnabled()) {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Invalid calendar slot']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "Invalid calendar slot",
+ ]);
return;
}
$file = $slot->getFile();
- if ($file === '' || !is_file($file)) {
+ if ($file === "" || !is_file($file)) {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'No local file for this slot']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "No local file for this slot",
+ ]);
return;
}
$eventData = [
- 'summary' => $summary,
- 'date' => $dateIso,
- 'allDay' => $INPUT->bool('allday'),
- 'startTime' => $INPUT->str('start_time'),
- 'endTime' => $INPUT->str('end_time'),
- 'location' => trim($INPUT->str('location')),
- 'description' => trim($INPUT->str('description')),
+ "summary" => $summary,
+ "date" => $dateIso,
+ "allDay" => $INPUT->bool("allday"),
+ "startTime" => $INPUT->str("start_time"),
+ "endTime" => $INPUT->str("end_time"),
+ "location" => trim($INPUT->str("location")),
+ "description" => trim($INPUT->str("description")),
];
- $ok = IcsWriter::editEvent($file, $uid, $recurrence, $eventData, $scope);
+ $ok = IcsWriter::editEvent(
+ $file,
+ $uid,
+ $recurrence,
+ $eventData,
+ $scope,
+ );
if (!$ok) {
http_status(500);
- echo json_encode(['ok' => false, 'error' => 'Failed to update event']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "Failed to update event",
+ ]);
return;
}
@@ -1105,19 +1441,19 @@ class action_plugin_luxtools extends ActionPlugin
// CalDAV write-back if configured
$remoteOk = true;
- $remoteError = '';
+ $remoteError = "";
if ($slot->hasRemoteSource()) {
$remoteOk = $this->pushEventToCalDav($slot, $file, $uid);
if (!$remoteOk) {
- $remoteError = 'Local event updated, but CalDAV upload failed.';
+ $remoteError = "Local event updated, but CalDAV upload failed.";
}
}
echo json_encode([
- 'ok' => true,
- 'message' => 'Event updated.',
- 'remoteOk' => $remoteOk,
- 'remoteError' => $remoteError,
+ "ok" => true,
+ "message" => "Event updated.",
+ "remoteOk" => $remoteOk,
+ "remoteError" => $remoteError,
]);
}
@@ -1129,40 +1465,55 @@ class action_plugin_luxtools extends ActionPlugin
*/
protected function handleEventDelete($INPUT): void
{
- $uid = $INPUT->str('uid');
- $recurrence = $INPUT->str('recurrence');
- $slotKey = $INPUT->str('slot');
- $dateIso = $INPUT->str('date');
- $scope = $INPUT->str('scope');
+ $uid = $INPUT->str("uid");
+ $recurrence = $INPUT->str("recurrence");
+ $slotKey = $INPUT->str("slot");
+ $dateIso = $INPUT->str("date");
+ $scope = $INPUT->str("scope");
- if ($uid === '') {
+ if ($uid === "") {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Missing event UID']);
+ echo json_encode(["ok" => false, "error" => "Missing event UID"]);
return;
}
- if (!in_array($scope, ['all', 'this', 'future'], true)) {
- $scope = 'all';
+ if (!in_array($scope, ["all", "this", "future"], true)) {
+ $scope = "all";
}
$slots = CalendarSlot::loadAll($this);
$slot = $slots[$slotKey] ?? null;
if ($slot === null || !$slot->isEnabled()) {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'Invalid calendar slot']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "Invalid calendar slot",
+ ]);
return;
}
$file = $slot->getFile();
- if ($file === '' || !is_file($file)) {
+ if ($file === "" || !is_file($file)) {
http_status(400);
- echo json_encode(['ok' => false, 'error' => 'No local file for this slot']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "No local file for this slot",
+ ]);
return;
}
- $ok = IcsWriter::deleteEvent($file, $uid, $recurrence, $dateIso, $scope);
+ $ok = IcsWriter::deleteEvent(
+ $file,
+ $uid,
+ $recurrence,
+ $dateIso,
+ $scope,
+ );
if (!$ok) {
http_status(500);
- echo json_encode(['ok' => false, 'error' => 'Failed to delete event']);
+ echo json_encode([
+ "ok" => false,
+ "error" => "Failed to delete event",
+ ]);
return;
}
@@ -1170,23 +1521,23 @@ class action_plugin_luxtools extends ActionPlugin
// CalDAV write-back: push updated file for this UID
$remoteOk = true;
- $remoteError = '';
+ $remoteError = "";
if ($slot->hasRemoteSource()) {
- if ($scope === 'all') {
+ if ($scope === "all") {
$remoteOk = $this->deleteEventFromCalDav($slot, $uid);
} else {
$remoteOk = $this->pushEventToCalDav($slot, $file, $uid);
}
if (!$remoteOk) {
- $remoteError = 'Local event deleted, but CalDAV update failed.';
+ $remoteError = "Local event deleted, but CalDAV update failed.";
}
}
echo json_encode([
- 'ok' => true,
- 'message' => 'Event deleted.',
- 'remoteOk' => $remoteOk,
- 'remoteError' => $remoteError,
+ "ok" => true,
+ "message" => "Event deleted.",
+ "remoteOk" => $remoteOk,
+ "remoteError" => $remoteError,
]);
}
@@ -1199,33 +1550,45 @@ class action_plugin_luxtools extends ActionPlugin
* @param string $uid
* @return bool
*/
- protected function pushEventToCalDav(CalendarSlot $slot, string $file, string $uid): bool
- {
+ protected function pushEventToCalDav(
+ CalendarSlot $slot,
+ string $file,
+ string $uid,
+ ): bool {
try {
$raw = @file_get_contents($file);
- if (!is_string($raw) || trim($raw) === '') return false;
+ if (!is_string($raw) || trim($raw) === "") {
+ return false;
+ }
- $calendar = \Sabre\VObject\Reader::read($raw, \Sabre\VObject\Reader::OPTION_FORGIVING);
- if (!($calendar instanceof \Sabre\VObject\Component\VCalendar)) return false;
+ $calendar = \Sabre\VObject\Reader::read(
+ $raw,
+ \Sabre\VObject\Reader::OPTION_FORGIVING,
+ );
+ if (!($calendar instanceof \Sabre\VObject\Component\VCalendar)) {
+ return false;
+ }
// Extract just the components for this UID into a new calendar
$eventCal = new \Sabre\VObject\Component\VCalendar();
- $eventCal->PRODID = '-//LuxTools DokuWiki Plugin//EN';
+ $eventCal->PRODID = "-//LuxTools DokuWiki Plugin//EN";
$found = false;
// Copy relevant VTIMEZONE
- foreach ($calendar->select('VTIMEZONE') as $tz) {
+ foreach ($calendar->select("VTIMEZONE") as $tz) {
$eventCal->add(clone $tz);
}
- foreach ($calendar->select('VEVENT') as $component) {
- if (trim((string)($component->UID ?? '')) === $uid) {
+ foreach ($calendar->select("VEVENT") as $component) {
+ if (trim((string) ($component->UID ?? "")) === $uid) {
$eventCal->add(clone $component);
$found = true;
}
}
- if (!$found) return false;
+ if (!$found) {
+ return false;
+ }
$icsData = $eventCal->serialize();
@@ -1234,31 +1597,31 @@ class action_plugin_luxtools extends ActionPlugin
$slot->getCaldavUrl(),
$slot->getUsername(),
$slot->getPassword(),
- $uid
+ $uid,
);
if ($objectInfo !== null) {
// Update existing object
$error = CalDavClient::putCalendarObjectPublic(
- $objectInfo['href'],
+ $objectInfo["href"],
$slot->getUsername(),
$slot->getPassword(),
$icsData,
- $objectInfo['etag']
+ $objectInfo["etag"],
);
- return $error === '';
+ return $error === "";
}
// Create new object
- $href = rtrim($slot->getCaldavUrl(), '/') . '/' . $uid . '.ics';
+ $href = rtrim($slot->getCaldavUrl(), "/") . "/" . $uid . ".ics";
$error = CalDavClient::putCalendarObjectPublic(
$href,
$slot->getUsername(),
$slot->getPassword(),
$icsData,
- ''
+ "",
);
- return $error === '';
+ return $error === "";
} catch (\Throwable $e) {
return false;
}
@@ -1271,23 +1634,27 @@ class action_plugin_luxtools extends ActionPlugin
* @param string $uid
* @return bool
*/
- protected function deleteEventFromCalDav(CalendarSlot $slot, string $uid): bool
- {
+ protected function deleteEventFromCalDav(
+ CalendarSlot $slot,
+ string $uid,
+ ): bool {
try {
$objectInfo = CalDavClient::findObjectByUidPublic(
$slot->getCaldavUrl(),
$slot->getUsername(),
$slot->getPassword(),
- $uid
+ $uid,
);
- if ($objectInfo === null) return true; // Already gone
+ if ($objectInfo === null) {
+ return true;
+ } // Already gone
return CalDavClient::deleteCalendarObject(
- $objectInfo['href'],
+ $objectInfo["href"],
$slot->getUsername(),
$slot->getPassword(),
- $objectInfo['etag']
+ $objectInfo["etag"],
);
} catch (\Throwable $e) {
return false;
@@ -1303,19 +1670,23 @@ class action_plugin_luxtools extends ActionPlugin
protected function buildChronologicalEventsWiki(string $dateIso): string
{
$slots = CalendarSlot::loadEnabled($this);
- if ($slots === []) return '';
+ if ($slots === []) {
+ return "";
+ }
$events = CalendarService::eventsForDate($slots, $dateIso);
- if ($events === []) return '';
+ if ($events === []) {
+ return "";
+ }
$lines = [];
foreach ($events as $event) {
- $summary = str_replace(["\n", "\r"], ' ', $event->summary);
+ $summary = str_replace(["\n", "\r"], " ", $event->summary);
- if ($event->allDay || $event->time === '') {
- $lines[] = ' * ' . $summary;
+ if ($event->allDay || $event->time === "") {
+ $lines[] = " * " . $summary;
} else {
- $lines[] = ' * ' . $event->time . ' - ' . $summary;
+ $lines[] = " * " . $event->time . " - " . $summary;
}
}
@@ -1377,18 +1748,30 @@ class action_plugin_luxtools extends ActionPlugin
*/
public function addInvalidateCacheMenuItem(Event $event, $param)
{
- if (!is_array($event->data)) return;
- if (($event->data['view'] ?? '') !== 'page') return;
- if (!function_exists('auth_isadmin') || !auth_isadmin()) return;
- if (!isset($event->data['items']) || !is_array($event->data['items'])) return;
+ if (!is_array($event->data)) {
+ return;
+ }
+ if (($event->data["view"] ?? "") !== "page") {
+ return;
+ }
+ if (!function_exists("auth_isadmin") || !auth_isadmin()) {
+ return;
+ }
+ if (!isset($event->data["items"]) || !is_array($event->data["items"])) {
+ return;
+ }
- $label = (string)$this->getLang('cache_invalidate_button');
- if ($label === '') $label = 'Invalidate Cache';
+ $label = (string) $this->getLang("cache_invalidate_button");
+ if ($label === "") {
+ $label = "Invalidate Cache";
+ }
- $title = (string)$this->getLang('cache_invalidate_button_title');
- if ($title === '') $title = 'Invalidate luxtools cache for this page';
+ $title = (string) $this->getLang("cache_invalidate_button_title");
+ if ($title === "") {
+ $title = "Invalidate luxtools cache for this page";
+ }
- $event->data['items'][] = new InvalidateCache($label, $title);
+ $event->data["items"][] = new InvalidateCache($label, $title);
}
/**
@@ -1400,57 +1783,71 @@ class action_plugin_luxtools extends ActionPlugin
*/
public function handleInvalidateCacheAction(Event $event, $param)
{
- if (!is_string($event->data) || $event->data !== 'show') return;
-
- global $INPUT;
- if (!$INPUT->bool('luxtools_invalidate_cache')) return;
-
- global $ID;
- $id = is_string($ID) ? $ID : '';
- if (function_exists('cleanID')) {
- $id = (string)cleanID($id);
+ if (!is_string($event->data) || $event->data !== "show") {
+ return;
}
- if (!function_exists('auth_isadmin') || !auth_isadmin()) {
- $message = (string)$this->getLang('cache_invalidate_denied');
- if ($message === '') $message = 'Only admins can invalidate cache.';
+ global $INPUT;
+ if (!$INPUT->bool("luxtools_invalidate_cache")) {
+ return;
+ }
+
+ global $ID;
+ $id = is_string($ID) ? $ID : "";
+ if (function_exists("cleanID")) {
+ $id = (string) cleanID($id);
+ }
+
+ if (!function_exists("auth_isadmin") || !auth_isadmin()) {
+ $message = (string) $this->getLang("cache_invalidate_denied");
+ if ($message === "") {
+ $message = "Only admins can invalidate cache.";
+ }
msg($message, -1);
$this->redirectToShow($id);
return;
}
if (!checkSecurityToken()) {
- $message = (string)$this->getLang('cache_invalidate_badtoken');
- if ($message === '') $message = 'Security token mismatch. Please retry.';
+ $message = (string) $this->getLang("cache_invalidate_badtoken");
+ if ($message === "") {
+ $message = "Security token mismatch. Please retry.";
+ }
msg($message, -1);
$this->redirectToShow($id);
return;
}
$result = CacheInvalidation::purgeSelected(
- $INPUT->bool('luxtools_purge_pagelinks'),
- $INPUT->bool('luxtools_purge_thumbs')
+ $INPUT->bool("luxtools_purge_pagelinks"),
+ $INPUT->bool("luxtools_purge_thumbs"),
);
$parts = [];
- $dokuwikiMsg = (string)$this->getLang('cache_invalidate_success');
- if ($dokuwikiMsg === '') $dokuwikiMsg = 'DokuWiki cache invalidated.';
- $parts[] = $dokuwikiMsg . ' (' . $result['dokuwiki'] . ')';
+ $dokuwikiMsg = (string) $this->getLang("cache_invalidate_success");
+ if ($dokuwikiMsg === "") {
+ $dokuwikiMsg = "DokuWiki cache invalidated.";
+ }
+ $parts[] = $dokuwikiMsg . " (" . $result["dokuwiki"] . ")";
- if ($result['pagelinks'] !== null) {
- $msg = (string)$this->getLang('cache_purge_pagelinks_success');
- if ($msg === '') $msg = 'Pagelinks cache purged.';
- $parts[] = $msg . ' (' . $result['pagelinks'] . ')';
+ if ($result["pagelinks"] !== null) {
+ $msg = (string) $this->getLang("cache_purge_pagelinks_success");
+ if ($msg === "") {
+ $msg = "Pagelinks cache purged.";
+ }
+ $parts[] = $msg . " (" . $result["pagelinks"] . ")";
}
- if ($result['thumbs'] !== null) {
- $msg = (string)$this->getLang('cache_purge_thumbs_success');
- if ($msg === '') $msg = 'Thumbnail cache purged.';
- $parts[] = $msg . ' (' . $result['thumbs'] . ')';
+ if ($result["thumbs"] !== null) {
+ $msg = (string) $this->getLang("cache_purge_thumbs_success");
+ if ($msg === "") {
+ $msg = "Thumbnail cache purged.";
+ }
+ $parts[] = $msg . " (" . $result["thumbs"] . ")";
}
- msg(implode(' ', $parts), 1);
+ msg(implode(" ", $parts), 1);
$this->redirectToShow($id);
}
@@ -1463,8 +1860,8 @@ class action_plugin_luxtools extends ActionPlugin
*/
protected function redirectToShow(string $id): void
{
- $params = ['do' => 'show'];
- send_redirect(wl($id, $params, true, '&'));
+ $params = ["do" => "show"];
+ send_redirect(wl($id, $params, true, "&"));
}
/**
@@ -1474,10 +1871,12 @@ class action_plugin_luxtools extends ActionPlugin
*/
protected function sendNoStoreHeaders(): void
{
- if (headers_sent()) return;
+ if (headers_sent()) {
+ return;
+ }
- header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
- header('Pragma: no-cache');
- header('Expires: 0');
+ header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
+ header("Pragma: no-cache");
+ header("Expires: 0");
}
}
diff --git a/dialog.css b/dialog.css
new file mode 100644
index 0000000..f53c9a7
--- /dev/null
+++ b/dialog.css
@@ -0,0 +1,62 @@
+/* ============================================================
+ * Dialog Infrastructure (shared overlay & popup)
+ * ============================================================ */
+.luxtools-dialog-overlay {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.4);
+ z-index: 10000;
+ justify-content: center;
+ align-items: center;
+}
+
+.luxtools-dialog {
+ background: @ini_background;
+ border: 1px solid @ini_border;
+ border-radius: 0.4em;
+ padding: 1.5em;
+ max-width: 500px;
+ width: 90%;
+ max-height: 80vh;
+ overflow-y: auto;
+ position: relative;
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
+}
+
+.luxtools-dialog-close {
+ position: absolute;
+ top: 0.5em;
+ right: 0.75em;
+ background: none;
+ border: none;
+ font-size: 1.5em;
+ cursor: pointer;
+ color: @ini_text;
+ line-height: 1;
+}
+
+.luxtools-dialog-close:hover {
+ opacity: 0.7;
+}
+
+.luxtools-dialog-title {
+ margin: 0 0 0.75em 0;
+ padding-right: 1.5em;
+}
+
+.luxtools-dialog-field {
+ margin: 0.5em 0;
+}
+
+.luxtools-dialog-actions {
+ margin-top: 1em;
+ padding-top: 0.75em;
+ border-top: 1px solid @ini_border;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5em;
+}
diff --git a/js/dialog.js b/js/dialog.js
new file mode 100644
index 0000000..d22eedd
--- /dev/null
+++ b/js/dialog.js
@@ -0,0 +1,95 @@
+/* global window, document */
+
+/**
+ * Unified Dialog Infrastructure
+ *
+ * Provides a shared modal overlay and dialog container that all other
+ * client-side modules (event popups, cache purge, etc.) can use.
+ *
+ * Usage:
+ * Luxtools.Dialog.show(htmlString) – render content and open
+ * Luxtools.Dialog.close() – close the dialog
+ * Luxtools.Dialog.getContainer() – return the dialog DOM element
+ */
+(function () {
+ 'use strict';
+
+ var Luxtools = window.Luxtools || (window.Luxtools = {});
+
+ var Dialog = (function () {
+ var overlay = null;
+ var container = null;
+
+ /**
+ * Lazily create the overlay + dialog container and attach them to
+ * the document body. Wires up click-outside-to-close and Escape.
+ */
+ function ensureElements() {
+ if (overlay) return;
+
+ overlay = document.createElement('div');
+ overlay.className = 'luxtools-dialog-overlay';
+ overlay.style.display = 'none';
+
+ container = document.createElement('div');
+ container.className = 'luxtools-dialog';
+ container.setAttribute('role', 'dialog');
+ container.setAttribute('aria-modal', 'true');
+
+ overlay.appendChild(container);
+ document.body.appendChild(overlay);
+
+ // Close when clicking the backdrop (but not the dialog itself)
+ overlay.addEventListener('click', function (e) {
+ if (e.target === overlay) close();
+ });
+
+ // Close on Escape
+ document.addEventListener('keydown', function (e) {
+ if (e.key === 'Escape' && overlay && overlay.style.display !== 'none') {
+ close();
+ }
+ });
+ }
+
+ /**
+ * Show the dialog with the given HTML content.
+ *
+ * The HTML should include a close button with class
+ * `luxtools-dialog-close` – it will be wired up automatically.
+ *
+ * @param {string} html - innerHTML for the dialog container
+ */
+ function show(html) {
+ ensureElements();
+ container.innerHTML = html;
+ overlay.style.display = 'flex';
+
+ // Auto-wire the close button inside the rendered content
+ var closeBtn = container.querySelector('.luxtools-dialog-close');
+ if (closeBtn) closeBtn.addEventListener('click', close);
+ }
+
+ /**
+ * Close (hide) the dialog.
+ */
+ function close() {
+ if (overlay) overlay.style.display = 'none';
+ }
+
+ /**
+ * Return the dialog container element (creates it if necessary).
+ * Useful for querying form inputs after `show()`.
+ *
+ * @returns {HTMLElement}
+ */
+ function getContainer() {
+ ensureElements();
+ return container;
+ }
+
+ return { show: show, close: close, getContainer: getContainer };
+ })();
+
+ Luxtools.Dialog = Dialog;
+})();
diff --git a/js/event-popup.js b/js/event-popup.js
index f53f423..a7da062 100644
--- a/js/event-popup.js
+++ b/js/event-popup.js
@@ -11,7 +11,7 @@
* complete/reopen the task.
*/
(function () {
- 'use strict';
+ "use strict";
var Luxtools = window.Luxtools || (window.Luxtools = {});
@@ -22,40 +22,44 @@
// Shared helpers
// ============================================================
function escapeHtml(text) {
- var div = document.createElement('div');
+ var div = document.createElement("div");
div.appendChild(document.createTextNode(text));
return div.innerHTML;
}
function pad2(value) {
- return String(value).padStart(2, '0');
+ return String(value).padStart(2, "0");
}
function formatDate(isoStr) {
- if (!isoStr) return '';
+ if (!isoStr) return "";
var d = new Date(isoStr);
if (isNaN(d.getTime())) return isoStr;
- return pad2(d.getDate()) + '.' + pad2(d.getMonth() + 1) + '.' + d.getFullYear();
+ return (
+ pad2(d.getDate()) + "." + pad2(d.getMonth() + 1) + "." + d.getFullYear()
+ );
}
function formatDateTime(isoStr) {
- if (!isoStr) return '';
+ if (!isoStr) return "";
var d = new Date(isoStr);
if (isNaN(d.getTime())) return isoStr;
- return formatDate(isoStr) + ' ' + pad2(d.getHours()) + ':' + pad2(d.getMinutes());
+ return (
+ formatDate(isoStr) + " " + pad2(d.getHours()) + ":" + pad2(d.getMinutes())
+ );
}
function formatTimeOnly(isoStr) {
- if (!isoStr) return '';
+ if (!isoStr) return "";
var d = new Date(isoStr);
if (isNaN(d.getTime())) return isoStr;
- return pad2(d.getHours()) + ':' + pad2(d.getMinutes());
+ return pad2(d.getHours()) + ":" + pad2(d.getMinutes());
}
function formatEventListTime(startIso, fallbackTime) {
var formatted = formatTimeOnly(startIso);
if (!formatted || formatted === startIso) {
- return fallbackTime || '';
+ return fallbackTime || "";
}
return formatted;
}
@@ -66,22 +70,25 @@
}
function getAjaxUrl() {
- return (window.DOKU_BASE || '/') + 'lib/exe/ajax.php';
+ return (window.DOKU_BASE || "/") + "lib/exe/ajax.php";
}
function getSecurityToken(el) {
// Try element hierarchy
if (el) {
- var container = el.closest ? el.closest('[data-luxtools-sectok]') : null;
+ var container = el.closest ? el.closest("[data-luxtools-sectok]") : null;
if (container) {
- var tok = container.getAttribute('data-luxtools-sectok');
+ var tok = container.getAttribute("data-luxtools-sectok");
if (tok) return tok;
}
}
- if (window.JSINFO && window.JSINFO.sectok) return String(window.JSINFO.sectok);
- var input = document.querySelector('input[name="sectok"], input[name="securitytoken"]');
+ if (window.JSINFO && window.JSINFO.sectok)
+ return String(window.JSINFO.sectok);
+ var input = document.querySelector(
+ 'input[name="sectok"], input[name="securitytoken"]',
+ );
if (input && input.value) return String(input.value);
- return '';
+ return "";
}
function isAuthenticated() {
@@ -89,23 +96,25 @@
if (window.JSINFO && window.JSINFO.isadmin) return true;
if (window.JSINFO && window.JSINFO.id !== undefined) {
// Check if user info exists (logged in users have a userinfo)
- var userinfo = document.querySelector('.user');
+ var userinfo = document.querySelector(".user");
if (userinfo) return true;
// Alternative: check for logout form
- var logoutLink = document.querySelector('a[href*="do=logout"], .action.logout');
+ var logoutLink = document.querySelector(
+ 'a[href*="do=logout"], .action.logout',
+ );
if (logoutLink) return true;
}
return false;
}
function showNotification(message, type) {
- if (typeof window.msg === 'function') {
- var level = (type === 'error') ? -1 : ((type === 'warning') ? 0 : 1);
+ if (typeof window.msg === "function") {
+ var level = type === "error" ? -1 : type === "warning" ? 0 : 1;
window.msg(message, level);
return;
}
- var notif = document.createElement('div');
- notif.className = 'luxtools-notification luxtools-notification-' + type;
+ var notif = document.createElement("div");
+ notif.className = "luxtools-notification luxtools-notification-" + type;
notif.textContent = message;
document.body.appendChild(notif);
setTimeout(function () {
@@ -113,64 +122,16 @@
}, 5000);
}
- // ============================================================
- // Popup infrastructure (shared overlay)
- // ============================================================
- var PopupUI = (function () {
- var overlay = null;
- var popup = null;
-
- function ensureElements() {
- if (overlay) return;
-
- overlay = document.createElement('div');
- overlay.className = 'luxtools-event-popup-overlay';
- overlay.style.display = 'none';
-
- popup = document.createElement('div');
- popup.className = 'luxtools-event-popup';
- popup.setAttribute('role', 'dialog');
- popup.setAttribute('aria-modal', 'true');
-
- overlay.appendChild(popup);
- document.body.appendChild(overlay);
-
- overlay.addEventListener('click', function (e) {
- if (e.target === overlay) close();
- });
-
- document.addEventListener('keydown', function (e) {
- if (e.key === 'Escape' && overlay && overlay.style.display !== 'none') {
- close();
- }
- });
- }
-
- function show(html) {
- ensureElements();
- popup.innerHTML = html;
- overlay.style.display = 'flex';
- var closeBtn = popup.querySelector('.luxtools-event-popup-close');
- if (closeBtn) closeBtn.addEventListener('click', close);
- }
-
- function close() {
- if (overlay) overlay.style.display = 'none';
- }
-
- function getPopup() {
- ensureElements();
- return popup;
- }
-
- return { show: show, close: close, getPopup: getPopup };
- })();
+ // Lazy reference to the shared dialog infrastructure (dialog.js).
+ // Accessed via function to handle any script-loading order variation.
+ function getDialog() {
+ return Luxtools.Dialog;
+ }
// ============================================================
// Event Popup (single event detail)
// ============================================================
var EventPopup = (function () {
-
/**
* Open event detail popup.
* @param {Element} el - Element with data-event-* attributes
@@ -180,92 +141,131 @@
function open(el, opts) {
opts = opts || {};
- var summary = el.getAttribute('data-event-summary') || '';
- var start = el.getAttribute('data-event-start') || '';
- var end = el.getAttribute('data-event-end') || '';
- var location = el.getAttribute('data-event-location') || '';
- var description = el.getAttribute('data-event-description') || '';
- var allDay = el.getAttribute('data-event-allday') === '1';
- var slot = el.getAttribute('data-event-slot') || '';
- var uid = el.getAttribute('data-event-uid') || '';
- var recurrence = el.getAttribute('data-event-recurrence') || '';
- var dateIso = el.getAttribute('data-event-date') || '';
+ var summary = el.getAttribute("data-event-summary") || "";
+ var start = el.getAttribute("data-event-start") || "";
+ var end = el.getAttribute("data-event-end") || "";
+ var location = el.getAttribute("data-event-location") || "";
+ var description = el.getAttribute("data-event-description") || "";
+ var allDay = el.getAttribute("data-event-allday") === "1";
+ var slot = el.getAttribute("data-event-slot") || "";
+ var uid = el.getAttribute("data-event-uid") || "";
+ var recurrence = el.getAttribute("data-event-recurrence") || "";
+ var dateIso = el.getAttribute("data-event-date") || "";
- var html = '";
- PopupUI.show(html);
+ getDialog().show(html);
}
function close() {
- PopupUI.close();
+ getDialog().close();
}
return { open: open, close: close };
@@ -275,65 +275,105 @@
// Day Popup (list events for a specific day)
// ============================================================
var DayPopup = (function () {
-
function open(dayCell) {
- var dateIso = dayCell.getAttribute('data-luxtools-date') || '';
+ var dateIso = dayCell.getAttribute("data-luxtools-date") || "";
if (!dateIso) return;
var events = [];
- var rawJson = dayCell.getAttribute('data-luxtools-day-events');
+ var rawJson = dayCell.getAttribute("data-luxtools-day-events");
if (rawJson) {
- try { events = JSON.parse(rawJson); } catch (e) { events = []; }
+ try {
+ events = JSON.parse(rawJson);
+ } catch (e) {
+ events = [];
+ }
}
- var html = '";
- PopupUI.show(html);
+ getDialog().show(html);
}
return { open: open };
@@ -346,11 +386,14 @@
var _calendarSlots = null;
function loadSlots(callback) {
- if (_calendarSlots) { callback(_calendarSlots); return; }
+ if (_calendarSlots) {
+ callback(_calendarSlots);
+ return;
+ }
var xhr = new XMLHttpRequest();
- xhr.open('GET', getAjaxUrl() + '?call=luxtools_calendar_slots', true);
- xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+ xhr.open("GET", getAjaxUrl() + "?call=luxtools_calendar_slots", true);
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.onload = function () {
try {
var result = JSON.parse(xhr.responseText);
@@ -373,253 +416,338 @@
function openCreate(dateIso) {
loadSlots(function (slots) {
- renderForm({
- mode: 'create',
- date: dateIso,
- summary: '',
- startTime: '',
- endTime: '',
- location: '',
- description: '',
- allDay: true,
- slot: (slots.length > 0) ? slots[0].key : 'general',
- }, slots);
+ renderForm(
+ {
+ mode: "create",
+ date: dateIso,
+ summary: "",
+ startTime: "",
+ endTime: "",
+ location: "",
+ description: "",
+ allDay: true,
+ slot: slots.length > 0 ? slots[0].key : "general",
+ },
+ slots,
+ );
});
}
function openEdit(data) {
loadSlots(function (slots) {
// Parse start/end times
- var startTime = '';
- var endTime = '';
+ var startTime = "";
+ var endTime = "";
if (!data.allDay && data.start) {
var sd = new Date(data.start);
- if (!isNaN(sd.getTime())) startTime = pad2(sd.getHours()) + ':' + pad2(sd.getMinutes());
+ if (!isNaN(sd.getTime()))
+ startTime = pad2(sd.getHours()) + ":" + pad2(sd.getMinutes());
}
if (!data.allDay && data.end) {
var ed = new Date(data.end);
- if (!isNaN(ed.getTime())) endTime = pad2(ed.getHours()) + ':' + pad2(ed.getMinutes());
+ if (!isNaN(ed.getTime()))
+ endTime = pad2(ed.getHours()) + ":" + pad2(ed.getMinutes());
}
- renderForm({
- mode: 'edit',
- uid: data.uid || '',
- recurrence: data.recurrence || '',
- date: data.date || '',
- summary: data.summary || '',
- startTime: startTime,
- endTime: endTime,
- location: data.location || '',
- description: data.description || '',
- allDay: data.allDay,
- slot: data.slot || 'general',
- }, slots);
+ renderForm(
+ {
+ mode: "edit",
+ uid: data.uid || "",
+ recurrence: data.recurrence || "",
+ date: data.date || "",
+ summary: data.summary || "",
+ startTime: startTime,
+ endTime: endTime,
+ location: data.location || "",
+ description: data.description || "",
+ allDay: data.allDay,
+ slot: data.slot || "general",
+ },
+ slots,
+ );
});
}
function renderForm(data, slots) {
- var isEdit = data.mode === 'edit';
- var title = isEdit ? 'Edit Event' : 'Create Event';
+ var isEdit = data.mode === "edit";
+ var title = isEdit ? "Edit Event" : "Create Event";
- var html = '";
html += '';
- html += '';
- html += '
';
+ html +=
+ '";
+ html += "";
- html += '';
+ html +=
+ '';
+ html += "";
+ html += "";
html += '';
- html += '';
- html += '
';
+ html +=
+ '';
+ html += "";
html += '';
- html += '';
- html += '
';
+ html +=
+ '";
+ html += "";
html += '';
html += '';
- html += '
';
+ html += "";
+ html += "";
- html += '';
+ html += '';
+ html +=
+ ' ";
+ html +=
+ '';
+ html += "
";
- html += '';
+ html += "";
- PopupUI.show(html);
+ getDialog().show(html);
// Wire up all-day checkbox toggle
- var popup = PopupUI.getPopup();
- var allDayCheckbox = popup.querySelector('.luxtools-form-allday');
- var timeFields = popup.querySelector('.luxtools-event-form-time-fields');
+ var popup = getDialog().getContainer();
+ var allDayCheckbox = popup.querySelector(".luxtools-form-allday");
+ var timeFields = popup.querySelector(".luxtools-event-form-time-fields");
if (allDayCheckbox && timeFields) {
- allDayCheckbox.addEventListener('change', function () {
- timeFields.style.display = allDayCheckbox.checked ? 'none' : '';
+ allDayCheckbox.addEventListener("change", function () {
+ timeFields.style.display = allDayCheckbox.checked ? "none" : "";
});
}
}
function collectFormData() {
- var popup = PopupUI.getPopup();
+ var popup = getDialog().getContainer();
return {
- summary: (popup.querySelector('.luxtools-form-summary') || {}).value || '',
- date: (popup.querySelector('.luxtools-form-date') || {}).value || '',
- allDay: !!(popup.querySelector('.luxtools-form-allday') || {}).checked,
- startTime: (popup.querySelector('.luxtools-form-start-time') || {}).value || '',
- endTime: (popup.querySelector('.luxtools-form-end-time') || {}).value || '',
- location: (popup.querySelector('.luxtools-form-location') || {}).value || '',
- description: (popup.querySelector('.luxtools-form-description') || {}).value || '',
- slot: (popup.querySelector('.luxtools-form-slot') || {}).value || 'general',
+ summary:
+ (popup.querySelector(".luxtools-form-summary") || {}).value || "",
+ date: (popup.querySelector(".luxtools-form-date") || {}).value || "",
+ allDay: !!(popup.querySelector(".luxtools-form-allday") || {}).checked,
+ startTime:
+ (popup.querySelector(".luxtools-form-start-time") || {}).value || "",
+ endTime:
+ (popup.querySelector(".luxtools-form-end-time") || {}).value || "",
+ location:
+ (popup.querySelector(".luxtools-form-location") || {}).value || "",
+ description:
+ (popup.querySelector(".luxtools-form-description") || {}).value || "",
+ slot:
+ (popup.querySelector(".luxtools-form-slot") || {}).value || "general",
};
}
function save(saveBtn) {
- var mode = saveBtn.getAttribute('data-mode');
+ var mode = saveBtn.getAttribute("data-mode");
var formData = collectFormData();
if (!formData.summary.trim()) {
- showNotification('Summary is required', 'error');
+ showNotification("Summary is required", "error");
return;
}
if (!formData.date) {
- showNotification('Date is required', 'error');
+ showNotification("Date is required", "error");
return;
}
// For recurring event edits, ask about scope first
- if (mode === 'edit') {
- var recurrence = saveBtn.getAttribute('data-recurrence') || '';
+ if (mode === "edit") {
+ var recurrence = saveBtn.getAttribute("data-recurrence") || "";
if (recurrence) {
showRecurrenceEditScopeDialog(saveBtn, formData);
return;
}
}
- submitSave(saveBtn, formData, 'all');
+ submitSave(saveBtn, formData, "all");
}
function showRecurrenceEditScopeDialog(saveBtn, formData) {
- var uid = saveBtn.getAttribute('data-uid') || '';
- var recurrence = saveBtn.getAttribute('data-recurrence') || '';
+ var uid = saveBtn.getAttribute("data-uid") || "";
+ var recurrence = saveBtn.getAttribute("data-recurrence") || "";
- var html = '";
// Store formData on the global scope so the scope button handler can access it
_pendingEditFormData = formData;
- PopupUI.show(html);
+ getDialog().show(html);
}
function submitSave(saveBtn, formData, scope) {
saveBtn.disabled = true;
- saveBtn.textContent = 'Saving...';
+ saveBtn.textContent = "Saving...";
- var mode = saveBtn.getAttribute('data-mode') || 'create';
+ var mode = saveBtn.getAttribute("data-mode") || "create";
- var params = 'call=luxtools_calendar_event'
- + '&action=' + encodeURIComponent(mode === 'edit' ? 'edit' : 'create')
- + '&summary=' + encodeURIComponent(formData.summary)
- + '&date=' + encodeURIComponent(formData.date)
- + '&allday=' + (formData.allDay ? '1' : '0')
- + '&start_time=' + encodeURIComponent(formData.startTime)
- + '&end_time=' + encodeURIComponent(formData.endTime)
- + '&location=' + encodeURIComponent(formData.location)
- + '&description=' + encodeURIComponent(formData.description)
- + '&slot=' + encodeURIComponent(formData.slot)
- + '§ok=' + encodeURIComponent(getSecurityToken(saveBtn));
+ var params =
+ "call=luxtools_calendar_event" +
+ "&action=" +
+ encodeURIComponent(mode === "edit" ? "edit" : "create") +
+ "&summary=" +
+ encodeURIComponent(formData.summary) +
+ "&date=" +
+ encodeURIComponent(formData.date) +
+ "&allday=" +
+ (formData.allDay ? "1" : "0") +
+ "&start_time=" +
+ encodeURIComponent(formData.startTime) +
+ "&end_time=" +
+ encodeURIComponent(formData.endTime) +
+ "&location=" +
+ encodeURIComponent(formData.location) +
+ "&description=" +
+ encodeURIComponent(formData.description) +
+ "&slot=" +
+ encodeURIComponent(formData.slot) +
+ "§ok=" +
+ encodeURIComponent(getSecurityToken(saveBtn));
- if (mode === 'edit') {
- params += '&uid=' + encodeURIComponent(saveBtn.getAttribute('data-uid') || '');
- params += '&recurrence=' + encodeURIComponent(saveBtn.getAttribute('data-recurrence') || '');
- params += '&scope=' + encodeURIComponent(scope);
+ if (mode === "edit") {
+ params +=
+ "&uid=" + encodeURIComponent(saveBtn.getAttribute("data-uid") || "");
+ params +=
+ "&recurrence=" +
+ encodeURIComponent(saveBtn.getAttribute("data-recurrence") || "");
+ params += "&scope=" + encodeURIComponent(scope);
}
var xhr = new XMLHttpRequest();
- xhr.open('POST', getAjaxUrl(), true);
- xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+ xhr.open("POST", getAjaxUrl(), true);
+ xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onload = function () {
try {
var result = JSON.parse(xhr.responseText);
if (result.ok) {
- PopupUI.close();
- showNotification(result.message || 'Saved', 'success');
+ getDialog().close();
+ showNotification(result.message || "Saved", "success");
window.location.reload();
} else {
- showNotification(result.error || 'Save failed', 'error');
+ showNotification(result.error || "Save failed", "error");
saveBtn.disabled = false;
- saveBtn.textContent = 'Save';
+ saveBtn.textContent = "Save";
}
} catch (e) {
- showNotification('Invalid response', 'error');
+ showNotification("Invalid response", "error");
saveBtn.disabled = false;
- saveBtn.textContent = 'Save';
+ saveBtn.textContent = "Save";
}
};
xhr.onerror = function () {
- showNotification('Network error', 'error');
+ showNotification("Network error", "error");
saveBtn.disabled = false;
- saveBtn.textContent = 'Save';
+ saveBtn.textContent = "Save";
};
xhr.send(params);
}
- return { openCreate: openCreate, openEdit: openEdit, save: save, submitSave: submitSave };
+ return {
+ openCreate: openCreate,
+ openEdit: openEdit,
+ save: save,
+ submitSave: submitSave,
+ };
})();
// ============================================================
// Event Deletion
// ============================================================
var EventDelete = (function () {
-
function confirmDelete(btn) {
- var uid = btn.getAttribute('data-uid') || '';
- var slot = btn.getAttribute('data-slot') || '';
- var recurrence = btn.getAttribute('data-recurrence') || '';
- var dateIso = btn.getAttribute('data-date') || '';
+ var uid = btn.getAttribute("data-uid") || "";
+ var slot = btn.getAttribute("data-slot") || "";
+ var recurrence = btn.getAttribute("data-recurrence") || "";
+ var dateIso = btn.getAttribute("data-date") || "";
if (!uid) return;
@@ -630,89 +758,129 @@
}
// Simple confirmation
- var html = '';
+ var html = '';
+ html +=
+ '
';
+ html += '
';
+ html += "
Are you sure you want to delete this event?
";
+ html += '
';
+ html +=
+ ' ";
+ html +=
+ '';
+ html += "
";
- PopupUI.show(html);
+ getDialog().show(html);
}
function showRecurrenceDeleteDialog(uid, slot, recurrence, dateIso) {
- var html = '";
- PopupUI.show(html);
+ getDialog().show(html);
}
function executeDelete(btn) {
- var uid = btn.getAttribute('data-uid') || '';
- var slot = btn.getAttribute('data-slot') || '';
- var recurrence = btn.getAttribute('data-recurrence') || '';
- var dateIso = btn.getAttribute('data-date') || '';
- var scope = btn.getAttribute('data-scope') || 'all';
+ var uid = btn.getAttribute("data-uid") || "";
+ var slot = btn.getAttribute("data-slot") || "";
+ var recurrence = btn.getAttribute("data-recurrence") || "";
+ var dateIso = btn.getAttribute("data-date") || "";
+ var scope = btn.getAttribute("data-scope") || "all";
btn.disabled = true;
- btn.textContent = 'Deleting...';
+ btn.textContent = "Deleting...";
- var params = 'call=luxtools_calendar_event'
- + '&action=delete'
- + '&uid=' + encodeURIComponent(uid)
- + '&slot=' + encodeURIComponent(slot)
- + '&recurrence=' + encodeURIComponent(recurrence)
- + '&date=' + encodeURIComponent(dateIso)
- + '&scope=' + encodeURIComponent(scope)
- + '§ok=' + encodeURIComponent(getSecurityToken(btn));
+ var params =
+ "call=luxtools_calendar_event" +
+ "&action=delete" +
+ "&uid=" +
+ encodeURIComponent(uid) +
+ "&slot=" +
+ encodeURIComponent(slot) +
+ "&recurrence=" +
+ encodeURIComponent(recurrence) +
+ "&date=" +
+ encodeURIComponent(dateIso) +
+ "&scope=" +
+ encodeURIComponent(scope) +
+ "§ok=" +
+ encodeURIComponent(getSecurityToken(btn));
var xhr = new XMLHttpRequest();
- xhr.open('POST', getAjaxUrl(), true);
- xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+ xhr.open("POST", getAjaxUrl(), true);
+ xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onload = function () {
try {
var result = JSON.parse(xhr.responseText);
if (result.ok) {
- PopupUI.close();
- showNotification(result.message || 'Deleted', 'success');
+ getDialog().close();
+ showNotification(result.message || "Deleted", "success");
window.location.reload();
} else {
- showNotification(result.error || 'Delete failed', 'error');
+ showNotification(result.error || "Delete failed", "error");
btn.disabled = false;
- btn.textContent = btn.getAttribute('data-scope') === 'all' ? 'Delete' : btn.textContent;
+ btn.textContent =
+ btn.getAttribute("data-scope") === "all"
+ ? "Delete"
+ : btn.textContent;
}
} catch (e) {
- showNotification('Invalid response', 'error');
+ showNotification("Invalid response", "error");
btn.disabled = false;
}
};
xhr.onerror = function () {
- showNotification('Network error', 'error');
+ showNotification("Network error", "error");
btn.disabled = false;
};
xhr.send(params);
@@ -725,18 +893,26 @@
// Maintenance Task Actions
// ============================================================
var MaintenanceTasks = (function () {
-
function handleAction(button) {
- var action = button.getAttribute('data-action');
+ var action = button.getAttribute("data-action");
if (!action) return;
- var item = button.closest('[data-task-uid]');
- if (!item) item = button.closest('[data-uid]');
+ var item = button.closest("[data-task-uid]");
+ if (!item) item = button.closest("[data-uid]");
if (!item) return;
- var uid = item.getAttribute('data-task-uid') || item.getAttribute('data-uid') || '';
- var date = item.getAttribute('data-task-date') || item.getAttribute('data-date') || '';
- var recurrence = item.getAttribute('data-task-recurrence') || item.getAttribute('data-recurrence') || '';
+ var uid =
+ item.getAttribute("data-task-uid") ||
+ item.getAttribute("data-uid") ||
+ "";
+ var date =
+ item.getAttribute("data-task-date") ||
+ item.getAttribute("data-date") ||
+ "";
+ var recurrence =
+ item.getAttribute("data-task-recurrence") ||
+ item.getAttribute("data-recurrence") ||
+ "";
if (!uid || !date) return;
@@ -744,50 +920,59 @@
var sectok = getSecurityToken(item);
button.disabled = true;
- button.textContent = '...';
+ button.textContent = "...";
- var params = 'call=luxtools_maintenance_task'
- + '&action=' + encodeURIComponent(action)
- + '&uid=' + encodeURIComponent(uid)
- + '&date=' + encodeURIComponent(date)
- + '&recurrence=' + encodeURIComponent(recurrence)
- + '§ok=' + encodeURIComponent(sectok);
+ var params =
+ "call=luxtools_maintenance_task" +
+ "&action=" +
+ encodeURIComponent(action) +
+ "&uid=" +
+ encodeURIComponent(uid) +
+ "&date=" +
+ encodeURIComponent(date) +
+ "&recurrence=" +
+ encodeURIComponent(recurrence) +
+ "§ok=" +
+ encodeURIComponent(sectok);
var xhr = new XMLHttpRequest();
- xhr.open('POST', ajaxUrl, true);
- xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+ xhr.open("POST", ajaxUrl, true);
+ xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onload = function () {
var result;
- try { result = JSON.parse(xhr.responseText); }
- catch (e) { result = { ok: false, error: 'Invalid response' }; }
+ try {
+ result = JSON.parse(xhr.responseText);
+ } catch (e) {
+ result = { ok: false, error: "Invalid response" };
+ }
if (result.ok) {
- if (action === 'complete') {
- item.classList.add('luxtools-task-completed');
- button.textContent = 'Reopen';
- button.setAttribute('data-action', 'reopen');
+ if (action === "complete") {
+ item.classList.add("luxtools-task-completed");
+ button.textContent = "Reopen";
+ button.setAttribute("data-action", "reopen");
} else {
- item.classList.remove('luxtools-task-completed');
- item.style.opacity = '1';
- button.textContent = 'Complete';
- button.setAttribute('data-action', 'complete');
+ item.classList.remove("luxtools-task-completed");
+ item.style.opacity = "1";
+ button.textContent = "Complete";
+ button.setAttribute("data-action", "complete");
}
button.disabled = false;
if (result.remoteOk === false && result.remoteError) {
- showNotification(result.remoteError, 'warning');
+ showNotification(result.remoteError, "warning");
}
} else {
- showNotification(result.error || 'Action failed', 'error');
- button.textContent = action === 'complete' ? 'Complete' : 'Reopen';
+ showNotification(result.error || "Action failed", "error");
+ button.textContent = action === "complete" ? "Complete" : "Reopen";
button.disabled = false;
}
};
xhr.onerror = function () {
- showNotification('Network error', 'error');
- button.textContent = action === 'complete' ? 'Complete' : 'Reopen';
+ showNotification("Network error", "error");
+ button.textContent = action === "complete" ? "Complete" : "Reopen";
button.disabled = false;
};
@@ -800,129 +985,184 @@
// ============================================================
// Event Delegation
// ============================================================
- document.addEventListener('click', function (e) {
- var target = e.target;
+ document.addEventListener(
+ "click",
+ function (e) {
+ var target = e.target;
- // Maintenance task action buttons (day pages)
- if (target.classList && target.classList.contains('luxtools-task-action')) {
- e.preventDefault();
- MaintenanceTasks.handleAction(target);
- return;
- }
-
- // Maintenance task complete buttons (syntax plugin list)
- if (target.classList && target.classList.contains('luxtools-task-complete-btn')) {
- e.preventDefault();
- MaintenanceTasks.handleAction(target);
- return;
- }
-
- // Event form save
- if (target.classList && target.classList.contains('luxtools-event-form-save')) {
- e.preventDefault();
- EventForm.save(target);
- return;
- }
-
- // Event form cancel
- if (target.classList && target.classList.contains('luxtools-event-form-cancel')) {
- e.preventDefault();
- PopupUI.close();
- return;
- }
-
- // Event create button (from day popup)
- if (target.classList && target.classList.contains('luxtools-event-create-btn')) {
- e.preventDefault();
- EventForm.openCreate(target.getAttribute('data-date') || '');
- return;
- }
-
- // Event edit button
- if (target.classList && target.classList.contains('luxtools-event-edit-btn')) {
- e.preventDefault();
- EventForm.openEdit({
- uid: target.getAttribute('data-uid') || '',
- slot: target.getAttribute('data-slot') || '',
- recurrence: target.getAttribute('data-recurrence') || '',
- date: target.getAttribute('data-date') || '',
- summary: target.getAttribute('data-summary') || '',
- start: target.getAttribute('data-start') || '',
- end: target.getAttribute('data-end') || '',
- location: target.getAttribute('data-location') || '',
- description: target.getAttribute('data-description') || '',
- allDay: target.getAttribute('data-allday') === '1',
- });
- return;
- }
-
- // Event delete button
- if (target.classList && target.classList.contains('luxtools-event-delete-btn')) {
- e.preventDefault();
- EventDelete.confirmDelete(target);
- return;
- }
-
- // Confirm delete button
- if (target.classList && target.classList.contains('luxtools-event-confirm-delete')) {
- e.preventDefault();
- EventDelete.executeDelete(target);
- return;
- }
-
- // Confirm edit scope button (recurring event edit)
- if (target.classList && target.classList.contains('luxtools-event-confirm-edit-scope')) {
- e.preventDefault();
- if (_pendingEditFormData) {
- EventForm.submitSave(target, _pendingEditFormData, target.getAttribute('data-scope') || 'all');
- _pendingEditFormData = null;
+ // Maintenance task action buttons (day pages)
+ if (
+ target.classList &&
+ target.classList.contains("luxtools-task-action")
+ ) {
+ e.preventDefault();
+ MaintenanceTasks.handleAction(target);
+ return;
}
- return;
- }
- // Event popup: clicking an event item within the day popup
- // (items inside the popup that have data-luxtools-event)
- var dayPopupEvent = target.closest ? target.closest('.luxtools-day-popup-event-item[data-luxtools-event]') : null;
- if (dayPopupEvent) {
- if (target.tagName === 'BUTTON' || (target.closest && target.closest('button'))) return;
- e.preventDefault();
- EventPopup.open(dayPopupEvent, { hideDatetime: true });
- return;
- }
+ // Maintenance task complete buttons (syntax plugin list)
+ if (
+ target.classList &&
+ target.classList.contains("luxtools-task-complete-btn")
+ ) {
+ e.preventDefault();
+ MaintenanceTasks.handleAction(target);
+ return;
+ }
- // Event popup: find closest element with data-luxtools-event
- var eventEl = target.closest ? target.closest('[data-luxtools-event]') : null;
- if (!eventEl) {
- var el = target;
- while (el && el !== document) {
- if (el.getAttribute && el.getAttribute('data-luxtools-event') === '1') {
- eventEl = el;
- break;
+ // Event form save
+ if (
+ target.classList &&
+ target.classList.contains("luxtools-event-form-save")
+ ) {
+ e.preventDefault();
+ EventForm.save(target);
+ return;
+ }
+
+ // Event form cancel
+ if (
+ target.classList &&
+ target.classList.contains("luxtools-event-form-cancel")
+ ) {
+ e.preventDefault();
+ getDialog().close();
+ return;
+ }
+
+ // Event create button (from day popup)
+ if (
+ target.classList &&
+ target.classList.contains("luxtools-event-create-btn")
+ ) {
+ e.preventDefault();
+ EventForm.openCreate(target.getAttribute("data-date") || "");
+ return;
+ }
+
+ // Event edit button
+ if (
+ target.classList &&
+ target.classList.contains("luxtools-event-edit-btn")
+ ) {
+ e.preventDefault();
+ EventForm.openEdit({
+ uid: target.getAttribute("data-uid") || "",
+ slot: target.getAttribute("data-slot") || "",
+ recurrence: target.getAttribute("data-recurrence") || "",
+ date: target.getAttribute("data-date") || "",
+ summary: target.getAttribute("data-summary") || "",
+ start: target.getAttribute("data-start") || "",
+ end: target.getAttribute("data-end") || "",
+ location: target.getAttribute("data-location") || "",
+ description: target.getAttribute("data-description") || "",
+ allDay: target.getAttribute("data-allday") === "1",
+ });
+ return;
+ }
+
+ // Event delete button
+ if (
+ target.classList &&
+ target.classList.contains("luxtools-event-delete-btn")
+ ) {
+ e.preventDefault();
+ EventDelete.confirmDelete(target);
+ return;
+ }
+
+ // Confirm delete button
+ if (
+ target.classList &&
+ target.classList.contains("luxtools-event-confirm-delete")
+ ) {
+ e.preventDefault();
+ EventDelete.executeDelete(target);
+ return;
+ }
+
+ // Confirm edit scope button (recurring event edit)
+ if (
+ target.classList &&
+ target.classList.contains("luxtools-event-confirm-edit-scope")
+ ) {
+ e.preventDefault();
+ if (_pendingEditFormData) {
+ EventForm.submitSave(
+ target,
+ _pendingEditFormData,
+ target.getAttribute("data-scope") || "all",
+ );
+ _pendingEditFormData = null;
}
- el = el.parentNode;
+ return;
}
- }
- if (eventEl) {
- if (target.tagName === 'BUTTON' || (target.closest && target.closest('button'))) return;
+ // Event popup: clicking an event item within the day popup
+ // (items inside the popup that have data-luxtools-event)
+ var dayPopupEvent = target.closest
+ ? target.closest(".luxtools-day-popup-event-item[data-luxtools-event]")
+ : null;
+ if (dayPopupEvent) {
+ if (
+ target.tagName === "BUTTON" ||
+ (target.closest && target.closest("button"))
+ )
+ return;
+ e.preventDefault();
+ EventPopup.open(dayPopupEvent, { hideDatetime: true });
+ return;
+ }
- // Check if this event is inside a day cell (calendar context)
- var dayCell = eventEl.closest ? eventEl.closest('td[data-luxtools-day="1"]') : null;
- e.preventDefault();
- EventPopup.open(eventEl, { hideDatetime: !!dayCell });
- return;
- }
+ // Event popup: find closest element with data-luxtools-event
+ var eventEl = target.closest
+ ? target.closest("[data-luxtools-event]")
+ : null;
+ if (!eventEl) {
+ var el = target;
+ while (el && el !== document) {
+ if (
+ el.getAttribute &&
+ el.getAttribute("data-luxtools-event") === "1"
+ ) {
+ eventEl = el;
+ break;
+ }
+ el = el.parentNode;
+ }
+ }
- // Day cell click: open day popup when clicking empty space
- var clickedDayCell = target.closest ? target.closest('td[data-luxtools-day="1"]') : null;
- if (clickedDayCell) {
- // Don't interfere with link clicks inside the cell
- if (target.tagName === 'A' || (target.closest && target.closest('a'))) return;
- e.preventDefault();
- DayPopup.open(clickedDayCell);
- return;
- }
- }, false);
+ if (eventEl) {
+ if (
+ target.tagName === "BUTTON" ||
+ (target.closest && target.closest("button"))
+ )
+ return;
+
+ // Check if this event is inside a day cell (calendar context)
+ var dayCell = eventEl.closest
+ ? eventEl.closest('td[data-luxtools-day="1"]')
+ : null;
+ e.preventDefault();
+ EventPopup.open(eventEl, { hideDatetime: !!dayCell });
+ return;
+ }
+
+ // Day cell click: open day popup when clicking empty space
+ var clickedDayCell = target.closest
+ ? target.closest('td[data-luxtools-day="1"]')
+ : null;
+ if (clickedDayCell) {
+ // Don't interfere with link clicks inside the cell
+ if (target.tagName === "A" || (target.closest && target.closest("a")))
+ return;
+ e.preventDefault();
+ DayPopup.open(clickedDayCell);
+ return;
+ }
+ },
+ false,
+ );
Luxtools.EventPopup = EventPopup;
Luxtools.DayPopup = DayPopup;
diff --git a/js/main.js b/js/main.js
index 61a629e..84cf290 100644
--- a/js/main.js
+++ b/js/main.js
@@ -1,7 +1,7 @@
/* global window, document */
(function () {
- 'use strict';
+ "use strict";
var Luxtools = window.Luxtools || (window.Luxtools = {});
var Lightbox = Luxtools.Lightbox;
@@ -16,7 +16,7 @@
function findOpenElement(target) {
var el = target;
while (el && el !== document) {
- if (el.classList && el.classList.contains('luxtools-open')) return el;
+ if (el.classList && el.classList.contains("luxtools-open")) return el;
el = el.parentNode;
}
return null;
@@ -25,7 +25,8 @@
function findGalleryItem(target) {
var el = target;
while (el && el !== document) {
- if (el.classList && el.classList.contains('luxtools-gallery-item')) return el;
+ if (el.classList && el.classList.contains("luxtools-gallery-item"))
+ return el;
el = el.parentNode;
}
return null;
@@ -35,7 +36,9 @@
// Image gallery lightbox: intercept clicks so we don't navigate away.
var galleryItem = findGalleryItem(event.target);
if (galleryItem && Lightbox && Lightbox.open) {
- var gallery = galleryItem.closest ? galleryItem.closest('div.luxtools-gallery[data-luxtools-gallery="1"]') : null;
+ var gallery = galleryItem.closest
+ ? galleryItem.closest('div.luxtools-gallery[data-luxtools-gallery="1"]')
+ : null;
if (gallery) {
event.preventDefault();
Lightbox.open(gallery, galleryItem);
@@ -47,49 +50,56 @@
if (!el) return;
// {{open>...}} renders as a link; avoid jumping to '#'.
- if (el.tagName && el.tagName.toLowerCase() === 'a') {
+ if (el.tagName && el.tagName.toLowerCase() === "a") {
event.preventDefault();
}
- var raw = el.getAttribute('data-path') || '';
+ var raw = el.getAttribute("data-path") || "";
if (!raw) return;
if (!OpenService || !OpenService.openViaService) return;
// Prefer local client service.
- OpenService.openViaService(el, raw)
- .catch(function (err) {
- // If the browser blocks the request before it reaches localhost (mixed-content,
- // extensions, stricter CORS handling), fall back to a no-CORS GET ping.
- if (OpenService && OpenService.pingOpenViaImage) {
- OpenService.pingOpenViaImage(el, raw);
- }
+ OpenService.openViaService(el, raw).catch(function (err) {
+ // If the browser blocks the request before it reaches localhost (mixed-content,
+ // extensions, stricter CORS handling), fall back to a no-CORS GET ping.
+ if (OpenService && OpenService.pingOpenViaImage) {
+ OpenService.pingOpenViaImage(el, raw);
+ }
- // Fallback to old behavior (often blocked in modern browsers).
- var url = OpenService && OpenService.normalizeToFileUrl ? OpenService.normalizeToFileUrl(raw) : '';
- if (!url) return;
- console.warn('Local client service failed, falling back to file:// navigation:', err);
+ // Fallback to old behavior (often blocked in modern browsers).
+ var url =
+ OpenService && OpenService.normalizeToFileUrl
+ ? OpenService.normalizeToFileUrl(raw)
+ : "";
+ if (!url) return;
+ console.warn(
+ "Local client service failed, falling back to file:// navigation:",
+ err,
+ );
+ try {
+ window.open(url, "_blank", "noopener");
+ } catch (e) {
try {
- window.open(url, '_blank', 'noopener');
- } catch (e) {
- try {
- window.location.href = url;
- } catch (e2) {
- console.error('Failed to open file URL:', e2);
- }
+ window.location.href = url;
+ } catch (e2) {
+ console.error("Failed to open file URL:", e2);
}
- });
+ }
+ });
}
function initChronologicalEventTimes() {
- var nodes = document.querySelectorAll('.luxtools-event-time[data-luxtools-start]');
+ var nodes = document.querySelectorAll(
+ ".luxtools-event-time[data-luxtools-start]",
+ );
if (!nodes || nodes.length === 0) return;
var formatter;
try {
- formatter = new Intl.DateTimeFormat('de-DE', {
- hour: '2-digit',
- minute: '2-digit',
- hour12: false
+ formatter = new Intl.DateTimeFormat("de-DE", {
+ hour: "2-digit",
+ minute: "2-digit",
+ hour12: false,
});
} catch (e) {
formatter = null;
@@ -97,7 +107,7 @@
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
- var raw = node.getAttribute('data-luxtools-start') || '';
+ var raw = node.getAttribute("data-luxtools-start") || "";
if (!raw) continue;
var date = new Date(raw);
@@ -107,9 +117,9 @@
if (formatter) {
label = formatter.format(date);
} else {
- var hh = String(date.getHours()).padStart(2, '0');
- var mm = String(date.getMinutes()).padStart(2, '0');
- label = hh + ':' + mm;
+ var hh = String(date.getHours()).padStart(2, "0");
+ var mm = String(date.getMinutes()).padStart(2, "0");
+ label = hh + ":" + mm;
}
node.textContent = label;
@@ -120,123 +130,185 @@
// Purge Cache Dialog
// ============================================================
function initPurgeCacheDialog() {
- var $link = jQuery('a.luxtools-invalidate-cache');
- if ($link.length === 0) return;
+ document.addEventListener(
+ "click",
+ function (e) {
+ var link = e.target.closest
+ ? e.target.closest("a.luxtools-invalidate-cache")
+ : null;
+ if (!link) return;
- $link.on('click.luxtools', function (e) {
- e.preventDefault();
+ e.preventDefault();
- var href = $link.attr('href') || '';
- var lang = (window.LANG && window.LANG.plugins && window.LANG.plugins.luxtools)
- ? window.LANG.plugins.luxtools
- : {};
+ var href = link.getAttribute("href") || "";
+ var lang =
+ window.LANG && window.LANG.plugins && window.LANG.plugins.luxtools
+ ? window.LANG.plugins.luxtools
+ : {};
- var $dialog = jQuery(
- '' +
- '
' + (lang.cache_purge_dialog_intro || 'The DokuWiki cache will always be purged. Optionally also purge the luxtools-specific caches:') + '
' +
- '
' +
- '
' +
- '
'
- );
+ var html = '';
+ html +=
+ '
';
+ html +=
+ '
";
+ html +=
+ "
" +
+ (lang.cache_purge_dialog_intro ||
+ "The DokuWiki cache will always be purged. Optionally also purge the luxtools-specific caches:") +
+ "
";
+ html +=
+ '
";
+ html +=
+ '
";
+ html += '
';
+ html +=
+ ' ";
+ html +=
+ '";
+ html += "
";
+ html += "
";
- $dialog.dialog({
- title: lang.cache_purge_dialog_title || 'Purge Cache',
- modal: true,
- width: 420,
- buttons: [
- {
- text: lang.cache_purge_cancel || 'Cancel',
- click: function () { $dialog.dialog('close'); }
- },
- {
- text: lang.cache_purge_confirm || 'Purge Cache',
- click: function () {
- var url = href;
- if ($dialog.find('#luxtools-purge-pagelinks').prop('checked')) {
- url += '&luxtools_purge_pagelinks=1';
- }
- if ($dialog.find('#luxtools-purge-thumbs').prop('checked')) {
- url += '&luxtools_purge_thumbs=1';
- }
- $dialog.dialog('close');
- window.location.href = url;
- }
- }
- ],
- close: function () {
- jQuery(this).dialog('destroy').remove();
+ Luxtools.Dialog.show(html);
+
+ var container = Luxtools.Dialog.getContainer();
+
+ var cancelBtn = container.querySelector(".luxtools-purge-cancel");
+ if (cancelBtn) {
+ cancelBtn.addEventListener("click", function () {
+ Luxtools.Dialog.close();
+ });
}
- });
- });
+
+ var confirmBtn = container.querySelector(".luxtools-purge-confirm");
+ if (confirmBtn) {
+ confirmBtn.addEventListener("click", function () {
+ var url = href;
+ var plCheck = container.querySelector("#luxtools-purge-pagelinks");
+ var thCheck = container.querySelector("#luxtools-purge-thumbs");
+ if (plCheck && plCheck.checked) {
+ url += "&luxtools_purge_pagelinks=1";
+ }
+ if (thCheck && thCheck.checked) {
+ url += "&luxtools_purge_thumbs=1";
+ }
+ Luxtools.Dialog.close();
+ window.location.href = url;
+ });
+ }
+ },
+ false,
+ );
}
// ============================================================
// Calendar Sync Button (syntax widget)
// ============================================================
function initCalendarSyncButtons() {
- document.addEventListener('click', function (e) {
- var btn = e.target;
- if (!btn || !btn.classList || !btn.classList.contains('luxtools-calendar-sync-btn')) return;
+ document.addEventListener(
+ "click",
+ function (e) {
+ var btn = e.target;
+ if (
+ !btn ||
+ !btn.classList ||
+ !btn.classList.contains("luxtools-calendar-sync-btn")
+ )
+ return;
- e.preventDefault();
+ e.preventDefault();
- var ajaxUrl = btn.getAttribute('data-luxtools-ajax-url') || '';
- var sectok = btn.getAttribute('data-luxtools-sectok') || '';
- if (!ajaxUrl) return;
+ var ajaxUrl = btn.getAttribute("data-luxtools-ajax-url") || "";
+ var sectok = btn.getAttribute("data-luxtools-sectok") || "";
+ if (!ajaxUrl) return;
- var status = btn.parentNode ? btn.parentNode.querySelector('.luxtools-calendar-sync-status') : null;
+ var status = btn.parentNode
+ ? btn.parentNode.querySelector(".luxtools-calendar-sync-status")
+ : null;
- btn.disabled = true;
- if (status) {
- status.textContent = 'Syncing...';
- status.style.color = '';
- }
-
- var xhr = new XMLHttpRequest();
- xhr.open('POST', ajaxUrl, true);
- xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
- xhr.onload = function () {
- btn.disabled = false;
- try {
- var r = JSON.parse(xhr.responseText);
- if (status) {
- status.textContent = r.message || (r.ok ? 'Done' : 'Failed');
- status.style.color = r.ok ? 'green' : 'red';
- }
- } catch (ex) {
- if (status) {
- status.textContent = 'Error';
- status.style.color = 'red';
- }
- }
- };
- xhr.onerror = function () {
- btn.disabled = false;
+ btn.disabled = true;
if (status) {
- status.textContent = 'Network error';
- status.style.color = 'red';
+ status.textContent = "Syncing...";
+ status.style.color = "";
}
- };
- xhr.send('call=luxtools_calendar_sync§ok=' + encodeURIComponent(sectok));
- }, false);
+
+ var xhr = new XMLHttpRequest();
+ xhr.open("POST", ajaxUrl, true);
+ xhr.setRequestHeader(
+ "Content-Type",
+ "application/x-www-form-urlencoded",
+ );
+ xhr.onload = function () {
+ btn.disabled = false;
+ try {
+ var r = JSON.parse(xhr.responseText);
+ if (status) {
+ status.textContent = r.message || (r.ok ? "Done" : "Failed");
+ status.style.color = r.ok ? "green" : "red";
+ }
+ } catch (ex) {
+ if (status) {
+ status.textContent = "Error";
+ status.style.color = "red";
+ }
+ }
+ };
+ xhr.onerror = function () {
+ btn.disabled = false;
+ if (status) {
+ status.textContent = "Network error";
+ status.style.color = "red";
+ }
+ };
+ xhr.send(
+ "call=luxtools_calendar_sync§ok=" + encodeURIComponent(sectok),
+ );
+ },
+ false,
+ );
}
// ============================================================
// Initialize
// ============================================================
- document.addEventListener('click', onClick, false);
- document.addEventListener('DOMContentLoaded', function () {
- if (GalleryThumbnails && GalleryThumbnails.init) GalleryThumbnails.init();
- initChronologicalEventTimes();
- if (CalendarWidget && CalendarWidget.init) CalendarWidget.init();
- initPurgeCacheDialog();
- initCalendarSyncButtons();
- }, false);
- document.addEventListener('DOMContentLoaded', function () {
- if (Scratchpads && Scratchpads.init) Scratchpads.init();
- }, false);
+ document.addEventListener("click", onClick, false);
+ document.addEventListener(
+ "DOMContentLoaded",
+ function () {
+ if (GalleryThumbnails && GalleryThumbnails.init) GalleryThumbnails.init();
+ initChronologicalEventTimes();
+ if (CalendarWidget && CalendarWidget.init) CalendarWidget.init();
+ initPurgeCacheDialog();
+ initCalendarSyncButtons();
+ },
+ false,
+ );
+ document.addEventListener(
+ "DOMContentLoaded",
+ function () {
+ if (Scratchpads && Scratchpads.init) Scratchpads.init();
+ },
+ false,
+ );
Luxtools.initChronologicalEventTimes = initChronologicalEventTimes;
})();
diff --git a/style.css b/style.css
index d9336e5..d91986d 100644
--- a/style.css
+++ b/style.css
@@ -1,3 +1,5 @@
+/* Dialog infrastructure styles are in dialog.css, loaded via CSS_STYLES_INCLUDED hook in action.php */
+
/* luxtools plugin styles
* Keep this minimal and scoped to the plugin container.
*/
@@ -7,7 +9,6 @@ div.luxtools-plugin table thead tr:hover > * {
background-color: @ini_background_alt !important;
}
-
/* "Open Location" row above the header should be visually smaller. */
div.luxtools-plugin table thead tr.luxtools-openlocation-row td {
font-size: 80%;
@@ -229,8 +230,12 @@ div.plugin_luxtools_admin form.plugin_luxtools_admin_form label.block > br {
}
div.plugin_luxtools_admin form.plugin_luxtools_admin_form textarea.edit,
-div.plugin_luxtools_admin form.plugin_luxtools_admin_form input[type="text"].edit,
-div.plugin_luxtools_admin form.plugin_luxtools_admin_form input[type="number"].edit,
+div.plugin_luxtools_admin
+ form.plugin_luxtools_admin_form
+ input[type="text"].edit,
+div.plugin_luxtools_admin
+ form.plugin_luxtools_admin_form
+ input[type="number"].edit,
div.plugin_luxtools_admin form.plugin_luxtools_admin_form select {
flex: 1 1 auto;
margin-left: auto;
@@ -249,7 +254,10 @@ div.plugin_luxtools_admin form.plugin_luxtools_admin_form select {
}
/* Checkbox controls: keep them in the control column, left-aligned. */
-div.plugin_luxtools_admin form.plugin_luxtools_admin_form label.block input[type="checkbox"] {
+div.plugin_luxtools_admin
+ form.plugin_luxtools_admin_form
+ label.block
+ input[type="checkbox"] {
margin-left: 0;
align-self: center;
}
@@ -265,8 +273,12 @@ div.plugin_luxtools_admin form.plugin_luxtools_admin_form label.block input[type
}
div.plugin_luxtools_admin form.plugin_luxtools_admin_form textarea.edit,
- div.plugin_luxtools_admin form.plugin_luxtools_admin_form input[type="text"].edit,
- div.plugin_luxtools_admin form.plugin_luxtools_admin_form input[type="number"].edit,
+ div.plugin_luxtools_admin
+ form.plugin_luxtools_admin_form
+ input[type="text"].edit,
+ div.plugin_luxtools_admin
+ form.plugin_luxtools_admin_form
+ input[type="number"].edit,
div.plugin_luxtools_admin form.plugin_luxtools_admin_form select {
width: 100%;
max-width: 100%;
@@ -386,12 +398,12 @@ html.luxtools-noscroll body {
}
.luxtools-lightbox button.luxtools-lightbox-zone-prev::after {
- content: '‹';
+ content: "‹";
left: 0.35em;
}
.luxtools-lightbox button.luxtools-lightbox-zone-next::after {
- content: '›';
+ content: "›";
right: 0.35em;
}
@@ -416,7 +428,7 @@ html.luxtools-noscroll body {
.luxtools-lightbox button.luxtools-lightbox-close:hover,
.luxtools-lightbox button.luxtools-lightbox-close:focus-visible {
- background: rgba(0, 0, 0, 0.60);
+ background: rgba(0, 0, 0, 0.6);
border-radius: 999px;
}
@@ -432,7 +444,10 @@ html.luxtools-noscroll body {
.luxtools-grouping {
display: grid;
- grid-template-columns: repeat(var(--luxtools-grouping-cols, 2), minmax(0, 1fr));
+ grid-template-columns: repeat(
+ var(--luxtools-grouping-cols, 2),
+ minmax(0, 1fr)
+ );
gap: var(--luxtools-grouping-gap, 0);
justify-content: var(--luxtools-grouping-justify, start);
align-items: var(--luxtools-grouping-align, start);
@@ -604,7 +619,10 @@ div.luxtools-calendar td.luxtools-calendar-day-today {
}
div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a {
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > span.curid
+ > a {
display: flex;
align-items: center;
justify-content: center;
@@ -617,33 +635,77 @@ div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > sp
padding: 0.1em 0;
}
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a.wikilink2:link,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a.wikilink2:visited,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a.wikilink2:link,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a.wikilink2:visited {
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > a.wikilink2:link,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > a.wikilink2:visited,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > span.curid
+ > a.wikilink2:link,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > span.curid
+ > a.wikilink2:visited {
color: @ini_missing;
border-bottom: 0;
- }
+}
-
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a:hover,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a:focus,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a:active,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > a:visited,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:hover,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:focus,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:active,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:visited {
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > a:hover,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > a:focus,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > a:active,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > a:visited,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > span.curid
+ > a:hover,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > span.curid
+ > a:focus,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > span.curid
+ > a:active,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > span.curid
+ > a:visited {
text-decoration: none;
border-bottom: 0;
box-shadow: none;
}
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:visited,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:hover,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:focus,
-div.luxtools-calendar.luxtools-calendar-size-small td.luxtools-calendar-day > span.curid > a:active {
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > span.curid
+ > a,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > span.curid
+ > a:visited,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > span.curid
+ > a:hover,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > span.curid
+ > a:focus,
+div.luxtools-calendar.luxtools-calendar-size-small
+ td.luxtools-calendar-day
+ > span.curid
+ > a:active {
font-weight: bold;
text-decoration: underline;
border-bottom: 0;
@@ -654,7 +716,8 @@ div.luxtools-calendar td.luxtools-calendar-day:hover {
background-color: @ini_background_alt;
}
-div.luxtools-calendar td.luxtools-calendar-day.luxtools-calendar-day-today:hover {
+div.luxtools-calendar
+ td.luxtools-calendar-day.luxtools-calendar-day-today:hover {
background-color: @ini_highlight;
}
@@ -668,7 +731,9 @@ div.luxtools-calendar td.luxtools-calendar-day {
position: relative;
}
-div.luxtools-calendar.luxtools-calendar-size-large table.luxtools-calendar-table td {
+div.luxtools-calendar.luxtools-calendar-size-large
+ table.luxtools-calendar-table
+ td {
text-align: left;
vertical-align: top;
}
@@ -677,25 +742,36 @@ div.luxtools-calendar.luxtools-calendar-size-large td.luxtools-calendar-day {
height: 8.25em;
}
-div.luxtools-calendar.luxtools-calendar-size-large td.luxtools-calendar-day-empty {
+div.luxtools-calendar.luxtools-calendar-size-large
+ td.luxtools-calendar-day-empty {
height: 8.25em;
}
-div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-frame {
+div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-day-frame {
min-height: 8.25em;
padding: 0.35em 0.4em 0.4em 0.4em;
box-sizing: border-box;
}
-div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number {
+div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-day-number {
text-align: right;
margin-bottom: 0.25em;
line-height: 1.1;
}
-div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number > a,
-div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number > span.curid > a,
-div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number span.curid > a {
+div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-day-number
+ > a,
+div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-day-number
+ > span.curid
+ > a,
+div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-day-number
+ span.curid
+ > a {
display: inline;
min-height: 0;
padding: 0;
@@ -706,18 +782,30 @@ div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number
font-weight: bold;
}
-div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number > a.wikilink2:link,
-div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number > a.wikilink2:visited,
-div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number span.curid > a.wikilink2:link,
-div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-number span.curid > a.wikilink2:visited {
+div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-day-number
+ > a.wikilink2:link,
+div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-day-number
+ > a.wikilink2:visited,
+div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-day-number
+ span.curid
+ > a.wikilink2:link,
+div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-day-number
+ span.curid
+ > a.wikilink2:visited {
color: @ini_missing;
}
-div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-events {
+div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-day-events {
overflow: hidden;
}
-div.luxtools-calendar.luxtools-calendar-size-large ul.luxtools-calendar-event-list {
+div.luxtools-calendar.luxtools-calendar-size-large
+ ul.luxtools-calendar-event-list {
list-style: none;
margin: 0;
padding: 0;
@@ -735,17 +823,20 @@ div.luxtools-calendar.luxtools-calendar-size-large li.luxtools-calendar-event {
cursor: pointer;
}
-div.luxtools-calendar.luxtools-calendar-size-large li.luxtools-calendar-event:hover {
+div.luxtools-calendar.luxtools-calendar-size-large
+ li.luxtools-calendar-event:hover {
background-color: @ini_highlight;
}
-div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-event-time {
+div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-event-time {
flex: 0 0 auto;
font-weight: bold;
white-space: nowrap;
}
-div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-event-title {
+div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-event-title {
flex: 1 1 auto;
min-width: 0;
white-space: nowrap;
@@ -753,7 +844,8 @@ div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-event-titl
text-overflow: ellipsis;
}
-div.luxtools-calendar.luxtools-calendar-size-large li.luxtools-calendar-event-more {
+div.luxtools-calendar.luxtools-calendar-size-large
+ li.luxtools-calendar-event-more {
border-left-color: @ini_border;
justify-content: flex-end;
font-style: italic;
@@ -761,8 +853,10 @@ div.luxtools-calendar.luxtools-calendar-size-large li.luxtools-calendar-event-mo
@media (max-width: 800px) {
div.luxtools-calendar.luxtools-calendar-size-large td.luxtools-calendar-day,
- div.luxtools-calendar.luxtools-calendar-size-large td.luxtools-calendar-day-empty,
- div.luxtools-calendar.luxtools-calendar-size-large .luxtools-calendar-day-frame {
+ div.luxtools-calendar.luxtools-calendar-size-large
+ td.luxtools-calendar-day-empty,
+ div.luxtools-calendar.luxtools-calendar-size-large
+ .luxtools-calendar-day-frame {
height: 7em;
min-height: 7em;
}
@@ -807,7 +901,6 @@ div.luxtools-calendar.luxtools-calendar-size-large li.luxtools-calendar-event-mo
clip-path: polygon(0 0, 0 100%, 100% 100%);
}
-
/* ============================================================
* Chronological Events on Day Pages
* ============================================================ */
@@ -833,7 +926,6 @@ div.luxtools-chronological-events li[data-luxtools-event] .luxtools-event-time {
margin-right: 0.25em;
}
-
/* ============================================================
* Maintenance Tasks
* ============================================================ */
@@ -868,7 +960,6 @@ button.luxtools-task-complete-btn:disabled {
cursor: wait;
}
-
/* ============================================================
* Maintenance Task List (syntax plugin)
* ============================================================ */
@@ -907,61 +998,10 @@ li.luxtools-task-overdue .luxtools-task-date {
text-decoration: line-through;
}
-
/* ============================================================
- * Event Popup
+ * Event Popup (content-specific styles – structural dialog
+ * styles live in dialog.css)
* ============================================================ */
-.luxtools-event-popup-overlay {
- display: none;
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.4);
- z-index: 10000;
- justify-content: center;
- align-items: center;
-}
-
-.luxtools-event-popup {
- background: @ini_background;
- border: 1px solid @ini_border;
- border-radius: 0.4em;
- padding: 1.5em;
- max-width: 500px;
- width: 90%;
- max-height: 80vh;
- overflow-y: auto;
- position: relative;
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
-}
-
-.luxtools-event-popup-close {
- position: absolute;
- top: 0.5em;
- right: 0.75em;
- background: none;
- border: none;
- font-size: 1.5em;
- cursor: pointer;
- color: @ini_text;
- line-height: 1;
-}
-
-.luxtools-event-popup-close:hover {
- opacity: 0.7;
-}
-
-.luxtools-event-popup-title {
- margin: 0 0 0.75em 0;
- padding-right: 1.5em;
-}
-
-.luxtools-event-popup-field {
- margin: 0.5em 0;
-}
-
.luxtools-event-popup-description {
white-space: pre-wrap;
word-break: break-word;
@@ -973,16 +1013,6 @@ li.luxtools-task-overdue .luxtools-task-date {
font-size: 0.9em;
}
-/* Event popup action buttons */
-.luxtools-event-popup-actions {
- margin-top: 1em;
- padding-top: 0.75em;
- border-top: 1px solid @ini_border;
- display: flex;
- flex-wrap: wrap;
- gap: 0.5em;
-}
-
.luxtools-recurrence-actions {
flex-direction: column;
}
@@ -1051,7 +1081,6 @@ td.luxtools-calendar-day[data-luxtools-day] {
cursor: pointer;
}
-
/* ============================================================
* Notifications (fallback)
* ============================================================ */