parent
8cf1e8cbf5
commit
c4f3e78c02
|
@ -72,7 +72,6 @@ class FilterController extends ActionController
|
||||||
$this->getParam('filter_module')
|
$this->getParam('filter_module')
|
||||||
);
|
);
|
||||||
$urlTarget = $this->parse($query, $target);
|
$urlTarget = $this->parse($query, $target);
|
||||||
die(print_r($urlTarget,true));
|
|
||||||
$this->redirect($urlTarget['urlParam']);
|
$this->redirect($urlTarget['urlParam']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +107,8 @@ class FilterController extends ActionController
|
||||||
return array(
|
return array(
|
||||||
'state' => 'success',
|
'state' => 'success',
|
||||||
'proposals' => $this->registry->getProposalsForQuery($text),
|
'proposals' => $this->registry->getProposalsForQuery($text),
|
||||||
'urlParam' => $registry::getUrlForTarget($target, $queryTree)
|
'urlParam' => $registry::getUrlForTarget($target, $queryTree),
|
||||||
|
'valid' => count($this->registry->getIgnoredQueryParts()) === 0
|
||||||
);
|
);
|
||||||
} catch (\Exception $exc) {
|
} catch (\Exception $exc) {
|
||||||
Logger::error($exc);
|
Logger::error($exc);
|
||||||
|
|
|
@ -66,13 +66,22 @@ class TreeToSqlParser
|
||||||
* @param String $operator The operator from the query node
|
* @param String $operator The operator from the query node
|
||||||
* @return string The operator for the sql query part
|
* @return string The operator for the sql query part
|
||||||
*/
|
*/
|
||||||
private function getSqlOperator($operator)
|
private function getSqlOperator($operator, array $right)
|
||||||
{
|
{
|
||||||
|
|
||||||
switch($operator) {
|
switch($operator) {
|
||||||
case Node::OPERATOR_EQUALS:
|
case Node::OPERATOR_EQUALS:
|
||||||
return 'LIKE';
|
if (count($right) > 1) {
|
||||||
|
return 'IN';
|
||||||
|
} else {
|
||||||
|
return 'LIKE';
|
||||||
|
}
|
||||||
case Node::OPERATOR_EQUALS_NOT:
|
case Node::OPERATOR_EQUALS_NOT:
|
||||||
return 'NOT LIKE';
|
if (count($right) > 1) {
|
||||||
|
return 'NOT IN';
|
||||||
|
} else {
|
||||||
|
return 'NOT LIKE';
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return $operator;
|
return $operator;
|
||||||
}
|
}
|
||||||
|
@ -125,14 +134,14 @@ class TreeToSqlParser
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
$this->query->requireColumn($node->left);
|
$this->query->requireColumn($node->left);
|
||||||
$queryString = $this->query->getMappedField($node->left);
|
$queryString = '(' . $this->query->getMappedField($node->left) . ')';
|
||||||
|
|
||||||
if ($this->query->isAggregateColumn($node->left)) {
|
if ($this->query->isAggregateColumn($node->left)) {
|
||||||
$this->type = 'HAVING';
|
$this->type = 'HAVING';
|
||||||
}
|
}
|
||||||
$queryString .= ' ' . (is_integer($node->right) ?
|
$queryString .= ' ' . (is_integer($node->right) ?
|
||||||
$node->operator : $this->getSqlOperator($node->operator)) . ' ';
|
$node->operator : $this->getSqlOperator($node->operator, $node->right)) . ' ';
|
||||||
$queryString .= $this->getParameterValue($node);
|
$queryString = $this->addValueToQuery($node, $queryString);
|
||||||
return $queryString;
|
return $queryString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,18 +154,27 @@ class TreeToSqlParser
|
||||||
* @param Node $node The node to retrieve the sql string value from
|
* @param Node $node The node to retrieve the sql string value from
|
||||||
* @return String|int The converted and quoted value
|
* @return String|int The converted and quoted value
|
||||||
*/
|
*/
|
||||||
private function getParameterValue(Node $node) {
|
private function addValueToQuery(Node $node, $query) {
|
||||||
$value = $node->right;
|
$values = array();
|
||||||
if ($node->operator === Node::OPERATOR_EQUALS || $node->operator === Node::OPERATOR_EQUALS_NOT) {
|
|
||||||
$value = str_replace('*', '%', $value);
|
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)) {
|
$valueString = join(',', $values);
|
||||||
$node->context = Node::CONTEXT_TIMESTRING;
|
|
||||||
|
if (count($values) > 1) {
|
||||||
|
return '( '. $valueString . ')';
|
||||||
}
|
}
|
||||||
if ($node->context === Node::CONTEXT_TIMESTRING) {
|
return $query . $valueString;
|
||||||
$value = strtotime($value);
|
|
||||||
}
|
|
||||||
return $this->query->getDatasource()->getConnection()->quote($value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -186,7 +186,7 @@ class Filter extends QueryProposer
|
||||||
}
|
}
|
||||||
$proposals = array_merge($proposals, $this->getDefaultDomain()->getProposalsForQuery($query));
|
$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)
|
private function splitQueryAtNextConjunction($query)
|
||||||
{
|
{
|
||||||
$delimiter = array('AND', 'OR');
|
$delimiter = array('AND'/*, 'OR'*/); // or is not supported currently
|
||||||
$inStr = false;
|
$inStr = false;
|
||||||
for ($i = 0; $i < strlen($query); $i++) {
|
for ($i = 0; $i < strlen($query); $i++) {
|
||||||
// Skip strings
|
// Skip strings
|
||||||
|
|
|
@ -113,6 +113,14 @@ class Node
|
||||||
$node->type = self::TYPE_OPERATOR;
|
$node->type = self::TYPE_OPERATOR;
|
||||||
$node->operator = $operator;
|
$node->operator = $operator;
|
||||||
$node->left = $left;
|
$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;
|
$node->right = $right;
|
||||||
return $node;
|
return $node;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,23 @@ class Tree
|
||||||
*/
|
*/
|
||||||
private $lastNode;
|
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
|
* Insert a node into this tree, recognizing type and insert position
|
||||||
*
|
*
|
||||||
|
@ -77,7 +94,6 @@ class Tree
|
||||||
$this->insert(Node::createAndNode());
|
$this->insert(Node::createAndNode());
|
||||||
}
|
}
|
||||||
$node->parent = $this->lastNode;
|
$node->parent = $this->lastNode;
|
||||||
|
|
||||||
if ($this->lastNode->left == null) {
|
if ($this->lastNode->left == null) {
|
||||||
$this->lastNode->left = $node;
|
$this->lastNode->left = $node;
|
||||||
} elseif ($this->lastNode->right == null) {
|
} elseif ($this->lastNode->right == null) {
|
||||||
|
@ -86,6 +102,7 @@ class Tree
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->lastNode = $node;
|
$this->lastNode = $node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,16 +367,23 @@ class Tree
|
||||||
if ($ctx === null) {
|
if ($ctx === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if ($ctx->type === Node::TYPE_OPERATOR) {
|
|
||||||
if ($ctx->left == $node->left && $ctx->right == $node->right && $ctx->operator == $node->operator) {
|
if ($ctx->type == Node::TYPE_OPERATOR) {
|
||||||
return $ctx;
|
if ($ctx->left == $node->left && $ctx->operator == $node->operator) {
|
||||||
|
if(empty($node->right) || $ctx->right == $node->right) {
|
||||||
|
return $ctx;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
$result = $this->findNode($node, $ctx->left);
|
$result = null;
|
||||||
if ($result === null) {
|
if ($ctx->left) {
|
||||||
|
$result = $this->findNode($node, $ctx->left);
|
||||||
|
} if ($result == null && $ctx->right) {
|
||||||
$result = $this->findNode($node, $ctx->right);
|
$result = $this->findNode($node, $ctx->right);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,21 +391,25 @@ class Tree
|
||||||
/**
|
/**
|
||||||
* Return true if A node with the given attribute on the left side exists
|
* Return true if A node with the given attribute on the left side exists
|
||||||
*
|
*
|
||||||
* @param String $name The attribute to test for existence
|
* @param String $name The attribute to test for existence
|
||||||
* @param Node $ctx The current root node
|
* @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
|
* @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) {
|
if ($ctx === null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ($ctx->type === Node::TYPE_OPERATOR) {
|
if ($ctx->type === Node::TYPE_OPERATOR) {
|
||||||
return $ctx->left === $name;
|
return $ctx->left === $name;
|
||||||
} else {
|
} 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,7 +225,8 @@ class Query extends BaseQuery
|
||||||
$indexes = array_keys($state[$target]);
|
$indexes = array_keys($state[$target]);
|
||||||
if ($baseGroup) {
|
if ($baseGroup) {
|
||||||
$baseGroup->setQuery($this);
|
$baseGroup->setQuery($this);
|
||||||
$indexes = $baseGroup->filter($state[$target]);
|
$idx = array_keys($state[$target]);
|
||||||
|
$indexes = $baseGroup->filter($state[$target], $idx );
|
||||||
}
|
}
|
||||||
if (!isset($result[$target])) {
|
if (!isset($result[$target])) {
|
||||||
$result[$target] = $indexes;
|
$result[$target] = $indexes;
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
|
|
||||||
namespace Icinga\Protocol\Statusdat\Query;
|
namespace Icinga\Protocol\Statusdat\Query;
|
||||||
|
|
||||||
|
use Icinga\Protocol\Ldap\Exception;
|
||||||
|
|
||||||
class Expression implements IQueryPart
|
class Expression implements IQueryPart
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -117,12 +119,18 @@ class Expression implements IQueryPart
|
||||||
case "LIKE":
|
case "LIKE":
|
||||||
$this->CB = "isLike";
|
$this->CB = "isLike";
|
||||||
break;
|
break;
|
||||||
|
case "NOT_LIKE":
|
||||||
|
$this->CB = "isNotLike";
|
||||||
|
break;
|
||||||
case "!=":
|
case "!=":
|
||||||
$this->CB = "isNotEqual";
|
$this->CB = "isNotEqual";
|
||||||
break;
|
break;
|
||||||
case "IN":
|
case "IN":
|
||||||
$this->CB = "isIn";
|
$this->CB = "isIn";
|
||||||
break;
|
break;
|
||||||
|
case "NOT_IN":
|
||||||
|
$this->CB = "isNotIn";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new \Exception("Unknown operator $token in expression $this->expression !");
|
throw new \Exception("Unknown operator $token in expression $this->expression !");
|
||||||
}
|
}
|
||||||
|
@ -216,7 +224,6 @@ class Expression implements IQueryPart
|
||||||
$idx = array_keys($base);
|
$idx = array_keys($base);
|
||||||
}
|
}
|
||||||
$this->basedata = $base;
|
$this->basedata = $base;
|
||||||
|
|
||||||
return array_filter($idx, array($this, "filterFn"));
|
return array_filter($idx, array($this, "filterFn"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,12 +255,24 @@ class Expression implements IQueryPart
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->CB == "isIn") {
|
if ($this->CB == "isIn" || $this->CB == "isNotIn") {
|
||||||
return count(array_intersect($values, $this->value)) > 0;
|
$cmpValues = is_array($this->value) ? $this->value : array($this->value);
|
||||||
}
|
foreach ($cmpValues as $cmpValue) {
|
||||||
if ($this->CB == "isNotIn") {
|
$this->value = $cmpValue;
|
||||||
return count(array_intersect($values, $this->value)) == 0;
|
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) {
|
if ($this->function) {
|
||||||
$values = call_user_func($this->function, $values);
|
$values = call_user_func($this->function, $values);
|
||||||
if (!is_array($values)) {
|
if (!is_array($values)) {
|
||||||
|
@ -355,6 +374,15 @@ class Expression implements IQueryPart
|
||||||
return preg_match("/^" . str_replace("%", ".*", $this->value) . "$/", $value) ? true : false;
|
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
|
* @param $value
|
||||||
* @return bool
|
* @return bool
|
||||||
|
|
|
@ -26,30 +26,48 @@
|
||||||
*/
|
*/
|
||||||
// {{{ICINGA_LICENSE_HEADER}}}
|
// {{{ICINGA_LICENSE_HEADER}}}
|
||||||
|
|
||||||
|
|
||||||
namespace Icinga\Protocol\Statusdat;
|
namespace Icinga\Protocol\Statusdat;
|
||||||
|
|
||||||
|
|
||||||
use Icinga\Filter\Filterable;
|
use Icinga\Filter\Filterable;
|
||||||
use Icinga\Filter\Query\Node;
|
use Icinga\Filter\Query\Node;
|
||||||
use Icinga\Filter\Query\Tree;
|
use Icinga\Filter\Query\Tree;
|
||||||
use Icinga\Protocol\Statusdat\Query\Expression;
|
use Icinga\Protocol\Statusdat\Query\Expression;
|
||||||
use Icinga\Protocol\Statusdat\Query\Group;
|
use Icinga\Protocol\Statusdat\Query\Group;
|
||||||
|
use Icinga\Protocol\Statusdat\Query\IQueryPart;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parser to create statusdat filter expressions from query trees
|
||||||
|
*
|
||||||
|
*/
|
||||||
class TreeToStatusdatQueryParser
|
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) {
|
if ($node->type === Node::TYPE_OPERATOR) {
|
||||||
$op = $node->operator;
|
$op = $node->operator;
|
||||||
$value = $node->right;
|
|
||||||
$node->left = $source->getMappedField($node->left);
|
$node->left = $source->getMappedField($node->left);
|
||||||
if (stripos($node->right, '*') !== false) {
|
$op = 'IN';
|
||||||
$op = 'LIKE';
|
$values = $node->right;
|
||||||
|
|
||||||
|
if ($node->operator === NODE::OPERATOR_EQUALS_NOT) {
|
||||||
|
$op = 'NOT_' . $op;
|
||||||
|
|
||||||
|
}
|
||||||
|
foreach ($values as &$value) {
|
||||||
$value = str_replace('*', '%', $value);
|
$value = str_replace('*', '%', $value);
|
||||||
}
|
}
|
||||||
return new Expression($node->left . ' ' . $op . ' ?', $value);
|
$values = array($values);
|
||||||
|
return new Expression($node->left . ' ' . $op . ' ? ', $values);
|
||||||
} else {
|
} else {
|
||||||
$group = new Group();
|
$group = new Group();
|
||||||
$group->setType(($node->type === Node::TYPE_OR) ? Group::TYPE_OR : Group::TYPE_AND);
|
$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)
|
public function treeToQuery(Tree $tree, Filterable $source)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -70,4 +96,4 @@ class TreeToStatusdatQueryParser
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,29 @@ class FilterBadgeRenderer implements Widget
|
||||||
private $conjunctionCellar = '';
|
private $conjunctionCellar = '';
|
||||||
private $urlFilter;
|
private $urlFilter;
|
||||||
|
|
||||||
|
private $tpl =<<<'EOT'
|
||||||
|
<div class="btn-group">
|
||||||
|
<a title="Click To Remove" class="btn btn-default btn-xs dropdown-toggle" href="{{REMOVE_FILTER}}" data-icinga-target="self">
|
||||||
|
{{FILTER_SUM}}
|
||||||
|
</a>
|
||||||
|
{{SUBLIST}}
|
||||||
|
</div>
|
||||||
|
EOT;
|
||||||
|
|
||||||
|
private $subTpl =<<<'EOT'
|
||||||
|
<button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" role="menu">
|
||||||
|
{{SUBFILTER_LIST}}
|
||||||
|
</ul>
|
||||||
|
EOT;
|
||||||
|
|
||||||
|
private $subItemTpl =<<<'EOT'
|
||||||
|
<li><a title="Click To Remove" href="{{REMOVE_FILTER}}" data-icinga-target="self">{{FILTER_TEXT}}</a></li>
|
||||||
|
EOT;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new badge renderer for this tree
|
* Create a new badge renderer for this tree
|
||||||
*
|
*
|
||||||
|
@ -58,6 +81,41 @@ class FilterBadgeRenderer implements Widget
|
||||||
$this->tree = $tree;
|
$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
|
* Create a removable badge from a query tree node
|
||||||
*
|
*
|
||||||
|
@ -74,15 +132,18 @@ class FilterBadgeRenderer implements Widget
|
||||||
$newTree = $this->tree->withoutNode($node);
|
$newTree = $this->tree->withoutNode($node);
|
||||||
$url = $this->urlFilter->fromTree($newTree);
|
$url = $this->urlFilter->fromTree($newTree);
|
||||||
$url = $basePath . (empty($allParams) ? '?' : '&') . $url;
|
$url = $basePath . (empty($allParams) ? '?' : '&') . $url;
|
||||||
|
$sumText = $this->getSummarizedText($node);
|
||||||
|
|
||||||
return ' <a class="filter-badge btn btn-default btn-xs" href="' . $url . '">'
|
$tpl = str_replace('{{FILTER_SUM}}', $sumText, $this->tpl);
|
||||||
. $this->conjunctionCellar . ' '
|
$tpl = str_replace('{{REMOVE_FILTER}}', $url, $tpl);
|
||||||
. ucfirst($node->left) . ' '
|
if (count($node->right) > 1) {
|
||||||
. $node->operator . ' '
|
$tpl = str_replace('{{SUBLIST}}', $this->getSubEntries($node), $tpl);
|
||||||
. $node->right . '</a>';
|
} else {
|
||||||
|
$tpl = str_replace('{{SUBLIST}}', '', $tpl);
|
||||||
|
}
|
||||||
|
return $tpl;
|
||||||
}
|
}
|
||||||
$result = '';
|
$result = '';
|
||||||
|
|
||||||
$result .= $this->nodeToBadge($node->left);
|
$result .= $this->nodeToBadge($node->left);
|
||||||
$this->conjunctionCellar = $node->type;
|
$this->conjunctionCellar = $node->type;
|
||||||
$result .= $this->nodeToBadge($node->right);
|
$result .= $this->nodeToBadge($node->right);
|
||||||
|
|
|
@ -113,6 +113,7 @@ EOT;
|
||||||
$form->setIgnoreChangeDiscarding(true);
|
$form->setIgnoreChangeDiscarding(true);
|
||||||
$badges = new FilterBadgeRenderer($this->initialFilter);
|
$badges = new FilterBadgeRenderer($this->initialFilter);
|
||||||
$html = str_replace('{{FORM}}', $form->render($view), self::$TPL);
|
$html = str_replace('{{FORM}}', $form->render($view), self::$TPL);
|
||||||
|
$html = '<div class="input-append">' . $html . '</div>';
|
||||||
return str_replace('{{BADGES}}', $badges->render($view), $html);
|
return str_replace('{{BADGES}}', $badges->render($view), $html);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ class Registry implements FilterRegistry
|
||||||
|
|
||||||
$domain->registerAttribute(
|
$domain->registerAttribute(
|
||||||
FilterAttribute::create(new TextFilter())
|
FilterAttribute::create(new TextFilter())
|
||||||
->setHandledAttributes('Name', 'Hostname')
|
->setHandledAttributes('Name', 'Host', 'Hostname')
|
||||||
->setField('host_name')
|
->setField('host_name')
|
||||||
)->registerAttribute(
|
)->registerAttribute(
|
||||||
FilterAttribute::create(StatusFilter::createForHost())
|
FilterAttribute::create(StatusFilter::createForHost())
|
||||||
|
@ -173,6 +173,10 @@ class Registry implements FilterRegistry
|
||||||
FilterAttribute::create(self::getNextCheckFilterType())
|
FilterAttribute::create(self::getNextCheckFilterType())
|
||||||
->setHandledAttributes('Next Check')
|
->setHandledAttributes('Next Check')
|
||||||
->setField('service_next_check')
|
->setField('service_next_check')
|
||||||
|
)->registerAttribute(
|
||||||
|
FilterAttribute::create(new TextFilter())
|
||||||
|
->setHandledAttributes('Hostname', 'Host')
|
||||||
|
->setField('host_name')
|
||||||
);
|
);
|
||||||
return $domain;
|
return $domain;
|
||||||
}
|
}
|
||||||
|
@ -211,11 +215,11 @@ class Registry implements FilterRegistry
|
||||||
|
|
||||||
parse_str($lastQuery, $lastParameters);
|
parse_str($lastQuery, $lastParameters);
|
||||||
if ($lastFilter->root) {
|
if ($lastFilter->root) {
|
||||||
$filter->insert($lastFilter->root);
|
$filter->insertTree($lastFilter);
|
||||||
}
|
}
|
||||||
$params = array();
|
$params = array();
|
||||||
foreach ($lastParameters as $key => $param) {
|
foreach ($lastParameters as $key => $param) {
|
||||||
if (!$filter->hasNodeWithAttribute($key)) {
|
if (!$filter->hasNodeWithAttribute($key) && $view->isValidFilterTarget($key)) {
|
||||||
$params[$key] = $param;
|
$params[$key] = $param;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,4 +234,9 @@ class Registry implements FilterRegistry
|
||||||
$urlString .= $urlParser->fromTree($filter);
|
$urlString .= $urlParser->fromTree($filter);
|
||||||
return '/' . $urlString;
|
return '/' . $urlString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isValid($query)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ class StatusFilter extends FilterType
|
||||||
'Is' => Node::OPERATOR_EQUALS,
|
'Is' => Node::OPERATOR_EQUALS,
|
||||||
'=' => Node::OPERATOR_EQUALS,
|
'=' => Node::OPERATOR_EQUALS,
|
||||||
'!=' => Node::OPERATOR_EQUALS_NOT,
|
'!=' => 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)
|
private function getOperatorValueArray($query)
|
||||||
{
|
{
|
||||||
$result = array(null, null, null);
|
$result = array(null, null, null);
|
||||||
foreach ($this->getOperators() as $operator) {
|
$result[0] = self::getMatchingOperatorForQuery($query);
|
||||||
if (stripos($query, $operator) === 0) {
|
|
||||||
$result[0] = $operator;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($result[0] === null) {
|
if ($result[0] === null) {
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
@ -234,6 +229,7 @@ class StatusFilter extends FilterType
|
||||||
if ($result[2] && !$this->subFilter->isValidQuery($result[2])) {
|
if ($result[2] && !$this->subFilter->isValidQuery($result[2])) {
|
||||||
return array(null, null, null);
|
return array(null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,9 @@ class UrlViewFilter
|
||||||
*/
|
*/
|
||||||
private $target;
|
private $target;
|
||||||
|
|
||||||
|
private $supportedConjunctions = array('&'/*, '|'*/);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new ViewFilter
|
* Create a new ViewFilter
|
||||||
*
|
*
|
||||||
|
@ -77,9 +80,43 @@ class UrlViewFilter
|
||||||
if ($this->target) {
|
if ($this->target) {
|
||||||
$filter = $filter->getCopyForFilterable($this->target);
|
$filter = $filter->getCopyForFilterable($this->target);
|
||||||
}
|
}
|
||||||
|
$filter = $this->normalizeTreeNode($filter->root);
|
||||||
|
$filter->root = $filter->normalizeTree($filter->root);
|
||||||
return $this->convertNodeToUrlString($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
|
* Parse the given given url and return a query tree
|
||||||
*
|
*
|
||||||
|
@ -103,8 +140,8 @@ class UrlViewFilter
|
||||||
} elseif (is_array($token)) {
|
} elseif (is_array($token)) {
|
||||||
$tree->insert(
|
$tree->insert(
|
||||||
Node::createOperatorNode(
|
Node::createOperatorNode(
|
||||||
$token[self::FILTER_OPERATOR],
|
trim($token[self::FILTER_OPERATOR]),
|
||||||
$token[self::FILTER_TARGET],
|
trim($token[self::FILTER_TARGET]),
|
||||||
$token[self::FILTER_VALUE]
|
$token[self::FILTER_VALUE]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -133,12 +170,16 @@ class UrlViewFilter
|
||||||
{
|
{
|
||||||
$left = null;
|
$left = null;
|
||||||
$right = null;
|
$right = null;
|
||||||
|
|
||||||
if ($node->type === Node::TYPE_OPERATOR) {
|
if ($node->type === Node::TYPE_OPERATOR) {
|
||||||
if ($this->target && !$this->target->isValidFilterTarget($node->left)) {
|
if ($this->target && !$this->target->isValidFilterTarget($node->left)) {
|
||||||
return null;
|
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) {
|
if ($node->left) {
|
||||||
$left = $this->convertNodeToUrlString($node->left);
|
$left = $this->convertNodeToUrlString($node->left);
|
||||||
|
@ -167,7 +208,7 @@ class UrlViewFilter
|
||||||
* array(
|
* array(
|
||||||
* self::FILTER_TARGET => 'Attribute',
|
* self::FILTER_TARGET => 'Attribute',
|
||||||
* self::FILTER_OPERATOR => '!=',
|
* self::FILTER_OPERATOR => '!=',
|
||||||
* self::FILTER_VALUE => 'Value'
|
* self::FILTER_VALUE => array('Value')
|
||||||
* )
|
* )
|
||||||
*
|
*
|
||||||
* @param String $query The query to tokenize
|
* @param String $query The query to tokenize
|
||||||
|
@ -233,7 +274,6 @@ class UrlViewFilter
|
||||||
*/
|
*/
|
||||||
private function parseTarget($query, $currentPos, array &$tokenList)
|
private function parseTarget($query, $currentPos, array &$tokenList)
|
||||||
{
|
{
|
||||||
$conjunctions = array('&', '|');
|
|
||||||
$i = $currentPos;
|
$i = $currentPos;
|
||||||
|
|
||||||
for ($i; $i < strlen($query); $i++) {
|
for ($i; $i < strlen($query); $i++) {
|
||||||
|
@ -254,7 +294,7 @@ class UrlViewFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implicit value token (test=1|2)
|
// 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);
|
$nrOfSymbols = count($tokenList);
|
||||||
if ($nrOfSymbols <= 2) {
|
if ($nrOfSymbols <= 2) {
|
||||||
return array($i, self::FILTER_TARGET);
|
return array($i, self::FILTER_TARGET);
|
||||||
|
@ -292,7 +332,6 @@ class UrlViewFilter
|
||||||
{
|
{
|
||||||
|
|
||||||
$i = $currentPos;
|
$i = $currentPos;
|
||||||
$conjunctions = array('&', '|');
|
|
||||||
$nrOfSymbols = count($tokenList);
|
$nrOfSymbols = count($tokenList);
|
||||||
|
|
||||||
if ($nrOfSymbols == 0) {
|
if ($nrOfSymbols == 0) {
|
||||||
|
@ -301,7 +340,7 @@ class UrlViewFilter
|
||||||
$lastState = &$tokenList[$nrOfSymbols-1];
|
$lastState = &$tokenList[$nrOfSymbols-1];
|
||||||
for ($i; $i < strlen($query); $i++) {
|
for ($i; $i < strlen($query); $i++) {
|
||||||
$currentChar = $query[$i];
|
$currentChar = $query[$i];
|
||||||
if (in_array($currentChar, $conjunctions)) {
|
if (in_array($currentChar, $this->supportedConjunctions)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,9 +351,9 @@ class UrlViewFilter
|
||||||
array_pop($tokenList);
|
array_pop($tokenList);
|
||||||
return array($currentPos, self::FILTER_TARGET);
|
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;
|
$tokenList[] = $currentChar;
|
||||||
}
|
}
|
||||||
return array($i, self::FILTER_TARGET);
|
return array($i, self::FILTER_TARGET);
|
||||||
|
@ -331,10 +370,9 @@ class UrlViewFilter
|
||||||
*/
|
*/
|
||||||
private function skip($query, $currentPos)
|
private function skip($query, $currentPos)
|
||||||
{
|
{
|
||||||
$conjunctions = array('&', '|');
|
|
||||||
for ($i = $currentPos; strlen($query); $i++) {
|
for ($i = $currentPos; strlen($query); $i++) {
|
||||||
$currentChar = $query[$i];
|
$currentChar = $query[$i];
|
||||||
if (in_array($currentChar, $conjunctions)) {
|
if (in_array($currentChar, $this->supportedConjunctions)) {
|
||||||
return array($i, self::FILTER_TARGET);
|
return array($i, self::FILTER_TARGET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ define(['jquery', 'logging', 'URIjs/URI', 'components/app/container'], function(
|
||||||
* @param {String} state The HTTP state as a string
|
* @param {String} state The HTTP state as a string
|
||||||
*/
|
*/
|
||||||
this.showError = function(error, state) {
|
this.showError = function(error, state) {
|
||||||
if (state === 'abort') {
|
if (!error.message || state === 'abort') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.inputDom.popover('destroy').popover({
|
this.inputDom.popover('destroy').popover({
|
||||||
|
@ -136,7 +136,12 @@ define(['jquery', 'logging', 'URIjs/URI', 'components/app/container'], function(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var list = $('<ul>').addClass('nav nav-stacked nav-pills');
|
if (response.valid) {
|
||||||
|
this.inputDom.parent('div').removeClass('has-error').addClass('has-success');
|
||||||
|
} else {
|
||||||
|
this.inputDom.parent('div').removeClass('has-success').addClass('has-error');
|
||||||
|
}
|
||||||
|
var list = $('<ul>').addClass('nav nav-stacked');
|
||||||
$.each(response.proposals, (function(idx, token) {
|
$.each(response.proposals, (function(idx, token) {
|
||||||
var displayToken = token.replace(/(\{|\})/g, '');
|
var displayToken = token.replace(/(\{|\})/g, '');
|
||||||
var proposal = $('<li>').
|
var proposal = $('<li>').
|
||||||
|
@ -165,7 +170,7 @@ define(['jquery', 'logging', 'URIjs/URI', 'components/app/container'], function(
|
||||||
*/
|
*/
|
||||||
this.updateFilter = function() {
|
this.updateFilter = function() {
|
||||||
var query = $.trim(this.inputDom.val());
|
var query = $.trim(this.inputDom.val());
|
||||||
this.pendingRequest = $.ajax(this.getRequestParams(query))
|
$.ajax(this.getRequestParams(query))
|
||||||
.done((function(response) {
|
.done((function(response) {
|
||||||
var domContainer = new Container(this.inputDom);
|
var domContainer = new Container(this.inputDom);
|
||||||
var url = response.urlParam;
|
var url = response.urlParam;
|
||||||
|
@ -217,7 +222,7 @@ define(['jquery', 'logging', 'URIjs/URI', 'components/app/container'], function(
|
||||||
if (this.lastQueuedEvent) {
|
if (this.lastQueuedEvent) {
|
||||||
window.clearTimeout(this.lastQueuedEvent);
|
window.clearTimeout(this.lastQueuedEvent);
|
||||||
}
|
}
|
||||||
this.lastQueuedEvent = window.setTimeout(this.getProposal.bind(this), 200);
|
this.lastQueuedEvent = window.setTimeout(this.getProposal.bind(this), 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.construct();
|
this.construct();
|
||||||
|
|
Loading…
Reference in New Issue