From 9e40f5f2c7958143a29606e5bce650a93e477ecc Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Wed, 19 Aug 2015 16:09:42 +0200 Subject: [PATCH 01/59] Remove option to skip certificate validation to prevent insecure configurations Skipping certificate validation will allow MITM on every single request and not give any real security over just running unencrypted queries. On top of that, there is no way to configure this behavior from within PHP except of setting environment variables, which is really hacky and has side effects on other requests. fixes #9607 --- .../Config/Resource/LdapResourceForm.php | 16 ----- .../Icinga/Protocol/Ldap/LdapConnection.php | 64 ++++--------------- 2 files changed, 14 insertions(+), 66 deletions(-) diff --git a/application/forms/Config/Resource/LdapResourceForm.php b/application/forms/Config/Resource/LdapResourceForm.php index c6e452d2c..d20ec55af 100644 --- a/application/forms/Config/Resource/LdapResourceForm.php +++ b/application/forms/Config/Resource/LdapResourceForm.php @@ -81,22 +81,6 @@ class LdapResourceForm extends Form ) ); - if (isset($formData['encryption']) && $formData['encryption'] !== 'none') { - // TODO(jom): Do not show this checkbox unless the connection is actually failing due to certificate errors - $this->addElement( - 'checkbox', - 'reqcert', - array( - 'required' => true, - 'label' => $this->translate('Require Certificate'), - 'description' => $this->translate( - 'When checked, the LDAP server must provide a valid and known (trusted) certificate.' - ), - 'value' => 1 - ) - ); - } - $this->addElement( 'text', 'root_dn', diff --git a/library/Icinga/Protocol/Ldap/LdapConnection.php b/library/Icinga/Protocol/Ldap/LdapConnection.php index 841e855da..13036f847 100644 --- a/library/Icinga/Protocol/Ldap/LdapConnection.php +++ b/library/Icinga/Protocol/Ldap/LdapConnection.php @@ -122,13 +122,6 @@ class LdapConnection implements Selectable, Inspectable */ protected $rootDn; - /** - * Whether to load the configuration for strict certificate validation or the one for non-strict validation - * - * @var bool - */ - protected $validateCertificate; - /** * Whether the bind on this connection has already been performed * @@ -176,7 +169,6 @@ class LdapConnection implements Selectable, Inspectable $this->bindPw = $config->bind_pw; $this->rootDn = $config->root_dn; $this->port = $config->get('port', 389); - $this->validateCertificate = (bool) $config->get('reqcert', true); $this->encryption = $config->encryption; if ($this->encryption !== null) { @@ -957,16 +949,9 @@ class LdapConnection implements Selectable, Inspectable $info = new Inspection(''); } - if ($this->encryption === static::STARTTLS || $this->encryption === static::LDAPS) { - $this->prepareTlsEnvironment(); - } - $hostname = $this->hostname; if ($this->encryption === static::LDAPS) { $info->write('Connect using LDAPS'); - if (! $this->validateCertificate) { - $info->write('Skip certificate validation'); - } $hostname = 'ldaps://' . $hostname; } @@ -983,9 +968,6 @@ class LdapConnection implements Selectable, Inspectable if ($this->encryption === static::STARTTLS) { $this->encrypted = true; $info->write('Connect using STARTTLS'); - if (! $this->validateCertificate) { - $info->write('Skip certificate validation'); - } if (! ldap_start_tls($ds)) { throw new LdapException('LDAP STARTTLS failed: %s', ldap_error($ds)); } @@ -998,30 +980,6 @@ class LdapConnection implements Selectable, Inspectable return $ds; } - /** - * Set up how to handle StartTLS connections - * - * @throws LdapException In case the LDAPRC environment variable cannot be set - */ - protected function prepareTlsEnvironment() - { - // TODO: allow variable known CA location (system VS Icinga) - if (Platform::isWindows()) { - putenv('LDAPTLS_REQCERT=never'); - } else { - if ($this->validateCertificate) { - $ldap_conf = $this->getConfigDir('ldap_ca.conf'); - } else { - $ldap_conf = $this->getConfigDir('ldap_nocert.conf'); - } - - putenv('LDAPRC=' . $ldap_conf); // TODO: Does not have any effect - if (getenv('LDAPRC') !== $ldap_conf) { - throw new LdapException('putenv failed'); - } - } - } - /** * Create an LDAP entry * @@ -1103,6 +1061,13 @@ class LdapConnection implements Selectable, Inspectable try { $ds = $this->prepareNewConnection($insp); } catch (Exception $e) { + if ($this->encryption === 'starttls') { + // The Exception does not return any proper error messages in case of certificate errors. Connecting + // by STARTTLS will usually fail at this point when the certificate is unknown, + // so at least try to give some hints. + $insp->write('NOTE: There might be an issue with the chosen encryption. Ensure that the LDAP-Server ' . + 'supports STARTTLS and that the LDAP-Client is configured to accept its certificate.'); + } return $insp->error($e->getMessage()); } @@ -1116,6 +1081,13 @@ class LdapConnection implements Selectable, Inspectable '***' /* $this->bindPw */ ); if (! $success) { + // ldap_error does not return any proper error messages in case of certificate errors. Connecting + // by LDAPS will usually fail at this point when the certificate is unknown, so at least try to give + // some hints. + if ($this->encryption === 'ldaps') { + $insp->write('NOTE: There might be an issue with the chosen encryption. Ensure that the LDAP-Server ' . + ' supports LDAPS and that the LDAP-Client is configured to accept its certificate.'); + } return $insp->error(sprintf('%s failed: %s', $msg, ldap_error($ds))); } $insp->write(sprintf($msg . ' successful')); @@ -1137,12 +1109,4 @@ class LdapConnection implements Selectable, Inspectable } return $insp; } - - /** - * Reset the environment variables set by self::prepareTlsEnvironment() - */ - public function __destruct() - { - putenv('LDAPRC'); - } } From 41d68f6a7417430991d34e27988ced05ecd8b45f Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Wed, 19 Aug 2015 16:36:47 +0200 Subject: [PATCH 02/59] Fix delete comments and downtimes button Fix faulty target names and add additional error checking in case the target DOM Element is not present. fixes #9330 --- .../monitoring/application/views/scripts/list/comments.phtml | 2 +- .../monitoring/application/views/scripts/list/downtimes.phtml | 2 +- public/js/icinga/events.js | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/views/scripts/list/comments.phtml b/modules/monitoring/application/views/scripts/list/comments.phtml index 3287f08e9..44b452f42 100644 --- a/modules/monitoring/application/views/scripts/list/comments.phtml +++ b/modules/monitoring/application/views/scripts/list/comments.phtml @@ -67,7 +67,7 @@ ) : $this->translate('This comment does not expire.'); ?> - + populate( diff --git a/modules/monitoring/application/views/scripts/list/downtimes.phtml b/modules/monitoring/application/views/scripts/list/downtimes.phtml index 1125aa9ba..a45bd0499 100644 --- a/modules/monitoring/application/views/scripts/list/downtimes.phtml +++ b/modules/monitoring/application/views/scripts/list/downtimes.phtml @@ -126,7 +126,7 @@ if (! $this->compact): ?> - + populate( diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 71408d712..4b9576a5d 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -509,6 +509,9 @@ self.icinga.ui.layout1col(); } else { $target = $('#' + targetId); + if (! $target.length) { + self.icinga.logger.warn('Link target "#' + targetId + '" does not exist in DOM.'); + } } } From 690d60672c485243c9c8f9256055ed24d3a7364e Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 14:34:43 +0200 Subject: [PATCH 03/59] Introduce form element decorator Spinner refs #8369 --- library/Icinga/Web/Form.php | 8 ++++ library/Icinga/Web/Form/Decorator/Spinner.php | 45 +++++++++++++++++++ public/css/icinga/forms.less | 19 ++++++++ 3 files changed, 72 insertions(+) create mode 100644 library/Icinga/Web/Form/Decorator/Spinner.php diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 2730f6998..40d6bb409 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -801,9 +801,17 @@ class Form extends Zend_Form && ! array_key_exists('disabledLoadDefaultDecorators', $options) ) { $options['decorators'] = static::$defaultElementDecorators; + if (! isset($options['data-progress-label']) && ($type === 'submit' + || ($type === 'button' && isset($options['type']) && $options['type'] === 'submit')) + ) { + array_splice($options['decorators'], 1, 0, array(array('Spinner', array('separator' => '')))); + } } } else { $options = array('decorators' => static::$defaultElementDecorators); + if ($type === 'submit') { + array_splice($options['decorators'], 1, 0, array(array('Spinner', array('separator' => '')))); + } } $el = parent::createElement($type, $name, $options); diff --git a/library/Icinga/Web/Form/Decorator/Spinner.php b/library/Icinga/Web/Form/Decorator/Spinner.php new file mode 100644 index 000000000..802e001d9 --- /dev/null +++ b/library/Icinga/Web/Form/Decorator/Spinner.php @@ -0,0 +1,45 @@ +getViewRenderer()->view; + } + + /** + * Add a spinner icon to a form element + * + * @param string $content The html rendered so far + * + * @return string The updated html + */ + public function render($content = '') + { + $spinner = '
' + . $this->getView()->icon('spin6') + . '
'; + + switch ($this->getPlacement()) { + case self::APPEND: + return $content . $spinner; + case self::PREPEND: + return $spinner . $content; + } + } +} \ No newline at end of file diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index 82a5f5184..f49c485a6 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -107,6 +107,25 @@ form.inline { display: inline; } +div.spinner { + display: inline-block; + margin-top: 0.2em; + margin-left: 0.25em; + + i { + visibility: hidden; + + &.active { + visibility: visible; + } + + &:before { + margin: 0; // Disables wobbling + .animate(spin 2s infinite linear); + } + } +} + button, .button-like { font-size: 0.9em; font-weight: bold; From 291c712b4422318e91e2c8a01cd6b271d411dd7f Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 14:35:40 +0200 Subject: [PATCH 04/59] Fix form element label style --- public/css/icinga/forms.less | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index f49c485a6..b761f478d 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -271,12 +271,13 @@ form div.element { form label { display: inline-block; + margin-top: 0.3em; margin-right: 1em; font-size: 0.9em; width: 10em; } -form div.element > * { +label ~ * { vertical-align: top; } @@ -314,8 +315,8 @@ select.grant-permissions { width: auto; } -label ~ input, label ~ select { - margin-left: 1.35em; +label ~ input, label ~ select, label ~ textarea { + margin-left: 1.3em; } label + i ~ input, label + i ~ select { From 2fe3c6e5cf8c5b35f913bd140c56c4db0e3f336d Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 14:36:55 +0200 Subject: [PATCH 05/59] events.js: Properly handle the default for param `autosubmit' refs #8369 --- public/js/icinga/events.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 71408d712..109eea255 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -246,6 +246,10 @@ encoding = 'application/x-www-form-urlencoded'; } + if (typeof autosubmit === 'undefined') { + autosubmit = false; + } + if ($button.length === 0) { $button = $('input[type=submit]', $form).add('button[type=submit]', $form).first(); } @@ -271,7 +275,7 @@ if (method === 'GET') { var dataObj = $form.serializeObject(); - if (typeof autosubmit === 'undefined' || ! autosubmit) { + if (! autosubmit) { if ($button.length && $button.attr('name') !== 'undefined') { dataObj[$button.attr('name')] = $button.attr('value'); } @@ -289,7 +293,7 @@ $form.find(':input:not(:disabled)').prop('disabled', true); }, 0); - if (! typeof autosubmit === 'undefined' && autosubmit) { + if (autosubmit) { if ($button.length) { // We're autosubmitting the form so the button has not been clicked, however, // to be really safe, we're disabling the button explicitly, just in case.. @@ -310,7 +314,7 @@ data = $form.serializeArray(); } - if (typeof autosubmit === 'undefined' || ! autosubmit) { + if (! autosubmit) { if ($button.length && $button.attr('name') !== 'undefined') { if (encoding === 'multipart/form-data') { data.append($button.attr('name'), $button.attr('value')); From e3d99cb00d5da67154683ef5897263458c104ad7 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 14:38:03 +0200 Subject: [PATCH 06/59] loader.js: Add support for ajax progress timer refs #8369 refs #8848 --- public/js/icinga/loader.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index ff63f808a..6a296d03c 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -42,13 +42,15 @@ /** * Load the given URL to the given target * - * @param {string} url URL to be loaded - * @param {object} target Target jQuery element - * @param {object} data Optional parameters, usually for POST requests - * @param {string} method HTTP method, default is 'GET' - * @param {string} action How to handle the response ('replace' or 'append'), default is 'replace' + * @param {string} url URL to be loaded + * @param {object} target Target jQuery element + * @param {object} data Optional parameters, usually for POST requests + * @param {string} method HTTP method, default is 'GET' + * @param {string} action How to handle the response ('replace' or 'append'), default is 'replace' + * @param {boolean} autorefresh Whether the cause is a autorefresh or not + * @param {object} progressTimer A timer to be stopped when the request is done */ - loadUrl: function (url, $target, data, method, action, autorefresh) { + loadUrl: function (url, $target, data, method, action, autorefresh, progressTimer) { var id = null; // Default method is GET @@ -131,6 +133,7 @@ req.autorefresh = autorefresh; req.action = action; req.addToHistory = true; + req.progressTimer = progressTimer; if (id) { this.requests[id] = req; @@ -537,6 +540,10 @@ return; } + if (typeof req.progressTimer !== 'undefined') { + this.icinga.timer.unregister(req.progressTimer); + } + // .html() removes outer div we added above this.renderContentToContainer($resp.html(), req.$target, req.action, req.autorefresh); if (oldNotifications) { @@ -638,6 +645,10 @@ req.$target.data('icingaUrl', url); } + if (typeof req.progressTimer !== 'undefined') { + this.icinga.timer.unregister(req.progressTimer); + } + if (req.status > 0) { this.icinga.logger.error( req.status, From fedda16bd4cb2a5f6d4ea6de34c9679bcdfad384 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 14:40:05 +0200 Subject: [PATCH 07/59] js: Use the last button instead of the first one for form submits refs #8369 fixes #9245 --- public/js/icinga/events.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 109eea255..5b5a5ae84 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -227,11 +227,11 @@ icinga.logger.debug('events/submitForm: Button is event.currentTarget'); } - if ($el && ($el.is('input[type=submit]') || $el.is('button[type=submit]'))) { + if ($el && ($el.is('input[type=submit]') || $el.is('button[type=submit]')) && $el.is(':focus')) { $button = $el; } else { icinga.logger.debug( - 'events/submitForm: Can not determine submit button, using the first one in form' + 'events/submitForm: Can not determine submit button, using the last one in form' ); } } @@ -251,7 +251,7 @@ } if ($button.length === 0) { - $button = $('input[type=submit]', $form).add('button[type=submit]', $form).first(); + $button = $('button[type=submit]', $form).add('input[type=submit]', $form).last(); } if ($button.length) { From 7da8ad4e44895a2f442f3c990e1627a9a5656667 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 14:44:49 +0200 Subject: [PATCH 08/59] js: Show activity indicators for form submits refs #8369 --- public/css/icinga/forms.less | 16 ++++++++++++++++ public/js/icinga/events.js | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index b761f478d..510a970a9 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -327,6 +327,22 @@ button.noscript-apply { margin-left: 0.5em; } +i.autosubmit-warning { + display: inline-block; + margin-left: 0.2em; + margin-top: 0.25em; + + &.spinning { + .animate(spin 2s infinite linear); + + &:before { + margin: 0; // Disables wobbling + font-size: 90%; // icon-spin6 seems to be bigger than icon-cw + content: '\e874'; // icon-spin6 + } + } +} + html.no-js i.autosubmit-warning { .sr-only; } diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 5b5a5ae84..ce6d62e5d 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -212,6 +212,7 @@ var method = $form.attr('method'); var encoding = $form.attr('enctype'); var $button = $('input[type=submit]:focus', $form).add('button[type=submit]:focus', $form); + var progressTimer; var $target; var data; @@ -332,7 +333,39 @@ // Note that disabled form inputs will not be enabled via JavaScript again $form.find(':input:not(#search):not(:disabled)').prop('disabled', true); - icinga.loader.loadUrl(url, $target, data, method); + // Show a spinner depending on how the form is being submitted + if (autosubmit && typeof $el !== 'undefined' && $el.next().hasClass('autosubmit-warning')) { + $el.next().addClass('spinning'); + } else if ($button.length) { + if ($button.attr('data-progress-label')) { + var isInput = $button.is('input'); + if (isInput) { + $button.prop('value', $button.attr('data-progress-label')); + } else { + $button.html($button.attr('data-progress-label')); + } + + progressTimer = icinga.timer.register(function () { + var label = isInput ? $button.prop('value') : $button.html(); + + if (label.substr(-3) === '...') { + label = label.slice(0, -2); + } else { + label += '.'; + } + + if (isInput) { + $button.prop('value', label); + } else { + $button.html(label); + } + }, null, 100); + } else if ($button.next().hasClass('spinner')) { + $('i', $button.next()).addClass('active'); + } + } + + icinga.loader.loadUrl(url, $target, data, method).progressTimer = progressTimer; event.stopPropagation(); event.preventDefault(); From 275b57cb6927445416b14d7813f8ae42db890329 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 15:04:15 +0200 Subject: [PATCH 09/59] Form: Allow to set a progress label refs #8369 --- library/Icinga/Web/Form.php | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 40d6bb409..c5eb413d8 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -81,6 +81,13 @@ class Form extends Zend_Form */ protected $submitLabel; + /** + * Label to use for showing the user an activity indicator when submitting the form + * + * @var string + */ + protected $progressLabel; + /** * The url to redirect to upon success * @@ -263,6 +270,29 @@ class Form extends Zend_Form return $this->submitLabel; } + /** + * Set the label to use for showing the user an activity indicator when submitting the form + * + * @param string $label + * + * @return $this + */ + public function setProgressLabel($label) + { + $this->progressLabel = $label; + return $this; + } + + /** + * Return the label to use for showing the user an activity indicator when submitting the form + * + * @return string + */ + public function getProgressLabel() + { + return $this->progressLabel; + } + /** * Set the url to redirect to upon success * @@ -738,9 +768,10 @@ class Form extends Zend_Form 'submit', 'btn_submit', array( - 'ignore' => true, - 'label' => $submitLabel, - 'decorators' => array( + 'ignore' => true, + 'label' => $submitLabel, + 'data-progress-label' => $this->getProgressLabel(), + 'decorators' => array( 'ViewHelper', array('HtmlTag', array('tag' => 'div')) ) From a77dc3665c68c6504c757df187cffae1566f68a0 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 15:05:02 +0200 Subject: [PATCH 10/59] Form: Show a spinner for ongoing form submits This will only be shown if there's no progress label set. refs #8369 --- library/Icinga/Web/Form.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index c5eb413d8..cd262375a 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -773,7 +773,8 @@ class Form extends Zend_Form 'data-progress-label' => $this->getProgressLabel(), 'decorators' => array( 'ViewHelper', - array('HtmlTag', array('tag' => 'div')) + array('Spinner', array('separator' => '')), + array('HtmlTag', array('tag' => 'div', 'class' => 'buttons')) ) ) ); From 3f4221b2490ca9e2158e613864f74e7db9143285 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 15:32:31 +0200 Subject: [PATCH 11/59] js: Use a fixed with for form buttons once a form submit is ongoing ...and a progress label is set. refs #8369 --- public/js/icinga/events.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index ce6d62e5d..6a0a5c26b 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -340,18 +340,25 @@ if ($button.attr('data-progress-label')) { var isInput = $button.is('input'); if (isInput) { - $button.prop('value', $button.attr('data-progress-label')); + $button.prop('value', $button.attr('data-progress-label') + '...'); } else { - $button.html($button.attr('data-progress-label')); + $button.html($button.attr('data-progress-label') + '...'); } + // Use a fixed width to prevent the button from wobbling + $button.css('width', $button.css('width')); + progressTimer = icinga.timer.register(function () { var label = isInput ? $button.prop('value') : $button.html(); + var dots = label.substr(-3); - if (label.substr(-3) === '...') { - label = label.slice(0, -2); - } else { - label += '.'; + // Using empty spaces here to prevent centered labels from wobbling + if (dots === '...') { + label = label.slice(0, -2) + ' '; + } else if (dots === '.. ') { + label = label.slice(0, -1) + '.'; + } else if (dots === '. ') { + label = label.slice(0, -2) + '. '; } if (isInput) { From bf1e17702355326452082a67097465bdec993fd3 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 15:32:53 +0200 Subject: [PATCH 12/59] LoginForm: Provide a progress label refs #8369 --- application/forms/Authentication/LoginForm.php | 1 + 1 file changed, 1 insertion(+) diff --git a/application/forms/Authentication/LoginForm.php b/application/forms/Authentication/LoginForm.php index f7bae3d1b..9646046f6 100644 --- a/application/forms/Authentication/LoginForm.php +++ b/application/forms/Authentication/LoginForm.php @@ -27,6 +27,7 @@ class LoginForm extends Form $this->setRequiredCue(null); $this->setName('form_login'); $this->setSubmitLabel($this->translate('Login')); + $this->setProgressLabel($this->translate('Logging in')); } /** From 5602d57d0b7c730ff6e8e3ccb62797b92f9cd39e Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 15:48:41 +0200 Subject: [PATCH 13/59] Indicate that the configuration is being validated refs #8369 --- application/forms/Config/ResourceConfigForm.php | 7 ++++--- application/forms/Config/UserBackendConfigForm.php | 7 ++++--- modules/monitoring/library/Monitoring/MonitoringWizard.php | 7 ++++--- modules/setup/library/Setup/WebWizard.php | 7 ++++--- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/application/forms/Config/ResourceConfigForm.php b/application/forms/Config/ResourceConfigForm.php index 51e1b1bff..db951e67a 100644 --- a/application/forms/Config/ResourceConfigForm.php +++ b/application/forms/Config/ResourceConfigForm.php @@ -344,9 +344,10 @@ class ResourceConfigForm extends ConfigForm 'submit', 'resource_validation', array( - 'ignore' => true, - 'label' => $this->translate('Validate Configuration'), - 'decorators' => array('ViewHelper') + 'ignore' => true, + 'label' => $this->translate('Validate Configuration'), + 'data-progress-label' => $this->translate('Validation In Progress'), + 'decorators' => array('ViewHelper') ) ); $this->addDisplayGroup( diff --git a/application/forms/Config/UserBackendConfigForm.php b/application/forms/Config/UserBackendConfigForm.php index 5ee4674bb..1183ae724 100644 --- a/application/forms/Config/UserBackendConfigForm.php +++ b/application/forms/Config/UserBackendConfigForm.php @@ -464,9 +464,10 @@ class UserBackendConfigForm extends ConfigForm 'submit', 'backend_validation', array( - 'ignore' => true, - 'label' => $this->translate('Validate Configuration'), - 'decorators' => array('ViewHelper') + 'ignore' => true, + 'label' => $this->translate('Validate Configuration'), + 'data-progress-label' => $this->translate('Validation In Progress'), + 'decorators' => array('ViewHelper') ) ); $this->addDisplayGroup( diff --git a/modules/monitoring/library/Monitoring/MonitoringWizard.php b/modules/monitoring/library/Monitoring/MonitoringWizard.php index 0a224610c..4b495c36e 100644 --- a/modules/monitoring/library/Monitoring/MonitoringWizard.php +++ b/modules/monitoring/library/Monitoring/MonitoringWizard.php @@ -120,9 +120,10 @@ class MonitoringWizard extends Wizard implements SetupWizard 'submit', 'backend_validation', array( - 'ignore' => true, - 'label' => t('Validate Configuration'), - 'decorators' => array('ViewHelper') + 'ignore' => true, + 'label' => t('Validate Configuration'), + 'data-progress-label' => t('Validation In Progress'), + 'decorators' => array('ViewHelper') ) ); $page->getDisplayGroup('buttons')->addElement($page->getElement('backend_validation')); diff --git a/modules/setup/library/Setup/WebWizard.php b/modules/setup/library/Setup/WebWizard.php index e3831e673..7b67a2c2f 100644 --- a/modules/setup/library/Setup/WebWizard.php +++ b/modules/setup/library/Setup/WebWizard.php @@ -369,9 +369,10 @@ class WebWizard extends Wizard implements SetupWizard 'submit', 'backend_validation', array( - 'ignore' => true, - 'label' => t('Validate Configuration'), - 'decorators' => array('ViewHelper') + 'ignore' => true, + 'label' => t('Validate Configuration'), + 'data-progress-label' => t('Validation In Progress'), + 'decorators' => array('ViewHelper') ) ); $page->getDisplayGroup('buttons')->addElement($page->getElement('backend_validation')); From 4edf1223646517b36999fd3ee1d8c2667d91581d Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 15:49:25 +0200 Subject: [PATCH 14/59] Show a spinner when deleting comments or downtimes refs #8369 --- .../forms/Command/Object/DeleteCommentCommandForm.php | 2 +- .../forms/Command/Object/DeleteDowntimeCommandForm.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php index 691b7dbed..442b9b2ce 100644 --- a/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/DeleteCommentCommandForm.php @@ -68,7 +68,7 @@ class DeleteCommentCommandForm extends CommandForm 'ignore' => true, 'escape' => false, 'type' => 'submit', - 'class' => 'link-like', + 'class' => 'link-like spinner', 'label' => $this->getView()->icon('trash'), 'title' => $this->translate('Delete this comment'), 'decorators' => array('ViewHelper') diff --git a/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php b/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php index c58179fe6..27fbae67a 100644 --- a/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php +++ b/modules/monitoring/application/forms/Command/Object/DeleteDowntimeCommandForm.php @@ -68,7 +68,7 @@ class DeleteDowntimeCommandForm extends CommandForm 'ignore' => true, 'escape' => false, 'type' => 'submit', - 'class' => 'link-like', + 'class' => 'link-like spinner', 'label' => $this->getView()->icon('trash'), 'title' => $this->translate('Delete this downtime'), 'decorators' => array('ViewHelper') From 736d715d8c2f81480ada9b35df6525601b5667c5 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 16:13:12 +0200 Subject: [PATCH 15/59] js: Allow to provide a fallback spinner for forms refs #8369 --- public/js/icinga/events.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 6a0a5c26b..c05acf047 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -369,6 +369,11 @@ }, null, 100); } else if ($button.next().hasClass('spinner')) { $('i', $button.next()).addClass('active'); + } else if ($form.attr('data-progress-element')) { + var $progressElement = $('#' + $form.attr('data-progress-element')); + if ($progressElement.length) { + $('i', $progressElement).addClass('active'); + } } } From 20f144bd4ba2d3ed185fdd35ee4e12a6318c1dba Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 16:13:30 +0200 Subject: [PATCH 16/59] Spinner: Accept option `id' refs #8369 --- library/Icinga/Web/Form/Decorator/Spinner.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Web/Form/Decorator/Spinner.php b/library/Icinga/Web/Form/Decorator/Spinner.php index 802e001d9..b47798d26 100644 --- a/library/Icinga/Web/Form/Decorator/Spinner.php +++ b/library/Icinga/Web/Form/Decorator/Spinner.php @@ -31,7 +31,10 @@ class Spinner extends Zend_Form_Decorator_Abstract */ public function render($content = '') { - $spinner = '
' + $spinner = '
getOption('id') !== null ? ' id="' . $this->getOption('id') . '"' : '') + . 'class="spinner ' . ($this->getOption('class') ?: '') . '"' + . '>' . $this->getView()->icon('spin6') . '
'; From 32f8c0770c91878d128442593a77748f24828b2e Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 16:13:56 +0200 Subject: [PATCH 17/59] Wizard: Use a single spinner for all submit buttons refs #8369 --- library/Icinga/Web/Wizard.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Web/Wizard.php b/library/Icinga/Web/Wizard.php index 1af211017..f70452d3b 100644 --- a/library/Icinga/Web/Wizard.php +++ b/library/Icinga/Web/Wizard.php @@ -655,8 +655,21 @@ class Wizard ); } + $page->setAttrib('data-progress-element', 'wizard-progress'); + $page->addElement( + 'note', + 'progress', + array( + 'order' => 99, + 'decorators' => array( + 'ViewHelper', + array('Spinner', array('id' => 'wizard-progress')) + ) + ) + ); + $page->addDisplayGroup( - array(static::BTN_PREV, static::BTN_NEXT), + array(static::BTN_PREV, static::BTN_NEXT, 'progress'), 'buttons', array( 'decorators' => array( From ebd34422cb9625b4137f27697a130f451d1da7dc Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 16:53:26 +0200 Subject: [PATCH 18/59] forms: Fix textarea layout with help icons --- public/css/icinga/forms.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index 510a970a9..1563b8c0d 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -319,7 +319,7 @@ label ~ input, label ~ select, label ~ textarea { margin-left: 1.3em; } -label + i ~ input, label + i ~ select { +label + i ~ input, label + i ~ select, label + i ~ textarea { margin-left: 0; } From 46cd47b73cc7ef657e8c80b998184e5bca0de622 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 16:54:20 +0200 Subject: [PATCH 19/59] Form: Automatically set data-progress-element... ..for forms with form based autosubmit warnings. refs #8369 --- library/Icinga/Web/Form.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index cd262375a..40ddf4ee0 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -668,6 +668,12 @@ class Form extends Zend_Form public function setUseFormAutosubmit($state = true) { $this->useFormAutosubmit = (bool) $state; + if ($this->useFormAutosubmit) { + $this->setAttrib('data-progress-element', 'form-header'); + } else { + $this->removeAttrib('data-progress-element'); + } + return $this; } @@ -1203,7 +1209,7 @@ class Form extends Zend_Form $this->addDecorator('Description', array('tag' => 'h1')); if ($this->getUseFormAutosubmit()) { $this->addDecorator('Autosubmit', array('accessible' => true)) - ->addDecorator('HtmlTag', array('tag' => 'div', 'class' => 'header')); + ->addDecorator('HtmlTag', array('tag' => 'div', 'class' => 'header', 'id' => 'form-header')); } $this->addDecorator('FormDescriptions') From d9855d82736a64bbf836e947aa176182add33329 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 20 Aug 2015 16:55:03 +0200 Subject: [PATCH 20/59] js: Fix that the fallback spinner is not utilized for auto submits refs #8369 --- public/js/icinga/events.js | 70 ++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index c05acf047..470486285 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -336,43 +336,45 @@ // Show a spinner depending on how the form is being submitted if (autosubmit && typeof $el !== 'undefined' && $el.next().hasClass('autosubmit-warning')) { $el.next().addClass('spinning'); - } else if ($button.length) { - if ($button.attr('data-progress-label')) { - var isInput = $button.is('input'); - if (isInput) { - $button.prop('value', $button.attr('data-progress-label') + '...'); - } else { - $button.html($button.attr('data-progress-label') + '...'); + } else if ($button.length && $button.attr('data-progress-label')) { + var isInput = $button.is('input'); + if (isInput) { + $button.prop('value', $button.attr('data-progress-label') + '...'); + } else { + $button.html($button.attr('data-progress-label') + '...'); + } + + // Use a fixed width to prevent the button from wobbling + $button.css('width', $button.css('width')); + + progressTimer = icinga.timer.register(function () { + var label = isInput ? $button.prop('value') : $button.html(); + var dots = label.substr(-3); + + // Using empty spaces here to prevent centered labels from wobbling + if (dots === '...') { + label = label.slice(0, -2) + ' '; + } else if (dots === '.. ') { + label = label.slice(0, -1) + '.'; + } else if (dots === '. ') { + label = label.slice(0, -2) + '. '; } - // Use a fixed width to prevent the button from wobbling - $button.css('width', $button.css('width')); - - progressTimer = icinga.timer.register(function () { - var label = isInput ? $button.prop('value') : $button.html(); - var dots = label.substr(-3); - - // Using empty spaces here to prevent centered labels from wobbling - if (dots === '...') { - label = label.slice(0, -2) + ' '; - } else if (dots === '.. ') { - label = label.slice(0, -1) + '.'; - } else if (dots === '. ') { - label = label.slice(0, -2) + '. '; - } - - if (isInput) { - $button.prop('value', label); - } else { - $button.html(label); - } - }, null, 100); - } else if ($button.next().hasClass('spinner')) { - $('i', $button.next()).addClass('active'); - } else if ($form.attr('data-progress-element')) { - var $progressElement = $('#' + $form.attr('data-progress-element')); - if ($progressElement.length) { + if (isInput) { + $button.prop('value', label); + } else { + $button.html(label); + } + }, null, 100); + } else if ($button.length && $button.next().hasClass('spinner')) { + $('i', $button.next()).addClass('active'); + } else if ($form.attr('data-progress-element')) { + var $progressElement = $('#' + $form.attr('data-progress-element')); + if ($progressElement.length) { + if ($progressElement.hasClass('spinner')) { $('i', $progressElement).addClass('active'); + } else { + $('i.autosubmit-warning', $progressElement).addClass('spinning'); } } } From 44271471e3b1621e5aa461f0d6b5fe42346c0fbc Mon Sep 17 00:00:00 2001 From: Matthias Jentsch Date: Thu, 20 Aug 2015 17:38:55 +0200 Subject: [PATCH 21/59] Allow declarative definitions of badge renderers and improve interface Allow the data backend, columns and generated tooltips to be defined in the configuration instead of providing subclasses for every new configuration. Provide an abstract BadgeMenuItemRenderer that allows creating Badges with less boilerplate. fixes #9694 --- library/Icinga/Web/Menu.php | 29 ++- .../Icinga/Web/Menu/BadgeMenuItemRenderer.php | 65 +++++++ .../Web/Menu/ForeignMenuItemRenderer.php | 17 -- library/Icinga/Web/Menu/MenuItemRenderer.php | 111 +++++------ .../Web/Menu/ProblemMenuItemRenderer.php | 64 ------ .../Web/Menu/SummaryMenuItemRenderer.php | 94 +++++++++ modules/monitoring/configuration.php | 25 ++- .../BackendAvailabilityMenuItemRenderer.php | 56 +++--- .../Menu/MonitoringBadgeMenuItemRenderer.php | 183 ++++++++++++++++++ .../Web/Menu/MonitoringMenuItemRenderer.php | 109 ----------- .../Web/Menu/ProblemMenuItemRenderer.php | 12 -- .../Menu/UnhandledHostMenuItemRenderer.php | 11 -- .../Menu/UnhandledServiceMenuItemRenderer.php | 11 -- 13 files changed, 461 insertions(+), 326 deletions(-) create mode 100644 library/Icinga/Web/Menu/BadgeMenuItemRenderer.php delete mode 100644 library/Icinga/Web/Menu/ForeignMenuItemRenderer.php delete mode 100644 library/Icinga/Web/Menu/ProblemMenuItemRenderer.php create mode 100644 library/Icinga/Web/Menu/SummaryMenuItemRenderer.php create mode 100644 modules/monitoring/library/Monitoring/Web/Menu/MonitoringBadgeMenuItemRenderer.php delete mode 100644 modules/monitoring/library/Monitoring/Web/Menu/MonitoringMenuItemRenderer.php delete mode 100644 modules/monitoring/library/Monitoring/Web/Menu/ProblemMenuItemRenderer.php delete mode 100644 modules/monitoring/library/Monitoring/Web/Menu/UnhandledHostMenuItemRenderer.php delete mode 100644 modules/monitoring/library/Monitoring/Web/Menu/UnhandledServiceMenuItemRenderer.php diff --git a/library/Icinga/Web/Menu.php b/library/Icinga/Web/Menu.php index 7992364b9..7124a4776 100644 --- a/library/Icinga/Web/Menu.php +++ b/library/Icinga/Web/Menu.php @@ -117,9 +117,19 @@ class Menu implements RecursiveIterator foreach ($props as $key => $value) { $method = 'set' . implode('', array_map('ucfirst', explode('_', strtolower($key)))); if ($key === 'renderer') { + // nested configuration is used to pass multiple arguments to the item renderer + if ($value instanceof ConfigObject) { + $args = $value; + $value = $value->get('0'); + } + $value = '\\' . ltrim($value, '\\'); if (class_exists($value)) { - $value = new $value; + if (isset($args)) { + $value = new $value($args); + } else { + $value = new $value; + } } else { $class = '\Icinga\Web\Menu' . $value; if (!class_exists($class)) { @@ -127,7 +137,11 @@ class Menu implements RecursiveIterator sprintf('ItemRenderer with class "%s" does not exist', $class) ); } - $value = new $class; + if (isset($args)) { + $value = new $class($args); + } else { + $value = new $class; + } } } if (method_exists($this, $method)) { @@ -226,7 +240,6 @@ class Menu implements RecursiveIterator $auth = Auth::getInstance(); if ($auth->isAuthenticated()) { - $this->add(t('Dashboard'), array( 'url' => 'dashboard', 'icon' => 'dashboard', @@ -236,7 +249,10 @@ class Menu implements RecursiveIterator $section = $this->add(t('System'), array( 'icon' => 'services', 'priority' => 700, - 'renderer' => 'ProblemMenuItemRenderer' + 'renderer' => array( + 'SummaryMenuItemRenderer', + 'state' => 'critical' + ) )); $section->add(t('About'), array( 'url' => 'about', @@ -297,7 +313,10 @@ class Menu implements RecursiveIterator $section->add(t('Logout'), array( 'url' => 'authentication/logout', 'priority' => 990, - 'renderer' => 'ForeignMenuItemRenderer' + 'renderer' => array( + 'MenuItemRenderer', + 'target' => '_self' + ) )); } } diff --git a/library/Icinga/Web/Menu/BadgeMenuItemRenderer.php b/library/Icinga/Web/Menu/BadgeMenuItemRenderer.php new file mode 100644 index 000000000..ebde0b38f --- /dev/null +++ b/library/Icinga/Web/Menu/BadgeMenuItemRenderer.php @@ -0,0 +1,65 @@ +renderBadge() . $this->createLink($menu); + } + + /** + * Render the badge + * + * @return string + */ + protected function renderBadge() + { + if ($count = $this->getCount()) { + return sprintf( + '
%s
', + $this->getView()->escape($this->getTitle()), + $this->getView()->escape($this->getState()), + $count + ); + } + return ''; + } +} diff --git a/library/Icinga/Web/Menu/ForeignMenuItemRenderer.php b/library/Icinga/Web/Menu/ForeignMenuItemRenderer.php deleted file mode 100644 index b898b4d08..000000000 --- a/library/Icinga/Web/Menu/ForeignMenuItemRenderer.php +++ /dev/null @@ -1,17 +0,0 @@ - '_self' - ); -} diff --git a/library/Icinga/Web/Menu/MenuItemRenderer.php b/library/Icinga/Web/Menu/MenuItemRenderer.php index d40650d91..27abcc94a 100644 --- a/library/Icinga/Web/Menu/MenuItemRenderer.php +++ b/library/Icinga/Web/Menu/MenuItemRenderer.php @@ -7,6 +7,7 @@ use Icinga\Application\Icinga; use Icinga\Web\Menu; use Icinga\Web\Url; use Icinga\Web\View; +use Icinga\Data\ConfigObject; /** * Default MenuItemRenderer class @@ -14,38 +15,39 @@ use Icinga\Web\View; class MenuItemRenderer { /** - * Contains element specific attributes - * - * @var array - */ - protected $attributes = array(); - - /** - * View + * The view this menu item is being rendered to * * @var View|null */ - protected $view; + protected $view = null; /** - * Set the view + * The link target * - * @param View $view - * - * @return $this + * @var string */ - public function setView(View $view) + protected $target = null; + + /** + * Create a new instance of MenuItemRenderer + * + * Is is possible to configure the link target using the option 'target' + * + * @param ConfigObject|null $configuration + */ + public function __construct(ConfigObject $configuration = null) { - $this->view = $view; - return $this; + if ($configuration !== null) { + $this->target = $configuration->get('target', null); + } } /** - * Get the view + * Get the view this menu item is being rendered to * * @return View */ - public function getView() + protected function getView() { if ($this->view === null) { $this->view = Icinga::app()->getViewRenderer()->view; @@ -53,6 +55,36 @@ class MenuItemRenderer return $this->view; } + /** + * Creates a menu item link element + * + * @param Menu $menu + * + * @return string + */ + public function createLink(Menu $menu) + { + $attributes = isset($this->target) ? sprintf(' target="%s"', $this->getView()->escape($this->target)) : ''; + + if ($menu->getIcon() && strpos($menu->getIcon(), '.') === false) { + return sprintf( + '%s', + $menu->getUrl() ? : '#', + $attributes, + $menu->getIcon(), + $this->getView()->escape($menu->getTitle()) + ); + } + + return sprintf( + '%s%s', + $menu->getUrl() ? : '#', + $attributes, + $menu->getIcon() ? ' ' : '', + $this->getView()->escape($menu->getTitle()) + ); + } + /** * Renders the html content of a single menu item * @@ -64,47 +96,4 @@ class MenuItemRenderer { return $this->createLink($menu); } - - /** - * Creates a menu item link element - * - * @param Menu $menu - * - * @return string - */ - public function createLink(Menu $menu) - { - if ($menu->getIcon() && strpos($menu->getIcon(), '.') === false) { - return sprintf( - '%s', - $menu->getUrl() ? : '#', - $this->getAttributes(), - $menu->getIcon(), - $this->getView()->escape($menu->getTitle()) - ); - } - - return sprintf( - '%s%s', - $menu->getUrl() ? : '#', - $this->getAttributes(), - $menu->getIcon() ? ' ' : '', - $this->getView()->escape($menu->getTitle()) - ); - } - - /** - * Returns element specific attributes if present - * - * @return string - */ - protected function getAttributes() - { - $attributes = ''; - $view = $this->getView(); - foreach ($this->attributes as $attribute => $value) { - $attributes .= ' ' . $view->escape($attribute) . '="' . $view->escape($value) . '"'; - } - return $attributes; - } } diff --git a/library/Icinga/Web/Menu/ProblemMenuItemRenderer.php b/library/Icinga/Web/Menu/ProblemMenuItemRenderer.php deleted file mode 100644 index 010bdc034..000000000 --- a/library/Icinga/Web/Menu/ProblemMenuItemRenderer.php +++ /dev/null @@ -1,64 +0,0 @@ -getParent() !== null && $menu->hasSubMenus()) { - /** @var $submenu Menu */ - foreach ($menu->getSubMenus() as $submenu) { - $renderer = $submenu->getRenderer(); - if (method_exists($renderer, 'getSummary')) { - if ($renderer->getSummary() !== null) { - $this->summary[] = $renderer->getSummary(); - } - } - } - } - return $this->getBadge() . $this->createLink($menu); - } - - /** - * Get the problem badge - * - * @return string - */ - protected function getBadge() - { - if (count($this->summary) > 0) { - $problems = 0; - $titles = array(); - - foreach ($this->summary as $summary) { - $problems += $summary['problems']; - $titles[] = $summary['title']; - } - - return sprintf( - '
%s
', - implode(', ', $titles), - $problems - ); - } - return ''; - } -} diff --git a/library/Icinga/Web/Menu/SummaryMenuItemRenderer.php b/library/Icinga/Web/Menu/SummaryMenuItemRenderer.php new file mode 100644 index 000000000..942ddbc09 --- /dev/null +++ b/library/Icinga/Web/Menu/SummaryMenuItemRenderer.php @@ -0,0 +1,94 @@ +state = $configuration->get('state', self::STATE_CRITICAL); + } + + /** + * Renders the html content of a single menu item and summarized sub-menus + * + * @param Menu $menu + * + * @return string + */ + public function render(Menu $menu) + { + /** @var $submenu Menu */ + foreach ($menu->getSubMenus() as $submenu) { + $renderer = $submenu->getRenderer(); + if ($renderer instanceof BadgeMenuItemRenderer) { + if ($renderer->getState() === $this->state) { + $this->titles[] = $renderer->getTitle(); + $this->count += $renderer->getCount(); + } + } + } + return $this->renderBadge() . $this->createLink($menu); + } + + /** + * The amount of items to display in the badge + * + * @return int + */ + public function getCount() + { + return $this->count; + } + + /** + * Defines the color of the badge + * + * @return string + */ + public function getState() + { + return $this->state; + } + + /** + * The tooltip title + * + * @return string + */ + public function getTitle() + { + return implode(', ', $this->titles); + } +} diff --git a/modules/monitoring/configuration.php b/modules/monitoring/configuration.php index 8844c3247..fa95c3785 100644 --- a/modules/monitoring/configuration.php +++ b/modules/monitoring/configuration.php @@ -89,17 +89,34 @@ $this->provideSearchUrl($this->translate('Servicegroups'), 'monitoring/list/serv * Problems Section */ $section = $this->menuSection($this->translate('Problems'), array( - 'renderer' => 'Icinga\Module\Monitoring\Web\Menu\ProblemMenuItemRenderer', + 'renderer' => array( + 'SummaryMenuItemRenderer', + 'state' => 'critical' + ), 'icon' => 'block', 'priority' => 20 )); $section->add($this->translate('Unhandled Hosts'), array( - 'renderer' => 'Icinga\Module\Monitoring\Web\Menu\UnhandledHostMenuItemRenderer', + 'renderer' => array( + 'Icinga\Module\Monitoring\Web\Menu\MonitoringBadgeMenuItemRenderer', + 'columns' => array( + 'hosts_down_unhandled' => $this->translate('%d unhandled hosts down') + ), + 'state' => 'critical', + 'dataView' => 'statussummary' + ), 'url' => 'monitoring/list/hosts?host_problem=1&host_handled=0', 'priority' => 30 )); $section->add($this->translate('Unhandled Services'), array( - 'renderer' => 'Icinga\Module\Monitoring\Web\Menu\UnhandledServiceMenuItemRenderer', + 'renderer' => array( + 'Icinga\Module\Monitoring\Web\Menu\MonitoringBadgeMenuItemRenderer', + 'columns' => array( + 'services_critical_unhandled' => $this->translate('%d unhandled services critical') + ), + 'state' => 'critical', + 'dataView' => 'statussummary' + ), 'url' => 'monitoring/list/services?service_problem=1&service_handled=0&sort=service_severity', 'priority' => 40 )); @@ -204,7 +221,7 @@ $section = $this->menuSection($this->translate('System')); $section->add($this->translate('Monitoring Health'), array( 'url' => 'monitoring/process/info', 'priority' => 720, - 'renderer' => 'Icinga\Module\Monitoring\Web\Menu\BackendAvailabilityMenuItemRenderer' + 'renderer' => 'Icinga\Module\Monitoring\Web\Menu\BackendAvailabilityMenuItemRenderer' )); /* diff --git a/modules/monitoring/library/Monitoring/Web/Menu/BackendAvailabilityMenuItemRenderer.php b/modules/monitoring/library/Monitoring/Web/Menu/BackendAvailabilityMenuItemRenderer.php index f65f25993..ca589d3cb 100644 --- a/modules/monitoring/library/Monitoring/Web/Menu/BackendAvailabilityMenuItemRenderer.php +++ b/modules/monitoring/library/Monitoring/Web/Menu/BackendAvailabilityMenuItemRenderer.php @@ -4,10 +4,10 @@ namespace Icinga\Module\Monitoring\Web\Menu; use Icinga\Web\Menu; -use Icinga\Web\Menu\MenuItemRenderer; +use Icinga\Web\Menu\BadgeMenuItemRenderer; use Icinga\Module\Monitoring\Backend\MonitoringBackend; -class BackendAvailabilityMenuItemRenderer extends MenuItemRenderer +class BackendAvailabilityMenuItemRenderer extends BadgeMenuItemRenderer { /** * Get whether or not the monitoring backend is currently running @@ -27,47 +27,39 @@ class BackendAvailabilityMenuItemRenderer extends MenuItemRenderer } /** - * {@inheritdoc} - */ - public function render(Menu $menu) - { - return $this->getBadge() . $this->createLink($menu); - } - - /** - * Get the problem badge HTML + * The css class of the badge * * @return string */ - protected function getBadge() + public function getState() { - if (! $this->isCurrentlyRunning()) { - return sprintf( - '
%d
', - sprintf( - mt('monitoring', 'Monitoring backend %s is not running'), MonitoringBackend::instance()->getName() - ), - 1 - ); - } - return ''; + return self::STATE_CRITICAL; } /** - * Get the problem data for the summary + * The amount of items to display in the badge * - * @return array|null + * @return int */ - public function getSummary() + public function getCount() { if (! $this->isCurrentlyRunning()) { - return array( - 'problems' => 1, - 'title' => sprintf( - mt('monitoring', 'Monitoring backend %s is not running'), MonitoringBackend::instance()->getName() - ) - ); + return 1; } - return null; + return 0; + } + + /** + * The tooltip title + * + * @return string + * @throws \Icinga\Exception\ConfigurationError + */ + public function getTitle() + { + return sprintf( + mt('monitoring', 'Monitoring backend %s is not running'), + MonitoringBackend::instance()->getName() + ); } } diff --git a/modules/monitoring/library/Monitoring/Web/Menu/MonitoringBadgeMenuItemRenderer.php b/modules/monitoring/library/Monitoring/Web/Menu/MonitoringBadgeMenuItemRenderer.php new file mode 100644 index 000000000..90d02fe52 --- /dev/null +++ b/modules/monitoring/library/Monitoring/Web/Menu/MonitoringBadgeMenuItemRenderer.php @@ -0,0 +1,183 @@ +columns = $configuration->get('columns'); + $this->state = $configuration->get('state'); + $this->dataView = $configuration->get('dataView'); + + // clear the outdated summary cache, since new columns are being added. Optimally all menu item are constructed + // before any rendering is going on to avoid trashing too man old requests + if (isset(self::$summaries[$this->dataView])) { + unset(self::$summaries[$this->dataView]); + } + + // add the new columns to this view + if (! isset(self::$dataViews[$this->dataView])) { + self::$dataViews[$this->dataView] = array(); + } + foreach ($this->columns as $column => $title) { + if (! array_search($column, self::$dataViews[$this->dataView])) { + self::$dataViews[$this->dataView][] = $column; + } + $this->titles[$column] = $title; + } + } + + /** + * Apply a restriction on the given data view + * + * @param string $restriction The name of restriction + * @param Filterable $filterable The filterable to restrict + * + * @return Filterable The filterable + */ + protected static function applyRestriction($restriction, Filterable $filterable) + { + $restrictions = Filter::matchAny(); + foreach (Auth::getInstance()->getRestrictions($restriction) as $filter) { + $restrictions->addFilter(Filter::fromQueryString($filter)); + } + $filterable->applyFilter($restrictions); + return $filterable; + } + + /** + * Fetch the response from the database or access cache + * + * @param $view + * + * @return null + * @throws \Icinga\Exception\ConfigurationError + */ + protected static function summary($view) + { + if (! isset(self::$summaries[$view])) { + $summary = MonitoringBackend::instance()->select()->from( + $view, + self::$dataViews[$view] + ); + static::applyRestriction('monitoring/filter/objects', $summary); + self::$summaries[$view] = $summary->fetchRow(); + } + return isset(self::$summaries[$view]) ? self::$summaries[$view] : null; + } + + /** + * Defines the color of the badge + * + * @return string + */ + public function getState() + { + return $this->state; + } + + /** + * The amount of items to display in the badge + * + * @return int + */ + public function getCount() + { + $sum = self::summary($this->dataView); + $count = 0; + + foreach ($this->columns as $col => $title) { + if (isset($sum->$col)) { + $count += $sum->$col; + } + } + return $count; + } + + /** + * The tooltip title + * + * @return string + */ + public function getTitle() + { + $titles = array(); + $sum = $this->summary($this->dataView); + foreach ($this->columns as $column => $value) { + if (isset($sum->$column) && $sum->$column > 0) { + $titles[] = sprintf($this->titles[$column], $sum->$column); + } + } + return implode(', ', $titles); + } +} diff --git a/modules/monitoring/library/Monitoring/Web/Menu/MonitoringMenuItemRenderer.php b/modules/monitoring/library/Monitoring/Web/Menu/MonitoringMenuItemRenderer.php deleted file mode 100644 index df901b165..000000000 --- a/modules/monitoring/library/Monitoring/Web/Menu/MonitoringMenuItemRenderer.php +++ /dev/null @@ -1,109 +0,0 @@ -getRestrictions($restriction) as $filter) { - $restrictions->addFilter(Filter::fromQueryString($filter)); - } - $filterable->applyFilter($restrictions); - return $filterable; - } - - protected static function summary($column = null) - { - if (self::$summary === null) { - $summary = MonitoringBackend::instance()->select()->from( - 'statussummary', - array( - 'hosts_down_unhandled', - 'services_critical_unhandled' - ) - ); - static::applyRestriction('monitoring/filter/objects', $summary); - self::$summary = $summary->fetchRow(); - } - - if ($column === null) { - return self::$summary; - } elseif (isset(self::$summary->$column)) { - return self::$summary->$column; - } else { - return null; - } - } - - protected function getBadgeTitle() - { - $translations = array( - 'hosts_down_unhandled' => mt('monitoring', '%d unhandled hosts down'), - 'services_critical_unhandled' => mt('monitoring', '%d unhandled services critical') - ); - - $titles = array(); - $sum = $this->summary(); - - foreach ($this->columns as $col) { - if (isset($sum->$col) && $sum->$col > 0) { - $titles[] = sprintf($translations[$col], $sum->$col); - } - } - - return implode(', ', $titles); - } - - protected function countItems() - { - $sum = self::summary(); - $count = 0; - - foreach ($this->columns as $col) { - if (isset($sum->$col)) { - $count += $sum->$col; - } - } - - return $count; - } - - public function render(Menu $menu) - { - return $this->getBadge() . $this->createLink($menu); - } - - protected function getBadge() - { - if ($count = $this->countItems()) { - return sprintf( - '
%s
', - $this->getBadgeTitle(), - $count - ); - } - return ''; - } -} diff --git a/modules/monitoring/library/Monitoring/Web/Menu/ProblemMenuItemRenderer.php b/modules/monitoring/library/Monitoring/Web/Menu/ProblemMenuItemRenderer.php deleted file mode 100644 index d17431398..000000000 --- a/modules/monitoring/library/Monitoring/Web/Menu/ProblemMenuItemRenderer.php +++ /dev/null @@ -1,12 +0,0 @@ - Date: Fri, 21 Aug 2015 09:52:57 +0200 Subject: [PATCH 22/59] Wizard: Add Spinner decorator to the first (and only) submit button refs #8369 --- library/Icinga/Web/Wizard.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Wizard.php b/library/Icinga/Web/Wizard.php index f70452d3b..bf68e3527 100644 --- a/library/Icinga/Web/Wizard.php +++ b/library/Icinga/Web/Wizard.php @@ -606,7 +606,7 @@ class Wizard 'type' => 'submit', 'value' => $pages[1]->getName(), 'label' => t('Next'), - 'decorators' => array('ViewHelper') + 'decorators' => array('ViewHelper', 'Spinner') ) ); } elseif ($index < count($pages) - 1) { From 7dd3fc6c78fa929bd9530242dd50a46291d5356d Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Aug 2015 09:54:05 +0200 Subject: [PATCH 23/59] Make sure that the spinner is only animated when visible refs #8369 --- public/css/icinga/forms.less | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index 1563b8c0d..18b7da2b8 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -117,11 +117,14 @@ div.spinner { &.active { visibility: visible; + + &:before { + .animate(spin 2s infinite linear); + } } &:before { margin: 0; // Disables wobbling - .animate(spin 2s infinite linear); } } } From 7244906515d7f5ace611533768e06907756396c0 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Aug 2015 10:16:23 +0200 Subject: [PATCH 24/59] Wizard: Use a constant for the name and id of the progress element refs #8369 --- library/Icinga/Web/Wizard.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/library/Icinga/Web/Wizard.php b/library/Icinga/Web/Wizard.php index bf68e3527..d397db52b 100644 --- a/library/Icinga/Web/Wizard.php +++ b/library/Icinga/Web/Wizard.php @@ -38,6 +38,11 @@ class Wizard */ const BTN_PREV = 'btn_prev'; + /** + * The name and id of the element for showing the user an activity indicator when advancing the wizard + */ + const PROGRESS_ELEMENT = 'wizard_progress'; + /** * This wizard's parent * @@ -655,21 +660,21 @@ class Wizard ); } - $page->setAttrib('data-progress-element', 'wizard-progress'); + $page->setAttrib('data-progress-element', static::PROGRESS_ELEMENT); $page->addElement( 'note', - 'progress', + static::PROGRESS_ELEMENT, array( - 'order' => 99, + 'order' => 99, // Ensures that it's shown on the right even if a sub-class adds another button 'decorators' => array( 'ViewHelper', - array('Spinner', array('id' => 'wizard-progress')) + array('Spinner', array('id' => static::PROGRESS_ELEMENT)) ) ) ); $page->addDisplayGroup( - array(static::BTN_PREV, static::BTN_NEXT, 'progress'), + array(static::BTN_PREV, static::BTN_NEXT, static::PROGRESS_ELEMENT), 'buttons', array( 'decorators' => array( From a960f2cb4c246530fa332345cacea46824521b35 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Aug 2015 10:17:03 +0200 Subject: [PATCH 25/59] forms.less: Animate the icon char, not the i-tag refs #8369 --- public/css/icinga/forms.less | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index 18b7da2b8..b3933b496 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -335,14 +335,11 @@ i.autosubmit-warning { margin-left: 0.2em; margin-top: 0.25em; - &.spinning { + &.spinning:before { + margin: 0; // Disables wobbling + font-size: 90%; // icon-spin6 seems to be bigger than icon-cw + content: '\e874'; // icon-spin6 .animate(spin 2s infinite linear); - - &:before { - margin: 0; // Disables wobbling - font-size: 90%; // icon-spin6 seems to be bigger than icon-cw - content: '\e874'; // icon-spin6 - } } } From c395dbd813b34b798a13c5b8fb67598427e141ce Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Aug 2015 10:35:10 +0200 Subject: [PATCH 26/59] Wizard: Add missing progress element to custom form scripts refs #8369 --- .../views/scripts/form/setup-modules.phtml | 12 ++++++++++-- .../views/scripts/form/setup-requirements.phtml | 10 +++++++++- .../views/scripts/form/setup-summary.phtml | 11 ++++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/modules/setup/application/views/scripts/form/setup-modules.phtml b/modules/setup/application/views/scripts/form/setup-modules.phtml index 8d8e9ecf9..859acfaa2 100644 --- a/modules/setup/application/views/scripts/form/setup-modules.phtml +++ b/modules/setup/application/views/scripts/form/setup-modules.phtml @@ -3,11 +3,18 @@ use Icinga\Web\Wizard; ?> -
+

translate('Modules', 'setup.page.title'); ?>

translate('The following modules were found in your Icinga Web 2 installation. To enable and configure a module, just tick it and click "Next".'); ?>

getElements() as $element): ?> - getName(), array(Wizard::BTN_PREV, Wizard::BTN_NEXT, $form->getTokenElementName(), $form->getUidElementName()))): ?> + getName(), array(Wizard::BTN_PREV, Wizard::BTN_NEXT, Wizard::PROGRESS_ELEMENT, $form->getTokenElementName(), $form->getUidElementName()))): ?>

@@ -20,5 +27,6 @@ use Icinga\Web\Wizard;
getElement(Wizard::BTN_PREV); ?> getElement(Wizard::BTN_NEXT); ?> + getElement(Wizard::PROGRESS_ELEMENT); ?>
diff --git a/modules/setup/application/views/scripts/form/setup-requirements.phtml b/modules/setup/application/views/scripts/form/setup-requirements.phtml index fbd2c692a..3ddd5b404 100644 --- a/modules/setup/application/views/scripts/form/setup-requirements.phtml +++ b/modules/setup/application/views/scripts/form/setup-requirements.phtml @@ -9,7 +9,14 @@ use Icinga\Web\Wizard;

translate('Module'); ?>

getRequirements(); ?> -
+ getElement($form->getTokenElementName()); ?> getElement($form->getUidElementName()); ?>
@@ -21,6 +28,7 @@ use Icinga\Web\Wizard; } echo $btn; ?> + getElement(Wizard::PROGRESS_ELEMENT); ?>
translate('You may also need to restart the web-server for the changes to take effect!'); ?> qlink( diff --git a/modules/setup/application/views/scripts/form/setup-summary.phtml b/modules/setup/application/views/scripts/form/setup-summary.phtml index e8053a897..7c91ecd05 100644 --- a/modules/setup/application/views/scripts/form/setup-summary.phtml +++ b/modules/setup/application/views/scripts/form/setup-summary.phtml @@ -20,11 +20,20 @@ use Icinga\Web\Wizard;
- + getElement($form->getTokenElementName()); ?> getElement($form->getUidElementName()); ?>
getElement(Wizard::BTN_PREV); ?> getElement(Wizard::BTN_NEXT)->setAttrib('class', 'finish'); ?> + getElement(Wizard::PROGRESS_ELEMENT); ?>
\ No newline at end of file From b9d64b40a4f62d72d06f0e0945e1603ca1fa8650 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Aug 2015 10:36:08 +0200 Subject: [PATCH 27/59] forms.less: Fix help icon layout --- library/Icinga/Web/Form/Decorator/Help.php | 5 ++++- public/css/icinga/forms.less | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Form/Decorator/Help.php b/library/Icinga/Web/Form/Decorator/Help.php index 152d548af..531072a6b 100644 --- a/library/Icinga/Web/Form/Decorator/Help.php +++ b/library/Icinga/Web/Form/Decorator/Help.php @@ -96,7 +96,10 @@ class Help extends Zend_Form_Decorator_Abstract $helpContent = $this->getView()->icon( 'help', $description . ($description && $requirement ? ' ' : '') . $requirement, - array('aria-hidden' => $this->accessible ? 'true' : 'false') + array( + 'class' => 'help', + 'aria-hidden' => $this->accessible ? 'true' : 'false' + ) ) . $helpContent; } diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index b3933b496..16b4968b6 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -274,12 +274,17 @@ form div.element { form label { display: inline-block; - margin-top: 0.3em; + vertical-align: top; + margin-top: 0.25em; margin-right: 1em; font-size: 0.9em; width: 10em; } +form div.element i.help:before { + margin-top: 0.25em; +} + label ~ * { vertical-align: top; } From 89bc1f13edec7d5e57e44e48f393cd24178694a8 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Aug 2015 10:53:16 +0200 Subject: [PATCH 28/59] forms.less: Prevent that the sort control "wobbles" I love this word.. refs #8369 --- public/css/icinga/forms.less | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index 16b4968b6..574acadfa 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -340,9 +340,11 @@ i.autosubmit-warning { margin-left: 0.2em; margin-top: 0.25em; - &.spinning:before { + &:before { margin: 0; // Disables wobbling - font-size: 90%; // icon-spin6 seems to be bigger than icon-cw + } + + &.spinning:before { content: '\e874'; // icon-spin6 .animate(spin 2s infinite linear); } From fc28088dc0d136f4f13c003467bf5fa6982aa748 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:09:54 +0200 Subject: [PATCH 29/59] monitoring: Remove deprecated method ListController::applyRestriction() --- .../application/controllers/ListController.php | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index d26cc0c52..affef2e40 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -24,24 +24,6 @@ class Monitoring_ListController extends Controller $this->createTabs(); } - /** - * @deprecated DO NOT USE. THIS IS A HACK. This is removed once we fix the eventhistory action w/ filters. - */ - protected function applyFilter($query) - { - $params = clone $this->params; - $params->shift('format'); - $params->shift('limit'); - $params->shift('page'); - $params->shift('view'); - if ($sort = $params->shift('sort')) { - $query->order($sort, $params->shift('dir')); - } - $query->applyFilter(Filter::fromQuerystring((string) $params)); - $this->handleFormatRequest($query); - return $query; - } - /** * Overwrite the backend to use (used for testing) * From 67bbc3ed6a5572ee06a925158c75648eeaefb500 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:11:50 +0200 Subject: [PATCH 30/59] monitoring: Apply restriction on hosts before calling filterQuery which also handles format refs #9965 --- modules/monitoring/application/controllers/ListController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index affef2e40..46dd38fcb 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -73,8 +73,8 @@ class Monitoring_ListController extends Controller 'host_current_check_attempt', 'host_max_check_attempts' ), $this->addColumns())); - $this->filterQuery($query); $this->applyRestriction('monitoring/filter/objects', $query); + $this->filterQuery($query); $this->view->hosts = $query; $stats = $this->backend->select()->from('hoststatussummary', array( 'hosts_total', From 4aa0af5dcaa5b0d5e440adcbe825b403bd04c523 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:15:32 +0200 Subject: [PATCH 31/59] monitoring: Apply restriction on services before calling filterQuery which also handles format refs #9965 --- modules/monitoring/application/controllers/ListController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 46dd38fcb..aaf0265fa 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -159,8 +159,8 @@ class Monitoring_ListController extends Controller 'max_check_attempts' => 'service_max_check_attempts' ), $this->addColumns()); $query = $this->backend->select()->from('servicestatus', $columns); - $this->filterQuery($query); $this->applyRestriction('monitoring/filter/objects', $query); + $this->filterQuery($query); $this->view->services = $query; $this->setupLimitControl(); From 5d325f318229c9ade82727b0d766d88492bb8e91 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:16:02 +0200 Subject: [PATCH 32/59] monitoring: Apply restriction on downtimes before calling filterQuery which also handles format refs #9965 --- modules/monitoring/application/controllers/ListController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index aaf0265fa..41651d880 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -224,9 +224,8 @@ class Monitoring_ListController extends Controller 'host_display_name', 'service_display_name' )); - $this->filterQuery($query); - $this->applyRestriction('monitoring/filter/objects', $query); + $this->filterQuery($query); $this->view->downtimes = $query; From d463a86304ac7e428dc29e5f9150a6cc7dd9ebd9 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:16:53 +0200 Subject: [PATCH 33/59] monitoring: Apply restriction on notifications before calling filterQuery which also handles format refs #9965 --- modules/monitoring/application/controllers/ListController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 41651d880..8508261d1 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -272,8 +272,8 @@ class Monitoring_ListController extends Controller 'host_display_name', 'service_display_name' )); - $this->filterQuery($query); $this->applyRestriction('monitoring/filter/objects', $query); + $this->filterQuery($query); $this->view->notifications = $query; $this->setupLimitControl(); From 959cff4d100a873ea3eab56c966b590ddb126cc7 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:17:44 +0200 Subject: [PATCH 34/59] monitoring: Apply restriction on contacts before calling filterQuery which also handles format refs #9965 --- modules/monitoring/application/controllers/ListController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 8508261d1..2c3f19d28 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -295,8 +295,8 @@ class Monitoring_ListController extends Controller 'contact_notify_service_timeperiod', 'contact_notify_host_timeperiod' )); - $this->filterQuery($query); $this->applyRestriction('monitoring/filter/objects', $query); + $this->filterQuery($query); $this->view->contacts = $query; $this->setupLimitControl(); From e58c0a8e427115b4a8e6e56fd7efe13fc13a9eec Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:18:30 +0200 Subject: [PATCH 35/59] monitoring: Apply restriction on contact groups before calling filterQuery which also handles format refs #9965 --- modules/monitoring/application/controllers/ListController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 2c3f19d28..3abd50193 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -367,8 +367,8 @@ class Monitoring_ListController extends Controller 'contact_email', 'contact_pager' )); - $this->filterQuery($query); $this->applyRestriction('monitoring/filter/objects', $query); + $this->filterQuery($query); $this->setupSortControl(array( 'contactgroup_name' => $this->translate('Contactgroup Name'), From 9dd13d2a82b105fca54811aa96c5c1f201c68b8b Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:19:00 +0200 Subject: [PATCH 36/59] monitoring: Apply restriction on comments before calling filterQuery which also handles format refs #9965 --- modules/monitoring/application/controllers/ListController.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 3abd50193..0863d40e9 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -411,10 +411,8 @@ class Monitoring_ListController extends Controller 'host_display_name', 'service_display_name' )); - $this->filterQuery($query); - $this->applyRestriction('monitoring/filter/objects', $query); - + $this->filterQuery($query): $this->view->comments = $query; $this->setupLimitControl(); From 3dc140b125dc0b9b4d986d269c33a3dfa3491cac Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:19:31 +0200 Subject: [PATCH 37/59] monitoring: Apply restriction on service groups before calling filterQuery which also handles format refs #9965 --- modules/monitoring/application/controllers/ListController.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 0863d40e9..2ee41c0f9 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -464,10 +464,8 @@ class Monitoring_ListController extends Controller 'services_warning_last_state_change_unhandled' => 'services_warning_unhandled_last_state_change', 'services_warning_unhandled' )); - $this->filterQuery($query); - $this->applyRestriction('monitoring/filter/objects', $query); - + $this->filterQuery($query); $this->view->servicegroups = $query; $this->setupLimitControl(); From 4c68b4a0177f01f6737812f5f1b7366d68b3025a Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:19:56 +0200 Subject: [PATCH 38/59] monitoring: Apply restriction on host groups before calling filterQuery which also handles format refs #9965 --- modules/monitoring/application/controllers/ListController.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 2ee41c0f9..e6add2f4a 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -508,10 +508,8 @@ class Monitoring_ListController extends Controller 'services_warning_handled', 'services_warning_unhandled' )); - $this->filterQuery($query); - $this->applyRestriction('monitoring/filter/objects', $query); - + $this->filterQuery($query); $this->view->hostgroups = $query; $this->setupLimitControl(); From de00a1998e450207cc99e1cb75f01f5b627887a8 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:20:56 +0200 Subject: [PATCH 39/59] monitoring: Apply restriction on the service grid before calling filterQuery which also handles format refs #9965 --- modules/monitoring/application/controllers/ListController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index e6add2f4a..d5dc930f5 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -564,8 +564,8 @@ class Monitoring_ListController extends Controller 'service_output', 'service_handled' )); - $this->filterQuery($query); $this->applyRestriction('monitoring/filter/objects', $query); + $this->filterQuery($query); $this->setupSortControl(array( 'host_name' => $this->translate('Hostname'), 'service_description' => $this->translate('Service description') From b08715f70eea15a8ce80f34a9775634131008d3a Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:21:52 +0200 Subject: [PATCH 40/59] monitoring: Fix typo in ListController refs #9965 --- modules/monitoring/application/controllers/ListController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index d5dc930f5..44172f1ea 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -412,7 +412,7 @@ class Monitoring_ListController extends Controller 'service_display_name' )); $this->applyRestriction('monitoring/filter/objects', $query); - $this->filterQuery($query): + $this->filterQuery($query); $this->view->comments = $query; $this->setupLimitControl(); From 90ef2883627dd5a3423956eef633e0ba57b6e551 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 20 Aug 2015 15:50:02 +0200 Subject: [PATCH 41/59] monitoring: Clone the filter before modyfing it in the IdoQuery fixes #9971 --- .../library/Monitoring/Backend/Ido/Query/IdoQuery.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/IdoQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/IdoQuery.php index 85d9169a9..c8d2e3140 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/IdoQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/IdoQuery.php @@ -479,8 +479,12 @@ abstract class IdoQuery extends DbQuery } } + /** + * {@inheritdoc} + */ public function addFilter(Filter $filter) { + $filter = clone $filter; $this->requireFilterColumns($filter); return parent::addFilter($filter); } From 85ef98f72d4f7ccc2cbf998262244e3a0eb5a531 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Fri, 21 Aug 2015 11:27:03 +0200 Subject: [PATCH 42/59] lib: Add PHPDoc to Request::hasCookieSupport() --- library/Icinga/Web/Request.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/Icinga/Web/Request.php b/library/Icinga/Web/Request.php index b4d7e92d4..5d11798a0 100644 --- a/library/Icinga/Web/Request.php +++ b/library/Icinga/Web/Request.php @@ -119,6 +119,11 @@ class Request extends Zend_Controller_Request_Http return $id . '-' . $this->uniqueId; } + /** + * Detect whether cookies are enabled + * + * @return bool + */ public function hasCookieSupport() { $cookie = new Cookie($this); From 747f6dcf770b91097402af6eb9c59fa1a4c0c2b4 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Aug 2015 11:40:38 +0200 Subject: [PATCH 43/59] Form: Show the form autosubmit warning in the header tag... ...and use a unique id for the progress element. refs #8369 --- library/Icinga/Web/Form.php | 38 +++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 40ddf4ee0..ef8168b5c 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -669,7 +669,7 @@ class Form extends Zend_Form { $this->useFormAutosubmit = (bool) $state; if ($this->useFormAutosubmit) { - $this->setAttrib('data-progress-element', 'form-header'); + $this->setAttrib('data-progress-element', 'header-' . $this->getId()); } else { $this->removeAttrib('data-progress-element'); } @@ -1206,13 +1206,16 @@ class Form extends Zend_Form 'form' => $this )); } else { - $this->addDecorator('Description', array('tag' => 'h1')); - if ($this->getUseFormAutosubmit()) { - $this->addDecorator('Autosubmit', array('accessible' => true)) - ->addDecorator('HtmlTag', array('tag' => 'div', 'class' => 'header', 'id' => 'form-header')); - } - - $this->addDecorator('FormDescriptions') + $this->addDecorator('Description', array('tag' => 'h1', 'escape' => !$this->getUseFormAutosubmit())) + ->addDecorator( + 'HtmlTag', + array( + 'tag' => 'div', + 'class' => 'header', + 'id' => 'header-' . $this->getId() + ) + ) + ->addDecorator('FormDescriptions') ->addDecorator('FormNotifications') ->addDecorator('FormErrors', array('onlyCustomFormErrors' => true)) ->addDecorator('FormElements') @@ -1256,6 +1259,25 @@ class Form extends Zend_Form return $name; } + /** + * Retrieve form description + * + * This will return the escaped description with the autosubmit warning icon if form autosubmit is enabled. + * + * @return string + */ + public function getDescription() + { + $description = parent::getDescription(); + if ($description && $this->getUseFormAutosubmit()) { + $autosubmit = $this->_getDecorator('Autosubmit', array('accessible' => true)); + $autosubmit->setElement($this); + $description = $autosubmit->render($this->getView()->escape($description)); + } + + return $description; + } + /** * Set the action to submit this form against * From a11705a3fc7237fbd6ded2ad3875e5cd1ab52606 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Aug 2015 11:46:39 +0200 Subject: [PATCH 44/59] monitoring: Show the autosubmit warning when toggling object features refs #8369 --- modules/monitoring/public/css/module.less | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less index 3aabe1f51..6ea967a1c 100644 --- a/modules/monitoring/public/css/module.less +++ b/modules/monitoring/public/css/module.less @@ -186,10 +186,6 @@ table.avp { } } - .autosubmit-warning { - display: none; - } - .object-features { label { font-weight: normal; From 08aefe0b25b668f8163118addf934423d2ddb371 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Aug 2015 13:28:35 +0200 Subject: [PATCH 45/59] js: Allow buttons to animate themself refs #8369 --- public/js/icinga/events.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 470486285..1114f79f0 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -336,6 +336,8 @@ // Show a spinner depending on how the form is being submitted if (autosubmit && typeof $el !== 'undefined' && $el.next().hasClass('autosubmit-warning')) { $el.next().addClass('spinning'); + } else if ($button.length && $button.is('button') && $button.hasClass('animated')) { + $button.addClass('active'); } else if ($button.length && $button.attr('data-progress-label')) { var isInput = $button.is('input'); if (isInput) { From 1cb2009dcd9d4dcf35fc282a3688d8398958761e Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 21 Aug 2015 13:29:29 +0200 Subject: [PATCH 46/59] Animate the button when reordering authentication backends refs #8369 --- .../scripts/form/reorder-authbackend.phtml | 4 +- public/css/icinga/animation.less | 251 ++++++++++++++++++ public/css/icinga/forms.less | 10 + 3 files changed, 263 insertions(+), 2 deletions(-) diff --git a/application/views/scripts/form/reorder-authbackend.phtml b/application/views/scripts/form/reorder-authbackend.phtml index ec1cf7810..4edc7e06f 100644 --- a/application/views/scripts/form/reorder-authbackend.phtml +++ b/application/views/scripts/form/reorder-authbackend.phtml @@ -39,7 +39,7 @@ 0): ?> -