From ef1f09a58dc03d13309baf2219692f72a4d8fc16 Mon Sep 17 00:00:00 2001 From: amercader Date: Wed, 21 Jun 2017 11:21:50 +0100 Subject: [PATCH 01/40] Update version number for 2.5.6b --- ckan/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckan/__init__.py b/ckan/__init__.py index 75bb565ac23..67428c8c5d2 100644 --- a/ckan/__init__.py +++ b/ckan/__init__.py @@ -1,4 +1,4 @@ -__version__ = '2.5.5' +__version__ = '2.5.5b' __description__ = 'CKAN Software' __long_description__ = \ From 505f6b6d0397620671da5d7f20b3d5d91cadccc4 Mon Sep 17 00:00:00 2001 From: Yan Date: Sun, 26 Feb 2017 21:37:39 +0200 Subject: [PATCH 02/40] [#3457]Create new resource view if resource format changed --- ckan/logic/action/update.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index 1ca9b9caa46..919ca23a6f4 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -124,6 +124,7 @@ def resource_update(context, data_dict): model = context['model'] user = context['user'] id = _get_or_bust(data_dict, "id") + old_resource = _get_action('resource_show')(context, {'id': id}) resource = model.Resource.get(id) context["resource"] = resource @@ -172,6 +173,19 @@ def resource_update(context, data_dict): resource = _get_action('resource_show')(context, {'id': id}) + if old_resource['format'] != resource['format']: + q = model.Session.query(model.ResourceView) \ + .filter(model.ResourceView.resource_id == resource['id']) + resources_view_id = q.all() + if resources_view_id: + for view_id in resources_view_id: + _get_action( + 'resource_view_delete')(context, {'id': view_id.id}) + _get_action('package_create_default_resource_views')( + {'model': context['model'], 'user': context['user'], + 'ignore_auth': True}, + {'package': updated_pkg_dict}) + for plugin in plugins.PluginImplementations(plugins.IResourceController): plugin.after_update(context, resource) From 1f0b514a8015c0ae9c0ff52ce006dc4390c4ee98 Mon Sep 17 00:00:00 2001 From: Yan Date: Mon, 22 May 2017 15:36:23 +0300 Subject: [PATCH 03/40] Added test and modification to logic --- ckan/logic/action/update.py | 7 -- ckan/tests/logic/action/test_update.py | 96 ++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 7 deletions(-) diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index 919ca23a6f4..6b9a4a55111 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -174,13 +174,6 @@ def resource_update(context, data_dict): resource = _get_action('resource_show')(context, {'id': id}) if old_resource['format'] != resource['format']: - q = model.Session.query(model.ResourceView) \ - .filter(model.ResourceView.resource_id == resource['id']) - resources_view_id = q.all() - if resources_view_id: - for view_id in resources_view_id: - _get_action( - 'resource_view_delete')(context, {'id': view_id.id}) _get_action('package_create_default_resource_views')( {'model': context['model'], 'user': context['user'], 'ignore_auth': True}, diff --git a/ckan/tests/logic/action/test_update.py b/ckan/tests/logic/action/test_update.py index c85a4e8910c..801ccbe2c92 100644 --- a/ckan/tests/logic/action/test_update.py +++ b/ckan/tests/logic/action/test_update.py @@ -794,6 +794,102 @@ def test_datastore_active_not_present_if_not_provided_and_not_datastore_plugin_e assert 'datastore_active' not in res_returned + def test_resource_format_update(self): + dataset = factories.Dataset() + + # Create resource without format + resource = factories.Resource(package=dataset, + url='http://localhost', + name='Test') + res_views = helpers.call_action( + 'resource_view_list', + id=resource['id']) + + assert_equals(len(res_views), 0) + + # Update resource with format + resource = helpers.call_action( + 'resource_update', + id=resource['id'], + format='CSV') + + # Format changed + assert_equals(resource['format'], 'CSV') + + res_views = helpers.call_action( + 'resource_view_list', + id=resource['id']) + + # View for resource is created + assert_equals(len(res_views), 1) + + second_resource = factories.Resource( + package=dataset, + url='http://localhost', + name='Test2', + format='CSV') + + res_views = helpers.call_action( + 'resource_view_list', + id=second_resource['id']) + + assert_equals(len(res_views), 1) + + second_resource = helpers.call_action( + 'resource_update', + id=second_resource['id'], + format='PNG') + + # Format changed + assert_equals(second_resource['format'], 'PNG') + + res_views = helpers.call_action( + 'resource_view_list', + id=second_resource['id']) + + assert_equals(len(res_views), 2) + + third_resource = factories.Resource( + package=dataset, + url='http://localhost', + name='Test2') + + res_views = helpers.call_action( + 'resource_view_list', + id=third_resource['id']) + + assert_equals(len(res_views), 0) + + third_resource = helpers.call_action( + 'resource_update', + id=third_resource['id'], + format='Test format') + + # Format added + assert_equals(third_resource['format'], 'Test format') + + res_views = helpers.call_action( + 'resource_view_list', + id=third_resource['id']) + + # No view created, cause no such format + assert_equals(len(res_views), 0) + + third_resource = helpers.call_action( + 'resource_update', + id=third_resource['id'], + format='CSV') + + # Format changed + assert_equals(third_resource['format'], 'CSV') + + res_views = helpers.call_action( + 'resource_view_list', + id=third_resource['id']) + + # View is created + assert_equals(len(res_views), 1) + class TestConfigOptionUpdate(object): From 2fb647570a8a40b6e7fd25e387bcff67f07a7951 Mon Sep 17 00:00:00 2001 From: Yan Date: Mon, 22 May 2017 17:49:31 +0300 Subject: [PATCH 04/40] Added logic fix and test fix # Conflicts: # ckan/tests/logic/action/test_update.py --- ckan/logic/action/update.py | 9 +++++---- ckan/tests/logic/action/test_update.py | 11 +++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/ckan/logic/action/update.py b/ckan/logic/action/update.py index 6b9a4a55111..dfddf236224 100644 --- a/ckan/logic/action/update.py +++ b/ckan/logic/action/update.py @@ -124,10 +124,10 @@ def resource_update(context, data_dict): model = context['model'] user = context['user'] id = _get_or_bust(data_dict, "id") - old_resource = _get_action('resource_show')(context, {'id': id}) resource = model.Resource.get(id) context["resource"] = resource + old_resource_format = resource.format if not resource: log.error('Could not find resource ' + id) @@ -173,11 +173,12 @@ def resource_update(context, data_dict): resource = _get_action('resource_show')(context, {'id': id}) - if old_resource['format'] != resource['format']: - _get_action('package_create_default_resource_views')( + if old_resource_format != resource['format']: + _get_action('resource_create_default_resource_views')( {'model': context['model'], 'user': context['user'], 'ignore_auth': True}, - {'package': updated_pkg_dict}) + {'package': updated_pkg_dict, + 'resource': resource}) for plugin in plugins.PluginImplementations(plugins.IResourceController): plugin.after_update(context, resource) diff --git a/ckan/tests/logic/action/test_update.py b/ckan/tests/logic/action/test_update.py index 801ccbe2c92..18bd5671fa4 100644 --- a/ckan/tests/logic/action/test_update.py +++ b/ckan/tests/logic/action/test_update.py @@ -631,8 +631,17 @@ def setup(self): import ckan.model as model model.repo.rebuild_db() + @classmethod + def setup_class(cls): + if not p.plugin_loaded('image_view'): + p.load('image_view') + if not p.plugin_loaded('recline_view'): + p.load('recline_view') + @classmethod def teardown_class(cls): + p.unload('image_view') + p.unload('recline_view') helpers.reset_db() def test_url_only(self): @@ -794,6 +803,8 @@ def test_datastore_active_not_present_if_not_provided_and_not_datastore_plugin_e assert 'datastore_active' not in res_returned + + @helpers.change_config('ckan.views.default_views', 'image_view recline_view') def test_resource_format_update(self): dataset = factories.Dataset() From 3f162b8c5a9b23c07102e2cf66901ea4e0976567 Mon Sep 17 00:00:00 2001 From: Sergey Motornyuk Date: Tue, 2 May 2017 13:09:29 +0300 Subject: [PATCH 05/40] Restrict access to `members` and `bulk_actions` After this fix only users with permission `bulk_update_public` will be able to visit `bulc_process` page and only those who have `group_edit_permissions' will be able to visit `members` page # Conflicts: # ckan/controllers/group.py --- ckan/controllers/group.py | 14 ++++++++++---- ckan/logic/auth/update.py | 30 ++++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/ckan/controllers/group.py b/ckan/controllers/group.py index 8483ea07c0b..054c40e8394 100644 --- a/ckan/controllers/group.py +++ b/ckan/controllers/group.py @@ -405,6 +405,7 @@ def bulk_process(self, id): data_dict = {'id': id} try: + self._check_access('bulk_update_public', context, data_dict) # Do not query for the group datasets when dictizing, as they will # be ignored and get requested on the controller anyway data_dict['include_datasets'] = False @@ -413,7 +414,7 @@ def bulk_process(self, id): except NotFound: abort(404, _('Group not found')) except NotAuthorized: - abort(401, _('Unauthorized to read group %s') % id) + abort(403, _('User %r not authorized to edit %s') % (c.user, id)) #use different form names so that ie7 can be detected form_names = set(["bulk_action.public", "bulk_action.delete", @@ -673,16 +674,21 @@ def members(self, id): 'user': c.user or c.author} try: + data_dict = {'id': id} + self._check_access('group_edit_permissions', context, data_dict) c.members = self._action('member_list')( context, {'id': id, 'object_type': 'user'} ) - data_dict = {'id': id} data_dict['include_datasets'] = False c.group_dict = self._action('group_show')(context, data_dict) - except NotAuthorized: - abort(401, _('Unauthorized to delete group %s') % '') except NotFound: abort(404, _('Group not found')) + except NotAuthorized: + abort( + 403, + _('User %r not authorized to edit members of %s') % ( + c.user, id)) + return self._render_template('group/members.html', group_type) def member_new(self, id): diff --git a/ckan/logic/auth/update.py b/ckan/logic/auth/update.py index a5f4a0e24f1..1d484616b28 100644 --- a/ckan/logic/auth/update.py +++ b/ckan/logic/auth/update.py @@ -173,14 +173,32 @@ def group_edit_permissions(context, data_dict): user = context['user'] group = logic_auth.get_group_object(context, data_dict) - authorized = authz.has_user_permission_for_group_or_org(group.id, - user, - 'update') + authorized = authz.has_user_permission_for_group_or_org( + group.id, user, 'update') if not authorized: - return {'success': False, - 'msg': _('User %s not authorized to edit permissions of group %s') % - (str(user), group.id)} + return { + 'success': False, + 'msg': _('User %s not authorized to' + ' edit permissions of group %s') % + (str(user), group.id)} + else: + return {'success': True} + + +def organization_edit_permissions(context, data_dict): + user = context['user'] + group = logic_auth.get_group_object(context, data_dict) + + authorized = authz.has_user_permission_for_group_or_org( + group.id, user, 'update') + + if not authorized: + return { + 'success': False, + 'msg': _('User %s not authorized to edit' + ' permissions of organization %s') % + (str(user), group.id)} else: return {'success': True} From 7261336a35ff89ea8ec25c96f035ea0a57f77a78 Mon Sep 17 00:00:00 2001 From: Sergey Motornyuk Date: Tue, 2 May 2017 14:27:25 +0300 Subject: [PATCH 06/40] test fixes --- ckan/controllers/group.py | 4 ++-- ckan/logic/auth/update.py | 17 ----------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/ckan/controllers/group.py b/ckan/controllers/group.py index 054c40e8394..f760926ac75 100644 --- a/ckan/controllers/group.py +++ b/ckan/controllers/group.py @@ -405,7 +405,7 @@ def bulk_process(self, id): data_dict = {'id': id} try: - self._check_access('bulk_update_public', context, data_dict) + self._check_access('bulk_update_public', context, {'org_id': id}) # Do not query for the group datasets when dictizing, as they will # be ignored and get requested on the controller anyway data_dict['include_datasets'] = False @@ -675,7 +675,7 @@ def members(self, id): try: data_dict = {'id': id} - self._check_access('group_edit_permissions', context, data_dict) + check_access('group_edit_permissions', context, data_dict) c.members = self._action('member_list')( context, {'id': id, 'object_type': 'user'} ) diff --git a/ckan/logic/auth/update.py b/ckan/logic/auth/update.py index 1d484616b28..8ee8dab0857 100644 --- a/ckan/logic/auth/update.py +++ b/ckan/logic/auth/update.py @@ -186,23 +186,6 @@ def group_edit_permissions(context, data_dict): return {'success': True} -def organization_edit_permissions(context, data_dict): - user = context['user'] - group = logic_auth.get_group_object(context, data_dict) - - authorized = authz.has_user_permission_for_group_or_org( - group.id, user, 'update') - - if not authorized: - return { - 'success': False, - 'msg': _('User %s not authorized to edit' - ' permissions of organization %s') % - (str(user), group.id)} - else: - return {'success': True} - - @logic.auth_allow_anonymous_access def user_update(context, data_dict): user = context['user'] From 210054ad416a4e0d8ddce80c06731467f62adb55 Mon Sep 17 00:00:00 2001 From: Sergey Motornyuk Date: Tue, 2 May 2017 15:56:06 +0300 Subject: [PATCH 07/40] fixes in controller tests --- ckan/tests/controllers/test_group.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ckan/tests/controllers/test_group.py b/ckan/tests/controllers/test_group.py index a240cb00b3b..7fcdb5796f3 100644 --- a/ckan/tests/controllers/test_group.py +++ b/ckan/tests/controllers/test_group.py @@ -264,9 +264,12 @@ def test_membership_list(self): group = self._create_group(user_one['name'], other_users) + env = {'REMOTE_USER': user_one['name'].encode('ascii')} + member_list_url = url_for(controller='group', action='members', id=group['id']) - member_list_response = app.get(member_list_url) + member_list_response = app.get( + member_list_url, extra_environ=env) assert_true('2 members' in member_list_response) @@ -357,7 +360,7 @@ def test_remove_member(self): env = {'REMOTE_USER': user_one['name'].encode('ascii')} remove_response = app.post(remove_url, extra_environ=env, status=302) # redirected to member list after removal - remove_response = remove_response.follow() + remove_response = remove_response.follow(extra_environ=env) assert_true('Group member has been deleted.' in remove_response) assert_true('1 members' in remove_response) From 44a82d732e8be97ac2df03c11b2659ff1a4d6e30 Mon Sep 17 00:00:00 2001 From: Jinfei Fan Date: Wed, 7 Jun 2017 14:11:07 -0400 Subject: [PATCH 08/40] fix broken language toggle url when there is params in current url # Conflicts: # ckan/lib/helpers.py # ckan/templates/snippets/language_selector.html --- ckan/lib/helpers.py | 5 +++++ ckan/templates/snippets/language_selector.html | 3 +-- ckan/tests/controllers/test_package.py | 10 ++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index cd48dc2465a..4d228c228a6 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -347,6 +347,11 @@ def full_current_url(): return (url_for(request.environ['CKAN_CURRENT_URL'], qualified=True)) +def current_url(): + ''' Returns current url unquoted''' + return urllib.unquote(request.environ['CKAN_CURRENT_URL']) + + def lang(): ''' Return the language code for the current locale eg `en` ''' return request.environ.get('CKAN_LANG') diff --git a/ckan/templates/snippets/language_selector.html b/ckan/templates/snippets/language_selector.html index dfe62c1686a..fa48b643ce2 100644 --- a/ckan/templates/snippets/language_selector.html +++ b/ckan/templates/snippets/language_selector.html @@ -1,10 +1,9 @@ -{% set current_url = request.environ.CKAN_CURRENT_URL %} {% set current_lang = request.environ.CKAN_LANG %}