diff --git a/application/controllers/CommentController.php b/application/controllers/CommentController.php index b184d6b9d..1354dbdc3 100644 --- a/application/controllers/CommentController.php +++ b/application/controllers/CommentController.php @@ -10,7 +10,7 @@ use Icinga\Module\Icingadb\Model\Comment; use Icinga\Module\Icingadb\Web\Controller; use Icinga\Module\Icingadb\Widget\Detail\CommentDetail; -use Icinga\Module\Icingadb\Widget\ItemList\CommentList; +use Icinga\Module\Icingadb\Widget\Detail\CommentHeader; use ipl\Stdlib\Filter; use ipl\Web\Url; @@ -49,11 +49,7 @@ public function init() public function indexAction() { - $this->addControl((new CommentList([$this->comment])) - ->setViewMode('minimal') - ->setDetailActionsDisabled() - ->setCaptionDisabled() - ->setNoSubjectLink()); + $this->addControl(new CommentHeader($this->comment)); $this->addContent((new CommentDetail($this->comment))->setTicketLinkEnabled()); diff --git a/application/controllers/DowntimeController.php b/application/controllers/DowntimeController.php index a0a7fa070..1fc0f1edf 100644 --- a/application/controllers/DowntimeController.php +++ b/application/controllers/DowntimeController.php @@ -10,7 +10,7 @@ use Icinga\Module\Icingadb\Model\Downtime; use Icinga\Module\Icingadb\Web\Controller; use Icinga\Module\Icingadb\Widget\Detail\DowntimeDetail; -use Icinga\Module\Icingadb\Widget\ItemList\DowntimeList; +use Icinga\Module\Icingadb\Widget\Detail\DowntimeHeader; use ipl\Stdlib\Filter; use ipl\Web\Url; @@ -57,17 +57,11 @@ public function init() $this->downtime = $downtime; } - public function indexAction() + public function indexAction(): void { - $detail = new DowntimeDetail($this->downtime); + $this->addControl(new DowntimeHeader($this->downtime)); - $this->addControl((new DowntimeList([$this->downtime])) - ->setViewMode('minimal') - ->setDetailActionsDisabled() - ->setCaptionDisabled() - ->setNoSubjectLink()); - - $this->addContent($detail); + $this->addContent(new DowntimeDetail($this->downtime)); $this->setAutorefreshInterval(10); } diff --git a/application/controllers/EventController.php b/application/controllers/EventController.php index 7108606d4..95c89e8ef 100644 --- a/application/controllers/EventController.php +++ b/application/controllers/EventController.php @@ -4,12 +4,10 @@ namespace Icinga\Module\Icingadb\Controllers; -use ArrayObject; use Icinga\Module\Icingadb\Model\History; use Icinga\Module\Icingadb\Web\Controller; use Icinga\Module\Icingadb\Widget\Detail\EventDetail; -use Icinga\Module\Icingadb\Widget\ItemList\HistoryList; -use ipl\Orm\ResultSet; +use Icinga\Module\Icingadb\Widget\Detail\EventHeader; use ipl\Stdlib\Filter; class EventController extends Controller @@ -60,12 +58,7 @@ public function init() public function indexAction() { - $this->addControl((new HistoryList(new ResultSet(new ArrayObject([$this->event])))) - ->setViewMode('minimal') - ->setPageSize(1) - ->setCaptionDisabled() - ->setNoSubjectLink() - ->setDetailActionsDisabled()); + $this->addControl(new EventHeader($this->event)); $this->addContent((new EventDetail($this->event))->setTicketLinkEnabled()); } } diff --git a/application/controllers/HostController.php b/application/controllers/HostController.php index 259dd33f0..d5dfbb608 100644 --- a/application/controllers/HostController.php +++ b/application/controllers/HostController.php @@ -19,10 +19,10 @@ use Icinga\Module\Icingadb\Redis\VolatileStateResults; use Icinga\Module\Icingadb\Web\Controller; use Icinga\Module\Icingadb\Widget\Detail\HostDetail; +use Icinga\Module\Icingadb\Widget\Detail\HostHeader; use Icinga\Module\Icingadb\Widget\Detail\HostInspectionDetail; use Icinga\Module\Icingadb\Widget\Detail\HostMetaInfo; use Icinga\Module\Icingadb\Widget\Detail\QuickActions; -use Icinga\Module\Icingadb\Widget\ItemList\HostList; use Icinga\Module\Icingadb\Widget\ItemList\HistoryList; use Icinga\Module\Icingadb\Widget\ItemList\ServiceList; use ipl\Stdlib\Filter; @@ -57,6 +57,8 @@ public function init() $this->host = $host; $this->loadTabsForObject($host); + $this->addControl(new HostHeader($this->host)); + $this->setTitleTab($this->getRequest()->getActionName()); $this->setTitle($host->display_name); } @@ -72,10 +74,6 @@ public function indexAction() $this->controls->addAttributes(['class' => 'overdue']); } - $this->addControl((new HostList([$this->host])) - ->setViewMode('objectHeader') - ->setDetailActionsDisabled() - ->setNoSubjectLink()); $this->addControl(new HostMetaInfo($this->host)); $this->addControl(new QuickActions($this->host)); @@ -97,10 +95,6 @@ public function sourceAction() $this->controls->addAttributes(['class' => 'overdue']); } - $this->addControl((new HostList([$this->host])) - ->setViewMode('objectHeader') - ->setDetailActionsDisabled() - ->setNoSubjectLink()); $this->addContent(new HostInspectionDetail( $this->host, reset($apiResult) @@ -158,10 +152,6 @@ public function historyAction() yield $this->export($history); - $this->addControl((new HostList([$this->host])) - ->setViewMode('objectHeader') - ->setDetailActionsDisabled() - ->setNoSubjectLink()); $this->addControl($sortControl); $this->addControl($limitControl); $this->addControl($viewModeSwitcher); @@ -221,10 +211,6 @@ public function servicesAction() $serviceList = (new ServiceList($services)) ->setViewMode($viewModeSwitcher->getViewMode()); - $this->addControl((new HostList([$this->host])) - ->setViewMode('objectHeader') - ->setDetailActionsDisabled() - ->setNoSubjectLink()); $this->addControl($paginationControl); $this->addControl($sortControl); $this->addControl($limitControl); @@ -288,6 +274,6 @@ protected function getCommandTargetsUrl(): Url protected function getDefaultTabControls(): array { - return [(new HostList([$this->host]))->setDetailActionsDisabled()->setNoSubjectLink()]; + return [(new HostHeader($this->host))]; } } diff --git a/application/controllers/HostgroupController.php b/application/controllers/HostgroupController.php index e48adca20..409278877 100644 --- a/application/controllers/HostgroupController.php +++ b/application/controllers/HostgroupController.php @@ -12,8 +12,8 @@ use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions; use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher; use Icinga\Module\Icingadb\Web\Controller; +use Icinga\Module\Icingadb\Widget\Detail\HostgroupHeader; use Icinga\Module\Icingadb\Widget\ItemList\HostList; -use Icinga\Module\Icingadb\Widget\ItemTable\HostgroupTableRow; use ipl\Html\Html; use ipl\Stdlib\Filter; use ipl\Web\Control\LimitControl; @@ -114,10 +114,10 @@ public function indexAction(): Generator // ICINGAWEB_EXPORT_FORMAT is not set yet and $this->format is inaccessible, yeah... if ($this->getRequest()->getParam('format') === 'pdf') { - $this->addContent(new HostgroupTableRow($hostgroup)); + $this->addContent(new HostgroupHeader($hostgroup)); $this->addContent(Html::tag('h2', null, t('Hosts'))); } else { - $this->addControl(new HostgroupTableRow($hostgroup)); + $this->addControl(new HostgroupHeader($hostgroup)); } $this->addControl($paginationControl); diff --git a/application/controllers/ServiceController.php b/application/controllers/ServiceController.php index 2857718ae..c88430aa6 100644 --- a/application/controllers/ServiceController.php +++ b/application/controllers/ServiceController.php @@ -18,10 +18,10 @@ use Icinga\Module\Icingadb\Web\Controller; use Icinga\Module\Icingadb\Widget\Detail\QuickActions; use Icinga\Module\Icingadb\Widget\Detail\ServiceDetail; +use Icinga\Module\Icingadb\Widget\Detail\ServiceHeader; use Icinga\Module\Icingadb\Widget\Detail\ServiceInspectionDetail; use Icinga\Module\Icingadb\Widget\Detail\ServiceMetaInfo; use Icinga\Module\Icingadb\Widget\ItemList\HistoryList; -use Icinga\Module\Icingadb\Widget\ItemList\ServiceList; use ipl\Stdlib\Filter; use ipl\Web\Url; @@ -65,6 +65,8 @@ public function init() $this->service = $service; $this->loadTabsForObject($service); + $this->addControl(new ServiceHeader($this->service)); + $this->setTitleTab($this->getRequest()->getActionName()); $this->setTitle( t('%s on %s', ' on '), @@ -79,10 +81,6 @@ public function indexAction() $this->controls->addAttributes(['class' => 'overdue']); } - $this->addControl((new ServiceList([$this->service])) - ->setViewMode('objectHeader') - ->setDetailActionsDisabled() - ->setNoSubjectLink()); $this->addControl(new ServiceMetaInfo($this->service)); $this->addControl(new QuickActions($this->service)); @@ -104,10 +102,6 @@ public function sourceAction() $this->controls->addAttributes(['class' => 'overdue']); } - $this->addControl((new ServiceList([$this->service])) - ->setViewMode('objectHeader') - ->setDetailActionsDisabled() - ->setNoSubjectLink()); $this->addContent(new ServiceInspectionDetail( $this->service, reset($apiResult) @@ -166,10 +160,6 @@ public function historyAction() yield $this->export($history); - $this->addControl((new ServiceList([$this->service])) - ->setViewMode('objectHeader') - ->setDetailActionsDisabled() - ->setNoSubjectLink()); $this->addControl($sortControl); $this->addControl($limitControl); $this->addControl($viewModeSwitcher); @@ -242,6 +232,6 @@ protected function getCommandTargetsUrl(): Url protected function getDefaultTabControls(): array { - return [(new ServiceList([$this->service]))->setDetailActionsDisabled()->setNoSubjectLink()]; + return [new ServiceHeader($this->service)]; } } diff --git a/application/controllers/ServicegroupController.php b/application/controllers/ServicegroupController.php index c3dea15bc..cf5e57e23 100644 --- a/application/controllers/ServicegroupController.php +++ b/application/controllers/ServicegroupController.php @@ -12,8 +12,8 @@ use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions; use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher; use Icinga\Module\Icingadb\Web\Controller; +use Icinga\Module\Icingadb\Widget\Detail\ServicegroupHeader; use Icinga\Module\Icingadb\Widget\ItemList\ServiceList; -use Icinga\Module\Icingadb\Widget\ItemTable\ServicegroupTableRow; use ipl\Html\Html; use ipl\Stdlib\Filter; use ipl\Web\Control\LimitControl; @@ -122,10 +122,10 @@ public function indexAction(): Generator // ICINGAWEB_EXPORT_FORMAT is not set yet and $this->format is inaccessible, yeah... if ($this->getRequest()->getParam('format') === 'pdf') { - $this->addContent(new ServicegroupTableRow($servicegroup)); + $this->addContent(new ServicegroupHeader($servicegroup)); $this->addContent(Html::tag('h2', null, t('Services'))); } else { - $this->addControl(new ServicegroupTableRow($servicegroup)); + $this->addControl(new ServicegroupHeader($servicegroup)); } $this->addControl($paginationControl); diff --git a/application/controllers/UserController.php b/application/controllers/UserController.php index a80f2b450..924d97d8f 100644 --- a/application/controllers/UserController.php +++ b/application/controllers/UserController.php @@ -8,7 +8,7 @@ use Icinga\Module\Icingadb\Model\User; use Icinga\Module\Icingadb\Web\Controller; use Icinga\Module\Icingadb\Widget\Detail\UserDetail; -use Icinga\Module\Icingadb\Widget\ItemTable\UserTableRow; +use Icinga\Module\Icingadb\Widget\Detail\UserHeader; use ipl\Stdlib\Filter; class UserController extends Controller @@ -40,7 +40,7 @@ public function init() public function indexAction() { - $this->addControl(new UserTableRow($this->user)); + $this->addControl(new UserHeader($this->user)); $this->addContent(new UserDetail($this->user)); $this->setAutorefreshInterval(10); diff --git a/application/controllers/UsergroupController.php b/application/controllers/UsergroupController.php index 8c3fed8b6..1eafdd398 100644 --- a/application/controllers/UsergroupController.php +++ b/application/controllers/UsergroupController.php @@ -8,7 +8,7 @@ use Icinga\Module\Icingadb\Model\Usergroup; use Icinga\Module\Icingadb\Web\Controller; use Icinga\Module\Icingadb\Widget\Detail\UsergroupDetail; -use Icinga\Module\Icingadb\Widget\ItemTable\UsergroupTableRow; +use Icinga\Module\Icingadb\Widget\Detail\UsergroupHeader; use ipl\Stdlib\Filter; class UsergroupController extends Controller @@ -40,7 +40,7 @@ public function init() public function indexAction() { - $this->addControl(new UsergroupTableRow($this->usergroup)); + $this->addControl(new UsergroupHeader($this->usergroup)); $this->addContent(new UsergroupDetail($this->usergroup)); $this->setAutorefreshInterval(10); diff --git a/library/Icingadb/Common/CaptionDisabled.php b/library/Icingadb/Common/CaptionDisabled.php deleted file mode 100644 index 2cee178b8..000000000 --- a/library/Icingadb/Common/CaptionDisabled.php +++ /dev/null @@ -1,31 +0,0 @@ -captionDisabled; - } - - /** - * @param bool $captionDisabled - * - * @return $this - */ - public function setCaptionDisabled(bool $captionDisabled = true): self - { - $this->captionDisabled = $captionDisabled; - - return $this; - } -} diff --git a/library/Icingadb/Common/ListItemCommonLayout.php b/library/Icingadb/Common/ListItemCommonLayout.php index 5a11be30b..5db304ce2 100644 --- a/library/Icingadb/Common/ListItemCommonLayout.php +++ b/library/Icingadb/Common/ListItemCommonLayout.php @@ -8,8 +8,6 @@ trait ListItemCommonLayout { - use CaptionDisabled; - protected function assembleHeader(BaseHtmlElement $header): void { $header->addHtml($this->createTitle()); @@ -19,8 +17,6 @@ protected function assembleHeader(BaseHtmlElement $header): void protected function assembleMain(BaseHtmlElement $main): void { $main->addHtml($this->createHeader()); - if (!$this->isCaptionDisabled()) { - $main->addHtml($this->createCaption()); - } + $main->addHtml($this->createCaption()); } } diff --git a/library/Icingadb/Common/ListItemMinimalLayout.php b/library/Icingadb/Common/ListItemMinimalLayout.php index 3cdf3a9ec..f146e79b0 100644 --- a/library/Icingadb/Common/ListItemMinimalLayout.php +++ b/library/Icingadb/Common/ListItemMinimalLayout.php @@ -8,14 +8,10 @@ trait ListItemMinimalLayout { - use CaptionDisabled; - protected function assembleHeader(BaseHtmlElement $header): void { $header->add($this->createTitle()); - if (! $this->isCaptionDisabled()) { - $header->add($this->createCaption()); - } + $header->add($this->createCaption()); $header->add($this->createTimestamp()); } diff --git a/library/Icingadb/Common/NoSubjectLink.php b/library/Icingadb/Common/NoSubjectLink.php deleted file mode 100644 index 76c9a84dc..000000000 --- a/library/Icingadb/Common/NoSubjectLink.php +++ /dev/null @@ -1,35 +0,0 @@ -noSubjectLink = $state; - - return $this; - } - - /** - * Get whether a list item's subject should be a link - * - * @return bool - */ - public function getNoSubjectLink(): bool - { - return $this->noSubjectLink; - } -} diff --git a/library/Icingadb/Widget/Detail/BaseHostAndServiceHeader.php b/library/Icingadb/Widget/Detail/BaseHostAndServiceHeader.php new file mode 100644 index 000000000..366d187bb --- /dev/null +++ b/library/Icingadb/Widget/Detail/BaseHostAndServiceHeader.php @@ -0,0 +1,75 @@ +state = $this->object->state; + } + + protected function getObject(): Model + { + return $this->object; + } + + protected function createSubject(): ValidHtml + { + return new HtmlElement( + 'span', + Attributes::create(['class' => 'subject']), + Text::create($this->object->display_name) + ); + } + + protected function getStateBallSize(): string + { + return ''; // not required because the parent class overrides the assembleVisual method + } + + protected function wantIconImage(): bool + { + return isset($this->object->icon_image->icon_image); + } + + protected function assembleHeader(BaseHtmlElement $header): void + { + $header->addHtml($this->createTitle()); + $header->addHtml($this->createCaption()); + $header->add($this->createTimestamp()); + } + + protected function assembleMain(BaseHtmlElement $main): void + { + $main->addHtml($this->createHeader()); + } + + protected function assemble(): void + { + if ($this->state->is_overdue) { + $this->addAttributes(['class' => 'overdue']); + } + + $this->add([ + $this->createVisual(), + $this->createIconImage(), + $this->createMain() + ]); + } +} diff --git a/library/Icingadb/Widget/Detail/ObjectHeader.php b/library/Icingadb/Widget/Detail/BaseObjectHeader.php similarity index 80% rename from library/Icingadb/Widget/Detail/ObjectHeader.php rename to library/Icingadb/Widget/Detail/BaseObjectHeader.php index 158622021..064b71f11 100644 --- a/library/Icingadb/Widget/Detail/ObjectHeader.php +++ b/library/Icingadb/Widget/Detail/BaseObjectHeader.php @@ -7,16 +7,10 @@ use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\HtmlElement; -use ipl\Html\Text; -use ipl\I18n\Translation; use ipl\Orm\Model; -use ipl\Web\Widget\StateBall; -use ipl\Web\Widget\TimeSince; -abstract class ObjectHeader extends BaseHtmlElement +abstract class BaseObjectHeader extends BaseHtmlElement { - use Translation; - /** @var array */ protected $baseAttributes = ['class' => 'object-header']; @@ -55,11 +49,6 @@ protected function assembleVisual(BaseHtmlElement $visual): void { } - protected function getStateBallSize(): string - { - return StateBall::SIZE_BIG; - } - protected function createCaption(): BaseHtmlElement { $caption = new HtmlElement('section', Attributes::create(['class' => 'caption'])); @@ -89,17 +78,7 @@ protected function createMain(): BaseHtmlElement protected function createTimestamp(): ?BaseHtmlElement { - //TODO: add support for host/service - return new TimeSince($this->object->state->last_state_change->getTimestamp()); - } - - protected function createSubject(): BaseHtmlElement - { - return new HtmlElement( - 'span', - Attributes::create(['class' => 'subject']), - Text::create($this->object->display_name) - ); + return null; } protected function createTitle(): BaseHtmlElement diff --git a/library/Icingadb/Widget/Detail/CommentHeader.php b/library/Icingadb/Widget/Detail/CommentHeader.php new file mode 100644 index 000000000..e820c4639 --- /dev/null +++ b/library/Icingadb/Widget/Detail/CommentHeader.php @@ -0,0 +1,68 @@ + 'comment-header']; + + /** @var bool Whether to create an object link */ + protected $wantObjectLink; + + /** @var bool Whether to create caption */ + protected $wantCaption; + + public function __construct(Comment $object, bool $wantObjectLink = true, $wantCaption = false) + { + $this->wantObjectLink = $wantObjectLink; + $this->wantCaption = $wantCaption; + + parent::__construct($object); + } + + protected function getObject(): Comment + { + return $this->object; + } + + protected function wantSubjectLink(): bool + { + return false; + } + + protected function wantObjectLink(): bool + { + return $this->wantObjectLink; + } + + protected function assembleHeader(BaseHtmlElement $header): void + { + $header->addHtml($this->createTitle()); + $header->addHtml($this->createTimestamp()); + } + + protected function assembleMain(BaseHtmlElement $main): void + { + $main->addHtml($this->createHeader()); + + if ($this->wantCaption) { + $main->addHtml($this->createCaption()); + } + } + + protected function assemble(): void + { + $this->addHtml($this->createVisual()); + $this->addHtml($this->createMain()); + } +} diff --git a/library/Icingadb/Widget/Detail/CommentHeaderUtils.php b/library/Icingadb/Widget/Detail/CommentHeaderUtils.php new file mode 100644 index 000000000..e549eca29 --- /dev/null +++ b/library/Icingadb/Widget/Detail/CommentHeaderUtils.php @@ -0,0 +1,141 @@ +addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => 'user-ball']), + Text::create($this->getObject()->author[0]) + )); + } + + protected function assembleTitle(BaseHtmlElement $title): void + { + $comment = $this->getObject(); + $isAck = $comment->entry_type === 'ack'; + $expires = $comment->expire_time; + + $subjectText = sprintf( + $isAck ? t('%s acknowledged', '..') : t('%s commented', '..'), + $comment->author + ); + + $headerParts = [ + new Icon(Icons::USER), + $this->wantSubjectLink() + ? new Link($subjectText, Links::comment($comment), ['class' => 'subject']) + : new HtmlElement('span', Attributes::create(['class' => 'subject']), Text::create($subjectText)) + + ]; + + if ($isAck) { + $label = [Text::create('ack')]; + + if ($comment->is_persistent) { + array_unshift($label, new Icon(Icons::IS_PERSISTENT)); + } + + $headerParts[] = Text::create(' '); + $headerParts[] = new HtmlElement('span', Attributes::create(['class' => 'ack-badge badge']), ...$label); + } + + if ($expires !== null) { + $headerParts[] = Text::create(' '); + $headerParts[] = new HtmlElement( + 'span', + Attributes::create(['class' => 'ack-badge badge']), + Text::create(t('EXPIRES')) + ); + } + + if ($this->wantObjectLink()) { + $headerParts[] = $comment->object_type === 'host' + ? $this->createHostLink($comment->host, true) + : $this->createServiceLink($comment->service, $comment->service->host, true); + } + + $title->addHtml(...$headerParts); + } + + protected function assembleCaption(BaseHtmlElement $caption): void + { + $markdownLine = new MarkdownLine($this->createTicketLinks($this->getObject()->text)); + + $caption->getAttributes()->add($markdownLine->getAttributes()); + $caption->addFrom($markdownLine); + } + + + protected function createTimestamp(): BaseHtmlElement + { + $comment = $this->getObject(); + if ($comment->expire_time) { + return Html::tag( + 'span', + FormattedString::create( + $this->translate("expires %s"), + new TimeUntil($comment->expire_time->getTimestamp()) + ) + ); + } + + return Html::tag( + 'span', + FormattedString::create( + $this->translate("created %s"), + new TimeAgo($comment->entry_time->getTimestamp()) + ) + ); + } +} diff --git a/library/Icingadb/Widget/Detail/DowntimeHeader.php b/library/Icingadb/Widget/Detail/DowntimeHeader.php new file mode 100644 index 000000000..50c5bdf9c --- /dev/null +++ b/library/Icingadb/Widget/Detail/DowntimeHeader.php @@ -0,0 +1,50 @@ + 'downtime-header']; + + protected function getObject(): Downtime + { + return $this->object; + } + + protected function wantSubjectLink(): bool + { + return false; + } + + protected function wantObjectLink(): bool + { + return true; + } + + protected function assembleHeader(BaseHtmlElement $header): void + { + $header->addHtml($this->createTitle()); + $header->addHtml($this->createTimestamp()); + } + + protected function assembleMain(BaseHtmlElement $main): void + { + $main->addHtml($this->createHeader()); + } + + protected function assemble(): void + { + $this->addHtml($this->createVisual()); + $this->addHtml($this->createMain()); + } +} diff --git a/library/Icingadb/Widget/Detail/DowntimeHeaderUtils.php b/library/Icingadb/Widget/Detail/DowntimeHeaderUtils.php new file mode 100644 index 000000000..e129740ca --- /dev/null +++ b/library/Icingadb/Widget/Detail/DowntimeHeaderUtils.php @@ -0,0 +1,214 @@ +isPrepared) { + return; + } + + $downtime = $this->getObject(); + if (isset($downtime->start_time, $downtime->end_time) && $downtime->is_flexible && $downtime->is_in_effect) { + $this->startTime = $downtime->start_time->getTimestamp(); + $this->endTime = $downtime->end_time->getTimestamp(); + } else { + $this->startTime = $downtime->scheduled_start_time->getTimestamp(); + $this->endTime = $downtime->scheduled_end_time->getTimestamp(); + } + + $currentTime = time(); + + $this->isActive = ($downtime->is_in_effect //todo: fixed false positive by wrapping + || $downtime->is_flexible) && $downtime->scheduled_start_time->getTimestamp() <= $currentTime; + + $until = ($this->isActive ? $this->endTime : $this->startTime) - $currentTime; + $this->duration = explode(' ', DateFormatter::formatDuration( + $until <= 3600 ? $until : $until + (3600 - ((int) $until % 3600)) + ), 2)[0]; + + if ($downtime->is_in_effect) { + $this->getAttributes()->add('class', 'in-effect'); + } + + $this->isPrepared = true; + } + + protected function assembleVisual(BaseHtmlElement $visual): void + { + $this->prepare(); + $dateTime = DateFormatter::formatDateTime($this->endTime); + + if ($this->isActive) { + $visual->addHtml(Html::sprintf( + t('%s left', '..'), + Html::tag( + 'strong', + Html::tag( + 'time', + [ + 'datetime' => $dateTime, + 'title' => $dateTime + ], + $this->duration + ) + ) + )); + } else { + $visual->addHtml(Html::sprintf( + t('in %s', '..'), + Html::tag('strong', $this->duration) + )); + } + } + + protected function assembleTitle(BaseHtmlElement $title): void + { + $downtime = $this->getObject(); + $link = null; + if ($this->wantObjectLink()) { + $link = $downtime->object_type === 'host' + ? $this->createHostLink($downtime->host, true) + : $this->createServiceLink($downtime->service, $downtime->service->host, true); + } + + if ($downtime->is_flexible) { + if ($link !== null) { + $template = t('{{#link}}Flexible Downtime{{/link}} for %s'); + } else { + $template = t('Flexible Downtime'); + } + } else { + if ($link !== null) { + $template = t('{{#link}}Fixed Downtime{{/link}} for %s'); + } else { + $template = t('Fixed Downtime'); + } + } + + if (! $this->wantSubjectLink()) { + if ($link === null) { + $title->addHtml(HtmlElement::create('span', [ 'class' => 'subject'], $template)); + } else { + $title->addHtml(TemplateString::create( + $template, + ['link' => HtmlElement::create('span', [ 'class' => 'subject'])], + $link + )); + } + } else { + if ($link === null) { + $title->addHtml(new Link($template, Links::downtime($downtime))); + } else { + $title->addHtml(TemplateString::create( + $template, + ['link' => new Link('', Links::downtime($downtime))], + $link + )); + } + } + } + + protected function assembleCaption(BaseHtmlElement $caption): void + { + $downtime = $this->getObject(); + $markdownLine = new MarkdownLine($this->createTicketLinks($downtime->comment)); + $caption->getAttributes()->add($markdownLine->getAttributes()); + $caption->addHtml( + new HtmlElement( + 'span', + null, + new Icon(Icons::USER), + Text::create($downtime->author) + ), + Text::create(': ') + )->addFrom($markdownLine); + } + + protected function createTimestamp(): ?BaseHtmlElement + { + $this->prepare(); + $dateTime = DateFormatter::formatDateTime($this->isActive ? $this->endTime : $this->startTime); + + return Html::tag( + 'time', + [ + 'datetime' => $dateTime, + 'title' => $dateTime + ], + sprintf( + $this->isActive + ? t('expires in %s', '..') + : t('starts in %s', '..'), + $this->duration + ) + ); + } +} diff --git a/library/Icingadb/Widget/Detail/EventHeader.php b/library/Icingadb/Widget/Detail/EventHeader.php new file mode 100644 index 000000000..749db984a --- /dev/null +++ b/library/Icingadb/Widget/Detail/EventHeader.php @@ -0,0 +1,52 @@ + 'event-header']; + + protected function getObject(): History + { + return $this->object; + } + + protected function getStateBallSize(): string + { + return StateBall::SIZE_BIG; + } + + protected function wantSubjectLink(): bool + { + return false; + } + + protected function assembleHeader(BaseHtmlElement $header): void + { + $header->addHtml($this->createTitle()); + $header->addHtml($this->createTimestamp()); + } + + protected function assembleMain(BaseHtmlElement $main): void + { + $main->addHtml($this->createHeader()); + } + + protected function assemble(): void + { + $this->addHtml($this->createVisual()); + $this->addHtml($this->createMain()); + } +} diff --git a/library/Icingadb/Widget/Detail/EventHeaderUtils.php b/library/Icingadb/Widget/Detail/EventHeaderUtils.php new file mode 100644 index 000000000..d7b31de5c --- /dev/null +++ b/library/Icingadb/Widget/Detail/EventHeaderUtils.php @@ -0,0 +1,443 @@ +getObject(); + switch ($event->event_type) { + case 'comment_add': + $visual->addHtml( + HtmlElement::create( + 'div', + ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], + new Icon(Icons::COMMENT) + ) + ); + + break; + case 'comment_remove': + case 'downtime_end': + case 'ack_clear': + $visual->addHtml( + HtmlElement::create( + 'div', + ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], + new Icon(Icons::REMOVE) + ) + ); + + break; + case 'downtime_start': + $visual->addHtml( + HtmlElement::create( + 'div', + ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], + new Icon(Icons::IN_DOWNTIME) + ) + ); + + break; + case 'ack_set': + $visual->addHtml( + HtmlElement::create( + 'div', + ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], + new Icon(Icons::IS_ACKNOWLEDGED) + ) + ); + + break; + case 'flapping_end': + case 'flapping_start': + $visual->addHtml( + HtmlElement::create( + 'div', + ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], + new Icon(Icons::IS_FLAPPING) + ) + ); + + break; + case 'notification': + $visual->addHtml( + HtmlElement::create( + 'div', + ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], + new Icon(Icons::NOTIFICATION) + ) + ); + + break; + case 'state_change': + if ($event->state->state_type === 'soft') { + $stateType = 'soft_state'; + $previousStateType = 'previous_soft_state'; + + if ($event->state->previous_soft_state === 0) { + $previousStateType = 'hard_state'; + } + + $visual->addHtml( + new CheckAttempt( + (int)$event->state->check_attempt, + (int)$event->state->max_check_attempts + ) + ); + } else { + $stateType = 'hard_state'; + $previousStateType = 'previous_hard_state'; + + if ($event->state->hard_state === $event->state->previous_hard_state) { + $previousStateType = 'previous_soft_state'; + } + } + + if ($event->object_type === 'host') { + $state = HostStates::text($event->state->$stateType); + $previousState = HostStates::text($event->state->$previousStateType); + } else { + $state = ServiceStates::text($event->state->$stateType); + $previousState = ServiceStates::text($event->state->$previousStateType); + } + + $stateChange = new StateChange($state, $previousState); + if ($stateType === 'soft_state') { + $stateChange->setCurrentStateBallSize(StateBall::SIZE_MEDIUM_LARGE); + } + + if ($previousStateType === 'previous_soft_state') { + $stateChange->setPreviousStateBallSize(StateBall::SIZE_MEDIUM_LARGE); + if ($stateType === 'soft_state') { + $visual->getAttributes()->add('class', 'small-state-change'); + } + } + + $visual->prependHtml($stateChange); + + break; + } + } + + protected function assembleTitle(BaseHtmlElement $title): void + { + $event = $this->getObject(); + switch ($event->event_type) { + case 'comment_add': + $subjectLabel = $this->translate('Comment added'); + + break; + case 'comment_remove': + if (! empty($event->comment->removed_by)) { + if ($event->comment->removed_by !== $event->comment->author) { + $subjectLabel = sprintf( + $this->translate('Comment removed by %s', '..'), + $event->comment->removed_by + ); + } else { + $subjectLabel = $this->translate('Comment removed by author'); + } + } elseif (isset($event->comment->expire_time)) { + $subjectLabel = $this->translate('Comment expired'); + } else { + $subjectLabel = $this->translate('Comment removed'); + } + + break; + case 'downtime_end': + if (! empty($event->downtime->cancelled_by)) { + if ($event->downtime->cancelled_by !== $event->downtime->author) { + $subjectLabel = sprintf( + $this->translate('Downtime cancelled by %s', '..'), + $event->downtime->cancelled_by + ); + } else { + $subjectLabel = $this->translate('Downtime cancelled by author'); + } + } elseif ($event->downtime->has_been_cancelled === 'y') { + $subjectLabel = $this->translate('Downtime cancelled'); + } else { + $subjectLabel = $this->translate('Downtime ended'); + } + + break; + case 'downtime_start': + $subjectLabel = $this->translate('Downtime started'); + + break; + case 'flapping_start': + $subjectLabel = $this->translate('Flapping started'); + + break; + case 'flapping_end': + $subjectLabel = $this->translate('Flapping stopped'); + + break; + case 'ack_set': + $subjectLabel = $this->translate('Acknowledgement set'); + + break; + case 'ack_clear': + if (! empty($event->acknowledgement->cleared_by)) { + if ($event->acknowledgement->cleared_by !== $event->acknowledgement->author) { + $subjectLabel = sprintf( + $this->translate('Acknowledgement cleared by %s', '..'), + $event->acknowledgement->cleared_by + ); + } else { + $subjectLabel = $this->translate('Acknowledgement cleared by author'); + } + } elseif (isset($event->acknowledgement->expire_time)) { + $subjectLabel = $this->translate('Acknowledgement expired'); + } else { + $subjectLabel = $this->translate('Acknowledgement cleared'); + } + + break; + case 'notification': + $subjectLabel = isset($event->notification->type) ? sprintf( + NotificationListItem::phraseForType($event->notification->type), + ucfirst($event->object_type) + ) : $event->event_type; + + break; + case 'state_change': + $state = $event->state->state_type === 'hard' + ? $event->state->hard_state + : $event->state->soft_state; + if ($state === 0) { + if ($event->object_type === 'service') { + $subjectLabel = $this->translate('Service recovered'); + } else { + $subjectLabel = $this->translate('Host recovered'); + } + } else { + if ($event->state->state_type === 'hard') { + $subjectLabel = $this->translate('Hard state changed'); + } else { + $subjectLabel = $this->translate('Soft state changed'); + } + } + + break; + default: + $subjectLabel = $event->event_type; + + break; + } + + if (! $this->wantSubjectLink()) { + $title->addHtml(HtmlElement::create('span', ['class' => 'subject'], $subjectLabel)); + } else { + $title->addHtml(new Link($subjectLabel, Links::event($event), ['class' => 'subject'])); + } + + if ($event->object_type === 'host') { + if (isset($event->host->id)) { + $link = $this->createHostLink($event->host, true); + } + } else { + if (isset($event->host->id, $event->service->id)) { + $link = $this->createServiceLink($event->service, $event->host, true); + } + } + + $title->addHtml(Text::create(' ')); + if (isset($link)) { + $title->addHtml($link); + } + } + + protected function assembleCaption(BaseHtmlElement $caption): void + { + $event = $this->getObject(); + switch ($event->event_type) { + case 'comment_add': + case 'comment_remove': + $markdownLine = new MarkdownLine($this->createTicketLinks($event->comment->comment)); + $caption->getAttributes()->add($markdownLine->getAttributes()); + $caption->add([ + new Icon(Icons::USER), + $event->comment->author, + ': ' + ])->addFrom($markdownLine); + + break; + case 'downtime_end': + case 'downtime_start': + $markdownLine = new MarkdownLine($this->createTicketLinks($event->downtime->comment)); + $caption->getAttributes()->add($markdownLine->getAttributes()); + $caption->add([ + new Icon(Icons::USER), + $event->downtime->author, + ': ' + ])->addFrom($markdownLine); + + break; + case 'flapping_start': + $caption + ->add( + sprintf( + t('State Change Rate: %.2f%%; Start Threshold: %.2f%%'), + $event->flapping->percent_state_change_start, + $event->flapping->flapping_threshold_high + ) + ) + ->getAttributes() + ->add('class', 'plugin-output'); + + break; + case 'flapping_end': + $caption + ->add( + sprintf( + t('State Change Rate: %.2f%%; End Threshold: %.2f%%; Flapping for %s'), + $event->flapping->percent_state_change_end, + $event->flapping->flapping_threshold_low, + isset($event->flapping->end_time) + ? DateFormatter::formatDuration( + $event->flapping->end_time->getTimestamp() + - $event->flapping->start_time->getTimestamp() + ) + : t('n. a.') + ) + ) + ->getAttributes() + ->add('class', 'plugin-output'); + + break; + case 'ack_clear': + case 'ack_set': + if (! isset($event->acknowledgement->comment) && ! isset($event->acknowledgement->author)) { + $caption->addHtml( + new EmptyState( + $this->translate('This acknowledgement was set before Icinga DB history recording') + ) + ); + } else { + $markdownLine = new MarkdownLine($this->createTicketLinks($event->acknowledgement->comment)); + $caption->getAttributes()->add($markdownLine->getAttributes()); + $caption->add([ + new Icon(Icons::USER), + $event->acknowledgement->author, + ': ' + ])->addFrom($markdownLine); + } + + break; + case 'notification': + if (! empty($event->notification->author)) { + $caption->add([ + new Icon(Icons::USER), + $event->notification->author, + ': ', + $event->notification->text + ]); + } else { + $commandName = $event->object_type === 'host' + ? $event->host->checkcommand_name + : $event->service->checkcommand_name; + if (isset($commandName)) { + if (empty($event->notification->text)) { + $caption->addHtml(new EmptyState($this->translate('Output unavailable.'))); + } else { + $caption->addHtml( + new PluginOutputContainer( + (new PluginOutput($event->notification->text)) + ->setCommandName($commandName) + ) + ); + } + } else { + $caption->addHtml( + new EmptyState($this->translate('Waiting for Icinga DB to synchronize the config.')) + ); + } + } + + break; + case 'state_change': + $commandName = $event->object_type === 'host' + ? $event->host->checkcommand_name + : $event->service->checkcommand_name; + if (isset($commandName)) { + if (empty($event->state->output)) { + $caption->addHtml(new EmptyState($this->translate('Output unavailable.'))); + } else { + $caption->addHtml( + new PluginOutputContainer( + (new PluginOutput($event->state->output)) + ->setCommandName($commandName) + ) + ); + } + } else { + $caption->addHtml( + new EmptyState($this->translate('Waiting for Icinga DB to synchronize the config.')) + ); + } + + break; + } + } + + protected function createTimestamp(): ?BaseHtmlElement + { + return new TimeAgo($this->getObject()->event_time->getTimestamp()); + } +} diff --git a/library/Icingadb/Widget/Detail/HostAndServiceHeaderUtils.php b/library/Icingadb/Widget/Detail/HostAndServiceHeaderUtils.php new file mode 100644 index 000000000..4601f3370 --- /dev/null +++ b/library/Icingadb/Widget/Detail/HostAndServiceHeaderUtils.php @@ -0,0 +1,170 @@ +getObject(); + $state = $object->state; + + $stateBall = new StateBall($state->getStateText(), $this->getStateBallSize()); + $stateBall->add($state->getIcon()); + if ($state->is_problem && ($state->is_handled || ! $state->is_reachable)) { + $stateBall->getAttributes()->add('class', 'handled'); + } + + $visual->addHtml($stateBall); + if ($state->state_type === 'soft') { + $visual->addHtml( + new CheckAttempt((int) $state->check_attempt, (int) $object->max_check_attempts) + ); + } + } + + protected function assembleTitle(BaseHtmlElement $title): void + { + $object = $this->getObject(); + $state = $object->state; + $title->addHtml(Html::sprintf( + $this->translate('%s is %s', ' is '), + $this->createSubject(), + Html::tag('span', ['class' => 'state-text'], $state->getStateTextTranslated()) + )); + + if ($state->affects_children) { + $total = (int) $object->affected_children; + + if ($total > 1000) { + $total = '1000+'; + $tooltip = $this->translate('Up to 1000+ affected objects'); + } else { + $tooltip = sprintf( + $this->translatePlural( + '%d affected object', + 'Up to %d affected objects', + $total + ), + $total + ); + } + + $icon = new Icon(Icons::UNREACHABLE); + + $title->addHtml(new HtmlElement( + 'span', + Attributes::create([ + 'class' => 'affected-objects', + 'title' => $tooltip + ]), + $icon, + Text::create($total) + )); + } + } + + protected function assembleCaption(BaseHtmlElement $caption): void + { + $object = $this->getObject(); + $state = $object->state; + if ($state->soft_state === null && $state->output === null) { + $caption->addHtml(Text::create($this->translate('Waiting for Icinga DB to synchronize the state.'))); + } else { + if (empty($state->output)) { + $pluginOutput = new EmptyState($this->translate('Output unavailable.')); + } else { + $pluginOutput = new PluginOutputContainer(PluginOutput::fromObject($object)); + } + + $caption->addHtml($pluginOutput); + } + } + + protected function createTimestamp(): ?BaseHtmlElement + { + $state = $this->getObject()->state; + $since = null; + if ($state->is_overdue) { + $since = new TimeSince($state->next_update->getTimestamp()); + $since->prepend($this->translate('Overdue') . ' '); + $since->prependHtml(new Icon(Icons::WARNING)); + } elseif ($state->last_state_change !== null && $state->last_state_change->getTimestamp() > 0) { + $since = new TimeSince($state->last_state_change->getTimestamp()); + } + + return $since; + } + + protected function createIconImage(): ?BaseHtmlElement + { + if (! $this->wantIconImage()) { + return null; + } + + $iconImage = HtmlElement::create('div', [ + 'class' => 'icon-image' + ]); + + $object = $this->getObject(); + if (isset($object->icon_image->icon_image)) { + $iconImage->addHtml(new IconImage($object->icon_image->icon_image, $object->icon_image_alt)); + } else { + $iconImage->addAttributes(['class' => 'placeholder']); + } + + return $iconImage; + } +} diff --git a/library/Icingadb/Widget/ItemList/HostDetailHeader.php b/library/Icingadb/Widget/Detail/HostHeader.php similarity index 76% rename from library/Icingadb/Widget/ItemList/HostDetailHeader.php rename to library/Icingadb/Widget/Detail/HostHeader.php index b7afb2327..cf0e81db9 100644 --- a/library/Icingadb/Widget/ItemList/HostDetailHeader.php +++ b/library/Icingadb/Widget/Detail/HostHeader.php @@ -1,20 +1,23 @@ 'host-header']; protected function assembleVisual(BaseHtmlElement $visual): void { @@ -56,14 +59,4 @@ protected function assembleVisual(BaseHtmlElement $visual): void $visual->addHtml($stateChange); } - - protected function assemble(): void - { - $attributes = $this->list->getAttributes(); - if (! in_array('minimal', $attributes->get('class')->getValue())) { - $attributes->add('class', 'minimal'); - } - - parent::assemble(); - } } diff --git a/library/Icingadb/Widget/Detail/HostgroupHeader.php b/library/Icingadb/Widget/Detail/HostgroupHeader.php new file mode 100644 index 000000000..a447dc13c --- /dev/null +++ b/library/Icingadb/Widget/Detail/HostgroupHeader.php @@ -0,0 +1,59 @@ + 'hostgroup-header']; + + protected function assembleTitle(BaseHtmlElement $title): void + { + $title->addHtml(new HtmlElement( + 'span', + Attributes::create(['class' => 'subject']), + Text::create($this->object->display_name) + )); + + $title->addHtml(new HtmlElement('span', null, Text::create($this->object->name))); + } + + protected function assembleCaption(BaseHtmlElement $caption): void + { + $hostStats = (new HostStatistics($this->object)) + ->setBaseFilter(Filter::equal('hostgroup.name', $this->object->name)); + + + $serviceStats = (new ServiceStatistics($this->object)) + ->setBaseFilter(Filter::equal('hostgroup.name', $this->object->name)); + + $caption->addHtml($hostStats, $serviceStats); + } + + protected function assembleHeader(BaseHtmlElement $header): void + { + $header->addHtml($this->createTitle()); + $header->addHtml($this->createCaption()); + } + + protected function assembleMain(BaseHtmlElement $main): void + { + $main->addHtml($this->createHeader()); + } + + protected function assemble(): void + { + $this->addHtml($this->createMain()); + } +} diff --git a/library/Icingadb/Widget/Detail/RedundancyGroupHeader.php b/library/Icingadb/Widget/Detail/RedundancyGroupHeader.php index 12154c348..395d7470e 100644 --- a/library/Icingadb/Widget/Detail/RedundancyGroupHeader.php +++ b/library/Icingadb/Widget/Detail/RedundancyGroupHeader.php @@ -6,17 +6,22 @@ use Icinga\Module\Icingadb\Model\RedundancyGroup; use Icinga\Module\Icingadb\Model\RedundancyGroupSummary; -use Icinga\Module\Icingadb\Widget\DependencyNodeStatistics; +use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\HtmlElement; use ipl\Html\Text; +use ipl\Html\ValidHtml; use ipl\Web\Widget\StateBall; /** * @property RedundancyGroup $object */ -class RedundancyGroupHeader extends ObjectHeader +class RedundancyGroupHeader extends BaseObjectHeader { + use RedundancyGroupHeaderUtils; + + protected $defaultAttributes = ['class' => 'redundancygroup-header']; + /** @var RedundancyGroupSummary */ protected $summary; @@ -27,37 +32,38 @@ public function __construct(RedundancyGroup $object, RedundancyGroupSummary $sum parent::__construct($object); } - protected function assembleVisual(BaseHtmlElement $visual): void + protected function getObject(): RedundancyGroup + { + return $this->object; + } + + protected function getSummary(): RedundancyGroupSummary { - $visual->addHtml(new StateBall($this->object->state->getStateText(), $this->getStateBallSize())); + return $this->summary; } - protected function assembleTitle(BaseHtmlElement $title): void + protected function createSubject(): ValidHtml { - $title->addHtml($this->createSubject()); - if ($this->object->state->failed) { - $text = $this->translate('has no working objects'); - } else { - $text = $this->translate('has working objects'); - } - - $title->addHtml(HtmlElement::create('span', null, Text::create($text))); + return new HtmlElement( + 'span', + Attributes::create(['class' => 'subject']), + Text::create($this->object->display_name) + ); } - protected function createStatistics(): BaseHtmlElement + protected function getStateBallSize(): string { - return new DependencyNodeStatistics($this->summary); + return StateBall::SIZE_BIG; } protected function assembleHeader(BaseHtmlElement $header): void { - $header->add($this->createTitle()); - $header->add($this->createStatistics()); - $header->add($this->createTimestamp()); + $header->addHtml($this->createTitle()); + $header->addHtml($this->createCaption()); } protected function assembleMain(BaseHtmlElement $main): void { - $main->add($this->createHeader()); + $main->addHtml($this->createHeader()); } } diff --git a/library/Icingadb/Widget/Detail/RedundancyGroupHeaderUtils.php b/library/Icingadb/Widget/Detail/RedundancyGroupHeaderUtils.php new file mode 100644 index 000000000..e19e5da9b --- /dev/null +++ b/library/Icingadb/Widget/Detail/RedundancyGroupHeaderUtils.php @@ -0,0 +1,76 @@ +addHtml(new StateBall($this->getObject()->state->getStateText(), $this->getStateBallSize())); + } + + protected function assembleTitle(BaseHtmlElement $title): void + { + $title->addHtml($this->createSubject()); + if ($this->getObject()->state->failed) { + $text = $this->translate('has no working objects'); + } else { + $text = $this->translate('has working objects'); + } + + $title->addHtml(HtmlElement::create('span', null, Text::create($text))); + } + + protected function assembleCaption(BaseHtmlElement $caption): void + { + $caption->addHtml(new DependencyNodeStatistics($this->getSummary())); + } + + protected function createTimestamp(): BaseHtmlElement + { + return new TimeSince($this->getObject()->state->last_state_change->getTimestamp()); + } +} diff --git a/library/Icingadb/Widget/ItemList/ServiceDetailHeader.php b/library/Icingadb/Widget/Detail/ServiceHeader.php similarity index 60% rename from library/Icingadb/Widget/ItemList/ServiceDetailHeader.php rename to library/Icingadb/Widget/Detail/ServiceHeader.php index 8c5094d73..57c13ec1e 100644 --- a/library/Icingadb/Widget/ItemList/ServiceDetailHeader.php +++ b/library/Icingadb/Widget/Detail/ServiceHeader.php @@ -1,21 +1,30 @@ 'service-header']; protected function assembleVisual(BaseHtmlElement $visual): void { if ($this->state->state_type === 'soft') { @@ -57,13 +66,18 @@ protected function assembleVisual(BaseHtmlElement $visual): void $visual->addHtml($stateChange); } - protected function assemble(): void + protected function createSubject(): ValidHtml { - $attributes = $this->list->getAttributes(); - if (! in_array('minimal', $attributes->get('class')->getValue())) { - $attributes->add('class', 'minimal'); - } + $service = $this->object->display_name; + $host = [ + new StateBall($this->object->host->state->getStateText(), StateBall::SIZE_MEDIUM), + ' ', + $this->object->host->display_name + ]; + + $host = new Link($host, Links::host($this->object->host), ['class' => 'subject']); + $service = new HtmlElement('span', Attributes::create(['class' => 'subject']), Text::create($service)); - parent::assemble(); + return Html::sprintf(t('%s on %s', ' on '), $service, $host); } } diff --git a/library/Icingadb/Widget/Detail/ServicegroupHeader.php b/library/Icingadb/Widget/Detail/ServicegroupHeader.php new file mode 100644 index 000000000..71de93b63 --- /dev/null +++ b/library/Icingadb/Widget/Detail/ServicegroupHeader.php @@ -0,0 +1,19 @@ + 'servicegroup-header']; + + protected function assembleCaption(BaseHtmlElement $caption): void + { + $caption->addHtml( + (new ServiceStatistics($this->object)) + ->setBaseFilter(Filter::equal('servicegroup.name', $this->object->name)) + ); + } +} diff --git a/library/Icingadb/Widget/Detail/UserHeader.php b/library/Icingadb/Widget/Detail/UserHeader.php new file mode 100644 index 000000000..d89f11f80 --- /dev/null +++ b/library/Icingadb/Widget/Detail/UserHeader.php @@ -0,0 +1,58 @@ + 'user-header']; + + protected const BALL_CLASS_NAME = 'user-ball'; + + protected function assembleTitle(BaseHtmlElement $title): void + { + $title->addHtml( + new HtmlElement( + 'span', + Attributes::create(['class' => 'subject']), + Text::create($this->object->display_name) + ), + new HtmlElement('span', null, Text::create($this->object->name)) + ); + } + + protected function assembleVisual(BaseHtmlElement $visual): void + { + $visual->addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => static::BALL_CLASS_NAME]), + Text::create($this->object->display_name[0]) + )); + } + + protected function assembleHeader(BaseHtmlElement $header): void + { + $header->addHtml($this->createTitle()); + } + + protected function assembleMain(BaseHtmlElement $main): void + { + $main->addHtml($this->createHeader()); + } + + protected function assemble(): void + { + $this->addHtml($this->createVisual()); + $this->addHtml($this->createMain()); + } +} diff --git a/library/Icingadb/Widget/Detail/UsergroupHeader.php b/library/Icingadb/Widget/Detail/UsergroupHeader.php new file mode 100644 index 000000000..caa27f2d8 --- /dev/null +++ b/library/Icingadb/Widget/Detail/UsergroupHeader.php @@ -0,0 +1,17 @@ + 'usergroup-header']; + + protected const BALL_CLASS_NAME = 'usergroup-ball'; +} diff --git a/library/Icingadb/Widget/ItemList/BaseCommentListItem.php b/library/Icingadb/Widget/ItemList/BaseCommentListItem.php index de11c0c97..836d9c5a2 100644 --- a/library/Icingadb/Widget/ItemList/BaseCommentListItem.php +++ b/library/Icingadb/Widget/ItemList/BaseCommentListItem.php @@ -4,27 +4,10 @@ namespace Icinga\Module\Icingadb\Widget\ItemList; -use Icinga\Module\Icingadb\Common\NoSubjectLink; -use Icinga\Module\Icingadb\Common\ObjectLinkDisabled; -use Icinga\Module\Icingadb\Common\TicketLinks; -use ipl\Html\Html; -use Icinga\Module\Icingadb\Common\HostLink; -use Icinga\Module\Icingadb\Common\Icons; -use Icinga\Module\Icingadb\Common\Links; -use Icinga\Module\Icingadb\Widget\MarkdownLine; -use Icinga\Module\Icingadb\Common\ServiceLink; +use Icinga\Module\Icingadb\Widget\Detail\CommentHeaderUtils; use Icinga\Module\Icingadb\Model\Comment; -use ipl\Html\FormattedString; use ipl\Web\Common\BaseListItem; -use ipl\Web\Widget\TimeAgo; -use ipl\Html\Attributes; -use ipl\Html\BaseHtmlElement; -use ipl\Html\HtmlElement; -use ipl\Html\Text; use ipl\Stdlib\Filter; -use ipl\Web\Widget\Icon; -use ipl\Web\Widget\Link; -use ipl\Web\Widget\TimeUntil; /** * Comment item of a comment list. Represents one database row. @@ -34,90 +17,21 @@ */ abstract class BaseCommentListItem extends BaseListItem { - use HostLink; - use ServiceLink; - use NoSubjectLink; - use ObjectLinkDisabled; - use TicketLinks; + use CommentHeaderUtils; - protected function assembleCaption(BaseHtmlElement $caption): void + protected function getObject(): Comment { - $markdownLine = new MarkdownLine($this->createTicketLinks($this->item->text)); - - $caption->getAttributes()->add($markdownLine->getAttributes()); - $caption->addFrom($markdownLine); + return $this->item; } - protected function assembleTitle(BaseHtmlElement $title): void + protected function wantSubjectLink(): bool { - $isAck = $this->item->entry_type === 'ack'; - $expires = $this->item->expire_time; - - $subjectText = sprintf( - $isAck ? t('%s acknowledged', '..') : t('%s commented', '..'), - $this->item->author - ); - - $headerParts = [ - new Icon(Icons::USER), - $this->getNoSubjectLink() - ? new HtmlElement('span', Attributes::create(['class' => 'subject']), Text::create($subjectText)) - : new Link($subjectText, Links::comment($this->item), ['class' => 'subject']) - ]; - - if ($isAck) { - $label = [Text::create('ack')]; - - if ($this->item->is_persistent) { - array_unshift($label, new Icon(Icons::IS_PERSISTENT)); - } - - $headerParts[] = Text::create(' '); - $headerParts[] = new HtmlElement('span', Attributes::create(['class' => 'ack-badge badge']), ...$label); - } - - if ($expires !== null) { - $headerParts[] = Text::create(' '); - $headerParts[] = new HtmlElement( - 'span', - Attributes::create(['class' => 'ack-badge badge']), - Text::create(t('EXPIRES')) - ); - } - - if ($this->getObjectLinkDisabled()) { - // pass - } elseif ($this->item->object_type === 'host') { - $headerParts[] = $this->createHostLink($this->item->host, true); - } else { - $headerParts[] = $this->createServiceLink($this->item->service, $this->item->service->host, true); - } - - $title->addHtml(...$headerParts); - } - - protected function assembleVisual(BaseHtmlElement $visual): void - { - $visual->addHtml(new HtmlElement( - 'div', - Attributes::create(['class' => 'user-ball']), - Text::create($this->item->author[0]) - )); + return true; } - protected function createTimestamp(): ?BaseHtmlElement + protected function wantObjectLink(): bool { - if ($this->item->expire_time) { - return Html::tag( - 'span', - FormattedString::create(t("expires %s"), new TimeUntil($this->item->expire_time->getTimestamp())) - ); - } - - return Html::tag( - 'span', - FormattedString::create(t("created %s"), new TimeAgo($this->item->entry_time->getTimestamp())) - ); + return true; } protected function init(): void @@ -125,7 +39,5 @@ protected function init(): void $this->setTicketLinkEnabled($this->list->getTicketLinkEnabled()); $this->list->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); $this->list->addMultiselectFilterAttribute($this, Filter::equal('name', $this->item->name)); - $this->setObjectLinkDisabled($this->list->getObjectLinkDisabled()); - $this->setNoSubjectLink($this->list->getNoSubjectLink()); } } diff --git a/library/Icingadb/Widget/ItemList/BaseDowntimeListItem.php b/library/Icingadb/Widget/ItemList/BaseDowntimeListItem.php index dedaa7215..d3c963f55 100644 --- a/library/Icingadb/Widget/ItemList/BaseDowntimeListItem.php +++ b/library/Icingadb/Widget/ItemList/BaseDowntimeListItem.php @@ -4,26 +4,13 @@ namespace Icinga\Module\Icingadb\Widget\ItemList; -use Icinga\Date\DateFormatter; -use Icinga\Module\Icingadb\Common\HostLink; -use Icinga\Module\Icingadb\Common\Icons; -use Icinga\Module\Icingadb\Common\Links; -use Icinga\Module\Icingadb\Common\NoSubjectLink; -use Icinga\Module\Icingadb\Common\ObjectLinkDisabled; -use Icinga\Module\Icingadb\Common\ServiceLink; -use Icinga\Module\Icingadb\Common\TicketLinks; use Icinga\Module\Icingadb\Model\Downtime; -use Icinga\Module\Icingadb\Widget\MarkdownLine; +use Icinga\Module\Icingadb\Widget\Detail\DowntimeHeaderUtils; use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; -use ipl\Html\Html; use ipl\Html\HtmlElement; -use ipl\Html\TemplateString; -use ipl\Html\Text; use ipl\Stdlib\Filter; use ipl\Web\Common\BaseListItem; -use ipl\Web\Widget\Icon; -use ipl\Web\Widget\Link; /** * Downtime item of a downtime list. Represents one database row. @@ -33,60 +20,28 @@ */ abstract class BaseDowntimeListItem extends BaseListItem { - use HostLink; - use ServiceLink; - use NoSubjectLink; - use ObjectLinkDisabled; - use TicketLinks; + use DowntimeHeaderUtils; - /** @var int Current Time */ - protected $currentTime; - - /** @var int Duration */ - protected $duration; - - /** @var int Downtime end time */ - protected $endTime; + protected function getObject(): Downtime + { + return $this->item; + } - /** @var bool Whether the downtime is active */ - protected $isActive; + protected function wantSubjectLink(): bool + { + return true; + } - /** @var int Downtime start time */ - protected $startTime; + protected function wantObjectLink(): bool + { + return true; + } protected function init(): void { - if ( - isset($this->item->start_time, $this->item->end_time) - && $this->item->is_flexible - && $this->item->is_in_effect - ) { - $this->startTime = $this->item->start_time->getTimestamp(); - $this->endTime = $this->item->end_time->getTimestamp(); - } else { - $this->startTime = $this->item->scheduled_start_time->getTimestamp(); - $this->endTime = $this->item->scheduled_end_time->getTimestamp(); - } - - $this->currentTime = time(); - - $this->isActive = $this->item->is_in_effect - || $this->item->is_flexible && $this->item->scheduled_start_time->getTimestamp() <= $this->currentTime; - - $until = ($this->isActive ? $this->endTime : $this->startTime) - $this->currentTime; - $this->duration = explode(' ', DateFormatter::formatDuration( - $until <= 3600 ? $until : $until + (3600 - ((int) $until % 3600)) - ), 2)[0]; - $this->list->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); $this->list->addMultiselectFilterAttribute($this, Filter::equal('name', $this->item->name)); - $this->setObjectLinkDisabled($this->list->getObjectLinkDisabled()); - $this->setNoSubjectLink($this->list->getNoSubjectLink()); $this->setTicketLinkEnabled($this->list->getTicketLinkEnabled()); - - if ($this->item->is_in_effect) { - $this->getAttributes()->add('class', 'in-effect'); - } } protected function createProgress(): BaseHtmlElement @@ -105,112 +60,4 @@ protected function createProgress(): BaseHtmlElement ) ); } - - protected function assembleCaption(BaseHtmlElement $caption): void - { - $markdownLine = new MarkdownLine($this->createTicketLinks($this->item->comment)); - $caption->getAttributes()->add($markdownLine->getAttributes()); - $caption->addHtml( - new HtmlElement( - 'span', - null, - new Icon(Icons::USER), - Text::create($this->item->author) - ), - Text::create(': ') - )->addFrom($markdownLine); - } - - protected function assembleTitle(BaseHtmlElement $title): void - { - if ($this->getObjectLinkDisabled()) { - $link = null; - } elseif ($this->item->object_type === 'host') { - $link = $this->createHostLink($this->item->host, true); - } else { - $link = $this->createServiceLink($this->item->service, $this->item->service->host, true); - } - - if ($this->item->is_flexible) { - if ($link !== null) { - $template = t('{{#link}}Flexible Downtime{{/link}} for %s'); - } else { - $template = t('Flexible Downtime'); - } - } else { - if ($link !== null) { - $template = t('{{#link}}Fixed Downtime{{/link}} for %s'); - } else { - $template = t('Fixed Downtime'); - } - } - - if ($this->getNoSubjectLink()) { - if ($link === null) { - $title->addHtml(HtmlElement::create('span', [ 'class' => 'subject'], $template)); - } else { - $title->addHtml(TemplateString::create( - $template, - ['link' => HtmlElement::create('span', [ 'class' => 'subject'])], - $link - )); - } - } else { - if ($link === null) { - $title->addHtml(new Link($template, Links::downtime($this->item))); - } else { - $title->addHtml(TemplateString::create( - $template, - ['link' => new Link('', Links::downtime($this->item))], - $link - )); - } - } - } - - protected function assembleVisual(BaseHtmlElement $visual): void - { - $dateTime = DateFormatter::formatDateTime($this->endTime); - - if ($this->isActive) { - $visual->addHtml(Html::sprintf( - t('%s left', '..'), - Html::tag( - 'strong', - Html::tag( - 'time', - [ - 'datetime' => $dateTime, - 'title' => $dateTime - ], - $this->duration - ) - ) - )); - } else { - $visual->addHtml(Html::sprintf( - t('in %s', '..'), - Html::tag('strong', $this->duration) - )); - } - } - - protected function createTimestamp(): ?BaseHtmlElement - { - $dateTime = DateFormatter::formatDateTime($this->isActive ? $this->endTime : $this->startTime); - - return Html::tag( - 'time', - [ - 'datetime' => $dateTime, - 'title' => $dateTime - ], - sprintf( - $this->isActive - ? t('expires in %s', '..') - : t('starts in %s', '..'), - $this->duration - ) - ); - } } diff --git a/library/Icingadb/Widget/ItemList/BaseHistoryListItem.php b/library/Icingadb/Widget/ItemList/BaseHistoryListItem.php index 61da9fd1d..3f79d89e6 100644 --- a/library/Icingadb/Widget/ItemList/BaseHistoryListItem.php +++ b/library/Icingadb/Widget/ItemList/BaseHistoryListItem.php @@ -4,38 +4,14 @@ namespace Icinga\Module\Icingadb\Widget\ItemList; -use Icinga\Date\DateFormatter; -use Icinga\Module\Icingadb\Common\HostLink; -use Icinga\Module\Icingadb\Common\HostStates; -use Icinga\Module\Icingadb\Common\Icons; -use Icinga\Module\Icingadb\Common\Links; -use Icinga\Module\Icingadb\Common\NoSubjectLink; -use Icinga\Module\Icingadb\Common\TicketLinks; -use Icinga\Module\Icingadb\Widget\MarkdownLine; -use Icinga\Module\Icingadb\Common\ServiceLink; -use Icinga\Module\Icingadb\Common\ServiceStates; use Icinga\Module\Icingadb\Model\History; -use Icinga\Module\Icingadb\Util\PluginOutput; -use Icinga\Module\Icingadb\Widget\CheckAttempt; -use Icinga\Module\Icingadb\Widget\PluginOutputContainer; -use Icinga\Module\Icingadb\Widget\StateChange; +use Icinga\Module\Icingadb\Widget\Detail\EventHeaderUtils; use ipl\Stdlib\Filter; use ipl\Web\Common\BaseListItem; -use ipl\Web\Widget\EmptyState; -use ipl\Web\Widget\StateBall; -use ipl\Web\Widget\TimeAgo; -use ipl\Html\BaseHtmlElement; -use ipl\Html\HtmlElement; -use ipl\Html\Text; -use ipl\Web\Widget\Icon; -use ipl\Web\Widget\Link; abstract class BaseHistoryListItem extends BaseListItem { - use HostLink; - use NoSubjectLink; - use ServiceLink; - use TicketLinks; + use EventHeaderUtils; /** @var History */ protected $item; @@ -45,363 +21,17 @@ abstract class BaseHistoryListItem extends BaseListItem protected function init(): void { - $this->setNoSubjectLink($this->list->getNoSubjectLink()); $this->setTicketLinkEnabled($this->list->getTicketLinkEnabled()); $this->list->addDetailFilterAttribute($this, Filter::equal('id', bin2hex($this->item->id))); } - abstract protected function getStateBallSize(): string; - - protected function assembleCaption(BaseHtmlElement $caption): void - { - switch ($this->item->event_type) { - case 'comment_add': - case 'comment_remove': - $markdownLine = new MarkdownLine($this->createTicketLinks($this->item->comment->comment)); - $caption->getAttributes()->add($markdownLine->getAttributes()); - $caption->add([ - new Icon(Icons::USER), - $this->item->comment->author, - ': ' - ])->addFrom($markdownLine); - - break; - case 'downtime_end': - case 'downtime_start': - $markdownLine = new MarkdownLine($this->createTicketLinks($this->item->downtime->comment)); - $caption->getAttributes()->add($markdownLine->getAttributes()); - $caption->add([ - new Icon(Icons::USER), - $this->item->downtime->author, - ': ' - ])->addFrom($markdownLine); - - break; - case 'flapping_start': - $caption - ->add(sprintf( - t('State Change Rate: %.2f%%; Start Threshold: %.2f%%'), - $this->item->flapping->percent_state_change_start, - $this->item->flapping->flapping_threshold_high - )) - ->getAttributes() - ->add('class', 'plugin-output'); - - break; - case 'flapping_end': - $caption - ->add(sprintf( - t('State Change Rate: %.2f%%; End Threshold: %.2f%%; Flapping for %s'), - $this->item->flapping->percent_state_change_end, - $this->item->flapping->flapping_threshold_low, - isset($this->item->flapping->end_time) - ? DateFormatter::formatDuration( - $this->item->flapping->end_time->getTimestamp() - - $this->item->flapping->start_time->getTimestamp() - ) - : t('n. a.') - )) - ->getAttributes() - ->add('class', 'plugin-output'); - - break; - case 'ack_clear': - case 'ack_set': - if (! isset($this->item->acknowledgement->comment) && ! isset($this->item->acknowledgement->author)) { - $caption->addHtml(new EmptyState( - t('This acknowledgement was set before Icinga DB history recording') - )); - } else { - $markdownLine = new MarkdownLine($this->createTicketLinks($this->item->acknowledgement->comment)); - $caption->getAttributes()->add($markdownLine->getAttributes()); - $caption->add([ - new Icon(Icons::USER), - $this->item->acknowledgement->author, - ': ' - ])->addFrom($markdownLine); - } - - break; - case 'notification': - if (! empty($this->item->notification->author)) { - $caption->add([ - new Icon(Icons::USER), - $this->item->notification->author, - ': ', - $this->item->notification->text - ]); - } else { - $commandName = $this->item->object_type === 'host' - ? $this->item->host->checkcommand_name - : $this->item->service->checkcommand_name; - if (isset($commandName)) { - if (empty($this->item->notification->text)) { - $caption->addHtml(new EmptyState(t('Output unavailable.'))); - } else { - $caption->addHtml(new PluginOutputContainer( - (new PluginOutput($this->item->notification->text)) - ->setCommandName($commandName) - )); - } - } else { - $caption->addHtml(new EmptyState(t('Waiting for Icinga DB to synchronize the config.'))); - } - } - - break; - case 'state_change': - $commandName = $this->item->object_type === 'host' - ? $this->item->host->checkcommand_name - : $this->item->service->checkcommand_name; - if (isset($commandName)) { - if (empty($this->item->state->output)) { - $caption->addHtml(new EmptyState(t('Output unavailable.'))); - } else { - $caption->addHtml(new PluginOutputContainer( - (new PluginOutput($this->item->state->output)) - ->setCommandName($commandName) - )); - } - } else { - $caption->addHtml(new EmptyState(t('Waiting for Icinga DB to synchronize the config.'))); - } - - break; - } - } - - protected function assembleVisual(BaseHtmlElement $visual): void - { - switch ($this->item->event_type) { - case 'comment_add': - $visual->addHtml(HtmlElement::create( - 'div', - ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], - new Icon(Icons::COMMENT) - )); - - break; - case 'comment_remove': - case 'downtime_end': - case 'ack_clear': - $visual->addHtml(HtmlElement::create( - 'div', - ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], - new Icon(Icons::REMOVE) - )); - - break; - case 'downtime_start': - $visual->addHtml(HtmlElement::create( - 'div', - ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], - new Icon(Icons::IN_DOWNTIME) - )); - - break; - case 'ack_set': - $visual->addHtml(HtmlElement::create( - 'div', - ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], - new Icon(Icons::IS_ACKNOWLEDGED) - )); - - break; - case 'flapping_end': - case 'flapping_start': - $visual->addHtml(HtmlElement::create( - 'div', - ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], - new Icon(Icons::IS_FLAPPING) - )); - - break; - case 'notification': - $visual->addHtml(HtmlElement::create( - 'div', - ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], - new Icon(Icons::NOTIFICATION) - )); - - break; - case 'state_change': - if ($this->item->state->state_type === 'soft') { - $stateType = 'soft_state'; - $previousStateType = 'previous_soft_state'; - - if ($this->item->state->previous_soft_state === 0) { - $previousStateType = 'hard_state'; - } - - $visual->addHtml(new CheckAttempt( - (int) $this->item->state->check_attempt, - (int) $this->item->state->max_check_attempts - )); - } else { - $stateType = 'hard_state'; - $previousStateType = 'previous_hard_state'; - - if ($this->item->state->hard_state === $this->item->state->previous_hard_state) { - $previousStateType = 'previous_soft_state'; - } - } - - if ($this->item->object_type === 'host') { - $state = HostStates::text($this->item->state->$stateType); - $previousState = HostStates::text($this->item->state->$previousStateType); - } else { - $state = ServiceStates::text($this->item->state->$stateType); - $previousState = ServiceStates::text($this->item->state->$previousStateType); - } - - $stateChange = new StateChange($state, $previousState); - if ($stateType === 'soft_state') { - $stateChange->setCurrentStateBallSize(StateBall::SIZE_MEDIUM_LARGE); - } - - if ($previousStateType === 'previous_soft_state') { - $stateChange->setPreviousStateBallSize(StateBall::SIZE_MEDIUM_LARGE); - if ($stateType === 'soft_state') { - $visual->getAttributes()->add('class', 'small-state-change'); - } - } - - $visual->prependHtml($stateChange); - - break; - } - } - - protected function assembleTitle(BaseHtmlElement $title): void + protected function getObject(): History { - switch ($this->item->event_type) { - case 'comment_add': - $subjectLabel = t('Comment added'); - - break; - case 'comment_remove': - if (! empty($this->item->comment->removed_by)) { - if ($this->item->comment->removed_by !== $this->item->comment->author) { - $subjectLabel = sprintf( - t('Comment removed by %s', '..'), - $this->item->comment->removed_by - ); - } else { - $subjectLabel = t('Comment removed by author'); - } - } elseif (isset($this->item->comment->expire_time)) { - $subjectLabel = t('Comment expired'); - } else { - $subjectLabel = t('Comment removed'); - } - - break; - case 'downtime_end': - if (! empty($this->item->downtime->cancelled_by)) { - if ($this->item->downtime->cancelled_by !== $this->item->downtime->author) { - $subjectLabel = sprintf( - t('Downtime cancelled by %s', '..'), - $this->item->downtime->cancelled_by - ); - } else { - $subjectLabel = t('Downtime cancelled by author'); - } - } elseif ($this->item->downtime->has_been_cancelled === 'y') { - $subjectLabel = t('Downtime cancelled'); - } else { - $subjectLabel = t('Downtime ended'); - } - - break; - case 'downtime_start': - $subjectLabel = t('Downtime started'); - - break; - case 'flapping_start': - $subjectLabel = t('Flapping started'); - - break; - case 'flapping_end': - $subjectLabel = t('Flapping stopped'); - - break; - case 'ack_set': - $subjectLabel = t('Acknowledgement set'); - - break; - case 'ack_clear': - if (! empty($this->item->acknowledgement->cleared_by)) { - if ($this->item->acknowledgement->cleared_by !== $this->item->acknowledgement->author) { - $subjectLabel = sprintf( - t('Acknowledgement cleared by %s', '..'), - $this->item->acknowledgement->cleared_by - ); - } else { - $subjectLabel = t('Acknowledgement cleared by author'); - } - } elseif (isset($this->item->acknowledgement->expire_time)) { - $subjectLabel = t('Acknowledgement expired'); - } else { - $subjectLabel = t('Acknowledgement cleared'); - } - - break; - case 'notification': - $subjectLabel = isset($this->item->notification->type) ? sprintf( - NotificationListItem::phraseForType($this->item->notification->type), - ucfirst($this->item->object_type) - ) : $this->item->event_type; - - break; - case 'state_change': - $state = $this->item->state->state_type === 'hard' - ? $this->item->state->hard_state - : $this->item->state->soft_state; - if ($state === 0) { - if ($this->item->object_type === 'service') { - $subjectLabel = t('Service recovered'); - } else { - $subjectLabel = t('Host recovered'); - } - } else { - if ($this->item->state->state_type === 'hard') { - $subjectLabel = t('Hard state changed'); - } else { - $subjectLabel = t('Soft state changed'); - } - } - - break; - default: - $subjectLabel = $this->item->event_type; - - break; - } - - if ($this->getNoSubjectLink()) { - $title->addHtml(HtmlElement::create('span', ['class' => 'subject'], $subjectLabel)); - } else { - $title->addHtml(new Link($subjectLabel, Links::event($this->item), ['class' => 'subject'])); - } - - if ($this->item->object_type === 'host') { - if (isset($this->item->host->id)) { - $link = $this->createHostLink($this->item->host, true); - } - } else { - if (isset($this->item->host->id, $this->item->service->id)) { - $link = $this->createServiceLink($this->item->service, $this->item->host, true); - } - } - - $title->addHtml(Text::create(' ')); - if (isset($link)) { - $title->addHtml($link); - } + return $this->item; } - protected function createTimestamp(): ?BaseHtmlElement + protected function wantSubjectLink(): bool { - return new TimeAgo($this->item->event_time->getTimestamp()); + return true; } } diff --git a/library/Icingadb/Widget/ItemList/BaseHostListItem.php b/library/Icingadb/Widget/ItemList/BaseHostListItem.php index edaf6c888..d6b647652 100644 --- a/library/Icingadb/Widget/ItemList/BaseHostListItem.php +++ b/library/Icingadb/Widget/ItemList/BaseHostListItem.php @@ -5,12 +5,8 @@ namespace Icinga\Module\Icingadb\Widget\ItemList; use Icinga\Module\Icingadb\Common\Links; -use Icinga\Module\Icingadb\Common\NoSubjectLink; use Icinga\Module\Icingadb\Model\Host; -use ipl\Html\Attributes; -use ipl\Html\BaseHtmlElement; -use ipl\Html\HtmlElement; -use ipl\Html\Text; +use ipl\Html\ValidHtml; use ipl\Stdlib\Filter; use ipl\Web\Widget\Link; @@ -22,34 +18,20 @@ */ abstract class BaseHostListItem extends StateListItem { - use NoSubjectLink; - /** * Create new subject link * - * @return BaseHtmlElement + * @return Link */ - protected function createSubject() + protected function createSubject(): ValidHtml { - if ($this->getNoSubjectLink()) { - return new HtmlElement( - 'span', - Attributes::create(['class' => 'subject']), - Text::create($this->item->display_name) - ); - } else { - return new Link($this->item->display_name, Links::host($this->item), ['class' => 'subject']); - } + return new Link($this->item->display_name, Links::host($this->item), ['class' => 'subject']); } protected function init(): void { parent::init(); - if ($this->list->getNoSubjectLink()) { - $this->setNoSubjectLink(); - } - $this->list->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)) ->addMultiselectFilterAttribute($this, Filter::equal('host.name', $this->item->name)); } diff --git a/library/Icingadb/Widget/ItemList/BaseNotificationListItem.php b/library/Icingadb/Widget/ItemList/BaseNotificationListItem.php index b538ac4ab..2eb50a8de 100644 --- a/library/Icingadb/Widget/ItemList/BaseNotificationListItem.php +++ b/library/Icingadb/Widget/ItemList/BaseNotificationListItem.php @@ -8,27 +8,25 @@ use Icinga\Module\Icingadb\Common\HostStates; use Icinga\Module\Icingadb\Common\Icons; use Icinga\Module\Icingadb\Common\Links; -use Icinga\Module\Icingadb\Common\NoSubjectLink; use Icinga\Module\Icingadb\Common\ServiceLink; use Icinga\Module\Icingadb\Common\ServiceStates; use Icinga\Module\Icingadb\Util\PluginOutput; use Icinga\Module\Icingadb\Widget\PluginOutputContainer; use Icinga\Module\Icingadb\Widget\StateChange; -use ipl\Stdlib\Filter; -use ipl\Web\Common\BaseListItem; -use ipl\Web\Widget\EmptyState; -use ipl\Web\Widget\TimeAgo; use InvalidArgumentException; use ipl\Html\BaseHtmlElement; use ipl\Html\HtmlElement; use ipl\Html\Text; +use ipl\Stdlib\Filter; +use ipl\Web\Common\BaseListItem; +use ipl\Web\Widget\EmptyState; use ipl\Web\Widget\Icon; use ipl\Web\Widget\Link; +use ipl\Web\Widget\TimeAgo; abstract class BaseNotificationListItem extends BaseListItem { use HostLink; - use NoSubjectLink; use ServiceLink; /** @var NotificationList */ @@ -36,7 +34,6 @@ abstract class BaseNotificationListItem extends BaseListItem protected function init(): void { - $this->setNoSubjectLink($this->list->getNoSubjectLink()); $this->list->addDetailFilterAttribute($this, Filter::equal('id', bin2hex($this->item->history->id))); } @@ -159,19 +156,11 @@ protected function assembleVisual(BaseHtmlElement $visual): void protected function assembleTitle(BaseHtmlElement $title): void { - if ($this->getNoSubjectLink()) { - $title->addHtml(HtmlElement::create( - 'span', - ['class' => 'subject'], - sprintf(self::phraseForType($this->item->type), ucfirst($this->item->object_type)) - )); - } else { - $title->addHtml(new Link( - sprintf(self::phraseForType($this->item->type), ucfirst($this->item->object_type)), - Links::event($this->item->history), - ['class' => 'subject'] - )); - } + $title->addHtml(new Link( + sprintf(self::phraseForType($this->item->type), ucfirst($this->item->object_type)), + Links::event($this->item->history), + ['class' => 'subject'] + )); if ($this->item->object_type === 'host') { $link = $this->createHostLink($this->item->host, true); diff --git a/library/Icingadb/Widget/ItemList/BaseServiceListItem.php b/library/Icingadb/Widget/ItemList/BaseServiceListItem.php index fe4f01462..29b9396c0 100644 --- a/library/Icingadb/Widget/ItemList/BaseServiceListItem.php +++ b/library/Icingadb/Widget/ItemList/BaseServiceListItem.php @@ -5,12 +5,9 @@ namespace Icinga\Module\Icingadb\Widget\ItemList; use Icinga\Module\Icingadb\Common\Links; -use Icinga\Module\Icingadb\Common\NoSubjectLink; use Icinga\Module\Icingadb\Model\Service; -use ipl\Html\Attributes; use ipl\Html\Html; -use ipl\Html\HtmlElement; -use ipl\Html\Text; +use ipl\Html\ValidHtml; use ipl\Stdlib\Filter; use ipl\Web\Widget\Link; use ipl\Web\Widget\StateBall; @@ -23,9 +20,7 @@ */ abstract class BaseServiceListItem extends StateListItem { - use NoSubjectLink; - - protected function createSubject() + protected function createSubject(): ValidHtml { $service = $this->item->display_name; $host = [ @@ -35,23 +30,15 @@ protected function createSubject() ]; $host = new Link($host, Links::host($this->item->host), ['class' => 'subject']); - if ($this->getNoSubjectLink()) { - $service = new HtmlElement('span', Attributes::create(['class' => 'subject']), Text::create($service)); - } else { - $service = new Link($service, Links::service($this->item, $this->item->host), ['class' => 'subject']); - } + $service = new Link($service, Links::service($this->item, $this->item->host), ['class' => 'subject']); - return [Html::sprintf(t('%s on %s', ' on '), $service, $host)]; + return Html::sprintf(t('%s on %s', ' on '), $service, $host); } protected function init(): void { parent::init(); - if ($this->list->getNoSubjectLink()) { - $this->setNoSubjectLink(); - } - $this->list->addMultiselectFilterAttribute( $this, Filter::all( diff --git a/library/Icingadb/Widget/ItemList/CommentList.php b/library/Icingadb/Widget/ItemList/CommentList.php index 5cf65aeb1..1f338b475 100644 --- a/library/Icingadb/Widget/ItemList/CommentList.php +++ b/library/Icingadb/Widget/ItemList/CommentList.php @@ -4,10 +4,8 @@ namespace Icinga\Module\Icingadb\Widget\ItemList; -use Icinga\Module\Icingadb\Common\CaptionDisabled; use Icinga\Module\Icingadb\Common\DetailActions; use Icinga\Module\Icingadb\Common\Links; -use Icinga\Module\Icingadb\Common\NoSubjectLink; use Icinga\Module\Icingadb\Common\ObjectLinkDisabled; use Icinga\Module\Icingadb\Common\TicketLinks; use Icinga\Module\Icingadb\Common\ViewMode; @@ -16,8 +14,6 @@ class CommentList extends BaseItemList { - use CaptionDisabled; - use NoSubjectLink; use ObjectLinkDisabled; use ViewMode; use TicketLinks; diff --git a/library/Icingadb/Widget/ItemList/CommentListItemMinimal.php b/library/Icingadb/Widget/ItemList/CommentListItemMinimal.php index 3c23ccd37..4e8a34a3a 100644 --- a/library/Icingadb/Widget/ItemList/CommentListItemMinimal.php +++ b/library/Icingadb/Widget/ItemList/CommentListItemMinimal.php @@ -9,13 +9,4 @@ class CommentListItemMinimal extends BaseCommentListItem { use ListItemMinimalLayout; - - protected function init(): void - { - parent::init(); - - if ($this->list->isCaptionDisabled()) { - $this->setCaptionDisabled(); - } - } } diff --git a/library/Icingadb/Widget/ItemList/DowntimeList.php b/library/Icingadb/Widget/ItemList/DowntimeList.php index 591ad9844..e54cecef8 100644 --- a/library/Icingadb/Widget/ItemList/DowntimeList.php +++ b/library/Icingadb/Widget/ItemList/DowntimeList.php @@ -4,10 +4,8 @@ namespace Icinga\Module\Icingadb\Widget\ItemList; -use Icinga\Module\Icingadb\Common\CaptionDisabled; use Icinga\Module\Icingadb\Common\DetailActions; use Icinga\Module\Icingadb\Common\Links; -use Icinga\Module\Icingadb\Common\NoSubjectLink; use Icinga\Module\Icingadb\Common\ObjectLinkDisabled; use Icinga\Module\Icingadb\Common\TicketLinks; use Icinga\Module\Icingadb\Common\ViewMode; @@ -16,8 +14,6 @@ class DowntimeList extends BaseItemList { - use CaptionDisabled; - use NoSubjectLink; use ObjectLinkDisabled; use ViewMode; use TicketLinks; diff --git a/library/Icingadb/Widget/ItemList/DowntimeListItemMinimal.php b/library/Icingadb/Widget/ItemList/DowntimeListItemMinimal.php index b8581d294..b2d3634dc 100644 --- a/library/Icingadb/Widget/ItemList/DowntimeListItemMinimal.php +++ b/library/Icingadb/Widget/ItemList/DowntimeListItemMinimal.php @@ -9,13 +9,4 @@ class DowntimeListItemMinimal extends BaseDowntimeListItem { use ListItemMinimalLayout; - - protected function init(): void - { - parent::init(); - - if ($this->list->isCaptionDisabled()) { - $this->setCaptionDisabled(); - } - } } diff --git a/library/Icingadb/Widget/ItemList/HistoryList.php b/library/Icingadb/Widget/ItemList/HistoryList.php index d3b623289..0af68769b 100644 --- a/library/Icingadb/Widget/ItemList/HistoryList.php +++ b/library/Icingadb/Widget/ItemList/HistoryList.php @@ -4,10 +4,8 @@ namespace Icinga\Module\Icingadb\Widget\ItemList; -use Icinga\Module\Icingadb\Common\CaptionDisabled; use Icinga\Module\Icingadb\Common\DetailActions; use Icinga\Module\Icingadb\Common\LoadMore; -use Icinga\Module\Icingadb\Common\NoSubjectLink; use Icinga\Module\Icingadb\Common\TicketLinks; use Icinga\Module\Icingadb\Common\ViewMode; use ipl\Orm\ResultSet; @@ -16,8 +14,6 @@ class HistoryList extends BaseItemList { - use CaptionDisabled; - use NoSubjectLink; use ViewMode; use LoadMore; use TicketLinks; diff --git a/library/Icingadb/Widget/ItemList/HistoryListItemMinimal.php b/library/Icingadb/Widget/ItemList/HistoryListItemMinimal.php index 5a7f21453..e737f1b79 100644 --- a/library/Icingadb/Widget/ItemList/HistoryListItemMinimal.php +++ b/library/Icingadb/Widget/ItemList/HistoryListItemMinimal.php @@ -11,15 +11,6 @@ class HistoryListItemMinimal extends BaseHistoryListItem { use ListItemMinimalLayout; - protected function init(): void - { - parent::init(); - - if ($this->list->isCaptionDisabled()) { - $this->setCaptionDisabled(); - } - } - protected function getStateBallSize(): string { return StateBall::SIZE_BIG; diff --git a/library/Icingadb/Widget/ItemList/HostList.php b/library/Icingadb/Widget/ItemList/HostList.php index 2be1f84a7..4a74b302b 100644 --- a/library/Icingadb/Widget/ItemList/HostList.php +++ b/library/Icingadb/Widget/ItemList/HostList.php @@ -23,8 +23,6 @@ protected function getItemClass(): string $this->removeAttribute('class', 'default-layout'); return HostListItemDetailed::class; - case 'objectHeader': - return HostDetailHeader::class; default: return HostListItem::class; } diff --git a/library/Icingadb/Widget/ItemList/HostListItemDetailed.php b/library/Icingadb/Widget/ItemList/HostListItemDetailed.php index 255bdcc19..bcc008732 100644 --- a/library/Icingadb/Widget/ItemList/HostListItemDetailed.php +++ b/library/Icingadb/Widget/ItemList/HostListItemDetailed.php @@ -6,7 +6,7 @@ use Icinga\Module\Icingadb\Common\ListItemDetailedLayout; use Icinga\Module\Icingadb\Util\PerfDataSet; -use Icinga\Module\Icingadb\Widget\ItemList\CommentList; +use Icinga\Module\Icingadb\Widget\Detail\CommentHeader; use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\HtmlElement; @@ -33,11 +33,7 @@ protected function assembleFooter(BaseHtmlElement $footer): void if ($this->item->state->last_comment->host_id === $this->item->id) { $comment = $this->item->state->last_comment; - $comment->host = $this->item; - $comment = (new CommentList([$comment])) - ->setNoSubjectLink() - ->setObjectLinkDisabled() - ->setDetailActionsDisabled(); + $comment = new CommentHeader($comment, false, true); $statusIcons->addHtml( new HtmlElement( diff --git a/library/Icingadb/Widget/ItemList/NotificationList.php b/library/Icingadb/Widget/ItemList/NotificationList.php index 3a16b0b31..d1874aa97 100644 --- a/library/Icingadb/Widget/ItemList/NotificationList.php +++ b/library/Icingadb/Widget/ItemList/NotificationList.php @@ -4,10 +4,8 @@ namespace Icinga\Module\Icingadb\Widget\ItemList; -use Icinga\Module\Icingadb\Common\CaptionDisabled; use Icinga\Module\Icingadb\Common\DetailActions; use Icinga\Module\Icingadb\Common\LoadMore; -use Icinga\Module\Icingadb\Common\NoSubjectLink; use Icinga\Module\Icingadb\Common\ViewMode; use ipl\Orm\ResultSet; use ipl\Web\Common\BaseItemList; @@ -15,8 +13,6 @@ class NotificationList extends BaseItemList { - use CaptionDisabled; - use NoSubjectLink; use ViewMode; use LoadMore; use DetailActions; diff --git a/library/Icingadb/Widget/ItemList/NotificationListItemMinimal.php b/library/Icingadb/Widget/ItemList/NotificationListItemMinimal.php index dd6d22612..dbf33dda6 100644 --- a/library/Icingadb/Widget/ItemList/NotificationListItemMinimal.php +++ b/library/Icingadb/Widget/ItemList/NotificationListItemMinimal.php @@ -11,15 +11,6 @@ class NotificationListItemMinimal extends BaseNotificationListItem { use ListItemMinimalLayout; - protected function init(): void - { - parent::init(); - - if ($this->list->isCaptionDisabled()) { - $this->setCaptionDisabled(); - } - } - protected function getStateBallSize(): string { return StateBall::SIZE_BIG; diff --git a/library/Icingadb/Widget/ItemList/RedundancyGroupListItem.php b/library/Icingadb/Widget/ItemList/RedundancyGroupListItem.php index eca3c9271..727658b82 100644 --- a/library/Icingadb/Widget/ItemList/RedundancyGroupListItem.php +++ b/library/Icingadb/Widget/ItemList/RedundancyGroupListItem.php @@ -8,17 +8,15 @@ use Icinga\Module\Icingadb\Common\Database; use Icinga\Module\Icingadb\Common\ListItemCommonLayout; use Icinga\Module\Icingadb\Model\RedundancyGroup; -use Icinga\Module\Icingadb\Model\RedundancyGroupSummary; use Icinga\Module\Icingadb\Model\RedundancyGroupState; -use Icinga\Module\Icingadb\Widget\DependencyNodeStatistics; -use ipl\Html\BaseHtmlElement; +use Icinga\Module\Icingadb\Model\RedundancyGroupSummary; +use Icinga\Module\Icingadb\Widget\Detail\RedundancyGroupHeaderUtils; +use ipl\Html\ValidHtml; use ipl\Stdlib\Filter; +use ipl\Web\Common\BaseListItem; use ipl\Web\Url; use ipl\Web\Widget\Link; use ipl\Web\Widget\StateBall; -use ipl\Html\HtmlElement; -use ipl\Html\Text; -use ipl\Web\Widget\TimeSince; /** * Redundancy group list item. Represents one database row. @@ -26,32 +24,36 @@ * @property RedundancyGroup $item * @property RedundancyGroupState $state */ -class RedundancyGroupListItem extends StateListItem +class RedundancyGroupListItem extends BaseListItem { use ListItemCommonLayout; use Database; use Auth; + use RedundancyGroupHeaderUtils; protected $defaultAttributes = ['class' => ['redundancy-group-list-item']]; protected function init(): void { - parent::init(); - $this->addAttributes(['data-action-item' => true]); } - protected function getStateBallSize(): string + protected function getObject(): RedundancyGroup { - return StateBall::SIZE_LARGE; + return $this->item; } - protected function createTimestamp(): BaseHtmlElement + protected function getSummary(): RedundancyGroupSummary { - return new TimeSince($this->state->last_state_change->getTimestamp()); + $summary = RedundancyGroupSummary::on($this->getDb()) + ->filter(Filter::equal('id', $this->item->id)); + + $this->applyRestrictions($summary); + + return $summary->first(); } - protected function createSubject(): Link + protected function createSubject(): ValidHtml { return new Link( $this->item->display_name, @@ -60,38 +62,15 @@ protected function createSubject(): Link ); } - protected function assembleVisual(BaseHtmlElement $visual): void - { - $visual->addHtml(new StateBall($this->state->getStateText(), $this->getStateBallSize())); - } - - protected function assembleCaption(BaseHtmlElement $caption): void - { - $summary = RedundancyGroupSummary::on($this->getDb()) - ->filter(Filter::equal('id', $this->item->id)); - - $this->applyRestrictions($summary); - - $caption->addHtml(new DependencyNodeStatistics($summary->first())); - } - - protected function assembleTitle(BaseHtmlElement $title): void + protected function getStateBallSize(): string { - $title->addHtml($this->createSubject()); - if ($this->state->failed) { - $text = $this->translate('has no working objects'); - } else { - $text = $this->translate('has working objects'); - } - - $title->addHtml(HtmlElement::create('span', null, Text::create($text))); + return StateBall::SIZE_LARGE; } protected function assemble(): void { $this->add([ $this->createVisual(), - $this->createIconImage(), $this->createMain() ]); } diff --git a/library/Icingadb/Widget/ItemList/ServiceList.php b/library/Icingadb/Widget/ItemList/ServiceList.php index 8d41a701b..83e76f3c3 100644 --- a/library/Icingadb/Widget/ItemList/ServiceList.php +++ b/library/Icingadb/Widget/ItemList/ServiceList.php @@ -20,8 +20,6 @@ protected function getItemClass(): string $this->removeAttribute('class', 'default-layout'); return ServiceListItemDetailed::class; - case 'objectHeader': - return ServiceDetailHeader::class; default: return ServiceListItem::class; } diff --git a/library/Icingadb/Widget/ItemList/ServiceListItemDetailed.php b/library/Icingadb/Widget/ItemList/ServiceListItemDetailed.php index 16135998b..a5101911a 100644 --- a/library/Icingadb/Widget/ItemList/ServiceListItemDetailed.php +++ b/library/Icingadb/Widget/ItemList/ServiceListItemDetailed.php @@ -6,6 +6,7 @@ use Icinga\Module\Icingadb\Common\ListItemDetailedLayout; use Icinga\Module\Icingadb\Util\PerfDataSet; +use Icinga\Module\Icingadb\Widget\Detail\CommentHeader; use Icinga\Module\Icingadb\Widget\ItemList\CommentList; use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; @@ -34,10 +35,7 @@ protected function assembleFooter(BaseHtmlElement $footer): void if ($this->item->state->last_comment->service_id === $this->item->id) { $comment = $this->item->state->last_comment; $comment->service = $this->item; - $comment = (new CommentList([$comment])) - ->setNoSubjectLink() - ->setObjectLinkDisabled() - ->setDetailActionsDisabled(); + $comment = new CommentHeader($comment, false, true); $statusIcons->addHtml( new HtmlElement( diff --git a/library/Icingadb/Widget/ItemList/StateList.php b/library/Icingadb/Widget/ItemList/StateList.php index 9671c9722..1f0b525ea 100644 --- a/library/Icingadb/Widget/ItemList/StateList.php +++ b/library/Icingadb/Widget/ItemList/StateList.php @@ -5,7 +5,6 @@ namespace Icinga\Module\Icingadb\Widget\ItemList; use Icinga\Module\Icingadb\Common\DetailActions; -use Icinga\Module\Icingadb\Common\NoSubjectLink; use Icinga\Module\Icingadb\Common\ViewMode; use Icinga\Module\Icingadb\Redis\VolatileStateResults; use Icinga\Module\Icingadb\Widget\Notice; @@ -15,7 +14,6 @@ abstract class StateList extends BaseItemList { use ViewMode; - use NoSubjectLink; use DetailActions; /** @var bool Whether the list contains at least one item with an icon_image */ diff --git a/library/Icingadb/Widget/ItemList/StateListItem.php b/library/Icingadb/Widget/ItemList/StateListItem.php index f8704d8a6..0f029225f 100644 --- a/library/Icingadb/Widget/ItemList/StateListItem.php +++ b/library/Icingadb/Widget/ItemList/StateListItem.php @@ -4,30 +4,17 @@ namespace Icinga\Module\Icingadb\Widget\ItemList; -use Icinga\Module\Icingadb\Common\Icons; use Icinga\Module\Icingadb\Model\State; -use Icinga\Module\Icingadb\Util\PluginOutput; -use Icinga\Module\Icingadb\Widget\CheckAttempt; -use Icinga\Module\Icingadb\Widget\IconImage; -use Icinga\Module\Icingadb\Widget\PluginOutputContainer; -use ipl\Html\Attributes; -use ipl\Html\HtmlElement; -use ipl\I18n\Translation; +use Icinga\Module\Icingadb\Widget\Detail\HostAndServiceHeaderUtils; +use ipl\Orm\Model; use ipl\Web\Common\BaseListItem; -use ipl\Web\Widget\EmptyState; -use ipl\Web\Widget\TimeSince; -use ipl\Html\BaseHtmlElement; -use ipl\Html\Html; -use ipl\Html\Text; -use ipl\Web\Widget\Icon; -use ipl\Web\Widget\StateBall; /** * Host or service item of a host or service list. Represents one database row. */ abstract class StateListItem extends BaseListItem { - use Translation; + use HostAndServiceHeaderUtils; /** @var StateList The list where the item is part of */ protected $list; @@ -44,119 +31,14 @@ protected function init(): void } } - abstract protected function createSubject(); - - abstract protected function getStateBallSize(): string; - - /** - * @return ?BaseHtmlElement - */ - protected function createIconImage(): ?BaseHtmlElement - { - if (! $this->list->hasIconImages()) { - return null; - } - - $iconImage = HtmlElement::create('div', [ - 'class' => 'icon-image', - ]); - - $this->assembleIconImage($iconImage); - - return $iconImage; - } - - protected function assembleCaption(BaseHtmlElement $caption): void - { - if ($this->state->soft_state === null && $this->state->output === null) { - $caption->addHtml(Text::create($this->translate('Waiting for Icinga DB to synchronize the state.'))); - } else { - if (empty($this->state->output)) { - $pluginOutput = new EmptyState($this->translate('Output unavailable.')); - } else { - $pluginOutput = new PluginOutputContainer(PluginOutput::fromObject($this->item)); - } - - $caption->addHtml($pluginOutput); - } - } - - protected function assembleIconImage(BaseHtmlElement $iconImage): void - { - if (isset($this->item->icon_image->icon_image)) { - $iconImage->addHtml(new IconImage($this->item->icon_image->icon_image, $this->item->icon_image_alt)); - } else { - $iconImage->addAttributes(['class' => 'placeholder']); - } - } - - protected function assembleTitle(BaseHtmlElement $title): void + protected function getObject(): Model { - $title->addHtml(Html::sprintf( - $this->translate('%s is %s', ' is '), - $this->createSubject(), - Html::tag('span', ['class' => 'state-text'], $this->state->getStateTextTranslated()) - )); - - if ($this->state->affects_children) { - $total = (int) $this->item->affected_children; - - if ($total > 1000) { - $total = '1000+'; - $tooltip = $this->translate('Up to 1000+ affected objects'); - } else { - $tooltip = sprintf( - $this->translatePlural( - '%d affected object', - 'Up to %d affected objects', - $total - ), - $total - ); - } - - $icon = new Icon(Icons::UNREACHABLE); - - $title->addHtml(new HtmlElement( - 'span', - Attributes::create([ - 'class' => 'affected-objects', - 'title' => $tooltip - ]), - $icon, - Text::create($total) - )); - } + return $this->item; } - protected function assembleVisual(BaseHtmlElement $visual): void + protected function wantIconImage(): bool { - $stateBall = new StateBall($this->state->getStateText(), $this->getStateBallSize()); - $stateBall->add($this->state->getIcon()); - if ($this->state->is_problem && ($this->state->is_handled || ! $this->state->is_reachable)) { - $stateBall->getAttributes()->add('class', 'handled'); - } - - $visual->addHtml($stateBall); - if ($this->state->state_type === 'soft') { - $visual->addHtml( - new CheckAttempt((int) $this->state->check_attempt, (int) $this->item->max_check_attempts) - ); - } - } - - protected function createTimestamp(): ?BaseHtmlElement - { - $since = null; - if ($this->state->is_overdue) { - $since = new TimeSince($this->state->next_update->getTimestamp()); - $since->prepend($this->translate('Overdue') . ' '); - $since->prependHtml(new Icon(Icons::WARNING)); - } elseif ($this->state->last_state_change !== null && $this->state->last_state_change->getTimestamp() > 0) { - $since = new TimeSince($this->state->last_state_change->getTimestamp()); - } - - return $since; + return $this->list->hasIconImages(); } protected function assemble(): void diff --git a/library/Icingadb/Widget/ItemTable/BaseHostGroupItem.php b/library/Icingadb/Widget/ItemTable/BaseHostGroupItem.php index aa581eed3..7a352746a 100644 --- a/library/Icingadb/Widget/ItemTable/BaseHostGroupItem.php +++ b/library/Icingadb/Widget/ItemTable/BaseHostGroupItem.php @@ -6,7 +6,6 @@ use Icinga\Module\Icingadb\Common\Links; use Icinga\Module\Icingadb\Model\Hostgroupsummary; -use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\HtmlElement; use ipl\Html\Text; @@ -27,37 +26,27 @@ abstract class BaseHostGroupItem extends BaseTableRowItem protected function init(): void { - if (isset($this->table)) { - $this->table->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); - } + $this->table->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); } protected function createSubject(): BaseHtmlElement { - if (isset($this->table)) { - $link = new Link( - $this->item->display_name, - Links::hostgroup($this->item), - [ - 'class' => 'subject', - 'title' => sprintf( - $this->translate('List all hosts in the group "%s"'), - $this->item->display_name - ) - ] - ); - if ($this->table->hasBaseFilter()) { - $link->getUrl()->setFilter($this->table->getBaseFilter()); - } - - return $link; + $link = new Link( + $this->item->display_name, + Links::hostgroup($this->item), + [ + 'class' => 'subject', + 'title' => sprintf( + $this->translate('List all hosts in the group "%s"'), + $this->item->display_name + ) + ] + ); + if ($this->table->hasBaseFilter()) { + $link->getUrl()->setFilter($this->table->getBaseFilter()); } - return new HtmlElement( - 'span', - Attributes::create(['class' => 'subject']), - Text::create($this->item->display_name) - ); + return $link; } protected function createCaption(): BaseHtmlElement diff --git a/library/Icingadb/Widget/ItemTable/BaseServiceGroupItem.php b/library/Icingadb/Widget/ItemTable/BaseServiceGroupItem.php index 4719bb9e5..f8a50cb2e 100644 --- a/library/Icingadb/Widget/ItemTable/BaseServiceGroupItem.php +++ b/library/Icingadb/Widget/ItemTable/BaseServiceGroupItem.php @@ -6,7 +6,6 @@ use Icinga\Module\Icingadb\Common\Links; use Icinga\Module\Icingadb\Model\ServicegroupSummary; -use ipl\Html\Attributes; use ipl\Html\BaseHtmlElement; use ipl\Html\HtmlElement; use ipl\Html\Text; @@ -27,37 +26,27 @@ abstract class BaseServiceGroupItem extends BaseTableRowItem protected function init(): void { - if (isset($this->table)) { - $this->table->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); - } + $this->table->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); } protected function createSubject(): BaseHtmlElement { - if (isset($this->table)) { - $link = new Link( - $this->item->display_name, - Links::servicegroup($this->item), - [ - 'class' => 'subject', - 'title' => sprintf( - $this->translate('List all services in the group "%s"'), - $this->item->display_name - ) - ] - ); - if ($this->table->hasBaseFilter()) { - $link->getUrl()->setFilter($this->table->getBaseFilter()); - } - - return $link; + $link = new Link( + $this->item->display_name, + Links::servicegroup($this->item), + [ + 'class' => 'subject', + 'title' => sprintf( + $this->translate('List all services in the group "%s"'), + $this->item->display_name + ) + ] + ); + if ($this->table->hasBaseFilter()) { + $link->getUrl()->setFilter($this->table->getBaseFilter()); } - return new HtmlElement( - 'span', - Attributes::create(['class' => 'subject']), - Text::create($this->item->display_name) - ); + return $link; } protected function createCaption(): BaseHtmlElement diff --git a/library/Icingadb/Widget/ItemTable/HostgroupTableRow.php b/library/Icingadb/Widget/ItemTable/HostgroupTableRow.php index cb3f06a9a..5094f132f 100644 --- a/library/Icingadb/Widget/ItemTable/HostgroupTableRow.php +++ b/library/Icingadb/Widget/ItemTable/HostgroupTableRow.php @@ -32,7 +32,7 @@ protected function createStatistics(): array $hostStats = new HostStatistics($this->item); $hostStats->setBaseFilter(Filter::equal('hostgroup.name', $this->item->name)); - if (isset($this->table) && $this->table->hasBaseFilter()) { + if ($this->table->hasBaseFilter()) { $hostStats->setBaseFilter( Filter::all($hostStats->getBaseFilter(), $this->table->getBaseFilter()) ); @@ -41,7 +41,7 @@ protected function createStatistics(): array $serviceStats = new ServiceStatistics($this->item); $serviceStats->setBaseFilter(Filter::equal('hostgroup.name', $this->item->name)); - if (isset($this->table) && $this->table->hasBaseFilter()) { + if ($this->table->hasBaseFilter()) { $serviceStats->setBaseFilter( Filter::all($serviceStats->getBaseFilter(), $this->table->getBaseFilter()) ); diff --git a/library/Icingadb/Widget/ItemTable/ServicegroupTableRow.php b/library/Icingadb/Widget/ItemTable/ServicegroupTableRow.php index e34c029ff..24abf7ec6 100644 --- a/library/Icingadb/Widget/ItemTable/ServicegroupTableRow.php +++ b/library/Icingadb/Widget/ItemTable/ServicegroupTableRow.php @@ -31,7 +31,7 @@ protected function createStatistics(): array $serviceStats = new ServiceStatistics($this->item); $serviceStats->setBaseFilter(Filter::equal('servicegroup.name', $this->item->name)); - if (isset($this->table) && $this->table->hasBaseFilter()) { + if ($this->table->hasBaseFilter()) { $serviceStats->setBaseFilter( Filter::all($serviceStats->getBaseFilter(), $this->table->getBaseFilter()) ); diff --git a/library/Icingadb/Widget/ItemTable/UserTableRow.php b/library/Icingadb/Widget/ItemTable/UserTableRow.php index c10851e14..b824d62ed 100644 --- a/library/Icingadb/Widget/ItemTable/UserTableRow.php +++ b/library/Icingadb/Widget/ItemTable/UserTableRow.php @@ -27,9 +27,7 @@ class UserTableRow extends BaseTableRowItem protected function init(): void { - if (isset($this->table)) { - $this->table->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); - } + $this->table->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); } protected function assembleVisual(BaseHtmlElement $visual): void @@ -44,13 +42,7 @@ protected function assembleVisual(BaseHtmlElement $visual): void protected function assembleTitle(BaseHtmlElement $title): void { $title->addHtml( - isset($this->table) - ? new Link($this->item->display_name, Links::user($this->item), ['class' => 'subject']) - : new HtmlElement( - 'span', - Attributes::create(['class' => 'subject']), - Text::create($this->item->display_name) - ), + new Link($this->item->display_name, Links::user($this->item), ['class' => 'subject']), new HtmlElement('span', null, Text::create($this->item->name)) ); } diff --git a/library/Icingadb/Widget/ItemTable/UsergroupTableRow.php b/library/Icingadb/Widget/ItemTable/UsergroupTableRow.php index c3cbf74e2..97dd1b595 100644 --- a/library/Icingadb/Widget/ItemTable/UsergroupTableRow.php +++ b/library/Icingadb/Widget/ItemTable/UsergroupTableRow.php @@ -27,9 +27,7 @@ class UsergroupTableRow extends BaseTableRowItem protected function init(): void { - if (isset($this->table)) { - $this->table->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); - } + $this->table->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); } protected function assembleVisual(BaseHtmlElement $visual): void @@ -44,13 +42,7 @@ protected function assembleVisual(BaseHtmlElement $visual): void protected function assembleTitle(BaseHtmlElement $title): void { $title->addHtml( - isset($this->table) - ? new Link($this->item->display_name, Links::usergroup($this->item), ['class' => 'subject']) - : new HtmlElement( - 'span', - Attributes::create(['class' => 'subject']), - Text::create($this->item->display_name) - ), + new Link($this->item->display_name, Links::usergroup($this->item), ['class' => 'subject']), new HtmlElement('span', null, Text::create($this->item->name)) ); } diff --git a/public/css/common.less b/public/css/common.less index 3a28d03b0..f5d5d8cbf 100644 --- a/public/css/common.less +++ b/public/css/common.less @@ -109,25 +109,6 @@ div.show-more { margin-bottom: 0; } - .item-list { - width: 100%; - - .list-item .main { - border-top: none; - } - - .list-item .visual { - width: auto; - margin-top: 0; - } - - .list-item .visual, - .list-item .main { - padding-bottom: .25em; - padding-top: .25em; - } - } - .search-controls .continue-with { margin-right: -.5em; margin-left: .5em; @@ -347,51 +328,22 @@ div.show-more { } } -.controls .hostgroup-table-row, -.controls .servicegroup-table-row, -.controls .usergroup-table-row, -.controls .user-table-row { - .title .column-content { - display: inline-flex; - align-items: center; - - > :first-child { - flex: 0 1 auto; - } - - > :last-child { - flex: 1 1 auto; - } - - .subject { - margin-right: .5em; - } - } - - .vertical-key-value { - br { - display: none; - } - - .key { - padding-left: .417em; - } - - .value { - vertical-align: middle; - } - } -} - -.history-list, -.objectHeader { +.history-list { .visual.small-state-change .state-change { padding-top: .25em; } } -.comment-popup { - .comment-list .main { +.comment-popup .comment-header { + padding-left: 1em; + padding-right: 1em; + + .user-ball { + height: 2em; + width: 2em; + } + + .main { // This is necessary to limit the visible comment lines // because the popup is shown in detailed list mode only .caption { diff --git a/public/css/list/item-list.less b/public/css/list/item-list.less index 251eec312..8577b7184 100644 --- a/public/css/list/item-list.less +++ b/public/css/list/item-list.less @@ -138,17 +138,3 @@ } } } - -.controls .item-list:not(.detailed):not(.minimal) .list-item { - .plugin-output { - line-height: 1.5 - } - - .caption { - height: 2.5em; - } -} - -.controls .item-list.minimal .icon-image { - margin-top: 0; -} diff --git a/public/css/list/user-list.less b/public/css/list/user-list.less deleted file mode 100644 index 078d1b94c..000000000 --- a/public/css/list/user-list.less +++ /dev/null @@ -1,26 +0,0 @@ -// Layout - -.controls .user-list, -.controls .usergroup-list { - .usergroup-ball, - .user-ball { - height: 1.5em; - width: 1.5em; - line-height: ~"calc(1.5em - 4px)"; - display: block; - } - - .title br { - display: none; - } - - .list-item { - & > .visual { - padding-top: 0.25em; - } - - & > .col { - padding: .25em 0; - } - } -} diff --git a/public/css/widget/object-header.less b/public/css/widget/object-header.less index 9c6be51a8..aecbe0223 100644 --- a/public/css/widget/object-header.less +++ b/public/css/widget/object-header.less @@ -4,20 +4,43 @@ .visual { display: flex; - padding: 0.5em 0; align-items: center; + flex-direction: column; + + margin-right: .5em; + } + + .visual, + .main { + padding-bottom: .25em; + padding-top: .25em; + } + + .icon-image { + height: 2em; + width: 2em; + line-height: 2; + text-align: center; + margin-right: .5em; + overflow: hidden; + + img { + max-height: 100%; + max-width: 100%; + height: auto; + width: auto; + } } .main { flex: 1 1 auto; - padding: 0.5em 0; + padding: 0.25em 0; width: 0; - margin-left: .5em; header { display: flex; + align-items: flex-start; justify-content: space-between; - align-items: center; .title { display: inline-flex; @@ -27,6 +50,14 @@ & > * { margin: 0 .28125em; // 0 calculated   width + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } } .subject { @@ -34,24 +65,176 @@ } } + .caption { + flex: 1 1 auto; + height: 1.5em; + margin-right: 1em; + width: 0; + + .text-ellipsis(); + + .plugin-output { + font-size: 11/12em; + line-height: 1.5*12/11em; + .text-ellipsis(); + } + + .object-statistics { + justify-content: end; + font-size: 75%; + } + } + time { white-space: nowrap; } + + &.overdue time { + margin-right: -.5em; + padding: 0 0.5em; + } + + :last-child { + margin-right: 0; + } } } } .object-header { + &.overdue { + background-color: @gray-lighter; + } + + &.overdue header > *:not(time), + &.overdue .caption { + opacity: 0.6; + } + + &.overdue time { + .rounded-corners(); + background-color: @color-critical; + color: @text-color-on-icinga-blue; + } + + .title { + .state-text { + color: @text-color; + text-transform: uppercase; + } + } + color: @default-text-color-light; .title { .subject { color: @default-text-color; } + + a { + color: @default-text-color; + font-weight: bold; + + &:hover { + color: @list-item-title-hover-color; + text-decoration: none; + + .subject { + color: @list-item-title-hover-color; + } + } + } + } +} + +.user-header, +.user-group-header { + .usergroup-ball, + .user-ball { + height: 1.5em; + width: 1.5em; + line-height: ~"calc(1.5em - 4px)"; + display: block; + } +} + +.hostgroup-header, +.servicegroup-header { + .main > header { + .caption { + display: flex; + justify-content: end; + gap: 1em; + } + + .vertical-key-value { + br { + display: none; + } + + .key { + padding-left: .417em; + } + + .value { + vertical-align: middle; + } + } + } +} + +.comment-header { + .user-ball { + font-size: .857em; + height: 1.75em; + line-height: 1.5em; + width: 1.75em; } - .object-statistics { - margin-left: auto; - margin-right: 1em; + .title > i:first-child { + margin-right: 0; } + + .title > .subject + .badge, + .title > .badge + .subject, + .title > .badge:last-of-type { + margin-left: 0; + } + + .title a { + &:not(.subject) { + .text-ellipsis(); + } + } + + .title .subject:not(:last-child) { + margin-left: 0; + } + + .title .subject:nth-child(3):last-child { + margin-left: 0; + } +} + +.downtime-header { + .visual { + width: 8em; + background-color: @gray-lighter; + flex-direction: row; + justify-content: center; + gap: 0.28125em; // 0 calculated   width + + margin-right: 1em; //TODO: to make bigger gap as before + } + + &.in-effect { + .visual { + background-color: @color-ok; + color: @text-color-on-icinga-blue; + } + } +} + +.event-header .check-attempt { + display: none; }