Merge branch 'feature/provide-meaningful-form-error-messages-8415'
resolves #8415
This commit is contained in:
commit
6a3d1c665b
|
@ -141,7 +141,7 @@ class AuthenticationController extends ActionController
|
|||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->view->errorInfo = $e->getMessage();
|
||||
$this->view->form->addError($e->getMessage());
|
||||
}
|
||||
|
||||
$this->view->requiresExternalAuth = $triedOnlyExternalAuth && !$auth->isAuthenticated();
|
||||
|
|
|
@ -40,13 +40,22 @@ class ExternalBackendForm extends Form
|
|||
array(
|
||||
'pattern' => '/^[^\\[\\]:]+$/',
|
||||
'messages' => array(
|
||||
'regexNotMatch' => 'The backend name cannot contain \'[\', \']\' or \':\'.'
|
||||
'regexNotMatch' => $this->translate(
|
||||
'The backend name cannot contain \'[\', \']\' or \':\'.'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
$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',
|
||||
'strip_username_regexp',
|
||||
|
@ -56,11 +65,7 @@ class ExternalBackendForm extends Form
|
|||
'The regular expression to use to strip specific parts off from usernames.'
|
||||
. ' Leave empty if you do not want to strip off anything'
|
||||
),
|
||||
'validators' => array(
|
||||
new Zend_Validate_Callback(function ($value) {
|
||||
return @preg_match($value, '') !== false;
|
||||
})
|
||||
)
|
||||
'validators' => array($callbackValidator)
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
|
|
|
@ -5,7 +5,6 @@ namespace Icinga\Forms\Config\General;
|
|||
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Validator\WritablePathValidator;
|
||||
|
||||
class LoggingConfigForm extends Form
|
||||
{
|
||||
|
@ -75,7 +74,9 @@ class LoggingConfigForm extends Form
|
|||
array(
|
||||
'pattern' => '/^[^\W]+$/',
|
||||
'messages' => array(
|
||||
'regexNotMatch' => 'The application prefix cannot contain any whitespaces.'
|
||||
'regexNotMatch' => $this->translate(
|
||||
'The application prefix must not contain whitespace.'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -107,7 +108,7 @@ class LoggingConfigForm extends Form
|
|||
'label' => $this->translate('File path'),
|
||||
'description' => $this->translate('The full path to the log file to write messages to.'),
|
||||
'value' => '/var/log/icingaweb2/icingaweb2.log',
|
||||
'validators' => array(new WritablePathValidator())
|
||||
'validators' => array('WritablePathValidator')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
namespace Icinga\Forms\Config\Resource;
|
||||
|
||||
use Icinga\Web\Form;
|
||||
use Icinga\Web\Form\Validator\ReadablePathValidator;
|
||||
|
||||
/**
|
||||
* Form class for adding/modifying file resources
|
||||
|
@ -40,7 +39,7 @@ class FileResourceForm extends Form
|
|||
'required' => true,
|
||||
'label' => $this->translate('Filepath'),
|
||||
'description' => $this->translate('The filename to fetch information from'),
|
||||
'validators' => array(new ReadablePathValidator())
|
||||
'validators' => array('ReadablePathValidator')
|
||||
)
|
||||
);
|
||||
$this->addElement(
|
||||
|
|
|
@ -53,4 +53,30 @@ class String
|
|||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return all similar strings in $possibilites matching $string with the given minimum $similarity
|
||||
*
|
||||
* @param string $string
|
||||
* @param array $possibilities
|
||||
* @param float $similarity
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function findSimilar($string, array $possibilities, $similarity = 0.33)
|
||||
{
|
||||
if (empty($string)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$matches = array();
|
||||
foreach ($possibilities as $possibility) {
|
||||
$distance = levenshtein($string, $possibility);
|
||||
if ($distance / strlen($string) <= $similarity) {
|
||||
$matches[] = $possibility;
|
||||
}
|
||||
}
|
||||
|
||||
return $matches;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ use Icinga\Application\Icinga;
|
|||
use Icinga\Authentication\Manager;
|
||||
use Icinga\Security\SecurityException;
|
||||
use Icinga\Util\Translator;
|
||||
use Icinga\Web\Form\ErrorLabeller;
|
||||
use Icinga\Web\Form\Decorator\NoScriptApply;
|
||||
use Icinga\Web\Form\Element\CsrfCounterMeasure;
|
||||
|
||||
|
@ -520,6 +521,15 @@ class Form extends Zend_Form
|
|||
}
|
||||
|
||||
$el = parent::createElement($type, $name, $options);
|
||||
$el->setTranslator(new ErrorLabeller(array('element' => $el)));
|
||||
|
||||
$el->addPrefixPaths(array(
|
||||
array(
|
||||
'prefix' => 'Icinga\\Web\\Form\\Validator\\',
|
||||
'path' => Icinga::app()->getLibraryDir('Icinga/Web/Form/Validator'),
|
||||
'type' => $el::VALIDATE
|
||||
)
|
||||
));
|
||||
|
||||
if (($description = $el->getDescription()) !== null && ($label = $el->getDecorator('label')) !== false) {
|
||||
$label->setOptions(array(
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Form;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Zend_Translate_Adapter;
|
||||
use Zend_Validate_NotEmpty;
|
||||
use Icinga\Web\Form\Validator\DateTimeValidator;
|
||||
use Icinga\Web\Form\Validator\ReadablePathValidator;
|
||||
use Icinga\Web\Form\Validator\WritablePathValidator;
|
||||
|
||||
class ErrorLabeller extends Zend_Translate_Adapter
|
||||
{
|
||||
protected $messages;
|
||||
|
||||
public function __construct($options = array())
|
||||
{
|
||||
if (! isset($options['element'])) {
|
||||
throw new BadMethodCallException('Option "element" is missing');
|
||||
}
|
||||
|
||||
$this->messages = $this->createMessages($options['element']);
|
||||
}
|
||||
|
||||
public function isTranslated($messageId, $original = false, $locale = null)
|
||||
{
|
||||
return array_key_exists($messageId, $this->messages);
|
||||
}
|
||||
|
||||
public function translate($messageId, $locale = null)
|
||||
{
|
||||
if (array_key_exists($messageId, $this->messages)) {
|
||||
return $this->messages[$messageId];
|
||||
}
|
||||
|
||||
return $messageId;
|
||||
}
|
||||
|
||||
protected function createMessages($element)
|
||||
{
|
||||
$label = $element->getLabel();
|
||||
|
||||
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),
|
||||
DateTimeValidator::INVALID_DATETIME_FORMAT => sprintf(
|
||||
t('%s not in the expected format: %%value%%'),
|
||||
$label
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected function _loadTranslationData($data, $locale, array $options = array())
|
||||
{
|
||||
// nonsense, required as being abstract otherwise...
|
||||
}
|
||||
|
||||
public function toString()
|
||||
{
|
||||
return 'ErrorLabeller'; // nonsense, required as being abstract otherwise...
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ namespace Icinga\Web\Form\Validator;
|
|||
|
||||
use DateTime;
|
||||
use Zend_Validate_Abstract;
|
||||
use Icinga\Util\DateTimeFactory;
|
||||
|
||||
/**
|
||||
* Validator for date-and-time input controls
|
||||
|
@ -13,6 +14,21 @@ use Zend_Validate_Abstract;
|
|||
*/
|
||||
class DateTimeValidator extends Zend_Validate_Abstract
|
||||
{
|
||||
const INVALID_DATETIME_TYPE = 'invalidDateTimeType';
|
||||
const INVALID_DATETIME_FORMAT = 'invalidDateTimeFormat';
|
||||
|
||||
/**
|
||||
* The messages to write on differen error states
|
||||
*
|
||||
* @var array
|
||||
*
|
||||
* @see Zend_Validate_Abstract::$_messageTemplates‚
|
||||
*/
|
||||
protected $_messageTemplates = array(
|
||||
self::INVALID_DATETIME_TYPE => 'Invalid type given. Instance of DateTime or date/time string expected',
|
||||
self::INVALID_DATETIME_FORMAT => 'Date/time string not in the expected format: %value%'
|
||||
);
|
||||
|
||||
protected $local;
|
||||
|
||||
/**
|
||||
|
@ -38,14 +54,14 @@ class DateTimeValidator extends Zend_Validate_Abstract
|
|||
public function isValid($value, $context = null)
|
||||
{
|
||||
if (! $value instanceof DateTime && ! is_string($value)) {
|
||||
$this->_error(t('Invalid type given. Instance of DateTime or date/time string expected'));
|
||||
$this->_error(self::INVALID_DATETIME_TYPE);
|
||||
return false;
|
||||
}
|
||||
if (is_string($value)) {
|
||||
$format = $this->local === true ? 'Y-m-d\TH:i:s' : DateTime::RFC3339;
|
||||
$dateTime = DateTime::createFromFormat($format, $value);
|
||||
if ($dateTime === false || $dateTime->format($format) !== $value) {
|
||||
$this->_error(sprintf(t('Date/time string not in the expected format %s'), $format));
|
||||
$this->_error(self::INVALID_DATETIME_FORMAT, DateTimeFactory::create()->format($format));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Form\Validator;
|
||||
|
||||
use Zend_Validate_InArray;
|
||||
use Icinga\Util\String;
|
||||
|
||||
class InArray extends Zend_Validate_InArray
|
||||
{
|
||||
protected function _error($messageKey, $value = null)
|
||||
{
|
||||
if ($messageKey === static::NOT_IN_ARRAY) {
|
||||
$matches = String::findSimilar($this->_value, $this->_haystack);
|
||||
if (empty($matches)) {
|
||||
$this->_messages[$messageKey] = sprintf(t('"%s" is not in the list of allowed values.'), $this->_value);
|
||||
} else {
|
||||
$this->_messages[$messageKey] = sprintf(
|
||||
t('"%s" is not in the list of allowed values. Did you mean one of the following?: %s'),
|
||||
$this->_value,
|
||||
implode(', ', $matches)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
parent::_error($messageKey, $value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,9 @@ use Zend_Validate_Abstract;
|
|||
*/
|
||||
class ReadablePathValidator extends Zend_Validate_Abstract
|
||||
{
|
||||
const NOT_READABLE = 'notReadable';
|
||||
const DOES_NOT_EXIST = 'doesNotExist';
|
||||
|
||||
/**
|
||||
* The messages to write on different error states
|
||||
*
|
||||
|
@ -20,18 +23,10 @@ class ReadablePathValidator extends Zend_Validate_Abstract
|
|||
*
|
||||
* @see Zend_Validate_Abstract::$_messageTemplates‚
|
||||
*/
|
||||
protected $_messageTemplates;
|
||||
|
||||
/**
|
||||
* Initialize this validator
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->_messageTemplates = array(
|
||||
'NOT_READABLE' => t('Path is not readable'),
|
||||
'DOES_NOT_EXIST' => t('Path does not exist')
|
||||
);
|
||||
}
|
||||
protected $_messageTemplates = array(
|
||||
self::NOT_READABLE => 'Path is not readable',
|
||||
self::DOES_NOT_EXIST => 'Path does not exist'
|
||||
);
|
||||
|
||||
/**
|
||||
* Check whether the given value is a readable filepath
|
||||
|
@ -44,12 +39,13 @@ class ReadablePathValidator extends Zend_Validate_Abstract
|
|||
public function isValid($value, $context = null)
|
||||
{
|
||||
if (false === file_exists($value)) {
|
||||
$this->_error('DOES_NOT_EXIST');
|
||||
$this->_error(self::DOES_NOT_EXIST);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (false === is_readable($value)) {
|
||||
$this->_error('NOT_READABLE');
|
||||
$this->_error(self::NOT_READABLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -10,6 +10,9 @@ use Zend_Validate_Abstract;
|
|||
*/
|
||||
class WritablePathValidator extends Zend_Validate_Abstract
|
||||
{
|
||||
const NOT_WRITABLE = 'notWritable';
|
||||
const DOES_NOT_EXIST = 'doesNotExist';
|
||||
|
||||
/**
|
||||
* The messages to write on differen error states
|
||||
*
|
||||
|
@ -18,8 +21,8 @@ class WritablePathValidator extends Zend_Validate_Abstract
|
|||
* @see Zend_Validate_Abstract::$_messageTemplates‚
|
||||
*/
|
||||
protected $_messageTemplates = array(
|
||||
'NOT_WRITABLE' => 'Path is not writable',
|
||||
'DOES_NOT_EXIST' => 'Path does not exist'
|
||||
self::NOT_WRITABLE => 'Path is not writable',
|
||||
self::DOES_NOT_EXIST => 'Path does not exist'
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -53,7 +56,7 @@ class WritablePathValidator extends Zend_Validate_Abstract
|
|||
|
||||
$this->_setValue($value);
|
||||
if ($this->requireExistence && !file_exists($value)) {
|
||||
$this->_error('DOES_NOT_EXIST');
|
||||
$this->_error(self::DOES_NOT_EXIST);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -62,7 +65,8 @@ class WritablePathValidator extends Zend_Validate_Abstract
|
|||
) {
|
||||
return true;
|
||||
}
|
||||
$this->_error('NOT_WRITABLE');
|
||||
|
||||
$this->_error(self::NOT_WRITABLE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue