mirror of
https://github.com/Icinga/icingadb-web.git
synced 2026-05-28 04:36:06 -04:00
Introduce Icinga\Module\Icingadb\Common\SearchControls
This commit is contained in:
parent
c6db259248
commit
94cdec37f1
2 changed files with 163 additions and 249 deletions
161
library/Icingadb/Common/SearchControls.php
Normal file
161
library/Icingadb/Common/SearchControls.php
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
/* Icinga DB Web | (c) 2022 Icinga GmbH | GPLv2 */
|
||||
|
||||
namespace Icinga\Module\Icingadb\Common;
|
||||
|
||||
use GuzzleHttp\Psr7\ServerRequest;
|
||||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Orm\Exception\InvalidRelationException;
|
||||
use ipl\Orm\Query;
|
||||
use ipl\Orm\UnionQuery;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Web\Control\SearchBar;
|
||||
use ipl\Web\Control\SearchEditor;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\ContinueWith;
|
||||
|
||||
trait SearchControls
|
||||
{
|
||||
use \ipl\Web\Compat\SearchControls {
|
||||
\ipl\Web\Compat\SearchControls::createSearchBar as private webCreateSearchBar;
|
||||
\ipl\Web\Compat\SearchControls::enrichFilterCondition as private webEnrichFilterCondition;
|
||||
\ipl\Web\Compat\SearchControls::createSearchEditor as private webCreateSearchEditor;
|
||||
}
|
||||
|
||||
public function fetchMetaData(Query $query): array
|
||||
{
|
||||
return iterator_to_array(ObjectSuggestions::collectFilterColumns($query->getModel(), $query->getResolver()));
|
||||
}
|
||||
|
||||
private function callHandleRequest(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return the SearchBar
|
||||
*
|
||||
* @param Query $query The query being filtered
|
||||
* @param array $preserveParams Query params to preserve when redirecting
|
||||
*
|
||||
* @return SearchBar
|
||||
*/
|
||||
public function createSearchBar(Query $query, array $preserveParams = null): SearchBar
|
||||
{
|
||||
$searchBar = $this->webCreateSearchBar($query, $preserveParams);
|
||||
|
||||
$columnValidator = function (SearchBar\ValidatedColumn $column) use ($query) {
|
||||
$columnPath = $column->getSearchValue();
|
||||
if (($pos = strpos($columnPath, '.vars.')) !== false) {
|
||||
$column->setMessage(null); // webCreateSearchBar will have set one
|
||||
|
||||
try {
|
||||
if ($query instanceof UnionQuery) {
|
||||
// TODO: This can't be right. Finally solve this god-damn union-query-model structure!!!1
|
||||
$query = $query->getUnions()[0];
|
||||
}
|
||||
|
||||
$relationPath = $query->getResolver()->qualifyPath(
|
||||
substr($columnPath, 0, $pos + 5),
|
||||
$query->getModel()->getTableName()
|
||||
);
|
||||
$query->getResolver()->resolveRelation($relationPath);
|
||||
} catch (InvalidRelationException $e) {
|
||||
$column->setMessage(sprintf(
|
||||
t('"%s" is not a valid relation'),
|
||||
substr($e->getRelation(), 0, $pos)
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$searchBar->on(SearchBar::ON_ADD, $columnValidator)
|
||||
->on(SearchBar::ON_INSERT, $columnValidator)
|
||||
->on(SearchBar::ON_SAVE, $columnValidator)
|
||||
->handleRequest(ServerRequest::fromGlobals());
|
||||
|
||||
Html::tag('div', ['class' => 'filter'])->wrap($searchBar);
|
||||
|
||||
return $searchBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return the SearchEditor
|
||||
*
|
||||
* @param Query $query The query being filtered
|
||||
* @param array $preserveParams Query params to preserve when redirecting
|
||||
*
|
||||
* @return SearchEditor
|
||||
*/
|
||||
public function createSearchEditor(Query $query, array $preserveParams = null): SearchEditor
|
||||
{
|
||||
$editor = $this->webCreateSearchEditor($query, $preserveParams);
|
||||
|
||||
$editor->on(SearchEditor::ON_VALIDATE_COLUMN, function (Filter\Condition $condition) use ($query) {
|
||||
$column = $condition->getColumn();
|
||||
if (($pos = strpos($column, '.vars.')) !== false) {
|
||||
try {
|
||||
$query->getResolver()->resolveRelation(substr($column, 0, $pos + 5));
|
||||
} catch (InvalidRelationException $e) {
|
||||
throw new SearchBar\SearchException(sprintf(
|
||||
t('"%s" is not a valid relation'),
|
||||
substr($e->getRelation(), 0, $pos)
|
||||
));
|
||||
}
|
||||
}
|
||||
})->handleRequest(ServerRequest::fromGlobals());
|
||||
|
||||
return $editor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a ContinueWith
|
||||
*
|
||||
* This will automatically be appended to the SearchBar's wrapper. It's not necessary
|
||||
* to add it separately as control or content!
|
||||
*
|
||||
* @param Url $detailsUrl
|
||||
* @param SearchBar $searchBar
|
||||
*
|
||||
* @return ContinueWith
|
||||
*/
|
||||
public function createContinueWith(Url $detailsUrl, SearchBar $searchBar): ContinueWith
|
||||
{
|
||||
$continueWith = new ContinueWith($detailsUrl, [$searchBar, 'getFilter']);
|
||||
$continueWith->setTitle(t('Show bulk processing actions for all filtered results'));
|
||||
$continueWith->setBaseTarget('_next');
|
||||
$continueWith->getAttributes()
|
||||
->set('id', $this->getRequest()->protectId('continue-with'));
|
||||
|
||||
$searchBar->getWrapper()->add($continueWith);
|
||||
|
||||
return $continueWith;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enrich the filter condition with meta data from the query
|
||||
*
|
||||
* @param Filter\Condition $condition
|
||||
* @param Query $query
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function enrichFilterCondition(Filter\Condition $condition, Query $query)
|
||||
{
|
||||
$this->webEnrichFilterCondition($condition, $query);
|
||||
|
||||
$path = $condition->getColumn();
|
||||
if (strpos($path, '.vars.') !== false) {
|
||||
list($target, $varName) = explode('.vars.', $path);
|
||||
if (strpos($target, '.') === false) {
|
||||
// Programmatically translated since the full definition is available in class ObjectSuggestions
|
||||
$condition->metaData()->set(
|
||||
'columnLabel',
|
||||
sprintf(t(ucfirst($target) . ' %s', '..<customvar-name>'), $varName)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,8 +17,8 @@ use Icinga\Exception\ConfigurationError;
|
|||
use Icinga\Exception\Json\JsonDecodeException;
|
||||
use Icinga\Module\Icingadb\Common\Auth;
|
||||
use Icinga\Module\Icingadb\Common\Database;
|
||||
use Icinga\Module\Icingadb\Web\Control\SearchBar\ObjectSuggestions;
|
||||
use Icinga\Module\Icingadb\Common\BaseItemList;
|
||||
use Icinga\Module\Icingadb\Common\SearchControls;
|
||||
use Icinga\Module\Icingadb\Web\Control\ViewModeSwitcher;
|
||||
use Icinga\Module\Pdfexport\PrintableHtmlDocument;
|
||||
use Icinga\Module\Pdfexport\ProvidedHook\Pdfexport;
|
||||
|
|
@ -30,26 +30,22 @@ use Icinga\Util\Json;
|
|||
use ipl\Html\Html;
|
||||
use ipl\Html\ValidHtml;
|
||||
use ipl\Orm\Common\SortUtil;
|
||||
use ipl\Orm\Exception\InvalidRelationException;
|
||||
use ipl\Orm\Query;
|
||||
use ipl\Orm\UnionQuery;
|
||||
use ipl\Stdlib\Contract\Paginatable;
|
||||
use ipl\Stdlib\Filter;
|
||||
use ipl\Stdlib\Seq;
|
||||
use ipl\Web\Compat\CompatController;
|
||||
use ipl\Web\Control\LimitControl;
|
||||
use ipl\Web\Control\PaginationControl;
|
||||
use ipl\Web\Control\SearchBar;
|
||||
use ipl\Web\Control\SearchEditor;
|
||||
use ipl\Web\Control\SortControl;
|
||||
use ipl\Web\Filter\QueryString;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\ContinueWith;
|
||||
|
||||
class Controller extends CompatController
|
||||
{
|
||||
use Auth;
|
||||
use Database;
|
||||
use SearchControls;
|
||||
|
||||
/** @var Filter\Rule Filter from query string parameters */
|
||||
private $filter;
|
||||
|
|
@ -145,213 +141,6 @@ class Controller extends CompatController
|
|||
return $sortControl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return the SearchBar
|
||||
*
|
||||
* @param Query $query The query being filtered
|
||||
* @param array $preserveParams Query params to preserve when redirecting
|
||||
*
|
||||
* @return SearchBar
|
||||
*/
|
||||
public function createSearchBar(Query $query, array $preserveParams = null): SearchBar
|
||||
{
|
||||
$requestUrl = Url::fromRequest();
|
||||
$redirectUrl = $preserveParams !== null
|
||||
? $requestUrl->onlyWith($preserveParams)
|
||||
: (clone $requestUrl)->setParams([]);
|
||||
|
||||
$filter = QueryString::fromString((string) $this->params)
|
||||
->on(QueryString::ON_CONDITION, function (Filter\Condition $condition) use ($query) {
|
||||
$this->enrichFilterCondition($condition, $query);
|
||||
})
|
||||
->parse();
|
||||
|
||||
$searchBar = new SearchBar();
|
||||
$searchBar->setFilter($filter);
|
||||
$searchBar->setAction($redirectUrl->getAbsoluteUrl());
|
||||
$searchBar->setIdProtector([$this->getRequest(), 'protectId']);
|
||||
|
||||
if (method_exists($this, 'completeAction')) {
|
||||
$searchBar->setSuggestionUrl(Url::fromPath(
|
||||
'icingadb/' . $this->getRequest()->getControllerName() . '/complete',
|
||||
['_disableLayout' => true, 'showCompact' => true]
|
||||
));
|
||||
}
|
||||
|
||||
if (method_exists($this, 'searchEditorAction')) {
|
||||
$searchBar->setEditorUrl(Url::fromPath(
|
||||
'icingadb/' . $this->getRequest()->getControllerName() . '/search-editor'
|
||||
)->setParams($redirectUrl->getParams()));
|
||||
}
|
||||
|
||||
$metaData = iterator_to_array(
|
||||
ObjectSuggestions::collectFilterColumns($query->getModel(), $query->getResolver())
|
||||
);
|
||||
$columnValidator = function (SearchBar\ValidatedColumn $column) use ($query, $metaData) {
|
||||
$columnPath = $column->getSearchValue();
|
||||
if (($pos = strpos($columnPath, '.vars.')) !== false) {
|
||||
try {
|
||||
if ($query instanceof UnionQuery) {
|
||||
// TODO: This can't be right. Finally solve this god-damn union-query-model structure!!!1
|
||||
$query = $query->getUnions()[0];
|
||||
}
|
||||
|
||||
$relationPath = $query->getResolver()->qualifyPath(
|
||||
substr($columnPath, 0, $pos + 5),
|
||||
$query->getModel()->getTableName()
|
||||
);
|
||||
$query->getResolver()->resolveRelation($relationPath);
|
||||
} catch (InvalidRelationException $e) {
|
||||
$column->setMessage(sprintf(
|
||||
t('"%s" is not a valid relation'),
|
||||
substr($e->getRelation(), 0, $pos)
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if (strpos($columnPath, '.') === false) {
|
||||
$columnPath = $query->getResolver()->qualifyPath($columnPath, $query->getModel()->getTableName());
|
||||
}
|
||||
|
||||
if (! isset($metaData[$columnPath])) {
|
||||
list($columnPath, $columnLabel) = Seq::find($metaData, $column->getSearchValue(), false);
|
||||
if ($columnPath === null) {
|
||||
$column->setMessage(t('Is not a valid column'));
|
||||
} else {
|
||||
$column->setSearchValue($columnPath);
|
||||
$column->setLabel($columnLabel);
|
||||
}
|
||||
} else {
|
||||
$column->setLabel($metaData[$columnPath]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$searchBar->on(SearchBar::ON_ADD, $columnValidator)
|
||||
->on(SearchBar::ON_INSERT, $columnValidator)
|
||||
->on(SearchBar::ON_SAVE, $columnValidator)
|
||||
->on(SearchBar::ON_SENT, function (SearchBar $form) use ($redirectUrl) {
|
||||
$existingParams = $redirectUrl->getParams();
|
||||
$redirectUrl->setQueryString(QueryString::render($form->getFilter()));
|
||||
foreach ($existingParams->toArray(false) as $name => $value) {
|
||||
if (is_int($name)) {
|
||||
$name = $value;
|
||||
$value = true;
|
||||
}
|
||||
|
||||
$redirectUrl->getParams()->addEncoded($name, $value);
|
||||
}
|
||||
|
||||
$form->setRedirectUrl($redirectUrl);
|
||||
})->on(SearchBar::ON_SUCCESS, function (SearchBar $form) {
|
||||
$this->getResponse()->redirectAndExit($form->getRedirectUrl());
|
||||
})->handleRequest(ServerRequest::fromGlobals());
|
||||
|
||||
Html::tag('div', ['class' => 'filter'])->wrap($searchBar);
|
||||
|
||||
return $searchBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return the SearchEditor
|
||||
*
|
||||
* @param Query $query The query being filtered
|
||||
* @param array $preserveParams Query params to preserve when redirecting
|
||||
*
|
||||
* @return SearchEditor
|
||||
*/
|
||||
public function createSearchEditor(Query $query, array $preserveParams = null): SearchEditor
|
||||
{
|
||||
$requestUrl = Url::fromRequest();
|
||||
$redirectUrl = Url::fromPath('icingadb/' . $this->getRequest()->getControllerName());
|
||||
if (! empty($preserveParams)) {
|
||||
$redirectUrl->setParams($requestUrl->onlyWith($preserveParams)->getParams());
|
||||
}
|
||||
|
||||
$editor = new SearchEditor();
|
||||
$editor->setQueryString((string) $this->params->without($preserveParams));
|
||||
$editor->setAction($requestUrl->getAbsoluteUrl());
|
||||
|
||||
if (method_exists($this, 'completeAction')) {
|
||||
$editor->setSuggestionUrl(Url::fromPath(
|
||||
'icingadb/' . $this->getRequest()->getControllerName() . '/complete',
|
||||
['_disableLayout' => true, 'showCompact' => true]
|
||||
));
|
||||
}
|
||||
|
||||
$editor->getParser()->on(QueryString::ON_CONDITION, function (Filter\Condition $condition) use ($query) {
|
||||
if ($condition->getColumn()) {
|
||||
$this->enrichFilterCondition($condition, $query);
|
||||
}
|
||||
});
|
||||
|
||||
$metaData = iterator_to_array(
|
||||
ObjectSuggestions::collectFilterColumns($query->getModel(), $query->getResolver())
|
||||
);
|
||||
$editor->on(SearchEditor::ON_VALIDATE_COLUMN, function (Filter\Condition $condition) use ($query, $metaData) {
|
||||
$column = $condition->getColumn();
|
||||
if (($pos = strpos($column, '.vars.')) !== false) {
|
||||
try {
|
||||
$query->getResolver()->resolveRelation(substr($column, 0, $pos + 5));
|
||||
} catch (InvalidRelationException $e) {
|
||||
throw new SearchBar\SearchException(sprintf(
|
||||
t('"%s" is not a valid relation'),
|
||||
substr($e->getRelation(), 0, $pos)
|
||||
));
|
||||
}
|
||||
} else {
|
||||
if (! isset($metaData[$column])) {
|
||||
$path = Seq::findKey($metaData, $condition->metaData()->get('columnLabel', $column), false);
|
||||
if ($path === null) {
|
||||
throw new SearchBar\SearchException(t('Is not a valid column'));
|
||||
} else {
|
||||
$condition->setColumn($path);
|
||||
}
|
||||
}
|
||||
}
|
||||
})->on(SearchEditor::ON_SUCCESS, function (SearchEditor $form) use ($redirectUrl) {
|
||||
$existingParams = $redirectUrl->getParams();
|
||||
$redirectUrl->setQueryString(QueryString::render($form->getFilter()));
|
||||
foreach ($existingParams->toArray(false) as $name => $value) {
|
||||
if (is_int($name)) {
|
||||
$name = $value;
|
||||
$value = true;
|
||||
}
|
||||
|
||||
$redirectUrl->getParams()->addEncoded($name, $value);
|
||||
}
|
||||
|
||||
$this->getResponse()
|
||||
->setHeader('X-Icinga-Container', '_self')
|
||||
->redirectAndExit($redirectUrl);
|
||||
})->handleRequest(ServerRequest::fromGlobals());
|
||||
|
||||
return $editor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a ContinueWith
|
||||
*
|
||||
* This will automatically be appended to the SearchBar's wrapper. It's not necessary
|
||||
* to add it separately as control or content!
|
||||
*
|
||||
* @param Url $detailsUrl
|
||||
* @param SearchBar $searchBar
|
||||
*
|
||||
* @return ContinueWith
|
||||
*/
|
||||
public function createContinueWith(Url $detailsUrl, SearchBar $searchBar): ContinueWith
|
||||
{
|
||||
$continueWith = new ContinueWith($detailsUrl, [$searchBar, 'getFilter']);
|
||||
$continueWith->setTitle(t('Show bulk processing actions for all filtered results'));
|
||||
$continueWith->setBaseTarget('_next');
|
||||
$continueWith->getAttributes()
|
||||
->set('id', $this->getRequest()->protectId('continue-with'));
|
||||
|
||||
$searchBar->getWrapper()->add($continueWith);
|
||||
|
||||
return $continueWith;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return the ViewModeSwitcher
|
||||
*
|
||||
|
|
@ -755,42 +544,6 @@ class Controller extends CompatController
|
|||
$this->_helper->notifyPostDispatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enrich the filter condition with meta data from the query
|
||||
*
|
||||
* @param Filter\Condition $condition
|
||||
* @param Query $query
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function enrichFilterCondition(Filter\Condition $condition, Query $query)
|
||||
{
|
||||
$path = $condition->getColumn();
|
||||
if (strpos($path, '.') === false) {
|
||||
$path = $query->getResolver()->qualifyPath($path, $query->getModel()->getTableName());
|
||||
$condition->setColumn($path);
|
||||
}
|
||||
|
||||
if (strpos($path, '.vars.') !== false) {
|
||||
list($target, $varName) = explode('.vars.', $path);
|
||||
if (strpos($target, '.') === false) {
|
||||
// Programmatically translated since the full definition is available in class ObjectSuggestions
|
||||
$condition->metaData()->set(
|
||||
'columnLabel',
|
||||
sprintf(t(ucfirst($target) . ' %s', '..<customvar-name>'), $varName)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$label = Seq::findValue(
|
||||
ObjectSuggestions::collectFilterColumns($query->getModel(), $query->getResolver()),
|
||||
$path
|
||||
);
|
||||
if ($label !== null) {
|
||||
$condition->metaData()->set('columnLabel', $label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function addContent(ValidHtml $content)
|
||||
{
|
||||
if ($content instanceof BaseItemList) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue