mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-12 23:14:18 +01:00
N°6414 Move existing AbstractSimpleField::Validate impl to custom validators
- LinkedSetField - SelectObjectField - MultipleChoicesField (warning this hierarchy contains non multiple value fields like SelectField !) Also change AbstractValidator::Validate signature : now we are returning an array of error messages, so that we can return multiple ones
This commit is contained in:
@@ -2477,8 +2477,6 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
$oFormField->SetLnkAttributesToDisplay($aLnkAttributesToDisplay);
|
||||
}
|
||||
|
||||
$oFormField->AddValidator(new LinkedSetValidator());
|
||||
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
|
||||
@@ -416,13 +416,16 @@ 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\\Helper\\FieldHelper' => $baseDir . '/sources/Form/Helper/FieldHelper.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',
|
||||
'Combodo\\iTop\\Form\\Validator\\MultipleChoicesValidator' => $baseDir . '/sources/Form/Validator/MultipleChoicesValidator.php',
|
||||
'Combodo\\iTop\\Form\\Validator\\NotEmptyExtKeyValidator' => $baseDir . '/sources/Form/Validator/NotEmptyExtKeyValidator.php',
|
||||
'Combodo\\iTop\\Form\\Validator\\SelectObjectValidator' => $baseDir . '/sources/Form/Validator/SelectObjectValidator.php',
|
||||
'Combodo\\iTop\\Form\\Validator\\Validator' => $baseDir . '/sources/Form/Validator/Validator.php',
|
||||
'Combodo\\iTop\\Renderer\\BlockRenderer' => $baseDir . '/sources/Renderer/BlockRenderer.php',
|
||||
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFieldRendererMappings' => $baseDir . '/sources/Renderer/Bootstrap/BsFieldRendererMappings.php',
|
||||
@@ -534,6 +537,7 @@ return array(
|
||||
'DesignerSubFormField' => $baseDir . '/application/forms.class.inc.php',
|
||||
'DesignerTabularForm' => $baseDir . '/application/forms.class.inc.php',
|
||||
'DesignerTextField' => $baseDir . '/application/forms.class.inc.php',
|
||||
'DesignerXMLField' => $baseDir . '/application/forms.class.inc.php',
|
||||
'Dict' => $baseDir . '/core/dict.class.inc.php',
|
||||
'DictException' => $baseDir . '/application/exceptions/dict/DictException.php',
|
||||
'DictExceptionMissingString' => $baseDir . '/application/exceptions/dict/DictExceptionMissingString.php',
|
||||
|
||||
@@ -780,13 +780,16 @@ 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\\Helper\\FieldHelper' => __DIR__ . '/../..' . '/sources/Form/Helper/FieldHelper.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',
|
||||
'Combodo\\iTop\\Form\\Validator\\MultipleChoicesValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/MultipleChoicesValidator.php',
|
||||
'Combodo\\iTop\\Form\\Validator\\NotEmptyExtKeyValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/NotEmptyExtKeyValidator.php',
|
||||
'Combodo\\iTop\\Form\\Validator\\SelectObjectValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/SelectObjectValidator.php',
|
||||
'Combodo\\iTop\\Form\\Validator\\Validator' => __DIR__ . '/../..' . '/sources/Form/Validator/Validator.php',
|
||||
'Combodo\\iTop\\Renderer\\BlockRenderer' => __DIR__ . '/../..' . '/sources/Renderer/BlockRenderer.php',
|
||||
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFieldRendererMappings' => __DIR__ . '/../..' . '/sources/Renderer/Bootstrap/BsFieldRendererMappings.php',
|
||||
@@ -898,6 +901,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'DesignerSubFormField' => __DIR__ . '/../..' . '/application/forms.class.inc.php',
|
||||
'DesignerTabularForm' => __DIR__ . '/../..' . '/application/forms.class.inc.php',
|
||||
'DesignerTextField' => __DIR__ . '/../..' . '/application/forms.class.inc.php',
|
||||
'DesignerXMLField' => __DIR__ . '/../..' . '/application/forms.class.inc.php',
|
||||
'Dict' => __DIR__ . '/../..' . '/core/dict.class.inc.php',
|
||||
'DictException' => __DIR__ . '/../..' . '/application/exceptions/dict/DictException.php',
|
||||
'DictExceptionMissingString' => __DIR__ . '/../..' . '/application/exceptions/dict/DictExceptionMissingString.php',
|
||||
|
||||
@@ -26,12 +26,13 @@ class AbstractSimpleField extends Field
|
||||
|
||||
if (!$bEmpty || $this->GetMandatory()) {
|
||||
foreach ($this->GetValidators() as $oValidator) {
|
||||
[$bIsFieldValid, $sValidationErrorMessage] = $oValidator->Validate($this->GetCurrentValue());
|
||||
$aValidationErrorMessages = $oValidator->Validate($this->GetCurrentValue());
|
||||
|
||||
/** @var bool $bIsFieldValid */
|
||||
if (false === $bIsFieldValid) {
|
||||
if (count($aValidationErrorMessages) > 0) {
|
||||
$this->SetValid(false);
|
||||
$this->AddErrorMessage($sValidationErrorMessage);
|
||||
foreach ($aValidationErrorMessages as $sErrorMessage) {
|
||||
$this->AddErrorMessage($sErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,15 +505,33 @@ abstract class Field
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sValidatorClassName validator class name, should be one of {@see AbstractValidator} children
|
||||
* @return $this
|
||||
* @since 3.1.0 N°6414
|
||||
*/
|
||||
final public function RemoveValidatorsOfClass(string $sValidatorClassName)
|
||||
{
|
||||
foreach ($this->aValidators as $iKey => $oValue) {
|
||||
if ($oValue instanceof $sValidatorClassName) {
|
||||
unset($this->aValidators[$iKey]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note : Function is protected as aErrorMessages should not be add from outside
|
||||
*
|
||||
* @param string $sErrorMessage
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function AddErrorMessage(string $sErrorMessage)
|
||||
{
|
||||
$this->aErrorMessages[] = $sErrorMessage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
namespace Combodo\iTop\Form\Field;
|
||||
|
||||
use Closure;
|
||||
use Dict;
|
||||
use ormLinkSet;
|
||||
use Combodo\iTop\Form\Validator\LinkedSetValidator;
|
||||
|
||||
/**
|
||||
* Description of LinkedSetField
|
||||
@@ -55,7 +54,7 @@ class LinkedSetField extends AbstractSimpleField
|
||||
protected $aLimitedAccessItemIDs;
|
||||
/** @var array $aAttributesToDisplay */
|
||||
protected $aAttributesToDisplay;
|
||||
/** @var array $aLnkAttributesToDisplay */
|
||||
/** @var array $aLnkAttributesToDisplay attcode as key */
|
||||
protected $aLnkAttributesToDisplay;
|
||||
/** @var string $sSearchEndpoint */
|
||||
protected $sSearchEndpoint;
|
||||
@@ -281,25 +280,25 @@ class LinkedSetField extends AbstractSimpleField
|
||||
public function GetLnkAttributesToDisplay(bool $bAttCodesOnly = false)
|
||||
{
|
||||
return ($bAttCodesOnly) ? array_keys($this->aLnkAttributesToDisplay) : $this->aLnkAttributesToDisplay;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @since 3.1
|
||||
*
|
||||
* @param array $aAttributesToDisplay
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function SetLnkAttributesToDisplay(array $aAttributesToDisplay)
|
||||
{
|
||||
$this->aLnkAttributesToDisplay = $aAttributesToDisplay;
|
||||
/**
|
||||
* @param array $aAttributesToDisplay
|
||||
* @return $this
|
||||
* @since 3.1.0 N°803
|
||||
*/
|
||||
public function SetLnkAttributesToDisplay(array $aAttributesToDisplay)
|
||||
{
|
||||
$this->aLnkAttributesToDisplay = $aAttributesToDisplay;
|
||||
|
||||
return $this;
|
||||
}
|
||||
$this->RemoveValidatorsOfClass(LinkedSetValidator::class);
|
||||
$this->AddValidator(new LinkedSetValidator($aAttributesToDisplay));
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function GetSearchEndpoint()
|
||||
{
|
||||
@@ -349,39 +348,4 @@ class LinkedSetField extends AbstractSimpleField
|
||||
{
|
||||
return in_array($iItemID, $this->aLimitedAccessItemIDs, false);
|
||||
}
|
||||
|
||||
/** @inheritdoc @since 3.1 */
|
||||
public function Validate()
|
||||
{
|
||||
$bValid = parent::Validate();
|
||||
|
||||
/** @var ormLinkSet $oSet */
|
||||
$oSet = $this->GetCurrentValue();
|
||||
|
||||
// retrieve displayed attributes
|
||||
$aAttributesToDisplayCodes = $this->GetLnkAttributesToDisplay(true);
|
||||
|
||||
// validate each links...
|
||||
/** @var \DBObject $oItem */
|
||||
foreach ($oSet as $oItem) {
|
||||
$aChanges = $oItem->ListChanges();
|
||||
foreach ($aChanges as $sAttCode => $value) {
|
||||
if (!in_array($sAttCode, $aAttributesToDisplayCodes)) {
|
||||
continue;
|
||||
}
|
||||
$res = $oItem->CheckValue($sAttCode);
|
||||
if ($res !== true) {
|
||||
$sAttLabel = $this->GetLabel($sAttCode);
|
||||
$sItem = $oItem->Get('friendlyname') != '' ? $oItem->Get('friendlyname') : Dict::S('UI:Links:NewItem');
|
||||
$sIssue = Dict::Format('Core:CheckValueError', $sAttLabel, $sAttCode, $res);
|
||||
$this->AddErrorMessage('<b>'.$sItem.' : </b>'.$sIssue);
|
||||
$bValid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oSet->Rewind();
|
||||
|
||||
return $bValid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
namespace Combodo\iTop\Form\Field;
|
||||
|
||||
use Closure;
|
||||
use ContextTag;
|
||||
use utils;
|
||||
use Combodo\iTop\Form\Validator\MultipleChoicesValidator;
|
||||
|
||||
/**
|
||||
* Description of MultipleChoicesField
|
||||
@@ -51,6 +50,8 @@ abstract class MultipleChoicesField extends AbstractSimpleField
|
||||
$this->bMultipleValuesEnabled = static::DEFAULT_MULTIPLE_VALUES_ENABLED;
|
||||
$this->aChoices = array();
|
||||
$this->currentValue = array();
|
||||
|
||||
$this->InitValidators();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,69 +180,68 @@ abstract class MultipleChoicesField extends AbstractSimpleField
|
||||
public function SetChoices(array $aChoices)
|
||||
{
|
||||
$this->aChoices = $aChoices;
|
||||
|
||||
$this->InitValidators();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sId
|
||||
* @param null $choice
|
||||
* @param null $choice choice value (eg label)
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function AddChoice(string $sId, $choice = null)
|
||||
{
|
||||
if ($choice === null)
|
||||
{
|
||||
if ($choice === null) {
|
||||
$choice = $sId;
|
||||
}
|
||||
$this->aChoices[$sId] = $choice;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sId
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function RemoveChoice(string $sId)
|
||||
{
|
||||
if (in_array($sId, $this->aChoices))
|
||||
{
|
||||
unset($this->aChoices[$sId]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
$this->InitValidators();
|
||||
|
||||
public function Validate() {
|
||||
$this->SetValid(true);
|
||||
$this->EmptyErrorMessages();
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ((ContextTag::Check(ContextTag::TAG_REST)) && ($this->GetReadOnly() === false)) {
|
||||
// Only doing the check when coming from the REST API, as the user portal might send invalid values (see VerifyCurrentValue() method below)
|
||||
// Also do not check read only fields, are they are send with a null value when submitting request template from the console
|
||||
if (count($this->currentValue) > 0) {
|
||||
foreach ($this->currentValue as $sCode => $value) {
|
||||
if (utils::IsNullOrEmptyString($value)) {
|
||||
continue;
|
||||
}
|
||||
if (false === array_key_exists($value, $this->aChoices)) {
|
||||
$this->SetValid(false);
|
||||
$this->AddErrorMessage("Value ({$value}) is not part of the field possible values list");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param string $sId
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function RemoveChoice(string $sId)
|
||||
{
|
||||
if (in_array($sId, $this->aChoices)) {
|
||||
unset($this->aChoices[$sId]);
|
||||
}
|
||||
|
||||
foreach ($this->GetValidators() as $oValidator) {
|
||||
foreach ($this->currentValue as $value) {
|
||||
if (!preg_match($oValidator->GetRegExp(true), $value)) {
|
||||
$this->SetValid(false);
|
||||
$this->AddErrorMessage($oValidator->GetErrorMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->GetValid();
|
||||
}
|
||||
/**
|
||||
* @param bool $bReadOnly
|
||||
* @return MultipleChoicesField
|
||||
* @since 3.1.0 N°6414
|
||||
*/
|
||||
public function SetReadOnly(bool $bReadOnly)
|
||||
{
|
||||
if ($bReadOnly) {
|
||||
/** @noinspection PhpRedundantOptionalArgumentInspection */
|
||||
$this->SetValidationDisabled(true);
|
||||
} else {
|
||||
$this->SetValidationDisabled(false);
|
||||
}
|
||||
|
||||
return parent::SetReadOnly($bReadOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @since 3.1.0 N°6414
|
||||
*/
|
||||
protected function InitValidators(): void
|
||||
{
|
||||
$this->RemoveValidatorsOfClass(MultipleChoicesValidator::class);
|
||||
$this->AddValidator(new MultipleChoicesValidator($this->aChoices));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,5 +28,4 @@ class MultipleSelectField extends SelectField
|
||||
{
|
||||
/** @inheritDoc */
|
||||
const DEFAULT_MULTIPLE_VALUES_ENABLED = true;
|
||||
|
||||
}
|
||||
|
||||
@@ -20,18 +20,14 @@
|
||||
|
||||
namespace Combodo\iTop\Form\Field;
|
||||
|
||||
use BinaryExpression;
|
||||
use Closure;
|
||||
use Combodo\iTop\Form\Helper\FieldHelper;
|
||||
use Combodo\iTop\Form\Validator\AbstractValidator;
|
||||
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
|
||||
use ContextTag;
|
||||
use DBObjectSet;
|
||||
use Combodo\iTop\Form\Validator\SelectObjectValidator;
|
||||
use DBSearch;
|
||||
use DeprecatedCallsLog;
|
||||
use FieldExpression;
|
||||
use MetaModel;
|
||||
use ScalarExpression;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Description of SelectObjectField
|
||||
@@ -95,6 +91,9 @@ class SelectObjectField extends AbstractSimpleField
|
||||
{
|
||||
$this->oSearch = $oSearch;
|
||||
|
||||
$this->RemoveValidatorsOfClass(SelectObjectValidator::class);
|
||||
$this->AddValidator(new SelectObjectValidator($oSearch));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -229,27 +228,6 @@ class SelectObjectField extends AbstractSimpleField
|
||||
return $this->sSearchEndpoint;
|
||||
}
|
||||
|
||||
public function Validate() {
|
||||
if ((ContextTag::Check(ContextTag::TAG_REST)) && ($this->GetReadOnly() === false)) {
|
||||
// Only doing the check when coming from the REST API, as the user portal might send invalid values (see VerifyCurrentValue() method below)
|
||||
// Also do not check read only fields, are they are send with a null value when submitting request template from the console
|
||||
$sCurrentValueForExtKey = $this->currentValue;
|
||||
if (utils::IsNotNullOrEmptyString($sCurrentValueForExtKey) && ($sCurrentValueForExtKey !== 0)) {
|
||||
$oSetForExistingCurrentValue = $this->GetObjectsSet();
|
||||
$iObjectsCount = $oSetForExistingCurrentValue->CountWithLimit(1);
|
||||
|
||||
if ($iObjectsCount === 0) {
|
||||
$this->SetValid(false);
|
||||
$this->AddErrorMessage("Value $sCurrentValueForExtKey does not match the corresponding filter set");
|
||||
|
||||
return $this->GetValid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parent::Validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets current value if not among allowed ones.
|
||||
* By default, reset is done ONLY when the field is not read-only.
|
||||
@@ -283,25 +261,11 @@ class SelectObjectField extends AbstractSimpleField
|
||||
*/
|
||||
public function ResetCurrentValueIfNotAmongAllowedValues(bool $bAlways = false) {
|
||||
if (!$this->GetReadOnly() || $bAlways) {
|
||||
$oValuesSet = $this->GetObjectsSet();
|
||||
$oValuesSet = FieldHelper::GetObjectsSetFromSearchAndCurrentValueId($this->oSearch, $this->currentValue);
|
||||
|
||||
if ($oValuesSet->Count() === 0) {
|
||||
$this->currentValue = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final protected function GetObjectsSet() {
|
||||
$sCurrentValueForExtKey = $this->currentValue;
|
||||
|
||||
$oSearchForExistingCurrentValue = $this->oSearch->DeepClone();
|
||||
$oCheckIdAgainstCurrentValueExpression = new BinaryExpression(
|
||||
new FieldExpression('id', $oSearchForExistingCurrentValue->GetClassAlias()),
|
||||
'=',
|
||||
new ScalarExpression($sCurrentValueForExtKey)
|
||||
);
|
||||
$oSearchForExistingCurrentValue->AddConditionExpression($oCheckIdAgainstCurrentValueExpression);
|
||||
|
||||
return new DBObjectSet($oSearchForExistingCurrentValue);
|
||||
}
|
||||
}
|
||||
|
||||
36
sources/Form/Helper/FieldHelper.php
Normal file
36
sources/Form/Helper/FieldHelper.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Form\Helper;
|
||||
|
||||
use BinaryExpression;
|
||||
use DBObjectSet;
|
||||
use DBSearch;
|
||||
use FieldExpression;
|
||||
use ScalarExpression;
|
||||
|
||||
/**
|
||||
* Utility methods for {@see \Combodo\iTop\Form\Field\Field} classes
|
||||
*
|
||||
* @since 3.1.0 N°6414
|
||||
*/
|
||||
class FieldHelper {
|
||||
/**
|
||||
* @since 3.1.0 N°6414 Method creation to factorize between uses in {@see \Combodo\iTop\Form\Field\Field} and {@see \Combodo\iTop\Form\Validator\SelectObjectValidator}
|
||||
*/
|
||||
public static function GetObjectsSetFromSearchAndCurrentValueId(DBSearch $oSearch, string $sCurrentValueId) {
|
||||
$oSearchForExistingCurrentValue = $oSearch->DeepClone();
|
||||
$oCheckIdAgainstCurrentValueExpression = new BinaryExpression(
|
||||
new FieldExpression('id', $oSearchForExistingCurrentValue->GetClassAlias()),
|
||||
'=',
|
||||
new ScalarExpression($sCurrentValueId)
|
||||
);
|
||||
$oSearchForExistingCurrentValue->AddConditionExpression($oCheckIdAgainstCurrentValueExpression);
|
||||
|
||||
return new DBObjectSet($oSearchForExistingCurrentValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,23 +18,23 @@ abstract class AbstractRegexpValidator extends AbstractValidator
|
||||
|
||||
protected string $sRegExp;
|
||||
|
||||
public function __construct(?string $sErrorMessage = null)
|
||||
{
|
||||
$this->sRegExp = static::DEFAULT_REGEXP;
|
||||
parent::__construct($sErrorMessage);
|
||||
}
|
||||
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];
|
||||
}
|
||||
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 [];
|
||||
}
|
||||
|
||||
return [false, $this->sErrorMessage];
|
||||
}
|
||||
return [$this->sErrorMessage];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the regular expression of the validator.
|
||||
|
||||
@@ -20,19 +20,20 @@ abstract class AbstractValidator
|
||||
/** @var string message / dict key to use when an error occurs */
|
||||
protected string $sErrorMessage;
|
||||
|
||||
public function __construct(?string $sErrorMessage)
|
||||
public function __construct(?string $sErrorMessage = null)
|
||||
{
|
||||
if (false === utils::IsNullOrEmptyString($sErrorMessage)) {
|
||||
$this->sErrorMessage = $sErrorMessage;
|
||||
} else {
|
||||
$this->sErrorMessage = self::DEFAULT_ERROR_MESSAGE;
|
||||
}
|
||||
else {
|
||||
$this->sErrorMessage = static::DEFAULT_ERROR_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return array<bool,?string> boolean valid for valid / invalid, and error message if invalid
|
||||
* @return string[] list of error messages, empty array if no error
|
||||
*/
|
||||
abstract public function Validate($value): array;
|
||||
|
||||
|
||||
@@ -19,12 +19,56 @@
|
||||
|
||||
namespace Combodo\iTop\Form\Validator;
|
||||
|
||||
use Dict;
|
||||
use ormLinkSet;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Description of LinkedSetValidator
|
||||
*
|
||||
* @since 3.1
|
||||
* @since 3.1.0 N°6414
|
||||
*/
|
||||
class LinkedSetValidator extends AbstractRegexpValidator
|
||||
{
|
||||
public const VALIDATOR_NAME = 'LinkedSetValidator';
|
||||
public const VALIDATOR_NAME = 'linkedset_validator';
|
||||
private $aAttributesToDisplayCodes;
|
||||
|
||||
public function __construct($aAttributesToDisplayCodes)
|
||||
{
|
||||
$this->aAttributesToDisplayCodes = $aAttributesToDisplayCodes;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function Validate($value): array
|
||||
{
|
||||
$aErrorMessages = [];
|
||||
|
||||
/** @var ormLinkSet $oSet */
|
||||
$oSet = $value;
|
||||
|
||||
// validate each links...
|
||||
/** @var \DBObject $oItem */
|
||||
foreach ($oSet as $oItem) {
|
||||
$aChanges = $oItem->ListChanges();
|
||||
foreach ($aChanges as $sAttCode => $AttValue) {
|
||||
if (!in_array($sAttCode, $this->aAttributesToDisplayCodes)) {
|
||||
continue;
|
||||
}
|
||||
$res = $oItem->CheckValue($sAttCode);
|
||||
if ($res !== true) {
|
||||
$sAttLabel = $oItem->GetLabel($sAttCode);
|
||||
$sItem = utils::IsNullOrEmptyString($oItem->Get('friendlyname'))
|
||||
? Dict::S('UI:Links:NewItem')
|
||||
: $oItem->Get('friendlyname');
|
||||
$sIssue = Dict::Format('Core:CheckValueError', $sAttLabel, $sAttCode, $res);
|
||||
$aErrorMessages[] = '<b>' . $sItem . ' : </b>' . $sIssue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oSet->Rewind();
|
||||
|
||||
return $aErrorMessages;
|
||||
}
|
||||
}
|
||||
|
||||
59
sources/Form/Validator/MultipleChoicesValidator.php
Normal file
59
sources/Form/Validator/MultipleChoicesValidator.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Form\Validator;
|
||||
|
||||
use utils;
|
||||
|
||||
class MultipleChoicesValidator extends AbstractValidator
|
||||
{
|
||||
public const VALIDATOR_NAME = 'multiple_choices_validator';
|
||||
|
||||
/** @var array List of possible choices */
|
||||
private array $aChoices;
|
||||
|
||||
public function __construct(array $aChoices)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->aChoices = $aChoices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value Warning can either be an array (if multiple values are allowed in the field) or a primitive : {@see \Combodo\iTop\Form\Field\MultipleChoicesField::GetCurrentValue()}
|
||||
*
|
||||
* @return array|string[]
|
||||
*/
|
||||
public function Validate($value): array
|
||||
{
|
||||
$aErrorMessages = [];
|
||||
if (false === is_array($value)) {
|
||||
$this->CheckValueAgainstChoices($value, $aErrorMessages);
|
||||
|
||||
return $aErrorMessages;
|
||||
}
|
||||
|
||||
if (count($value) === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnusedLocalVariableInspection */
|
||||
foreach ($value as $sCode => $valueItem) {
|
||||
if (utils::IsNullOrEmptyString($valueItem)) {
|
||||
continue;
|
||||
}
|
||||
$this->CheckValueAgainstChoices($valueItem, $aErrorMessages);
|
||||
}
|
||||
|
||||
return $aErrorMessages;
|
||||
}
|
||||
|
||||
private function CheckValueAgainstChoices(string $sValue, array &$aErrorMessages): void
|
||||
{
|
||||
if (false === array_key_exists($sValue, $this->aChoices)) {
|
||||
$aErrorMessages[] = "Value ({$sValue}) is not part of the field possible values list";
|
||||
}
|
||||
}
|
||||
}
|
||||
45
sources/Form/Validator/SelectObjectValidator.php
Normal file
45
sources/Form/Validator/SelectObjectValidator.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Form\Validator;
|
||||
|
||||
use Combodo\iTop\Form\Helper\FieldHelper;
|
||||
use DBSearch;
|
||||
use utils;
|
||||
|
||||
class SelectObjectValidator extends AbstractValidator
|
||||
{
|
||||
public const VALIDATOR_NAME = 'select_object_validator';
|
||||
|
||||
/** @var \DBSearch $oSearch */
|
||||
private $oSearch;
|
||||
|
||||
public function __construct(DBSearch $oSearch)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->oSearch = $oSearch;
|
||||
}
|
||||
|
||||
public function Validate($value): array
|
||||
{
|
||||
if (utils::IsNullOrEmptyString($value)) {
|
||||
return [];
|
||||
}
|
||||
if (($value === 0) || ($value === '0')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$oSetForExistingCurrentValue = FieldHelper::GetObjectsSetFromSearchAndCurrentValueId($this->oSearch, $value);
|
||||
$iObjectsCount = $oSetForExistingCurrentValue->CountWithLimit(1);
|
||||
|
||||
if ($iObjectsCount === 0) {
|
||||
return ["Value $value does not match the corresponding filter set"];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -100,4 +100,28 @@ class FieldTest extends ItopTestCase
|
||||
$this->assertTrue($bIsSubFormFieldValidAfterFieldUpdate);
|
||||
$this->assertCount(0, $oSubFormField->GetErrorMessages());
|
||||
}
|
||||
|
||||
public function testRemoveValidatorsOfClass(): void {
|
||||
$oField = new StringField('test');
|
||||
|
||||
$this->assertCount(0, $oField->GetValidators());
|
||||
$oField->RemoveValidatorsOfClass(CustomRegexpValidator::class);
|
||||
$this->assertCount(0, $oField->GetValidators());
|
||||
|
||||
$oField->AddValidator(new IntegerValidator());
|
||||
$this->assertCount(1, $oField->GetValidators());
|
||||
$oField->RemoveValidatorsOfClass(CustomRegexpValidator::class);
|
||||
$this->assertCount(1, $oField->GetValidators());
|
||||
|
||||
$oField->AddValidator(new CustomRegexpValidator('^.*$'));
|
||||
$this->assertCount(2, $oField->GetValidators());
|
||||
$oField->RemoveValidatorsOfClass(CustomRegexpValidator::class);
|
||||
$this->assertCount(1, $oField->GetValidators());
|
||||
|
||||
$oField->AddValidator(new CustomRegexpValidator('^.*$'));
|
||||
$oField->AddValidator(new CustomRegexpValidator('^.*$'));
|
||||
$this->assertCount(3, $oField->GetValidators());
|
||||
$oField->RemoveValidatorsOfClass(CustomRegexpValidator::class);
|
||||
$this->assertCount(1, $oField->GetValidators());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Form;
|
||||
|
||||
use Combodo\iTop\Form\Field\LinkedSetField;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use ormLinkSet;
|
||||
use Person;
|
||||
|
||||
class LinkedSetFieldTest extends ItopDataTestCase {
|
||||
public function testValidate(): void {
|
||||
$sLinkedClass = Ticket::class;
|
||||
$oLinkedSetField = new LinkedSetField('test');
|
||||
$oLinkedSetField->SetIndirect(false);
|
||||
$oLinkedSetField->SetTargetClass($sLinkedClass);
|
||||
$oLinkedSetField->SetLinkedClass($sLinkedClass);
|
||||
$oLinkedSetField->SetLnkAttributesToDisplay(['title' => 'title']);
|
||||
|
||||
$oSetThreeExistingTickets = new ormLinkSet(Person::class, 'tickets_list');
|
||||
$this->CreateTestOrganization();
|
||||
$oUserRequest1 = $this->CreateUserRequest(1);
|
||||
$oUserRequest2 = $this->CreateUserRequest(2);
|
||||
$oUserRequest3 = $this->CreateUserRequest(3);
|
||||
$oSetThreeExistingTickets->AddItem($oUserRequest1);
|
||||
$oSetThreeExistingTickets->AddItem($oUserRequest2);
|
||||
$oSetThreeExistingTickets->AddItem($oUserRequest3);
|
||||
$oLinkedSetField->SetCurrentValue($oSetThreeExistingTickets);
|
||||
$this->assertTrue($oLinkedSetField->Validate(), 'A set with existing objects and no modifications must be OK');
|
||||
|
||||
$oUserRequest1->Set('title', 'this a modified title !');
|
||||
$this->assertTrue($oLinkedSetField->Validate(), 'A set with existing objects and a valid modification must be OK');
|
||||
|
||||
$oUserRequest1->Set('title', '');
|
||||
$this->assertFalse($oLinkedSetField->Validate(), 'A set with existing objects and an invalid modification must be KO');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Form;
|
||||
|
||||
use Combodo\iTop\Form\Field\MultipleChoicesField;
|
||||
use Combodo\iTop\Form\Field\MultipleSelectField;
|
||||
use Combodo\iTop\Form\Field\SelectField;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
use ContextTag;
|
||||
|
||||
class MultipleChoicesFieldTest extends ItopTestCase
|
||||
{
|
||||
public function testValidateForMultipleSelectField(): void
|
||||
{
|
||||
$oMultipleChoicesField = new MultipleSelectField('test');
|
||||
$oMultipleChoicesField->AddChoice('A');
|
||||
$oMultipleChoicesField->AddChoice('B');
|
||||
$oMultipleChoicesField->AddChoice('C');
|
||||
$oMultipleChoicesField->AddChoice('D');
|
||||
|
||||
// N°1150 the control was added for the REST API initially and was only triggered with the corresponding ContextTag
|
||||
$oRestContext = new ContextTag(ContextTag::TAG_REST);
|
||||
$this->ValidateMultipleSelectField($oMultipleChoicesField);
|
||||
|
||||
// retrying without REST context
|
||||
unset($oRestContext);
|
||||
$this->ValidateMultipleSelectField($oMultipleChoicesField);
|
||||
}
|
||||
|
||||
private function ValidateMultipleSelectField(MultipleChoicesField $oMultipleChoicesField): void
|
||||
{
|
||||
$oMultipleChoicesField->SetCurrentValue(null);
|
||||
$this->assertTrue($oMultipleChoicesField->Validate(), 'No value must be valid');
|
||||
|
||||
$sExistingValue = 'A';
|
||||
$sNonExistingValue = 'Non existing choice';
|
||||
$sNonExistingValue1 = 'Non existing choice 1';
|
||||
$sNonExistingValue2 = 'Non existing choice 2';
|
||||
|
||||
$oMultipleChoicesField->SetCurrentValue($sExistingValue);
|
||||
$this->assertTrue($oMultipleChoicesField->Validate(), 'Value among possible ones is valid');
|
||||
|
||||
$oMultipleChoicesField->SetCurrentValue($sNonExistingValue);
|
||||
$this->assertFalse($oMultipleChoicesField->Validate(), 'Value not among possible ones is invalid');
|
||||
$this->assertCount(1, $oMultipleChoicesField->GetErrorMessages());
|
||||
$this->assertStringContainsString($sNonExistingValue, $oMultipleChoicesField->GetErrorMessages()[0]);
|
||||
|
||||
$oMultipleChoicesField->SetCurrentValue([$sNonExistingValue1, $sNonExistingValue2]);
|
||||
$this->assertFalse($oMultipleChoicesField->Validate(), 'Multiple values not among possible ones is invalid');
|
||||
$this->assertCount(2, $oMultipleChoicesField->GetErrorMessages());
|
||||
$this->assertStringContainsString($sNonExistingValue1, $oMultipleChoicesField->GetErrorMessages()[0]);
|
||||
$this->assertStringContainsString($sNonExistingValue2, $oMultipleChoicesField->GetErrorMessages()[1]);
|
||||
|
||||
$oMultipleChoicesField->SetCurrentValue([$sExistingValue, $sNonExistingValue]);
|
||||
$this->assertFalse($oMultipleChoicesField->Validate(), 'Valid value + Value not among possible ones is invalid');
|
||||
$this->assertCount(1, $oMultipleChoicesField->GetErrorMessages());
|
||||
$this->assertStringContainsString($sNonExistingValue, $oMultipleChoicesField->GetErrorMessages()[0]);
|
||||
|
||||
$oMultipleChoicesField->SetCurrentValue([$sExistingValue, $sNonExistingValue1, $sNonExistingValue2]);
|
||||
$this->assertFalse($oMultipleChoicesField->Validate(), 'Valid value + Multiple values not among possible ones is invalid');
|
||||
$this->assertCount(2, $oMultipleChoicesField->GetErrorMessages());
|
||||
$this->assertStringContainsString($sNonExistingValue1, $oMultipleChoicesField->GetErrorMessages()[0]);
|
||||
$this->assertStringContainsString($sNonExistingValue2, $oMultipleChoicesField->GetErrorMessages()[1]);
|
||||
}
|
||||
|
||||
public function testValidateForSelectField(): void
|
||||
{
|
||||
$oMultipleChoicesField = new SelectField('test');
|
||||
$oMultipleChoicesField->AddChoice('A');
|
||||
$oMultipleChoicesField->AddChoice('B');
|
||||
$oMultipleChoicesField->AddChoice('C');
|
||||
$oMultipleChoicesField->AddChoice('D');
|
||||
|
||||
// N°1150 the control was added for the REST API initially and was only triggered with the corresponding ContextTag
|
||||
$oRestContext = new ContextTag(ContextTag::TAG_REST);
|
||||
$this->ValidateSelectField($oMultipleChoicesField);
|
||||
|
||||
// retrying without REST context
|
||||
unset($oRestContext);
|
||||
$this->ValidateSelectField($oMultipleChoicesField);
|
||||
|
||||
$oMultipleChoicesField = new SelectField('test');
|
||||
$oMultipleChoicesField->SetChoices(['A' => 'A', 'B' => 'B', 'C' => 'C', 'D' => 'D']);
|
||||
$this->ValidateSelectField($oMultipleChoicesField);
|
||||
}
|
||||
|
||||
private function ValidateSelectField(MultipleChoicesField $oMultipleChoicesField): void
|
||||
{
|
||||
$oMultipleChoicesField->SetCurrentValue(null);
|
||||
$this->assertTrue($oMultipleChoicesField->Validate(), 'No value must be valid');
|
||||
|
||||
$sExistingValue = 'A';
|
||||
$sNonExistingValue = 'Non existing choice';
|
||||
|
||||
$oMultipleChoicesField->SetCurrentValue($sExistingValue);
|
||||
$this->assertTrue($oMultipleChoicesField->Validate(), 'Value among possible ones is valid');
|
||||
|
||||
$oMultipleChoicesField->SetCurrentValue($sNonExistingValue);
|
||||
$this->assertFalse($oMultipleChoicesField->Validate(), 'Value not among possible ones is invalid');
|
||||
$this->assertCount(1, $oMultipleChoicesField->GetErrorMessages());
|
||||
$this->assertStringContainsString($sNonExistingValue, $oMultipleChoicesField->GetErrorMessages()[0]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Form;
|
||||
|
||||
use Combodo\iTop\Form\Field\SelectObjectField;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
use ContextTag;
|
||||
use DBObjectSearch;
|
||||
use Organization;
|
||||
|
||||
class SelectObjectFieldTest extends ItopDataTestCase {
|
||||
public function testValidate(): void {
|
||||
$oSelectObjectField = new SelectObjectField('test');
|
||||
$oSelectObjectField->SetSearch(DBObjectSearch::FromOQL('SELECT '.Organization::class));
|
||||
|
||||
// N°1150 the control was added for the REST API initially and was only triggered with the corresponding ContextTag
|
||||
$oRestContext = new ContextTag(ContextTag::TAG_REST);
|
||||
$this->ValidateSelectObjectField($oSelectObjectField);
|
||||
|
||||
// retrying without REST context
|
||||
unset($oRestContext);
|
||||
$this->ValidateSelectObjectField($oSelectObjectField);
|
||||
}
|
||||
|
||||
private function ValidateSelectObjectField(SelectObjectField $oSelectObjectField): void {
|
||||
$oSelectObjectField->SetCurrentValue(null);
|
||||
$this->assertTrue($oSelectObjectField->Validate(), 'No value must be valid');
|
||||
|
||||
$sExistingOrganizationId = 1;
|
||||
$oSelectObjectField->SetCurrentValue($sExistingOrganizationId);
|
||||
$this->assertTrue($oSelectObjectField->Validate(), 'An existing object id must be valid');
|
||||
|
||||
$sNonExistingOrganizationId = 999999;
|
||||
$oSelectObjectField->SetCurrentValue($sNonExistingOrganizationId);
|
||||
$this->assertFalse($oSelectObjectField->Validate(), 'An non existing object id must be invalid');
|
||||
$this->assertCount(1, $oSelectObjectField->GetErrorMessages());
|
||||
$this->assertStringContainsString($sNonExistingOrganizationId, $oSelectObjectField->GetErrorMessages()[0]);
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
namespace Combodo\iTop\Test\UnitTest\Sources\Form\Validator;
|
||||
|
||||
use Combodo\iTop\Form\Field\StringField;
|
||||
use Combodo\iTop\Form\Validator\MandatoryValidator;
|
||||
use Combodo\iTop\Test\UnitTest\ItopTestCase;
|
||||
|
||||
class ValidatorTest extends ItopTestCase
|
||||
@@ -24,5 +25,7 @@ class ValidatorTest extends ItopTestCase
|
||||
$bIsMandatoryFieldValidWithNoValue = $oField->Validate();
|
||||
$this->assertFalse($bIsMandatoryFieldValidWithNoValue);
|
||||
$this->assertNotEmpty($oField->GetErrorMessages());
|
||||
$this->assertCount(1, $oField->GetErrorMessages());
|
||||
$this->assertStringContainsString(MandatoryValidator::DEFAULT_ERROR_MESSAGE, $oField->GetErrorMessages()[0]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user