From f973dfb112799cc2094e8e2926e001fa051fd355 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Wed, 2 Apr 2025 12:14:09 +0200 Subject: [PATCH 01/22] Introduce class ChannelRenderer and ObjectList - Links: Add missing channel links - Remove obsolete code --- .../controllers/ChannelsController.php | 22 ++++---- library/Notifications/Common/Links.php | 15 ++++++ .../Notifications/View/ChannelRenderer.php | 48 +++++++++++++++++ .../Widget/ItemList/ChannelList.php | 20 ------- .../Widget/ItemList/ChannelListItem.php | 54 ------------------- .../Widget/ItemList/ObjectList.php | 29 ++++++++++ public/css/common.less | 2 +- 7 files changed, 105 insertions(+), 85 deletions(-) create mode 100644 library/Notifications/View/ChannelRenderer.php delete mode 100644 library/Notifications/Widget/ItemList/ChannelList.php delete mode 100644 library/Notifications/Widget/ItemList/ChannelListItem.php create mode 100644 library/Notifications/Widget/ItemList/ObjectList.php diff --git a/application/controllers/ChannelsController.php b/application/controllers/ChannelsController.php index 90127d360..d6fb00c27 100644 --- a/application/controllers/ChannelsController.php +++ b/application/controllers/ChannelsController.php @@ -5,10 +5,12 @@ namespace Icinga\Module\Notifications\Controllers; use Icinga\Module\Notifications\Common\Database; +use Icinga\Module\Notifications\Common\Links; use Icinga\Module\Notifications\Forms\ChannelForm; use Icinga\Module\Notifications\Model\Channel; +use Icinga\Module\Notifications\View\ChannelRenderer; use Icinga\Module\Notifications\Web\Control\SearchBar\ObjectSuggestions; -use Icinga\Module\Notifications\Widget\ItemList\ChannelList; +use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use Icinga\Web\Notification; use Icinga\Web\Widget\Tab; use Icinga\Web\Widget\Tabs; @@ -21,7 +23,7 @@ use ipl\Web\Control\LimitControl; use ipl\Web\Control\SortControl; use ipl\Web\Filter\QueryString; -use ipl\Web\Url; +use ipl\Web\Layout\MinimalItemLayout; use ipl\Web\Widget\ButtonLink; class ChannelsController extends CompatController @@ -85,15 +87,15 @@ public function indexAction() $this->addControl($limitControl); $this->addControl($searchBar); $this->addContent( - (new ButtonLink( - t('Add Channel'), - Url::fromPath('notifications/channels/add'), - 'plus' - ))->setBaseTarget('_next') - ->addAttributes(['class' => 'add-new-component']) + (new ButtonLink(t('Add Channel'), Links::channelAdd(), 'plus')) + ->setBaseTarget('_next') + ->addAttributes(['class' => 'add-new-component']) ); - $this->addContent(new ChannelList($channels)); + $this->addContent( + (new ObjectList($channels, new ChannelRenderer())) + ->setItemLayoutClass(MinimalItemLayout::class) + ); if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { $this->sendMultipartUpdate(); @@ -112,7 +114,7 @@ public function addAction() $form->getValue('name') ) ); - $this->redirectNow(Url::fromPath('notifications/channels')); + $this->redirectNow(Links::channels()); }) ->handleRequest($this->getServerRequest()); diff --git a/library/Notifications/Common/Links.php b/library/Notifications/Common/Links.php index fbdd03e5e..309483eda 100644 --- a/library/Notifications/Common/Links.php +++ b/library/Notifications/Common/Links.php @@ -41,6 +41,21 @@ public static function contact(int $id): Url return Url::fromPath('notifications/contact', ['id' => $id]); } + public static function channels(): Url + { + return Url::fromPath('notifications/channels'); + } + + public static function channel(int $id): Url + { + return Url::fromPath('notifications/channel', ['id' => $id]); + } + + public static function channelAdd(): Url + { + return Url::fromPath('notifications/channels/add'); + } + public static function eventRules(): Url { return Url::fromPath('notifications/event-rules'); diff --git a/library/Notifications/View/ChannelRenderer.php b/library/Notifications/View/ChannelRenderer.php new file mode 100644 index 000000000..09b6dac7f --- /dev/null +++ b/library/Notifications/View/ChannelRenderer.php @@ -0,0 +1,48 @@ + */ +class ChannelRenderer implements ItemRenderer +{ + public function assembleAttributes($item, Attributes $attributes, string $layout): void + { + $attributes->get('class')->addValue('channel'); + } + + public function assembleVisual($item, HtmlDocument $visual, string $layout): void + { + $visual->addHtml($item->getIcon()); + } + + public function assembleTitle($item, HtmlDocument $title, string $layout): void + { + $title->addHtml(new Link($item->name, Links::channel($item->id), ['class' => 'subject'])); + } + + public function assembleCaption($item, HtmlDocument $caption, string $layout): void + { + } + + public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void + { + } + + public function assembleFooter($item, HtmlDocument $footer, string $layout): void + { + } + + public function assemble($item, string $name, HtmlDocument $element, string $layout): bool + { + return false; // no custom sections + } +} diff --git a/library/Notifications/Widget/ItemList/ChannelList.php b/library/Notifications/Widget/ItemList/ChannelList.php deleted file mode 100644 index 25e97e09f..000000000 --- a/library/Notifications/Widget/ItemList/ChannelList.php +++ /dev/null @@ -1,20 +0,0 @@ - ['action-list', 'channel-list']]; - - protected function getItemClass(): string - { - return ChannelListItem::class; - } -} diff --git a/library/Notifications/Widget/ItemList/ChannelListItem.php b/library/Notifications/Widget/ItemList/ChannelListItem.php deleted file mode 100644 index 4881eea10..000000000 --- a/library/Notifications/Widget/ItemList/ChannelListItem.php +++ /dev/null @@ -1,54 +0,0 @@ -getAttributes() - ->set('data-action-item', true); - } - - protected function assembleVisual(BaseHtmlElement $visual): void - { - $visual->addHtml($this->item->getIcon()); - } - - protected function assembleTitle(BaseHtmlElement $title): void - { - $title->addHtml(new Link( - $this->item->name, - Url::fromPath('notifications/channel', ['id' => $this->item->id]), - ['class' => 'subject'] - )); - } - - protected function assembleHeader(BaseHtmlElement $header): void - { - $header->add($this->createTitle()); - } - - protected function assembleMain(BaseHtmlElement $main): void - { - $main->add($this->createHeader()); - } -} diff --git a/library/Notifications/Widget/ItemList/ObjectList.php b/library/Notifications/Widget/ItemList/ObjectList.php new file mode 100644 index 000000000..659fb788d --- /dev/null +++ b/library/Notifications/Widget/ItemList/ObjectList.php @@ -0,0 +1,29 @@ + + */ +class ObjectList extends ItemList +{ + protected $defaultAttributes = ['class' => 'action-list']; + + protected function createListItem(object $data): ListItem + { + return parent::createListItem($data) + ->addAttributes(['data-action-item' => true]); + } +} diff --git a/public/css/common.less b/public/css/common.less index ef6275737..fe4b55b0b 100644 --- a/public/css/common.less +++ b/public/css/common.less @@ -92,7 +92,7 @@ color: @text-color-light; } -.full-width .add-new-component { +.add-new-component { margin: 0 0 1em 1em; } From 5e9788c3484a144c13e648317fca47ff109de713 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Wed, 2 Apr 2025 12:56:06 +0200 Subject: [PATCH 02/22] Introduce class ContactRenderer - Links: Add missing contactAdd() method - Remove obsolete code --- .../controllers/ContactGroupController.php | 10 ++-- .../controllers/ContactsController.php | 18 ++++--- library/Notifications/Common/Links.php | 5 ++ .../Notifications/View/ContactRenderer.php | 54 +++++++++++++++++++ .../Widget/ItemList/ContactList.php | 17 ------ 5 files changed, 76 insertions(+), 28 deletions(-) create mode 100644 library/Notifications/View/ContactRenderer.php delete mode 100644 library/Notifications/Widget/ItemList/ContactList.php diff --git a/application/controllers/ContactGroupController.php b/application/controllers/ContactGroupController.php index b1c31aa4f..1997592ae 100644 --- a/application/controllers/ContactGroupController.php +++ b/application/controllers/ContactGroupController.php @@ -9,8 +9,8 @@ use Icinga\Module\Notifications\Forms\ContactGroupForm; use Icinga\Module\Notifications\Model\Contact; use Icinga\Module\Notifications\Model\Contactgroup; -use Icinga\Module\Notifications\Model\ContactgroupMember; -use Icinga\Module\Notifications\Widget\ItemList\ContactList; +use Icinga\Module\Notifications\View\ContactRenderer; +use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use Icinga\Web\Notification; use ipl\Html\Attributes; use ipl\Html\Form; @@ -20,6 +20,7 @@ use ipl\Stdlib\Filter; use ipl\Web\Common\BaseItemList; use ipl\Web\Compat\CompatController; +use ipl\Web\Layout\MinimalItemLayout; use ipl\Web\Widget\ButtonLink; class ContactGroupController extends CompatController @@ -64,7 +65,10 @@ public function indexAction(): void ))->openInModal() ); - $this->addContent(new ContactList($contacts)); + $this->addContent( + (new ObjectList($contacts, new ContactRenderer())) + ->setItemLayoutClass(MinimalItemLayout::class) + ); $this->addTitleTab(t('Contact Group')); $this->setTitle(sprintf(t('Contact Group: %s'), $group->name)); diff --git a/application/controllers/ContactsController.php b/application/controllers/ContactsController.php index 0ce973306..20d69be53 100644 --- a/application/controllers/ContactsController.php +++ b/application/controllers/ContactsController.php @@ -5,11 +5,12 @@ namespace Icinga\Module\Notifications\Controllers; use Icinga\Module\Notifications\Common\Links; +use Icinga\Module\Notifications\View\ContactRenderer; use Icinga\Module\Notifications\Web\Control\SearchBar\ObjectSuggestions; use Icinga\Module\Notifications\Common\Database; use Icinga\Module\Notifications\Model\Contact; use Icinga\Module\Notifications\Web\Form\ContactForm; -use Icinga\Module\Notifications\Widget\ItemList\ContactList; +use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use Icinga\Web\Notification; use ipl\Sql\Connection; use ipl\Stdlib\Filter; @@ -19,6 +20,7 @@ use ipl\Web\Control\LimitControl; use ipl\Web\Control\SortControl; use ipl\Web\Filter\QueryString; +use ipl\Web\Layout\MinimalItemLayout; use ipl\Web\Widget\ButtonLink; use ipl\Html\ValidHtml; @@ -80,15 +82,15 @@ public function indexAction() $this->addControl($limitControl); $this->addControl($searchBar); $this->addContent( - (new ButtonLink( - t('Add Contact'), - 'notifications/contacts/add', - 'plus' - ))->setBaseTarget('_next') - ->addAttributes(['class' => 'add-new-component']) + (new ButtonLink(t('Add Contact'), Links::contactAdd(), 'plus')) + ->setBaseTarget('_next') + ->addAttributes(['class' => 'add-new-component']) ); - $this->addContent(new ContactList($contacts)); + $this->addContent( + (new ObjectList($contacts, new ContactRenderer())) + ->setItemLayoutClass(MinimalItemLayout::class) + ); if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { $this->sendMultipartUpdate(); diff --git a/library/Notifications/Common/Links.php b/library/Notifications/Common/Links.php index 309483eda..3773f8589 100644 --- a/library/Notifications/Common/Links.php +++ b/library/Notifications/Common/Links.php @@ -41,6 +41,11 @@ public static function contact(int $id): Url return Url::fromPath('notifications/contact', ['id' => $id]); } + public static function contactAdd(): Url + { + return Url::fromPath('notifications/contacts/add'); + } + public static function channels(): Url { return Url::fromPath('notifications/channels'); diff --git a/library/Notifications/View/ContactRenderer.php b/library/Notifications/View/ContactRenderer.php new file mode 100644 index 000000000..b41bbef6b --- /dev/null +++ b/library/Notifications/View/ContactRenderer.php @@ -0,0 +1,54 @@ + */ +class ContactRenderer implements ItemRenderer +{ + public function assembleAttributes($item, Attributes $attributes, string $layout): void + { + $attributes->get('class')->addValue('contact'); + } + + public function assembleVisual($item, HtmlDocument $visual, string $layout): void + { + $visual->addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => 'contact-ball']), + Text::create(grapheme_substr($item->full_name, 0, 1)) + )); + } + + public function assembleTitle($item, HtmlDocument $title, string $layout): void + { + $title->addHtml(new Link($item->full_name, Links::contact($item->id), ['class' => 'subject'])); + } + + public function assembleCaption($item, HtmlDocument $caption, string $layout): void + { + } + + public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void + { + } + + public function assembleFooter($item, HtmlDocument $footer, string $layout): void + { + } + + public function assemble($item, string $name, HtmlDocument $element, string $layout): bool + { + return false; // no custom sections + } +} diff --git a/library/Notifications/Widget/ItemList/ContactList.php b/library/Notifications/Widget/ItemList/ContactList.php deleted file mode 100644 index 7735a1154..000000000 --- a/library/Notifications/Widget/ItemList/ContactList.php +++ /dev/null @@ -1,17 +0,0 @@ - ['action-list', 'contact-list']]; - - protected function getItemClass(): string - { - return ContactListItem::class; - } -} From 7b3db0134447b58a439d50cc26ef2aba0907ec2a Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Wed, 2 Apr 2025 15:52:46 +0200 Subject: [PATCH 03/22] Introduce class ContactgroupRenderer - Remove obsolete code --- .../controllers/ContactGroupsController.php | 9 +++- .../View/ContactgroupRenderer.php | 54 +++++++++++++++++++ .../Widget/ItemList/ContactGroupList.php | 17 ------ 3 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 library/Notifications/View/ContactgroupRenderer.php delete mode 100644 library/Notifications/Widget/ItemList/ContactGroupList.php diff --git a/application/controllers/ContactGroupsController.php b/application/controllers/ContactGroupsController.php index feb228314..3acef9efd 100644 --- a/application/controllers/ContactGroupsController.php +++ b/application/controllers/ContactGroupsController.php @@ -8,8 +8,9 @@ use Icinga\Module\Notifications\Common\Links; use Icinga\Module\Notifications\Forms\ContactGroupForm; use Icinga\Module\Notifications\Model\Contactgroup; +use Icinga\Module\Notifications\View\ContactgroupRenderer; use Icinga\Module\Notifications\Web\Control\SearchBar\ObjectSuggestions; -use Icinga\Module\Notifications\Widget\ItemList\ContactGroupList; +use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use Icinga\Module\Notifications\Widget\MemberSuggestions; use Icinga\Web\Notification; use ipl\Html\Form; @@ -22,6 +23,7 @@ use ipl\Web\Control\LimitControl; use ipl\Web\Control\SortControl; use ipl\Web\Filter\QueryString; +use ipl\Web\Layout\MinimalItemLayout; use ipl\Web\Widget\ButtonLink; use ipl\Web\Widget\Tabs; @@ -87,7 +89,10 @@ public function indexAction(): void ))->openInModal() ); - $this->addContent(new ContactGroupList($groups)); + $this->addContent( + (new ObjectList($groups, new ContactgroupRenderer())) + ->setItemLayoutClass(MinimalItemLayout::class) + ); if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { $this->sendMultipartUpdate(); diff --git a/library/Notifications/View/ContactgroupRenderer.php b/library/Notifications/View/ContactgroupRenderer.php new file mode 100644 index 000000000..94446f5d5 --- /dev/null +++ b/library/Notifications/View/ContactgroupRenderer.php @@ -0,0 +1,54 @@ + */ +class ContactgroupRenderer implements ItemRenderer +{ + public function assembleAttributes($item, Attributes $attributes, string $layout): void + { + $attributes->get('class')->addValue('contactgroup'); + } + + public function assembleVisual($item, HtmlDocument $visual, string $layout): void + { + $visual->addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => 'contact-ball']), + Text::create(grapheme_substr($item->name, 0, 1)) + )); + } + + public function assembleTitle($item, HtmlDocument $title, string $layout): void + { + $title->addHtml(new Link($item->name, Links::contactGroup($item->id), ['class' => 'subject'])); + } + + public function assembleCaption($item, HtmlDocument $caption, string $layout): void + { + } + + public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void + { + } + + public function assembleFooter($item, HtmlDocument $footer, string $layout): void + { + } + + public function assemble($item, string $name, HtmlDocument $element, string $layout): bool + { + return false; // no custom sections + } +} diff --git a/library/Notifications/Widget/ItemList/ContactGroupList.php b/library/Notifications/Widget/ItemList/ContactGroupList.php deleted file mode 100644 index 99fc351ee..000000000 --- a/library/Notifications/Widget/ItemList/ContactGroupList.php +++ /dev/null @@ -1,17 +0,0 @@ - ['action-list', 'contactgroup-list']]; - - protected function getItemClass(): string - { - return ContactGroupListItem::class; - } -} From eea075fb4d7e37b87372b0e772b56f406de06ae5 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Wed, 2 Apr 2025 16:11:02 +0200 Subject: [PATCH 04/22] Introduce classes EventRenderer, ObjectHeader and LoadMoreObjectList - Remove obsolete code --- application/controllers/EventController.php | 10 +- application/controllers/EventsController.php | 15 +- library/Notifications/View/EventRenderer.php | 113 +++++++++++++++ .../Widget/Detail/ObjectHeader.php | 57 ++++++++ .../Widget/ItemList/EventList.php | 46 ------ .../Widget/ItemList/EventListItem.php | 134 ------------------ .../Widget/ItemList/LoadMoreObjectList.php | 39 +++++ public/css/list/event-rule-list.less | 18 --- 8 files changed, 223 insertions(+), 209 deletions(-) create mode 100644 library/Notifications/View/EventRenderer.php create mode 100644 library/Notifications/Widget/Detail/ObjectHeader.php delete mode 100644 library/Notifications/Widget/ItemList/EventList.php delete mode 100644 library/Notifications/Widget/ItemList/EventListItem.php create mode 100644 library/Notifications/Widget/ItemList/LoadMoreObjectList.php delete mode 100644 public/css/list/event-rule-list.less diff --git a/application/controllers/EventController.php b/application/controllers/EventController.php index f3ae1e8d1..5b5efc74a 100644 --- a/application/controllers/EventController.php +++ b/application/controllers/EventController.php @@ -4,13 +4,11 @@ namespace Icinga\Module\Notifications\Controllers; -use ArrayObject; use Icinga\Module\Notifications\Common\Auth; use Icinga\Module\Notifications\Common\Database; use Icinga\Module\Notifications\Model\Event; use Icinga\Module\Notifications\Widget\Detail\EventDetail; -use Icinga\Module\Notifications\Widget\ItemList\EventList; -use ipl\Orm\ResultSet; +use Icinga\Module\Notifications\Widget\Detail\ObjectHeader; use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; @@ -40,11 +38,7 @@ public function indexAction(): void $this->httpNotFound(t('Event not found')); } - $this->addControl( - (new EventList(new ResultSet(new ArrayObject([$event])))) - ->setPageSize(1) - ->setNoSubjectLink() - ); + $this->addControl(new ObjectHeader($event)); $this->controls->addAttributes(['class' => 'event-detail']); diff --git a/application/controllers/EventsController.php b/application/controllers/EventsController.php index 470facfa6..336bc3f31 100644 --- a/application/controllers/EventsController.php +++ b/application/controllers/EventsController.php @@ -6,9 +6,10 @@ use Icinga\Module\Notifications\Common\Auth; use Icinga\Module\Notifications\Common\Database; +use Icinga\Module\Notifications\Hook\ObjectsRendererHook; use Icinga\Module\Notifications\Web\Control\SearchBar\ObjectSuggestions; -use Icinga\Module\Notifications\Widget\ItemList\EventList; use Icinga\Module\Notifications\Model\Event; +use Icinga\Module\Notifications\Widget\ItemList\LoadMoreObjectList; use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; use ipl\Web\Compat\SearchControls; @@ -16,6 +17,8 @@ use ipl\Web\Control\SortControl; use ipl\Web\Filter\QueryString; use ipl\Web\Url; +use ipl\Web\Widget\ItemList; +use ipl\Web\Widget\ListItem; class EventsController extends CompatController { @@ -83,9 +86,15 @@ public function indexAction(): void $url = Url::fromRequest()->onlyWith($preserveParams); $url->setQueryString(QueryString::render($filter) . '&' . $url->getParams()->toString()); - $eventList = (new EventList($events->execute())) + $eventList = (new LoadMoreObjectList($events->execute())) ->setPageSize($limitControl->getLimit()) - ->setLoadMoreUrl($url->setParam('before', $before)); + ->setLoadMoreUrl($url->setParam('before', $before)) + ->on(ItemList::ON_ITEM_ADD, function (ListItem $item, Event $data) { + ObjectsRendererHook::register($data->object); + }) + ->on(ItemList::ON_ASSEMBLED, function () { + ObjectsRendererHook::load(); + }); if ($compact) { $eventList->setPageNumber($page); diff --git a/library/Notifications/View/EventRenderer.php b/library/Notifications/View/EventRenderer.php new file mode 100644 index 000000000..4ed98d6dc --- /dev/null +++ b/library/Notifications/View/EventRenderer.php @@ -0,0 +1,113 @@ + */ +class EventRenderer implements ItemRenderer +{ + public function assembleAttributes($item, Attributes $attributes, string $layout): void + { + $attributes->get('class')->addValue('event'); + } + + public function assembleVisual($item, HtmlDocument $visual, string $layout): void + { + $icon = $item->getIcon(); + if ($icon) { + $visual->addHtml($icon); + } + } + + public function assembleTitle($item, HtmlDocument $title, string $layout): void + { + if (! $item->incident instanceof Incident) { + throw new InvalidArgumentException('Incidents must be loaded with the event'); + } + + if ($item->incident->id !== null) { + $title->addHtml(Html::tag('span', [], sprintf('#%d:', $item->incident->id))); + } + + if ($layout === 'header') { + $content = new HtmlElement('span'); + } else { + $content = new Link(null, Links::event($item->id)); + } + + /** @var Objects $obj */ + $obj = $item->object; + $name = $obj->getName(); + + $content->addAttributes($name->getAttributes()); + $content->addFrom($name); + + $title->addHtml($content); + $title->addHtml(HtmlElement::create('span', ['class' => 'state'], $item->getTypeText())); + } + + public function assembleCaption($item, HtmlDocument $caption, string $layout): void + { + switch ($item->type) { + case 'mute': + case 'unmute': + case 'flapping-start': + case 'flapping-end': + case 'downtime-start': + case 'downtime-end': + case 'downtime-removed': + case 'acknowledgement-set': + case 'acknowledgement-cleared': + if ($item->mute_reason !== null) { + $caption->add($item->mute_reason); + break; + } + + // Sometimes these events have no mute reason, but a message + default: + $caption->add($item->message); + } + } + + public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void + { + if ($item->type === 'state') { + /** @var Objects $object */ + $object = $item->object; + /** @var Source $source */ + $source = $object->source; + $info->addHtml( + (new Ball(Ball::SIZE_BIG)) + ->addAttributes(['class' => 'source-icon']) + ->addHtml($source->getIcon()) + ); + } + + $info->addHtml(new TimeAgo($item->time->getTimestamp())); + } + + public function assembleFooter($item, HtmlDocument $footer, string $layout): void + { + } + + public function assemble($item, string $name, HtmlDocument $element, string $layout): bool + { + return false; // no custom sections + } +} diff --git a/library/Notifications/Widget/Detail/ObjectHeader.php b/library/Notifications/Widget/Detail/ObjectHeader.php new file mode 100644 index 000000000..00cd049eb --- /dev/null +++ b/library/Notifications/Widget/Detail/ObjectHeader.php @@ -0,0 +1,57 @@ +object = $object; + } + + /** + * @throws NotImplementedError When the object type is not supported + */ + protected function assemble(): void + { + switch (true) { + case $this->object instanceof Event: + $renderer = new EventRenderer(); + + break; + default: + throw new NotImplementedError('Not implemented'); + } + + $layout = new HeaderItemLayout($this->object, $renderer); + + $this->addAttributes($layout->getAttributes()); + $this->addHtml($layout); + } +} diff --git a/library/Notifications/Widget/ItemList/EventList.php b/library/Notifications/Widget/ItemList/EventList.php deleted file mode 100644 index 96d3b9a6c..000000000 --- a/library/Notifications/Widget/ItemList/EventList.php +++ /dev/null @@ -1,46 +0,0 @@ - ['action-list', 'event-list']]; - - /** @var ResultSet */ - protected $data; - - public function __construct(ResultSet $data) - { - parent::__construct($data); - } - - protected function init(): void - { - $this->data = $this->getIterator($this->data); - - $this->on(self::ON_ITEM_ADD, function (EventListItem $item, Event $data) { - ObjectsRendererHook::register($data->object); - }); - - $this->on(self::ON_ASSEMBLED, function () { - ObjectsRendererHook::load(); - }); - } - - protected function getItemClass(): string - { - return EventListItem::class; - } -} diff --git a/library/Notifications/Widget/ItemList/EventListItem.php b/library/Notifications/Widget/ItemList/EventListItem.php deleted file mode 100644 index 874d02ebb..000000000 --- a/library/Notifications/Widget/ItemList/EventListItem.php +++ /dev/null @@ -1,134 +0,0 @@ -item->incident instanceof Incident) { - throw new InvalidArgumentException('Incidents must be loaded with the event'); - } else { - $this->incident = $this->item->incident; - } - - if (! $this->list->getNoSubjectLink()) { - $this->getAttributes() - ->set('data-action-item', true); - } - } - - protected function assembleVisual(BaseHtmlElement $visual): void - { - $icon = $this->item->getIcon(); - if ($icon) { - $visual->addHtml($icon); - } - } - - protected function assembleTitle(BaseHtmlElement $title): void - { - if ($this->incident->id !== null) { - $title->addHtml(Html::tag('span', [], sprintf('#%d:', $this->incident->id))); - } - - if (! $this->list->getNoSubjectLink()) { - $content = new Link(null, Links::event($this->item->id)); - } else { - $content = new HtmlElement('span'); - } - - /** @var Objects $obj */ - $obj = $this->item->object; - $name = $obj->getName(); - - $content->addAttributes($name->getAttributes()); - $content->addFrom($name); - - $title->addHtml($content); - $title->addHtml(HtmlElement::create('span', ['class' => 'state'], $this->item->getTypeText())); - } - - protected function assembleCaption(BaseHtmlElement $caption): void - { - switch ($this->item->type) { - case 'mute': - case 'unmute': - case 'flapping-start': - case 'flapping-end': - case 'downtime-start': - case 'downtime-end': - case 'downtime-removed': - case 'acknowledgement-set': - case 'acknowledgement-cleared': - if ($this->item->mute_reason !== null) { - $caption->add($this->item->mute_reason); - break; - } - - // Sometimes these events have no mute reason, but a message - default: - $caption->add($this->item->message); - } - } - - protected function assembleHeader(BaseHtmlElement $header): void - { - $content = []; - if ($this->item->type === 'state') { - /** @var Objects $object */ - $object = $this->item->object; - /** @var Source $source */ - $source = $object->source; - $content[] = (new Ball(Ball::SIZE_BIG)) - ->addAttributes(['class' => 'source-icon']) - ->addHtml($source->getIcon()); - } - - $content[] = new TimeAgo($this->item->time->getTimestamp()); - - $header->add($this->createTitle()); - $header->add( - Html::tag( - 'span', - ['class' => 'meta'], - $content - ) - ); - } - - protected function assembleMain(BaseHtmlElement $main): void - { - $main->add($this->createHeader()); - $main->add($this->createCaption()); - $main->add($this->createFooter()); - } -} diff --git a/library/Notifications/Widget/ItemList/LoadMoreObjectList.php b/library/Notifications/Widget/ItemList/LoadMoreObjectList.php new file mode 100644 index 000000000..e363afeb3 --- /dev/null +++ b/library/Notifications/Widget/ItemList/LoadMoreObjectList.php @@ -0,0 +1,39 @@ + + */ +class LoadMoreObjectList extends ObjectList +{ + use LoadMore; + + public function __construct(ResultSet $data) + { + parent::__construct($data, function (Model $item) { + if ($item instanceof Event) { + return new EventRenderer(); + } + + throw new NotImplementedError('Not implemented'); + }); + + $this->data = $this->getIterator($data); + } +} diff --git a/public/css/list/event-rule-list.less b/public/css/list/event-rule-list.less deleted file mode 100644 index 87282e271..000000000 --- a/public/css/list/event-rule-list.less +++ /dev/null @@ -1,18 +0,0 @@ -.item-list.event-rule-list { - .list-item .main { - margin-left: 1em; - - header, - footer { - height: 2em; - } - - .meta { - margin-left: auto; - - > span { - margin-left: 1em; - } - } - } -} \ No newline at end of file From 666c81136627d1281e653230e310321475f3492f Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Thu, 3 Apr 2025 11:24:26 +0200 Subject: [PATCH 05/22] Introduce class IncidentRenderer - Remove obsolete code --- .../controllers/IncidentController.php | 7 +- .../controllers/IncidentsController.php | 18 ++- .../Notifications/View/IncidentRenderer.php | 116 +++++++++++++++++ .../Widget/Detail/EventDetail.php | 7 +- .../Widget/Detail/ObjectHeader.php | 8 +- .../Widget/ItemList/IncidentList.php | 33 ----- .../Widget/ItemList/IncidentListItem.php | 122 ------------------ public/css/common.less | 6 +- 8 files changed, 149 insertions(+), 168 deletions(-) create mode 100644 library/Notifications/View/IncidentRenderer.php delete mode 100644 library/Notifications/Widget/ItemList/IncidentList.php delete mode 100644 library/Notifications/Widget/ItemList/IncidentListItem.php diff --git a/application/controllers/IncidentController.php b/application/controllers/IncidentController.php index b6695aa2d..742cab1ef 100644 --- a/application/controllers/IncidentController.php +++ b/application/controllers/IncidentController.php @@ -11,7 +11,7 @@ use Icinga\Module\Notifications\Model\Incident; use Icinga\Module\Notifications\Widget\Detail\IncidentDetail; use Icinga\Module\Notifications\Widget\Detail\IncidentQuickActions; -use Icinga\Module\Notifications\Widget\ItemList\IncidentList; +use Icinga\Module\Notifications\Widget\Detail\ObjectHeader; use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; @@ -38,10 +38,7 @@ public function indexAction(): void $this->httpNotFound(t('Incident not found')); } - $this->addControl( - (new IncidentList($query)) - ->setNoSubjectLink() - ); + $this->addControl(new ObjectHeader($incident)); $this->controls->addAttributes(['class' => 'incident-detail']); diff --git a/application/controllers/IncidentsController.php b/application/controllers/IncidentsController.php index a8e644446..13ebb8431 100644 --- a/application/controllers/IncidentsController.php +++ b/application/controllers/IncidentsController.php @@ -6,15 +6,20 @@ use Icinga\Module\Notifications\Common\Auth; use Icinga\Module\Notifications\Common\Database; +use Icinga\Module\Notifications\Hook\ObjectsRendererHook; +use Icinga\Module\Notifications\View\IncidentRenderer; use Icinga\Module\Notifications\Web\Control\SearchBar\ObjectSuggestions; use Icinga\Module\Notifications\Model\Incident; -use Icinga\Module\Notifications\Widget\ItemList\IncidentList; +use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; use ipl\Web\Compat\SearchControls; use ipl\Web\Control\LimitControl; use ipl\Web\Control\SortControl; use ipl\Web\Filter\QueryString; +use ipl\Web\Layout\MinimalItemLayout; +use ipl\Web\Widget\ItemList; +use ipl\Web\Widget\ListItem; class IncidentsController extends CompatController { @@ -68,7 +73,16 @@ public function indexAction(): void $this->addControl($limitControl); $this->addControl($searchBar); - $this->addContent(new IncidentList($incidents)); + $incidentList = (new ObjectList($incidents, new IncidentRenderer())) + ->setItemLayoutClass(MinimalItemLayout::class) + ->on(ItemList::ON_ITEM_ADD, function (ListItem $item, Incident $data) { + ObjectsRendererHook::register($data->object); + }) + ->on(ItemList::ON_ASSEMBLED, function () { + ObjectsRendererHook::load(); + }); + + $this->addContent($incidentList); if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { $this->sendMultipartUpdate(); diff --git a/library/Notifications/View/IncidentRenderer.php b/library/Notifications/View/IncidentRenderer.php new file mode 100644 index 000000000..2d92767e3 --- /dev/null +++ b/library/Notifications/View/IncidentRenderer.php @@ -0,0 +1,116 @@ + */ +class IncidentRenderer implements ItemRenderer +{ + use Translation; + + public function assembleAttributes($item, Attributes $attributes, string $layout): void + { + $attributes->get('class')->addValue('incident'); + } + + public function assembleVisual($item, HtmlDocument $visual, string $layout): void + { + switch ($item->severity) { + case 'ok': + $icon = Icons::OK; + break; + case 'err': + $icon = Icons::ERROR; + break; + case 'crit': + $icon = Icons::CRITICAL; + break; + default: + $icon = Icons::WARNING; + } + + $content = new Icon($icon, ['class' => ['severity-' . $item->severity]]); + + if ($item->severity === 'ok' || $item->severity === 'err') { + $content->setStyle('fa-regular'); + } + + $visual->addHtml($content); + } + + public function assembleTitle($item, HtmlDocument $title, string $layout): void + { + $title->addHtml(Html::tag('span', [], sprintf('#%d:', $item->id))); + + if ($layout === 'header') { + $content = new HtmlElement('span'); + } else { + $content = new Link(null, Links::incident($item->id)); + } + + /** @var Objects $obj */ + $obj = $item->object; + $name = $obj->getName(); + + $content->addAttributes($name->getAttributes()); + $content->addFrom($name); + + $title->addHtml($content); + } + + public function assembleCaption($item, HtmlDocument $caption, string $layout): void + { + } + + public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void + { + if ($item->severity !== 'ok' && $item->object->mute_reason !== null) { + $info->addHtml(new Icon(Icons::MUTE, ['title' => $item->object->mute_reason])); + } + + /** @var Source $source */ + $source = $item->object->source; + $info->addHtml( + (new Ball(Ball::SIZE_BIG)) + ->addAttributes(['class' => 'source-icon']) + ->addHtml($source->getIcon()) + ); + + if ($item->recovered_at !== null) { + $info->addHtml(FormattedString::create( + $this->translate('closed %s', '(incident) ... '), + new TimeAgo($item->recovered_at->getTimestamp()) + )); + } else { + $info->addHtml(new TimeSince($item->started_at->getTimestamp())); + } + } + + public function assembleFooter($item, HtmlDocument $footer, string $layout): void + { + } + + public function assemble($item, string $name, HtmlDocument $element, string $layout): bool + { + return false; // no custom sections + } +} diff --git a/library/Notifications/Widget/Detail/EventDetail.php b/library/Notifications/Widget/Detail/EventDetail.php index 3bb130588..c6070d115 100644 --- a/library/Notifications/Widget/Detail/EventDetail.php +++ b/library/Notifications/Widget/Detail/EventDetail.php @@ -8,14 +8,15 @@ use Icinga\Module\Notifications\Hook\ObjectsRendererHook; use Icinga\Module\Notifications\Model\Event; use Icinga\Module\Notifications\Model\Incident; +use Icinga\Module\Notifications\View\IncidentRenderer; use Icinga\Module\Notifications\Widget\EventSourceBadge; -use Icinga\Module\Notifications\Widget\ItemList\IncidentList; use InvalidArgumentException; use ipl\Html\BaseHtmlElement; use ipl\Html\Html; use ipl\Html\HtmlElement; use ipl\Html\Text; use ipl\Html\ValidHtml; +use ipl\Web\Layout\MinimalItemLayout; use ipl\Web\Widget\HorizontalKeyValue; class EventDetail extends BaseHtmlElement @@ -123,9 +124,11 @@ protected function createIncident(): ?array return null; } + $incidentItem = new MinimalItemLayout($this->incident, new IncidentRenderer()); + return [ Html::tag('h2', t('Incident')), - new IncidentList([$this->incident]) + new HtmlElement('div', $incidentItem->getAttributes(), $incidentItem) ]; } diff --git a/library/Notifications/Widget/Detail/ObjectHeader.php b/library/Notifications/Widget/Detail/ObjectHeader.php index 00cd049eb..b2fefa117 100644 --- a/library/Notifications/Widget/Detail/ObjectHeader.php +++ b/library/Notifications/Widget/Detail/ObjectHeader.php @@ -6,7 +6,9 @@ use Icinga\Exception\NotImplementedError; use Icinga\Module\Notifications\Model\Event; +use Icinga\Module\Notifications\Model\Incident; use Icinga\Module\Notifications\View\EventRenderer; +use Icinga\Module\Notifications\View\IncidentRenderer; use ipl\Html\BaseHtmlElement; use ipl\Orm\Model; use ipl\Web\Layout\HeaderItemLayout; @@ -16,7 +18,7 @@ * * Create a header * - * @template Item of Event + * @template Item of Event|Incident */ class ObjectHeader extends BaseHtmlElement { @@ -44,6 +46,10 @@ protected function assemble(): void case $this->object instanceof Event: $renderer = new EventRenderer(); + break; + case $this->object instanceof Incident: + $renderer = new IncidentRenderer(); + break; default: throw new NotImplementedError('Not implemented'); diff --git a/library/Notifications/Widget/ItemList/IncidentList.php b/library/Notifications/Widget/ItemList/IncidentList.php deleted file mode 100644 index ba66e37a3..000000000 --- a/library/Notifications/Widget/ItemList/IncidentList.php +++ /dev/null @@ -1,33 +0,0 @@ - ['action-list', 'incident-list']]; - - protected function init(): void - { - $this->on(self::ON_ITEM_ADD, function (IncidentListItem $item, Incident $data) { - ObjectsRendererHook::register($data->object); - }); - - $this->on(self::ON_ASSEMBLED, function () { - ObjectsRendererHook::load(); - }); - } - - protected function getItemClass(): string - { - return IncidentListItem::class; - } -} diff --git a/library/Notifications/Widget/ItemList/IncidentListItem.php b/library/Notifications/Widget/ItemList/IncidentListItem.php deleted file mode 100644 index 619cb85c8..000000000 --- a/library/Notifications/Widget/ItemList/IncidentListItem.php +++ /dev/null @@ -1,122 +0,0 @@ -list->getNoSubjectLink()) { - $this->getAttributes() - ->set('data-action-item', true); - } - } - - protected function assembleVisual(BaseHtmlElement $visual): void - { - $content = new Icon($this->getSeverityIcon(), ['class' => ['severity-' . $this->item->severity]]); - - if ($this->item->severity === 'ok' || $this->item->severity === 'err') { - $content->setStyle('fa-regular'); - } - - $visual->addHtml($content); - } - - protected function assembleTitle(BaseHtmlElement $title): void - { - $title->addHtml(Html::tag('span', [], sprintf('#%d:', $this->item->id))); - - if (! $this->list->getNoSubjectLink()) { - $content = new Link(null, Links::incident($this->item->id)); - } else { - $content = new HtmlElement('span'); - } - - /** @var Objects $obj */ - $obj = $this->item->object; - $name = $obj->getName(); - - $content->addAttributes($name->getAttributes()); - $content->addFrom($name); - - $title->addHtml($content); - } - - protected function assembleHeader(BaseHtmlElement $header): void - { - $header->add($this->createTitle()); - $meta = new HtmlElement('span', Attributes::create(['class' => 'meta'])); - - if ($this->item->severity !== 'ok' && $this->item->object->mute_reason !== null) { - $meta->addHtml(new Icon(Icons::MUTE, ['title' => $this->item->object->mute_reason])); - } - - /** @var Source $source */ - $source = $this->item->object->source; - $meta->addHtml((new Ball(Ball::SIZE_BIG)) - ->addAttributes(['class' => 'source-icon']) - ->addHtml($source->getIcon())); - - if ($this->item->recovered_at !== null) { - $meta->addHtml(FormattedString::create( - $this->translate('closed %s', '(incident) ... '), - new TimeAgo($this->item->recovered_at->getTimestamp()) - )); - } else { - $meta->addHtml(new TimeSince($this->item->started_at->getTimestamp())); - } - - $header->addHtml($meta); - } - - protected function getSeverityIcon(): string - { - switch ($this->item->severity) { - case 'ok': - return Icons::OK; - case 'err': - return Icons::ERROR; - case 'crit': - return Icons::CRITICAL; - default: - return Icons::WARNING; - } - } - - protected function assembleMain(BaseHtmlElement $main): void - { - $main->add($this->createHeader()); - } -} diff --git a/public/css/common.less b/public/css/common.less index fe4b55b0b..b460ef0a4 100644 --- a/public/css/common.less +++ b/public/css/common.less @@ -96,9 +96,9 @@ margin: 0 0 1em 1em; } -.event-list .list-item, -.incident-list .list-item { - .meta > :not(:last-child) { +.item-layout.event, +.item-layout.incident { + .extended-info > :not(:last-child) { margin-right: 0.5em; } } From a39e4287a34f39b3f3ce84eed74097176c0ec542 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Thu, 3 Apr 2025 11:28:54 +0200 Subject: [PATCH 06/22] ObjectsRenderer: Use new ItemLayout class instead --- .../Notifications/ObjectsRenderer.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/library/Notifications/ProvidedHook/Notifications/ObjectsRenderer.php b/library/Notifications/ProvidedHook/Notifications/ObjectsRenderer.php index 66bf37e91..c31c327c8 100644 --- a/library/Notifications/ProvidedHook/Notifications/ObjectsRenderer.php +++ b/library/Notifications/ProvidedHook/Notifications/ObjectsRenderer.php @@ -9,8 +9,8 @@ use Icinga\Module\Icingadb\Model\Host; use Icinga\Module\Icingadb\Model\Service; use Icinga\Module\Icingadb\Redis\VolatileStateResults; -use Icinga\Module\Icingadb\Widget\ItemList\HostList; -use Icinga\Module\Icingadb\Widget\ItemList\ServiceList; +use Icinga\Module\Icingadb\View\HostRenderer; +use Icinga\Module\Icingadb\View\ServiceRenderer; use Icinga\Module\Notifications\Hook\ObjectsRendererHook; use ipl\Html\Attributes; use ipl\Html\Html; @@ -19,6 +19,7 @@ use ipl\Html\ValidHtml; use ipl\Orm\Query; use ipl\Stdlib\Filter; +use ipl\Web\Layout\MinimalItemLayout; use ipl\Web\Widget\StateBall; class ObjectsRenderer extends ObjectsRendererHook @@ -94,8 +95,9 @@ public function createObjectLink(array $objectIdTag): ?ValidHtml return null; } - return (new ServiceList([$serviceStates])) - ->setViewMode('minimal'); + $item = new MinimalItemLayout($serviceStates, new ServiceRenderer()); + + return new HtmlElement('div', $item->getAttributes(), $item); } $hostStates = $hostsQuery @@ -108,8 +110,9 @@ public function createObjectLink(array $objectIdTag): ?ValidHtml return null; } - return (new HostList([$hostStates])) - ->setViewMode('minimal'); + $item = new MinimalItemLayout($hostStates, new HostRenderer()); + + return new HtmlElement('div', $item->getAttributes(), $item); } public function getObjectNames(array $objectIdTags): Generator From 52b67598dc1ea6a0ac0dce475b5c5287e8e1136e Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Thu, 3 Apr 2025 15:06:07 +0200 Subject: [PATCH 07/22] Introduce class EventRuleRenderer - Remove obsolete code --- .../controllers/EventRulesController.php | 9 ++- .../Notifications/View/EventRuleRenderer.php | 68 +++++++++++++++++ .../Widget/ItemList/EventRuleList.php | 20 ----- .../Widget/ItemList/EventRuleListItem.php | 75 ------------------- public/css/common.less | 10 +++ 5 files changed, 85 insertions(+), 97 deletions(-) create mode 100644 library/Notifications/View/EventRuleRenderer.php delete mode 100644 library/Notifications/Widget/ItemList/EventRuleList.php delete mode 100644 library/Notifications/Widget/ItemList/EventRuleListItem.php diff --git a/application/controllers/EventRulesController.php b/application/controllers/EventRulesController.php index 8e1a0e6d7..142a35787 100644 --- a/application/controllers/EventRulesController.php +++ b/application/controllers/EventRulesController.php @@ -8,9 +8,10 @@ use Icinga\Module\Notifications\Common\Links; use Icinga\Module\Notifications\Forms\SaveEventRuleForm; use Icinga\Module\Notifications\Model\Rule; +use Icinga\Module\Notifications\View\EventRuleRenderer; use Icinga\Module\Notifications\Web\Control\SearchBar\ObjectSuggestions; use Icinga\Module\Notifications\Widget\EventRuleConfig; -use Icinga\Module\Notifications\Widget\ItemList\EventRuleList; +use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use Icinga\Web\Notification; use Icinga\Web\Session; use ipl\Html\Html; @@ -21,6 +22,7 @@ use ipl\Web\Control\SearchEditor; use ipl\Web\Control\SortControl; use ipl\Web\Filter\QueryString; +use ipl\Web\Layout\DetailedItemLayout; use ipl\Web\Url; use ipl\Web\Widget\ButtonLink; use ipl\Web\Widget\Icon; @@ -91,7 +93,10 @@ public function indexAction(): void ->addAttributes(['class' => 'new-event-rule']) ); - $this->addContent(new EventRuleList($eventRules)); + $this->addContent( + (new ObjectList($eventRules, new EventRuleRenderer())) + ->setItemLayoutClass(DetailedItemLayout::class) + ); if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { $this->sendMultipartUpdate(); diff --git a/library/Notifications/View/EventRuleRenderer.php b/library/Notifications/View/EventRuleRenderer.php new file mode 100644 index 000000000..561c0c338 --- /dev/null +++ b/library/Notifications/View/EventRuleRenderer.php @@ -0,0 +1,68 @@ + */ +class EventRuleRenderer implements ItemRenderer +{ + public function assembleAttributes($item, Attributes $attributes, string $layout): void + { + $attributes->get('class')->addValue('rule'); + } + + public function assembleVisual($item, HtmlDocument $visual, string $layout): void + { + } + + public function assembleTitle($item, HtmlDocument $title, string $layout): void + { + $title->addHtml(new Link($item->name, Links::eventRule($item->id), ['class' => 'subject'])); + } + + public function assembleCaption($item, HtmlDocument $caption, string $layout): void + { + } + + public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void + { + $rs = $item->rule_escalation->first(); + if ($rs) { + $recipientCount = $rs->rule_escalation_recipient->count(); + if ($recipientCount) { + $info->addHtml(new RuleEscalationRecipientBadge( + $rs->rule_escalation_recipient->first(), + $recipientCount - 1 + )); + } + } + } + + public function assembleFooter($item, HtmlDocument $footer, string $layout): void + { + if ($item->object_filter) { + $footer->addHtml(new Icon('filter')); + } + + $escalationCount = $item->rule_escalation->count(); + if ($escalationCount > 1) { + $footer->addHtml(new Icon('code-branch'), new Text($escalationCount)); + } + } + + public function assemble($item, string $name, HtmlDocument $element, string $layout): bool + { + return false; // no custom sections + } +} diff --git a/library/Notifications/Widget/ItemList/EventRuleList.php b/library/Notifications/Widget/ItemList/EventRuleList.php deleted file mode 100644 index 533448fcf..000000000 --- a/library/Notifications/Widget/ItemList/EventRuleList.php +++ /dev/null @@ -1,20 +0,0 @@ - ['action-list', 'event-rule-list']]; - - protected function getItemClass(): string - { - return EventRuleListItem::class; - } -} diff --git a/library/Notifications/Widget/ItemList/EventRuleListItem.php b/library/Notifications/Widget/ItemList/EventRuleListItem.php deleted file mode 100644 index 0e8afd14e..000000000 --- a/library/Notifications/Widget/ItemList/EventRuleListItem.php +++ /dev/null @@ -1,75 +0,0 @@ -getAttributes() - ->set('data-action-item', true); - } - - protected function assembleFooter(BaseHtmlElement $footer): void - { - $meta = Html::tag('span', ['class' => 'meta']); - - if ($this->item->object_filter) { - $meta->add(Html::tag('span', new Icon('filter'))); - } - - $escalationCount = $this->item->rule_escalation->count(); - if ($escalationCount > 1) { - $meta->add(Html::tag('span', [new Icon('code-branch'), $escalationCount])); - } - - $footer->add($meta); - } - - protected function assembleTitle(BaseHtmlElement $title): void - { - $title->addHtml(new Link($this->item->name, Links::eventRule($this->item->id), ['class' => 'subject'])); - } - - protected function assembleHeader(BaseHtmlElement $header): void - { - $header->add($this->createTitle()); - //TODO(sd): need fixes? - $rs = $this->item->rule_escalation->first(); - if ($rs) { - $recipientCount = $rs->rule_escalation_recipient->count(); - if ($recipientCount) { - $header->add(new RuleEscalationRecipientBadge( - $rs->rule_escalation_recipient->first(), - $recipientCount - 1 - )); - } - } - } - - protected function assembleMain(BaseHtmlElement $main): void - { - $main->add($this->createHeader()); - $main->add($this->createFooter()); - } -} diff --git a/public/css/common.less b/public/css/common.less index b460ef0a4..3f7d2d0f0 100644 --- a/public/css/common.less +++ b/public/css/common.less @@ -96,9 +96,19 @@ margin: 0 0 1em 1em; } +.item-layout.rule, .item-layout.event, .item-layout.incident { .extended-info > :not(:last-child) { margin-right: 0.5em; } } + +.item-layout.rule footer { + justify-content: end; + align-items: baseline; + + > :not(:last-child) { + margin-right: 0.5em; + } +} From 4631db8228ea96c12582caa4df2a5cc40003edac Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Thu, 3 Apr 2025 16:13:49 +0200 Subject: [PATCH 08/22] Introduce class ScheduleRenderer - Remove obsolete code - Adjust Schedule-list.less accordingly --- .../controllers/SchedulesController.php | 15 +++- .../Notifications/View/ScheduleRenderer.php | 75 +++++++++++++++++++ .../Widget/ItemList/ScheduleList.php | 37 --------- .../Widget/ItemList/ScheduleListItem.php | 72 ------------------ public/css/list/schedule-list.less | 65 ++++++++-------- 5 files changed, 121 insertions(+), 143 deletions(-) create mode 100644 library/Notifications/View/ScheduleRenderer.php delete mode 100644 library/Notifications/Widget/ItemList/ScheduleList.php delete mode 100644 library/Notifications/Widget/ItemList/ScheduleListItem.php diff --git a/application/controllers/SchedulesController.php b/application/controllers/SchedulesController.php index fa5ff70e0..7bb6b92f2 100644 --- a/application/controllers/SchedulesController.php +++ b/application/controllers/SchedulesController.php @@ -4,11 +4,16 @@ namespace Icinga\Module\Notifications\Controllers; +use DateTime; use Icinga\Module\Notifications\Common\Database; use Icinga\Module\Notifications\Common\Links; use Icinga\Module\Notifications\Model\Schedule; +use Icinga\Module\Notifications\View\ScheduleRenderer; use Icinga\Module\Notifications\Web\Control\SearchBar\ObjectSuggestions; -use Icinga\Module\Notifications\Widget\ItemList\ScheduleList; +use Icinga\Module\Notifications\Widget\ItemList\ObjectList; +use Icinga\Module\Notifications\Widget\TimeGrid\DaysHeader; +use ipl\Html\Attributes; +use ipl\Html\HtmlElement; use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; use ipl\Web\Compat\SearchControls; @@ -73,7 +78,13 @@ public function indexAction(): void ))->openInModal() ); - $this->addContent(new ScheduleList($schedules)); + $this->addContent(new HtmlElement( + 'div', + Attributes::create(['class' => 'schedules-header']), + new DaysHeader((new DateTime())->setTime(0, 0), 7) + )); + + $this->addContent(new ObjectList($schedules, new ScheduleRenderer())); if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { $this->sendMultipartUpdate(); diff --git a/library/Notifications/View/ScheduleRenderer.php b/library/Notifications/View/ScheduleRenderer.php new file mode 100644 index 000000000..d29c4d1cc --- /dev/null +++ b/library/Notifications/View/ScheduleRenderer.php @@ -0,0 +1,75 @@ + */ +class ScheduleRenderer implements ItemRenderer +{ + public function assembleAttributes($item, Attributes $attributes, string $layout): void + { + $attributes->get('class')->addValue('schedule-item'); + } + + public function assembleVisual($item, HtmlDocument $visual, string $layout): void + { + } + + public function assembleTitle($item, HtmlDocument $title, string $layout): void + { + $title->addHtml( + new Link( + $item->name, + Links::schedule($item->id), + ['class' => 'subject'] + ) + ); + } + + public function assembleCaption($item, HtmlDocument $caption, string $layout): void + { + // Number of days is set to 7, since default mode for schedule is week + // and the start day should be the current day + $timeline = (new Timeline((new DateTime())->setTime(0, 0), 7)) + ->minimalLayout() + ->setStyle( + (new Style()) + ->setNonce(Csp::getStyleNonce()) + ->setModule('notifications') + ); + + $rotations = $item->rotation->with('timeperiod')->orderBy('first_handoff', SORT_DESC); + + foreach ($rotations as $rotation) { + $timeline->addRotation(new Rotation($rotation)); + } + + $caption->addHtml($timeline); + } + + public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void + { + } + + public function assembleFooter($item, HtmlDocument $footer, string $layout): void + { + } + + public function assemble($item, string $name, HtmlDocument $element, string $layout): bool + { + return false; // no custom sections + } +} diff --git a/library/Notifications/Widget/ItemList/ScheduleList.php b/library/Notifications/Widget/ItemList/ScheduleList.php deleted file mode 100644 index f7e9182bf..000000000 --- a/library/Notifications/Widget/ItemList/ScheduleList.php +++ /dev/null @@ -1,37 +0,0 @@ - ['action-list', 'schedule-list']]; - - protected function getItemClass(): string - { - return ScheduleListItem::class; - } - - protected function assemble(): void - { - parent::assemble(); - - $this->prependWrapper( - (new HtmlDocument())->add( - HtmlElement::create( - 'div', - Attributes::create(['class' => 'schedules-header']), - new DaysHeader((new DateTime())->setTime(0, 0), 7) - ) - ) - ); - } -} diff --git a/library/Notifications/Widget/ItemList/ScheduleListItem.php b/library/Notifications/Widget/ItemList/ScheduleListItem.php deleted file mode 100644 index 5a6ffab6b..000000000 --- a/library/Notifications/Widget/ItemList/ScheduleListItem.php +++ /dev/null @@ -1,72 +0,0 @@ -getAttributes() - ->set('data-action-item', true); - } - - protected function assembleTitle(BaseHtmlElement $title): void - { - $title->addHtml(new Link( - $this->item->name, - Links::schedule($this->item->id), - ['class' => 'subject'] - )); - } - - protected function assembleHeader(BaseHtmlElement $header): void - { - $header->addHtml($this->createTitle()); - } - - protected function assembleCaption(BaseHtmlElement $caption): void - { - // Number of days is set to 7, since default mode for schedule is week - // and the start day should be the current day - $timeline = (new Timeline((new DateTime())->setTime(0, 0), 7)) - ->minimalLayout() - ->setStyle( - (new Style()) - ->setNonce(Csp::getStyleNonce()) - ->setModule('notifications') - ); - - foreach ($this->item->rotation->with('timeperiod')->orderBy('first_handoff', SORT_DESC) as $rotation) { - $timeline->addRotation(new Rotation($rotation)); - } - - $caption->addHtml($timeline); - } - - protected function assembleMain(BaseHtmlElement $main): void - { - $main->addHtml($this->createHeader(), $this->createCaption()); - } -} diff --git a/public/css/list/schedule-list.less b/public/css/list/schedule-list.less index c2520df2c..91b7e8355 100644 --- a/public/css/list/schedule-list.less +++ b/public/css/list/schedule-list.less @@ -3,6 +3,9 @@ margin-left: 12em; width: ~"calc(100% - 12em)"; + padding-right: 2em; // align the header with the list items + padding-left: 0.5em; + .days-header { display: grid; grid-template-columns: repeat(7, minmax(2em, 1fr)); @@ -15,47 +18,45 @@ } // Layout -.item-list.schedule-list { - .list-item { - .main { +.item-list .schedule-item { + .main { + display: flex; + align-items: center; + header .title { + width: 10em; + display: inline-flex; + } + + .caption { display: flex; + margin-left: 0.5em; + width: 100%; + height: 2em; align-items: center; - header .title { - width: 10em; - display: inline-flex; - } - - .caption { - display: flex; - margin-left: 0.5em; - width: 100%; - height: 2em; - align-items: center; - > .timeline { - flex-grow: 1; - } + > .timeline { + flex-grow: 1; } } + } - .timeline .time-grid { - grid-template-columns: minmax(0, 1fr); - grid-template-rows: minmax(0, 1fr); + .timeline .time-grid { + grid-template-columns: minmax(0, 1fr); + grid-template-rows: minmax(0, 1fr); - .grid { - border-width: 0 0 0 2px; - } + .grid { + border-width: 0 0 0 2px; + } - .grid, - .overlay { - grid-area: ~"1 / 1 / 2 / 2"; + .grid, + .overlay { + grid-area: ~"1 / 1 / 2 / 2"; - .entry { - margin: 0; + .entry { + margin: 0; - .title { - align-items: center; - } + .title { + align-items: center; } } } @@ -63,7 +64,7 @@ } // Design -.schedule-list .timeline { +.item-list .schedule-item .timeline { .time-grid { .grid { .step { From f28edb8a2bab8c28e2c63b03851b146658cc5d47 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Thu, 3 Apr 2025 16:32:19 +0200 Subject: [PATCH 09/22] Introduce IncidentContactRenderer - Remove obsolete code --- .../View/IncidentContactRenderer.php | 58 ++++++++++++++++++ .../Widget/Detail/IncidentDetail.php | 7 ++- .../Widget/ItemList/IncidentContactList.php | 17 ------ .../ItemList/IncidentContactListItem.php | 61 ------------------- public/css/detail/incident-detail.less | 18 +++--- 5 files changed, 71 insertions(+), 90 deletions(-) create mode 100644 library/Notifications/View/IncidentContactRenderer.php delete mode 100644 library/Notifications/Widget/ItemList/IncidentContactList.php delete mode 100644 library/Notifications/Widget/ItemList/IncidentContactListItem.php diff --git a/library/Notifications/View/IncidentContactRenderer.php b/library/Notifications/View/IncidentContactRenderer.php new file mode 100644 index 000000000..73b043e60 --- /dev/null +++ b/library/Notifications/View/IncidentContactRenderer.php @@ -0,0 +1,58 @@ + */ +class IncidentContactRenderer implements ItemRenderer +{ + use Translation; + + public function assembleAttributes($item, Attributes $attributes, string $layout): void + { + $attributes->get('class')->addValue('incident-contact'); + } + + public function assembleVisual($item, HtmlDocument $visual, string $layout): void + { + $visual->addHtml(new Icon($item->role === 'manager' ? Icons::USER_MANAGER : Icons::USER)); + } + + public function assembleTitle($item, HtmlDocument $title, string $layout): void + { + $title->addHtml(new Link($item->full_name, Links::contact($item->id), ['class' => 'subject'])); + + if ($item->role === 'manager') { + $title->addHtml(new Text($this->translate('manages this incident'))); + } + } + + public function assembleCaption($item, HtmlDocument $caption, string $layout): void + { + } + + public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void + { + } + + public function assembleFooter($item, HtmlDocument $footer, string $layout): void + { + } + + public function assemble($item, string $name, HtmlDocument $element, string $layout): bool + { + return false; // no custom sections + } +} diff --git a/library/Notifications/Widget/Detail/IncidentDetail.php b/library/Notifications/Widget/Detail/IncidentDetail.php index 3929dc17b..537466018 100644 --- a/library/Notifications/Widget/Detail/IncidentDetail.php +++ b/library/Notifications/Widget/Detail/IncidentDetail.php @@ -6,9 +6,10 @@ use Icinga\Module\Notifications\Hook\ObjectsRendererHook; use Icinga\Module\Notifications\Model\Incident; +use Icinga\Module\Notifications\View\IncidentContactRenderer; use Icinga\Module\Notifications\Widget\EventSourceBadge; -use Icinga\Module\Notifications\Widget\ItemList\IncidentContactList; use Icinga\Module\Notifications\Widget\ItemList\IncidentHistoryList; +use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\Html; @@ -16,6 +17,7 @@ use ipl\Html\Table; use ipl\Html\Text; use ipl\Stdlib\Filter; +use ipl\Web\Layout\MinimalItemLayout; class IncidentDetail extends BaseHtmlElement { @@ -51,7 +53,8 @@ protected function createContacts() return [ Html::tag('h2', t('Subscribers')), - new IncidentContactList($contacts) + (new ObjectList($contacts, new IncidentContactRenderer())) + ->setItemLayoutClass(MinimalItemLayout::class) ]; } diff --git a/library/Notifications/Widget/ItemList/IncidentContactList.php b/library/Notifications/Widget/ItemList/IncidentContactList.php deleted file mode 100644 index 93cee140d..000000000 --- a/library/Notifications/Widget/ItemList/IncidentContactList.php +++ /dev/null @@ -1,17 +0,0 @@ - ['action-list', 'minimal', 'incident-contact-list']]; - - protected function getItemClass(): string - { - return IncidentContactListItem::class; - } -} diff --git a/library/Notifications/Widget/ItemList/IncidentContactListItem.php b/library/Notifications/Widget/ItemList/IncidentContactListItem.php deleted file mode 100644 index 1f885045b..000000000 --- a/library/Notifications/Widget/ItemList/IncidentContactListItem.php +++ /dev/null @@ -1,61 +0,0 @@ -getAttributes() - ->set('data-action-item', true); - } - - protected function assembleVisual(BaseHtmlElement $visual): void - { - $iconName = $this->item->role === 'manager' ? Icons::USER_MANAGER : Icons::USER; - $visual->add(new Icon($iconName)); - } - - protected function assembleTitle(BaseHtmlElement $title): void - { - $title->addHtml(new Link( - $this->item->full_name, - Url::fromPath('notifications/contact', ['id' => $this->item->id]), - ['class' => 'subject'] - )); - - if ($this->item->role === 'manager') { - $title->addHtml(Html::tag('span', t('manages this incident'))); - } - } - - protected function assembleHeader(BaseHtmlElement $header): void - { - $header->add($this->createTitle()); - } - - protected function assembleMain(BaseHtmlElement $main): void - { - $main->add($this->createHeader()); - } -} diff --git a/public/css/detail/incident-detail.less b/public/css/detail/incident-detail.less index 653911db0..a659cac51 100644 --- a/public/css/detail/incident-detail.less +++ b/public/css/detail/incident-detail.less @@ -11,18 +11,16 @@ } } - .incident-contact-list { - .list-item { - &:not(:first-child) > .main { - border-top: 0; - } + .list-item.incident-contact { + &:not(:first-child) > .main { + border-top: 0; + } - .visual { - align-self: center; + .visual { + align-self: center; - i.icon { - font-size: 1em; - } + i.icon { + font-size: 1em; } } } From b85b8336ff6270e6a945cab655ba4cc246ac0975 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Mon, 7 Apr 2025 09:03:08 +0200 Subject: [PATCH 10/22] Introduce IncidentHistoryRenderer - Remove obsolete code - ObjectList: Add method to disable action-list functionality --- .../View/IncidentHistoryRenderer.php | 313 ++++++++++++++++++ .../Widget/Detail/IncidentDetail.php | 26 +- .../Widget/ItemList/IncidentHistoryList.php | 17 - .../ItemList/IncidentHistoryListItem.php | 281 ---------------- .../Widget/ItemList/ObjectList.php | 36 +- 5 files changed, 360 insertions(+), 313 deletions(-) create mode 100644 library/Notifications/View/IncidentHistoryRenderer.php delete mode 100644 library/Notifications/Widget/ItemList/IncidentHistoryList.php delete mode 100644 library/Notifications/Widget/ItemList/IncidentHistoryListItem.php diff --git a/library/Notifications/View/IncidentHistoryRenderer.php b/library/Notifications/View/IncidentHistoryRenderer.php new file mode 100644 index 000000000..7f45b3969 --- /dev/null +++ b/library/Notifications/View/IncidentHistoryRenderer.php @@ -0,0 +1,313 @@ + */ +class IncidentHistoryRenderer implements ItemRenderer +{ + use Translation; + + public function assembleAttributes($item, Attributes $attributes, string $layout): void + { + $classes = ['incident-history']; + if ($item->type === 'notified' && $item->notification_state === 'suppressed') { + $classes[] = 'notification-suppressed'; + } + + $attributes->get('class')->addValue($classes); + } + + public function assembleVisual($item, HtmlDocument $visual, string $layout): void + { + $incidentIcon = $this->getIncidentEventIcon($item); + if ($item->type === 'incident_severity_changed') { + $content = new Icon($incidentIcon, ['class' => 'severity-' . $item->new_severity]); + } else { + $content = new IconBall($incidentIcon); + } + + $visual->addHtml($content); + } + + public function assembleTitle($item, HtmlDocument $title, string $layout): void + { + } + + public function assembleCaption($item, HtmlDocument $caption, string $layout): void + { + $caption->addHtml(new Text($this->buildMessage($item))); + } + + public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void + { + $info->addHtml(new TimeAgo($item->time->getTimestamp())); + } + + public function assembleFooter($item, HtmlDocument $footer, string $layout): void + { + } + + public function assemble($item, string $name, HtmlDocument $element, string $layout): bool + { + return false; // no custom sections + } + + /** + * Get the icon for the incident event + * + * @param IncidentHistory $item + * + * @return string + */ + protected function getIncidentEventIcon(IncidentHistory $item): string + { + switch ($item->type) { + case 'opened': + return Icons::OPENED; + case 'muted': + return Icons::MUTE; + case 'unmuted': + return Icons::UNMUTE; + case 'incident_severity_changed': + return $this->getSeverityIcon($item); + case 'recipient_role_changed': + return $this->getRoleIcon($item); + case 'closed': + return Icons::CLOSED; + case 'rule_matched': + return Icons::RULE_MATCHED; + case 'escalation_triggered': + return Icons::TRIGGERED; + case 'notified': + return Icons::NOTIFIED; + default: + return Icons::UNDEFINED; + } + } + + /** + * Get the icon for the new incident severity + * + * @param IncidentHistory $item + * + * @return string + */ + protected function getSeverityIcon(IncidentHistory $item): string + { + switch ($item->new_severity) { + case 'ok': + return Icons::OK; + case 'warning': + return Icons::WARNING; + case 'err': + return Icons::ERROR; + case 'crit': + return Icons::CRITICAL; + default: + return Icons::UNDEFINED; + } + } + + /** + * Get the icon for the incident recipient role + * + * @param IncidentHistory $item + * + * @return string + */ + protected function getRoleIcon(IncidentHistory $item): string + { + switch ($item->new_recipient_role) { + case 'manager': + return Icons::MANAGE; + case 'subscriber': + return Icons::SUBSCRIBED; + default: + if ($item->old_recipient_role !== null) { + if ($item->old_recipient_role === 'manager') { + return Icons::UNMANAGE; + } else { + return Icons::UNSUBSCRIBED; + } + } + + return Icons::UNDEFINED; + } + } + + /** + * Build the message for the incident history item + * + * @param IncidentHistory $item + * + * @return string + */ + protected function buildMessage(IncidentHistory $item): string + { + switch ($item->type) { + case 'opened': + $message = sprintf( + $this->translate('Incident opened at severity %s'), + Event::mapSeverity($item->new_severity) + ); + + break; + case 'closed': + $message = $this->translate('Incident closed'); + + break; + case "notified": + if ($item->contactgroup_id) { + if ($item->notification_state === 'sent') { + $message = sprintf( + $this->translate('Contact %s notified via %s as member of contact group %s'), + $item->contact->full_name, + $item->channel->type, + $item->contactgroup->name + ); + } else { + $message = sprintf( + $this->translate('Contact %s notified via %s as member of contact group %s (%s)'), + $item->contact->full_name, + $item->channel->type, + $item->contactgroup->name, + IncidentHistory::translateNotificationState($item->notification_state) + ); + } + } elseif ($item->schedule_id) { + if ($item->notfication_state === 'sent') { + $message = sprintf( + $this->translate('Contact %s notified via %s as member of schedule %s'), + $item->contact->full_name, + $item->channel->type, + $item->schedule->name + ); + } else { + $message = sprintf( + $this->translate('Contact %s notified via %s as member of schedule %s (%s)'), + $item->contact->full_name, + $item->schedule->name, + $item->channel->type, + IncidentHistory::translateNotificationState($item->notification_state) + ); + } + } elseif ($item->notification_state === 'sent') { + $message = sprintf( + $this->translate('Contact %s notified via %s'), + $item->contact->full_name, + $item->channel->type + ); + } else { + $message = sprintf( + $this->translate('Contact %s notified via %s (%s)'), + $item->contact->full_name, + $item->channel->type, + IncidentHistory::translateNotificationState($item->notification_state) + ); + } + + break; + case 'incident_severity_changed': + $message = sprintf( + $this->translate('Incident severity changed from %s to %s'), + Event::mapSeverity($item->old_severity), + Event::mapSeverity($item->new_severity) + ); + + break; + case 'recipient_role_changed': + $newRole = $item->new_recipient_role; + $message = ''; + if ($newRole === 'manager' || (! $newRole && $item->old_recipient_role === 'manager')) { + if ($item->contact_id) { + $message = sprintf( + $this->translate('Contact %s %s managing this incident'), + $item->contact->full_name, + ! $item->new_recipient_role ? 'stopped' : 'started' + ); + } elseif ($item->contactgroup_id) { + $message = sprintf( + $this->translate('Contact group %s %s managing this incident'), + $item->contactgroup->name, + ! $item->new_recipient_role ? 'stopped' : 'started' + ); + } else { + $message = sprintf( + $this->translate('Schedule %s %s managing this incident'), + $item->schedule->name, + ! $item->new_recipient_role ? 'stopped' : 'started' + ); + } + } elseif ( + $newRole === 'subscriber' + || ( + ! $newRole && $item->old_recipient_role === 'subscriber' + ) + ) { + if ($item->contact_id) { + $message = sprintf( + $this->translate('Contact %s %s this incident'), + $item->contact->full_name, + ! $item->new_recipient_role ? 'unsubscribed from' : 'subscribed to' + ); + } elseif ($item->contactgroup_id) { + $message = sprintf( + $this->translate('Contact group %s %s this incident'), + $item->contactgroup->name, + ! $item->new_recipient_role ? 'unsubscribed from' : 'subscribed to' + ); + } else { + $message = sprintf( + $this->translate('Schedule %s %s this incident'), + $item->schedule->name, + ! $item->new_recipient_role ? 'unsubscribed from' : 'subscribed to' + ); + } + } + + break; + case 'rule_matched': + $message = sprintf($this->translate('Rule %s matched on this incident'), $item->rule->name); + + break; + case 'escalation_triggered': + $message = sprintf( + $this->translate('Rule %s reached escalation %s'), + $item->rule->name, + $item->rule_escalation->name + ); + + break; + case 'muted': + $message = $this->translate('Notifications for this incident have been muted'); + + break; + case 'unmuted': + $message = $this->translate('Notifications for this incident have been unmuted'); + + break; + default: + $message = ''; + } + + if ($item->message) { + $message = $message === '' ? $item->message : $message . ': ' . $item->message; + } + + return $message; + } +} diff --git a/library/Notifications/Widget/Detail/IncidentDetail.php b/library/Notifications/Widget/Detail/IncidentDetail.php index 537466018..46e6bedaf 100644 --- a/library/Notifications/Widget/Detail/IncidentDetail.php +++ b/library/Notifications/Widget/Detail/IncidentDetail.php @@ -7,8 +7,8 @@ use Icinga\Module\Notifications\Hook\ObjectsRendererHook; use Icinga\Module\Notifications\Model\Incident; use Icinga\Module\Notifications\View\IncidentContactRenderer; +use Icinga\Module\Notifications\View\IncidentHistoryRenderer; use Icinga\Module\Notifications\Widget\EventSourceBadge; -use Icinga\Module\Notifications\Widget\ItemList\IncidentHistoryList; use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; @@ -74,19 +74,21 @@ protected function createRelatedObject() protected function createHistory() { + $query = $this->incident->incident_history + ->with([ + 'contact', + 'rule', + 'rule_escalation', + 'contactgroup', + 'schedule', + 'channel' + ]); + return [ Html::tag('h2', t('Incident History')), - new IncidentHistoryList( - $this->incident->incident_history - ->with([ - 'contact', - 'rule', - 'rule_escalation', - 'contactgroup', - 'schedule', - 'channel' - ]) - ) + (new ObjectList($query, new IncidentHistoryRenderer())) + ->setItemLayoutClass(MinimalItemLayout::class) + ->disableActionList() ]; } diff --git a/library/Notifications/Widget/ItemList/IncidentHistoryList.php b/library/Notifications/Widget/ItemList/IncidentHistoryList.php deleted file mode 100644 index cbd6ac424..000000000 --- a/library/Notifications/Widget/ItemList/IncidentHistoryList.php +++ /dev/null @@ -1,17 +0,0 @@ - ['action-list', 'minimal']]; - - protected function getItemClass(): string - { - return IncidentHistoryListItem::class; - } -} diff --git a/library/Notifications/Widget/ItemList/IncidentHistoryListItem.php b/library/Notifications/Widget/ItemList/IncidentHistoryListItem.php deleted file mode 100644 index ad98a90cf..000000000 --- a/library/Notifications/Widget/ItemList/IncidentHistoryListItem.php +++ /dev/null @@ -1,281 +0,0 @@ -getIncidentEventIcon(); - if ($this->item->type === 'incident_severity_changed') { - $content = new Icon($incidentIcon, ['class' => 'severity-' . $this->item->new_severity]); - } else { - $content = new IconBall($incidentIcon); - } - - $visual->addHtml($content); - } - - protected function assembleCaption(BaseHtmlElement $caption): void - { - $caption->add($this->buildMessage()); - } - - protected function assembleHeader(BaseHtmlElement $header): void - { - $title = $this->createTitle(); - if (! $title->isEmpty()) { - $header->addHtml($title); - } - - $header->addHtml($this->createCaption()); - - $header->add(new TimeAgo($this->item->time->getTimestamp())); - } - - protected function getIncidentEventIcon(): string - { - switch ($this->item->type) { - case 'opened': - return Icons::OPENED; - case 'muted': - return Icons::MUTE; - case 'unmuted': - return Icons::UNMUTE; - case 'incident_severity_changed': - return $this->getSeverityIcon(); - case 'recipient_role_changed': - return $this->getRoleIcon(); - case 'closed': - return Icons::CLOSED; - case 'rule_matched': - return Icons::RULE_MATCHED; - case 'escalation_triggered': - return Icons::TRIGGERED; - case 'notified': - return Icons::NOTIFIED; - default: - return Icons::UNDEFINED; - } - } - - protected function getSeverityIcon(): string - { - switch ($this->item->new_severity) { - case 'ok': - return Icons::OK; - case 'warning': - return Icons::WARNING; - case 'err': - return Icons::ERROR; - case 'crit': - return Icons::CRITICAL; - default: - return Icons::UNDEFINED; - } - } - - protected function getRoleIcon(): string - { - switch ($this->item->new_recipient_role) { - case 'manager': - return Icons::MANAGE; - case 'subscriber': - return Icons::SUBSCRIBED; - default: - if ($this->item->old_recipient_role !== null) { - if ($this->item->old_recipient_role === 'manager') { - return Icons::UNMANAGE; - } else { - return Icons::UNSUBSCRIBED; - } - } - - return Icons::UNDEFINED; - } - } - - protected function assembleMain(BaseHtmlElement $main): void - { - $main->add($this->createHeader()); - } - - protected function buildMessage(): string - { - switch ($this->item->type) { - case 'opened': - $message = sprintf( - t('Incident opened at severity %s'), - Event::mapSeverity($this->item->new_severity) - ); - - break; - case 'closed': - $message = t('Incident closed'); - - break; - case "notified": - if ($this->item->contactgroup_id) { - if ($this->item->notification_state === 'sent') { - $message = sprintf( - t('Contact %s notified via %s as member of contact group %s'), - $this->item->contact->full_name, - $this->item->channel->type, - $this->item->contactgroup->name - ); - } else { - $message = sprintf( - t('Contact %s notified via %s as member of contact group %s (%s)'), - $this->item->contact->full_name, - $this->item->channel->type, - $this->item->contactgroup->name, - IncidentHistory::translateNotificationState($this->item->notification_state) - ); - } - } elseif ($this->item->schedule_id) { - if ($this->item->notfication_state === 'sent') { - $message = sprintf( - t('Contact %s notified via %s as member of schedule %s'), - $this->item->contact->full_name, - $this->item->channel->type, - $this->item->schedule->name - ); - } else { - $message = sprintf( - t('Contact %s notified via %s as member of schedule %s (%s)'), - $this->item->contact->full_name, - $this->item->schedule->name, - $this->item->channel->type, - IncidentHistory::translateNotificationState($this->item->notification_state) - ); - } - } elseif ($this->item->notification_state === 'sent') { - $message = sprintf( - t('Contact %s notified via %s'), - $this->item->contact->full_name, - $this->item->channel->type - ); - } else { - $message = sprintf( - t('Contact %s notified via %s (%s)'), - $this->item->contact->full_name, - $this->item->channel->type, - IncidentHistory::translateNotificationState($this->item->notification_state) - ); - } - - if ($this->item->notification_state === 'suppressed') { - $this->getAttributes()->add('class', 'notification-suppressed'); - } - - break; - case 'incident_severity_changed': - $message = sprintf( - t('Incident severity changed from %s to %s'), - Event::mapSeverity($this->item->old_severity), - Event::mapSeverity($this->item->new_severity) - ); - - break; - case 'recipient_role_changed': - $newRole = $this->item->new_recipient_role; - $message = ''; - if ($newRole === 'manager' || (! $newRole && $this->item->old_recipient_role === 'manager')) { - if ($this->item->contact_id) { - $message = sprintf( - t('Contact %s %s managing this incident'), - $this->item->contact->full_name, - ! $this->item->new_recipient_role ? 'stopped' : 'started' - ); - } elseif ($this->item->contactgroup_id) { - $message = sprintf( - t('Contact group %s %s managing this incident'), - $this->item->contactgroup->name, - ! $this->item->new_recipient_role ? 'stopped' : 'started' - ); - } else { - $message = sprintf( - t('Schedule %s %s managing this incident'), - $this->item->schedule->name, - ! $this->item->new_recipient_role ? 'stopped' : 'started' - ); - } - } elseif ( - $newRole === 'subscriber' - || ( - ! $newRole && $this->item->old_recipient_role === 'subscriber' - ) - ) { - if ($this->item->contact_id) { - $message = sprintf( - t('Contact %s %s this incident'), - $this->item->contact->full_name, - ! $this->item->new_recipient_role ? 'unsubscribed from' : 'subscribed to' - ); - } elseif ($this->item->contactgroup_id) { - $message = sprintf( - t('Contact group %s %s this incident'), - $this->item->contactgroup->name, - ! $this->item->new_recipient_role ? 'unsubscribed from' : 'subscribed to' - ); - } else { - $message = sprintf( - t('Schedule %s %s this incident'), - $this->item->schedule->name, - ! $this->item->new_recipient_role ? 'unsubscribed from' : 'subscribed to' - ); - } - } - - break; - case 'rule_matched': - $message = sprintf(t('Rule %s matched on this incident'), $this->item->rule->name); - - break; - case 'escalation_triggered': - $message = sprintf( - t('Rule %s reached escalation %s'), - $this->item->rule->name, - $this->item->rule_escalation->name - ); - - break; - case 'muted': - $message = t('Notifications for this incident have been muted'); - - break; - case 'unmuted': - $message = t('Notifications for this incident have been unmuted'); - - break; - default: - $message = ''; - } - - if ($this->item->message) { - $message = $message === '' ? $this->item->message : $message . ': ' . $this->item->message; - } - - return $message; - } -} diff --git a/library/Notifications/Widget/ItemList/ObjectList.php b/library/Notifications/Widget/ItemList/ObjectList.php index 659fb788d..8cde0fe04 100644 --- a/library/Notifications/Widget/ItemList/ObjectList.php +++ b/library/Notifications/Widget/ItemList/ObjectList.php @@ -19,11 +19,41 @@ */ class ObjectList extends ItemList { - protected $defaultAttributes = ['class' => 'action-list']; + /** @var bool Whether the action-list functionality should be disabled */ + protected $disableActionList = false; + + public function __construct($data, $itemRenderer) + { + parent::__construct($data, $itemRenderer); + + $this->getAttributes() // TODO(sd): only required for IncidentHistory, find a better solution + ->registerAttributeCallback('class', function () { + return $this->disableActionList ? null : 'action-list'; + }); + } + + /** + * Set whether the action-list functionality should be disabled + * + * @param bool $state + * + * @return $this + */ + public function disableActionList(bool $state = true): self + { + $this->disableActionList = $state; + + return $this; + } protected function createListItem(object $data): ListItem { - return parent::createListItem($data) - ->addAttributes(['data-action-item' => true]); + $item = parent::createListItem($data); + + if (! $this->disableActionList) { + $item->addAttributes(['data-action-item' => true]); + } + + return $item; } } From 7e83eb12e46e23a26b24345790f255d17a231087 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Mon, 7 Apr 2025 09:19:19 +0200 Subject: [PATCH 11/22] Introduce SourceRenderer - Remove obsoltete code --- application/controllers/SourcesController.php | 9 +++- library/Notifications/View/SourceRenderer.php | 52 +++++++++++++++++++ .../Widget/ItemList/SourceList.php | 17 ------ .../Widget/ItemList/SourceListItem.php | 50 ------------------ 4 files changed, 59 insertions(+), 69 deletions(-) create mode 100644 library/Notifications/View/SourceRenderer.php delete mode 100644 library/Notifications/Widget/ItemList/SourceList.php delete mode 100644 library/Notifications/Widget/ItemList/SourceListItem.php diff --git a/application/controllers/SourcesController.php b/application/controllers/SourcesController.php index 65b831fc4..3a57e7ba4 100644 --- a/application/controllers/SourcesController.php +++ b/application/controllers/SourcesController.php @@ -7,8 +7,9 @@ use Icinga\Module\Notifications\Common\Database; use Icinga\Module\Notifications\Forms\SourceForm; use Icinga\Module\Notifications\Model\Source; +use Icinga\Module\Notifications\View\SourceRenderer; use Icinga\Module\Notifications\Web\Control\SearchBar\ObjectSuggestions; -use Icinga\Module\Notifications\Widget\ItemList\SourceList; +use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use Icinga\Web\Notification; use Icinga\Web\Widget\Tabs; use ipl\Html\ValidHtml; @@ -18,6 +19,7 @@ use ipl\Web\Control\LimitControl; use ipl\Web\Control\SortControl; use ipl\Web\Filter\QueryString; +use ipl\Web\Layout\MinimalItemLayout; use ipl\Web\Url; use ipl\Web\Widget\ButtonLink; @@ -83,7 +85,10 @@ public function indexAction(): void $this->mergeTabs($this->Module()->getConfigTabs()); $this->getTabs()->activate('sources'); - $this->addContent(new SourceList($sources->execute())); + $this->addContent( + (new ObjectList($sources, new SourceRenderer())) + ->setItemLayoutClass(MinimalItemLayout::class) + ); if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { $this->sendMultipartUpdate(); diff --git a/library/Notifications/View/SourceRenderer.php b/library/Notifications/View/SourceRenderer.php new file mode 100644 index 000000000..3303c9bc6 --- /dev/null +++ b/library/Notifications/View/SourceRenderer.php @@ -0,0 +1,52 @@ + */ +class SourceRenderer implements ItemRenderer +{ + public function assembleAttributes($item, Attributes $attributes, string $layout): void + { + $attributes->get('class')->addValue('source'); + } + + public function assembleVisual($item, HtmlDocument $visual, string $layout): void + { + $visual->addHtml($item->getIcon()); + } + + public function assembleTitle($item, HtmlDocument $title, string $layout): void + { + $title->addHtml(new Link( + $item->name, + Url::fromPath('notifications/source', ['id' => $item->id]), + ['class' => 'subject'] + )); + } + + public function assembleCaption($item, HtmlDocument $caption, string $layout): void + { + } + + public function assembleExtendedInfo($item, HtmlDocument $info, string $layout): void + { + } + + public function assembleFooter($item, HtmlDocument $footer, string $layout): void + { + } + + public function assemble($item, string $name, HtmlDocument $element, string $layout): bool + { + return false; // no custom sections + } +} diff --git a/library/Notifications/Widget/ItemList/SourceList.php b/library/Notifications/Widget/ItemList/SourceList.php deleted file mode 100644 index 57f80b2c3..000000000 --- a/library/Notifications/Widget/ItemList/SourceList.php +++ /dev/null @@ -1,17 +0,0 @@ - 'action-list']; - - protected function getItemClass(): string - { - return SourceListItem::class; - } -} diff --git a/library/Notifications/Widget/ItemList/SourceListItem.php b/library/Notifications/Widget/ItemList/SourceListItem.php deleted file mode 100644 index 8c4633c03..000000000 --- a/library/Notifications/Widget/ItemList/SourceListItem.php +++ /dev/null @@ -1,50 +0,0 @@ -getAttributes() - ->set('data-action-item', true); - } - - protected function assembleVisual(BaseHtmlElement $visual): void - { - $visual->addHtml($this->item->getIcon()); - } - - protected function assembleTitle(BaseHtmlElement $title): void - { - $title->addHtml(new Link( - $this->item->name, - Url::fromPath('notifications/source', ['id' => $this->item->id]), - ['class' => 'subject'] - )); - } - - protected function assembleHeader(BaseHtmlElement $header): void - { - $header->addHtml($this->createTitle()); - } - - protected function assembleMain(BaseHtmlElement $main): void - { - $main->addHtml($this->createHeader()); - } -} From 475ed044bfb3214a463de8a6e79f5104a3ca61e9 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Mon, 7 Apr 2025 09:22:12 +0200 Subject: [PATCH 12/22] Controllers: Don't add css class `full-width` This padding is now handled by ipl-web - Optimize code --- application/controllers/ChannelsController.php | 18 ------------------ application/controllers/ConfigController.php | 2 -- application/controllers/ContactController.php | 4 ---- .../controllers/ContactGroupController.php | 11 ----------- .../controllers/ContactGroupsController.php | 11 ----------- application/controllers/ContactsController.php | 18 ------------------ .../controllers/EventRuleController.php | 1 - application/controllers/SourceController.php | 2 -- application/controllers/SourcesController.php | 18 ------------------ public/css/common.less | 14 -------------- 10 files changed, 99 deletions(-) diff --git a/application/controllers/ChannelsController.php b/application/controllers/ChannelsController.php index d6fb00c27..0858de438 100644 --- a/application/controllers/ChannelsController.php +++ b/application/controllers/ChannelsController.php @@ -14,10 +14,8 @@ use Icinga\Web\Notification; use Icinga\Web\Widget\Tab; use Icinga\Web\Widget\Tabs; -use ipl\Html\ValidHtml; use ipl\Sql\Connection; use ipl\Stdlib\Filter; -use ipl\Web\Common\BaseItemList; use ipl\Web\Compat\CompatController; use ipl\Web\Compat\SearchControls; use ipl\Web\Control\LimitControl; @@ -121,22 +119,6 @@ public function addAction() $this->addContent($form); } - /** - * Add attribute 'class' => 'full-width' if the content is an instance of BaseItemList - * - * @param ValidHtml $content - * - * @return ChannelsController - */ - protected function addContent(ValidHtml $content) - { - if ($content instanceof BaseItemList) { - $this->content->getAttributes()->add('class', 'full-width'); - } - - return parent::addContent($content); - } - public function completeAction(): void { $suggestions = new ObjectSuggestions(); diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index a35d943a6..d2d529faf 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -6,11 +6,9 @@ use Icinga\Application\Config; use Icinga\Module\Notifications\Forms\DatabaseConfigForm; -use Icinga\Web\Form; use Icinga\Web\Notification; use Icinga\Web\Widget\Tab; use Icinga\Web\Widget\Tabs; -use ipl\Html\HtmlString; use ipl\Web\Compat\CompatController; class ConfigController extends CompatController diff --git a/application/controllers/ContactController.php b/application/controllers/ContactController.php index 0fc7b4295..7b827dd65 100644 --- a/application/controllers/ContactController.php +++ b/application/controllers/ContactController.php @@ -5,12 +5,8 @@ namespace Icinga\Module\Notifications\Controllers; use Icinga\Module\Notifications\Common\Database; -use Icinga\Module\Notifications\Model\Contact; use Icinga\Module\Notifications\Web\Form\ContactForm; use Icinga\Web\Notification; -use ipl\Html\FormElement\FieldsetElement; -use ipl\Sql\Connection; -use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; class ContactController extends CompatController diff --git a/application/controllers/ContactGroupController.php b/application/controllers/ContactGroupController.php index 1997592ae..7d8198ef3 100644 --- a/application/controllers/ContactGroupController.php +++ b/application/controllers/ContactGroupController.php @@ -16,9 +16,7 @@ use ipl\Html\Form; use ipl\Html\HtmlElement; use ipl\Html\Text; -use ipl\Html\ValidHtml; use ipl\Stdlib\Filter; -use ipl\Web\Common\BaseItemList; use ipl\Web\Compat\CompatController; use ipl\Web\Layout\MinimalItemLayout; use ipl\Web\Widget\ButtonLink; @@ -115,13 +113,4 @@ public function editAction(): void $this->addContent($form); $this->setTitle(t('Edit Contact Group')); } - - protected function addContent(ValidHtml $content): self - { - if ($content instanceof BaseItemList) { - $this->content->getAttributes()->add('class', 'full-width'); - } - - return parent::addContent($content); - } } diff --git a/application/controllers/ContactGroupsController.php b/application/controllers/ContactGroupsController.php index 3acef9efd..11570c442 100644 --- a/application/controllers/ContactGroupsController.php +++ b/application/controllers/ContactGroupsController.php @@ -15,9 +15,7 @@ use Icinga\Web\Notification; use ipl\Html\Form; use ipl\Html\Text; -use ipl\Html\ValidHtml; use ipl\Stdlib\Filter; -use ipl\Web\Common\BaseItemList; use ipl\Web\Compat\CompatController; use ipl\Web\Compat\SearchControls; use ipl\Web\Control\LimitControl; @@ -183,15 +181,6 @@ public function getTabs(): Tabs ]); } - protected function addContent(ValidHtml $content): self - { - if ($content instanceof BaseItemList) { - $this->content->getAttributes()->add('class', 'full-width'); - } - - return parent::addContent($content); - } - /** * Get the filter created from query string parameters * diff --git a/application/controllers/ContactsController.php b/application/controllers/ContactsController.php index 20d69be53..8a28fad74 100644 --- a/application/controllers/ContactsController.php +++ b/application/controllers/ContactsController.php @@ -14,7 +14,6 @@ use Icinga\Web\Notification; use ipl\Sql\Connection; use ipl\Stdlib\Filter; -use ipl\Web\Common\BaseItemList; use ipl\Web\Compat\CompatController; use ipl\Web\Compat\SearchControls; use ipl\Web\Control\LimitControl; @@ -22,7 +21,6 @@ use ipl\Web\Filter\QueryString; use ipl\Web\Layout\MinimalItemLayout; use ipl\Web\Widget\ButtonLink; -use ipl\Html\ValidHtml; class ContactsController extends CompatController { @@ -116,22 +114,6 @@ public function addAction(): void $this->addContent($form); } - /** - * Add attribute 'class' => 'full-width' if the content is an instance of BaseItemList - * - * @param ValidHtml $content - * - * @return ContactsController - */ - protected function addContent(ValidHtml $content) - { - if ($content instanceof BaseItemList) { - $this->content->getAttributes()->add('class', 'full-width'); - } - - return parent::addContent($content); - } - public function completeAction(): void { $suggestions = new ObjectSuggestions(); diff --git a/application/controllers/EventRuleController.php b/application/controllers/EventRuleController.php index e331e3a25..c96c88bcd 100644 --- a/application/controllers/EventRuleController.php +++ b/application/controllers/EventRuleController.php @@ -9,7 +9,6 @@ use Icinga\Module\Notifications\Common\Links; use Icinga\Module\Notifications\Forms\EventRuleForm; use Icinga\Module\Notifications\Forms\SaveEventRuleForm; -use Icinga\Module\Notifications\Model\Incident; use Icinga\Module\Notifications\Model\Rule; use Icinga\Module\Notifications\Web\Control\SearchBar\ExtraTagSuggestions; use Icinga\Module\Notifications\Widget\EventRuleConfig; diff --git a/application/controllers/SourceController.php b/application/controllers/SourceController.php index 3c442fcca..f109313d5 100644 --- a/application/controllers/SourceController.php +++ b/application/controllers/SourceController.php @@ -6,10 +6,8 @@ use Icinga\Module\Notifications\Common\Database; use Icinga\Module\Notifications\Forms\SourceForm; -use Icinga\Module\Notifications\Model\Source; use Icinga\Web\Notification; use ipl\Html\Contract\FormSubmitElement; -use ipl\Stdlib\Filter; use ipl\Web\Compat\CompatController; class SourceController extends CompatController diff --git a/application/controllers/SourcesController.php b/application/controllers/SourcesController.php index 3a57e7ba4..29a5a14ef 100644 --- a/application/controllers/SourcesController.php +++ b/application/controllers/SourcesController.php @@ -12,8 +12,6 @@ use Icinga\Module\Notifications\Widget\ItemList\ObjectList; use Icinga\Web\Notification; use Icinga\Web\Widget\Tabs; -use ipl\Html\ValidHtml; -use ipl\Web\Common\BaseItemList; use ipl\Web\Compat\CompatController; use ipl\Web\Compat\SearchControls; use ipl\Web\Control\LimitControl; @@ -131,22 +129,6 @@ public function searchEditorAction(): void $this->getDocument()->add($editor); } - /** - * Add attribute 'class' => 'full-width' if the content is an instance of BaseItemList - * - * @param ValidHtml $content - * - * @return $this - */ - protected function addContent(ValidHtml $content) - { - if ($content instanceof BaseItemList) { - $this->content->getAttributes()->add('class', 'full-width'); - } - - return parent::addContent($content); - } - /** * Merge tabs with other tabs contained in this tab panel * diff --git a/public/css/common.less b/public/css/common.less index 3f7d2d0f0..7a805b1ac 100644 --- a/public/css/common.less +++ b/public/css/common.less @@ -1,17 +1,3 @@ -& > .content.full-width { - padding-left: 0; - padding-right: 0; - - .list-item { - padding-left: 1em; - padding-right: 1em; - } - - .item-list > .empty-state { - margin: 0 1em; - } -} - .controls { &.contactgroup-detail, &.event-detail, From 1c7b89a5b38fb2bb1f7f66772c749efdec0dc9f7 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Mon, 7 Apr 2025 12:36:58 +0200 Subject: [PATCH 13/22] Remove now superfluous css --- public/css/list/item-list.less | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/public/css/list/item-list.less b/public/css/list/item-list.less index 4125b105b..7fa125b42 100644 --- a/public/css/list/item-list.less +++ b/public/css/list/item-list.less @@ -11,31 +11,3 @@ } } } - -.item-list.minimal { - > .empty-state { - padding: .25em; - } - - .list-item { - align-items: center; - - header { - max-width: 100%; - } - - .visual { - width: 2.2em; - } - - .caption { - flex: 1 1 auto; - height: 1.5em; - margin-right: 1em; - width: 0; - - .text-ellipsis(); - .line-clamp("reset"); - } - } -} From 2e45fe82c27d85f48e98b56e7c0db18ee53c06e4 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Mon, 7 Apr 2025 13:06:40 +0200 Subject: [PATCH 14/22] ObjectList: Add php generics --- library/Notifications/Widget/ItemList/ObjectList.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/library/Notifications/Widget/ItemList/ObjectList.php b/library/Notifications/Widget/ItemList/ObjectList.php index 8cde0fe04..e68d99600 100644 --- a/library/Notifications/Widget/ItemList/ObjectList.php +++ b/library/Notifications/Widget/ItemList/ObjectList.php @@ -4,7 +4,16 @@ namespace Icinga\Module\Notifications\Widget\ItemList; +use Icinga\Module\Notifications\Model\Channel; +use Icinga\Module\Notifications\Model\Contact; +use Icinga\Module\Notifications\Model\Contactgroup; use Icinga\Module\Notifications\Model\Event; +use Icinga\Module\Notifications\Model\Incident; +use Icinga\Module\Notifications\Model\IncidentContact; +use Icinga\Module\Notifications\Model\IncidentHistory; +use Icinga\Module\Notifications\Model\Rule; +use Icinga\Module\Notifications\Model\Schedule; +use Icinga\Module\Notifications\Model\Source; use ipl\Web\Widget\ItemList; use ipl\Web\Widget\ListItem; @@ -13,7 +22,7 @@ * * Create a list * - * @template Item of Event + * @template Item of Event|Incident|IncidentHistory|IncidentContact|Source|Channel|Contact|Contactgroup|Rule|Schedule * * @extends ItemList */ From e23418a99b5be9be3f740e3bd523ec90713c9d54 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Wed, 9 Apr 2025 12:27:07 +0200 Subject: [PATCH 15/22] Fix `add` button margin --- application/controllers/EventRulesController.php | 2 +- application/controllers/SchedulesController.php | 2 +- public/css/detail/event-rule-detail.less | 4 ---- public/css/schedule.less | 4 ---- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/application/controllers/EventRulesController.php b/application/controllers/EventRulesController.php index 142a35787..d7c294b2d 100644 --- a/application/controllers/EventRulesController.php +++ b/application/controllers/EventRulesController.php @@ -90,7 +90,7 @@ public function indexAction(): void Url::fromPath('notifications/event-rule/edit', ['id' => -1, 'clearCache' => true]), 'plus' ))->openInModal() - ->addAttributes(['class' => 'new-event-rule']) + ->addAttributes(['class' => 'add-new-component']) ); $this->addContent( diff --git a/application/controllers/SchedulesController.php b/application/controllers/SchedulesController.php index 7bb6b92f2..2a022da6c 100644 --- a/application/controllers/SchedulesController.php +++ b/application/controllers/SchedulesController.php @@ -73,7 +73,7 @@ public function indexAction(): void Links::scheduleAdd(), 'plus', [ - 'class' => 'add-schedule-control' + 'class' => 'add-new-component' ] ))->openInModal() ); diff --git a/public/css/detail/event-rule-detail.less b/public/css/detail/event-rule-detail.less index 6f85b48dd..18ca41784 100644 --- a/public/css/detail/event-rule-detail.less +++ b/public/css/detail/event-rule-detail.less @@ -358,10 +358,6 @@ width: 14.5em; } -.new-event-rule { - margin-bottom: 1em; -} - .event-rule-and-save-forms { display: flex; flex-wrap: wrap; diff --git a/public/css/schedule.less b/public/css/schedule.less index 956df6ca4..ecd606d44 100644 --- a/public/css/schedule.less +++ b/public/css/schedule.less @@ -1,9 +1,5 @@ /* Layout */ -.add-schedule-control { - margin-bottom: 1em; -} - .schedule-detail-controls { .box-shadow(0, 0, 0, 1px, @gray-lighter); z-index: 2; // The content may clip, this ensures the separator and dropdown is always visible From bdba639399947bbe3117f4f7b22e45bc0d8d78b5 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Fri, 25 Apr 2025 11:06:35 +0200 Subject: [PATCH 16/22] Remove file `item-list.less` ipl-web provide these changes natively with https://github.com/Icinga/ipl-web/pull/273. --- public/css/list/item-list.less | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 public/css/list/item-list.less diff --git a/public/css/list/item-list.less b/public/css/list/item-list.less deleted file mode 100644 index 7fa125b42..000000000 --- a/public/css/list/item-list.less +++ /dev/null @@ -1,13 +0,0 @@ -// Layout - -.item-list { - .visual { - .icon { // TODO: should this be default in ipl-web? - font-size: 1.5em; - - &:before { - margin-right: 0; - } - } - } -} From 0ce43e32c2cdc195c65a246199df85f896e0c0bd Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Fri, 25 Apr 2025 12:21:10 +0200 Subject: [PATCH 17/22] Move Schedule class to detail and rename it to ScheduleDetail - Rename css class to `schedule-detail` _ Move the schedule controls --- application/controllers/ScheduleController.php | 8 ++++---- .../{Schedule.php => Detail/ScheduleDetail.php} | 16 +++++++++------- .../ScheduleDetail}/Controls.php | 2 +- public/css/schedule.less | 6 +++--- public/css/timeline.less | 2 +- 5 files changed, 18 insertions(+), 16 deletions(-) rename library/Notifications/Widget/{Schedule.php => Detail/ScheduleDetail.php} (80%) rename library/Notifications/Widget/{Schedule => Detail/ScheduleDetail}/Controls.php (97%) diff --git a/application/controllers/ScheduleController.php b/application/controllers/ScheduleController.php index 41bf9066b..92cd3e7c4 100644 --- a/application/controllers/ScheduleController.php +++ b/application/controllers/ScheduleController.php @@ -11,7 +11,7 @@ use Icinga\Module\Notifications\Forms\ScheduleForm; use Icinga\Module\Notifications\Model\Schedule; use Icinga\Module\Notifications\Widget\RecipientSuggestions; -use Icinga\Module\Notifications\Widget\Schedule as ScheduleWidget; +use Icinga\Module\Notifications\Widget\Detail\ScheduleDetail; use ipl\Html\Form; use ipl\Html\Html; use ipl\Stdlib\Filter; @@ -52,15 +52,15 @@ public function indexAction(): void $this->controls->addAttributes(['class' => 'schedule-detail-controls']); - $scheduleControls = (new ScheduleWidget\Controls()) + $scheduleControls = (new ScheduleDetail\Controls()) ->setAction(Url::fromRequest()->getAbsoluteUrl()) ->populate(['mode' => $this->params->get('mode')]) - ->on(Form::ON_SUCCESS, function (ScheduleWidget\Controls $controls) use ($id) { + ->on(Form::ON_SUCCESS, function (ScheduleDetail\Controls $controls) use ($id) { $this->redirectNow(Links::schedule($id)->with(['mode' => $controls->getMode()])); }) ->handleRequest($this->getServerRequest()); - $this->addContent(new ScheduleWidget($schedule, $scheduleControls)); + $this->addContent(new ScheduleDetail($schedule, $scheduleControls)); } public function settingsAction(): void diff --git a/library/Notifications/Widget/Schedule.php b/library/Notifications/Widget/Detail/ScheduleDetail.php similarity index 80% rename from library/Notifications/Widget/Schedule.php rename to library/Notifications/Widget/Detail/ScheduleDetail.php index 6d0ac2f14..0c353de01 100644 --- a/library/Notifications/Widget/Schedule.php +++ b/library/Notifications/Widget/Detail/ScheduleDetail.php @@ -2,9 +2,11 @@ /* Icinga Notifications Web | (c) 2023 Icinga GmbH | GPLv2 */ -namespace Icinga\Module\Notifications\Widget; +namespace Icinga\Module\Notifications\Widget\Detail; -use Icinga\Module\Notifications\Widget\Schedule\Controls; +use Icinga\Module\Notifications\Model\Schedule; +use Icinga\Module\Notifications\Widget\Detail\ScheduleDetail\Controls; +use Icinga\Module\Notifications\Widget\Timeline; use Icinga\Module\Notifications\Widget\Timeline\Rotation; use Icinga\Util\Csp; use ipl\Html\Attributes; @@ -13,15 +15,15 @@ use ipl\Web\Common\BaseTarget; use ipl\Web\Style; -class Schedule extends BaseHtmlElement +class ScheduleDetail extends BaseHtmlElement { use BaseTarget; protected $tag = 'div'; - protected $defaultAttributes = ['id' => 'notifications-schedule', 'class' => 'schedule']; + protected $defaultAttributes = ['id' => 'notifications-schedule', 'class' => 'schedule-detail']; - /** @var \Icinga\Module\Notifications\Model\Schedule */ + /** @var Schedule */ protected $schedule; /** @var Controls */ @@ -30,10 +32,10 @@ class Schedule extends BaseHtmlElement /** * Create a new Schedule * - * @param \Icinga\Module\Notifications\Model\Schedule $schedule + * @param Schedule $schedule * @param Controls $controls */ - public function __construct(\Icinga\Module\Notifications\Model\Schedule $schedule, Controls $controls) + public function __construct(Schedule $schedule, Controls $controls) { $this->schedule = $schedule; $this->controls = $controls; diff --git a/library/Notifications/Widget/Schedule/Controls.php b/library/Notifications/Widget/Detail/ScheduleDetail/Controls.php similarity index 97% rename from library/Notifications/Widget/Schedule/Controls.php rename to library/Notifications/Widget/Detail/ScheduleDetail/Controls.php index 1f99b2a3a..398566da3 100644 --- a/library/Notifications/Widget/Schedule/Controls.php +++ b/library/Notifications/Widget/Detail/ScheduleDetail/Controls.php @@ -2,7 +2,7 @@ /* Icinga Notifications Web | (c) 2024 Icinga GmbH | GPLv2 */ -namespace Icinga\Module\Notifications\Widget\Schedule; +namespace Icinga\Module\Notifications\Widget\Detail\ScheduleDetail; use DateTime; use Icinga\Web\Session; diff --git a/public/css/schedule.less b/public/css/schedule.less index ecd606d44..5e0464dc9 100644 --- a/public/css/schedule.less +++ b/public/css/schedule.less @@ -14,7 +14,7 @@ } } -.schedule { +.schedule-detail { display: flex; flex-direction: column; height: 100%; @@ -58,12 +58,12 @@ /* Design */ -.schedule .entry.highlighted { +.schedule-detail .entry.highlighted { outline: 2px solid var(--entry-border-color); outline-offset: 1px; } -.schedule .step.highlighted { +.schedule-detail .step.highlighted { background-color: @gray-lighter; border-color: @gray-light; } diff --git a/public/css/timeline.less b/public/css/timeline.less index 90a96bde9..eb32163c9 100644 --- a/public/css/timeline.less +++ b/public/css/timeline.less @@ -236,6 +236,6 @@ padding-bottom: .25em; } -#layout.twocols .schedule .timescale:has(:nth-child(n+62)) { // month view (--timestampsPerDay * --primaryColumns = 62) +#layout.twocols .schedule-detail .timescale:has(:nth-child(n+62)) { // month view (--timestampsPerDay * --primaryColumns = 62) display: none; } From 0b0bc57a69e0aeca6f0900862111077e16092bb3 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Fri, 25 Apr 2025 12:24:48 +0200 Subject: [PATCH 18/22] ScheduleRenderer: Use `schedule` as css class --- library/Notifications/View/ScheduleRenderer.php | 2 +- public/css/list/schedule-list.less | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/Notifications/View/ScheduleRenderer.php b/library/Notifications/View/ScheduleRenderer.php index d29c4d1cc..06c77e4e1 100644 --- a/library/Notifications/View/ScheduleRenderer.php +++ b/library/Notifications/View/ScheduleRenderer.php @@ -21,7 +21,7 @@ class ScheduleRenderer implements ItemRenderer { public function assembleAttributes($item, Attributes $attributes, string $layout): void { - $attributes->get('class')->addValue('schedule-item'); + $attributes->get('class')->addValue('schedule'); } public function assembleVisual($item, HtmlDocument $visual, string $layout): void diff --git a/public/css/list/schedule-list.less b/public/css/list/schedule-list.less index 91b7e8355..86fb9db01 100644 --- a/public/css/list/schedule-list.less +++ b/public/css/list/schedule-list.less @@ -18,7 +18,7 @@ } // Layout -.item-list .schedule-item { +.item-list .schedule { .main { display: flex; align-items: center; @@ -64,7 +64,7 @@ } // Design -.item-list .schedule-item .timeline { +.item-list .schedule .timeline { .time-grid { .grid { .step { From db1946ab5e181ea03d44ea20c1b032218956200c Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Fri, 25 Apr 2025 13:25:06 +0200 Subject: [PATCH 19/22] Schedules (list): Align the schedule header with list items --- public/css/list/schedule-list.less | 7 ++----- public/css/timeline.less | 5 ++++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/public/css/list/schedule-list.less b/public/css/list/schedule-list.less index 86fb9db01..9c010287a 100644 --- a/public/css/list/schedule-list.less +++ b/public/css/list/schedule-list.less @@ -1,10 +1,8 @@ // Header .schedules-header { margin-left: 12em; - width: ~"calc(100% - 12em)"; - - padding-right: 2em; // align the header with the list items - padding-left: 0.5em; + margin-right: 1em; + width: ~"calc(100% - 13em)"; // margin-left + margin-right .days-header { display: grid; @@ -29,7 +27,6 @@ .caption { display: flex; - margin-left: 0.5em; width: 100%; height: 2em; align-items: center; diff --git a/public/css/timeline.less b/public/css/timeline.less index eb32163c9..f64f54ead 100644 --- a/public/css/timeline.less +++ b/public/css/timeline.less @@ -11,7 +11,10 @@ --primaryRowHeight: 4em; --daysHeaderHeight: 3em; position: relative; - margin-right: 1em; // make midnight timestamp visible + + &:has(.timescale) { + margin-right: 1em; // make midnight timestamp visible + } .time-grid-header { box-sizing: border-box; From 574e8e12945aaf1cd7045a478215848adb24ec15 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Fri, 25 Apr 2025 14:37:57 +0200 Subject: [PATCH 20/22] Remove superfluous margin-right The .extended-info adds the gap --- public/css/common.less | 8 -------- 1 file changed, 8 deletions(-) diff --git a/public/css/common.less b/public/css/common.less index 7a805b1ac..8c50c1669 100644 --- a/public/css/common.less +++ b/public/css/common.less @@ -82,14 +82,6 @@ margin: 0 0 1em 1em; } -.item-layout.rule, -.item-layout.event, -.item-layout.incident { - .extended-info > :not(:last-child) { - margin-right: 0.5em; - } -} - .item-layout.rule footer { justify-content: end; align-items: baseline; From b0ff808971801ff388e93a723cd2975c393d34e8 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Fri, 25 Apr 2025 11:06:05 +0200 Subject: [PATCH 21/22] Cleanup phpstan baseline --- phpstan-baseline-7x.neon | 5 - phpstan-baseline-8x.neon | 5 - phpstan-baseline-standard.neon | 711 +-------------------------------- 3 files changed, 3 insertions(+), 718 deletions(-) diff --git a/phpstan-baseline-7x.neon b/phpstan-baseline-7x.neon index 5be0f3817..5c9adab60 100644 --- a/phpstan-baseline-7x.neon +++ b/phpstan-baseline-7x.neon @@ -1,10 +1,5 @@ parameters: ignoreErrors: - - - message: "#^Parameter \\#2 \\$str of function explode expects string, mixed given\\.$#" - count: 2 - path: application/forms/EntryForm.php - - message: "#^Parameter \\#2 \\$str of function explode expects string, mixed given\\.$#" count: 2 diff --git a/phpstan-baseline-8x.neon b/phpstan-baseline-8x.neon index 99ed68cfb..a26c7dd57 100644 --- a/phpstan-baseline-8x.neon +++ b/phpstan-baseline-8x.neon @@ -1,10 +1,5 @@ parameters: ignoreErrors: - - - message: "#^Parameter \\#2 \\$string of function explode expects string, mixed given\\.$#" - count: 2 - path: application/forms/EntryForm.php - - message: "#^Parameter \\#2 \\$string of function explode expects string, mixed given\\.$#" count: 2 diff --git a/phpstan-baseline-standard.neon b/phpstan-baseline-standard.neon index 7c04a7968..d5a77207d 100644 --- a/phpstan-baseline-standard.neon +++ b/phpstan-baseline-standard.neon @@ -1,38 +1,13 @@ parameters: ignoreErrors: - - - message: "#^Cannot access property \\$name on ipl\\\\Orm\\\\Model\\|null\\.$#" - count: 1 - path: application/controllers/ChannelController.php - - message: "#^Cannot call method getName\\(\\) on ipl\\\\Html\\\\Contract\\\\FormSubmitElement\\|null\\.$#" count: 1 path: application/controllers/ChannelController.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Controllers\\\\ChannelController\\:\\:indexAction\\(\\) has no return type specified\\.$#" - count: 1 - path: application/controllers/ChannelController.php - - - - message: "#^Parameter \\#1 \\$values of method Icinga\\\\Module\\\\Notifications\\\\Forms\\\\ChannelForm\\:\\:populate\\(\\) expects iterable\\, ipl\\\\Orm\\\\Model\\|null given\\.$#" - count: 1 - path: application/controllers/ChannelController.php - - - - message: "#^Parameter \\#2 \\$channelId of class Icinga\\\\Module\\\\Notifications\\\\Forms\\\\ChannelForm constructor expects int\\|null, mixed given\\.$#" - count: 1 - path: application/controllers/ChannelController.php - - - - message: "#^Parameter \\#2 \\$value of static method ipl\\\\Stdlib\\\\Filter\\:\\:equal\\(\\) expects array\\|bool\\|float\\|int\\|string, mixed given\\.$#" - count: 1 - path: application/controllers/ChannelController.php - - message: "#^Parameter \\#2 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" - count: 3 + count: 2 path: application/controllers/ChannelController.php - @@ -45,11 +20,6 @@ parameters: count: 1 path: application/controllers/ChannelsController.php - - - message: "#^Parameter \\#1 \\$name of method ipl\\\\Web\\\\Widget\\\\Tabs\\:\\:add\\(\\) expects string, mixed given\\.$#" - count: 1 - path: application/controllers/ChannelsController.php - - message: "#^Parameter \\#2 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" count: 1 @@ -60,41 +30,6 @@ parameters: count: 1 path: application/controllers/ConfigController.php - - - message: "#^Parameter \\#1 \\$name of method ipl\\\\Web\\\\Widget\\\\Tabs\\:\\:add\\(\\) expects string, mixed given\\.$#" - count: 1 - path: application/controllers/ConfigController.php - - - - message: "#^Cannot access property \\$full_name on ipl\\\\Orm\\\\Model\\|null\\.$#" - count: 1 - path: application/controllers/ContactController.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Controllers\\\\ContactController\\:\\:indexAction\\(\\) has no return type specified\\.$#" - count: 1 - path: application/controllers/ContactController.php - - - - message: "#^Parameter \\#1 \\$values of method Icinga\\\\Module\\\\Notifications\\\\Web\\\\Form\\\\ContactForm\\:\\:populate\\(\\) expects iterable\\, ipl\\\\Orm\\\\Model\\|null given\\.$#" - count: 1 - path: application/controllers/ContactController.php - - - - message: "#^Parameter \\#2 \\$value of static method ipl\\\\Stdlib\\\\Filter\\:\\:equal\\(\\) expects array\\|bool\\|float\\|int\\|string, mixed given\\.$#" - count: 1 - path: application/controllers/ContactController.php - - - - message: "#^Parameter \\#2 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, mixed given\\.$#" - count: 3 - path: application/controllers/ContactController.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Controllers\\\\ContactsController\\:\\:addAction\\(\\) has no return type specified\\.$#" - count: 1 - path: application/controllers/ContactsController.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Controllers\\\\ContactsController\\:\\:indexAction\\(\\) has no return type specified\\.$#" count: 1 @@ -105,36 +40,11 @@ parameters: count: 1 path: application/controllers/EventController.php - - - message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#" - count: 2 - path: application/controllers/EventRuleController.php - - message: "#^Cannot access offset 'object_filter' on mixed\\.$#" count: 2 path: application/controllers/EventRuleController.php - - - message: "#^Cannot access property \\$position on mixed\\.$#" - count: 2 - path: application/controllers/EventRuleController.php - - - - message: "#^Cannot access property \\$rule_escalation on ipl\\\\Orm\\\\Model\\|null\\.$#" - count: 1 - path: application/controllers/EventRuleController.php - - - - message: "#^Cannot access property \\$rule_escalation_recipient on mixed\\.$#" - count: 1 - path: application/controllers/EventRuleController.php - - - - message: "#^Cannot call method getTableName\\(\\) on mixed\\.$#" - count: 2 - path: application/controllers/EventRuleController.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Controllers\\\\EventRuleController\\:\\:fromDb\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -247,7 +157,7 @@ parameters: - message: "#^Cannot call method getName\\(\\) on ipl\\\\Html\\\\Contract\\\\FormSubmitElement\\|null\\.$#" - count: 2 + count: 1 path: application/forms/ChannelForm.php - @@ -260,176 +170,11 @@ parameters: count: 1 path: application/forms/ChannelForm.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Forms\\\\ChannelForm\\:\\:onSuccess\\(\\) has no return type specified\\.$#" - count: 1 - path: application/forms/ChannelForm.php - - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, mixed given\\.$#" - count: 1 - path: application/forms/ChannelForm.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Forms\\\\DatabaseConfigForm\\:\\:assemble\\(\\) has no return type specified\\.$#" count: 1 path: application/forms/DatabaseConfigForm.php - - - message: "#^Call to an undefined method ipl\\\\Html\\\\Contract\\\\FormElement\\:\\:isChecked\\(\\)\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Call to an undefined method ipl\\\\Html\\\\Contract\\\\FormElement\\:\\:prepareMultipartUpdate\\(\\)\\.$#" - count: 2 - path: application/forms/EntryForm.php - - - - message: "#^Call to an undefined method ipl\\\\Html\\\\Contract\\\\FormElement\\:\\:setDescription\\(\\)\\.$#" - count: 3 - path: application/forms/EntryForm.php - - - - message: "#^Call to an undefined method ipl\\\\Html\\\\Contract\\\\FormElement\\:\\:setLabel\\(\\)\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Call to an undefined method ipl\\\\Scheduler\\\\RRule\\:\\:getUntil\\(\\)\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Call to an undefined method ipl\\\\Sql\\\\Connection\\:\\:lastInsertId\\(\\)\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Cannot access offset 'frequency' on mixed\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Cannot access offset 'rrule' on mixed\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Cannot access offset 'start' on mixed\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Cannot access property \\$contact_id on mixed\\.$#" - count: 6 - path: application/forms/EntryForm.php - - - - message: "#^Cannot access property \\$contactgroup_id on mixed\\.$#" - count: 4 - path: application/forms/EntryForm.php - - - - message: "#^Cannot access property \\$full_name on mixed\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Cannot access property \\$id on mixed\\.$#" - count: 2 - path: application/forms/EntryForm.php - - - - message: "#^Cannot access property \\$name on mixed\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Cannot call method add\\(\\) on DateTime\\|false\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Cannot call method diff\\(\\) on mixed\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Cannot call method format\\(\\) on DateTime\\|false\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Cannot call method format\\(\\) on mixed\\.$#" - count: 2 - path: application/forms/EntryForm.php - - - - message: "#^Cannot call method getTimezone\\(\\) on DateTime\\|false\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Cannot cast mixed to int\\.$#" - count: 2 - path: application/forms/EntryForm.php - - - - message: "#^Cannot clone non\\-object variable \\$start of type DateTime\\|false\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Forms\\\\EntryForm\\:\\:assemble\\(\\) has no return type specified\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Forms\\\\EntryForm\\:\\:formValuesToDb\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Forms\\\\EntryForm\\:\\:getPartUpdates\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Method class@anonymous/application/forms/EntryForm\\.php\\:142\\:\\:assemble\\(\\) has no return type specified\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Method ipl\\\\Html\\\\Contract\\\\FormElement\\:\\:getValue\\(\\) invoked with 1 parameter, 0 required\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Parameter \\#1 \\$json of static method ipl\\\\Scheduler\\\\RRule\\:\\:fromJson\\(\\) expects string, string\\|false given\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Parameter \\#1 \\.\\.\\.\\$content of method ipl\\\\Html\\\\HtmlDocument\\:\\:setHtmlContent\\(\\) expects ipl\\\\Html\\\\ValidHtml, ipl\\\\Html\\\\Contract\\\\Wrappable\\|null given\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Parameter \\#2 \\$datetime of static method DateTime\\:\\:createFromFormat\\(\\) expects string, mixed given\\.$#" - count: 2 - path: application/forms/EntryForm.php - - - - message: "#^Parameter \\#2 \\$value of static method ipl\\\\Stdlib\\\\Filter\\:\\:equal\\(\\) expects array\\|bool\\|float\\|int\\|string, mixed given\\.$#" - count: 1 - path: application/forms/EntryForm.php - - - - message: "#^Property Icinga\\\\Module\\\\Notifications\\\\Forms\\\\EntryForm\\:\\:\\$submitLabel \\(string\\) on left side of \\?\\? is not nullable\\.$#" - count: 1 - path: application/forms/EntryForm.php - - message: "#^Call to an undefined method ipl\\\\Html\\\\Contract\\\\FormElement\\:\\:setName\\(\\)\\.$#" count: 4 @@ -537,7 +282,7 @@ parameters: - message: "#^Cannot access property \\$id on mixed\\.$#" - count: 3 + count: 1 path: application/forms/SaveEventRuleForm.php - @@ -580,11 +325,6 @@ parameters: count: 1 path: application/forms/ScheduleForm.php - - - message: "#^Cannot access property \\$id on mixed\\.$#" - count: 3 - path: application/forms/ScheduleForm.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Forms\\\\ScheduleForm\\:\\:assemble\\(\\) has no return type specified\\.$#" count: 1 @@ -600,36 +340,6 @@ parameters: count: 1 path: library/Notifications/Common/Database.php - - - message: "#^Parameter \\#1 \\$identifier of method ipl\\\\Sql\\\\Connection\\:\\:quoteIdentifier\\(\\) expects array\\\\|string, array\\ given\\.$#" - count: 1 - path: library/Notifications/Model/Behavior/HasAddress.php - - - - message: "#^Parameter \\#1 \\$name of method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Behavior\\\\HasAddress\\:\\:isSelectableColumn\\(\\) expects string, mixed given\\.$#" - count: 1 - path: library/Notifications/Model/Behavior/HasAddress.php - - - - message: "#^Parameter \\#1 \\$string of function strlen expects string, string\\|null given\\.$#" - count: 1 - path: library/Notifications/Model/Behavior/HasAddress.php - - - - message: "#^Parameter \\#2 \\$targetPath of method ipl\\\\Orm\\\\Query\\:\\:createSubQuery\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: library/Notifications/Model/Behavior/HasAddress.php - - - - message: "#^Part \\$column \\(mixed\\) of encapsed string cannot be cast to string\\.$#" - count: 1 - path: library/Notifications/Model/Behavior/HasAddress.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Channel\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Channel.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Channel\\:\\:getColumnDefinitions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -645,16 +355,6 @@ parameters: count: 1 path: library/Notifications/Model/Channel.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Contact\\:\\:createBehaviors\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Contact.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Contact\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Contact.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Contact\\:\\:getColumnDefinitions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -670,51 +370,16 @@ parameters: count: 1 path: library/Notifications/Model/Contact.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\ContactAddress\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/ContactAddress.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Contactgroup\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Contactgroup.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Event\\:\\:createBehaviors\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Event.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Event\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Event.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Event\\:\\:getColumnDefinitions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: library/Notifications/Model/Event.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Event\\:\\:getDefaultSort\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: library/Notifications/Model/Event.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Event\\:\\:getSearchColumns\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: library/Notifications/Model/Event.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Incident\\:\\:createBehaviors\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Incident.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Incident\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Incident.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Incident\\:\\:getColumnDefinitions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -730,26 +395,11 @@ parameters: count: 1 path: library/Notifications/Model/Incident.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\IncidentContact\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/IncidentContact.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\IncidentContact\\:\\:getColumnDefinitions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: library/Notifications/Model/IncidentContact.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\IncidentHistory\\:\\:createBehaviors\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/IncidentHistory.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\IncidentHistory\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/IncidentHistory.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\IncidentHistory\\:\\:getColumnDefinitions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -760,31 +410,6 @@ parameters: count: 1 path: library/Notifications/Model/IncidentHistory.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\ObjectExtraTag\\:\\:createBehaviors\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/ObjectExtraTag.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\ObjectExtraTag\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/ObjectExtraTag.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Objects\\:\\:createBehaviors\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Objects.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Objects\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Objects.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Rule\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Rule.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Rule\\:\\:getColumnDefinitions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -800,11 +425,6 @@ parameters: count: 1 path: library/Notifications/Model/Rule.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\RuleEscalation\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/RuleEscalation.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\RuleEscalation\\:\\:getColumnDefinitions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -820,16 +440,6 @@ parameters: count: 1 path: library/Notifications/Model/RuleEscalation.php - - - message: "#^Cannot call method first\\(\\) on mixed\\.$#" - count: 3 - path: library/Notifications/Model/RuleEscalationRecipient.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\RuleEscalationRecipient\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/RuleEscalationRecipient.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\RuleEscalationRecipient\\:\\:getColumnDefinitions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -840,46 +450,16 @@ parameters: count: 1 path: library/Notifications/Model/RuleEscalationRecipient.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\ScheduleMember\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/ScheduleMember.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Source\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Source.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Source\\:\\:getColumnDefinitions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: library/Notifications/Model/Source.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Source\\:\\:getDefaultSort\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: library/Notifications/Model/Source.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Source\\:\\:getSearchColumns\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: library/Notifications/Model/Source.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\Timeperiod\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/Timeperiod.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\TimeperiodEntry\\:\\:createBehaviors\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/TimeperiodEntry.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\TimeperiodEntry\\:\\:createRelations\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Model/TimeperiodEntry.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Model\\\\TimeperiodEntry\\:\\:getDefaultSort\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 @@ -935,11 +515,6 @@ parameters: count: 1 path: library/Notifications/Web/Control/SearchBar/ObjectSuggestions.php - - - message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#" - count: 1 - path: library/Notifications/Web/Form/ContactForm.php - - message: "#^Cannot access property \\$address on mixed\\.$#" count: 2 @@ -960,31 +535,11 @@ parameters: count: 1 path: library/Notifications/Web/Form/ContactForm.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Web\\\\Form\\\\ContactForm\\:\\:__construct\\(\\) has parameter \\$contactId with no type specified\\.$#" - count: 1 - path: library/Notifications/Web/Form/ContactForm.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Web\\\\Form\\\\ContactForm\\:\\:assemble\\(\\) has no return type specified\\.$#" count: 1 path: library/Notifications/Web/Form/ContactForm.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Web\\\\Form\\\\ContactForm\\:\\:insertOrUpdateAddress\\(\\) has parameter \\$addressFromDb with no value type specified in iterable type array\\.$#" - count: 1 - path: library/Notifications/Web/Form/ContactForm.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Web\\\\Form\\\\ContactForm\\:\\:insertOrUpdateAddress\\(\\) has parameter \\$addressFromForm with no value type specified in iterable type array\\.$#" - count: 1 - path: library/Notifications/Web/Form/ContactForm.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Web\\\\Form\\\\ContactForm\\:\\:removeContact\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Web/Form/ContactForm.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Web\\\\Form\\\\EventRuleDecorator\\:\\:assemble\\(\\) has no return type specified\\.$#" count: 1 @@ -1000,26 +555,11 @@ parameters: count: 1 path: library/Notifications/Web/Form/EventRuleDecorator.php - - - message: "#^Call to an undefined method DateTimeInterface\\:\\:add\\(\\)\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar.php - - - - message: "#^Cannot call method diff\\(\\) on DateTime\\|null\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\:\\:assemble\\(\\) has no return type specified\\.$#" count: 1 path: library/Notifications/Widget/Calendar.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\:\\:getEntries\\(\\) return type has no value type specified in iterable type Traversable\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\:\\:getModeStart\\(\\) should return DateTime but returns DateTime\\|false\\.$#" count: 2 @@ -1030,26 +570,6 @@ parameters: count: 1 path: library/Notifications/Widget/Calendar.php - - - message: "#^Parameter \\#1 \\$from of static method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\Util\\:\\:diffHours\\(\\) expects DateTime, DateTime\\|null given\\.$#" - count: 2 - path: library/Notifications/Widget/Calendar.php - - - - message: "#^Parameter \\#1 \\$start of method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\Entry\\:\\:setStart\\(\\) expects DateTime, DateTimeInterface given\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar.php - - - - message: "#^Parameter \\#1 \\$start of method ipl\\\\Scheduler\\\\RRule\\:\\:startAt\\(\\) expects DateTimeInterface, DateTime\\|null given\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar.php - - - - message: "#^Parameter \\#2 \\$to of static method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\Util\\:\\:diffHours\\(\\) expects DateTime, DateTime\\|null given\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar.php - - message: "#^Property Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\:\\:\\$addEntryUrl \\(ipl\\\\Web\\\\Url\\) does not accept ipl\\\\Web\\\\Url\\|null\\.$#" count: 1 @@ -1060,31 +580,6 @@ parameters: count: 1 path: library/Notifications/Widget/Calendar/Attendee.php - - - message: "#^Cannot call method format\\(\\) on DateTime\\|null\\.$#" - count: 7 - path: library/Notifications/Widget/Calendar/BaseGrid.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\BaseGrid\\:\\:createGridSteps\\(\\) return type has no value type specified in iterable type Traversable\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/BaseGrid.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\BaseGrid\\:\\:getGridArea\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/BaseGrid.php - - - - message: "#^Parameter \\#1 \\$content of static method ipl\\\\Html\\\\Text\\:\\:create\\(\\) expects string, string\\|null given\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/BaseGrid.php - - - - message: "#^Property Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\BaseGrid\\:\\:\\$extraEntriesCount type has no value type specified in iterable type array\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/BaseGrid.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\Controls\\:\\:assemble\\(\\) has no return type specified\\.$#" count: 1 @@ -1100,96 +595,16 @@ parameters: count: 1 path: library/Notifications/Widget/Calendar/DayGrid.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\DayGrid\\:\\:createGridSteps\\(\\) return type has no value type specified in iterable type Traversable\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/DayGrid.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\DayGrid\\:\\:getGridArea\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/DayGrid.php - - - - message: "#^Property Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\Entry\\:\\:\\$description has no type specified\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/Entry.php - - - - message: "#^Property Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\Entry\\:\\:\\$end has no type specified\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/Entry.php - - - - message: "#^Property Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\Entry\\:\\:\\$id has no type specified\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/Entry.php - - - - message: "#^Property Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\Entry\\:\\:\\$isOccurrence has no type specified\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/Entry.php - - - - message: "#^Property Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\Entry\\:\\:\\$rrule has no type specified\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/Entry.php - - - - message: "#^Property Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\Entry\\:\\:\\$start has no type specified\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/Entry.php - - - - message: "#^Property Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\Entry\\:\\:\\$url \\(ipl\\\\Web\\\\Url\\) does not accept ipl\\\\Web\\\\Url\\|null\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/Entry.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\ExtraEntryCount\\:\\:assemble\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/ExtraEntryCount.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\MonthGrid\\:\\:assemble\\(\\) has no return type specified\\.$#" count: 1 path: library/Notifications/Widget/Calendar/MonthGrid.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\MonthGrid\\:\\:createGridSteps\\(\\) return type has no value type specified in iterable type Traversable\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/MonthGrid.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\MonthGrid\\:\\:getGridArea\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/MonthGrid.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\Util\\:\\:diffHours\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/Util.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\WeekGrid\\:\\:assemble\\(\\) has no return type specified\\.$#" count: 1 path: library/Notifications/Widget/Calendar/WeekGrid.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\WeekGrid\\:\\:createGridSteps\\(\\) return type has no value type specified in iterable type Traversable\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/WeekGrid.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Calendar\\\\WeekGrid\\:\\:getGridArea\\(\\) return type has no value type specified in iterable type array\\.$#" - count: 1 - path: library/Notifications/Widget/Calendar/WeekGrid.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\CheckboxIcon\\:\\:assemble\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Widget/CheckboxIcon.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Detail\\\\EventDetail\\:\\:assemble\\(\\) has no return type specified\\.$#" count: 1 @@ -1210,11 +625,6 @@ parameters: count: 1 path: library/Notifications/Widget/Detail/IncidentDetail.php - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Detail\\\\IncidentDetail\\:\\:createObjectTag\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Widget/Detail/IncidentDetail.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Detail\\\\IncidentDetail\\:\\:createRelatedObject\\(\\) has no return type specified\\.$#" count: 1 @@ -1345,46 +755,6 @@ parameters: count: 1 path: library/Notifications/Widget/FlowLine.php - - - message: "#^Cannot access offset 0 on mixed\\.$#" - count: 1 - path: library/Notifications/Widget/ItemList/ContactListItem.php - - - - message: "#^Parameter \\#1 \\$content of static method ipl\\\\Html\\\\Text\\:\\:create\\(\\) expects string, mixed given\\.$#" - count: 1 - path: library/Notifications/Widget/ItemList/ContactListItem.php - - - - message: "#^Parameter \\#2 \\$value of method Icinga\\\\Web\\\\Url\\:\\:setParam\\(\\) expects array\\|bool\\|string, int given\\.$#" - count: 1 - path: library/Notifications/Widget/ItemList/EventList.php - - - - message: "#^Property Icinga\\\\Module\\\\Notifications\\\\Widget\\\\ItemList\\\\EventList\\:\\:\\$data \\(ipl\\\\Orm\\\\ResultSet\\) does not accept Generator\\.$#" - count: 1 - path: library/Notifications/Widget/ItemList/EventList.php - - - - message: "#^Parameter \\#2 \\$value of method Icinga\\\\Web\\\\Url\\:\\:setParam\\(\\) expects array\\|bool\\|string, int given\\.$#" - count: 1 - path: library/Notifications/Widget/ItemList/EventRuleList.php - - - - message: "#^Cannot call method count\\(\\) on mixed\\.$#" - count: 1 - path: library/Notifications/Widget/ItemList/EventRuleListItem.php - - - - message: "#^Cannot call method first\\(\\) on mixed\\.$#" - count: 1 - path: library/Notifications/Widget/ItemList/EventRuleListItem.php - - - - message: "#^Parameter \\#1 \\$id of static method Icinga\\\\Module\\\\Notifications\\\\Common\\\\Links\\:\\:eventRule\\(\\) expects int, mixed given\\.$#" - count: 1 - path: library/Notifications/Widget/ItemList/EventRuleListItem.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\ItemList\\\\PageSeparatorItem\\:\\:assemble\\(\\) has no return type specified\\.$#" count: 1 @@ -1410,11 +780,6 @@ parameters: count: 2 path: library/Notifications/Widget/RecipientSuggestions.php - - - message: "#^Cannot access property \\$color on mixed\\.$#" - count: 2 - path: library/Notifications/Widget/RecipientSuggestions.php - - message: "#^Cannot access property \\$full_name on mixed\\.$#" count: 2 @@ -1455,16 +820,6 @@ parameters: count: 1 path: library/Notifications/Widget/RecipientSuggestions.php - - - message: "#^Cannot access property \\$full_name on ipl\\\\Orm\\\\Model\\|null\\.$#" - count: 1 - path: library/Notifications/Widget/RuleEscalationRecipientBadge.php - - - - message: "#^Cannot access property \\$name on ipl\\\\Orm\\\\Model\\|null\\.$#" - count: 1 - path: library/Notifications/Widget/RuleEscalationRecipientBadge.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\RuleEscalationRecipientBadge\\:\\:assemble\\(\\) has no return type specified\\.$#" count: 1 @@ -1480,66 +835,6 @@ parameters: count: 1 path: library/Notifications/Widget/RuleEscalationRecipientBadge.php - - - message: "#^Cannot access property \\$contact on mixed\\.$#" - count: 2 - path: library/Notifications/Widget/Schedule.php - - - - message: "#^Cannot access property \\$contact_id on mixed\\.$#" - count: 1 - path: library/Notifications/Widget/Schedule.php - - - - message: "#^Cannot access property \\$contactgroup on mixed\\.$#" - count: 2 - path: library/Notifications/Widget/Schedule.php - - - - message: "#^Cannot access property \\$description on mixed\\.$#" - count: 1 - path: library/Notifications/Widget/Schedule.php - - - - message: "#^Cannot access property \\$end_time on mixed\\.$#" - count: 1 - path: library/Notifications/Widget/Schedule.php - - - - message: "#^Cannot access property \\$id on mixed\\.$#" - count: 2 - path: library/Notifications/Widget/Schedule.php - - - - message: "#^Cannot access property \\$rrule on mixed\\.$#" - count: 1 - path: library/Notifications/Widget/Schedule.php - - - - message: "#^Cannot access property \\$start_time on mixed\\.$#" - count: 1 - path: library/Notifications/Widget/Schedule.php - - - - message: "#^Cannot access property \\$timeperiod_id on mixed\\.$#" - count: 1 - path: library/Notifications/Widget/Schedule.php - - - - message: "#^Cannot access property \\$timezone on mixed\\.$#" - count: 2 - path: library/Notifications/Widget/Schedule.php - - - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Schedule\\:\\:assemble\\(\\) has no return type specified\\.$#" - count: 1 - path: library/Notifications/Widget/Schedule.php - - - - message: "#^Property Icinga\\\\Module\\\\Notifications\\\\Widget\\\\Schedule\\:\\:\\$schedule \\(Icinga\\\\Module\\\\Notifications\\\\Model\\\\Schedule\\) does not accept Icinga\\\\Module\\\\Notifications\\\\Model\\\\Schedule\\|null\\.$#" - count: 1 - path: library/Notifications/Widget/Schedule.php - - message: "#^Method Icinga\\\\Module\\\\Notifications\\\\Widget\\\\ShowMore\\:\\:assemble\\(\\) has no return type specified\\.$#" count: 1 From b5cd11ed9cf70b188909d7ea3323ff76945067a1 Mon Sep 17 00:00:00 2001 From: Sukhwinder Dhillon Date: Fri, 25 Apr 2025 15:35:45 +0200 Subject: [PATCH 22/22] Phpstan: Remove now obsolete version based baseline files Phpstan now ignores these error messages, so we can remove these. --- phpstan-baseline-8.2+.neon | 6 ------ phpstan-baseline-by-php-version.php | 6 ------ phpstan-baseline-pre-8.2.neon | 6 ------ 3 files changed, 18 deletions(-) delete mode 100644 phpstan-baseline-8.2+.neon delete mode 100644 phpstan-baseline-pre-8.2.neon diff --git a/phpstan-baseline-8.2+.neon b/phpstan-baseline-8.2+.neon deleted file mode 100644 index 083123c02..000000000 --- a/phpstan-baseline-8.2+.neon +++ /dev/null @@ -1,6 +0,0 @@ -parameters: - ignoreErrors: - - - message: "#^Parameter \\#1 \\$iterator of function iterator_to_array expects iterable, ipl\\\\Orm\\\\Model\\|null given\\.$#" - count: 1 - path: application/controllers/EventRuleController.php diff --git a/phpstan-baseline-by-php-version.php b/phpstan-baseline-by-php-version.php index 053894311..4bd791e86 100644 --- a/phpstan-baseline-by-php-version.php +++ b/phpstan-baseline-by-php-version.php @@ -7,12 +7,6 @@ $includes[] = __DIR__ . '/phpstan-baseline-7x.neon'; } -if (PHP_VERSION_ID >= 80200) { - $includes[] = __DIR__ . '/phpstan-baseline-8.2+.neon'; -} else { - $includes[] = __DIR__ . '/phpstan-baseline-pre-8.2.neon'; -} - return [ 'includes' => $includes ]; diff --git a/phpstan-baseline-pre-8.2.neon b/phpstan-baseline-pre-8.2.neon deleted file mode 100644 index ed41ceec4..000000000 --- a/phpstan-baseline-pre-8.2.neon +++ /dev/null @@ -1,6 +0,0 @@ -parameters: - ignoreErrors: - - - message: "#^Parameter \\#1 \\$iterator of function iterator_to_array expects Traversable, ipl\\\\Orm\\\\Model\\|null given\\.$#" - count: 1 - path: application/controllers/EventRuleController.php