setFilter($props['filter']); } if (array_key_exists('query', $props)) { $this->setQuery($props['query']); } } public function setFilter(Filter $filter) { $this->filter = $filter; return $this; } public function getFilter() { if ($this->filter === null) { $this->filter = Filter::fromQueryString((string) $this->url()->getParams()); } return $this->filter; } /** * Set columns to search in * * @param array $searchColumns * * @return $this */ public function setSearchColumns(array $searchColumns = null) { $this->searchColumns = $searchColumns; return $this; } public function setUrl($url) { $this->url = $url; return $this; } protected function url() { if ($this->url === null) { $this->url = Url::fromRequest(); } return $this->url; } protected function preservedUrl() { if ($this->preservedUrl === null) { $this->preservedUrl = $this->url()->with($this->preservedParams); } return $this->preservedUrl; } /** * Set the query to filter * * @param Filterable $query * * @return $this */ public function setQuery(Filterable $query) { $this->query = $query; return $this; } public function ignoreParams() { $this->ignoreParams = func_get_args(); return $this; } public function preserveParams() { $this->preserveParams = func_get_args(); return $this; } /** * Get whether the filter control is visible * * @return bool */ public function isVisible() { return $this->visible; } /** * Set whether the filter control is visible * * @param bool $visible * * @return $this */ public function setVisible($visible) { $this->visible = (bool) $visible; return $this; } protected function redirectNow($url) { $response = Icinga::app()->getFrontController()->getResponse(); $response->redirectAndExit($url); } protected function mergeRootExpression($filter, $column, $sign, $expression) { $found = false; if ($filter->isChain() && $filter->getOperatorName() === 'AND') { foreach ($filter->filters() as $f) { if ($f->isExpression() && $f->getColumn() === $column && $f->getSign() === $sign ) { $f->setExpression($expression); $found = true; break; } } } elseif ($filter->isExpression()) { if ($filter->getColumn() === $column && $filter->getSign() === $sign) { $filter->setExpression($expression); $found = true; } } if (! $found) { $filter = $filter->andFilter( Filter::expression($column, $sign, $expression) ); } return $filter; } protected function resetSearchColumns(Filter &$filter) { if ($filter->isChain()) { $filters = &$filter->filters(); if (!($empty = empty($filters))) { foreach ($filters as $k => &$f) { if (false === $this->resetSearchColumns($f)) { unset($filters[$k]); } } } return $empty || !empty($filters); } return $filter->isExpression() ? !( in_array($filter->getColumn(), $this->searchColumns) && $filter->getSign() === '=' ) : true; } public function handleRequest($request) { $this->setUrl($request->getUrl()->without($this->ignoreParams)); $params = $this->url()->getParams(); $preserve = array(); foreach ($this->preserveParams as $key) { if (null !== ($value = $params->shift($key))) { $preserve[$key] = $value; } } $this->preservedParams = $preserve; $add = $params->shift('addFilter'); $remove = $params->shift('removeFilter'); $strip = $params->shift('stripFilter'); $modify = $params->shift('modifyFilter'); $search = null; if ($request->isPost()) { $search = $request->getPost('q'); } if ($search === null) { $search = $params->shift('q'); } $filter = $this->getFilter(); if ($search !== null) { if (strpos($search, '=') !== false) { list($k, $v) = preg_split('/=/', $search); $filter = $this->mergeRootExpression($filter, trim($k), '=', ltrim($v)); } else { if ($this->searchColumns === null && $this->query instanceof FilterColumns) { $this->searchColumns = $this->query->getSearchColumns($search); } if (! empty($this->searchColumns)) { if (! $this->resetSearchColumns($filter)) { $filter = Filter::matchAll(); } $filters = array(); $search = trim($search); foreach ($this->searchColumns as $searchColumn) { $filters[] = Filter::expression($searchColumn, '=', "*$search*"); } $filter = $filter->andFilter(new FilterOr($filters)); } else { Notification::error(mt('monitoring', 'Cannot search here')); return $this; } } $url = Url::fromRequest()->onlyWith($this->preserveParams); $urlParams = $url->getParams(); $url->setQueryString($filter->toQueryString()); foreach ($urlParams->toArray(false) as $key => $value) { $url->getParams()->addEncoded($key, $value); } $this->redirectNow($url); } if ($remove) { $redirect = $this->url(); if ($filter->getById($remove)->isRootNode()) { $redirect->setQueryString(''); } else { $filter->removeId($remove); $redirect->setQueryString($filter->toQueryString())->getParams()->add('modifyFilter'); } $this->redirectNow($redirect->addParams($preserve)); } if ($strip) { $redirect = $this->url(); $subId = $strip . '-1'; if ($filter->getId() === $strip) { $filter = $filter->getById($strip . '-1'); } else { $filter->replaceById($strip, $filter->getById($strip . '-1')); } $redirect->setQueryString($filter->toQueryString())->getParams()->add('modifyFilter'); $this->redirectNow($redirect->addParams($preserve)); } if ($modify) { if ($request->isPost()) { if ($request->get('cancel') === 'Cancel') { $this->redirectNow($this->preservedUrl()->without('modifyFilter')); } if ($request->get('formUID') === 'FilterEditor') { $filter = $this->applyChanges($request->getPost()); $url = $this->url()->setQueryString($filter->toQueryString())->addParams($preserve); $url->getParams()->add('modifyFilter'); $addFilter = $request->get('add_filter'); if ($addFilter !== null) { $url->setParam('addFilter', $addFilter); } $removeFilter = $request->get('remove_filter'); if ($removeFilter !== null) { $url->setParam('removeFilter', $removeFilter); } $this->redirectNow($url); } } $this->url()->getParams()->add('modifyFilter'); } if ($add) { $this->addFilterToId($add); } if ($this->query !== null && $request->isGet()) { $this->query->applyFilter($this->getFilter()); } return $this; } protected function select($name, $list, $selected, $attributes = null) { $view = $this->view(); if ($attributes === null) { $attributes = ''; } else { $attributes = $view->propertiesToString($attributes); } $html = sprintf( '' . "\n\n"; return $html; } protected function addFilterToId($id) { $this->addTo = $id; return $this; } protected function removeIndex($idx) { $this->selectedIdx = $idx; return $this; } protected function removeLink(Filter $filter) { return "'; } protected function addLink(Filter $filter) { return "'; } protected function stripLink(Filter $filter) { return $this->view()->qlink( '', $this->preservedUrl()->with('stripFilter', $filter->getId()), null, array( 'icon' => 'minus', 'title' => t('Strip this filter') ) ); } protected function cancelLink() { return $this->view()->qlink( '', $this->preservedUrl()->without('addFilter'), null, array( 'icon' => 'cancel', 'title' => t('Cancel this operation') ) ); } protected function renderFilter($filter, $level = 0) { if ($level === 0 && $filter->isChain() && $filter->isEmpty()) { return ''; } if ($filter instanceof FilterChain) { return $this->renderFilterChain($filter, $level); } elseif ($filter instanceof FilterExpression) { return $this->renderFilterExpression($filter); } else { throw new ProgrammingError('Got a Filter being neither expression nor chain'); } } protected function renderFilterChain(FilterChain $filter, $level) { $html = ' ' . $this->selectOperator($filter) . $this->removeLink($filter) . ($filter->count() === 1 ? $this->stripLink($filter) : '') . $this->addLink($filter); if ($filter->isEmpty() && ! $this->addTo) { return $html; } $parts = array(); foreach ($filter->filters() as $f) { $parts[] = '
  • ' . $this->renderFilter($f, $level + 1) . '
  • '; } if ($this->addTo && $this->addTo == $filter->getId()) { $parts[] = '
  • ' . $this->renderNewFilter() .$this->cancelLink(). '
  • '; } $class = $level === 0 ? ' class="datafilter"' : ''; $html .= sprintf( "\n%s\n", $class, implode("", $parts) ); return $html; } protected function renderFilterExpression(FilterExpression $filter) { if ($this->addTo && $this->addTo === $filter->getId()) { return preg_replace( '/ class="autosubmit"/', ' class="autofocus"', $this->selectOperator() ) . '' ; } else { return $this->selectColumn($filter) . $this->selectSign($filter) . $this->text($filter) . $this->removeLink($filter) . $this->addLink($filter) ; } } protected function text(Filter $filter = null) { $value = $filter === null ? '' : $filter->getExpression(); if (is_array($value)) { $value = '(' . implode('|', $value) . ')'; } return sprintf( '', $this->elementId('value', $filter), $this->view()->escape($value) ); } protected function renderNewFilter() { $html = $this->selectColumn() . $this->selectSign() . $this->text(); return preg_replace( '/ class="autosubmit"/', '', $html ); } protected function arrayForSelect($array, $flip = false) { $res = array(); foreach ($array as $k => $v) { if (is_int($k)) { $res[$v] = ucwords(str_replace('_', ' ', $v)); } elseif ($flip) { $res[$v] = $k; } else { $res[$k] = $v; } } // sort($res); return $res; } protected function elementId($prefix, Filter $filter = null) { if ($filter === null) { return $prefix . '_new_' . ($this->addTo ?: '0'); } else { return $prefix . '_' . $filter->getId(); } } protected function selectOperator(Filter $filter = null) { $ops = array( 'AND' => 'AND', 'OR' => 'OR', 'NOT' => 'NOT' ); return $this->select( $this->elementId('operator', $filter), $ops, $filter === null ? null : $filter->getOperatorName(), ['class' => 'filter-operator'] ); } protected function selectSign(Filter $filter = null) { $signs = array( '=' => '=', '!=' => '!=', '>' => '>', '<' => '<', '>=' => '>=', '<=' => '<=', ); return $this->select( $this->elementId('sign', $filter), $signs, $filter === null ? null : $filter->getSign(), ['class' => 'filter-rule'] ); } public function setColumns(array $columns = null) { $this->cachedColumnSelect = $columns ? $this->arrayForSelect($columns) : null; return $this; } protected function selectColumn(Filter $filter = null) { $active = $filter === null ? null : $filter->getColumn(); if ($this->cachedColumnSelect === null && $this->query === null) { return sprintf( '', $this->elementId('column', $filter), $this->view()->escape($active) // Escape attribute? ); } if ($this->cachedColumnSelect === null && $this->query instanceof FilterColumns) { $this->cachedColumnSelect = $this->arrayForSelect($this->query->getFilterColumns(), true); asort($this->cachedColumnSelect); } elseif ($this->cachedColumnSelect === null) { throw new ProgrammingError('No columns set nor does the query provide any'); } $cols = $this->cachedColumnSelect; if ($active && !isset($cols[$active])) { $cols[$active] = str_replace('_', ' ', ucfirst(ltrim($active, '_'))); } return $this->select($this->elementId('column', $filter), $cols, $active); } protected function applyChanges($changes) { $filter = $this->filter; $pairs = array(); $addTo = null; $add = array(); foreach ($changes as $k => $v) { if (preg_match('/^(column|value|sign|operator)((?:_new)?)_([\d-]+)$/', $k, $m)) { if ($m[2] === '_new') { if ($addTo !== null && $addTo !== $m[3]) { throw new \Exception('F...U'); } $addTo = $m[3]; $add[$m[1]] = $v; } else { $pairs[$m[3]][$m[1]] = $v; } } } $operators = array(); foreach ($pairs as $id => $fs) { if (array_key_exists('operator', $fs)) { $operators[$id] = $fs['operator']; } else { $f = $filter->getById($id); $f->setColumn($fs['column']); if ($f->getSign() !== $fs['sign']) { if ($f->isRootNode()) { $filter = $f->setSign($fs['sign']); } else { $filter->replaceById($id, $f->setSign($fs['sign'])); } } $f->setExpression($fs['value']); } } krsort($operators, SORT_NATURAL); foreach ($operators as $id => $operator) { $f = $filter->getById($id); if ($f->getOperatorName() !== $operator) { if ($f->isRootNode()) { $filter = $f->setOperatorName($operator); } else { $filter->replaceById($id, $f->setOperatorName($operator)); } } } if ($addTo !== null) { if ($addTo === '0') { $filter = Filter::expression($add['column'], $add['sign'], $add['value']); } else { $parent = $filter->getById($addTo); $f = Filter::expression($add['column'], $add['sign'], $add['value']); if (isset($add['operator'])) { switch ($add['operator']) { case 'AND': if ($parent->isExpression()) { if ($parent->isRootNode()) { $filter = Filter::matchAll(clone $parent, $f); } else { $filter = $filter->replaceById($addTo, Filter::matchAll(clone $parent, $f)); } } else { $parent->addFilter(Filter::matchAll($f)); } break; case 'OR': if ($parent->isExpression()) { if ($parent->isRootNode()) { $filter = Filter::matchAny(clone $parent, $f); } else { $filter = $filter->replaceById($addTo, Filter::matchAny(clone $parent, $f)); } } else { $parent->addFilter(Filter::matchAny($f)); } break; case 'NOT': if ($parent->isExpression()) { if ($parent->isRootNode()) { $filter = Filter::not(Filter::matchAll($parent, $f)); } else { $filter = $filter->replaceById($addTo, Filter::not(Filter::matchAll($parent, $f))); } } else { $parent->addFilter(Filter::not($f)); } break; } } else { $parent->addFilter($f); } } } return $filter; } public function renderSearch() { $preservedUrl = $this->preservedUrl(); $html = ' '; if ($this->filter->isEmpty()) { $title = t('Filter this list'); } else { $title = t('Modify this filter'); if (! $this->filter->isEmpty()) { $title .= ': ' . $this->view()->escape($this->filter); } } return $html . '' . '' . ''; } public function render() { if (! $this->visible) { return ''; } if (! $this->preservedUrl()->getParam('modifyFilter')) { return '
    ' . $this->renderSearch() . $this->view()->escape($this->shorten($this->filter, 50)) . '
    '; } return '
    ' . $this->renderSearch() . '
    ' . '' . '
    • ' . $this->renderFilter($this->filter) . '
    ' . '
    ' . '' . '' . '
    ' . '' . '
    ' . '
    '; } protected function shorten($string, $length) { if (strlen($string) > $length) { return substr($string, 0, $length) . '...'; } return $string; } public function __toString() { try { return $this->render(); } catch (Exception $e) { return 'ERROR in FilterEditor: ' . $e->getMessage(); } } }