db = $db; if ($type !== null) { $this->setType($type); } $this->name = $name; } public function setType($type) { $this->type = $type; $this->typeName = $this->translate( ucfirst(preg_replace('/^icinga_/', '', $type)) // really? ); return $this; } /** * @param Url $url * @return HtmlElement * @throws \Icinga\Exception\IcingaException */ public function getPagination(Url $url) { /** @var Url $url */ $url = $url->without('checksum')->without('show'); $div = Html::tag('div', [ 'class' => ['pagination-control', 'activity-log-control'], ]); $ul = Html::tag('ul', ['class' => 'nav tab-nav']); $li = Html::tag('li', ['class' => 'nav-item']); $ul->add($li); $neighbors = $this->getNeighbors(); $iconLeft = new Icon('angle-double-left'); $iconRight = new Icon('angle-double-right'); if ($neighbors->prev) { $li->add(new Link($iconLeft, $url->with('id', $neighbors->prev))); } else { $li->add(Html::tag('span', ['class' => 'disabled'], $iconLeft)); } $li = Html::tag('li', ['class' => 'nav-item']); $ul->add($li); if ($neighbors->next) { $li->add(new Link($iconRight, $url->with('id', $neighbors->next))); } else { $li->add(Html::tag('span', ['class' => 'disabled'], $iconRight)); } return $div->add($ul); } /** * @param $tabName * @return $this * @throws \Icinga\Exception\Http\HttpNotFoundException * @throws \Icinga\Exception\IcingaException */ public function showTab($tabName) { if ($tabName === null) { $tabName = $this->defaultTab; } $this->getTabs()->activate($tabName); $this->add($this->getInfoTable()); if ($tabName === 'old') { // $title = sprintf('%s former config', $this->entry->object_name); $diffs = IcingaConfigDiff::getDiffs($this->oldConfig(), $this->emptyConfig()); } elseif ($tabName === 'new') { // $title = sprintf('%s new config', $this->entry->object_name); $diffs = IcingaConfigDiff::getDiffs($this->emptyConfig(), $this->newConfig()); } else { $diffs = IcingaConfigDiff::getDiffs($this->oldConfig(), $this->newConfig()); } $this->addDiffs($diffs); return $this; } protected function emptyConfig() { return new IcingaConfig($this->db); } /** * @param $diffs * @throws \Icinga\Exception\IcingaException */ protected function addDiffs($diffs) { foreach ($diffs as $file => $diff) { $this->add(Html::tag('h3', null, $file))->add($diff); } } /** * @return RestoreObjectForm * @throws \Icinga\Exception\IcingaException */ protected function getRestoreForm() { return RestoreObjectForm::load() ->setDb($this->db) ->setObject($this->oldObject()) ->handleRequest(); } public function setChecksum($checksum) { if ($checksum !== null) { $this->entry = $this->db->fetchActivityLogEntry($checksum); $this->id = (int) $this->entry->id; } return $this; } public function setId($id) { if ($id !== null) { $this->entry = $this->db->fetchActivityLogEntryById($id); $this->id = (int) $id; } return $this; } public function getNeighbors() { return $this->db->getActivitylogNeighbors( $this->id, $this->type, $this->name ); } public function getCurrentObject() { return IcingaObject::loadByType( $this->type, $this->name, $this->db ); } /** * @return bool * @deprecated No longer used? */ public function objectStillExists() { return IcingaObject::existsByType( $this->type, $this->objectKey(), $this->db ); } protected function oldProperties() { if ($this->oldProperties === null) { if (property_exists($this->entry, 'old_properties')) { $this->oldProperties = JsonString::decodeOptional($this->entry->old_properties); } if ($this->oldProperties === null) { $this->oldProperties = new \stdClass; } } return $this->oldProperties; } protected function newProperties() { if ($this->newProperties === null) { if (property_exists($this->entry, 'new_properties')) { $this->newProperties = JsonString::decodeOptional($this->entry->new_properties); } if ($this->newProperties === null) { $this->newProperties = new \stdClass; } } return $this->newProperties; } protected function getEntryProperty($key) { $entry = $this->entry; if (property_exists($entry, $key)) { return $entry->{$key}; } elseif (property_exists($this->newProperties(), $key)) { return $this->newProperties->{$key}; } elseif (property_exists($this->oldProperties(), $key)) { return $this->oldProperties->{$key}; } else { return null; } } protected function objectLinkParams() { $entry = $this->entry; $params = ['name' => $entry->object_name]; if ($entry->object_type === 'icinga_service') { if (($set = $this->getEntryProperty('service_set')) !== null) { $params['set'] = $set; return $params; } elseif (($host = $this->getEntryProperty('host')) !== null) { $params['host'] = $host; return $params; } else { return $params; } } elseif ($entry->object_type === 'icinga_service_set') { return $params; } else { return $params; } } protected function getActionExtraHtml() { $entry = $this->entry; $info = ''; $host = null; if ($entry->object_type === 'icinga_service') { if (($set = $this->getEntryProperty('service_set')) !== null) { $info = Html::sprintf( '%s "%s"', $this->translate('on service set'), Link::create( $set, 'director/serviceset', ['name' => $set], ['data-base-target' => '_next'] ) ); } else { $host = $this->getEntryProperty('host'); } } elseif ($entry->object_type === 'icinga_service_set') { $host = $this->getEntryProperty('host'); } if ($host !== null) { $info = Html::sprintf( '%s "%s"', $this->translate('on host'), Link::create( $host, 'director/host', ['name' => $host], ['data-base-target' => '_next'] ) ); } return $info; } /** * @return array * @deprecated No longer used? */ protected function objectKey() { $entry = $this->entry; if ($entry->object_type === 'icinga_service' || $entry->object_type === 'icinga_service_set') { // TODO: this is not correct. Activity needs to get (multi) key support return ['name' => $entry->object_name]; } return $entry->object_name; } /** * @param Url|null $url * @return Tabs */ public function getTabs(Url $url = null) { if ($this->tabs === null) { $this->tabs = $this->createTabs($url); } return $this->tabs; } /** * @param Url $url * @return Tabs */ public function createTabs(Url $url) { $entry = $this->entry; $tabs = new Tabs(); if ($entry->action_name === DirectorActivityLog::ACTION_MODIFY) { $tabs->add('diff', [ 'label' => $this->translate('Diff'), 'url' => $url->without('show')->with('id', $entry->id) ]); $this->defaultTab = 'diff'; } if (in_array($entry->action_name, [ DirectorActivityLog::ACTION_CREATE, DirectorActivityLog::ACTION_MODIFY, ])) { $tabs->add('new', [ 'label' => $this->translate('New object'), 'url' => $url->with(['id' => $entry->id, 'show' => 'new']) ]); if ($this->defaultTab === null) { $this->defaultTab = 'new'; } } if (in_array($entry->action_name, [ DirectorActivityLog::ACTION_DELETE, DirectorActivityLog::ACTION_MODIFY, ])) { $tabs->add('old', [ 'label' => $this->translate('Former object'), 'url' => $url->with(['id' => $entry->id, 'show' => 'old']) ]); if ($this->defaultTab === null) { $this->defaultTab = 'old'; } } return $tabs; } /** * @return IcingaObject * @throws \Icinga\Exception\IcingaException */ protected function oldObject() { if ($this->oldObject === null) { $this->oldObject = $this->createObject( $this->entry->object_type, $this->entry->old_properties ); } return $this->oldObject; } /** * @return IcingaObject * @throws \Icinga\Exception\IcingaException */ protected function newObject() { return $this->createObject( $this->entry->object_type, $this->entry->new_properties ); } protected function objectToConfig(IcingaObject $object) { if ($object instanceof IcingaService) { return $this->previewService($object); } elseif ($object instanceof IcingaServiceSet) { return $this->previewServiceSet($object); } else { return $object->toSingleIcingaConfig(); } } /** * Render service set to be previewed * * @param IcingaServiceSet $object * * @return IcingaConfig */ protected function previewServiceSet(IcingaServiceSet $object) { $config = $object->toSingleIcingaConfig(); foreach ($object->getCachedServices() as $service) { $service->renderToConfig($config); } return $config; } protected function previewService(IcingaService $service) { if (($set = $service->get('service_set')) !== null) { // simulate rendering of service in set $set = IcingaServiceSet::load($set, $this->db); $service->set('service_set_id', null); if (($assign = $set->get('assign_filter')) !== null) { $service->set('object_type', 'apply'); $service->set('assign_filter', $assign); } } return $service->toSingleIcingaConfig(); } /** * @return IcingaConfig * @throws \Icinga\Exception\IcingaException */ protected function newConfig() { return $this->objectToConfig($this->newObject()); } /** * @return IcingaConfig * @throws \Icinga\Exception\IcingaException */ protected function oldConfig() { return $this->objectToConfig($this->oldObject()); } protected function getLinkToObject() { // TODO: This logic is redundant and should be centralized $entry = $this->entry; $name = $entry->object_name; $controller = preg_replace('/^icinga_/', '', $entry->object_type); if ($controller === 'service_set') { $controller = 'serviceset'; } elseif ($controller === 'scheduled_downtime') { $controller = 'scheduled-downtime'; } return Link::create( $name, 'director/' . $controller, $this->objectLinkParams(), ['data-base-target' => '_next'] ); } /** * @return NameValueTable * @throws \Icinga\Exception\IcingaException */ public function getInfoTable() { $entry = $this->entry; $table = new NameValueTable(); $table->addNameValuePairs([ $this->translate('Author') => $entry->author, $this->translate('Date') => DateFormatter::formatDateTime( $entry->change_time_ts ), ]); if (null === $this->name) { $table->addNameValueRow( $this->translate('Action'), Html::sprintf( '%s %s "%s" %s', $entry->action_name, $entry->object_type, $this->getLinkToObject(), $this->getActionExtraHtml() ) ); } else { $table->addNameValueRow( $this->translate('Action'), $entry->action_name ); } if ($comment = $this->getOptionalRangeComment()) { $table->addNameValueRow( $this->translate('Remark'), $comment ); } if ($this->hasBeenEnabled()) { $table->addNameValueRow( $this->translate('Rendering'), $this->translate('This object has been enabled') ); } elseif ($this->hasBeenDisabled()) { $table->addNameValueRow( $this->translate('Rendering'), $this->translate('This object has been disabled') ); } $table->addNameValueRow( $this->translate('Checksum'), $entry->checksum ); if ($this->entry->old_properties) { $table->addNameValueRow( $this->translate('Actions'), $this->getRestoreForm() ); } return $table; } public function hasBeenEnabled() { return false; } public function hasBeenDisabled() { return false; } /** * @return string * @throws ProgrammingError */ public function getTitle() { switch ($this->entry->action_name) { case DirectorActivityLog::ACTION_CREATE: $msg = $this->translate('%s "%s" has been created'); break; case DirectorActivityLog::ACTION_DELETE: $msg = $this->translate('%s "%s" has been deleted'); break; case DirectorActivityLog::ACTION_MODIFY: $msg = $this->translate('%s "%s" has been modified'); break; default: throw new ProgrammingError( 'Unable to deal with "%s" activity', $this->entry->action_name ); } return sprintf($msg, $this->typeName, $this->entry->object_name); } protected function getOptionalRangeComment() { if ($this->id) { $db = $this->db->getDbAdapter(); return $db->fetchOne( $db->select() ->from('director_activity_log_remark', 'remark') ->where('first_related_activity <= ?', $this->id) ->where('last_related_activity >= ?', $this->id) ); } return null; } /** * @param $type * @param $props * @return IcingaObject * @throws \Icinga\Exception\IcingaException */ protected function createObject($type, $props) { $props = json_decode($props); $newProps = ['object_name' => $props->object_name]; if (property_exists($props, 'object_type')) { $newProps['object_type'] = $props->object_type; } $object = IcingaObject::createByType( $type, $newProps, $this->db ); if ($type === 'icinga_service_set' && isset($props->services)) { $services = []; foreach ($props->services as $service) { $services[$service->object_name] = IcingaObject::createByType( 'icinga_service', (array) $service, $this->db ); } /** @var IcingaServiceSet $object */ $object->setCachedServices($services); unset($props->services); } return $object->setProperties((array) $props); } }