Skip to content

Commit

Permalink
Show extra appointments link for week and month views
Browse files Browse the repository at this point in the history
  • Loading branch information
raviks789 committed Oct 18, 2023
1 parent 578cb39 commit 3726ba2
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 49 deletions.
2 changes: 1 addition & 1 deletion library/Notifications/Model/Timeperiod.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function createRelations(Relations $relations)
$relations->hasOne('parent', Schedule::class)
->setCandidateKey('owned_by_schedule_id')
->setJoinType('LEFT');
$relations->hasMany('entry', TimeperiodEntry::class)
$relations->hasMany('timeperiod_entry', TimeperiodEntry::class)
->setJoinType('LEFT');
}
}
23 changes: 22 additions & 1 deletion library/Notifications/Widget/Calendar.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ class Calendar extends BaseHtmlElement
/** @var Url */
protected $addEntryUrl;

/** @var Url */
protected $url;

public function setControls(Controls $controls): self
{
$this->controls = $controls;
Expand Down Expand Up @@ -79,6 +82,22 @@ public function getAddEntryUrl(): ?Url
return $this->addEntryUrl;
}

public function setUrl(?Url $url): self
{
$this->url = $url;

return $this;
}

public function prepareDayViewUrl(DateTime $date): ?Url
{
$url = clone $this->url;
return $url->overwriteParams([
'mode' => 'day',
'day' => $date->format('Y-m-d')
]);
}

protected function getModeStart(): DateTime
{
switch ($this->getControls()->getViewMode()) {
Expand All @@ -91,7 +110,9 @@ protected function getModeStart(): DateTime

return (new DateTime())->setTimestamp(strtotime($week));
default:
return DateTime::createFromFormat('Y-m-d', $this->getControls()->getValue('day'));
$day = $this->getControls()->getValue('day') ?: (new DateTime())->format('Y-m-d');

return DateTime::createFromFormat('Y-m-d H:i:s', $day . ' 00:00:00');
}
}

Expand Down
47 changes: 42 additions & 5 deletions library/Notifications/Widget/Calendar/BaseGrid.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Icinga\Module\Notifications\Widget\Calendar;

use DateInterval;
use DateTime;
use DateTimeInterface;
use Icinga\Module\Notifications\Widget\Calendar;
Expand Down Expand Up @@ -35,6 +36,9 @@ abstract class BaseGrid extends BaseHtmlElement
/** @var DateTime */
protected $end;

/** @var array Extra counts stored as [date1 => count1, date2 => count2]*/
protected $extraEntriesCount = [];

/**
* Create a new calendar
*
Expand Down Expand Up @@ -137,6 +141,18 @@ protected function createGridOverlay(): BaseHtmlElement
return $overlay;
}

/**
* Fetch the count of additional number of entries for the given date
*
* @param DateTime $date
*
* @return int
*/
public function getExtraEntryCount(DateTime $date): int
{
return $this->extraEntriesCount[$date->format('Y-m-d')] ?? 0;
}

protected function assembleGridOverlay(BaseHtmlElement $overlay): void
{
$style = (new Style())->setNonce(Csp::getStyleNonce());
Expand Down Expand Up @@ -218,21 +234,42 @@ protected function assembleGridOverlay(BaseHtmlElement $overlay): void
}
}

$this->extraEntriesCount = [];
foreach ($occupiedCells as $entry) {
$continuation = false;
$rows = $occupiedCells->getInfo();
foreach ($rows as $row => $hours) {
list($rowStart, $rowSpan) = $rowPlacements[spl_object_id($entry)][$row];
$colStart = min($hours);
$colEnd = max($hours);

// Calculate number of entries that are not displayed in the grid for each date
if ($rowStart > $row + $sectionsPerStep) {
// TODO: Register as +1
$startOffset = (int) (($row / $sectionsPerStep) * ($gridBorderAt / 48) + $colStart / 48);
$endOffset = (int) (($row / $sectionsPerStep) * ($gridBorderAt / 48) + $colEnd / 48);
$startDate = (clone $this->getGridStart())->add(new DateInterval("P$startOffset" . 'D'));
$duration = $endOffset - $startOffset;
for ($i = 0; $i <= $duration; $i++) {
$countIdx = $startDate->format('Y-m-d');
if (! isset($this->extraEntriesCount[$countIdx])) {
$this->extraEntriesCount[$countIdx] = 1;
} else {
$this->extraEntriesCount[$countIdx] += 1;
}

$startDate->add(new DateInterval('P1D'));
}

continue;
}

$rowEnd = $rowStart + $rowSpan;
$colStart = min($hours) + 1;
$colEnd = max($hours) + 2;
$gridArea = $this->getGridArea(
$rowStart,
$rowStart + $rowSpan,
$colStart + 1,
$colEnd + 2
);

$gridArea = $this->getGridArea($rowStart, $rowEnd, $colStart, $colEnd);
$entryClass = 'area-' . implode('-', $gridArea);

$style->add(".$entryClass", [
Expand Down
23 changes: 20 additions & 3 deletions library/Notifications/Widget/Calendar/DayGrid.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use DateInterval;
use DateTime;
use InvalidArgumentException;
use ipl\Html\Attributes;
use ipl\Html\BaseHtmlElement;
use ipl\Html\HtmlElement;
Expand All @@ -14,13 +15,20 @@

class DayGrid extends BaseGrid
{
protected $mode = 'day';

public function setGridStart(DateTime $start): BaseGrid
{
if ($start->format('H:i:s') !== '00:00:00') {
throw new InvalidArgumentException('Start is not midnight');
}

return parent::setGridStart($start);
}

protected function getMaximumRowSpan(): int
{
return 28;
}

protected function calculateGridEnd(): DateTime
{
return (clone $this->getGridStart())->add(new DateInterval('P1D'));
Expand Down Expand Up @@ -50,6 +58,15 @@ protected function createGridSteps(): Traversable
protected function createHeader(): BaseHtmlElement
{
$header = new HtmlElement('div', Attributes::create(['class' => 'header']));
$dayNames = [
'Mon' => t('Mon', 'monday'),
'Tue' => t('Tue', 'tuesday'),
'Wed' => t('Wed', 'wednesday'),
'Thu' => t('Thu', 'thursday'),
'Fri' => t('Fri', 'friday'),
'Sat' => t('Sat', 'saturday'),
'Sun' => t('Sun', 'sunday')
];

$currentDay = clone $this->getGridStart();
$interval = new DateInterval('P1D');
Expand All @@ -59,7 +76,7 @@ protected function createHeader(): BaseHtmlElement
new HtmlElement(
'span',
Attributes::create(['class' => 'day-name']),
Text::create($this->getGridStart()->format('D'))
Text::create($dayNames[$currentDay->format('D')])
),
new HtmlElement(
'span',
Expand Down
65 changes: 65 additions & 0 deletions library/Notifications/Widget/Calendar/ExtraEntryCount.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

namespace Icinga\Module\Notifications\Widget\Calendar;

use DateTime;
use ipl\I18n\Translation;
use ipl\Web\Widget\Link;

class ExtraEntryCount extends Link
{
use Translation;

protected $defaultAttributes = ['class' => 'extra-count', 'target' => '_self'];

/** @var BaseGrid */
protected $grid;

/** @var DateTime Day for which the extra count is being registered */
protected $gridStep;

/**
* Set the grid the tied to this extra count
*
* @param BaseGrid $grid
*
* @return $this
*/
public function setGrid(BaseGrid $grid): self
{
$this->grid = $grid;

return $this;
}

/**
* Set the day for which the extra count is being registered
*
* @param DateTime $gridStep Grid step
*
* @return $this
*/
public function setGridStep(DateTime $gridStep): self
{
$this->gridStep = clone $gridStep;

return $this;
}

protected function assemble()
{
$count = $this->grid->getExtraEntryCount($this->gridStep);
if ($count > 0) {
$this->setContent(
sprintf(
$this->translatePlural(
'+%d entry',
'+%d entries',
$count
),
$count
)
);
}
}
}
7 changes: 7 additions & 0 deletions library/Notifications/Widget/Calendar/MonthGrid.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ protected function calculateGridEnd(): DateTime
protected function assembleGridStep(BaseHtmlElement $content, DateTime $step): void
{
$content->addHtml(Text::create($step->format('j')));

$dayViewUrl = $this->calendar->prepareDayViewUrl($step);
$content->addHtml(
(new ExtraEntryCount(null, $dayViewUrl))
->setGrid($this)
->setGridStep($step)
);
}

protected function getRowStartModifier(): int
Expand Down
12 changes: 12 additions & 0 deletions library/Notifications/Widget/Calendar/WeekGrid.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ protected function createSidebar(): BaseHtmlElement
return $sidebar;
}

protected function assembleGridStep(BaseHtmlElement $content, DateTime $step): void
{
if ($step->format('H') === '23') {
$dayViewUrl = $this->calendar->prepareDayViewUrl($step);
$content->addHtml(
(new ExtraEntryCount(null, $dayViewUrl))
->setGrid($this)
->setGridStep($step)
);
}
}

protected function assemble()
{
$this->getAttributes()->add('class', 'week');
Expand Down
89 changes: 50 additions & 39 deletions library/Notifications/Widget/Schedule.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
namespace Icinga\Module\Notifications\Widget;

use DateTimeZone;
use Icinga\Module\Notifications\Common\Database;
use Icinga\Module\Notifications\Model\ScheduleMember;
use Icinga\Module\Notifications\Model\TimeperiodEntry;
use Icinga\Module\Notifications\Widget\Calendar\Attendee;
use Icinga\Module\Notifications\Widget\Calendar\Controls;
use Icinga\Module\Notifications\Widget\Calendar\Entry;
Expand Down Expand Up @@ -44,47 +47,55 @@ protected function assembleCalendar(Calendar $calendar): void
['schedule' => $this->schedule->id]
));

$members = $this->schedule->member->with(['timeperiod', 'contact', 'contactgroup']);
foreach ($members as $member) {
if ($member->contact_id !== null) {
$attendee = new Attendee($member->contact->full_name);
$attendee->setColor($member->contact->color);
} else { // $member->contactgroup_id !== null
$attendee = new Attendee($member->contactgroup->name);
$attendee->setColor($member->contactgroup->color);
$attendee->setIcon('users');
}
$calendar->setUrl(Url::fromPath(
'notifications/schedules',
['schedule' => $this->schedule->id]
));

$entries = $member->timeperiod->entry;

// TODO: This shouldn't be necessary. ipl/orm should be able to handle this by itself
$entries->setFilter(Filter::all(Filter::equal('timeperiod_id', $member->timeperiod->id)));
$entries->getSelectBase()->resetWhere();

$entryFilter = Filter::any(
Filter::all( // all entries that start in the shown range
Filter::greaterThanOrEqual('start_time', $calendar->getGrid()->getGridStart()->getTimestamp()),
Filter::lessThanOrEqual('start_time', $calendar->getGrid()->getGridEnd()->getTimestamp())
),
Filter::all( // all entries that end in the shown range
Filter::greaterThanOrEqual('end_time', $calendar->getGrid()->getGridStart()->getTimestamp()),
Filter::lessThanOrEqual('end_time', $calendar->getGrid()->getGridEnd()->getTimestamp())
),
Filter::all( // all entries that start before and end after the shown range
Filter::lessThanOrEqual('start_time', $calendar->getGrid()->getGridStart()->getTimestamp()),
Filter::greaterThanOrEqual('end_time', $calendar->getGrid()->getGridEnd()->getTimestamp())
),
Filter::none( // all entries that are repeated and may still occur in the shown range
Filter::lessThanOrEqual('until_time', $calendar->getGrid()->getGridStart()->getTimestamp())
),
Filter::all( // all entries that are repeated endlessly and already started in the past
Filter::unlike('until_time', '*'),
Filter::like('rrule', '*'),
Filter::lessThanOrEqual('start_time', $calendar->getGrid()->getGridStart()->getTimestamp())
)
);
$db = Database::get();
$entries = TimeperiodEntry::on($db)
->filter(Filter::equal('timeperiod.schedule.id', $this->schedule->id))
->orderBy(['start_time', 'timeperiod_id']);

$entryFilter = Filter::any(
Filter::all( // all entries that start in the shown range
Filter::greaterThanOrEqual('start_time', $calendar->getGrid()->getGridStart()->getTimestamp()),
Filter::lessThanOrEqual('start_time', $calendar->getGrid()->getGridEnd()->getTimestamp())
),
Filter::all( // all entries that end in the shown range
Filter::greaterThanOrEqual('end_time', $calendar->getGrid()->getGridStart()->getTimestamp()),
Filter::lessThanOrEqual('end_time', $calendar->getGrid()->getGridEnd()->getTimestamp())
),
Filter::all( // all entries that start before and end after the shown range
Filter::lessThanOrEqual('start_time', $calendar->getGrid()->getGridStart()->getTimestamp()),
Filter::greaterThanOrEqual('end_time', $calendar->getGrid()->getGridEnd()->getTimestamp())
),
Filter::none( // all entries that are repeated and may still occur in the shown range
Filter::lessThanOrEqual('until_time', $calendar->getGrid()->getGridStart()->getTimestamp())
),
Filter::all( // all entries that are repeated endlessly and already started in the past
Filter::unlike('until_time', '*'),
Filter::like('rrule', '*'),
Filter::lessThanOrEqual('start_time', $calendar->getGrid()->getGridStart()->getTimestamp())
)
);

foreach ($entries->filter($entryFilter) as $entry) {
$members = ScheduleMember::on($db)
->with(['timeperiod', 'contact', 'contactgroup'])
->filter(Filter::equal('timeperiod_id', $entry->timeperiod_id))
->orderBy(['contact_id', 'contactgroup_id']);

foreach ($members as $member) {
if ($member->contact_id !== null) {
$attendee = new Attendee($member->contact->full_name);
$attendee->setColor($member->contact->color);
} else { // $member->contactgroup_id !== null
$attendee = new Attendee($member->contactgroup->name);
$attendee->setColor($member->contactgroup->color);
$attendee->setIcon('users');
}

foreach ($member->timeperiod->entry->filter($entryFilter) as $entry) {
$calendar->addEntry(
(new Entry($entry->id))
->setDescription($entry->description)
Expand Down
Loading

0 comments on commit 3726ba2

Please sign in to comment.