From 702a9c95230c77b758da4a28a9312bfc6f67d97d Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 30 Jun 2015 13:22:54 +0200 Subject: [PATCH 001/108] Form: Show notifications and errors below any descriptions They might be textually related to one or more descriptions. refs #8983 --- library/Icinga/Web/Form.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 679091fde..c450aaef6 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -1043,9 +1043,9 @@ class Form extends Zend_Form ->addDecorator('HtmlTag', array('tag' => 'div', 'class' => 'header')); } - $this->addDecorator('FormErrors', array('onlyCustomFormErrors' => true)) + $this->addDecorator('FormDescriptions') ->addDecorator('FormNotifications') - ->addDecorator('FormDescriptions') + ->addDecorator('FormErrors', array('onlyCustomFormErrors' => true)) ->addDecorator('FormElements') //->addDecorator('HtmlTag', array('tag' => 'dl', 'class' => 'zend_form')) ->addDecorator('Form'); From 147f6be714468c700b3c6987c9dc322c7a0ad9a2 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 30 Jun 2015 14:25:33 +0200 Subject: [PATCH 002/108] Form: Fix notifications * Coding style issues * Notifications were not grouped by type * Notifications of sub-forms were overwriting existing ones --- library/Icinga/Web/Form.php | 127 +++++++++++------- .../Web/Form/Decorator/FormNotifications.php | 50 ++++--- public/css/icinga/forms.less | 27 ++-- 3 files changed, 127 insertions(+), 77 deletions(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index c450aaef6..24f0e2657 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -39,26 +39,19 @@ class Form extends Zend_Form const DEFAULT_SUFFIX = '_default'; /** - * The type of the notification for the error + * Identifier for notifications of type error */ - const NOTIFICATION_ERROR = 0; + const NOTIFICATION_ERROR = 0; /** - * The type of the notification for the warning + * Identifier for notifications of type warning */ - const NOTIFICATION_WARNING = 2; + const NOTIFICATION_WARNING = 1; /** - * The type of the notification for the info + * Identifier for notifications of type info */ - const NOTIFICATION_INFO = 4; - - /** - * The notifications of the form - * - * @var array - */ - protected $notifications = array(); + const NOTIFICATION_INFO = 2; /** * Whether this form has been created @@ -160,6 +153,13 @@ class Form extends Zend_Form */ protected $descriptions; + /** + * The notifications of this form + * + * @var array + */ + protected $notifications; + /** * Whether the Autosubmit decorator should be applied to this form * @@ -522,6 +522,50 @@ class Form extends Zend_Form return $this->descriptions; } + /** + * Set the notifications for this form + * + * @param array $notifications + * + * @return $this + */ + public function setNotifications(array $notifications) + { + $this->notifications = $notifications; + return $this; + } + + /** + * Add a notification for this form + * + * If $notification is an array the second value should be + * an array as well containing additional HTML properties. + * + * @param string|array $notification + * @param int $type + * + * @return $this + */ + public function addNotification($notification, $type) + { + $this->notifications[$type][] = $notification; + return $this; + } + + /** + * Return the notifications of this form + * + * @return array + */ + public function getNotifications() + { + if ($this->notifications === null) { + return array(); + } + + return $this->notifications; + } + /** * Set whether the Autosubmit decorator should be applied to this form * @@ -1264,55 +1308,48 @@ class Form extends Zend_Form } /** - * Return all form notifications + * Add a error notification and prevent the form from being successfully validated * - * @return array - */ - public function getNotifications() - { - return $this->notifications; - } - - /** - * Add a typed message to the notifications + * @param string|array $message The notfication's message * - * @param string $message The message which would be displayed to the user - * - * @param int $type The type of the message notification - */ - public function addNotification($message, $type = self::NOTIFICATION_ERROR) - { - $this->notifications[$message] = $type; - $this->markAsError(); - } - - /** - * Add a error message to notifications - * - * @param string $message + * @return $this */ public function error($message) { - $this->addNotification($message, $type = self::NOTIFICATION_ERROR); + $this->addNotification($message, self::NOTIFICATION_ERROR); + $this->markAsError(); + return $this; } /** - * Add a warning message to notifications + * Add a warning notification and prevent the form from being successfully validated * - * @param string $message + * @param string|array $message The notfication's message + * + * @return $this */ public function warning($message) { - $this->addNotification($message, $type = self::NOTIFICATION_WARNING); + $this->addNotification($message, self::NOTIFICATION_WARNING); + $this->markAsError(); + return $this; } /** - * Add a info message to notifications + * Add a info notification * - * @param string $message + * @param string|array $message The notfication's message + * @param bool $markAsError Whether to prevent the form from being successfully validated or not + * + * @return $this */ - public function info($message) + public function info($message, $markAsError = true) { - $this->addNotification($message, $type = self::NOTIFICATION_INFO); + $this->addNotification($message, self::NOTIFICATION_INFO); + if ($markAsError) { + $this->markAsError(); + } + + return $this; } } diff --git a/library/Icinga/Web/Form/Decorator/FormNotifications.php b/library/Icinga/Web/Form/Decorator/FormNotifications.php index ed725e625..935c0ce41 100644 --- a/library/Icinga/Web/Form/Decorator/FormNotifications.php +++ b/library/Icinga/Web/Form/Decorator/FormNotifications.php @@ -4,15 +4,16 @@ namespace Icinga\Web\Form\Decorator; use Zend_Form_Decorator_Abstract; -use Icinga\Web\Form as Form; +use Icinga\Exception\ProgrammingError; +use Icinga\Web\Form; /** - * Decorator to add a list of notifications at the top of a form + * Decorator to add a list of notifications at the top or bottom of a form */ class FormNotifications extends Zend_Form_Decorator_Abstract { /** - * Render form descriptions + * Render form notifications * * @param string $content The html rendered so far * @@ -31,16 +32,27 @@ class FormNotifications extends Zend_Form_Decorator_Abstract } $notifications = $this->recurseForm($form); - if (empty($notifications)) { return $content; } $html = '' . $content; + } + } + + /** + * Recurse the given form and return the hints for it and all of its subforms + * + * @param Form $form The form to recurse + * @param mixed $entirelyRequired Set by reference, true means all elements in the hierarchy are + * required, false only a partial subset and null none at all + * @param bool $elementsPassed Whether there were any elements passed during the recursion until now + * + * @return array + */ + protected function recurseForm(Form $form, & $entirelyRequired = null, $elementsPassed = false) + { + $requiredLabels = array(); + if ($form->getRequiredCue() !== null) { + $partiallyRequired = $partiallyOptional = false; + foreach ($form->getElements() as $element) { + if (! in_array($element->getType(), $this->blacklist)) { + if (! $element->isRequired()) { + $partiallyOptional = true; + if ($entirelyRequired) { + $entirelyRequired = false; + } + } else { + $partiallyRequired = true; + if (($label = $element->getDecorator('label')) !== false) { + $requiredLabels[] = $label; + } + } + } + } + + if (! $elementsPassed) { + $elementsPassed = $partiallyRequired || $partiallyOptional; + if ($entirelyRequired === null && $partiallyRequired) { + $entirelyRequired = ! $partiallyOptional; + } + } elseif ($entirelyRequired === null && $partiallyRequired) { + $entirelyRequired = false; + } + } + + $hints = array($form->getHints()); + foreach ($form->getSubForms() as $subForm) { + $hints[] = $this->recurseForm($subForm, $entirelyRequired, $elementsPassed); + } + + if ($entirelyRequired) { + foreach ($requiredLabels as $label) { + $label->setRequiredSuffix(''); + } + } + + return call_user_func_array('array_merge', $hints); + } +} diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index 42f04d7f3..3323520db 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -259,3 +259,13 @@ form ul.descriptions { } } } + +form ul.hints { + .non-list-like-list; + padding: 0.5em 0.5em 0 0.5em; + + li { + font-size: 0.8em; + padding-bottom: 0.5em; + } +} \ No newline at end of file From b61c9708bb0d94639b63c1048e09aacafab22b06 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 30 Jun 2015 15:05:57 +0200 Subject: [PATCH 005/108] main-content.less: Use a more friendlier color for info boxes refs #8983 --- public/css/icinga/main-content.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/css/icinga/main-content.less b/public/css/icinga/main-content.less index 72cffa19b..c51ef8722 100644 --- a/public/css/icinga/main-content.less +++ b/public/css/icinga/main-content.less @@ -104,7 +104,7 @@ table.benchmark { .info-box { padding: 0.5em; border: 1px solid lightgrey; - background-color: #fbfcc5; + background-color: #f2f4fd; } /* Action table */ From 0dc604029ac314e1c9ef921bfd851ec3b32ebedf Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 30 Jun 2015 15:10:17 +0200 Subject: [PATCH 006/108] AdminAccountPage: Do not put an element's description at the top of the form --- .../setup/application/forms/AdminAccountPage.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/modules/setup/application/forms/AdminAccountPage.php b/modules/setup/application/forms/AdminAccountPage.php index 5118ce291..26e274443 100644 --- a/modules/setup/application/forms/AdminAccountPage.php +++ b/modules/setup/application/forms/AdminAccountPage.php @@ -89,9 +89,6 @@ class AdminAccountPage extends Form } if (count($choices) > 1) { - $this->addDescription($this->translate( - 'Below are several options you can choose from for how to define the desired account.' - )); $this->addElement( 'select', 'user_type', @@ -99,6 +96,7 @@ class AdminAccountPage extends Form 'required' => true, 'autosubmit' => true, 'label' => $this->translate('Type Of Definition'), + 'description' => $this->translate('Choose how to define the desired account.'), 'multiOptions' => $choices, 'value' => $choice ) @@ -124,7 +122,7 @@ class AdminAccountPage extends Form 'label' => $this->translate('Username'), 'description' => $this->translate( 'Define the initial administrative account by providing a username that reflects' - . ' a user created later or one that is authenticated using external mechanisms' + . ' a user created later or one that is authenticated using external mechanisms.' ) ) ); @@ -139,7 +137,7 @@ class AdminAccountPage extends Form 'label' => $this->translate('Username'), 'description' => sprintf( $this->translate( - 'Choose a user reported by the %s backend as the initial administrative account', + 'Choose a user reported by the %s backend as the initial administrative account.', 'setup.admin' ), $this->backendConfig['backend'] === 'db' @@ -159,7 +157,7 @@ class AdminAccountPage extends Form 'required' => true, 'label' => $this->translate('Username'), 'description' => $this->translate( - 'Enter the username to be used when creating an initial administrative account' + 'Enter the username to be used when creating an initial administrative account.' ) ) ); @@ -169,7 +167,7 @@ class AdminAccountPage extends Form array( 'required' => true, 'label' => $this->translate('Password'), - 'description' => $this->translate('Enter the password to assign to the newly created account') + 'description' => $this->translate('Enter the password to assign to the newly created account.') ) ); $this->addElement( @@ -179,7 +177,7 @@ class AdminAccountPage extends Form 'required' => true, 'label' => $this->translate('Repeat password'), 'description' => $this->translate( - 'Please repeat the password given above to avoid typing errors' + 'Please repeat the password given above to avoid typing errors.' ), 'validators' => array( array('identical', false, array('new_user_password')) From f3c8f2229faee287de0b1ec84440b1988623f84b Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 15 Jul 2015 15:15:15 +0200 Subject: [PATCH 007/108] ErrorLabeller: Provide localized output for the MimeType validator refs #8758 --- library/Icinga/Web/Form/ErrorLabeller.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/library/Icinga/Web/Form/ErrorLabeller.php b/library/Icinga/Web/Form/ErrorLabeller.php index f66260149..803e1d80d 100644 --- a/library/Icinga/Web/Form/ErrorLabeller.php +++ b/library/Icinga/Web/Form/ErrorLabeller.php @@ -6,6 +6,7 @@ namespace Icinga\Web\Form; use BadMethodCallException; use Zend_Translate_Adapter; use Zend_Validate_NotEmpty; +use Zend_Validate_File_MimeType; use Icinga\Web\Form\Validator\DateTimeValidator; use Icinga\Web\Form\Validator\ReadablePathValidator; use Icinga\Web\Form\Validator\WritablePathValidator; @@ -42,10 +43,15 @@ class ErrorLabeller extends Zend_Translate_Adapter $label = $element->getLabel() ?: $element->getName(); return array( - Zend_Validate_NotEmpty::IS_EMPTY => sprintf(t('%s is required and must not be empty'), $label), - WritablePathValidator::NOT_WRITABLE => sprintf(t('%s is not writable', 'config.path'), $label), - WritablePathValidator::DOES_NOT_EXIST => sprintf(t('%s does not exist', 'config.path'), $label), - ReadablePathValidator::NOT_READABLE => sprintf(t('%s is not readable', 'config.path'), $label), + Zend_Validate_NotEmpty::IS_EMPTY => sprintf(t('%s is required and must not be empty'), $label), + Zend_Validate_File_MimeType::FALSE_TYPE => sprintf( + t('%s (%%value%%) has a false MIME type of "%%type%%"'), + $label + ), + Zend_Validate_File_MimeType::NOT_DETECTED => sprintf(t('%s (%%value%%) has no MIME type'), $label), + WritablePathValidator::NOT_WRITABLE => sprintf(t('%s is not writable', 'config.path'), $label), + WritablePathValidator::DOES_NOT_EXIST => sprintf(t('%s does not exist', 'config.path'), $label), + ReadablePathValidator::NOT_READABLE => sprintf(t('%s is not readable', 'config.path'), $label), DateTimeValidator::INVALID_DATETIME_FORMAT => sprintf( t('%s not in the expected format: %%value%%'), $label From c1d9cde312d2a7306819c013aad41d28b3365e1b Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 15 Jul 2015 15:25:40 +0200 Subject: [PATCH 008/108] js: Properly submit multipart/form-data This is not IE <10 compatible. Fix follows. ;-) refs #8758 --- public/js/icinga/events.js | 23 ++++++++++++++++++----- public/js/icinga/loader.js | 14 +++++++++++++- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 00a0ff9f4..2ba666f47 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -199,6 +199,7 @@ var $form = $(event.currentTarget).closest('form'); var url = $form.attr('action'); var method = $form.attr('method'); + var encoding = $form.attr('enctype'); var $button = $('input[type=submit]:focus', $form).add('button[type=submit]:focus', $form); var $target; var data; @@ -230,6 +231,10 @@ method = method.toUpperCase(); } + if (typeof encoding === 'undefined') { + encoding = 'application/x-www-form-urlencoded'; + } + if ($button.length === 0) { $button = $('input[type=submit]', $form).add('button[type=submit]', $form).first(); } @@ -266,14 +271,22 @@ url = icinga.utils.addUrlParams(url, dataObj); } else { - data = $form.serializeArray(); + if (encoding === 'multipart/form-data') { + data = new FormData($form[0]); + } else { + data = $form.serializeArray(); + } if (typeof autosubmit === 'undefined' || ! autosubmit) { if ($button.length && $button.attr('name') !== 'undefined') { - data.push({ - name: $button.attr('name'), - value: $button.attr('value') - }); + if (data instanceof FormData) { + data.append($button.attr('name'), $button.attr('value')); + } else { + data.push({ + name: $button.attr('name'), + value: $button.attr('value') + }); + } } } } diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index b2e8061e2..e7645f313 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -100,13 +100,25 @@ headers['X-Icinga-WindowId'] = 'undefined'; } + // This is jQuery's default content type + var contentType = 'application/x-www-form-urlencoded; charset=UTF-8'; + + var isFormData = data instanceof FormData; + if (isFormData) { + // Setting false is mandatory as the form's data + // won't be recognized by the server otherwise + contentType = false; + } + var self = this; var req = $.ajax({ type : method, url : url, data : data, headers: headers, - context: self + context: self, + contentType: contentType, + processData: ! isFormData }); req.$target = $target; From 6d4d99aa62817d855cf710f7fc5f58065f645322 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 15 Jul 2015 15:49:34 +0200 Subject: [PATCH 009/108] js: Use window.FormData instead of just FormData refs #8758 --- public/js/icinga/events.js | 4 ++-- public/js/icinga/loader.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 2ba666f47..54ea46725 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -272,14 +272,14 @@ url = icinga.utils.addUrlParams(url, dataObj); } else { if (encoding === 'multipart/form-data') { - data = new FormData($form[0]); + data = new window.FormData($form[0]); } else { data = $form.serializeArray(); } if (typeof autosubmit === 'undefined' || ! autosubmit) { if ($button.length && $button.attr('name') !== 'undefined') { - if (data instanceof FormData) { + if (encoding === 'multipart/form-data') { data.append($button.attr('name'), $button.attr('value')); } else { data.push({ diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index e7645f313..5677eb629 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -103,7 +103,7 @@ // This is jQuery's default content type var contentType = 'application/x-www-form-urlencoded; charset=UTF-8'; - var isFormData = data instanceof FormData; + var isFormData = data instanceof window.FormData; if (isFormData) { // Setting false is mandatory as the form's data // won't be recognized by the server otherwise From d192410435250d30686f39a555480325dde80152 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 16 Jul 2015 11:23:48 +0200 Subject: [PATCH 010/108] Introduce GET parameter _disableLayout to ... disable the entire layout refs #8758 --- library/Icinga/Web/Controller/ActionController.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/library/Icinga/Web/Controller/ActionController.php b/library/Icinga/Web/Controller/ActionController.php index 571f0792f..750c101f3 100644 --- a/library/Icinga/Web/Controller/ActionController.php +++ b/library/Icinga/Web/Controller/ActionController.php @@ -96,6 +96,9 @@ class ActionController extends Zend_Controller_Action if ($this->rerenderLayout = $request->getUrl()->shift('renderLayout')) { $this->xhrLayout = 'body'; } + if ($request->getUrl()->shift('_disableLayout')) { + $this->_helper->layout()->disableLayout(); + } if ($this->requiresLogin()) { $this->redirectToLogin(Url::fromRequest()); From 549f36f82c1074af601bd3d4b0284404fcd9db55 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 16 Jul 2015 11:27:02 +0200 Subject: [PATCH 011/108] Form: Do not create a new instance of Url when setting the action We're shifting parameters and by creating a new instance we'll lose such changes. refs #8758 --- library/Icinga/Web/Form.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index e2ee66d0a..188f8e09c 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -615,7 +615,7 @@ class Form extends Zend_Form // TODO(el): Re-evalute this necessity. JavaScript could use the container's URL if there's no action set. // We MUST set an action as JS gets confused otherwise, if // this form is being displayed in an additional column - $this->setAction(Url::fromRequest()->without(array_keys($this->getElements()))); + $this->setAction($this->getRequest()->getUrl()->without(array_keys($this->getElements()))); } $this->created = true; From 2164bb86c886b4041b2016c12883385c6a144560 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 16 Jul 2015 11:27:31 +0200 Subject: [PATCH 012/108] Tabs: Do not create a new instance of Url when rendering the refresh url We're shifting parameters and by creating a new instance we'll lose such changes. refs #8758 --- library/Icinga/Web/Widget/Tabs.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Widget/Tabs.php b/library/Icinga/Web/Widget/Tabs.php index aa5e7cda6..220393aff 100644 --- a/library/Icinga/Web/Widget/Tabs.php +++ b/library/Icinga/Web/Widget/Tabs.php @@ -309,7 +309,7 @@ EOT; private function renderRefreshTab() { - $url = Url::fromRequest()->without('renderLayout'); + $url = Icinga::app()->getFrontController()->getRequest()->getUrl(); $tab = $this->get($this->getActiveName()); if ($tab !== null) { From 95bcb95cb5fad5b7973bb44e1e5b8250fd907693 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 20 Jul 2015 10:13:48 +0200 Subject: [PATCH 013/108] events.js: Prevent default form submission as late as possible --- public/js/icinga/events.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 54ea46725..51f9846f9 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -239,9 +239,6 @@ $button = $('input[type=submit]', $form).add('button[type=submit]', $form).first(); } - event.stopPropagation(); - event.preventDefault(); - if ($button.length) { // Activate spinner if ($button.hasClass('spinner')) { @@ -297,6 +294,8 @@ icinga.loader.loadUrl(url, $target, data, method); + event.stopPropagation(); + event.preventDefault(); return false; }, From be88683c19d2cce1df0b810fc821dfbc873ce228 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 20 Jul 2015 10:14:55 +0200 Subject: [PATCH 014/108] layout.phtml: Add hidden iframe for non-xhr file uploads refs #8758 --- application/layouts/scripts/layout.phtml | 1 + public/css/icinga/layout-structure.less | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/application/layouts/scripts/layout.phtml b/application/layouts/scripts/layout.phtml index cbfce7714..1d766b505 100644 --- a/application/layouts/scripts/layout.phtml +++ b/application/layouts/scripts/layout.phtml @@ -54,6 +54,7 @@ $iframeClass = $isIframe ? ' iframe' : '';
render('body.phtml') ?>
+ diff --git a/public/css/icinga/layout-structure.less b/public/css/icinga/layout-structure.less index 2b4228b5f..3f9d4858e 100644 --- a/public/css/icinga/layout-structure.less +++ b/public/css/icinga/layout-structure.less @@ -47,6 +47,10 @@ html { } } +#fileupload-frame-target { + display: none; +} + #responsive-debug { font-size: 0.9em; font-family: Courier new, monospace; From 37b87eb284c08530457d93c611eba582fd4fa82a Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 20 Jul 2015 10:52:28 +0200 Subject: [PATCH 015/108] js: Add fallback in case XHR file uploads are not possible refs #8758 --- public/js/icinga/events.js | 11 +++++++++++ public/js/icinga/loader.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index 51f9846f9..a1048095b 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -269,6 +269,17 @@ url = icinga.utils.addUrlParams(url, dataObj); } else { if (encoding === 'multipart/form-data') { + if (typeof window.FormData === 'undefined') { + icinga.loader.submitFormToIframe($form, url, $target); + + // Disable all form controls to prevent resubmission as early as possible. + // (This relies on native form submission, so using setTimeout is the only possible solution) + setTimeout(function () { + $form.find(':input:not(:disabled)').prop('disabled', true); + }, 0); + return true; + } + data = new window.FormData($form[0]); } else { data = $form.serializeArray(); diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index 5677eb629..3a3e1a4bf 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -140,6 +140,35 @@ return req; }, + /** + * Mimic XHR form submission by using an iframe + * + * @param {object} $form The form being submitted + * @param {string} action The form's action URL + * @param {object} $target The target container + */ + submitFormToIframe: function ($form, action, $target) { + var self = this; + + $form.prop('action', self.icinga.utils.addUrlParams(action, { + '_disableLayout': true + })); + $form.prop('target', 'fileupload-frame-target'); + $('#fileupload-frame-target').on('load', function (event) { + var $frame = $(event.target); + + // Fetch the frame's new content, paste it into the target.. + self.renderContentToContainer( + $frame.contents().find('body').html(), + $target, + 'replace' + ); + $frame.prop('src', 'about:blank'); // ..and clear the frame's dom + + $frame.off('load'); // Unbind the event as it's set on demand + }); + }, + /** * Create an URL relative to the Icinga base Url, still unused * From 33aa78d8b787a983f64e6d53112d7509faa3785f Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 20 Jul 2015 16:40:24 +0200 Subject: [PATCH 016/108] Puppet: Support Apache 2.4 refs #9453 --- .../templates/icingaweb.conf.erb | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/.puppet/profiles/icingaweb2_dev/templates/icingaweb.conf.erb b/.puppet/profiles/icingaweb2_dev/templates/icingaweb.conf.erb index d19dbe9ee..c8b23e97a 100644 --- a/.puppet/profiles/icingaweb2_dev/templates/icingaweb.conf.erb +++ b/.puppet/profiles/icingaweb2_dev/templates/icingaweb.conf.erb @@ -1,23 +1,38 @@ -Alias /<%= @web_path %> /vagrant/public +Alias /<%= @web_path %> "/vagrant/public" - - Options FollowSymLinks + + Options SymLinksIfOwnerMatch AllowOverride None - Order allow,deny - Allow from all - # SetEnv ICINGAWEB_CONFIGDIR <%= @config %> + + # Apache 2.4 + + Require all granted + + + + + # Apache 2.2 + Order allow,deny + Allow from all + + + SetEnv ICINGAWEB_CONFIGDIR <%= @config %> EnableSendfile Off - RewriteEngine on - RewriteBase /<%= @web_path %>/ - RewriteCond %{REQUEST_FILENAME} -s [OR] - RewriteCond %{REQUEST_FILENAME} -l [OR] - RewriteCond %{REQUEST_FILENAME} -d - RewriteRule ^.*$ - [NC,L] - RewriteRule ^.*$ index.php [NC,L] + + RewriteEngine on + RewriteBase /<%= @web_path %>/ + RewriteCond %{REQUEST_FILENAME} -s [OR] + RewriteCond %{REQUEST_FILENAME} -l [OR] + RewriteCond %{REQUEST_FILENAME} -d + RewriteRule ^.*$ - [NC,L] + RewriteRule ^.*$ index.php [NC,L] + - php_value xdebug.idekey PHPSTORM + + DirectoryIndex error_norewrite.html + ErrorDocument 404 /error_norewrite.html + - From b74b10a6d01cd499dc8db2a909f6952996c9e1ce Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 20 Jul 2015 16:44:48 +0200 Subject: [PATCH 017/108] Puppet: Disable api feature of Icinga 2 refs #9453 --- .puppet/modules/icinga2/manifests/init.pp | 4 ++++ .puppet/profiles/icinga2_dev/manifests/init.pp | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.puppet/modules/icinga2/manifests/init.pp b/.puppet/modules/icinga2/manifests/init.pp index 0545ba7c5..26c7300a5 100644 --- a/.puppet/modules/icinga2/manifests/init.pp +++ b/.puppet/modules/icinga2/manifests/init.pp @@ -39,4 +39,8 @@ class icinga2 { } icinga2::feature { [ 'statusdata', 'command', 'compatlog' ]: } + + icinga2::feature { 'api': + ensure => absent, + } } diff --git a/.puppet/profiles/icinga2_dev/manifests/init.pp b/.puppet/profiles/icinga2_dev/manifests/init.pp index a6ee98683..7082fe935 100644 --- a/.puppet/profiles/icinga2_dev/manifests/init.pp +++ b/.puppet/profiles/icinga2_dev/manifests/init.pp @@ -1,6 +1,6 @@ # Class: icinga2_dev # -# This class installs Icinga 2 w/ MySQL and provides Icinga 2 test configuration. +# This class installs Icinga 2 w/ MySQL and PostgreSQL and provides Icinga 2 test configuration. # # Requires: # @@ -19,7 +19,8 @@ class icinga2_dev { include monitoring_test_config icinga2::config { [ - 'conf.d/test-config', 'conf.d/commands', 'constants' ]: + 'conf.d/test-config', 'conf.d/commands', 'constants', + ]: source => 'puppet:///modules/icinga2_dev', } From 7f8c589cdbfada93a5d7784520eb0b7ac26c810c Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 20 Jul 2015 16:45:21 +0200 Subject: [PATCH 018/108] Puppet: Fix epel to use the major release version of the OS refs #9453 --- .puppet/modules/epel/manifests/init.pp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.puppet/modules/epel/manifests/init.pp b/.puppet/modules/epel/manifests/init.pp index 9ec1ba692..2792ff918 100644 --- a/.puppet/modules/epel/manifests/init.pp +++ b/.puppet/modules/epel/manifests/init.pp @@ -15,10 +15,10 @@ class epel { yumrepo { 'epel': - mirrorlist => "http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=${::architecture}", + mirrorlist => "http://mirrors.fedoraproject.org/mirrorlist?repo=epel-${::operatingsystemmajrelease}&arch=${::architecture}", enabled => '1', gpgcheck => '0', - descr => "Extra Packages for Enterprise Linux 6 - ${::architecture}" + descr => "Extra Packages for Enterprise Linux ${::operatingsystemmajrelease} - ${::architecture}" } } From 2ac27e33253e118146357c1cfa4c60ff0a31ecad Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 20 Jul 2015 16:46:25 +0200 Subject: [PATCH 019/108] Puppet: Fix icinga_packages to use the major release version of the OS refs #9453 --- .puppet/modules/icinga_packages/manifests/init.pp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.puppet/modules/icinga_packages/manifests/init.pp b/.puppet/modules/icinga_packages/manifests/init.pp index 7e3e8d188..b6e46960a 100644 --- a/.puppet/modules/icinga_packages/manifests/init.pp +++ b/.puppet/modules/icinga_packages/manifests/init.pp @@ -8,7 +8,7 @@ # class icinga_packages { yumrepo { 'icinga_packages': - baseurl => 'http://packages.icinga.org/epel/6/snapshot/', + baseurl => "http://packages.icinga.org/epel/${::operatingsystemmajrelease}/snapshot/", enabled => '1', gpgcheck => '1', gpgkey => 'http://packages.icinga.org/icinga.key', From af4dbadd3681419e8313509921ce8e5601ea45a0 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 20 Jul 2015 16:47:29 +0200 Subject: [PATCH 020/108] Puppet: Use mariadb on RHEL/CentOS >= 7 refs #9453 --- .puppet/modules/mysql/manifests/init.pp | 34 +++++++++++++++++----- .puppet/modules/mysql/templates/my.cnf.erb | 4 +-- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/.puppet/modules/mysql/manifests/init.pp b/.puppet/modules/mysql/manifests/init.pp index a235a540c..612211755 100644 --- a/.puppet/modules/mysql/manifests/init.pp +++ b/.puppet/modules/mysql/manifests/init.pp @@ -1,6 +1,6 @@ # Class: mysql # -# This class installs the mysql server and client software. +# This class installs the MySQL server and client software on a RHEL or CentOS # # Parameters: # @@ -16,21 +16,39 @@ class mysql { Exec { path => '/usr/bin' } + if versioncmp($::operatingsystemmajrelease, '7') >= 0 { + $client_package_name = 'mariadb' + $server_package_name = 'mariadb-server' + $server_service_name = 'mariadb' + $cnf = '/etc/my.cnf.d/server.cnf' + $log_error = '/var/log/mariadb/mariadb.log' + $pid_file = '/var/run/mariadb/mariadb.pid' + } else { + $client_package_name = 'mysql' + $server_package_name = 'mysql-server' + $server_service_name = 'mysqld' + $cnf = '/etc/my.cnf' + $log_error = '/var/log/mysqld.log' + $pid_file = '/var/run/mysqld/mysqld.pid' + } + package { [ - 'mysql', 'mysql-server' + $client_package_name, $server_package_name, ]: ensure => latest, } - service { 'mysqld': - ensure => running, + service { $server_service_name: + alias => 'mysqld', enable => true, - require => Package['mysql-server'] + ensure => running, + require => Package[$server_package_name], } - file { '/etc/my.cnf': + file { $cnf: content => template('mysql/my.cnf.erb'), - require => Package['mysql-server'], - notify => Service['mysqld'] + notify => Service['mysqld'], + recurse => true, + require => Package[$server_package_name], } } diff --git a/.puppet/modules/mysql/templates/my.cnf.erb b/.puppet/modules/mysql/templates/my.cnf.erb index d26583ee3..7f819af73 100644 --- a/.puppet/modules/mysql/templates/my.cnf.erb +++ b/.puppet/modules/mysql/templates/my.cnf.erb @@ -104,8 +104,8 @@ innodb_file_per_table innodb_log_file_size = 64M [mysqld_safe] -log-error=/var/log/mysqld.log -pid-file=/var/run/mysqld/mysqld.pid +log-error=<%= @log_error %> +pid-file=<%= @pid_file %> # Increase the amount of open files allowed per process. Warning: Make # sure you have set the global system limit high enough! The high value From a9bb42029c1c84262ab432b09a94639e0b72f88d Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 20 Jul 2015 16:48:02 +0200 Subject: [PATCH 021/108] Puppet: Don't fail on createlang for PostgreSQL databases refs #9453 --- .puppet/modules/pgsql/manifests/database/create.pp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.puppet/modules/pgsql/manifests/database/create.pp b/.puppet/modules/pgsql/manifests/database/create.pp index 97fcb163d..004b4c077 100644 --- a/.puppet/modules/pgsql/manifests/database/create.pp +++ b/.puppet/modules/pgsql/manifests/database/create.pp @@ -25,7 +25,7 @@ define pgsql::database::create ($username, $password) { unless => "psql -tAc \"SELECT 1 FROM pg_roles WHERE rolname='${username}'\" | grep -q 1", command => "psql -c \"CREATE ROLE ${username} WITH LOGIN PASSWORD '${password}';\" && \ createdb -O ${username} -E UTF8 -T template0 ${name} && \ -createlang plpgsql ${name}", +(createlang plpgsql ${name} || true)", user => 'postgres', require => Class['pgsql'] } From cd4caf769b57933521f5fff72cf1e154ff67a110 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 20 Jul 2015 16:48:40 +0200 Subject: [PATCH 022/108] Puppet: Fix indent in puppet.sh refs #9453 --- .puppet/manifests/puppet.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.puppet/manifests/puppet.sh b/.puppet/manifests/puppet.sh index 225ce7398..01463be51 100644 --- a/.puppet/manifests/puppet.sh +++ b/.puppet/manifests/puppet.sh @@ -3,7 +3,7 @@ set -e if which puppet >/dev/null 2>&1; then - exit 0 + exit 0 fi RELEASEVER=$(rpm -q --qf "%{VERSION}" $(rpm -q --whatprovides redhat-release)) @@ -20,7 +20,7 @@ esac echo "Adding puppet repository.." rpm --import "https://yum.puppetlabs.com/RPM-GPG-KEY-puppetlabs" -rpm -ivh $PUPPET >/dev/null +rpm -Uvh $PUPPET >/dev/null echo "Installing puppet.." yum install -y puppet >/dev/null From 9dc4dbf24bac008ff27b48194b2f8214bd8d2997 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Mon, 20 Jul 2015 16:49:17 +0200 Subject: [PATCH 023/108] Vagrant: Use CentOS 7 base boxes refs #9453 --- Vagrantfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index 28ac3897d..198016789 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -22,13 +22,13 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.provision :shell, :path => ".puppet/manifests/puppet.sh" config.vm.provider :virtualbox do |v, override| - override.vm.box = "puppetlabs/centos-6.6-64-puppet" + override.vm.box = "puppetlabs/centos-7.0-64-puppet" v.customize ["modifyvm", :id, "--memory", "1024"] end config.vm.provider :parallels do |p, override| - override.vm.box = "parallels/centos-6.5" + override.vm.box = "parallels/centos-7.1" p.name = "Icinga Web 2 Development" From 570dada0d802e339875c402c0fc677a7db697f71 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 21 Jul 2015 14:15:12 +0200 Subject: [PATCH 024/108] js: Manually submit the form if it's being automatically submitted... ...in case we're submitting a multipart/form-data form using the iframe fallback. The form wouldn't be submitted otherwise. refs #8758 --- public/js/icinga/events.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/public/js/icinga/events.js b/public/js/icinga/events.js index a1048095b..000ecf103 100644 --- a/public/js/icinga/events.js +++ b/public/js/icinga/events.js @@ -192,7 +192,6 @@ * */ submitForm: function (event, autosubmit) { - //return false; var self = event.data.self; var icinga = self.icinga; // .closest is not required unless subelements to trigger this @@ -277,7 +276,21 @@ setTimeout(function () { $form.find(':input:not(:disabled)').prop('disabled', true); }, 0); - return true; + + if (! typeof autosubmit === 'undefined' && 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.. + $button.prop('disabled', true); + } + + $form[0].submit(); // This should actually not trigger the onSubmit event, let's hope that this is true for all browsers.. + event.stopPropagation(); + event.preventDefault(); + return false; + } else { + return true; + } } data = new window.FormData($form[0]); From 0a9a06674979dcabf186af32df2cc375711e84f7 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 21 Jul 2015 15:13:56 +0200 Subject: [PATCH 025/108] Form: Do not create a new instance of Url when returning the redirect url We're shifting parameters and by creating a new instance we'll lose such changes. refs #8758 --- library/Icinga/Web/Form.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 188f8e09c..816ea0469 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -277,7 +277,7 @@ class Form extends Zend_Form public function getRedirectUrl() { if ($this->redirectUrl === null) { - $url = Url::fromRequest(array(), $this->getRequest()); + $url = $this->getRequest()->getUrl(); // Be sure to remove all form dependent params because we do not want to submit it again $this->redirectUrl = $url->without(array_keys($this->getElements())); } From fc481e527bc021144205e9fea88397bdf5ce60bc Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 21 Jul 2015 15:22:24 +0200 Subject: [PATCH 026/108] Form: Process request parameter _frameUpload This parameter is being used to flag a request as form submission issued by utilizing an iframe. Appending it to a form's action causes no redirection to take place in case of successful submission and a reduced but still valid layout to be printed in any way. Redirection must be handled by the client regarding the meta tag "redirectUrl". refs #8758 --- application/layouts/scripts/wrapped.phtml | 10 ++++++++++ library/Icinga/Web/Form.php | 10 +++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 application/layouts/scripts/wrapped.phtml diff --git a/application/layouts/scripts/wrapped.phtml b/application/layouts/scripts/wrapped.phtml new file mode 100644 index 000000000..25a7cf2e4 --- /dev/null +++ b/application/layouts/scripts/wrapped.phtml @@ -0,0 +1,10 @@ + + + layout()->redirectUrl)): ?> + + + + + render('inline.phtml'); ?> + + \ No newline at end of file diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 816ea0469..1e5487f7c 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -946,12 +946,20 @@ class Form extends Zend_Form $formData = $this->getRequestData(); if ($this->getUidDisabled() || $this->wasSent($formData)) { + if (($frameUpload = (bool) $request->getUrl()->shift('_frameUpload', false))) { + $this->getView()->layout()->setLayout('wrapped'); + } + $this->populate($formData); // Necessary to get isSubmitted() to work if (! $this->getSubmitLabel() || $this->isSubmitted()) { if ($this->isValid($formData) && (($this->onSuccess !== null && false !== call_user_func($this->onSuccess, $this)) || ($this->onSuccess === null && false !== $this->onSuccess()))) { - $this->getResponse()->redirectAndExit($this->getRedirectUrl()); + if (! $frameUpload) { + $this->getResponse()->redirectAndExit($this->getRedirectUrl()); + } + + $this->getView()->layout()->redirectUrl = $this->getRedirectUrl(); } } elseif ($this->getValidatePartial()) { // The form can't be processed but we may want to show validation errors though From 9471c3c574d12943524dc70f8b942d902f2d746e Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 21 Jul 2015 15:43:47 +0200 Subject: [PATCH 027/108] js: Make use of the _frameUpload parameter when submitting a form.. ..to an iframe. This ensures that stuff like notifications are immediately visible to the user after successful form submission. refs #8758 --- public/js/icinga/loader.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index 3a3e1a4bf..0af0863e3 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -151,20 +151,26 @@ var self = this; $form.prop('action', self.icinga.utils.addUrlParams(action, { - '_disableLayout': true + '_frameUpload': true })); $form.prop('target', 'fileupload-frame-target'); $('#fileupload-frame-target').on('load', function (event) { var $frame = $(event.target); + var $contents = $frame.contents(); - // Fetch the frame's new content, paste it into the target.. - self.renderContentToContainer( - $frame.contents().find('body').html(), - $target, - 'replace' - ); - $frame.prop('src', 'about:blank'); // ..and clear the frame's dom + var $redirectMeta = $contents.find('meta[name="redirectUrl"]'); + if ($redirectMeta.length) { + self.loadUrl($redirectMeta.attr('content'), $target); + } else { + // Fetch the frame's new content and paste it into the target + self.renderContentToContainer( + $contents.find('body').html(), + $target, + 'replace' + ); + } + $frame.prop('src', 'about:blank'); // Clear the frame's dom $frame.off('load'); // Unbind the event as it's set on demand }); }, From 6c9819204dbdd9ccd138733ac88ca7ebce5b0886 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 21 Jul 2015 16:33:24 +0200 Subject: [PATCH 028/108] Form: Only work with a single type when handling redirect urls $form->setRedirectUrl('some/url') still works, but $form->getRedirectUrl() will only return instances of Icinga\Web\Url now. --- library/Icinga/Web/Form.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 1e5487f7c..78ae1f587 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -84,7 +84,7 @@ class Form extends Zend_Form /** * The url to redirect to upon success * - * @var string|Url + * @var Url */ protected $redirectUrl; @@ -262,9 +262,17 @@ class Form extends Zend_Form * @param string|Url $url The url to redirect to * * @return $this + * + * @throws ProgrammingError In case $url is neither a string nor a instance of Icinga\Web\Url */ public function setRedirectUrl($url) { + if (is_string($url)) { + $url = Url::fromPath($url, array(), $this->getRequest()); + } elseif (! $url instanceof Url) { + throw new ProgrammingError('$url must be a string or instance of Icinga\Web\Url'); + } + $this->redirectUrl = $url; return $this; } @@ -272,7 +280,7 @@ class Form extends Zend_Form /** * Return the url to redirect to upon success * - * @return string|Url + * @return Url */ public function getRedirectUrl() { From 7c89887773c649a7d916274d39ba7d1c2449a47f Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 21 Jul 2015 16:35:56 +0200 Subject: [PATCH 029/108] Form: Provide an absolute url as redirect url for successful frame uploads refs #8758 --- library/Icinga/Web/Form.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 78ae1f587..4e587a148 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -967,7 +967,7 @@ class Form extends Zend_Form $this->getResponse()->redirectAndExit($this->getRedirectUrl()); } - $this->getView()->layout()->redirectUrl = $this->getRedirectUrl(); + $this->getView()->layout()->redirectUrl = $this->getRedirectUrl()->getAbsoluteUrl(); } } elseif ($this->getValidatePartial()) { // The form can't be processed but we may want to show validation errors though From 93fbb0231c9ab5027f323226e2c79d6ceb5ec817 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 21 Jul 2015 16:37:21 +0200 Subject: [PATCH 030/108] loader.js: Split processRedirectHeader() to allow partly reuse refs #8758 --- public/js/icinga/loader.js | 46 +++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index 0af0863e3..9d518d39d 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -326,16 +326,34 @@ } } + this.redirectToUrl(redirect, req.$target, req.getResponseHeader('X-Icinga-Rerender-Layout')); + return true; + }, + + /** + * Redirect to the given url + * + * @param {string} url + * @param {object} $target + * @param {boolean} rerenderLayout + */ + redirectToUrl: function (url, $target, rerenderLayout) { + var icinga = this.icinga; + + if (typeof rerenderLayout === 'undefined') { + rerenderLayout = false; + } + icinga.logger.debug( - 'Got redirect for ', req.$target, ', URL was ' + redirect + 'Got redirect for ', $target, ', URL was ' + url ); - if (req.getResponseHeader('X-Icinga-Rerender-Layout')) { - var parts = redirect.split(/#!/); - redirect = parts.shift(); - var redirectionUrl = this.addUrlFlag(redirect, 'renderLayout'); + if (rerenderLayout) { + var parts = url.split(/#!/); + url = parts.shift(); + var redirectionUrl = this.addUrlFlag(url, 'renderLayout'); var r = this.loadUrl(redirectionUrl, $('#layout')); - r.url = redirect; + r.url = url; if (parts.length) { r.loadNext = parts; } else if (!! document.location.hash) { @@ -345,28 +363,24 @@ r.loadNext = parts; } } - } else { - - if (redirect.match(/#!/)) { - var parts = redirect.split(/#!/); + if (url.match(/#!/)) { + var parts = url.split(/#!/); icinga.ui.layout2col(); this.loadUrl(parts.shift(), $('#col1')); this.loadUrl(parts.shift(), $('#col2')); } else { - - if (req.$target.attr('id') === 'col2') { // TODO: multicol - if ($('#col1').data('icingaUrl').split('?')[0] === redirect.split('?')[0]) { + if ($target.attr('id') === 'col2') { // TODO: multicol + if ($('#col1').data('icingaUrl').split('?')[0] === url.split('?')[0]) { icinga.ui.layout1col(); - req.$target = $('#col1'); + $target = $('#col1'); delete(this.requests['col2']); } } - this.loadUrl(redirect, req.$target); + this.loadUrl(url, $target); } } - return true; }, cacheLoadedIcons: function($container) { From 5e1ea958b4f364c6ca8d7be49bb8383046b4679f Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 21 Jul 2015 16:38:52 +0200 Subject: [PATCH 031/108] js: Correctly process the redirectUrl-meta tag for successful frame uploads refs #8758 --- public/js/icinga/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index 9d518d39d..232ecbc7e 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -160,7 +160,7 @@ var $redirectMeta = $contents.find('meta[name="redirectUrl"]'); if ($redirectMeta.length) { - self.loadUrl($redirectMeta.attr('content'), $target); + self.redirectToUrl($redirectMeta.attr('content'), $target); } else { // Fetch the frame's new content and paste it into the target self.renderContentToContainer( From 163911ffd7e5f93e240a4616a4fb0d0575281f45 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 21 Jul 2015 16:47:17 +0200 Subject: [PATCH 032/108] Indicate empty icinga_programstatus table as problem fixes #9695 --- .../BackendAvailabilityMenuItemRenderer.php | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/modules/monitoring/library/Monitoring/Web/Menu/BackendAvailabilityMenuItemRenderer.php b/modules/monitoring/library/Monitoring/Web/Menu/BackendAvailabilityMenuItemRenderer.php index 186351a40..4359d5da5 100644 --- a/modules/monitoring/library/Monitoring/Web/Menu/BackendAvailabilityMenuItemRenderer.php +++ b/modules/monitoring/library/Monitoring/Web/Menu/BackendAvailabilityMenuItemRenderer.php @@ -3,41 +3,50 @@ namespace Icinga\Module\Monitoring\Web\Menu; -use Icinga\Web\Menu as Menu; -use Icinga\Module\Monitoring\Backend\MonitoringBackend; +use Icinga\Web\Menu; use Icinga\Web\Menu\MenuItemRenderer; +use Icinga\Module\Monitoring\Backend\MonitoringBackend; class BackendAvailabilityMenuItemRenderer extends MenuItemRenderer { /** - * Checks whether the monitoring backend is running or not + * Get whether or not the monitoring backend is currently running * - * @return mixed + * @return bool */ protected function isCurrentlyRunning() { - return MonitoringBackend::instance()->select()->from( - 'programstatus', - array( - 'is_currently_running' + $programStatus = MonitoringBackend::instance() + ->select() + ->from( + 'programstatus', + array('is_currently_running') ) - )->getQuery()->fetchRow()->is_currently_running; + ->fetchRow(); + return $programStatus !== false ? (bool) $programStatus : false; } /** - * @see MenuItemRenderer::render() + * {@inheritdoc} */ public function render(Menu $menu) { return $this->getBadge() . $this->createLink($menu); } + /** + * Get the problem badge HTML + * + * @return string + */ protected function getBadge() { - if (! (bool)$this->isCurrentlyRunning()) { + if (! $this->isCurrentlyRunning()) { return sprintf( - '
%s
', - mt('monitoring', 'monitoring backend is not running'), + '
%d
', + sprintf( + mt('monitoring', 'Monitoring backend %s is not running'), MonitoringBackend::instance()->getName() + ), 1 ); } @@ -51,10 +60,12 @@ class BackendAvailabilityMenuItemRenderer extends MenuItemRenderer */ public function getSummary() { - if (! (bool)$this->isCurrentlyRunning()) { + if (! $this->isCurrentlyRunning()) { return array( - 'problems' => 1, - 'title' => mt('monitoring', 'monitoring backend is not running') + 'problems' => 1, + 'title' => sprintf( + mt('monitoring', 'Monitoring backend %s is not running'), MonitoringBackend::instance()->getName() + ) ); } return null; From 8f2849f32a1bd92330a74fb7b2c628d4b207478f Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 21 Jul 2015 16:48:56 +0200 Subject: [PATCH 033/108] Form: Improve code readability.. ..and avoid the necessity to provide another mock when testing Form::handleRequest(). refs #8758 --- library/Icinga/Web/Form.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 4e587a148..f41672b28 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -965,9 +965,9 @@ class Form extends Zend_Form || ($this->onSuccess === null && false !== $this->onSuccess()))) { if (! $frameUpload) { $this->getResponse()->redirectAndExit($this->getRedirectUrl()); + } else { + $this->getView()->layout()->redirectUrl = $this->getRedirectUrl()->getAbsoluteUrl(); } - - $this->getView()->layout()->redirectUrl = $this->getRedirectUrl()->getAbsoluteUrl(); } } elseif ($this->getValidatePartial()) { // The form can't be processed but we may want to show validation errors though From b4214dcf328b8ccd66dc87516fd42135b2e16dc4 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Tue, 21 Jul 2015 16:49:59 +0200 Subject: [PATCH 034/108] FormTest: getRedirectUrl now returns an instance of Icinga\Web\Url --- test/php/library/Icinga/Web/FormTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/php/library/Icinga/Web/FormTest.php b/test/php/library/Icinga/Web/FormTest.php index 0fff50a3f..df17c4118 100644 --- a/test/php/library/Icinga/Web/FormTest.php +++ b/test/php/library/Icinga/Web/FormTest.php @@ -92,7 +92,8 @@ class FormTest extends BaseTestCase public function testWhetherAnExplicitlySetRedirectUrlIsUsedForRedirection() { - $this->getResponseMock()->shouldReceive('redirectAndExit')->atLeast()->once()->with('special/route'); + $this->getResponseMock()->shouldReceive('redirectAndExit')->atLeast()->once() + ->with(Mockery::on(function ($url) { return $url->getRelativeUrl() === 'special/route'; })); $form = new SuccessfulForm(); $form->setTokenDisabled(); From ce2b6862528df32516d68adac7baae81534a5bf0 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 21 Jul 2015 16:54:23 +0200 Subject: [PATCH 035/108] Add file and line of logged menu item renderer exceptions fixes #9696 --- library/Icinga/Web/MenuRenderer.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Web/MenuRenderer.php b/library/Icinga/Web/MenuRenderer.php index 4dec9404e..7b6ad9856 100644 --- a/library/Icinga/Web/MenuRenderer.php +++ b/library/Icinga/Web/MenuRenderer.php @@ -118,7 +118,13 @@ class MenuRenderer extends RecursiveIteratorIterator try { return $child->getRenderer()->render($child); } catch (Exception $e) { - Logger::error('Could not invoke custom renderer. Exception: '. $e->getMessage()); + Logger::error( + 'Could not invoke custom menu renderer. %s in %s:%d with message: %s', + get_class($e), + $e->getFile(), + $e->getLine(), + $e->getMessage() + ); } } From 45ef285e3ded3547898ed52e752c0344d4cf0bdb Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 21 Jul 2015 17:32:17 +0200 Subject: [PATCH 036/108] RPM: Let php-Icinga require Zend and Zend's MySQL and PostgreSQL adapters This installs both the MySQL and PostgreSQL libs even if the user only wants to use either MySQL or PostgreSQL. But the depencies installed--mysql-libs and postgresql-libs respectively--are so minimal that this is a good trade off for managing our dependencies atm. fixes #9314 --- icingaweb2.spec | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/icingaweb2.spec b/icingaweb2.spec index 7cddee0d3..e3513e979 100644 --- a/icingaweb2.spec +++ b/icingaweb2.spec @@ -46,8 +46,6 @@ Requires: %{name}-vendor-HTMLPurifier Requires: %{name}-vendor-JShrink Requires: %{name}-vendor-lessphp Requires: %{name}-vendor-Parsedown -Requires: %{zend} -Obsoletes: %{name}-vendor-zend %description @@ -84,6 +82,10 @@ Requires: %{php}-gd %{php}-intl %{?fedora:Requires: php-pecl-imagick} %{?rhel:Requires: php-pecl-imagick} %{?suse_version:Requires: %{php}-gettext %{php}-json %{php}-openssl %{php}-posix} +Requires: %{zend} +Obsoletes: %{name}-vendor-zend +Requires: %{zend}-Db-Adapter-Pdo-Mysql +Requires: %{zend}-Db-Adapter-Pdo-Pgsql %description -n php-Icinga Icinga Web 2 PHP library From c0de2e6ee3d386c81ae073b7efe01c6fa926c58e Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 22 Jul 2015 08:39:24 +0200 Subject: [PATCH 037/108] Form: Add missing use statement for the ProgrammingError exception --- library/Icinga/Web/Form.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index f41672b28..39cc5ecbb 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -3,13 +3,13 @@ namespace Icinga\Web; -use LogicException; use Zend_Config; use Zend_Form; use Zend_Form_Element; use Zend_View_Interface; use Icinga\Application\Icinga; use Icinga\Authentication\Manager; +use Icinga\Exception\ProgrammingError; use Icinga\Security\SecurityException; use Icinga\Util\Translator; use Icinga\Web\Form\ErrorLabeller; @@ -222,12 +222,12 @@ class Form extends Zend_Form * * @return $this * - * @throws LogicException If the callback is not callable + * @throws ProgrammingError If the callback is not callable */ public function setOnSuccess($onSuccess) { if (! is_callable($onSuccess)) { - throw new LogicException('The option `onSuccess\' is not callable'); + throw new ProgrammingError('The option `onSuccess\' is not callable'); } $this->onSuccess = $onSuccess; return $this; From bf82bd4ce1827361a039cbebb272df81584044e5 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 22 Jul 2015 10:53:42 +0200 Subject: [PATCH 038/108] ModuleActionController: fix forward/X-Icinga-Module --- library/Icinga/Web/Controller/ModuleActionController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Controller/ModuleActionController.php b/library/Icinga/Web/Controller/ModuleActionController.php index 9286ff679..071236cdd 100644 --- a/library/Icinga/Web/Controller/ModuleActionController.php +++ b/library/Icinga/Web/Controller/ModuleActionController.php @@ -73,6 +73,6 @@ class ModuleActionController extends ActionController public function postDispatchXhr() { parent::postDispatchXhr(); - $this->getResponse()->setHeader('X-Icinga-Module', $this->moduleName); + $this->getResponse()->setHeader('X-Icinga-Module', $this->moduleName, true); } } From d44547e46977ace54e6a0c471e961276555513c2 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 22 Jul 2015 10:57:14 +0200 Subject: [PATCH 039/108] forms.less: textarea should look like other inputs --- 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 38289877e..c6df47ca6 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -25,7 +25,7 @@ label { font-weight: bold; } -input, select { +input, select, textarea { box-sizing: border-box; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; From c5d68995ecc396e8b6666f0bd30c4d267c11a5d7 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 22 Jul 2015 11:03:01 +0200 Subject: [PATCH 040/108] forms.less: top-align field lables and related... ...items like icons. They used to be bottom-aligned, resulting in slightly confusing form layouts when text areas where involved. --- public/css/icinga/forms.less | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index c6df47ca6..cfb26f801 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -200,6 +200,14 @@ form label { width: 10em; } +form div.element > * { + vertical-align: top; +} + +form dt { + vertical-align: top; +} + select, input[type=text], textarea { width: 20em; display: inline-block; From a199d36207ddf00a377c9653f7c8d21a648f3acc Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 22 Jul 2015 12:45:10 +0200 Subject: [PATCH 041/108] Stylesheet: Disable extendend @import statements provided by LESS fixes #9687 --- library/Icinga/Web/LessCompiler.php | 14 +++++++++++--- library/Icinga/Web/StyleSheet.php | 1 + 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/library/Icinga/Web/LessCompiler.php b/library/Icinga/Web/LessCompiler.php index 33c0f8e83..13205f73c 100644 --- a/library/Icinga/Web/LessCompiler.php +++ b/library/Icinga/Web/LessCompiler.php @@ -8,7 +8,6 @@ use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use RegexIterator; use RecursiveRegexIterator; -use Zend_Controller_Front; use Icinga\Application\Icinga; use lessc; @@ -31,8 +30,6 @@ class LessCompiler */ private $lessc; - private $baseUrl; - private $source; /** @@ -44,6 +41,17 @@ class LessCompiler $this->lessc = new lessc(); } + /** + * Disable the extendend import functionality + * + * @return $this + */ + public function disableExtendedImport() + { + $this->lessc->importDisabled = true; + return $this; + } + public function compress() { $this->lessc->setPreserveComments(false); diff --git a/library/Icinga/Web/StyleSheet.php b/library/Icinga/Web/StyleSheet.php index 5f92e83a7..2e8ecea2c 100644 --- a/library/Icinga/Web/StyleSheet.php +++ b/library/Icinga/Web/StyleSheet.php @@ -99,6 +99,7 @@ class StyleSheet } $less = new LessCompiler(); + $less->disableExtendedImport(); foreach ($lessFiles as $file) { $less->addFile($file); } From 0f2351ff1db67ba4f97a60234a437a9dded20b16 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 22 Jul 2015 13:29:44 +0200 Subject: [PATCH 042/108] js: Fix XHR loading of URLs in case window.FormData is not supported refs #8758 --- public/js/icinga/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/js/icinga/loader.js b/public/js/icinga/loader.js index 232ecbc7e..da23ebea8 100644 --- a/public/js/icinga/loader.js +++ b/public/js/icinga/loader.js @@ -103,7 +103,7 @@ // This is jQuery's default content type var contentType = 'application/x-www-form-urlencoded; charset=UTF-8'; - var isFormData = data instanceof window.FormData; + var isFormData = typeof window.FormData !== 'undefined' && data instanceof window.FormData; if (isFormData) { // Setting false is mandatory as the form's data // won't be recognized by the server otherwise From ab8e7751884dcd6d76597742ec8b3687630aade5 Mon Sep 17 00:00:00 2001 From: Markus Frosch Date: Tue, 2 Jun 2015 15:53:00 +0200 Subject: [PATCH 043/108] Fix duplicate headers on forward() inside a controller This avoids that the JS loader flattening arrays. refs #9349 --- library/Icinga/Web/Controller/ActionController.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/library/Icinga/Web/Controller/ActionController.php b/library/Icinga/Web/Controller/ActionController.php index 750c101f3..808c9127e 100644 --- a/library/Icinga/Web/Controller/ActionController.php +++ b/library/Icinga/Web/Controller/ActionController.php @@ -458,11 +458,11 @@ class ActionController extends Zend_Controller_Action foreach ($notifications->getMessages() as $m) { $notificationList[] = rawurlencode($m->type . ' ' . $m->message); } - $resp->setHeader('X-Icinga-Notification', implode('&', $notificationList)); + $resp->setHeader('X-Icinga-Notification', implode('&', $notificationList), true); } if ($this->reloadCss) { - $resp->setHeader('X-Icinga-CssReload', 'now'); + $resp->setHeader('X-Icinga-CssReload', 'now', true); } if ($this->view->title) { @@ -472,18 +472,18 @@ class ActionController extends Zend_Controller_Action } $resp->setHeader( 'X-Icinga-Title', - rawurlencode($this->view->title . ' :: Icinga Web') + rawurlencode($this->view->title . ' :: Icinga Web', true) ); } else { - $resp->setHeader('X-Icinga-Title', rawurlencode('Icinga Web')); + $resp->setHeader('X-Icinga-Title', rawurlencode('Icinga Web'), true); } if ($this->rerenderLayout) { - $this->getResponse()->setHeader('X-Icinga-Container', 'layout'); + $this->getResponse()->setHeader('X-Icinga-Container', 'layout', true); } if ($this->autorefreshInterval !== null) { - $resp->setHeader('X-Icinga-Refresh', $this->autorefreshInterval); + $resp->setHeader('X-Icinga-Refresh', $this->autorefreshInterval, true); } } From 52e352751e382f89735b66beaffce52b9955bb00 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 22 Jul 2015 13:31:12 +0200 Subject: [PATCH 044/108] Revert "ModuleActionController: fix forward/X-Icinga-Module" This reverts commit bf82bd4ce1827361a039cbebb272df81584044e5. We have a not merged branch for this. refs #9349 --- library/Icinga/Web/Controller/ModuleActionController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Icinga/Web/Controller/ModuleActionController.php b/library/Icinga/Web/Controller/ModuleActionController.php index 071236cdd..9286ff679 100644 --- a/library/Icinga/Web/Controller/ModuleActionController.php +++ b/library/Icinga/Web/Controller/ModuleActionController.php @@ -73,6 +73,6 @@ class ModuleActionController extends ActionController public function postDispatchXhr() { parent::postDispatchXhr(); - $this->getResponse()->setHeader('X-Icinga-Module', $this->moduleName, true); + $this->getResponse()->setHeader('X-Icinga-Module', $this->moduleName); } } From 13d954a956598e5f2cd33ede9f3ff68d7ea5cf47 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 22 Jul 2015 13:36:25 +0200 Subject: [PATCH 045/108] Fix rawurlencode call --- library/Icinga/Web/Controller/ActionController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Web/Controller/ActionController.php b/library/Icinga/Web/Controller/ActionController.php index 808c9127e..0fb71f816 100644 --- a/library/Icinga/Web/Controller/ActionController.php +++ b/library/Icinga/Web/Controller/ActionController.php @@ -472,7 +472,8 @@ class ActionController extends Zend_Controller_Action } $resp->setHeader( 'X-Icinga-Title', - rawurlencode($this->view->title . ' :: Icinga Web', true) + rawurlencode($this->view->title . ' :: Icinga Web'), + true ); } else { $resp->setHeader('X-Icinga-Title', rawurlencode('Icinga Web'), true); From 63c51c01e0fa20e5e3bcd24a6965a1d4277066c2 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 22 Jul 2015 13:40:50 +0200 Subject: [PATCH 046/108] monitoring: Auto-refresh tactical overview every 15 seconds fixes #9626 --- .../monitoring/application/controllers/TacticalController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/monitoring/application/controllers/TacticalController.php b/modules/monitoring/application/controllers/TacticalController.php index fa2e7deec..fce4664f0 100644 --- a/modules/monitoring/application/controllers/TacticalController.php +++ b/modules/monitoring/application/controllers/TacticalController.php @@ -9,6 +9,7 @@ class Monitoring_TacticalController extends MonitoringController { public function indexAction() { + $this->setAutorefreshInterval(15); $this->getTabs()->add( 'tactical_overview', array( From 910dee199f24887c4da10d136d11f56b7c1748b5 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Wed, 22 Jul 2015 14:21:53 +0200 Subject: [PATCH 047/108] Fix form test --- test/php/library/Icinga/Web/FormTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/php/library/Icinga/Web/FormTest.php b/test/php/library/Icinga/Web/FormTest.php index df17c4118..662b1421f 100644 --- a/test/php/library/Icinga/Web/FormTest.php +++ b/test/php/library/Icinga/Web/FormTest.php @@ -239,7 +239,7 @@ class FormTest extends BaseTestCase } /** - * @expectedException LogicException + * @expectedException \Icinga\Exception\ProgrammingError */ public function testWhetherTheOnSuccessOptionMustBeCallable() { From 1d3a0f63eb8f9391b77eeea8a81069287b55f71e Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 23 Jul 2015 12:25:30 +0200 Subject: [PATCH 048/108] BackendConfigForm: Fix that skipping the schema validation is not possible fixes #9719 --- .../application/forms/Config/BackendConfigForm.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/monitoring/application/forms/Config/BackendConfigForm.php b/modules/monitoring/application/forms/Config/BackendConfigForm.php index 504ec9a59..ade94128d 100644 --- a/modules/monitoring/application/forms/Config/BackendConfigForm.php +++ b/modules/monitoring/application/forms/Config/BackendConfigForm.php @@ -309,10 +309,15 @@ class BackendConfigForm extends ConfigForm return false; } - $resourceConfig = ResourceFactory::getResourceConfig($this->getValue('resource')); - if (! self::isValidIdoSchema($this, $resourceConfig) || !self::isValidIdoInstance($this, $resourceConfig)) { - $this->addSkipValidationCheckbox(); - return false; + if (($el = $this->getElement('skip_validation')) === null || false === $el->isChecked()) { + $resourceConfig = ResourceFactory::getResourceConfig($this->getValue('resource')); + if (! self::isValidIdoSchema($this, $resourceConfig) || !self::isValidIdoInstance($this, $resourceConfig)) { + if ($el === null) { + $this->addSkipValidationCheckbox(); + } + + return false; + } } return true; From 3ffe657f716e6642e7413687834db1878e22d459 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 23 Jul 2015 12:37:23 +0200 Subject: [PATCH 049/108] Puppet: Use future parser --- Vagrantfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Vagrantfile b/Vagrantfile index 198016789..11b5e2b9c 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -47,5 +47,6 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| puppet.module_path = [ ".puppet/modules", ".puppet/profiles" ] puppet.manifests_path = ".puppet/manifests" puppet.manifest_file = "site.pp" + puppet.options = "--parser=future" end end From 67af7b51350b7d3e9ae6e7a0bbfcb9891577cbcc Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 23 Jul 2015 12:39:12 +0200 Subject: [PATCH 050/108] Puppet: Don't install icinga2-debuginfo --- .puppet/modules/icinga2/manifests/init.pp | 8 ++------ .puppet/profiles/icinga2_dev/manifests/init.pp | 6 +++++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.puppet/modules/icinga2/manifests/init.pp b/.puppet/modules/icinga2/manifests/init.pp index 26c7300a5..e5b77ad75 100644 --- a/.puppet/modules/icinga2/manifests/init.pp +++ b/.puppet/modules/icinga2/manifests/init.pp @@ -15,7 +15,7 @@ class icinga2 { include icinga_packages package { [ - 'icinga2', 'icinga2-doc', 'icinga2-debuginfo' + 'icinga2', 'icinga2-doc' ]: ensure => latest, require => Class['icinga_packages'], @@ -35,12 +35,8 @@ class icinga2 { links => follow, owner => 'icinga', group => 'icinga', - mode => 6750, + mode => '6750', } icinga2::feature { [ 'statusdata', 'command', 'compatlog' ]: } - - icinga2::feature { 'api': - ensure => absent, - } } diff --git a/.puppet/profiles/icinga2_dev/manifests/init.pp b/.puppet/profiles/icinga2_dev/manifests/init.pp index 7082fe935..322480594 100644 --- a/.puppet/profiles/icinga2_dev/manifests/init.pp +++ b/.puppet/profiles/icinga2_dev/manifests/init.pp @@ -19,11 +19,15 @@ class icinga2_dev { include monitoring_test_config icinga2::config { [ - 'conf.d/test-config', 'conf.d/commands', 'constants', + 'conf.d/test-config', 'conf.d/commands', 'constants' ]: source => 'puppet:///modules/icinga2_dev', } + icinga2::feature { 'api': + ensure => absent, + } + icinga2::feature { 'ido-pgsql': ensure => absent, require => Class['icinga2_pgsql'], From af6def7d7f5e57dd9974cb70507083e7fb948151 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 23 Jul 2015 12:39:36 +0200 Subject: [PATCH 051/108] Puppet: Use strings for file modes --- .puppet/modules/icingaweb2/manifests/config/general.pp | 2 +- .puppet/modules/icingaweb2/manifests/config/module.pp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.puppet/modules/icingaweb2/manifests/config/general.pp b/.puppet/modules/icingaweb2/manifests/config/general.pp index 8ccea172f..200a4f79e 100644 --- a/.puppet/modules/icingaweb2/manifests/config/general.pp +++ b/.puppet/modules/icingaweb2/manifests/config/general.pp @@ -10,7 +10,7 @@ define icingaweb2::config::general ( content => template("${source}/${name}.ini.erb"), owner => 'root', group => $web_group, - mode => 0660, + mode => '0660', replace => $replace, } } diff --git a/.puppet/modules/icingaweb2/manifests/config/module.pp b/.puppet/modules/icingaweb2/manifests/config/module.pp index 19db02250..90d98d083 100644 --- a/.puppet/modules/icingaweb2/manifests/config/module.pp +++ b/.puppet/modules/icingaweb2/manifests/config/module.pp @@ -20,7 +20,7 @@ define icingaweb2::config::module ( source => "${source}/modules/${module}/${name}.ini", owner => 'root', group => $web_group, - mode => 0660, + mode => '0660', replace => $replace, } } From b4861fe689d2086919359305c3618f27942c0156 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 23 Jul 2015 12:40:12 +0200 Subject: [PATCH 052/108] Puppet: Set date.timezone for PHP in a separate INI file --- .puppet/modules/php/manifests/init.pp | 17 ++------------- .puppet/modules/php/manifests/phpd.pp | 21 +++++++++++++++++++ .../modules/php/templates/timezone.ini.erb | 1 + 3 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 .puppet/modules/php/manifests/phpd.pp create mode 100644 .puppet/modules/php/templates/timezone.ini.erb diff --git a/.puppet/modules/php/manifests/init.pp b/.puppet/modules/php/manifests/init.pp index dd6df83b3..788ad5df1 100644 --- a/.puppet/modules/php/manifests/init.pp +++ b/.puppet/modules/php/manifests/init.pp @@ -20,24 +20,11 @@ class php { package { 'php': ensure => latest, - require => Package['apache'], - notify => Service['apache'] - } - # TODO(el): Always executed. Should be a resource - -> exec { 'php-timezone': - command => 'sed -re $\'s#^;?(date\\.timezone =).*$#\\1 "UTC"#\' -i /etc/php.ini', notify => Service['apache'], + require => Package['apache'], } - file { '/etc/php.d/error_reporting.ini': - content => template('php/error_reporting.ini.erb'), + php::phpd { ['error_reporting', 'timezone', 'xdebug_settings' ]: require => Package['php'], - notify => Service['apache'] - } - - file { '/etc/php.d/xdebug_settings.ini': - content => template('php/xdebug_settings.ini.erb'), - require => Package['php'], - notify => Service['apache'] } } diff --git a/.puppet/modules/php/manifests/phpd.pp b/.puppet/modules/php/manifests/phpd.pp new file mode 100644 index 000000000..4d59223cb --- /dev/null +++ b/.puppet/modules/php/manifests/phpd.pp @@ -0,0 +1,21 @@ +# define: php::phpd +# +# Provision php.d config +# +# Parameters: +# +# Actions: +# +# Requires: +# +# Sample Usage: +# +define php::phpd { + + include php + + file { "/etc/php.d/$name.ini": + content => template("php/$name.ini.erb"), + notify => Service['apache'], + } +} diff --git a/.puppet/modules/php/templates/timezone.ini.erb b/.puppet/modules/php/templates/timezone.ini.erb new file mode 100644 index 000000000..51e82763b --- /dev/null +++ b/.puppet/modules/php/templates/timezone.ini.erb @@ -0,0 +1 @@ +date.timezone = "UTC" From 593f90f330a2ab66ea1acd6896dd72ef9a5825fd Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Thu, 23 Jul 2015 12:40:51 +0200 Subject: [PATCH 053/108] Puppet: Fix that the LDAP server is empty when using CentOS 7 --- .puppet/modules/openldap/manifests/init.pp | 14 +++++++++++++- .../profiles/icingaweb2_dev/files/openldap/db.ldif | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.puppet/modules/openldap/manifests/init.pp b/.puppet/modules/openldap/manifests/init.pp index 069c4a157..11cc2b42a 100644 --- a/.puppet/modules/openldap/manifests/init.pp +++ b/.puppet/modules/openldap/manifests/init.pp @@ -20,6 +20,18 @@ class openldap { service { 'slapd': ensure => running, - require => Package['openldap-servers'] + require => Package['openldap-servers'], + } + + if versioncmp($::operatingsystemmajrelease, '7') >= 0 { + ['core', 'cosine', 'inetorgperson', 'nis', 'misc', 'openldap'].each |String $schema| { + exec { "slapd-schema-${schema}": + command => "ldapadd -Y EXTERNAL -H ldapi:// -f /etc/openldap/schema/${schema}.ldif", + group => 'root', + require => Package['openldap-servers'], + unless => "test -n \"$(find /etc/openldap/slapd.d/cn=config/cn=schema/ -name cn={*}${schema}.ldif -print -quit)\"", + user => 'root', + } + } } } diff --git a/.puppet/profiles/icingaweb2_dev/files/openldap/db.ldif b/.puppet/profiles/icingaweb2_dev/files/openldap/db.ldif index eab9b7bac..03263f9c3 100644 --- a/.puppet/profiles/icingaweb2_dev/files/openldap/db.ldif +++ b/.puppet/profiles/icingaweb2_dev/files/openldap/db.ldif @@ -6,7 +6,7 @@ olcRootPW: {SSHA}N/2WMqT8q7cElh7KUQz+p9TJbjmKv/u9 replace: olcRootDN olcRootDN: cn=admin,cn=config -dn: olcDatabase={2}bdb,cn=config +dn: olcDatabase={2}hdb,cn=config changetype: modify replace: olcRootPW olcRootPW: {SSHA}MxMpLBo2/TSymoIBf/Sb5iQac7Wwiur5 From 727a2d71411f781dd9cb1286accda249fb81db72 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 23 Jul 2015 13:48:49 +0200 Subject: [PATCH 054/108] Paginator: relax requirements to Paginatable * we need no full query interface here * introduced new interface "Paginatable" fixes #9483 --- library/Icinga/Data/Paginatable.php | 9 +++++++++ library/Icinga/Data/QueryInterface.php | 4 +--- library/Icinga/Web/Widget/Paginator.php | 8 ++++---- 3 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 library/Icinga/Data/Paginatable.php diff --git a/library/Icinga/Data/Paginatable.php b/library/Icinga/Data/Paginatable.php new file mode 100644 index 000000000..d9a6de397 --- /dev/null +++ b/library/Icinga/Data/Paginatable.php @@ -0,0 +1,9 @@ +query = $query; return $this; From 4e3da3a6eb5f5608f67fd9f6a137513d9bc705bf Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 23 Jul 2015 16:18:09 +0200 Subject: [PATCH 055/108] UserBackendConfigForm: Adjust how to process requests... ...and use sub-forms, finally. refs #9602 --- application/controllers/ConfigController.php | 126 ++++-- .../Config/UserBackend/DbBackendForm.php | 68 +--- .../Config/UserBackend/LdapBackendForm.php | 62 +-- .../forms/Config/UserBackendConfigForm.php | 376 ++++++++++-------- .../forms/Config/UserBackendReorderForm.php | 14 +- .../{userbackend/create.phtml => form.phtml} | 0 .../scripts/config/userbackend/modify.phtml | 6 - .../scripts/config/userbackend/remove.phtml | 6 - .../application/forms/AuthBackendPage.php | 36 +- 9 files changed, 370 insertions(+), 324 deletions(-) rename application/views/scripts/config/{userbackend/create.phtml => form.phtml} (100%) delete mode 100644 application/views/scripts/config/userbackend/modify.phtml delete mode 100644 application/views/scripts/config/userbackend/remove.phtml diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php index 206d3c88b..e9fbbd7ef 100644 --- a/application/controllers/ConfigController.php +++ b/application/controllers/ConfigController.php @@ -5,6 +5,8 @@ use Icinga\Application\Config; use Icinga\Application\Icinga; use Icinga\Application\Modules\Module; use Icinga\Data\ResourceFactory; +use Icinga\Exception\ConfigurationError; +use Icinga\Exception\NotFoundError; use Icinga\Forms\Config\UserBackendConfigForm; use Icinga\Forms\Config\UserBackendReorderForm; use Icinga\Forms\Config\GeneralConfigForm; @@ -193,80 +195,130 @@ class ConfigController extends Controller } /** - * Action for creating a new user backend + * Create a new user backend */ public function createuserbackendAction() { $this->assertPermission('config/application/userbackend'); $form = new UserBackendConfigForm(); + $form->setRedirectUrl('config/userbackend'); $form->setTitle($this->translate('Create New User Backend')); $form->addDescription($this->translate( 'Create a new backend for authenticating your users. This backend' . ' will be added at the end of your authentication order.' )); $form->setIniConfig(Config::app('authentication')); - $form->setResourceConfig(ResourceFactory::getResourceConfigs()); - $form->setRedirectUrl('config/userbackend'); + + try { + $form->setResourceConfig(ResourceFactory::getResourceConfigs()); + } catch (ConfigurationError $e) { + if ($this->hasPermission('config/application/resources')) { + Notification::error($e->getMessage()); + $this->redirectNow('config/createresource'); + } + + throw $e; // No permission for resource configuration, show the error + } + + $form->setOnSuccess(function (UserBackendConfigForm $form) { + try { + $form->add(array_filter($form->getValues())); + } catch (Exception $e) { + $form->error($e->getMessage()); + return false; + } + + if ($form->save()) { + Notification::success(t('User backend successfully created')); + return true; + } + + return false; + }); $form->handleRequest(); $this->view->form = $form; - $this->render('userbackend/create'); + $this->render('form'); } /** - * Action for editing user backends + * Edit a user backend */ public function edituserbackendAction() { $this->assertPermission('config/application/userbackend'); + $backendName = $this->params->getRequired('backend'); + $form = new UserBackendConfigForm(); - $form->setTitle($this->translate('Edit User Backend')); - $form->setIniConfig(Config::app('authentication')); - $form->setResourceConfig(ResourceFactory::getResourceConfigs()); - $form->setRedirectUrl('config/userbackend'); $form->setAction(Url::fromRequest()); - $form->handleRequest(); + $form->setRedirectUrl('config/userbackend'); + $form->setTitle(sprintf($this->translate('Edit User Backend %s'), $backendName)); + $form->setIniConfig(Config::app('authentication')); + $form->setOnSuccess(function (UserBackendConfigForm $form) use ($backendName) { + try { + $form->edit($backendName, array_map( + function ($v) { + return $v !== '' ? $v : null; + }, + $form->getValues() + )); + } catch (Exception $e) { + $form->error($e->getMessage()); + return false; + } + + if ($form->save()) { + Notification::success(sprintf(t('User backend "%s" successfully updated'), $backendName)); + return true; + } + + return false; + }); + + try { + $form->load($backendName); + $form->setResourceConfig(ResourceFactory::getResourceConfigs()); + $form->handleRequest(); + } catch (NotFoundError $_) { + $this->httpNotFound(sprintf($this->translate('User backend "%s" not found'), $backendName)); + } $this->view->form = $form; - $this->render('userbackend/modify'); + $this->render('form'); } /** - * Action for removing a user backend + * Display a confirmation form to remove the backend identified by the 'backend' parameter */ public function removeuserbackendAction() { $this->assertPermission('config/application/userbackend'); - $form = new ConfirmRemovalForm(array( - 'onSuccess' => function ($form) { - $configForm = new UserBackendConfigForm(); - $configForm->setIniConfig(Config::app('authentication')); - $authBackend = $form->getRequest()->getQuery('backend'); + $backendName = $this->params->getRequired('backend'); - try { - $configForm->remove($authBackend); - } catch (InvalidArgumentException $e) { - Notification::error($e->getMessage()); - return false; - } - - if ($configForm->save()) { - Notification::success(sprintf( - t('User backend "%s" has been successfully removed'), - $authBackend - )); - } else { - return false; - } - } - )); - $form->setTitle($this->translate('Remove User Backend')); + $backendForm = new UserBackendConfigForm(); + $backendForm->setIniConfig(Config::app('authentication')); + $form = new ConfirmRemovalForm(); $form->setRedirectUrl('config/userbackend'); - $form->setAction(Url::fromRequest()); + $form->setTitle(sprintf($this->translate('Remove User Backend %s'), $backendName)); + $form->setOnSuccess(function (ConfirmRemovalForm $form) use ($backendName, $backendForm) { + try { + $backendForm->delete($backendName); + } catch (Exception $e) { + $form->error($e->getMessage()); + return false; + } + + if ($backendForm->save()) { + Notification::success(sprintf(t('User backend "%s" successfully removed'), $backendName)); + return true; + } + + return false; + }); $form->handleRequest(); $this->view->form = $form; - $this->render('userbackend/remove'); + $this->render('form'); } /** diff --git a/application/forms/Config/UserBackend/DbBackendForm.php b/application/forms/Config/UserBackend/DbBackendForm.php index c09ec166c..49426c073 100644 --- a/application/forms/Config/UserBackend/DbBackendForm.php +++ b/application/forms/Config/UserBackend/DbBackendForm.php @@ -3,11 +3,7 @@ namespace Icinga\Forms\Config\UserBackend; -use Exception; use Icinga\Web\Form; -use Icinga\Data\ConfigObject; -use Icinga\Data\ResourceFactory; -use Icinga\Authentication\User\DbUserBackend; /** * Form class for adding/modifying database user backends @@ -43,7 +39,9 @@ class DbBackendForm extends Form } /** - * @see Form::createElements() + * Create and add elements to this form + * + * @param array $formData */ public function createElements(array $formData) { @@ -56,6 +54,20 @@ class DbBackendForm extends Form 'description' => $this->translate( 'The name of this authentication provider that is used to differentiate it from others' ), + 'validators' => array( + array( + 'Regex', + false, + array( + 'pattern' => '/^[^\\[\\]:]+$/', + 'messages' => array( + 'regexNotMatch' => $this->translate( + 'The name cannot contain \'[\', \']\' or \':\'.' + ) + ) + ) + ) + ) ) ); $this->addElement( @@ -67,7 +79,7 @@ class DbBackendForm extends Form 'description' => $this->translate( 'The database connection to use for authenticating with this provider' ), - 'multiOptions' => false === empty($this->resources) + 'multiOptions' => !empty($this->resources) ? array_combine($this->resources, $this->resources) : array() ) @@ -80,49 +92,5 @@ class DbBackendForm extends Form 'value' => 'db' ) ); - - return $this; - } - - /** - * Validate that the selected resource is a valid database user backend - * - * @see Form::onSuccess() - */ - public function onSuccess() - { - if (false === static::isValidUserBackend($this)) { - return false; - } - } - - /** - * Validate the configuration by creating a backend and requesting the user count - * - * @param Form $form The form to fetch the configuration values from - * - * @return bool Whether validation succeeded or not - */ - public static function isValidUserBackend(Form $form) - { - $backend = new DbUserBackend(ResourceFactory::createResource($form->getResourceConfig())); - $result = $backend->inspect(); - if ($result->hasError()) { - $form->addError(sprintf($form->translate('Using the specified backend failed: %s'), $result->getError())); - } - - // TODO: display diagnostics in $result->toArray() to the user - - return ! $result->hasError(); - } - - /** - * Return the configuration for the chosen resource - * - * @return ConfigObject - */ - public function getResourceConfig() - { - return ResourceFactory::getResourceConfig($this->getValue('resource')); } } diff --git a/application/forms/Config/UserBackend/LdapBackendForm.php b/application/forms/Config/UserBackend/LdapBackendForm.php index 6427fbf34..b5b2c966f 100644 --- a/application/forms/Config/UserBackend/LdapBackendForm.php +++ b/application/forms/Config/UserBackend/LdapBackendForm.php @@ -3,14 +3,7 @@ namespace Icinga\Forms\Config\UserBackend; -use Exception; -use Icinga\Authentication\User\LdapUserBackend; -use Icinga\Data\Inspection; use Icinga\Web\Form; -use Icinga\Data\ConfigObject; -use Icinga\Data\ResourceFactory; -use Icinga\Exception\AuthenticationException; -use Icinga\Authentication\User\UserBackend; /** * Form class for adding/modifying LDAP user backends @@ -46,7 +39,9 @@ class LdapBackendForm extends Form } /** - * @see Form::createElements() + * Create and add elements to this form + * + * @param array $formData */ public function createElements(array $formData) { @@ -60,6 +55,20 @@ class LdapBackendForm extends Form 'label' => $this->translate('Backend Name'), 'description' => $this->translate( 'The name of this authentication provider that is used to differentiate it from others.' + ), + 'validators' => array( + array( + 'Regex', + false, + array( + 'pattern' => '/^[^\\[\\]:]+$/', + 'messages' => array( + 'regexNotMatch' => $this->translate( + 'The name cannot contain \'[\', \']\' or \':\'.' + ) + ) + ) + ) ) ) ); @@ -72,7 +81,7 @@ class LdapBackendForm extends Form 'description' => $this->translate( 'The LDAP connection to use for authenticating with this provider.' ), - 'multiOptions' => false === empty($this->resources) + 'multiOptions' => !empty($this->resources) ? array_combine($this->resources, $this->resources) : array() ) @@ -162,40 +171,5 @@ class LdapBackendForm extends Form ) ) ); - return $this; - } - - /** - * Validate that the selected resource is a valid ldap user backend - * - * @see Form::onSuccess() - */ - public function onSuccess() - { - if (false === static::isValidUserBackend($this)) { - return false; - } - } - - /** - * Validate the configuration by creating a backend and requesting the user count - * - * @param Form $form The form to fetch the configuration values from - * - * @return bool Whether validation succeeded or not - */ - public static function isValidUserBackend(Form $form) - { - /** - * @var $result Inspection - */ - $result = UserBackend::create(null, new ConfigObject($form->getValues()))->inspect(); - if ($result->hasError()) { - $form->addError($result->getError()); - } - - // TODO: display diagnostics in $result->toArray() to the user - - return ! $result->hasError(); } } diff --git a/application/forms/Config/UserBackendConfigForm.php b/application/forms/Config/UserBackendConfigForm.php index 62a68e70b..0afc54a44 100644 --- a/application/forms/Config/UserBackendConfigForm.php +++ b/application/forms/Config/UserBackendConfigForm.php @@ -4,26 +4,38 @@ namespace Icinga\Forms\Config; use InvalidArgumentException; -use Icinga\Forms\ConfigForm; -use Icinga\Web\Notification; use Icinga\Application\Config; -use Icinga\Application\Platform; -use Icinga\Data\ConfigObject; -use Icinga\Data\ResourceFactory; +use Icinga\Authentication\User\UserBackend; use Icinga\Exception\ConfigurationError; +use Icinga\Exception\IcingaException; +use Icinga\Exception\NotFoundError; +use Icinga\Data\ConfigObject; +use Icinga\Data\Inspectable; +use Icinga\Forms\ConfigForm; +use Icinga\Forms\Config\UserBackend\ExternalBackendForm; use Icinga\Forms\Config\UserBackend\DbBackendForm; use Icinga\Forms\Config\UserBackend\LdapBackendForm; -use Icinga\Forms\Config\UserBackend\ExternalBackendForm; +use Icinga\Web\Form; +/** + * Form for managing user backends + */ class UserBackendConfigForm extends ConfigForm { /** - * The available resources split by type + * The available user backend resources split by type * * @var array */ protected $resources; + /** + * The backend to load when displaying the form for the first time + * + * @var string + */ + protected $backendToLoad; + /** * Initialize this form */ @@ -36,15 +48,39 @@ class UserBackendConfigForm extends ConfigForm /** * Set the resource configuration to use * - * @param Config $resources The resource configuration + * @param Config $resourceConfig The resource configuration * * @return $this + * + * @throws ConfigurationError In case there are no valid resources for authentication available */ public function setResourceConfig(Config $resourceConfig) { $resources = array(); foreach ($resourceConfig as $name => $resource) { - $resources[strtolower($resource->type)][] = $name; + if (in_array($resource->type, array('db', 'ldap'))) { + $resources[$resource->type][] = $name; + } + } + + if (empty($resources)) { + $externalBackends = $this->config->toArray(); + array_walk( + $externalBackends, + function (& $authBackendCfg) { + if (! isset($authBackendCfg['backend']) || $authBackendCfg['backend'] !== 'external') { + $authBackendCfg = null; + } + } + ); + if (count(array_filter($externalBackends)) > 0 && ( + $this->backendToLoad === null || !isset($externalBackends[$this->backendToLoad]) + )) { + throw new ConfigurationError($this->translate( + 'Could not find any valid user backend resources.' + . ' Please configure a resource for authentication first.' + )); + } } $this->resources = $resources; @@ -54,9 +90,11 @@ class UserBackendConfigForm extends ConfigForm /** * Return a form object for the given backend type * - * @param string $type The backend type for which to return a form + * @param string $type The backend type for which to return a form * * @return Form + * + * @throws InvalidArgumentException In case the given backend type is invalid */ public function getBackendForm($type) { @@ -84,81 +122,103 @@ class UserBackendConfigForm extends ConfigForm } /** - * Add a particular user backend + * Populate the form with the given backend's config * - * The backend to add is identified by the array-key `name'. - * - * @param array $values The values to extend the configuration with + * @param string $name * * @return $this * - * @throws InvalidArgumentException In case the backend does already exist + * @throws NotFoundError In case no backend with the given name is found */ - public function add(array $values) + public function load($name) { - $name = isset($values['name']) ? $values['name'] : ''; - if (! $name) { - throw new InvalidArgumentException($this->translate('User backend name missing')); - } elseif ($this->config->hasSection($name)) { - throw new InvalidArgumentException($this->translate('User backend already exists')); + if (! $this->config->hasSection($name)) { + throw new NotFoundError('No user backend called "%s" found', $name); } - unset($values['name']); - $this->config->setSection($name, $values); + $this->backendToLoad = $name; return $this; } /** - * Edit a particular user backend + * Add a new user backend * - * @param string $name The name of the backend to edit - * @param array $values The values to edit the configuration with + * The backend to add is identified by the array-key `name'. * - * @return array The edited backend configuration + * @param array $data * - * @throws InvalidArgumentException In case the backend does not exist + * @return $this + * + * @throws InvalidArgumentException In case $data does not contain a backend name + * @throws IcingaException In case a backend with the same name already exists */ - public function edit($name, array $values) + public function add(array $data) { - if (! $name) { - throw new InvalidArgumentException($this->translate('Old user backend name missing')); - } elseif (! ($newName = isset($values['name']) ? $values['name'] : '')) { - throw new InvalidArgumentException($this->translate('New user backend name missing')); - } elseif (! $this->config->hasSection($name)) { - throw new InvalidArgumentException($this->translate('Unknown user backend provided')); + if (! isset($data['name'])) { + throw new InvalidArgumentException('Key \'name\' missing'); } - $backendConfig = $this->config->getSection($name); - if ($newName !== $name) { - // Only remove the old entry if it has changed as the order gets screwed when editing backend names - $this->config->removeSection($name); + $backendName = $data['name']; + if ($this->config->hasSection($backendName)) { + throw new IcingaException( + $this->translate('A user backend with the name "%s" does already exist'), + $backendName + ); } - unset($values['name']); - $this->config->setSection($newName, $backendConfig->merge($values)); - return $backendConfig; + unset($data['name']); + $this->config->setSection($backendName, $data); + return $this; } /** - * Remove the given user backend + * Edit a user backend * - * @param string $name The name of the backend to remove + * @param string $name + * @param array $data * - * @return array The removed backend configuration + * @return $this * - * @throws InvalidArgumentException In case the backend does not exist + * @throws NotFoundError In case no backend with the given name is found */ - public function remove($name) + public function edit($name, array $data) { - if (! $name) { - throw new InvalidArgumentException($this->translate('user backend name missing')); - } elseif (! $this->config->hasSection($name)) { - throw new InvalidArgumentException($this->translate('Unknown user backend provided')); + if (! $this->config->hasSection($name)) { + throw new NotFoundError('No user backend called "%s" found', $name); } $backendConfig = $this->config->getSection($name); + if (isset($data['name'])) { + if ($data['name'] !== $name) { + $this->config->removeSection($name); + $name = $data['name']; + } + + unset($data['name']); + } + + $backendConfig->merge($data); + foreach ($backendConfig->toArray() as $k => $v) { + if ($v === null) { + unset($backendConfig->$k); + } + } + + $this->config->setSection($name, $backendConfig); + return $this; + } + + /** + * Remove a user backend + * + * @param string $name + * + * @return $this + */ + public function delete($name) + { $this->config->removeSection($name); - return $backendConfig; + return $this; } /** @@ -169,14 +229,12 @@ class UserBackendConfigForm extends ConfigForm * * @return $this * - * @throws InvalidArgumentException In case the backend does not exist + * @throws NotFoundError In case no backend with the given name is found */ public function move($name, $position) { - if (! $name) { - throw new InvalidArgumentException($this->translate('User backend name missing')); - } elseif (! $this->config->hasSection($name)) { - throw new InvalidArgumentException($this->translate('Unknown user backend provided')); + if (! $this->config->hasSection($name)) { + throw new NotFoundError('No user backend called "%s" found', $name); } $backendOrder = $this->config->keys(); @@ -194,105 +252,9 @@ class UserBackendConfigForm extends ConfigForm } /** - * Add or edit an user backend and save the configuration + * Create and add elements to this form * - * Performs a connectivity validation using the submitted values. A checkbox is - * added to the form to skip the check if it fails and redirection is aborted. - * - * @see Form::onSuccess() - */ - public function onSuccess() - { - if (($el = $this->getElement('force_creation')) === null || false === $el->isChecked()) { - $backendForm = $this->getBackendForm($this->getElement('type')->getValue()); - if (false === $backendForm::isValidUserBackend($this)) { - $this->addElement($this->getForceCreationCheckbox()); - return false; - } - } - - $authBackend = $this->request->getQuery('backend'); - try { - if ($authBackend === null) { // create new backend - $this->add($this->getValues()); - $message = $this->translate('User backend "%s" has been successfully created'); - } else { // edit existing backend - $this->edit($authBackend, $this->getValues()); - $message = $this->translate('User backend "%s" has been successfully changed'); - } - } catch (InvalidArgumentException $e) { - Notification::error($e->getMessage()); - return; - } - - if ($this->save()) { - Notification::success(sprintf($message, $this->getElement('name')->getValue())); - } else { - return false; - } - } - - /** - * Populate the form in case an user backend is being edited - * - * @see Form::onRequest() - * - * @throws ConfigurationError In case the backend name is missing in the request or is invalid - */ - public function onRequest() - { - $authBackend = $this->request->getQuery('backend'); - if ($authBackend !== null) { - if ($authBackend === '') { - throw new ConfigurationError($this->translate('User backend name missing')); - } elseif (! $this->config->hasSection($authBackend)) { - throw new ConfigurationError($this->translate('Unknown user backend provided')); - } elseif ($this->config->getSection($authBackend)->backend === null) { - throw new ConfigurationError( - sprintf($this->translate('Backend "%s" has no `backend\' setting'), $authBackend) - ); - } - - $configValues = $this->config->getSection($authBackend)->toArray(); - $configValues['type'] = $configValues['backend']; - $configValues['name'] = $authBackend; - $this->populate($configValues); - } elseif (empty($this->resources)) { - $externalBackends = array_filter( - $this->config->toArray(), - function ($authBackendCfg) { - return isset($authBackendCfg['backend']) && $authBackendCfg['backend'] === 'external'; - } - ); - - if (false === empty($externalBackends)) { - throw new ConfigurationError($this->translate('Could not find any resources for authentication')); - } - } - } - - /** - * Return a checkbox to be displayed at the beginning of the form - * which allows the user to skip the connection validation - * - * @return Zend_Form_Element - */ - protected function getForceCreationCheckbox() - { - return $this->createElement( - 'checkbox', - 'force_creation', - array( - 'order' => 0, - 'ignore' => true, - 'label' => $this->translate('Force Changes'), - 'description' => $this->translate('Check this box to enforce changes without connectivity validation') - ) - ); - } - - /** - * @see Form::createElements() + * @param array $formData */ public function createElements(array $formData) { @@ -302,7 +264,7 @@ class UserBackendConfigForm extends ConfigForm if (isset($this->resources['db'])) { $backendTypes['db'] = $this->translate('Database'); } - if (isset($this->resources['ldap']) && ($backendType === 'ldap' || Platform::extensionLoaded('ldap'))) { + if (isset($this->resources['ldap'])) { $backendTypes['ldap'] = 'LDAP'; $backendTypes['msldap'] = 'ActiveDirectory'; } @@ -336,21 +298,107 @@ class UserBackendConfigForm extends ConfigForm ) ); - if (isset($formData['force_creation']) && $formData['force_creation']) { + if (isset($formData['skip_validation']) && $formData['skip_validation']) { // In case another error occured and the checkbox was displayed before - $this->addElement($this->getForceCreationCheckbox()); + $this->addSkipValidationCheckbox(); } - $this->addElements($this->getBackendForm($backendType)->createElements($formData)->getElements()); + $this->addSubForm($this->getBackendForm($backendType)->create($formData), 'backend_form'); } /** - * Return the configuration for the chosen resource - * - * @return ConfigObject + * Populate the configuration of the backend to load */ - public function getResourceConfig() + public function onRequest() { - return ResourceFactory::getResourceConfig($this->getValue('resource')); + if ($this->backendToLoad) { + $data = $this->config->getSection($this->backendToLoad)->toArray(); + $data['name'] = $this->backendToLoad; + $data['type'] = $data['backend']; + $this->populate($data); + } + } + + /** + * Retrieve all form element values + * + * @param bool $suppressArrayNotation Ignored + * + * @return array + */ + public function getValues($suppressArrayNotation = false) + { + $values = parent::getValues(); + $values = array_merge($values, $values['backend_form']); + unset($values['backend_form']); + return $values; + } + + /** + * Return whether the given values are valid + * + * @param array $formData The data to validate + * + * @return bool + */ + public function isValid($formData) + { + if (! parent::isValid($formData)) { + return false; + } + + if (($el = $this->getElement('skip_validation')) === null || false === $el->isChecked()) { + $backendForm = $this->getBackendForm($this->getValue('type')); + if (! static::isValidUserBackend($this)) { + if ($el === null) { + $this->addSkipValidationCheckbox(); + } + + return false; + } + } + + return true; + } + + /** + * Validate the configuration by creating a backend and running its inspection checks + * + * @param Form $form The form to fetch the configuration values from + * + * @return bool Whether inspection succeeded or not + */ + public static function isValidUserBackend(Form $form) + { + $backend = UserBackend::create(null, new ConfigObject($form->getValues())); + if ($backend instanceof Inspectable) { + $inspection = $backend->inspect(); + if ($inspection->hasError()) { + $form->error($inspection->getError()); + return false; + } + } + + return true; + } + + /** + * Add a checkbox to the form by which the user can skip the connection validation + */ + protected function addSkipValidationCheckbox() + { + $this->addElement( + 'checkbox', + 'skip_validation', + array( + 'order' => 0, + 'ignore' => true, + 'required' => true, + 'label' => $this->translate('Skip Validation'), + 'description' => $this->translate( + 'Check this box to enforce changes without validating that authentication is possible.' + ) + ) + ); } } diff --git a/application/forms/Config/UserBackendReorderForm.php b/application/forms/Config/UserBackendReorderForm.php index 9069e73e3..359f515b1 100644 --- a/application/forms/Config/UserBackendReorderForm.php +++ b/application/forms/Config/UserBackendReorderForm.php @@ -3,9 +3,9 @@ namespace Icinga\Forms\Config; -use InvalidArgumentException; -use Icinga\Web\Notification; use Icinga\Forms\ConfigForm; +use Icinga\Exception\NotFoundError; +use Icinga\Web\Notification; class UserBackendReorderForm extends ConfigForm { @@ -29,7 +29,9 @@ class UserBackendReorderForm extends ConfigForm } /** - * @see Form::createElements() + * Create and add elements to this form + * + * @param array $formData */ public function createElements(array $formData) { @@ -39,8 +41,6 @@ class UserBackendReorderForm extends ConfigForm /** * Update the user backend order and save the configuration - * - * @see Form::onSuccess() */ public function onSuccess() { @@ -55,8 +55,8 @@ class UserBackendReorderForm extends ConfigForm } else { return false; } - } catch (InvalidArgumentException $e) { - Notification::error($e->getMessage()); + } catch (NotFoundError $_) { + Notification::error(sprintf($this->translate('User backend "%s" not found'), $backendName)); } } } diff --git a/application/views/scripts/config/userbackend/create.phtml b/application/views/scripts/config/form.phtml similarity index 100% rename from application/views/scripts/config/userbackend/create.phtml rename to application/views/scripts/config/form.phtml diff --git a/application/views/scripts/config/userbackend/modify.phtml b/application/views/scripts/config/userbackend/modify.phtml deleted file mode 100644 index 338d6807e..000000000 --- a/application/views/scripts/config/userbackend/modify.phtml +++ /dev/null @@ -1,6 +0,0 @@ -
- tabs->showOnlyCloseButton() ?> -
-
- -
diff --git a/application/views/scripts/config/userbackend/remove.phtml b/application/views/scripts/config/userbackend/remove.phtml deleted file mode 100644 index a53f51721..000000000 --- a/application/views/scripts/config/userbackend/remove.phtml +++ /dev/null @@ -1,6 +0,0 @@ -
- tabs->showOnlyCloseButton() ?> -
-
- -
\ No newline at end of file diff --git a/modules/setup/application/forms/AuthBackendPage.php b/modules/setup/application/forms/AuthBackendPage.php index 2caab9122..06a2fec2c 100644 --- a/modules/setup/application/forms/AuthBackendPage.php +++ b/modules/setup/application/forms/AuthBackendPage.php @@ -3,11 +3,12 @@ namespace Icinga\Module\Setup\Forms; -use Icinga\Web\Form; +use Icinga\Data\ConfigObject; +use Icinga\Forms\Config\UserBackendConfigForm; use Icinga\Forms\Config\UserBackend\DbBackendForm; use Icinga\Forms\Config\UserBackend\LdapBackendForm; use Icinga\Forms\Config\UserBackend\ExternalBackendForm; -use Icinga\Data\ConfigObject; +use Icinga\Web\Form; /** * Wizard page to define authentication backend specific details @@ -66,14 +67,14 @@ class AuthBackendPage extends Form $this->setRequiredCue(null); $backendForm = new DbBackendForm(); $backendForm->setRequiredCue(null); - $backendForm->createElements($formData)->removeElement('resource'); + $backendForm->create($formData)->removeElement('resource'); $this->addDescription($this->translate( 'As you\'ve chosen to use a database for authentication all you need ' . 'to do now is defining a name for your first authentication backend.' )); } elseif ($this->config['type'] === 'ldap') { $backendForm = new LdapBackendForm(); - $backendForm->createElements($formData)->removeElement('resource'); + $backendForm->create($formData)->removeElement('resource'); $this->addDescription($this->translate( 'Before you are able to authenticate using the LDAP connection defined earlier you need to' . ' provide some more information so that Icinga Web 2 is able to locate account details.' @@ -97,15 +98,30 @@ class AuthBackendPage extends Form ); } else { // $this->config['type'] === 'external' $backendForm = new ExternalBackendForm(); - $backendForm->createElements($formData); + $backendForm->create($formData); $this->addDescription($this->translate( 'You\'ve chosen to authenticate using a web server\'s mechanism so it may be necessary' . ' to adjust usernames before any permissions, restrictions, etc. are being applied.' )); } - $this->addElements($backendForm->getElements()); - $this->getElement('name')->setValue('icingaweb2'); + $backendForm->getElement('name')->setValue('icingaweb2'); + $this->addSubForm($backendForm, 'backend_form'); + } + + /** + * Retrieve all form element values + * + * @param bool $suppressArrayNotation Ignored + * + * @return array + */ + public function getValues($suppressArrayNotation = false) + { + $values = parent::getValues(); + $values = array_merge($values, $values['backend_form']); + unset($values['backend_form']); + return $values; } /** @@ -117,11 +133,11 @@ class AuthBackendPage extends Form */ public function isValid($data) { - if (false === parent::isValid($data)) { + if (! parent::isValid($data)) { return false; } - if ($this->config['type'] === 'ldap' && ( !isset($data['skip_validation']) || $data['skip_validation'] == 0)) { + if ($this->config['type'] === 'ldap' && (! isset($data['skip_validation']) || $data['skip_validation'] == 0)) { $self = clone $this; $self->addElement( 'text', @@ -130,7 +146,7 @@ class AuthBackendPage extends Form 'value' => $this->getResourceConfig() ) ); - if (! LdapBackendForm::isValidUserBackend($self)) { + if (! UserBackendConfigForm::isValidUserBackend($self)) { $this->addSkipValidationCheckbox(); return false; } From 666c401a4064cf4c36a5f1e83946fcf9bcac6b42 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 23 Jul 2015 16:18:35 +0200 Subject: [PATCH 056/108] BackendConfigForm: Translate exceptions shown to the user --- .../application/forms/Config/BackendConfigForm.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/monitoring/application/forms/Config/BackendConfigForm.php b/modules/monitoring/application/forms/Config/BackendConfigForm.php index 504ec9a59..d2fe4e938 100644 --- a/modules/monitoring/application/forms/Config/BackendConfigForm.php +++ b/modules/monitoring/application/forms/Config/BackendConfigForm.php @@ -109,7 +109,10 @@ class BackendConfigForm extends ConfigForm $backendName = $data['name']; if ($this->config->hasSection($backendName)) { - throw new IcingaException('A monitoring backend with the name "%s" does already exist', $backendName); + throw new IcingaException( + $this->translate('A monitoring backend with the name "%s" does already exist'), + $backendName + ); } unset($data['name']); From 0448323697bf86701c47fe8acec32f71fd50acba Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 23 Jul 2015 16:18:44 +0200 Subject: [PATCH 057/108] InstanceConfigForm: Translate exceptions shown to the user --- .../application/forms/Config/InstanceConfigForm.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/monitoring/application/forms/Config/InstanceConfigForm.php b/modules/monitoring/application/forms/Config/InstanceConfigForm.php index 652279722..27e1e410d 100644 --- a/modules/monitoring/application/forms/Config/InstanceConfigForm.php +++ b/modules/monitoring/application/forms/Config/InstanceConfigForm.php @@ -95,7 +95,10 @@ class InstanceConfigForm extends ConfigForm $instanceName = $data['name']; if ($this->config->hasSection($instanceName)) { - throw new IcingaException('A monitoring instance with the name "%s" does already exist', $instanceName); + throw new IcingaException( + $this->translate('A monitoring instance with the name "%s" does already exist'), + $instanceName + ); } unset($data['name']); From 073243364dc64db54305ef92c25ff68e9f203b01 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 23 Jul 2015 16:49:19 +0200 Subject: [PATCH 058/108] forms.less: Re-introduce class control-groups refs #9602 --- public/css/icinga/forms.less | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index cfb26f801..ec0550739 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -190,7 +190,8 @@ form ul.form-notifications { } form div.element { - margin: 0.5em 0; + margin-top: 0.5em; + margin-bottom: 0.5em; } form label { @@ -277,3 +278,18 @@ form ul.hints { padding-bottom: 0.5em; } } + +.control-group { + & > * { + float: left; + margin-right: 0.5em; + } + + &:after { + content: "."; + visibility: hidden; + display: block; + height: 0; + clear: both; + } +} \ No newline at end of file From 44643a6c4b63b5f16bf112238603fdee9b5a16d1 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 23 Jul 2015 16:49:50 +0200 Subject: [PATCH 059/108] PreferenceForm: Improve button placement --- application/forms/PreferenceForm.php | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/application/forms/PreferenceForm.php b/application/forms/PreferenceForm.php index d098cb781..409feb751 100644 --- a/application/forms/PreferenceForm.php +++ b/application/forms/PreferenceForm.php @@ -205,10 +205,7 @@ class PreferenceForm extends Form array( 'ignore' => true, 'label' => $this->translate('Save to the Preferences'), - 'decorators' => array( - 'ViewHelper', - array('HtmlTag', array('tag' => 'div')) - ) + 'decorators' => array('ViewHelper') ) ); } @@ -219,10 +216,7 @@ class PreferenceForm extends Form array( 'ignore' => true, 'label' => $this->translate('Save for the current Session'), - 'decorators' => array( - 'ViewHelper', - array('HtmlTag', array('tag' => 'div')) - ) + 'decorators' => array('ViewHelper') ) ); From 8ed816002bdfb92008f04822e6ced9912e3e9a89 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 23 Jul 2015 17:33:29 +0200 Subject: [PATCH 060/108] forms.less: Remove redundant margin from form elements in control groups refs #9602 --- public/css/icinga/forms.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index ec0550739..d45f14a28 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -285,6 +285,11 @@ form ul.hints { margin-right: 0.5em; } + div.element { + margin-top: 0; + margin-bottom: 0; + } + &:after { content: "."; visibility: hidden; From f06be5c9bc7f66f202a4482ff1bb1ea83856f105 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 23 Jul 2015 17:34:09 +0200 Subject: [PATCH 061/108] LdapConnection: Let self::bind() return $this --- library/Icinga/Protocol/Ldap/LdapConnection.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/Icinga/Protocol/Ldap/LdapConnection.php b/library/Icinga/Protocol/Ldap/LdapConnection.php index 38440e029..841e855da 100644 --- a/library/Icinga/Protocol/Ldap/LdapConnection.php +++ b/library/Icinga/Protocol/Ldap/LdapConnection.php @@ -314,7 +314,7 @@ class LdapConnection implements Selectable, Inspectable public function bind() { if ($this->bound) { - return; + return $this; } $ds = $this->getConnection(); @@ -332,6 +332,7 @@ class LdapConnection implements Selectable, Inspectable } $this->bound = true; + return $this; } /** From 760c7e4374f08f7d51c756e312bb7a68022fe2d7 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Thu, 23 Jul 2015 17:42:02 +0200 Subject: [PATCH 062/108] LdapBackendForm: Allow to discover a connection's default settings refs #9602 --- .../Config/UserBackend/LdapBackendForm.php | 71 +++++++++++++++++-- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/application/forms/Config/UserBackend/LdapBackendForm.php b/application/forms/Config/UserBackend/LdapBackendForm.php index b5b2c966f..fbdfb5c00 100644 --- a/application/forms/Config/UserBackend/LdapBackendForm.php +++ b/application/forms/Config/UserBackend/LdapBackendForm.php @@ -3,6 +3,7 @@ namespace Icinga\Forms\Config\UserBackend; +use Icinga\Data\ResourceFactory; use Icinga\Web\Form; /** @@ -86,6 +87,60 @@ class LdapBackendForm extends Form : array() ) ); + + $baseDn = null; + $hasAdOid = false; + if (! $isAd && !empty($this->resources)) { + $this->addElement( + 'button', + 'discovery_btn', + array( + 'type' => 'submit', + 'value' => 'discovery_btn', + 'label' => $this->translate('Discover', 'A button to discover LDAP capabilities'), + 'title' => $this->translate( + 'Push to fill in the chosen connection\'s default settings.' + ), + 'decorators' => array( + array('ViewHelper', array('separator' => '')), + array('HtmlTag', array('tag' => 'div', 'class' => 'element')) + ), + 'formnovalidate' => 'formnovalidate' + ) + ); + $this->addDisplayGroup( + array('resource', 'discovery_btn'), + 'connection_discovery', + array( + 'decorators' => array( + 'FormElements', + array('HtmlTag', array('tag' => 'div', 'class' => 'control-group')) + ) + ) + ); + + if ($this->getElement('discovery_btn')->isChecked()) { + $connection = ResourceFactory::create( + isset($formData['resource']) ? $formData['resource'] : reset($this->resources) + ); + $capabilities = $connection->bind()->getCapabilities(); + $baseDn = $capabilities->getDefaultNamingContext(); + $hasAdOid = $capabilities->isActiveDirectory(); + } + } + + if ($isAd || $hasAdOid) { + // ActiveDirectory defaults + $userClass = 'user'; + $filter = '!(objectClass=computer)'; + $userNameAttribute = 'sAMAccountName'; + } else { + // OpenLDAP defaults + $userClass = 'inetOrgPerson'; + $filter = null; + $userNameAttribute = 'uid'; + } + $this->addElement( 'text', 'user_class', @@ -96,7 +151,7 @@ class LdapBackendForm extends Form 'disabled' => $isAd ?: null, 'label' => $this->translate('LDAP User Object Class'), 'description' => $this->translate('The object class used for storing users on the LDAP server.'), - 'value' => $isAd ? 'user' : 'inetOrgPerson' + 'value' => $userClass ) ); $this->addElement( @@ -105,7 +160,7 @@ class LdapBackendForm extends Form array( 'preserveDefault' => true, 'allowEmpty' => true, - 'value' => $isAd ? '!(objectClass=computer)' : null, + 'value' => $filter, 'label' => $this->translate('LDAP Filter'), 'description' => $this->translate( 'An additional filter to use when looking up users using the specified connection. ' @@ -148,7 +203,7 @@ class LdapBackendForm extends Form 'description' => $this->translate( 'The attribute name used for storing the user name on the LDAP server.' ), - 'value' => $isAd ? 'sAMAccountName' : 'uid' + 'value' => $userNameAttribute ) ); $this->addElement( @@ -163,12 +218,14 @@ class LdapBackendForm extends Form 'text', 'base_dn', array( - 'required' => false, - 'label' => $this->translate('LDAP Base DN'), - 'description' => $this->translate( + 'preserveDefault' => true, + 'required' => false, + 'label' => $this->translate('LDAP Base DN'), + 'description' => $this->translate( 'The path where users can be found on the LDAP server. Leave ' . 'empty to select all users available using the specified connection.' - ) + ), + 'value' => $baseDn ) ); } From ce1cab53c8dbeb19659a83b449f32f7427c0ac86 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 24 Jul 2015 10:11:31 +0200 Subject: [PATCH 063/108] css: Move button styles from the setup module into the framework --- public/css/icinga/forms.less | 46 ++++++++++++++++++++++++++++++++---- public/css/icinga/setup.less | 28 ---------------------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/public/css/icinga/forms.less b/public/css/icinga/forms.less index d45f14a28..2173532ec 100644 --- a/public/css/icinga/forms.less +++ b/public/css/icinga/forms.less @@ -44,10 +44,19 @@ input[type=submit] { font-weight: bold; text-align: center; color: #fff; - border: 2px solid #ddd; + border: 1px solid; border-color: @colorPetrol; background: @colorPetrol; outline: 0; + + &[disabled] { + background-color: #666; + border-color: black; + } + + &:hover[disabled], &:active[disabled], &:focus[disabled] { + background-color: #666; + } } input[type=submit]:hover, a.button:hover, input[type=submit]:focus { @@ -87,10 +96,6 @@ button::-moz-focus-inner { outline: 0; } -button { - cursor: pointer; -} - select::-moz-focus-inner { border: 0; outline: 0; @@ -297,4 +302,35 @@ form ul.hints { height: 0; clear: both; } +} + +button, .button-like { + font-size: 0.9em; + font-weight: bold; + outline: 0; + color: #fff; + padding: 0.2em; + border: 1px solid; + border-color: @colorPetrol; + background: @colorPetrol; + + &[disabled] { + background-color: #666; + border-color: black; + } + + &:hover, &:focus, &:active { + background-color: #333; + border-color: #333; + cursor: pointer; + + &[disabled] { + background-color: #666; + } + } +} + +a.button-like { + cursor: default; + text-decoration: none; } \ No newline at end of file diff --git a/public/css/icinga/setup.less b/public/css/icinga/setup.less index 9ec689538..94fc0ea19 100644 --- a/public/css/icinga/setup.less +++ b/public/css/icinga/setup.less @@ -127,29 +127,6 @@ } button, .button-like { - font-size: 0.9em; - font-weight: bold; - outline: 0; - color: #fff; - border: 2px solid; - border-color: @colorPetrol; - background: @colorPetrol; - - &[disabled="1"] { - background-color: #aaa; - border: 1px solid black; - } - - &:hover, &:focus, &:active { - background-color: #666; - border-color: #666; - cursor: pointer; - - &[disabled="1"] { - background-color: #aaa; - } - } - &.finish, &.login { min-width: 25em; color: #fffafa; @@ -160,11 +137,6 @@ } } } - - a.button-like { - cursor: default; - text-decoration: none; - } } form#setup_requirements { From 3b4bb6d8909ef4ed00022da711f302a29179c601 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Fri, 24 Jul 2015 10:13:26 +0200 Subject: [PATCH 064/108] ifont: Add new icons up|down|right|left-small --- application/fonts/fontello-ifont/LICENSE.txt | 9 + application/fonts/fontello-ifont/README.txt | 24 +- application/fonts/fontello-ifont/config.json | 24 ++ .../fonts/fontello-ifont/css/ifont-codes.css | 6 +- .../fontello-ifont/css/ifont-embedded.css | 18 +- .../fontello-ifont/css/ifont-ie7-codes.css | 6 +- .../fonts/fontello-ifont/css/ifont-ie7.css | 6 +- .../fonts/fontello-ifont/css/ifont.css | 24 +- application/fonts/fontello-ifont/demo.html | 290 +++++++++++------- 9 files changed, 258 insertions(+), 149 deletions(-) diff --git a/application/fonts/fontello-ifont/LICENSE.txt b/application/fonts/fontello-ifont/LICENSE.txt index 152fb787d..7e9af2aa8 100644 --- a/application/fonts/fontello-ifont/LICENSE.txt +++ b/application/fonts/fontello-ifont/LICENSE.txt @@ -46,3 +46,12 @@ Font license info Homepage: http://fontello.com +## Typicons + + (c) Stephen Hutchings 2012 + + Author: Stephen Hutchings + License: SIL (http://scripts.sil.org/OFL) + Homepage: http://typicons.com/ + + diff --git a/application/fonts/fontello-ifont/README.txt b/application/fonts/fontello-ifont/README.txt index 43e23f283..a91438a9a 100644 --- a/application/fonts/fontello-ifont/README.txt +++ b/application/fonts/fontello-ifont/README.txt @@ -11,7 +11,7 @@ webfont pack. Details available in LICENSE.txt file. - If your project is open-source, usually, it will be ok to make LICENSE.txt file publically available in your repository. -- Fonts, used in Fontello, don't require to make clickable links on your site. +- Fonts, used in Fontello, don't require a clickable link on your site. But any kind of additional authors crediting is welcome. ================================================================================ @@ -29,8 +29,8 @@ Comments on archive content - LICENSE.txt - license info about source fonts, used to build your one. -- config.json - keeps your settings. You can import it back to fontello anytime, - to continue your work +- config.json - keeps your settings. You can import it back into fontello + anytime, to continue your work Why so many CSS files ? @@ -38,17 +38,17 @@ Why so many CSS files ? Because we like to fit all your needs :) -- basic file, .css - is usually enougth, in contains @font-face - and character codes definition +- basic file, .css - is usually enough, it contains @font-face + and character code definitions - *-ie7.css - if you need IE7 support, but still don't wish to put char codes directly into html - *-codes.css and *-ie7-codes.css - if you like to use your own @font-face - rules, but still wish to benefit of css generation. That can be very - convenient for automated assets build systems. When you need to update font - - no needs to manually edit files, just override old version with archive - content. See fontello source codes for example. + rules, but still wish to benefit from css generation. That can be very + convenient for automated asset build systems. When you need to update font - + no need to manually edit files, just override old version with archive + content. See fontello source code for examples. - *-embedded.css - basic css file, but with embedded WOFF font, to avoid CORS issues in Firefox and IE9+, when fonts are hosted on the separate domain. @@ -63,11 +63,11 @@ Because we like to fit all your needs :) Attention for server setup -------------------------- -You MUST setup server to reply with proper `mime-types` for font files. In other -case, some browsers will fail to show fonts. +You MUST setup server to reply with proper `mime-types` for font files - +otherwise some browsers will fail to show fonts. Usually, `apache` already has necessary settings, but `nginx` and other -webservers should be tuned. Here is list of mime types for our file extentions: +webservers should be tuned. Here is list of mime types for our file extensions: - `application/vnd.ms-fontobject` - eot - `application/x-font-woff` - woff diff --git a/application/fonts/fontello-ifont/config.json b/application/fonts/fontello-ifont/config.json index e9a58cc1a..3a7894589 100644 --- a/application/fonts/fontello-ifont/config.json +++ b/application/fonts/fontello-ifont/config.json @@ -672,6 +672,30 @@ "code": 59492, "src": "entypo" }, + { + "uid": "c16a63e911bc47b46dc2a7129d2f0c46", + "css": "down-small", + "code": 59509, + "src": "typicons" + }, + { + "uid": "58b78b6ca784d5c3db5beefcd9e18061", + "css": "left-small", + "code": 59510, + "src": "typicons" + }, + { + "uid": "877a233d7fdca8a1d82615b96ed0d7a2", + "css": "right-small", + "code": 59511, + "src": "typicons" + }, + { + "uid": "62bc6fe2a82e4864e2b94d4c0985ee0c", + "css": "up-small", + "code": 59512, + "src": "typicons" + }, { "uid": "b90d80c250a9bbdd6cd3fe00e6351710", "css": "ok", diff --git a/application/fonts/fontello-ifont/css/ifont-codes.css b/application/fonts/fontello-ifont/css/ifont-codes.css index ed241ebe9..78c477ddb 100644 --- a/application/fonts/fontello-ifont/css/ifont-codes.css +++ b/application/fonts/fontello-ifont/css/ifont-codes.css @@ -115,4 +115,8 @@ .icon-chart-bar:before { content: '\e871'; } /* '' */ .icon-beaker:before { content: '\e872'; } /* '' */ .icon-magic:before { content: '\e873'; } /* '' */ -.icon-spin6:before { content: '\e874'; } /* 'î¡´' */ \ No newline at end of file +.icon-spin6:before { content: '\e874'; } /* 'î¡´' */ +.icon-down-small:before { content: '\e875'; } /* '' */ +.icon-left-small:before { content: '\e876'; } /* 'î¡¶' */ +.icon-right-small:before { content: '\e877'; } /* 'î¡·' */ +.icon-up-small:before { content: '\e878'; } /* '' */ \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/ifont-embedded.css b/application/fonts/fontello-ifont/css/ifont-embedded.css index e35b001b2..c069e28e6 100644 --- a/application/fonts/fontello-ifont/css/ifont-embedded.css +++ b/application/fonts/fontello-ifont/css/ifont-embedded.css @@ -1,15 +1,15 @@ @font-face { font-family: 'ifont'; - src: url('../font/ifont.eot?57837527'); - src: url('../font/ifont.eot?57837527#iefix') format('embedded-opentype'), - url('../font/ifont.svg?57837527#ifont') format('svg'); + src: url('../font/ifont.eot?12843713'); + src: url('../font/ifont.eot?12843713#iefix') format('embedded-opentype'), + url('../font/ifont.svg?12843713#ifont') format('svg'); font-weight: normal; font-style: normal; } @font-face { font-family: 'ifont'; - src: url('data:application/octet-stream;base64,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') format('woff'), - url('data:application/octet-stream;base64,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BjYGFQ4BFS4BNx4BFxYHBgcGFxQGFgGtdMZycsboyG4GerwBEgEIAwECBAMRFRMKAQwEDAMBBwYEBAoFBgQBCAEGAQQEBAIEBgEGAggJBQQFBQMBCAwBBRwHAgIBCAEOAQIHCQMEBAEEAgMBBwoCBAUNBAIUDhMECAYBAgECBQkCARMJAgQGBQYKAwgEBwUDAgYJBAYBBQkEBQMDAgUEAQ4HCw8EEAMDAQgECAEIAwEIBAQEAwMEAgQSBQMMDAEDAwIMGRsDAwgFEwUDCwQNCwEEAgYECAQJBFEyBAUCBgUDARgKAQIHBQQDBAQEAQIBAQECCgcHEgQHCQQDCAQCDgEBAgIOAgQCAg8IAwQDAgMFAQQKCgEECAQFDAcCAwgDCQcWBgYFCAgQBBQKAQIEAgYDDgMEAQoFCBEKAgICAgEFAgQBCgIDDAMCCAECCAMBAwIHCwQBAgIIFAMICgECAQQCAwUCAQIBBAECAgQYAwkDAQEBAw0CDgQCAwEEAwUCBggEAgIBCAQEBwgFBwwEBAICAgYBBQQDAgMFBwQDAhIBBAICBQwCCQICCggFCQIIBAIKCQ0JaXJRAQwBDQEEAxUBAwUCAwICAQUMCAMEBQEKAQMBAQQIBAoBBwYCCgIEAQwBAQICBAsPAQIJCgEDC3TE6sR0dMTqxHT+3QEIAgYGAQQIAwULAQwCAgQMAQoHAgMEAgQBAgYMBQYDCgEGBAEBAgICAQMDAgEDCAQCBgIDAwQFBAYHBAYICgcEBQYFDAMBAgQCAQMMCQ4DBAUHCAUDEQIDDgcGDAMBAwkCBwoDBgEOBAoEAQIFAgIGCgQHBwcBCQUIBwgDAgcDAgQCBgIEBQoDAw4CBQEBAgUEBwIBCggPAQMCAgcEAw4DAgQDBwMGBAQBAS1PBAEIBAMEBg8KAgYEBQQFDgkUCwIBBhoCARcFBAYDBRQDAxAFAgEECAUIBAELFw4FDAICBAQMCA4EDgEKCxQHCAEFAw0CAQIBEgMKBAQJBQYCAwoDAgMFDAIQCRMDAwQEBgIECgcOAQUCBAEEAgIQBQ8FAgUDAgsCCAQEAgIEGA4JDgUJAQQGAQIDAQEBBAMGBwYFAg8KAQQBAgMBAgMIBRcEAggIAwQPAgoKBQECAwQLCQUCAgICBgIKBwYFBAQEAwEECgQGAQcCAQcGBQMEAQEBBQQC/g0VVQIC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KABDACkAIAAyADAAMQA1ACAAYgB5ACAAbwByAGkAZwBpAG4AYQBsACAAYQB1AHQAaABvAHIAcwAgAEAAIABmAG8AbgB0AGUAbABsAG8ALgBjAG8AbQBpAGYAbwBuAHQAUgBlAGcAdQBsAGEAcgBpAGYAbwBuAHQAaQBmAG8AbgB0AFYAZQByAHMAaQBvAG4AIAAxAC4AMABpAGYAbwBuAHQARwBlAG4AZQByAGEAdABlAGQAIABiAHkAIABzAHYAZwAyAHQAdABmACAAZgByAG8AbQAgAEYAbwBuAHQAZQBsAGwAbwAgAHAAcgBvAGoAZQBjAHQALgBoAHQAdABwADoALwAvAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAAAAAAIAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAQIBAwEEAQUBBgEHAQgBCQEKAQsBDAENAQ4BDwEQAREBEgETARQBFQEWARcBGAEZARoBGwEcAR0BHgEfASABIQEiASMBJAElASYBJwEoASkBKgErASwBLQEuAS8BMAExATIBMwE0ATUBNgE3ATgBOQE6ATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFGAUcBSAFJAUoBSwFMAU0BTgFPAVABUQFSAVMBVAFVAVYBVwFYAVkBWgFbAVwBXQFeAV8BYAFhAWIBYwFkAWUBZgFnAWgBaQFqAWsBbAFtAW4BbwFwAXEBcgFzAXQBdQF2CWRhc2hib2FyZAR1c2VyBXVzZXJzAm9rBmNhbmNlbARwbHVzBW1pbnVzDGZvbGRlci1lbXB0eQhkb3dubG9hZAZ1cGxvYWQDZ2l0BWN1YmVzCGRhdGFiYXNlBWdhdWdlB3NpdGVtYXAMc29ydC1uYW1lLXVwDnNvcnQtbmFtZS1kb3duCW1lZ2FwaG9uZQNidWcFdGFza3MGZmlsdGVyA29mZgRib29rBXBhc3RlCHNjaXNzb3JzBWdsb2JlBWNsb3VkBWZsYXNoCGJhcmNoYXJ0CGRvd24tZGlyBnVwLWRpcghsZWZ0LWRpcglyaWdodC1kaXIJZG93bi1vcGVuCnJpZ2h0LW9wZW4HdXAtb3BlbglsZWZ0LW9wZW4GdXAtYmlnCXJpZ2h0LWJpZwhsZWZ0LWJpZwhkb3duLWJpZw9yZXNpemUtZnVsbC1hbHQLcmVzaXplLWZ1bGwMcmVzaXplLXNtYWxsBG1vdmURcmVzaXplLWhvcml6b250YWwPcmVzaXplLXZlcnRpY2FsB3pvb20taW4FYmxvY2sIem9vbS1vdXQJbGlnaHRidWxiBWNsb2NrCXZvbHVtZS11cAt2b2x1bWUtZG93bgp2b2x1bWUtb2ZmBG11dGUDbWljB2VuZHRpbWUJc3RhcnR0aW1lDmNhbGVuZGFyLWVtcHR5CGNhbGVuZGFyBndyZW5jaAdzbGlkZXJzCHNlcnZpY2VzB3NlcnZpY2UFcGhvbmUIZmlsZS1wZGYJZmlsZS13b3JkCmZpbGUtZXhjZWwIZG9jLXRleHQFdHJhc2gNY29tbWVudC1lbXB0eQdjb21tZW50BGNoYXQKY2hhdC1lbXB0eQRiZWxsCGJlbGwtYWx0DWF0dGVudGlvbi1hbHQFcHJpbnQEZWRpdAdmb3J3YXJkBXJlcGx5CXJlcGx5LWFsbANleWUDdGFnBHRhZ3MNbG9jay1vcGVuLWFsdAlsb2NrLW9wZW4EbG9jawRob21lBGluZm8EaGVscAZzZWFyY2gIZmxhcHBpbmcGcmV3aW5kCmNoYXJ0LWxpbmUIYmVsbC1vZmYOYmVsbC1vZmYtZW1wdHkEcGx1ZwdleWUtb2ZmCnJlc2NoZWR1bGUCY3cEaG9zdAl0aHVtYnMtdXALdGh1bWJzLWRvd24Hc3Bpbm5lcgZhdHRhY2gIa2V5Ym9hcmQEbWVudQR3aWZpBG1vb24JY2hhcnQtcGllCmNoYXJ0LWFyZWEJY2hhcnQtYmFyBmJlYWtlcgVtYWdpYwVzcGluNgAAAAEAAf//AA8AAAAAAAAAAAAAAACwACwgsABVWEVZICBLuAAOUUuwBlNaWLA0G7AoWWBmIIpVWLACJWG5CAAIAGNjI2IbISGwAFmwAEMjRLIAAQBDYEItsAEssCBgZi2wAiwgZCCwwFCwBCZasigBCkNFY0VSW1ghIyEbilggsFBQWCGwQFkbILA4UFghsDhZWSCxAQpDRWNFYWSwKFBYIbEBCkNFY0UgsDBQWCGwMFkbILDAUFggZiCKimEgsApQWGAbILAgUFghsApgGyCwNlBYIbA2YBtgWVlZG7ABK1lZI7AAUFhlWVktsAMsIEUgsAQlYWQgsAVDUFiwBSNCsAYjQhshIVmwAWAtsAQsIyEjISBk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') format('truetype'); + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,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BjYGFQ4BFS4BNx4BFxYHBgcGFxQGFgGtdMZycsboyG4GerwBEgEIAwECBAMRFRMKAQwEDAMBBwYEBAoFBgQBCAEGAQQEBAIEBgEGAggJBQQFBQMBCAwBBRwHAgIBCAEOAQIHCQMEBAEEAgMBBwoCBAUNBAIUDhMECAYBAgECBQkCARMJAgQGBQYKAwgEBwUDAgYJBAYBBQkEBQMDAgUEAQ4HCw8EEAMDAQgECAEIAwEIBAQEAwMEAgQSBQMMDAEDAwIMGRsDAwgFEwUDCwQNCwEEAgYECAQJBFEyBAUCBgUDARgKAQIHBQQDBAQEAQIBAQECCgcHEgQHCQQDCAQCDgEBAgIOAgQCAg8IAwQDAgMFAQQKCgEECAQFDAcCAwgDCQcWBgYFCAgQBBQKAQIEAgYDDgMEAQoFCBEKAgICAgEFAgQBCgIDDAMCCAECCAMBAwIHCwQBAgIIFAMICgECAQQCAwUCAQIBBAECAgQYAwkDAQEBAw0CDgQCAwEEAwUCBggEAgIBCAQEBwgFBwwEBAICAgYBBQQDAgMFBwQDAhIBBAICBQwCCQICCggFCQIIBAIKCQ0JaXJRAQwBDQEEAxUBAwUCAwICAQUMCAMEBQEKAQMBAQQIBAoBBwYCCgIEAQwBAQICBAsPAQIJCgEDC3TE6sR0dMTqxHT+3QEIAgYGAQQIAwULAQwCAgQMAQoHAgMEAgQBAgYMBQYDCgEGBAEBAgICAQMDAgEDCAQCBgIDAwQFBAYHBAYICgcEBQYFDAMBAgQCAQMMCQ4DBAUHCAUDEQIDDgcGDAMBAwkCBwoDBgEOBAoEAQIFAgIGCgQHBwcBCQUIBwgDAgcDAgQCBgIEBQoDAw4CBQEBAgUEBwIBCggPAQMCAgcEAw4DAgQDBwMGBAQBAS1PBAEIBAMEBg8KAgYEBQQFDgkUCwIBBhoCARcFBAYDBRQDAxAFAgEECAUIBAELFw4FDAICBAQMCA4EDgEKCxQHCAEFAw0CAQIBEgMKBAQJBQYCAwoDAgMFDAIQCRMDAwQEBgIECgcOAQUCBAEEAgIQBQ8FAgUDAgsCCAQEAgIEGA4JDgUJAQQGAQIDAQEBBAMGBwYFAg8KAQQBAgMBAgMIBRcEAggIAwQPAgoKBQECAwQLCQUCAgICBgIKBwYFBAQEAwEECgQGAQcCAQcGBQMEAQEBBQQC/g0VVQIC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AXABcQFyAXMBdAF1AXYBdwF4AXkBeglkYXNoYm9hcmQEdXNlcgV1c2VycwJvawZjYW5jZWwEcGx1cwVtaW51cwxmb2xkZXItZW1wdHkIZG93bmxvYWQGdXBsb2FkA2dpdAVjdWJlcwhkYXRhYmFzZQVnYXVnZQdzaXRlbWFwDHNvcnQtbmFtZS11cA5zb3J0LW5hbWUtZG93bgltZWdhcGhvbmUDYnVnBXRhc2tzBmZpbHRlcgNvZmYEYm9vawVwYXN0ZQhzY2lzc29ycwVnbG9iZQVjbG91ZAVmbGFzaAhiYXJjaGFydAhkb3duLWRpcgZ1cC1kaXIIbGVmdC1kaXIJcmlnaHQtZGlyCWRvd24tb3BlbgpyaWdodC1vcGVuB3VwLW9wZW4JbGVmdC1vcGVuBnVwLWJpZwlyaWdodC1iaWcIbGVmdC1iaWcIZG93bi1iaWcPcmVzaXplLWZ1bGwtYWx0C3Jlc2l6ZS1mdWxsDHJlc2l6ZS1zbWFsbARtb3ZlEXJlc2l6ZS1ob3Jpem9udGFsD3Jlc2l6ZS12ZXJ0aWNhbAd6b29tLWluBWJsb2NrCHpvb20tb3V0CWxpZ2h0YnVsYgVjbG9jawl2b2x1bWUtdXALdm9sdW1lLWRvd24Kdm9sdW1lLW9mZgRtdXRlA21pYwdlbmR0aW1lCXN0YXJ0dGltZQ5jYWxlbmRhci1lbXB0eQhjYWxlbmRhcgZ3cmVuY2gHc2xpZGVycwhzZXJ2aWNlcwdzZXJ2aWNlBXBob25lCGZpbGUtcGRmCWZpbGUtd29yZApmaWxlLWV4Y2VsCGRvYy10ZXh0BXRyYXNoDWNvbW1lbnQtZW1wdHkHY29tbWVudARjaGF0CmNoYXQtZW1wdHkEYmVsbAhiZWxsLWFsdA1hdHRlbnRpb24tYWx0BXByaW50BGVkaXQHZm9yd2FyZAVyZXBseQlyZXBseS1hbGwDZXllA3RhZwR0YWdzDWxvY2stb3Blbi1hbHQJbG9jay1vcGVuBGxvY2sEaG9tZQRpbmZvBGhlbHAGc2VhcmNoCGZsYXBwaW5nBnJld2luZApjaGFydC1saW5lCGJlbGwtb2ZmDmJlbGwtb2ZmLWVtcHR5BHBsdWcHZXllLW9mZgpyZXNjaGVkdWxlAmN3BGhvc3QJdGh1bWJzLXVwC3RodW1icy1kb3duB3Nw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') format('truetype'); } /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ @@ -17,7 +17,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'ifont'; - src: url('../font/ifont.svg?57837527#ifont') format('svg'); + src: url('../font/ifont.svg?12843713#ifont') format('svg'); } } */ @@ -168,4 +168,8 @@ .icon-chart-bar:before { content: '\e871'; } /* '' */ .icon-beaker:before { content: '\e872'; } /* '' */ .icon-magic:before { content: '\e873'; } /* '' */ -.icon-spin6:before { content: '\e874'; } /* 'î¡´' */ \ No newline at end of file +.icon-spin6:before { content: '\e874'; } /* 'î¡´' */ +.icon-down-small:before { content: '\e875'; } /* '' */ +.icon-left-small:before { content: '\e876'; } /* 'î¡¶' */ +.icon-right-small:before { content: '\e877'; } /* 'î¡·' */ +.icon-up-small:before { content: '\e878'; } /* '' */ \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/ifont-ie7-codes.css b/application/fonts/fontello-ifont/css/ifont-ie7-codes.css index 1f3c43354..4f30af4af 100644 --- a/application/fonts/fontello-ifont/css/ifont-ie7-codes.css +++ b/application/fonts/fontello-ifont/css/ifont-ie7-codes.css @@ -115,4 +115,8 @@ .icon-chart-bar { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-beaker { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-magic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-spin6 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file +.icon-spin6 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-down-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-left-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-right-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-up-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/ifont-ie7.css b/application/fonts/fontello-ifont/css/ifont-ie7.css index 18b372d2b..c0047b50a 100644 --- a/application/fonts/fontello-ifont/css/ifont-ie7.css +++ b/application/fonts/fontello-ifont/css/ifont-ie7.css @@ -126,4 +126,8 @@ .icon-chart-bar { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-beaker { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-magic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-spin6 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file +.icon-spin6 { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-down-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-left-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-right-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-up-small { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/application/fonts/fontello-ifont/css/ifont.css b/application/fonts/fontello-ifont/css/ifont.css index 80f91f8b2..971bf44b3 100644 --- a/application/fonts/fontello-ifont/css/ifont.css +++ b/application/fonts/fontello-ifont/css/ifont.css @@ -1,10 +1,10 @@ @font-face { font-family: 'ifont'; - src: url('../font/ifont.eot?6491776'); - src: url('../font/ifont.eot?6491776#iefix') format('embedded-opentype'), - url('../font/ifont.woff?6491776') format('woff'), - url('../font/ifont.ttf?6491776') format('truetype'), - url('../font/ifont.svg?6491776#ifont') format('svg'); + src: url('../font/ifont.eot?54745533'); + src: url('../font/ifont.eot?54745533#iefix') format('embedded-opentype'), + url('../font/ifont.woff?54745533') format('woff'), + url('../font/ifont.ttf?54745533') format('truetype'), + url('../font/ifont.svg?54745533#ifont') format('svg'); font-weight: normal; font-style: normal; } @@ -14,7 +14,7 @@ @media screen and (-webkit-min-device-pixel-ratio:0) { @font-face { font-family: 'ifont'; - src: url('../font/ifont.svg?6491776#ifont') format('svg'); + src: url('../font/ifont.svg?54745533#ifont') format('svg'); } } */ @@ -35,7 +35,7 @@ /* For safety - reset parent styles, that can break glyph codes*/ font-variant: normal; text-transform: none; - + /* fix buttons height, for twitter bootstrap */ line-height: 1em; @@ -46,6 +46,10 @@ /* you can be more comfortable with increased icons size */ /* font-size: 120%; */ + /* Font smoothing. That was taken from TWBS */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + /* Uncomment for 3D effect */ /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ } @@ -166,4 +170,8 @@ .icon-chart-bar:before { content: '\e871'; } /* '' */ .icon-beaker:before { content: '\e872'; } /* '' */ .icon-magic:before { content: '\e873'; } /* '' */ -.icon-spin6:before { content: '\e874'; } /* 'î¡´' */ \ No newline at end of file +.icon-spin6:before { content: '\e874'; } /* 'î¡´' */ +.icon-down-small:before { content: '\e875'; } /* '' */ +.icon-left-small:before { content: '\e876'; } /* 'î¡¶' */ +.icon-right-small:before { content: '\e877'; } /* 'î¡·' */ +.icon-up-small:before { content: '\e878'; } /* '' */ \ No newline at end of file diff --git a/application/fonts/fontello-ifont/demo.html b/application/fonts/fontello-ifont/demo.html index dff2d3250..5cfd8dbf8 100644 --- a/application/fonts/fontello-ifont/demo.html +++ b/application/fonts/fontello-ifont/demo.html @@ -227,8 +227,54 @@ body { .i-code { display: none; } - - +@font-face { + font-family: 'ifont'; + src: url('./font/ifont.eot?11424534'); + src: url('./font/ifont.eot?11424534#iefix') format('embedded-opentype'), + url('./font/ifont.woff?11424534') format('woff'), + url('./font/ifont.ttf?11424534') format('truetype'), + url('./font/ifont.svg?11424534#ifont') format('svg'); + font-weight: normal; + font-style: normal; + } + + + .demo-icon + { + font-family: "ifont"; + font-style: normal; + font-weight: normal; + speak: none; + + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: .2em; + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + margin-left: .2em; + + /* You can be more comfortable with increased icons size */ + /* font-size: 120%; */ + + /* Font smoothing. That was taken from TWBS */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ + } +