Index: /main/waeup.ikoba/trunk/CHANGES.txt
===================================================================
--- /main/waeup.ikoba/trunk/CHANGES.txt	(revision 15257)
+++ /main/waeup.ikoba/trunk/CHANGES.txt	(revision 15258)
@@ -4,4 +4,8 @@
 0.2.dev0 (unreleased)
 =====================
+
+* Do not allow uploading data with trailing whitespaces.
+
+* Set ssl index URL in buildout.cfg
 
 * Remove 'hidden' attribute from ordereddSelectionList which has
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/browser/pages.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/browser/pages.py	(revision 15257)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/browser/pages.py	(revision 15258)
@@ -1142,9 +1142,14 @@
             invalid_line = check_csv_charset(filecontent.splitlines())
             if invalid_line:
-                self.flash(_(
-                    "Your file contains forbidden characters or "
-                    "has invalid CSV format. "
-                    "First problematic line detected: line %s. "
-                    "Please replace." % invalid_line), type='danger')
+                if invalid_line == -1:
+                    self.flash(_(
+                        "The data in your file contain trailing whitespaces."
+                        "Please replace."), type='danger')
+                else:
+                    self.flash(_(
+                        "Your file contains forbidden characters or "
+                        "has invalid CSV format. "
+                        "First problematic line detected: line %s. "
+                        "Please replace." % invalid_line), type='danger')
                 logger.info('%s - invalid file uploaded: %s' %
                             (ob_class, target))
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/browser/tests/forbidden_file_2.csv
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/browser/tests/forbidden_file_2.csv	(revision 15258)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/browser/tests/forbidden_file_2.csv	(revision 15258)
@@ -0,0 +1,2 @@
+student_id,firstname,lastname,reg_number,date_of_birth,matric_number,email,phone,sex,state
+X666666,Aaren ,Pieri,1,1990-01-02,100000,,1234,M,courses validated
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/browser/tests/test_browser.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/browser/tests/test_browser.py	(revision 15257)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/browser/tests/test_browser.py	(revision 15258)
@@ -36,4 +36,8 @@
 from waeup.ikoba.tests.test_async import FunctionalAsyncTestCase
 
+SAMPLE_FILE = os.path.join(os.path.dirname(__file__), 'test_file.csv')
+FORBIDDEN_FILE = os.path.join(os.path.dirname(__file__), 'forbidden_file.csv')
+FORBIDDEN_FILE_2 = os.path.join(
+    os.path.dirname(__file__), 'forbidden_file_2.csv')
 
 class CompanySetup(FunctionalTestCase):
@@ -133,4 +137,78 @@
             self.datacenter_path + "/@@show/?logname=myspecial.log&query=.*")
         assert "SOME &lt;TAG&gt; AND ÜMLÄUTS IN LOG" in self.browser.contents
+
+    def test_file_download_delete(self):
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open(self.datacenter_path)
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.url, self.datacenter_path)
+        self.browser.getLink("Upload data").click()
+        file = open(SAMPLE_FILE)
+        ctrl = self.browser.getControl(name='uploadfile:file')
+        file_ctrl = ctrl.mech_control
+        file_ctrl.add_file(file, filename='my_test_data.csv')
+        self.browser.getControl('Upload').click()
+        self.browser.open(self.datacenter_path)
+        self.browser.getLink('my_test_data_zope.mgr.csv').click()
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.headers['Content-Type'],
+                         'text/csv; charset=UTF-8')
+        logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'datacenter.log')
+        logcontent = open(logfile).read()
+        self.assertTrue('zope.mgr - browser.pages.FileDownloadView - '
+                        'downloaded: my_test_data_zope.mgr.csv' in logcontent)
+        self.browser.open(self.datacenter_path)
+        ctrl = self.browser.getControl(name='val_id')
+        ctrl.getControl(value='my_test_data_zope.mgr.csv').selected = True
+        self.browser.getControl("Remove selected", index=0).click()
+        self.assertTrue('Successfully deleted: my_test_data_zope.mgr.csv'
+            in self.browser.contents)
+        logcontent = open(logfile).read()
+        self.assertTrue('zope.mgr - browser.pages.DatacenterPage - '
+                        'deleted: my_test_data_zope.mgr.csv' in logcontent)
+        return
+
+    def test_forbidden_file_upload(self):
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open(self.datacenter_path)
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.url, self.datacenter_path)
+        self.browser.getLink("Upload data").click()
+        file = open(FORBIDDEN_FILE)
+        ctrl = self.browser.getControl(name='uploadfile:file')
+        file_ctrl = ctrl.mech_control
+        file_ctrl.add_file(file, filename='my_corrupted_file.csv')
+        self.browser.getControl('Upload').click()
+        self.assertTrue(
+            'Your file contains forbidden characters or'
+            in self.browser.contents)
+        logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'datacenter.log')
+        logcontent = open(logfile).read()
+        self.assertTrue('zope.mgr - browser.pages.DatacenterUploadPage - '
+            'invalid file uploaded:' in logcontent)
+        return
+
+    def test_forbidden_file_upload_2(self):
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open(self.datacenter_path)
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.url, self.datacenter_path)
+        self.browser.getLink("Upload data").click()
+        file = open(FORBIDDEN_FILE_2)
+        ctrl = self.browser.getControl(name='uploadfile:file')
+        file_ctrl = ctrl.mech_control
+        file_ctrl.add_file(file, filename='my_corrupted_file.csv')
+        self.browser.getControl('Upload').click()
+        self.assertTrue(
+            'The data in your file contain trailing whitespaces.'
+            in self.browser.contents)
+        logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'datacenter.log')
+        logcontent = open(logfile).read()
+        self.assertTrue('zope.mgr - browser.pages.DatacenterUploadPage - '
+            'invalid file uploaded:' in logcontent)
+        return
 
     def test_user_data_import_permission(self):
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/utils/helpers.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/utils/helpers.py	(revision 15257)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/utils/helpers.py	(revision 15258)
@@ -756,5 +756,6 @@
 
 def check_csv_charset(iterable):
-    """Check contents of `iterable` regarding valid CSV encoding.
+    """Check contents of `iterable` regarding valid CSV encoding and
+    trailing whitespaces in data.
 
     `iterable` is expected to be an iterable on _rows_ (not
@@ -764,11 +765,15 @@
 
     Returns line num of first illegal char or ``None``. Line nums
-    start counting with 1 (not zero).
+    start counting with 1 (not zero). Returns -1 if data contain
+    trailing whitespaces.
     """
     linenum = 1
-    reader = csv.DictReader(iterable)
     try:
+        reader = csv.DictReader(iterable)
         for row in reader:
             linenum += 1
+            for value in row.values():
+                if value.endswith(' '):
+                    return -1
     except UnicodeDecodeError:
         return linenum
Index: /main/waeup.ikoba/trunk/src/waeup/ikoba/utils/tests/test_helpers.py
===================================================================
--- /main/waeup.ikoba/trunk/src/waeup/ikoba/utils/tests/test_helpers.py	(revision 15257)
+++ /main/waeup.ikoba/trunk/src/waeup/ikoba/utils/tests/test_helpers.py	(revision 15258)
@@ -533,7 +533,16 @@
             'some text that \n'
             '\n'      # this empty line will break
-            'is not a csv file \n' + chr(0x92) + '\n'
+            'is not a csv file\n' + chr(0x92) + '\n'
             ).splitlines()
         self.assertEqual(helpers.check_csv_charset(csv), 2)
+
+    def test_invalid_data3(self):
+        csv = (
+            "code,title,title_prefix\n"
+            "FAC1,Faculty 1,faculty\n"
+            "FAC2,Faculty 2 ,institute\n"
+            "FAC3,Fäcülty 3,school\n"
+            ).splitlines()
+        self.assertEqual(helpers.check_csv_charset(csv), -1)
 
 
