Remove maintenance feature

This commit is contained in:
2026-04-03 14:53:05 +02:00
parent 946c269d42
commit d33c7a748b
11 changed files with 28 additions and 889 deletions

View File

@@ -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();
}
}

View File

@@ -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<string,CalendarEvent[]> In-request cache keyed by "slotKey|dateIso" */
protected static $dayCache = [];
/** @var array<string,CalendarEvent[]> In-request cache keyed by "slotKey|all" for open tasks */
protected static $taskCache = [];
/** @var array<string,VCalendar|null> 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 = [];
}
}

View File

@@ -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<string,string> 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 */