diff --git a/application/controllers/HostController.php b/application/controllers/HostController.php index 097f3963e..ec1ca0cd4 100644 --- a/application/controllers/HostController.php +++ b/application/controllers/HostController.php @@ -150,6 +150,35 @@ public function historyAction(): Generator ); $viewModeSwitcher = $this->createViewModeSwitcher($paginationControl, $limitControl, true); + $preserveParams = [ + $limitControl->getLimitParam(), + $sortControl->getSortParam(), + $viewModeSwitcher->getViewModeParam(), + 'name' + ]; + + $requestParams = Url::fromRequest()->onlyWith($preserveParams)->getParams(); + $searchBar = $this->createSearchBar($history, $preserveParams) + ->setEditorUrl( + Url::fromPath('icingadb/host/history-search-editor') + ->setParams($requestParams) + )->setSuggestionUrl( + Url::fromPath('icingadb/host/history-complete') + ->setParams(clone $requestParams) + ); + + if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) { + if ($searchBar->hasBeenSubmitted()) { + $filter = $this->getFilter(); + } else { + $this->addControl($searchBar); + $this->sendMultipartUpdate(); + return; + } + } else { + $filter = $searchBar->getFilter(); + } + $history->peekAhead(); $page = $paginationControl->getCurrentPageNumber(); @@ -160,17 +189,19 @@ public function historyAction(): Generator } $history->filter(Filter::lessThanOrEqual('event_time', $before)); + $this->filter($history, $filter); yield $this->export($history); $this->addControl($sortControl); $this->addControl($limitControl); $this->addControl($viewModeSwitcher); + $this->addControl($searchBar); $historyList = (new LoadMoreObjectList($history->execute())) ->setViewMode($viewModeSwitcher->getViewMode()) ->setPageSize($limitControl->getLimit()) - ->setLoadMoreUrl($url->setParam('before', $before)); + ->setLoadMoreUrl($url->setParam('before', $before)->setFilter($filter)); if ($compact) { $historyList->setPageNumber($page); @@ -181,6 +212,10 @@ public function historyAction(): Generator } else { $this->addContent($historyList); } + + if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { + $this->sendMultipartUpdate(); + } } public function servicesAction(): Generator @@ -218,6 +253,37 @@ public function servicesAction(): Generator ['service.state.severity DESC', 'service.state.last_state_change DESC'] ); + $preserveParams = [ + $limitControl->getLimitParam(), + $sortControl->getSortParam(), + $viewModeSwitcher->getViewModeParam(), + 'name' + ]; + + $requestParams = Url::fromRequest()->onlyWith($preserveParams)->getParams(); + $searchBar = $this->createSearchBar($services, $preserveParams) + ->setEditorUrl( + Url::fromPath('icingadb/host/services-search-editor') + ->setParams($requestParams) + )->setSuggestionUrl( + Url::fromPath('icingadb/host/services-complete') + ->setParams(clone $requestParams) + ); + + if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) { + if ($searchBar->hasBeenSubmitted()) { + $filter = $this->getFilter(); + } else { + $this->addControl($searchBar); + $this->sendMultipartUpdate(); + return; + } + } else { + $filter = $searchBar->getFilter(); + } + + $services->filter($filter); + yield $this->export($services); $serviceList = (new ObjectList($services)) @@ -228,9 +294,18 @@ public function servicesAction(): Generator $this->addControl($sortControl); $this->addControl($limitControl); $this->addControl($viewModeSwitcher); + $this->addControl($searchBar); + $continueWith = $this->createContinueWith( + Links::servicesDetails()->setFilter(Filter::equal('host.name', $this->host->name)), + $searchBar + ); $this->addContent($serviceList); + if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { + $this->sendMultipartUpdate($continueWith); + } + $this->setAutorefreshInterval(10); } @@ -391,6 +466,26 @@ public function childrenCompleteAction(): void $this->getDocument()->add($suggestions); } + public function historyCompleteAction(): void + { + $suggestions = (new ObjectSuggestions()) + ->setModel(History::class) + ->setBaseFilter(Filter::equal('host.id', $this->host->id)) + ->forRequest($this->getServerRequest()); + + $this->getDocument()->addHtml($suggestions); + } + + public function servicesCompleteAction(): void + { + $suggestions = (new ObjectSuggestions()) + ->setModel(Service::class) + ->setBaseFilter(Filter::equal('host.id', $this->host->id)) + ->forRequest($this->getServerRequest()); + + $this->getDocument()->addHtml($suggestions); + } + public function searchEditorAction(): void { $editor = $this->createSearchEditor( @@ -432,6 +527,50 @@ public function childrenSearchEditorAction(): void $this->setTitle($this->translate('Adjust Filter')); } + public function historySearchEditorAction(): void + { + $preserveParams = [ + LimitControl::DEFAULT_LIMIT_PARAM, + SortControl::DEFAULT_SORT_PARAM, + ViewModeSwitcher::DEFAULT_VIEW_MODE_PARAM, + 'name' + ]; + $editor = $this->createSearchEditor( + History::on($this->getDb()), + Url::fromPath('icingadb/host/history', ['name' => $this->host->name]), + $preserveParams + ); + $editor->setSuggestionUrl( + Url::fromPath('icingadb/host/history-complete') + ->setParams(Url::fromRequest()->onlyWith($preserveParams)->getParams()) + ); + + $this->getDocument()->addHtml($editor); + $this->setTitle($this->translate('Adjust Filter')); + } + + public function servicesSearchEditorAction(): void + { + $preserveParams = [ + LimitControl::DEFAULT_LIMIT_PARAM, + SortControl::DEFAULT_SORT_PARAM, + ViewModeSwitcher::DEFAULT_VIEW_MODE_PARAM, + 'name' + ]; + $editor = $this->createSearchEditor( + Service::on($this->getDb()), + Url::fromPath('icingadb/host/services', ['name' => $this->host->name]), + $preserveParams + ); + $editor->setSuggestionUrl( + Url::fromPath('icingadb/host/services-complete') + ->setParams(Url::fromRequest()->onlyWith($preserveParams)->getParams()) + ); + + $this->getDocument()->addHtml($editor); + $this->setTitle($this->translate('Adjust Filter')); + } + /** * Fetch the dependency nodes of the current host * diff --git a/application/controllers/ServiceController.php b/application/controllers/ServiceController.php index 28abca424..20a5d7d70 100644 --- a/application/controllers/ServiceController.php +++ b/application/controllers/ServiceController.php @@ -300,6 +300,36 @@ public function historyAction(): Generator ); $viewModeSwitcher = $this->createViewModeSwitcher($paginationControl, $limitControl, true); + $preserveParams = [ + $limitControl->getLimitParam(), + $sortControl->getSortParam(), + $viewModeSwitcher->getViewModeParam(), + 'name', + 'host.name' + ]; + + $requestParams = Url::fromRequest()->onlyWith($preserveParams)->getParams(); + $searchBar = $this->createSearchBar($history, $preserveParams) + ->setEditorUrl( + Url::fromPath('icingadb/service/history-search-editor') + ->setParams($requestParams) + )->setSuggestionUrl( + Url::fromPath('icingadb/service/history-complete') + ->setParams(clone $requestParams) + ); + + if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) { + if ($searchBar->hasBeenSubmitted()) { + $filter = $this->getFilter(); + } else { + $this->addControl($searchBar); + $this->sendMultipartUpdate(); + return; + } + } else { + $filter = $searchBar->getFilter(); + } + $history->peekAhead(); $page = $paginationControl->getCurrentPageNumber(); @@ -310,17 +340,19 @@ public function historyAction(): Generator } $history->filter(Filter::lessThanOrEqual('event_time', $before)); + $this->filter($history, $filter); yield $this->export($history); $this->addControl($sortControl); $this->addControl($limitControl); $this->addControl($viewModeSwitcher); + $this->addControl($searchBar); $historyList = (new LoadMoreObjectList($history->execute())) ->setViewMode($viewModeSwitcher->getViewMode()) ->setPageSize($limitControl->getLimit()) - ->setLoadMoreUrl($url->setParam('before', $before)); + ->setLoadMoreUrl($url->setParam('before', $before)->setFilter($filter)); if ($compact) { $historyList->setPageNumber($page); @@ -331,6 +363,10 @@ public function historyAction(): Generator } else { $this->addContent($historyList); } + + if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { + $this->sendMultipartUpdate(); + } } public function completeAction(): void @@ -355,6 +391,16 @@ public function childrenCompleteAction(): void $this->getDocument()->add($suggestions); } + public function historyCompleteAction(): void + { + $suggestions = (new ObjectSuggestions()) + ->setModel(History::class) + ->setBaseFilter(Filter::equal('service.id', $this->service->id)) + ->forRequest($this->getServerRequest()); + + $this->getDocument()->addHtml($suggestions); + } + public function searchEditorAction(): void { $editor = $this->createSearchEditor( @@ -404,6 +450,29 @@ public function childrenSearchEditorAction(): void $this->setTitle($this->translate('Adjust Filter')); } + public function historySearchEditorAction(): void + { + $preserveParams = [ + LimitControl::DEFAULT_LIMIT_PARAM, + SortControl::DEFAULT_SORT_PARAM, + ViewModeSwitcher::DEFAULT_VIEW_MODE_PARAM, + 'name', + 'host.name' + ]; + $editor = $this->createSearchEditor( + History::on($this->getDb()), + Url::fromPath('icingadb/service/history', ['name' => $this->service->name]), + $preserveParams + ); + $editor->setSuggestionUrl( + Url::fromPath('icingadb/service/history-complete') + ->setParams(Url::fromRequest()->onlyWith($preserveParams)->getParams()) + ); + + $this->getDocument()->addHtml($editor); + $this->setTitle($this->translate('Adjust Filter')); + } + /** * Fetch the dependency nodes of the current service * diff --git a/library/Icingadb/Hook/ActionsHook/ObjectActionsHook.php b/library/Icingadb/Hook/ActionsHook/ObjectActionsHook.php index 13de8cd66..1461e90b0 100644 --- a/library/Icingadb/Hook/ActionsHook/ObjectActionsHook.php +++ b/library/Icingadb/Hook/ActionsHook/ObjectActionsHook.php @@ -61,6 +61,10 @@ final public static function loadActions(Model $object): HtmlElement continue; } + if ($link->getBaseTarget() === null && ! $link->hasAttribute('target')) { + $link->setBaseTarget('_next'); + } + // It may be ValidHtml, but modules shouldn't be able to break our views. // That's why it needs to be rendered instantly, as any error will then // be caught here. diff --git a/library/Icingadb/Widget/Detail/HostDetail.php b/library/Icingadb/Widget/Detail/HostDetail.php index 17e1ab565..4c285d754 100644 --- a/library/Icingadb/Widget/Detail/HostDetail.php +++ b/library/Icingadb/Widget/Detail/HostDetail.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Icingadb\Widget\Detail; +use Icinga\Module\Icingadb\Common\HostLinks; use Icinga\Module\Icingadb\Hook\ExtensionHook\ObjectDetailExtensionHook; use Icinga\Module\Icingadb\Model\Host; use Icinga\Module\Icingadb\Model\ServicestateSummary; @@ -25,8 +26,8 @@ public function __construct(Host $object, ServicestateSummary $serviceSummary) protected function createServiceStatistics(): array { if ($this->serviceSummary->services_total > 0) { - $services = new ServiceStatistics($this->serviceSummary); - $services->setBaseFilter(Filter::equal('host.name', $this->object->name)); + $services = (new ServiceStatistics($this->serviceSummary)) + ->setUrl(HostLinks::services($this->object)); } else { $services = new EmptyState(t('This host has no services')); } diff --git a/library/Icingadb/Widget/Detail/ObjectDetail.php b/library/Icingadb/Widget/Detail/ObjectDetail.php index 0aa727f23..c2b3726ac 100644 --- a/library/Icingadb/Widget/Detail/ObjectDetail.php +++ b/library/Icingadb/Widget/Detail/ObjectDetail.php @@ -43,7 +43,6 @@ use Icinga\Module\Icingadb\Widget\ShowMore; use ipl\Sql\Expression; use ipl\Sql\Filter\Exists; -use ipl\Web\Url; use ipl\Web\Widget\CopyToClipboard; use ipl\Web\Widget\EmptyState; use ipl\Web\Widget\EmptyStateBar; diff --git a/library/Icingadb/Widget/Detail/ObjectHeader.php b/library/Icingadb/Widget/Detail/ObjectHeader.php index 4036f579d..8991ff8fc 100644 --- a/library/Icingadb/Widget/Detail/ObjectHeader.php +++ b/library/Icingadb/Widget/Detail/ObjectHeader.php @@ -42,6 +42,8 @@ */ class ObjectHeader extends BaseHtmlElement { + protected $defaultAttributes = ['data-base-target' => '_next']; + /** @var Item */ protected $object; diff --git a/library/Icingadb/Widget/Detail/ServiceStatistics.php b/library/Icingadb/Widget/Detail/ServiceStatistics.php index 51aced122..eab2ff920 100644 --- a/library/Icingadb/Widget/Detail/ServiceStatistics.php +++ b/library/Icingadb/Widget/Detail/ServiceStatistics.php @@ -5,9 +5,11 @@ namespace Icinga\Module\Icingadb\Widget\Detail; use Icinga\Chart\Donut; +use Icinga\Module\Icingadb\Common\HostLinks; use Icinga\Module\Icingadb\Common\Links; use Icinga\Module\Icingadb\Widget\ServiceStateBadges; use ipl\Html\ValidHtml; +use ipl\Web\Url; use ipl\Web\Widget\VerticalKeyValue; use ipl\Html\HtmlString; use ipl\Web\Widget\Link; @@ -16,11 +18,40 @@ class ServiceStatistics extends ObjectStatistics { protected $summary; + /** @var ?Url */ + protected ?Url $url; + public function __construct($summary) { $this->summary = $summary; } + /** + * Return the URL pointing to all matching services. + * + * If not set, the URL of the services overview is returned as fallback. + * + * @return Url + */ + public function getUrl(): Url + { + return $this->url ?? Links::services(); + } + + /** + * Set the URL pointing to all matching services. + * + * @param Url $url The URL to set. + * + * @return $this + */ + public function setUrl(Url $url): self + { + $this->url = $url; + + return $this; + } + protected function createDonut(): ValidHtml { $donut = (new Donut()) @@ -38,7 +69,7 @@ protected function createDonut(): ValidHtml protected function createTotal(): ValidHtml { - $url = Links::services(); + $url = $this->getUrl(); if ($this->hasBaseFilter()) { $url->setFilter($this->getBaseFilter()); } @@ -59,6 +90,6 @@ protected function createBadges(): ValidHtml $badges->setBaseFilter($this->getBaseFilter()); } - return $badges; + return $badges->setUrl($this->getUrl()); } } diff --git a/library/Icingadb/Widget/TagList.php b/library/Icingadb/Widget/TagList.php index 6a28a9cc7..bfa47ab83 100644 --- a/library/Icingadb/Widget/TagList.php +++ b/library/Icingadb/Widget/TagList.php @@ -12,7 +12,7 @@ class TagList extends BaseHtmlElement { protected $content = []; - protected $defaultAttributes = ['class' => 'tag-list']; + protected $defaultAttributes = ['class' => 'tag-list', 'data-base-target' => '_next']; protected $tag = 'div';