Repository: ckan/ckan

Issue:
Email validators block email addressees with space at  the end
**CKAN version**
2.9.2
**Describe the bug**
If you enter email address with space at the end, validators don't allow it. 
IT isn't major problem, but it would be nice if address can be trimmed before validating and saving..

**Steps to reproduce**
Steps to reproduce the behavior:
When creating new dataset in author email enter address with space at the end (or start) " test@example.com "

**Expected behavior**
Trim data before validation and saving

**Additional details**
For me this is big issue because i am harvesting data from older portals which don't have email validation, and some of resources are failing because they have spaces in email addresses..
IT can be fixed in harvester but i think this is better place..



Discussion:
@VladimirZD that makes sense. Actually a generic `trim_value` validator that is applied in most text fields would be a really good addition.
In the meantime you can implement a [custom schema](https://github.com/ckan/ckanext-scheming) for your extension and add the trim validator to the email field.

@amercader @VladimirZD , I am interested to work on this issue. I have investigated and found that Email validators not only block email addresses with space at the end but also blocks email addresses starting with the space.
@Gauravp-NEC here's what I would suggest:

* Create a new `strip_value` validator in `validators.py` that trims whitespace at either end of the provided value. Something simple like:
    ```python
    def strip_value(value):
        return value.strip()
    ```
* Add it to the relevant (all?) text fields in the [dataset schema](https://github.com/ckan/ckan/blob/3ec23fec9f904019a246372e60ea228de258b6c3/ckan/logic/schema.py#L102) (and you can also add them to other schemas like the group, user, etc)
* Add a couple of tests in [test_validators.py](https://github.com/ckan/ckan/blob/master/ckan/tests/logic/test_validators.py)
@amercader , Thankyou for your suggestion. 

I want to know why there is a need of strip_value in group schema?
https://github.com/ckan/ckan/blob/3ec23fec9f904019a246372e60ea228de258b6c3/ckan/logic/schema.py#L247-L250

Resolving patch:
diff --git a/ckan/logic/schema.py b/ckan/logic/schema.py
--- a/ckan/logic/schema.py
+++ b/ckan/logic/schema.py
@@ -102,7 +102,7 @@ def default_create_tag_schema(
 def default_create_package_schema(
         duplicate_extras_key, ignore, empty_if_not_sysadmin, ignore_missing,
         unicode_safe, package_id_does_not_exist, not_empty, name_validator,
-        package_name_validator, if_empty_same_as, email_validator,
+        package_name_validator, if_empty_same_as, strip_value, email_validator,
         package_version_validator, ignore_not_package_admin,
         boolean_validator, datasets_with_no_organization_cannot_be_private,
         empty, tag_string_convert, owner_org_validator, no_http):
@@ -114,9 +114,11 @@ def default_create_package_schema(
             not_empty, unicode_safe, name_validator, package_name_validator],
         'title': [if_empty_same_as("name"), unicode_safe],
         'author': [ignore_missing, unicode_safe],
-        'author_email': [ignore_missing, unicode_safe, email_validator],
+        'author_email': [ignore_missing, unicode_safe, strip_value,
+                         email_validator],
         'maintainer': [ignore_missing, unicode_safe],
-        'maintainer_email': [ignore_missing, unicode_safe, email_validator],
+        'maintainer_email': [ignore_missing, unicode_safe, strip_value,
+                             email_validator],
         'license_id': [ignore_missing, unicode_safe],
         'notes': [ignore_missing, unicode_safe],
         'url': [ignore_missing, unicode_safe],
@@ -386,7 +388,7 @@ def default_update_relationship_schema(
 def default_user_schema(
         ignore_missing, unicode_safe, name_validator, user_name_validator,
         user_password_validator, user_password_not_empty, email_is_unique,
-        ignore_not_sysadmin, not_empty, email_validator,
+        ignore_not_sysadmin, not_empty, strip_value, email_validator,
         user_about_validator, ignore, boolean_validator, json_object):
     return {
         'id': [ignore_missing, unicode_safe],
@@ -396,7 +398,8 @@ def default_user_schema(
         'password': [user_password_validator, user_password_not_empty,
                      ignore_missing, unicode_safe],
         'password_hash': [ignore_missing, ignore_not_sysadmin, unicode_safe],
-        'email': [not_empty, email_validator, email_is_unique, unicode_safe],
+        'email': [not_empty, strip_value, email_validator, email_is_unique,
+                  unicode_safe],
         'about': [ignore_missing, user_about_validator, unicode_safe],
         'created': [ignore],
         'sysadmin': [ignore_missing, ignore_not_sysadmin],
@@ -445,13 +448,13 @@ def user_edit_form_schema(
 def default_update_user_schema(
         ignore_missing, name_validator, user_name_validator,
         unicode_safe, user_password_validator, email_is_unique,
-        not_empty, email_validator):
+        not_empty, strip_value, email_validator):
     schema = default_user_schema()
 
     schema['name'] = [
         ignore_missing, name_validator, user_name_validator, unicode_safe]
     schema['email'] = [
-        not_empty, email_validator, email_is_unique, unicode_safe]
+        not_empty, strip_value, email_validator, email_is_unique, unicode_safe]
     schema['password'] = [
         user_password_validator, ignore_missing, unicode_safe]
 
diff --git a/ckan/logic/validators.py b/ckan/logic/validators.py
--- a/ckan/logic/validators.py
+++ b/ckan/logic/validators.py
@@ -883,6 +883,11 @@ def empty_if_not_sysadmin(key, data, errors, context):
                         )
 
 
+def strip_value(value):
+    '''Trims the Whitespace'''
+    return value.strip()
+
+
 def email_validator(value, context):
     '''Validate email input '''
 


Test patch:
diff --git a/ckan/tests/logic/test_validators.py b/ckan/tests/logic/test_validators.py
--- a/ckan/tests/logic/test_validators.py
+++ b/ckan/tests/logic/test_validators.py
@@ -294,6 +294,21 @@ def call_validator(*args, **kwargs):
         call_validator(valid_value)
 
 
+def test_strip_value_with_valid_value():
+    valid_values = [
+        " test@example.com",
+        "  test@example.com",
+        "test@example.com ",
+        "test@example.com  ",
+        " test@example.com ",
+        "  test@example.com  ",
+    ]
+
+    for valid_value in valid_values:
+
+        assert validators.strip_value(valid_value) == "test@example.com"
+
+
 def test_name_validator_with_valid_value():
     """If given a valid string name_validator() should do nothing and
     return the string.