diff --git a/asset/js/widget/Completer.js b/asset/js/widget/Completer.js index a11548a3f..a3377f633 100644 --- a/asset/js/widget/Completer.js +++ b/asset/js/widget/Completer.js @@ -721,6 +721,7 @@ define(["../notjQuery"], function ($) { // choose a suggestion, an up2date contextual value will be transmitted with // completion requests and the server can properly identify a new value upon submit input.dataset.search = input.value; + input.title = input.value; if (typeof input.form[input.name + '-search'] !== 'undefined') { let dataElement = input.form[input.name + '-search']; if (dataElement instanceof RadioNodeList) { @@ -730,6 +731,10 @@ define(["../notjQuery"], function ($) { dataElement.value = input.value; } + input.form.querySelectorAll( + `input[type="hidden"][name^="${input.name}-"]:not([name="${input.name}-search"])` + ).forEach(i => i.value = ''); + let [value, data] = this.prepareCompletionData(input); this.completedInput = input; this.completedValue = value; diff --git a/src/Control/SearchEditor.php b/src/Control/SearchEditor.php index db6d0a114..8581fe80e 100644 --- a/src/Control/SearchEditor.php +++ b/src/Control/SearchEditor.php @@ -2,6 +2,7 @@ namespace ipl\Web\Control; +use Icinga\Exception\ConfigurationError; use ipl\Html\Attributes; use ipl\Html\Form; use ipl\Html\FormDecorator\CallbackDecorator; @@ -55,10 +56,14 @@ class SearchEditor extends Form /** @var bool */ protected $cleared = false; + /** @var string[] Additional fields to add to the condition's `metadata` */ + protected array $metadataFields = []; + /** * Set the filter query string to populate the form with * * Use {@see SearchEditor::getParser()} to subscribe to parser events. + * Calling this resets any filter previously set by {@see SearchEditor::setFilter()} * * @param string $query * @@ -67,6 +72,7 @@ class SearchEditor extends Form public function setQueryString($query) { $this->queryString = $query; + $this->filter = null; return $this; } @@ -95,6 +101,32 @@ public function setSuggestionUrl(Url $url) return $this; } + /** + * Set additional fields to add to the condition's `metadata` + * + * The values of these fields are populated into the condition's `metadata` + * + * @param array $fields + * + * @return $this + * + * @throws ConfigurationError When array contains reserved keywords: `search`, `title` + */ + public function setMetadataFields(array $fields): static + { + // `search` is already a hidden field, and `title` has special handling in js Completer.complete() + $reserved = array_intersect($fields, ['search', 'title']); + if (! empty($reserved)) { + throw new ConfigurationError( + sprintf("Reserved keyword(s) not allowed as metadata field: %s", implode(', ', $reserved)) + ); + } + + $this->metadataFields = $fields; + + return $this; + } + /** * Get the query string parser being used * @@ -125,6 +157,25 @@ public function getFilter() return $this->filter; } + /** + * Set the filter to populate the form with + * + * Overwrites any query string previously set by {@see SearchEditor::setQueryString()}. + * + * @param Filter\Rule $filter + * + * @return $this + */ + public function setFilter(Filter\Rule $filter): static + { + $this->filter = $filter; + $this->queryString = $filter instanceof Filter\Chain && $filter->isEmpty() + ? '' + : (new Renderer($filter))->setStrict()->render(); + + return $this; + } + /** * Get inline stylesheet * @@ -195,6 +246,10 @@ protected function applyChanges(Filter\Rule $rule, array &$values, array $path = } else { // Make sure we don't forget to present the column labels again $rule->metaData()->set('columnLabel', $this->popKey($values, $identifier . '-column')); + + foreach ($this->metadataFields as $fieldName) { + $rule->metaData()->set($fieldName, $this->popKey($values, $identifier . '-column-' . $fieldName)); + } } if ($newColumn !== null && $rule->getColumn() !== $newColumn) { @@ -551,6 +606,16 @@ protected function createCondition(Filter\Condition $condition, $identifier) }] ]); + $metadataFields = new HtmlDocument(); + foreach ($this->metadataFields as $fieldNameSuffix) { + $columnMetaInput = $this->createElement('hidden', $identifier . '-column-' . $fieldNameSuffix, [ + 'value' => $condition->metaData()->get($fieldNameSuffix) + ]); + $this->registerElement($columnMetaInput); + + $metadataFields->addHtml($columnMetaInput); + } + $operatorInput = $this->createElement('select', $identifier . '-operator', [ 'options' => [ '~' => '~', @@ -588,6 +653,7 @@ protected function createCondition(Filter\Condition $condition, $identifier) $columnInput, $columnFakeInput, $columnSearchInput, + $metadataFields, $operatorInput, $valueInput );