diff --git a/application/Espo/Resources/i18n/en_US/Global.json b/application/Espo/Resources/i18n/en_US/Global.json index 33481f8969..007ebb4722 100644 --- a/application/Espo/Resources/i18n/en_US/Global.json +++ b/application/Espo/Resources/i18n/en_US/Global.json @@ -265,6 +265,7 @@ "fieldShouldBeGreater": "{field} shouldn't be less then {value}", "fieldBadPasswordConfirm": "{field} not confirmed properly", "fieldMaxFileSizeError": "File should not exceed {max} Mb", + "fieldValueDuplicate": "Duplicate value", "fieldIsUploading": "Uploading in progress", "resetPreferencesDone": "Preferences has been reset to defaults", "confirmation": "Are you sure?", diff --git a/client/src/views/fields/email.js b/client/src/views/fields/email.js index c4467d988b..645a937cf2 100644 --- a/client/src/views/fields/email.js +++ b/client/src/views/fields/email.js @@ -42,20 +42,33 @@ Espo.define('views/fields/email', 'views/fields/varchar', function (Dep) { validateEmailData: function () { var data = this.model.get(this.dataFieldName); - if (data && data.length) { - var re = /\S+@+\S+/; - var notValid = false; - data.forEach(function (row, i) { - var emailAddress = row.emailAddress; - if (!re.test(emailAddress) && emailAddress.indexOf(this.erasedPlaceholder) !== 0) { - var msg = this.translate('fieldShouldBeEmail', 'messages').replace('{field}', this.getLabelText()); - this.showValidationMessage(msg, 'div.email-address-block:nth-child(' + (i + 1).toString() + ') input'); - notValid = true; - } - }, this); - if (notValid) { - return true; + if (!data || !data.length) return; + + var addressList = []; + + var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + + var notValid = false; + + data.forEach(function (row, i) { + var address = row.emailAddress; + var addressLowerCase = String(address).toLowerCase(); + if (!re.test(addressLowerCase) && address.indexOf(this.erasedPlaceholder) !== 0) { + var msg = this.translate('fieldShouldBeEmail', 'messages').replace('{field}', this.getLabelText()); + this.showValidationMessage(msg, 'div.email-address-block:nth-child(' + (i + 1).toString() + ') input'); + notValid = true; + return; } + if (~addressList.indexOf(addressLowerCase)) { + var msg = this.translate('fieldValueDuplicate', 'messages').replace('{field}', this.getLabelText()); + this.showValidationMessage(msg, 'div.email-address-block:nth-child(' + (i + 1).toString() + ') input'); + notValid = true; + return; + } + addressList.push(addressLowerCase); + }, this); + if (notValid) { + return true; } }, diff --git a/client/src/views/fields/phone.js b/client/src/views/fields/phone.js index 07a435f86f..98ded90f96 100644 --- a/client/src/views/fields/phone.js +++ b/client/src/views/fields/phone.js @@ -38,7 +38,7 @@ Espo.define('views/fields/phone', 'views/fields/varchar', function (Dep) { listTemplate: 'fields/phone/list', - validations: ['required'], + validations: ['required', 'phoneData'], validateRequired: function () { if (this.isRequired()) { @@ -50,6 +50,30 @@ Espo.define('views/fields/phone', 'views/fields/varchar', function (Dep) { } }, + validatePhoneData: function () { + var data = this.model.get(this.dataFieldName); + if (!data || !data.length) return; + + var numberList = []; + + var notValid = false; + data.forEach(function (row, i) { + var number = row.phoneNumber; + var numberClean = String(number).replace(/[\s\+]/g, ''); + + if (~numberList.indexOf(numberClean)) { + var msg = this.translate('fieldValueDuplicate', 'messages').replace('{field}', this.getLabelText()); + this.showValidationMessage(msg, 'div.phone-number-block:nth-child(' + (i + 1).toString() + ') input'); + notValid = true; + return; + } + numberList.push(numberClean); + }, this); + if (notValid) { + return true; + } + }, + data: function () { var phoneNumberData; if (this.mode == 'edit') {