diff options
Diffstat (limited to 'library/Icingadb/Widget/ItemList')
43 files changed, 2655 insertions, 0 deletions
diff --git a/library/Icingadb/Widget/ItemList/BaseCommentListItem.php b/library/Icingadb/Widget/ItemList/BaseCommentListItem.php new file mode 100644 index 0000000..5fbd033 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/BaseCommentListItem.php @@ -0,0 +1,131 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +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\NoSubjectLink; +use Icinga\Module\Icingadb\Common\ObjectLinkDisabled; +use Icinga\Module\Icingadb\Common\ServiceLink; +use Icinga\Module\Icingadb\Model\Comment; +use Icinga\Module\Icingadb\Common\BaseListItem; +use ipl\Html\FormattedString; +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. + * + * @property Comment $item + * @property CommentList $list + */ +abstract class BaseCommentListItem extends BaseListItem +{ + use HostLink; + use ServiceLink; + use NoSubjectLink; + use ObjectLinkDisabled; + use TicketLinks; + + protected function assembleCaption(BaseHtmlElement $caption) + { + $markdownLine = new MarkdownLine($this->createTicketLinks($this->item->text)); + + $caption->getAttributes()->add($markdownLine->getAttributes()); + $caption->addFrom($markdownLine); + } + + protected function assembleTitle(BaseHtmlElement $title) + { + $isAck = $this->item->entry_type === 'ack'; + $expires = $this->item->expire_time; + + $subjectText = sprintf( + $isAck ? t('%s acknowledged', '<username>..') : t('%s commented', '<username>..'), + $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 != 0) { + $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) + { + $visual->addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => 'user-ball']), + Text::create($this->item->author[0]) + )); + } + + protected function createTimestamp() + { + if ($this->item->expire_time) { + return Html::tag( + 'span', + FormattedString::create(t("expires %s"), new TimeUntil($this->item->expire_time)) + ); + } + + return Html::tag( + 'span', + FormattedString::create(t("created %s"), new TimeAgo($this->item->entry_time)) + ); + } + + protected function init() + { + $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 new file mode 100644 index 0000000..04e8e1b --- /dev/null +++ b/library/Icingadb/Widget/ItemList/BaseDowntimeListItem.php @@ -0,0 +1,217 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Date\DateFormatter; +use Icinga\Module\Icingadb\Common\BaseListItem; +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 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\Widget\Icon; +use ipl\Web\Widget\Link; + +/** + * Downtime item of a downtime list. Represents one database row. + * + * @property Downtime $item + * @property DowntimeList $list + */ +abstract class BaseDowntimeListItem extends BaseListItem +{ + use HostLink; + use ServiceLink; + use NoSubjectLink; + use ObjectLinkDisabled; + use TicketLinks; + + /** @var int Current Time */ + protected $currentTime; + + /** @var int Duration */ + protected $duration; + + /** @var int Downtime end time */ + protected $endTime; + + /** @var bool Whether the downtime is active */ + protected $isActive; + + /** @var int Downtime start time */ + protected $startTime; + + protected function init() + { + if ($this->item->is_flexible && $this->item->is_in_effect) { + $this->startTime = $this->item->start_time; + $this->endTime = $this->item->end_time; + } else { + $this->startTime = $this->item->scheduled_start_time; + $this->endTime = $this->item->scheduled_end_time; + } + + $this->currentTime = time(); + + $this->isActive = $this->item->is_in_effect + || $this->item->is_flexible && $this->item->scheduled_start_time <= $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 + { + $ref = floor( + (float) ($this->currentTime - $this->startTime) + / (float) ($this->endTime - $this->startTime) + * 100 + ); + + $progress = Html::tag( + 'div', + ['class' => 'progress'], + Html::tag( + 'div', + [ + 'class' => 'progress-bar', + 'style' => sprintf('width: %F%%', $ref) + ] + ) + ); + + return $progress; + } + + protected function assembleCaption(BaseHtmlElement $caption) + { + $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) + { + 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) + { + $dateTime = DateFormatter::formatDateTime($this->endTime); + + if ($this->isActive) { + $visual->addHtml(Html::sprintf( + t('%s left', '<timespan>..'), + Html::tag( + 'strong', + Html::tag( + 'time', + [ + 'datetime' => $dateTime, + 'title' => $dateTime + ], + $this->duration + ) + ) + )); + } else { + $visual->addHtml(Html::sprintf( + t('in %s', '..<timespan>'), + Html::tag('strong', $this->duration) + )); + } + } + + protected function createTimestamp() + { + $dateTime = DateFormatter::formatDateTime($this->endTime); + + return Html::tag( + 'time', + [ + 'datetime' => $dateTime, + 'title' => $dateTime + ], + sprintf( + $this->isActive + ? t('expires in %s', '..<timespan>') + : t('starts in %s', '..<timespan>'), + $this->duration + ) + ); + } +} diff --git a/library/Icingadb/Widget/ItemList/BaseHistoryListItem.php b/library/Icingadb/Widget/ItemList/BaseHistoryListItem.php new file mode 100644 index 0000000..f7408d7 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/BaseHistoryListItem.php @@ -0,0 +1,404 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +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\TicketLinks; +use Icinga\Module\Icingadb\Widget\EmptyState; +use Icinga\Module\Icingadb\Widget\MarkdownLine; +use Icinga\Module\Icingadb\Common\NoSubjectLink; +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\Common\BaseListItem; +use Icinga\Module\Icingadb\Widget\CheckAttempt; +use Icinga\Module\Icingadb\Widget\PluginOutputContainer; +use Icinga\Module\Icingadb\Widget\StateChange; +use ipl\Stdlib\Filter; +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; + + /** @var History */ + protected $item; + + /** @var HistoryList */ + protected $list; + + protected function init() + { + $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) + { + 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, + DateFormatter::formatDuration( + $this->item->flapping->end_time - $this->item->flapping->start_time + ) + )) + ->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) + { + 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) + { + 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', '..<username>'), + $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', '..<username>'), + $this->item->downtime->cancelled_by + ); + } else { + $subjectLabel = t('Downtime cancelled by author'); + } + } elseif (isset($this->item->downtime->cancel_time)) { + $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', '..<username>'), + $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 = sprintf( + NotificationListItem::phraseForType($this->item->notification->type), + ucfirst($this->item->object_type) + ); + + break; + case 'state_change': + $state = $this->item->state === '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); + } + } + + protected function createTimestamp() + { + return new TimeAgo($this->item->event_time); + } +} diff --git a/library/Icingadb/Widget/ItemList/BaseHostListItem.php b/library/Icingadb/Widget/ItemList/BaseHostListItem.php new file mode 100644 index 0000000..99a8c63 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/BaseHostListItem.php @@ -0,0 +1,56 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +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\Stdlib\Filter; +use ipl\Web\Widget\Link; + +/** + * Host item of a host list. Represents one database row. + * + * @property Host $item + * @property HostList $list + */ +abstract class BaseHostListItem extends StateListItem +{ + use NoSubjectLink; + + /** + * Create new subject link + * + * @return BaseHtmlElement + */ + protected function createSubject() + { + 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']); + } + } + + protected function init() + { + 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 new file mode 100644 index 0000000..962fe3e --- /dev/null +++ b/library/Icingadb/Widget/ItemList/BaseNotificationListItem.php @@ -0,0 +1,189 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +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\ServiceLink; +use Icinga\Module\Icingadb\Common\ServiceStates; +use Icinga\Module\Icingadb\Util\PluginOutput; +use Icinga\Module\Icingadb\Common\BaseListItem; +use Icinga\Module\Icingadb\Widget\EmptyState; +use Icinga\Module\Icingadb\Widget\PluginOutputContainer; +use Icinga\Module\Icingadb\Widget\StateChange; +use ipl\Stdlib\Filter; +use ipl\Web\Widget\TimeAgo; +use InvalidArgumentException; +use ipl\Html\BaseHtmlElement; +use ipl\Html\HtmlElement; +use ipl\Html\Text; +use ipl\Web\Widget\Icon; +use ipl\Web\Widget\Link; + +abstract class BaseNotificationListItem extends BaseListItem +{ + use HostLink; + use NoSubjectLink; + use ServiceLink; + + /** @var NotificationList */ + protected $list; + + protected function init() + { + $this->setNoSubjectLink($this->list->getNoSubjectLink()); + $this->list->addDetailFilterAttribute($this, Filter::equal('id', bin2hex($this->item->history->id))); + } + + /** + * Get a localized phrase for the given notification type + * + * @param string $type + * + * @return string + */ + public static function phraseForType(string $type): string + { + switch ($type) { + case 'acknowledgement': + return t('Problem acknowledged'); + case 'custom': + return t('Custom Notification triggered'); + case 'downtime_end': + return t('Downtime ended'); + case 'downtime_removed': + return t('Downtime removed'); + case 'downtime_start': + return t('Downtime started'); + case 'flapping_end': + return t('Flapping stopped'); + case 'flapping_start': + return t('Flapping started'); + case 'problem': + return t('%s ran into a problem'); + case 'recovery': + return t('%s recovered'); + default: + throw new InvalidArgumentException(sprintf('Type %s is not a valid notification type', $type)); + } + } + + abstract protected function getStateBallSize(); + + protected function assembleCaption(BaseHtmlElement $caption) + { + if (in_array($this->item->type, ['flapping_end', 'flapping_start', 'problem', 'recovery'])) { + $commandName = $this->item->object_type === 'host' + ? $this->item->host->checkcommand_name + : $this->item->service->checkcommand_name; + if (isset($commandName)) { + if (empty($this->item->text)) { + $caption->addHtml(new EmptyState(t('Output unavailable.'))); + } else { + $caption->addHtml(new PluginOutputContainer( + (new PluginOutput($this->item->text)) + ->setCommandName($commandName) + )); + } + } else { + $caption->addHtml(new EmptyState(t('Waiting for Icinga DB to synchronize the config.'))); + } + } else { + $caption->add([ + new Icon(Icons::USER), + $this->item->author, + ': ', + $this->item->text + ]); + } + } + + protected function assembleVisual(BaseHtmlElement $visual) + { + switch ($this->item->type) { + case 'acknowledgement': + $visual->addHtml(HtmlElement::create( + 'div', + ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], + new Icon(Icons::IS_ACKNOWLEDGED) + )); + + break; + case 'custom': + $visual->addHtml(HtmlElement::create( + 'div', + ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], + new Icon(Icons::NOTIFICATION) + )); + + break; + case 'downtime_end': + case 'downtime_removed': + case 'downtime_start': + $visual->addHtml(HtmlElement::create( + 'div', + ['class' => ['icon-ball', 'ball-size-' . $this->getStateBallSize()]], + new Icon(Icons::IN_DOWNTIME) + )); + + 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 'problem': + case 'recovery': + if ($this->item->object_type === 'host') { + $state = HostStates::text($this->item->state); + $previousHardState = HostStates::text($this->item->previous_hard_state); + } else { + $state = ServiceStates::text($this->item->state); + $previousHardState = ServiceStates::text($this->item->previous_hard_state); + } + + $visual->addHtml(new StateChange($state, $previousHardState)); + + break; + } + } + + protected function assembleTitle(BaseHtmlElement $title) + { + 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'] + )); + } + + if ($this->item->object_type === 'host') { + $link = $this->createHostLink($this->item->host, true); + } else { + $link = $this->createServiceLink($this->item->service, $this->item->host, true); + } + + $title->addHtml(Text::create(' '), $link); + } + + protected function createTimestamp() + { + return new TimeAgo($this->item->send_time); + } +} diff --git a/library/Icingadb/Widget/ItemList/BaseServiceListItem.php b/library/Icingadb/Widget/ItemList/BaseServiceListItem.php new file mode 100644 index 0000000..208e496 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/BaseServiceListItem.php @@ -0,0 +1,70 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +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\Stdlib\Filter; +use ipl\Web\Widget\Link; +use ipl\Web\Widget\StateBall; + +/** + * Service item of a service list. Represents one database row. + * + * @property Service $item + * @property ServiceList $list + */ +abstract class BaseServiceListItem extends StateListItem +{ + use NoSubjectLink; + + protected function createSubject() + { + $service = $this->item->display_name; + $host = [ + new StateBall($this->item->host->state->getStateText(), StateBall::SIZE_MEDIUM), + ' ', + $this->item->host->display_name + ]; + + $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']); + } + + return [Html::sprintf(t('%s on %s', '<service> on <host>'), $service, $host)]; + } + + protected function init() + { + parent::init(); + + if ($this->list->getNoSubjectLink()) { + $this->setNoSubjectLink(); + } + + $this->list->addMultiselectFilterAttribute( + $this, + Filter::all( + Filter::equal('service.name', $this->item->name), + Filter::equal('host.name', $this->item->host->name) + ) + ); + $this->list->addDetailFilterAttribute( + $this, + Filter::all( + Filter::equal('name', $this->item->name), + Filter::equal('host.name', $this->item->host->name) + ) + ); + } +} diff --git a/library/Icingadb/Widget/ItemList/CommandTransportList.php b/library/Icingadb/Widget/ItemList/CommandTransportList.php new file mode 100644 index 0000000..50ae06d --- /dev/null +++ b/library/Icingadb/Widget/ItemList/CommandTransportList.php @@ -0,0 +1,22 @@ +<?php + +/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\BaseOrderedItemList; +use ipl\Web\Url; + +class CommandTransportList extends BaseOrderedItemList +{ + protected function init() + { + $this->getAttributes()->add('class', 'command-transport-list'); + $this->setDetailUrl(Url::fromPath('icingadb/command-transport/show')); + } + + protected function getItemClass(): string + { + return CommandTransportListItem::class; + } +} diff --git a/library/Icingadb/Widget/ItemList/CommandTransportListItem.php b/library/Icingadb/Widget/ItemList/CommandTransportListItem.php new file mode 100644 index 0000000..c15e1bc --- /dev/null +++ b/library/Icingadb/Widget/ItemList/CommandTransportListItem.php @@ -0,0 +1,70 @@ +<?php + +/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\BaseOrderedListItem; +use ipl\Html\BaseHtmlElement; +use ipl\Html\HtmlElement; +use ipl\Html\Text; +use ipl\Stdlib\Filter; +use ipl\Web\Url; +use ipl\Web\Widget\Icon; +use ipl\Web\Widget\Link; + +class CommandTransportListItem extends BaseOrderedListItem +{ + protected function init() + { + $this->list->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); + } + + protected function assembleHeader(BaseHtmlElement $header) + { + } + + protected function assembleMain(BaseHtmlElement $main) + { + $main->addHtml(new Link( + new HtmlElement('strong', null, Text::create($this->item->name)), + Url::fromPath('icingadb/command-transport/show', ['name' => $this->item->name]) + )); + + $main->addHtml(new Link( + new Icon('trash', ['title' => sprintf(t('Remove command transport "%s"'), $this->item->name)]), + Url::fromPath('icingadb/command-transport/remove', ['name' => $this->item->name]), + [ + 'class' => 'pull-right action-link', + 'data-icinga-modal' => true, + 'data-no-icinga-ajax' => true + ] + )); + + if ($this->getOrder() + 1 < $this->list->count()) { + $main->addHtml((new Link( + new Icon('arrow-down'), + Url::fromPath('icingadb/command-transport/sort', [ + 'name' => $this->item->name, + 'pos' => $this->getOrder() + 1 + ]), + ['class' => 'pull-right action-link'] + ))->setBaseTarget('_self')); + } + + if ($this->getOrder() > 0) { + $main->addHtml((new Link( + new Icon('arrow-up'), + Url::fromPath('icingadb/command-transport/sort', [ + 'name' => $this->item->name, + 'pos' => $this->getOrder() - 1 + ]), + ['class' => 'pull-right action-link'] + ))->setBaseTarget('_self')); + } + } + + protected function createVisual() + { + } +} diff --git a/library/Icingadb/Widget/ItemList/CommentList.php b/library/Icingadb/Widget/ItemList/CommentList.php new file mode 100644 index 0000000..db164ff --- /dev/null +++ b/library/Icingadb/Widget/ItemList/CommentList.php @@ -0,0 +1,44 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\CaptionDisabled; +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; +use Icinga\Module\Icingadb\Common\BaseItemList; +use ipl\Web\Url; + +class CommentList extends BaseItemList +{ + use CaptionDisabled; + use NoSubjectLink; + use ObjectLinkDisabled; + use ViewMode; + use TicketLinks; + + protected $defaultAttributes = ['class' => 'comment-list']; + + protected function getItemClass(): string + { + $viewMode = $this->getViewMode(); + + $this->addAttributes(['class' => $viewMode]); + + if ($viewMode === 'minimal') { + return CommentListItemMinimal::class; + } + + return CommentListItem::class; + } + + protected function init() + { + $this->setMultiselectUrl(Links::commentsDetails()); + $this->setDetailUrl(Url::fromPath('icingadb/comment')); + } +} diff --git a/library/Icingadb/Widget/ItemList/CommentListItem.php b/library/Icingadb/Widget/ItemList/CommentListItem.php new file mode 100644 index 0000000..3bbd0c2 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/CommentListItem.php @@ -0,0 +1,12 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemCommonLayout; + +class CommentListItem extends BaseCommentListItem +{ + use ListItemCommonLayout; +} diff --git a/library/Icingadb/Widget/ItemList/CommentListItemMinimal.php b/library/Icingadb/Widget/ItemList/CommentListItemMinimal.php new file mode 100644 index 0000000..fc204b3 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/CommentListItemMinimal.php @@ -0,0 +1,21 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemMinimalLayout; + +class CommentListItemMinimal extends BaseCommentListItem +{ + use ListItemMinimalLayout; + + protected function init() + { + parent::init(); + + if ($this->list->isCaptionDisabled()) { + $this->setCaptionDisabled(); + } + } +} diff --git a/library/Icingadb/Widget/ItemList/DowntimeList.php b/library/Icingadb/Widget/ItemList/DowntimeList.php new file mode 100644 index 0000000..964fd20 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/DowntimeList.php @@ -0,0 +1,44 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\BaseItemList; +use Icinga\Module\Icingadb\Common\CaptionDisabled; +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; +use ipl\Web\Url; + +class DowntimeList extends BaseItemList +{ + use CaptionDisabled; + use NoSubjectLink; + use ObjectLinkDisabled; + use ViewMode; + use TicketLinks; + + protected $defaultAttributes = ['class' => 'downtime-list']; + + protected function getItemClass(): string + { + $viewMode = $this->getViewMode(); + + $this->addAttributes(['class' => $viewMode]); + + if ($viewMode === 'minimal') { + return DowntimeListItemMinimal::class; + } + + return DowntimeListItem::class; + } + + protected function init() + { + $this->setMultiselectUrl(Links::downtimesDetails()); + $this->setDetailUrl(Url::fromPath('icingadb/downtime')); + } +} diff --git a/library/Icingadb/Widget/ItemList/DowntimeListItem.php b/library/Icingadb/Widget/ItemList/DowntimeListItem.php new file mode 100644 index 0000000..0a00986 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/DowntimeListItem.php @@ -0,0 +1,23 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemCommonLayout; +use ipl\Html\BaseHtmlElement; + +class DowntimeListItem extends BaseDowntimeListItem +{ + use ListItemCommonLayout; + + protected function assembleMain(BaseHtmlElement $main) + { + if ($this->item->is_in_effect) { + $main->add($this->createProgress()); + } + + $main->add($this->createHeader()); + $main->add($this->createCaption()); + } +} diff --git a/library/Icingadb/Widget/ItemList/DowntimeListItemMinimal.php b/library/Icingadb/Widget/ItemList/DowntimeListItemMinimal.php new file mode 100644 index 0000000..58ef3d8 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/DowntimeListItemMinimal.php @@ -0,0 +1,21 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemMinimalLayout; + +class DowntimeListItemMinimal extends BaseDowntimeListItem +{ + use ListItemMinimalLayout; + + protected function init() + { + parent::init(); + + if ($this->list->isCaptionDisabled()) { + $this->setCaptionDisabled(); + } + } +} diff --git a/library/Icingadb/Widget/ItemList/HistoryList.php b/library/Icingadb/Widget/ItemList/HistoryList.php new file mode 100644 index 0000000..dbb7cea --- /dev/null +++ b/library/Icingadb/Widget/ItemList/HistoryList.php @@ -0,0 +1,58 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\CaptionDisabled; +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 Icinga\Module\Icingadb\Common\BaseItemList; +use ipl\Orm\ResultSet; +use ipl\Web\Url; + +class HistoryList extends BaseItemList +{ + use CaptionDisabled; + use NoSubjectLink; + use ViewMode; + use LoadMore; + use TicketLinks; + + protected $defaultAttributes = ['class' => 'history-list']; + + /** @var ResultSet */ + protected $data; + + public function __construct(ResultSet $data) + { + parent::__construct($data); + } + + protected function init() + { + $this->data = $this->getIterator($this->data); + $this->setDetailUrl(Url::fromPath('icingadb/event')); + } + + protected function getItemClass(): string + { + switch ($this->getViewMode()) { + case 'minimal': + return HistoryListItemMinimal::class; + case 'detailed': + return HistoryListItemDetailed::class; + default: + return HistoryListItem::class; + } + } + + protected function assemble() + { + $this->addAttributes(['class' => $this->getViewMode()]); + + parent::assemble(); + } +} diff --git a/library/Icingadb/Widget/ItemList/HistoryListItem.php b/library/Icingadb/Widget/ItemList/HistoryListItem.php new file mode 100644 index 0000000..c44a807 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/HistoryListItem.php @@ -0,0 +1,18 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemCommonLayout; +use ipl\Web\Widget\StateBall; + +class HistoryListItem extends BaseHistoryListItem +{ + use ListItemCommonLayout; + + protected function getStateBallSize(): string + { + return StateBall::SIZE_LARGE; + } +} diff --git a/library/Icingadb/Widget/ItemList/HistoryListItemDetailed.php b/library/Icingadb/Widget/ItemList/HistoryListItemDetailed.php new file mode 100644 index 0000000..7129d2d --- /dev/null +++ b/library/Icingadb/Widget/ItemList/HistoryListItemDetailed.php @@ -0,0 +1,18 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemDetailedLayout; +use ipl\Web\Widget\StateBall; + +class HistoryListItemDetailed extends BaseHistoryListItem +{ + use ListItemDetailedLayout; + + protected function getStateBallSize(): string + { + return StateBall::SIZE_LARGE; + } +} diff --git a/library/Icingadb/Widget/ItemList/HistoryListItemMinimal.php b/library/Icingadb/Widget/ItemList/HistoryListItemMinimal.php new file mode 100644 index 0000000..b516a4e --- /dev/null +++ b/library/Icingadb/Widget/ItemList/HistoryListItemMinimal.php @@ -0,0 +1,27 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemMinimalLayout; +use ipl\Web\Widget\StateBall; + +class HistoryListItemMinimal extends BaseHistoryListItem +{ + use ListItemMinimalLayout; + + protected function init() + { + parent::init(); + + if ($this->list->isCaptionDisabled()) { + $this->setCaptionDisabled(); + } + } + + protected function getStateBallSize(): string + { + return StateBall::SIZE_BIG; + } +} diff --git a/library/Icingadb/Widget/ItemList/HostDetailHeader.php b/library/Icingadb/Widget/ItemList/HostDetailHeader.php new file mode 100644 index 0000000..0c90fea --- /dev/null +++ b/library/Icingadb/Widget/ItemList/HostDetailHeader.php @@ -0,0 +1,72 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\HostStates; +use Icinga\Module\Icingadb\Widget\CheckAttempt; +use Icinga\Module\Icingadb\Widget\StateChange; +use ipl\Html\BaseHtmlElement; +use ipl\Web\Widget\Icon; +use ipl\Web\Widget\StateBall; + +class HostDetailHeader extends HostListItemMinimal +{ + protected function getStateBallSize(): string + { + return ''; + } + + protected function assembleVisual(BaseHtmlElement $visual) + { + if ($this->state->state_type === 'soft') { + $stateType = 'soft_state'; + $previousStateType = 'previous_soft_state'; + + if ($this->state->previous_soft_state === 0) { + $previousStateType = 'hard_state'; + } + } else { + $stateType = 'hard_state'; + $previousStateType = 'previous_hard_state'; + + if ($this->state->hard_state === $this->state->previous_hard_state) { + $previousStateType = 'previous_soft_state'; + } + } + + $state = HostStates::text($this->state->$stateType); + $previousState = HostStates::text($this->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'); + } + } + + if ($this->state->is_handled) { + $currentStateBall = $stateChange->ensureAssembled()->getContent()[1]; + $currentStateBall->addHtml(new Icon($this->getHandledIcon())); + $currentStateBall->getAttributes()->add('class', 'handled'); + } + + $visual->addHtml($stateChange); + } + + protected function assemble() + { + $attributes = $this->list->getAttributes(); + if (! in_array('minimal', $attributes->get('class')->getValue())) { + $attributes->add('class', 'minimal'); + } + + parent::assemble(); + } +} diff --git a/library/Icingadb/Widget/ItemList/HostList.php b/library/Icingadb/Widget/ItemList/HostList.php new file mode 100644 index 0000000..51d218e --- /dev/null +++ b/library/Icingadb/Widget/ItemList/HostList.php @@ -0,0 +1,36 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\Links; +use ipl\Web\Url; + +/** + * Host list + */ +class HostList extends StateList +{ + protected $defaultAttributes = ['class' => 'host-list']; + + protected function getItemClass(): string + { + switch ($this->getViewMode()) { + case 'minimal': + return HostListItemMinimal::class; + case 'detailed': + return HostListItemDetailed::class; + case 'objectHeader': + return HostDetailHeader::class; + default: + return HostListItem::class; + } + } + + protected function init() + { + $this->setMultiselectUrl(Links::hostsDetails()); + $this->setDetailUrl(Url::fromPath('icingadb/host')); + } +} diff --git a/library/Icingadb/Widget/ItemList/HostListItem.php b/library/Icingadb/Widget/ItemList/HostListItem.php new file mode 100644 index 0000000..2eae660 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/HostListItem.php @@ -0,0 +1,18 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemCommonLayout; +use ipl\Web\Widget\StateBall; + +class HostListItem extends BaseHostListItem +{ + use ListItemCommonLayout; + + protected function getStateBallSize(): string + { + return StateBall::SIZE_LARGE; + } +} diff --git a/library/Icingadb/Widget/ItemList/HostListItemDetailed.php b/library/Icingadb/Widget/ItemList/HostListItemDetailed.php new file mode 100644 index 0000000..a2f1909 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/HostListItemDetailed.php @@ -0,0 +1,103 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemDetailedLayout; +use Icinga\Module\Icingadb\Util\PerfDataSet; +use Icinga\Module\Icingadb\Widget\ItemList\CommentList; +use ipl\Html\Attributes; +use ipl\Html\BaseHtmlElement; +use ipl\Html\HtmlElement; +use ipl\Html\HtmlString; +use ipl\Html\Text; +use ipl\Web\Widget\Icon; +use ipl\Web\Widget\StateBall; + +class HostListItemDetailed extends BaseHostListItem +{ + use ListItemDetailedLayout; + + /** @var int Max pie charts to be shown */ + const PIE_CHART_LIMIT = 5; + + protected function getStateBallSize(): string + { + return StateBall::SIZE_LARGE; + } + + protected function assembleFooter(BaseHtmlElement $footer) + { + $statusIcons = new HtmlElement('div', Attributes::create(['class' => 'status-icons'])); + + 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(); + + $statusIcons->addHtml( + new HtmlElement( + 'div', + Attributes::create(['class' => 'comment-wrapper']), + new HtmlElement('div', Attributes::create(['class' => 'comment-popup']), $comment), + (new Icon('comments', ['class' => 'comment-icon'])) + ) + ); + } + + if ($this->item->state->is_flapping) { + $statusIcons->addHtml(new Icon( + 'random', + [ + 'title' => sprintf(t('Host "%s" is in flapping state'), $this->item->display_name), + ] + )); + } + + if (! $this->item->notifications_enabled) { + $statusIcons->addHtml(new Icon('bell-slash', ['title' => t('Notifications disabled')])); + } + + if (! $this->item->active_checks_enabled) { + $statusIcons->addHtml(new Icon('eye-slash', ['title' => t('Active checks disabled')])); + } + + $performanceData = new HtmlElement('div', Attributes::create(['class' => 'performance-data'])); + if ($this->item->state->performance_data) { + $pieChartData = PerfDataSet::fromString($this->item->state->normalized_performance_data)->asArray(); + + $pies = []; + foreach ($pieChartData as $i => $perfdata) { + if ($perfdata->isVisualizable()) { + $pies[] = $perfdata->asInlinePie()->render(); + } + + // Check if number of visualizable pie charts is larger than PIE_CHART_LIMIT + if (count($pies) > HostListItemDetailed::PIE_CHART_LIMIT) { + break; + } + } + + $maxVisiblePies = HostListItemDetailed::PIE_CHART_LIMIT - 2; + $numOfPies = count($pies); + foreach ($pies as $i => $pie) { + if ( + // Show max. 5 elements: if there are more than 5, show 4 + `…` + $i > $maxVisiblePies && $numOfPies > HostListItemDetailed::PIE_CHART_LIMIT + ) { + $performanceData->addHtml(new HtmlElement('span', null, Text::create('…'))); + break; + } + + $performanceData->addHtml(HtmlString::create($pie)); + } + } + + $footer->addHtml($statusIcons); + $footer->addHtml($performanceData); + } +} diff --git a/library/Icingadb/Widget/ItemList/HostListItemMinimal.php b/library/Icingadb/Widget/ItemList/HostListItemMinimal.php new file mode 100644 index 0000000..f04b991 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/HostListItemMinimal.php @@ -0,0 +1,18 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemMinimalLayout; +use ipl\Web\Widget\StateBall; + +class HostListItemMinimal extends BaseHostListItem +{ + use ListItemMinimalLayout; + + protected function getStateBallSize(): string + { + return StateBall::SIZE_BIG; + } +} diff --git a/library/Icingadb/Widget/ItemList/HostgroupList.php b/library/Icingadb/Widget/ItemList/HostgroupList.php new file mode 100644 index 0000000..e6c8279 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/HostgroupList.php @@ -0,0 +1,33 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\NoSubjectLink; +use Icinga\Module\Icingadb\Common\ViewMode; +use Icinga\Module\Icingadb\Common\BaseItemList; +use ipl\Web\Url; + +class HostgroupList extends BaseItemList +{ + use NoSubjectLink; + use ViewMode; + + protected $defaultAttributes = ['class' => 'hostgroup-list item-table']; + + protected function init() + { + parent::init(); + + $this->getAttributes()->get('class')->removeValue('item-list'); + $this->setDetailUrl(Url::fromPath('icingadb/hostgroup')); + } + + protected function getItemClass(): string + { + $this->addAttributes(['class' => $this->getViewMode()]); + + return HostgroupListItem::class; + } +} diff --git a/library/Icingadb/Widget/ItemList/HostgroupListItem.php b/library/Icingadb/Widget/ItemList/HostgroupListItem.php new file mode 100644 index 0000000..5df0432 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/HostgroupListItem.php @@ -0,0 +1,84 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\BaseTableRowItem; +use Icinga\Module\Icingadb\Common\Links; +use Icinga\Module\Icingadb\Common\NoSubjectLink; +use Icinga\Module\Icingadb\Model\Hostgroup; +use Icinga\Module\Icingadb\Widget\Detail\HostStatistics; +use Icinga\Module\Icingadb\Widget\Detail\ServiceStatistics; +use ipl\Html\Attributes; +use ipl\Html\BaseHtmlElement; +use ipl\Html\HtmlDocument; +use ipl\Html\HtmlElement; +use ipl\Html\Text; +use ipl\Stdlib\Filter; +use ipl\Web\Widget\Link; + +/** + * Hostgroup item of a hostgroup list. Represents one database row. + * + * @property Hostgroup $item + * @property HostgroupList $list + */ +class HostgroupListItem extends BaseTableRowItem +{ + use NoSubjectLink; + + protected function init() + { + $this->setNoSubjectLink($this->list->getNoSubjectLink()); + $this->list->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); + } + + protected function assembleColumns(HtmlDocument $columns) + { + $hostStats = new HostStatistics($this->item); + + $hostStats->setBaseFilter(Filter::equal('hostgroup.name', $this->item->name)); + if ($this->list->hasBaseFilter()) { + $hostStats->setBaseFilter( + Filter::all($hostStats->getBaseFilter(), $this->list->getBaseFilter()) + ); + } + + $columns->addFrom($hostStats, function (BaseHtmlElement $item) { + $item->getAttributes()->add(['class' => 'col']); + $item->setTag('div'); + return $item; + }); + + $serviceStats = new ServiceStatistics($this->item); + + $serviceStats->setBaseFilter(Filter::equal('hostgroup.name', $this->item->name)); + if ($this->list->hasBaseFilter()) { + $serviceStats->setBaseFilter( + Filter::all($serviceStats->getBaseFilter(), $this->list->getBaseFilter()) + ); + } + + $columns->addFrom($serviceStats, function (BaseHtmlElement $item) { + $item->getAttributes()->add(['class' => 'col']); + $item->setTag('div'); + return $item; + }); + } + + protected function assembleTitle(BaseHtmlElement $title) + { + $title->addHtml( + $this->getNoSubjectLink() + ? new HtmlElement( + 'span', + Attributes::create(['class' => 'subject']), + Text::create($this->item->display_name) + ) + : new Link($this->item->display_name, Links::hostgroup($this->item), ['class' => 'subject']), + new HtmlElement('br'), + Text::create($this->item->name) + ); + } +} diff --git a/library/Icingadb/Widget/ItemList/NotificationList.php b/library/Icingadb/Widget/ItemList/NotificationList.php new file mode 100644 index 0000000..8c95d26 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/NotificationList.php @@ -0,0 +1,56 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\CaptionDisabled; +use Icinga\Module\Icingadb\Common\LoadMore; +use Icinga\Module\Icingadb\Common\NoSubjectLink; +use Icinga\Module\Icingadb\Common\ViewMode; +use Icinga\Module\Icingadb\Common\BaseItemList; +use ipl\Orm\ResultSet; +use ipl\Web\Url; + +class NotificationList extends BaseItemList +{ + use CaptionDisabled; + use NoSubjectLink; + use ViewMode; + use LoadMore; + + protected $defaultAttributes = ['class' => 'notification-list']; + + /** @var ResultSet */ + protected $data; + + public function __construct(ResultSet $data) + { + parent::__construct($data); + } + + protected function init() + { + $this->data = $this->getIterator($this->data); + $this->setDetailUrl(Url::fromPath('icingadb/event')); + } + + protected function getItemClass(): string + { + switch ($this->getViewMode()) { + case 'minimal': + return NotificationListItemMinimal::class; + case 'detailed': + return NotificationListItemDetailed::class; + default: + return NotificationListItem::class; + } + } + + protected function assemble() + { + $this->addAttributes(['class' => $this->getViewMode()]); + + parent::assemble(); + } +} diff --git a/library/Icingadb/Widget/ItemList/NotificationListItem.php b/library/Icingadb/Widget/ItemList/NotificationListItem.php new file mode 100644 index 0000000..683762f --- /dev/null +++ b/library/Icingadb/Widget/ItemList/NotificationListItem.php @@ -0,0 +1,18 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemCommonLayout; +use ipl\Web\Widget\StateBall; + +class NotificationListItem extends BaseNotificationListItem +{ + use ListItemCommonLayout; + + protected function getStateBallSize(): string + { + return StateBall::SIZE_LARGE; + } +} diff --git a/library/Icingadb/Widget/ItemList/NotificationListItemDetailed.php b/library/Icingadb/Widget/ItemList/NotificationListItemDetailed.php new file mode 100644 index 0000000..0a7449e --- /dev/null +++ b/library/Icingadb/Widget/ItemList/NotificationListItemDetailed.php @@ -0,0 +1,18 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemDetailedLayout; +use ipl\Web\Widget\StateBall; + +class NotificationListItemDetailed extends BaseNotificationListItem +{ + use ListItemDetailedLayout; + + protected function getStateBallSize(): string + { + return StateBall::SIZE_LARGE; + } +} diff --git a/library/Icingadb/Widget/ItemList/NotificationListItemMinimal.php b/library/Icingadb/Widget/ItemList/NotificationListItemMinimal.php new file mode 100644 index 0000000..dcda5fd --- /dev/null +++ b/library/Icingadb/Widget/ItemList/NotificationListItemMinimal.php @@ -0,0 +1,27 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemMinimalLayout; +use ipl\Web\Widget\StateBall; + +class NotificationListItemMinimal extends BaseNotificationListItem +{ + use ListItemMinimalLayout; + + protected function init() + { + parent::init(); + + if ($this->list->isCaptionDisabled()) { + $this->setCaptionDisabled(); + } + } + + protected function getStateBallSize(): string + { + return StateBall::SIZE_BIG; + } +} diff --git a/library/Icingadb/Widget/ItemList/PageSeparatorItem.php b/library/Icingadb/Widget/ItemList/PageSeparatorItem.php new file mode 100644 index 0000000..3e252eb --- /dev/null +++ b/library/Icingadb/Widget/ItemList/PageSeparatorItem.php @@ -0,0 +1,36 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use ipl\Html\BaseHtmlElement; +use ipl\Html\Html; + +class PageSeparatorItem extends BaseHtmlElement +{ + protected $defaultAttributes = ['class' => 'list-item page-separator']; + + /** @var int */ + protected $pageNumber; + + /** @var string */ + protected $tag = 'li'; + + public function __construct(int $pageNumber) + { + $this->pageNumber = $pageNumber; + } + + protected function assemble() + { + $this->add(Html::tag( + 'a', + [ + 'id' => 'page-' . $this->pageNumber, + 'data-icinga-no-scroll-on-focus' => true + ], + $this->pageNumber + )); + } +} diff --git a/library/Icingadb/Widget/ItemList/ServiceDetailHeader.php b/library/Icingadb/Widget/ItemList/ServiceDetailHeader.php new file mode 100644 index 0000000..6036929 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/ServiceDetailHeader.php @@ -0,0 +1,72 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ServiceStates; +use Icinga\Module\Icingadb\Widget\CheckAttempt; +use Icinga\Module\Icingadb\Widget\StateChange; +use ipl\Html\BaseHtmlElement; +use ipl\Web\Widget\Icon; +use ipl\Web\Widget\StateBall; + +class ServiceDetailHeader extends ServiceListItemMinimal +{ + protected function getStateBallSize(): string + { + return ''; + } + + protected function assembleVisual(BaseHtmlElement $visual) + { + if ($this->state->state_type === 'soft') { + $stateType = 'soft_state'; + $previousStateType = 'previous_soft_state'; + + if ($this->state->previous_soft_state === 0) { + $previousStateType = 'hard_state'; + } + } else { + $stateType = 'hard_state'; + $previousStateType = 'previous_hard_state'; + + if ($this->state->hard_state === $this->state->previous_hard_state) { + $previousStateType = 'previous_soft_state'; + } + } + + $state = ServiceStates::text($this->state->$stateType); + $previousState = ServiceStates::text($this->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'); + } + } + + if ($this->state->is_handled) { + $currentStateBall = $stateChange->ensureAssembled()->getContent()[1]; + $currentStateBall->addHtml(new Icon($this->getHandledIcon())); + $currentStateBall->getAttributes()->add('class', 'handled'); + } + + $visual->addHtml($stateChange); + } + + protected function assemble() + { + $attributes = $this->list->getAttributes(); + if (! in_array('minimal', $attributes->get('class')->getValue())) { + $attributes->add('class', 'minimal'); + } + + parent::assemble(); + } +} diff --git a/library/Icingadb/Widget/ItemList/ServiceList.php b/library/Icingadb/Widget/ItemList/ServiceList.php new file mode 100644 index 0000000..11febb0 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/ServiceList.php @@ -0,0 +1,33 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\Links; +use ipl\Web\Url; + +class ServiceList extends StateList +{ + protected $defaultAttributes = ['class' => 'service-list']; + + protected function getItemClass(): string + { + switch ($this->getViewMode()) { + case 'minimal': + return ServiceListItemMinimal::class; + case 'detailed': + return ServiceListItemDetailed::class; + case 'objectHeader': + return ServiceDetailHeader::class; + default: + return ServiceListItem::class; + } + } + + protected function init() + { + $this->setMultiselectUrl(Links::servicesDetails()); + $this->setDetailUrl(Url::fromPath('icingadb/service')); + } +} diff --git a/library/Icingadb/Widget/ItemList/ServiceListItem.php b/library/Icingadb/Widget/ItemList/ServiceListItem.php new file mode 100644 index 0000000..a974581 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/ServiceListItem.php @@ -0,0 +1,18 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemCommonLayout; +use ipl\Web\Widget\StateBall; + +class ServiceListItem extends BaseServiceListItem +{ + use ListItemCommonLayout; + + protected function getStateBallSize(): string + { + return StateBall::SIZE_LARGE; + } +} diff --git a/library/Icingadb/Widget/ItemList/ServiceListItemDetailed.php b/library/Icingadb/Widget/ItemList/ServiceListItemDetailed.php new file mode 100644 index 0000000..a068beb --- /dev/null +++ b/library/Icingadb/Widget/ItemList/ServiceListItemDetailed.php @@ -0,0 +1,107 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemDetailedLayout; +use Icinga\Module\Icingadb\Util\PerfDataSet; +use Icinga\Module\Icingadb\Widget\ItemList\CommentList; +use ipl\Html\Attributes; +use ipl\Html\BaseHtmlElement; +use ipl\Html\HtmlElement; +use ipl\Html\HtmlString; +use ipl\Html\Text; +use ipl\Web\Widget\Icon; +use ipl\Web\Widget\StateBall; + +class ServiceListItemDetailed extends BaseServiceListItem +{ + use ListItemDetailedLayout; + + /** @var int Max pie charts to be shown */ + const PIE_CHART_LIMIT = 5; + + protected function getStateBallSize(): string + { + return StateBall::SIZE_LARGE; + } + + protected function assembleFooter(BaseHtmlElement $footer) + { + $statusIcons = new HtmlElement('div', Attributes::create(['class' => 'status-icons'])); + + 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(); + + $statusIcons->addHtml( + new HtmlElement( + 'div', + Attributes::create(['class' => 'comment-wrapper']), + new HtmlElement('div', Attributes::create(['class' => 'comment-popup']), $comment), + (new Icon('comments', ['class' => 'comment-icon'])) + ) + ); + } + + if ($this->item->state->is_flapping) { + $statusIcons->addHtml(new Icon( + 'random', + [ + 'title' => sprintf( + t('Service "%s" on "%s" is in flapping state'), + $this->item->display_name, + $this->item->host->display_name + ), + ] + )); + } + + if (! $this->item->notifications_enabled) { + $statusIcons->addHtml(new Icon('bell-slash', ['title' => t('Notifications disabled')])); + } + + if (! $this->item->active_checks_enabled) { + $statusIcons->addHtml(new Icon('eye-slash', ['title' => t('Active checks disabled')])); + } + + $performanceData = new HtmlElement('div', Attributes::create(['class' => 'performance-data'])); + if ($this->item->state->performance_data) { + $pieChartData = PerfDataSet::fromString($this->item->state->normalized_performance_data)->asArray(); + + $pies = []; + foreach ($pieChartData as $i => $perfdata) { + if ($perfdata->isVisualizable()) { + $pies[] = $perfdata->asInlinePie()->render(); + } + + // Check if number of visualizable pie charts is larger than PIE_CHART_LIMIT + if (count($pies) > ServiceListItemDetailed::PIE_CHART_LIMIT) { + break; + } + } + + $maxVisiblePies = ServiceListItemDetailed::PIE_CHART_LIMIT - 2; + $numOfPies = count($pies); + foreach ($pies as $i => $pie) { + if ( + // Show max. 5 elements: if there are more than 5, show 4 + `…` + $i > $maxVisiblePies && $numOfPies > ServiceListItemDetailed::PIE_CHART_LIMIT + ) { + $performanceData->addHtml(new HtmlElement('span', null, Text::create('…'))); + break; + } + + $performanceData->addHtml(HtmlString::create($pie)); + } + } + + $footer->addHtml($statusIcons); + $footer->addHtml($performanceData); + } +} diff --git a/library/Icingadb/Widget/ItemList/ServiceListItemMinimal.php b/library/Icingadb/Widget/ItemList/ServiceListItemMinimal.php new file mode 100644 index 0000000..e7a1bc6 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/ServiceListItemMinimal.php @@ -0,0 +1,18 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\ListItemMinimalLayout; +use ipl\Web\Widget\StateBall; + +class ServiceListItemMinimal extends BaseServiceListItem +{ + use ListItemMinimalLayout; + + protected function getStateBallSize(): string + { + return StateBall::SIZE_BIG; + } +} diff --git a/library/Icingadb/Widget/ItemList/ServicegroupList.php b/library/Icingadb/Widget/ItemList/ServicegroupList.php new file mode 100644 index 0000000..fa612f1 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/ServicegroupList.php @@ -0,0 +1,33 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\NoSubjectLink; +use Icinga\Module\Icingadb\Common\ViewMode; +use Icinga\Module\Icingadb\Common\BaseItemList; +use ipl\Web\Url; + +class ServicegroupList extends BaseItemList +{ + use NoSubjectLink; + use ViewMode; + + protected $defaultAttributes = ['class' => 'servicegroup-list item-table']; + + protected function init() + { + parent::init(); + + $this->getAttributes()->get('class')->removeValue('item-list'); + $this->setDetailUrl(Url::fromPath('icingadb/servicegroup')); + } + + protected function getItemClass(): string + { + $this->addAttributes(['class' => $this->getViewMode()]); + + return ServicegroupListItem::class; + } +} diff --git a/library/Icingadb/Widget/ItemList/ServicegroupListItem.php b/library/Icingadb/Widget/ItemList/ServicegroupListItem.php new file mode 100644 index 0000000..6a8320c --- /dev/null +++ b/library/Icingadb/Widget/ItemList/ServicegroupListItem.php @@ -0,0 +1,68 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\BaseTableRowItem; +use Icinga\Module\Icingadb\Common\Links; +use Icinga\Module\Icingadb\Common\NoSubjectLink; +use Icinga\Module\Icingadb\Model\Servicegroup; +use Icinga\Module\Icingadb\Widget\Detail\ServiceStatistics; +use ipl\Html\Attributes; +use ipl\Html\BaseHtmlElement; +use ipl\Html\HtmlDocument; +use ipl\Html\HtmlElement; +use ipl\Html\Text; +use ipl\Stdlib\Filter; +use ipl\Web\Widget\Link; + +/** + * Servicegroup item of a servicegroup list. Represents one database row. + * + * @property Servicegroup $item + * @property ServicegroupList $list + */ +class ServicegroupListItem extends BaseTableRowItem +{ + use NoSubjectLink; + + protected function init() + { + $this->setNoSubjectLink($this->list->getNoSubjectLink()); + $this->list->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); + } + + protected function assembleColumns(HtmlDocument $columns) + { + $serviceStats = new ServiceStatistics($this->item); + + $serviceStats->setBaseFilter(Filter::equal('servicegroup.name', $this->item->name)); + if ($this->list->hasBaseFilter()) { + $serviceStats->setBaseFilter( + Filter::all($serviceStats->getBaseFilter(), $this->list->getBaseFilter()) + ); + } + + $columns->addFrom($serviceStats, function (BaseHtmlElement $item) { + $item->getAttributes()->add(['class' => 'col']); + $item->setTag('div'); + return $item; + }); + } + + protected function assembleTitle(BaseHtmlElement $title) + { + $title->addHtml( + $this->getNoSubjectLink() + ? new HtmlElement( + 'span', + Attributes::create(['class' => 'subject']), + Text::create($this->item->display_name) + ) + : new Link($this->item->display_name, Links::servicegroup($this->item), ['class' => 'subject']), + new HtmlElement('br'), + Text::create($this->item->name) + ); + } +} diff --git a/library/Icingadb/Widget/ItemList/StateList.php b/library/Icingadb/Widget/ItemList/StateList.php new file mode 100644 index 0000000..cf6ec0b --- /dev/null +++ b/library/Icingadb/Widget/ItemList/StateList.php @@ -0,0 +1,31 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\BaseItemList; +use Icinga\Module\Icingadb\Common\NoSubjectLink; +use Icinga\Module\Icingadb\Common\ViewMode; +use Icinga\Module\Icingadb\Redis\VolatileStateResults; +use Icinga\Module\Icingadb\Widget\Notice; +use ipl\Html\HtmlDocument; + +abstract class StateList extends BaseItemList +{ + use ViewMode; + use NoSubjectLink; + + protected function assemble() + { + $this->addAttributes(['class' => $this->getViewMode()]); + + parent::assemble(); + + if ($this->data instanceof VolatileStateResults && $this->data->isRedisUnavailable()) { + $this->prependWrapper((new HtmlDocument())->addHtml(new Notice( + t('Icinga Redis is currently unavailable. The shown information might be outdated.') + ))); + } + } +} diff --git a/library/Icingadb/Widget/ItemList/StateListItem.php b/library/Icingadb/Widget/ItemList/StateListItem.php new file mode 100644 index 0000000..d5eb4fd --- /dev/null +++ b/library/Icingadb/Widget/ItemList/StateListItem.php @@ -0,0 +1,129 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\BaseListItem; +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\EmptyState; +use Icinga\Module\Icingadb\Widget\IconImage; +use Icinga\Module\Icingadb\Widget\PluginOutputContainer; +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 +{ + /** @var State The state of the item */ + protected $state; + + protected function init() + { + $this->state = $this->item->state; + + if (isset($this->item->icon_image->icon_image)) { + $this->list->setHasIconImages(true); + } + } + + abstract protected function createSubject(); + + abstract protected function getStateBallSize(): string; + + protected function assembleCaption(BaseHtmlElement $caption) + { + if ($this->state->soft_state === null && $this->state->output === null) { + $caption->addHtml(Text::create(t('Waiting for Icinga DB to synchronize the state.'))); + } else { + if (empty($this->state->output)) { + $pluginOutput = new EmptyState(t('Output unavailable.')); + } else { + $pluginOutput = new PluginOutputContainer(PluginOutput::fromObject($this->item)); + } + + $caption->addHtml($pluginOutput); + } + } + + protected function assembleIconImage(BaseHtmlElement $iconImage) + { + 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) + { + $title->addHtml(Html::sprintf( + t('%s is %s', '<hostname> is <state-text>'), + $this->createSubject(), + Html::tag('span', ['class' => 'state-text'], $this->state->getStateTextTranslated()) + )); + } + + protected function assembleVisual(BaseHtmlElement $visual) + { + $stateBall = new StateBall($this->state->getStateText(), $this->getStateBallSize()); + + if ($this->state->is_handled) { + $stateBall->addHtml(new Icon($this->getHandledIcon())); + $stateBall->getAttributes()->add('class', 'handled'); + } elseif ($this->state->getStateText() === 'pending' && $this->state->in_downtime) { + $stateBall->addHtml(new Icon($this->getHandledIcon())); + } + + $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() + { + if ($this->state->is_overdue) { + $since = new TimeSince($this->state->next_update); + $since->prepend(t('Overdue') . ' '); + $since->prependHtml(new Icon(Icons::WARNING)); + return $since; + } elseif ($this->state->last_state_change > 0) { + return new TimeSince($this->state->last_state_change); + } + } + + protected function getHandledIcon(): string + { + switch (true) { + case $this->state->is_acknowledged: + return Icons::IS_ACKNOWLEDGED; + case $this->state->in_downtime: + return Icons::IN_DOWNTIME; + case $this->state->is_flapping: + return Icons::IS_FLAPPING; + default: + return Icons::HOST_DOWN; + } + } + + protected function assemble() + { + if ($this->state->is_overdue) { + $this->addAttributes(['class' => 'overdue']); + } + + parent::assemble(); + } +} diff --git a/library/Icingadb/Widget/ItemList/UserList.php b/library/Icingadb/Widget/ItemList/UserList.php new file mode 100644 index 0000000..826a467 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/UserList.php @@ -0,0 +1,29 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\NoSubjectLink; +use Icinga\Module\Icingadb\Common\BaseItemList; +use ipl\Web\Url; + +class UserList extends BaseItemList +{ + use NoSubjectLink; + + protected $defaultAttributes = ['class' => 'user-list item-table']; + + protected function init() + { + parent::init(); + + $this->getAttributes()->get('class')->removeValue('item-list'); + $this->setDetailUrl(Url::fromPath('icingadb/user')); + } + + protected function getItemClass(): string + { + return UserListItem::class; + } +} diff --git a/library/Icingadb/Widget/ItemList/UserListItem.php b/library/Icingadb/Widget/ItemList/UserListItem.php new file mode 100644 index 0000000..e43692f --- /dev/null +++ b/library/Icingadb/Widget/ItemList/UserListItem.php @@ -0,0 +1,62 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\BaseTableRowItem; +use Icinga\Module\Icingadb\Common\Links; +use Icinga\Module\Icingadb\Common\NoSubjectLink; +use Icinga\Module\Icingadb\Model\User; +use ipl\Html\Attributes; +use ipl\Html\BaseHtmlElement; +use ipl\Html\HtmlDocument; +use ipl\Html\HtmlElement; +use ipl\Html\Text; +use ipl\Stdlib\Filter; +use ipl\Web\Widget\Link; + +/** + * User item of a user list. Represents one database row. + * + * @property User $item + * @property UserList $list + */ +class UserListItem extends BaseTableRowItem +{ + use NoSubjectLink; + + protected function init() + { + $this->setNoSubjectLink($this->list->getNoSubjectLink()); + $this->list->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); + } + + protected function assembleVisual(BaseHtmlElement $visual) + { + $visual->addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => 'user-ball']), + Text::create($this->item->display_name[0]) + )); + } + + protected function assembleTitle(BaseHtmlElement $title) + { + $title->addHtml( + $this->getNoSubjectLink() + ? 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('br'), + Text::create($this->item->name) + ); + } + + protected function assembleColumns(HtmlDocument $columns) + { + } +} diff --git a/library/Icingadb/Widget/ItemList/UsergroupList.php b/library/Icingadb/Widget/ItemList/UsergroupList.php new file mode 100644 index 0000000..2e95368 --- /dev/null +++ b/library/Icingadb/Widget/ItemList/UsergroupList.php @@ -0,0 +1,29 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\NoSubjectLink; +use Icinga\Module\Icingadb\Common\BaseItemList; +use ipl\Web\Url; + +class UsergroupList extends BaseItemList +{ + use NoSubjectLink; + + protected $defaultAttributes = ['class' => 'usergroup-list item-table']; + + protected function init() + { + parent::init(); + + $this->getAttributes()->get('class')->removeValue('item-list'); + $this->setDetailUrl(Url::fromPath('icingadb/usergroup')); + } + + protected function getItemClass(): string + { + return UsergroupListItem::class; + } +} diff --git a/library/Icingadb/Widget/ItemList/UsergroupListItem.php b/library/Icingadb/Widget/ItemList/UsergroupListItem.php new file mode 100644 index 0000000..f8c800d --- /dev/null +++ b/library/Icingadb/Widget/ItemList/UsergroupListItem.php @@ -0,0 +1,62 @@ +<?php + +/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */ + +namespace Icinga\Module\Icingadb\Widget\ItemList; + +use Icinga\Module\Icingadb\Common\BaseTableRowItem; +use Icinga\Module\Icingadb\Common\Links; +use Icinga\Module\Icingadb\Common\NoSubjectLink; +use Icinga\Module\Icingadb\Model\Usergroup; +use ipl\Html\Attributes; +use ipl\Html\BaseHtmlElement; +use ipl\Html\HtmlDocument; +use ipl\Html\HtmlElement; +use ipl\Html\Text; +use ipl\Stdlib\Filter; +use ipl\Web\Widget\Link; + +/** + * Usergroup item of a usergroup list. Represents one database row. + * + * @property Usergroup $item + * @property UsergroupList $list + */ +class UsergroupListItem extends BaseTableRowItem +{ + use NoSubjectLink; + + protected function init() + { + $this->setNoSubjectLink($this->list->getNoSubjectLink()); + $this->list->addDetailFilterAttribute($this, Filter::equal('name', $this->item->name)); + } + + protected function assembleVisual(BaseHtmlElement $visual) + { + $visual->addHtml(new HtmlElement( + 'div', + Attributes::create(['class' => 'usergroup-ball']), + Text::create($this->item->display_name[0]) + )); + } + + protected function assembleTitle(BaseHtmlElement $title) + { + $title->addHtml( + $this->getNoSubjectLink() + ? 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('br'), + Text::create($this->item->name) + ); + } + + protected function assembleColumns(HtmlDocument $columns) + { + } +} |