diff --git a/application/Espo/Classes/FieldValidators/VarcharType.php b/application/Espo/Classes/FieldValidators/VarcharType.php index d6849cf407..f2a199cacf 100644 --- a/application/Espo/Classes/FieldValidators/VarcharType.php +++ b/application/Espo/Classes/FieldValidators/VarcharType.php @@ -29,10 +29,18 @@ namespace Espo\Classes\FieldValidators; +use Espo\Core\Utils\Metadata; use Espo\ORM\Entity; class VarcharType { + private Metadata $metadata; + + public function __construct(Metadata $metadata) + { + $this->metadata = $metadata; + } + public function checkRequired(Entity $entity, string $field): bool { return $this->isNotEmpty($entity, $field); @@ -51,8 +59,32 @@ class VarcharType return true; } + public function checkPattern(Entity $entity, string $field, ?string $validationValue): bool + { + if (!$this->isNotEmpty($entity, $field) || !$validationValue) { + return true; + } + + $value = $entity->get($field); + $pattern = $validationValue; + + if ($validationValue[0] === '$') { + $patternName = substr($validationValue, 1); + + $pattern = $this->metadata->get(['app', 'regExpPatterns', $patternName, 'pattern']) ?? + $pattern; + } + + $preparedPattern = '/^' . $pattern . '$/'; + + return (bool) preg_match($preparedPattern, $value); + } + protected function isNotEmpty(Entity $entity, string $field): bool { - return $entity->has($field) && $entity->get($field) !== '' && $entity->get($field) !== null; + return + $entity->has($field) && + $entity->get($field) !== '' && + $entity->get($field) !== null; } } diff --git a/application/Espo/Core/Templates/Metadata/Base/entityDefs.json b/application/Espo/Core/Templates/Metadata/Base/entityDefs.json index 329f6b9144..a75723018d 100644 --- a/application/Espo/Core/Templates/Metadata/Base/entityDefs.json +++ b/application/Espo/Core/Templates/Metadata/Base/entityDefs.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "description": { "type": "text" diff --git a/application/Espo/Core/Templates/Metadata/BasePlus/entityDefs.json b/application/Espo/Core/Templates/Metadata/BasePlus/entityDefs.json index babfc01f73..af224b06d4 100644 --- a/application/Espo/Core/Templates/Metadata/BasePlus/entityDefs.json +++ b/application/Espo/Core/Templates/Metadata/BasePlus/entityDefs.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "description": { "type": "text" diff --git a/application/Espo/Core/Templates/Metadata/CategoryTree/entityDefs.json b/application/Espo/Core/Templates/Metadata/CategoryTree/entityDefs.json index 3f87b31298..056bcbdc3b 100644 --- a/application/Espo/Core/Templates/Metadata/CategoryTree/entityDefs.json +++ b/application/Espo/Core/Templates/Metadata/CategoryTree/entityDefs.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "order": { "type": "int", @@ -106,4 +107,4 @@ } } } -} \ No newline at end of file +} diff --git a/application/Espo/Core/Templates/Metadata/Company/entityDefs.json b/application/Espo/Core/Templates/Metadata/Company/entityDefs.json index 1e96c174f3..4442203c24 100644 --- a/application/Espo/Core/Templates/Metadata/Company/entityDefs.json +++ b/application/Espo/Core/Templates/Metadata/Company/entityDefs.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "description": { "type": "text" diff --git a/application/Espo/Core/Templates/Metadata/Event/entityDefs.json b/application/Espo/Core/Templates/Metadata/Event/entityDefs.json index d15324e15d..ca720b0633 100644 --- a/application/Espo/Core/Templates/Metadata/Event/entityDefs.json +++ b/application/Espo/Core/Templates/Metadata/Event/entityDefs.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "status": { "type": "enum", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Account.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Account.json index ff450d7c1f..ce7b439e02 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Account.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Account.json @@ -4,7 +4,8 @@ "type": "varchar", "maxLength": 249, "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "website": { "type": "url", @@ -86,7 +87,8 @@ "sicCode": { "type": "varchar", "maxLength": 40, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "contactRole": { "type": "varchar", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Call.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Call.json index 62d7d4c2f3..608f410e7f 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Call.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Call.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "status": { "type": "enum", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Campaign.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Campaign.json index 8b94e51ca6..1c87d600dc 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Campaign.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Campaign.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "status": { "type": "enum", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Case.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Case.json index 3111a9844c..fb9d32b8c2 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Case.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Case.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "number": { "type": "autoincrement", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Document.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Document.json index bac0286c78..19c21b8ba3 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Document.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Document.json @@ -4,7 +4,8 @@ "type": "varchar", "required": true, "view": "crm:views/document/fields/name", - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "file": { "type": "file", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/DocumentFolder.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/DocumentFolder.json index 8d0f1a9496..d4ab59a4d0 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/DocumentFolder.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/DocumentFolder.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "description": { "type": "text" diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/KnowledgeBaseArticle.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/KnowledgeBaseArticle.json index b8568ce392..598758aaaa 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/KnowledgeBaseArticle.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/KnowledgeBaseArticle.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "status": { "type": "enum", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/KnowledgeBaseCategory.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/KnowledgeBaseCategory.json index f59ffc5f10..d21fee699b 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/KnowledgeBaseCategory.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/KnowledgeBaseCategory.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "description": { "type": "text" diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Lead.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Lead.json index f126e01592..6c4529823d 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Lead.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Lead.json @@ -27,7 +27,8 @@ }, "title": { "type": "varchar", - "maxLength": 100 + "maxLength": 100, + "pattern": "$noBadCharacters" }, "status": { "type": "enum", @@ -137,7 +138,8 @@ "view": "views/fields/user" }, "accountName": { - "type": "varchar" + "type": "varchar", + "pattern": "$noBadCharacters" }, "assignedUser": { "type": "link", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/MassEmail.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/MassEmail.json index 0be4a63e8f..9dc77881e0 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/MassEmail.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/MassEmail.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "status": { "type": "enum", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Meeting.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Meeting.json index 9673340bd0..921798ebae 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Meeting.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Meeting.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "status": { "type": "enum", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Opportunity.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Opportunity.json index b292e5eb9a..4f61a7c46f 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Opportunity.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Opportunity.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "amount": { "type": "currency", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/TargetList.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/TargetList.json index b9b9f627af..397009f29a 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/TargetList.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/TargetList.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "entryCount": { "type": "int", diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Task.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Task.json index e15296eb2a..d4da884334 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Task.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/Task.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "status": { "type": "enum", diff --git a/application/Espo/Resources/i18n/en_US/Admin.json b/application/Espo/Resources/i18n/en_US/Admin.json index ee0bd14564..c53519689a 100644 --- a/application/Espo/Resources/i18n/en_US/Admin.json +++ b/application/Espo/Resources/i18n/en_US/Admin.json @@ -213,7 +213,8 @@ "onlyDefaultCurrency": "Only default currency", "displayRawText": "Display raw text (no markdown)", "conversionDisabled": "Disable Conversion", - "decimalPlaces": "Decimal Places" + "decimalPlaces": "Decimal Places", + "pattern": "Pattern" }, "strings" : { "rebuildRequired": "Rebuild is required" diff --git a/application/Espo/Resources/i18n/en_US/FieldManager.json b/application/Espo/Resources/i18n/en_US/FieldManager.json index 98ce6ac23e..bd21feeb7a 100644 --- a/application/Espo/Resources/i18n/en_US/FieldManager.json +++ b/application/Espo/Resources/i18n/en_US/FieldManager.json @@ -87,7 +87,8 @@ "fileAccept": "Which file types to accept. It's possible to add custom items.", "barcodeLastChar": "For EAN-13 type.", "maxFileSize": "If empty or 0 then no limit.", - "conversionDisabled": "The currency conversion action won't be applied to this field." + "conversionDisabled": "The currency conversion action won't be applied to this field.", + "pattern": "A regular expression to check a field value against." }, "fieldParts": { "address": { diff --git a/application/Espo/Resources/i18n/en_US/Global.json b/application/Espo/Resources/i18n/en_US/Global.json index 07075e4573..0b8b8ddd37 100644 --- a/application/Espo/Resources/i18n/en_US/Global.json +++ b/application/Espo/Resources/i18n/en_US/Global.json @@ -277,6 +277,8 @@ "notModified": "You have not modified the record", "duplicate": "The record you are creating might already exist", "dropToAttach": "Drop to attach", + "fieldNotMatchingPattern": "{field} does not match the pattern `{pattern}`", + "fieldNotMatchingPattern$noBadCharacters": "{field} contains not allowed characters", "fieldInvalid": "{field} is invalid", "fieldIsRequired": "{field} is required", "fieldShouldBeEmail": "{field} should be a valid email", diff --git a/application/Espo/Resources/metadata/app/regExpPatterns.json b/application/Espo/Resources/metadata/app/regExpPatterns.json new file mode 100644 index 0000000000..518496c1a4 --- /dev/null +++ b/application/Espo/Resources/metadata/app/regExpPatterns.json @@ -0,0 +1,5 @@ +{ + "noBadCharacters": { + "pattern": "[^<>=]+" + } +} diff --git a/application/Espo/Resources/metadata/entityDefs/EmailAccount.json b/application/Espo/Resources/metadata/entityDefs/EmailAccount.json index e63bbe4f7b..ab62af71d8 100644 --- a/application/Espo/Resources/metadata/entityDefs/EmailAccount.json +++ b/application/Espo/Resources/metadata/entityDefs/EmailAccount.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "emailAddress": { "type": "varchar", diff --git a/application/Espo/Resources/metadata/entityDefs/EmailFilter.json b/application/Espo/Resources/metadata/entityDefs/EmailFilter.json index fe724a30b1..9a61724fe4 100644 --- a/application/Espo/Resources/metadata/entityDefs/EmailFilter.json +++ b/application/Espo/Resources/metadata/entityDefs/EmailFilter.json @@ -5,7 +5,8 @@ "required": true, "maxLength": 100, "tooltip": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "from": { "type": "varchar", diff --git a/application/Espo/Resources/metadata/entityDefs/EmailFolder.json b/application/Espo/Resources/metadata/entityDefs/EmailFolder.json index 08e7e27ba2..8ce67de526 100644 --- a/application/Espo/Resources/metadata/entityDefs/EmailFolder.json +++ b/application/Espo/Resources/metadata/entityDefs/EmailFolder.json @@ -4,7 +4,8 @@ "type": "varchar", "required": true, "maxLength": 64, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "order": { "type": "int" diff --git a/application/Espo/Resources/metadata/entityDefs/InboundEmail.json b/application/Espo/Resources/metadata/entityDefs/InboundEmail.json index 79e003abf5..b635135c7f 100644 --- a/application/Espo/Resources/metadata/entityDefs/InboundEmail.json +++ b/application/Espo/Resources/metadata/entityDefs/InboundEmail.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "emailAddress": { "type": "varchar", diff --git a/application/Espo/Resources/metadata/entityDefs/Portal.json b/application/Espo/Resources/metadata/entityDefs/Portal.json index cf5d11a8e5..10eb5fb944 100644 --- a/application/Espo/Resources/metadata/entityDefs/Portal.json +++ b/application/Espo/Resources/metadata/entityDefs/Portal.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "maxLength": 100, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "logo": { "type": "image" diff --git a/application/Espo/Resources/metadata/entityDefs/PortalRole.json b/application/Espo/Resources/metadata/entityDefs/PortalRole.json index dac1d94e0d..3f5604e49d 100644 --- a/application/Espo/Resources/metadata/entityDefs/PortalRole.json +++ b/application/Espo/Resources/metadata/entityDefs/PortalRole.json @@ -4,7 +4,8 @@ "maxLength": 150, "required": true, "type": "varchar", - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "data": { "type": "jsonObject" diff --git a/application/Espo/Resources/metadata/entityDefs/Role.json b/application/Espo/Resources/metadata/entityDefs/Role.json index 086f2178d4..b8d51b3374 100644 --- a/application/Espo/Resources/metadata/entityDefs/Role.json +++ b/application/Espo/Resources/metadata/entityDefs/Role.json @@ -4,7 +4,8 @@ "maxLength": 150, "required": true, "type": "varchar", - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "assignmentPermission": { "type": "enum", diff --git a/application/Espo/Resources/metadata/entityDefs/Team.json b/application/Espo/Resources/metadata/entityDefs/Team.json index 0805f8d7e9..430ae9fc82 100644 --- a/application/Espo/Resources/metadata/entityDefs/Team.json +++ b/application/Espo/Resources/metadata/entityDefs/Team.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "maxLength": 100, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "roles": { "type": "linkMultiple", diff --git a/application/Espo/Resources/metadata/entityDefs/Template.json b/application/Espo/Resources/metadata/entityDefs/Template.json index 4f4e6685d0..3ab1ee7aca 100644 --- a/application/Espo/Resources/metadata/entityDefs/Template.json +++ b/application/Espo/Resources/metadata/entityDefs/Template.json @@ -3,7 +3,8 @@ "name": { "type": "varchar", "required": true, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "body": { "type": "wysiwyg", @@ -125,4 +126,4 @@ "order": "asc" }, "optimisticConcurrencyControl": true -} \ No newline at end of file +} diff --git a/application/Espo/Resources/metadata/entityDefs/User.json b/application/Espo/Resources/metadata/entityDefs/User.json index 6b1a05f530..366fcb61a1 100644 --- a/application/Espo/Resources/metadata/entityDefs/User.json +++ b/application/Espo/Resources/metadata/entityDefs/User.json @@ -91,7 +91,8 @@ "title": { "type": "varchar", "maxLength": 100, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "position": { "type": "varchar", diff --git a/application/Espo/Resources/metadata/fields/address.json b/application/Espo/Resources/metadata/fields/address.json index 5f04eea787..20727173e2 100644 --- a/application/Espo/Resources/metadata/fields/address.json +++ b/application/Espo/Resources/metadata/fields/address.json @@ -17,26 +17,30 @@ "trim": true, "maxLength": 255, "view": "views/fields/address-city", - "customizationOptionsDisabled": true + "customizationOptionsDisabled": true, + "pattern": "$noBadCharacters" }, "state": { "type":"varchar", "trim": true, "maxLength": 255, "view": "views/fields/address-state", - "customizationOptionsDisabled": true + "customizationOptionsDisabled": true, + "pattern": "$noBadCharacters" }, "country": { "type":"varchar", "trim": true, "maxLength": 255, "view": "views/fields/address-country", - "customizationOptionsDisabled": true + "customizationOptionsDisabled": true, + "pattern": "$noBadCharacters" }, "postalCode": { "type":"varchar", "maxLength": 40, - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "map": { "type": "map", diff --git a/application/Espo/Resources/metadata/fields/personName.json b/application/Espo/Resources/metadata/fields/personName.json index 4e3cb3d83e..61acb04911 100644 --- a/application/Espo/Resources/metadata/fields/personName.json +++ b/application/Espo/Resources/metadata/fields/personName.json @@ -21,16 +21,19 @@ }, "first":{ "type":"varchar", - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "last":{ "type":"varchar", - "trim": true + "trim": true, + "pattern": "$noBadCharacters" }, "middle":{ "type": "varchar", "trim": true, - "maxLength": 100 + "maxLength": 100, + "pattern": "$noBadCharacters" } }, "naming":"prefix", @@ -46,4 +49,4 @@ "first", "last" ] -} \ No newline at end of file +} diff --git a/application/Espo/Resources/metadata/fields/varchar.json b/application/Espo/Resources/metadata/fields/varchar.json index 1554efdc9f..c2764e8020 100644 --- a/application/Espo/Resources/metadata/fields/varchar.json +++ b/application/Espo/Resources/metadata/fields/varchar.json @@ -21,6 +21,13 @@ "name": "options", "type": "multiEnum" }, + { + "name": "pattern", + "type": "varchar", + "default": null, + "tooltip": true, + "view": "views/admin/field-manager/fields/pattern" + }, { "name": "audited", "type": "bool" @@ -37,7 +44,8 @@ ], "validationList": [ "required", - "maxLength" + "maxLength", + "pattern" ], "filter": true, "personalData": true, diff --git a/client/src/views/admin/field-manager/fields/pattern.js b/client/src/views/admin/field-manager/fields/pattern.js new file mode 100644 index 0000000000..3516c92bd3 --- /dev/null +++ b/client/src/views/admin/field-manager/fields/pattern.js @@ -0,0 +1,48 @@ +/************************************************************************ + * This file is part of EspoCRM. + * + * EspoCRM - Open Source CRM application. + * Copyright (C) 2014-2022 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko + * Website: https://www.espocrm.com + * + * EspoCRM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EspoCRM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EspoCRM. If not, see http://www.gnu.org/licenses/. + * + * 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 General Public License version 3. + * + * In accordance with Section 7(b) of the GNU General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +define('views/admin/field-manager/fields/pattern', ['views/fields/varchar'], function (Dep) { + + /** + * @class + * @name Class + * @memberOf module:views/admin/field-manager/fields/pattern + * @extends module:views/fields/varchar.Class + */ + return Dep.extend(/** @lends module:views/admin/field-manager/fields/pattern.Class# */{ + + noSpellCheck: true, + + setupOptions: function () { + let patternList = Object.keys(this.getMetadata().get(['app', 'regExpPatterns']) || {}) + .map(item => '$' + item); + + this.setOptionList(patternList); + }, + }) +}); diff --git a/client/src/views/fields/address.js b/client/src/views/fields/address.js index 9b4f76a23b..7d0ab4f647 100644 --- a/client/src/views/fields/address.js +++ b/client/src/views/fields/address.js @@ -26,7 +26,7 @@ * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ -define('views/fields/address', ['views/fields/base'], function (Dep) { +define('views/fields/address', ['views/fields/base', 'views/fields/varchar'], function (Dep, Varchar) { /** * An address field. @@ -56,6 +56,14 @@ define('views/fields/address', ['views/fields/base'], function (Dep) { searchTemplate: 'fields/address/search', + /** + * @inheritDoc + */ + validations: [ + 'required', + 'pattern', + ], + /** * @inheritDoc */ @@ -606,6 +614,23 @@ define('views/fields/address', ['views/fields/base'], function (Dep) { this.model.getFieldParam(this.countryField, 'required'); }, + validatePattern: function () { + let fieldList = [ + this.postalCodeField, + this.stateField, + this.cityField, + this.countryField, + ]; + + let result = false; + + for (let field of fieldList) { + result = Varchar.prototype.fieldValidatePattern.call(this, field) || result; + } + + return result; + }, + fetch: function () { var data = {}; diff --git a/client/src/views/fields/person-name.js b/client/src/views/fields/person-name.js index 82f5dc08f7..71974402e0 100644 --- a/client/src/views/fields/person-name.js +++ b/client/src/views/fields/person-name.js @@ -28,7 +28,13 @@ define('views/fields/person-name', ['views/fields/varchar'], function (Dep) { - return Dep.extend({ + /** + * @class + * @name Class + * @extends module:views/fields/varchar.Class + * @memberOf module:views/fields/person-name + */ + return Dep.extend(/** @lends module:views/fields/person-name.Class# */{ type: 'personName', @@ -42,6 +48,14 @@ define('views/fields/person-name', ['views/fields/varchar'], function (Dep) { editTemplateFirstMiddleLast: 'fields/person-name/edit-first-middle-last', + /** + * @inheritDoc + */ + validations: [ + 'required', + 'pattern', + ], + data: function () { var data = Dep.prototype.data.call(this); @@ -270,6 +284,16 @@ define('views/fields/person-name', ['views/fields/varchar'], function (Dep) { return result; }, + validatePattern: function () { + let result = false; + + result = this.fieldValidatePattern(this.firstField) || result; + result = this.fieldValidatePattern(this.lastField) || result; + result = this.fieldValidatePattern(this.middleField) || result; + + return result; + }, + hasRequiredMarker: function () { if (this.isRequired()) { return true; @@ -281,7 +305,7 @@ define('views/fields/person-name', ['views/fields/varchar'], function (Dep) { this.model.getFieldParam(this.lastField, 'required'); }, - fetch: function (form) { + fetch: function () { var data = {}; data[this.salutationField] = this.$salutation.val() || null; diff --git a/client/src/views/fields/varchar.js b/client/src/views/fields/varchar.js index 4aae402f3d..15ae472a5c 100644 --- a/client/src/views/fields/varchar.js +++ b/client/src/views/fields/varchar.js @@ -306,11 +306,11 @@ define('views/fields/varchar', ['views/fields/base'], function (Dep) { let messageKey = 'fieldNotMatchingPattern'; if (pattern[0] === '$') { - let patternName = pattern; + let patternName = pattern.slice(1); let foundPattern = this.getMetadata().get(['app', 'regExpPatterns', patternName, 'pattern']); if (foundPattern) { - messageKey += patternName; + messageKey += '$' + patternName; pattern = foundPattern; } }