Merge branch 'feature/exhaustive-form-descriptions-7947'

resolves #7947
This commit is contained in:
Johannes Meyer 2015-03-06 09:51:09 +01:00
commit ff6658324a
6 changed files with 112 additions and 49 deletions

View File

@ -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)
)
);

View File

@ -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(

View File

@ -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)
)
);

View File

@ -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(' <span aria-hidden="true">%s</span>', $cue));
$label->setRequiredSuffix(sprintf(' <span aria-hidden="true">%s</span>', $cue));
}
}

View File

@ -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);
}
}

View File

@ -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 = '<span id="'
$helpContent = '<span id="'
. $this->getDescriptionId()
. '" class="sr-only">'
. $description
. '</span>' . $content;
. ($description && $requirement ? ' ' : '')
. $requirement
. '</span>';
}
$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;
}
}
}