From 26a5018d16fd8e4514120153e379a75851fad123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannis=20Mo=C3=9Fhammer?= Date: Wed, 14 Aug 2013 10:53:25 +0200 Subject: [PATCH 01/20] Add Forms and conditional hidden refs #3777 --- application/controllers/ConfigController.php | 47 ++- application/forms/Config/GeneralForm.php | 278 ++++++++++++++++++ application/forms/Config/LoggingForm.php | 217 ++++++++++++++ application/views/scripts/config/index.phtml | 12 +- .../views/scripts/config/logging.phtml | 3 + library/Icinga/Config/IniEditor.php | 3 +- .../Web/Form/Decorator/ConditionalHidden.php | 65 ++++ .../controllers/ConfigController.php | 1 - 8 files changed, 604 insertions(+), 22 deletions(-) create mode 100644 application/forms/Config/GeneralForm.php create mode 100644 application/forms/Config/LoggingForm.php create mode 100644 application/views/scripts/config/logging.phtml create mode 100644 library/Icinga/Web/Form/Decorator/ConditionalHidden.php diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index 4f7adf963..88ea60639 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -31,6 +31,9 @@ use \Icinga\Web\Widget\Tab; use \Icinga\Web\Url; use \Icinga\Web\Hook\Configuration\ConfigurationTabBuilder; use \Icinga\Application\Icinga; +use \Icinga\Form\Config\GeneralForm; +use \Icinga\Form\Config\LoggingForm; +use \Icinga\Config\PreservingIniWriter; /** * Application wide controller for application preferences @@ -49,18 +52,23 @@ class ConfigController extends BaseConfigController return array( 'index' => new Tab( array( - 'name' => 'index', - 'title' => 'Configuration', - 'iconCls' => 'wrench', - 'url' => Url::fromPath('/config') + "name" => "index", + "title" => "Application", + "url" => Url::fromPath("/config") ) ), - 'modules' => new Tab( + "logging" => new Tab( array( - 'name' => 'modules', - 'title' => 'Modules', - 'iconCls' => 'puzzle-piece', - 'url' => Url::fromPath('/config/moduleoverview') + "name" => "logging", + "title" => "Logging", + "url" => Url::fromPath("/config/logging") + ) + ), + "modules" => new Tab( + array( + "name" => "modules", + "title" => "Modules", + "url" => Url::fromPath("/config/moduleoverview") ) ) ); @@ -72,7 +80,26 @@ class ConfigController extends BaseConfigController */ public function indexAction() { + $form = new GeneralForm(); + $form->setConfiguration(IcingaConfig::app()); + $form->setRequest($this->_request); + if ($form->isSubmittedAndValid()) { + $cfg = IcingaConfig::app()->getConfigFile(); + $writer = new PreservingIniWriter( + array('config' => $form->getConfig(),'filename' => $cfg) + ); + print_r($writer->render());die(); + } + $this->view->form = $form; + } + public function loggingAction() + { + $form = new LoggingForm(); + $form->setConfiguration(IcingaConfig::app()); + $form->setRequest($this->_request); + + $this->view->form = $form; } /** @@ -106,5 +133,7 @@ class ConfigController extends BaseConfigController $manager->disableModule($this->_getParam('name')); $this->redirectNow('config/moduleoverview?_render=body'); } + + } // @codingStandardsIgnoreEnd diff --git a/application/forms/Config/GeneralForm.php b/application/forms/Config/GeneralForm.php new file mode 100644 index 000000000..4a416b48d --- /dev/null +++ b/application/forms/Config/GeneralForm.php @@ -0,0 +1,278 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Form\Config; +/** + * [global] +environment = development +timezone = "Europe/Berlin" +indexModule = monitoring +indexController = dashboard +moduleFolder = "/etc/icinga2-web/enabledModules" +dateFormat = "d/m/Y" +timeFormat = "g:i A" + +[logging] +; General log +enable = 1 +type = stream +verbose = 1 +target = /tmp/icinga2.log + +; For development and debug purposes: Logs additional (non critical) events to a +; seperate log +debug.enable = 1 +debug.type = stream +debug.target = /tmp/icinga2.debug.log + +; Use ini store to store preferences on local disk +[preferences] +type=ini + + */ +use \Icinga\Application\Config as IcingaConfig; +use Icinga\Application\Icinga; +use \Icinga\Web\Form; +use \Zend_Form_Element_Radio; +use \DateTimeZone; +use \Zend_Config; +use Icinga\Web\Form\Element\Note; + +/** + * Configuration form for general, application-wide settings + * + */ +class GeneralForm extends Form +{ + /** + * The configuration to use for populating this form + * + * @var IcingaConfig + */ + private $config = null; + + /** + * The base directory of the icingaweb configuration + * + * @var string + */ + private $configDir = null; + + /** + * Set the configuration to be used for this form + * + * @param IcingaConfig $cfg + */ + public function setConfiguration(IcingaConfig $cfg) + { + $this->config = $cfg; + } + + /** + * Set a specific configuration directory to use for configuration specific default paths + * + * @param string $dir + */ + public function setConfigDir($dir) + { + $this->configDir = $dir; + } + + /** + * Return the config path set for this form or the application wide config path if none is set + * + * @return string + * @see IcingaConfig::configDir + */ + public function getConfigDir() + { + return $this->configDir === null ? IcingaConfig::$configDir : $this->configDir; + } + + /** + * Add the checkbox for using the development environment to this form + * + * @param Zend_Config $cfg The "global" section of the config.ini + */ + private function addDevelopmentCheckbox(Zend_Config $cfg) + { + $env = $cfg->get('environment', 'development'); + $this->addElement( + 'checkbox', + 'environment', + array( + 'label' => 'Development mode', + 'required' => true, + 'tooltip' => 'More verbose output', + 'value' => $env === 'development' + ) + ); + $this->addElement(new Note(array( + 'name' => 'note_env', + 'value' => 'Set true to show more detailed errors and disable certain optimizations ' + . 'in order to make debugging easier.' + ))); + } + + /** + * Add a select field for setting the default timezone. + * + * Possible values are determined by DateTimeZone::listIdentifiers + * + * @param Zend_Config $cfg The "global" section of the config.ini + */ + private function addTimezoneSelection(Zend_Config $cfg) + { + $tzList = array(); + foreach(DateTimeZone::listIdentifiers() as $tz) { + $tzList[$tz] = $tz; + } + + $this->addElement( + 'select', + 'timezone', + array( + 'label' => 'Default application timezone', + 'required' => true, + 'multiOptions' => $tzList, + 'value' => $cfg->get('timezone', date_default_timezone_get()) + ) + ); + $this->addElement(new Note(array( + 'name' => 'noteTimezone', + 'value' => 'Select the timezone to be used as the default. User\'s can set their own timezone if'. + ' they like to, but this is the timezone to be used as the default setting .' + ))); + } + + /** + * Add configuration settings for module paths + * + * @param Zend_Config $cfg The "global" section of the config.ini + */ + private function addModuleSettings(Zend_Config $cfg) + { + $this->addElement( + 'text', + 'module_folder', + array( + 'label' => 'Module folder', + 'required' => true, + 'value' => $cfg->get('moduleFolder', $this->getConfigDir() . '/config/enabledModules') + ) + ); + $this->addElement(new Note(array( + 'name' => 'noteModuleFolder', + 'value' => 'Use this folder to activate modules (must be writable by your webserver)' + ))); + } + + /** + * Add text fields for the date and time format used in the application + * + * @param Zend_Config $cfg The "global" section of the config.ini + */ + private function addDateFormatSettings(Zend_Config $cfg) + { + $phpUrl = 'the official PHP documentation'; + + $this->addElement( + 'text', + 'date_format', + array( + 'label' => 'Date format', + 'required' => true, + 'value' => $cfg->get('dateFormat', 'd/m/Y') + ) + ); + $this->addElement(new Note(array( + 'name' => 'noteDateFormat', + 'value' => 'Display dates according to this format. See ' . $phpUrl . ' for possible values' + ))); + + + $this->addElement( + 'text', + 'time_format', + array( + 'label' => 'Time format', + 'required' => true, + 'value' => $cfg->get('timeFormat', 'g:i A') + ) + ); + $this->addElement(new Note(array( + 'name' => 'noteTimeFormat', + 'value' => 'Display times according to this format. See ' . $phpUrl . ' for possible values' + ))); + } + + /** + * Create the general form, using the provided configuration + * + * @see Form::create() + */ + public function create() + { + if ($this->config === null) { + $this->config = new Zend_Config(array()); + } + $global = $this->config->global; + if ($global === null) { + $global = new Zend_Config(array()); + } + + $this->addDevelopmentCheckbox($global); + $this->addTimezoneSelection($global); + $this->addModuleSettings($global); + $this->addDateFormatSettings($global); + + $this->setSubmitLabel('Save changes'); + } + + public function getConfig() + { + if ($this->config === null) { + $this->config = new Zend_Config(array()); + } + $global = $this->config->global; + if ($global === null) { + $this->config->global = new Zend_Config(array()); + } + + $values = $this->getValues(); + $cfg = clone $this->config; + $cfg->global->environment = ($values['environment'] == 1) ? 'development' : 'production'; + $cfg->global->timezone = $values['timezone']; + $cfg->global->moduleFolder = $values['module_folder']; + $cfg->global->dateFormat = $values['date_format']; + $cfg->global->timeFormat = $values['time_format']; + + return $cfg; + } + +} \ No newline at end of file diff --git a/application/forms/Config/LoggingForm.php b/application/forms/Config/LoggingForm.php new file mode 100644 index 000000000..952ae1c61 --- /dev/null +++ b/application/forms/Config/LoggingForm.php @@ -0,0 +1,217 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Form\Config; + +use \Icinga\Application\Config as IcingaConfig; +use \Icinga\Application\Icinga; +use \Icinga\Web\Form; +use \Icinga\Web\Form\Element\Note; +use \Icinga\Web\Form\Decorator\ConditionalHidden; +use \Zend_Config; +use \Zend_Form_Element_Text; + +/** + * Form class for setting the application wide logging configuration + * + */ +class LoggingForm extends Form +{ + /** + * The configuration to use for this form + * + * @var Zend_Config + */ + private $config = null; + + /** + * Set the configuration of this form + * + * If not called, default values are used instead + * + * @param Zend_Config $cfg The config.ini to set with this form + */ + public function setConfiguration(Zend_Config $cfg) + { + $this->config = $cfg; + } + + /** + * Return true if the debug log path textfield should be displayed + * + * This is the case if the "logging_use_debug" field is autosubmitted + * and true or if it is not submitted, but the configuration for debug + * logging is set to true + * + * @param Zend_Config $config The debug section of the config.ini + * + * @return bool Whether to display the debug path field or not + */ + private function shouldDisplayDebugLog(Zend_Config $config) + { + $debugParam = $this->getRequest()->getParam('logging_use_debug', null); + if ($debugParam !== null) { + return intval($debugParam) === 1; + } else { + return intval($config->get('enable', 0)) === 1; + } + + } + + private function loggingIsEnabled(Zend_Config $config) + { + $loggingRequestParam = $this->getRequest()->getParam('logging_enable', null); + + if ($loggingRequestParam !== null) { + return intval($loggingRequestParam) === 1; + } else { + return intval($config->get('enable', 0)) === 1; + } + } + + /** + * @see Form::create() + */ + public function create() + { + if ($this->config === null) { + $this->config = new Zend_Config(array()); + } + + $logging = $this->config->logging; + if ($logging === null) { + $logging = new IcingaConfig(array()); + } + + $debug = $logging->debug; + if ($debug === null) { + $debug = new IcingaConfig(array()); + } + $this->addElement( + 'checkbox', + 'logging_enable', + array( + 'label' => 'Logging enabled', + 'required' => true, + 'value' => $this->loggingIsEnabled($logging) + ) + ); + if (!$this->loggingIsEnabled($debug)) { + $this->addElement( + new Note( + array( + 'name' => 'note_logging_disabled', + 'value' => 'Logging is disabled.' + ) + ) + ); + $this->setSubmitLabel('Save changes'); + $this->enableAutoSubmit(array('logging_enable')); + + return; + } + $this->addDecorator(new ConditionalHidden()); + + $this->addElement( + 'text', + 'logging_app_path', + array( + 'label' => 'Application log path', + 'required' => true, + 'value' => $logging->get('target', '/var/log/icingaweb.log') + ) + ); + + $this->addElement(new Note( + array( + 'name' => 'note_logging_app_path', + 'value'=> 'The logfile to write the icingaweb debug logs to. The webserver must be able to write' + . 'at this location' + ) + )); + + $this->addElement( + 'checkbox', + 'logging_app_verbose', + array( + 'label' => 'Verbose logging', + 'required' => true, + 'value' => intval($logging->get('verbose', 0)) === 1 + ) + ); + + $this->addElement(new Note( + array( + 'name' => 'note_logging_app_verbose', + 'value'=> 'Check to write more verbose output to the icinga log file' + ) + )); + + $this->addElement( + 'checkbox', + 'logging_use_debug', + array( + 'label' => 'Use debug log', + 'required' => true, + 'value' => $this->shouldDisplayDebugLog($debug) + ) + ); + $this->addElement(new Note( + array( + 'name' => 'note_logging_use_debug', + 'value'=> 'Check to write a seperate debug log (Warning: This file can grow very big)' + ) + )); + + + $textLoggingDebugPath = new Zend_Form_Element_Text( + array( + 'name' => 'logging_debug_path', + 'label' => 'Debug log path', + 'required' => true, + 'condition' => $this->shouldDisplayDebugLog($debug), + 'value' => $debug->get('target', '/var/log/icingaweb.debug.log') + ) + ); + $loggingPathNote = new Note( + array( + 'name' => 'note_logging_debug_path', + 'value' => 'Set the path to the debug log', + 'condition' => $this->shouldDisplayDebugLog($debug) + ) + ); + + $this->addElement($textLoggingDebugPath); + $this->addElement($loggingPathNote); + + $this->enableAutoSubmit(array('logging_use_debug', 'logging_enable')); + + $this->setSubmitLabel('Save changes'); + } + +} \ No newline at end of file diff --git a/application/views/scripts/config/index.phtml b/application/views/scripts/config/index.phtml index ef3c458db..f0b046499 100644 --- a/application/views/scripts/config/index.phtml +++ b/application/views/scripts/config/index.phtml @@ -1,12 +1,2 @@ tabs->render($this); ?> - -

Configuration

- -
-

Dear developer

-
-

- Add shortcuts for often used configuration options so users can quickly change settings without having to - search in the tab overview -

-
\ No newline at end of file +form ?> \ No newline at end of file diff --git a/application/views/scripts/config/logging.phtml b/application/views/scripts/config/logging.phtml new file mode 100644 index 000000000..aa8ca9f49 --- /dev/null +++ b/application/views/scripts/config/logging.phtml @@ -0,0 +1,3 @@ +tabs->render($this); ?> + +form ?> \ No newline at end of file diff --git a/library/Icinga/Config/IniEditor.php b/library/Icinga/Config/IniEditor.php index afb89d44a..42dd60380 100644 --- a/library/Icinga/Config/IniEditor.php +++ b/library/Icinga/Config/IniEditor.php @@ -225,10 +225,11 @@ class IniEditor */ private function cleanUpWhitespaces() { + $i = count($this->text) - 1; for (; $i > 0; $i--) { $line = $this->text[$i]; - if ($this->isSectionDeclaration($line)) { + if ($this->isSectionDeclaration($line) && $i > 0) { $i--; $line = $this->text[$i]; /* diff --git a/library/Icinga/Web/Form/Decorator/ConditionalHidden.php b/library/Icinga/Web/Form/Decorator/ConditionalHidden.php new file mode 100644 index 000000000..7dc54a8fe --- /dev/null +++ b/library/Icinga/Web/Form/Decorator/ConditionalHidden.php @@ -0,0 +1,65 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + + +/** + * Decorator to hide elements using a >noscript< tag instead of + * type='hidden' or css styles. + * + * This allows to hide depending elements for browsers with javascript + * (who can then automatically refresh their pages) but show them in + * case JavaScript is disabled + */ + +namespace Icinga\Web\Form\Decorator; + +use \Zend_Form_Decorator_Abstract; + +class ConditionalHidden extends Zend_Form_Decorator_Abstract +{ + /** + * Generates a html number input + * + * @access public + * + * @param string $name The element name. + * @param string $value The default value. + * @param array $attribs Attributes which should be added to the input tag. + * + * @return string The input tag and options XHTML. + */ + public function render($content ='') + { + $attributes = $this->getElement()->getAttribs(); + $condition = isset($attributes['condition']) ? $attributes['condition'] : 1; + if ($condition != 1) { + $content = ''; + } + return $content; + } +} diff --git a/modules/monitoring/application/controllers/ConfigController.php b/modules/monitoring/application/controllers/ConfigController.php index fbc0a52f5..e92e28f8f 100644 --- a/modules/monitoring/application/controllers/ConfigController.php +++ b/modules/monitoring/application/controllers/ConfigController.php @@ -38,7 +38,6 @@ class Monitoring_ConfigController extends BaseConfigController { return array( "backends" => new Tab(array( "name" => "backends", - "iconCls" => "hdd", "title" => "Monitoring Backends", "url" => Url::fromPath("/monitoring/config/backend") )) From 9ddc03d571aeb17ca6ce01a07bbbd9161caa8571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannis=20Mo=C3=9Fhammer?= Date: Wed, 14 Aug 2013 14:52:47 +0200 Subject: [PATCH 02/20] Implement General configuration form Missing: - Logical validation (check for writable paths) - DB Resource selection (see #4503) refs #3777 --- application/controllers/ConfigController.php | 7 +- application/forms/Config/GeneralForm.php | 105 +++++++++++++------ application/forms/Config/LoggingForm.php | 8 +- library/Icinga/Web/Form.php | 6 +- library/Icinga/Web/Form/Element/Note.php | 5 + 5 files changed, 92 insertions(+), 39 deletions(-) diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index 88ea60639..b2d0a7b2e 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -86,9 +86,12 @@ class ConfigController extends BaseConfigController if ($form->isSubmittedAndValid()) { $cfg = IcingaConfig::app()->getConfigFile(); $writer = new PreservingIniWriter( - array('config' => $form->getConfig(),'filename' => $cfg) + array( + 'config' => $form->getConfig(), + 'filename' => $cfg + ) ); - print_r($writer->render());die(); + $writer->write(); } $this->view->form = $form; } diff --git a/application/forms/Config/GeneralForm.php b/application/forms/Config/GeneralForm.php index 4a416b48d..bb9858b87 100644 --- a/application/forms/Config/GeneralForm.php +++ b/application/forms/Config/GeneralForm.php @@ -27,41 +27,17 @@ // {{{ICINGA_LICENSE_HEADER}}} namespace Icinga\Form\Config; -/** - * [global] -environment = development -timezone = "Europe/Berlin" -indexModule = monitoring -indexController = dashboard -moduleFolder = "/etc/icinga2-web/enabledModules" -dateFormat = "d/m/Y" -timeFormat = "g:i A" -[logging] -; General log -enable = 1 -type = stream -verbose = 1 -target = /tmp/icinga2.log - -; For development and debug purposes: Logs additional (non critical) events to a -; seperate log -debug.enable = 1 -debug.type = stream -debug.target = /tmp/icinga2.debug.log - -; Use ini store to store preferences on local disk -[preferences] -type=ini - - */ use \Icinga\Application\Config as IcingaConfig; -use Icinga\Application\Icinga; +use \Icinga\Application\Icinga; use \Icinga\Web\Form; -use \Zend_Form_Element_Radio; +use \Icinga\Web\Form\Decorator\ConditionalHidden; +use \Icinga\Web\Form\Element\Note; + use \DateTimeZone; use \Zend_Config; -use Icinga\Web\Form\Element\Note; +use \Zend_Form_Element_Text; + /** * Configuration form for general, application-wide settings @@ -231,6 +207,56 @@ class GeneralForm extends Form ))); } + public function addUserPreferencesDialog(Zend_Config $cfg) + { + $backend = $cfg->get('type', 'ini'); + if ($this->getRequest()->get('preferences_type', null) !== null) { + $backend = $this->getRequest()->get('preferences_type'); + } + $this->addElement( + 'select', + 'preferences_type', + array( + 'label' => 'User preference storage type', + 'required' => true, + 'value' => $backend, + 'multiOptions' => array( + 'ini' => 'File system (ini files)', + 'db' => 'Database' + ) + ) + ); + + $txtPreferencesIniPath = new Zend_Form_Element_Text( + array( + 'name' => 'preferences_ini_path', + 'label' => 'Path to store user preference files', + 'required' => $backend === 'ini', + 'condition' => $backend === 'ini', + 'value' => $cfg->get('configPath') + ) + ); + + $txtPreferencesDbResource = new Zend_Form_Element_Text( + array( + 'name' => 'preferences_db_resource', + 'label' => 'Database connection (TODO: Make select field)', + 'required' => $backend === 'db', + 'condition' => $backend === 'db', + 'value' => $cfg->get('resource') + ) + ); + + $this->addElement($txtPreferencesIniPath); + $this->addElement($txtPreferencesDbResource); + + $txtPreferencesIniPath->addDecorator(new ConditionalHidden()); + $txtPreferencesDbResource->addDecorator(new ConditionalHidden()); + $this->enableAutoSubmit(array( + 'preferences_type' + )); + } + /** * Create the general form, using the provided configuration * @@ -245,11 +271,17 @@ class GeneralForm extends Form if ($global === null) { $global = new Zend_Config(array()); } + $preferences = $this->config->preferences; + if ($preferences === null) { + $preferences = new Zend_Config(array()); + } $this->addDevelopmentCheckbox($global); $this->addTimezoneSelection($global); $this->addModuleSettings($global); $this->addDateFormatSettings($global); + $this->addUserPreferencesDialog($preferences); + $this->setSubmitLabel('Save changes'); } @@ -259,10 +291,12 @@ class GeneralForm extends Form if ($this->config === null) { $this->config = new Zend_Config(array()); } - $global = $this->config->global; - if ($global === null) { + if ($this->config->global === null) { $this->config->global = new Zend_Config(array()); } + if ($this->config->preferences === null) { + $this->config->preferences = new Zend_Config(array()); + } $values = $this->getValues(); $cfg = clone $this->config; @@ -272,6 +306,13 @@ class GeneralForm extends Form $cfg->global->dateFormat = $values['date_format']; $cfg->global->timeFormat = $values['time_format']; + $cfg->preferences->type = $values['preferences_type']; + if ($cfg->preferences->type === 'ini') { + $cfg->preferences->configPath = $values['preferences_ini_path']; + } elseif ($cfg->preferences->type === 'db') { + $cfg->preferences->resource = $values['preferences_db_resource']; + } + return $cfg; } diff --git a/application/forms/Config/LoggingForm.php b/application/forms/Config/LoggingForm.php index 952ae1c61..107e75ff2 100644 --- a/application/forms/Config/LoggingForm.php +++ b/application/forms/Config/LoggingForm.php @@ -135,7 +135,6 @@ class LoggingForm extends Form return; } - $this->addDecorator(new ConditionalHidden()); $this->addElement( 'text', @@ -195,7 +194,7 @@ class LoggingForm extends Form 'label' => 'Debug log path', 'required' => true, 'condition' => $this->shouldDisplayDebugLog($debug), - 'value' => $debug->get('target', '/var/log/icingaweb.debug.log') + 'value' => $debug->get('target') ) ); $loggingPathNote = new Note( @@ -205,10 +204,13 @@ class LoggingForm extends Form 'condition' => $this->shouldDisplayDebugLog($debug) ) ); - + $decorator = new ConditionalHidden(); $this->addElement($textLoggingDebugPath); $this->addElement($loggingPathNote); + $textLoggingDebugPath->addDecorator($decorator); + $loggingPathNote->addDecorator($decorator); + $this->enableAutoSubmit(array('logging_use_debug', 'logging_enable')); $this->setSubmitLabel('Save changes'); diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 93873e4fd..593977d40 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -25,13 +25,14 @@ namespace Icinga\Web; +use \Icinga\Web\Form\Decorator\ConditionalHidden; use \Zend_Controller_Request_Abstract; use \Zend_Form_Element_Submit; use \Zend_Form_Element_Reset; use \Zend_View_Interface; use \Zend_Form; -use Icinga\Exception\ProgrammingError; -use Icinga\Web\Form\InvalidCSRFTokenException; +use \Icinga\Exception\ProgrammingError; +use \Icinga\Web\Form\InvalidCSRFTokenException; /** * Base class for forms providing CSRF protection, confirmation logic and auto submission @@ -179,6 +180,7 @@ abstract class Form extends Zend_Form */ public function buildForm() { + if ($this->created === false) { $this->initCsrfToken(); $this->create(); diff --git a/library/Icinga/Web/Form/Element/Note.php b/library/Icinga/Web/Form/Element/Note.php index 77ec2a785..3d8af596b 100644 --- a/library/Icinga/Web/Form/Element/Note.php +++ b/library/Icinga/Web/Form/Element/Note.php @@ -40,4 +40,9 @@ class Note extends Zend_Form_Element_Xhtml * @var string */ public $helper = 'formNote'; + + public function isValid($value) + { + return true; + } } From 17c1e5a4493747ec759def7d76a62c1f57498a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannis=20Mo=C3=9Fhammer?= Date: Thu, 15 Aug 2013 10:56:17 +0200 Subject: [PATCH 03/20] Add tests, move BaseFormTest away from modules/monitoring refs #3777 --- application/forms/Config/GeneralForm.php | 2 +- .../Backend/Livestatus/Query/StatusQuery.php | 34 +--- .../forms/Command/AcknowledgeFormTest.php | 3 +- .../forms/Command/CommentFormTest.php | 3 +- .../forms/Command/ConfirmationFormTest.php | 4 +- .../ConfirmationWithIdentifierFormTest.php | 3 +- .../Command/CustomNotificationFormTest.php | 3 +- .../Command/DelayNotificationFormTest.php | 3 +- .../Command/RescheduleNextCheckFormTest.php | 3 +- .../Command/ScheduleDowntimeFormTest.php | 3 +- .../Command/SubmitPassiveCheckResultTest.php | 3 +- .../forms/Config/GeneralFormTest.php | 161 ++++++++++++++++++ .../library/Icinga/Web/Form}/BaseFormTest.php | 26 ++- 13 files changed, 203 insertions(+), 48 deletions(-) create mode 100644 test/php/application/forms/Config/GeneralFormTest.php rename {modules/monitoring/test/php/application/forms/Command => test/php/library/Icinga/Web/Form}/BaseFormTest.php (66%) diff --git a/application/forms/Config/GeneralForm.php b/application/forms/Config/GeneralForm.php index bb9858b87..671d9694d 100644 --- a/application/forms/Config/GeneralForm.php +++ b/application/forms/Config/GeneralForm.php @@ -64,7 +64,7 @@ class GeneralForm extends Form * * @param IcingaConfig $cfg */ - public function setConfiguration(IcingaConfig $cfg) + public function setConfiguration($cfg) { $this->config = $cfg; } diff --git a/modules/monitoring/library/Monitoring/Backend/Livestatus/Query/StatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Livestatus/Query/StatusQuery.php index ece3c02c0..cc1be7363 100644 --- a/modules/monitoring/library/Monitoring/Backend/Livestatus/Query/StatusQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Livestatus/Query/StatusQuery.php @@ -1,6 +1,6 @@ 'host_accept_passive_checks', 'host_last_state_change', - 'host_problems' => 'is_flapping', - 'service_in_downtime' => 'is_flapping', - 'service_handled' => 'is_flapping', - // Service config 'service_description' => 'description', 'service_display_name' => 'display_name', @@ -41,34 +37,12 @@ class StatusQuery extends AbstractQuery 'service_last_state_change' => 'last_state_change', // Service comments - //'comments_with_info', - //'downtimes_with_info', + 'comments_with_info', + 'downtimes_with_info', ); - public function init() - { - $this->query = $this->createQuery(); - //print_r($this->ds->getConnection()->fetchAll($this->query)); - //die('asdf'); - } - - public function count() - { - return $this->ds->getConnection()->count($this->query); - } - - public function fetchAll() - { - return $this->ds->getConnection()->fetchAll($this->query); - } - - public function fetchRow() - { - return array_shift($this->ds->getConnection()->fetchAll($this->query)); - } - protected function createQuery() { - return $this->ds->getConnection()->select()->from('services', $this->available_columns); + return $this->connection->getConnection()->select()->from('services', $this->available_columns); } } diff --git a/modules/monitoring/test/php/application/forms/Command/AcknowledgeFormTest.php b/modules/monitoring/test/php/application/forms/Command/AcknowledgeFormTest.php index 1e0e08f44..981d900cb 100644 --- a/modules/monitoring/test/php/application/forms/Command/AcknowledgeFormTest.php +++ b/modules/monitoring/test/php/application/forms/Command/AcknowledgeFormTest.php @@ -5,7 +5,7 @@ namespace Test\Monitoring\Forms\Command; -require_once realpath(__DIR__ . '/BaseFormTest.php'); +require_once realpath('library/Icinga/Web/Form/BaseFormTest.php'); require_once realpath(__DIR__ . '/../../../../../../../modules/monitoring/application/forms/Command/AcknowledgeForm.php'); require_once realpath(__DIR__ . '/../../../../../../../library/Icinga/Util/ConfigAwareFactory.php'); require_once realpath(__DIR__ . '/../../../../../../../library/Icinga/Util/DateTimeFactory.php'); @@ -13,6 +13,7 @@ require_once realpath(__DIR__ . '/../../../../../../../library/Icinga/Util/DateT use \DateTimeZone; use \Monitoring\Form\Command\AcknowledgeForm; // Used by constant FORMCLASS use \Icinga\Util\DateTimeFactory; +use \Test\Icinga\Web\Form\BaseFormTest; class AcknowledgeFormTest extends BaseFormTest { diff --git a/modules/monitoring/test/php/application/forms/Command/CommentFormTest.php b/modules/monitoring/test/php/application/forms/Command/CommentFormTest.php index 354aad114..e740b7101 100644 --- a/modules/monitoring/test/php/application/forms/Command/CommentFormTest.php +++ b/modules/monitoring/test/php/application/forms/Command/CommentFormTest.php @@ -5,10 +5,11 @@ namespace Test\Monitoring\Forms\Command; -require_once realpath(__DIR__ . '/BaseFormTest.php'); +require_once realpath('library/Icinga/Web/Form/BaseFormTest.php'); require_once realpath(__DIR__ . '/../../../../../../../modules/monitoring/application/forms/Command/CommentForm.php'); use \Monitoring\Form\Command\CommentForm; // Used by constant FORMCLASS +use \Test\Icinga\Web\Form\BaseFormTest; class CommentFormTest extends BaseFormTest { diff --git a/modules/monitoring/test/php/application/forms/Command/ConfirmationFormTest.php b/modules/monitoring/test/php/application/forms/Command/ConfirmationFormTest.php index 70e8b64c6..53434bb0a 100644 --- a/modules/monitoring/test/php/application/forms/Command/ConfirmationFormTest.php +++ b/modules/monitoring/test/php/application/forms/Command/ConfirmationFormTest.php @@ -1,7 +1,7 @@ + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Test\Icinga\Form\Config; + + +require_once('Zend/Config.php'); +require_once('Zend/Config/Ini.php'); +require_once(realpath('library/Icinga/Web/Form/BaseFormTest.php')); +require_once(realpath('../../application/forms/Config/GeneralForm.php')); + +use Test\Icinga\Web\Form\BaseFormTest; +use \Icinga\Web\Form; +use \DOMDocument; +use \Zend_Config; +use \Zend_View; + +class GeneralFormTest extends \Test\Icinga\Web\Form\BaseFormTest +{ + + private function isHiddenElement($value, $htmlString) + { + $html = new DOMDocument(); + $html->loadHTML($htmlString); + $hidden = $html->getElementsByTagName('noscript'); + + foreach($hidden as $node) { + foreach($node->childNodes as $child) { + if ($child->hasAttributes() === false) { + continue; + } + if (strpos($child->attributes->item(0)->value, $value) !== false) { + return true; + } + } + } + return false; + } + /** + * + */ + public function testCorrectFieldPopulation() + { + date_default_timezone_set('UTC'); + $form = $this->getRequestForm(array(), 'Icinga\Form\Config\GeneralForm'); + $form->setConfiguration( + new Zend_Config( + array( + 'global' => array( + 'environment' => 'development', + 'timezone' => 'Europe/Berlin', + 'indexModule' => 'monitoring', + 'indexController' => 'dashboard', + 'moduleFolder' => '/my/module/path', + 'dateFormat' => 'd-m/Y', + 'timeFormat' => 'A:i' + ), + 'preferences' => array( + 'type' => 'ini', + 'configPath' => './my/path' + ) + ) + ) + ); + + $form->setConfigDir('/tmp'); + $view = new Zend_View(); + + $form->create(); + $this->assertEquals(1, $form->getValue('environment'), 'Asserting the checkbox for devlopment being set to true'); + $this->assertEquals('Europe/Berlin', $form->getValue('timezone'), 'Asserting the correct timezone to be displayed'); + $this->assertEquals('/my/module/path', $form->getValue('module_folder'), 'Asserting the correct module folder to be set'); + $this->assertEquals('d-m/Y', $form->getValue('date_format'), 'Asserting the correct data format to be set'); + $this->assertEquals('A:i', $form->getValue('time_format'), 'Asserting the correct time to be set'); + $this->assertEquals('ini', $form->getValue('preferences_type'), 'Asserting the correct preference type to be set'); + $this->assertEquals('./my/path', $form->getValue('preferences_ini_path'), 'Asserting the correct ini path to be set'); + $this->assertEquals('', $form->getValue('preferences_db_resource'), 'Asserting the database resource not to be set'); + } + + public function testCorrectConditionalIniFieldRendering() + { + date_default_timezone_set('UTC'); + $form = $this->getRequestForm(array(), 'Icinga\Form\Config\GeneralForm'); + $form->setConfiguration( + new Zend_Config( + array( + 'preferences' => array( + 'type' => 'ini', + 'configPath' => './my/path' + ) + ) + ) + ); + $form->setConfigDir('/tmp'); + $form->create(); + $view = new Zend_View(); + + $this->assertFalse( + $this->isHiddenElement('preferences_ini_path', $form->render($view)), + "Asserting the ini path field to be displayed when an ini preference is set" + ); + $this->assertTrue( + $this->isHiddenElement('preferences_db_resource', $form->render($view)), + "Asserting the db resource to be hidden when an ini preference is set" + ); + } + + public function testCorrectConditionalDbFieldRendering() + { + date_default_timezone_set('UTC'); + $form = $this->getRequestForm(array(), 'Icinga\Form\Config\GeneralForm'); + $form->setConfiguration( + new Zend_Config( + array( + 'preferences' => array( + 'type' => 'db', + 'configPath' => './my/path', + 'resource' => 'my_resource' + ) + ) + ) + ); + $form->setConfigDir('/tmp'); + $form->create(); + $view = new Zend_View(); + + $this->assertTrue( + $this->isHiddenElement('preferences_ini_path', $form->render($view)), + "Asserting the ini path field to be hidden when db preference is set" + ); + $this->assertFalse( + $this->isHiddenElement('preferences_ini_resource', $form->render($view)), + "Asserting the db resource to be displayed when db preference is set" + ); + } +} \ No newline at end of file diff --git a/modules/monitoring/test/php/application/forms/Command/BaseFormTest.php b/test/php/library/Icinga/Web/Form/BaseFormTest.php similarity index 66% rename from modules/monitoring/test/php/application/forms/Command/BaseFormTest.php rename to test/php/library/Icinga/Web/Form/BaseFormTest.php index aaba6575c..1cde68e66 100644 --- a/modules/monitoring/test/php/application/forms/Command/BaseFormTest.php +++ b/test/php/library/Icinga/Web/Form/BaseFormTest.php @@ -1,4 +1,8 @@ assertTrue(true); + } } } From 5a768ccaa9cafd0f40f4b76e2e7b8dc58f5e4e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannis=20Mo=C3=9Fhammer?= Date: Thu, 15 Aug 2013 14:27:53 +0200 Subject: [PATCH 04/20] Start authentication form refs #3777 --- application/controllers/ConfigController.php | 19 ++ .../forms/Config/AuthenticationForm.php | 279 ++++++++++++++++++ application/forms/Config/GeneralForm.php | 58 +++- .../views/scripts/config/authentication.phtml | 2 + 4 files changed, 350 insertions(+), 8 deletions(-) create mode 100644 application/forms/Config/AuthenticationForm.php create mode 100644 application/views/scripts/config/authentication.phtml diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index b2d0a7b2e..5e69c219b 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -32,6 +32,7 @@ use \Icinga\Web\Url; use \Icinga\Web\Hook\Configuration\ConfigurationTabBuilder; use \Icinga\Application\Icinga; use \Icinga\Form\Config\GeneralForm; +use \Icinga\Form\Config\AuthenticationForm; use \Icinga\Form\Config\LoggingForm; use \Icinga\Config\PreservingIniWriter; @@ -57,6 +58,15 @@ class ConfigController extends BaseConfigController "url" => Url::fromPath("/config") ) ), + + "authentication" => new Tab( + array( + "name" => "auth", + "title" => "Authentication", + "url" => Url::fromPath('/config/authentication') + ) + ), + "logging" => new Tab( array( "name" => "logging", @@ -96,6 +106,15 @@ class ConfigController extends BaseConfigController $this->view->form = $form; } + public function authenticationAction() + { + $form = new AuthenticationForm(); + $form->setConfiguration(IcingaConfig::app('authentication')); + $form->setRequest($this->_request); + $form->isSubmittedAndValid(); + $this->view->form = $form; + } + public function loggingAction() { $form = new LoggingForm(); diff --git a/application/forms/Config/AuthenticationForm.php b/application/forms/Config/AuthenticationForm.php new file mode 100644 index 000000000..d835a956f --- /dev/null +++ b/application/forms/Config/AuthenticationForm.php @@ -0,0 +1,279 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Form\Config; + +use \Icinga\Application\Config as IcingaConfig; +use \Icinga\Application\Icinga; +use \Icinga\Application\Logger; +use \Icinga\Application\DbAdapterFactory; + +use \Icinga\Web\Form; +use \Icinga\Web\Form\Element\Note; +use \Icinga\Web\Form\Decorator\ConditionalHidden; +use \Zend_Config; +use \Zend_Form_Element_Text; +use \Zend_Form_Element_Select; + +class AuthenticationForm extends Form +{ + /** + * The configuration to use for populating this form + * + * @var IcingaConfig + */ + private $config = null; + + /** + * The resources to use instead of the factory provided ones (use for testing) + * + * @var null + */ + private $resources = null; + + /** + * Set an alternative array of resources that should be used instead of the DBFactory resource set + * (used for testing) + * + * @param array $resources The resources to use for populating the db selection field + */ + public function setResources(array $resources) + { + $this->resources = $resources; + } + + /** + * Return content of the resources.ini or previously set resources for displaying in the database selection field + * + * @return array + */ + public function getResources() + { + if ($this->resources === null ) { + return DbAdapterFactory::getResources(); + } else { + return $this->resources; + } + } + + /** + * Set the configuration to be used for this form + * + * @param IcingaConfig $cfg + */ + public function setConfiguration($cfg) + { + $this->config = $cfg; + } + + private function addProviderFormForDb($name, $backend) + { + + $backends = array(); + foreach ($this->getResources() as $resname => $resource) + { + if ($resource['type'] !== 'db') { + continue; + } + $backends[$resname] = $resname; + } + + $this->addElement( + 'select', + 'backend_' . $name . '_resource', + array( + 'label' => 'Database connection', + 'required' => true, + 'value' => $backend->get('resource'), + 'multiOptions' => $backends + ) + ); + + + $this->addElement( + 'submit', + 'backend_' . $name . '_remove', + array( + 'label' => 'Remove this backend', + 'required' => true + ) + ); + + $this->addDisplayGroup( + array( + 'backend_' . $name . '_resource', + 'backend_' . $name . '_remove' + ), + 'auth_provider_' . $name, + array( + 'legend' => 'DB Authentication ' . $name + ) + ); + } + + private function addProviderFormForLdap($name, $backend) + { + $this->addElement( + 'text', + 'backend_' . $name . '_hostname', + array( + 'label' => 'LDAP server host', + 'value' => $backend->get('hostname', 'localhost'), + 'required' => true + ) + ); + + $this->addElement( + 'text', + 'backend_' . $name . '_root_dn', + array( + 'label' => 'LDAP root dn', + 'value' => $backend->get('hostname', 'ou=people,dc=icinga,dc=org'), + 'required' => true + ) + ); + + $this->addElement( + 'text', + 'backend_' . $name . '_bind_dn', + array( + 'label' => 'LDAP bind dn', + 'value' => $backend->get('bind_dn', 'cn=admin,cn=config'), + 'required' => true + ) + ); + + $this->addElement( + 'password', + 'backend_' . $name . '_bind_pw', + array( + 'label' => 'LDAP bind password', + 'value' => $backend->get('bind_pw', 'admin'), + 'required' => true + ) + ); + + $this->addElement( + 'text', + 'backend_' . $name . '_bind_user_class', + array( + 'label' => 'LDAP user object class', + 'value' => $backend->get('user_class', 'inetOrgPerson'), + 'required' => true + ) + ); + + $this->addElement( + 'text', + 'backend_' . $name . '_bind_user_name_attribute', + array( + 'label' => 'LDAP user name attribute', + 'value' => $backend->get('user_name_attribute', 'uid'), + 'required' => true + ) + ); + + $this->addElement( + 'submit', + 'backend_' . $name . '_remove', + array( + 'label' => 'Remove this backend' + ) + ); + + $this->addDisplayGroup( + array( + 'backend_' . $name . '_hostname', + 'backend_' . $name . '_root_dn', + 'backend_' . $name . '_bind_dn', + 'backend_' . $name . '_bind_pw', + 'backend_' . $name . '_bind_user_class', + 'backend_' . $name . '_bind_user_name_attribute', + 'backend_' . $name . '_remove' + ), + 'auth_provider_' . $name, + array( + 'legend' => 'LDAP Authentication ' . $name + ) + ); + } + + + public function addPriorityButtons($name, $pos) + { + if ($pos > 0) { + $this->addElement( + 'submit', + 'priority_change_'.$name.'_down', + array( + 'label' => 'Move up in authentication order', + 'value' => $pos-1 + ) + ); + } + if ($pos+1 < count($this->config->keys())) { + $this->addElement( + 'submit', + 'priority_change_'.$name.'_up', + array( + 'label' => 'Move down in authentication order', + 'value' => $pos+1 + ) + ); + } + } + + public function create() + { + $this->addElement( + 'submit', + 'add_backend', + array( + 'label' => 'Add a new authentication provider', + 'class' => 'btn' + ) + ); + $pos = 0; + foreach ($this->config as $name => $backend) { + + $type = strtolower($backend->get('backend')); + if ($type === 'db') { + $this->addProviderFormForDb($name, $backend); + } elseif ($type === 'ldap') { + $this->addProviderFormForLdap($name, $backend); + } else { + Logger::error('Unsupported backend found in authentication configuration: ' . $backend->get('backend')); + continue; + } + $this->addPriorityButtons($name, $pos); + + $pos++; + } + $this->setSubmitLabel('Save changes'); + } +} \ No newline at end of file diff --git a/application/forms/Config/GeneralForm.php b/application/forms/Config/GeneralForm.php index 671d9694d..3d83272cd 100644 --- a/application/forms/Config/GeneralForm.php +++ b/application/forms/Config/GeneralForm.php @@ -30,6 +30,7 @@ namespace Icinga\Form\Config; use \Icinga\Application\Config as IcingaConfig; use \Icinga\Application\Icinga; +use \Icinga\Application\DbAdapterFactory; use \Icinga\Web\Form; use \Icinga\Web\Form\Decorator\ConditionalHidden; use \Icinga\Web\Form\Element\Note; @@ -37,7 +38,7 @@ use \Icinga\Web\Form\Element\Note; use \DateTimeZone; use \Zend_Config; use \Zend_Form_Element_Text; - +use \Zend_Form_Element_Select; /** * Configuration form for general, application-wide settings @@ -59,6 +60,14 @@ class GeneralForm extends Form */ private $configDir = null; + + /** + * The resources to use instead of the factory provided ones (use for testing) + * + * @var null + */ + private $resources = null; + /** * Set the configuration to be used for this form * @@ -90,6 +99,31 @@ class GeneralForm extends Form return $this->configDir === null ? IcingaConfig::$configDir : $this->configDir; } + /** + * Set an alternative array of resources that should be used instead of the DBFactory resource set + * (used for testing) + * + * @param array $resources The resources to use for populating the db selection field + */ + public function setResources(array $resources) + { + $this->resources = $resources; + } + + /** + * Return content of the resources.ini or previously set resources for displaying in the database selection field + * + * @return array + */ + public function getResources() + { + if ($this->resources === null ) { + return DbAdapterFactory::getResources(); + } else { + return $this->resources; + } + } + /** * Add the checkbox for using the development environment to this form * @@ -236,14 +270,23 @@ class GeneralForm extends Form 'value' => $cfg->get('configPath') ) ); + $backends = array(); + foreach ($this->getResources() as $name => $resource) + { + if ($resource['type'] !== 'db') { + continue; + } + $backends[$name] = $name; + } - $txtPreferencesDbResource = new Zend_Form_Element_Text( + $txtPreferencesDbResource = new Zend_Form_Element_Select( array( - 'name' => 'preferences_db_resource', - 'label' => 'Database connection (TODO: Make select field)', - 'required' => $backend === 'db', - 'condition' => $backend === 'db', - 'value' => $cfg->get('resource') + 'name' => 'preferences_db_resource', + 'label' => 'Database connection', + 'required' => $backend === 'db', + 'condition' => $backend === 'db', + 'value' => $cfg->get('resource'), + 'multiOptions' => $backends ) ); @@ -282,7 +325,6 @@ class GeneralForm extends Form $this->addDateFormatSettings($global); $this->addUserPreferencesDialog($preferences); - $this->setSubmitLabel('Save changes'); } diff --git a/application/views/scripts/config/authentication.phtml b/application/views/scripts/config/authentication.phtml new file mode 100644 index 000000000..f0b046499 --- /dev/null +++ b/application/views/scripts/config/authentication.phtml @@ -0,0 +1,2 @@ +tabs->render($this); ?> +form ?> \ No newline at end of file From c705f5d475dd0739ff886406d8f8ac655a219f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannis=20Mo=C3=9Fhammer?= Date: Fri, 16 Aug 2013 16:24:12 +0200 Subject: [PATCH 05/20] Implement Authentication form - Allow creation of authentication providers - Allow modification of authentication providers - Allow reordering of authentication providers refs #3777 --- application/controllers/ConfigController.php | 71 +++- .../Config/Authentication/BaseBackendForm.php | 140 +++++++ .../Config/Authentication/DbBackendForm.php | 122 ++++++ .../Config/Authentication/LdapBackendForm.php | 157 ++++++++ .../forms/Config/AuthenticationForm.php | 356 +++++++++--------- application/forms/Config/GeneralForm.php | 99 +++-- application/forms/Config/LoggingForm.php | 45 ++- .../views/scripts/config/authentication.phtml | 18 + .../authentication/show-configuration.phtml | 30 ++ library/Icinga/Web/Form.php | 1 + .../Web/Form/Decorator/ConditionalHidden.php | 2 +- 11 files changed, 813 insertions(+), 228 deletions(-) create mode 100644 application/forms/Config/Authentication/BaseBackendForm.php create mode 100644 application/forms/Config/Authentication/DbBackendForm.php create mode 100644 application/forms/Config/Authentication/LdapBackendForm.php create mode 100644 application/views/scripts/config/authentication/show-configuration.phtml diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index 5e69c219b..fb8e63f34 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -33,6 +33,8 @@ use \Icinga\Web\Hook\Configuration\ConfigurationTabBuilder; use \Icinga\Application\Icinga; use \Icinga\Form\Config\GeneralForm; use \Icinga\Form\Config\AuthenticationForm; +use \Icinga\Form\Config\Authentication\LdapBackendForm; +use \Icinga\Form\Config\Authentication\DbBackendForm; use \Icinga\Form\Config\LoggingForm; use \Icinga\Config\PreservingIniWriter; @@ -106,14 +108,7 @@ class ConfigController extends BaseConfigController $this->view->form = $form; } - public function authenticationAction() - { - $form = new AuthenticationForm(); - $form->setConfiguration(IcingaConfig::app('authentication')); - $form->setRequest($this->_request); - $form->isSubmittedAndValid(); - $this->view->form = $form; - } + public function loggingAction() { @@ -156,6 +151,66 @@ class ConfigController extends BaseConfigController $this->redirectNow('config/moduleoverview?_render=body'); } + private function writeAuthenticationFile(array $config) + { + $cfg = new Zend_Config($config); + $writer = new Zend_Config_Writer_Ini( + array( + 'config' => $cfg, + 'filename' => IcingaConfig::app('authentication')->getConfigFile() + ) + ); + try { + $writer->write(); + } catch (Exception $exc) { + $this->view->exceptionMessage = $exc->getMessage(); + $this->view->iniConfigurationString = $writer->render(); + $this->render('authentication/show-configuration'); + } + } + + /** + * Action for creating a new authentication backend + * + */ + public function authenticationAction() + { + $form = new AuthenticationForm(); + $config = IcingaConfig::app('authentication'); + $form->setConfiguration($config); + $form->setRequest($this->_request); + + if ($form->isSubmittedAndValid()) { + $this->writeAuthenticationFile($form->getConfig()); + } + $this->view->form = $form; + + } + + /** + * Action for creating a new authentication backend + * + */ + public function createauthenticationbackendAction() + { + if ($this->getRequest()->getParam('type') === 'ldap') { + $form = new LdapBackendForm(); + } else { + $form = new DbBackendForm(); + } + $form->setRequest($this->getRequest()); + if ($form->isSubmittedAndValid()) { + $backendCfg = IcingaConfig::app('authentication')->toArray(); + + foreach ($form->getConfig() as $backendName => $settings) { + $backendCfg[$backendName] = $settings; + } + + $this->writeAuthenticationFile($backendCfg); + } + $this->view->form = $form; + $this->render('authentication/modify'); + } } // @codingStandardsIgnoreEnd diff --git a/application/forms/Config/Authentication/BaseBackendForm.php b/application/forms/Config/Authentication/BaseBackendForm.php new file mode 100644 index 000000000..1844d4d47 --- /dev/null +++ b/application/forms/Config/Authentication/BaseBackendForm.php @@ -0,0 +1,140 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + + +namespace Icinga\Form\Config\Authentication; + +use \Icinga\Application\Config as IcingaConfig; +use \Icinga\Application\Icinga; +use \Icinga\Application\Logger; +use \Icinga\Application\DbAdapterFactory; +use \Icinga\Web\Form; +use \Zend_Config; + +/** + * Base form for authentication backend forms + * + */ +abstract class BaseBackendForm extends Form +{ + /** + * The name of the backend currently displayed in this form + * + * Will be the section in the authentication.ini file + * + * @var string + */ + private $backendName = ""; + + /** + * The backend configuration as a Zend_Config object + * + * @var Zend_Config + */ + private $backend = null; + + /** + * The resources to use instead of the factory provided ones (use for testing) + * + * @var Zend_Config + */ + private $resources = null; + + /** + * Set the name of the currently displayed backend + * + * @param string $name The name to be stored as the section when persisting + */ + public function setBackendName($name) + { + $this->backendName = $name; + } + + /** + * Return the backend name of this form + * + * @return string + */ + public function getBackendName() + { + return $this->backendName; + } + + /** + * Return the backend configuration or a empty Zend_Config object if none is given + * + * @return Zend_Config + */ + public function getBackend() + { + return ($this->backend !== null) ? $this->backend : new Zend_Config(array()); + } + + /** + * Set the backend configuration for initial population + * + * @param Zend_Config $backend The backend to display in this form + */ + public function setBackend(Zend_Config $backend) + { + $this->backend = $backend; + } + + /** + * Set an alternative array of resources that should be used instead of the DBFactory resource set + * (used for testing) + * + * @param array $resources The resources to use for populating the db selection field + */ + public function setResources(array $resources) + { + $this->resources = $resources; + } + + /** + * Return content of the resources.ini or previously set resources for displaying in the database selection field + * + * @return array + */ + public function getResources() + { + if ($this->resources === null) { + return DbAdapterFactory::getResources(); + } else { + return $this->resources; + } + } + + /** + * Return an array containing all sections defined by this form as the key and all settings + * as an keyvalue subarray + * + * @return array + */ + abstract public function getConfig(); +} diff --git a/application/forms/Config/Authentication/DbBackendForm.php b/application/forms/Config/Authentication/DbBackendForm.php new file mode 100644 index 000000000..de2b0464f --- /dev/null +++ b/application/forms/Config/Authentication/DbBackendForm.php @@ -0,0 +1,122 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + + +namespace Icinga\Form\Config\Authentication; + + +use \Icinga\Application\Config as IcingaConfig; +use \Icinga\Application\Icinga; +use \Icinga\Application\Logger; +use \Icinga\Application\DbAdapterFactory; + +use \Icinga\Web\Form; +use \Zend_Config; + +/** + * Form class for adding/modifying database authentication backends + * + */ +class DbBackendForm extends BaseBackendForm +{ + + /** + * Return a list of all database resource ready to be used as the multiOptions + * attribute in a Zend_Form_Element_Select object + * + * @return array + */ + private function getDatabaseResources() + { + $backends = array(); + foreach ($this->getResources() as $resname => $resource) { + if ($resource['type'] !== 'db') { + continue; + } + $backends[$resname] = $resname; + } + return $backends; + } + + /** + * Create this form and add all required elements + * + * @see Form::create() + */ + public function create() + { + $name = $this->filterName($this->getBackendName()); + + $this->addElement( + 'text', + 'backend_' . $name . '_name', + array( + 'required' => true, + 'allowEmpty' => false, + 'label' => 'Backend name', + 'value' => $this->getBackendName() + ) + ); + + $this->addElement( + 'select', + 'backend_' . $name . '_resource', + array( + 'label' => 'Database connection', + 'required' => true, + 'allowEmpty' => false, + 'value' => $this->getBackend()->get('resource'), + 'multiOptions' => $this->getDatabaseResources() + ) + ); + + $this->setSubmitLabel('Save backend'); + } + + /** + * Return the datatbase authentication backend configuration for this form + * + * @return array + * @see BaseBackendForm::getConfig + */ + public function getConfig() + { + $name = $this->getBackendName(); + $prefix = 'backend_' . $this->filterName($name) . '_'; + + $section = $this->getValue($prefix . 'name'); + $cfg = array( + 'backend' => 'db', + 'target' => 'user', + 'resource' => $this->getValue($prefix . 'resource'), + ); + return array( + $section => $cfg + ); + } +} diff --git a/application/forms/Config/Authentication/LdapBackendForm.php b/application/forms/Config/Authentication/LdapBackendForm.php new file mode 100644 index 000000000..ea92c756b --- /dev/null +++ b/application/forms/Config/Authentication/LdapBackendForm.php @@ -0,0 +1,157 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Form\Config\Authentication; + +use \Icinga\Application\Config as IcingaConfig; +use \Icinga\Application\Icinga; +use \Icinga\Application\Logger; +use \Icinga\Application\DbAdapterFactory; + +use \Icinga\Web\Form; +use \Zend_Config; + +/** + * Form for adding or modifying LDAP authentication backends + * + */ +class LdapBackendForm extends BaseBackendForm +{ + /** + * Create this form and add all required elements + * + * @see Form::create() + */ + public function create() + { + $name = $this->filterName($this->getBackendName()); + $backend = $this->getBackend(); + + $this->addElement( + 'text', + 'backend_'.$name.'_name', + array( + 'required' => true, + 'allowEmpty' => false, + 'label' => 'Backend name', + 'value' => $this->getBackendName() + ) + ); + + $this->addElement( + 'text', + 'backend_' . $name . '_hostname', + array( + 'label' => 'LDAP server host', + 'allowEmpty' => false, + 'value' => $backend->get('hostname', 'localhost'), + 'required' => true + ) + ); + + $this->addElement( + 'text', + 'backend_' . $name . '_root_dn', + array( + 'label' => 'LDAP root dn', + 'value' => $backend->get('hostname', 'ou=people,dc=icinga,dc=org'), + 'required' => true + ) + ); + + $this->addElement( + 'text', + 'backend_' . $name . '_bind_dn', + array( + 'label' => 'LDAP bind dn', + 'value' => $backend->get('bind_dn', 'cn=admin,cn=config'), + 'required' => true + ) + ); + + $this->addElement( + 'password', + 'backend_' . $name . '_bind_pw', + array( + 'label' => 'LDAP bind password', + 'renderPassword' => true, + 'value' => $backend->get('bind_pw', 'admin'), + 'required' => true + ) + ); + + $this->addElement( + 'text', + 'backend_' . $name . '_bind_user_class', + array( + 'label' => 'LDAP user object class', + 'value' => $backend->get('user_class', 'inetOrgPerson'), + 'required' => true + ) + ); + + $this->addElement( + 'text', + 'backend_' . $name . '_bind_user_name_attribute', + array( + 'label' => 'LDAP user name attribute', + 'value' => $backend->get('user_name_attribute', 'uid'), + 'required' => true + ) + ); + + $this->setSubmitLabel('Save backend'); + } + + /** + * Return the ldap authentication backend configuration for this form + * + * @return array + * @see BaseBackendForm::getConfig + */ + public function getConfig() + { + $name = $this->getBackendName(); + $prefix = 'backend_' . $this->filterName($name) . '_'; + + $section = $this->getValue($prefix . 'name'); + $cfg = array( + 'backend' => 'ldap', + 'target' => 'user', + 'hostname' => $this->getValue($prefix . 'hostname'), + 'root_dn' => $this->getValue($prefix . 'root_dn'), + 'bind_dn' => $this->getValue($prefix . 'bind_dn'), + 'bind_pw' => $this->getValue($prefix . 'bind_pw'), + 'bind_user_class' => $this->getValue($prefix . 'bind_user_class'), + 'bind_user_name_attribute' => $this->getValue($prefix . 'bind_user_name_attribute') + ); + return array( + $section => $cfg + ); + } +} diff --git a/application/forms/Config/AuthenticationForm.php b/application/forms/Config/AuthenticationForm.php index d835a956f..135ddefdc 100644 --- a/application/forms/Config/AuthenticationForm.php +++ b/application/forms/Config/AuthenticationForm.php @@ -32,6 +32,8 @@ use \Icinga\Application\Config as IcingaConfig; use \Icinga\Application\Icinga; use \Icinga\Application\Logger; use \Icinga\Application\DbAdapterFactory; +use \Icinga\Form\Config\Authentication\DbBackendForm; +use \Icinga\Form\Config\Authentication\LdapBackendForm; use \Icinga\Web\Form; use \Icinga\Web\Form\Element\Note; @@ -39,6 +41,7 @@ use \Icinga\Web\Form\Decorator\ConditionalHidden; use \Zend_Config; use \Zend_Form_Element_Text; use \Zend_Form_Element_Select; +use \Zend_Form_Element_Button; class AuthenticationForm extends Form { @@ -56,6 +59,10 @@ class AuthenticationForm extends Form */ private $resources = null; + + private $backendForms = array(); + + /** * Set an alternative array of resources that should be used instead of the DBFactory resource set * (used for testing) @@ -67,20 +74,6 @@ class AuthenticationForm extends Form $this->resources = $resources; } - /** - * Return content of the resources.ini or previously set resources for displaying in the database selection field - * - * @return array - */ - public function getResources() - { - if ($this->resources === null ) { - return DbAdapterFactory::getResources(); - } else { - return $this->resources; - } - } - /** * Set the configuration to be used for this form * @@ -91,189 +84,214 @@ class AuthenticationForm extends Form $this->config = $cfg; } - private function addProviderFormForDb($name, $backend) - { - $backends = array(); - foreach ($this->getResources() as $resname => $resource) - { - if ($resource['type'] !== 'db') { - continue; - } - $backends[$resname] = $resname; + private function addRemoveHint($name) + { + $this->addElement( + 'checkbox', + 'backend_' . $name . '_remove', + array( + 'name' => 'backend_' . $name . '_remove', + 'label' => 'Remove this authentication provider', + 'value' => $name, + 'checked' => $this->isMarkedForDeletion($name) + ) + ); + $this->enableAutoSubmit(array('backend_' . $name . '_remove')); + return 'backend_' . $name . '_remove'; + } + + private function addProviderForm($name, $backend) + { + $type = ucfirst(strtolower($backend->get('backend'))); + $formClass = '\Icinga\Form\Config\Authentication\\' . $type . 'BackendForm'; + if (!class_exists($formClass)) { + Logger::error('Unsupported backend found in authentication configuration: ' . $backend->get('backend')); + return; } - $this->addElement( - 'select', - 'backend_' . $name . '_resource', - array( - 'label' => 'Database connection', - 'required' => true, - 'value' => $backend->get('resource'), - 'multiOptions' => $backends - ) - ); + $form = new $formClass(); + $form->setBackendName($name); + $form->setBackend($backend); + if ($this->resources) { + $form->setResources($this->resources); + } + // It would be nice to directly set the form via + // this->setForm, but Zend doesn't handle form validation + // properly if doing so. + $form->create(); + foreach ($form->getElements() as $name => $element) { + $this->addElement($element, $name); + } - $this->addElement( - 'submit', - 'backend_' . $name . '_remove', - array( - 'label' => 'Remove this backend', - 'required' => true - ) - ); - - $this->addDisplayGroup( - array( - 'backend_' . $name . '_resource', - 'backend_' . $name . '_remove' - ), - 'auth_provider_' . $name, - array( - 'legend' => 'DB Authentication ' . $name - ) - ); - } - - private function addProviderFormForLdap($name, $backend) - { - $this->addElement( - 'text', - 'backend_' . $name . '_hostname', - array( - 'label' => 'LDAP server host', - 'value' => $backend->get('hostname', 'localhost'), - 'required' => true - ) - ); - - $this->addElement( - 'text', - 'backend_' . $name . '_root_dn', - array( - 'label' => 'LDAP root dn', - 'value' => $backend->get('hostname', 'ou=people,dc=icinga,dc=org'), - 'required' => true - ) - ); - - $this->addElement( - 'text', - 'backend_' . $name . '_bind_dn', - array( - 'label' => 'LDAP bind dn', - 'value' => $backend->get('bind_dn', 'cn=admin,cn=config'), - 'required' => true - ) - ); - - $this->addElement( - 'password', - 'backend_' . $name . '_bind_pw', - array( - 'label' => 'LDAP bind password', - 'value' => $backend->get('bind_pw', 'admin'), - 'required' => true - ) - ); - - $this->addElement( - 'text', - 'backend_' . $name . '_bind_user_class', - array( - 'label' => 'LDAP user object class', - 'value' => $backend->get('user_class', 'inetOrgPerson'), - 'required' => true - ) - ); - - $this->addElement( - 'text', - 'backend_' . $name . '_bind_user_name_attribute', - array( - 'label' => 'LDAP user name attribute', - 'value' => $backend->get('user_name_attribute', 'uid'), - 'required' => true - ) - ); - - $this->addElement( - 'submit', - 'backend_' . $name . '_remove', - array( - 'label' => 'Remove this backend' - ) - ); - - $this->addDisplayGroup( - array( - 'backend_' . $name . '_hostname', - 'backend_' . $name . '_root_dn', - 'backend_' . $name . '_bind_dn', - 'backend_' . $name . '_bind_pw', - 'backend_' . $name . '_bind_user_class', - 'backend_' . $name . '_bind_user_name_attribute', - 'backend_' . $name . '_remove' - ), - 'auth_provider_' . $name, - array( - 'legend' => 'LDAP Authentication ' . $name - ) - ); + $this->backendForms[] = $form; } - public function addPriorityButtons($name, $pos) + + + + public function addPriorityButtons($name, $order = array()) { - if ($pos > 0) { + $formEls = array(); + $priorities = array( + "up" => join(',', self::moveElementUp($name, $order)), + "down" => join(',', self::moveElementDown($name, $order)) + ); + if ($priorities["up"] != join(',', $order)) { $this->addElement( - 'submit', - 'priority_change_'.$name.'_down', + 'button', + 'priority' . $name . '_up', array( + 'name' => 'priority', 'label' => 'Move up in authentication order', - 'value' => $pos-1 + 'value' => $priorities["up"], + 'type' => 'submit' ) ); + $formEls[] = 'priority' . $name . '_up'; } - if ($pos+1 < count($this->config->keys())) { + if ($priorities["down"] != join(',', $order)) { $this->addElement( - 'submit', - 'priority_change_'.$name.'_up', + 'button', + 'priority' . $name . '_down', array( + 'name' => 'priority', 'label' => 'Move down in authentication order', - 'value' => $pos+1 + 'value' => $priorities["down"], + 'type' => 'submit' ) ); + $formEls[] = 'priority' . $name . '_down'; } + + return $formEls; } + + public function populate(array $values) + { + $last_priority = $this->getValue('current_priority'); + parent::populate($values); + $this->getElement('current_priority')->setValue($last_priority); + + } + + private function getAuthenticationOrder () + { + $request = $this->getRequest(); + $order = $request->getParam( + 'priority', + $request->getParam('current_priority', null) + ); + + if ($order === null) { + $order = array_keys($this->config->toArray()); + } else { + $order = explode(',', $order); + } + + return $order; + } + + + private function isMarkedForDeletion($backendName) + { + return intval($this->getRequest()->getParam('backend_' . $backendName . '_remove', 0)) === 1; + } + + private function addPersistentState() + { + + $this->addElement( + 'hidden', + 'current_priority', + array( + 'name' => 'current_priority', + 'value' => join(',', $this->getAuthenticationOrder()) + ) + ); + } + + + public function create() { - $this->addElement( - 'submit', - 'add_backend', - array( - 'label' => 'Add a new authentication provider', - 'class' => 'btn' - ) - ); - $pos = 0; - foreach ($this->config as $name => $backend) { + $order = $this->getAuthenticationOrder(); - $type = strtolower($backend->get('backend')); - if ($type === 'db') { - $this->addProviderFormForDb($name, $backend); - } elseif ($type === 'ldap') { - $this->addProviderFormForLdap($name, $backend); - } else { - Logger::error('Unsupported backend found in authentication configuration: ' . $backend->get('backend')); + foreach ($order as $name) { + $this->addElement( + new Note( + array( + 'escape' => false, + 'name' => 'title_backend_' . $name, + 'value' => '

Backend ' . $name . '

' + ) + ) + ); + $this->addRemoveHint($this->filterName($name)); + $backend = $this->config->get($name, null); + if ($backend === null) { continue; } - $this->addPriorityButtons($name, $pos); - - $pos++; + if (!$this->isMarkedForDeletion($this->filterName($name))) { + $this->addProviderForm($name, $backend); + $this->addPriorityButtons($name, $order); + } } + + $this->addPersistentState(); + $this->enableConditionalDecorator(); $this->setSubmitLabel('Save changes'); } -} \ No newline at end of file + + public function getConfig() + { + $result = array(); + foreach ($this->backendForms as $name) { + + $name->populate($this->getRequest()->getParams()); + $result += $name->getConfig(); + + } + return $result; + } + + private function enableConditionalDecorator() + { + foreach ($this->getElements() as $element) { + $element->addDecorator(new ConditionalHidden()); + } + } + + private static function moveElementUp($key, array $array) + { + $swap = null; + for ($i=0; $iresources === null ) { + if ($this->resources === null) { return DbAdapterFactory::getResources(); } else { return $this->resources; @@ -142,11 +142,16 @@ class GeneralForm extends Form 'value' => $env === 'development' ) ); - $this->addElement(new Note(array( - 'name' => 'note_env', - 'value' => 'Set true to show more detailed errors and disable certain optimizations ' - . 'in order to make debugging easier.' - ))); + $this->addElement( + new Note( + array( + 'name' => 'note_env', + 'value' => 'Set true to show more detailed errors ' + . 'and disable certain optimizations ' + . 'in order to make debugging easier.' + ) + ) + ); } /** @@ -159,7 +164,7 @@ class GeneralForm extends Form private function addTimezoneSelection(Zend_Config $cfg) { $tzList = array(); - foreach(DateTimeZone::listIdentifiers() as $tz) { + foreach (DateTimeZone::listIdentifiers() as $tz) { $tzList[$tz] = $tz; } @@ -173,11 +178,15 @@ class GeneralForm extends Form 'value' => $cfg->get('timezone', date_default_timezone_get()) ) ); - $this->addElement(new Note(array( - 'name' => 'noteTimezone', - 'value' => 'Select the timezone to be used as the default. User\'s can set their own timezone if'. - ' they like to, but this is the timezone to be used as the default setting .' - ))); + $this->addElement( + new Note( + array( + 'name' => 'noteTimezone', + 'value' => 'Select the timezone to be used as the default. User\'s can set their own timezone if' + . ' they like to, but this is the timezone to be used as the default setting .' + ) + ) + ); } /** @@ -193,13 +202,17 @@ class GeneralForm extends Form array( 'label' => 'Module folder', 'required' => true, - 'value' => $cfg->get('moduleFolder', $this->getConfigDir() . '/config/enabledModules') + 'value' => $cfg->get('moduleFolder', $this->getConfigDir() . '/config/enabledModules') + ) + ); + $this->addElement( + new Note( + array( + 'name' => 'noteModuleFolder', + 'value' => 'Use this folder to activate modules (must be writable by your webserver)' + ) ) ); - $this->addElement(new Note(array( - 'name' => 'noteModuleFolder', - 'value' => 'Use this folder to activate modules (must be writable by your webserver)' - ))); } /** @@ -209,7 +222,8 @@ class GeneralForm extends Form */ private function addDateFormatSettings(Zend_Config $cfg) { - $phpUrl = 'the official PHP documentation'; + $phpUrl = '' + . 'the official PHP documentation'; $this->addElement( 'text', @@ -220,10 +234,14 @@ class GeneralForm extends Form 'value' => $cfg->get('dateFormat', 'd/m/Y') ) ); - $this->addElement(new Note(array( - 'name' => 'noteDateFormat', - 'value' => 'Display dates according to this format. See ' . $phpUrl . ' for possible values' - ))); + $this->addElement( + new Note( + array( + 'name' => 'noteDateFormat', + 'value' => 'Display dates according to this format. See ' . $phpUrl . ' for possible values' + ) + ) + ); $this->addElement( @@ -235,12 +253,22 @@ class GeneralForm extends Form 'value' => $cfg->get('timeFormat', 'g:i A') ) ); - $this->addElement(new Note(array( - 'name' => 'noteTimeFormat', - 'value' => 'Display times according to this format. See ' . $phpUrl . ' for possible values' - ))); + $this->addElement( + new Note( + array( + 'name' => 'noteTimeFormat', + 'value' => 'Display times according to this format. See ' + . $phpUrl . ' for possible values' + ) + ) + ); } + /** + * Add form elements for setting the user preference storage backend + * + * @param Zend_Config $cfg The Zend_config object of preference section + */ public function addUserPreferencesDialog(Zend_Config $cfg) { $backend = $cfg->get('type', 'ini'); @@ -271,8 +299,7 @@ class GeneralForm extends Form ) ); $backends = array(); - foreach ($this->getResources() as $name => $resource) - { + foreach ($this->getResources() as $name => $resource) { if ($resource['type'] !== 'db') { continue; } @@ -295,9 +322,11 @@ class GeneralForm extends Form $txtPreferencesIniPath->addDecorator(new ConditionalHidden()); $txtPreferencesDbResource->addDecorator(new ConditionalHidden()); - $this->enableAutoSubmit(array( - 'preferences_type' - )); + $this->enableAutoSubmit( + array( + 'preferences_type' + ) + ); } /** @@ -328,6 +357,11 @@ class GeneralForm extends Form $this->setSubmitLabel('Save changes'); } + /** + * Return an Zend_Config object containing the configuration set in this form + * + * @return Zend_Config + */ public function getConfig() { if ($this->config === null) { @@ -357,5 +391,4 @@ class GeneralForm extends Form return $cfg; } - -} \ No newline at end of file +} diff --git a/application/forms/Config/LoggingForm.php b/application/forms/Config/LoggingForm.php index 107e75ff2..962f2f152 100644 --- a/application/forms/Config/LoggingForm.php +++ b/application/forms/Config/LoggingForm.php @@ -83,6 +83,12 @@ class LoggingForm extends Form } + /** + * Return true when logging is enabled according to the request and the configuration + * + * @param Zend_Config $config The logging section of the config.ini + * @return bool + */ private function loggingIsEnabled(Zend_Config $config) { $loggingRequestParam = $this->getRequest()->getParam('logging_enable', null); @@ -146,13 +152,15 @@ class LoggingForm extends Form ) ); - $this->addElement(new Note( - array( - 'name' => 'note_logging_app_path', - 'value'=> 'The logfile to write the icingaweb debug logs to. The webserver must be able to write' + $this->addElement( + new Note( + array( + 'name' => 'note_logging_app_path', + 'value'=> 'The logfile to write the icingaweb debug logs to. The webserver must be able to write' . 'at this location' + ) ) - )); + ); $this->addElement( 'checkbox', @@ -164,12 +172,14 @@ class LoggingForm extends Form ) ); - $this->addElement(new Note( - array( - 'name' => 'note_logging_app_verbose', - 'value'=> 'Check to write more verbose output to the icinga log file' + $this->addElement( + new Note( + array( + 'name' => 'note_logging_app_verbose', + 'value'=> 'Check to write more verbose output to the icinga log file' + ) ) - )); + ); $this->addElement( 'checkbox', @@ -180,12 +190,14 @@ class LoggingForm extends Form 'value' => $this->shouldDisplayDebugLog($debug) ) ); - $this->addElement(new Note( - array( - 'name' => 'note_logging_use_debug', - 'value'=> 'Check to write a seperate debug log (Warning: This file can grow very big)' + $this->addElement( + new Note( + array( + 'name' => 'note_logging_use_debug', + 'value'=> 'Check to write a seperate debug log (Warning: This file can grow very big)' + ) ) - )); + ); $textLoggingDebugPath = new Zend_Form_Element_Text( @@ -215,5 +227,4 @@ class LoggingForm extends Form $this->setSubmitLabel('Save changes'); } - -} \ No newline at end of file +} diff --git a/application/views/scripts/config/authentication.phtml b/application/views/scripts/config/authentication.phtml index f0b046499..1e041a914 100644 --- a/application/views/scripts/config/authentication.phtml +++ b/application/views/scripts/config/authentication.phtml @@ -1,2 +1,20 @@ + 'ldap') +)->getAbsoluteUrl(); + +$createDbBackend = Url::fromPath( + '/config/createAuthenticationBackend', + array('type' => 'db') +)->getAbsoluteUrl(); + +?> tabs->render($this); ?> + + form ?> \ No newline at end of file diff --git a/application/views/scripts/config/authentication/show-configuration.phtml b/application/views/scripts/config/authentication/show-configuration.phtml new file mode 100644 index 000000000..afc255224 --- /dev/null +++ b/application/views/scripts/config/authentication/show-configuration.phtml @@ -0,0 +1,30 @@ +tabs->render($this); ?> +
+
+

Saving authentication.ini failed

+
+

+ Your authentication configuration couldn't be stored (error: "exceptionMessage; ?>"). This could have one or more + of the following reasons: +

+
    +
  • You don't have file-system permissions to write to the authentication.ini file
  • +
  • Something went wrong while writing the file
  • +
  • There's an application error preventing you from persisting the configuration
  • +
+
+ +

+ Details can be seen in your application log (if you don't have access to this file, call your administrator in this case). +
+ In case you can access the configuration file (config/authentication.ini) by yourself, you can open it and + insert the config manually: + +

+

+

+        
+escape($this->iniConfigurationString); ?>
+        
+    
+

\ No newline at end of file diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 593977d40..d31c235d3 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -309,6 +309,7 @@ abstract class Form extends Zend_Form } } + /** * Disable CSRF counter measure and remove its field if already added * diff --git a/library/Icinga/Web/Form/Decorator/ConditionalHidden.php b/library/Icinga/Web/Form/Decorator/ConditionalHidden.php index 7dc54a8fe..e3356d44c 100644 --- a/library/Icinga/Web/Form/Decorator/ConditionalHidden.php +++ b/library/Icinga/Web/Form/Decorator/ConditionalHidden.php @@ -53,7 +53,7 @@ class ConditionalHidden extends Zend_Form_Decorator_Abstract * * @return string The input tag and options XHTML. */ - public function render($content ='') + public function render($content = '') { $attributes = $this->getElement()->getAttribs(); $condition = isset($attributes['condition']) ? $attributes['condition'] : 1; From 4810a0d47e40a77572ab8dab7e521441b65e8e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannis=20Mo=C3=9Fhammer?= Date: Fri, 16 Aug 2013 16:46:58 +0200 Subject: [PATCH 06/20] Code style and test fixes refs #3777 --- library/Icinga/Config/IniEditor.php | 130 +++++++++--------- .../forms/Config/GeneralFormTest.php | 17 ++- 2 files changed, 81 insertions(+), 66 deletions(-) diff --git a/library/Icinga/Config/IniEditor.php b/library/Icinga/Config/IniEditor.php index 42dd60380..085b1a425 100644 --- a/library/Icinga/Config/IniEditor.php +++ b/library/Icinga/Config/IniEditor.php @@ -28,8 +28,6 @@ namespace Icinga\Config; -use \Zend_Config_Exception; - /** * Edit the sections and keys of an ini in-place */ @@ -52,7 +50,7 @@ class IniEditor /** * Create a new IniEditor * - * @param string $content The content of the ini as string + * @param string $content The content of the ini as string */ public function __construct($content) { @@ -62,9 +60,9 @@ class IniEditor /** * Set the value of the given key. * - * @param array $key The key to set - * @param mixed $value The value to set - * @param string $section The section to insert to. + * @param array $key The key to set + * @param string $value The value to set + * @param array $section The section to insert to. */ public function set(array $key, $value, $section = null) { @@ -80,8 +78,8 @@ class IniEditor /** * Reset the value of the given array element * - * @param array $key The key of the array value - * @param string $section The section of the array. + * @param array $key The key of the array value + * @param array $section The section of the array. */ public function resetArrayElement(array $key, $section = null) { @@ -94,9 +92,9 @@ class IniEditor /** * Set the value for an array element * - * @param array $key The key of the property - * @param mixed $value The value of the property - * @param string $section The section to use + * @param array $key The key of the property + * @param string $value The value of the property + * @param array $section The section to use */ public function setArrayElement(array $key, $value, $section = null) { @@ -119,14 +117,15 @@ class IniEditor /** * Get the line of an array element * - * @param array $key The key of the property. - * @param string $section The section to use + * @param array $key The key of the property. + * @param $value The value + * @param $section The section to use * - * @return int The line of the array element. + * @return The line of the array element. */ private function getArrayElement(array $key, $section = null) { - $line = isset($section) ? $this->getSectionLine($section) +1 : 0; + $line = isset($section) ? $this->getSectionLine($section) + 1 : 0; $index = array_pop($key); $formatted = $this->formatKey($key); for (; $line < count($this->text); $line++) { @@ -134,7 +133,7 @@ class IniEditor if ($this->isSectionDeclaration($l)) { return -1; } - if (preg_match('/^\s*' . $formatted.'\[\]\s*=/', $l) === 1) { + if (preg_match('/^\s*' . $formatted . '\[\]\s*=/', $l) === 1) { return $line; } if ($this->isPropertyDeclaration($l, array_merge($key, array($index)))) { @@ -147,8 +146,8 @@ class IniEditor /** * When it exists, set the key back to null * - * @param array $key The key to reset - * @param string $section The section of the key + * @param array $key The key to reset + * @param array $section The section of the key */ public function reset(array $key, $section = null) { @@ -162,15 +161,15 @@ class IniEditor /** * Create the section if it does not exist and set the properties * - * @param string $section The section name - * @param string $extend The section that should be extended by this section + * @param string $section The section name + * @param array $extend The section that should be extended by this section */ public function setSection($section, $extend = null) { if (isset($extend)) { $decl = '[' . $section . ' : ' . $extend.']'; } else { - $decl = '[' . $section.']'; + $decl = '[' . $section . ']'; } $line = $this->getSectionLine($section); if ($line !== -1) { @@ -185,7 +184,7 @@ class IniEditor /** * Remove a section declaration * - * @param string $section The section name + * @param string $section The section name */ public function removeSection($section) { @@ -198,9 +197,9 @@ class IniEditor /** * Insert the key at the end of the corresponding section * - * @param array $key The key to insert - * @param mixed $value The value to insert - * @param string $section + * @param array $key The key to insert + * @param mixed $value The value to insert + * @param array $key The key to insert */ private function insert(array $key, $value, $section = null) { @@ -260,8 +259,8 @@ class IniEditor /** * Insert the text at line $lineNr * - * @param int $lineNr The line nr the inserted line should have - * @param string $toInsert The text that will be inserted + * @param $lineNr The line nr the inserted line should have + * @param $toInsert The text that will be inserted */ private function insertAtLine($lineNr, $toInsert) { @@ -271,14 +270,14 @@ class IniEditor /** * Update the line $lineNr * - * @param int $lineNr The line number of the target line - * @param string $content The content to replace + * @param $lineNr The line number of the target line + * @param $toInsert The new line content */ private function updateLine($lineNr, $content) { $comment = $this->getComment($this->text[$lineNr]); if (strlen($comment) > 0) { - $comment = ' ; ' . trim($comment); + $comment = " ; " . trim($comment); } $this->text[$lineNr] = str_pad($content, 43) . $comment; } @@ -286,9 +285,9 @@ class IniEditor /** * Get the comment from the given line * - * @param string $lineContent The content of the line + * @param $lineContent The content of the line * - * @return string The extracted comment + * @return string The extracted comment */ private function getComment($lineContent) { @@ -298,14 +297,14 @@ class IniEditor */ $cleaned = preg_replace('/^[^;"]*"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"/s', '', $lineContent); - $matches = mb_split(';', $cleaned, 2); + $matches = explode(';', $cleaned, 2); return array_key_exists(1, $matches) ? $matches[1] : ''; } /** * Delete the line $lineNr * - * @param int $lineNr The lineNr starting at 0 + * @param $lineNr The lineNr starting at 0 */ private function deleteLine($lineNr) { @@ -315,10 +314,10 @@ class IniEditor /** * Format a key-value pair to an INI file-entry * - * @param array $key The key - * @param mixed $value The value + * @param array $key The key to format + * @param string $value The value to format * - * @return string The formatted key-value pair + * @return string The formatted key-value pair */ private function formatKeyValuePair(array $key, $value) { @@ -328,8 +327,9 @@ class IniEditor /** * Format a key to an INI key * - * @param array $key - * @return string + * @param array $key the key array to format + * + * @return string */ private function formatKey(array $key) { @@ -339,14 +339,14 @@ class IniEditor /** * Get the first line after the given $section * - * @param string $section The name of the section + * @param $section The name of the section * - * @return int The line number of the section + * @return int The line number of the section */ private function getSectionEnd($section = null) { $i = 0; - $started = isset($section) ? false: true; + $started = isset($section) ? false : true; foreach ($this->text as $line) { if ($started && $this->isSectionDeclaration($line)) { if ($i === 0) { @@ -374,15 +374,15 @@ class IniEditor /** * Check if the line contains the property declaration for a key * - * @param string $lineContent The content of the line - * @param array $key The key this declaration is supposed to have + * @param string $lineContent The content of the line + * @param array $key The key this declaration is supposed to have * - * @return bool True, when the lineContent is a property declaration + * @return boolean True, when the lineContent is a property declaration */ private function isPropertyDeclaration($lineContent, array $key) { return preg_match( - '/^\s*' . $this->formatKey($key) .'\s*=\s*/', + '/^\s*' . $this->formatKey($key) . '\s*=\s*/', $lineContent ) === 1; } @@ -390,15 +390,15 @@ class IniEditor /** * Check if the given line contains a section declaration * - * @param string $lineContent The content of the line - * @param string $section The optional section name that will be assumed + * @param $lineContent The content of the line + * @param string $section The optional section name that will be assumed * - * @return bool True, when the lineContent is a section declaration + * @return bool True, when the lineContent is a section declaration */ private function isSectionDeclaration($lineContent, $section = null) { if (isset($section)) { - return preg_match('/^\s*\[\s*'.$section.'\s*[\]:]/', $lineContent) === 1; + return preg_match('/^\s*\[\s*' . $section . '\s*[\]:]/', $lineContent) === 1; } else { return preg_match('/^\s*\[/', $lineContent) === 1; } @@ -407,9 +407,9 @@ class IniEditor /** * Get the line where the section begins * - * @param string $section The section + * @param $section The section * - * @return int The line number + * @return int The line number */ private function getSectionLine($section) { @@ -426,13 +426,14 @@ class IniEditor /** * Get the line number where the given key occurs * - * @param array $keys The key and its parents - * @param string $section The section of the key + * @param array $keys The key and its parents + * @param $section The section of the key * * @return int The line number */ private function getKeyLine(array $keys, $section = null) { + $key = implode($this->nestSeparator, $keys); $inSection = isset($section) ? false : true; $i = 0; foreach ($this->text as $line) { @@ -453,7 +454,7 @@ class IniEditor /** * Get the last line number occurring in the text * - * @return int The line number of the last line + * @return The line number of the last line */ private function getLastLine() { @@ -463,9 +464,9 @@ class IniEditor /** * Insert a new element into a specific position of an array * - * @param array $array The array to use - * @param int $pos The target position - * @param mixed $element The element to insert + * @param $array The array to use + * @param $pos The target position + * @param $element The element to insert * * @return array The changed array */ @@ -478,12 +479,10 @@ class IniEditor /** * Remove an element from an array * - * @param array $array The array to use - * @param mixed $pos The position to remove - * - * @return array + * @param $array The array to use + * @param $pos The position to remove */ - private function removeFromArray(array $array, $pos) + private function removeFromArray($array, $pos) { unset($array[$pos]); return array_values($array); @@ -492,9 +491,10 @@ class IniEditor /** * Prepare a value for INe * - * @param mixed $value The value of the string + * @param $value The value of the string + * + * @return string The formatted value * - * @return string The formatted value * @throws Zend_Config_Exception */ private function formatValue($value) diff --git a/test/php/application/forms/Config/GeneralFormTest.php b/test/php/application/forms/Config/GeneralFormTest.php index 8bbfa565e..0ea5deb07 100644 --- a/test/php/application/forms/Config/GeneralFormTest.php +++ b/test/php/application/forms/Config/GeneralFormTest.php @@ -87,7 +87,11 @@ class GeneralFormTest extends \Test\Icinga\Web\Form\BaseFormTest ) ) ); - + $form->setResources( + array( + 'db' => 'db' + ) + ); $form->setConfigDir('/tmp'); $view = new Zend_View(); @@ -117,7 +121,13 @@ class GeneralFormTest extends \Test\Icinga\Web\Form\BaseFormTest ) ); $form->setConfigDir('/tmp'); + $form->setResources( + array( + 'db' => 'db' + ) + ); $form->create(); + $view = new Zend_View(); $this->assertFalse( @@ -146,6 +156,11 @@ class GeneralFormTest extends \Test\Icinga\Web\Form\BaseFormTest ) ); $form->setConfigDir('/tmp'); + $form->setResources( + array( + 'db' => 'db' + ) + ); $form->create(); $view = new Zend_View(); From 972bc9853ee9b4b13e21a9537f3afa6d2833bce3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jannis=20Mo=C3=9Fhammer?= Date: Mon, 19 Aug 2013 18:25:20 +0200 Subject: [PATCH 07/20] Configuration interfaces for logging, authentication and general configuration - Added HelpText decorator - Added Time and DateFormatValidator - Added tests refs #3777 --- application/controllers/ConfigController.php | 144 +++++--- .../Config/Authentication/DbBackendForm.php | 10 +- .../Config/Authentication/LdapBackendForm.php | 31 +- .../forms/Config/AuthenticationForm.php | 151 +++++++-- application/forms/Config/GeneralForm.php | 79 ++--- application/forms/Config/LoggingForm.php | 136 +++++--- .../views/scripts/config/authentication.phtml | 15 + .../config/authentication/modify.phtml | 4 + .../views/scripts/config/logging.phtml | 14 + .../show-configuration.phtml | 10 +- library/Icinga/Web/Form.php | 11 +- .../Web/Form/Decorator/ConditionalHidden.php | 11 +- .../Icinga/Web/Form/Decorator/HelpText.php | 63 ++++ .../Form/Validator/DateFormatValidator.php | 86 +++++ .../Form/Validator/TimeFormatValidator.php | 85 +++++ .../Form/Validator/WritablePathValidator.php | 72 ++++ .../forms/Command/AcknowledgeFormTest.php | 1 + .../forms/Config/AuthenticationFormTest.php | 315 ++++++++++++++++++ .../forms/Config/LoggingFormTest.php | 214 ++++++++++++ .../library/Icinga/Web/Form/BaseFormTest.php | 45 ++- 20 files changed, 1276 insertions(+), 221 deletions(-) create mode 100644 application/views/scripts/config/authentication/modify.phtml rename application/views/scripts/config/{authentication => }/show-configuration.phtml (57%) create mode 100644 library/Icinga/Web/Form/Decorator/HelpText.php create mode 100644 library/Icinga/Web/Form/Validator/DateFormatValidator.php create mode 100644 library/Icinga/Web/Form/Validator/TimeFormatValidator.php create mode 100644 library/Icinga/Web/Form/Validator/WritablePathValidator.php create mode 100644 test/php/application/forms/Config/AuthenticationFormTest.php create mode 100644 test/php/application/forms/Config/LoggingFormTest.php diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index fb8e63f34..cb4bf6c85 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -31,6 +31,7 @@ use \Icinga\Web\Widget\Tab; use \Icinga\Web\Url; use \Icinga\Web\Hook\Configuration\ConfigurationTabBuilder; use \Icinga\Application\Icinga; +use \Icinga\Application\Config as IcingaConfig; use \Icinga\Form\Config\GeneralForm; use \Icinga\Form\Config\AuthenticationForm; use \Icinga\Form\Config\Authentication\LdapBackendForm; @@ -55,32 +56,32 @@ class ConfigController extends BaseConfigController return array( 'index' => new Tab( array( - "name" => "index", - "title" => "Application", - "url" => Url::fromPath("/config") + 'name' => 'index', + 'title' => 'Application', + 'url' => Url::fromPath('/config') ) ), - "authentication" => new Tab( + 'authentication' => new Tab( array( - "name" => "auth", - "title" => "Authentication", - "url" => Url::fromPath('/config/authentication') + 'name' => 'auth', + 'title' => 'Authentication', + 'url' => Url::fromPath('/config/authentication') ) ), - "logging" => new Tab( + 'logging' => new Tab( array( - "name" => "logging", - "title" => "Logging", - "url" => Url::fromPath("/config/logging") + 'name' => 'logging', + 'title' => 'Logging', + 'url' => Url::fromPath('/config/logging') ) ), - "modules" => new Tab( + 'modules' => new Tab( array( - "name" => "modules", - "title" => "Modules", - "url" => Url::fromPath("/config/moduleoverview") + 'name' => 'modules', + 'title' => 'Modules', + 'url' => Url::fromPath('/config/moduleoverview') ) ) ); @@ -88,7 +89,6 @@ class ConfigController extends BaseConfigController /** * Index action, entry point for configuration - * @TODO: Implement configuration interface (#3777) */ public function indexAction() { @@ -96,26 +96,30 @@ class ConfigController extends BaseConfigController $form->setConfiguration(IcingaConfig::app()); $form->setRequest($this->_request); if ($form->isSubmittedAndValid()) { - $cfg = IcingaConfig::app()->getConfigFile(); - $writer = new PreservingIniWriter( - array( - 'config' => $form->getConfig(), - 'filename' => $cfg - ) - ); - $writer->write(); + if (!$this->writeConfigFile($form->getConfig(), 'config')) { + return false; + } + $this->redirectNow('/config'); } $this->view->form = $form; } - + /** + * Form for modifying the logging configuration + */ public function loggingAction() { $form = new LoggingForm(); $form->setConfiguration(IcingaConfig::app()); $form->setRequest($this->_request); - + if ($form->isSubmittedAndValid()) { + $config = $form->getConfig(); + if (!$this->writeConfigFile($form->getConfig(), 'config')) { + return false; + } + $this->redirectNow('/config/logging'); + } $this->view->form = $form; } @@ -151,24 +155,6 @@ class ConfigController extends BaseConfigController $this->redirectNow('config/moduleoverview?_render=body'); } - private function writeAuthenticationFile(array $config) - { - $cfg = new Zend_Config($config); - $writer = new Zend_Config_Writer_Ini( - array( - 'config' => $cfg, - 'filename' => IcingaConfig::app('authentication')->getConfigFile() - ) - ); - try { - $writer->write(); - } catch (Exception $exc) { - $this->view->exceptionMessage = $exc->getMessage(); - $this->view->iniConfigurationString = $writer->render(); - $this->render('authentication/show-configuration'); - } - } - /** * Action for creating a new authentication backend * @@ -181,10 +167,16 @@ class ConfigController extends BaseConfigController $form->setRequest($this->_request); if ($form->isSubmittedAndValid()) { - $this->writeAuthenticationFile($form->getConfig()); + $modifiedConfig = $form->getConfig(); + if (empty($modifiedConfig)) { + $form->addError('You need at least one authentication backend.'); + } else if (!$this->writeAuthenticationFile($modifiedConfig)) { + return; + } else { + $this->redirectNow('/config/authentication'); + } } $this->view->form = $form; - } /** @@ -201,16 +193,72 @@ class ConfigController extends BaseConfigController $form->setRequest($this->getRequest()); if ($form->isSubmittedAndValid()) { $backendCfg = IcingaConfig::app('authentication')->toArray(); - foreach ($form->getConfig() as $backendName => $settings) { $backendCfg[$backendName] = $settings; } + if (!$this->writeAuthenticationFile($backendCfg)) { + return; + } + $this->redirectNow('/config/authentication'); - $this->writeAuthenticationFile($backendCfg); } $this->view->form = $form; $this->render('authentication/modify'); } + /** + * Write changes to an authentication file. + * + * This uses the Zend_Config_Writer_Ini implementation for now, as the Preserving ini writer can't + * handle ordering + * + * @param array $config The configuration changes + * + * @return bool True when persisting succeeded, otherwise false + * @see writeConfigFile() + */ + private function writeAuthenticationFile($config) { + $writer = new Zend_Config_Writer_Ini( + array( + 'config' => new Zend_Config($config), + 'filename' => IcingaConfig::app('authentication')->getConfigFile() + ) + ); + return $this->writeConfigFile($config, 'authentication', $writer); + } + + /** + * Write changes to a configuration file $file, using the supllied writer or PreservingIniWriter if none is set + * + * @param array|Zend_Config $config The configuration to write + * @param string $file The filename to write to (without .ini) + * @param Zend_Config_Writer $writer An optional writer to use for persisting changes + * + * @return bool True when persisting succeeded, otherwise false + */ + private function writeConfigFile($config, $file, $writer = null) + { + if (is_array($config)) { + $config = new Zend_Config($config); + } + if ($writer === null) { + $writer = new PreservingIniWriter( + array( + 'config' => $config, + 'filename' => IcingaConfig::app($file)->getConfigFile() + ) + ); + } + try { + $writer->write(); + return true; + } catch (Exception $exc) { + $this->view->exceptionMessage = $exc->getMessage(); + $this->view->iniConfigurationString = $writer->render(); + $this->view->file = $file; + $this->render('show-configuration'); + return false; + } + } } // @codingStandardsIgnoreEnd diff --git a/application/forms/Config/Authentication/DbBackendForm.php b/application/forms/Config/Authentication/DbBackendForm.php index de2b0464f..b331bfa1b 100644 --- a/application/forms/Config/Authentication/DbBackendForm.php +++ b/application/forms/Config/Authentication/DbBackendForm.php @@ -76,10 +76,11 @@ class DbBackendForm extends BaseBackendForm 'text', 'backend_' . $name . '_name', array( - 'required' => true, - 'allowEmpty' => false, - 'label' => 'Backend name', - 'value' => $this->getBackendName() + 'required' => true, + 'allowEmpty'=> false, + 'label' => 'Backend name', + 'helptext' => 'The name of this authentication provider', + 'value' => $this->getBackendName() ) ); @@ -90,6 +91,7 @@ class DbBackendForm extends BaseBackendForm 'label' => 'Database connection', 'required' => true, 'allowEmpty' => false, + 'helptext' => 'The database connection to use for authenticating with this provider', 'value' => $this->getBackend()->get('resource'), 'multiOptions' => $this->getDatabaseResources() ) diff --git a/application/forms/Config/Authentication/LdapBackendForm.php b/application/forms/Config/Authentication/LdapBackendForm.php index ea92c756b..51d9bbb51 100644 --- a/application/forms/Config/Authentication/LdapBackendForm.php +++ b/application/forms/Config/Authentication/LdapBackendForm.php @@ -56,10 +56,11 @@ class LdapBackendForm extends BaseBackendForm 'text', 'backend_'.$name.'_name', array( - 'required' => true, - 'allowEmpty' => false, - 'label' => 'Backend name', - 'value' => $this->getBackendName() + 'required' => true, + 'allowEmpty'=> false, + 'label' => 'Backend name', + 'helptext' => 'The name of this authentication backend', + 'value' => $this->getBackendName() ) ); @@ -67,10 +68,11 @@ class LdapBackendForm extends BaseBackendForm 'text', 'backend_' . $name . '_hostname', array( - 'label' => 'LDAP server host', + 'label' => 'LDAP server host', 'allowEmpty' => false, - 'value' => $backend->get('hostname', 'localhost'), - 'required' => true + 'value' => $backend->get('hostname', 'localhost'), + 'helptext' => 'The hostname or address of the LDAP server to use for authentication', + 'required' => true ) ); @@ -79,7 +81,8 @@ class LdapBackendForm extends BaseBackendForm 'backend_' . $name . '_root_dn', array( 'label' => 'LDAP root dn', - 'value' => $backend->get('hostname', 'ou=people,dc=icinga,dc=org'), + 'value' => $backend->get('root_dn', 'ou=people,dc=icinga,dc=org'), + 'helptext' => 'The path where users can be found on the ldap server', 'required' => true ) ); @@ -90,6 +93,7 @@ class LdapBackendForm extends BaseBackendForm array( 'label' => 'LDAP bind dn', 'value' => $backend->get('bind_dn', 'cn=admin,cn=config'), + 'helptext' => 'The user dn to use for querying the ldap server', 'required' => true ) ); @@ -101,26 +105,29 @@ class LdapBackendForm extends BaseBackendForm 'label' => 'LDAP bind password', 'renderPassword' => true, 'value' => $backend->get('bind_pw', 'admin'), + 'helptext' => 'The password to use for querying the ldap server', 'required' => true ) ); $this->addElement( 'text', - 'backend_' . $name . '_bind_user_class', + 'backend_' . $name . '_user_class', array( 'label' => 'LDAP user object class', 'value' => $backend->get('user_class', 'inetOrgPerson'), + 'helptext' => 'The object class used for storing users on the ldap server', 'required' => true ) ); $this->addElement( 'text', - 'backend_' . $name . '_bind_user_name_attribute', + 'backend_' . $name . '_user_name_attribute', array( 'label' => 'LDAP user name attribute', 'value' => $backend->get('user_name_attribute', 'uid'), + 'helptext' => 'The attribute name used for storing the user name on the ldap server', 'required' => true ) ); @@ -147,8 +154,8 @@ class LdapBackendForm extends BaseBackendForm 'root_dn' => $this->getValue($prefix . 'root_dn'), 'bind_dn' => $this->getValue($prefix . 'bind_dn'), 'bind_pw' => $this->getValue($prefix . 'bind_pw'), - 'bind_user_class' => $this->getValue($prefix . 'bind_user_class'), - 'bind_user_name_attribute' => $this->getValue($prefix . 'bind_user_name_attribute') + 'user_class' => $this->getValue($prefix . 'user_class'), + 'user_name_attribute' => $this->getValue($prefix . 'user_name_attribute') ); return array( $section => $cfg diff --git a/application/forms/Config/AuthenticationForm.php b/application/forms/Config/AuthenticationForm.php index 135ddefdc..93ad7d05d 100644 --- a/application/forms/Config/AuthenticationForm.php +++ b/application/forms/Config/AuthenticationForm.php @@ -32,17 +32,17 @@ use \Icinga\Application\Config as IcingaConfig; use \Icinga\Application\Icinga; use \Icinga\Application\Logger; use \Icinga\Application\DbAdapterFactory; -use \Icinga\Form\Config\Authentication\DbBackendForm; -use \Icinga\Form\Config\Authentication\LdapBackendForm; use \Icinga\Web\Form; use \Icinga\Web\Form\Element\Note; use \Icinga\Web\Form\Decorator\ConditionalHidden; use \Zend_Config; -use \Zend_Form_Element_Text; -use \Zend_Form_Element_Select; -use \Zend_Form_Element_Button; +/** + * Form for modifying the authentication provider and order. + * + * This is a composite form from one or more forms under the Authentication folder + */ class AuthenticationForm extends Form { /** @@ -59,7 +59,11 @@ class AuthenticationForm extends Form */ private $resources = null; - + /** + * An array containing all provider subforms currently displayed + * + * @var array + */ private $backendForms = array(); @@ -84,7 +88,15 @@ class AuthenticationForm extends Form $this->config = $cfg; } - + /** + * Add a hint to remove the backend identified by $name + * + * The button will have the name "backend_$name_remove" + * + * @param string $name The backend to add this button for + * + * @return string The id of the added button + */ private function addRemoveHint($name) { $this->addElement( @@ -101,6 +113,19 @@ class AuthenticationForm extends Form return 'backend_' . $name . '_remove'; } + /** + * Add the form for the provider identified by $name, with the configuration $backend + * + * Supported backends are backends with a form found under \Icinga\Form\Config\Authentication. + * The backend name ist the (uppercase first) prefix with 'BackendForm' as the suffix. + * + * Originally it was intended to add the provider as a subform. As this didn't really work with + * the Zend validation logic (maybe our own validation logic breaks it), we now create the form, but add + * all elements to this form explicitly. + * + * @param string $name The name of the backend to add + * @param Zend_Config $backend The configuration of the backend + */ private function addProviderForm($name, $backend) { $type = ucfirst(strtolower($backend->get('backend'))); @@ -117,49 +142,56 @@ class AuthenticationForm extends Form if ($this->resources) { $form->setResources($this->resources); } + // It would be nice to directly set the form via // this->setForm, but Zend doesn't handle form validation // properly if doing so. $form->create(); - foreach ($form->getElements() as $name => $element) { - $this->addElement($element, $name); + foreach ($form->getElements() as $elName => $element) { + if ($elName === 'backend_' . $this->filterName($name) . '_name') { + continue; + } + $this->addElement($element, $elName); } - $this->backendForms[] = $form; } - - - - + /** + * Add the buttons for modifying authentication priorities + * + * @param string $name The name of the backend to add the buttons for + * @param array $order The current order which will be used to determine the changed order + * + * @return array An array containing the newly added form element ids as strings + */ public function addPriorityButtons($name, $order = array()) { $formEls = array(); $priorities = array( - "up" => join(',', self::moveElementUp($name, $order)), - "down" => join(',', self::moveElementDown($name, $order)) + 'up' => join(',', self::moveElementUp($name, $order)), + 'down' => join(',', self::moveElementDown($name, $order)) ); - if ($priorities["up"] != join(',', $order)) { + if ($priorities['up'] != join(',', $order)) { $this->addElement( 'button', 'priority' . $name . '_up', array( 'name' => 'priority', 'label' => 'Move up in authentication order', - 'value' => $priorities["up"], + 'value' => $priorities['up'], 'type' => 'submit' ) ); $formEls[] = 'priority' . $name . '_up'; } - if ($priorities["down"] != join(',', $order)) { + if ($priorities['down'] != join(',', $order)) { $this->addElement( 'button', 'priority' . $name . '_down', array( 'name' => 'priority', 'label' => 'Move down in authentication order', - 'value' => $priorities["down"], + 'value' => $priorities['down'], 'type' => 'submit' ) ); @@ -169,7 +201,14 @@ class AuthenticationForm extends Form return $formEls; } - + /** + * Overwrite for Zend_Form::populate in order to preserve the modified priority of the backends + * + * @param array $values The values to populate the form with + * + * @return void|\Zend_Form + * @see Zend_Form::populate + */ public function populate(array $values) { $last_priority = $this->getValue('current_priority'); @@ -178,7 +217,13 @@ class AuthenticationForm extends Form } - private function getAuthenticationOrder () + /** + * Return an array containing all authentication providers in the order they should be used + * + * @return array An array containing the identifiers (section names) of the authentication backend in + * the order they should be persisted + */ + private function getAuthenticationOrder() { $request = $this->getRequest(); $order = $request->getParam( @@ -195,15 +240,26 @@ class AuthenticationForm extends Form return $order; } - + /** + * Return true if the backend should be deleted when the changes are persisted + * + * @param string $backendName The name of the backend to check for being in a 'delete' state + * + * @return bool Whether this backend will be deleted on save + */ private function isMarkedForDeletion($backendName) { return intval($this->getRequest()->getParam('backend_' . $backendName . '_remove', 0)) === 1; } + /** + * Add persistent values to the form in hidden fields + * + * Currently this adds the 'current_priority' field to persist priority modifications. This prevents changes in the + * authentication order to be lost as soon as other changes are submitted (like marking a backend for deletion) + */ private function addPersistentState() { - $this->addElement( 'hidden', 'current_priority', @@ -214,8 +270,11 @@ class AuthenticationForm extends Form ); } - - + /** + * Create the authentication provider configuration form + * + * @see IcingaForm::create() + */ public function create() { $order = $this->getAuthenticationOrder(); @@ -246,6 +305,11 @@ class AuthenticationForm extends Form $this->setSubmitLabel('Save changes'); } + /** + * Return the configuration state defined by this form + * + * @return array + */ public function getConfig() { $result = array(); @@ -258,6 +322,11 @@ class AuthenticationForm extends Form return $result; } + /** + * Enable the "ConditionalHidden" Decorator for all elements in this form + * + * @see ConditionalHidden + */ private function enableConditionalDecorator() { foreach ($this->getElements() as $element) { @@ -265,6 +334,21 @@ class AuthenticationForm extends Form } } + /** + * Static helper for moving an element in an array one slot up, if possible + * + * Example: + * + *
+     * $array = array('first', 'second', 'third');
+     * moveElementUp('third', $array); // returns ['first', 'third', 'second']
+     * 
+ * + * @param string $key The key to bubble up one slot + * @param array $array The array to work with + * + * @return array The modified array + */ private static function moveElementUp($key, array $array) { $swap = null; @@ -280,6 +364,21 @@ class AuthenticationForm extends Form return $array; } + /** + * Static helper for moving an element in an array one slot up, if possible + * + * Example: + * + *
+     * $array = array('first', 'second', 'third');
+     * moveElementDown('first', $array); // returns ['second', 'first', 'third']
+     * 
+ * + * @param string $key The key to bubble up one slot + * @param array $array The array to work with + * + * @return array The modified array + */ private static function moveElementDown($key, array $array) { $swap = null; diff --git a/application/forms/Config/GeneralForm.php b/application/forms/Config/GeneralForm.php index 404c925d3..be2f8c358 100644 --- a/application/forms/Config/GeneralForm.php +++ b/application/forms/Config/GeneralForm.php @@ -32,8 +32,10 @@ use \Icinga\Application\Config as IcingaConfig; use \Icinga\Application\Icinga; use \Icinga\Application\DbAdapterFactory; use \Icinga\Web\Form; +use \Icinga\Web\Form\Validator\WritablePathValidator; +use \Icinga\Web\Form\Validator\TimeFormatValidator; +use \Icinga\Web\Form\Validator\DateFormatValidator; use \Icinga\Web\Form\Decorator\ConditionalHidden; -use \Icinga\Web\Form\Element\Note; use \DateTimeZone; use \Zend_Config; @@ -138,20 +140,14 @@ class GeneralForm extends Form array( 'label' => 'Development mode', 'required' => true, + 'helptesxt' => 'Set true to show more detailed errors ' + . 'and disable certain optimizations ' + . 'in order to make debugging easier.', 'tooltip' => 'More verbose output', 'value' => $env === 'development' ) ); - $this->addElement( - new Note( - array( - 'name' => 'note_env', - 'value' => 'Set true to show more detailed errors ' - . 'and disable certain optimizations ' - . 'in order to make debugging easier.' - ) - ) - ); + } /** @@ -167,6 +163,8 @@ class GeneralForm extends Form foreach (DateTimeZone::listIdentifiers() as $tz) { $tzList[$tz] = $tz; } + $helptext = 'Select the timezone to be used as the default. User\'s can set their own timezone if' + . ' they like to, but this is the timezone to be used as the default setting .'; $this->addElement( 'select', @@ -175,18 +173,10 @@ class GeneralForm extends Form 'label' => 'Default application timezone', 'required' => true, 'multiOptions' => $tzList, + 'helptext' => $helptext, 'value' => $cfg->get('timezone', date_default_timezone_get()) ) ); - $this->addElement( - new Note( - array( - 'name' => 'noteTimezone', - 'value' => 'Select the timezone to be used as the default. User\'s can set their own timezone if' - . ' they like to, but this is the timezone to be used as the default setting .' - ) - ) - ); } /** @@ -202,17 +192,10 @@ class GeneralForm extends Form array( 'label' => 'Module folder', 'required' => true, + 'helptext' => 'Use this folder to activate modules (must be writable by your webserver)', 'value' => $cfg->get('moduleFolder', $this->getConfigDir() . '/config/enabledModules') ) ); - $this->addElement( - new Note( - array( - 'name' => 'noteModuleFolder', - 'value' => 'Use this folder to activate modules (must be writable by your webserver)' - ) - ) - ); } /** @@ -225,43 +208,29 @@ class GeneralForm extends Form $phpUrl = '' . 'the official PHP documentation'; - $this->addElement( - 'text', - 'date_format', + $txtDefaultDateFormat = new Zend_Form_Element_Text( array( + 'name' => 'date_format', 'label' => 'Date format', + 'helptext' => 'Display dates according to this format. See ' . $phpUrl . ' for possible values', 'required' => true, - 'value' => $cfg->get('dateFormat', 'd/m/Y') - ) - ); - $this->addElement( - new Note( - array( - 'name' => 'noteDateFormat', - 'value' => 'Display dates according to this format. See ' . $phpUrl . ' for possible values' - ) + 'value' => $cfg->get('dateFormat', 'd/m/Y') ) ); + $this->addElement($txtDefaultDateFormat); + $txtDefaultDateFormat->addValidator(new DateFormatValidator()); - - $this->addElement( - 'text', - 'time_format', + $txtDefaultTimeFormat = new Zend_Form_Element_Text( array( + 'name' => 'time_format', 'label' => 'Time format', 'required' => true, - 'value' => $cfg->get('timeFormat', 'g:i A') - ) - ); - $this->addElement( - new Note( - array( - 'name' => 'noteTimeFormat', - 'value' => 'Display times according to this format. See ' - . $phpUrl . ' for possible values' - ) + 'helptext' => 'Display times according to this format. See ' . $phpUrl . ' for possible values', + 'value' => $cfg->get('timeFormat', 'g:i A') ) ); + $txtDefaultTimeFormat->addValidator(new TimeFormatValidator()); + $this->addElement($txtDefaultTimeFormat); } /** @@ -316,7 +285,7 @@ class GeneralForm extends Form 'multiOptions' => $backends ) ); - + $txtPreferencesIniPath->addValidator(new WritablePathValidator()); $this->addElement($txtPreferencesIniPath); $this->addElement($txtPreferencesDbResource); diff --git a/application/forms/Config/LoggingForm.php b/application/forms/Config/LoggingForm.php index 962f2f152..ca302b4e5 100644 --- a/application/forms/Config/LoggingForm.php +++ b/application/forms/Config/LoggingForm.php @@ -32,6 +32,7 @@ use \Icinga\Application\Config as IcingaConfig; use \Icinga\Application\Icinga; use \Icinga\Web\Form; use \Icinga\Web\Form\Element\Note; +use \Icinga\Web\Form\Validator\WritablePathValidator; use \Icinga\Web\Form\Decorator\ConditionalHidden; use \Zend_Config; use \Zend_Form_Element_Text; @@ -49,6 +50,13 @@ class LoggingForm extends Form */ private $config = null; + /** + * Base directory to use instead of the one provided by Icinga::app (used for testing) + * + * @var null + */ + private $baseDir = null; + /** * Set the configuration of this form * @@ -61,6 +69,32 @@ class LoggingForm extends Form $this->config = $cfg; } + /** + * Set a different base directory to use for default paths instead of the one provided by Icinga::app() + * + * @param string $dir The new directory to use + */ + public function setBaseDir($dir) + { + $this->baseDir = $dir; + } + + /** + * Return the applications base directory or the value from a previous setBaseDir call + * + * This is used to determine the default logging paths in a manner that allows to set a different path + * during testing + * + * @return string + */ + public function getBaseDir() + { + if ($this->baseDir) { + return $this->baseDir; + } + return realpath(Icinga::app()->getApplicationDir().'/../'); + } + /** * Return true if the debug log path textfield should be displayed * @@ -74,7 +108,7 @@ class LoggingForm extends Form */ private function shouldDisplayDebugLog(Zend_Config $config) { - $debugParam = $this->getRequest()->getParam('logging_use_debug', null); + $debugParam = $this->getRequest()->getParam('logging_debug_enable', null); if ($debugParam !== null) { return intval($debugParam) === 1; } else { @@ -101,6 +135,8 @@ class LoggingForm extends Form } /** + * Create this logging configuration form + * * @see Form::create() */ public function create() @@ -127,7 +163,7 @@ class LoggingForm extends Form 'value' => $this->loggingIsEnabled($logging) ) ); - if (!$this->loggingIsEnabled($debug)) { + if (!$this->loggingIsEnabled($logging)) { $this->addElement( new Note( array( @@ -142,25 +178,19 @@ class LoggingForm extends Form return; } - $this->addElement( - 'text', - 'logging_app_path', + + $txtLogPath = new Zend_Form_Element_Text( array( + 'name' => 'logging_app_target', 'label' => 'Application log path', + 'helptext' => 'The logfile to write the icingaweb debug logs to.' + . 'The webserver must be able to write at this location', 'required' => true, 'value' => $logging->get('target', '/var/log/icingaweb.log') ) ); - - $this->addElement( - new Note( - array( - 'name' => 'note_logging_app_path', - 'value'=> 'The logfile to write the icingaweb debug logs to. The webserver must be able to write' - . 'at this location' - ) - ) - ); + $txtLogPath->addValidator(new WritablePathValidator()); + $this->addElement($txtLogPath); $this->addElement( 'checkbox', @@ -168,63 +198,73 @@ class LoggingForm extends Form array( 'label' => 'Verbose logging', 'required' => true, + 'helptext' => 'Check to write more verbose output to the icinga log file', 'value' => intval($logging->get('verbose', 0)) === 1 ) ); - $this->addElement( - new Note( - array( - 'name' => 'note_logging_app_verbose', - 'value'=> 'Check to write more verbose output to the icinga log file' - ) - ) - ); - $this->addElement( 'checkbox', - 'logging_use_debug', + 'logging_debug_enable', array( 'label' => 'Use debug log', 'required' => true, + 'helptext' => 'Check to write a seperate debug log (Warning: This file can grow very big)', 'value' => $this->shouldDisplayDebugLog($debug) ) ); - $this->addElement( - new Note( - array( - 'name' => 'note_logging_use_debug', - 'value'=> 'Check to write a seperate debug log (Warning: This file can grow very big)' - ) - ) - ); - $textLoggingDebugPath = new Zend_Form_Element_Text( array( - 'name' => 'logging_debug_path', + 'name' => 'logging_debug_target', 'label' => 'Debug log path', - 'required' => true, + 'required' => $this->shouldDisplayDebugLog($debug), 'condition' => $this->shouldDisplayDebugLog($debug), - 'value' => $debug->get('target') - ) - ); - $loggingPathNote = new Note( - array( - 'name' => 'note_logging_debug_path', - 'value' => 'Set the path to the debug log', - 'condition' => $this->shouldDisplayDebugLog($debug) + 'value' => $debug->get('target', $this->getBaseDir() . '/var/log/icinga2.debug.log'), + 'helptext' => 'Set the path to the debug log' ) ); + $textLoggingDebugPath->addValidator(new WritablePathValidator()); + $decorator = new ConditionalHidden(); $this->addElement($textLoggingDebugPath); - $this->addElement($loggingPathNote); - $textLoggingDebugPath->addDecorator($decorator); - $loggingPathNote->addDecorator($decorator); - $this->enableAutoSubmit(array('logging_use_debug', 'logging_enable')); + + $this->enableAutoSubmit(array('logging_debug_enable', 'logging_enable')); $this->setSubmitLabel('Save changes'); } + + /** + * Return a Zend_Config object containing the state defined in this form + * + * @return Zend_Config The config defined in this form + */ + public function getConfig() + { + if ($this->config === null) { + $this->config = new Zend_Config(array()); + } + if ($this->config->logging === null) { + $this->config->logging = new Zend_Config(array()); + } + if ($this->config->logging->debug === null) { + $this->config->logging->debug = new Zend_Config(array()); + + } + + $values = $this->getValues(); + $cfg = $this->config->toArray(); + + $cfg['logging']['enable'] = intval($values['logging_enable']); + $cfg['logging']['type'] = 'stream'; + $cfg['logging']['verbose'] = $values['logging_app_verbose']; + $cfg['logging']['target'] = $values['logging_app_target']; + + $cfg['logging']['debug']['enable'] = intval($values['logging_debug_enable']); + $cfg['logging']['debug']['type'] = 'stream'; + $cfg['logging']['debug']['target'] = $values['logging_debug_target']; + return new Zend_Config($cfg); + } } diff --git a/application/views/scripts/config/authentication.phtml b/application/views/scripts/config/authentication.phtml index 1e041a914..8e4f8e864 100644 --- a/application/views/scripts/config/authentication.phtml +++ b/application/views/scripts/config/authentication.phtml @@ -13,6 +13,21 @@ $createDbBackend = Url::fromPath( ?> tabs->render($this); ?> +form->getErrorMessages(); + if (!empty($errors)): +?> +
+

There are errors in your configuration

+
+
    + +
  • + +
+
+ +
Create a new LDAP authentication backend
Create a new DB authentication backend diff --git a/application/views/scripts/config/authentication/modify.phtml b/application/views/scripts/config/authentication/modify.phtml new file mode 100644 index 000000000..23d46b9c5 --- /dev/null +++ b/application/views/scripts/config/authentication/modify.phtml @@ -0,0 +1,4 @@ +tabs->render($this); ?> + +

Create new backend

+form ?> \ No newline at end of file diff --git a/application/views/scripts/config/logging.phtml b/application/views/scripts/config/logging.phtml index aa8ca9f49..b09c06fb9 100644 --- a/application/views/scripts/config/logging.phtml +++ b/application/views/scripts/config/logging.phtml @@ -1,3 +1,17 @@ tabs->render($this); ?> +form->getErrorMessages(); ?> + +
+

Errors occured when trying to save the project

+

+ The following errors occured when trying to save the configuration: +

+
    + +
  • escape($error) ?>
  • + +
+
+ form ?> \ No newline at end of file diff --git a/application/views/scripts/config/authentication/show-configuration.phtml b/application/views/scripts/config/show-configuration.phtml similarity index 57% rename from application/views/scripts/config/authentication/show-configuration.phtml rename to application/views/scripts/config/show-configuration.phtml index afc255224..87a86b51f 100644 --- a/application/views/scripts/config/authentication/show-configuration.phtml +++ b/application/views/scripts/config/show-configuration.phtml @@ -1,14 +1,14 @@ tabs->render($this); ?>
-

Saving authentication.ini failed

+

Saving escape($this->file); ?>.ini failed


- Your authentication configuration couldn't be stored (error: "exceptionMessage; ?>"). This could have one or more - of the following reasons: + Your escape($this->file); ?> configuration couldn't be stored (error: "exceptionMessage; ?>").
+ This could have one or more of the following reasons:

    -
  • You don't have file-system permissions to write to the authentication.ini file
  • +
  • You don't have file-system permissions to write to the escape($this->file); ?>.ini file
  • Something went wrong while writing the file
  • There's an application error preventing you from persisting the configuration
@@ -17,7 +17,7 @@

Details can be seen in your application log (if you don't have access to this file, call your administrator in this case).
- In case you can access the configuration file (config/authentication.ini) by yourself, you can open it and + In case you can access the configuration file (config/escape($this->file); ?>.ini) by yourself, you can open it and insert the config manually:

diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index d31c235d3..14209a7c9 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -25,7 +25,7 @@ namespace Icinga\Web; -use \Icinga\Web\Form\Decorator\ConditionalHidden; +use \Icinga\Web\Form\Decorator\HelpText; use \Zend_Controller_Request_Abstract; use \Zend_Form_Element_Submit; use \Zend_Form_Element_Reset; @@ -197,7 +197,7 @@ abstract class Form extends Zend_Form if (!$this->getAction() && $this->getRequest()) { $this->setAction($this->getRequest()->getRequestUri()); } - + $this->enableAdditionalDecorators(); $this->created = true; } } @@ -387,6 +387,13 @@ abstract class Form extends Zend_Form return $token === hash('sha256', $this->getSessionId() . $seed); } + public function enableAdditionalDecorators() + { + foreach ($this->getElements() as $element) { + $element->addDecorator(new HelpText()); + } + } + /** * Generate a new (seed, token) pair * diff --git a/library/Icinga/Web/Form/Decorator/ConditionalHidden.php b/library/Icinga/Web/Form/Decorator/ConditionalHidden.php index e3356d44c..d39b2f157 100644 --- a/library/Icinga/Web/Form/Decorator/ConditionalHidden.php +++ b/library/Icinga/Web/Form/Decorator/ConditionalHidden.php @@ -26,6 +26,9 @@ */ // {{{ICINGA_LICENSE_HEADER}}} +namespace Icinga\Web\Form\Decorator; + +use \Zend_Form_Decorator_Abstract; /** * Decorator to hide elements using a >noscript< tag instead of @@ -35,15 +38,11 @@ * (who can then automatically refresh their pages) but show them in * case JavaScript is disabled */ - -namespace Icinga\Web\Form\Decorator; - -use \Zend_Form_Decorator_Abstract; - class ConditionalHidden extends Zend_Form_Decorator_Abstract { /** - * Generates a html number input + * Generate a field that will be wrapped in