diff --git a/application/forms/Config/Authentication/ExternalBackendForm.php b/application/forms/Config/Authentication/ExternalBackendForm.php index e25661596..86087eb66 100644 --- a/application/forms/Config/Authentication/ExternalBackendForm.php +++ b/application/forms/Config/Authentication/ExternalBackendForm.php @@ -53,7 +53,7 @@ class ExternalBackendForm extends Form return @preg_match($value, '') !== false; }); $callbackValidator->setMessage( - $this->translate('"%value%" is not a valid regular expression'), + $this->translate('"%value%" is not a valid regular expression.'), Zend_Validate_Callback::INVALID_VALUE ); $this->addElement( @@ -62,9 +62,10 @@ class ExternalBackendForm extends Form array( 'label' => $this->translate('Filter Pattern'), 'description' => $this->translate( - 'The regular expression to use to strip specific parts off from usernames.' - . ' Leave empty if you do not want to strip off anything' + 'The filter to use to strip specific parts off from usernames.' + . ' Leave empty if you do not want to strip off anything.' ), + 'requirement' => $this->translate('The filter pattern must be a valid regular expression.'), 'validators' => array($callbackValidator) ) ); diff --git a/application/forms/Config/General/LoggingConfigForm.php b/application/forms/Config/General/LoggingConfigForm.php index 1c4e090c4..bd62f4490 100644 --- a/application/forms/Config/General/LoggingConfigForm.php +++ b/application/forms/Config/General/LoggingConfigForm.php @@ -66,6 +66,7 @@ class LoggingConfigForm extends Form 'description' => $this->translate( 'The name of the application by which to prefix syslog messages.' ), + 'requirement' => $this->translate('The application prefix must not contain whitespace.'), 'value' => 'icingaweb2', 'validators' => array( array( diff --git a/application/forms/Config/Resource/FileResourceForm.php b/application/forms/Config/Resource/FileResourceForm.php index b1bb0c6ae..184a5c18a 100644 --- a/application/forms/Config/Resource/FileResourceForm.php +++ b/application/forms/Config/Resource/FileResourceForm.php @@ -3,6 +3,7 @@ namespace Icinga\Forms\Config\Resource; +use Zend_Validate_Callback; use Icinga\Web\Form; /** @@ -42,13 +43,22 @@ class FileResourceForm extends Form 'validators' => array('ReadablePathValidator') ) ); + $callbackValidator = new Zend_Validate_Callback(function ($value) { + return @preg_match($value, '') !== false; + }); + $callbackValidator->setMessage( + $this->translate('"%value%" is not a valid regular expression.'), + Zend_Validate_Callback::INVALID_VALUE + ); $this->addElement( 'text', 'fields', array( 'required' => true, 'label' => $this->translate('Pattern'), - 'description' => $this->translate('The regular expression by which to identify columns') + 'description' => $this->translate('The pattern by which to identify columns.'), + 'requirement' => $this->translate('The column pattern must be a valid regular expression.'), + 'validators' => array($callbackValidator) ) ); diff --git a/library/Icinga/Web/Form.php b/library/Icinga/Web/Form.php index 3ebfdaf04..265da8420 100644 --- a/library/Icinga/Web/Form.php +++ b/library/Icinga/Web/Form.php @@ -157,7 +157,7 @@ class Form extends Zend_Form public static $defaultElementDecorators = array( array('ViewHelper', array('separator' => '')), array('Errors', array('separator' => '')), - array('Help'), + array('Help', array('placement' => 'PREPEND')), array('Label', array('separator' => '')), array('HtmlTag', array('tag' => 'div', 'class' => 'element')) ); @@ -622,7 +622,7 @@ class Form extends Zend_Form public function addSubForm(Zend_Form $form, $name = null, $order = null) { if ($form instanceof self) { - $form->removeDecorator('Form'); + $form->setDecorators(array('FormElements')); // TODO: Makes it difficult to customise subform decorators.. $form->setSubmitLabel(''); $form->setTokenDisabled(); $form->setUidDisabled(); @@ -743,7 +743,7 @@ class Form extends Zend_Form if (($cue = $this->getRequiredCue()) !== null && ($label = $element->getDecorator('label')) !== false) { $element->setLabel($this->getView()->escape($element->getLabel())); $label->setOption('escape', false); - $label->setOption('requiredSuffix', sprintf(' ', $cue)); + $label->setRequiredSuffix(sprintf(' ', $cue)); } } diff --git a/library/Icinga/Web/Form/Decorator/FormDescriptions.php b/library/Icinga/Web/Form/Decorator/FormDescriptions.php index 012c934a1..c8ba16ead 100644 --- a/library/Icinga/Web/Form/Decorator/FormDescriptions.php +++ b/library/Icinga/Web/Form/Decorator/FormDescriptions.php @@ -13,6 +13,29 @@ use Icinga\Web\Form; */ class FormDescriptions extends Zend_Form_Decorator_Abstract { + /** + * A list of element class names to be ignored when detecting which message to use to describe required elements + * + * @var array + */ + protected $blacklist; + + /** + * {@inheritdoc} + */ + public function __construct($options = null) + { + parent::__construct($options); + $this->blacklist = array( + 'Zend_Form_Element_Hidden', + 'Zend_Form_Element_Submit', + 'Zend_Form_Element_Button', + 'Icinga\Web\Form\Element\Note', + 'Icinga\Web\Form\Element\Button', + 'Icinga\Web\Form\Element\CsrfCounterMeasure' + ); + } + /** * Render form descriptions * @@ -32,9 +55,16 @@ class FormDescriptions extends Zend_Form_Decorator_Abstract return $content; } - $descriptions = $form->getDescriptions(); - if (($requiredDesc = $this->getRequiredDescription($form)) !== null) { - $descriptions[] = $requiredDesc; + $descriptions = $this->recurseForm($form, $entirelyRequired); + if ($entirelyRequired) { + $descriptions[] = $form->getView()->translate( + 'All fields are required and must be filled in to complete the form.' + ); + } elseif ($entirelyRequired === false) { + $descriptions[] = $form->getView()->translate(sprintf( + 'Required fields are marked with %s and must be filled in to complete the form.', + $form->getRequiredCue() + )); } if (empty($descriptions)) { @@ -60,53 +90,57 @@ class FormDescriptions extends Zend_Form_Decorator_Abstract } /** - * Return the description for the given form's required elements + * Recurse the given form and return the descriptions for it and all of its subforms * - * @param Form $form + * @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 string|null + * @return array */ - protected function getRequiredDescription(Form $form) + protected function recurseForm(Form $form, & $entirelyRequired = null, $elementsPassed = false) { - if (($cue = $form->getRequiredCue()) === null) { - return; - } - $requiredLabels = array(); - $entirelyRequired = true; - $partiallyRequired = false; - $blacklist = array( - 'Zend_Form_Element_Hidden', - 'Zend_Form_Element_Submit', - 'Zend_Form_Element_Button', - 'Icinga\Web\Form\Element\Note', - 'Icinga\Web\Form\Element\Button', - 'Icinga\Web\Form\Element\CsrfCounterMeasure' - ); - foreach ($form->getElements() as $element) { - if (! in_array($element->getType(), $blacklist)) { - if (! $element->isRequired()) { - $entirelyRequired = false; - } else { - $partiallyRequired = true; - if (($label = $element->getDecorator('label')) !== false) { - $requiredLabels[] = $label; + 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; + } } - if ($entirelyRequired && $partiallyRequired) { + $descriptions = array($form->getDescriptions()); + foreach ($form->getSubForms() as $subForm) { + $descriptions[] = $this->recurseForm($subForm, $entirelyRequired, $elementsPassed); + } + + if ($entirelyRequired) { foreach ($requiredLabels as $label) { $label->setRequiredSuffix(''); } - - return $form->getView()->translate('All fields are required and must be filled in to complete the form.'); - } elseif ($partiallyRequired) { - return $form->getView()->translate(sprintf( - 'Required fields are marked with %s and must be filled in to complete the form.', - $cue - )); } + + return call_user_func_array('array_merge', $descriptions); } } diff --git a/library/Icinga/Web/Form/Decorator/Help.php b/library/Icinga/Web/Form/Decorator/Help.php index a02443994..152d548af 100644 --- a/library/Icinga/Web/Form/Decorator/Help.php +++ b/library/Icinga/Web/Form/Decorator/Help.php @@ -76,18 +76,35 @@ class Help extends Zend_Form_Decorator_Abstract */ public function render($content = '') { - if ($content && ($description = $this->getElement()->getDescription()) !== null) { + $element = $this->getElement(); + $description = $element->getDescription(); + $requirement = $element->getAttrib('requirement'); + unset($element->requirement); + + $helpContent = ''; + if ($description || $requirement) { if ($this->accessible) { - $content = 'getDescriptionId() . '" class="sr-only">' . $description - . '' . $content; + . ($description && $requirement ? ' ' : '') + . $requirement + . ''; } - $content = $this->getView()->icon('help', $description, array('aria-hidden' => 'true')) . $content; + $helpContent = $this->getView()->icon( + 'help', + $description . ($description && $requirement ? ' ' : '') . $requirement, + array('aria-hidden' => $this->accessible ? 'true' : 'false') + ) . $helpContent; } - return $content; + switch ($this->getPlacement()) { + case self::APPEND: + return $content . $helpContent; + case self::PREPEND: + return $helpContent . $content; + } } }