Merge branch 'feature/nested-apply-rules-12033'
This commit is contained in:
commit
d804ebb321
|
@ -7,7 +7,6 @@ use Icinga\Module\Director\Web\Form\QuickSubForm;
|
|||
|
||||
class AssignListSubForm extends QuickSubForm
|
||||
{
|
||||
|
||||
protected $object;
|
||||
|
||||
public function setObject($object)
|
||||
|
@ -16,20 +15,15 @@ class AssignListSubForm extends QuickSubForm
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setValue($value)
|
||||
{
|
||||
var_dump($value);
|
||||
}
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$idx = -1;
|
||||
|
||||
if ($this->object && $this->object->supportsAssignments()) {
|
||||
// $this->setElementValue('assignlist', $object->assignments()->getFormValues());
|
||||
foreach ($this->object->assignments()->getFormValues() as $values) {
|
||||
|
||||
foreach ($this->object->assignments()->getValues() as $values) {
|
||||
$idx++;
|
||||
$sub = new AssignmentSubForm();
|
||||
$sub = $this->loadForm('assignmentSub');
|
||||
$sub->setObject($this->object);
|
||||
$sub->setup();
|
||||
$sub->populate($values);
|
||||
|
@ -38,7 +32,7 @@ class AssignListSubForm extends QuickSubForm
|
|||
}
|
||||
|
||||
$idx++;
|
||||
$sub = new AssignmentSubForm();
|
||||
$sub = $this->loadForm('assignmentSub');
|
||||
$sub->setObject($this->object);
|
||||
$sub->setup();
|
||||
$this->addSubForm($sub, $idx);
|
||||
|
@ -48,6 +42,7 @@ class AssignListSubForm extends QuickSubForm
|
|||
'ignore' => true,
|
||||
));
|
||||
$this->getElement('addmore')->setDecorators(array('ViewHelper'));
|
||||
|
||||
}
|
||||
|
||||
public function loadDefaultDecorators()
|
||||
|
@ -56,7 +51,7 @@ class AssignListSubForm extends QuickSubForm
|
|||
'FormElements',
|
||||
array('HtmlTag', array(
|
||||
'tag' => 'ul',
|
||||
'class' => 'assign-rule'
|
||||
'class' => 'assign-rule required'
|
||||
)),
|
||||
array('Fieldset', array(
|
||||
'legend' => 'Assignment rules',
|
||||
|
|
|
@ -24,44 +24,13 @@ class AssignmentSubForm extends QuickSubForm
|
|||
'class' => 'assign-type',
|
||||
'value' => 'assign'
|
||||
));
|
||||
$this->addElement('select', 'property', array(
|
||||
'label' => $this->translate('Property'),
|
||||
'class' => 'assign-property autosubmit',
|
||||
'multiOptions' => $this->optionalEnum(IcingaHost::enumProperties($this->object->getConnection(), 'host.'))
|
||||
));
|
||||
$this->addElement('select', 'operator', array(
|
||||
'label' => $this->translate('Operator'),
|
||||
'multiOptions' => array(
|
||||
'=' => '=',
|
||||
'!=' => '!=',
|
||||
'>' => '>',
|
||||
'>=' => '>=',
|
||||
'<=' => '<=',
|
||||
'<' => '<',
|
||||
),
|
||||
'required' => $this->valueIsEmpty($this->getValue('property')),
|
||||
'value' => '=',
|
||||
'class' => 'assign-operator',
|
||||
|
||||
$this->addElement('dataFilter', 'filter_string', array(
|
||||
'columns' => IcingaHost::enumProperties($this->db)
|
||||
));
|
||||
|
||||
$this->addElement('text', 'expression', array(
|
||||
'label' => $this->translate('Expression'),
|
||||
'placeholder' => $this->translate('Expression'),
|
||||
'class' => 'assign-expression',
|
||||
'required' => !$this->valueIsEmpty($this->getValue('property'))
|
||||
));
|
||||
/*
|
||||
$this->addElement('submit', 'remove', array(
|
||||
'label' => '-',
|
||||
'ignore' => true
|
||||
));
|
||||
$this->addElement('submit', 'add', array(
|
||||
'label' => '+',
|
||||
'ignore' => true
|
||||
));
|
||||
*/
|
||||
foreach ($this->getElements() as $el) {
|
||||
$el->setDecorators(array('ViewHelper'));
|
||||
$el->setDecorators(array('ViewHelper', 'Errors'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Icinga\Module\Director\Forms;
|
||||
|
||||
use Icinga\Module\Director\Objects\IcingaHost;
|
||||
use Icinga\Module\Director\Web\Form\DirectorObjectForm;
|
||||
|
||||
class IcingaHostGroupForm extends DirectorObjectForm
|
||||
|
@ -23,12 +24,14 @@ class IcingaHostGroupForm extends DirectorObjectForm
|
|||
|
||||
protected function addAssignmentElements()
|
||||
{
|
||||
$sub = new AssignListSubForm();
|
||||
$sub->setObject($this->object());
|
||||
$sub->setup();
|
||||
$sub->setOrder(30);
|
||||
|
||||
$this->addSubForm($sub, 'assignlist');
|
||||
$this->addAssignFilter(array(
|
||||
'columns' => IcingaHost::enumProperties($this->db, 'host.'),
|
||||
'required' => true,
|
||||
'description' => $this->translate(
|
||||
'This allows you to configure an assignment filter. Please feel'
|
||||
. ' free to combine as many nested operators as you want'
|
||||
)
|
||||
));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Icinga\Module\Director\Forms;
|
||||
|
||||
use Icinga\Module\Director\Objects\IcingaHost;
|
||||
use Icinga\Module\Director\Objects\IcingaService;
|
||||
use Icinga\Module\Director\Web\Form\DirectorObjectForm;
|
||||
|
||||
class IcingaNotificationForm extends DirectorObjectForm
|
||||
|
@ -41,31 +43,40 @@ class IcingaNotificationForm extends DirectorObjectForm
|
|||
return $this;
|
||||
}
|
||||
|
||||
$this->addElement(
|
||||
'select',
|
||||
'apply_to',
|
||||
array(
|
||||
$this->addElement('select', 'apply_to', array(
|
||||
'label' => $this->translate('Apply to'),
|
||||
'description' => $this->translate(
|
||||
'Whether this notification should affect hosts or services'
|
||||
),
|
||||
'required' => true,
|
||||
'class' => 'autosubmit',
|
||||
'multiOptions' => $this->optionalEnum(
|
||||
array(
|
||||
'host' => $this->translate('Hosts'),
|
||||
'service' => $this->translate('Services'),
|
||||
)
|
||||
)
|
||||
));
|
||||
|
||||
$applyTo = $this->getSentOrObjectValue('apply_to');
|
||||
|
||||
if ($applyTo === 'host') {
|
||||
$columns = IcingaHost::enumProperties($this->db, 'host.');
|
||||
} elseif ($applyTo === 'service') {
|
||||
// TODO: Also add host properties
|
||||
$columns = IcingaService::enumProperties($this->db, 'service.');
|
||||
} else {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->addAssignFilter(array(
|
||||
'columns' => $columns,
|
||||
'required' => true,
|
||||
'description' => $this->translate(
|
||||
'This allows you to configure an assignment filter. Please feel'
|
||||
. ' free to combine as many nested operators as you want'
|
||||
)
|
||||
);
|
||||
|
||||
$sub = new AssignListSubForm();
|
||||
$sub->setObject($this->getObject());
|
||||
$sub->setup();
|
||||
$sub->setOrder(30);
|
||||
|
||||
$this->addSubForm($sub, 'assignlist');
|
||||
|
||||
));
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
|
@ -157,16 +157,14 @@ class IcingaServiceForm extends DirectorObjectForm
|
|||
|
||||
protected function addAssignmentElements()
|
||||
{
|
||||
if (!$this->object || !$this->object->isApplyRule()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$sub = new AssignListSubForm();
|
||||
$sub->setObject($this->getObject());
|
||||
$sub->setup();
|
||||
$sub->setOrder(30);
|
||||
|
||||
$this->addSubForm($sub, 'assignlist');
|
||||
$this->addAssignFilter(array(
|
||||
'columns' => IcingaHost::enumProperties($this->db, 'host.'),
|
||||
'required' => true,
|
||||
'description' => $this->translate(
|
||||
'This allows you to configure an assignment filter. Please feel'
|
||||
. ' free to combine as many nested operators as you want'
|
||||
)
|
||||
));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ class IcingaNotificationTable extends IcingaObjectTable
|
|||
$htm .= ' ' . $v->qlink(
|
||||
'Create apply-rule',
|
||||
'director/notification/add',
|
||||
array('apply' => $row->notification),
|
||||
array('apply' => $row->notification, 'type' => 'apply'),
|
||||
array('class' => 'icon-plus')
|
||||
);
|
||||
|
||||
|
@ -72,23 +72,12 @@ class IcingaNotificationTable extends IcingaObjectTable
|
|||
|
||||
protected function appliedOnes($id)
|
||||
{
|
||||
if ($this->connection()->isPgsql()) {
|
||||
$nameCol = "s.object_name || COALESCE(': ' || ARRAY_TO_STRING(ARRAY_AGG("
|
||||
. "a.assign_type || ' where ' || a.filter_string"
|
||||
. " ORDER BY a.assign_type, a.filter_string), ', '), '')";
|
||||
} else {
|
||||
$nameCol = "s.object_name || COALESCE(': ' || GROUP_CONCAT("
|
||||
. "a.assign_type || ' where ' || a.filter_string"
|
||||
. " ORDER BY a.assign_type, a.filter_string SEPARATOR ', '"
|
||||
. "), '')";
|
||||
}
|
||||
|
||||
$db = $this->connection()->getConnection();
|
||||
$query = $db->select()->from(
|
||||
array('s' => 'icinga_notification'),
|
||||
array(
|
||||
'id' => 's.id',
|
||||
'objectname' => $nameCol,
|
||||
'objectname' => 's.object_name',
|
||||
)
|
||||
)->join(
|
||||
array('i' => 'icinga_notification_inheritance'),
|
||||
|
@ -97,11 +86,6 @@ class IcingaNotificationTable extends IcingaObjectTable
|
|||
)->where('i.parent_notification_id = ?', $id)
|
||||
->where('s.object_type = ?', 'apply');
|
||||
|
||||
$query->joinLeft(
|
||||
array('a' => 'icinga_notification_assignment'),
|
||||
'a.notification_id = s.id',
|
||||
array()
|
||||
)->group('s.id');
|
||||
|
||||
return $db->fetchPairs($query);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace Icinga\Module\Director\Tables;
|
||||
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Module\Director\IcingaConfig\AssignRenderer;
|
||||
use Icinga\Module\Director\Web\Table\QuickTable;
|
||||
|
||||
class IcingaServiceTable extends QuickTable
|
||||
|
@ -55,13 +58,26 @@ class IcingaServiceTable extends QuickTable
|
|||
}
|
||||
|
||||
} else {
|
||||
$htm .= '. Related apply rules: <ul class="apply-rules">';
|
||||
foreach ($extra as $id => $service) {
|
||||
$htm .= '<li>'
|
||||
. $v->qlink($service, 'director/service', array('id' => $id))
|
||||
. '</li>';
|
||||
$htm .= '. Related apply rules: <table class="apply-rules">';
|
||||
foreach ($extra as $service) {
|
||||
$href = $v->url('director/service', array('id' => $service->id));
|
||||
$htm .= "<tr href=\"$href\">";
|
||||
|
||||
try {
|
||||
$prettyFilter = AssignRenderer::forFilter(
|
||||
Filter::fromQueryString($service->assign_filter)
|
||||
)->renderAssign();
|
||||
}
|
||||
$htm .= '</ul>';
|
||||
catch (IcingaException $e) {
|
||||
// ignore errors in filter rendering
|
||||
$prettyFilter = 'Error in Filter rendering: ' . $e->getMessage();
|
||||
}
|
||||
|
||||
$htm .= "<td><a href=\"$href\">" . $service->object_name . '</a></td>';
|
||||
$htm .= '<td>' . $prettyFilter . '</td>';
|
||||
$htm .= '<tr>';
|
||||
}
|
||||
$htm .= '</table>';
|
||||
$htm .= $v->qlink(
|
||||
'Add more',
|
||||
'director/service/add',
|
||||
|
@ -110,7 +126,8 @@ class IcingaServiceTable extends QuickTable
|
|||
array('s' => 'icinga_service'),
|
||||
array(
|
||||
'id' => 's.id',
|
||||
'objectname' => $nameCol,
|
||||
'object_name' => 's.object_name',
|
||||
'assign_filter' => 's.assign_filter',
|
||||
)
|
||||
)->join(
|
||||
array('i' => 'icinga_service_inheritance'),
|
||||
|
@ -119,13 +136,7 @@ class IcingaServiceTable extends QuickTable
|
|||
)->where('i.parent_service_id = ?', $id)
|
||||
->where('s.object_type = ?', 'apply');
|
||||
|
||||
$query->joinLeft(
|
||||
array('a' => 'icinga_service_assignment'),
|
||||
'a.service_id = s.id',
|
||||
array()
|
||||
)->group('s.id');
|
||||
|
||||
return $db->fetchPairs($query);
|
||||
return $db->fetchAll($query);
|
||||
}
|
||||
|
||||
public function getBaseQuery()
|
||||
|
|
|
@ -0,0 +1,453 @@
|
|||
<?php
|
||||
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Data\Filter\FilterChain;
|
||||
use Icinga\Data\Filter\FilterExpression;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Module\Director\Objects\IcingaObject;
|
||||
use Icinga\Module\Director\Objects\IcingaObjectGroup;
|
||||
use Icinga\Module\Director\Web\Form\IconHelper;
|
||||
|
||||
/**
|
||||
* View helper for extensible sets
|
||||
*
|
||||
* Avoid complaints about class names:
|
||||
* @codingStandardsIgnoreStart
|
||||
*/
|
||||
class Zend_View_Helper_FormDataFilter extends Zend_View_Helper_FormElement
|
||||
{
|
||||
private $currentId;
|
||||
|
||||
private $fieldName;
|
||||
|
||||
private $cachedColumnSelect;
|
||||
|
||||
private $query;
|
||||
|
||||
/**
|
||||
* Generates an 'extensible set' element.
|
||||
*
|
||||
* @codingStandardsIgnoreEnd
|
||||
*
|
||||
* @param string|array $name If a string, the element name. If an
|
||||
* array, all other parameters are ignored, and the array elements
|
||||
* are used in place of added parameters.
|
||||
*
|
||||
* @param mixed $value The element value.
|
||||
*
|
||||
* @param array $attribs Attributes for the element tag.
|
||||
*
|
||||
* @return string The element XHTML.
|
||||
*/
|
||||
public function formDataFilter($name, $value = null, $attribs = null)
|
||||
{
|
||||
$info = $this->_getInfo($name, $value, $attribs);
|
||||
extract($info); // id, name, value, attribs, options, listsep, disable
|
||||
if (array_key_exists('columns', $attribs)) {
|
||||
$this->setColumns($attribs['columns']);
|
||||
unset($attribs['columns']);
|
||||
}
|
||||
|
||||
// TODO: check for columns in attribs, preserve & remove them from the
|
||||
// array use attribs? class etc? disabled?
|
||||
// override _getInfo?
|
||||
$this->fieldName = $name;
|
||||
|
||||
if ($value === null) {
|
||||
$value = $this->emptyExpression();
|
||||
} elseif (is_string($value)) {
|
||||
$value = Filter::fromQueryString($value);
|
||||
}
|
||||
|
||||
return $this->beginRoot()
|
||||
. $this->renderFilter($value)
|
||||
. $this->endRoot();
|
||||
}
|
||||
|
||||
protected function renderFilter(Filter $filter)
|
||||
{
|
||||
if ($filter instanceof FilterChain) {
|
||||
return $this->renderFilterChain($filter);
|
||||
} elseif ($filter instanceof FilterExpression) {
|
||||
return $this->renderFilterExpression($filter);
|
||||
} else {
|
||||
throw new ProgrammingError('Got a Filter being neither expression nor chain');
|
||||
}
|
||||
}
|
||||
|
||||
protected function beginRoot()
|
||||
{
|
||||
return '<ul class="filter-root">';
|
||||
}
|
||||
|
||||
protected function endRoot()
|
||||
{
|
||||
return '</ul>';
|
||||
}
|
||||
|
||||
protected function renderFilterChain(FilterChain $filter)
|
||||
{
|
||||
$parts = array();
|
||||
foreach ($filter->filters() as $f) {
|
||||
$parts[] = $this->renderFilter($f);
|
||||
}
|
||||
|
||||
return $this->beginChain($filter)
|
||||
. implode('', $parts)
|
||||
. $this->endChain($filter);
|
||||
}
|
||||
|
||||
protected function beginChain(FilterChain $filter)
|
||||
{
|
||||
$list = $filter->isEmpty() ? '' : '<ul>' . "\n";
|
||||
|
||||
return '<li class="filter-chain"><span class="handle"> </span>'
|
||||
. $this->selectOperator($filter)
|
||||
. $this->removeLink($filter)
|
||||
. $this->addLink($filter)
|
||||
. ($filter->count() === 1 ? $this->stripLink($filter) : '')
|
||||
. $list;
|
||||
}
|
||||
|
||||
protected function endChain(FilterChain $filter)
|
||||
{
|
||||
$list = $filter->isEmpty() ? '' : "</ul>\n";
|
||||
return $list . "</li>\n";
|
||||
}
|
||||
|
||||
protected function beginExpression(FilterExpression $filter)
|
||||
{
|
||||
return '<div class="filter-expression">' . "\n";
|
||||
}
|
||||
|
||||
protected function endExpression(FilterExpression $filter)
|
||||
{
|
||||
return "</div>\n";
|
||||
}
|
||||
|
||||
protected function beginElement(FilterExpression $filter)
|
||||
{
|
||||
return '<div class="expression-wrapper">' . "\n";
|
||||
}
|
||||
|
||||
protected function endElement(FilterExpression $filter)
|
||||
{
|
||||
return "</div>\n";
|
||||
}
|
||||
|
||||
protected function filterExpressionHtml(FilterExpression $filter)
|
||||
{
|
||||
return $this->selectColumn($filter)
|
||||
. $this->selectSign($filter)
|
||||
. $this->beginElement($filter)
|
||||
. $this->element($filter)
|
||||
. $this->endElement($filter)
|
||||
. $this->removeLink($filter)
|
||||
. $this->expandLink($filter);
|
||||
}
|
||||
|
||||
protected function renderFilterExpression(FilterExpression $filter)
|
||||
{
|
||||
return $this->beginExpression($filter)
|
||||
. $this->filterExpressionHtml($filter)
|
||||
. $this->endExpression($filter);
|
||||
}
|
||||
|
||||
protected function element(FilterExpression $filter = null)
|
||||
{
|
||||
if ($filter) {
|
||||
// TODO: Make this configurable
|
||||
$type = 'host';
|
||||
$filter = clone($filter);
|
||||
|
||||
$filter->setExpression(json_decode($filter->getExpression()));
|
||||
$dummy = IcingaObject::createByType($type);
|
||||
$col = $filter->getColumn();
|
||||
if ($dummy->hasProperty($col)) {
|
||||
if ($dummy->propertyIsBoolean($col)) {
|
||||
return $this->boolean($filter);
|
||||
}
|
||||
}
|
||||
|
||||
if ($col === 'groups' && $dummy->supportsGroups()) {
|
||||
return $this->selectGroup($type, $filter);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->text($filter);
|
||||
}
|
||||
|
||||
protected function selectGroup($type, Filter $filter)
|
||||
{
|
||||
$available = IcingaObjectGroup::enumForType($type);
|
||||
|
||||
return $this->select(
|
||||
$this->elementId('value', $filter),
|
||||
$this->optionalEnum($available),
|
||||
$filter->getExpression()
|
||||
);
|
||||
}
|
||||
|
||||
protected function boolean(Filter $filter = null)
|
||||
{
|
||||
$value = $filter === null ? '' : $filter->getExpression();
|
||||
|
||||
$el = new Icinga\Module\Director\Web\Form\Element\Boolean(
|
||||
$this->elementId('value', $filter),
|
||||
array(
|
||||
'value' => $value,
|
||||
'decorators' => array('ViewHelper'),
|
||||
)
|
||||
);
|
||||
|
||||
return $el;
|
||||
}
|
||||
|
||||
protected function text(Filter $filter = null)
|
||||
{
|
||||
$value = $filter === null ? '' : $filter->getExpression();
|
||||
if (is_array($value)) {
|
||||
return $this->view->formExtensibleSet(
|
||||
$this->elementId('value', $filter),
|
||||
$value
|
||||
);
|
||||
|
||||
$value = '(' . implode('|', $value) . ')';
|
||||
}
|
||||
|
||||
return $this->view->formText(
|
||||
$this->elementId('value', $filter),
|
||||
$value
|
||||
);
|
||||
}
|
||||
|
||||
protected function emptyExpression()
|
||||
{
|
||||
return Filter::expression('', '=', '');
|
||||
}
|
||||
|
||||
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($field, Filter $filter = null)
|
||||
{
|
||||
$prefix = $this->fieldName . '[id_';
|
||||
$suffix = '][' . $field . ']';
|
||||
|
||||
return $prefix . $filter->getId() . $suffix;
|
||||
}
|
||||
|
||||
protected function selectOperator(Filter $filter = null)
|
||||
{
|
||||
$ops = array(
|
||||
'AND' => 'AND',
|
||||
'OR' => 'OR',
|
||||
'NOT' => 'NOT'
|
||||
);
|
||||
|
||||
return $this->view->formSelect(
|
||||
$this->elementId('operator', $filter),
|
||||
$filter === null ? null : $filter->getOperatorName(),
|
||||
array(
|
||||
'class' => 'operator autosubmit',
|
||||
),
|
||||
$ops
|
||||
);
|
||||
return $this->select(
|
||||
$this->elementId('operator', $filter),
|
||||
$ops,
|
||||
$filter === null ? null : $filter->getOperatorName(),
|
||||
array('class' => 'operator autosubmit')
|
||||
);
|
||||
}
|
||||
|
||||
protected function selectSign(Filter $filter = null)
|
||||
{
|
||||
$signs = array(
|
||||
'=' => '=',
|
||||
'!=' => '!=',
|
||||
'>' => '>',
|
||||
'<' => '<',
|
||||
'>=' => '>=',
|
||||
'<=' => '<=',
|
||||
'in' => 'in',
|
||||
'true' => 'is true (or set)',
|
||||
);
|
||||
|
||||
if ($filter === null) {
|
||||
$sign = null;
|
||||
} else {
|
||||
if ($filter->getExpression() === true) {
|
||||
$sign = 'true';
|
||||
} elseif (is_array($filter->getExpression())) {
|
||||
$sign = 'in';
|
||||
} else {
|
||||
$sign = $filter->getSign();
|
||||
}
|
||||
}
|
||||
|
||||
$class = 'sign autosubmit';
|
||||
if (strlen($sign) > 3) {
|
||||
$class .= ' wide';
|
||||
}
|
||||
|
||||
return $this->select(
|
||||
$this->elementId('sign', $filter),
|
||||
$signs,
|
||||
$sign,
|
||||
array('class' => $class)
|
||||
);
|
||||
}
|
||||
|
||||
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->hasColumnList()) {
|
||||
return $this->view->formText(
|
||||
$this->elementId('column', $filter),
|
||||
$active
|
||||
);
|
||||
}
|
||||
|
||||
$cols = $this->getColumnList();
|
||||
if ($active && !isset($cols[$active])) {
|
||||
$cols[$active] = str_replace(
|
||||
'_',
|
||||
' ',
|
||||
ucfirst(ltrim($active, '_'))
|
||||
); // ??
|
||||
}
|
||||
|
||||
$cols = $this->optionalEnum($cols);
|
||||
|
||||
return $this->select(
|
||||
$this->elementId('column', $filter),
|
||||
$cols,
|
||||
$active,
|
||||
array('class' => 'column autosubmit')
|
||||
);
|
||||
}
|
||||
|
||||
protected function optionalEnum($enum)
|
||||
{
|
||||
return array_merge(
|
||||
array(null => $this->view->translate('- please choose -')),
|
||||
$enum
|
||||
);
|
||||
}
|
||||
|
||||
protected function hasColumnList()
|
||||
{
|
||||
return $this->cachedColumnSelect !== null || $this->query !== null;
|
||||
}
|
||||
|
||||
protected function getColumnList()
|
||||
{
|
||||
if ($this->cachedColumnSelect === null) {
|
||||
$this->fetchColumnList();
|
||||
}
|
||||
|
||||
return $this->cachedColumnSelect;
|
||||
}
|
||||
|
||||
protected function fetchColumnList()
|
||||
{
|
||||
if ($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');
|
||||
}
|
||||
}
|
||||
|
||||
protected function select($name, $list, $selected, $attributes = null)
|
||||
{
|
||||
return $this->view->formSelect($name, $selected, $attributes, $list);
|
||||
}
|
||||
|
||||
protected function removeLink(Filter $filter)
|
||||
{
|
||||
return $this->filterActionButton(
|
||||
$filter,
|
||||
'cancel',
|
||||
t('Remove this part of your filter')
|
||||
);
|
||||
}
|
||||
|
||||
protected function addLink(Filter $filter)
|
||||
{
|
||||
return $this->filterActionButton(
|
||||
$filter,
|
||||
'plus',
|
||||
t('Add another filter')
|
||||
);
|
||||
}
|
||||
|
||||
protected function expandLink(Filter $filter)
|
||||
{
|
||||
return $this->filterActionButton(
|
||||
$filter,
|
||||
'angle-double-right',
|
||||
t('Wrap this expression into an operator')
|
||||
);
|
||||
}
|
||||
|
||||
protected function stripLink(Filter $filter)
|
||||
{
|
||||
return $this->filterActionButton(
|
||||
$filter,
|
||||
'minus',
|
||||
t('Strip this operator, preserve child nodes')
|
||||
);
|
||||
}
|
||||
|
||||
protected function filterActionButton(Filter $filter, $action, $title)
|
||||
{
|
||||
return $this->iconButton(
|
||||
$this->getActionButtonName($filter),
|
||||
$action,
|
||||
$title
|
||||
);
|
||||
}
|
||||
|
||||
protected function getActionButtonName(Filter $filter)
|
||||
{
|
||||
return sprintf(
|
||||
'%s[id_%s][action]',
|
||||
$this->fieldName,
|
||||
$filter->getId()
|
||||
);
|
||||
}
|
||||
|
||||
protected function iconButton($name, $icon, $title)
|
||||
{
|
||||
return $this->view->formSubmit(
|
||||
$name,
|
||||
IconHelper::instance()->iconCharacter($icon),
|
||||
array('class' => 'icon-button', 'title' => $title)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -54,6 +54,23 @@ class AssignRenderer
|
|||
}
|
||||
}
|
||||
|
||||
protected function renderEquals($column, $expression)
|
||||
{
|
||||
if ($column === 'groups') {
|
||||
return sprintf(
|
||||
'%s in %s',
|
||||
$expression,
|
||||
$column
|
||||
);
|
||||
} else {
|
||||
return sprintf(
|
||||
'%s == %s',
|
||||
$column,
|
||||
$expression
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function renderFilterExpression($filter)
|
||||
{
|
||||
$column = $filter->getColumn();
|
||||
|
@ -67,11 +84,7 @@ class AssignRenderer
|
|||
|
||||
} elseif ($filter instanceof FilterMatch) {
|
||||
if (strpos($expression, '*') === false) {
|
||||
return sprintf(
|
||||
'%s == %s',
|
||||
$column,
|
||||
$expression
|
||||
);
|
||||
return $this->renderEquals($column, $expression);
|
||||
} else {
|
||||
return sprintf(
|
||||
'match(%s, %s)',
|
||||
|
|
|
@ -139,6 +139,8 @@ class IcingaHost extends IcingaObject
|
|||
$properties[$props] = $hostProperties;
|
||||
}
|
||||
|
||||
$properties['groups'] = 'Groups';
|
||||
|
||||
if (!empty($hostVars)) {
|
||||
$properties[$vars] = $hostVars;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ class IcingaNotification extends IcingaObject
|
|||
'notification_interval' => null,
|
||||
'period_id' => null,
|
||||
'zone_id' => null,
|
||||
'assign_filter' => null,
|
||||
);
|
||||
|
||||
protected $supportsCustomVars = true;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Module\Director\CustomVariable\CustomVariables;
|
||||
use Icinga\Module\Director\IcingaConfig\AssignRenderer;
|
||||
use Icinga\Module\Director\Data\Db\DbObject;
|
||||
use Icinga\Module\Director\Db\Cache\PrefetchCache;
|
||||
use Icinga\Module\Director\Db;
|
||||
|
@ -385,6 +386,38 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
return $this->supportsSets;
|
||||
}
|
||||
|
||||
public function setAssignments($value)
|
||||
{
|
||||
return IcingaObjectLegacyAssignments::applyToObject($this, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @codingStandardsIgnoreStart
|
||||
*/
|
||||
public function setAssign_filter($filter)
|
||||
{
|
||||
if (! $this->supportsAssignments()) {
|
||||
if ($this->hasProperty('object_type')) {
|
||||
$type = $this->object_type;
|
||||
} else {
|
||||
$type = get_class($this);
|
||||
}
|
||||
|
||||
throw new ProgrammingError(
|
||||
'I can only assign for applied objects or objects with native'
|
||||
. ' support for assigments, got %s',
|
||||
$type
|
||||
);
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreEnd
|
||||
if ($filter instanceof Filter) {
|
||||
$filter = $filter->toQueryString();
|
||||
}
|
||||
|
||||
return $this->reallySet('assign_filter', $filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* It sometimes makes sense to defer lookups for related properties. This
|
||||
* kind of lazy-loading allows us to for example set host = 'localhost' and
|
||||
|
@ -440,10 +473,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
return true;
|
||||
}
|
||||
|
||||
if ($this->supportsAssignments() && $this->assignments !== null && $this->assignments()->hasBeenModified()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->loadedRelatedSets as $set) {
|
||||
if ($set->hasBeenModified()) {
|
||||
return true;
|
||||
|
@ -626,21 +655,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
return $this->arguments()->toPlainObject();
|
||||
}
|
||||
|
||||
protected function setAssignments($value)
|
||||
{
|
||||
$this->assignments()->setValues($value);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function assignments()
|
||||
{
|
||||
if ($this->assignments === null) {
|
||||
$this->assignments = new IcingaObjectAssignments($this);
|
||||
}
|
||||
|
||||
return $this->assignments;
|
||||
}
|
||||
|
||||
protected function getRanges()
|
||||
{
|
||||
return $this->ranges()->getValues();
|
||||
|
@ -1213,11 +1227,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
return $fields;
|
||||
}
|
||||
|
||||
protected function getAssignments()
|
||||
{
|
||||
return $this->assignments()->getValues();
|
||||
}
|
||||
|
||||
public function hasProperty($key)
|
||||
{
|
||||
if ($this->propertyIsRelatedSet($key)) {
|
||||
|
@ -1264,8 +1273,7 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
->storeImports()
|
||||
->storeRanges()
|
||||
->storeRelatedSets()
|
||||
->storeArguments()
|
||||
->storeAssignments();
|
||||
->storeArguments();
|
||||
}
|
||||
|
||||
protected function beforeStore()
|
||||
|
@ -1330,15 +1338,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
return $this;
|
||||
}
|
||||
|
||||
protected function storeAssignments()
|
||||
{
|
||||
if ($this->supportsAssignments()) {
|
||||
$this->assignments !== null && $this->assignments()->store();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function storeRelatedSets()
|
||||
{
|
||||
foreach ($this->loadedRelatedSets as $set) {
|
||||
|
@ -1895,15 +1894,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
);
|
||||
}
|
||||
|
||||
protected function renderAssignments()
|
||||
{
|
||||
if ($this->supportsAssignments()) {
|
||||
return $this->assignments()->toConfigString();
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
protected function renderLegacyObjectHeader()
|
||||
{
|
||||
$type = strtolower($this->getType());
|
||||
|
@ -1933,6 +1923,17 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* @codingStandardsIgnoreStart
|
||||
*/
|
||||
public function renderAssign_Filter()
|
||||
{
|
||||
// @codingStandardsIgnoreEnd
|
||||
return ' ' . AssignRenderer::forFilter(
|
||||
Filter::fromQueryString($this->assign_filter)
|
||||
)->renderAssign() . "\n";
|
||||
}
|
||||
|
||||
public function toLegacyConfigString()
|
||||
{
|
||||
$str = implode(array(
|
||||
|
@ -1946,7 +1947,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
//$this->renderMultiRelations(),
|
||||
//$this->renderCustomExtensions(),
|
||||
//$this->renderCustomVars(),
|
||||
//$this->renderAssignments(),
|
||||
$this->renderLegacySuffix()
|
||||
));
|
||||
|
||||
|
@ -1993,7 +1993,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
$this->renderMultiRelations(),
|
||||
$this->renderCustomExtensions(),
|
||||
$this->renderCustomVars(),
|
||||
$this->renderAssignments(),
|
||||
$this->renderSuffix()
|
||||
));
|
||||
|
||||
|
@ -2235,10 +2234,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
);
|
||||
}
|
||||
|
||||
if ($this->supportsAssignments()) {
|
||||
$props['assignments'] = $this->assignments()->getPlain();
|
||||
}
|
||||
|
||||
if ($this->supportsCustomVars()) {
|
||||
if ($resolved) {
|
||||
$props['vars'] = $this->getResolvedVars();
|
||||
|
@ -2414,10 +2409,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
}
|
||||
}
|
||||
|
||||
if ($this->supportsAssignments()) {
|
||||
$props['assignments'] = $this->assignments()->getUnmodifiedPlain();
|
||||
}
|
||||
|
||||
foreach ($this->relatedSets() as $property => $set) {
|
||||
if ($set->isEmpty()) {
|
||||
continue;
|
||||
|
@ -2465,7 +2456,6 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer
|
|||
unset($this->ranges);
|
||||
unset($this->arguments);
|
||||
|
||||
|
||||
parent::__destruct();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,313 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Module\Director\IcingaConfig\AssignRenderer;
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
|
||||
|
||||
class IcingaObjectAssignments
|
||||
{
|
||||
protected $object;
|
||||
|
||||
protected $stored;
|
||||
|
||||
protected $current;
|
||||
|
||||
public function __construct(IcingaObject $object)
|
||||
{
|
||||
if (! $object->supportsAssignments()) {
|
||||
throw new ProgrammingError(
|
||||
'I can only assign for applied objects, got %s',
|
||||
$object->object_type
|
||||
);
|
||||
}
|
||||
|
||||
$this->object = $object;
|
||||
}
|
||||
|
||||
public function store()
|
||||
{
|
||||
if ($this->hasBeenModified()) {
|
||||
$this->reallyStore();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setValues($values)
|
||||
{
|
||||
if (is_string($values)) {
|
||||
return $this->setValues(array($values));
|
||||
}
|
||||
|
||||
$this->current = array();
|
||||
if (is_object($values)) {
|
||||
$values = (array) $values;
|
||||
}
|
||||
|
||||
ksort($values);
|
||||
foreach ((array) $values as $type => $value) {
|
||||
if (is_numeric($type)) {
|
||||
$this->addRule($value);
|
||||
} else {
|
||||
if (is_string($value)) {
|
||||
$this->addRule($value, $type);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($value as $key => $strings) {
|
||||
$this->addRule($strings, $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFormValues()
|
||||
{
|
||||
$result = array();
|
||||
foreach ($this->getCurrent() as $rule) {
|
||||
$f = array(
|
||||
'assign_type' => $rule['assign_type']
|
||||
);
|
||||
|
||||
$filter = Filter::fromQueryString($rule['filter_string']);
|
||||
if (!$filter->isExpression()) {
|
||||
throw new IcingaException(
|
||||
'We currently support only flat filters in our forms, got %',
|
||||
(string) $filter
|
||||
);
|
||||
}
|
||||
|
||||
$f['property'] = $filter->getColumn();
|
||||
$f['operator'] = $filter->getSign();
|
||||
$f['expression'] = trim(stripcslashes($filter->getExpression()), '"');
|
||||
|
||||
$result[] = $f;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function setFormValues($values)
|
||||
{
|
||||
$rows = array();
|
||||
|
||||
foreach ($values as $key => $val) {
|
||||
if (! is_numeric($key)) {
|
||||
// Skip buttons or similar
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!array_key_exists($val['assign_type'], $rows)) {
|
||||
$rows[$val['assign_type']] = array();
|
||||
}
|
||||
|
||||
if (empty($val['property'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_numeric($val['expression'])) {
|
||||
$expression = $val['expression'];
|
||||
} else {
|
||||
$expression = '"' . addcslashes($val['expression'], '"') . '"';
|
||||
}
|
||||
|
||||
$rows[$val['assign_type']][] = $this->rerenderFilter(
|
||||
implode('', array(
|
||||
$val['property'],
|
||||
$val['operator'],
|
||||
$expression,
|
||||
))
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return $this->setValues($rows);
|
||||
}
|
||||
|
||||
protected function addRule($string, $type = 'assign')
|
||||
{
|
||||
// TODO: validate
|
||||
$this->current[] = array(
|
||||
'assign_type' => $type,
|
||||
'filter_string' => $this->rerenderFilter($string)
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValues()
|
||||
{
|
||||
return $this->getCurrent();
|
||||
}
|
||||
|
||||
public function getUnmodifiedValues()
|
||||
{
|
||||
return $this->getStored();
|
||||
}
|
||||
|
||||
public function toConfigString()
|
||||
{
|
||||
return $this->renderRules($this->getCurrent());
|
||||
}
|
||||
|
||||
public function toUnmodifiedConfigString()
|
||||
{
|
||||
return $this->renderRules($this->getStored());
|
||||
}
|
||||
|
||||
protected function renderRules($rules)
|
||||
{
|
||||
if (empty($rules)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$filters = array();
|
||||
|
||||
foreach ($rules as $rule) {
|
||||
$filters[] = AssignRenderer::forFilter(
|
||||
Filter::fromQueryString($rule['filter_string'])
|
||||
)->render($rule['assign_type']);
|
||||
}
|
||||
|
||||
return "\n " . implode("\n ", $filters) . "\n";
|
||||
}
|
||||
|
||||
public function getPlain()
|
||||
{
|
||||
if ($this->current === null) {
|
||||
if (! $this->object->hasBeenLoadedFromDb()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$this->current = $this->getStored();
|
||||
}
|
||||
|
||||
return $this->createPlain($this->current);
|
||||
}
|
||||
|
||||
public function getUnmodifiedPlain()
|
||||
{
|
||||
if (! $this->object->hasBeenLoadedFromDb()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $this->createPlain($this->getStored());
|
||||
}
|
||||
|
||||
public function hasBeenModified()
|
||||
{
|
||||
if ($this->current === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return json_encode($this->getCurrent()) !== json_encode($this->getStored());
|
||||
}
|
||||
|
||||
protected function getCurrent()
|
||||
{
|
||||
if ($this->current === null) {
|
||||
$this->current = $this->getStored();
|
||||
}
|
||||
|
||||
return $this->current;
|
||||
}
|
||||
|
||||
protected function getStored()
|
||||
{
|
||||
if ($this->stored === null) {
|
||||
$this->stored = $this->loadFromDb();
|
||||
}
|
||||
|
||||
return $this->stored;
|
||||
}
|
||||
|
||||
protected function rerenderFilter($string)
|
||||
{
|
||||
return rawurldecode(Filter::fromQueryString($string)->toQueryString());
|
||||
}
|
||||
|
||||
protected function createPlain($dbRows)
|
||||
{
|
||||
$result = array();
|
||||
foreach ($dbRows as $row) {
|
||||
if (! array_key_exists($row['assign_type'], $result)) {
|
||||
$result[$row['assign_type']] = array();
|
||||
}
|
||||
|
||||
$result[$row['assign_type']][] = $row['filter_string'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getDb()
|
||||
{
|
||||
return $this->object->getDb();
|
||||
}
|
||||
|
||||
protected function loadFromDb()
|
||||
{
|
||||
$db = $this->getDb();
|
||||
$object = $this->object;
|
||||
|
||||
$query = $db->select()->from(
|
||||
$this->getTableName(),
|
||||
array('assign_type', 'filter_string')
|
||||
)->where($this->createWhere())->order('assign_type', 'filter_string');
|
||||
|
||||
$this->stored = array();
|
||||
foreach ($db->fetchAll($query) as $row) {
|
||||
$this->stored[] = (array) $row;
|
||||
}
|
||||
|
||||
return $this->stored;
|
||||
}
|
||||
|
||||
protected function createWhere()
|
||||
{
|
||||
return $this->getRelationColumn()
|
||||
. ' = '
|
||||
. $this->getObjectId();
|
||||
}
|
||||
|
||||
protected function getObjectId()
|
||||
{
|
||||
return (int) $this->object->id;
|
||||
}
|
||||
|
||||
protected function getRelationColumn()
|
||||
{
|
||||
return $this->object->getShortTableName() . '_id';
|
||||
}
|
||||
|
||||
protected function getTableName()
|
||||
{
|
||||
return $this->object->getTableName() . '_assignment';
|
||||
}
|
||||
|
||||
protected function reallyStore()
|
||||
{
|
||||
$db = $this->getDb();
|
||||
$table = $this->getTableName();
|
||||
$objectId = $this->object->id;
|
||||
$relationCol = $this->getRelationColumn();
|
||||
|
||||
$db->delete($table, $this->createWhere());
|
||||
|
||||
foreach ($this->getCurrent() as $row) {
|
||||
$data = (array) $row;
|
||||
$data[$relationCol] = $objectId;
|
||||
$db->insert($table, $data);
|
||||
}
|
||||
|
||||
$this->stored = $this->current;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Module\Director\Db;
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
||||
|
||||
abstract class IcingaObjectGroup extends IcingaObject
|
||||
|
@ -14,10 +17,39 @@ abstract class IcingaObjectGroup extends IcingaObject
|
|||
'object_type' => null,
|
||||
'disabled' => 'n',
|
||||
'display_name' => null,
|
||||
'assign_filter' => null,
|
||||
);
|
||||
|
||||
public function getRenderingZone(IcingaConfig $config = null)
|
||||
{
|
||||
return $this->connection->getDefaultGlobalZoneName();
|
||||
}
|
||||
|
||||
public static function enumForType($type, Db $connection = null)
|
||||
{
|
||||
if ($connection === null) {
|
||||
// TODO: not nice :(
|
||||
$connection = Db::fromResourceName(
|
||||
Config::module('director')->get('db', 'resource')
|
||||
);
|
||||
}
|
||||
|
||||
// Last resort defense against potentiall lousy checks:
|
||||
if (! ctype_alpha($type)) {
|
||||
throw new IcingaException(
|
||||
'Holy shit, you should never have reached this'
|
||||
);
|
||||
}
|
||||
|
||||
$db = $connection->getDbAdapter();
|
||||
$select = $db->select()->from(
|
||||
'icinga_' . $type . 'group',
|
||||
array(
|
||||
'name' => 'object_name',
|
||||
'display' => 'COALESCE(display_name, object_name)'
|
||||
)
|
||||
)->where('object_type = ?', 'object')->order('display');
|
||||
|
||||
return $db->fetchPairs($select);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
/**
|
||||
* This class is required for historical reasons
|
||||
*
|
||||
* Objects with assignments in your activity log would otherwise not be able
|
||||
* to render themselves
|
||||
*/
|
||||
class IcingaObjectLegacyAssignments
|
||||
{
|
||||
public static function applyToObject(IcingaObject $object, $values)
|
||||
{
|
||||
if (! $object->supportsAssignments()) {
|
||||
throw new ProgrammingError(
|
||||
'I can only assign for applied objects, got %s',
|
||||
$object->object_type
|
||||
);
|
||||
}
|
||||
|
||||
if ($values === null) {
|
||||
return $object;
|
||||
}
|
||||
|
||||
if (! is_array($values)) {
|
||||
static::throwCompatError();
|
||||
}
|
||||
|
||||
if (empty($values)) {
|
||||
return $object;
|
||||
}
|
||||
|
||||
$assigns = array();
|
||||
$ignores = array();
|
||||
foreach ($values as $type => $value) {
|
||||
if (strpos($value, '|') !== false || strpos($value, '&' !== false)) {
|
||||
$value = '(' . $value . ')';
|
||||
}
|
||||
|
||||
if ($type === 'assign') {
|
||||
$assigns[] = $value;
|
||||
} elseif ($type === 'ignore') {
|
||||
$ignores[] = $value;
|
||||
} else {
|
||||
static::throwCompatError();
|
||||
}
|
||||
}
|
||||
|
||||
$assign = implode('|', $assigns);
|
||||
$ignore = implode('&', $ignores);
|
||||
if (empty($assign)) {
|
||||
$filter = $ignore;
|
||||
} elseif (empty($ignore)) {
|
||||
$filter = $assign;
|
||||
} else {
|
||||
if (count($assigns) === 1) {
|
||||
$filter = $assign . '&' . $ignore;
|
||||
} else {
|
||||
$filter = '(' . $assign . ')&(' . $ignore . ')';
|
||||
}
|
||||
}
|
||||
|
||||
$object->assign_filter = $filter;
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
protected static function throwCompatError()
|
||||
{
|
||||
throw new ProgrammingError(
|
||||
'You ran into an unexpected compatibility issue. Please report'
|
||||
. ' this with details helping us to reproduce this to the'
|
||||
. ' Icinga project'
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Icinga\Module\Director\Objects;
|
||||
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfig;
|
||||
use Icinga\Module\Director\IcingaConfig\IcingaConfigHelper as c;
|
||||
|
@ -41,6 +42,7 @@ class IcingaService extends IcingaObject
|
|||
'use_agent' => null,
|
||||
'apply_for' => null,
|
||||
'use_var_overrides' => null,
|
||||
'assign_filter' => null,
|
||||
);
|
||||
|
||||
protected $relations = array(
|
||||
|
@ -193,21 +195,6 @@ class IcingaService extends IcingaObject
|
|||
return parent::renderObjectHeader();
|
||||
}
|
||||
|
||||
protected function renderAssignments()
|
||||
{
|
||||
if (! $this->hasBeenAssignedToHostTemplate()) {
|
||||
return parent::renderAssignments();
|
||||
}
|
||||
|
||||
// TODO: use assignment renderer?
|
||||
$filter = sprintf(
|
||||
'assign where %s in host.templates',
|
||||
c::renderString($this->host)
|
||||
);
|
||||
|
||||
return "\n " . $filter . "\n";
|
||||
}
|
||||
|
||||
protected function hasBeenAssignedToHostTemplate()
|
||||
{
|
||||
return $this->host_id && $this->getRelatedObject(
|
||||
|
@ -238,18 +225,30 @@ class IcingaService extends IcingaObject
|
|||
|
||||
protected function renderCustomExtensions()
|
||||
{
|
||||
$output = '';
|
||||
|
||||
if ($this->hasBeenAssignedToHostTemplate()) {
|
||||
// TODO: use assignment renderer?
|
||||
$filter = sprintf(
|
||||
'assign where %s in host.templates',
|
||||
c::renderString($this->host)
|
||||
);
|
||||
|
||||
$output .= "\n " . $filter . "\n";
|
||||
}
|
||||
|
||||
// A hand-crafted command endpoint overrides use_agent
|
||||
if ($this->command_endpoint_id !== null) {
|
||||
return '';
|
||||
return $output;
|
||||
}
|
||||
|
||||
// In case use_agent isn't defined, do nothing
|
||||
// TODO: what if we inherit use_agent and override it with 'n'?
|
||||
if ($this->use_agent !== 'y') {
|
||||
return '';
|
||||
return $output;
|
||||
}
|
||||
|
||||
return c::renderKeyValue('command_endpoint', 'host_name');
|
||||
return $output . c::renderKeyValue('command_endpoint', 'host_name');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -551,9 +551,6 @@ abstract class DirectorObjectForm extends QuickForm
|
|||
|
||||
$post = $this->getRequest()->getPost();
|
||||
// ?? $this->populate($post);
|
||||
if (array_key_exists('assignlist', $post)) {
|
||||
$object->assignments()->setFormValues($post['assignlist']);
|
||||
}
|
||||
|
||||
foreach ($post as $key => $value) {
|
||||
$el = $this->getElement($key);
|
||||
|
@ -564,10 +561,6 @@ abstract class DirectorObjectForm extends QuickForm
|
|||
}
|
||||
|
||||
if ($object instanceof IcingaObject) {
|
||||
if ($object->supportsAssignments()) {
|
||||
$this->setElementValue('assignlist', $object->assignments()->getFormValues());
|
||||
}
|
||||
|
||||
$this->handleProperties($object, $values);
|
||||
$this->handleCustomVars($object, $post);
|
||||
$this->handleRanges($object, $values);
|
||||
|
@ -1136,6 +1129,71 @@ abstract class DirectorObjectForm extends QuickForm
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an assign_filter form element
|
||||
*
|
||||
* Forms should use this helper method for objects using the typical
|
||||
* assign_filter column
|
||||
*
|
||||
* @param array $properties Form element properties
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function addAssignFilter($properties)
|
||||
{
|
||||
if (!$this->object || !$this->object->supportsAssignments()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->addFilterElement('assign_filter', $properties);
|
||||
$el = $this->getElement('assign_filter');
|
||||
|
||||
$this->addDisplayGroup(array($el), 'assign', array(
|
||||
'decorators' => array(
|
||||
'FormElements',
|
||||
array('HtmlTag', array('tag' => 'dl')),
|
||||
'Fieldset',
|
||||
),
|
||||
'order' => 30,
|
||||
'legend' => $this->translate('Assign where')
|
||||
));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a dataFilter element with fitting decorators
|
||||
*
|
||||
* TODO: Evaluate whether parts or all of this could be moved to the element
|
||||
* class.
|
||||
*
|
||||
* @param string $name Element name
|
||||
* @param array $properties Form element properties
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
protected function addFilterElement($name, $properties)
|
||||
{
|
||||
$this->addElement('dataFilter', $name, $properties);
|
||||
$el = $this->getElement($name);
|
||||
|
||||
$ddClass = 'full-width';
|
||||
if (array_key_exists('required', $properties) && $properties['required']) {
|
||||
$ddClass .= ' required';
|
||||
}
|
||||
|
||||
$el->clearDecorators()
|
||||
->addDecorator('ViewHelper')
|
||||
->addDecorator('Errors')
|
||||
->addDecorator('Description', array('tag' => 'p', 'class' => 'description'))
|
||||
->addDecorator('HtmlTag', array(
|
||||
'tag' => 'dd',
|
||||
'class' => $ddClass,
|
||||
));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function addEventFilterElements()
|
||||
{
|
||||
$this->addElement('extensibleSet', 'states', array(
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Director\Web\Form\Element;
|
||||
|
||||
use Icinga\Data\Filter\Filter;
|
||||
use Icinga\Module\Director\Web\Form\IconHelper;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Input control for extensible sets
|
||||
*/
|
||||
class DataFilter extends FormElement
|
||||
{
|
||||
/**
|
||||
* Default form view helper to use for rendering
|
||||
* @var string
|
||||
*/
|
||||
public $helper = 'formDataFilter';
|
||||
|
||||
private $addTo;
|
||||
|
||||
private $removeFilter;
|
||||
|
||||
private $stripFilter;
|
||||
|
||||
private $filter;
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
$value = parent::getValue();
|
||||
if ($value !== null && $this->isEmpty($value)) {
|
||||
$value = null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
protected function isEmpty(Filter $filter)
|
||||
{
|
||||
return $filter->isEmpty() || $this->isEmptyExpression($filter);
|
||||
}
|
||||
|
||||
protected function isEmptyExpression($filter)
|
||||
{
|
||||
return $filter->isExpression() &&
|
||||
$filter->getColumn() === '' &&
|
||||
$filter->getExpression() === '""'; // -> json_encode('')
|
||||
}
|
||||
|
||||
/**
|
||||
* @codingStandardsIgnoreStart
|
||||
*/
|
||||
protected function _filterValue(&$value, &$key)
|
||||
{
|
||||
// @codingStandardsIgnoreEnd
|
||||
try {
|
||||
if ($value instanceof Filter) {
|
||||
// OK
|
||||
} elseif (is_string($value)) {
|
||||
$value = Filter::fromQueryString($value);
|
||||
} else {
|
||||
$value = $this->arrayToFilter($value);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
$value = null;
|
||||
// TODO: getFile, getLine
|
||||
// Hint: cannot addMessage at it would loop through getValue
|
||||
$this->addErrorMessage($e->getMessage());
|
||||
$this->_isErrorForced = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method transforms filter form data into a filter
|
||||
* and reacts on pressed buttons
|
||||
*/
|
||||
protected function arrayToFilter($array)
|
||||
{
|
||||
if ($array === null) {
|
||||
return null;
|
||||
return Filter::matchAll();
|
||||
}
|
||||
|
||||
$this->filter = null;
|
||||
foreach ($array as $id => $entry) {
|
||||
$filterId = $this->idToFilterId($id);
|
||||
$sub = $this->entryToFilter($entry);
|
||||
$this->checkEntryForActions($filterId, $entry);
|
||||
$parentId = $this->parentIdFor($filterId);
|
||||
|
||||
if ($this->filter === null) {
|
||||
$this->filter = $sub;
|
||||
} else {
|
||||
$this->filter->getById($parentId)->addFilter($sub);
|
||||
}
|
||||
}
|
||||
|
||||
$this->removeFilterIfRequested()
|
||||
->stripFilterIfRequested()
|
||||
->addNewFilterIfRequested()
|
||||
->fixNotsWithMultipleChildren();
|
||||
|
||||
return $this->filter;
|
||||
}
|
||||
|
||||
protected function removeFilterIfRequested()
|
||||
{
|
||||
if ($this->removeFilter !== null) {
|
||||
if ($this->filter->getById($this->removeFilter)->isRootNode()) {
|
||||
$this->filter = $this->emptyExpression();
|
||||
} else {
|
||||
$this->filter->removeId($this->removeFilter);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
protected function stripFilterIfRequested()
|
||||
{
|
||||
if ($this->stripFilter !== null) {
|
||||
$strip = $this->stripFilter;
|
||||
$subId = $strip . '-1';
|
||||
if ($this->filter->getId() === $strip) {
|
||||
$this->filter = $this->filter->getById($strip . '-1');
|
||||
} else {
|
||||
$this->filter->replaceById($strip, $this->filter->getById($strip . '-1'));
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function addNewFilterIfRequested()
|
||||
{
|
||||
if ($this->addTo !== null) {
|
||||
$parent = $this->filter->getById($this->addTo);
|
||||
|
||||
if ($parent->isChain()) {
|
||||
if ($parent->isEmpty()) {
|
||||
$parent->addFilter($this->emptyExpression());
|
||||
} else {
|
||||
$parent->addFilter($this->emptyExpression());
|
||||
}
|
||||
} else {
|
||||
$replacement = Filter::matchAll(clone($parent));
|
||||
if ($parent->isRootNode()) {
|
||||
$this->filter = $replacement;
|
||||
} else {
|
||||
$this->filter->replaceById($parent->getId(), $replacement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function fixNotsWithMultipleChildren(Filter $filter = null)
|
||||
{
|
||||
$this->filter = $this->fixNotsWithMultipleChildrenForFilter($this->filter);
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function fixNotsWithMultipleChildrenForFilter(Filter $filter)
|
||||
{
|
||||
if ($filter->isChain()) {
|
||||
if ($filter->getOperatorName() === 'NOT') {
|
||||
if ($filter->count() > 1) {
|
||||
$filter = $this->notToNotAnd($filter);
|
||||
}
|
||||
}
|
||||
foreach ($filter->filters() as $sub) {
|
||||
$filter->replaceById(
|
||||
$sub->getId(),
|
||||
$this->fixNotsWithMultipleChildrenForFilter($sub)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
protected function notToNotAnd(Filter $not)
|
||||
{
|
||||
$and = Filter::matchAll();
|
||||
foreach ($not->filters() as $sub) {
|
||||
$and->addFilter(clone($sub));
|
||||
}
|
||||
|
||||
return Filter::not($and);
|
||||
}
|
||||
|
||||
protected function emptyExpression()
|
||||
{
|
||||
return Filter::expression('', '=', '');
|
||||
}
|
||||
|
||||
protected function parentIdFor($id)
|
||||
{
|
||||
if (false === ($pos = strrpos($id, '-'))) {
|
||||
return '0';
|
||||
} else {
|
||||
return substr($id, 0, $pos);
|
||||
}
|
||||
}
|
||||
|
||||
protected function idToFilterId($id)
|
||||
{
|
||||
if (! preg_match('/^id_(new_)?(\d+(?:-\d+)*)$/', $id, $m)) {
|
||||
die('nono' . $id);
|
||||
}
|
||||
|
||||
return $m[2];
|
||||
}
|
||||
|
||||
protected function checkEntryForActions($filterId, $entry)
|
||||
{
|
||||
switch ($this->entryAction($entry)) {
|
||||
case 'cancel':
|
||||
$this->removeFilter = $filterId;
|
||||
break;
|
||||
|
||||
case 'minus':
|
||||
$this->stripFilter = $filterId;
|
||||
break;
|
||||
|
||||
case 'plus':
|
||||
case 'angle-double-right':
|
||||
$this->addTo = $filterId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a single submitted form component from an array
|
||||
* into a Filter object
|
||||
*
|
||||
* @param Array $entry The array as submitted through the form
|
||||
*
|
||||
* @return Filter
|
||||
*/
|
||||
protected function entryToFilter($entry)
|
||||
{
|
||||
if (array_key_exists('operator', $entry)) {
|
||||
return Filter::chain($entry['operator']);
|
||||
} else {
|
||||
return $this->entryToFilterExpression($entry);
|
||||
}
|
||||
}
|
||||
|
||||
protected function entryToFilterExpression($entry)
|
||||
{
|
||||
if ($entry['sign'] === 'true') {
|
||||
return Filter::expression(
|
||||
$entry['column'],
|
||||
'=',
|
||||
json_encode(true)
|
||||
);
|
||||
} elseif ($entry['sign'] === 'in') {
|
||||
if (array_key_exists('value', $entry)) {
|
||||
if (is_array($entry['value'])) {
|
||||
$value = array_filter($entry['value'], 'strlen');
|
||||
} elseif (empty($entry['value'])) {
|
||||
$value = array();
|
||||
} else {
|
||||
$value = array($entry['value']);
|
||||
}
|
||||
} else {
|
||||
$value = array();
|
||||
}
|
||||
return Filter::expression(
|
||||
$entry['column'],
|
||||
'=',
|
||||
json_encode($value)
|
||||
);
|
||||
} else {
|
||||
$value = array_key_exists('value', $entry) ? $entry['value'] : null;
|
||||
|
||||
return Filter::expression(
|
||||
$entry['column'],
|
||||
$entry['sign'],
|
||||
json_encode($value)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function entryAction($entry)
|
||||
{
|
||||
if (array_key_exists('action', $entry)) {
|
||||
return IconHelper::instance()->characterIconName($entry['action']);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function hasIncompleteExpressions(Filter $filter)
|
||||
{
|
||||
if ($filter->isChain()) {
|
||||
foreach ($filter->filters() as $sub) {
|
||||
if ($this->hasIncompleteExpressions($sub)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($filter->isRootNode() && $this->isEmptyExpression($filter)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $filter->getColumn() === '';
|
||||
}
|
||||
}
|
||||
|
||||
public function isValid($value, $context = null)
|
||||
{
|
||||
if (! $value instanceof Filter) {
|
||||
// TODO: try, return false on E
|
||||
$filter = $this->arrayToFilter($value);
|
||||
$this->setValue($filter);
|
||||
}
|
||||
|
||||
if ($this->hasIncompleteExpressions($filter)) {
|
||||
$this->addError('The configured filter is incomplete');
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::isValid($value);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ class IconHelper
|
|||
'plus' => 'e805',
|
||||
'cancel' => 'e804',
|
||||
'help' => 'e85b',
|
||||
'angle-double-right' => 'e87b',
|
||||
);
|
||||
|
||||
private $mappedUtf8Icons;
|
||||
|
|
|
@ -186,6 +186,7 @@ input, select, select option, textarea {
|
|||
|
||||
form ul.form-errors {
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
ul.errors li {
|
||||
background: @color-critical;
|
||||
font-weight: bold;
|
||||
|
@ -228,6 +229,10 @@ select option {
|
|||
padding-top: 0.3em;
|
||||
}
|
||||
|
||||
select[multiple=multiple] {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
label {
|
||||
line-height: 2em;
|
||||
}
|
||||
|
@ -620,31 +625,6 @@ form dt label {
|
|||
}
|
||||
}
|
||||
|
||||
ul.assign-rule {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
select, input[type=text] {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
select.assign-type {
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
select.assign-operator {
|
||||
width: 3em;
|
||||
}
|
||||
|
||||
select.assign-property {
|
||||
width: 12em;
|
||||
}
|
||||
|
||||
input.assign-expression {
|
||||
width: 12em;
|
||||
}
|
||||
}
|
||||
|
||||
form fieldset {
|
||||
min-width: 36em;
|
||||
}
|
||||
|
@ -689,6 +669,10 @@ form dt {
|
|||
}
|
||||
}
|
||||
|
||||
form .errors label {
|
||||
color: @color-critical;
|
||||
}
|
||||
|
||||
form dd {
|
||||
display: inline-block;
|
||||
width: 63%;
|
||||
|
@ -700,6 +684,11 @@ form dd {
|
|||
border-color: @color-critical;
|
||||
}
|
||||
}
|
||||
|
||||
&.full-width {
|
||||
padding: 0.5em;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
form dd:after {
|
||||
|
@ -898,25 +887,25 @@ table.icinga-objects {
|
|||
}
|
||||
|
||||
table.assignment-table {
|
||||
ul.apply-rules {
|
||||
table.apply-rules {
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0;
|
||||
border: 1px solid @gray-lighter;
|
||||
width: 100%;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
tr {
|
||||
border-bottom: 1px solid @gray-lighter;
|
||||
border-left: 5px solid transparent;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
float: none;
|
||||
font-weight: normal;
|
||||
background-color: white;
|
||||
padding: 0.3em 1.5em;
|
||||
&:hover {
|
||||
background-color: @icinga-blue;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
td {
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
&:hover, &:active, &.active {
|
||||
border-left: 5px solid @icinga-blue;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1101,6 +1090,147 @@ table.config-diff {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
ul.assign-rule {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
select, input[type=text] {
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
select.assign-type {
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding-left: 2em;
|
||||
|
||||
li::before {
|
||||
// icon: down-dir
|
||||
font-family: 'ifont';
|
||||
content: '\e81d';
|
||||
// icon: right-small
|
||||
content: '\e877';
|
||||
margin-left: -1em;
|
||||
padding-top: 0em;
|
||||
float: left;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul.filter-root {
|
||||
margin-top: 0;
|
||||
width: 100%;
|
||||
padding-left: 0.5em;
|
||||
list-style-type: none;
|
||||
|
||||
ul {
|
||||
padding-left: 1.5em;
|
||||
list-style-type: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
li.filter-chain, div.filter-expression {
|
||||
width: 100%;
|
||||
padding: 0.3em 0.5em;
|
||||
min-width: 30em;
|
||||
}
|
||||
|
||||
ul li.filter-chain::before, ul .filter-expression::before {
|
||||
font-family: 'ifont';
|
||||
// Formerly: icon-down-open: e821
|
||||
// icon-right-small:
|
||||
content: '\e877';
|
||||
float: left;
|
||||
margin-left: -1.5em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
ul.extensible-set {
|
||||
padding-left: 0;
|
||||
border: none;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
li::before {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
input[type=submit].icon-button {
|
||||
display: none;
|
||||
font-family: 'ifont';
|
||||
font-weight: normal;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0.2em 0.4em 0.2em 0.4em;
|
||||
margin: 0 0 0 0.2em;
|
||||
}
|
||||
|
||||
.active input[type=submit].icon-button,
|
||||
li:hover input[type=submit].icon-button,
|
||||
div:hover input[type=submit].icon-button
|
||||
{
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.errors > ul.filter-root {
|
||||
input[type=text], select {
|
||||
border-color: transparent;
|
||||
border-bottom-color: @gray-lighter;
|
||||
}
|
||||
|
||||
select.column, select.operator {
|
||||
border-left-color: @color-critical;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
li.filter-chain > select.operator {
|
||||
min-width: 5em;
|
||||
max-width: 5em;
|
||||
width: 5em;
|
||||
}
|
||||
|
||||
div.filter-expression {
|
||||
.column {
|
||||
min-width: 7em;
|
||||
max-width: 10em;
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
.sign {
|
||||
min-width: 4em;
|
||||
max-width: 4em;
|
||||
width: 4em;
|
||||
margin: 0 0.3em;
|
||||
&.wide {
|
||||
min-width: 6em;
|
||||
max-width: 6em;
|
||||
width: 6em;
|
||||
}
|
||||
}
|
||||
|
||||
div.expression-wrapper {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.expression-wrapper > input[type=text],
|
||||
div.expression-wrapper > select {
|
||||
min-width: 7em;
|
||||
width: 10em;
|
||||
max-width: 10em;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.tree li a {
|
||||
display: inline-block;
|
||||
padding-left: 2.4em;
|
||||
|
|
|
@ -188,7 +188,7 @@
|
|||
}
|
||||
var $li = $input.closest('li');
|
||||
var $dt = $dd.prev();
|
||||
var $form = $dt.closest('form');
|
||||
var $form = $dd.closest('form');
|
||||
|
||||
$form.find('dt, dd, li').removeClass('active');
|
||||
$li.addClass('active');
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
ALTER TABLE icinga_service ADD COLUMN assign_filter TEXT;
|
||||
|
||||
UPDATE icinga_service s JOIN (
|
||||
|
||||
SELECT
|
||||
service_id,
|
||||
CASE WHEN COUNT(*) = 0 THEN NULL
|
||||
WHEN COUNT(*) = 1 THEN sa.filter_string
|
||||
ELSE GROUP_CONCAT(sa.filter_string SEPARATOR '&') END AS filter_string
|
||||
FROM (
|
||||
SELECT
|
||||
sa_not.service_id,
|
||||
CASE WHEN COUNT(*) = 0 THEN NULL
|
||||
WHEN COUNT(*) = 1 THEN sa_not.filter_string
|
||||
ELSE '(' || GROUP_CONCAT(sa_not.filter_string SEPARATOR '&') || ')' END AS filter_string
|
||||
FROM ( SELECT
|
||||
sa.service_id,
|
||||
'!' || sa.filter_string AS filter_string
|
||||
FROM icinga_service_assignment sa
|
||||
WHERE assign_type = 'ignore'
|
||||
) sa_not
|
||||
GROUP BY service_id
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
sa_yes.service_id,
|
||||
CASE WHEN COUNT(*) = 0 THEN NULL
|
||||
WHEN COUNT(*) = 1 THEN sa_yes.filter_string
|
||||
ELSE '(' || GROUP_CONCAT(sa_yes.filter_string SEPARATOR '|') || ')' END AS filter_string
|
||||
FROM ( SELECT
|
||||
sa.service_id,
|
||||
sa.filter_string AS filter_string
|
||||
FROM icinga_service_assignment sa
|
||||
WHERE assign_type = 'assign'
|
||||
) sa_yes
|
||||
GROUP BY service_id
|
||||
|
||||
) sa GROUP BY service_id
|
||||
|
||||
) flat_assign ON s.id = flat_assign.service_id SET s.assign_filter = flat_assign.filter_string;
|
||||
|
||||
DROP TABLE icinga_service_assignment;
|
||||
|
||||
ALTER TABLE icinga_service_set ADD COLUMN assign_filter TEXT;
|
||||
|
||||
UPDATE icinga_service_set s JOIN (
|
||||
|
||||
SELECT
|
||||
service_set_id,
|
||||
CASE WHEN COUNT(*) = 0 THEN NULL
|
||||
WHEN COUNT(*) = 1 THEN sa.filter_string
|
||||
ELSE GROUP_CONCAT(sa.filter_string SEPARATOR '&') END AS filter_string
|
||||
FROM (
|
||||
SELECT
|
||||
sa_not.service_set_id,
|
||||
CASE WHEN COUNT(*) = 0 THEN NULL
|
||||
WHEN COUNT(*) = 1 THEN sa_not.filter_string
|
||||
ELSE '(' || GROUP_CONCAT(sa_not.filter_string SEPARATOR '&') || ')' END AS filter_string
|
||||
FROM ( SELECT
|
||||
sa.service_set_id,
|
||||
'!' || sa.filter_string AS filter_string
|
||||
FROM icinga_service_set_assignment sa
|
||||
WHERE assign_type = 'ignore'
|
||||
) sa_not
|
||||
GROUP BY service_set_id
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
sa_yes.service_set_id,
|
||||
CASE WHEN COUNT(*) = 0 THEN NULL
|
||||
WHEN COUNT(*) = 1 THEN sa_yes.filter_string
|
||||
ELSE '(' || GROUP_CONCAT(sa_yes.filter_string SEPARATOR '|') || ')' END AS filter_string
|
||||
FROM ( SELECT
|
||||
sa.service_set_id,
|
||||
sa.filter_string AS filter_string
|
||||
FROM icinga_service_set_assignment sa
|
||||
WHERE assign_type = 'assign'
|
||||
) sa_yes
|
||||
GROUP BY service_set_id
|
||||
|
||||
) sa GROUP BY service_set_id
|
||||
|
||||
) flat_assign ON s.id = flat_assign.service_set_id SET s.assign_filter = flat_assign.filter_string;
|
||||
|
||||
DROP TABLE icinga_service_set_assignment;
|
||||
|
||||
|
||||
ALTER TABLE icinga_notification ADD COLUMN assign_filter TEXT;
|
||||
|
||||
UPDATE icinga_notification s JOIN (
|
||||
|
||||
SELECT
|
||||
notification_id,
|
||||
CASE WHEN COUNT(*) = 0 THEN NULL
|
||||
WHEN COUNT(*) = 1 THEN sa.filter_string
|
||||
ELSE GROUP_CONCAT(sa.filter_string SEPARATOR '&') END AS filter_string
|
||||
FROM (
|
||||
SELECT
|
||||
sa_not.notification_id,
|
||||
CASE WHEN COUNT(*) = 0 THEN NULL
|
||||
WHEN COUNT(*) = 1 THEN sa_not.filter_string
|
||||
ELSE '(' || GROUP_CONCAT(sa_not.filter_string SEPARATOR '&') || ')' END AS filter_string
|
||||
FROM ( SELECT
|
||||
sa.notification_id,
|
||||
'!' || sa.filter_string AS filter_string
|
||||
FROM icinga_notification_assignment sa
|
||||
WHERE assign_type = 'ignore'
|
||||
) sa_not
|
||||
GROUP BY notification_id
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
sa_yes.notification_id,
|
||||
CASE WHEN COUNT(*) = 0 THEN NULL
|
||||
WHEN COUNT(*) = 1 THEN sa_yes.filter_string
|
||||
ELSE '(' || GROUP_CONCAT(sa_yes.filter_string SEPARATOR '|') || ')' END AS filter_string
|
||||
FROM ( SELECT
|
||||
sa.notification_id,
|
||||
sa.filter_string AS filter_string
|
||||
FROM icinga_notification_assignment sa
|
||||
WHERE assign_type = 'assign'
|
||||
) sa_yes
|
||||
GROUP BY notification_id
|
||||
|
||||
) sa GROUP BY notification_id
|
||||
|
||||
) flat_assign ON s.id = flat_assign.notification_id SET s.assign_filter = flat_assign.filter_string;
|
||||
|
||||
DROP TABLE icinga_notification_assignment;
|
||||
|
||||
ALTER TABLE icinga_hostgroup ADD COLUMN assign_filter TEXT;
|
||||
|
||||
UPDATE icinga_hostgroup s JOIN (
|
||||
|
||||
SELECT
|
||||
hostgroup_id,
|
||||
CASE WHEN COUNT(*) = 0 THEN NULL
|
||||
WHEN COUNT(*) = 1 THEN sa.filter_string
|
||||
ELSE GROUP_CONCAT(sa.filter_string SEPARATOR '&') END AS filter_string
|
||||
FROM (
|
||||
SELECT
|
||||
sa_not.hostgroup_id,
|
||||
CASE WHEN COUNT(*) = 0 THEN NULL
|
||||
WHEN COUNT(*) = 1 THEN sa_not.filter_string
|
||||
ELSE '(' || GROUP_CONCAT(sa_not.filter_string SEPARATOR '&') || ')' END AS filter_string
|
||||
FROM ( SELECT
|
||||
sa.hostgroup_id,
|
||||
'!' || sa.filter_string AS filter_string
|
||||
FROM icinga_hostgroup_assignment sa
|
||||
WHERE assign_type = 'ignore'
|
||||
) sa_not
|
||||
GROUP BY hostgroup_id
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
sa_yes.hostgroup_id,
|
||||
CASE WHEN COUNT(*) = 0 THEN NULL
|
||||
WHEN COUNT(*) = 1 THEN sa_yes.filter_string
|
||||
ELSE '(' || GROUP_CONCAT(sa_yes.filter_string SEPARATOR '|') || ')' END AS filter_string
|
||||
FROM ( SELECT
|
||||
sa.hostgroup_id,
|
||||
sa.filter_string AS filter_string
|
||||
FROM icinga_hostgroup_assignment sa
|
||||
WHERE assign_type = 'assign'
|
||||
) sa_yes
|
||||
GROUP BY hostgroup_id
|
||||
|
||||
) sa GROUP BY hostgroup_id
|
||||
|
||||
) flat_assign ON s.id = flat_assign.hostgroup_id SET s.assign_filter = flat_assign.filter_string;
|
||||
|
||||
DROP TABLE icinga_hostgroup_assignment;
|
||||
|
||||
|
||||
ALTER TABLE icinga_servicegroup ADD COLUMN assign_filter TEXT;
|
||||
|
||||
|
||||
INSERT INTO director_schema_migration
|
||||
(schema_version, migration_time)
|
||||
VALUES (120, NOW());
|
|
@ -47,9 +47,7 @@ class IcingaServiceTest extends BaseTestCase
|
|||
{
|
||||
$service = $this->service();
|
||||
$service->object_type = 'apply';
|
||||
$service->assignments = array(
|
||||
'host.address="127.*"'
|
||||
);
|
||||
$service->assign_filter = 'host.address="127.*"';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,9 +56,7 @@ class IcingaServiceTest extends BaseTestCase
|
|||
public function testRefusesAssignRulesWhenNotBeingAnApply()
|
||||
{
|
||||
$service = $this->service();
|
||||
$service->assignments = array(
|
||||
'host.address=127.*'
|
||||
);
|
||||
$service->assign_filter = 'host.address=127.*';
|
||||
}
|
||||
|
||||
public function testAcceptsAndRendersFlatAssignRules()
|
||||
|
@ -76,10 +72,7 @@ class IcingaServiceTest extends BaseTestCase
|
|||
// Service apply rule rendering requires access to settings:
|
||||
$service->setConnection($db);
|
||||
$service->object_type = 'apply';
|
||||
$service->assignments = array(
|
||||
'host.address="127.*"',
|
||||
'host.vars.env="test"'
|
||||
);
|
||||
$service->assign_filter = 'host.address="127.*"|host.vars.env="test"';
|
||||
|
||||
$this->assertEquals(
|
||||
$this->loadRendered('service1'),
|
||||
|
@ -87,8 +80,8 @@ class IcingaServiceTest extends BaseTestCase
|
|||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'host.address="127.*"',
|
||||
$service->toPlainObject()->assignments['assign'][0]
|
||||
'host.address="127.*"|host.vars.env="test"',
|
||||
$service->assign_filter
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -104,10 +97,7 @@ class IcingaServiceTest extends BaseTestCase
|
|||
// Service apply rule rendering requires access to settings:
|
||||
$service->setConnection($db);
|
||||
$service->object_type = 'apply';
|
||||
$service->assignments = array(
|
||||
'host.address="127.*"',
|
||||
'host.vars.env="test"'
|
||||
);
|
||||
$service->assign_filter = 'host.address="127.*"|host.vars.env="test"';
|
||||
|
||||
$this->assertEquals(
|
||||
$this->loadRendered('service1'),
|
||||
|
@ -115,8 +105,8 @@ class IcingaServiceTest extends BaseTestCase
|
|||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'host.address="127.*"',
|
||||
$service->toPlainObject()->assignments['assign'][0]
|
||||
'host.address="127.*"|host.vars.env="test"',
|
||||
$service->assign_filter = 'host.address="127.*"|host.vars.env="test"'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -130,10 +120,7 @@ class IcingaServiceTest extends BaseTestCase
|
|||
|
||||
$service = $this->service();
|
||||
$service->object_type = 'apply';
|
||||
$service->assignments = array(
|
||||
'host.address="127.*"',
|
||||
'host.vars.env="test"'
|
||||
);
|
||||
$service->assign_filter = 'host.address="127.*"|host.vars.env="test"';
|
||||
|
||||
$service->store($db);
|
||||
|
||||
|
@ -144,74 +131,8 @@ class IcingaServiceTest extends BaseTestCase
|
|||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'host.address="127.*"',
|
||||
$service->toPlainObject()->assignments['assign'][0]
|
||||
);
|
||||
|
||||
$service->delete();
|
||||
}
|
||||
|
||||
public function testStaysUnmodifiedWhenSameFiltersAreSetInDifferentWays()
|
||||
{
|
||||
if ($this->skipForMissingDb()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$db = $this->getDb();
|
||||
|
||||
$service = $this->service();
|
||||
$service->object_type = 'apply';
|
||||
$service->assignments = 'host.address="127.*"';
|
||||
$service->store($db);
|
||||
$this->assertFalse($service->hasBeenModified());
|
||||
|
||||
$service->assignments = array(
|
||||
'host.address="127.*"',
|
||||
);
|
||||
$this->assertFalse($service->hasBeenModified());
|
||||
|
||||
$service->assignments = 'host.address="128.*"';
|
||||
$this->assertTrue($service->hasBeenModified());
|
||||
|
||||
$service->store();
|
||||
$this->assertFalse($service->hasBeenModified());
|
||||
|
||||
$service->assignments = array('assign' => 'host.address="128.*"');
|
||||
$this->assertFalse($service->hasBeenModified());
|
||||
|
||||
$service->assignments = array(
|
||||
'assign' => array(
|
||||
'host.address="128.*"'
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertFalse($service->hasBeenModified());
|
||||
|
||||
$service->assignments = array(
|
||||
'assign' => array(
|
||||
'host.address="128.*"'
|
||||
),
|
||||
'ignore' => 'host.name="localhost"'
|
||||
);
|
||||
|
||||
$this->assertTrue($service->hasBeenModified());
|
||||
|
||||
$service->store();
|
||||
$service = IcingaService::loadWithAutoIncId($service->id, $db);
|
||||
|
||||
$this->assertEquals(
|
||||
'host.address="128.*"',
|
||||
$service->toPlainObject()->assignments['assign'][0]
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'host.name="localhost"',
|
||||
$service->toPlainObject()->assignments['ignore'][0]
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
$this->loadRendered('service2'),
|
||||
(string) $service
|
||||
'host.address="127.*"|host.vars.env="test"',
|
||||
$service->assign_filter
|
||||
);
|
||||
|
||||
$service->delete();
|
||||
|
@ -247,9 +168,7 @@ class IcingaServiceTest extends BaseTestCase
|
|||
$service->setConnection($db);
|
||||
$service->object_type = 'apply';
|
||||
$service->display_name = 'Service: $host.vars.replaced$';
|
||||
$service->assignments = array(
|
||||
'host.address="127.*"',
|
||||
);
|
||||
$service->assign_filter = 'host.address="127.*"';
|
||||
$service->{'vars.custom_var'} = '$host.vars.replaced$';
|
||||
|
||||
$this->assertEquals(
|
||||
|
@ -290,9 +209,7 @@ class IcingaServiceTest extends BaseTestCase
|
|||
$service = $this->service()->setConnection($db);
|
||||
$service->object_type = 'apply';
|
||||
$service->apply_for = 'host.vars.test1';
|
||||
$service->assignments = array(
|
||||
'host.vars.env="test"'
|
||||
);
|
||||
$service->assign_filter = 'host.vars.env="test"';
|
||||
$this->assertEquals(
|
||||
$this->loadRendered('service5'),
|
||||
(string) $service
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
apply Service "___TEST___service" {
|
||||
display_name = "Whatever service"
|
||||
assign where match("127.*", host.address) || host.vars.env == "test"
|
||||
vars.test1 = "string"
|
||||
vars.test2 = 17
|
||||
vars.test3 = false
|
||||
|
@ -8,9 +9,6 @@ apply Service "___TEST___service" {
|
|||
@this = "is"
|
||||
}
|
||||
|
||||
assign where match("127.*", host.address)
|
||||
assign where host.vars.env == "test"
|
||||
|
||||
import DirectorOverrideTemplate
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
apply Service "___TEST___service_$not_replaced$" {
|
||||
display_name = "Service: " + host.vars.replaced
|
||||
assign where match("127.*", host.address)
|
||||
vars.custom_var = host.vars.replaced
|
||||
vars.test1 = "string"
|
||||
vars.test2 = 17
|
||||
|
@ -9,8 +10,6 @@ apply Service "___TEST___service_$not_replaced$" {
|
|||
@this = "is"
|
||||
}
|
||||
|
||||
assign where match("127.*", host.address)
|
||||
|
||||
import DirectorOverrideTemplate
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
apply Service "___TEST___service" for (config in host.vars.test1) {
|
||||
display_name = "Whatever service"
|
||||
assign where host.vars.env == "test"
|
||||
vars.test1 = "string"
|
||||
vars.test2 = 17
|
||||
vars.test3 = false
|
||||
|
@ -8,8 +9,6 @@ apply Service "___TEST___service" for (config in host.vars.test1) {
|
|||
@this = "is"
|
||||
}
|
||||
|
||||
assign where host.vars.env == "test"
|
||||
|
||||
import DirectorOverrideTemplate
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
apply Service for (config in host.vars.test1) {
|
||||
name = "___TEST" + config + "___service " + host.var.bla
|
||||
display_name = "Whatever service"
|
||||
assign where host.vars.env == "test"
|
||||
vars.test1 = "string"
|
||||
vars.test2 = 17
|
||||
vars.test3 = false
|
||||
|
@ -9,8 +10,6 @@ apply Service for (config in host.vars.test1) {
|
|||
@this = "is"
|
||||
}
|
||||
|
||||
assign where host.vars.env == "test"
|
||||
|
||||
import DirectorOverrideTemplate
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
apply Service for (config in host.vars.test1) {
|
||||
display_name = "Whatever service"
|
||||
assign where host.vars.env == "test"
|
||||
vars.test1 = "string"
|
||||
vars.test2 = 17
|
||||
vars.test3 = false
|
||||
|
@ -8,8 +9,6 @@ apply Service for (config in host.vars.test1) {
|
|||
@this = "is"
|
||||
}
|
||||
|
||||
assign where host.vars.env == "test"
|
||||
|
||||
import DirectorOverrideTemplate
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue