Repository: freedomofpress/securedrop

Issue:
Rename `datetimeformat` to prevent collisions with Babel functions
# Feature request

## Description

Babel injects a function called `datetimeformat` into the jinja environment. We do too, manually. They do not have the same args, and we get `TypeError`s. We should rename our functions to fix this.

## User Stories

As a dev, I don't want to have to track down weird errors if we don't do things in exactly the right order.



Discussion:
I don't think this is the root of the problem because the error we sometime get is about an argument (relative) which exists in both implementation.

The current fix for this iproblem (with the global journalist/source implemenation) s to ensure the test file reloads the file that journalist/source module. See for instance test_i18n.py 

<pre>
    @classmethod
    def teardown_class(cls):
        reload(journalist)
        reload(source)
</pre>
 
I take that back. The [format_dateime](https://github.com/python-babel/flask-babel/blob/master/flask_babel/__init__.py#L381) provided by default does **not** have a **relative** kwargs argument. So the problem is that the babel module gets reloaded (how ?) and resets the jinja filters. And if the journalist/source is not also reloaded, the implementation is not overloaded.

I suspect changing the name won't help. If the filters are reset we will just get an error because the filter does not exist at all. We need to figure out why the jinja filters are being reset after a test.
Doing a `reoload(journalist)` just resets it the same way. `journalist.app` is calling `journanlst_app.create_app` which calls `i18n.setup_app` and also does the jinja stuff. And this happens in the same order every time. The reason this was a problem was that in the refactor, I changed the order and our function was always being overwritten by the babel one.
Ah, that makes sense. 👍 on renaming the function to avoid that kind of headache.

Resolving patch:
diff --git a/securedrop/journalist.py b/securedrop/journalist.py
--- a/securedrop/journalist.py
+++ b/securedrop/journalist.py
@@ -45,7 +45,8 @@
     app.jinja_env.globals['header_image'] = 'logo.png'
     app.jinja_env.globals['use_custom_header_image'] = False
 
-app.jinja_env.filters['datetimeformat'] = template_filters.datetimeformat
+app.jinja_env.filters['rel_datetime_format'] = \
+    template_filters.rel_datetime_format
 app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat
 
 
diff --git a/securedrop/source_app/__init__.py b/securedrop/source_app/__init__.py
--- a/securedrop/source_app/__init__.py
+++ b/securedrop/source_app/__init__.py
@@ -36,8 +36,6 @@ def create_app(config):
     assets = Environment(app)
     app.config['assets'] = assets
 
-    # this set needs to happen *before* we set the jinja filters otherwise
-    # we get name collisions
     i18n.setup_app(app)
 
     app.jinja_env.globals['version'] = version.__version__
@@ -48,7 +46,8 @@ def create_app(config):
         app.jinja_env.globals['header_image'] = 'logo.png'
         app.jinja_env.globals['use_custom_header_image'] = False
 
-    app.jinja_env.filters['datetimeformat'] = template_filters.datetimeformat
+    app.jinja_env.filters['rel_datetime_format'] = \
+        template_filters.rel_datetime_format
     app.jinja_env.filters['nl2br'] = evalcontextfilter(template_filters.nl2br)
     app.jinja_env.filters['filesizeformat'] = template_filters.filesizeformat
 
diff --git a/securedrop/template_filters.py b/securedrop/template_filters.py
--- a/securedrop/template_filters.py
+++ b/securedrop/template_filters.py
@@ -6,7 +6,7 @@
 import math
 
 
-def datetimeformat(dt, fmt=None, relative=False):
+def rel_datetime_format(dt, fmt=None, relative=False):
     """Template filter for readable formatting of datetime.datetime"""
     if relative:
         time = dates.format_timedelta(datetime.utcnow() - dt,


Test patch:
diff --git a/securedrop/tests/test_template_filters.py b/securedrop/tests/test_template_filters.py
--- a/securedrop/tests/test_template_filters.py
+++ b/securedrop/tests/test_template_filters.py
@@ -18,34 +18,36 @@
 
 class TestTemplateFilters(object):
 
-    def verify_datetimeformat(self, app):
+    def verify_rel_datetime_format(self, app):
         with app.test_client() as c:
             c.get('/')
             assert session.get('locale') is None
-            result = template_filters.datetimeformat(
+            result = template_filters.rel_datetime_format(
                 datetime(2016, 1, 1, 1, 1, 1))
             assert "Jan 01, 2016 01:01 AM" == result
 
-            result = template_filters.datetimeformat(
+            result = template_filters.rel_datetime_format(
                 datetime(2016, 1, 1, 1, 1, 1), fmt="yyyy")
             assert "2016" == result
 
             test_time = datetime.utcnow() - timedelta(hours=2)
-            result = template_filters.datetimeformat(test_time, relative=True)
+            result = template_filters.rel_datetime_format(test_time,
+                                                          relative=True)
             assert "2 hours ago" == result
 
             c.get('/?l=fr_FR')
             assert session.get('locale') == 'fr_FR'
-            result = template_filters.datetimeformat(
+            result = template_filters.rel_datetime_format(
                 datetime(2016, 1, 1, 1, 1, 1))
             assert "janv. 01, 2016 01:01 AM" == result
 
-            result = template_filters.datetimeformat(
+            result = template_filters.rel_datetime_format(
                 datetime(2016, 1, 1, 1, 1, 1), fmt="yyyy")
             assert "2016" == result
 
             test_time = datetime.utcnow() - timedelta(hours=2)
-            result = template_filters.datetimeformat(test_time, relative=True)
+            result = template_filters.rel_datetime_format(test_time,
+                                                          relative=True)
             assert "2 heures ago" == result
 
     def verify_filesizeformat(self, app):
@@ -106,7 +108,7 @@ def test_filters(self):
             app.config['BABEL_TRANSLATION_DIRECTORIES'] = config.TEMP_DIR
             i18n.setup_app(app)
             self.verify_filesizeformat(app)
-            self.verify_datetimeformat(app)
+            self.verify_rel_datetime_format(app)
 
     @classmethod
     def teardown_class(cls):