diff --git a/.eslintrc b/.eslintrc index eef33ec8a0..69c731b079 100644 --- a/.eslintrc +++ b/.eslintrc @@ -78,6 +78,7 @@ "has_common": true, "has_words": true, "validate_email": true, + "validate_name": true, "validate_phone": true, "get_number_format": true, "format_number": true, diff --git a/frappe/exceptions.py b/frappe/exceptions.py index ef75a36e03..9a1c1fb0b0 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -78,6 +78,7 @@ class TimestampMismatchError(ValidationError): pass class EmptyTableError(ValidationError): pass class LinkExistsError(ValidationError): pass class InvalidEmailAddressError(ValidationError): pass +class InvalidNameError(ValidationError): pass class InvalidPhoneNumberError(ValidationError): pass class TemplateNotFoundError(ValidationError): pass class UniqueValidationError(ValidationError): pass @@ -95,4 +96,4 @@ class DataTooLongException(ValidationError): pass # OAuth exceptions class InvalidAuthorizationHeader(CSRFTokenError): pass class InvalidAuthorizationPrefix(CSRFTokenError): pass -class InvalidAuthorizationToken(CSRFTokenError): pass \ No newline at end of file +class InvalidAuthorizationToken(CSRFTokenError): pass diff --git a/frappe/model/__init__.py b/frappe/model/__init__.py index 7af987f4bc..93ef78df7b 100644 --- a/frappe/model/__init__.py +++ b/frappe/model/__init__.py @@ -48,7 +48,7 @@ table_fields = ('Table', 'Table MultiSelect') core_doctypes_list = ('DocType', 'DocField', 'DocPerm', 'DocType Action', 'DocType Link', 'User', 'Role', 'Has Role', 'Page', 'Module Def', 'Print Format', 'Report', 'Customize Form', 'Customize Form Field', 'Property Setter', 'Custom Field', 'Custom Script') -data_field_options = ('Email', 'Phone') +data_field_options = ('Email', 'Name', 'Phone') def copytables(srctype, src, srcfield, tartype, tar, tarfield, srcfields, tarfields=[]): if not tarfields: diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 9ab1ef7799..5d1d693f8e 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -571,6 +571,9 @@ class BaseDocument(object): for email_address in frappe.utils.split_emails(data): frappe.utils.validate_email_address(email_address, throw=True) + if data_field_options == "Name": + frappe.utils.validate_name(data, throw=True) + if data_field_options == "Phone": frappe.utils.validate_phone_number(data, throw=True) diff --git a/frappe/public/js/frappe/form/controls/data.js b/frappe/public/js/frappe/form/controls/data.js index a7f0050d65..c943ec89bb 100644 --- a/frappe/public/js/frappe/form/controls/data.js +++ b/frappe/public/js/frappe/form/controls/data.js @@ -96,6 +96,9 @@ frappe.ui.form.ControlData = frappe.ui.form.ControlInput.extend({ if(this.df.options == 'Phone') { this.df.invalid = !validate_phone(v); return v; + } else if (this.df.options == 'Name') { + this.df.invalid = !validate_name(v); + return v; } else if(this.df.options == 'Email') { var email_list = frappe.utils.split_emails(v); if (!email_list) { diff --git a/frappe/public/js/frappe/utils/datatype.js b/frappe/public/js/frappe/utils/datatype.js index 16f87b872f..1b9206f434 100644 --- a/frappe/public/js/frappe/utils/datatype.js +++ b/frappe/public/js/frappe/utils/datatype.js @@ -48,6 +48,10 @@ window.validate_phone = function(txt) { return frappe.utils.validate_type(txt, "phone"); }; +window.validate_name = function(txt) { + return frappe.utils.validate_type(txt, "name"); +}; + window.nth = function(number) { number = cint(number); var s = 'th'; @@ -73,4 +77,4 @@ window.has_common = function(list1, list2) { if(in_list(list2, list1[i]))return true; } return false; -}; \ No newline at end of file +}; diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index 315637092d..f07d47326e 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -237,6 +237,9 @@ Object.assign(frappe.utils, { case "phone": regExp = /^([0-9\ \+\_\-\,\.\*\#\(\)]){1,20}$/; break; + case "name": + regExp = /^[\w][\w\'\-]*([ \w][\w\'\-]+)*$/; + break; case "number": regExp = /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/; break; diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index 649d3bf72c..34432839bb 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -81,13 +81,29 @@ def validate_phone_number(phone_number, throw=False): return False phone_number = phone_number.strip() - match = re.match("([0-9\ \+\_\-\,\.\*\#\(\)]){1,20}$", phone_number) + match = re.match(r"([0-9\ \+\_\-\,\.\*\#\(\)]){1,20}$", phone_number) if not match and throw: frappe.throw(frappe._("{0} is not a valid Phone Number").format(phone_number), frappe.InvalidPhoneNumberError) return bool(match) +def validate_name(name, throw=False): + """Returns True if the name is valid + valid names may have unicode and ascii characters, dash, quotes, numbers + anything else is considered invalid + """ + if not name: + return False + + name = name.strip() + match = re.match(r"^[\w][\w\'\-]*([ \w][\w\'\-]+)*$", name) + + if not match and throw: + frappe.throw(frappe._("{0} is not a valid Name").format(name), frappe.InvalidNameError) + + return bool(match) + def validate_email_address(email_str, throw=False): """Validates the email string""" email = email_str = (email_str or "").strip()