diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php
index 1f7662548..9fe7fe019 100644
--- a/core/attributedef.class.inc.php
+++ b/core/attributedef.class.inc.php
@@ -2477,8 +2477,6 @@ class AttributeLinkedSet extends AttributeDefinition
$oFormField->SetLnkAttributesToDisplay($aLnkAttributesToDisplay);
}
- $oFormField->AddValidator(new LinkedSetValidator());
-
parent::MakeFormField($oObject, $oFormField);
return $oFormField;
diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php
index d74eb09ed..5b700ec56 100644
--- a/lib/composer/autoload_classmap.php
+++ b/lib/composer/autoload_classmap.php
@@ -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',
diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php
index b41cc4d5a..f764ea377 100644
--- a/lib/composer/autoload_static.php
+++ b/lib/composer/autoload_static.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',
diff --git a/sources/Form/Field/AbstractSimpleField.php b/sources/Form/Field/AbstractSimpleField.php
index 65777bae3..451ce5cf7 100644
--- a/sources/Form/Field/AbstractSimpleField.php
+++ b/sources/Form/Field/AbstractSimpleField.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);
+ }
}
}
}
diff --git a/sources/Form/Field/Field.php b/sources/Form/Field/Field.php
index 8bda28540..c89a077e7 100644
--- a/sources/Form/Field/Field.php
+++ b/sources/Form/Field/Field.php
@@ -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;
}
diff --git a/sources/Form/Field/LinkedSetField.php b/sources/Form/Field/LinkedSetField.php
index 14f6cdc9c..1c76dba90 100644
--- a/sources/Form/Field/LinkedSetField.php
+++ b/sources/Form/Field/LinkedSetField.php
@@ -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(''.$sItem.' : '.$sIssue);
- $bValid = false;
- }
- }
- }
-
- $oSet->Rewind();
-
- return $bValid;
- }
}
diff --git a/sources/Form/Field/MultipleChoicesField.php b/sources/Form/Field/MultipleChoicesField.php
index a86587bc1..097c3e278 100644
--- a/sources/Form/Field/MultipleChoicesField.php
+++ b/sources/Form/Field/MultipleChoicesField.php
@@ -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));
+ }
}
diff --git a/sources/Form/Field/MultipleSelectField.php b/sources/Form/Field/MultipleSelectField.php
index 6e131783b..61199073b 100644
--- a/sources/Form/Field/MultipleSelectField.php
+++ b/sources/Form/Field/MultipleSelectField.php
@@ -28,5 +28,4 @@ class MultipleSelectField extends SelectField
{
/** @inheritDoc */
const DEFAULT_MULTIPLE_VALUES_ENABLED = true;
-
}
diff --git a/sources/Form/Field/SelectObjectField.php b/sources/Form/Field/SelectObjectField.php
index a2d3a60f4..345f0f8d2 100644
--- a/sources/Form/Field/SelectObjectField.php
+++ b/sources/Form/Field/SelectObjectField.php
@@ -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);
- }
}
diff --git a/sources/Form/Helper/FieldHelper.php b/sources/Form/Helper/FieldHelper.php
new file mode 100644
index 000000000..66d11df2a
--- /dev/null
+++ b/sources/Form/Helper/FieldHelper.php
@@ -0,0 +1,36 @@
+DeepClone();
+ $oCheckIdAgainstCurrentValueExpression = new BinaryExpression(
+ new FieldExpression('id', $oSearchForExistingCurrentValue->GetClassAlias()),
+ '=',
+ new ScalarExpression($sCurrentValueId)
+ );
+ $oSearchForExistingCurrentValue->AddConditionExpression($oCheckIdAgainstCurrentValueExpression);
+
+ return new DBObjectSet($oSearchForExistingCurrentValue);
+ }
+
+}
\ No newline at end of file
diff --git a/sources/Form/Validator/AbstractRegexpValidator.php b/sources/Form/Validator/AbstractRegexpValidator.php
index a3434f54d..d5afa7860 100644
--- a/sources/Form/Validator/AbstractRegexpValidator.php
+++ b/sources/Form/Validator/AbstractRegexpValidator.php
@@ -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.
diff --git a/sources/Form/Validator/AbstractValidator.php b/sources/Form/Validator/AbstractValidator.php
index da0780e8a..6bfec4928 100644
--- a/sources/Form/Validator/AbstractValidator.php
+++ b/sources/Form/Validator/AbstractValidator.php
@@ -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 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;
diff --git a/sources/Form/Validator/LinkedSetValidator.php b/sources/Form/Validator/LinkedSetValidator.php
index 5ac6fbd2a..dd6d109a7 100644
--- a/sources/Form/Validator/LinkedSetValidator.php
+++ b/sources/Form/Validator/LinkedSetValidator.php
@@ -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[] = '' . $sItem . ' : ' . $sIssue;
+ }
+ }
+ }
+
+ $oSet->Rewind();
+
+ return $aErrorMessages;
+ }
}
diff --git a/sources/Form/Validator/MultipleChoicesValidator.php b/sources/Form/Validator/MultipleChoicesValidator.php
new file mode 100644
index 000000000..408ac6350
--- /dev/null
+++ b/sources/Form/Validator/MultipleChoicesValidator.php
@@ -0,0 +1,59 @@
+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";
+ }
+ }
+}
\ No newline at end of file
diff --git a/sources/Form/Validator/SelectObjectValidator.php b/sources/Form/Validator/SelectObjectValidator.php
new file mode 100644
index 000000000..8c56fefda
--- /dev/null
+++ b/sources/Form/Validator/SelectObjectValidator.php
@@ -0,0 +1,45 @@
+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 [];
+ }
+}
\ No newline at end of file
diff --git a/tests/php-unit-tests/unitary-tests/sources/Form/Field/FieldTest.php b/tests/php-unit-tests/unitary-tests/sources/Form/Field/FieldTest.php
index 7bc1cd1f5..d915082ef 100644
--- a/tests/php-unit-tests/unitary-tests/sources/Form/Field/FieldTest.php
+++ b/tests/php-unit-tests/unitary-tests/sources/Form/Field/FieldTest.php
@@ -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());
+ }
}
\ No newline at end of file
diff --git a/tests/php-unit-tests/unitary-tests/sources/Form/LinkedSetFieldTest.php b/tests/php-unit-tests/unitary-tests/sources/Form/LinkedSetFieldTest.php
new file mode 100644
index 000000000..6d006257e
--- /dev/null
+++ b/tests/php-unit-tests/unitary-tests/sources/Form/LinkedSetFieldTest.php
@@ -0,0 +1,40 @@
+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');
+ }
+}
\ No newline at end of file
diff --git a/tests/php-unit-tests/unitary-tests/sources/Form/MultipleChoicesFieldTest.php b/tests/php-unit-tests/unitary-tests/sources/Form/MultipleChoicesFieldTest.php
new file mode 100644
index 000000000..8724502e2
--- /dev/null
+++ b/tests/php-unit-tests/unitary-tests/sources/Form/MultipleChoicesFieldTest.php
@@ -0,0 +1,107 @@
+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]);
+ }
+}
\ No newline at end of file
diff --git a/tests/php-unit-tests/unitary-tests/sources/Form/SelectObjectFieldTest.php b/tests/php-unit-tests/unitary-tests/sources/Form/SelectObjectFieldTest.php
new file mode 100644
index 000000000..1f788f043
--- /dev/null
+++ b/tests/php-unit-tests/unitary-tests/sources/Form/SelectObjectFieldTest.php
@@ -0,0 +1,43 @@
+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]);
+ }
+}
\ No newline at end of file
diff --git a/tests/php-unit-tests/unitary-tests/sources/Form/Validator/ValidatorTest.php b/tests/php-unit-tests/unitary-tests/sources/Form/Validator/ValidatorTest.php
index c7c066f38..2f87eacc1 100644
--- a/tests/php-unit-tests/unitary-tests/sources/Form/Validator/ValidatorTest.php
+++ b/tests/php-unit-tests/unitary-tests/sources/Form/Validator/ValidatorTest.php
@@ -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]);
}
}
\ No newline at end of file