diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 60da9bf02..6f316b2e3 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -1152,6 +1152,13 @@ abstract class AttributeDefinition $oFormField->AddMetadata('value-raw', (string)$oObject->Get($this->GetCode())); } + // We don't want to invalidate field because of old untouched values that are no longer valid + $aModifiedAttCodes = $oObject->ListChanges(); + $bAttributeHasBeenModified = array_key_exists($this->GetCode(), $aModifiedAttCodes); + if (false === $bAttributeHasBeenModified) { + $oFormField->SetValidationDisabled(true); + } + return $oFormField; } diff --git a/sources/Form/Field/Field.php b/sources/Form/Field/Field.php index eeb5c3175..66bea9c3e 100644 --- a/sources/Form/Field/Field.php +++ b/sources/Form/Field/Field.php @@ -70,6 +70,12 @@ abstract class Field protected $sDisplayMode; /** @var array */ protected $aValidators; + /** + * @var bool + * @since 3.1.0 N°6414 + */ + protected $bValidationDisabled; + /** @var bool */ protected $bValid; /** @var array */ @@ -96,6 +102,7 @@ abstract class Field $this->bMandatory = static::DEFAULT_MANDATORY; $this->sDisplayMode = static::DEFAULT_DISPLAY_MODE; $this->aValidators = array(); + $this->bValidationDisabled = false; $this->bValid = static::DEFAULT_VALID; $this->aErrorMessages = array(); $this->onFinalizeCallback = $onFinalizeCallback; @@ -522,7 +529,7 @@ abstract class Field /** * Returns if the field is editable. Meaning that it is not editable nor hidden. - * + * * @return boolean */ public function IsEditable() @@ -537,14 +544,35 @@ abstract class Field public function OnFinalize() { - if ($this->onFinalizeCallback !== null) - { + if ($this->onFinalizeCallback !== null) { // Note : We MUST have a temp variable to call the Closure. otherwise it won't work when the Closure is a class member $callback = $this->onFinalizeCallback; $callback($this); } } + /** + * @param bool $bValidationDisabled + * + * @return $this + * + * @since 3.1.0 N°6414 + */ + public function SetValidationDisabled(bool $bValidationDisabled = true): Field + { + $this->bValidationDisabled = $bValidationDisabled; + + return $this; + } + + /** + * @return bool + */ + public function IsValidationDisabled(): bool + { + return $this->bValidationDisabled; + } + /** * Checks the validators to see if the field's current value is valid. * Then sets $bValid and $aErrorMessages. @@ -556,14 +584,15 @@ abstract class Field $this->SetValid(true); $this->EmptyErrorMessages(); - $bEmpty = ( ($this->GetCurrentValue() === null) || ($this->GetCurrentValue() === '') ); + if ($this->bValidationDisabled) { + return $this->GetValid(); + } - if (!$bEmpty || $this->GetMandatory()) - { - foreach ($this->GetValidators() as $oValidator) - { - if (!preg_match($oValidator->GetRegExp(true), $this->GetCurrentValue())) - { + $bEmpty = (($this->GetCurrentValue() === null) || ($this->GetCurrentValue() === '')); + + if (!$bEmpty || $this->GetMandatory()) { + foreach ($this->GetValidators() as $oValidator) { + if (!preg_match($oValidator->GetRegExp(true), $this->GetCurrentValue())) { $this->SetValid(false); $this->AddErrorMessage($oValidator->GetErrorMessage()); } diff --git a/sources/Form/Form.php b/sources/Form/Form.php index 3a4c23e94..dd72126ed 100644 --- a/sources/Form/Form.php +++ b/sources/Form/Form.php @@ -19,11 +19,11 @@ namespace Combodo\iTop\Form; -use \Exception; -use \Dict; -use \Combodo\iTop\Form\Field\Field; -use \Combodo\iTop\Form\Field\CaseLogField; -use \Combodo\iTop\Form\Field\SubFormField; +use Combodo\iTop\Form\Field\CaseLogField; +use Combodo\iTop\Form\Field\Field; +use Combodo\iTop\Form\Field\SubFormField; +use Dict; +use Exception; /** * Description of Form @@ -34,6 +34,7 @@ class Form { protected $sId; protected $sTransactionId; + /** @var \Combodo\iTop\Form\Field\Field[] */ protected $aFields; protected $aDependencies; protected $bValid; @@ -566,17 +567,14 @@ class Form $this->SetValid(true); $this->EmptyErrorMessages(); - foreach ($this->aFields as $oField) - { - if (!$oField->Validate()) - { - $this->SetValid(false); - foreach ($oField->GetErrorMessages() as $sErrorMessage) - { - $this->AddErrorMessage(Dict::S($sErrorMessage), $oField->Getid()); - } - } - } + foreach ($this->aFields as $oField) { + if (!$oField->Validate()) { + $this->SetValid(false); + foreach ($oField->GetErrorMessages() as $sErrorMessage) { + $this->AddErrorMessage(Dict::S($sErrorMessage), $oField->Getid()); + } + } + } return $this->GetValid(); } diff --git a/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php b/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php index b8abf5d75..dac1b0608 100644 --- a/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php +++ b/tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php @@ -181,4 +181,17 @@ PHP ], ]; } + + public function testMakeFormField(): void + { + $oPerson = $this->CreatePerson(1); + $oPerson->Set('email', 'toto@tutu.com'); + $oAttDef = MetaModel::GetAttributeDef(get_class($oPerson), 'email'); + $oFormFieldWithTouchedAtt = $oAttDef->MakeFormField($oPerson); + $this->assertFalse($oFormFieldWithTouchedAtt->IsValidationDisabled(), 'email is part of modified fields, we must have field validation'); + + $oPerson->DBUpdate(); // reset list of changed attributes + $oFormFieldNoTouchedAtt = $oAttDef->MakeFormField($oPerson); + $this->assertTrue($oFormFieldNoTouchedAtt->IsValidationDisabled(), 'email wasn\'t modified, we must not validate the corresponding field'); + } } \ No newline at end of file diff --git a/tests/php-unit-tests/unitary-tests/sources/Form/FieldTest.php b/tests/php-unit-tests/unitary-tests/sources/Form/FieldTest.php new file mode 100644 index 000000000..c0fa04ba7 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/sources/Form/FieldTest.php @@ -0,0 +1,63 @@ +SetCurrentValue('toto@johny.invalid'); + $oDumbEmailValidator = new Validator('\d+@\d+\.\d{2,3}'); + $oField->AddValidator($oDumbEmailValidator); + + $bIsFieldValid = $oField->Validate(); + $this->assertFalse($bIsFieldValid); + + $oField->SetValidationDisabled(true); + $bIsFieldValidWithValidationDisabled = $oField->Validate(); + $this->assertTrue($bIsFieldValidWithValidationDisabled); + } + + public function testSetMandatory(): void + { + $oField = new StringField('test'); + $this->assertCount(0, $oField->GetValidators()); + + $oField->SetMandatory(true); + $aValidatorsAfterSetMandatoryTrue = $oField->GetValidators(); + $this->assertCount(1, $aValidatorsAfterSetMandatoryTrue); + $this->assertIsObject($aValidatorsAfterSetMandatoryTrue[0]); + $this->assertSame(MandatoryValidator::class, get_class($aValidatorsAfterSetMandatoryTrue[0])); + + $oField->SetMandatory(false); + $this->assertCount(0, $oField->GetValidators()); + } + + public function testAddValidator(): void + { + $oField = new StringField('test'); + $this->assertCount(0, $oField->GetValidators()); + + $oField->SetCurrentValue('not a numeric value'); + $this->assertTrue($oField->Validate()); + + $oField->AddValidator(new IntegerValidator()); + $aValidatorsAfterAddingIntegerValidator = $oField->GetValidators(); + $this->assertCount(1, $aValidatorsAfterAddingIntegerValidator); + $this->assertIsObject($aValidatorsAfterAddingIntegerValidator[0]); + $this->assertSame(IntegerValidator::class, get_class($aValidatorsAfterAddingIntegerValidator[0])); + $this->assertFalse($oField->Validate()); + $this->assertCount(1, $oField->GetErrorMessages()); + } +} \ No newline at end of file