summaryrefslogtreecommitdiffstats
path: root/application/forms/SyncPropertyForm.php
diff options
context:
space:
mode:
Diffstat (limited to 'application/forms/SyncPropertyForm.php')
-rw-r--r--application/forms/SyncPropertyForm.php444
1 files changed, 444 insertions, 0 deletions
diff --git a/application/forms/SyncPropertyForm.php b/application/forms/SyncPropertyForm.php
new file mode 100644
index 0000000..720237e
--- /dev/null
+++ b/application/forms/SyncPropertyForm.php
@@ -0,0 +1,444 @@
+<?php
+
+namespace Icinga\Module\Director\Forms;
+
+use Exception;
+use Icinga\Module\Director\Hook\ImportSourceHook;
+use Icinga\Module\Director\Objects\SyncProperty;
+use Icinga\Module\Director\Objects\SyncRule;
+use Icinga\Module\Director\Objects\IcingaObject;
+use Icinga\Module\Director\Objects\ImportSource;
+use Icinga\Module\Director\Web\Form\DirectorObjectForm;
+
+class SyncPropertyForm extends DirectorObjectForm
+{
+ /**
+ * @var SyncRule
+ */
+ private $rule;
+
+ /** @var ImportSource */
+ private $importSource;
+
+ /** @var ImportSourceHook */
+ private $importSourceHook;
+
+ private $dummyObject;
+
+ const EXPRESSION = '__EXPRESSION__';
+
+ /**
+ * @throws \Zend_Form_Exception
+ */
+ public function setup()
+ {
+ $this->addHidden('rule_id', $this->rule->get('id'));
+
+ $this->addElement('select', 'source_id', array(
+ 'label' => $this->translate('Source Name'),
+ 'multiOptions' => $this->enumImportSource(),
+ 'required' => true,
+ 'class' => 'autosubmit',
+ ));
+ if (! $this->hasObject() && ! $this->getSentValue('source_id')) {
+ return;
+ }
+
+ $this->addElement('select', 'destination_field', array(
+ 'label' => $this->translate('Destination Field'),
+ 'multiOptions' => $this->optionalEnum($this->listDestinationFields()),
+ 'required' => true,
+ 'class' => 'autosubmit',
+ ));
+
+ if ($this->getSentValue('destination_field')) {
+ $destination = $this->getSentValue('destination_field');
+ } elseif ($this->hasObject()) {
+ $destination = $this->getObject()->destination_field;
+ } else {
+ return;
+ }
+
+ $isCustomvar = substr($destination, 0, 5) === 'vars.';
+
+ if ($isCustomvar) {
+ $varname = substr($destination, 5);
+ $this->addElement('text', 'customvar', array(
+ 'label' => $this->translate('Custom variable'),
+ 'required' => true,
+ 'ignore' => true,
+ ));
+
+ if ($varname !== '*') {
+ $this->setElementValue('destination_field', 'vars.*');
+ $this->setElementValue('customvar', $varname);
+ if ($this->hasObject()) {
+ $this->getObject()->destination_field = 'vars.*';
+ }
+ }
+ }
+
+ $this->addSourceColumnElement($destination);
+
+ $this->addElement('YesNo', 'use_filter', array(
+ 'label' => $this->translate('Set based on filter'),
+ 'ignore' => true,
+ 'class' => 'autosubmit',
+ 'required' => true,
+ ));
+
+ if ($this->hasBeenSent()) {
+ $useFilter = $this->getSentValue('use_filter');
+ if ($useFilter === null) {
+ $this->setElementValue('use_filter', $useFilter = 'n');
+ }
+ } else {
+ $expression = $this->getObject()->filter_expression;
+ $useFilter = ($expression === null || strlen($expression) === 0) ? 'n' : 'y';
+ $this->setElementValue('use_filter', $useFilter);
+ }
+
+ if ($useFilter === 'y') {
+ $this->addElement('text', 'filter_expression', array(
+ 'label' => $this->translate('Filter Expression'),
+ 'description' => $this->translate(
+ 'This allows to filter for specific parts within the given source expression.'
+ . ' You are allowed to refer all imported columns. Examples: host=www* would'
+ . ' set this property only for rows imported with a host property starting'
+ . ' with "www". Complex example: host=www*&!(address=127.*|address6=::1)'
+ ),
+ 'required' => true,
+ // TODO: validate filter
+ ));
+ }
+
+ if ($isCustomvar || $destination === 'vars') {
+ $this->addElement('select', 'merge_policy', array(
+ 'label' => $this->translate('Merge Policy'),
+ 'description' => $this->translate(
+ 'Whether you want to merge or replace the destination field.'
+ . ' Makes no difference for strings'
+ ),
+ 'required' => true,
+ 'multiOptions' => $this->optionalEnum(array(
+ 'merge' => 'merge',
+ 'override' => 'replace'
+ ))
+ ));
+ } else {
+ $this->addHidden('merge_policy', 'override');
+ }
+
+ $this->setButtons();
+ }
+
+ protected function hasSubOption($options, $key)
+ {
+ foreach ($options as $mainKey => $sub) {
+ if (! is_array($sub)) {
+ // null -> please choose - or similar
+ continue;
+ }
+
+ if (array_key_exists($key, $sub)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param $destination
+ * @return $this
+ * @throws \Zend_Form_Exception
+ */
+ protected function addSourceColumnElement($destination)
+ {
+ $error = false;
+
+ $srcTitle = $this->translate('Source columns');
+ try {
+ $columns[$srcTitle] = $this->listSourceColumns();
+ natsort($columns[$srcTitle]);
+ } catch (Exception $e) {
+ $srcTitle .= sprintf(' (%s)', $this->translate('failed to fetch'));
+ $columns[$srcTitle] = array();
+ $error = sprintf(
+ $this->translate('Unable to fetch data: %s'),
+ $e->getMessage()
+ );
+ }
+
+ if ($destination === 'import') {
+ $this->addIcingaTempateColumns($columns);
+ } elseif ($destination === 'list_id') {
+ $this->addDatalistsColumns($columns);
+ }
+
+ $xpTitle = $this->translate('Expert mode');
+ $columns[$xpTitle][self::EXPRESSION] = $this->translate('Custom expression');
+
+ $this->addElement('select', 'source_column', array(
+ 'label' => $this->translate('Source Column'),
+ 'multiOptions' => $this->optionalEnum($columns),
+ 'required' => true,
+ 'ignore' => true,
+ 'class' => 'autosubmit',
+ ));
+
+ if ($error) {
+ $this->getElement('source_column')->addError($error);
+ }
+
+ $showExpression = false;
+
+ if ($this->hasBeenSent()) {
+ $sentValue = $this->getSentValue('source_column');
+ if ($sentValue === self::EXPRESSION) {
+ $showExpression = true;
+ }
+ } elseif ($this->hasObject()) {
+ $objectValue = $this->getObject()->source_expression;
+ if ($this->hasSubOption($columns, $objectValue)) {
+ $this->setElementValue('source_column', $objectValue);
+ } else {
+ $this->setElementValue('source_column', self::EXPRESSION);
+ $showExpression = true;
+ }
+ }
+
+ if ($showExpression) {
+ $this->addElement('text', 'source_expression', array(
+ 'label' => $this->translate('Source Expression'),
+ 'description' => $this->translate(
+ 'A custom string. Might contain source columns, please use placeholders'
+ . ' of the form ${columnName} in such case. Structured data sources'
+ . ' can be referenced as ${columnName.sub.key}'
+ ),
+ 'required' => true,
+ ));
+ }
+
+
+ return $this;
+ }
+
+ protected function addIcingaTempateColumns(&$columns)
+ {
+ $funcTemplates = 'enum' . ucfirst($this->rule->get('object_type')) . 'Templates';
+ if (method_exists($this->db, $funcTemplates)) {
+ $templates = $this->db->$funcTemplates();
+ if (! empty($templates)) {
+ $templates = array_combine($templates, $templates);
+ }
+
+ $title = $this->translate('Existing templates');
+ $columns[$title] = $templates;
+ natsort($columns[$title]);
+ }
+ }
+
+ protected function addDatalistsColumns(&$columns)
+ {
+ // Clear other columns, we don't allow them right now
+ $columns = [];
+ $db = $this->db->getDbAdapter();
+ $enum = $db->fetchPairs(
+ $db->select()->from('director_datalist', ['id', 'list_name'])->order('list_name')
+ );
+
+ $columns[$this->translate('Existing Data Lists')] = $enum;
+ }
+
+ protected function enumImportSource()
+ {
+ $sources = $this->db->enumImportSource();
+ $usedIds = $this->rule->listInvolvedSourceIds();
+ if (empty($usedIds)) {
+ return $this->optionalEnum($sources);
+ }
+ $usedSources = array();
+ foreach ($usedIds as $id) {
+ $usedSources[$id] = $sources[$id];
+ unset($sources[$id]);
+ }
+
+ if (empty($sources)) {
+ return $this->optionalEnum($usedSources);
+ }
+
+ return $this->optionalEnum(
+ array(
+ $this->translate('Used sources') => $usedSources,
+ $this->translate('Other sources') => $sources
+ )
+ );
+ }
+
+ /**
+ * @return array
+ * @throws \Icinga\Exception\ConfigurationError
+ * @throws \Icinga\Exception\NotFoundError
+ */
+ protected function listSourceColumns()
+ {
+ $columns = array();
+ $source = $this->getImportSource();
+ $hook = $this->getImportSourceHook();
+ foreach ($hook->listColumns() as $col) {
+ $columns['${' . $col . '}'] = $col;
+ }
+
+ foreach ($source->listModifierTargetProperties() as $property) {
+ $columns['${' . $property . '}'] = $property;
+ }
+
+ return $columns;
+ }
+
+ protected function listDestinationFields()
+ {
+ $props = [];
+ $special = [];
+ $dummy = $this->dummyObject();
+
+ if ($dummy instanceof IcingaObject) {
+ if ($dummy->supportsCustomVars()) {
+ $special['vars.*'] = $this->translate('Custom variable (vars.)');
+ $special['vars'] = $this->translate('All custom variables (vars)');
+ }
+ if ($dummy->supportsImports()) {
+ $special['import'] = $this->translate('Inheritance (import)');
+ }
+ if ($dummy->supportsArguments()) {
+ $special['arguments'] = $this->translate('Arguments');
+ }
+ if ($dummy->supportsGroups()) {
+ $special['groups'] = $this->translate('Group membership');
+ }
+ if ($dummy->supportsRanges()) {
+ $special['ranges'] = $this->translate('Time ranges');
+ }
+ }
+
+ foreach ($dummy->listProperties() as $prop) {
+ if ($dummy instanceof IcingaObject && $prop === 'id') {
+ continue;
+ }
+
+ // TODO: allow those fields, but munge them (store ids)
+ //if (preg_match('~_id$~', $prop)) continue;
+ if (substr($prop, -3) === '_id') {
+ $short = substr($prop, 0, -3);
+ if ($dummy instanceof IcingaObject) {
+ if ($dummy->hasRelation($short)) {
+ $prop = $short;
+ } else {
+ continue;
+ }
+ }
+ }
+
+ $props[$prop] = $prop;
+ }
+
+ if ($dummy instanceof IcingaObject) {
+ foreach ($dummy->listMultiRelations() as $prop) {
+ $props[$prop] = sprintf('%s (%s)', $prop, $this->translate('a list'));
+ }
+ }
+
+ ksort($props);
+
+ $result = [];
+ if (! empty($special)) {
+ $result[$this->translate('Special properties')] = $special;
+ }
+ if (! empty($props)) {
+ $result[$this->translate('Object properties')] = $props;
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return ImportSource
+ * @throws \Icinga\Exception\NotFoundError
+ */
+ protected function getImportSource()
+ {
+ if ($this->importSource === null) {
+ if ($this->hasObject()) {
+ $id = (int) $this->object->get('source_id');
+ } else {
+ $id = (int) $this->getSentValue('source_id');
+ }
+ $this->importSource = ImportSource::loadWithAutoIncId($id, $this->db);
+ }
+
+ return $this->importSource;
+ }
+
+ /**
+ * @return ImportSourceHook
+ * @throws \Icinga\Exception\ConfigurationError
+ * @throws \Icinga\Exception\NotFoundError
+ */
+ protected function getImportSourceHook()
+ {
+ if ($this->importSourceHook === null) {
+ $this->importSourceHook = ImportSourceHook::loadByName(
+ $this->getImportSource()->get('source_name'),
+ $this->db
+ );
+ }
+
+ return $this->importSourceHook;
+ }
+
+ public function onSuccess()
+ {
+ /** @var SyncProperty $object */
+ $object = $this->getObject();
+ $object->set('rule_id', $this->rule->get('id')); // ?!
+
+ if ($this->getValue('use_filter') === 'n') {
+ $object->set('filter_expression', null);
+ }
+
+ $sourceColumn = $this->getValue('source_column');
+ $this->removeElement('source_column');
+
+ if ($sourceColumn !== self::EXPRESSION) {
+ $object->set('source_expression', $sourceColumn);
+ }
+
+ $destination = $this->getValue('destination_field');
+ if ($destination === 'vars.*') {
+ $destination = $this->getValue('customvar');
+ $object->set('destination_field', 'vars.' . $destination);
+ }
+
+ return parent::onSuccess();
+ }
+
+ protected function dummyObject()
+ {
+ if ($this->dummyObject === null) {
+ $this->dummyObject = IcingaObject::createByType(
+ $this->rule->get('object_type'),
+ array(),
+ $this->db
+ );
+ }
+
+ return $this->dummyObject;
+ }
+
+ public function setRule(SyncRule $rule)
+ {
+ $this->rule = $rule;
+ return $this;
+ }
+}