From d33c7a748bb9caf3b928c4b062cd3e4285adfe51 Mon Sep 17 00:00:00 2001 From: luxick Date: Fri, 3 Apr 2026 14:53:05 +0200 Subject: [PATCH] Remove maintenance feature --- action.php | 277 +--------------------------------------- admin/main.php | 20 +-- conf/default.php | 14 +- js/event-popup.js | 118 +---------------- lang/de/lang.php | 8 -- lang/en/lang.php | 8 -- src/CalendarEvent.php | 33 +---- src/CalendarService.php | 161 +---------------------- src/CalendarSlot.php | 10 +- style.css | 74 +---------- syntax/maintenance.php | 194 ---------------------------- 11 files changed, 28 insertions(+), 889 deletions(-) delete mode 100644 syntax/maintenance.php diff --git a/action.php b/action.php index 03b25bf..d031f2e 100644 --- a/action.php +++ b/action.php @@ -82,12 +82,6 @@ class action_plugin_luxtools extends ActionPlugin $this, "handleCalendarWidgetAjax", ); - $controller->register_hook( - "AJAX_CALL_UNKNOWN", - "BEFORE", - $this, - "handleMaintenanceTaskAction", - ); $controller->register_hook( "AJAX_CALL_UNKNOWN", "BEFORE", @@ -753,21 +747,8 @@ class action_plugin_luxtools extends ActionPlugin ); } - // 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, - ); - } - - // Render slot3/slot4 if present - foreach (["slot3", "slot4"] as $slotKey) { + // Render slot2/slot3/slot4 if present + foreach (["slot2", "slot3", "slot4"] as $slotKey) { if (isset($grouped[$slotKey]) && isset($slots[$slotKey])) { $label = $slots[$slotKey]->getLabel(); $html .= $this->renderEventSection( @@ -814,51 +795,6 @@ class action_plugin_luxtools extends ActionPlugin ""; } - /** - * Render a maintenance task section with completion buttons. - * - * @param CalendarEvent[] $events - * @param string $title - * @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"; - - foreach ($events as $event) { - $items .= $this->renderMaintenanceListItem($event, $ajaxUrl); - } - if ($items === "") { - return ""; - } - - $secToken = function_exists("getSecurityToken") - ? getSecurityToken() - : ""; - - return '
' . - "

" . - hsc($title) . - "

" . - "" . - "
"; - } - /** * Render a single event as a list item with popup data attributes. * @@ -921,215 +857,6 @@ class action_plugin_luxtools extends ActionPlugin ""; } - /** - * Render a maintenance task as a list item with completion button. - * - * @param CalendarEvent $event - * @param string $ajaxUrl - * @return string - */ - protected function renderMaintenanceListItem( - CalendarEvent $event, - string $ajaxUrl, - ): string { - $isCompleted = $event->isCompleted(); - $classes = "luxtools-maintenance-task"; - if ($isCompleted) { - $classes .= " luxtools-task-completed"; - } - - $summaryHtml = hsc($event->summary); - - // Data attributes for popup and completion - $dataAttrs = ' data-luxtools-event="1"'; - $dataAttrs .= ' data-event-summary="' . hsc($event->summary) . '"'; - $dataAttrs .= ' data-event-start="' . hsc($event->startIso) . '"'; - if ($event->endIso !== "") { - $dataAttrs .= ' data-event-end="' . hsc($event->endIso) . '"'; - } - if ($event->location !== "") { - $dataAttrs .= - ' data-event-location="' . hsc($event->location) . '"'; - } - if ($event->description !== "") { - $dataAttrs .= - ' data-event-description="' . hsc($event->description) . '"'; - } - $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-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"; - - $buttonHtml = - '"; - - $timeHtml = ""; - if (!$event->allDay && $event->time !== "") { - $timeHtml = - '' . - hsc($event->time) . - " - "; - } - - return '
  • " . - $timeHtml . - '' . - $summaryHtml . - " " . - $buttonHtml . - "
  • "; - } - - /** - * Handle AJAX requests for marking maintenance tasks complete/reopen. - * - * @param Event $event - * @param mixed $param - * @return void - */ - public function handleMaintenanceTaskAction(Event $event, $param) - { - if ($event->data !== "luxtools_maintenance_task") { - return; - } - - $event->preventDefault(); - $event->stopPropagation(); - - header("Content-Type: application/json; charset=utf-8"); - $this->sendNoStoreHeaders(); - - global $INPUT; - - // Verify security token - if (!checkSecurityToken()) { - http_status(403); - 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"); - - if (!in_array($action, ["complete", "reopen"], true)) { - http_status(400); - echo json_encode(["ok" => false, "error" => "Invalid action"]); - return; - } - if ($uid === "" || !ChronoID::isIsoDate($dateIso)) { - http_status(400); - echo json_encode(["ok" => false, "error" => "Missing uid or date"]); - return; - } - - $slots = CalendarSlot::loadAll($this); - $maintenanceSlot = $slots["maintenance"] ?? null; - if ($maintenanceSlot === null || !$maintenanceSlot->isEnabled()) { - http_status(400); - echo json_encode([ - "ok" => false, - "error" => "Maintenance calendar not configured", - ]); - return; - } - - $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 (!$localOk) { - http_status(500); - echo json_encode([ - "ok" => false, - "error" => $this->getLang("maintenance_complete_error"), - ]); - return; - } - - // Clear caches so next render picks up changes - CalendarService::clearCache(); - - // Remote CalDAV write-back if configured - $remoteOk = true; - $remoteError = ""; - if ($maintenanceSlot->hasRemoteSource()) { - try { - $caldavResult = CalDavClient::updateEventStatus( - $maintenanceSlot->getCaldavUrl(), - $maintenanceSlot->getUsername(), - $maintenanceSlot->getPassword(), - $uid, - $recurrence, - $newStatus, - $dateIso, - ); - if ($caldavResult !== "") { - $remoteOk = false; - $remoteError = - $this->getLang("maintenance_remote_write_failed") . - ": " . - $caldavResult; - } - } catch (Throwable $e) { - $remoteOk = false; - $remoteError = - $this->getLang("maintenance_remote_write_failed") . - ": " . - $e->getMessage(); - } - } - - $msg = - $action === "complete" - ? $this->getLang("maintenance_complete_success") - : $this->getLang("maintenance_reopen_success"); - - echo json_encode([ - "ok" => true, - "message" => $msg, - "remoteOk" => $remoteOk, - "remoteError" => $remoteError, - ]); - } - /** * Handle AJAX requests for manual calendar sync. * diff --git a/admin/main.php b/admin/main.php index 85560fd..e0bad8a 100644 --- a/admin/main.php +++ b/admin/main.php @@ -9,7 +9,7 @@ if (!defined('DOKU_INC')) die(); class admin_plugin_luxtools_main extends DokuWiki_Admin_Plugin { /** @var string[] Calendar slot keys */ - protected $calendarSlotKeys = ['general', 'maintenance', 'slot3', 'slot4']; + protected $calendarSlotKeys = ['general', 'slot2', 'slot3', 'slot4']; /** @var string[] */ protected $configKeys = [ @@ -38,12 +38,12 @@ class admin_plugin_luxtools_main extends DokuWiki_Admin_Plugin 'calendar_general_password', 'calendar_general_color', 'calendar_general_display', - 'calendar_maintenance_file', - 'calendar_maintenance_caldav_url', - 'calendar_maintenance_username', - 'calendar_maintenance_password', - 'calendar_maintenance_color', - 'calendar_maintenance_display', + 'calendar_slot2_file', + 'calendar_slot2_caldav_url', + 'calendar_slot2_username', + 'calendar_slot2_password', + 'calendar_slot2_color', + 'calendar_slot2_display', 'calendar_slot3_file', 'calendar_slot3_caldav_url', 'calendar_slot3_username', @@ -278,9 +278,9 @@ class admin_plugin_luxtools_main extends DokuWiki_Admin_Plugin // Calendar slot settings $slotLabels = [ 'general' => 'General', - 'maintenance' => 'Maintenance', - 'slot3' => 'Slot 3', - 'slot4' => 'Slot 4', + 'slot2' => 'Slot 2', + 'slot3' => 'Slot 3', + 'slot4' => 'Slot 4', ]; $displayOptions = [ 'none' => (string)$this->getLang('calendar_slot_display_none'), diff --git a/conf/default.php b/conf/default.php index 8dce750..7acbbe3 100644 --- a/conf/default.php +++ b/conf/default.php @@ -37,7 +37,7 @@ $conf['open_service_url'] = 'http://127.0.0.1:8765'; // Base filesystem path for chronological photo integration. $conf['image_base_path'] = ''; -// Calendar slot configuration (4 slots: general, maintenance, slot3, slot4) +// Calendar slot configuration (4 slots: general, slot2, slot3, slot4) // Each slot has: file, caldav_url, username, password, color, display $conf['calendar_general_file'] = ''; $conf['calendar_general_caldav_url'] = ''; @@ -46,12 +46,12 @@ $conf['calendar_general_password'] = ''; $conf['calendar_general_color'] = '#4a90d9'; $conf['calendar_general_display'] = 'none'; -$conf['calendar_maintenance_file'] = ''; -$conf['calendar_maintenance_caldav_url'] = ''; -$conf['calendar_maintenance_username'] = ''; -$conf['calendar_maintenance_password'] = ''; -$conf['calendar_maintenance_color'] = '#e67e22'; -$conf['calendar_maintenance_display'] = 'none'; +$conf['calendar_slot2_file'] = ''; +$conf['calendar_slot2_caldav_url'] = ''; +$conf['calendar_slot2_username'] = ''; +$conf['calendar_slot2_password'] = ''; +$conf['calendar_slot2_color'] = '#e67e22'; +$conf['calendar_slot2_display'] = 'none'; $conf['calendar_slot3_file'] = ''; $conf['calendar_slot3_caldav_url'] = ''; diff --git a/js/event-popup.js b/js/event-popup.js index a7da062..8ed6434 100644 --- a/js/event-popup.js +++ b/js/event-popup.js @@ -1,14 +1,12 @@ /* global window, document, jQuery */ /** - * Event Popup, Day Popup, Event CRUD, and Maintenance Task Handling + * Event Popup, Day Popup, and Event CRUD * * - Clicking an event item with data-luxtools-event="1" opens a detail popup. * - Clicking empty space in a calendar day cell opens a day popup listing all events. * - Day popup includes a "Create Event" action for authenticated users. * - Event popup includes "Edit" and "Delete" actions for authenticated users. - * - Clicking a maintenance task action button sends an AJAX request to - * complete/reopen the task. */ (function () { "use strict"; @@ -889,99 +887,6 @@ return { confirmDelete: confirmDelete, executeDelete: executeDelete }; })(); - // ============================================================ - // Maintenance Task Actions - // ============================================================ - var MaintenanceTasks = (function () { - function handleAction(button) { - var action = button.getAttribute("data-action"); - if (!action) return; - - 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") || - ""; - - if (!uid || !date) return; - - var ajaxUrl = getAjaxUrl(); - var sectok = getSecurityToken(item); - - button.disabled = true; - button.textContent = "..."; - - 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.onload = function () { - var result; - 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"); - } else { - 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"); - } - } else { - 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"; - button.disabled = false; - }; - - xhr.send(params); - } - - return { handleAction: handleAction }; - })(); - // ============================================================ // Event Delegation // ============================================================ @@ -990,26 +895,6 @@ 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 && @@ -1166,5 +1051,4 @@ Luxtools.EventPopup = EventPopup; Luxtools.DayPopup = DayPopup; - Luxtools.MaintenanceTasks = MaintenanceTasks; })(); diff --git a/lang/de/lang.php b/lang/de/lang.php index 9f5e89f..94c9c91 100644 --- a/lang/de/lang.php +++ b/lang/de/lang.php @@ -95,14 +95,6 @@ $lang["pagelink_unlinked"] = "Seite nicht verknüpft"; $lang["pagelink_multi_warning"] = "Mehrere Ordner verknüpft"; $lang["chronological_photos_title"] = "Fotos"; $lang["chronological_events_title"] = "Termine"; -$lang["chronological_maintenance_title"] = "Aufgaben"; -$lang["maintenance_task_complete"] = "Erledigen"; -$lang["maintenance_task_reopen"] = "Wieder öffnen"; -$lang["maintenance_no_tasks"] = "Keine offenen Aufgaben."; -$lang["maintenance_complete_success"] = "Aufgabe als erledigt markiert."; -$lang["maintenance_complete_error"] = "Aktualisierung der Aufgabe fehlgeschlagen."; -$lang["maintenance_reopen_success"] = "Aufgabe wieder geöffnet."; -$lang["maintenance_remote_write_failed"] = "Lokale Aktualisierung erfolgreich, aber CalDAV-Update fehlgeschlagen. Wird bei nächster Synchronisierung erneut versucht."; $lang["calendar_sync_button"] = "Kalender synchronisieren"; $lang["calendar_sync_success"] = "Kalender-Synchronisierung abgeschlossen."; $lang["calendar_sync_error"] = "Kalender-Synchronisierung fehlgeschlagen."; diff --git a/lang/en/lang.php b/lang/en/lang.php index f7e78c6..4ab8705 100644 --- a/lang/en/lang.php +++ b/lang/en/lang.php @@ -96,14 +96,6 @@ $lang["pagelink_multi_warning"] = "Multiple folders linked"; $lang["calendar_err_badmonth"] = "Invalid calendar month. Use YYYY-MM."; $lang["chronological_photos_title"] = "Photos"; $lang["chronological_events_title"] = "Events"; -$lang["chronological_maintenance_title"] = "Tasks"; -$lang["maintenance_task_complete"] = "Complete"; -$lang["maintenance_task_reopen"] = "Reopen"; -$lang["maintenance_no_tasks"] = "No open tasks."; -$lang["maintenance_complete_success"] = "Task marked as completed."; -$lang["maintenance_complete_error"] = "Failed to update task."; -$lang["maintenance_reopen_success"] = "Task reopened."; -$lang["maintenance_remote_write_failed"] = "Local update succeeded, but remote CalDAV update failed. Will retry on next sync."; $lang["calendar_sync_button"] = "Sync Calendars"; $lang["calendar_sync_success"] = "Calendar sync completed."; $lang["calendar_sync_error"] = "Calendar sync failed."; diff --git a/src/CalendarEvent.php b/src/CalendarEvent.php index 44a8c01..dcdef58 100644 --- a/src/CalendarEvent.php +++ b/src/CalendarEvent.php @@ -10,7 +10,7 @@ namespace dokuwiki\plugin\luxtools; */ class CalendarEvent { - /** @var string Calendar slot key (e.g. 'general', 'maintenance') */ + /** @var string Calendar slot key (e.g. 'general', 'slot2') */ public $slotKey; /** @var string Unique source event UID */ @@ -52,35 +52,4 @@ class CalendarEvent /** @var string The date (YYYY-MM-DD) this event applies to */ public $dateIso; - - /** - * Build a stable completion key for maintenance task tracking. - * - * @return string - */ - public function completionKey(): string - { - return implode('|', [$this->slotKey, $this->uid, $this->dateIso]); - } - - /** - * Whether this event/task is marked as completed. - * - * @return bool - */ - public function isCompleted(): bool - { - $s = strtoupper($this->status); - return $s === 'COMPLETED'; - } - - /** - * Whether this event/task is open (for maintenance filtering). - * - * @return bool - */ - public function isOpen(): bool - { - return !$this->isCompleted(); - } } diff --git a/src/CalendarService.php b/src/CalendarService.php index d650e33..e7b9a9d 100644 --- a/src/CalendarService.php +++ b/src/CalendarService.php @@ -14,17 +14,13 @@ use Throwable; /** * Slot-aware calendar service. * - * Provides normalized event data grouped by slot for rendering, - * widget indicators, task list queries, and completion tracking. + * Provides normalized event data grouped by slot for rendering and widget indicators. */ class CalendarService { /** @var array In-request cache keyed by "slotKey|dateIso" */ protected static $dayCache = []; - /** @var array In-request cache keyed by "slotKey|all" for open tasks */ - protected static $taskCache = []; - /** @var array In-request cache keyed by file path */ protected static $vcalCache = []; @@ -111,81 +107,6 @@ class CalendarService return self::slotEventsForDate($slot, $dateIso) !== []; } - /** - * Get all open maintenance tasks due up to (and including) today. - * - * @param CalendarSlot $maintenanceSlot - * @param string $todayIso YYYY-MM-DD - * @param int $pastDays Maximum number of overdue days to include - * @return CalendarEvent[] Sorted: overdue first, then today, then by title - */ - public static function openMaintenanceTasks(CalendarSlot $maintenanceSlot, string $todayIso, int $pastDays = 30): array - { - if (!$maintenanceSlot->isEnabled()) return []; - if (!ChronoID::isIsoDate($todayIso)) return []; - - $pastDays = max(0, $pastDays); - - $file = $maintenanceSlot->getFile(); - if ($file === '' || !is_file($file) || !is_readable($file)) return []; - - $cacheKey = $maintenanceSlot->getKey() . '|tasks|' . $todayIso . '|' . $pastDays; - if (isset(self::$taskCache[$cacheKey])) { - return self::$taskCache[$cacheKey]; - } - - $tasks = self::parseAllTasksFromFile($file, $maintenanceSlot->getKey(), $todayIso); - $oldestOpenDateIso = self::oldestOpenTaskDate($todayIso, $pastDays); - - // Filter: only non-completed, due today or earlier, and not older than the configured overdue window. - $open = []; - foreach ($tasks as $task) { - if ($task->isCompleted()) continue; - if ($task->dateIso > $todayIso) continue; - if ($task->dateIso < $oldestOpenDateIso) continue; - $open[] = $task; - } - - // Sort: overdue first, then today, then by time, then by title - usort($open, static function (CalendarEvent $a, CalendarEvent $b) use ($todayIso): int { - $aOverdue = $a->dateIso < $todayIso; - $bOverdue = $b->dateIso < $todayIso; - if ($aOverdue !== $bOverdue) { - return $aOverdue ? -1 : 1; - } - $dateCmp = strcmp($a->dateIso, $b->dateIso); - if ($dateCmp !== 0) return $dateCmp; - - $timeCmp = strcmp($a->time, $b->time); - if ($timeCmp !== 0) return $timeCmp; - - return strcmp($a->summary, $b->summary); - }); - - self::$taskCache[$cacheKey] = $open; - return $open; - } - - /** - * @param string $todayIso - * @param int $pastDays - * @return string - */ - protected static function oldestOpenTaskDate(string $todayIso, int $pastDays): string - { - try { - $today = new DateTimeImmutable($todayIso . ' 00:00:00', new DateTimeZone('UTC')); - } catch (Throwable $e) { - return $todayIso; - } - - if ($pastDays === 0) { - return $today->format('Y-m-d'); - } - - return $today->sub(new DateInterval('P' . $pastDays . 'D'))->format('Y-m-d'); - } - /** * Get slot-level day indicator data for a whole month. * @@ -329,49 +250,6 @@ class CalendarService } } - /** - * Parse all tasks (VEVENT with STATUS) from a maintenance file, - * expanding recurrences up to the given date. - * - * @param string $file - * @param string $slotKey - * @param string $todayIso - * @return CalendarEvent[] - */ - protected static function parseAllTasksFromFile(string $file, string $slotKey, string $todayIso): array - { - $calendar = self::readCalendar($file); - if ($calendar === null) return []; - - try { - $component = $calendar; - - // Expand from a reasonable lookback to tomorrow - $utc = new DateTimeZone('UTC'); - $rangeStart = new DateTimeImmutable('2020-01-01 00:00:00', $utc); - $rangeEnd = new DateTimeImmutable($todayIso . ' 00:00:00', $utc); - $rangeEnd = $rangeEnd->add(new DateInterval('P1D')); - - $expanded = $component->expand($rangeStart, $rangeEnd); - if (!($expanded instanceof VCalendar)) return []; - - $tasks = []; - - // Collect VEVENTs - foreach ($expanded->select('VEVENT') as $vevent) { - if (!($vevent instanceof VEvent)) continue; - $event = self::normalizeVEvent($vevent, $slotKey); - if ($event !== null) { - $tasks[] = $event; - } - } - - return $tasks; - } catch (Throwable $e) { - return []; - } - } - /** * Collect normalized events from an expanded VCalendar for a specific date. * @@ -440,42 +318,6 @@ class CalendarService return $event; } - /** - * Normalize a VEVENT into a CalendarEvent (without day filtering). - * - * @param VEvent $vevent - * @param string $slotKey - * @return CalendarEvent|null - */ - protected static function normalizeVEvent(VEvent $vevent, string $slotKey): ?CalendarEvent - { - if (!isset($vevent->DTSTART)) return null; - - $isAllDay = strtoupper((string)($vevent->DTSTART['VALUE'] ?? '')) === 'DATE'; - $start = self::toImmutable($vevent->DTSTART->getDateTime()); - if ($start === null) return null; - - $end = self::resolveEnd($vevent, $start, $isAllDay); - - $event = new CalendarEvent(); - $event->slotKey = $slotKey; - $event->uid = trim((string)($vevent->UID ?? '')); - $event->recurrenceId = isset($vevent->{'RECURRENCE-ID'}) ? trim((string)$vevent->{'RECURRENCE-ID'}) : ''; - $event->summary = trim((string)($vevent->SUMMARY ?? '')); - if ($event->summary === '') $event->summary = '(ohne Titel)'; - $event->startIso = $start->format(DateTimeInterface::ATOM); - $event->endIso = $end->format(DateTimeInterface::ATOM); - $event->allDay = $isAllDay; - $event->time = $isAllDay ? '' : $start->format('H:i'); - $event->location = trim((string)($vevent->LOCATION ?? '')); - $event->description = trim((string)($vevent->DESCRIPTION ?? '')); - $event->status = strtoupper(trim((string)($vevent->STATUS ?? ''))); - $event->componentType = 'VEVENT'; - $event->dateIso = $start->format('Y-m-d'); - - return $event; - } - /** * Resolve the end date/time for a VEVENT. * @@ -573,7 +415,6 @@ class CalendarService public static function clearCache(): void { self::$dayCache = []; - self::$taskCache = []; self::$vcalCache = []; } } diff --git a/src/CalendarSlot.php b/src/CalendarSlot.php index 7022729..d929aac 100644 --- a/src/CalendarSlot.php +++ b/src/CalendarSlot.php @@ -12,17 +12,17 @@ namespace dokuwiki\plugin\luxtools; class CalendarSlot { /** @var string[] Ordered list of all supported slot keys */ - public const SLOT_KEYS = ['general', 'maintenance', 'slot3', 'slot4']; + public const SLOT_KEYS = ['general', 'slot2', 'slot3', 'slot4']; /** @var string[] Allowed widget indicator display positions */ public const INDICATOR_DISPLAYS = ['none', 'top-left', 'top-right', 'bottom-left', 'bottom-right']; /** @var array Human-readable labels for slot keys */ public const SLOT_LABELS = [ - 'general' => 'General', - 'maintenance' => 'Maintenance', - 'slot3' => 'Slot 3', - 'slot4' => 'Slot 4', + 'general' => 'General', + 'slot2' => 'Slot 2', + 'slot3' => 'Slot 3', + 'slot4' => 'Slot 4', ]; /** @var string */ diff --git a/style.css b/style.css index d91986d..64ece8a 100644 --- a/style.css +++ b/style.css @@ -724,7 +724,7 @@ div.luxtools-calendar /* ============================================================ * Calendar Widget Indicators * Colored corner markers showing which slots have events on a day. - * Positions: general=top-left, maintenance=top-right, + * Positions: general=top-left, slot2=top-right, * slot3=bottom-right, slot4=bottom-left (clockwise) * ============================================================ */ div.luxtools-calendar td.luxtools-calendar-day { @@ -926,78 +926,6 @@ div.luxtools-chronological-events li[data-luxtools-event] .luxtools-event-time { margin-right: 0.25em; } -/* ============================================================ - * Maintenance Tasks - * ============================================================ */ -div.luxtools-chronological-maintenance li { - border-left-color: #e67e22; -} - -li.luxtools-maintenance-task.luxtools-task-completed { - opacity: 0.5; - text-decoration: line-through; -} - -button.luxtools-task-action, -button.luxtools-task-complete-btn { - margin-left: 0.5em; - padding: 0.15em 0.5em; - font-size: 0.85em; - border: 1px solid @ini_border; - border-radius: 0.2em; - background-color: @ini_background_alt; - cursor: pointer; -} - -button.luxtools-task-action:hover, -button.luxtools-task-complete-btn:hover { - background-color: @ini_highlight; -} - -button.luxtools-task-action:disabled, -button.luxtools-task-complete-btn:disabled { - opacity: 0.5; - cursor: wait; -} - -/* ============================================================ - * Maintenance Task List (syntax plugin) - * ============================================================ */ -div.luxtools-maintenance-tasks { - margin: 1em 0; -} - -ul.luxtools-maintenance-task-list { - list-style: none; - padding-left: 0; -} - -ul.luxtools-maintenance-task-list li { - padding: 0.35em 0.5em; - margin: 0.25em 0; - border-left: 3px solid #e67e22; -} - -li.luxtools-task-overdue .luxtools-task-date { - color: #c0392b; - font-weight: bold; -} - -.luxtools-task-date { - font-family: monospace; - margin-right: 0.5em; -} - -.luxtools-task-time { - font-weight: bold; - margin-right: 0.25em; -} - -.luxtools-maintenance-task-item.luxtools-task-completed { - opacity: 0.5; - text-decoration: line-through; -} - /* ============================================================ * Event Popup (content-specific styles – structural dialog * styles live in dialog.css) diff --git a/syntax/maintenance.php b/syntax/maintenance.php deleted file mode 100644 index 60cbc64..0000000 --- a/syntax/maintenance.php +++ /dev/null @@ -1,194 +0,0 @@ -}} - * {{maintenance_tasks>&past=14}} - */ -class syntax_plugin_luxtools_maintenance extends SyntaxPlugin -{ - private const DEFAULT_PAST_DAYS = 30; - - /** @inheritdoc */ - public function getType() - { - return 'substition'; - } - - /** @inheritdoc */ - public function getPType() - { - return 'block'; - } - - /** @inheritdoc */ - public function getSort() - { - return 225; - } - - /** @inheritdoc */ - public function connectTo($mode) - { - $this->Lexer->addSpecialPattern( - '\{\{maintenance_tasks>.*?\}\}', - $mode, - 'plugin_luxtools_maintenance' - ); - } - - /** @inheritdoc */ - public function handle($match, $state, $pos, Doku_Handler $handler) - { - $match = substr($match, strlen('{{maintenance_tasks>'), -2); - $params = $this->parseFlags($match); - - return [ - 'ok' => true, - 'past' => $this->normalizePastDays($params['past'] ?? null), - ]; - } - - /** @inheritdoc */ - public function render($format, Doku_Renderer $renderer, $data) - { - if ($data === false || !is_array($data)) return false; - if ($format !== 'xhtml') return false; - if (!($renderer instanceof Doku_Renderer_xhtml)) return false; - - $renderer->nocache(); - - $slots = CalendarSlot::loadAll($this); - $maintenanceSlot = $slots['maintenance'] ?? null; - - if ($maintenanceSlot === null || !$maintenanceSlot->isEnabled()) { - $renderer->doc .= '
    ' - . '

    ' - . hsc($this->getLang('maintenance_no_tasks')) - . '

    '; - return true; - } - - $todayIso = date('Y-m-d'); - $pastDays = $this->normalizePastDays($data['past'] ?? null); - $tasks = CalendarService::openMaintenanceTasks($maintenanceSlot, $todayIso, $pastDays); - $ajaxUrl = defined('DOKU_BASE') ? (string)DOKU_BASE . 'lib/exe/ajax.php' : 'lib/exe/ajax.php'; - $secToken = function_exists('getSecurityToken') ? (string)getSecurityToken() : ''; - - $title = (string)$this->getLang('chronological_maintenance_title'); - if ($title === '') $title = 'Tasks'; - - $renderer->doc .= '
    '; - $renderer->doc .= '

    ' . hsc($title) . '

    '; - - if ($tasks === []) { - $noTasks = (string)$this->getLang('maintenance_no_tasks'); - if ($noTasks === '') $noTasks = 'No open tasks.'; - $renderer->doc .= '

    ' . hsc($noTasks) . '

    '; - } else { - $renderer->doc .= '
      '; - foreach ($tasks as $task) { - $overdue = ($task->dateIso < $todayIso); - $classes = 'luxtools-maintenance-task-item'; - if ($overdue) { - $classes .= ' luxtools-task-overdue'; - } - - $renderer->doc .= '
    • doc .= ' data-uid="' . hsc($task->uid) . '"'; - $renderer->doc .= ' data-date="' . hsc($task->dateIso) . '"'; - $renderer->doc .= ' data-recurrence="' . hsc($task->recurrenceId) . '"'; - $renderer->doc .= '>'; - - // Date badge - $renderer->doc .= '' . hsc($task->dateIso) . ' '; - - // Time if not all-day - if ($task->time !== '') { - $renderer->doc .= '' . hsc($task->time) . ' '; - } - - // Summary - $renderer->doc .= '' . hsc($task->summary) . ''; - - // Complete button - $completeLabel = (string)$this->getLang('maintenance_task_complete'); - if ($completeLabel === '') $completeLabel = 'Complete'; - $renderer->doc .= ' '; - - $renderer->doc .= '
    • '; - } - $renderer->doc .= '
    '; - } - - $renderer->doc .= '
    '; - - return true; - } - - /** - * @param string $rawFlags - * @return array - */ - protected function parseFlags(string $rawFlags): array - { - $rawFlags = trim($rawFlags); - if ($rawFlags === '') { - return []; - } - - if ($rawFlags[0] === '&') { - $rawFlags = substr($rawFlags, 1); - } - - $params = []; - foreach (explode('&', $rawFlags) as $flag) { - if (trim($flag) === '') continue; - - [$name, $value] = array_pad(explode('=', $flag, 2), 2, ''); - $name = strtolower(trim($name)); - $value = trim($value); - - if ($name === '') continue; - $params[$name] = $value; - } - - return $params; - } - - /** - * @param mixed $value - * @return int - */ - protected function normalizePastDays($value): int - { - if ($value === null || $value === '') { - return self::DEFAULT_PAST_DAYS; - } - - if (is_int($value)) { - return max(0, $value); - } - - $value = trim((string)$value); - if ($value === '' || !preg_match('/^-?\d+$/', $value)) { - return self::DEFAULT_PAST_DAYS; - } - - return max(0, (int)$value); - } -}