diff --git a/library/Notifications/Widget/Calendar/BaseGrid.php b/library/Notifications/Widget/Calendar/BaseGrid.php index fc48b0bac..766e0d17f 100644 --- a/library/Notifications/Widget/Calendar/BaseGrid.php +++ b/library/Notifications/Widget/Calendar/BaseGrid.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Notifications\Widget\Calendar; +use DateInterval; use DateTime; use DateTimeInterface; use Icinga\Module\Notifications\Widget\Calendar; @@ -14,6 +15,7 @@ use ipl\Html\Text; use ipl\I18n\Translation; use ipl\Web\Style; +use ipl\Web\Url; use ipl\Web\Widget\Link; use SplObjectStorage; use Traversable; @@ -35,6 +37,8 @@ abstract class BaseGrid extends BaseHtmlElement /** @var DateTime */ protected $end; + protected $mode; + /** * Create a new calendar * @@ -75,6 +79,19 @@ abstract protected function getNoOfVisuallyConnectedHours(): int; abstract protected function getGridArea(int $rowStart, int $rowEnd, int $colStart, int $colEnd): array; + abstract protected function updateExtraEntriesCount( + array $gridArea, + array &$extraEntriesCount, + array &$removedEntry + ): void; + + abstract protected function removeRowLastEntry( + array $gridArea, + BaseHtmlElement $entryHtml, + array &$removedEntry, + array &$extraEntriesCount + ): void; + protected function getSectionsPerStep(): int { return $this->getMaximumRowSpan(); @@ -202,7 +219,10 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void foreach ($competingOccupiers as $otherId) { list($otherRowStart, $otherRowSpan) = $rowPlacements[$otherId][$row]; - if ($otherRowStart === $rowStart) { + if ($this->mode === 'day') { + $rowStart += $otherRowSpan; + $rowPlacements[$otherId][$row] = [$otherRowStart, $otherRowSpan]; + } elseif ($otherRowStart === $rowStart) { $otherRowSpan = (int) ceil($otherRowSpan / 2); $rowStart += $otherRowSpan; $rowSpan -= $otherRowSpan; @@ -218,16 +238,13 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void } } + $extraEntriesCount = []; + $removedEntry = []; foreach ($occupiedCells as $entry) { $continuation = false; $rows = $occupiedCells->getInfo(); foreach ($rows as $row => $hours) { list($rowStart, $rowSpan) = $rowPlacements[spl_object_id($entry)][$row]; - if ($rowStart > $row + $sectionsPerStep) { - // TODO: Register as +1 - continue; - } - $rowEnd = $rowStart + $rowSpan; $colStart = min($hours) + 1; $colEnd = max($hours) + 2; @@ -235,6 +252,12 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void $gridArea = $this->getGridArea($rowStart, $rowEnd, $colStart, $colEnd); $entryClass = 'area-' . implode('-', $gridArea); + if ($this->mode !== 'day' && $rowStart > $row + $sectionsPerStep) { + $this->updateExtraEntriesCount($gridArea, $extraEntriesCount, $removedEntry); + + continue; + } + $style->add(".$entryClass", [ 'grid-area' => sprintf('~"%d / %d / %d / %d"', ...$gridArea), 'background-color' => $entry->getAttendee()->getColor() . dechex((int) (256 * 0.1)), @@ -255,9 +278,58 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void $this->assembleEntry($entryHtml, $entry, $continuation); $overlay->addHtml($entryHtml); + if ($this->mode !== 'day' && $rowStart === $row + $sectionsPerStep) { + $this->removeRowLastEntry( + $gridArea, + $entryHtml, + $removedEntry, + $extraEntriesCount + ); + } + $continuation = true; } } + + foreach ($extraEntriesCount as $dayOffset => $count) { + $start = clone $this->getGridStart(); + $start->add(new DateInterval("P$dayOffset" . "D")); + if ($count === 0) { + continue; + } elseif ($removedEntry[$dayOffset][0] !== null) { + $overlay->remove($removedEntry[$dayOffset][0]); + } + + $countStyle = [ + 'grid-row-start' => $removedEntry[$dayOffset][1], + 'grid-column-start' => $removedEntry[$dayOffset][2], + 'pointer-events' => 'all', + 'text-align' => 'end', + 'margin-right' => '0.2em' + ]; + $style->add(".additional-$dayOffset", $countStyle); + $entryHtml = new HtmlElement( + 'div', + Attributes::create( + [ + 'class' => ["additional-$dayOffset"] + ] + ), + new Link( + "+$count", + Url::fromPath( + 'notifications/schedules', + [ + 'mode' => 'day', + 'day' => $start->format('Y-m-d') + ] + ), + ['target' => '_self'] + ) + ); + + $overlay->addHtml($entryHtml); + } } protected function assembleEntry(BaseHtmlElement $html, Entry $entry, bool $isContinuation): void diff --git a/library/Notifications/Widget/Calendar/DayGrid.php b/library/Notifications/Widget/Calendar/DayGrid.php index 277f88c32..9ca070d0a 100644 --- a/library/Notifications/Widget/Calendar/DayGrid.php +++ b/library/Notifications/Widget/Calendar/DayGrid.php @@ -107,4 +107,16 @@ protected function assemble() $this->createGridOverlay() ); } + + protected function updateExtraEntriesCount(array $gridArea, array &$extraEntriesCount, array &$removedEntry): void + { + } + + protected function removeRowLastEntry( + array $gridArea, + BaseHtmlElement $entryHtml, + array &$removedEntry, + array &$extraEntriesCount + ): void { + } } diff --git a/library/Notifications/Widget/Calendar/MonthGrid.php b/library/Notifications/Widget/Calendar/MonthGrid.php index e925cc1ae..e207dc31c 100644 --- a/library/Notifications/Widget/Calendar/MonthGrid.php +++ b/library/Notifications/Widget/Calendar/MonthGrid.php @@ -15,6 +15,8 @@ class MonthGrid extends BaseGrid { + protected $mode = 'month'; + public function setGridStart(DateTime $start): BaseGrid { if ($start->format('j:H:i:s') !== '1:00:00:00') { @@ -53,6 +55,16 @@ protected function getNoOfVisuallyConnectedHours(): int return 7 * 24; } + protected function getNumOfColumns() + { + return 7 * 48; + } + + protected function getColumnSpan() + { + return 48; + } + protected function getGridArea(int $rowStart, int $rowEnd, int $colStart, int $colEnd): array { return [$rowStart, $colStart, $rowEnd, $colEnd]; @@ -131,4 +143,80 @@ protected function assemble() $this->createGridOverlay() ); } + + protected function getDaysOffset(int $row, int $col): int + { + return (intval($row / $this->getSectionsPerStep()) - 1) * 7 + intval($col / 48); + } + + protected function updateExtraEntriesCount(array $gridArea, array &$extraEntriesCount, array &$removedEntry): void + { + $day = $this->getDaysOffset($gridArea[0], $gridArea[1]); + $startDay = $day; + + $colSpan = $this->getColumnSpan(); + $endDay = $this->getDaysOffset($gridArea[2], $gridArea[3]); + while (($endDay - $startDay) >= 0) { + $col = $gridArea[1] + ($startDay - $day) * $colSpan; + if ($startDay !== $day && $col > $this->getNumOfColumns()) { + break; + } + + if (! isset($extraEntriesCount[$startDay])) { + $removedEntry[$startDay] = [ + null, + $gridArea[0] - 1, + (intval($col / $colSpan) + 1) * $colSpan - 3 + ]; + + $extraEntriesCount[$startDay] = 1; + } elseif ($extraEntriesCount[$startDay] === 0) { + $extraEntriesCount[$startDay] += 2; + } else { + $extraEntriesCount[$startDay] += 1; + } + + $startDay += 1; + } + } + + protected function removeRowLastEntry( + array $gridArea, + BaseHtmlElement $entryHtml, + array &$removedEntry, + array &$extraEntriesCount + ): void { + $day = $this->getDaysOffset($gridArea[0], $gridArea[1]); + $startDay = $day; + + $colSpan = $this->getColumnSpan(); + $numCols = $this->getNumOfColumns(); + $endDay = $this->getDaysOffset($gridArea[2], $gridArea[3]); + while (($endDay - $startDay) >= 0) { + $col = $gridArea[1] + ($startDay - $day) * $colSpan; + + if ($startDay !== $day && $col > $numCols) { + $startDay += 1; + continue; + } + + if ($startDay === $day) { + $removedEntry[$startDay] = [ + $entryHtml, + $gridArea[0], + (intval($col / $colSpan) + 1) * $colSpan - 3 + ]; + } else { + $removedEntry[$startDay] = [ + null, + $gridArea[0], + (intval($col / $colSpan) + 1) * $colSpan - 3 + ]; + } + + $extraEntriesCount[$startDay] = 0; + + $startDay += 1; + } + } } diff --git a/library/Notifications/Widget/Calendar/WeekGrid.php b/library/Notifications/Widget/Calendar/WeekGrid.php index d26835b41..ea836e8f3 100644 --- a/library/Notifications/Widget/Calendar/WeekGrid.php +++ b/library/Notifications/Widget/Calendar/WeekGrid.php @@ -11,10 +11,15 @@ use ipl\Html\BaseHtmlElement; use ipl\Html\HtmlElement; use ipl\Html\Text; +use ipl\Web\Style; +use ipl\Web\Url; +use ipl\Web\Widget\Link; use Traversable; class WeekGrid extends BaseGrid { + protected $mode = 'week'; + public function setGridStart(DateTime $start): BaseGrid { if ($start->format('w:H:i:s') !== '1:00:00:00') { @@ -127,4 +132,38 @@ protected function assemble() $this->createGridOverlay() ); } + + protected function getDaysOffset(int $col): int + { + return intval($col / 4) - 1; + } + + protected function updateExtraEntriesCount(array $gridArea, array &$extraEntriesCount, array &$removedEntry): void + { + $day = $this->getDaysOffset($gridArea[1]); + + if (! isset($extraEntriesCount[$day])) { + $extraEntriesCount[$day] = 1; + } elseif ($extraEntriesCount[$day] === 0) { + $extraEntriesCount[$day] += 2; + } else { + $extraEntriesCount[$day] += 1; + } + } + + protected function removeRowLastEntry( + array $gridArea, + BaseHtmlElement $entryHtml, + array &$removedEntry, + array &$extraEntriesCount + ): void { + $day = $this->getDaysOffset($gridArea[1]); + $removedEntry[$day] = [ + $entryHtml, + 2 * $this->getNoOfVisuallyConnectedHours(), + $gridArea[1] + ]; + + $extraEntriesCount[$day] = 0; + } }