Files
luxtools-plugin/syntax/maintenance.php
2026-03-11 13:50:45 +01:00

195 lines
6.0 KiB
PHP

<?php
use dokuwiki\Extension\SyntaxPlugin;
use dokuwiki\plugin\luxtools\CalendarService;
use dokuwiki\plugin\luxtools\CalendarSlot;
require_once(__DIR__ . '/../autoload.php');
/**
* luxtools Plugin: Maintenance task list syntax.
*
* Renders a list of all non-completed maintenance tasks due today or earlier.
*
* Syntax:
* {{maintenance_tasks>}}
* {{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 .= '<div class="luxtools-plugin luxtools-maintenance-tasks">'
. '<p class="luxtools-empty">'
. hsc($this->getLang('maintenance_no_tasks'))
. '</p></div>';
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 .= '<div class="luxtools-plugin luxtools-maintenance-tasks"'
. ' data-luxtools-ajax-url="' . hsc($ajaxUrl) . '"'
. ' data-luxtools-sectok="' . hsc($secToken) . '">';
$renderer->doc .= '<h3>' . hsc($title) . '</h3>';
if ($tasks === []) {
$noTasks = (string)$this->getLang('maintenance_no_tasks');
if ($noTasks === '') $noTasks = 'No open tasks.';
$renderer->doc .= '<p class="luxtools-empty">' . hsc($noTasks) . '</p>';
} else {
$renderer->doc .= '<ul class="luxtools-maintenance-task-list">';
foreach ($tasks as $task) {
$overdue = ($task->dateIso < $todayIso);
$classes = 'luxtools-maintenance-task-item';
if ($overdue) {
$classes .= ' luxtools-task-overdue';
}
$renderer->doc .= '<li class="' . $classes . '"';
$renderer->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 .= '<span class="luxtools-task-date">' . hsc($task->dateIso) . '</span> ';
// Time if not all-day
if ($task->time !== '') {
$renderer->doc .= '<span class="luxtools-task-time">' . hsc($task->time) . '</span> ';
}
// Summary
$renderer->doc .= '<span class="luxtools-task-summary">' . hsc($task->summary) . '</span>';
// Complete button
$completeLabel = (string)$this->getLang('maintenance_task_complete');
if ($completeLabel === '') $completeLabel = 'Complete';
$renderer->doc .= ' <button class="luxtools-task-complete-btn" type="button"'
. ' data-action="complete"'
. '>' . hsc($completeLabel) . '</button>';
$renderer->doc .= '</li>';
}
$renderer->doc .= '</ul>';
}
$renderer->doc .= '</div>';
return true;
}
/**
* @param string $rawFlags
* @return array<string,string>
*/
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);
}
}