diff options
Diffstat (limited to 'library/Cube/Web/Controller.php')
-rw-r--r-- | library/Cube/Web/Controller.php | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/library/Cube/Web/Controller.php b/library/Cube/Web/Controller.php new file mode 100644 index 0000000..028a744 --- /dev/null +++ b/library/Cube/Web/Controller.php @@ -0,0 +1,297 @@ +<?php + +// Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2 + +namespace Icinga\Module\Cube\Web; + +use Icinga\Module\Cube\DimensionParams; +use Icinga\Module\Cube\Forms\DimensionsForm; +use Icinga\Module\Cube\Hook\IcingaDbActionsHook; +use Icinga\Module\Cube\IcingaDb\CustomVariableDimension; +use Icinga\Module\Cube\IcingaDb\IcingaDbCube; +use Icinga\Module\Icingadb\Common\Auth; +use Icinga\Module\Icingadb\Common\Database; +use Icinga\Module\Icingadb\Web\Control\ProblemToggle; +use ipl\Html\FormElement\CheckboxElement; +use ipl\Html\HtmlString; +use ipl\Stdlib\Filter; +use ipl\Stdlib\Str; +use ipl\Web\Compat\CompatController; +use ipl\Web\Compat\SearchControls; +use ipl\Web\Control\SortControl; +use ipl\Web\Filter\QueryString; +use ipl\Web\Url; +use ipl\Web\Widget\Tabs; + +abstract class Controller extends CompatController +{ + use SearchControls; + use Database; + use Auth; + + /** @var string[] Preserved params for searchbar and search editor controls */ + protected $preserveParams = [ + 'dimensions', + 'showSettings', + 'wantNull', + 'problems', + 'sort' + ]; + + /** @var Filter\Rule Filter from query string parameters */ + private $filter; + + /** + * Return this controllers' cube + * + * @return IcingaDbCube + */ + abstract protected function getCube(): IcingaDbCube; + + /** + * Get the filter created from query string parameters + * + * @return Filter\Rule + */ + public function getFilter(): Filter\Rule + { + if ($this->filter === null) { + $this->filter = QueryString::parse((string) $this->params); + } + + return $this->filter; + } + + public function detailsAction(): void + { + $cube = $this->prepareCube(); + $this->getTabs()->add('details', [ + 'label' => $this->translate('Cube details'), + 'url' => $this->getRequest()->getUrl() + ])->activate('details'); + + $cube->setBaseFilter($this->getFilter()); + + $this->setTitle($cube->getSlicesLabel()); + $this->view->links = IcingaDbActionsHook::renderAll($cube); + + $this->addContent( + HtmlString::create($this->view->render('/cube-details.phtml')) + ); + } + + protected function renderCube(): void + { + $cube = $this->prepareCube(); + $this->setTitle(sprintf( + $this->translate('Cube: %s'), + $cube->getPathLabel() + )); + + $this->params->shift('format'); + $showSettings = $this->params->shift('showSettings'); + + $query = $cube->innerQuery(); + $problemsOnly = (bool) $this->params->shift('problems', false); + $problemToggle = (new ProblemToggle($problemsOnly ?: null)) + ->setIdProtector([$this->getRequest(), 'protectId']) + ->on(ProblemToggle::ON_SUCCESS, function (ProblemToggle $form) { + /** @var CheckboxElement $problems */ + $problems = $form->getElement('problems'); + if (! $problems->isChecked()) { + $this->redirectNow(Url::fromRequest()->remove('problems')); + } else { + $this->redirectNow(Url::fromRequest()->setParam('problems')); + } + })->handleRequest($this->getServerRequest()); + + $this->addControl($problemToggle); + + $sortControl = SortControl::create([ + IcingaDbCube::DIMENSION_VALUE_SORT_PARAM => t('Value'), + IcingaDbCube::DIMENSION_SEVERITY_SORT_PARAM . ' desc' => t('Severity'), + ]); + + $this->params->shift($sortControl->getSortParam()); + $cube->sortBy($sortControl->getSort()); + $this->addControl($sortControl); + + $searchBar = $this->createSearchBar( + $query, + $this->preserveParams + ); + + if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) { + if ($searchBar->hasBeenSubmitted()) { + $filter = $this->getFilter(); + } else { + $this->addControl($searchBar); + $this->sendMultipartUpdate(); + return; + } + } else { + $filter = $searchBar->getFilter(); + } + + if ($problemsOnly) { + $filter = Filter::all($filter, Filter::equal('state.is_problem', true)); + } + + $cube->setBaseFilter($filter); + $cube->problemsOnly($problemsOnly); + + $this->addControl($searchBar); + + if (count($cube->listDimensions()) > 0) { + $this->view->cube = $cube; + } else { + $showSettings = true; + } + + $this->view->url = Url::fromRequest() + ->onlyWith($this->preserveParams) + ->setFilter($searchBar->getFilter()); + + if ($showSettings) { + $form = (new DimensionsForm()) + ->setUrl($this->view->url) + ->setCube($cube) + ->on(DimensionsForm::ON_SUCCESS, function ($form) { + $this->redirectNow($form->getRedirectUrl()); + }) + ->handleRequest($this->getServerRequest()); + + $this->view->form = $form; + } else { + $this->setAutorefreshInterval(15); + } + + $this->addContent( + HtmlString::create($this->view->render('/cube-index.phtml')) + ); + + if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { + $this->sendMultipartUpdate(); + } + } + + private function prepareCube(): IcingaDbCube + { + $cube = $this->getCube(); + $cube->chooseFacts(array_keys($cube->getAvailableFactColumns())); + + $dimensions = DimensionParams::fromString( + $this->params->shift('dimensions', '') + )->getDimensions(); + + if ($this->hasLegacyDimensionParams($dimensions)) { + $this->transformLegacyDimensionParamsAndRedirect($dimensions); + } + + $wantNull = $this->params->shift('wantNull'); + foreach ($dimensions as $dimension) { + $cube->addDimensionByName($dimension); + if ($wantNull) { + $cube->getDimension($dimension)->wantNull(); + } + + $sliceParamWithPrefix = rawurlencode($cube::SLICE_PREFIX . $dimension); + + if ($this->params->has($sliceParamWithPrefix)) { + $this->preserveParams[] = $sliceParamWithPrefix; + $cube->slice($dimension, $this->params->shift($sliceParamWithPrefix)); + } + } + + return $cube; + } + + /** + * Get whether the given dimension param is legacy dimension param + * + * @param string $dimensionParam + * + * @return bool + */ + private function isLegacyDimensionParam(string $dimensionParam): bool + { + return ! Str::startsWith($dimensionParam, CustomVariableDimension::HOST_PREFIX) + && ! Str::startsWith($dimensionParam, CustomVariableDimension::SERVICE_PREFIX); + } + + /** + * Get whether the dimensions contain legacy dimension + * + * @param array $dimensions + * + * @return bool + */ + private function hasLegacyDimensionParams(array $dimensions): bool + { + foreach ($dimensions as $dimension) { + if ($this->isLegacyDimensionParam($dimension)) { + return true; + } + } + + return false; + } + + /** + * Transform legacy dimension and slice params and redirect + * + * This adds the new prefix to params and then redirects so that the new URL contains the prefixed params + * Slices are prefixed to differ filter and slice params + * + * @param array $legacyDimensions + */ + private function transformLegacyDimensionParamsAndRedirect(array $legacyDimensions): void + { + $dimensions = []; + $slices = []; + + $dimensionPrefix = CustomVariableDimension::HOST_PREFIX; + if ($this->getRequest()->getControllerName() === 'services') { + $dimensionPrefix = CustomVariableDimension::SERVICE_PREFIX; + } + + foreach ($legacyDimensions as $param) { + $newParam = $param; + if ($this->isLegacyDimensionParam($param)) { + $newParam = $dimensionPrefix . $param; + } + + $slice = $this->params->shift($param); + if ($slice) { + $slices[IcingaDbCube::SLICE_PREFIX . $newParam] = $slice; + } + + $dimensions[] = $newParam; + } + + $this->redirectNow( + Url::fromRequest() + ->setParam('dimensions', DimensionParams::fromArray($dimensions)->getParams()) + ->addParams($slices) + ->without($legacyDimensions) + ); + } + + public function createTabs(): Tabs + { + $params = Url::fromRequest() + ->onlyWith($this->preserveParams) + ->getParams() + ->toString(); + + return $this->getTabs() + ->add('cube/hosts', [ + 'label' => $this->translate('Hosts'), + 'url' => 'cube/hosts' . ($params === '' ? '' : '?' . $params) + ]) + ->add('cube/services', [ + 'label' => $this->translate('Services'), + 'url' => 'cube/services' . ($params === '' ? '' : '?' . $params) + ]); + } +} |