Repository: ckan/ckan

Issue:
Backwards compatibility of templates using "c"
### CKAN Version if known (or site URL)
Current master

### Please describe the expected behaviour
It should be possible to use the same template from 2.8 in the current master, without having to deal with missing variables in `c`.

### Please describe the actual behaviour
I have an extension that overrides the `organization/index.html` page, it is based on the template from CKAN 2.8.1. Therefore some variables from `c` are used, e.g.:

```
<h2><span class="result-count">{{ c.page.item_count }}</span></h2>
```

In CKAN 2.8.1 this works perfectly, but when [running my tests on the current master, it fails](https://travis-ci.org/opendata-swiss/ckanext-switzerland/jobs/441573420#L1332):

```
UndefinedError: 'werkzeug.local.LocalProxy object' has no attribute 'page'
``` 

### What steps can be taken to reproduce the issue?
1. Override the organization/index.html template and access `c.page` in the template
2. Try to render the template using the latest master



Discussion:
I think this "error" was introduced in #4143, since the group view does not expose the old variables in `g`. Now I'm not sure if the goal is actually to be able to use a 2.8 template 1:1 in the current master.

A simple workaround would be to start the template with:

```
{% if c.page %}
   {% set page = c.page %}
{% endif %}
```
And then simply use `page` in this template. I'm just not sure if this is what you want.
Shouldn't there be at least one release that supports both worlds?

cc @amercader @tino097 
Thanks @metaodi, it looks that i need to add the `page` to `g` variable as it was done [here](https://github.com/tino097/ckan/blob/5d051d01edea0e82ac1a1cab3e5870890bd9793f/ckan/views/group.py#L179). 
This is not an error. Makes perfect sense in a reusable extension or one that needs to be compatible with multiple ckan versions, but if we add a block like the one you suggest to ckan it will be dead, untested code. 

ckan's templates are bundled with ckan so they don't need to be compatible with multiple versions. Changes to the templates are also not covered by the deprecate-for-one-version pattern we have for plugin interfaces because the surface exposed for extensions is just too large.
@wardi I think this is a misunderstanding, my block was an example of what I could add to **my template** to make it work with multiple versions of CKAN. I was just not sure if the current idea is to force me as an extension author to add such a block to my code or if I can expect the variables to still be available in `c`.
@metaodi my mistake, I apologise! I think we have have added some `c` backwards compatibility fixes like the one @tino097 linked.

A better comment with the code would be great though. e.g.
```python
# ckan 2.9: Adding variables that were removed from c object for 
# compatibility with templates in existing extensions
```
@tino097 will create a PR adding the missing vars to `c` so please flag any other var that we might have missed.

Going forward the plan is to keep existing vars in `c` for a few versions but not use them in core templates. This will allow existing extensions to keep working. 

All these was mentioned on previous issues and PRs but obviously it needs to be in a more prominent documentation. This is planned to be in the Flask migration for extensions documentation.

Resolving patch:
diff --git a/ckan/views/group.py b/ckan/views/group.py
--- a/ckan/views/group.py
+++ b/ckan/views/group.py
@@ -178,6 +178,8 @@ def index(group_type, is_organization):
     sort_by = request.params.get(u'sort')
 
     # TODO: Remove
+    # ckan 2.9: Adding variables that were removed from c object for
+    # compatibility with templates in existing extensions
     g.q = q
     g.sort_by_selected = sort_by
 
@@ -228,6 +230,11 @@ def index(group_type, is_organization):
 
     extra_vars["page"].items = page_results
     extra_vars["group_type"] = group_type
+
+    # TODO: Remove
+    # ckan 2.9: Adding variables that were removed from c object for
+    # compatibility with templates in existing extensions
+    g.page = extra_vars["page"]
     return base.render(_index_template(group_type), extra_vars)
 
 
@@ -246,6 +253,8 @@ def _read(id, limit, group_type):
     q = request.params.get(u'q', u'')
 
     # TODO: Remove
+    # ckan 2.9: Adding variables that were removed from c object for
+    # compatibility with templates in existing extensions
     g.q = q
 
     # Search within group
@@ -322,6 +331,8 @@ def pager_url(q=None, page=None):
                     search_extras[param] = value
 
         # TODO: Remove
+        # ckan 2.9: Adding variables that were removed from c object for
+        # compatibility with templates in existing extensions
         g.fields = fields
         g.fields_grouped = fields_grouped
 
@@ -368,6 +379,8 @@ def pager_url(q=None, page=None):
             items_per_page=limit)
 
         # TODO: Remove
+        # ckan 2.9: Adding variables that were removed from c object for
+        # compatibility with templates in existing extensions
         g.group_dict['package_count'] = query['count']
 
         extra_vars["search_facets"] = g.search_facets = query['search_facets']
@@ -386,7 +399,9 @@ def pager_url(q=None, page=None):
         extra_vars["query_error"] = True
         extra_vars["page"] = h.Page(collection=[])
 
-    # TODO: Rempve
+    # TODO: Remove
+    # ckan 2.9: Adding variables that were removed from c object for
+    # compatibility with templates in existing extensions
     g.facet_titles = facets
     g.page = extra_vars["page"]
 
@@ -451,6 +466,8 @@ def read(group_type, is_organization, id=None, limit=20):
                              id=group_dict['name'])
 
     # TODO: Remove
+    # ckan 2.9: Adding variables that were removed from c object for
+    # compatibility with templates in existing extensions
     g.q = q
     g.group_dict = group_dict
     g.group = group
@@ -493,6 +510,8 @@ def activity(id, group_type, is_organization, offset=0):
         base.abort(400, error.message)
 
     # TODO: Remove
+    # ckan 2.9: Adding variables that were removed from c object for
+    # compatibility with templates in existing extensions
     g.group_activity_stream = extra_vars["group_activity_stream"]
     g.group_dict = group_dict
 
@@ -510,6 +529,8 @@ def about(id, group_type, is_organization):
     _setup_template_variables(context, {u'id': id}, group_type=group_type)
 
     # TODO: Remove
+    # ckan 2.9: Adding variables that were removed from c object for
+    # compatibility with templates in existing extensions
     g.group_dict = group_dict
     g.group_type = group_type
 
@@ -540,7 +561,9 @@ def members(id, group_type, is_organization):
                    _(u'User %r not authorized to edit members of %s') %
                    (g.user, id))
 
-    # TODO:Remove
+    # TODO: Remove
+    # ckan 2.9: Adding variables that were removed from c object for
+    # compatibility with templates in existing extensions
     g.members = members
     g.group_dict = group_dict
 
@@ -577,6 +600,8 @@ def member_delete(id, group_type, is_organization):
         user_dict = _action(u'group_show')(context, {u'id': user_id})
 
         # TODO: Remove
+        # ckan 2.9: Adding variables that were removed from c object for
+        # compatibility with templates in existing extensions
         g.user_dict = user_dict
         g.user_id = user_id
         g.group_id = id
@@ -611,6 +636,8 @@ def history(id, group_type, is_organization):
             error = \
                 _(u'Select two revisions before doing the comparison.')
             # TODO: Remove
+            # ckan 2.9: Adding variables that were removed from c object for
+            # compatibility with templates in existing extensions
             g.error = error
         else:
             params[u'diff_entity'] = u'group'
@@ -674,9 +701,12 @@ def history(id, group_type, is_organization):
         return feed.writeString(u'utf-8')
 
     # TODO: Remove
+    # ckan 2.9: Adding variables that were removed from c object for
+    # compatibility with templates in existing extensions
     g.group_dict = group_dict
     g.group_revisions = group_revisions
     g.group = group
+
     extra_vars = {
         u"group_dict": group_dict,
         u"group_revisions": group_revisions,
@@ -739,6 +769,8 @@ def followers(id, group_type, is_organization):
         base.abort(403, _(u'Unauthorized to view followers %s') % u'')
 
     # TODO: Remove
+    # ckan 2.9: Adding variables that were removed from c object for
+    # compatibility with templates in existing extensions
     g.group_dict = group_dict
     g.followers = followers
 
@@ -757,6 +789,8 @@ def admins(id, group_type, is_organization):
     admins = authz.get_group_or_org_admin_ids(id)
 
     # TODO: Remove
+    # ckan 2.9: Adding variables that were removed from c object for
+    # compatibility with templates in existing extensions
     g.group_dict = group_dict
     g.admins = admins
 
@@ -805,6 +839,8 @@ def get(self, id, group_type, is_organization):
         # If no action then just show the datasets
         limit = 500
         # TODO: Remove
+        # ckan 2.9: Adding variables that were removed from c object for
+        # compatibility with templates in existing extensions
         g.group_dict = group_dict
         g.group = group
         extra_vars = _read(id, limit, group_type)
@@ -842,6 +878,8 @@ def post(self, id, group_type, is_organization, data=None):
             raise Exception(u'Must be an organization')
 
         # TODO: Remove
+        # ckan 2.9: Adding variables that were removed from c object for
+        # compatibility with templates in existing extensions
         g.group_dict = group_dict
         g.group = group
 
@@ -958,6 +996,8 @@ def get(self, group_type, is_organization,
             _group_form(group_type=group_type), extra_vars)
 
         # TODO: Remove
+        # ckan 2.9: Adding variables that were removed from c object for
+        # compatibility with templates in existing extensions
         g.form = form
 
         extra_vars["form"] = form
@@ -1042,6 +1082,8 @@ def get(self, id, group_type, is_organization,
         form = base.render(_group_form(group_type), extra_vars)
 
         # TODO: Remove
+        # ckan 2.9: Adding variables that were removed from c object for
+        # compatibility with templates in existing extensions
         g.grouptitle = grouptitle
         g.groupname = groupname
         g.data = data


Test patch: