-
+
+= dumpTree(
+ (object) array(
+ 'name' => 'Templates',
+ 'children' => $this->tree
+ ),
+ $this
+) ?>
+
diff --git a/application/controllers/DatalistentryController.php b/application/controllers/DatalistentryController.php index 5d040f8f..5916dccc 100644 --- a/application/controllers/DatalistentryController.php +++ b/application/controllers/DatalistentryController.php @@ -11,19 +11,13 @@ class Director_DatalistentryController extends ActionController public function editAction() { - $this->indexAction(); + $this->indexAction(true); } public function indexAction($edit = false) { $request = $this->getRequest(); - if ($request->getParam('edit')) { - $edit = true; - } else { - $edit = false; - } - $listId = $this->params->get('list_id'); $this->view->lastId = $listId; @@ -46,6 +40,7 @@ class Director_DatalistentryController extends ActionController } $form = $this->view->form = $this->loadForm('directorDatalistentry') + ->setListId($listId) ->setSuccessUrl('director/datalistentry' . '?list_id=' . $listId) ->setDb($this->db()); diff --git a/application/controllers/HosttemplatesController.php b/application/controllers/HosttemplatesController.php new file mode 100644 index 00000000..49b15307 --- /dev/null +++ b/application/controllers/HosttemplatesController.php @@ -0,0 +1,7 @@ +schemas = array( + 'mysql' => $this->translate('MySQL schema'), + 'pgsql' => $this->translate('PostgreSQL schema'), + ); + } + + protected function tabs() + { + $tabs = $this->getTabs(); + foreach ($this->schemas as $type => $title) { + $tabs->add($type, array( + 'url' => 'director/schema/' . $type, + 'label' => $title, + )); + } + return $tabs; + } + + public function mysqlAction() + { + $this->serveSchema('mysql'); + } + + public function pgsqlAction() + { + $this->serveSchema('pgsql'); + } + + protected function serveSchema($type) + { + $schema = file_get_contents( + sprintf( + '%s/schema/%s.sql', + $this->Module()->getBasedir(), + $type + ) + ); + + if ($this->params->get('format') === 'sql') { + header('Content-type: application/octet-stream'); + header('Content-Disposition: attachment; filename=' . $type . '.sql'); + echo $schema; + exit; + // TODO: Shutdown + } else { + $this->tabs()->activate($type); + $this->view->title = $this->schemas[$type]; + $this->view->schema = $schema; + $this->render('schema'); + } + } +} diff --git a/application/controllers/ServicetemplatesController.php b/application/controllers/ServicetemplatesController.php new file mode 100644 index 00000000..0434ecaa --- /dev/null +++ b/application/controllers/ServicetemplatesController.php @@ -0,0 +1,7 @@ +view->tabs = $this->Module()->getConfigTabs()->activate('config'); + $this->view->tabs = $this->Module() + ->getConfigTabs() + ->activate('config'); - $resource = $this->Config()->get('db', 'resource'); - - $form = new ConfigForm(); - - $form->setIniConfig($this->Config('config')); - $form->addElement('select', 'resource', array( - 'required' => true, - 'label' => $this->translate('DB Resource'), - 'multiOptions' => array(null => $this->translate('- please choose -')) + $this->getResources(), - 'value' => $resource - )); - $form->setSubmitLabel($this->translate('Save')); - - $form->setOnSuccess(function($form) { - /** @var $form ConfigForm */ - $this->Config('config')->setSection('db', array( - 'resource' => $form->getValue('resource') - )); - $form->save(); - }); - - $form->handleRequest(); - - $this->view->form = $form; - } - - public function getResources() - { - $resources = array(); - foreach (ResourceFactory::getResourceConfigs() as $name => $resource) { - if ($resource->type === 'db') { - $resources['ido'][$name] = $name; - } - } - return $resources; + $this->view->form = $this->loadForm('config') + ->setModuleConfig($this->Config()) + ->handleRequest(); } } diff --git a/application/forms/ConfigForm.php b/application/forms/ConfigForm.php new file mode 100644 index 00000000..4c9be27c --- /dev/null +++ b/application/forms/ConfigForm.php @@ -0,0 +1,152 @@ +enumResources(); + + $this->addElement('select', 'resource', array( + 'required' => true, + 'label' => $this->translate('DB Resource'), + 'multiOptions' => $this->optionalEnum($resources), + 'class' => 'autosubmit', + 'value' => $this->config()->get('db', 'resource') + )); + + if (empty($resources)) { + $this->getElement('resource')->addError( + $this->translate('This has to be a MySQL or PostgreSQL database') + ); + + $hint = $this->translate('Please click %s to create new DB resources'); + $link = $this->getView()->qlink( + $this->translate('here'), + 'config/resource', + null, + array('data-base-target' => '_main') + ); + $this->addHtmlHint(sprintf($hint, $link)); + } + + $this->setSubmitLabel($this->translate('Store configuration')); + } + + protected function onSetup() + { + if ($this->hasBeenSubmitted()) { + // Do not hinder the form from being stored + return; + } + + if ($this->hasBeenSent() && $this->isValidPartial($this->getRequest()->getPost())) { + $resourceName = $this->getValue('resource'); + } else { + $resourceName = $this->config()->get('db', 'resource'); + } + + if ($resourceName) { + $resource = ResourceFactory::create($resourceName); + $db = $resource->getDbAdapter(); + + try { + $query = $db->select()->from('director_dbversion', 'COUNT(*)'); + $db->fetchOne($query); + + if (! $this->hasBeenSent()) { + $hint = $this->translate( + 'Configuration looks good, you should be ready to %s' + . ' Icinga Director' + ); + $link = $this->getView()->qlink( + $this->translate('start using'), + 'director/welcome', + null, + array('data-base-target' => '_main') + ); + $this->addHtmlHint(sprintf($hint, $link)); + $this->moveSubmitToBottom(); + } + + } catch (Exception $e) { + $this->getElement('resource') + ->addError('Could not fetch: ' . $e->getMessage()) + ->removeDecorator('description'); + + $hint = $this->translate( + 'Please make sure that your database grants enough permissions' + . ' and that you deployed the correct %s.' + ); + $link = $this->getView()->qlink( + $this->translate('database schema'), + 'director/schema/' . $resource->getDbType(), + null, + array('data-base-target' => '_next') + ); + $this->addHtmlHint(sprintf($hint, $link)); + $this->moveSubmitToBottom(); + } + } + } + + public function setModuleConfig(Config $config) + { + $this->config = $config; + return $this; + } + + public function onSuccess() + { + $config = $this->config(); + $value = $this->getValue('resource'); + + $config->setSection('db', array('resource' => $value)); + + try { + $config->saveIni(); + $this->redirectOnSuccess($this->translate('Configuration has been stored')); + } catch (Exception $e) { + $this->getElement('resource')->addError( + sprintf( + $this->translate('Unable to store the configuration to "%s"'), + $config->getConfigFile() + ) + )->removeDecorator('description'); + $this->addHtmlHint( + '
' . $config . '' + ); + } + } + + protected function config() + { + if ($this->config === null) { + $this->config = Config::module('director'); + } + + return $this->config; + } + + protected function enumResources() + { + $resources = array(); + $allowed = array('mysql', 'pgsql'); + + foreach (ResourceFactory::getResourceConfigs() as $name => $resource) { + if ($resource->type === 'db' && in_array($resource->db, $allowed)) { + $resources[$name] = $name; + } + } + + return $resources; + } +} diff --git a/application/forms/CustomvarForm.php b/application/forms/CustomvarForm.php new file mode 100644 index 00000000..759464c9 --- /dev/null +++ b/application/forms/CustomvarForm.php @@ -0,0 +1,26 @@ +removeCsrfToken(); + $this->removeElement(self::ID); + $this->addElement('text', 'varname', array( + 'label' => $this->translate('Variable name'), + 'required' => true, + )); + + $this->addElement('text', 'varvalue', array( + 'label' => $this->translate('Value'), + )); + + // $this->addHidden('format', 'string'); // expression, json? + } +} diff --git a/application/forms/DirectorDatafieldForm.php b/application/forms/DirectorDatafieldForm.php index 71feb9a0..e6298191 100644 --- a/application/forms/DirectorDatafieldForm.php +++ b/application/forms/DirectorDatafieldForm.php @@ -7,22 +7,30 @@ use Icinga\Web\Hook; class DirectorDatafieldForm extends DirectorObjectForm { + protected $objectName = 'Data field'; + public function setup() { + $this->addHtmlHint( + $this->translate('Data fields allow you to customize input controls your custom variables.') + ); + $this->addElement('text', 'varname', array( - 'required' => true, - 'label' => $this->translate('Field name'), - 'description' => $this->translate('The unique name of the field') + 'label' => $this->translate('Field name'), + 'description' => $this->translate('The unique name of the field'), + 'required' => true, )); $this->addElement('text', 'caption', array( - 'label' => $this->translate('Caption'), + 'label' => $this->translate('Caption'), + 'required' => true, 'description' => $this->translate('The caption which should be displayed') )); $this->addElement('textarea', 'description', array( - 'label' => $this->translate('Description'), - 'description' => $this->translate('A description about the field') + 'label' => $this->translate('Description'), + 'description' => $this->translate('A description about the field'), + 'rows' => '3', )); $this->addElement('select', 'datatype', array( @@ -30,15 +38,23 @@ class DirectorDatafieldForm extends DirectorObjectForm 'description' => $this->translate('Field type'), 'required' => true, 'multiOptions' => $this->enumDataTypes(), - 'class' => 'autosubmit' + 'class' => 'autosubmit', )); - if ($class = $this->object()->datatype) { - $this->addSettings($class); - } elseif ($class = $this->getSentValue('datatype')) { + + if ($class = $this->getSentValue('datatype')) { if ($class && array_key_exists($class, $this->enumDataTypes())) { $this->addSettings($class); } + } elseif ($class = $this->object()->datatype) { + $this->addSettings($class); + } + + $this->addSettings(); + foreach ($this->object()->getSettings() as $key => $val) { + if ($el = $this->getElement($key)) { + $el->setValue($val); + } } } @@ -53,8 +69,31 @@ class DirectorDatafieldForm extends DirectorObjectForm } } + protected function clearOutdatedSettings() + { + $names = array(); + $object = $this->object(); + $global = array('varname', 'description', 'caption', 'datatype'); + + foreach ($this->getElements() as $el) { + if ($el->getIgnore()) continue; + $name = $el->getName(); + if (in_array($name, $global)) continue; + $names[$name] = $name; + } + + + foreach ($object->getSettings() as $setting => $value) { + if (! array_key_exists($setting, $names)) { + unset($object->$setting); + } + } + } + public function onSuccess() { + $this->clearOutdatedSettings(); + if ($class = $this->getValue('datatype')) { if (array_key_exists($class, $this->enumDataTypes())) { $this->addHidden('format', $class::getFormat()); @@ -64,21 +103,6 @@ class DirectorDatafieldForm extends DirectorObjectForm parent::onSuccess(); } - public function loadObject($id) - { - parent::loadObject($id); - - $this->addSettings(); - foreach ($this->object()->getSettings() as $key => $val) { - if ($el = $this->getElement($key)) { - $el->setValue($val); - } - } - $this->moveSubmitToBottom(); - - return $this; - } - protected function enumDataTypes() { $hooks = Hook::all('Director\\DataType'); diff --git a/application/forms/DirectorDatalistForm.php b/application/forms/DirectorDatalistForm.php index 475e23bd..80da505d 100644 --- a/application/forms/DirectorDatalistForm.php +++ b/application/forms/DirectorDatalistForm.php @@ -3,22 +3,21 @@ namespace Icinga\Module\Director\Forms; use Icinga\Module\Director\Web\Form\DirectorObjectForm; -use Icinga\Authentication\Manager as Auth; +use Icinga\Authentication\Auth; class DirectorDatalistForm extends DirectorObjectForm { public function setup() { $this->addElement('text', 'list_name', array( - 'label' => $this->translate('List name') + 'label' => $this->translate('List name'), + 'required' => true, )); - - $this->addElement('hidden', 'owner'); } public function onSuccess() { - $this->addHidden('owner', self::username()); + $this->object()->owner = self::username(); parent::onSuccess(); } diff --git a/application/forms/DirectorDatalistentryForm.php b/application/forms/DirectorDatalistentryForm.php index 63ee5506..2e411a4a 100644 --- a/application/forms/DirectorDatalistentryForm.php +++ b/application/forms/DirectorDatalistentryForm.php @@ -6,6 +6,8 @@ use Icinga\Module\Director\Web\Form\DirectorObjectForm; class DirectorDatalistEntryForm extends DirectorObjectForm { + protected $listId; + public function setup() { $this->addElement('text', 'entry_name', array( @@ -18,9 +20,17 @@ class DirectorDatalistEntryForm extends DirectorObjectForm 'label' => 'Type', 'multiOptions' => array('string' => $this->translate('String')) )); + } - $this->addElement('hidden', 'list_id', array( - 'value' => $this->getRequest()->getParam('list_id'), - )); + public function onSuccess() + { + $this->object()->list_id = $this->listId; + parent::onSuccess(); + } + + public function setListId($id) + { + $this->listId = $id; + return $this; } } diff --git a/application/forms/IcingaCommandArgumentForm.php b/application/forms/IcingaCommandArgumentForm.php index 19eec1bf..c4f51b93 100644 --- a/application/forms/IcingaCommandArgumentForm.php +++ b/application/forms/IcingaCommandArgumentForm.php @@ -8,10 +8,10 @@ class IcingaCommandArgumentForm extends DirectorObjectForm { public function setup() { - $this->addElement('select', 'command_id', array( - 'label' => $this->translate('Check command'), - 'description' => $this->translate('Check command definition') + 'label' => $this->translate('Check command'), + 'description' => $this->translate('Check command definition'), + 'multiOptions' => $this->optionalEnum($this->db->enumCommands()) )); $this->addElement('text', 'argument_name', array( @@ -26,7 +26,6 @@ class IcingaCommandArgumentForm extends DirectorObjectForm )); $this->addHidden('value_format', 'string'); // expression, json? - } diff --git a/application/forms/IcingaEndpointForm.php b/application/forms/IcingaEndpointForm.php index f792eac3..718116fb 100644 --- a/application/forms/IcingaEndpointForm.php +++ b/application/forms/IcingaEndpointForm.php @@ -8,7 +8,8 @@ class IcingaEndpointForm extends DirectorObjectForm { public function setup() { - $isTemplate = isset($_POST['object_type']) && $_POST['object_type'] === 'template'; + $isTemplate = $this->getSentValue('object_type') === 'template'; + $this->addElement('select', 'object_type', array( 'label' => $this->translate('Object type'), 'description' => $this->translate('Whether this should be a template'), @@ -48,15 +49,7 @@ class IcingaEndpointForm extends DirectorObjectForm 'description' => $this->translate('The log duration time.') )); - $this->addElement('select', 'zone_id', array( - 'label' => $this->translate('Cluster Zone'), - 'description' => $this->translate('Check this host in this specific Icinga cluster zone'), - 'required' => true - )); - - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited endpoint template names') - )); + $this->addZoneElement() + ->addImportsElement(); } } diff --git a/application/forms/IcingaHostFieldForm.php b/application/forms/IcingaHostFieldForm.php index fb2f9713..5c7b8f08 100644 --- a/application/forms/IcingaHostFieldForm.php +++ b/application/forms/IcingaHostFieldForm.php @@ -9,21 +9,21 @@ class IcingaHostFieldForm extends DirectorObjectForm public function setup() { $this->addElement('select', 'host_id', array( - 'label' => 'Host Tpl', - 'description' => 'Host Template', - 'multiOptions' => $this->optionalEnum($this->getDb()->enumHostTemplates()) + 'label' => 'Host Tpl', + 'description' => 'Host Template', + 'multiOptions' => $this->optionalEnum($this->db->enumHostTemplates()) )); $this->addElement('select', 'datafield_id', array( - 'label' => 'Field', - 'description' => 'Field to assign', - 'multiOptions' => $this->optionalEnum($this->getDb()->enumDatafields()) + 'label' => 'Field', + 'description' => 'Field to assign', + 'multiOptions' => $this->optionalEnum($this->db->enumDatafields()) )); $this->optionalBoolean( 'is_required', $this->translate('Required'), - $this->translate('Whether this filed is required or not.') + $this->translate('Whether this field should be required or not') ); } } diff --git a/application/forms/IcingaHostForm.php b/application/forms/IcingaHostForm.php index d6f3cda2..5d7dc6be 100644 --- a/application/forms/IcingaHostForm.php +++ b/application/forms/IcingaHostForm.php @@ -9,32 +9,12 @@ class IcingaHostForm extends DirectorObjectForm { public function setup() { - $isTemplate = isset($_POST['object_type']) && $_POST['object_type'] === 'template'; - $this->addElement('select', 'object_type', array( - 'label' => $this->translate('Object type'), - 'description' => $this->translate('Whether this should be a template'), - 'multiOptions' => array( - null => '- please choose -', - 'object' => 'Host object', - 'template' => 'Host template', - ), - 'class' => 'autosubmit' + $this->addElement('text', 'object_name', array( + 'label' => $this->translate('Hostname'), + 'required' => true, + 'description' => $this->translate('Icinga object name for this host') )); - if ($isTemplate) { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Host template name'), - 'required' => true, - 'description' => $this->translate('Name for the Icinga host template you are going to create') - )); - } else { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Hostname'), - 'required' => true, - 'description' => $this->translate('Hostname for the Icinga host you are going to create') - )); - } - $this->addElement('text', 'address', array( 'label' => $this->translate('Host address'), 'description' => $this->translate('Host address. Usually an IPv4 address, but may be any kind of address your check plugin is able to deal with') @@ -45,95 +25,31 @@ class IcingaHostForm extends DirectorObjectForm 'description' => $this->translate('Usually your hosts main IPv6 address') )); - $this->addElement('select', 'check_command_id', array( - 'label' => $this->translate('Check command'), - 'description' => $this->translate('Check command definition') - )); - - $this->optionalBoolean( - 'enable_notifications', - $this->translate('Send notifications'), - $this->translate('Whether to send notifications for this host') - ); - - $this->optionalBoolean( - 'enable_active_checks', - $this->translate('Execute active checks'), - $this->translate('Whether to actively check this host') - ); - - $this->optionalBoolean( - 'enable_passive_checks', - $this->translate('Accept passive checks'), - $this->translate('Whether to accept passive check results for this host') - ); - - $this->optionalBoolean( - 'enable_event_handler', - $this->translate('Enable event handler'), - $this->translate('Whether to enable event handlers this host') - ); - - $this->optionalBoolean( - 'enable_perfdata', - $this->translate('Process performance data'), - $this->translate('Whether to process performance data provided by this host') - ); - - $this->optionalBoolean( - 'volatile', - $this->translate('Volatile'), - $this->translate('Whether this check is volatile.') - ); + $this->addImportsElement(); + /* $this->addElement('text', 'groups', array( 'label' => $this->translate('Hostgroups'), 'description' => $this->translate('One or more comma separated hostgroup names') )); + */ - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited host template names') - )); + if ($this->isTemplate()) { + $this->addElement('text', 'address', array( + 'label' => $this->translate('Host address'), + 'description' => $this->translate('Host address. Usually an IPv4 address, but may be any kind of address your check plugin is able to deal with') + )); - $this->addElement('select', 'zone_id', array( - 'label' => $this->translate('Cluster Zone'), - 'description' => $this->translate('Check this host in this specific Icinga cluster zone') - )); - } + $this->addElement('text', 'address6', array( + 'label' => $this->translate('IPv6 address'), + 'description' => $this->translate('Usually your hosts main IPv6 address') + )); - public function loadObject($id) - { - parent::loadObject($id); - - $this->addFields(); - - $this->moveSubmitToBottom(); - } - - public function addFields() - { - $fields = $this->getObject()->getFields($this); - $vars = $this->getObject()->vars(); - - foreach ($fields as $field) { - $datatype = new $field->datatype; - $datafield = DirectorDatafield::load($field->datafield_id, $this->getDb()); - $datatype->setSettings($datafield->getSettings()); - $varname = $datafield->varname; - $el = $datatype->getFormElement('var_' . $varname, $this); - $el->setLabel($field->caption); - $el->setDescription($field->description); - if (isset($vars->$varname)) { - $el->setValue($vars->{$varname}->getValue()); - } - - if ($field->is_required === 'y') - { - $el->setRequired(true); - } - - $this->addElement($el); + $this->addCheckExecutionElements(); + } else { + $this->getElement('imports')->setRequired(); } + + $this->addZoneElement(); } } diff --git a/application/forms/IcingaHostGroupForm.php b/application/forms/IcingaHostGroupForm.php index 59acf50c..31c309c9 100644 --- a/application/forms/IcingaHostGroupForm.php +++ b/application/forms/IcingaHostGroupForm.php @@ -8,39 +8,15 @@ class IcingaHostGroupForm extends DirectorObjectForm { public function setup() { - $isTemplate = isset($_POST['object_type']) && $_POST['object_type'] === 'template'; - $this->addElement('select', 'object_type', array( - 'label' => $this->translate('Object type'), - 'description' => $this->translate('Whether this should be a template'), - 'multiOptions' => array( - null => '- please choose -', - 'object' => 'Hostgroup object', - 'template' => 'Hostgroup template', - ) + $this->addElement('text', 'object_name', array( + 'label' => $this->translate('Hostgroup'), + 'required' => true, + 'description' => $this->translate('Icinga object name for this hostgroup') )); - if ($isTemplate) { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Hostgroup template name'), - 'required' => true, - 'description' => $this->translate('Hostgroup for the Icinga hostgroup template you are going to create') - )); - } else { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Hostgroup'), - 'required' => true, - 'description' => $this->translate('Hostgroup for the Icinga hostgroup you are going to create') - )); - } - $this->addElement('text', 'display_name', array( 'label' => $this->translate('Display Name'), 'description' => $this->translate('The name which should displayed.') )); - - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited hostgroup template names') - )); } } diff --git a/application/forms/IcingaHostVarForm.php b/application/forms/IcingaHostVarForm.php index 3fe0a70d..cb15bcb0 100644 --- a/application/forms/IcingaHostVarForm.php +++ b/application/forms/IcingaHostVarForm.php @@ -12,23 +12,24 @@ class IcingaHostVarForm extends DirectorObjectForm public function setup() { $this->addElement('select', 'host_id', array( - 'label' => $this->translate('Host'), - 'description' => $this->translate('The name of the host'), - 'required' => true + 'label' => $this->translate('Host'), + 'description' => $this->translate('The name of the host'), + 'multiOptions' => $this->optionalEnum($this->db->enumHosts()), + 'required' => true )); $this->addElement('text', 'varname', array( - 'label' => $this->translate('Name'), + 'label' => $this->translate('Name'), 'description' => $this->translate('host var name') )); $this->addElement('textarea', 'varvalue', array( - 'label' => $this->translate('Value'), + 'label' => $this->translate('Value'), 'description' => $this->translate('host var value') )); $this->addElement('text', 'format', array( - 'label' => $this->translate('Format'), + 'label' => $this->translate('Format'), 'description' => $this->translate('value format') )); } diff --git a/application/forms/IcingaServiceFieldForm.php b/application/forms/IcingaServiceFieldForm.php index 49c506a9..202b4fd7 100644 --- a/application/forms/IcingaServiceFieldForm.php +++ b/application/forms/IcingaServiceFieldForm.php @@ -9,21 +9,20 @@ class IcingaServiceFieldForm extends DirectorObjectForm public function setup() { $this->addElement('select', 'service_id', array( - 'label' => 'Service Tpl', - 'description' => 'Service Template', - 'multiOptions' => $this->optionalEnum($this->getDb()->enumServiceTemplates()) + 'label' => 'Service template', + 'multiOptions' => $this->optionalEnum($this->db->enumServiceTemplates()) )); $this->addElement('select', 'datafield_id', array( - 'label' => 'Field', - 'description' => 'Field to assign', - 'multiOptions' => $this->optionalEnum($this->getDb()->enumDatafields()) + 'label' => 'Field', + 'description' => 'Field to assign', + 'multiOptions' => $this->optionalEnum($this->db->enumDatafields()) )); $this->optionalBoolean( 'is_required', $this->translate('Required'), - $this->translate('Whether this filed is required or not.') + $this->translate('Whether this field should be required or not') ); } } diff --git a/application/forms/IcingaServiceForm.php b/application/forms/IcingaServiceForm.php index 2a37227b..5f94f814 100644 --- a/application/forms/IcingaServiceForm.php +++ b/application/forms/IcingaServiceForm.php @@ -8,86 +8,23 @@ class IcingaServiceForm extends DirectorObjectForm { public function setup() { - $isTemplate = isset($_POST['object_type']) && $_POST['object_type'] === 'template'; - $this->addElement('select', 'object_type', array( - 'label' => $this->translate('Object type'), - 'description' => $this->translate('Whether this should be a template'), - 'multiOptions' => array( - null => '- please choose -', - 'object' => 'Service object', - 'template' => 'Service template', - ), - 'class' => 'autosubmit' - )); - - if ($isTemplate) { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Service template name'), - 'required' => true, - 'description' => $this->translate('Name for the Icinga service template you are going to create') - )); - } else { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Servicename'), - 'required' => true, - 'description' => $this->translate('Servicename for the Icinga service you are going to create') - )); - } - - $this->addElement('select', 'check_command_id', array( - 'label' => $this->translate('Check command'), - 'description' => $this->translate('Check command definition') - )); - - $this->optionalBoolean( - 'enable_notifications', - $this->translate('Send notifications'), - $this->translate('Whether to send notifications for this service') - ); - - $this->optionalBoolean( - 'enable_active_checks', - $this->translate('Execute active checks'), - $this->translate('Whether to actively check this service') - ); - - $this->optionalBoolean( - 'enable_passive_checks', - $this->translate('Accept passive checks'), - $this->translate('Whether to accept passive check results for this service') - ); - - $this->optionalBoolean( - 'enable_event_handler', - $this->translate('Enable event handler'), - $this->translate('Whether to enable event handlers this service') - ); - - $this->optionalBoolean( - 'enable_perfdata', - $this->translate('Process performance data'), - $this->translate('Whether to process performance data provided by this service') - ); - - $this->optionalBoolean( - 'volatile', - $this->translate('Volatile'), - $this->translate('Whether this check is volatile.') - ); - - $this->addElement('select', 'zone_id', array( - 'label' => $this->translate('Cluster Zone'), - 'description' => $this->translate('Check this host in this specific Icinga cluster zone') + $this->addElement('text', 'object_name', array( + 'label' => $this->translate('Name'), + 'required' => true, + 'description' => $this->translate('Name for the Icinga object you are going to create') )); + /* $this->addElement('text', 'groups', array( 'label' => $this->translate('Servicegroups'), 'description' => $this->translate('One or more comma separated servicegroup names') )); + */ - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited service template names') - )); + $this->addImportsElement(); + + if ($this->isTemplate()) { + $this->addCheckExecutionElements(); + } } } diff --git a/application/forms/IcingaServiceGroupForm.php b/application/forms/IcingaServiceGroupForm.php index 6829d16c..9ec7e02c 100644 --- a/application/forms/IcingaServiceGroupForm.php +++ b/application/forms/IcingaServiceGroupForm.php @@ -8,39 +8,15 @@ class IcingaServiceGroupForm extends DirectorObjectForm { public function setup() { - $isTemplate = isset($_POST['object_type']) && $_POST['object_type'] === 'template'; - $this->addElement('select', 'object_type', array( - 'label' => $this->translate('Object type'), - 'description' => $this->translate('Whether this should be a template'), - 'multiOptions' => array( - null => '- please choose -', - 'object' => 'Servicegroup object', - 'template' => 'Servicegroup template', - ) + $this->addElement('text', 'object_name', array( + 'label' => $this->translate('Servicegroup'), + 'required' => true, + 'description' => $this->translate('Icinga object name for this servicegroup') )); - if ($isTemplate) { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Servicegroup template name'), - 'required' => true, - 'description' => $this->translate('Servicegroup for the Icinga servicegroup template you are going to create') - )); - } else { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Servicegroup'), - 'required' => true, - 'description' => $this->translate('Servicegroup for the Icinga servicegroup you are going to create') - )); - } - $this->addElement('text', 'display_name', array( 'label' => $this->translate('Display Name'), 'description' => $this->translate('The name which should displayed.') )); - - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited servicegroup template names') - )); } } diff --git a/application/forms/IcingaServiceVarForm.php b/application/forms/IcingaServiceVarForm.php index e0fcb74d..e7ac4a07 100644 --- a/application/forms/IcingaServiceVarForm.php +++ b/application/forms/IcingaServiceVarForm.php @@ -12,23 +12,24 @@ class IcingaServiceVarForm extends DirectorObjectForm public function setup() { $this->addElement('select', 'service_id', array( - 'label' => $this->translate('Service'), - 'description' => $this->translate('The name of the service'), - 'required' => true + 'label' => $this->translate('Service'), + 'description' => $this->translate('The name of the service'), + 'multiOptions' => $this->optionalEnum($this->db->enumServices()), + 'required' => true )); $this->addElement('text', 'varname', array( - 'label' => $this->translate('Name'), + 'label' => $this->translate('Name'), 'description' => $this->translate('service var name') )); $this->addElement('textarea', 'varvalue', array( - 'label' => $this->translate('Value'), + 'label' => $this->translate('Value'), 'description' => $this->translate('service var value') )); $this->addElement('text', 'format', array( - 'label' => $this->translate('Format'), + 'label' => $this->translate('Format'), 'description' => $this->translate('value format') )); } diff --git a/application/forms/IcingaTimePeriodForm.php b/application/forms/IcingaTimePeriodForm.php index 2470f250..51a2de5f 100644 --- a/application/forms/IcingaTimePeriodForm.php +++ b/application/forms/IcingaTimePeriodForm.php @@ -43,15 +43,7 @@ class IcingaTimePeriodForm extends DirectorObjectForm 'description' => $this->translate('the update method'), )); - $this->addElement('select', 'zone_id', array( - 'label' => $this->translate('Cluster Zone'), - 'description' => $this->translate('Check this host in this specific Icinga cluster zone'), - 'required' => true - )); - - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited timperiods template names') - )); + $this->addZoneElement(); + $this->addImportsElement(); } } diff --git a/application/forms/IcingaUserForm.php b/application/forms/IcingaUserForm.php index b94bcf1a..133c3ca5 100644 --- a/application/forms/IcingaUserForm.php +++ b/application/forms/IcingaUserForm.php @@ -49,20 +49,11 @@ class IcingaUserForm extends DirectorObjectForm $this->translate('Whether to send notifications for this user') ); - - $this->addElement('select', 'zone_id', array( - 'label' => $this->translate('Cluster Zone'), - 'description' => $this->translate('Check this user in this specific Icinga cluster zone') - )); - $this->addElement('text', 'groups', array( 'label' => $this->translate('Usergroups'), 'description' => $this->translate('One or more comma separated usergroup names') )); - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited user template names') - )); + $this->addImportsElement(); } } diff --git a/application/forms/IcingaUserGroupForm.php b/application/forms/IcingaUserGroupForm.php index ff1b15c8..aa1a7faa 100644 --- a/application/forms/IcingaUserGroupForm.php +++ b/application/forms/IcingaUserGroupForm.php @@ -8,44 +8,15 @@ class IcingaUserGroupForm extends DirectorObjectForm { public function setup() { - $isTemplate = isset($_POST['object_type']) && $_POST['object_type'] === 'template'; - $this->addElement('select', 'object_type', array( - 'label' => $this->translate('Object type'), - 'description' => $this->translate('Whether this should be a template'), - 'multiOptions' => array( - null => '- please choose -', - 'object' => 'Usergroup object', - 'template' => 'Usergroup template', - ) + $this->addElement('text', 'object_name', array( + 'label' => $this->translate('Usergroup'), + 'required' => true, + 'description' => $this->translate('Icinga object name for this usergroup') )); - if ($isTemplate) { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Usergroup template name'), - 'required' => true, - 'description' => $this->translate('Usergroup for the Icinga usergroup template you are going to create') - )); - } else { - $this->addElement('text', 'object_name', array( - 'label' => $this->translate('Usergroup'), - 'required' => true, - 'description' => $this->translate('Usergroup for the Icinga usergroup you are going to create') - )); - } - $this->addElement('text', 'display_name', array( 'label' => $this->translate('Display Name'), 'description' => $this->translate('The name which should displayed.') )); - - $this->addElement('select', 'zone_id', array( - 'label' => $this->translate('Cluster Zone'), - 'description' => $this->translate('Check this usergroup in this specific Icinga cluster zone') - )); - - $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), - 'description' => $this->translate('The inherited usergroup template names') - )); } } diff --git a/application/forms/IcingaZoneForm.php b/application/forms/IcingaZoneForm.php index 27c8219b..1705423c 100644 --- a/application/forms/IcingaZoneForm.php +++ b/application/forms/IcingaZoneForm.php @@ -10,7 +10,7 @@ class IcingaZoneForm extends DirectorObjectForm { $this->addElement('select', 'object_type', array( 'label' => $this->translate('Object type'), - 'description' => $this->translate('Whether this should be a template'), + 'description' => $this->translate('Whether this should be a template'), 'multiOptions' => $this->optionalEnum(array( 'object' => $this->translate('Zone object'), 'template' => $this->translate('Zone template'), @@ -24,22 +24,23 @@ class IcingaZoneForm extends DirectorObjectForm )); $this->addElement('select', 'is_global', array( - 'label' => 'Global zone', - 'description' => 'Whether this zone should be available everywhere', + 'label' => 'Global zone', + 'description' => 'Whether this zone should be available everywhere', 'multiOptions' => array( 'n' => $this->translate('No'), 'y' => $this->translate('Yes'), ), - 'required' => true, + 'required' => true, )); $this->addElement('select', 'parent_zone_id', array( - 'label' => $this->translate('Parent Zone'), - 'description' => $this->translate('Chose an (optional) parent zone') + 'label' => $this->translate('Parent Zone'), + 'description' => $this->translate('Chose an (optional) parent zone'), + 'multiOptions' => $this->optionalEnum($this->db->enumZones()) )); $this->addElement('text', 'imports', array( - 'label' => $this->translate('Imports'), + 'label' => $this->translate('Imports'), 'description' => $this->translate('The inherited zone template names') )); } diff --git a/application/tables/DatafieldTable.php b/application/tables/DatafieldTable.php index 72f5bd37..3b38598b 100644 --- a/application/tables/DatafieldTable.php +++ b/application/tables/DatafieldTable.php @@ -13,9 +13,11 @@ class DatafieldTable extends QuickTable public function getColumns() { return array( - 'id' => 'f.id', - 'varname' => 'f.varname', - 'datatype' => 'f.datatype', + 'id' => 'f.id', + 'varname' => 'f.varname', + 'caption' => 'f.caption', + 'description' => 'f.description', + 'datatype' => 'f.datatype', ); } @@ -28,8 +30,8 @@ class DatafieldTable extends QuickTable { $view = $this->view(); return array( - 'varname' => $view->translate('Field name'), - 'datatype' => $view->translate('Data type'), + 'caption' => $view->translate('Label'), + 'varname' => $view->translate('Field name'), ); } @@ -40,7 +42,7 @@ class DatafieldTable extends QuickTable $query = $db->select()->from( array('f' => 'director_datafield'), array() - )->order('varname ASC'); + )->order('caption ASC'); return $query; } diff --git a/application/tables/IcingaHostTemplateTable.php b/application/tables/IcingaHostTemplateTable.php new file mode 100644 index 00000000..191eda59 --- /dev/null +++ b/application/tables/IcingaHostTemplateTable.php @@ -0,0 +1,13 @@ +getUnfilteredQuery()->where('h.object_type = ?', 'template'); + } +} diff --git a/application/tables/IcingaServiceTable.php b/application/tables/IcingaServiceTable.php index f8019f02..27a3a625 100644 --- a/application/tables/IcingaServiceTable.php +++ b/application/tables/IcingaServiceTable.php @@ -33,7 +33,7 @@ class IcingaServiceTable extends QuickTable ); } - public function getBaseQuery() + public function getUnfilteredQuery() { $db = $this->connection()->getConnection(); $query = $db->select()->from( @@ -47,4 +47,9 @@ class IcingaServiceTable extends QuickTable return $query; } + + public function getBaseQuery() + { + return $this->getUnfilteredQuery()->where('s.object_type = ?', 'object'); + } } diff --git a/application/tables/IcingaServiceTemplateTable.php b/application/tables/IcingaServiceTemplateTable.php new file mode 100644 index 00000000..e5cbe359 --- /dev/null +++ b/application/tables/IcingaServiceTemplateTable.php @@ -0,0 +1,13 @@ +getUnfilteredQuery()->where('s.object_type = ?', 'template'); + } +} diff --git a/application/views/scripts/hosttemplates/tree.phtml b/application/views/scripts/hosttemplates/tree.phtml new file mode 100644 index 00000000..a97bba7a --- /dev/null +++ b/application/views/scripts/hosttemplates/tree.phtml @@ -0,0 +1,65 @@ +children); + if ($level === 0) { + $class = 'root'; + } else { + $class = 'host'; + } + + if ($hasChildren) { + $collapsed = ''; + } else { + $collapsed = ' class="collapsed"'; + } + + $html = '
= $this->escape($schema) ?>+
= $this->errorMessage ?>
-Nothing to see here yet. We will point you to the most common tasks later on. +translate('Monitoring Nodes'), 'director/commands'), + array('host', $this->translate('Host objecs'), 'director/hosts'), + array('services', $this->translate('Monitored Services'), 'director/services'), + array('users', $this->translate('Users / Contacts'), 'director/users'), + array('chat', $this->translate('Alarms and notifications'), 'director/notificatios'), + array('database', $this->translate('Sync / Import'), 'director/list/importsource'), + array('wrench', $this->translate('Configuration'), 'director/list/generatedconfig'), +); +?> + diff --git a/configuration.php b/configuration.php index f91ff7d1..4ca0e9c8 100644 --- a/configuration.php +++ b/configuration.php @@ -1,7 +1,20 @@ providePermission('director/templates', 'Allow to modify templates'); +$this->providePermission('director/hosts/read', $this->translate('Allow to configure hosts')); +$this->providePermission('director/hosts/write', $this->translate('Allow to configure hosts')); +$this->providePermission('director/templates/read', $this->translate('Allow to see template details')); +$this->providePermission('director/templates/write', $this->translate('Allow to configure templates')); + +$this->provideRestriction( + 'director/hosttemplates/filter', + $this->translate('Allow to use only host templates matching this filter') +); + +$this->provideRestriction( + 'director/dbresources/use', + $this->translate('Allow to use only these db resources (comma separated list)') +); + $this->provideConfigTab('config', array( 'title' => 'Configuration', @@ -12,9 +25,10 @@ $section = $this->menuSection( $this->translate('Icinga Director') )->setIcon('cubes'); +$section->add($this->translate('Overview'))->setUrl('director/welcome')->setPriority(20); $section->add($this->translate('Global'))->setUrl('director/commands'); $section->add($this->translate('Hosts'))->setUrl('director/hosts'); -$section->add($this->translate('Fields'))->setUrl('director/field/host'); +$section->add($this->translate('Fields'))->setUrl('director/field/host')->setPriority(903); $section->add($this->translate('Services'))->setUrl('director/services'); $section->add($this->translate('Users'))->setUrl('director/users'); $section->add($this->translate('Import / Sync')) diff --git a/library/Director/CustomVariable/CustomVariable.php b/library/Director/CustomVariable/CustomVariable.php index 9f620a1b..4a824cf4 100644 --- a/library/Director/CustomVariable/CustomVariable.php +++ b/library/Director/CustomVariable/CustomVariable.php @@ -45,12 +45,17 @@ abstract class CustomVariable implements IcingaConfigRenderer } // TODO: implement delete() - public function hasBeenDeleted() { return $this->deleted; } + public function delete() + { + $this->deleted = true; + return $this; + } + // TODO: abstract public function getDbValue() { diff --git a/library/Director/CustomVariable/CustomVariables.php b/library/Director/CustomVariable/CustomVariables.php index 5c830c1e..61aa6cc8 100644 --- a/library/Director/CustomVariable/CustomVariables.php +++ b/library/Director/CustomVariable/CustomVariables.php @@ -89,7 +89,12 @@ class CustomVariables implements Iterator, Countable, IcingaConfigRenderer protected function refreshIndex() { - $this->idx = array_keys($this->vars); + $this->idx = array(); + foreach ($this->vars as $name => $var) { + if (! $var->hasBeenDeleted()) { + $this->idx[] = $name; + } + } } public static function loadForStoredObject(IcingaObject $object) @@ -169,10 +174,18 @@ class CustomVariables implements Iterator, Countable, IcingaConfigRenderer public function setUnmodified() { $this->modified = false; - $this->storedVars = $this->vars; + $this->storedVars = array(); + foreach ($this->vars as $key => $var) { + $this->storedVars[$key] = clone($var); + } return $this; } + public function getOriginalVars() + { + return $this->storedVars; + } + public function toConfigString() { $out = ''; @@ -223,10 +236,11 @@ class CustomVariables implements Iterator, Countable, IcingaConfigRenderer public function __unset($key) { if (! array_key_exists($key, $this->vars)) { - throw new Exception('Trying to unset invalid key'); + return; } - unset($this->vars[$key]); + $this->vars[$key]->delete(); + $this->modified = true; $this->refreshIndex(); } diff --git a/library/Director/DataType/DataTypeSqlQuery.php b/library/Director/DataType/DataTypeSqlQuery.php index d4026dea..fa1b05a2 100644 --- a/library/Director/DataType/DataTypeSqlQuery.php +++ b/library/Director/DataType/DataTypeSqlQuery.php @@ -2,9 +2,11 @@ namespace Icinga\Module\Director\DataType; +use Exception; +use Icinga\Data\Db\DbConnection; use Icinga\Module\Director\Web\Form\QuickForm; use Icinga\Module\Director\Web\Hook\DataTypeHook; -use Icinga\Data\Db\DbConnection; +use Icinga\Module\Director\Util; class DataTypeSqlQuery extends DataTypeHook { @@ -16,11 +18,22 @@ class DataTypeSqlQuery extends DataTypeHook public function getFormElement($name, QuickForm $form) { + try { + $data = $this->fetchData(); + $error = false; + } catch (Exception $e) { + $data = array(); + $error = sprintf($form->translate('Unable to fetch data: %s'), $e->getMessage()); + } + $element = $form->createElement('select', $name, array( - 'multiOptions' => array(null => '- please choose -') + - $this->fetchData(), + 'multiOptions' => $form->optionalEnum($data), )); + if ($error) { + $element->addError($error); + } + return $element; } @@ -36,17 +49,13 @@ class DataTypeSqlQuery extends DataTypeHook public static function addSettingsFormFields(QuickForm $form) { - $db = $form->getDb(); - - $form->addElement('text', 'resource', array( - 'label' => 'Resource name', - 'required' => true, - )); + Util::addDbResourceFormElement($form, 'resource'); $form->addElement('textarea', 'query', array( 'label' => 'DB Query', 'description' => 'This query should return exactly two columns, value and label', 'required' => true, + 'rows' => 10, )); return $form; diff --git a/library/Director/Db.php b/library/Director/Db.php index 2cc86138..0c762527 100644 --- a/library/Director/Db.php +++ b/library/Director/Db.php @@ -85,50 +85,51 @@ class Db extends DbConnection return $db->fetchOne($query); } - public function fetchHostTemplateTree() + public function fetchTemplateTree($type) { $db = $this->db(); $query = $db->select()->from( - array('ph' => 'icinga_host'), + array('p' => 'icinga_' . $type), array( - 'host' => 'h.object_name', - 'parent' => 'ph.object_name' + 'name' => 'o.object_name', + 'parent' => 'p.object_name' ) )->join( - array('hi' => 'icinga_host_inheritance'), - 'ph.id = hi.parent_host_id', + array('i' => 'icinga_' . $type . '_inheritance'), + 'p.id = i.parent_' . $type . '_id', array() )->join( - array('h' => 'icinga_host'), - 'h.id = hi.host_id', + array('o' => 'icinga_' . $type), + 'o.id = i.' . $type . '_id', array() - )->where("h.object_type = 'template'") - ->order('ph.object_name') - ->order('h.object_name'); + )->where("o.object_type = 'template'") + ->order('p.object_name') + ->order('o.object_name'); $relations = $db->fetchAll($query); $children = array(); - $hosts = array(); + $objects = array(); foreach ($relations as $rel) { - foreach (array('host', 'parent') as $col) { - if (! array_key_exists($rel->$col, $hosts)) { - $hosts[$rel->$col] = (object) array( + foreach (array('name', 'parent') as $col) { + if (! array_key_exists($rel->$col, $objects)) { + $objects[$rel->$col] = (object) array( 'name' => $rel->$col, 'children' => array() ); } } } + foreach ($relations as $rel) { - $hosts[$rel->parent]->children[$rel->host] = $hosts[$rel->host]; - $children[$rel->host] = $rel->parent; + $objects[$rel->parent]->children[$rel->name] = $objects[$rel->name]; + $children[$rel->name] = $rel->parent; } - foreach ($children as $name => $host) { - unset($hosts[$name]); + foreach ($children as $name => $object) { + unset($objects[$name]); } - return $hosts; + return $objects; } public function fetchLatestImportedRows($source, $columns = null) diff --git a/library/Director/Import/ImportSourceSql.php b/library/Director/Import/ImportSourceSql.php index 0fa36481..416dae3b 100644 --- a/library/Director/Import/ImportSourceSql.php +++ b/library/Director/Import/ImportSourceSql.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Director\Import; +use Icinga\Module\Director\Util; use Icinga\Module\Director\Web\Form\QuickForm; use Icinga\Module\Director\Web\Hook\ImportSourceHook; use Icinga\Data\Db\DbConnection; @@ -23,13 +24,11 @@ class ImportSourceSql extends ImportSourceHook public static function addSettingsFormFields(QuickForm $form) { - $form->addElement('text', 'resource', array( - 'label' => 'Resource name', - 'required' => true, - )); + Util::addDbResourceFormElement($form, 'resource'); $form->addElement('textarea', 'query', array( 'label' => 'DB Query', 'required' => true, + 'rows' => 15, )); return $form; } diff --git a/library/Director/Objects/DirectorActivityLog.php b/library/Director/Objects/DirectorActivityLog.php index 1ff183cc..8f4e4f38 100644 --- a/library/Director/Objects/DirectorActivityLog.php +++ b/library/Director/Objects/DirectorActivityLog.php @@ -5,7 +5,7 @@ namespace Icinga\Module\Director\Objects; use Icinga\Module\Director\Data\Db\DbObject; use Icinga\Module\Director\Db; use Icinga\Module\Director\Util; -use Icinga\Authentication\Manager as Auth; +use Icinga\Authentication\Auth; class DirectorActivityLog extends DbObject { @@ -39,6 +39,59 @@ class DirectorActivityLog extends DbObject } } + protected static function prepareNewObjectProperties(DbObject $object) + { + $props = $object->getProperties(); + if ($object->supportsCustomVars()) { + // $props->vars = $object->vars()->toJson(); + } + if ($object->supportsGroups()) { + $props['groups'] = $object->groups()->listGroupNames(); + } + if ($object->supportsCustomVars()) { + $props['vars'] = $object->getVars(); + } + + return json_encode($props); + } + + protected static function prepareModifiedProperties(DbObject $object) + { + $props = $object->getModifiedProperties(); + if ($object->supportsCustomVars()) { + $mod = array(); + foreach ($object->vars() as $name => $var) { + if ($var->hasBeenModified()) { + $mod[$name] = $var->getValue(); + } + } + if (! empty($mod)) { + $props['vars'] = (object) $mod; + } + } + if ($object->supportsGroups()) { + // $props['groups'] = $object->groups()->listGroupNames(); + } + + return json_encode($props); + } + + protected static function prepareOriginalProperties(DbObject $object) + { + $props = $object->getModifiedProperties(); + if ($object->supportsCustomVars()) { + $props['vars'] = (object) array(); + foreach ($object->vars()->getOriginalVars() as $name => $var) { + $props['vars']->$name = $var->getValue(); + } + } + if ($object->supportsGroups()) { + // $props['groups'] = $object->groups()->listGroupNames(); + } + + return json_encode($props); + } + public static function logCreation(DbObject $object, Db $db) { $data = array( @@ -46,7 +99,7 @@ class DirectorActivityLog extends DbObject 'action_name' => 'create', 'author' => self::username(), 'object_type' => $object->getTableName(), - 'new_properties' => json_encode($object->getProperties()), + 'new_properties' => self::prepareNewObjectProperties($object), 'change_time' => date('Y-m-d H:i:s'), // TODO -> postgres! 'parent_checksum' => $db->getLastActivityChecksum() ); @@ -63,8 +116,8 @@ class DirectorActivityLog extends DbObject 'action_name' => 'modify', 'author' => self::username(), 'object_type' => $object->getTableName(), - 'old_properties' => json_encode($object->getOriginalProperties()), - 'new_properties' => json_encode($object->getModifiedProperties()), + 'old_properties' => self::prepareOriginalProperties($object), + 'new_properties' => self::prepareModifiedProperties($object), 'change_time' => date('Y-m-d H:i:s'), // TODO -> postgres! 'parent_checksum' => $db->getLastActivityChecksum() ); diff --git a/library/Director/Objects/DirectorDatafield.php b/library/Director/Objects/DirectorDatafield.php index 770587a0..07627495 100644 --- a/library/Director/Objects/DirectorDatafield.php +++ b/library/Director/Objects/DirectorDatafield.php @@ -23,7 +23,6 @@ class DirectorDatafield extends DbObject protected $settings = array(); - public function set($key, $value) { if ($this->hasProperty($key)) { @@ -33,6 +32,7 @@ class DirectorDatafield extends DbObject if (! array_key_exists($key, $this->settings) || $value !== $this->settings[$key]) { $this->hasBeenModified = true; } + $this->settings[$key] = $value; return $this; } @@ -50,6 +50,20 @@ class DirectorDatafield extends DbObject return parent::get($key); } + public function __unset($key) + { + if ($this->hasProperty($key)) { + return parent::__set($key, $value); + } + + if (array_key_exists($key, $this->settings)) { + unset($this->settings[$key]); + $this->hasBeenModified = true; + } + + return $this; + } + public function getSettings() { return $this->settings; @@ -100,7 +114,7 @@ class DirectorDatafield extends DbObject } foreach ($del as $key) { - $db->update( + $db->delete( 'director_datafield_setting', $db->quoteInto($where, $key) ); diff --git a/library/Director/Objects/IcingaHost.php b/library/Director/Objects/IcingaHost.php index 7462776c..1c918573 100644 --- a/library/Director/Objects/IcingaHost.php +++ b/library/Director/Objects/IcingaHost.php @@ -43,6 +43,8 @@ class IcingaHost extends IcingaObject protected $supportsImports = true; + protected $supportsFields = true; + protected function renderCheck_command_id() { return $this->renderCommandProperty($this->check_command_id); @@ -82,22 +84,4 @@ class IcingaHost extends IcingaObject { return $this->renderBooleanProperty('volatile'); } - - public function getFields(DirectorObjectForm $form) - { - $db = $this->getDb(); - - $query = $db->select() - ->from( - array('df' => 'director_datafield') - ) - ->join( - array('hf' => 'icinga_host_field'), - 'df.id = hf.datafield_id' - ) - ->where('hf.host_id = ?', (int) $this->id) - ->order('df.caption ASC'); - - return $db->fetchAll($query); - } } diff --git a/library/Director/Objects/IcingaObject.php b/library/Director/Objects/IcingaObject.php index f41ecafa..40f2047e 100644 --- a/library/Director/Objects/IcingaObject.php +++ b/library/Director/Objects/IcingaObject.php @@ -23,6 +23,8 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer protected $supportsImports = false; + protected $supportsFields = false; + private $type; private $vars; @@ -55,6 +57,11 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer return $this->supportsImports; } + public function supportsFields() + { + return $this->supportsFields; + } + public function hasBeenModified() { if ($this->supportsCustomVars() && $this->vars !== null && $this->vars()->hasBeenModified()) { @@ -132,37 +139,143 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer return $this->importedObjects; } + public function clearImportedObjects() + { + $this->importedObjects = null; + return $this; + } + public function getResolvedProperties() { - $res = $this->resolveProperties(); - return $res['_MERGED_']; + return $this->getResolved('Properties'); + } + + public function getInheritedProperties() + { + return $this->getInherited('Properties'); + } + + public function getOriginsProperties() + { + return $this->getOrigins('Properties'); } public function resolveProperties() { - $props = array(); - $props['_MERGED_'] = (object) array(); + return $this->resolve('Properties'); + } + + public function getResolvedFields() + { + return $this->getResolved('Fields'); + } + + public function getInheritedFields() + { + return $this->getInherited('Fields'); + } + + public function getOriginsFields() + { + return $this->getOrigins('Fields'); + } + + public function resolveFields() + { + return $this->resolve('Fields'); + } + + public function getResolvedVars() + { + return $this->getResolved('Vars'); + } + + public function getInheritedVars() + { + return $this->getInherited('Vars'); + } + + public function resolveVars() + { + return $this->resolve('Vars'); + } + + public function getOriginsVars() + { + return $this->getOrigins('Vars'); + } + + public function getVars() + { + $vars = (object) array(); + foreach ($this->vars() as $key => $var) { + $vars->$key = $var->getValue(); + } + + return $vars; + } + + protected function getResolved($what) + { + $func = 'resolve' . $what; + $res = $this->$func(); + return $res['_MERGED_']; + } + + protected function getInherited($what) + { + $func = 'resolve' . $what; + $res = $this->$func(); + return $res['_INHERITED_']; + } + + protected function getOrigins($what) + { + $func = 'resolve' . $what; + $res = $this->$func(); + return $res['_ORIGINS_']; + } + + protected function resolve($what) + { + $vals = array(); + $vals['_MERGED_'] = (object) array(); + $vals['_INHERITED_'] = (object) array(); + $vals['_ORIGINS_'] = (object) array(); $objects = $this->importedObjects(); - $objects[$this->object_name] = $this; - $blacklist = array('id', 'object_type', 'object_name'); + $get = 'get' . $what; + $getInherited = 'getInherited' . $what; + $getOrigins = 'getOrigins' . $what; + foreach ($objects as $name => $object) { - $props[$name] = (object) array(); - if ($name === $this->object_name) { - $pprops = $object->getProperties(); - } else { - $pprops = $object->getResolvedProperties(); + $origins = $object->$getOrigins(); + + foreach ($object->$getInherited() as $key => $value) { + // $vals[$name]->$key = $value; + $vals['_MERGED_']->$key = $value; + $vals['_INHERITED_']->$key = $value; + $vals['_ORIGINS_']->$key = $origins->$key; } - foreach ($pprops as $key => $value) { - if (in_array($key, $blacklist)) continue; - if ($value !== null) { - $props[$name]->$key = $value; - $props['_MERGED_']->$key = $value; - } + + foreach ($object->$get() as $key => $value) { + if ($value === null) continue; + $vals['_MERGED_']->$key = $value; + $vals['_INHERITED_']->$key = $value; + $vals['_ORIGINS_']->$key = $name; } } - return $props; + $blacklist = array('id', 'object_type', 'object_name'); + foreach ($this->$get() as $key => $value) { + if ($value === null) continue; + if (in_array($key, $blacklist)) continue; + + // $vals[$this->object_name]->$key = $value; + $vals['_MERGED_']->$key = $value; + } + + return $vals; } protected function assertCustomVarsSupport() @@ -243,6 +356,39 @@ abstract class IcingaObject extends DbObject implements IcingaConfigRenderer return $this->getShortTableName() . '_id'; } + public function getFields() + { + $fields = (object) array(); + + if (! $this->supportsFields()) { + return $fields; + } + + $db = $this->getDb(); + + $query = $db->select()->from( + array('df' => 'director_datafield'), + array( + 'datafield_id' => 'f.datafield_id', + 'is_required' => 'f.is_required', + 'varname' => 'df.varname' + ) + )->join( + array('f' => $this->getTableName() . '_field'), + 'df.id = f.datafield_id', + array() + )->where('f.' . $this->getShortTableName() . '_id = ?', (int) $this->id) + ->order('df.caption ASC'); + + $res = $db->fetchAll($query); + + foreach ($res as $r) { + $fields->{$r->varname} = $r; + } + + return $fields; + } + public function isTemplate() { return $this->hasProperty('object_type') diff --git a/library/Director/Objects/IcingaObjectImports.php b/library/Director/Objects/IcingaObjectImports.php index b8ab2c72..470c1244 100644 --- a/library/Director/Objects/IcingaObjectImports.php +++ b/library/Director/Objects/IcingaObjectImports.php @@ -158,19 +158,9 @@ class IcingaObjectImports implements Iterator, Countable, IcingaConfigRenderer if ($import instanceof $class) { $this->imports[$import->object_name] = $import; } elseif (is_string($import)) { - $query = $this->object->getDb()->select()->from( - $this->object->getTableName() - )->where('object_name = ?', $import); - $imports = $class::loadAll($connection, $query, 'object_name'); + $import = $class::load($import, $connection); + $this->imports[$import->object_name] = $import; } - if (! array_key_exists($import, $imports)) { - throw new ProgrammingError( - 'The import "%s" doesn\'t exists.', - $import - ); - } - - $this->imports[$import] = $imports[$import]; $this->modified = true; $this->refreshIndex(); diff --git a/library/Director/Objects/IcingaService.php b/library/Director/Objects/IcingaService.php index b6aa6a20..1c92d25f 100644 --- a/library/Director/Objects/IcingaService.php +++ b/library/Director/Objects/IcingaService.php @@ -38,6 +38,8 @@ class IcingaService extends IcingaObject protected $supportsCustomVars = true; + protected $supportsFields = true; + protected $supportsImports = true; protected function renderCheck_command_id() diff --git a/library/Director/Util.php b/library/Director/Util.php index 6053a74b..09e9840d 100644 --- a/library/Director/Util.php +++ b/library/Director/Util.php @@ -2,13 +2,21 @@ namespace Icinga\Module\Director; +use Icinga\Authentication\Auth; +use Icinga\Data\ResourceFactory; +use Icinga\Module\Director\Web\Form\QuickForm; +use Icinga\Web\Url; use Zend_Db_Expr; class Util { + protected static $auth; + + protected static $allowedDbResources; + public static function pgBinEscape($binary) { - return new \Zend_Db_Expr("'\\x" . bin2hex($binary) . "'"); + return new Zend_Db_Expr("'\\x" . bin2hex($binary) . "'"); } public static function hex2binary($bin) @@ -20,4 +28,84 @@ class Util { return current(unpack('H*', $hex)); } + + public static function auth() + { + if (self::$auth === null) { + self::$auth = Auth::getInstance(); + } + return self::$auth; + } + + public static function hasPermission($name) + { + return self::auth()->hasPermission($name); + } + + public static function getRestrictions($name) + { + return self::auth()->getRestrictions($name); + } + + public static function dbResourceIsAllowed($name) + { + if (self::$allowedDbResources === null) { + $restrictions = self::getRestrictions('director/dbresources/use'); + $list = array(); + foreach ($restrictions as $restriction) { + foreach (preg_split('/\s*,\s*/', $restriction, -1, PREG_SPLIT_NO_EMPTY) as $key) { + $list[$key] = $key; + } + } + + self::$allowedDbResources = $list; + } else { + $list = self::$allowedDbResources; + } + + if (empty($list) || array_key_exists($name, $list)) { + return true; + } + + return false; + } + + public static function enumDbResources() + { + $resources = array(); + foreach (ResourceFactory::getResourceConfigs() as $name => $resource) { + if ($resource->type === 'db' && self::dbResourceIsAllowed($name)) { + $resources[$name] = $name; + } + } + + return $resources; + } + + public static function addDbResourceFormElement(QuickForm $form, $name) + { + $list = Util::enumDbResources(); + + $form->addElement('select', $name, array( + 'label' => 'Resource name', + 'multiOptions' => $form->optionalEnum($list), + 'required' => true, + )); + + if (true && empty($list)) { + if (self::hasPermission('config/application/resources')) { + $hint = $form->translate('Please click %s to create new DB resources'); + $link = sprintf( + '%s', + $form->translate('here') + ); + $form->addHtmlHint(sprintf($hint, $link)); + $msg = $form->translate('No db resource available'); + } else { + $msg = $form->translate('Please ask an administrator to grant you access to DB resources'); + } + + $form->getElement($name)->addError($msg); + } + } } diff --git a/library/Director/Web/Controller/ObjectController.php b/library/Director/Web/Controller/ObjectController.php index f46b61ed..c7191e2c 100644 --- a/library/Director/Web/Controller/ObjectController.php +++ b/library/Director/Web/Controller/ObjectController.php @@ -16,18 +16,18 @@ abstract class ObjectController extends ActionController if ($name = $this->params->get('name')) { $params['name'] = $name; - $this->getTabs()->add($type, array( - 'url' => sprintf('director/%s', $ltype), - 'urlParams' => $params, - 'label' => $this->translate(ucfirst($ltype)), - ))->add('modify', array( + $this->getTabs()->add('modify', array( 'url' => sprintf('director/%s/edit', $ltype), 'urlParams' => $params, - 'label' => $this->translate('Modify') + 'label' => $this->translate(ucfirst($ltype)) ))->add('delete', array( 'url' => sprintf('director/%s/delete', $ltype), 'urlParams' => $params, 'label' => $this->translate('Delete') + ))->add('render', array( + 'url' => sprintf('director/%s/render', $ltype), + 'urlParams' => $params, + 'label' => $this->translate('Preview'), ))->add('history', array( 'url' => sprintf('director/%s/history', $ltype), 'urlParams' => $params, @@ -35,16 +35,21 @@ abstract class ObjectController extends ActionController )); } else { $this->getTabs()->add('add', array( - 'url' => sprintf('director/%s', $type), + 'url' => sprintf('director/%s/add', $type), 'label' => sprintf($this->translate('Add %s'), ucfirst($type)), )); } } public function indexAction() + { + return $this->editAction(); + } + + public function renderAction() { $type = $this->getType(); - $this->getTabs()->activate($type); + $this->getTabs()->activate('render'); $this->view->object = $this->object(); $this->render('object/show', null, true); } @@ -80,17 +85,22 @@ abstract class ObjectController extends ActionController 'icinga' . ucfirst($type) )->setDb($this->db()); $form->loadObject($this->params->get('name')); + $object = $form->getObject(); $url = Url::fromPath( sprintf('director/%s', $ltype), - array('name' => $form->getObject()->object_name) + array('name' => $object->object_name) ); $form->setSuccessUrl($url); - $this->view->title = sprintf( - $this->translate('Modify Icinga %s'), - ucfirst($ltype) - ); + if ($object->isTemplate()) { + $title = $this->translate('Modify Icinga %s template'); + $form->setObjectType('template'); // WHY?? + } else { + $title = $this->translate('Modify Icinga %s'); + } + + $this->view->title = sprintf($title, ucfirst($ltype)); $this->view->form->handleRequest(); $this->render('object/form', null, true); } @@ -102,15 +112,19 @@ abstract class ObjectController extends ActionController $ltype = strtolower($type); $url = sprintf('director/%ss', $ltype); - $this->view->form = $this->loadForm('icinga' . ucfirst($type)) + $form = $this->view->form = $this->loadForm('icinga' . ucfirst($type)) ->setDb($this->db()) ->setSuccessUrl($url); - $this->view->title = sprintf( - $this->translate('Add new Icinga %s'), - ucfirst($ltype) - ); - $this->view->form->handleRequest(); + if ($this->params->get('type') === 'template') { + $form->setObjectType('template'); + $title = $this->translate('Add new Icinga %s template'); + } else { + $title = $this->translate('Add new Icinga %s'); + } + + $this->view->title = sprintf($title, ucfirst($ltype)); + $form->handleRequest(); $this->render('object/form', null, true); } diff --git a/library/Director/Web/Controller/ObjectsController.php b/library/Director/Web/Controller/ObjectsController.php index e40c48ff..7b3e364e 100644 --- a/library/Director/Web/Controller/ObjectsController.php +++ b/library/Director/Web/Controller/ObjectsController.php @@ -16,14 +16,12 @@ abstract class ObjectsController extends ActionController public function init() { + $tabs = $this->getTabs(); $type = $this->getType(); - $ltype = strtolower($type); - - $object = $this->dummyObject(); if (in_array(ucfirst($type), $this->globalTypes)) { + $ltype = strtolower($type); - $tabs = $this->getTabs(); foreach ($this->globalTypes as $tabType) { $ltabType = strtolower($tabType); $tabs->add($ltabType, array( @@ -33,67 +31,98 @@ abstract class ObjectsController extends ActionController } $tabs->activate($ltype); - } elseif ($object->isGroup()) { - - $singleType = substr($type, 0, -5); - $tabs = $this->getTabs()->add('objects', array( - 'url' => sprintf('director/%ss', $singleType), - 'label' => $this->translate(ucfirst($singleType) . 's'), - )); - - $tabs->add('objectgroups', array( - 'url' => sprintf('director/%ss', strtolower($type)), - 'label' => $this->translate(ucfirst(strtolower($type)) . 's') - )); - - } else { - - $tabs = $this->getTabs()->add('objects', array( - 'url' => sprintf('director/%ss', strtolower($type)), - 'label' => $this->translate(ucfirst($type) . 's'), - )); - if ($object->supportsGroups()) { - $tabs->add('objectgroups', array( - 'url' => sprintf('director/%sgroups', $type), - 'label' => $this->translate(ucfirst($type) . 'groups') - )); - } - + return; } + + + $object = $this->dummyObject(); + if ($object->isGroup()) { + $type = substr($type, 0, -5); + } + + $tabs = $this->getTabs()->add('objects', array( + 'url' => sprintf('director/%ss', strtolower($type)), + 'label' => $this->translate(ucfirst($type) . 's'), + )); + $tabs = $this->getTabs()->add('objecttemplates', array( + 'url' => sprintf('director/%stemplates', strtolower($type)), + 'label' => $this->translate('Templates'), + )); + if ($object->supportsGroups() || $object->isGroup()) { + $tabs->add('objectgroups', array( + 'url' => sprintf('director/%sgroups', $type), + 'label' => $this->translate('Groups') + )); + } + + $tabs->add('tree', array( + 'url' => sprintf('director/%stemplates/tree', $type), + 'label' => $this->translate('Tree'), + )); } public function indexAction() { $type = $this->getType(); $ltype = strtolower($type); - + $dummy = $this->dummyObject(); if (! in_array($type, $this->globalTypes)) { - if ($this->dummyObject()->isGroup()) { + if ($dummy->isGroup()) { $this->getTabs()->activate('objectgroups'); + $table = 'icinga' . ucfirst($type); + } elseif ($dummy->isTemplate()) { + $this->getTabs()->activate('objecttemplates'); + $table = 'icinga' . ucfirst($type); + $this->loadTable($table); + $table = 'icinga' . ucfirst($type) . 'Template'; } else { $this->getTabs()->activate('objects'); + $table = 'icinga' . ucfirst($type); } + } else { + $table = 'icinga' . ucfirst($type); + } + + if ($dummy->isTemplate()) { + $addParams = array('type' => 'template'); + $addTitle = $this->translate('Add %s template'); + } else { + $addParams = array(); + $addTitle = $this->translate('Add %s'); } $this->view->addLink = $this->view->qlink( - $this->translate('Add ' . ucfirst($ltype)), - 'director/' . $ltype . '/add' + sprintf($addTitle, $this->translate(ucfirst($ltype))), + 'director/' . $ltype .'/add', + $addParams ); $this->view->title = $this->translate('Icinga ' . ucfirst($ltype)); - - $table = $this->loadTable('icinga' . ucfirst($type))->setConnection($this->db()); + $table = $this->loadTable($table)->setConnection($this->db()); $this->setupFilterControl($table->getFilterEditor($this->getRequest())); $this->view->table = $this->applyPaginationLimits($table); $this->render('objects/table', null, true); } + public function treeAction() + { + $this->getTabs()->activate('tree'); + $this->view->tree = $this->db()->fetchTemplateTree(strtolower($this->getType())); + } + protected function dummyObject() { if ($this->dummy === null) { $class = $this->getObjectClassname(); $this->dummy = $class::create(array()); + if ($this->dummy->hasProperty('object_type')) { + if (false === strpos($this->getRequest()->getControllerName(), 'template')) { + $this->dummy->object_type = 'object'; + } else { + $this->dummy->object_type = 'template'; + } + } } return $this->dummy; @@ -105,7 +134,11 @@ abstract class ObjectsController extends ActionController return preg_replace( array('/group$/', '/period$/', '/argument$/'), array('Group', 'Period', 'Argument'), - substr($this->getRequest()->getControllerName(), 0, -1) + str_replace( + 'template', + '', + substr($this->getRequest()->getControllerName(), 0, -1) + ) ); } diff --git a/library/Director/Web/Form/DirectorObjectForm.php b/library/Director/Web/Form/DirectorObjectForm.php index d3765d8c..1ef09a29 100644 --- a/library/Director/Web/Form/DirectorObjectForm.php +++ b/library/Director/Web/Form/DirectorObjectForm.php @@ -3,6 +3,8 @@ namespace Icinga\Module\Director\Web\Form; use Icinga\Module\Director\Objects\IcingaObject; +use Icinga\Module\Director\Objects\DirectorDatafield; +use Zend_Form_Element_Select as Zf_Select; abstract class DirectorObjectForm extends QuickForm { @@ -10,10 +12,12 @@ abstract class DirectorObjectForm extends QuickForm protected $object; - private $objectName; + protected $objectName; private $className; + private $objectType = 'object'; + protected function object($values = array()) { if ($this->object === null) { @@ -37,49 +41,124 @@ abstract class DirectorObjectForm extends QuickForm return; } - if ($object->supportsCustomVars()) { - $this->addElement('note', '_newvar_hint', array('label' => 'New custom variable')); - $this->addElement('text', '_newvar_name', array( - 'label' => 'Name' - )); - $this->addElement('text', '_newvar_value', array( - 'label' => 'Value' - )); - $this->addElement('select', '_newvar_format', array( - 'label' => 'Type', - 'multiOptions' => array('string' => $this->translate('String')) - )); + } + + protected function isTemplate() + { + return $this->objectType === 'template'; + } + + protected function handleImports($object, & $values) + { + if (! $object->supportsImports()) { + return; } - if (false && $object->supportsRanges()) { - /* TODO implement when new logic is there - $this->addElement('note', '_newrange_hint', array('label' => 'New range')); - $this->addElement('text', '_newrange_name', array( - 'label' => 'Name' - )); - $this->addElement('text', '_newrange_value', array( - 'label' => 'Value' - )); - */ + if (array_key_exists('imports', $values)) { + $value = $values['imports']; + unset($values['imports']); + $object->clearImportedObjects(); + $object->imports()->set($value); + } + + $el = $this->getElement('imports'); + if ($el) { + $el->setMultiOptions($this->enumAllowedTemplates()); + $el->setValue($object->imports()->listImportNames()); } } - protected function handleIcingaObject(& $values) + protected function handleRanges($object, & $values) { - $object = $this->object(); - $handled = array(); - - if ($object->supportsGroups()) { - if (array_key_exists('groups', $values)) { - $object->groups()->set( - preg_split('/\s*,\s*/', $values['groups'], -1, PREG_SPLIT_NO_EMPTY) - ); - $handled['groups'] = true; - } + if (! $object->supportsRanges()) { + return; } - if ($this->object->supportsCustomVars()) { + $key = 'ranges'; + $object = $this->object(); + + /* Sample: + + array( + 'monday' => 'eins', + 'tuesday' => '00:00-24:00', + 'sunday' => 'zwei', + ); + + */ + if (array_key_exists($key, $values)) { + $object->ranges()->set($values[$key]); + unset($values[$key]); + } + + foreach ($object->ranges()->getRanges() as $key => $value) { + $this->addRange($key, $value); + } + + /* + // TODO implement when new logic is there + $this->addElement('note', '_newrange_hint', array('label' => 'New range')); + $this->addElement('text', '_newrange_name', array( + 'label' => 'Name' + )); + $this->addElement('text', '_newrange_value', array( + 'label' => 'Value' + )); + */ + } + + protected function handleGroups($object, & $values) + { + if (! $object->supportsGroups()) { + return; + } + + if (array_key_exists('groups', $values)) { + $value = $values['groups']; + unset($values['groups']); + + // TODO: Drop this once we have arrays everwhere + if (is_string($value)) { + $value = preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY); + } + + $object->groups()->set($value); + } + } + + protected function handleProperties($object, & $values) + { + if ($this->hasBeenSent()) { + $object->setProperties($values); + } + + $props = $object->getProperties(); + if (! $object instanceof IcingaObject) { + $this->setDefaults($props); + return $this; + } + + $inherited = $object->getInheritedProperties(); + $origins = $object->getOriginsProperties(); + + foreach ($props as $k => $v) { + if (property_exists($inherited, $k)) { + $this->setElementValue($k, $v, $inherited->$k, $origins->$k); + } else { + $this->setElementValue($k, $v); + } + } + } + + protected function handleCustomVars($object, & $values) + { + if (! $object->supportsCustomVars()) { + return; + } + + if ($this->hasBeenSent()) { $vars = array(); + $handled = array(); $newvar = array( 'type' => 'string', 'name' => null, @@ -99,62 +178,160 @@ abstract class DirectorObjectForm extends QuickForm } foreach ($vars as $k => $v) { - $this->object->vars()->$k = $v; + if ($v === '' || $v === null) { + unset($object->vars()->$k); + } else { + $object->vars()->$k = $v; + } } if ($newvar['name'] && $newvar['value']) { - $this->object->vars()->{$newvar['name']} = $newvar['value']; + $object->vars()->{$newvar['name']} = $newvar['value']; + } + + foreach ($handled as $key) { + unset($values[$key]); } } - if ($object->supportsImports()) { - if (array_key_exists('imports', $values)) { - $object->imports()->set( - preg_split('/\s*,\s*/', $values['imports'], -1, PREG_SPLIT_NO_EMPTY) - ); - $handled['imports'] = true; + $vars = $object->getVars(); + + $fields = $object->getResolvedFields(); + $inherits = $object->getInheritedVars(); + $origins = $object->getOriginsVars(); + + foreach ($fields as $field) { + $varname = $field->varname; + + // Get value from the related varname if set: + if (property_exists($vars, $varname)) { + $value = $vars->$varname; + } else { + $value = null; } + + if (property_exists($inherits, $varname)) { + $inheritedValue = $inherits->$varname; + $inheritFrom = $origins->$varname; + if ($inheritFrom === $object->object_name) { + $inherited = false; + } else { + $inherited = true; + } + } else { + $inheritedValue = null; + $inheritFrom = false; + $inherited = false; + } + + $this->addField($field, $value, $inheritedValue, $inheritFrom); } - if ($object->supportsRanges()) { - $object->ranges()->set(array( - 'monday' => 'eins', - 'tuesday' => '00:00-24:00', - 'sunday' => 'zwei', + // Additional vars + foreach ($vars as $key => $value) { + // Did we already create a field for this var? Then skip it: + if (array_key_exists($key, $fields)) { + continue; + } + + // Show inheritance information in case we inherited this var: + if (isset($inherited->$key)) { + $this->addCustomVar($key, $value, $inherited->$key, $origins->$key); + } else { + $this->addCustomVar($key, $value); + } + + } + + if ($object->isTemplate()) { + $this->addHtml('