N°6414 Validator refactoring

New AbstractValidator class, with new method Validate
All existing validators are now children of AbstractRegexpValidator
Handle validators JS counterparts in renderers : only regexp validators are implemented client side
This commit is contained in:
Pierre Goiffon
2023-06-29 10:41:51 +02:00
parent 52049b7837
commit 6606af71ff
26 changed files with 348 additions and 247 deletions

View File

@@ -9,9 +9,8 @@ use Combodo\iTop\Application\UI\Links\Set\BlockLinkSetDisplayAsProperty;
use Combodo\iTop\Form\Field\LabelField;
use Combodo\iTop\Form\Field\TextAreaField;
use Combodo\iTop\Form\Form;
use Combodo\iTop\Form\Validator\CustomRegexpValidator;
use Combodo\iTop\Form\Validator\LinkedSetValidator;
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
use Combodo\iTop\Form\Validator\Validator;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
use Combodo\iTop\Service\Links\LinkSetModel;
@@ -1123,7 +1122,7 @@ abstract class AttributeDefinition
// Validation pattern
if ($this->GetValidationPattern() !== '') {
$oFormField->AddValidator(new Validator($this->GetValidationPattern()));
$oFormField->AddValidator(new CustomRegexpValidator($this->GetValidationPattern()));
}
// Description
@@ -7295,6 +7294,7 @@ class AttributeExternalKey extends AttributeDBFieldVoid
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
/** @var \Combodo\iTop\Form\Field\Field $oFormField */
if ($oFormField === null) {
// Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
$sFormFieldClass = static::GetFormFieldClass();
@@ -7325,19 +7325,12 @@ class AttributeExternalKey extends AttributeDBFieldVoid
}
});
}
else
{
else {
$oSearch = DBSearch::FromOQL($this->GetValuesDef()->GetFilterExpression());
$oSearch->SetInternalParams(array('this' => $oObject));
$oFormField->SetSearch($oSearch);
}
// If ExtKey is mandatory, we add a validator to ensure that the value 0 is not selected
if ($oObject->GetAttributeFlags($this->GetCode()) & OPT_ATT_MANDATORY)
{
$oFormField->AddValidator(new NotEmptyExtKeyValidator());
}
parent::MakeFormField($oObject, $oFormField);
return $oFormField;

View File

@@ -2,6 +2,11 @@
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
exit(1);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f::getLoader();

View File

@@ -149,7 +149,7 @@ class ClassLoader
/**
* @return string[] Array of classname => path
* @psalm-var array<string, string>
* @psalm-return array<string, string>
*/
public function getClassMap()
{

View File

@@ -2,7 +2,7 @@
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
@@ -415,6 +415,9 @@ return array(
'Combodo\\iTop\\Form\\Field\\UrlField' => $baseDir . '/sources/Form/Field/UrlField.php',
'Combodo\\iTop\\Form\\Form' => $baseDir . '/sources/Form/Form.php',
'Combodo\\iTop\\Form\\FormManager' => $baseDir . '/sources/Form/FormManager.php',
'Combodo\\iTop\\Form\\Validator\\AbstractRegexpValidator' => $baseDir . '/sources/Form/Validator/AbstractRegexpValidator.php',
'Combodo\\iTop\\Form\\Validator\\AbstractValidator' => $baseDir . '/sources/Form/Validator/AbstractValidator.php',
'Combodo\\iTop\\Form\\Validator\\CustomRegexpValidator' => $baseDir . '/sources/Form/Validator/CustomRegexpValidator.php',
'Combodo\\iTop\\Form\\Validator\\IntegerValidator' => $baseDir . '/sources/Form/Validator/IntegerValidator.php',
'Combodo\\iTop\\Form\\Validator\\LinkedSetValidator' => $baseDir . '/sources/Form/Validator/LinkedSetValidator.php',
'Combodo\\iTop\\Form\\Validator\\MandatoryValidator' => $baseDir . '/sources/Form/Validator/MandatoryValidator.php',

View File

@@ -2,23 +2,23 @@
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
'23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => $vendorDir . '/symfony/polyfill-php73/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'c9d07b32a2e02bc0fc582d4f0c1b56cc' => $vendorDir . '/laminas/laminas-servicemanager/src/autoload.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'c9d07b32a2e02bc0fc582d4f0c1b56cc' => $vendorDir . '/laminas/laminas-servicemanager/src/autoload.php',
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
);

View File

@@ -2,7 +2,7 @@
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(

View File

@@ -2,7 +2,7 @@
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(

View File

@@ -25,33 +25,20 @@ class ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f', 'loadClassLoader'));
$includePaths = require __DIR__ . '/include_paths.php';
$includePaths[] = get_include_path();
set_include_path(implode(PATH_SEPARATOR, $includePaths));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::getInitializer($loader));
} else {
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->setClassMapAuthoritative(true);
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
$includeFiles = \Composer\Autoload\ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f::$files;
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire7f81b4a2a468a061c306af5e447a9a9f($fileIdentifier, $file);
}
@@ -60,11 +47,16 @@ class ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f
}
}
/**
* @param string $fileIdentifier
* @param string $file
* @return void
*/
function composerRequire7f81b4a2a468a061c306af5e447a9a9f($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}

View File

@@ -7,21 +7,21 @@ namespace Composer\Autoload;
class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
{
public static $files = array (
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
'23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php',
'0d59ee240a4cd96ddbb4ff164fccea4d' => __DIR__ . '/..' . '/symfony/polyfill-php73/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'c9d07b32a2e02bc0fc582d4f0c1b56cc' => __DIR__ . '/..' . '/laminas/laminas-servicemanager/src/autoload.php',
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
'c9d07b32a2e02bc0fc582d4f0c1b56cc' => __DIR__ . '/..' . '/laminas/laminas-servicemanager/src/autoload.php',
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
);
public static $prefixLengthsPsr4 = array (
@@ -779,6 +779,9 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Form\\Field\\UrlField' => __DIR__ . '/../..' . '/sources/Form/Field/UrlField.php',
'Combodo\\iTop\\Form\\Form' => __DIR__ . '/../..' . '/sources/Form/Form.php',
'Combodo\\iTop\\Form\\FormManager' => __DIR__ . '/../..' . '/sources/Form/FormManager.php',
'Combodo\\iTop\\Form\\Validator\\AbstractRegexpValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/AbstractRegexpValidator.php',
'Combodo\\iTop\\Form\\Validator\\AbstractValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/AbstractValidator.php',
'Combodo\\iTop\\Form\\Validator\\CustomRegexpValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/CustomRegexpValidator.php',
'Combodo\\iTop\\Form\\Validator\\IntegerValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/IntegerValidator.php',
'Combodo\\iTop\\Form\\Validator\\LinkedSetValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/LinkedSetValidator.php',
'Combodo\\iTop\\Form\\Validator\\MandatoryValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/MandatoryValidator.php',

View File

@@ -2,7 +2,7 @@
// include_paths.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(

View File

@@ -8,8 +8,8 @@
namespace Combodo\iTop\Form\Field;
use Closure;
use Combodo\iTop\Form\Validator\AbstractValidator;
use Combodo\iTop\Form\Validator\MandatoryValidator;
use Combodo\iTop\Form\Validator\Validator;
/**
* Description of Field
@@ -68,7 +68,7 @@ abstract class Field
protected $bMandatory;
/** @var string */
protected $sDisplayMode;
/** @var array */
/** @var AbstractValidator[] */
protected $aValidators;
/**
* @var bool
@@ -228,10 +228,6 @@ abstract class Field
return $this->sDisplayMode;
}
/**
*
* @return array
*/
public function GetValidators()
{
return $this->aValidators;
@@ -352,38 +348,46 @@ abstract class Field
* Setting the value will automatically add/remove a MandatoryValidator to the Field
*
* @param boolean $bMandatory
*
* @return $this
*/
public function SetMandatory(bool $bMandatory)
{
// Before changing the property, we check if it was already mandatory. If not, we had the mandatory validator
if ($bMandatory && !$this->bMandatory)
{
$this->AddValidator(new MandatoryValidator());
if ($bMandatory && !$this->bMandatory) {
$this->AddValidator($this->GetMandatoryValidatorInstance());
}
if (!$bMandatory)
{
foreach ($this->aValidators as $iKey => $oValue)
{
if ($oValue::Getname() === MandatoryValidator::GetName())
{
if (false === $bMandatory) {
foreach ($this->aValidators as $iKey => $oValue) {
if ($oValue instanceof MandatoryValidator) {
unset($this->aValidators[$iKey]);
}
}
}
$this->bMandatory = $bMandatory;
return $this;
}
/**
* @return AbstractValidator
* @since 3.1.0 N°6414
*/
protected function GetMandatoryValidatorInstance(): AbstractValidator
{
return new MandatoryValidator();
}
/**
* Sets if the field is must change or not.
* Note: This not implemented yet! Just a pre-conception for CaseLogField
*
* @todo Implement
* @param boolean $bMustChange
*
* @return $this
* @todo Implement
*/
public function SetMustChange(bool $bMustChange)
{
@@ -477,30 +481,27 @@ abstract class Field
}
/**
*
* @param \Combodo\iTop\Form\Validator\Validator $oValidator
* @param AbstractValidator $oValidator
* @return $this
*/
public function AddValidator(Validator $oValidator)
public function AddValidator(AbstractValidator $oValidator)
{
$this->aValidators[] = $oValidator;
return $this;
}
/**
*
* @param \Combodo\iTop\Form\Validator\Validator $oValidator
* @return $this
*/
public function RemoveValidator(Validator $oValidator)
{
foreach ($this->aValidators as $iKey => $oValue)
{
if ($oValue === $oValidator)
public function RemoveValidator(AbstractValidator $oValidator)
{
foreach ($this->aValidators as $iKey => $oValue) {
if ($oValue === $oValidator) {
unset($this->aValidators[$iKey]);
}
}
return $this;
}
@@ -574,10 +575,13 @@ abstract class Field
}
/**
* Checks the validators to see if the field's current value is valid.
* Then sets $bValid and $aErrorMessages.
* Validates the field using the validators set.
*
* @return boolean
* Before overriding this method in children classes, try to add a custom validator !
*
* @uses GetValidators()
* @uses SetValid()
* @uses AddErrorMessage()
*/
public function Validate()
{
@@ -592,9 +596,12 @@ abstract class Field
if (!$bEmpty || $this->GetMandatory()) {
foreach ($this->GetValidators() as $oValidator) {
if (!preg_match($oValidator->GetRegExp(true), $this->GetCurrentValue())) {
[$bIsFieldValid, $sValidationErrorMessage] = $oValidator->Validate($this->GetCurrentValue());
/** @var bool $bIsFieldValid */
if (false === $bIsFieldValid) {
$this->SetValid(false);
$this->AddErrorMessage($oValidator->GetErrorMessage());
$this->AddErrorMessage($sValidationErrorMessage);
}
}
}

View File

@@ -22,6 +22,7 @@ namespace Combodo\iTop\Form\Field;
use BinaryExpression;
use Closure;
use Combodo\iTop\Form\Validator\AbstractValidator;
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
use ContextTag;
use DBObjectSet;
@@ -168,38 +169,15 @@ class SelectObjectField extends Field
return $this;
}
/**
* @inheritDoc
*/
public function SetMandatory(bool $bMandatory)
protected function GetMandatoryValidatorInstance(): AbstractValidator
{
// Before changing the property, we check if it was already mandatory. If not, we had the mandatory validator
if ($bMandatory && !$this->bMandatory)
{
$this->AddValidator(new NotEmptyExtKeyValidator());
}
if (!$bMandatory)
{
foreach ($this->aValidators as $iKey => $oValue)
{
if ($oValue::Getname() === NotEmptyExtKeyValidator::GetName())
{
unset($this->aValidators[$iKey]);
}
}
}
$this->bMandatory = $bMandatory;
return $this;
return new NotEmptyExtKeyValidator();
}
/**
* @return \DBSearch
*/
public function GetSearch()
{
public function GetSearch() {
return $this->oSearch;
}

View File

@@ -0,0 +1,56 @@
<?php
/*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Form\Validator;
/**
* @since 3.1.0 N°6414
*/
abstract class AbstractRegexpValidator extends AbstractValidator
{
public const VALIDATOR_NAME = 'abstract_regexp';
/** @var string Override in children classes to set regexp to use for validation */
public const DEFAULT_REGEXP = '';
protected string $sRegExp;
public function __construct(?string $sErrorMessage = null)
{
$this->sRegExp = static::DEFAULT_REGEXP;
parent::__construct($sErrorMessage);
}
public function Validate($value): array
{
if (is_null($value)) {
$value = ''; // calling preg_match with null as subject is deprecated since PHP 8.1
}
if (preg_match($this->GetRegExp(true), $value)) {
return [true, null];
}
return [false, $this->sErrorMessage];
}
/**
* Returns the regular expression of the validator.
*
* @param boolean $bWithSlashes If true, surrounds $sRegExp with '/'. Used with preg_match & co
*
* @return string
*/
public function GetRegExp($bWithSlashes = false)
{
if ($bWithSlashes) {
$sRet = '/'.str_replace('/', '\\/', $this->sRegExp).'/';
} else {
$sRet = $this->sRegExp;
}
return $sRet;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Form\Validator;
use utils;
/**
* @since 3.1.0 N°6414 Field Validators refactoring
*/
abstract class AbstractValidator
{
public const VALIDATOR_NAME = 'abstract';
/** @var string Default message / dict key when an error occurs, if no custom one is specified in the constructor */
public const DEFAULT_ERROR_MESSAGE = 'Core:Validator:Default';
/** @var string message / dict key to use when an error occurs */
protected string $sErrorMessage;
public function __construct(?string $sErrorMessage)
{
if (false === utils::IsNullOrEmptyString($sErrorMessage)) {
$this->sErrorMessage = $sErrorMessage;
} else {
$this->sErrorMessage = self::DEFAULT_ERROR_MESSAGE;
}
}
/**
* @param mixed $value
*
* @return array<bool,?string> boolean valid for valid / invalid, and error message if invalid
*/
abstract public function Validate($value): array;
/**
* Name to use for JS counterparts
*
* @return string
*/
public static function GetName()
{
return static::VALIDATOR_NAME;
}
/**
* Still used in \Combodo\iTop\Renderer\Console\FieldRenderer\ConsoleSelectObjectFieldRenderer::Render :(
*
* @return string
*/
public function GetErrorMessage()
{
return $this->sErrorMessage;
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Form\Validator;
/**
* @since 3.1.0 N°6414
*/
class CustomRegexpValidator extends AbstractRegexpValidator
{
public const VALIDATOR_NAME = 'custom_regexp';
public function __construct(string $sRegExp, ?string $sErrorMessage = null)
{
parent::__construct($sErrorMessage);
$this->sRegExp = $sRegExp; // must be done after parent constructor call !
}
}

View File

@@ -24,10 +24,9 @@ namespace Combodo\iTop\Form\Validator;
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
*/
class IntegerValidator extends Validator
class IntegerValidator extends AbstractRegexpValidator
{
const VALIDATOR_NAME = 'integer';
const DEFAULT_REGEXP = '^[0-9]+$';
const DEFAULT_ERROR_MESSAGE = 'Core:Validator:MustBeInteger';
public const VALIDATOR_NAME = 'integer';
public const DEFAULT_REGEXP = '^[0-9]+$';
public const DEFAULT_ERROR_MESSAGE = 'Core:Validator:MustBeInteger';
}

View File

@@ -24,13 +24,7 @@ namespace Combodo\iTop\Form\Validator;
*
* @since 3.1
*/
class LinkedSetValidator extends Validator
class LinkedSetValidator extends AbstractRegexpValidator
{
const VALIDATOR_NAME = 'LinkedSetValidator';
/** @inheritdoc */
public static function GetName()
{
return static::VALIDATOR_NAME;
}
public const VALIDATOR_NAME = 'LinkedSetValidator';
}

View File

@@ -26,10 +26,10 @@ namespace Combodo\iTop\Form\Validator;
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
*/
class MandatoryValidator extends Validator
class MandatoryValidator extends AbstractRegexpValidator
{
const VALIDATOR_NAME = 'mandatory';
const DEFAULT_REGEXP = '.*\S.*';
const DEFAULT_ERROR_MESSAGE = 'Core:Validator:Mandatory';
public const VALIDATOR_NAME = 'mandatory';
public const DEFAULT_REGEXP = '.*\S.*';
public const DEFAULT_ERROR_MESSAGE = 'Core:Validator:Mandatory';
}

View File

@@ -24,10 +24,9 @@ namespace Combodo\iTop\Form\Validator;
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
*/
class NotEmptyExtKeyValidator extends Validator
class NotEmptyExtKeyValidator extends MandatoryValidator
{
const VALIDATOR_NAME = 'notemptyextkey';
const DEFAULT_REGEXP = '^[0-9]*[1-9][0-9]*$';
const DEFAULT_ERROR_MESSAGE = 'Core:Validator:MustSelectOne';
public const VALIDATOR_NAME = 'notemptyextkey';
public const DEFAULT_REGEXP = '^[0-9]*[1-9][0-9]*$';
public const DEFAULT_ERROR_MESSAGE = 'Core:Validator:MustSelectOne';
}

View File

@@ -19,99 +19,22 @@
namespace Combodo\iTop\Form\Validator;
use DeprecatedCallsLog;
/**
* Description of Validator
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @deprecated 3.1.0 N°6414 use {@see \Combodo\iTop\Form\Validator\CustomRegexpValidator} instead
*/
class Validator
class Validator extends CustomRegexpValidator
{
const VALIDATOR_NAME = 'expression';
const DEFAULT_REGEXP = '';
const DEFAULT_ERROR_MESSAGE = 'Core:Validator:Default';
protected $sRegExp;
protected $sErrorMessage;
public static function GetName()
{
return static::VALIDATOR_NAME;
}
/**
*
* @param string $sRegExp
* @param string $sErrorMessage
*/
public function __construct($sRegExp = null, $sErrorMessage = null)
{
$this->sRegExp = ($sRegExp === null) ? static::DEFAULT_REGEXP : $sRegExp;
$this->sErrorMessage = ($sErrorMessage === null) ? static::DEFAULT_ERROR_MESSAGE : $sErrorMessage;
$this->ComputeConstraints();
}
// cannot use DeprecatedCallsLog::NotifyDeprecatedFile as it would trigger an exception on dev env
// because all autoloader files are loaded during MetaModel::Startup (calling \Combodo\iTop\Service\Events\EventService::InitService calling \utils::GetClassesForInterface)
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('3.1.0 N°6414 use '.CustomRegexpValidator::class.' instead');
/**
* Returns the regular expression of the validator.
*
* @param boolean $bWithSlashes If true, surrounds $sRegExp with '/'. Used with preg_match & co
* @return string
*/
public function GetRegExp($bWithSlashes = false)
{
if ($bWithSlashes)
{
$sRet = '/' . str_replace('/', '\\/', $this->sRegExp) . '/';
parent::__construct($sRegExp, $sErrorMessage);
}
else
{
$sRet = $this->sRegExp;
}
return $sRet;
}
public function GetErrorMessage()
{
return $this->sErrorMessage;
}
public function SetRegExp($sRegExp)
{
$this->sRegExp = $sRegExp;
$this->ComputeConstraints();
return $this;
}
public function SetErrorMessage($sErrorMessage)
{
$this->sErrorMessage = $sErrorMessage;
$this->ComputeConstraints();
return $this;
}
/**
* Computes the regular expression and error message when changing constraints on the validator.
* Should be called in the validator's setters.
*/
public function ComputeConstraints()
{
$this->ComputeRegularExpression();
$this->ComputeErrorMessage();
}
/**
* Computes the regular expression when changing constraints on the validator.
*/
public function ComputeRegularExpression()
{
// Overload when necessary
}
/**
* Computes the error message when changing constraints on the validator.
*/
public function ComputeErrorMessage()
{
// Overload when necessary
}
}

View File

@@ -28,8 +28,8 @@ use Combodo\iTop\Form\Field\DateTimeField;
use Combodo\iTop\Form\Field\Field;
use Combodo\iTop\Form\Field\MultipleChoicesField;
use Combodo\iTop\Form\Field\TextAreaField;
use Combodo\iTop\Form\Validator\AbstractRegexpValidator;
use Combodo\iTop\Form\Validator\MandatoryValidator;
use Combodo\iTop\Form\Validator\Validator;
use Combodo\iTop\Renderer\RenderingOutput;
use Dict;
use InlineImage;
@@ -458,9 +458,14 @@ EOF
// JS Form field widget construct
$aValidators = array();
foreach ($this->oField->GetValidators() as $oValidator) {
if (false === ($oValidator instanceof AbstractRegexpValidator)) {
// no JS counterpart, so skipping !
continue;
}
$aValidators[$oValidator::GetName()] = array(
'reg_exp' => $oValidator->GetRegExp(),
'message' => Dict::S($oValidator->GetErrorMessage())
'message' => Dict::S($oValidator->GetErrorMessage()),
);
}
@@ -758,7 +763,7 @@ JS
foreach ($oField->GetValidators() as $oValidator) {
// Validator
if (get_class($oValidator) === Validator::class) {
if ($oValidator instanceof AbstractRegexpValidator) {
if (!($oField instanceof DateField || $oField instanceof DateTimeField)) { // unrecognized regular expression
$sTags .= ' pattern="'.$oValidator->GetRegExp().'" ';
}

View File

@@ -27,7 +27,9 @@ use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectOptionUIBlockF
use Combodo\iTop\Application\UI\Base\Component\Input\SelectUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
use Combodo\iTop\Form\Field\SelectObjectField;
use Combodo\iTop\Form\Validator\AbstractRegexpValidator;
use Combodo\iTop\Form\Validator\MandatoryValidator;
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Renderer\FieldRenderer;
use DBObjectSet;
@@ -245,16 +247,19 @@ JS
$oOutput->AddHtml((BlockRenderer::RenderBlockTemplates($oBlock)));
// JS Form field widget construct
$aValidators = array();
foreach ($this->oField->GetValidators() as $oValidator)
{
if ($oValidator::GetName() == 'notemptyextkey')
{
foreach ($this->oField->GetValidators() as $oValidator) {
if (false === ($oValidator instanceof AbstractRegexpValidator)) {
// no JS counterpart, so skipping !
continue;
}
if ($oValidator instanceof NotEmptyExtKeyValidator) {
// The autocomplete widget returns an empty string if the value is undefined (and the select has been aligned with this behavior)
$oValidator = new MandatoryValidator();
}
$aValidators[$oValidator::GetName()] = array(
'reg_exp' => $oValidator->GetRegExp(),
'message' => Dict::S($oValidator->GetErrorMessage())
'message' => Dict::S($oValidator->GetErrorMessage()),
);
}
$sValidators = json_encode($aValidators);

View File

@@ -33,6 +33,7 @@ use Combodo\iTop\Application\UI\Base\Component\Input\TextArea;
use Combodo\iTop\Application\UI\Base\Component\Text\Text;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
use Combodo\iTop\Form\Field\TextAreaField;
use Combodo\iTop\Form\Validator\AbstractRegexpValidator;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Renderer\FieldRenderer;
use DateTimeFormat;
@@ -433,16 +434,20 @@ EOF
// JS Form field widget construct
$aValidators = array();
foreach ($this->oField->GetValidators() as $oValidator)
{
foreach ($this->oField->GetValidators() as $oValidator) {
if (false === ($oValidator instanceof AbstractRegexpValidator)) {
// no JS counterpart, so skipping !
continue;
}
$aValidators[$oValidator::GetName()] = array(
'reg_exp' => $oValidator->GetRegExp(),
'message' => Dict::S($oValidator->GetErrorMessage())
'message' => Dict::S($oValidator->GetErrorMessage()),
);
}
$sValidators = json_encode($aValidators);
$sFormFieldOptions =
<<<EOF
<<<EOF
{
validators: $sValidators,
on_validation_callback: function(me, oResult) {

View File

@@ -21,6 +21,7 @@
namespace Combodo\iTop\Renderer;
use Combodo\iTop\Form\Field\Field;
use Combodo\iTop\Form\Validator\AbstractRegexpValidator;
use Dict;
use utils;
@@ -84,9 +85,14 @@ abstract class FieldRenderer
$aValidators = array();
foreach ($this->oField->GetValidators() as $oValidator)
{
if (false === ($oValidator instanceof AbstractRegexpValidator)) {
// no JS counterpart, so skipping !
continue;
}
$aValidators[$oValidator::GetName()] = array(
'reg_exp' => $oValidator->GetRegExp(),
'message' => Dict::S($oValidator->GetErrorMessage())
'message' => Dict::S($oValidator->GetErrorMessage()),
);
}
// - Formatting options

View File

@@ -4,12 +4,12 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Test\UnitTest\Sources\Form;
namespace Combodo\iTop\Test\UnitTest\Sources\Form\Field;
use Combodo\iTop\Form\Field\StringField;
use Combodo\iTop\Form\Validator\CustomRegexpValidator;
use Combodo\iTop\Form\Validator\IntegerValidator;
use Combodo\iTop\Form\Validator\MandatoryValidator;
use Combodo\iTop\Form\Validator\Validator;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
class FieldTest extends ItopTestCase
@@ -18,15 +18,18 @@ class FieldTest extends ItopTestCase
{
$oField = new StringField('test');
$oField->SetCurrentValue('toto@johny.invalid');
$oDumbEmailValidator = new Validator('\d+@\d+\.\d{2,3}');
$sDumbEmailValidatorErrorMessage = 'dumb email validator error message';
$oDumbEmailValidator = new CustomRegexpValidator('\d+@\d+\.\d{2,3}', $sDumbEmailValidatorErrorMessage);
$oField->AddValidator($oDumbEmailValidator);
$bIsFieldValid = $oField->Validate();
$this->assertFalse($bIsFieldValid);
$this->assertCount(1, $oField->GetErrorMessages());
$this->assertSame($sDumbEmailValidatorErrorMessage, $oField->GetErrorMessages()[0]);
/** @noinspection PhpRedundantOptionalArgumentInspection */
$oField->SetValidationDisabled(true);
$bIsFieldValidWithValidationDisabled = $oField->Validate();
$this->assertTrue($bIsFieldValidWithValidationDisabled);
$this->assertTrue($oField->Validate());
}
public function testSetMandatory(): void
@@ -60,4 +63,21 @@ class FieldTest extends ItopTestCase
$this->assertFalse($oField->Validate());
$this->assertCount(1, $oField->GetErrorMessages());
}
public function testValidateWithTwoValidatorsFirstWrong(): void
{
$oField = new StringField('test');
$oField->SetCurrentValue('string with spaces');
$sFirstValidatorInvalidResultErrorMsg = 'dumb email validator error message';
$oFirstValidatorInvalidResult = new CustomRegexpValidator('^[a-z]+$', $sFirstValidatorInvalidResultErrorMsg);
$oField->AddValidator($oFirstValidatorInvalidResult);
$oSecondValidatorValidResult = new CustomRegexpValidator('^.+$', 'valid');
$oField->AddValidator($oSecondValidatorValidResult);
$this->assertFalse($oField->Validate());
$this->assertCount(1, $oField->GetErrorMessages());
$this->assertSame($sFirstValidatorInvalidResultErrorMsg, $oField->GetErrorMessages()[0]);
}
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Test\UnitTest\Sources\Form\Validator;
use Combodo\iTop\Form\Field\StringField;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
class ValidatorTest extends ItopTestCase
{
public function testMandatoryValidator()
{
$oField = new StringField('test');
$oField->SetMandatory(true);
$oField->SetCurrentValue('there is a value !');
$bIsMandatoryFieldValidWithExistingValue = $oField->Validate();
$this->assertTrue($bIsMandatoryFieldValidWithExistingValue);
$oField->SetCurrentValue(null);
$bIsMandatoryFieldValidWithNoValue = $oField->Validate();
$this->assertFalse($bIsMandatoryFieldValidWithNoValue);
$this->assertNotEmpty($oField->GetErrorMessages());
}
}