Files
luxtools-plugin/src/ChronologicalCalendarWidget.php

142 lines
6.1 KiB
PHP

<?php
namespace dokuwiki\plugin\luxtools;
/**
* Render the chronological calendar widget markup.
*/
class ChronologicalCalendarWidget
{
/**
* Render full calendar widget HTML for one month.
*
* @param int $year
* @param int $month
* @param string $baseNs
* @return string
*/
public static function render(int $year, int $month, string $baseNs = 'chronological'): string
{
if (!self::isValidMonth($year, $month)) return '';
$firstDayTs = mktime(0, 0, 0, $month, 1, $year);
$daysInMonth = (int)date('t', $firstDayTs);
$firstWeekday = (int)date('N', $firstDayTs); // 1..7 (Mon..Sun)
$monthCursor = \DateTimeImmutable::createFromFormat('!Y-n-j', sprintf('%04d-%d-1', $year, $month));
if (!($monthCursor instanceof \DateTimeImmutable)) return '';
$prevMonth = $monthCursor->sub(new \DateInterval('P1M'));
$nextMonth = $monthCursor->add(new \DateInterval('P1M'));
$monthStartDate = sprintf('%04d-%02d-01', $year, $month);
$monthDayId = ChronoID::dateToDayId($monthStartDate, $baseNs);
$monthId = $monthDayId !== null ? ChronoID::dayIdToMonthId($monthDayId, $baseNs) : null;
$yearId = $monthId !== null ? ChronoID::monthIdToYearId($monthId, $baseNs) : null;
$prevStartDate = $prevMonth->format('Y-m-d');
$prevDayId = ChronoID::dateToDayId($prevStartDate, $baseNs);
$prevMonthId = $prevDayId !== null ? ChronoID::dayIdToMonthId($prevDayId, $baseNs) : null;
$nextStartDate = $nextMonth->format('Y-m-d');
$nextDayId = ChronoID::dateToDayId($nextStartDate, $baseNs);
$nextMonthId = $nextDayId !== null ? ChronoID::dayIdToMonthId($nextDayId, $baseNs) : null;
$leadingEmpty = $firstWeekday - 1;
$totalCells = (int)ceil(($leadingEmpty + $daysInMonth) / 7) * 7;
$dayUrlTemplate = function_exists('wl') ? (string)wl('__LUXTOOLS_ID_RAW__') : '';
$monthUrlTemplate = $dayUrlTemplate;
$yearUrlTemplate = $dayUrlTemplate;
$ajaxUrl = defined('DOKU_BASE') ? (string)DOKU_BASE . 'lib/exe/ajax.php' : 'lib/exe/ajax.php';
$html = '<div class="luxtools-plugin luxtools-calendar" data-luxtools-calendar="1"'
. ' data-base-ns="' . hsc($baseNs) . '"'
. ' data-current-year="' . hsc((string)$year) . '"'
. ' data-current-month="' . hsc(sprintf('%02d', $month)) . '"'
. ' data-day-url-template="' . hsc($dayUrlTemplate) . '"'
. ' data-month-url-template="' . hsc($monthUrlTemplate) . '"'
. ' data-year-url-template="' . hsc($yearUrlTemplate) . '"'
. ' data-luxtools-ajax-url="' . hsc($ajaxUrl) . '"'
. ' data-prev-month-id="' . hsc((string)$prevMonthId) . '"'
. ' data-next-month-id="' . hsc((string)$nextMonthId) . '"'
. '>';
$html .= '<div class="luxtools-calendar-nav">';
$html .= '<div class="luxtools-calendar-nav-prev">';
$html .= '<button type="button" class="luxtools-calendar-nav-button" data-luxtools-dir="-12" aria-label="Previous year">⏮</button>';
$html .= '<button type="button" class="luxtools-calendar-nav-button" data-luxtools-dir="-1" aria-label="Previous month">◀</button>';
$html .= '</div>';
$html .= '<div class="luxtools-calendar-title">';
$monthLabel = date('F', $firstDayTs);
if ($monthId !== null && function_exists('wl')) {
$html .= '<a class="luxtools-calendar-month-link" href="' . hsc((string)wl($monthId)) . '">' . hsc($monthLabel) . '</a>';
} else {
$html .= hsc($monthLabel);
}
$html .= ' ';
if ($yearId !== null && function_exists('wl')) {
$html .= '<a class="luxtools-calendar-year-link" href="' . hsc((string)wl($yearId)) . '">' . hsc((string)$year) . '</a>';
} else {
$html .= hsc((string)$year);
}
$html .= '</div>';
$html .= '<div class="luxtools-calendar-nav-next">';
$html .= '<button type="button" class="luxtools-calendar-nav-button" data-luxtools-dir="1" aria-label="Next month">▶</button>';
$html .= '<button type="button" class="luxtools-calendar-nav-button" data-luxtools-dir="12" aria-label="Next year">⏭</button>';
$html .= '</div>';
$html .= '</div>';
$html .= '<table class="luxtools-calendar-table">';
$html .= '<thead><tr>';
foreach (['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'] as $weekday) {
$html .= '<th scope="col">' . hsc($weekday) . '</th>';
}
$html .= '</tr></thead><tbody>';
for ($cell = 0; $cell < $totalCells; $cell++) {
if ($cell % 7 === 0) $html .= '<tr>';
$dayNumber = $cell - $leadingEmpty + 1;
if ($dayNumber < 1 || $dayNumber > $daysInMonth) {
$html .= '<td class="luxtools-calendar-day luxtools-calendar-day-empty"></td>';
} else {
$date = sprintf('%04d-%02d-%02d', $year, $month, $dayNumber);
$dayId = ChronoID::dateToDayId($date, $baseNs);
$classes = 'luxtools-calendar-day';
$html .= '<td class="' . hsc($classes) . '" data-luxtools-date="' . hsc($date) . '">';
if ($dayId !== null && function_exists('html_wikilink')) {
$html .= (string)html_wikilink($dayId, (string)$dayNumber);
} else {
$html .= hsc((string)$dayNumber);
}
$html .= '</td>';
}
if ($cell % 7 === 6) $html .= '</tr>';
}
$html .= '</tbody></table>';
$html .= '<div class="luxtools-calendar-footer">';
$html .= '<button type="button" class="luxtools-calendar-nav-button luxtools-calendar-today-button" data-luxtools-today="1" aria-label="Current month">Today</button>';
$html .= '</div></div>';
return $html;
}
/**
* @param int $year
* @param int $month
* @return bool
*/
public static function isValidMonth(int $year, int $month): bool
{
if ($year < 1) return false;
if ($month < 1 || $month > 12) return false;
return true;
}
}