diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index 4f7adf963..5e6269ce8 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -31,6 +31,13 @@ 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; +use \Icinga\Form\Config\Authentication\DbBackendForm; +use \Icinga\Form\Config\LoggingForm; +use \Icinga\Config\PreservingIniWriter; /** * Application wide controller for application preferences @@ -50,16 +57,30 @@ class ConfigController extends BaseConfigController 'index' => new Tab( array( 'name' => 'index', - 'title' => 'Configuration', - 'iconCls' => 'wrench', + 'title' => 'Application', 'url' => Url::fromPath('/config') ) ), + + 'authentication' => new Tab( + array( + 'name' => 'auth', + 'title' => 'Authentication', + 'url' => Url::fromPath('/config/authentication') + ) + ), + + 'logging' => new Tab( + array( + 'name' => 'logging', + 'title' => 'Logging', + 'url' => Url::fromPath('/config/logging') + ) + ), 'modules' => new Tab( array( 'name' => 'modules', 'title' => 'Modules', - 'iconCls' => 'puzzle-piece', 'url' => Url::fromPath('/config/moduleoverview') ) ) @@ -68,11 +89,39 @@ class ConfigController extends BaseConfigController /** * Index action, entry point for configuration - * @TODO: Implement configuration interface (#3777) */ public function indexAction() { + $form = new GeneralForm(); + $form->setConfiguration(IcingaConfig::app()); + $form->setRequest($this->_request); + if ($form->isSubmittedAndValid()) { + 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; + } + $this->redirectNow('/config/logging'); + } + $this->view->form = $form; } /** @@ -106,5 +155,110 @@ class ConfigController extends BaseConfigController $manager->disableModule($this->_getParam('name')); $this->redirectNow('config/moduleoverview?_render=body'); } + + /** + * 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()) { + $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; + } + + /** + * 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; + } + if (!$this->writeAuthenticationFile($backendCfg)) { + return; + } + $this->redirectNow('/config/authentication'); + + } + $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 supplied 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/controllers/PreferenceController.php b/application/controllers/PreferenceController.php index d8bacbf1b..22dc8767f 100644 --- a/application/controllers/PreferenceController.php +++ b/application/controllers/PreferenceController.php @@ -29,13 +29,25 @@ use \Icinga\Web\Controller\BasePreferenceController; use \Icinga\Web\Widget\Tab; +use \Icinga\Application\Config as IcingaConfig; use \Icinga\Web\Url; +use \Icinga\Form\Preference\GeneralForm; /** * Application wide preference controller for user preferences */ class PreferenceController extends BasePreferenceController { + + /** + * This controller modifies the session + * + * @var bool + * + * @see \Icinga\Web\Controller\ActionController::$modifiesSession + */ + protected $modifiesSession = true; + /** * Create tabs for this preference controller * @@ -48,9 +60,8 @@ class PreferenceController extends BasePreferenceController return array( 'preference' => new Tab( array( - 'name' => 'preferences', - 'iconCls' => 'user', - 'title' => 'Preferences', + 'name' => 'general', + 'title' => 'General settings', 'url' => Url::fromPath('/preference') ) ) @@ -58,11 +69,39 @@ class PreferenceController extends BasePreferenceController } /** - * @TODO: Implement User preferences (feature #5425) + * General settings for date and time */ public function indexAction() { + $form = new GeneralForm(); + $form->setConfiguration(IcingaConfig::app()); + $form->setRequest($this->getRequest()); + if ($form->isSubmittedAndValid()) { + $preferences = $form->getPreferences(); + $userPreferences = $this->getRequest()->getUser()->getPreferences(); + $userPreferences->startTransaction(); + foreach ($preferences as $key => $value) { + if (!$value) { + $userPreferences->remove($key); + } else { + $userPreferences->set($key, $value); + } + } + try { + $userPreferences->commit(); + $this->view->success = true; + + // recreate form to show new values + $form = new GeneralForm(); + $form->setConfiguration(IcingaConfig::app()); + $form->setRequest($this->getRequest()); + + } catch (Exception $e) { + $this->view->exceptionMessage = $e->getMessage(); + } + } + $this->view->form = $form; } } // @codingStandardsIgnoreEnd diff --git a/application/forms/Config/Authentication/BaseBackendForm.php b/application/forms/Config/Authentication/BaseBackendForm.php new file mode 100644 index 000000000..01b0acb43 --- /dev/null +++ b/application/forms/Config/Authentication/BaseBackendForm.php @@ -0,0 +1,139 @@ + + * @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 \Zend_Config; +use \Icinga\Application\Config as IcingaConfig; +use \Icinga\Application\Icinga; +use \Icinga\Application\Logger; +use \Icinga\Application\DbAdapterFactory; +use \Icinga\Web\Form; + +/** + * 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; + + /** + * The resources to use instead of the factory provided ones (use for testing) + * + * @var Zend_Config + */ + private $resources; + + /** + * 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 key-value sub-array + * + * @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..df9d241a6 --- /dev/null +++ b/application/forms/Config/Authentication/DbBackendForm.php @@ -0,0 +1,121 @@ + + * @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 \Zend_Config; +use \Icinga\Application\Config as IcingaConfig; +use \Icinga\Application\Icinga; +use \Icinga\Application\Logger; +use \Icinga\Application\DbAdapterFactory; +use \Icinga\Web\Form; + +/** + * 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', + 'helptext' => 'The name of this authentication provider', + 'value' => $this->getBackendName() + ) + ); + + $this->addElement( + 'select', + 'backend_' . $name . '_resource', + array( + '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() + ) + ); + + $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..d186712f0 --- /dev/null +++ b/application/forms/Config/Authentication/LdapBackendForm.php @@ -0,0 +1,163 @@ + + * @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 \Zend_Config; +use \Icinga\Application\Config as IcingaConfig; +use \Icinga\Application\Icinga; +use \Icinga\Application\Logger; +use \Icinga\Application\DbAdapterFactory; +use \Icinga\Web\Form; + +/** + * 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', + 'helptext' => 'The name of this authentication backend', + 'value' => $this->getBackendName() + ) + ); + + $this->addElement( + 'text', + 'backend_' . $name . '_hostname', + array( + 'label' => 'LDAP Server Host', + 'allowEmpty' => false, + 'value' => $backend->get('hostname', 'localhost'), + 'helptext' => 'The hostname or address of the LDAP server to use for authentication', + 'required' => true + ) + ); + + $this->addElement( + 'text', + 'backend_' . $name . '_root_dn', + array( + 'label' => 'LDAP Root DN', + '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 + ) + ); + + $this->addElement( + 'text', + 'backend_' . $name . '_bind_dn', + 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 + ) + ); + + $this->addElement( + 'password', + 'backend_' . $name . '_bind_pw', + array( + '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 . '_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 . '_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 + ) + ); + + $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'), + '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 new file mode 100644 index 000000000..1ec5068a8 --- /dev/null +++ b/application/forms/Config/AuthenticationForm.php @@ -0,0 +1,396 @@ + + * @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 \Zend_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; + +/** + * 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 +{ + /** + * 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; + + /** + * An array containing all provider subforms currently displayed + * + * @var array + */ + private $backendForms = array(); + + + /** + * 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; + } + + /** + * Set the configuration to be used for this form + * + * @param IcingaConfig $cfg + */ + public function setConfiguration($cfg) + { + $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( + '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'; + } + + /** + * 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'))); + $formClass = '\Icinga\Form\Config\Authentication\\' . $type . 'BackendForm'; + if (!class_exists($formClass)) { + Logger::error('Unsupported backend found in authentication configuration: ' . $backend->get('backend')); + return; + } + + $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 $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)) + ); + if ($priorities['up'] != join(',', $order)) { + $this->addElement( + 'button', + 'priority' . $name . '_up', + array( + 'name' => 'priority', + 'label' => 'Move up in authentication order', + 'value' => $priorities['up'], + 'type' => 'submit' + ) + ); + $formEls[] = 'priority' . $name . '_up'; + } + if ($priorities['down'] != join(',', $order)) { + $this->addElement( + 'button', + 'priority' . $name . '_down', + array( + 'name' => 'priority', + 'label' => 'Move down in authentication order', + 'value' => $priorities['down'], + 'type' => 'submit' + ) + ); + $formEls[] = 'priority' . $name . '_down'; + } + + 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 self + * + * @see Zend_Form::populate() + */ + public function populate(array $values) + { + $last_priority = $this->getValue('current_priority'); + parent::populate($values); + $this->getElement('current_priority')->setValue($last_priority); + return $this; + } + + /** + * 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( + 'priority', + $request->getParam('current_priority', null) + ); + + if ($order === null) { + $order = array_keys($this->config->toArray()); + } else { + $order = explode(',', $order); + } + + 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', + array( + 'name' => 'current_priority', + 'value' => join(',', $this->getAuthenticationOrder()) + ) + ); + } + + /** + * Create the authentication provider configuration form + * + * @see IcingaForm::create() + */ + public function create() + { + $order = $this->getAuthenticationOrder(); + + 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; + } + if (!$this->isMarkedForDeletion($this->filterName($name))) { + $this->addProviderForm($name, $backend); + $this->addPriorityButtons($name, $order); + } + } + + $this->addPersistentState(); + $this->enableConditionalDecorator(); + $this->setSubmitLabel('Save Changes'); + } + + /** + * Return the configuration state defined by this form + * + * @return array + */ + public function getConfig() + { + $result = array(); + foreach ($this->backendForms as $name) { + + $name->populate($this->getRequest()->getParams()); + $result += $name->getConfig(); + + } + return $result; + } + + /** + * Enable the "ConditionalHidden" Decorator for all elements in this form + * + * @see ConditionalHidden + */ + private function enableConditionalDecorator() + { + foreach ($this->getElements() as $element) { + $element->addDecorator(new ConditionalHidden()); + } + } + + /** + * 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; + for ($i=0; $i + * $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; + for ($i=0; $i + * @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 \DateTimeZone; +use \Zend_Config; +use \Zend_Form_Element_Text; +use \Zend_Form_Element_Select; +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; + +/** + * 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; + + + /** + * 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 + * + * @param IcingaConfig $cfg + */ + public function setConfiguration($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; + } + + /** + * 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 + * + * @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, + 'helptext' => 'Set true to show more detailed errors and disable certain optimizations in order to ' + . 'make debugging easier.', + 'tooltip' => 'More verbose output', + 'value' => $env === 'development' + ) + ); + + } + + /** + * 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; + } + $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', + 'timezone', + array( + 'label' => 'Default Application Timezone', + 'required' => true, + 'multiOptions' => $tzList, + 'helptext' => $helptext, + 'value' => $cfg->get('timezone', date_default_timezone_get()) + ) + ); + } + + /** + * 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, + 'helptext' => 'The moduleFolder directive is currently not used anywhere but ' + . 'configureable via the frontend and ini. With feature #4607 moduleFolder ' + . 'will be replaced with a configuration directive for locations of ' + . 'installed modules', + 'value' => $cfg->get('moduleFolder', $this->getConfigDir() . '/config/enabledModules') + ) + ); + } + + /** + * 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'; + + $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($txtDefaultDateFormat); + $txtDefaultDateFormat->addValidator(new DateFormatValidator()); + + $txtDefaultTimeFormat = new Zend_Form_Element_Text( + array( + 'name' => 'time_format', + 'label' => 'Time Format', + 'required' => true, + '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); + } + + /** + * 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'); + 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' => 'User Preference Filepath', + 'required' => $backend === 'ini', + 'condition' => $backend === 'ini', + '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_Select( + array( + 'name' => 'preferences_db_resource', + 'label' => 'Database Connection', + 'required' => $backend === 'db', + 'condition' => $backend === 'db', + 'value' => $cfg->get('resource'), + 'multiOptions' => $backends + ) + ); + $validator = new WritablePathValidator(); + $validator->setRequireExistence(); + $txtPreferencesIniPath->addValidator($validator); + $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 + * + * @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()); + } + $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'); + } + + /** + * Return an Zend_Config object containing the configuration set in this form + * + * @return Zend_Config + */ + public function getConfig() + { + if ($this->config === null) { + $this->config = new Zend_Config(array()); + } + 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; + $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']; + + $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 new file mode 100644 index 000000000..bf426f342 --- /dev/null +++ b/application/forms/Config/LoggingForm.php @@ -0,0 +1,227 @@ + + * @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 \Zend_Config; +use \Zend_Form_Element_Text; +use \Icinga\Application\Config as IcingaConfig; +use \Icinga\Application\Icinga; +use \Icinga\Web\Form; +use \Icinga\Web\Form\Validator\WritablePathValidator; +use \Icinga\Web\Form\Decorator\ConditionalHidden; + +/** + * 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; + + /** + * 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 + * + * 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; + } + + /** + * 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 + * + * 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_debug_enable', null); + if ($debugParam !== null) { + return intval($debugParam) === 1; + } else { + return intval($config->get('enable', 0)) === 1; + } + + } + + /** + * Create this logging configuration form + * + * @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()); + } + + $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') + ) + ); + $txtLogPath->addValidator(new WritablePathValidator()); + $this->addElement($txtLogPath); + + $this->addElement( + 'checkbox', + 'logging_app_verbose', + 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( + 'checkbox', + '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) + ) + ); + + $textLoggingDebugPath = new Zend_Form_Element_Text( + array( + 'name' => 'logging_debug_target', + 'label' => 'Debug Log Path', + 'required' => $this->shouldDisplayDebugLog($debug), + '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); + $textLoggingDebugPath->addDecorator($decorator); + + + $this->enableAutoSubmit(array('logging_debug_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'] = 1; + $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/forms/Preference/GeneralForm.php b/application/forms/Preference/GeneralForm.php new file mode 100644 index 000000000..306439a17 --- /dev/null +++ b/application/forms/Preference/GeneralForm.php @@ -0,0 +1,243 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Form\Preference; + +use \DateTimeZone; +use \Zend_Config; +use \Zend_Form_Element_Text; +use \Zend_Form_Element_Select; +use \Icinga\Application\Config as IcingaConfig; +use \Icinga\Application\Icinga; +use \Icinga\Application\DbAdapterFactory; +use \Icinga\User\Preferences; +use \Icinga\Web\Form; +use \Icinga\Web\Form\Validator\TimeFormatValidator; +use \Icinga\Web\Form\Validator\DateFormatValidator; + +/** + * General user preferences + */ +class GeneralForm extends Form +{ + /** + * The configuration to use for populating this form + * + * @var IcingaConfig + */ + private $config; + + /** + * The preference object to use instead of the one from the user (used for testing) + * + * @var Zend_Config + */ + private $preferences; + + /** + * Set the configuration to be used for this form when no preferences are set yet + * + * @param IcingaConfig $cfg + */ + public function setConfiguration($cfg) + { + $this->config = $cfg; + } + + /** + * Set preferences to be used instead of the one from the user object (used for testing) + * + * @param Zend_Config $prefs + */ + public function setUserPreferences($prefs) + { + $this->preferences = $prefs; + } + + /** + * Return the preferences of the user or the overwritten ones + * + * @return Zend_Config + */ + public function getUserPreferences() + { + if ($this->preferences) { + return $this->preferences; + } + return $this->getRequest()->getUser()->getPreferences(); + } + + /** + * Add a select field for setting the user's timezone. + * + * Possible values are determined by DateTimeZone::listIdentifiers + * Also, a 'use default format' checkbox is added in order to allow a user to discard his overwritten setting + * + * @param Zend_Config $cfg The "global" section of the config.ini to be used as default valuse + */ + private function addTimezoneSelection(Zend_Config $cfg) + { + $tzList = array(); + foreach (DateTimeZone::listIdentifiers() as $tz) { + $tzList[$tz] = $tz; + } + $helptext = 'Use the following timezone for dates and times'; + $prefs = $this->getUserPreferences(); + $useGlobalTimezone = $this->getRequest()->getParam('default_timezone', !$prefs->has('app.timezone')); + + $selectTimezone = new Zend_Form_Element_Select( + array( + 'name' => 'timezone', + 'label' => 'Your Current Timezone', + 'required' => !$useGlobalTimezone, + 'multiOptions' => $tzList, + 'helptext' => $helptext, + 'value' => $prefs->get('app.timezone', $cfg->get('timezone', date_default_timezone_get())) + ) + ); + $this->addElement( + 'checkbox', + 'default_timezone', + array( + 'label' => 'Use Default Timezone', + 'value' => !$prefs->has('app.timezone'), + 'required' => true + ) + ); + if ($useGlobalTimezone) { + $selectTimezone->setAttrib('disabled', 1); + } + $this->addElement($selectTimezone); + $this->enableAutoSubmit(array('default_timezone')); + } + + /** + * Add text fields for the date and time format used for this user + * + * Also, a 'use default format' checkbox is added in order to allow a user to discard his overwritten setting + * + * @param Zend_Config $cfg The "global" section of the config.ini to be used as default values + */ + private function addDateFormatSettings(Zend_Config $cfg) + { + $prefs = $this->getUserPreferences(); + $useGlobalDateFormat = $this->getRequest()->getParam('default_date_format', !$prefs->has('app.dateFormat')); + $useGlobalTimeFormat = $this->getRequest()->getParam('default_time_format', !$prefs->has('app.timeFormat')); + + $phpUrl = '' + . 'the official PHP documentation'; + + + $this->addElement( + 'checkbox', + 'default_date_format', + array( + 'label' => 'Use Default Date Format', + 'value' => !$prefs->has('app.dateFormat'), + 'required' => true + ) + ); + $txtDefaultDateFormat = new Zend_Form_Element_Text( + array( + 'name' => 'date_format', + 'label' => 'Preferred Date Format', + 'helptext' => 'Display dates according to this format. See ' . $phpUrl . ' for possible values', + 'required' => !$useGlobalDateFormat, + 'value' => $prefs->get('app.dateFormat', $cfg->get('dateFormat', 'd/m/Y')) + ) + ); + + $this->addElement($txtDefaultDateFormat); + $txtDefaultDateFormat->addValidator(new DateFormatValidator()); + if ($useGlobalDateFormat) { + $txtDefaultDateFormat->setAttrib('disabled', '1'); + } + + $this->addElement( + 'checkbox', + 'default_time_format', + array( + 'label' => 'Use Default Time Format', + 'value' => !$prefs->has('app.timeFormat'), + 'required' => !$useGlobalTimeFormat + ) + ); + $txtDefaultTimeFormat = new Zend_Form_Element_Text( + array( + 'name' => 'time_format', + 'label' => 'Preferred Time Format', + 'required' => !$useGlobalTimeFormat, + 'helptext' => 'Display times according to this format. See ' . $phpUrl . ' for possible values', + 'value' => $prefs->get('app.timeFormat', $cfg->get('timeFormat', 'g:i A')) + ) + ); + $txtDefaultTimeFormat->addValidator(new TimeFormatValidator()); + $this->addElement($txtDefaultTimeFormat); + if ($useGlobalTimeFormat) { + $txtDefaultTimeFormat->setAttrib('disabled', '1'); + } + + $this->enableAutoSubmit(array('default_time_format', 'default_date_format')); + } + + /** + * Create the general form, using the global configuration as fallback values for preferences + * + * @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->addTimezoneSelection($global); + $this->addDateFormatSettings($global); + + $this->setSubmitLabel('Save Changes'); + } + + /** + * Return an array containing the preferences set in this form + * + * @return array + */ + public function getPreferences() + { + $values = $this->getValues(); + return array( + 'app.timezone' => $values['timezone'], + 'app.dateFormat' => $values['date_format'], + 'app.timeFormat' => $values['time_format'] + ); + } +} diff --git a/application/views/scripts/config/authentication.phtml b/application/views/scripts/config/authentication.phtml new file mode 100644 index 000000000..8e4f8e864 --- /dev/null +++ b/application/views/scripts/config/authentication.phtml @@ -0,0 +1,35 @@ + 'ldap') +)->getAbsoluteUrl(); + +$createDbBackend = Url::fromPath( + '/config/createAuthenticationBackend', + array('type' => 'db') +)->getAbsoluteUrl(); + +?> +tabs->render($this); ?> + +form->getErrorMessages(); + if (!empty($errors)): +?> +
+

There are errors in your configuration

+
+
    + +
  • + +
+
+ + + +form ?> \ No newline at end of file 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/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..f1b7ef3d1 --- /dev/null +++ b/application/views/scripts/config/logging.phtml @@ -0,0 +1,19 @@ +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/show-configuration.phtml b/application/views/scripts/config/show-configuration.phtml new file mode 100644 index 000000000..87a86b51f --- /dev/null +++ b/application/views/scripts/config/show-configuration.phtml @@ -0,0 +1,30 @@ +tabs->render($this); ?> +
+
+

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

+
+

+ 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 escape($this->file); ?>.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/escape($this->file); ?>.ini) by yourself, you can open it and + insert the config manually: + +

+

+

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

\ No newline at end of file diff --git a/application/views/scripts/preference/index.phtml b/application/views/scripts/preference/index.phtml index 81dc0fb8c..a718a201c 100644 --- a/application/views/scripts/preference/index.phtml +++ b/application/views/scripts/preference/index.phtml @@ -1,3 +1,15 @@ tabs->render($this); ?> -

Preferences

\ No newline at end of file +success)) : ?> +
+

Preferences updated sucessfully

+
+ + +exceptionMessage)): ?> +
+

Could not update preferences due to an internal exception (exceptionMessage ?>)

+
+ + +form ?> \ No newline at end of file diff --git a/config/config.ini b/config/config.ini index 1f57302d0..e10e68a6f 100755 --- a/config/config.ini +++ b/config/config.ini @@ -1,30 +1,36 @@ [global] -environment = development -timezone = "Europe/Berlin" -indexModule = monitoring -indexController = dashboard -moduleFolder = "/etc/icinga2-web/enabledModules" -dateFormat = "d/m/Y" -timeFormat = "g:i A" +environment = "development" +timezone = "Europe/Berlin" +indexModule = "monitoring" +indexController = "dashboard" +; The moduleFolder directive is currently not used anywhere but configureable +; via the frontend and this file. With feature #4607 moduleFolder will +; be replaced with a configuration directive for locations of +; installed modules +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 +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 +debug.enable = "1" +debug.type = "stream" +debug.target = "/tmp/icinga2.debug.log" ; Use ini store to store preferences on local disk [preferences] -type=ini +type = "ini" ; Use database to store preference into mysql or postgres ;[preferences] ;type=db ;resource=icingaweb-mysql + +configPath = "/vagrant/config/preferences" diff --git a/library/Icinga/Application/Web.php b/library/Icinga/Application/Web.php index 4bba5fb6b..4ff92ede9 100644 --- a/library/Icinga/Application/Web.php +++ b/library/Icinga/Application/Web.php @@ -35,6 +35,7 @@ use Icinga\User; use Icinga\Web\Request; use Zend_Controller_Front; use Zend_Layout; +use Zend_Config; use Zend_Paginator; use Zend_View_Helper_PaginationControl; use Zend_Controller_Action_HelperBroker; @@ -206,8 +207,9 @@ class Web extends ApplicationBootstrap /** * Create user object and inject preference interface * - * @throws ConfigurationError * @return User + * + * @throws ConfigurationError */ private function setupUser() { @@ -225,7 +227,9 @@ class Web extends ApplicationBootstrap $user = $authenticationManager->getUser(); - $this->getConfig()->preferences->configPath = $this->getConfigDir('preferences'); + $this->getConfig()->preferences->configPath = Config::app() + ->get('preferences', new Zend_Config(array())) + ->get('configPath', $this->getConfigDir('preferences')); $preferenceStore = StoreFactory::create( $this->getConfig()->preferences, @@ -325,7 +329,7 @@ class Web extends ApplicationBootstrap /** * Configure pagination settings - + * * @return self */ private function setupPagination() diff --git a/library/Icinga/Config/IniEditor.php b/library/Icinga/Config/IniEditor.php index afb89d44a..c24186ca0 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,14 @@ 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 mixed $section The section to use * - * @return int The line of the array element. + * @return int 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 +132,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 +145,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 +160,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 +183,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 +196,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) { @@ -225,10 +223,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]; /* @@ -259,8 +258,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) { @@ -270,8 +269,8 @@ class IniEditor /** * Update the line $lineNr * - * @param int $lineNr The line number of the target line - * @param string $content The content to replace + * @param int $lineNr The line number of the target line + * @param string $toInsert The new line content */ private function updateLine($lineNr, $content) { @@ -285,9 +284,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) { @@ -297,14 +296,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) { @@ -314,10 +313,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) { @@ -327,8 +326,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) { @@ -338,14 +338,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) { @@ -373,15 +373,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; } @@ -389,15 +389,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; } @@ -406,9 +406,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) { @@ -425,13 +425,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) { @@ -452,7 +453,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() { @@ -462,9 +463,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 */ @@ -477,12 +478,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); @@ -491,9 +490,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/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php b/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php index 272b4b34d..e90c89000 100644 --- a/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php +++ b/library/Icinga/Protocol/Commandpipe/Transport/SecureShell.php @@ -28,12 +28,14 @@ namespace Icinga\Protocol\Commandpipe\Transport; -use Icinga\Application\Logger; +use \RuntimeException; +use \Zend_Config; +use \Icinga\Application\Logger; /** - * Command pipe transport class that uses ssh for connecting to a remote filesystem with the icinga.cmd pipe - * The remote host must have KeyAuth enabled for this user + * Command pipe transport class that uses ssh for connecting to a remote filesystem with the icinga.cmd pipe * + * The remote host must have KeyAuth enabled for this user */ class SecureShell implements Transport { @@ -66,21 +68,27 @@ class SecureShell implements Transport private $user = null; /** - * @see Transport::setEndpoint() + * Overwrite the target file of this Transport class using the given config from instances.ini * + * @param Zend_Config $config + * + * @see Transport::setEndpoint() */ - public function setEndpoint(\Zend_Config $config) + public function setEndpoint(Zend_Config $config) { - $this->host = isset($config->host) ? $config->host : "localhost"; + $this->host = isset($config->host) ? $config->host : 'localhost'; $this->port = isset($config->port) ? $config->port : 22; $this->user = isset($config->user) ? $config->user : null; - $this->password = isset($config->password) ? $config->password : null; - $this->path = isset($config->path) ? $config->path : "/usr/local/icinga/var/rw/icinga.cmd"; + $this->path = isset($config->path) ? $config->path : '/usr/local/icinga/var/rw/icinga.cmd'; } /** - * @see Transport::send() + * Write the given external command to the command pipe * + * @param string $command + * + * @throws RuntimeException When the command could not be sent to the remote Icinga host + * @see Transport::send() */ public function send($command) { @@ -114,11 +122,11 @@ class SecureShell implements Transport Logger::debug("Return code %s: %s ", $retCode, $output); if ($retCode != 0) { - $msg = 'Could not send command to remote icinga host: ' + $msg = 'Could not send command to remote Icinga host: ' . implode(PHP_EOL, $output) . " (returncode $retCode)"; Logger::error($msg); - throw new \RuntimeException($msg); + throw new RuntimeException($msg); } } } diff --git a/library/Icinga/Protocol/Commandpipe/Transport/Transport.php b/library/Icinga/Protocol/Commandpipe/Transport/Transport.php index d249c86e0..c33190092 100644 --- a/library/Icinga/Protocol/Commandpipe/Transport/Transport.php +++ b/library/Icinga/Protocol/Commandpipe/Transport/Transport.php @@ -28,23 +28,24 @@ namespace Icinga\Protocol\Commandpipe\Transport; +use \Zend_Config; + /** * Interface for Transport classes handling the concrete access to the command pipe - * */ interface Transport { /** * Overwrite the target file of this Transport class using the given config from instances.ini * - * @param \Zend_Config $config A configuration file containing a 'path' setting + * @param Zend_Config $config A configuration file containing a 'path' setting */ - public function setEndpoint(\Zend_Config $config); + public function setEndpoint(Zend_Config $config); /** * Write the given external command to the command pipe * - * @param string $message The command to send, without the timestamp (this will be added here) + * @param string $message The command to send, without the timestamp (this will be added here) */ public function send($message); } diff --git a/library/Icinga/Web/Controller/ActionController.php b/library/Icinga/Web/Controller/ActionController.php index 5698de2d0..d5b06b61d 100755 --- a/library/Icinga/Web/Controller/ActionController.php +++ b/library/Icinga/Web/Controller/ActionController.php @@ -28,26 +28,23 @@ namespace Icinga\Web\Controller; +use \Zend_Controller_Action as ZfController; +use \Zend_Controller_Request_Abstract as ZfRequest; +use \Zend_Controller_Response_Abstract as ZfResponse; +use \Zend_Controller_Action_HelperBroker as ZfActionHelper; +use \Zend_Layout as ZfLayout; use \Icinga\Authentication\Manager as AuthManager; use \Icinga\Application\Benchmark; use \Icinga\Exception; use \Icinga\Application\Config; use \Icinga\Web\Notification; use \Icinga\Web\Widget\Tabs; -use \Zend_Layout as ZfLayout; -use \Zend_Controller_Action as ZfController; -use \Zend_Controller_Request_Abstract as ZfRequest; -use \Zend_Controller_Response_Abstract as ZfResponse; -use \Zend_Controller_Action_HelperBroker as ZfActionHelper; +use \Icinga\Web\Url; /** * Base class for all core action controllers * * All Icinga Web core controllers should extend this class - * - * @copyright Copyright (c) 2013 Icinga-Web Team - * @author Icinga-Web Team - * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License */ class ActionController extends ZfController { @@ -78,7 +75,7 @@ class ActionController extends ZfController protected $modifiesSession = false; /** - * True if authentication suceeded, otherwise false + * True if authentication succeeded, otherwise false * * @var bool */ @@ -148,7 +145,7 @@ class ActionController extends ZfController /** * Return the tabs * - * @return \Icinga\Widget\Web\Tabs + * @return Tabs */ public function getTabs() { @@ -159,39 +156,15 @@ class ActionController extends ZfController /** * Translate the given string with the global translation catalog * - * @param string $string The string that should be translated + * @param string $string The string that should be translated * - * @return string + * @return string */ public function translate($string) { return t($string); } - /** - * Whether the current user has the given permission - * - * TODO: This has not been implemented yet (Feature #4111) - * - * @return bool - */ - final protected function hasPermission($uri) - { - return true; - } - - /** - * Assert the current user has the given permission - * - * TODO: This has not been implemented yet (Feature #4111) - * - * @return self - */ - final protected function assertPermission() - { - return $this; - } - private function redirectToLogin() { $this->_request->setModuleName('default') @@ -202,8 +175,6 @@ class ActionController extends ZfController /** * Prepare action execution by testing for correct permissions and setting shortcuts - * - * @return void */ public function preDispatch() { @@ -229,7 +200,7 @@ class ActionController extends ZfController /** * Redirect to a specific url, updating the browsers URL field * - * @param Url|string $url The target to redirect to + * @param Url|string $url The target to redirect to **/ public function redirectNow($url) { diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 93873e4fd..2b7bf738f 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -26,12 +26,13 @@ namespace Icinga\Web; use \Zend_Controller_Request_Abstract; +use \Zend_Form; 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\Decorator\HelpText; +use \Icinga\Web\Form\InvalidCSRFTokenException; /** * Base class for forms providing CSRF protection, confirmation logic and auto submission @@ -40,6 +41,7 @@ abstract class Form extends Zend_Form { /** * The form's request object + * * @var Zend_Controller_Request_Abstract */ private $request; @@ -48,24 +50,28 @@ abstract class Form extends Zend_Form * Whether this form should NOT add random generated "challenge" tokens that are associated with the user's current * session in order to prevent Cross-Site Request Forgery (CSRF). It is the form's responsibility to verify the * existence and correctness of this token + * * @var bool */ protected $tokenDisabled = false; /** * Name of the CSRF token element (used to create non-colliding hashes) + * * @var string */ private $tokenElementName = 'CSRFToken'; /** * Flag to indicate that form is already build + * * @var bool */ private $created = false; /** * Session id used for CSRF token generation + * * @var string */ private $sessionId; @@ -94,6 +100,7 @@ abstract class Form extends Zend_Form * If the ID has never been set, the ID from session_id() is returned * * @return string + * * @see session_id() * @see setSessionId() */ @@ -110,7 +117,7 @@ abstract class Form extends Zend_Form * * This method should be used for testing purposes only * - * @param string $sessionId + * @param string $sessionId */ public function setSessionId($sessionId) { @@ -120,7 +127,7 @@ abstract class Form extends Zend_Form /** * Return the HTML element name of the CSRF token field * - * @return string + * @return string */ public function getTokenElementName() { @@ -131,6 +138,7 @@ abstract class Form extends Zend_Form * Render the form to HTML * * @param Zend_View_Interface $view + * * @return string */ public function render(Zend_View_Interface $view = null) @@ -155,7 +163,7 @@ abstract class Form extends Zend_Form /** * Setter for the request * - * @param Zend_Controller_Request_Abstract $request + * @param Zend_Controller_Request_Abstract $request */ public function setRequest(Zend_Controller_Request_Abstract $request) { @@ -165,7 +173,7 @@ abstract class Form extends Zend_Form /** * Getter for the request * - * @return Zend_Controller_Request_Abstract + * @return Zend_Controller_Request_Abstract */ public function getRequest() { @@ -179,6 +187,7 @@ abstract class Form extends Zend_Form */ public function buildForm() { + if ($this->created === false) { $this->initCsrfToken(); $this->create(); @@ -195,7 +204,7 @@ abstract class Form extends Zend_Form if (!$this->getAction() && $this->getRequest()) { $this->setAction($this->getRequest()->getRequestUri()); } - + $this->addElementDecorators(); $this->created = true; } } @@ -203,7 +212,7 @@ abstract class Form extends Zend_Form /** * Setter for the cancel label * - * @param string $cancelLabel + * @param string $cancelLabel */ public function setCancelLabel($cancelLabel) { @@ -228,7 +237,7 @@ abstract class Form extends Zend_Form /** * Setter for the submit label * - * @param string $submitLabel + * @param string $submitLabel */ public function setSubmitLabel($submitLabel) { @@ -255,8 +264,9 @@ abstract class Form extends Zend_Form * * Enables automatic submission of this form once the user edits specific elements * - * @param array $triggerElements The element names which should auto-submit the form - * @throws ProgrammingError When an element is found which does not yet exist + * @param array $triggerElements The element names which should auto-submit the form + * + * @throws ProgrammingError When an element is found which does not yet exist */ final public function enableAutoSubmit($triggerElements) { @@ -279,7 +289,7 @@ abstract class Form extends Zend_Form * Ensures that the current request method is POST, that the form was manually submitted and that the data provided * in the request is valid and gets repopulated in case its invalid. * - * @return bool + * @return bool */ public function isSubmittedAndValid() { @@ -312,7 +322,8 @@ abstract class Form extends Zend_Form * * This method should be used for testing purposes only * - * @param bool $disabled + * @param bool $disabled + * * @see tokenDisabled */ final public function setTokenDisabled($disabled = true) @@ -344,8 +355,9 @@ abstract class Form extends Zend_Form /** * Test the submitted data for a correct CSRF token * - * @param array $checkData The POST data send by the user - * @throws InvalidCSRFTokenException When CSRF Validation fails + * @param array $checkData The POST data send by the user + * + * @throws InvalidCSRFTokenException When CSRF Validation fails */ final public function assertValidCsrfToken(array $checkData) { @@ -363,7 +375,8 @@ abstract class Form extends Zend_Form /** * Check whether the form's CSRF token-field has a valid value * - * @param string $elementValue Value from the form element + * @param string $elementValue Value from the form element + * * @return bool */ private function hasValidCsrfToken($elementValue) @@ -384,10 +397,24 @@ abstract class Form extends Zend_Form return $token === hash('sha256', $this->getSessionId() . $seed); } + /** + * Add element decorators which apply to all elements + * + * Adds `HelpText` decorator + * + * @see HelpText + */ + private function addElementDecorators() + { + foreach ($this->getElements() as $element) { + $element->addDecorator(new HelpText()); + } + } + /** * Generate a new (seed, token) pair * - * @return array + * @return array */ final public function generateCsrfToken() { @@ -400,7 +427,7 @@ abstract class Form extends Zend_Form /** * Return the string representation of the CSRF seed/token pair * - * @return string + * @return string */ final public function generateCsrfTokenAsString() { diff --git a/library/Icinga/Web/Form/Decorator/ConditionalHidden.php b/library/Icinga/Web/Form/Decorator/ConditionalHidden.php new file mode 100644 index 000000000..40dac4a75 --- /dev/null +++ b/library/Icinga/Web/Form/Decorator/ConditionalHidden.php @@ -0,0 +1,60 @@ + + * @license http://www.gnu.org/licenses/gpl-2.0.txt GPL, version 2 + * @author Icinga Development Team + */ +// {{{ICINGA_LICENSE_HEADER}}} + +namespace Icinga\Web\Form\Decorator; + +use \Zend_Form_Decorator_Abstract; + +/** + * 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 + */ +class ConditionalHidden extends Zend_Form_Decorator_Abstract +{ + /** + * Generate a field that will be wrapped in