Repository: OCHA-DAP/hdx-ckan

Issue:
Resource/Edit:  after saving or deleting a resource redirect to the "Edit Resources" list
Current behavior redirects to the dataset view.   Closely related to #1576 



Discussion:

Resolving patch:
diff --git a/ckan/controllers/package.py b/ckan/controllers/package.py
--- a/ckan/controllers/package.py
+++ b/ckan/controllers/package.py
@@ -148,7 +148,7 @@ def drill_down_url(alternative_url=None, **by):
 
         def remove_field(key, value=None, replace=None):
             return h.remove_url_param(key, value=value, replace=replace,
-                                  controller='package', action='search')
+                                      controller='package', action='search')
 
         c.remove_field = remove_field
 
@@ -222,12 +222,12 @@ def pager_url(q=None, page=None):
             facets = OrderedDict()
 
             default_facet_titles = {
-                    'organization': _('Organizations'),
-                    'groups': _('Groups'),
-                    'tags': _('Tags'),
-                    'res_format': _('Formats'),
-                    'license_id': _('Licenses'),
-                    }
+                'organization': _('Organizations'),
+                'groups': _('Groups'),
+                'tags': _('Tags'),
+                'res_format': _('Formats'),
+                'license_id': _('Licenses'),
+            }
 
             for facet in g.facets:
                 if facet in default_facet_titles:
@@ -279,12 +279,12 @@ def pager_url(q=None, page=None):
                 abort(400, _('Parameter "{parameter_name}" is not '
                              'an integer').format(
                                  parameter_name='_%s_limit' % facet
-                             ))
+                ))
             c.search_facets_limits[facet] = limit
 
         maintain.deprecate_context_item(
-          'facets',
-          'Use `c.search_facets` instead.')
+            'facets',
+            'Use `c.search_facets` instead.')
 
         self._setup_template_variables(context, {},
                                        package_type=package_type)
@@ -402,7 +402,7 @@ def read(self, id, format='html'):
         except ckan.lib.render.TemplateNotFound:
             msg = _("Viewing {package_type} datasets in {format} format is "
                     "not supported (template file {file} not found).".format(
-                    package_type=package_type, format=format, file=template))
+                        package_type=package_type, format=format, file=template))
             abort(404, msg)
 
         assert False, "We should never get here"
@@ -581,15 +581,18 @@ def resource_edit(self, id, resource_id, data=None, errors=None,
         pkg_dict = get_action('package_show')(context, {'id': id})
         if pkg_dict['state'].startswith('draft'):
             # dataset has not yet been fully created
-            resource_dict = get_action('resource_show')(context, {'id': resource_id})
-            fields = ['url', 'resource_type', 'format', 'name', 'description', 'id']
+            resource_dict = get_action('resource_show')(
+                context, {'id': resource_id})
+            fields = [
+                'url', 'resource_type', 'format', 'name', 'description', 'id']
             data = {}
             for field in fields:
                 data[field] = resource_dict[field]
             return self.new_resource(id, data=data)
         # resource is fully created
         try:
-            resource_dict = get_action('resource_show')(context, {'id': resource_id})
+            resource_dict = get_action('resource_show')(
+                context, {'id': resource_id})
         except NotFound:
             abort(404, _('Resource not found'))
         c.pkg_dict = pkg_dict
@@ -627,7 +630,7 @@ def new_resource(self, id, data=None, errors=None, error_summary=None):
             data_provided = False
             for key, value in data.iteritems():
                 if ((value or isinstance(value, cgi.FieldStorage))
-                    and key != 'resource_type'):
+                        and key != 'resource_type'):
                     data_provided = True
                     break
 
@@ -643,7 +646,7 @@ def new_resource(self, id, data=None, errors=None, error_summary=None):
                     abort(401, _('Unauthorized to update dataset'))
                 except NotFound:
                     abort(404,
-                      _('The dataset {id} could not be found.').format(id=id))
+                          _('The dataset {id} could not be found.').format(id=id))
                 if not len(data_dict['resources']):
                     # no data so keep on page
                     msg = _('You must add at least one data resource')
@@ -675,7 +678,7 @@ def new_resource(self, id, data=None, errors=None, error_summary=None):
                 abort(401, _('Unauthorized to create a resource'))
             except NotFound:
                 abort(404,
-                    _('The dataset {id} could not be found.').format(id=id))
+                      _('The dataset {id} could not be found.').format(id=id))
             if save_action == 'go-metadata':
                 # go to final stage of add dataset
                 redirect(h.url_for(controller='package',
@@ -962,7 +965,8 @@ def _save_new(self, context, package_type=None):
                                 id=pkg_dict['name'])
                 redirect(url)
 
-            self._form_save_redirect(pkg_dict['name'], 'new', package_type=package_type)
+            self._form_save_redirect(
+                pkg_dict['name'], 'new', package_type=package_type)
         except NotAuthorized:
             abort(401, _('Unauthorized to read package %s') % '')
         except NotFound, e:
@@ -1013,7 +1017,8 @@ def _save_edit(self, name_or_id, context, package_type=None):
             c.pkg = context['package']
             c.pkg_dict = pkg
 
-            self._form_save_redirect(pkg['name'], 'edit', package_type=package_type)
+            self._form_save_redirect(
+                pkg['name'], 'edit', package_type=package_type)
         except NotAuthorized:
             abort(401, _('Unauthorized to read package %s') % id)
         except NotFound, e:
@@ -1045,7 +1050,8 @@ def _form_save_redirect(self, pkgname, action, package_type=None):
             url = url.replace('<NAME>', pkgname)
         else:
             if package_type is None or package_type == 'dataset':
-                url = h.url_for(controller='package', action='read', id=pkgname)
+                url = h.url_for(
+                    controller='package', action='read', id=pkgname)
             else:
                 url = h.url_for('{0}_read'.format(package_type), id=pkgname)
         redirect(url)
@@ -1088,7 +1094,8 @@ def delete(self, id):
     def resource_delete(self, id, resource_id):
 
         if 'cancel' in request.params:
-            h.redirect_to(controller='package', action='resource_edit', resource_id=resource_id, id=id)
+            h.redirect_to(
+                controller='package', action='resource_edit', resource_id=resource_id, id=id)
 
         context = {'model': model, 'session': model.Session,
                    'user': c.user or c.author, 'auth_user_obj': c.userobj}
@@ -1103,7 +1110,8 @@ def resource_delete(self, id, resource_id):
                 get_action('resource_delete')(context, {'id': resource_id})
                 h.flash_notice(_('Resource has been deleted.'))
                 h.redirect_to(controller='package', action='read', id=id)
-            c.resource_dict = get_action('resource_show')(context, {'id': resource_id})
+            c.resource_dict = get_action('resource_show')(
+                context, {'id': resource_id})
             c.pkg_id = id
         except NotAuthorized:
             abort(401, _('Unauthorized to delete resource %s') % '')
@@ -1196,7 +1204,8 @@ def resource_read(self, id, resource_id):
             c.package['isopen'] = False
 
         # TODO: find a nicer way of doing this
-        c.datastore_api = '%s/api/action' % config.get('ckan.site_url', '').rstrip('/')
+        c.datastore_api = '%s/api/action' % config.get(
+            'ckan.site_url', '').rstrip('/')
 
         c.related_count = c.pkg.related_count
 
@@ -1231,11 +1240,12 @@ def resource_download(self, id, resource_id, filename=None):
             filepath = upload.get_path(rsc['id'])
             fileapp = paste.fileapp.FileApp(filepath)
             try:
-               status, headers, app_iter = request.call_application(fileapp)
+                status, headers, app_iter = request.call_application(fileapp)
             except OSError:
-               abort(404, _('Resource data not found'))
+                abort(404, _('Resource data not found'))
             response.headers.update(dict(headers))
-            content_type, content_enc = mimetypes.guess_type(rsc.get('url',''))
+            content_type, content_enc = mimetypes.guess_type(
+                rsc.get('url', ''))
             response.headers['Content-Type'] = content_type
             response.status = status
             return app_iter
@@ -1256,7 +1266,7 @@ def follow(self, id):
                 package_dict['title']))
         except ValidationError as e:
             error_message = (e.extra_msg or e.message or e.error_summary
-                    or e.error_dict)
+                             or e.error_dict)
             h.flash_error(error_message)
         except NotAuthorized as e:
             h.flash_error(e.extra_msg)
@@ -1275,7 +1285,7 @@ def unfollow(self, id):
                 package_dict['title']))
         except ValidationError as e:
             error_message = (e.extra_msg or e.message or e.error_summary
-                    or e.error_dict)
+                             or e.error_dict)
             h.flash_error(error_message)
         except (NotFound, NotAuthorized) as e:
             error_message = e.extra_msg or e.message
@@ -1292,7 +1302,7 @@ def followers(self, id=None):
             c.pkg_dict = get_action('package_show')(context, data_dict)
             c.pkg = context['package']
             c.followers = get_action('dataset_follower_list')(context,
-                    {'id': c.pkg_dict['id']})
+                                                              {'id': c.pkg_dict['id']})
 
             c.related_count = c.pkg.related_count
         except NotFound:
@@ -1343,19 +1353,17 @@ def groups(self, id):
             redirect(h.url_for(controller='package',
                                action='groups', id=id))
 
-
-
         context['is_member'] = True
         users_groups = get_action('group_list_authz')(context, data_dict)
 
         pkg_group_ids = set(group['id'] for group
-                         in c.pkg_dict.get('groups', []))
+                            in c.pkg_dict.get('groups', []))
         user_group_ids = set(group['id'] for group
-                          in users_groups)
+                             in users_groups)
 
         c.group_dropdown = [[group['id'], group['display_name']]
-                           for group in users_groups if
-                           group['id'] not in pkg_group_ids]
+                            for group in users_groups if
+                            group['id'] not in pkg_group_ids]
 
         for group in c.pkg_dict.get('groups', []):
             group['user_member'] = (group['id'] in user_group_ids)
@@ -1373,8 +1381,8 @@ def activity(self, id):
             c.pkg_dict = get_action('package_show')(context, data_dict)
             c.pkg = context['package']
             c.package_activity_stream = get_action(
-                    'package_activity_list_html')(context,
-                            {'id': c.pkg_dict['id']})
+                'package_activity_list_html')(context,
+                                              {'id': c.pkg_dict['id']})
             c.related_count = c.pkg.related_count
         except NotFound:
             abort(404, _('Dataset not found'))
@@ -1445,7 +1453,8 @@ def _parse_recline_state(self, params):
         # for data api url - see http://trac.ckan.org/ticket/2639
         # fix by relocating this to url attribute which is the default location
         if 'dataset' in recline_state and 'elasticsearch_url' in recline_state['dataset']:
-            recline_state['dataset']['url'] = recline_state['dataset']['elasticsearch_url']
+            recline_state['dataset']['url'] = recline_state[
+                'dataset']['elasticsearch_url']
 
         # Ensure only the currentView is available
         # default to grid view if none specified
diff --git a/ckanext-hdx_package/ckanext/hdx_package/controllers/dataset_controller.py b/ckanext-hdx_package/ckanext/hdx_package/controllers/dataset_controller.py
--- a/ckanext-hdx_package/ckanext/hdx_package/controllers/dataset_controller.py
+++ b/ckanext-hdx_package/ckanext/hdx_package/controllers/dataset_controller.py
@@ -612,3 +612,33 @@ def _resource_preview(self, data_dict):
                     in datapreview.direct() + datapreview.loadable()
                     or datapreview.get_preview_plugin(
                         data_dict, return_first=True))
+
+# copy from package.py:1094
+    def resource_delete(self, id, resource_id):
+
+        if 'cancel' in request.params:
+            h.redirect_to(
+                controller='package', action='resource_edit', resource_id=resource_id, id=id)
+
+        context = {'model': model, 'session': model.Session,
+                   'user': c.user or c.author, 'auth_user_obj': c.userobj}
+
+        try:
+            check_access('package_delete', context, {'id': id})
+        except NotAuthorized:
+            abort(401, _('Unauthorized to delete package %s') % '')
+
+        try:
+            if request.method == 'POST':
+                get_action('resource_delete')(context, {'id': resource_id})
+                h.flash_notice(_('Resource has been deleted.'))
+                h.redirect_to(controller='package', action='resources',
+                              id=id)
+            c.resource_dict = get_action('resource_show')(
+                context, {'id': resource_id})
+            c.pkg_id = id
+        except NotAuthorized:
+            abort(401, _('Unauthorized to delete resource %s') % '')
+        except NotFound:
+            abort(404, _('Resource not found'))
+        return render('package/confirm_delete_resource.html')
diff --git a/ckanext-hdx_package/ckanext/hdx_package/plugin.py b/ckanext-hdx_package/ckanext/hdx_package/plugin.py
--- a/ckanext-hdx_package/ckanext/hdx_package/plugin.py
+++ b/ckanext-hdx_package/ckanext/hdx_package/plugin.py
@@ -20,6 +20,7 @@
 import ckanext.hdx_package.actions.authorize as authorize
 import ckanext.hdx_package.helpers.helpers as hdx_helpers
 
+
 def run_on_startup():
     cache_on_startup = config.get('hdx.cache.onstartup', 'true')
     if 'true' == cache_on_startup:
@@ -28,15 +29,16 @@ def run_on_startup():
 
 
 def _generate_license_list():
-    package.Package._license_register = license.LicenseRegister() 
+    package.Package._license_register = license.LicenseRegister()
     package.Package._license_register.licenses = [
-                                                  license.License(hdx_licenses.LicenseCreativeCommonsIntergovernmentalOrgs()),
-                                                  license.License(license.LicenseCreativeCommonsAttribution()),
-                                                  license.License(license.LicenseCreativeCommonsAttributionShareAlike()),
-                                                  license.License(hdx_licenses.LicenseOtherPublicDomainNoRestrictions()),
-                                                  license.License(hdx_licenses.LicenseHdxMultiple()),
-                                                  license.License(hdx_licenses.LicenseHdxOther())
-                                                  ]
+        license.License(
+            hdx_licenses.LicenseCreativeCommonsIntergovernmentalOrgs()),
+        license.License(license.LicenseCreativeCommonsAttribution()),
+        license.License(license.LicenseCreativeCommonsAttributionShareAlike()),
+        license.License(hdx_licenses.LicenseOtherPublicDomainNoRestrictions()),
+        license.License(hdx_licenses.LicenseHdxMultiple()),
+        license.License(hdx_licenses.LicenseHdxOther())
+    ]
 
 
 def cached_group_list():
@@ -57,23 +59,28 @@ def update_config(self, config):
 
     def before_map(self, map):
         map.connect('storage_file', '/storage/f/{label:.*}', controller='ckanext.hdx_package.controllers.storage_controller:FileDownloadController',
-                  action='file')
-        map.connect('dataset_preselect','/dataset/preselect', controller='ckanext.hdx_package.controllers.dataset_controller:DatasetController', action='preselect')
-        map.connect('resource_edit', '/dataset/{id}/resource_edit/{resource_id}', controller='ckanext.hdx_package.controllers.dataset_controller:DatasetController', action='resource_edit', ckan_icon='edit')
+                    action='file')
+        map.connect('dataset_preselect', '/dataset/preselect',
+                    controller='ckanext.hdx_package.controllers.dataset_controller:DatasetController', action='preselect')
+        map.connect('resource_edit', '/dataset/{id}/resource_edit/{resource_id}',
+                    controller='ckanext.hdx_package.controllers.dataset_controller:DatasetController', action='resource_edit', ckan_icon='edit')
         with SubMapper(map, controller='ckanext.hdx_package.controllers.dataset_controller:DatasetController') as m:
             m.connect('add dataset', '/dataset/new', action='new')
+            m.connect(
+                '/dataset/{id}/resource_delete/{resource_id}', action='resource_delete')
             m.connect('/dataset/{id}.{format}', action='read')
             m.connect('dataset_read', '/dataset/{id}', action='read',
-                  ckan_icon='sitemap')
+                      ckan_icon='sitemap')
             m.connect('/dataset/{action}/{id}',
-                  requirements=dict(action='|'.join([
-                      'new_metadata',
-                      'new_resource',
+                      requirements=dict(action='|'.join([
+                          'new_metadata',
+                          'new_resource',
                       ])))
 
-        map.connect('/indicator/{id}', controller='ckanext.hdx_package.controllers.indicator:IndicatorController', action='read')
+        map.connect(
+            '/indicator/{id}', controller='ckanext.hdx_package.controllers.indicator:IndicatorController', action='read')
         return map
-        
+
     def is_fallback(self):
         return True
 
@@ -82,29 +89,29 @@ def package_types(self):
         return []
 
     def _modify_package_schema(self, schema):
-        
+
         schema.update({
-                'notes': [tk.get_validator('not_empty')], #Notes == description. Makes description required
-                'package_creator': [tk.get_validator('not_empty'),
-                    tk.get_converter('convert_to_extras')],
-                'groups_list': [vd.groups_not_empty],
-                'indicator':[tk.get_validator('ignore_missing'),
-                    tk.get_converter('convert_to_extras')],
-            'caveats' : [tk.get_validator('ignore_missing'),
-                    tk.get_converter('convert_to_extras')],
-            'dataset_source' : [tk.get_validator('not_empty'),
-                    tk.get_converter('convert_to_extras')],
-            'dataset_date' : [tk.get_validator('ignore_missing'),
-                    tk.get_converter('convert_to_extras')],
-            'methodology' : [tk.get_validator('ignore_missing'),
-                    tk.get_converter('convert_to_extras')],
-            'license_other' : [tk.get_validator('ignore_missing'),
-                    tk.get_converter('convert_to_extras')],
-            })
+            # Notes == description. Makes description required
+            'notes': [tk.get_validator('not_empty')],
+            'package_creator': [tk.get_validator('not_empty'),
+                                tk.get_converter('convert_to_extras')],
+            'groups_list': [vd.groups_not_empty],
+            'indicator': [tk.get_validator('ignore_missing'),
+                          tk.get_converter('convert_to_extras')],
+            'caveats': [tk.get_validator('ignore_missing'),
+                        tk.get_converter('convert_to_extras')],
+            'dataset_source': [tk.get_validator('not_empty'),
+                               tk.get_converter('convert_to_extras')],
+            'dataset_date': [tk.get_validator('ignore_missing'),
+                             tk.get_converter('convert_to_extras')],
+            'methodology': [tk.get_validator('ignore_missing'),
+                            tk.get_converter('convert_to_extras')],
+            'license_other': [tk.get_validator('ignore_missing'),
+                              tk.get_converter('convert_to_extras')],
+        })
 
         return schema
 
-
     def create_package_schema(self):
         schema = super(HDXPackagePlugin, self).create_package_schema()
         schema = self._modify_package_schema(schema)
@@ -118,22 +125,23 @@ def update_package_schema(self):
     def show_package_schema(self):
         schema = super(HDXPackagePlugin, self).show_package_schema()
         schema.update({
-            'notes': [tk.get_validator('not_empty')], #Notes == description. Makes description required
+            # Notes == description. Makes description required
+            'notes': [tk.get_validator('not_empty')],
             'package_creator': [tk.get_converter('convert_from_extras'),
-                tk.get_validator('ignore_missing')],
-            'indicator':[tk.get_converter('convert_from_extras'),
-                tk.get_validator('ignore_missing')],
-            'caveats' : [tk.get_converter('convert_from_extras'),
-                tk.get_validator('ignore_missing')],
-            'dataset_source' : [tk.get_converter('convert_from_extras'),
-                tk.get_validator('ignore_missing')],
-            'dataset_date' : [tk.get_converter('convert_from_extras'),
-                tk.get_validator('ignore_missing')],
-            'methodology' : [tk.get_converter('convert_from_extras'),
-                tk.get_validator('ignore_missing')],
-            'license_other' : [tk.get_converter('convert_from_extras'),
-                tk.get_validator('ignore_missing')],
-            })
+                                tk.get_validator('ignore_missing')],
+            'indicator': [tk.get_converter('convert_from_extras'),
+                          tk.get_validator('ignore_missing')],
+            'caveats': [tk.get_converter('convert_from_extras'),
+                        tk.get_validator('ignore_missing')],
+            'dataset_source': [tk.get_converter('convert_from_extras'),
+                               tk.get_validator('ignore_missing')],
+            'dataset_date': [tk.get_converter('convert_from_extras'),
+                             tk.get_validator('ignore_missing')],
+            'methodology': [tk.get_converter('convert_from_extras'),
+                            tk.get_validator('ignore_missing')],
+            'license_other': [tk.get_converter('convert_from_extras'),
+                              tk.get_validator('ignore_missing')],
+        })
         return schema
 
     def get_helpers(self):
@@ -143,12 +151,12 @@ def get_helpers(self):
     def get_actions(self):
         from ckanext.hdx_package.helpers import helpers as hdx_actions
         return {
-                'package_update': update.package_update,
-                'hdx_get_activity_list': hdx_actions.hdx_get_activity_list,
-                'hdx_package_update_metadata': update.hdx_package_update_metadata,
-                'tag_autocomplete': hdx_actions.hdx_tag_autocomplete_list,
-                'package_create': hdx_actions.package_create
-                }
+            'package_update': update.package_update,
+            'hdx_get_activity_list': hdx_actions.hdx_get_activity_list,
+            'hdx_package_update_metadata': update.hdx_package_update_metadata,
+            'tag_autocomplete': hdx_actions.hdx_tag_autocomplete_list,
+            'package_create': hdx_actions.package_create
+        }
 
     def get_auth_functions(self):
         return {'package_create': authorize.package_create,


Test patch: