summaryrefslogtreecommitdiffstats
path: root/library/Icingadb/Widget/Detail/EventDetail.php
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--library/Icingadb/Widget/Detail/EventDetail.php651
1 files changed, 651 insertions, 0 deletions
diff --git a/library/Icingadb/Widget/Detail/EventDetail.php b/library/Icingadb/Widget/Detail/EventDetail.php
new file mode 100644
index 0000000..181c9ae
--- /dev/null
+++ b/library/Icingadb/Widget/Detail/EventDetail.php
@@ -0,0 +1,651 @@
+<?php
+
+/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Widget\Detail;
+
+use DateTime;
+use DateTimeZone;
+use Icinga\Date\DateFormatter;
+use Icinga\Module\Icingadb\Common\Auth;
+use Icinga\Module\Icingadb\Common\Database;
+use Icinga\Module\Icingadb\Common\HostLink;
+use Icinga\Module\Icingadb\Common\HostStates;
+use Icinga\Module\Icingadb\Common\Links;
+use Icinga\Module\Icingadb\Common\TicketLinks;
+use Icinga\Module\Icingadb\Hook\ExtensionHook\ObjectDetailExtensionHook;
+use Icinga\Module\Icingadb\Widget\MarkdownText;
+use Icinga\Module\Icingadb\Common\ServiceLink;
+use Icinga\Module\Icingadb\Common\ServiceStates;
+use Icinga\Module\Icingadb\Model\AcknowledgementHistory;
+use Icinga\Module\Icingadb\Model\CommentHistory;
+use Icinga\Module\Icingadb\Model\DowntimeHistory;
+use Icinga\Module\Icingadb\Model\FlappingHistory;
+use Icinga\Module\Icingadb\Model\History;
+use Icinga\Module\Icingadb\Model\NotificationHistory;
+use Icinga\Module\Icingadb\Model\StateHistory;
+use Icinga\Module\Icingadb\Util\PluginOutput;
+use Icinga\Module\Icingadb\Widget\ShowMore;
+use ipl\Web\Widget\CopyToClipboard;
+use ipl\Web\Widget\EmptyState;
+use ipl\Web\Widget\HorizontalKeyValue;
+use Icinga\Module\Icingadb\Widget\ItemTable\UserTable;
+use Icinga\Module\Icingadb\Widget\PluginOutputContainer;
+use ipl\Html\BaseHtmlElement;
+use ipl\Html\FormattedString;
+use ipl\Html\HtmlElement;
+use ipl\Html\TemplateString;
+use ipl\Html\Text;
+use ipl\Orm\ResultSet;
+use ipl\Stdlib\Filter;
+use ipl\Stdlib\Str;
+use ipl\Web\Widget\Icon;
+use ipl\Web\Widget\Link;
+use ipl\Web\Widget\StateBall;
+
+class EventDetail extends BaseHtmlElement
+{
+ use Auth;
+ use Database;
+ use HostLink;
+ use ServiceLink;
+ use TicketLinks;
+
+ protected $tag = 'div';
+
+ protected $defaultAttributes = ['class' => 'object-detail'];
+
+ /** @var History */
+ protected $event;
+
+ public function __construct(History $event)
+ {
+ $this->event = $event;
+ }
+
+ protected function assembleNotificationEvent(NotificationHistory $notification)
+ {
+ $pluginOutput = [];
+
+ $commandName = $notification->object_type === 'host'
+ ? $this->event->host->checkcommand_name
+ : $this->event->service->checkcommand_name;
+ if (isset($commandName)) {
+ if (empty($notification->text)) {
+ $notificationText = new EmptyState(t('Output unavailable.'));
+ } else {
+ $notificationText = new PluginOutputContainer(
+ (new PluginOutput($notification->text))
+ ->setCommandName($notification->object_type === 'host'
+ ? $this->event->host->checkcommand_name
+ : $this->event->service->checkcommand_name)
+ );
+
+ CopyToClipboard::attachTo($notificationText);
+ }
+
+ $pluginOutput = [
+ HtmlElement::create('h2', null, $notification->author ? t('Comment') : t('Plugin Output')),
+ HtmlElement::create('div', [
+ 'id' => 'check-output-' . $commandName,
+ 'class' => 'collapsible',
+ 'data-visible-height' => 100
+ ], $notificationText)
+ ];
+ } else {
+ $pluginOutput[] = new EmptyState(t('Waiting for Icinga DB to synchronize the config.'));
+ }
+
+ if ($notification->object_type === 'host') {
+ $objectKey = t('Host');
+ $objectInfo = HtmlElement::create('span', ['class' => 'accompanying-text'], [
+ HtmlElement::create('span', ['class' => 'state-change'], [
+ new StateBall(HostStates::text($notification->previous_hard_state), StateBall::SIZE_MEDIUM),
+ new StateBall(HostStates::text($notification->state), StateBall::SIZE_MEDIUM)
+ ]),
+ ' ',
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->host->display_name)
+ ]);
+ } else {
+ $objectKey = t('Service');
+ $objectInfo = HtmlElement::create('span', ['class' => 'accompanying-text'], [
+ HtmlElement::create('span', ['class' => 'state-change'], [
+ new StateBall(ServiceStates::text($notification->previous_hard_state), StateBall::SIZE_MEDIUM),
+ new StateBall(ServiceStates::text($notification->state), StateBall::SIZE_MEDIUM)
+ ]),
+ ' ',
+ FormattedString::create(
+ t('%s on %s', '<service> on <host>'),
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->service->display_name),
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->host->display_name)
+ )
+ ]);
+ }
+
+ $eventInfo = [
+ new HtmlElement('h2', null, Text::create(t('Event Info'))),
+ new HorizontalKeyValue(
+ t('Sent On'),
+ DateFormatter::formatDateTime($notification->send_time->getTimestamp())
+ )
+ ];
+
+ if ($notification->author) {
+ $eventInfo[] = (new HorizontalKeyValue(t('Sent by'), [
+ new Icon('user'),
+ $notification->author
+ ]));
+ }
+
+ $eventInfo[] = new HorizontalKeyValue(t('Type'), ucfirst(Str::camel($notification->type)));
+ $eventInfo[] = new HorizontalKeyValue(t('State'), $notification->object_type === 'host'
+ ? ucfirst(HostStates::text($notification->state))
+ : ucfirst(ServiceStates::text($notification->state)));
+ $eventInfo[] = new HorizontalKeyValue($objectKey, $objectInfo);
+
+
+ $notifiedUsers = [new HtmlElement('h2', null, Text::create(t('Notified Users')))];
+
+ if ($notification->users_notified === 0) {
+ $notifiedUsers[] = new EmptyState(t('None', 'notified users: none'));
+ } elseif (! $this->isPermittedRoute('users')) {
+ $notifiedUsers[] = Text::create(sprintf(tp(
+ 'This notification was sent to a single user',
+ 'This notification was sent to %d users',
+ $notification->users_notified
+ ), $notification->users_notified));
+ } elseif ($notification->users_notified > 0) {
+ $users = $notification->user
+ ->limit(5)
+ ->peekAhead();
+
+ $users = $users->execute();
+ /** @var ResultSet $users */
+
+ $notifiedUsers[] = new UserTable($users);
+ $notifiedUsers[] = (new ShowMore(
+ $users,
+ Links::users()->addParams(['notification_history.id' => bin2hex($notification->id)]),
+ sprintf(t('Show all %d recipients'), $notification->users_notified)
+ ))->setBaseTarget('_next');
+ }
+
+ $this->add(ObjectDetailExtensionHook::injectExtensions([
+ 0 => $pluginOutput,
+ 200 => $eventInfo,
+ 500 => $notifiedUsers
+ ], $this->createExtensions()));
+ }
+
+ protected function assembleStateChangeEvent(StateHistory $stateChange)
+ {
+ $pluginOutput = [];
+
+ $commandName = $stateChange->object_type === 'host'
+ ? $this->event->host->checkcommand_name
+ : $this->event->service->checkcommand_name;
+ if (isset($commandName)) {
+ if (empty($stateChange->output) && empty($stateChange->long_output)) {
+ $commandOutput = new EmptyState(t('Output unavailable.'));
+ } else {
+ $commandOutput = new PluginOutputContainer(
+ (new PluginOutput($stateChange->output . "\n" . $stateChange->long_output))
+ ->setCommandName($commandName)
+ );
+
+ CopyToClipboard::attachTo($commandOutput);
+ }
+
+ $pluginOutput = [
+ new HtmlElement('h2', null, Text::create(t('Plugin Output'))),
+ HtmlElement::create('div', [
+ 'id' => 'check-output-' . $commandName,
+ 'class' => 'collapsible',
+ 'data-visible-height' => 100
+ ], $commandOutput)
+ ];
+ } else {
+ $pluginOutput[] = new EmptyState(t('Waiting for Icinga DB to synchronize the config.'));
+ }
+
+ if ($stateChange->object_type === 'host') {
+ $objectKey = t('Host');
+ $objectState = $stateChange->state_type === 'hard' ? $stateChange->hard_state : $stateChange->soft_state;
+ $objectInfo = HtmlElement::create('span', ['class' => 'accompanying-text'], [
+ HtmlElement::create('span', ['class' => 'state-change'], [
+ new StateBall(HostStates::text($stateChange->previous_soft_state), StateBall::SIZE_MEDIUM),
+ new StateBall(HostStates::text($objectState), StateBall::SIZE_MEDIUM)
+ ]),
+ ' ',
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->host->display_name)
+ ]);
+ } else {
+ $objectKey = t('Service');
+ $objectState = $stateChange->state_type === 'hard' ? $stateChange->hard_state : $stateChange->soft_state;
+ $objectInfo = HtmlElement::create('span', ['class' => 'accompanying-text'], [
+ HtmlElement::create('span', ['class' => 'state-change'], [
+ new StateBall(ServiceStates::text($stateChange->previous_soft_state), StateBall::SIZE_MEDIUM),
+ new StateBall(ServiceStates::text($objectState), StateBall::SIZE_MEDIUM)
+ ]),
+ ' ',
+ FormattedString::create(
+ t('%s on %s', '<service> on <host>'),
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->service->display_name),
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->host->display_name)
+ )
+ ]);
+ }
+
+ $eventInfo = [
+ new HtmlElement('h2', null, Text::create(t('Event Info'))),
+ new HorizontalKeyValue(
+ t('Occurred On'),
+ DateFormatter::formatDateTime($stateChange->event_time->getTimestamp())
+ ),
+ new HorizontalKeyValue(t('Scheduling Source'), $stateChange->scheduling_source),
+ new HorizontalKeyValue(t('Check Source'), $stateChange->check_source)
+ ];
+
+ if ($stateChange->state_type === 'soft') {
+ $eventInfo[] = new HorizontalKeyValue(t('Check Attempt'), sprintf(
+ t('%d of %d'),
+ $stateChange->check_attempt,
+ $stateChange->max_check_attempts
+ ));
+ }
+
+ $eventInfo[] = new HorizontalKeyValue(
+ t('State'),
+ $stateChange->object_type === 'host'
+ ? ucfirst(HostStates::text($objectState))
+ : ucfirst(ServiceStates::text($objectState))
+ );
+
+ $eventInfo[] = new HorizontalKeyValue(
+ t('State Type'),
+ $stateChange->state_type === 'hard' ? t('Hard', 'state') : t('Soft', 'state')
+ );
+
+ $eventInfo[] = new HorizontalKeyValue($objectKey, $objectInfo);
+
+ $this->add(ObjectDetailExtensionHook::injectExtensions([
+ 0 => $pluginOutput,
+ 200 => $eventInfo
+ ], $this->createExtensions()));
+ }
+
+ protected function assembleDowntimeEvent(DowntimeHistory $downtime)
+ {
+ $commentInfo = [
+ new HtmlElement('h2', null, Text::create(t('Comment'))),
+ new MarkdownText($this->createTicketLinks($downtime->comment))
+ ];
+
+ $eventInfo = [new HtmlElement('h2', null, Text::create(t('Event Info')))];
+
+ if ($downtime->triggered_by_id !== null || $downtime->parent_id !== null) {
+ if ($downtime->triggered_by_id !== null) {
+ $label = t('Triggered By');
+ $relatedDowntime = $downtime->triggered_by;
+ } else {
+ $label = t('Parent');
+ $relatedDowntime = $downtime->parent;
+ }
+
+ $query = History::on($this->getDb())
+ ->columns('id')
+ ->filter(Filter::equal('event_type', 'downtime_start'))
+ ->filter(Filter::equal('history.downtime_history_id', $relatedDowntime->downtime_id));
+ $this->applyRestrictions($query);
+ if (($relatedEvent = $query->first()) !== null) {
+ /** @var History $relatedEvent */
+ $eventInfo[] = new HorizontalKeyValue(
+ $label,
+ HtmlElement::create('span', ['class' => 'accompanying-text'], TemplateString::create(
+ $relatedDowntime->is_flexible
+ ? t('{{#link}}Flexible Downtime{{/link}} for %s')
+ : t('{{#link}}Fixed Downtime{{/link}} for %s'),
+ ['link' => new Link(null, Links::event($relatedEvent), ['class' => 'subject'])],
+ ($relatedDowntime->object_type === 'host'
+ ? $this->createHostLink($relatedDowntime->host, true)
+ : $this->createServiceLink($relatedDowntime->service, $relatedDowntime->host, true))
+ ->addAttributes(['class' => 'subject'])
+ ))
+ );
+ }
+ }
+
+ $eventInfo[] = $downtime->object_type === 'host'
+ ? new HorizontalKeyValue(t('Host'), HtmlElement::create(
+ 'span',
+ ['class' => 'accompanying-text'],
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->host->display_name)
+ ))
+ : new HorizontalKeyValue(t('Service'), HtmlElement::create(
+ 'span',
+ ['class' => 'accompanying-text'],
+ FormattedString::create(
+ t('%s on %s', '<service> on <host>'),
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->service->display_name),
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->host->display_name)
+ )
+ ));
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Entered On'),
+ DateFormatter::formatDateTime($downtime->entry_time->getTimestamp())
+ );
+ $eventInfo[] = new HorizontalKeyValue(t('Author'), [new Icon('user'), $downtime->author]);
+ // TODO: The following should be presented in a specific widget (maybe just like the downtime card)
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Triggered On'),
+ DateFormatter::formatDateTime($downtime->trigger_time->getTimestamp())
+ );
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Scheduled Start'),
+ DateFormatter::formatDateTime($downtime->scheduled_start_time->getTimestamp())
+ );
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Actual Start'),
+ DateFormatter::formatDateTime($downtime->start_time->getTimestamp())
+ );
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Scheduled End'),
+ DateFormatter::formatDateTime($downtime->scheduled_end_time->getTimestamp())
+ );
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Actual End'),
+ DateFormatter::formatDateTime($downtime->end_time->getTimestamp())
+ );
+
+ if ($downtime->is_flexible) {
+ $eventInfo[] = new HorizontalKeyValue(t('Flexible'), t('Yes'));
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Duration'),
+ DateFormatter::formatDuration($downtime->flexible_duration / 1000)
+ );
+ }
+
+ $cancelInfo = [];
+ if ($downtime->has_been_cancelled) {
+ $cancelInfo = [
+ new HtmlElement('h2', null, Text::create(t('This downtime has been cancelled'))),
+ new HorizontalKeyValue(
+ t('Cancelled On'),
+ DateFormatter::formatDateTime($downtime->cancel_time->getTimestamp())
+ ),
+ new HorizontalKeyValue(t('Cancelled by'), [new Icon('user'), $downtime->cancelled_by])
+ ];
+ }
+
+
+ $this->add(ObjectDetailExtensionHook::injectExtensions([
+ 200 => $commentInfo,
+ 201 => $eventInfo,
+ 600 => $cancelInfo
+ ], $this->createExtensions()));
+ }
+
+ protected function assembleCommentEvent(CommentHistory $comment)
+ {
+ $commentInfo = [
+ new HtmlElement('h2', null, Text::create(t('Comment'))),
+ new MarkdownText($this->createTicketLinks($comment->comment))
+ ];
+
+ $eventInfo = [new HtmlElement('h2', null, Text::create(t('Event Info')))];
+ $eventInfo[] = $comment->object_type === 'host'
+ ? new HorizontalKeyValue(t('Host'), HtmlElement::create(
+ 'span',
+ ['class' => 'accompanying-text'],
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->host->display_name)
+ ))
+ : new HorizontalKeyValue(t('Service'), HtmlElement::create(
+ 'span',
+ ['class' => 'accompanying-text'],
+ FormattedString::create(
+ t('%s on %s', '<service> on <host>'),
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->service->display_name),
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->host->display_name)
+ )
+ ));
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Entered On'),
+ DateFormatter::formatDateTime($comment->entry_time->getTimestamp())
+ );
+ $eventInfo[] = new HorizontalKeyValue(t('Author'), [new Icon('user'), $comment->author]);
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Expires On'),
+ $comment->expire_time
+ ? DateFormatter::formatDateTime($comment->expire_time->getTimestamp())
+ : new EmptyState(t('Never'))
+ );
+
+ $tiedToAckInfo = [];
+ if ($comment->entry_type === 'ack') {
+ $tiedToAckInfo = [
+ new HtmlElement('h2', null, Text::create(t('This comment is tied to an acknowledgement'))),
+ new HorizontalKeyValue(t('Sticky'), $comment->is_sticky ? t('Yes') : t('No')),
+ new HorizontalKeyValue(t('Persistent'), $comment->is_persistent ? t('Yes') : t('No'))
+ ];
+ }
+
+ $removedInfo = [];
+ if ($comment->has_been_removed) {
+ $removedInfo[] = new HtmlElement('h2', null, Text::create(t('This comment has been removed')));
+ if ($comment->removed_by) {
+ $removedInfo[] = new HorizontalKeyValue(
+ t('Removed On'),
+ DateFormatter::formatDateTime($comment->remove_time->getTimestamp())
+ );
+ $removedInfo[] = new HorizontalKeyValue(
+ t('Removed by'),
+ [new Icon('user'), $comment->removed_by]
+ );
+ } else {
+ $removedInfo[] = new HorizontalKeyValue(
+ t('Expired On'),
+ DateFormatter::formatDateTime($comment->remove_time->getTimestamp())
+ );
+ }
+ }
+
+ $this->add(ObjectDetailExtensionHook::injectExtensions([
+ 200 => $commentInfo,
+ 201 => $eventInfo,
+ 500 => $tiedToAckInfo,
+ 600 => $removedInfo
+ ], $this->createExtensions()));
+ }
+
+ protected function assembleFlappingEvent(FlappingHistory $flapping)
+ {
+ $eventInfo = [
+ new HtmlElement('h2', null, Text::create(t('Event Info'))),
+ $flapping->object_type === 'host'
+ ? new HorizontalKeyValue(t('Host'), HtmlElement::create(
+ 'span',
+ ['class' => 'accompanying-text'],
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->host->display_name)
+ ))
+ : new HorizontalKeyValue(t('Service'), HtmlElement::create(
+ 'span',
+ ['class' => 'accompanying-text'],
+ FormattedString::create(
+ t('%s on %s', '<service> on <host>'),
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->service->display_name),
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->host->display_name)
+ )
+ )),
+ new HorizontalKeyValue(
+ t('Started on'),
+ DateFormatter::formatDateTime($flapping->start_time->getTimestamp())
+ )
+ ];
+ if ($this->event->event_type === 'flapping_start') {
+ $eventInfo[] = new HorizontalKeyValue(t('Reason'), sprintf(
+ t('State change rate of %.2f%% exceeded the threshold (%.2f%%)'),
+ $flapping->percent_state_change_start,
+ $flapping->flapping_threshold_high
+ ));
+ } else {
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Ended on'),
+ DateFormatter::formatDateTime($flapping->end_time->getTimestamp())
+ );
+ $eventInfo[] = new HorizontalKeyValue(t('Reason'), sprintf(
+ t('State change rate of %.2f%% undercut the threshold (%.2f%%)'),
+ $flapping->percent_state_change_end,
+ $flapping->flapping_threshold_low
+ ));
+ }
+
+ $this->add(ObjectDetailExtensionHook::injectExtensions([
+ 200 => $eventInfo
+ ], $this->createExtensions()));
+ }
+
+ protected function assembleAcknowledgeEvent(AcknowledgementHistory $acknowledgement)
+ {
+ $commentInfo = [];
+ if ($acknowledgement->comment) {
+ $commentInfo = [
+ new HtmlElement('h2', null, Text::create(t('Comment'))),
+ new MarkdownText($this->createTicketLinks($acknowledgement->comment))
+ ];
+ } elseif (! isset($acknowledgement->author)) {
+ $commentInfo[] = new EmptyState(t('This acknowledgement was set before Icinga DB history recording'));
+ }
+
+ $eventInfo = [
+ new HtmlElement('h2', null, Text::create(t('Event Info'))),
+ new HorizontalKeyValue(
+ t('Set on'),
+ DateFormatter::formatDateTime($acknowledgement->set_time->getTimestamp())
+ ),
+ new HorizontalKeyValue(t('Author'), $acknowledgement->author
+ ? [new Icon('user'), $acknowledgement->author]
+ : new EmptyState(t('n. a.'))),
+ $acknowledgement->object_type === 'host'
+ ? new HorizontalKeyValue(t('Host'), HtmlElement::create(
+ 'span',
+ ['class' => 'accompanying-text'],
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->host->display_name)
+ ))
+ : new HorizontalKeyValue(t('Service'), HtmlElement::create(
+ 'span',
+ ['class' => 'accompanying-text'],
+ FormattedString::create(
+ t('%s on %s', '<service> on <host>'),
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->service->display_name),
+ HtmlElement::create('span', ['class' => 'subject'], $this->event->host->display_name)
+ )
+ ))
+ ];
+
+ if ($this->event->event_type === 'ack_set') {
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Expires On'),
+ $acknowledgement->expire_time
+ ? DateFormatter::formatDateTime($acknowledgement->expire_time->getTimestamp())
+ : new EmptyState(t('Never'))
+ );
+ $eventInfo[] = new HorizontalKeyValue(t('Sticky'), isset($acknowledgement->is_sticky)
+ ? ($acknowledgement->is_sticky ? t('Yes') : t('No'))
+ : new EmptyState(t('n. a.')));
+ $eventInfo[] = new HorizontalKeyValue(t('Persistent'), isset($acknowledgement->is_persistent)
+ ? ($acknowledgement->is_persistent ? t('Yes') : t('No'))
+ : new EmptyState(t('n. a.')));
+ } else {
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Cleared on'),
+ DateFormatter::formatDateTime(
+ $acknowledgement->clear_time
+ ? $acknowledgement->clear_time->getTimestamp()
+ : $this->event->event_time->getTimestamp()
+ )
+ );
+ if ($acknowledgement->cleared_by) {
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Cleared by'),
+ [new Icon('user', $acknowledgement->cleared_by)]
+ );
+ } else {
+ $expired = false;
+ if ($acknowledgement->expire_time) {
+ $now = (new DateTime())->setTimezone(new DateTimeZone('UTC'));
+ $expiresOn = clone $now;
+ $expiresOn->setTimestamp($acknowledgement->expire_time->getTimestamp());
+ if ($now <= $expiresOn) {
+ $expired = true;
+ $eventInfo[] = new HorizontalKeyValue(t('Removal Reason'), t(
+ 'The acknowledgement expired on %s',
+ DateFormatter::formatDateTime($acknowledgement->expire_time->getTimestamp())
+ ));
+ }
+ }
+
+ if (! $expired) {
+ if ($acknowledgement->is_sticky) {
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Reason'),
+ $acknowledgement->object_type === 'host'
+ ? t('Host recovered')
+ : t('Service recovered')
+ );
+ } else {
+ $eventInfo[] = new HorizontalKeyValue(
+ t('Reason'),
+ $acknowledgement->object_type === 'host'
+ ? t('Host recovered') // Hosts have no other state between UP and DOWN
+ : t('Service changed its state')
+ );
+ }
+ }
+ }
+ }
+
+ $this->add(ObjectDetailExtensionHook::injectExtensions([
+ 200 => $commentInfo,
+ 201 => $eventInfo
+ ], $this->createExtensions()));
+ }
+
+ protected function createExtensions(): array
+ {
+ return ObjectDetailExtensionHook::loadExtensions($this->event);
+ }
+
+ protected function assemble()
+ {
+ switch ($this->event->event_type) {
+ case 'notification':
+ $this->assembleNotificationEvent($this->event->notification);
+
+ break;
+ case 'state_change':
+ $this->assembleStateChangeEvent($this->event->state);
+
+ break;
+ case 'downtime_start':
+ case 'downtime_end':
+ $this->assembleDowntimeEvent($this->event->downtime);
+
+ break;
+ case 'comment_add':
+ case 'comment_remove':
+ $this->assembleCommentEvent($this->event->comment);
+
+ break;
+ case 'flapping_start':
+ case 'flapping_end':
+ $this->assembleFlappingEvent($this->event->flapping);
+
+ break;
+ case 'ack_set':
+ case 'ack_clear':
+ $this->assembleAcknowledgeEvent($this->event->acknowledgement);
+
+ break;
+ }
+ }
+}