From c4f3e78c02e64bf49845690c503a7009eba2f8c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannis=20Mo=C3=9Fhammer?= Date: Mon, 21 Oct 2013 17:04:05 +0200 Subject: [PATCH] Fix filter behaviour, fix statusdat filter refs #4469 --- application/controllers/FilterController.php | 4 +- library/Icinga/Data/Db/TreeToSqlParser.php | 50 +++++++++---- library/Icinga/Filter/Filter.php | 4 +- library/Icinga/Filter/Query/Node.php | 8 ++ library/Icinga/Filter/Query/Tree.php | 50 ++++++++++--- library/Icinga/Protocol/Statusdat/Query.php | 3 +- .../Protocol/Statusdat/Query/Expression.php | 40 ++++++++-- .../Statusdat/TreeToStatusdatQueryParser.php | 42 +++++++++-- .../Icinga/Web/Widget/FilterBadgeRenderer.php | 73 +++++++++++++++++-- library/Icinga/Web/Widget/FilterBox.php | 1 + .../library/Monitoring/Filter/Registry.php | 15 +++- .../Monitoring/Filter/Type/StatusFilter.php | 10 +-- .../Monitoring/Filter/UrlViewFilter.php | 64 ++++++++++++---- public/js/icinga/components/semanticsearch.js | 13 +++- 14 files changed, 298 insertions(+), 79 deletions(-) diff --git a/application/controllers/FilterController.php b/application/controllers/FilterController.php index 00e598ea9..f79b7ed53 100644 --- a/application/controllers/FilterController.php +++ b/application/controllers/FilterController.php @@ -72,7 +72,6 @@ class FilterController extends ActionController $this->getParam('filter_module') ); $urlTarget = $this->parse($query, $target); - die(print_r($urlTarget,true)); $this->redirect($urlTarget['urlParam']); } @@ -108,7 +107,8 @@ class FilterController extends ActionController return array( 'state' => 'success', 'proposals' => $this->registry->getProposalsForQuery($text), - 'urlParam' => $registry::getUrlForTarget($target, $queryTree) + 'urlParam' => $registry::getUrlForTarget($target, $queryTree), + 'valid' => count($this->registry->getIgnoredQueryParts()) === 0 ); } catch (\Exception $exc) { Logger::error($exc); diff --git a/library/Icinga/Data/Db/TreeToSqlParser.php b/library/Icinga/Data/Db/TreeToSqlParser.php index 79de0e53d..4baedf644 100644 --- a/library/Icinga/Data/Db/TreeToSqlParser.php +++ b/library/Icinga/Data/Db/TreeToSqlParser.php @@ -66,13 +66,22 @@ class TreeToSqlParser * @param String $operator The operator from the query node * @return string The operator for the sql query part */ - private function getSqlOperator($operator) + private function getSqlOperator($operator, array $right) { + switch($operator) { case Node::OPERATOR_EQUALS: - return 'LIKE'; + if (count($right) > 1) { + return 'IN'; + } else { + return 'LIKE'; + } case Node::OPERATOR_EQUALS_NOT: - return 'NOT LIKE'; + if (count($right) > 1) { + return 'NOT IN'; + } else { + return 'NOT LIKE'; + } default: return $operator; } @@ -125,14 +134,14 @@ class TreeToSqlParser return ''; } $this->query->requireColumn($node->left); - $queryString = $this->query->getMappedField($node->left); + $queryString = '(' . $this->query->getMappedField($node->left) . ')'; if ($this->query->isAggregateColumn($node->left)) { $this->type = 'HAVING'; } $queryString .= ' ' . (is_integer($node->right) ? - $node->operator : $this->getSqlOperator($node->operator)) . ' '; - $queryString .= $this->getParameterValue($node); + $node->operator : $this->getSqlOperator($node->operator, $node->right)) . ' '; + $queryString = $this->addValueToQuery($node, $queryString); return $queryString; } @@ -145,18 +154,27 @@ class TreeToSqlParser * @param Node $node The node to retrieve the sql string value from * @return String|int The converted and quoted value */ - private function getParameterValue(Node $node) { - $value = $node->right; - if ($node->operator === Node::OPERATOR_EQUALS || $node->operator === Node::OPERATOR_EQUALS_NOT) { - $value = str_replace('*', '%', $value); + private function addValueToQuery(Node $node, $query) { + $values = array(); + + foreach ($node->right as $value) { + if ($node->operator === Node::OPERATOR_EQUALS || $node->operator === Node::OPERATOR_EQUALS_NOT) { + $value = str_replace('*', '%', $value); + } + if ($this->query->isTimestamp($node->left)) { + $node->context = Node::CONTEXT_TIMESTRING; + } + if ($node->context === Node::CONTEXT_TIMESTRING) { + $value = strtotime($value); + } + $values[] = $this->query->getDatasource()->getConnection()->quote($value); } - if ($this->query->isTimestamp($node->left)) { - $node->context = Node::CONTEXT_TIMESTRING; + $valueString = join(',', $values); + + if (count($values) > 1) { + return '( '. $valueString . ')'; } - if ($node->context === Node::CONTEXT_TIMESTRING) { - $value = strtotime($value); - } - return $this->query->getDatasource()->getConnection()->quote($value); + return $query . $valueString; } /** diff --git a/library/Icinga/Filter/Filter.php b/library/Icinga/Filter/Filter.php index 4d324ce74..b999d6bd4 100644 --- a/library/Icinga/Filter/Filter.php +++ b/library/Icinga/Filter/Filter.php @@ -186,7 +186,7 @@ class Filter extends QueryProposer } $proposals = array_merge($proposals, $this->getDefaultDomain()->getProposalsForQuery($query)); } - return $proposals; + return array_unique($proposals); } /** @@ -197,7 +197,7 @@ class Filter extends QueryProposer */ private function splitQueryAtNextConjunction($query) { - $delimiter = array('AND', 'OR'); + $delimiter = array('AND'/*, 'OR'*/); // or is not supported currently $inStr = false; for ($i = 0; $i < strlen($query); $i++) { // Skip strings diff --git a/library/Icinga/Filter/Query/Node.php b/library/Icinga/Filter/Query/Node.php index db82009c4..a287635c9 100644 --- a/library/Icinga/Filter/Query/Node.php +++ b/library/Icinga/Filter/Query/Node.php @@ -113,6 +113,14 @@ class Node $node->type = self::TYPE_OPERATOR; $node->operator = $operator; $node->left = $left; + if ($right === null) { + $right = array(); + } elseif (!is_array($right)) { + $right = array($right); + } + foreach($right as &$value) { + $value = trim($value); + } $node->right = $right; return $node; } diff --git a/library/Icinga/Filter/Query/Tree.php b/library/Icinga/Filter/Query/Tree.php index 49662cfb7..5c1690fff 100644 --- a/library/Icinga/Filter/Query/Tree.php +++ b/library/Icinga/Filter/Query/Tree.php @@ -54,6 +54,23 @@ class Tree */ private $lastNode; + public function insertTree(Tree $tree) + { + $this->insertSubTree($tree->root); + $this->root = $this->normalizeTree($this->root); + } + + private function insertSubTree(Node $node) + { + if ($node->type === Node::TYPE_OPERATOR) { + $this->insert($node); + } else { + $this->insert($node->type === Node::TYPE_AND ? Node::createAndNode() : Node::createOrNode()); + $this->insertSubTree($node->left); + $this->insertSubTree($node->right); + } + } + /** * Insert a node into this tree, recognizing type and insert position * @@ -77,7 +94,6 @@ class Tree $this->insert(Node::createAndNode()); } $node->parent = $this->lastNode; - if ($this->lastNode->left == null) { $this->lastNode->left = $node; } elseif ($this->lastNode->right == null) { @@ -86,6 +102,7 @@ class Tree break; } } + $this->lastNode = $node; } @@ -350,16 +367,23 @@ class Tree if ($ctx === null) { return null; } - if ($ctx->type === Node::TYPE_OPERATOR) { - if ($ctx->left == $node->left && $ctx->right == $node->right && $ctx->operator == $node->operator) { - return $ctx; + + if ($ctx->type == Node::TYPE_OPERATOR) { + if ($ctx->left == $node->left && $ctx->operator == $node->operator) { + if(empty($node->right) || $ctx->right == $node->right) { + return $ctx; + } } return null; } else { - $result = $this->findNode($node, $ctx->left); - if ($result === null) { + $result = null; + if ($ctx->left) { + $result = $this->findNode($node, $ctx->left); + } if ($result == null && $ctx->right) { $result = $this->findNode($node, $ctx->right); + } + return $result; } } @@ -367,21 +391,25 @@ class Tree /** * Return true if A node with the given attribute on the left side exists * - * @param String $name The attribute to test for existence - * @param Node $ctx The current root node + * @param String $name The attribute to test for existence + * @param Node $ctx The current root node + * @oaram bool $isRecursive Internal flag to disable null nodes being replaced with the tree root * * @return bool True if a node contains $name on the left side, otherwise false */ - public function hasNodeWithAttribute($name, $ctx = null) + public function hasNodeWithAttribute($name, $ctx = null, $isRecursive = false) { - $ctx = $ctx ? $ctx : $this->root; + if (!$isRecursive) { + $ctx = $ctx ? $ctx : $this->root; + } if ($ctx === null) { return false; } if ($ctx->type === Node::TYPE_OPERATOR) { return $ctx->left === $name; } else { - return $this->hasNodeWithAttribute($name, $ctx->left) || $this->hasNodeWithAttribute($name, $ctx->right); + return $this->hasNodeWithAttribute($name, $ctx->left, true) + || $this->hasNodeWithAttribute($name, $ctx->right, true); } } } diff --git a/library/Icinga/Protocol/Statusdat/Query.php b/library/Icinga/Protocol/Statusdat/Query.php index 4f844a896..85742e8ad 100755 --- a/library/Icinga/Protocol/Statusdat/Query.php +++ b/library/Icinga/Protocol/Statusdat/Query.php @@ -225,7 +225,8 @@ class Query extends BaseQuery $indexes = array_keys($state[$target]); if ($baseGroup) { $baseGroup->setQuery($this); - $indexes = $baseGroup->filter($state[$target]); + $idx = array_keys($state[$target]); + $indexes = $baseGroup->filter($state[$target], $idx ); } if (!isset($result[$target])) { $result[$target] = $indexes; diff --git a/library/Icinga/Protocol/Statusdat/Query/Expression.php b/library/Icinga/Protocol/Statusdat/Query/Expression.php index 1087eabca..659614d3f 100755 --- a/library/Icinga/Protocol/Statusdat/Query/Expression.php +++ b/library/Icinga/Protocol/Statusdat/Query/Expression.php @@ -28,6 +28,8 @@ namespace Icinga\Protocol\Statusdat\Query; +use Icinga\Protocol\Ldap\Exception; + class Expression implements IQueryPart { /** @@ -117,12 +119,18 @@ class Expression implements IQueryPart case "LIKE": $this->CB = "isLike"; break; + case "NOT_LIKE": + $this->CB = "isNotLike"; + break; case "!=": $this->CB = "isNotEqual"; break; case "IN": $this->CB = "isIn"; break; + case "NOT_IN": + $this->CB = "isNotIn"; + break; default: throw new \Exception("Unknown operator $token in expression $this->expression !"); } @@ -216,7 +224,6 @@ class Expression implements IQueryPart $idx = array_keys($base); } $this->basedata = $base; - return array_filter($idx, array($this, "filterFn")); } @@ -248,12 +255,24 @@ class Expression implements IQueryPart return false; } - if ($this->CB == "isIn") { - return count(array_intersect($values, $this->value)) > 0; - } - if ($this->CB == "isNotIn") { - return count(array_intersect($values, $this->value)) == 0; + if ($this->CB == "isIn" || $this->CB == "isNotIn") { + $cmpValues = is_array($this->value) ? $this->value : array($this->value); + foreach ($cmpValues as $cmpValue) { + $this->value = $cmpValue; + foreach ($values as $value) { + if ($this->CB == "isIn" && $this->isLike($value)) { + $this->value = $cmpValues; + return true; + } elseif ($this->CB == "isNotIn" && $this->isNotLike($value)) { + $this->value = $cmpValues; + return true; + } + } + } + $this->value = $cmpValues; + return false; } + if ($this->function) { $values = call_user_func($this->function, $values); if (!is_array($values)) { @@ -355,6 +374,15 @@ class Expression implements IQueryPart return preg_match("/^" . str_replace("%", ".*", $this->value) . "$/", $value) ? true : false; } + /** + * @param $value + * @return bool + */ + public function isNotLike($value) + { + return !preg_match("/^" . str_replace("%", ".*", $this->value) . "$/", $value) ? true : false; + } + /** * @param $value * @return bool diff --git a/library/Icinga/Protocol/Statusdat/TreeToStatusdatQueryParser.php b/library/Icinga/Protocol/Statusdat/TreeToStatusdatQueryParser.php index f1a2dcf17..13d1eab15 100644 --- a/library/Icinga/Protocol/Statusdat/TreeToStatusdatQueryParser.php +++ b/library/Icinga/Protocol/Statusdat/TreeToStatusdatQueryParser.php @@ -26,30 +26,48 @@ */ // {{{ICINGA_LICENSE_HEADER}}} - namespace Icinga\Protocol\Statusdat; - use Icinga\Filter\Filterable; use Icinga\Filter\Query\Node; use Icinga\Filter\Query\Tree; use Icinga\Protocol\Statusdat\Query\Expression; use Icinga\Protocol\Statusdat\Query\Group; +use Icinga\Protocol\Statusdat\Query\IQueryPart; +/** + * Parser to create statusdat filter expressions from query trees + * + */ class TreeToStatusdatQueryParser { - private function nodeToQuery(Node $node, Filterable $source) + /** + * Create a Statusdat expression from a Tree node + * + * @param Node $node The node to convert to an expression + * @param Filterable $source The filterable to use for field mapping + * + * @return IQueryPart Either a statusdat expression or an expression group + */ + private function nodeToQuery(Node $node, Filterable $source) { if ($node->type === Node::TYPE_OPERATOR) { $op = $node->operator; - $value = $node->right; + $node->left = $source->getMappedField($node->left); - if (stripos($node->right, '*') !== false) { - $op = 'LIKE'; + $op = 'IN'; + $values = $node->right; + + if ($node->operator === NODE::OPERATOR_EQUALS_NOT) { + $op = 'NOT_' . $op; + + } + foreach ($values as &$value) { $value = str_replace('*', '%', $value); } - return new Expression($node->left . ' ' . $op . ' ?', $value); + $values = array($values); + return new Expression($node->left . ' ' . $op . ' ? ', $values); } else { $group = new Group(); $group->setType(($node->type === Node::TYPE_OR) ? Group::TYPE_OR : Group::TYPE_AND); @@ -60,6 +78,14 @@ class TreeToStatusdatQueryParser } + /** + * Create a statusdat specific filter expression for the given query tree and filterable + * + * @param Tree $tree The tree to convert to a query + * @param Filterable $source The filterable to use for tree conversion + * + * @return IQueryPart A statusdat query object + */ public function treeToQuery(Tree $tree, Filterable $source) { @@ -70,4 +96,4 @@ class TreeToStatusdatQueryParser } return null; } -} \ No newline at end of file +} diff --git a/library/Icinga/Web/Widget/FilterBadgeRenderer.php b/library/Icinga/Web/Widget/FilterBadgeRenderer.php index aa274f89f..f98f12c51 100644 --- a/library/Icinga/Web/Widget/FilterBadgeRenderer.php +++ b/library/Icinga/Web/Widget/FilterBadgeRenderer.php @@ -48,6 +48,29 @@ class FilterBadgeRenderer implements Widget private $conjunctionCellar = ''; private $urlFilter; + private $tpl =<<<'EOT' +
+ + {{FILTER_SUM}} + + {{SUBLIST}} +
+EOT; + + private $subTpl =<<<'EOT' + + +EOT; + + private $subItemTpl =<<<'EOT' +
  • {{FILTER_TEXT}}
  • +EOT; + + /** * Create a new badge renderer for this tree * @@ -58,6 +81,41 @@ class FilterBadgeRenderer implements Widget $this->tree = $tree; } + private function getSummarizedText($node) + { + + if (count($node->right) === 1) { + $value = $node->right[0]; + } else { + $value = join(',', $node->right); + if(strlen($value) > 15) { + $value = substr($value, 0, 13) . '..'; + } + } + return $this->conjunctionCellar . ' '. ucfirst($node->left) . $node->operator . $value ; + } + + private function getSubEntries(Node $node) + { + $liItems = ""; + $basePath = $this->baseUrl->getAbsoluteUrl(); + $allParams = $this->baseUrl->getParams(); + + foreach ($node->right as $value) { + $newTree = $this->tree->createCopy(); + $affectedNode = $newTree->findNode($node); + $affectedNode->right = array_diff($affectedNode->right, array($value)); + $url = $this->urlFilter->fromTree($newTree); + $url = $basePath . (empty($allParams) ? '?' : '&') . $url; + + $liItem = str_replace('{{REMOVE_FILTER}}', $url, $this->subItemTpl); + $liItem = str_replace('{{FILTER_TEXT}}', ucfirst($node->left) . $node->operator . $value , $liItem); + $liItems .= $liItem; + } + + return str_replace('{{SUBFILTER_LIST}}', $liItems, $this->subTpl); + } + /** * Create a removable badge from a query tree node * @@ -74,15 +132,18 @@ class FilterBadgeRenderer implements Widget $newTree = $this->tree->withoutNode($node); $url = $this->urlFilter->fromTree($newTree); $url = $basePath . (empty($allParams) ? '?' : '&') . $url; + $sumText = $this->getSummarizedText($node); - return ' ' - . $this->conjunctionCellar . ' ' - . ucfirst($node->left) . ' ' - . $node->operator . ' ' - . $node->right . ''; + $tpl = str_replace('{{FILTER_SUM}}', $sumText, $this->tpl); + $tpl = str_replace('{{REMOVE_FILTER}}', $url, $tpl); + if (count($node->right) > 1) { + $tpl = str_replace('{{SUBLIST}}', $this->getSubEntries($node), $tpl); + } else { + $tpl = str_replace('{{SUBLIST}}', '', $tpl); + } + return $tpl; } $result = ''; - $result .= $this->nodeToBadge($node->left); $this->conjunctionCellar = $node->type; $result .= $this->nodeToBadge($node->right); diff --git a/library/Icinga/Web/Widget/FilterBox.php b/library/Icinga/Web/Widget/FilterBox.php index 1d5b503c0..77c8c18e3 100644 --- a/library/Icinga/Web/Widget/FilterBox.php +++ b/library/Icinga/Web/Widget/FilterBox.php @@ -113,6 +113,7 @@ EOT; $form->setIgnoreChangeDiscarding(true); $badges = new FilterBadgeRenderer($this->initialFilter); $html = str_replace('{{FORM}}', $form->render($view), self::$TPL); + $html = '
    ' . $html . '
    '; return str_replace('{{BADGES}}', $badges->render($view), $html); } } diff --git a/modules/monitoring/library/Monitoring/Filter/Registry.php b/modules/monitoring/library/Monitoring/Filter/Registry.php index 526aa2cb5..0f3223773 100644 --- a/modules/monitoring/library/Monitoring/Filter/Registry.php +++ b/modules/monitoring/library/Monitoring/Filter/Registry.php @@ -99,7 +99,7 @@ class Registry implements FilterRegistry $domain->registerAttribute( FilterAttribute::create(new TextFilter()) - ->setHandledAttributes('Name', 'Hostname') + ->setHandledAttributes('Name', 'Host', 'Hostname') ->setField('host_name') )->registerAttribute( FilterAttribute::create(StatusFilter::createForHost()) @@ -173,6 +173,10 @@ class Registry implements FilterRegistry FilterAttribute::create(self::getNextCheckFilterType()) ->setHandledAttributes('Next Check') ->setField('service_next_check') + )->registerAttribute( + FilterAttribute::create(new TextFilter()) + ->setHandledAttributes('Hostname', 'Host') + ->setField('host_name') ); return $domain; } @@ -211,11 +215,11 @@ class Registry implements FilterRegistry parse_str($lastQuery, $lastParameters); if ($lastFilter->root) { - $filter->insert($lastFilter->root); + $filter->insertTree($lastFilter); } $params = array(); foreach ($lastParameters as $key => $param) { - if (!$filter->hasNodeWithAttribute($key)) { + if (!$filter->hasNodeWithAttribute($key) && $view->isValidFilterTarget($key)) { $params[$key] = $param; } } @@ -230,4 +234,9 @@ class Registry implements FilterRegistry $urlString .= $urlParser->fromTree($filter); return '/' . $urlString; } + + public function isValid($query) + { + + } } diff --git a/modules/monitoring/library/Monitoring/Filter/Type/StatusFilter.php b/modules/monitoring/library/Monitoring/Filter/Type/StatusFilter.php index fd23004f5..6dc90b029 100644 --- a/modules/monitoring/library/Monitoring/Filter/Type/StatusFilter.php +++ b/modules/monitoring/library/Monitoring/Filter/Type/StatusFilter.php @@ -60,7 +60,7 @@ class StatusFilter extends FilterType 'Is' => Node::OPERATOR_EQUALS, '=' => Node::OPERATOR_EQUALS, '!=' => Node::OPERATOR_EQUALS_NOT, - 'Is not' => Node::OPERATOR_EQUALS_NOT + 'Is Not' => Node::OPERATOR_EQUALS_NOT ); /** @@ -214,12 +214,7 @@ class StatusFilter extends FilterType private function getOperatorValueArray($query) { $result = array(null, null, null); - foreach ($this->getOperators() as $operator) { - if (stripos($query, $operator) === 0) { - $result[0] = $operator; - break; - } - } + $result[0] = self::getMatchingOperatorForQuery($query); if ($result[0] === null) { return $result; } @@ -234,6 +229,7 @@ class StatusFilter extends FilterType if ($result[2] && !$this->subFilter->isValidQuery($result[2])) { return array(null, null, null); } + return $result; } diff --git a/modules/monitoring/library/Monitoring/Filter/UrlViewFilter.php b/modules/monitoring/library/Monitoring/Filter/UrlViewFilter.php index 34e045648..de3e77d9a 100644 --- a/modules/monitoring/library/Monitoring/Filter/UrlViewFilter.php +++ b/modules/monitoring/library/Monitoring/Filter/UrlViewFilter.php @@ -52,6 +52,9 @@ class UrlViewFilter */ private $target; + private $supportedConjunctions = array('&'/*, '|'*/); + + /** * Create a new ViewFilter * @@ -77,9 +80,43 @@ class UrlViewFilter if ($this->target) { $filter = $filter->getCopyForFilterable($this->target); } + $filter = $this->normalizeTreeNode($filter->root); + $filter->root = $filter->normalizeTree($filter->root); return $this->convertNodeToUrlString($filter->root); } + private function insertNormalizedOperatorNode($node, Tree $subTree = null) + { + + $searchNode = $subTree->findNode(Node::createOperatorNode($node->operator, $node->left, null)); + if ( $searchNode !== null) { + $result = array(); + foreach ($node->right as $item) { + if (stripos($item, '*')) { + $subTree->insert(Node::createOperatorNode($node->operator, $node->left, $item)); + } else { + $result = $result + $node->right; + } + } + $searchNode->right = array_merge($searchNode->right, $result); + } else { + $subTree->insert($node); + } + } + + public function normalizeTreeNode($node, Tree $subTree = null) + { + $subTree = $subTree ? $subTree : new Tree(); + if ($node->type === Node::TYPE_OPERATOR) { + $this->insertNormalizedOperatorNode($node, $subTree); + } else { + $subTree->insert($node->type === Node::TYPE_AND ? Node::createAndNode() : Node::createOrNode()); + $subTree = $this->normalizeTreeNode($node->left, $subTree); + $subTree = $this->normalizeTreeNode($node->right, $subTree); + } + return $subTree; + } + /** * Parse the given given url and return a query tree * @@ -103,8 +140,8 @@ class UrlViewFilter } elseif (is_array($token)) { $tree->insert( Node::createOperatorNode( - $token[self::FILTER_OPERATOR], - $token[self::FILTER_TARGET], + trim($token[self::FILTER_OPERATOR]), + trim($token[self::FILTER_TARGET]), $token[self::FILTER_VALUE] ) ); @@ -133,12 +170,16 @@ class UrlViewFilter { $left = null; $right = null; - if ($node->type === Node::TYPE_OPERATOR) { if ($this->target && !$this->target->isValidFilterTarget($node->left)) { return null; } - return urlencode($node->left) . $node->operator . urlencode($node->right); + $values = array(); + foreach ($node->right as $item) { + $values[] = urlencode($item); + + } + return urlencode($node->left) . $node->operator . join(',', $values); } if ($node->left) { $left = $this->convertNodeToUrlString($node->left); @@ -167,7 +208,7 @@ class UrlViewFilter * array( * self::FILTER_TARGET => 'Attribute', * self::FILTER_OPERATOR => '!=', - * self::FILTER_VALUE => 'Value' + * self::FILTER_VALUE => array('Value') * ) * * @param String $query The query to tokenize @@ -233,7 +274,6 @@ class UrlViewFilter */ private function parseTarget($query, $currentPos, array &$tokenList) { - $conjunctions = array('&', '|'); $i = $currentPos; for ($i; $i < strlen($query); $i++) { @@ -254,7 +294,7 @@ class UrlViewFilter } // Implicit value token (test=1|2) - if (in_array($currentChar, $conjunctions) || $i + 1 == strlen($query)) { + if (in_array($currentChar, $this->supportedConjunctions) || $i + 1 == strlen($query)) { $nrOfSymbols = count($tokenList); if ($nrOfSymbols <= 2) { return array($i, self::FILTER_TARGET); @@ -292,7 +332,6 @@ class UrlViewFilter { $i = $currentPos; - $conjunctions = array('&', '|'); $nrOfSymbols = count($tokenList); if ($nrOfSymbols == 0) { @@ -301,7 +340,7 @@ class UrlViewFilter $lastState = &$tokenList[$nrOfSymbols-1]; for ($i; $i < strlen($query); $i++) { $currentChar = $query[$i]; - if (in_array($currentChar, $conjunctions)) { + if (in_array($currentChar, $this->supportedConjunctions)) { break; } } @@ -312,9 +351,9 @@ class UrlViewFilter array_pop($tokenList); return array($currentPos, self::FILTER_TARGET); } - $lastState[self::FILTER_VALUE] = substr($query, $currentPos, $length); + $lastState[self::FILTER_VALUE] = explode(',', substr($query, $currentPos, $length)); - if (in_array($currentChar, $conjunctions)) { + if (in_array($currentChar, $this->supportedConjunctions)) { $tokenList[] = $currentChar; } return array($i, self::FILTER_TARGET); @@ -331,10 +370,9 @@ class UrlViewFilter */ private function skip($query, $currentPos) { - $conjunctions = array('&', '|'); for ($i = $currentPos; strlen($query); $i++) { $currentChar = $query[$i]; - if (in_array($currentChar, $conjunctions)) { + if (in_array($currentChar, $this->supportedConjunctions)) { return array($i, self::FILTER_TARGET); } } diff --git a/public/js/icinga/components/semanticsearch.js b/public/js/icinga/components/semanticsearch.js index 1ba0dc449..5b3bb01ac 100644 --- a/public/js/icinga/components/semanticsearch.js +++ b/public/js/icinga/components/semanticsearch.js @@ -93,7 +93,7 @@ define(['jquery', 'logging', 'URIjs/URI', 'components/app/container'], function( * @param {String} state The HTTP state as a string */ this.showError = function(error, state) { - if (state === 'abort') { + if (!error.message || state === 'abort') { return; } this.inputDom.popover('destroy').popover({ @@ -136,7 +136,12 @@ define(['jquery', 'logging', 'URIjs/URI', 'components/app/container'], function( return; } - var list = $('