Repository: ckan/ckan

Issue:
Missing c.action attribute in 2.8.0 templates
It seems that in CKAN 2.8.0, the `c.action` attribute is at least sometimes missing when templates are being rendered. I can reproduce this for the `home/index.html` template, which currently breaks ckanext-pages for CKAN 2.8.0 (see ckan/ckanext-pages#78).

It seems that this is a consequence of [a change to `routing.py`](https://github.com/ckan/ckan/pull/3891/files#diff-eb995cd7c3a1045a463f8172c7adb128) as part of #3891, although I'm not sure about that.


Discussion:
@smotornyuk can you have a look at this? 
I'm not sure whether this is the same problem, but it seems that `c` is also missing the `controller` attribute in some cases in 2.8.0. See stadt-karlsruhe/ckanext-discovery#1.

Resolving patch:
diff --git a/ckan/config/middleware/flask_app.py b/ckan/config/middleware/flask_app.py
--- a/ckan/config/middleware/flask_app.py
+++ b/ckan/config/middleware/flask_app.py
@@ -32,6 +32,7 @@
 from ckan.views import (identify_user,
                         set_cors_headers_for_response,
                         check_session_cookie,
+                        set_controller_and_action
                         )
 
 
@@ -279,6 +280,10 @@ def ckan_before_request():
     # Sets g.user and g.userobj
     identify_user()
 
+    # Provide g.controller and g.action for backward compatibility
+    # with extensions
+    set_controller_and_action()
+
 
 def ckan_after_request(response):
     u'''Common handler executed after all Flask requests'''
diff --git a/ckan/plugins/toolkit.py b/ckan/plugins/toolkit.py
--- a/ckan/plugins/toolkit.py
+++ b/ckan/plugins/toolkit.py
@@ -44,6 +44,8 @@ class _Toolkit(object):
         'literal',
         # get logic action function
         'get_action',
+        # get flask/pylons endpoint fragments
+        'get_endpoint',
         # decorator for chained action
         'chained_action',
         # get navl schema converter
@@ -283,6 +285,7 @@ def _initialize(self):
         t['add_ckan_admin_tab'] = self._add_ckan_admin_tabs
         t['requires_ckan_version'] = self._requires_ckan_version
         t['check_ckan_version'] = self._check_ckan_version
+        t['get_endpoint'] = self._get_endpoint
         t['CkanVersionException'] = CkanVersionException
         t['HelperError'] = HelperError
         t['enqueue_job'] = enqueue_job
@@ -456,6 +459,26 @@ def _requires_ckan_version(cls, min_version, max_version=None):
                 )
             raise CkanVersionException(error)
 
+    @classmethod
+    def _get_endpoint(cls):
+        """Returns tuple in format: (controller|blueprint, action|view).
+        """
+        import ckan.common as common
+        try:
+            # CKAN >= 2.8
+            endpoint = tuple(common.request.endpoint.split('.'))
+        except AttributeError:
+            try:
+                return common.c.controller, common.c.action
+            except AttributeError:
+                return (None, None)
+        # there are some routes('hello_world') that are not using blueprint
+        # For such case, let's assume that view function is a controller
+        # itself and action is None.
+        if len(endpoint) is 1:
+            return endpoint + (None,)
+        return endpoint
+
     def __getattr__(self, name):
         ''' return the function/object requested '''
         if not self._toolkit:
diff --git a/ckan/views/__init__.py b/ckan/views/__init__.py
--- a/ckan/views/__init__.py
+++ b/ckan/views/__init__.py
@@ -179,3 +179,14 @@ def _get_user_for_apikey():
     query = model.Session.query(model.User)
     user = query.filter_by(apikey=apikey).first()
     return user
+
+
+def set_controller_and_action():
+    try:
+        controller, action = request.endpoint.split(u'.')
+    except ValueError:
+        log.debug(
+            u'Endpoint does not contain dot: {}'.format(request.endpoint)
+        )
+        controller = action = request.endpoint
+    g.controller, g.action = controller, action


Test patch: