Repository: ckan/ckan

Issue:
DataStore view fails with UndefinedError: 'resource' is undefined
### CKAN Version if known (or site URL)
Any commit from master git branch after 8209814cbd0db62eeebcbf21bbebf0383e78c5a3 (e.g. current HEAD - ac1bbc1e681c6b9cda41bbe2d878d0f81c302e4a)

### Please describe the expected behaviour
DataStore view is rendered

i.e. exception when rendering these two highlighted tabs:
<img width="838" alt="screen shot 2018-07-27 at 10 43 51" src="https://user-images.githubusercontent.com/307612/43314207-2d5daf92-918a-11e8-8c8b-bc490475b65d.png">

### Please describe the actual behaviour
DataStore view fails with `UndefinedError: 'resource' is undefined`. This happens after commit 8209814cbd0db62eeebcbf21bbebf0383e78c5a3 in git master branch. I suspect that one of the @smotornyuk's changes from PR #4062 is at fault.

Stack trace
```
Error - <class 'jinja2.exceptions.UndefinedError'>: 'resource' is undefined
URL: http://172.17.0.7:8080/test/resource_data/4ed8742f-a95b-4e53-94ad-b136a2ff8501
File '/usr/lib/python2.7/site-packages/weberror/errormiddleware.py', line 171 in __call__
  app_iter = self.application(environ, sr_checker)
File '/usr/lib/python2.7/site-packages/webob/dec.py', line 147 in __call__
  resp = self.call_func(req, *args, **self.kwargs)
File '/usr/lib/python2.7/site-packages/webob/dec.py', line 208 in call_func
  return self.func(req, *args, **kwargs)
File '/usr/lib/python2.7/site-packages/fanstatic/publisher.py', line 234 in __call__
  return request.get_response(self.app)
File '/usr/lib/python2.7/site-packages/webob/request.py', line 1053 in get_response
  application, catch_exc_info=False)
File '/usr/lib/python2.7/site-packages/webob/request.py', line 1022 in call_application
  app_iter = application(self.environ, start_response)
File '/usr/lib/python2.7/site-packages/webob/dec.py', line 147 in __call__
  resp = self.call_func(req, *args, **self.kwargs)
File '/usr/lib/python2.7/site-packages/webob/dec.py', line 208 in call_func
  return self.func(req, *args, **kwargs)
File '/usr/lib/python2.7/site-packages/fanstatic/injector.py', line 54 in __call__
  response = request.get_response(self.app)
File '/usr/lib/python2.7/site-packages/webob/request.py', line 1053 in get_response
  application, catch_exc_info=False)
File '/usr/lib/python2.7/site-packages/webob/request.py', line 1022 in call_application
  app_iter = application(self.environ, start_response)
File '/srv/ckan/src/ckan/ckan/config/middleware/pylons_app.py', line 265 in inner
  result = application(environ, start_response)
File '/usr/lib/python2.7/site-packages/beaker/middleware.py', line 73 in __call__
  return self.app(environ, start_response)
File '/usr/lib/python2.7/site-packages/beaker/middleware.py', line 156 in __call__
  return self.wrap_app(environ, session_start_response)
File '/usr/lib/python2.7/site-packages/routes/middleware.py', line 131 in __call__
  response = self.app(environ, start_response)
File '/srv/ckan/src/ckan/ckan/config/middleware/common_middleware.py', line 30 in __call__
  return self.app(environ, start_response)
File '/srv/ckan/src/ckan/ckan/config/middleware/common_middleware.py', line 56 in __call__
  return self.app(environ, start_response)
File '/usr/lib/python2.7/site-packages/pylons/wsgiapp.py', line 125 in __call__
  response = self.dispatch(controller, environ, start_response)
File '/usr/lib/python2.7/site-packages/pylons/wsgiapp.py', line 324 in dispatch
  return controller(environ, start_response)
File '/srv/ckan/src/ckan/ckan/lib/base.py', line 241 in __call__
  res = WSGIController.__call__(self, environ, start_response)
File '/usr/lib/python2.7/site-packages/pylons/controllers/core.py', line 221 in __call__
  response = self._dispatch_call()
File '/usr/lib/python2.7/site-packages/pylons/controllers/core.py', line 172 in _dispatch_call
  response = self._inspect_call(func)
File '/usr/lib/python2.7/site-packages/pylons/controllers/core.py', line 107 in _inspect_call
  result = self._perform_call(func, args)
File '/usr/lib/python2.7/site-packages/pylons/controllers/core.py', line 60 in _perform_call
  return func(**args)
File '/srv/ckan/src/ckan/ckanext/datapusher/plugin.py', line 71 in resource_data
  extra_vars={'status': datapusher_status})
File '/srv/ckan/src/ckan/ckan/lib/base.py', line 126 in render
  return cached_template(template_name, renderer)
File '/usr/lib/python2.7/site-packages/pylons/templating.py', line 249 in cached_template
  return render_func()
File '/srv/ckan/src/ckan/ckan/lib/base.py', line 163 in render_template
  return render_jinja2(template_name, globs)
File '/srv/ckan/src/ckan/ckan/lib/base.py', line 95 in render_jinja2
  return template.render(**extra_vars)
File '/usr/lib/python2.7/site-packages/jinja2/environment.py', line 989 in render
  return self.environment.handle_exception(exc_info, True)
File '/usr/lib/python2.7/site-packages/jinja2/environment.py', line 754 in handle_exception
  reraise(exc_type, exc_value, tb)
File '/srv/ckan/src/ckan/ckanext/datapusher/templates/datapusher/resource_data.html', line 1 in top-level template code
  {% extends "package/resource_edit_base.html" %}
File '/srv/ckan/src/ckan/ckanext/datastore/templates/package/resource_edit_base.html', line 1 in top-level template code
  {% ckan_extends %}
File '/srv/ckan/src/ckan/ckanext/datapusher/templates/package/resource_edit_base.html', line 1 in top-level template code
  {% ckan_extends %}
File '/srv/ckan/src/ckan/ckan/templates/package/resource_edit_base.html', line 4 in top-level template code
  {% set res = resource %}
File '/srv/ckan/src/ckan/ckan/templates/package/base.html', line 3 in top-level template code
  {% set pkg = pkg_dict or c.pkg_dict %}
File '/srv/ckan/src/ckan/ckan/templates/page.html', line 1 in top-level template code
  {% extends "base.html" %}
File '/srv/ckan/src/ckanext-geoview/ckanext/geoview/templates/base.html', line 1 in top-level template code
  {% ckan_extends %}
File '/srv/ckan/src/ckan/ckan/templates/base.html', line 41 in top-level template code
  {%- block title -%}
File '/srv/ckan/src/ckan/ckan/templates/base.html', line 42 in block "title"
  {%- block subtitle %}{% endblock -%}
File '/srv/ckan/src/ckan/ckanext/datapusher/templates/datapusher/resource_data.html', line 3 in block "subtitle"
  {% block subtitle %}{{ h.dataset_display_name(pkg) }} - {{ h.resource_display_name(res) }}{% endblock %}
File '/srv/ckan/src/ckan/ckan/lib/helpers.py', line 1607 in resource_display_name
  name = get_translated(resource_dict, 'name')
File '/srv/ckan/src/ckan/ckan/lib/helpers.py', line 2565 in get_translated
  return data_dict[field + u'_translated'][language]
UndefinedError: 'resource' is undefined
```

### What steps can be taken to reproduce the issue? 
1. Install any CKAN from git master after 8209814cbd0db62eeebcbf21bbebf0383e78c5a3.
2. Install any compatible DataPusher (e.g. current git master - https://github.com/ckan/datapusher/commit/bd990de116adf76e685938b2266aa7e8b5187bfd).
3. Enable CKAN integration with DataPusher. 
4. Create a dataset and upload a file.
5. Try to visit DataStore status view.


Discussion:
Yeah, this is correct. I suppose this comes with living on the edge.
This fixes the exceptions for the "DataStore" and "Data Dictionary" tabs, but @smotornyuk is this the right way to go? If so, I'm happy to add a test and submit a PR.

```
diff --git a/ckanext/datapusher/plugin.py b/ckanext/datapusher/plugin.py
index d46a819bb..10736a665 100644
--- a/ckanext/datapusher/plugin.py
+++ b/ckanext/datapusher/plugin.py
@@ -68,7 +68,10 @@ class ResourceDataController(base.BaseController):
             base.abort(403, _('Not authorized to see this page'))

         return base.render('datapusher/resource_data.html',
-                           extra_vars={'status': datapusher_status})
+                           extra_vars={
+                               'status': datapusher_status,
+                               'resource': toolkit.c.resource,
+                           })


 class DatapusherPlugin(p.SingletonPlugin):
diff --git a/ckanext/datastore/controller.py b/ckanext/datastore/controller.py
index bd7fcb452..6fdec567e 100644
--- a/ckanext/datastore/controller.py
+++ b/ckanext/datastore/controller.py
@@ -109,7 +109,10 @@ class DatastoreController(BaseController):

         return render(
             'datastore/dictionary.html',
-            extra_vars={'fields': fields})
+            extra_vars={
+                'fields': fields,
+                'resource': c.resource,
+            })


 def dump_to(resource_id, output, fmt, offset, limit, options):
```
Yes, @davidread , it's the most adequate solution. During the migration of package controller, I updated all templates to use local variables instead of trying to fetch them from `c` object and missed these  datastore/datapusher moments .  Thank you for helping in this :)

Resolving patch:
diff --git a/ckanext/datapusher/plugin.py b/ckanext/datapusher/plugin.py
--- a/ckanext/datapusher/plugin.py
+++ b/ckanext/datapusher/plugin.py
@@ -68,7 +68,11 @@ def resource_data(self, id, resource_id):
             base.abort(403, _('Not authorized to see this page'))
 
         return base.render('datapusher/resource_data.html',
-                           extra_vars={'status': datapusher_status})
+                           extra_vars={
+                               'status': datapusher_status,
+                               'pkg_dict': toolkit.c.pkg_dict,
+                               'resource': toolkit.c.resource,
+                           })
 
 
 class DatapusherPlugin(p.SingletonPlugin):
diff --git a/ckanext/datastore/controller.py b/ckanext/datastore/controller.py
--- a/ckanext/datastore/controller.py
+++ b/ckanext/datastore/controller.py
@@ -109,7 +109,11 @@ def dictionary(self, id, resource_id):
 
         return render(
             'datastore/dictionary.html',
-            extra_vars={'fields': fields})
+            extra_vars={
+                'fields': fields,
+                'pkg_dict': c.pkg_dict,
+                'resource': c.resource,
+            })
 
 
 def dump_to(resource_id, output, fmt, offset, limit, options):


Test patch:
diff --git a/ckanext/datapusher/tests/test_controller.py b/ckanext/datapusher/tests/test_controller.py
new file mode 100644
--- /dev/null
+++ b/ckanext/datapusher/tests/test_controller.py
@@ -0,0 +1,34 @@
+# encoding: utf-8
+
+import nose
+
+import ckan.tests.legacy as tests
+from ckan.tests.helpers import FunctionalTestBase
+import ckan.tests.factories as factories
+
+
+class TestController(FunctionalTestBase):
+    sysadmin_user = None
+    normal_user = None
+
+    _load_plugins = [u'datastore', u'datapusher']
+
+    @classmethod
+    def setup_class(cls):
+        cls.app = cls._get_test_app()
+        if not tests.is_datastore_supported():
+            raise nose.SkipTest(u'Datastore not supported')
+        super(TestController, cls).setup_class()
+
+    def test_resource_data(self):
+        user = factories.User()
+        dataset = factories.Dataset(creator_user_id=user['id'])
+        resource = factories.Resource(package_id=dataset['id'],
+                                      creator_user_id=user['id'])
+        auth = {u'Authorization': str(user['apikey'])}
+
+        self.app.get(
+            url=u'/dataset/{id}/resource_data/{resource_id}'
+            .format(id=str(dataset['name']),
+                    resource_id=str(resource['id'])),
+            extra_environ=auth)
diff --git a/ckanext/datastore/tests/test_dictionary.py b/ckanext/datastore/tests/test_dictionary.py
new file mode 100644
--- /dev/null
+++ b/ckanext/datastore/tests/test_dictionary.py
@@ -0,0 +1,34 @@
+# encoding: utf-8
+
+from ckanext.datastore.tests.helpers import DatastoreFunctionalTestBase, DatastoreLegacyTestBase
+import ckan.tests.factories as factories
+import ckan.tests.helpers as helpers
+
+
+class TestDatastoreDictionary(DatastoreLegacyTestBase):
+
+    @classmethod
+    def setup_class(cls):
+        cls.app = helpers._get_test_app()
+        super(TestDatastoreDictionary, cls).setup_class()
+
+    def test_read(self):
+        user = factories.User()
+        dataset = factories.Dataset(creator_user_id=user['id'])
+        resource = factories.Resource(package_id=dataset['id'],
+                                      creator_user_id=user['id'])
+        data = {
+            u'resource_id': resource['id'],
+            u'force': True,
+            u'records': [
+                {u'from': u'Brazil', u'to': u'Brazil', u'num': 2},
+                {u'from': u'Brazil', u'to': u'Italy', u'num': 22}
+            ],
+        }
+        helpers.call_action(u'datastore_create', **data)
+        auth = {u'Authorization': str(user['apikey'])}
+        self.app.get(
+            url=u'/dataset/{id}/dictionary/{resource_id}'
+            .format(id=str(dataset['name']),
+                    resource_id=str(resource['id'])),
+            extra_environ=auth)