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) {
|
} catch (Exception $e) {
|
||||||
$this->view->errorInfo = $e->getMessage();
|
$this->view->form->addError($e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->requiresExternalAuth = $triedOnlyExternalAuth && !$auth->isAuthenticated();
|
$this->view->requiresExternalAuth = $triedOnlyExternalAuth && !$auth->isAuthenticated();
|
||||||
|
|
|
@ -40,12 +40,21 @@ class ExternalBackendForm extends Form
|
||||||
array(
|
array(
|
||||||
'pattern' => '/^[^\\[\\]:]+$/',
|
'pattern' => '/^[^\\[\\]:]+$/',
|
||||||
'messages' => array(
|
'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(
|
$this->addElement(
|
||||||
'text',
|
'text',
|
||||||
|
@ -56,11 +65,7 @@ class ExternalBackendForm extends Form
|
||||||
'The regular expression to use to strip specific parts off from usernames.'
|
'The regular expression to use to strip specific parts off from usernames.'
|
||||||
. ' Leave empty if you do not want to strip off anything'
|
. ' Leave empty if you do not want to strip off anything'
|
||||||
),
|
),
|
||||||
'validators' => array(
|
'validators' => array($callbackValidator)
|
||||||
new Zend_Validate_Callback(function ($value) {
|
|
||||||
return @preg_match($value, '') !== false;
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$this->addElement(
|
$this->addElement(
|
||||||
|
|
|
@ -5,7 +5,6 @@ namespace Icinga\Forms\Config\General;
|
||||||
|
|
||||||
use Icinga\Application\Logger;
|
use Icinga\Application\Logger;
|
||||||
use Icinga\Web\Form;
|
use Icinga\Web\Form;
|
||||||
use Icinga\Web\Form\Validator\WritablePathValidator;
|
|
||||||
|
|
||||||
class LoggingConfigForm extends Form
|
class LoggingConfigForm extends Form
|
||||||
{
|
{
|
||||||
|
@ -75,7 +74,9 @@ class LoggingConfigForm extends Form
|
||||||
array(
|
array(
|
||||||
'pattern' => '/^[^\W]+$/',
|
'pattern' => '/^[^\W]+$/',
|
||||||
'messages' => array(
|
'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'),
|
'label' => $this->translate('File path'),
|
||||||
'description' => $this->translate('The full path to the log file to write messages to.'),
|
'description' => $this->translate('The full path to the log file to write messages to.'),
|
||||||
'value' => '/var/log/icingaweb2/icingaweb2.log',
|
'value' => '/var/log/icingaweb2/icingaweb2.log',
|
||||||
'validators' => array(new WritablePathValidator())
|
'validators' => array('WritablePathValidator')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
namespace Icinga\Forms\Config\Resource;
|
namespace Icinga\Forms\Config\Resource;
|
||||||
|
|
||||||
use Icinga\Web\Form;
|
use Icinga\Web\Form;
|
||||||
use Icinga\Web\Form\Validator\ReadablePathValidator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form class for adding/modifying file resources
|
* Form class for adding/modifying file resources
|
||||||
|
@ -40,7 +39,7 @@ class FileResourceForm extends Form
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'label' => $this->translate('Filepath'),
|
'label' => $this->translate('Filepath'),
|
||||||
'description' => $this->translate('The filename to fetch information from'),
|
'description' => $this->translate('The filename to fetch information from'),
|
||||||
'validators' => array(new ReadablePathValidator())
|
'validators' => array('ReadablePathValidator')
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$this->addElement(
|
$this->addElement(
|
||||||
|
|
|
@ -53,4 +53,30 @@ class String
|
||||||
|
|
||||||
return $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\Authentication\Manager;
|
||||||
use Icinga\Security\SecurityException;
|
use Icinga\Security\SecurityException;
|
||||||
use Icinga\Util\Translator;
|
use Icinga\Util\Translator;
|
||||||
|
use Icinga\Web\Form\ErrorLabeller;
|
||||||
use Icinga\Web\Form\Decorator\NoScriptApply;
|
use Icinga\Web\Form\Decorator\NoScriptApply;
|
||||||
use Icinga\Web\Form\Element\CsrfCounterMeasure;
|
use Icinga\Web\Form\Element\CsrfCounterMeasure;
|
||||||
|
|
||||||
|
@ -520,6 +521,15 @@ class Form extends Zend_Form
|
||||||
}
|
}
|
||||||
|
|
||||||
$el = parent::createElement($type, $name, $options);
|
$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) {
|
if (($description = $el->getDescription()) !== null && ($label = $el->getDecorator('label')) !== false) {
|
||||||
$label->setOptions(array(
|
$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 DateTime;
|
||||||
use Zend_Validate_Abstract;
|
use Zend_Validate_Abstract;
|
||||||
|
use Icinga\Util\DateTimeFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validator for date-and-time input controls
|
* Validator for date-and-time input controls
|
||||||
|
@ -13,6 +14,21 @@ use Zend_Validate_Abstract;
|
||||||
*/
|
*/
|
||||||
class DateTimeValidator extends 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;
|
protected $local;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,14 +54,14 @@ class DateTimeValidator extends Zend_Validate_Abstract
|
||||||
public function isValid($value, $context = null)
|
public function isValid($value, $context = null)
|
||||||
{
|
{
|
||||||
if (! $value instanceof DateTime && ! is_string($value)) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
if (is_string($value)) {
|
if (is_string($value)) {
|
||||||
$format = $this->local === true ? 'Y-m-d\TH:i:s' : DateTime::RFC3339;
|
$format = $this->local === true ? 'Y-m-d\TH:i:s' : DateTime::RFC3339;
|
||||||
$dateTime = DateTime::createFromFormat($format, $value);
|
$dateTime = DateTime::createFromFormat($format, $value);
|
||||||
if ($dateTime === false || $dateTime->format($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;
|
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
|
class ReadablePathValidator extends Zend_Validate_Abstract
|
||||||
{
|
{
|
||||||
|
const NOT_READABLE = 'notReadable';
|
||||||
|
const DOES_NOT_EXIST = 'doesNotExist';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The messages to write on different error states
|
* The messages to write on different error states
|
||||||
*
|
*
|
||||||
|
@ -20,18 +23,10 @@ class ReadablePathValidator extends Zend_Validate_Abstract
|
||||||
*
|
*
|
||||||
* @see Zend_Validate_Abstract::$_messageTemplates‚
|
* @see Zend_Validate_Abstract::$_messageTemplates‚
|
||||||
*/
|
*/
|
||||||
protected $_messageTemplates;
|
protected $_messageTemplates = array(
|
||||||
|
self::NOT_READABLE => 'Path is not readable',
|
||||||
/**
|
self::DOES_NOT_EXIST => 'Path does not exist'
|
||||||
* Initialize this validator
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->_messageTemplates = array(
|
|
||||||
'NOT_READABLE' => t('Path is not readable'),
|
|
||||||
'DOES_NOT_EXIST' => t('Path does not exist')
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the given value is a readable filepath
|
* 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)
|
public function isValid($value, $context = null)
|
||||||
{
|
{
|
||||||
if (false === file_exists($value)) {
|
if (false === file_exists($value)) {
|
||||||
$this->_error('DOES_NOT_EXIST');
|
$this->_error(self::DOES_NOT_EXIST);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (false === is_readable($value)) {
|
if (false === is_readable($value)) {
|
||||||
$this->_error('NOT_READABLE');
|
$this->_error(self::NOT_READABLE);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -10,6 +10,9 @@ use Zend_Validate_Abstract;
|
||||||
*/
|
*/
|
||||||
class WritablePathValidator extends 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
|
* The messages to write on differen error states
|
||||||
*
|
*
|
||||||
|
@ -18,8 +21,8 @@ class WritablePathValidator extends Zend_Validate_Abstract
|
||||||
* @see Zend_Validate_Abstract::$_messageTemplates‚
|
* @see Zend_Validate_Abstract::$_messageTemplates‚
|
||||||
*/
|
*/
|
||||||
protected $_messageTemplates = array(
|
protected $_messageTemplates = array(
|
||||||
'NOT_WRITABLE' => 'Path is not writable',
|
self::NOT_WRITABLE => 'Path is not writable',
|
||||||
'DOES_NOT_EXIST' => 'Path does not exist'
|
self::DOES_NOT_EXIST => 'Path does not exist'
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,7 +56,7 @@ class WritablePathValidator extends Zend_Validate_Abstract
|
||||||
|
|
||||||
$this->_setValue($value);
|
$this->_setValue($value);
|
||||||
if ($this->requireExistence && !file_exists($value)) {
|
if ($this->requireExistence && !file_exists($value)) {
|
||||||
$this->_error('DOES_NOT_EXIST');
|
$this->_error(self::DOES_NOT_EXIST);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +65,8 @@ class WritablePathValidator extends Zend_Validate_Abstract
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
$this->_error('NOT_WRITABLE');
|
|
||||||
|
$this->_error(self::NOT_WRITABLE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue