diff --git a/application/Espo/Classes/FieldSanitizers/Date.php b/application/Espo/Classes/FieldSanitizers/Date.php new file mode 100644 index 0000000000..22f5a882ca --- /dev/null +++ b/application/Espo/Classes/FieldSanitizers/Date.php @@ -0,0 +1,70 @@ +. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU Affero General Public License version 3. + * + * In accordance with Section 7(b) of the GNU Affero General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +namespace Espo\Classes\FieldSanitizers; + +use DateTimeImmutable; +use DateTimeInterface; +use Espo\Core\Field\Date as DateValue; +use Espo\Core\FieldSanitize\Sanitizer; +use Espo\Core\FieldSanitize\Sanitizer\Data; +use Espo\Core\Utils\DateTime as DateTimeUtil; +use Exception; + +/** + * @noinspection PhpUnused + */ +class Date implements Sanitizer +{ + public function sanitize(Data $data, string $field): void + { + $value = $data->get($field); + + if ($value === null) { + return; + } + + try { + DateValue::fromString($value); + + return; + } + catch (Exception) {} + + $dateTime = DateTimeImmutable::createFromFormat(DateTimeInterface::ATOM, $value); + + if ($dateTime === false) { + return; + } + + $value = $dateTime->format(DateTimeUtil::SYSTEM_DATE_FORMAT); + + $data->set($field, $value); + } +} diff --git a/application/Espo/Classes/FieldSanitizers/Datetime.php b/application/Espo/Classes/FieldSanitizers/Datetime.php new file mode 100644 index 0000000000..d979628119 --- /dev/null +++ b/application/Espo/Classes/FieldSanitizers/Datetime.php @@ -0,0 +1,73 @@ +. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU Affero General Public License version 3. + * + * In accordance with Section 7(b) of the GNU Affero General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +namespace Espo\Classes\FieldSanitizers; + +use DateTimeImmutable; +use DateTimeInterface; +use DateTimeZone; +use Espo\Core\Field\DateTime as DateTimeValue; +use Espo\Core\FieldSanitize\Sanitizer; +use Espo\Core\FieldSanitize\Sanitizer\Data; +use Espo\Core\Utils\DateTime as DateTimeUtil; +use Exception; + +/** + * @noinspection PhpUnused + */ +class Datetime implements Sanitizer +{ + public function sanitize(Data $data, string $field): void + { + $value = $data->get($field); + + if ($value === null) { + return; + } + + try { + DateTimeValue::fromString($value); + + return; + } + catch (Exception) {} + + $dateTime = DateTimeImmutable::createFromFormat(DateTimeInterface::ATOM, $value); + + if ($dateTime === false) { + return; + } + + $value = $dateTime + ->setTimezone(new DateTimeZone('UTC')) + ->format(DateTimeUtil::SYSTEM_DATE_TIME_FORMAT); + + $data->set($field, $value); + } +} diff --git a/application/Espo/Classes/FieldSanitizers/DatetimeOptionalDate.php b/application/Espo/Classes/FieldSanitizers/DatetimeOptionalDate.php new file mode 100644 index 0000000000..96fbeae4a3 --- /dev/null +++ b/application/Espo/Classes/FieldSanitizers/DatetimeOptionalDate.php @@ -0,0 +1,72 @@ +. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU Affero General Public License version 3. + * + * In accordance with Section 7(b) of the GNU Affero General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +namespace Espo\Classes\FieldSanitizers; + +use DateTimeImmutable; +use DateTimeInterface; +use Espo\Core\Field\Date; +use Espo\Core\FieldSanitize\Sanitizer; +use Espo\Core\FieldSanitize\Sanitizer\Data; +use Espo\Core\Utils\DateTime as DateTimeUtil; +use Exception; + +/** + * @noinspection PhpUnused + */ +class DatetimeOptionalDate implements Sanitizer +{ + public function sanitize(Data $data, string $field): void + { + $attribute = $field . 'Date'; + + $value = $data->get($attribute); + + if ($value === null) { + return; + } + + try { + Date::fromString($value); + + return; + } + catch (Exception) {} + + $dateTime = DateTimeImmutable::createFromFormat(DateTimeInterface::ATOM, $value); + + if ($dateTime === false) { + return; + } + + $value = $dateTime->format(DateTimeUtil::SYSTEM_DATE_FORMAT); + + $data->set($attribute, $value); + } +} diff --git a/application/Espo/Resources/metadata/fields/date.json b/application/Espo/Resources/metadata/fields/date.json index 4a6678498e..2f11c096f8 100644 --- a/application/Espo/Resources/metadata/fields/date.json +++ b/application/Espo/Resources/metadata/fields/date.json @@ -80,5 +80,8 @@ "personalData": true, "valueFactoryClassName": "Espo\\Core\\Field\\Date\\DateFactory", "attributeExtractorClassName": "Espo\\Core\\Field\\Date\\DateAttributeExtractor", + "sanitizerClassNameList": [ + "Espo\\Classes\\FieldSanitizers\\Date" + ], "default": null } diff --git a/application/Espo/Resources/metadata/fields/datetime.json b/application/Espo/Resources/metadata/fields/datetime.json index 37563ce62b..74afb08cb9 100644 --- a/application/Espo/Resources/metadata/fields/datetime.json +++ b/application/Espo/Resources/metadata/fields/datetime.json @@ -94,5 +94,8 @@ "personalData": true, "valueFactoryClassName": "Espo\\Core\\Field\\DateTime\\DateTimeFactory", "attributeExtractorClassName": "Espo\\Core\\Field\\DateTime\\DateTimeAttributeExtractor", + "sanitizerClassNameList": [ + "Espo\\Classes\\FieldSanitizers\\Datetime" + ], "default": null } diff --git a/application/Espo/Resources/metadata/fields/datetimeOptional.json b/application/Espo/Resources/metadata/fields/datetimeOptional.json index 1047ad4d0a..a9bda0f234 100644 --- a/application/Espo/Resources/metadata/fields/datetimeOptional.json +++ b/application/Espo/Resources/metadata/fields/datetimeOptional.json @@ -102,5 +102,9 @@ "personalData": true, "valueFactoryClassName": "Espo\\Core\\Field\\DateTimeOptional\\DateTimeOptionalFactory", "attributeExtractorClassName": "Espo\\Core\\Field\\DateTimeOptional\\DateTimeOptionalAttributeExtractor", + "sanitizerClassNameList": [ + "Espo\\Classes\\FieldSanitizers\\Datetime", + "Espo\\Classes\\FieldSanitizers\\DatetimeOptionalDate" + ], "default": null } diff --git a/tests/integration/Espo/Record/SanitizeTest.php b/tests/integration/Espo/Record/SanitizeTest.php index 3aeef0fd93..2355d1614a 100644 --- a/tests/integration/Espo/Record/SanitizeTest.php +++ b/tests/integration/Espo/Record/SanitizeTest.php @@ -31,7 +31,11 @@ namespace tests\integration\Espo\Record; use Espo\Core\Record\CreateParams; use Espo\Core\Record\ServiceContainer; +use Espo\Entities\User; use Espo\Modules\Crm\Entities\Account; +use Espo\Modules\Crm\Entities\Meeting; +use Espo\Modules\Crm\Entities\Opportunity; +use Espo\Modules\Crm\Entities\Task; use Espo\Tools\FieldManager\FieldManager; use tests\integration\Core\BaseTestCase; @@ -39,6 +43,8 @@ class SanitizeTest extends BaseTestCase { public function testSanitize(): void { + // phone + /** @noinspection PhpUnhandledExceptionInspection */ $this->getInjectableFactory() ->create(FieldManager::class) @@ -101,5 +107,79 @@ class SanitizeTest extends BaseTestCase $this->assertEquals('+380904443322', $numbers[0]); $this->assertEquals('+380904443333', $numbers[1]); + + // datetime + + /** @noinspection PhpUnhandledExceptionInspection */ + $meeting = $this->getContainer() + ->getByClass(ServiceContainer::class) + ->getByClass(Meeting::class) + ->create((object) [ + 'name' => 'Test', + 'dateStart' => '2030-12-10 10:11:12', + 'dateEnd' => '2030-12-10T10:11:12-01:00', + 'assignedUserId' => $this->getContainer()->getByClass(User::class)->getId(), + ], CreateParams::create()); + + $this->assertEquals('2030-12-10 10:11:12', $meeting->get('dateStart')); + $this->assertEquals('2030-12-10 11:11:12', $meeting->get('dateEnd')); + + // datetimeOptional + + /** @noinspection PhpUnhandledExceptionInspection */ + $task = $this->getContainer() + ->getByClass(ServiceContainer::class) + ->getByClass(Task::class) + ->create((object) [ + 'name' => 'Test', + 'dateStartDate' => '2030-12-10T10:11:12-01:00', + 'dateEnd' => '2030-12-10T10:11:12-01:00', + 'assignedUserId' => $this->getContainer()->getByClass(User::class)->getId(), + ], CreateParams::create()); + + $this->assertEquals('2030-12-10', $task->get('dateStartDate')); + $this->assertEquals('2030-12-10 11:11:12', $task->get('dateEnd')); + + /** @noinspection PhpUnhandledExceptionInspection */ + $task = $this->getContainer() + ->getByClass(ServiceContainer::class) + ->getByClass(Task::class) + ->create((object) [ + 'name' => 'Test', + 'dateStartDate' => '2030-12-10', + 'assignedUserId' => $this->getContainer()->getByClass(User::class)->getId(), + ], CreateParams::create()); + + $this->assertEquals('2030-12-10', $task->get('dateStartDate')); + + // date + + /** @noinspection PhpUnhandledExceptionInspection */ + $meeting = $this->getContainer() + ->getByClass(ServiceContainer::class) + ->getByClass(Opportunity::class) + ->create((object) [ + 'name' => 'Test', + 'closeDate' => '2030-12-10T10:11:12-01:00', + 'assignedUserId' => $this->getContainer()->getByClass(User::class)->getId(), + 'probability' => 10, + 'amount' => 1.0, + ], CreateParams::create()); + + $this->assertEquals('2030-12-10', $meeting->get('closeDate')); + + /** @noinspection PhpUnhandledExceptionInspection */ + $meeting = $this->getContainer() + ->getByClass(ServiceContainer::class) + ->getByClass(Opportunity::class) + ->create((object) [ + 'name' => 'Test', + 'closeDate' => '2030-12-10', + 'assignedUserId' => $this->getContainer()->getByClass(User::class)->getId(), + 'probability' => 10, + 'amount' => 1.0, + ], CreateParams::create()); + + $this->assertEquals('2030-12-10', $meeting->get('closeDate')); } }