From 3d41beb3435951a23b17c2e32e6f0dbd3a0183c5 Mon Sep 17 00:00:00 2001 From: joetsoi Date: Wed, 20 Mar 2013 08:59:23 +0000 Subject: [PATCH 01/32] [#618] create symlink from docs/CONTRIBUTING.rst to CONTRIBUTING.txt --- doc/CONTRIBUTING.rst | 1 + 1 file changed, 1 insertion(+) create mode 120000 doc/CONTRIBUTING.rst diff --git a/doc/CONTRIBUTING.rst b/doc/CONTRIBUTING.rst new file mode 120000 index 00000000000..798f2aa2fc5 --- /dev/null +++ b/doc/CONTRIBUTING.rst @@ -0,0 +1 @@ +../CONTRIBUTING.rst \ No newline at end of file From 9f0ff9d64d364440105e00e0ebdf348054b6ef54 Mon Sep 17 00:00:00 2001 From: tobes Date: Wed, 20 Mar 2013 17:24:29 +0000 Subject: [PATCH 02/32] [#606] Fix no resource error to not me flash message --- ckan/controllers/package.py | 13 ++++++++++--- ckan/lib/app_globals.py | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/ckan/controllers/package.py b/ckan/controllers/package.py index 00cc0b09a37..8ae03c1094a 100644 --- a/ckan/controllers/package.py +++ b/ckan/controllers/package.py @@ -611,9 +611,16 @@ def new_resource(self, id, data=None, errors=None, error_summary=None): abort(401, _('Unauthorized to update dataset')) if not len(data_dict['resources']): # no data so keep on page - h.flash_error(_('You must add at least one data resource')) - redirect(h.url_for(controller='package', - action='new_resource', id=id)) + msg = _('You must add at least one data resource') + # On new templates do not use flash message + if g.legacy_templates: + h.flash_error(msg) + redirect(h.url_for(controller='package', + action='new_resource', id=id)) + else: + errors = {} + error_summary = {_('Error'): msg} + return self.new_resource(id, data, errors, error_summary) # we have a resource so let them add metadata redirect(h.url_for(controller='package', action='new_metadata', id=id)) diff --git a/ckan/lib/app_globals.py b/ckan/lib/app_globals.py index e98f975ca7d..ce32ca74505 100644 --- a/ckan/lib/app_globals.py +++ b/ckan/lib/app_globals.py @@ -57,6 +57,7 @@ 'openid_enabled': {'default': 'true', 'type' : 'bool'}, 'debug': {'default': 'false', 'type' : 'bool'}, 'ckan.debug_supress_header' : {'default': 'false', 'type' : 'bool'}, + 'ckan.legacy_templates' : {'default': 'false', 'type' : 'bool'}, # int 'ckan.datasets_per_page': {'default': '20', 'type': 'int'}, From e061b0c59af5a58032996b1e8bd80888ef941b97 Mon Sep 17 00:00:00 2001 From: tobes Date: Wed, 27 Mar 2013 12:23:23 +0000 Subject: [PATCH 03/32] [#509] Fix bug in add/remove groups --- ckan/lib/dictization/model_save.py | 35 ++++++++++++++++++------------ 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/ckan/lib/dictization/model_save.py b/ckan/lib/dictization/model_save.py index a87b53eb4dc..b43c49ffb78 100644 --- a/ckan/lib/dictization/model_save.py +++ b/ckan/lib/dictization/model_save.py @@ -233,29 +233,36 @@ def package_membership_list_save(group_dicts, package, context): ## need to flush so we can get out the package id model.Session.flush() - for group in groups - set(group_member.keys()): - if group: - member_obj = model.Member(table_id = package.id, - table_name = 'package', - group = group, - capacity = capacity, - group_id=group.id, - state = 'active') - session.add(member_obj) + + # Remove any groups we are no longer in for group in set(group_member.keys()) - groups: member_obj = group_member[group] + if member_obj and member_obj.state == 'deleted': + continue if new_authz.has_user_permission_for_group_or_org( member_obj.group_id, user, 'read'): member_obj.capacity = capacity member_obj.state = 'deleted' session.add(member_obj) - for group in set(group_member.keys()) & groups: - member_obj = group_member[group] + # Add any new groups + for group in groups: + member_obj = group_member.get(group) + if member_obj and member_obj.state == 'active': + continue if new_authz.has_user_permission_for_group_or_org( - member_obj.group_id, user, 'read'): - member_obj.capacity = capacity - member_obj.state = 'active' + group.id, user, 'read'): + member_obj = group_member.get(group) + if member_obj: + member_obj.capacity = capacity + member_obj.state = 'active' + else: + member_obj = model.Member(table_id=package.id, + table_name='package', + group=group, + capacity=capacity, + group_id=group.id, + state = 'active') session.add(member_obj) From 7551cbbc241c0304cb767f572965aac1e8142034 Mon Sep 17 00:00:00 2001 From: tobes Date: Wed, 3 Apr 2013 10:08:43 +0100 Subject: [PATCH 04/32] [#509] Test fixups --- ckan/lib/dictization/model_save.py | 3 ++- ckan/tests/lib/test_dictization.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ckan/lib/dictization/model_save.py b/ckan/lib/dictization/model_save.py index b43c49ffb78..27a7822ab45 100644 --- a/ckan/lib/dictization/model_save.py +++ b/ckan/lib/dictization/model_save.py @@ -229,7 +229,8 @@ def package_membership_list_save(group_dicts, package, context): group = session.query(model.Group).get(id) else: group = session.query(model.Group).filter_by(name=name).first() - groups.add(group) + if group: + groups.add(group) ## need to flush so we can get out the package id model.Session.flush() diff --git a/ckan/tests/lib/test_dictization.py b/ckan/tests/lib/test_dictization.py index 19f89aeba2f..abfbab4ac9b 100644 --- a/ckan/tests/lib/test_dictization.py +++ b/ckan/tests/lib/test_dictization.py @@ -359,6 +359,7 @@ def test_07_table_simple_save(self): def test_08_package_save(self): context = {"model": model, + "user": 'testsysadmin', "session": model.Session} anna1 = model.Session.query(model.Package).filter_by(name='annakarenina').one() From 2b46a450ce4ce118599a86ecefd3d4b8baba8d32 Mon Sep 17 00:00:00 2001 From: John Martin Date: Thu, 4 Apr 2013 12:47:51 +0100 Subject: [PATCH 05/32] [#726] Fix page zoom out bug up to zoom:0.6 --- ckan/public/base/less/layout.less | 3 +-- ckan/public/base/less/mixins.less | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/ckan/public/base/less/layout.less b/ckan/public/base/less/layout.less index a35f83f93a5..f0675a29b08 100644 --- a/ckan/public/base/less/layout.less +++ b/ckan/public/base/less/layout.less @@ -28,8 +28,7 @@ } [role=main] .primary { - width: 719px; - margin-left: 1px; + width: 717px; float: right; } diff --git a/ckan/public/base/less/mixins.less b/ckan/public/base/less/mixins.less index d53430356fe..a8bbbf9c0fc 100644 --- a/ckan/public/base/less/mixins.less +++ b/ckan/public/base/less/mixins.less @@ -134,8 +134,6 @@ a.tag:hover { .box { background-color: #FFF; - margin-left: -1px; - margin-right: -1px; border: 1px solid @layoutTrimBorderColor; .border-radius(4px); .box-shadow(0 0 0 4px rgba(0, 0, 0, 0.05)); From f9bb3ee1146acc10f11eb0a9c62968257335f8aa Mon Sep 17 00:00:00 2001 From: kindly Date: Sun, 7 Apr 2013 02:45:50 +0100 Subject: [PATCH 06/32] 739 add extra options to solr query --- ckan/lib/search/query.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/ckan/lib/search/query.py b/ckan/lib/search/query.py index e43825f8d1e..5d6c519ebac 100644 --- a/ckan/lib/search/query.py +++ b/ckan/lib/search/query.py @@ -16,9 +16,9 @@ _open_licenses = None VALID_SOLR_PARAMETERS = set([ - 'q', 'fl', 'fq', 'rows', 'sort', 'start', 'wt', 'qf', + 'q', 'fl', 'fq', 'rows', 'sort', 'start', 'wt', 'qf', 'bf', 'facet', 'facet.mincount', 'facet.limit', 'facet.field', - 'extras' # Not used by Solr, but useful for extensions + 'extras', 'fq_list', 'tie', 'defType', 'mm' ]) # for (solr) package searches, this specifies the fields that are searched @@ -332,7 +332,10 @@ def run(self, query): # filter for package status if not '+state:' in fq: fq += " +state:active" - query['fq'] = fq + query['fq'] = [fq] + + fq_list = query.get('fq_list', []) + query['fq'].extend(fq_list) # faceting query['facet'] = query.get('facet', 'true') @@ -346,12 +349,13 @@ def run(self, query): query['wt'] = query.get('wt', 'json') # If the query has a colon in it then consider it a fielded search and do use dismax. - if ':' not in query['q']: - query['defType'] = 'dismax' - query['tie'] = '0.1' + defType = query.get('defType', 'dismax') + if ':' not in query['q'] or defType == 'edismax': + query['defType'] = defType + query['tie'] = query.get('tie', '0.1') # this minimum match is explained # http://wiki.apache.org/solr/DisMaxQParserPlugin#mm_.28Minimum_.27Should.27_Match.29 - query['mm'] = '2<-1 5<80%' + query['mm'] = query.get('mm', '2<-1 5<80%') query['qf'] = query.get('qf', QUERY_FIELDS) conn = make_connection() From 4e149cbd888e93465c77731a7f18f603f6299ec8 Mon Sep 17 00:00:00 2001 From: kindly Date: Mon, 8 Apr 2013 16:29:38 +0100 Subject: [PATCH 07/32] #739 remove whitespace between : and term for comaptibility with edismax search --- ckan/controllers/group.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckan/controllers/group.py b/ckan/controllers/group.py index f3f8508c6f4..1ba3509faa9 100644 --- a/ckan/controllers/group.py +++ b/ckan/controllers/group.py @@ -197,9 +197,9 @@ def _read(self, id, limit): q = c.q = request.params.get('q', '') # Search within group if c.group_dict.get('is_organization'): - q += ' owner_org: "%s"' % c.group_dict.get('id') + q += ' owner_org:"%s"' % c.group_dict.get('id') else: - q += ' groups: "%s"' % c.group_dict.get('name') + q += ' groups:"%s"' % c.group_dict.get('name') try: description_formatted = ckan.misc.MarkdownFormat().to_html( From 48da70a71de2a0c2d3ed7da152dd289f2f166a51 Mon Sep 17 00:00:00 2001 From: John Martin Date: Tue, 9 Apr 2013 13:50:09 +0100 Subject: [PATCH 08/32] [#740] Adds ellipsis wrapper to resource_read --- ckan/public/base/less/prose.less | 6 ++++++ ckan/templates/package/resource_read.html | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ckan/public/base/less/prose.less b/ckan/public/base/less/prose.less index c3846d7db27..315668a0227 100644 --- a/ckan/public/base/less/prose.less +++ b/ckan/public/base/less/prose.less @@ -68,3 +68,9 @@ border-bottom-right-radius: @radius; } } + +.ellipsis { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/ckan/templates/package/resource_read.html b/ckan/templates/package/resource_read.html index 80156ff93dd..6e607ea3407 100644 --- a/ckan/templates/package/resource_read.html +++ b/ckan/templates/package/resource_read.html @@ -46,7 +46,7 @@ {% block resource_read_title %}

{{ h.resource_display_name(res) | truncate(50) }}

{% endblock %} {% block resource_read_url %} {% if res.url %} -

{{ _('URL:') }} {{ res.url }}

+

{{ _('URL:') }} {{ res.url }}

{% endif %} {% endblock %}
From 04d8df80da19579bd551e469a879f12c8dedb15c Mon Sep 17 00:00:00 2001 From: Sean Hammond Date: Wed, 10 Apr 2013 17:25:43 +0200 Subject: [PATCH 09/32] [#618] Rename contributing -> CONTRIBUTING in docs The source file in the root of this git repo is still called CONTRIBUTING.rst, but the symlink to it in docs/ is now lower-case contributing.rst. This means the docs.ckan.org URL is /contributing.html not /CONTRIBUTING.html. --- doc/{CONTRIBUTING.rst => contributing.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/{CONTRIBUTING.rst => contributing.rst} (100%) diff --git a/doc/CONTRIBUTING.rst b/doc/contributing.rst similarity index 100% rename from doc/CONTRIBUTING.rst rename to doc/contributing.rst From 25852a2032f11d3c3117a3cfa29c81643c545c2f Mon Sep 17 00:00:00 2001 From: Sean Hammond Date: Wed, 10 Apr 2013 17:27:43 +0200 Subject: [PATCH 10/32] [#618] Add contributing docs to docs index --- doc/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/index.rst b/doc/index.rst index aad7b737ca1..8737221c45c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -92,6 +92,7 @@ For CKAN Developers .. toctree:: :maxdepth: 1 + contributing architecture python-coding-standards javascript-coding-standards From 3428f8a5c1f873bfa36dfb08fa62eb226bf9092c Mon Sep 17 00:00:00 2001 From: Sean Hammond Date: Wed, 10 Apr 2013 17:37:24 +0200 Subject: [PATCH 11/32] [#618] Fix a broken link in CONTRIBUTING.rst --- CONTRIBUTING.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 6cecad332a1..c1a46c16a34 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -5,7 +5,6 @@ Contributing to CKAN .. _CKAN repo on GitHub: https://github.com/okfn/ckan .. _CKAN issue tracker: https://github.com/okfn/ckan/issues .. _docs.ckan.org: http://docs.ckan.org -.. _Contributing to CKAN's Documentation: https://github.com/okfn/ckan/blob/master/CONTRIBUTING.rst#contributing-to-ckans-documentation (This section is about contributing code, if you want to contribute documentation see `Contributing to CKAN's Documentation`_.) From 2ab6084a5e7e8ecd109a65d6b5dd2dc53f5f368c Mon Sep 17 00:00:00 2001 From: Sean Hammond Date: Wed, 10 Apr 2013 17:38:40 +0200 Subject: [PATCH 12/32] [#618] Move Coding Standards link to top of CONTRIBUTING.rst Make it more prominent. --- CONTRIBUTING.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index c1a46c16a34..e3d8c461661 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -15,6 +15,13 @@ your code to a feature branch on your fork, then make a pull request for your branch on the central CKAN repo. We'll go through each step in detail below... +Coding Standards +---------------- + +When writing code for CKAN, try to follow our +`coding standards `_. + + Fork CKAN on GitHub ------------------- @@ -132,13 +139,6 @@ When merging a feature or bug branch into master: - Use the ``--no-ff`` option in the ``git merge`` command, -Coding Standards ----------------- - -When writing code for CKAN, try to follow our -`coding standards `_. - - ==================================== Contributing to CKAN's Documentation ==================================== From 09220c47854666bd126a8bc8125621abbc6b399f Mon Sep 17 00:00:00 2001 From: John Martin Date: Wed, 10 Apr 2013 16:44:35 +0100 Subject: [PATCH 13/32] [#748] Changes page title on /organization index --- ckan/templates/organization/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckan/templates/organization/index.html b/ckan/templates/organization/index.html index 1ac66970154..74ef2cd9050 100644 --- a/ckan/templates/organization/index.html +++ b/ckan/templates/organization/index.html @@ -1,6 +1,6 @@ {% extends "page.html" %} -{% block subtitle %}{{ _('Organizations of Datasets') }}{% endblock %} +{% block subtitle %}{{ _('Organizations') }}{% endblock %} {% block breadcrumb_content %}
  • {% link_for _('Organizations'), controller='organization', action='index' %}
  • From 6760976d0e1868a03196d90949dd619cff7f89d9 Mon Sep 17 00:00:00 2001 From: Sean Hammond Date: Wed, 10 Apr 2013 17:51:03 +0200 Subject: [PATCH 14/32] Fix broken link in CONTRIBUTING.rst There seems to be a bug in GitHub's restructured text rendering, an internal link to a section with a ' in its title will be broken (but it works fine in Sphinx). Rename the section to workaround this. --- CONTRIBUTING.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index e3d8c461661..5cbf062dd5a 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -7,7 +7,7 @@ Contributing to CKAN .. _docs.ckan.org: http://docs.ckan.org (This section is about contributing code, if you want to contribute -documentation see `Contributing to CKAN's Documentation`_.) +documentation see `Contributing to the CKAN Documentation`_.) CKAN is a free software project and code contributions are welcome. To contribute code to CKAN you should fork CKAN to your own GitHub account, push @@ -124,7 +124,7 @@ When submitting a pull request: `CHANGELOG file `_ briefly summarising your code changes. - Your branch should contain new or updated documentation for any new or - updated code, see `Contributing to CKAN's Documentation`_. + updated code, see `Contributing to the CKAN Documentation`_. - Your branch should be up to date with the master branch of the central CKAN repo, see `Keeping Up with master`_. - All the CKAN tests should pass on your branch, see @@ -139,9 +139,9 @@ When merging a feature or bug branch into master: - Use the ``--no-ff`` option in the ``git merge`` command, -==================================== -Contributing to CKAN's Documentation -==================================== +====================================== +Contributing to the CKAN Documentation +====================================== Note: getting started with contributing to `docs.ckan.org`_ is a little complicated. An easier way to contribute documentation to CKAN is to From f3bb02d9fe3f749055ef838379ed4cf11ca3db00 Mon Sep 17 00:00:00 2001 From: Sean Hammond Date: Wed, 10 Apr 2013 18:04:24 +0200 Subject: [PATCH 15/32] Remove changelog update from contributing guidelines We agreed not to update the changelog as features are added, someone will update it all at once before a release instead. --- CONTRIBUTING.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 5cbf062dd5a..298a27765c4 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -120,9 +120,6 @@ When submitting a pull request: see `Feature Branches`_. - Your branch should contain new or changed tests for any new or changed code. -- Your branch should contain updates to the - `CHANGELOG file `_ - briefly summarising your code changes. - Your branch should contain new or updated documentation for any new or updated code, see `Contributing to the CKAN Documentation`_. - Your branch should be up to date with the master branch of the central From 5fff2bc88d6f84566178d76660fc37f6876131d5 Mon Sep 17 00:00:00 2001 From: joetsoi Date: Thu, 11 Apr 2013 09:42:26 +0100 Subject: [PATCH 16/32] [#509] minor indentation cleanup --- ckan/lib/dictization/model_save.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ckan/lib/dictization/model_save.py b/ckan/lib/dictization/model_save.py index 27a7822ab45..8afed9bdbc5 100644 --- a/ckan/lib/dictization/model_save.py +++ b/ckan/lib/dictization/model_save.py @@ -239,7 +239,7 @@ def package_membership_list_save(group_dicts, package, context): for group in set(group_member.keys()) - groups: member_obj = group_member[group] if member_obj and member_obj.state == 'deleted': - continue + continue if new_authz.has_user_permission_for_group_or_org( member_obj.group_id, user, 'read'): member_obj.capacity = capacity @@ -250,7 +250,7 @@ def package_membership_list_save(group_dicts, package, context): for group in groups: member_obj = group_member.get(group) if member_obj and member_obj.state == 'active': - continue + continue if new_authz.has_user_permission_for_group_or_org( group.id, user, 'read'): member_obj = group_member.get(group) From cd8f25f6556ef5ed9d381d71e343d62e71cdaf32 Mon Sep 17 00:00:00 2001 From: Vitor Baptista Date: Thu, 11 Apr 2013 12:22:42 -0300 Subject: [PATCH 17/32] [#726] Recompile CSS files. --- ckan/public/base/css/main.css | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ckan/public/base/css/main.css b/ckan/public/base/css/main.css index a3f654b889c..b4b5fc9cb69 100644 --- a/ckan/public/base/css/main.css +++ b/ckan/public/base/css/main.css @@ -4860,8 +4860,6 @@ a.tag:hover { } .box { background-color: #FFF; - margin-left: -1px; - margin-right: -1px; border: 1px solid #cccccc; -webkit-border-radius: 4px; -moz-border-radius: 4px; @@ -7659,8 +7657,6 @@ textarea { .wrapper { *zoom: 1; background-color: #FFF; - margin-left: -1px; - margin-right: -1px; border: 1px solid #cccccc; -webkit-border-radius: 4px; -moz-border-radius: 4px; @@ -7700,8 +7696,7 @@ textarea { border-top-width: 1px; } [role=main] .primary { - width: 719px; - margin-left: 1px; + width: 717px; float: right; } [role=main] .secondary { From 6f637791340bf04b61e6f6169fddabbb06b77251 Mon Sep 17 00:00:00 2001 From: jbspeakr Date: Fri, 5 Apr 2013 17:06:59 +0200 Subject: [PATCH 18/32] reformat /ckan/ckan/config/routing.py --- ckan/config/routing.py | 163 +++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 80 deletions(-) diff --git a/ckan/config/routing.py b/ckan/config/routing.py index 0400512bd60..0d3c00c5838 100644 --- a/ckan/config/routing.py +++ b/ckan/config/routing.py @@ -69,14 +69,13 @@ def make_map(): # import controllers here rather than at root level because # pylons config is initialised by this point. - # Helpers to reduce code clutter GET = dict(method=['GET']) PUT = dict(method=['PUT']) POST = dict(method=['POST']) DELETE = dict(method=['DELETE']) GET_POST = dict(method=['GET', 'POST']) - PUT_POST = dict(method=['PUT','POST']) + PUT_POST = dict(method=['PUT', 'POST']) PUT_POST_DELETE = dict(method=['PUT', 'POST', 'DELETE']) OPTIONS = dict(method=['OPTIONS']) @@ -93,7 +92,8 @@ def make_map(): map.connect('/error/{action}', controller='error') map.connect('/error/{action}/{id}', controller='error') - map.connect('*url', controller='home', action='cors_options', conditions=OPTIONS) + map.connect('*url', controller='home', action='cors_options', + conditions=OPTIONS) # CUSTOM ROUTES HERE for plugin in routing_plugins: @@ -104,39 +104,43 @@ def make_map(): # CKAN API versioned. register_list = [ - 'package', - 'dataset', - 'resource', - 'tag', - 'group', - 'related', - 'revision', - 'licenses', - 'rating', - 'user', - 'activity' - ] + 'package', + 'dataset', + 'resource', + 'tag', + 'group', + 'related', + 'revision', + 'licenses', + 'rating', + 'user', + 'activity' + ] register_list_str = '|'.join(register_list) # /api ver 3 or none - with SubMapper(map, controller='api', path_prefix='/api{ver:/3|}', ver='/3') as m: + with SubMapper(map, controller='api', path_prefix='/api{ver:/3|}', + ver='/3') as m: m.connect('/action/{logic_function}', action='action', conditions=GET_POST) # /api ver 1, 2, 3 or none - with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|/3|}', ver='/1') as m: + with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|/3|}', + ver='/1') as m: m.connect('', action='get_api') m.connect('/search/{register}', action='search') # /api ver 1, 2 or none - with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: + with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', + ver='/1') as m: m.connect('/tag_counts', action='tag_counts') m.connect('/rest', action='index') m.connect('/qos/throughput/', action='throughput', conditions=GET) # /api/rest ver 1, 2 or none - with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1', - requirements=dict(register=register_list_str)) as m: + with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', + ver='/1', requirements=dict(register=register_list_str) + ) as m: m.connect('/rest/{register}', action='list', conditions=GET) m.connect('/rest/{register}', action='create', conditions=POST) @@ -145,20 +149,21 @@ def make_map(): m.connect('/rest/{register}/{id}', action='update', conditions=POST) m.connect('/rest/{register}/{id}', action='delete', conditions=DELETE) m.connect('/rest/{register}/{id}/:subregister', action='list', - conditions=GET) + conditions=GET) m.connect('/rest/{register}/{id}/:subregister', action='create', - conditions=POST) + conditions=POST) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='create', - conditions=POST) + conditions=POST) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='show', - conditions=GET) + conditions=GET) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='update', - conditions=PUT) + conditions=PUT) m.connect('/rest/{register}/{id}/:subregister/{id2}', action='delete', - conditions=DELETE) + conditions=DELETE) # /api/util ver 1, 2 or none - with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', ver='/1') as m: + with SubMapper(map, controller='api', path_prefix='/api{ver:/1|/2|}', + ver='/1') as m: m.connect('/util/user/autocomplete', action='user_autocomplete') m.connect('/util/is_slug_valid', action='is_slug_valid', conditions=GET) @@ -190,7 +195,7 @@ def make_map(): map.redirect('/package/{url:.*}', '/dataset/{url}') with SubMapper(map, controller='related') as m: - m.connect('related_new', '/dataset/{id}/related/new', action='new') + m.connect('related_new', '/dataset/{id}/related/new', action='new') m.connect('related_edit', '/dataset/{id}/related/edit/{related_id}', action='edit') m.connect('related_delete', '/dataset/{id}/related/delete/{related_id}', @@ -205,35 +210,32 @@ def make_map(): highlight_actions='index search') m.connect('add dataset', '/dataset/new', action='new') m.connect('/dataset/{action}', - requirements=dict(action='|'.join([ - 'list', - 'autocomplete', - 'search' - ])) - ) + requirements=dict(action='|'.join([ + 'list', + 'autocomplete', + 'search' + ]))) m.connect('/dataset/{action}/{id}/{revision}', action='read_ajax', - requirements=dict(action='|'.join([ - 'read', - 'edit', - 'history', - ])) - ) + requirements=dict(action='|'.join([ + 'read', + 'edit', + 'history', + ]))) m.connect('/dataset/{action}/{id}', - requirements=dict(action='|'.join([ - 'edit', - 'new_metadata', - 'new_resource', - 'history', - 'read_ajax', - 'history_ajax', - 'follow', - 'activity', - 'unfollow', - 'delete', - 'api_data', - ])) - ) + requirements=dict(action='|'.join([ + 'edit', + 'new_metadata', + 'new_resource', + 'history', + 'read_ajax', + 'history_ajax', + 'follow', + 'activity', + 'unfollow', + 'delete', + 'api_data', + ]))) m.connect('dataset_followers', '/dataset/followers/{id}', action='followers', ckan_icon='group') m.connect('dataset_activity', '/dataset/activity/{id}', @@ -253,7 +255,8 @@ def make_map(): m.connect('/dataset/{id}/resource/{resource_id}/embed', action='resource_embedded_dataviewer') m.connect('/dataset/{id}/resource/{resource_id}/viewer', - action='resource_embedded_dataviewer', width="960", height="800") + action='resource_embedded_dataviewer', width="960", + height="800") m.connect('/dataset/{id}/resource/{resource_id}/preview', action='resource_datapreview') @@ -271,22 +274,21 @@ def make_map(): m.connect('group_index', '/group', action='index', highlight_actions='index search') m.connect('group_list', '/group/list', action='list') - m.connect('group_new', '/group/new', action='new') + m.connect('group_new', '/group/new', action='new') m.connect('group_action', '/group/{action}/{id}', - requirements=dict(action='|'.join([ - 'edit', - 'delete', - 'members', - 'member_new', - 'member_delete', - 'history', - 'followers', - 'follow', - 'unfollow', - 'admins', - 'activity', - ])) - ) + requirements=dict(action='|'.join([ + 'edit', + 'delete', + 'members', + 'member_new', + 'member_delete', + 'history', + 'followers', + 'follow', + 'unfollow', + 'admins', + 'activity', + ]))) m.connect('group_about', '/group/about/{id}', action='about', ckan_icon='info-sign'), m.connect('group_activity', '/group/activity/{id}/{offset}', @@ -300,14 +302,13 @@ def make_map(): m.connect('/organization/list', action='list') m.connect('/organization/new', action='new') m.connect('/organization/{action}/{id}', - requirements=dict(action='|'.join([ - 'delete', - 'admins', - 'member_new', - 'member_delete', - 'history' - ])) - ) + requirements=dict(action='|'.join([ + 'delete', + 'admins', + 'member_new', + 'member_delete', + 'history' + ]))) m.connect('organization_activity', '/organization/activity/{id}', action='activity', ckan_icon='time') m.connect('organization_read', '/organization/{id}', action='read') @@ -319,7 +320,8 @@ def make_map(): action='edit', ckan_icon='edit') m.connect('organization_members', '/organization/members/{id}', action='members', ckan_icon='group') - m.connect('organization_bulk_process', '/organization/bulk_process/{id}', + m.connect('organization_bulk_process', + '/organization/bulk_process/{id}', action='bulk_process', ckan_icon='sitemap') register_package_plugins(map) register_group_plugins(map) @@ -327,7 +329,8 @@ def make_map(): # tags map.redirect('/tags', '/tag') map.redirect('/tags/{url:.*}', '/tag/{url}') - map.redirect('/tag/read/{url:.*}', '/tag/{url}', _redirect_code='301 Moved Permanently') + map.redirect('/tag/read/{url:.*}', '/tag/{url}', + _redirect_code='301 Moved Permanently') map.connect('/tag', controller='tag', action='index') map.connect('/tag/{id}', controller='tag', action='read') # users From 87e0df4ea027f883c8f208f5b95ffee544f481a6 Mon Sep 17 00:00:00 2001 From: jbspeakr Date: Fri, 5 Apr 2013 17:43:07 +0200 Subject: [PATCH 19/32] reformat /ckan/ckan/controllers/error.py --- ckan/controllers/error.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ckan/controllers/error.py b/ckan/controllers/error.py index c5ba044d246..57180dc7289 100644 --- a/ckan/controllers/error.py +++ b/ckan/controllers/error.py @@ -10,7 +10,6 @@ class ErrorController(BaseController): - """Generates error documents as and when they are required. The ErrorDocuments middleware forwards to ErrorController when error From a591c7d33a88b65de9e0a3903acd82028f4225ad Mon Sep 17 00:00:00 2001 From: jbspeakr Date: Fri, 5 Apr 2013 17:48:10 +0200 Subject: [PATCH 20/32] reformat /ckan/ckan/controllers/feed.py --- ckan/controllers/feed.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/ckan/controllers/feed.py b/ckan/controllers/feed.py index 2e9af01ed5c..e1ea58fa511 100644 --- a/ckan/controllers/feed.py +++ b/ckan/controllers/feed.py @@ -152,7 +152,6 @@ def _create_atom_id(resource_path, authority_name=None, date_string=None): class FeedController(BaseController): - base_url = config.get('ckan.site_url') def _alternate_url(self, params, **kwargs): @@ -207,7 +206,6 @@ def group(self, id): navigation_urls=navigation_urls) def tag(self, id): - data_dict, params = self._parse_url_params() data_dict['fq'] = 'tags:"%s"' % id @@ -323,7 +321,6 @@ def custom(self): def output_feed(self, results, feed_title, feed_description, feed_link, feed_url, navigation_urls, feed_guid): - author_name = config.get('ckan.feeds.author_name', '').strip() or \ config.get('ckan.site_id', '').strip() author_link = config.get('ckan.feeds.author_link', '').strip() or \ @@ -349,8 +346,8 @@ def output_feed(self, results, feed_title, feed_description, feed.add_item( title=pkg.get('title', ''), link=self.base_url + h.url_for(controller='package', - action='read', - id=pkg['id']), + action='read', + id=pkg['id']), description=pkg.get('notes', ''), updated=h.date_str_to_datetime(pkg.get('metadata_modified')), published=h.date_str_to_datetime(pkg.get('metadata_created')), @@ -360,10 +357,10 @@ def output_feed(self, results, feed_title, feed_description, categories=[t['name'] for t in pkg.get('tags', [])], enclosure=webhelpers.feedgenerator.Enclosure( self.base_url + h.url_for(controller='api', - register='package', - action='show', - id=pkg['name'], - ver='2'), + register='package', + action='show', + id=pkg['name'], + ver='2'), unicode(len(json.dumps(pkg))), # TODO fix this u'application/json') ) @@ -433,7 +430,6 @@ def _parse_url_params(self): Returns the constructed search-query dict, and the valid URL query parameters. """ - try: page = int(request.params.get('page', 1)) or 1 except ValueError: From 1f2cbbaa15de2ce305cfadaed580e140b19fb47a Mon Sep 17 00:00:00 2001 From: jbspeakr Date: Fri, 5 Apr 2013 17:59:31 +0200 Subject: [PATCH 21/32] reformat /ckan/ckan/controllers/home.py --- ckan/controllers/home.py | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/ckan/controllers/home.py b/ckan/controllers/home.py index 21843ca4fb3..7a9fe062a0d 100644 --- a/ckan/controllers/home.py +++ b/ckan/controllers/home.py @@ -14,6 +14,7 @@ # horrible hack dirty_cached_group_stuff = None + class HomeController(base.BaseController): repo = model.repo @@ -32,7 +33,7 @@ def __before__(self, action, **env): ('no such table' in msg): # table missing, major database problem base.abort(503, _('This site is currently off-line. Database ' - 'is not initialised.')) + 'is not initialised.')) # TODO: send an email to the admin person (#1285) else: raise @@ -58,24 +59,27 @@ def index(self): c.facets = query['facets'] maintain.deprecate_context_item( - 'facets', - 'Use `c.search_facets` instead.') + 'facets', + 'Use `c.search_facets` instead.') c.search_facets = query['search_facets'] - c.facet_titles = {'groups': _('Groups'), - 'tags': _('Tags'), - 'res_format': _('Formats'), - 'license': _('Licence'), } + c.facet_titles = { + 'groups': _('Groups'), + 'tags': _('Tags'), + 'res_format': _('Formats'), + 'license': _('Licence'), + } data_dict = {'sort': 'packages', 'all_fields': 1} # only give the terms to group dictize that are returned in the # facets as full results take a lot longer if 'groups' in c.search_facets: - data_dict['groups'] = [ item['name'] for item in - c.search_facets['groups']['items'] ] + data_dict['groups'] = [ + item['name'] for item in c.search_facets['groups']['items'] + ] c.groups = logic.get_action('group_list')(context, data_dict) - except search.SearchError, se: + except search.SearchError: c.package_count = 0 c.groups = [] @@ -90,8 +94,8 @@ def index(self): msg = _(u'Please update your profile' u' and add your email address and your full name. ' u'{site} uses your email address' - u' if you need to reset your password.'.format(link=url, - site=g.site_title)) + u' if you need to reset your password.'.format( + link=url, site=g.site_title)) elif not c.userobj.email: msg = _('Please update your profile' ' and add your email address. ') % url + \ @@ -134,7 +138,7 @@ def db_to_form_schema(group_type=None): except logic.NotFound: return None - return {'group_dict' :group_dict} + return {'group_dict': group_dict} global dirty_cached_group_stuff if not dirty_cached_group_stuff: @@ -160,14 +164,14 @@ def db_to_form_schema(group_type=None): # We get all the packages or at least too many so # limit it to just 2 for group in groups_data: - group['group_dict']['packages'] = group['group_dict']['packages'][:2] + group['group_dict']['packages'] = \ + group['group_dict']['packages'][:2] #now add blanks so we have two while len(groups_data) < 2: - groups_data.append({'group_dict' :{}}) + groups_data.append({'group_dict': {}}) # cache for later use dirty_cached_group_stuff = groups_data - c.group_package_stuff = dirty_cached_group_stuff # END OF DIRTYNESS From 6ebc7d9010a29fb7d19a03513a5f02cf43aea812 Mon Sep 17 00:00:00 2001 From: jbspeakr Date: Fri, 5 Apr 2013 18:01:12 +0200 Subject: [PATCH 22/32] reformat /ckan/ckan/controllers/organization.py --- ckan/controllers/organization.py | 1 + ckan/controllers/package.py | 16 ++++++---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ckan/controllers/organization.py b/ckan/controllers/organization.py index 53996819a7b..b411f8e2b31 100644 --- a/ckan/controllers/organization.py +++ b/ckan/controllers/organization.py @@ -1,5 +1,6 @@ import ckan.controllers.group as group + class OrganizationController(group.GroupController): ''' The organization controller is pretty much just the group controller. It has a few templates defined that are different and sets diff --git a/ckan/controllers/package.py b/ckan/controllers/package.py index 00cc0b09a37..2be958f72ed 100644 --- a/ckan/controllers/package.py +++ b/ckan/controllers/package.py @@ -41,6 +41,7 @@ lookup_package_plugin = ckan.lib.plugins.lookup_package_plugin + def _encode_params(params): return [(k, v.encode('utf-8') if isinstance(v, basestring) else str(v)) for k, v in params] @@ -110,7 +111,6 @@ def _guess_package_type(self, expecting_name=False): return pt - def search(self): from ckan.lib.search import SearchError @@ -267,7 +267,6 @@ def pager_url(q=None, page=None): limit = int(request.params.get('_%s_limit' % facet, 10)) c.search_facets_limits[facet] = limit - maintain.deprecate_context_item( 'facets', 'Use `c.search_facets` instead.') @@ -331,7 +330,7 @@ def read(self, id, format='html'): abort(400, _('Invalid revision format: %r') % 'Too many "@" symbols') - #check if package exists + # check if package exists try: c.pkg_dict = get_action('package_show')(context, data_dict) c.pkg = context['package'] @@ -359,7 +358,7 @@ def comments(self, id): context = {'model': model, 'session': model.Session, 'user': c.user or c.author} - #check if package exists + # check if package exists try: c.pkg_dict = get_action('package_show')(context, {'id': id}) c.pkg = context['package'] @@ -371,7 +370,7 @@ def comments(self, id): # used by disqus plugin c.current_package_id = c.pkg.id - #render the package + # render the package package_saver.PackageSaver().render_package(c.pkg_dict) return render(self._comments_template(package_type)) @@ -400,7 +399,7 @@ def history(self, id): c.pkg_dict = get_action('package_show')(context, data_dict) c.pkg_revisions = get_action('package_revision_list')(context, data_dict) - #TODO: remove + # TODO: remove # Still necessary for the authz check in group/layout.html c.pkg = context['package'] @@ -543,7 +542,6 @@ def resource_edit(self, id, resource_id, data=None, errors=None, redirect(h.url_for(controller='package', action='resource_read', id=id, resource_id=resource_id)) - context = {'model': model, 'session': model.Session, 'api_version': 3, 'user': c.user or c.author,} @@ -575,8 +573,6 @@ def resource_edit(self, id, resource_id, data=None, errors=None, 'error_summary': error_summary, 'action': 'new'} return render('package/resource_edit.html', extra_vars=vars) - - def new_resource(self, id, data=None, errors=None, error_summary=None): ''' FIXME: This is a temporary action to allow styling of the forms. ''' @@ -1172,7 +1168,7 @@ 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 return render('package/resource_read.html') From 468210b5303371bec33c8d9e60b1f992c228bb04 Mon Sep 17 00:00:00 2001 From: jbspeakr Date: Fri, 5 Apr 2013 18:15:14 +0200 Subject: [PATCH 23/32] reformat /ckan/ckan/controllers/related.py --- ckan/controllers/related.py | 44 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/ckan/controllers/related.py b/ckan/controllers/related.py index e4840663d0d..68a68c8c1d1 100644 --- a/ckan/controllers/related.py +++ b/ckan/controllers/related.py @@ -11,7 +11,7 @@ c = base.c abort = base.abort -_get_action=logic.get_action +_get_action = logic.get_action class RelatedController(base.BaseController): @@ -32,15 +32,15 @@ def dashboard(self): 'featured': base.request.params.get('featured', '') } - params_nopage = [(k, v) for k,v in base.request.params.items() + params_nopage = [(k, v) for k, v in base.request.params.items() if k != 'page'] try: page = int(base.request.params.get('page', 1)) - except ValueError, e: + except ValueError: base.abort(400, ('"page" parameter must be an integer')) # Update ordering in the context - query = logic.get_action('related_list')(context,data_dict) + query = logic.get_action('related_list')(context, data_dict) def search_url(params): url = h.url_for(controller='related', action='dashboard') @@ -54,7 +54,6 @@ def pager_url(q=None, page=None): params.append(('page', page)) return search_url(params) - c.page = h.Page( collection=query.all(), page=page, @@ -66,13 +65,15 @@ def pager_url(q=None, page=None): c.filters = dict(params_nopage) c.type_options = self._type_options() - c.sort_options = ({'value': '', 'text': _('Most viewed')}, - {'value': 'view_count_desc', 'text': _('Most Viewed')}, - {'value': 'view_count_asc', 'text': _('Least Viewed')}, - {'value': 'created_desc', 'text': _('Newest')}, - {'value': 'created_asc', 'text': _('Oldest')}) + c.sort_options = ( + {'value': '', 'text': _('Most viewed')}, + {'value': 'view_count_desc', 'text': _('Most Viewed')}, + {'value': 'view_count_asc', 'text': _('Least Viewed')}, + {'value': 'created_desc', 'text': _('Newest')}, + {'value': 'created_asc', 'text': _('Oldest')} + ) - return base.render( "related/dashboard.html") + return base.render("related/dashboard.html") def read(self, id): context = {'model': model, 'session': model.Session, @@ -85,8 +86,8 @@ def read(self, id): except logic.NotAuthorized: base.abort(401, _('Not authorized to see this page')) - related = model.Session.query(model.Related).\ - filter(model.Related.id == id).first() + related = model.Session.query(model.Related) \ + .filter(model.Related.id == id).first() if not related: base.abort(404, _('The requested related item was not found')) @@ -97,7 +98,6 @@ def read(self, id): base.redirect(related.url) - def list(self, id): """ List all related items for a specific dataset """ context = {'model': model, 'session': model.Session, @@ -164,10 +164,9 @@ def _edit_or_new(self, id, related_id, is_edit): if base.request.method == "POST": try: data = logic.clean_dict( - df.unflatten( - logic.tuplize_dict( - logic.parse_params(base.request.params) - ))) + df.unflatten( + logic.tuplize_dict( + logic.parse_params(base.request.params)))) if is_edit: data['id'] = related_id @@ -182,9 +181,8 @@ def _edit_or_new(self, id, related_id, is_edit): else: h.flash_success(_("Related item was successfully updated")) - h.redirect_to(controller='related', - action='list', - id=c.pkg_dict['name']) + h.redirect_to( + controller='related', action='list', id=c.pkg_dict['name']) except df.DataError: base.abort(400, _(u'Integrity Error')) except logic.ValidationError, e: @@ -202,7 +200,6 @@ def _edit_or_new(self, id, related_id, is_edit): return base.render(tpl) def delete(self, id, related_id): - if 'cancel' in base.request.params: h.redirect_to(controller='related', action='edit', id=id, related_id=related_id) @@ -215,7 +212,8 @@ def delete(self, id, related_id): logic.get_action('related_delete')(context, {'id': related_id}) h.flash_notice(_('Related item has been deleted.')) h.redirect_to(controller='package', action='read', id=id) - c.related_dict = logic.get_action('related_show')(context, {'id': related_id}) + c.related_dict = logic.get_action('related_show')( + context, {'id': related_id}) c.pkg_id = id except logic.NotAuthorized: base.abort(401, _('Unauthorized to delete related item %s') % '') From f0088871d8f09c6243901c98ab542e53cf7aa011 Mon Sep 17 00:00:00 2001 From: jbspeakr Date: Fri, 5 Apr 2013 18:18:00 +0200 Subject: [PATCH 24/32] reformat /ckan/ckan/controllers/storage.py --- ckan/controllers/storage.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ckan/controllers/storage.py b/ckan/controllers/storage.py index c3a55248364..2ac00eb3697 100644 --- a/ckan/controllers/storage.py +++ b/ckan/controllers/storage.py @@ -97,7 +97,8 @@ def authorize(method, bucket, key, user, ofs): # now check user stuff context = {'user': c.user, 'model': model} - is_authorized = new_authz.is_authorized_boolean('file_upload', context, {}) + is_authorized = new_authz.is_authorized_boolean( + 'file_upload', context, {}) if not is_authorized: h.flash_error('Not authorized to upload files.') abort(401) @@ -143,9 +144,9 @@ def upload_handle(self): params['uploaded-by'] = c.userobj.name if c.userobj else "" self.ofs.put_stream(bucket_id, label, stream.file, params) - success_action_redirect = h.url_for('storage_upload_success', - qualified=True, - bucket=BUCKET, label=label) + success_action_redirect = h.url_for( + 'storage_upload_success', qualified=True, + bucket=BUCKET, label=label) # Do not redirect here as it breaks js file uploads (get infinite loop # in FF and crash in Chrome) return self.success(label) @@ -186,11 +187,10 @@ def file(self, label): fapp = FileApp(filepath, headers=None, **headers) return fapp(request.environ, self.start_response) else: - h.redirect_to(file_url.encode('ascii','ignore')) + h.redirect_to(file_url.encode('ascii', 'ignore')) class StorageAPIController(BaseController): - _ofs_impl = None @property @@ -270,7 +270,7 @@ def get_metadata(self, label): qualified=False ) if url.startswith('/'): - url = config.get('ckan.site_url','').rstrip('/') + url + url = config.get('ckan.site_url', '').rstrip('/') + url if not self.ofs.exists(bucket, label): abort(404) @@ -306,7 +306,7 @@ def auth_request(self, label): try: data = fix_stupid_pylons_encoding(request.body) headers = json.loads(data) - except Exception, e: + except Exception: from traceback import print_exc msg = StringIO() print_exc(msg) @@ -397,7 +397,7 @@ def auth_form(self, label): try: data = fix_stupid_pylons_encoding(request.body) headers = json.loads(data) - except Exception, e: + except Exception: from traceback import print_exc msg = StringIO() print_exc(msg) From 39b49dfd22e6b678fbdaa6ec2ad612e9afef9a91 Mon Sep 17 00:00:00 2001 From: jbspeakr Date: Fri, 5 Apr 2013 18:18:25 +0200 Subject: [PATCH 25/32] reformat /ckan/ckan/controllers/tag.py --- ckan/controllers/tag.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ckan/controllers/tag.py b/ckan/controllers/tag.py index a736c17e8d0..699d31fceb3 100644 --- a/ckan/controllers/tag.py +++ b/ckan/controllers/tag.py @@ -68,4 +68,5 @@ def read(self, id): if h.asbool(config.get('ckan.legacy_templates', False)): return base.render('tag/read.html') else: - h.redirect_to(controller='package', action='search', tags=c.tag.get('name')) + h.redirect_to(controller='package', action='search', + tags=c.tag.get('name')) From bbb15f9ca6dd256ce178c950f33f6264c1dcd5e6 Mon Sep 17 00:00:00 2001 From: jbspeakr Date: Fri, 5 Apr 2013 18:19:56 +0200 Subject: [PATCH 26/32] reformat /ckan/ckan/controllers/user.py --- ckan/controllers/user.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/ckan/controllers/user.py b/ckan/controllers/user.py index 638bd7a7231..d79d68a64e8 100644 --- a/ckan/controllers/user.py +++ b/ckan/controllers/user.py @@ -35,7 +35,6 @@ class UserController(base.BaseController): - def __before__(self, action, **env): base.BaseController.__before__(self, action, **env) try: @@ -238,7 +237,7 @@ def edit(self, id=None, data=None, errors=None, error_summary=None): except NotAuthorized: abort(401, _('Unauthorized to edit user %s') % '') - except NotFound, e: + except NotFound: abort(404, _('User not found')) user_obj = context.get('user_obj') @@ -507,8 +506,7 @@ def activity(self, id, offset=0): return render('user/activity_stream.html') - def _get_dashboard_context(self, filter_type=None, filter_id=None, - q=None): + def _get_dashboard_context(self, filter_type=None, filter_id=None, q=None): '''Return a dict needed by the dashboard view to determine context.''' def display_name(followee): @@ -520,8 +518,10 @@ def display_name(followee): return display_name or fullname or title or name if (filter_type and filter_id): - context = {'model': model, 'session': model.Session, - 'user': c.user or c.author, 'for_view': True} + context = { + 'model': model, 'session': model.Session, + 'user': c.user or c.author, 'for_view': True + } data_dict = {'id': filter_id} followee = None @@ -529,8 +529,9 @@ def display_name(followee): 'dataset': 'package_show', 'user': 'user_show', 'group': 'group_show' - } - action_function = logic.get_action(action_functions.get(filter_type)) + } + action_function = logic.get_action( + action_functions.get(filter_type)) # Is this a valid type? if action_function is None: raise abort(404, _('Follow item not found')) From 4361de6044a366da3c72a7c32542f2a62835f35c Mon Sep 17 00:00:00 2001 From: amercader Date: Fri, 12 Apr 2013 11:43:48 +0100 Subject: [PATCH 27/32] [#740] Rebuild css --- ckan/public/base/css/main.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ckan/public/base/css/main.css b/ckan/public/base/css/main.css index b4b5fc9cb69..0ea0e13e359 100644 --- a/ckan/public/base/css/main.css +++ b/ckan/public/base/css/main.css @@ -6513,6 +6513,11 @@ textarea { -moz-border-radius-bottomright: 2px; border-bottom-right-radius: 2px; } +.ellipsis { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} .ckan-icon { *margin-right: .3em; display: inline-block; From 77e26db4573e0f5ece9245ebffb074b0bab538aa Mon Sep 17 00:00:00 2001 From: tobes Date: Sat, 23 Mar 2013 11:03:38 +0000 Subject: [PATCH 28/32] Add IAuthenticator --- ckan/controllers/user.py | 11 ++++++++ ckan/lib/base.py | 52 ++++++++++++++++++++++++++++---------- ckan/plugins/interfaces.py | 18 +++++++++++++ 3 files changed, 68 insertions(+), 13 deletions(-) diff --git a/ckan/controllers/user.py b/ckan/controllers/user.py index d79d68a64e8..087f03dbe25 100644 --- a/ckan/controllers/user.py +++ b/ckan/controllers/user.py @@ -16,6 +16,7 @@ import ckan.lib.captcha as captcha import ckan.lib.mailer as mailer import ckan.lib.navl.dictization_functions as dictization_functions +import ckan.plugins as p log = logging.getLogger(__name__) @@ -293,6 +294,11 @@ def login(self, error=None): session.save() return h.redirect_to(locale=str(lang), controller='user', action='login') + + # Do any plugin login stuff + for item in p.PluginImplementations(p.IAuthenticator): + item.login() + if 'error' in request.params: h.flash_error(request.params['error']) @@ -351,6 +357,11 @@ def logout(self): # save our language in the session so we don't lose it session['lang'] = request.environ.get('CKAN_LANG') session.save() + + # Do any plugin logout stuff + for item in p.PluginImplementations(p.IAuthenticator): + item.logout() + h.redirect_to(self._get_repoze_handler('logout_handler_path')) def set_lang(self, lang): diff --git a/ckan/lib/base.py b/ckan/lib/base.py index 7f944a3393b..8f36da8f52f 100644 --- a/ckan/lib/base.py +++ b/ckan/lib/base.py @@ -23,7 +23,7 @@ import lib.render import ckan.lib.helpers as h import ckan.lib.app_globals as app_globals -from ckan.plugins import PluginImplementations, IGenshiStreamFilter +from ckan.plugins import PluginImplementations, IGenshiStreamFilter, IAuthenticator import ckan.model as model from ckan.common import json @@ -38,6 +38,12 @@ def abort(status_code=None, detail='', headers=None, comment=None): + if status_code == 401: + # Allow IAuthenticator plugins to alter the abort + for item in PluginImplementations(IAuthenticator): + result = item.abort(status_code, detail, headers, comment) + (status_code, detail, headers, comment) = result + if detail and status_code != 503: h.flash_error(detail) # #1267 Convert detail to plain text, since WebOb 0.9.7.1 (which comes @@ -208,7 +214,9 @@ def __before__(self, action, **params): c.__timer = time.time() c.__version__ = ckan.__version__ app_globals.app_globals._check_uptodate() + self._identify_user() + i18n.handle_request(request, c) # If the user is logged in add their number of new activities to the @@ -222,11 +230,7 @@ def __before__(self, action, **params): c.new_activities = new_activities_count(context, {}) def _identify_user(self): - ''' - Identifies the user using two methods: - a) If he has logged into the web interface then repoze.who will - set REMOTE_USER. - b) For API calls he may set a header with his API key. + '''Try to identify the user If the user is identified then: c.user = user name (unicode) c.userobj = user object @@ -234,14 +238,41 @@ def _identify_user(self): otherwise: c.user = None c.userobj = None - c.author = user\'s IP address (unicode) - ''' + c.author = user's IP address (unicode)''' # see if it was proxied first c.remote_addr = request.environ.get('HTTP_X_FORWARDED_FOR', '') if not c.remote_addr: c.remote_addr = request.environ.get('REMOTE_ADDR', 'Unknown IP Address') + # Authentication plugins get a chance to run here break as soon as a + # user is identified. + authenticators = PluginImplementations(IAuthenticator) + if authenticators: + for item in authenticators: + item.identify() + if c.user: + break + + # We haven't identified the user so try the default methods + if not c.user: + self._identify_user_default() + + # general settings + if c.user: + c.author = c.user + else: + c.author = c.remote_addr + c.author = unicode(c.author) + + def _identify_user_default(self): + ''' + Identifies the user using two methods: + a) If they logged into the web interface then repoze.who will + set REMOTE_USER. + b) For API calls they may set a header with an API key. + ''' + # environ['REMOTE_USER'] is set by repoze.who if it authenticates # a user's cookie or OpenID. But repoze.who doesn't check the user # (still) exists in our database - we need to do that here. (Another @@ -272,11 +303,6 @@ def _identify_user(self): c.userobj = self._get_user_for_apikey() if c.userobj is not None: c.user = c.userobj.name - if c.user: - c.author = c.user - else: - c.author = c.remote_addr - c.author = unicode(c.author) def __call__(self, environ, start_response): """Invoke the Controller""" diff --git a/ckan/plugins/interfaces.py b/ckan/plugins/interfaces.py index 1dd22b2f88c..f297f50667e 100644 --- a/ckan/plugins/interfaces.py +++ b/ckan/plugins/interfaces.py @@ -19,6 +19,7 @@ 'ITagController', 'ITemplateHelpers', 'IFacets', + 'IAuthenticator', ] from inspect import isclass @@ -875,3 +876,20 @@ def group_facets(self, facets_dict, group_type, package_type): def organization_facets(self, facets_dict, organization_type, package_type): ''' Update the facets_dict and return it. ''' return facets_dict + + +class IAuthenticator(Interface): + '''EXPERIMENTAL''' + + def identify(self): + '''called to identify the user.''' + + def login(self): + '''called at login.''' + + def logout(self): + '''called at logout.''' + + def abort(self, status_code, detail, headers, comment): + '''called on abort.''' + return (status_code, detail, headers, comment) From cdf792609a7977abbe8c84cab8f0e657418c7001 Mon Sep 17 00:00:00 2001 From: tobes Date: Sat, 13 Apr 2013 12:34:39 +0100 Subject: [PATCH 29/32] Improve logic/auth/__init__.py bad logic (data_dict={}) --- ckan/logic/auth/__init__.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/ckan/logic/auth/__init__.py b/ckan/logic/auth/__init__.py index 66062ead609..500be7fef3f 100644 --- a/ckan/logic/auth/__init__.py +++ b/ckan/logic/auth/__init__.py @@ -2,33 +2,41 @@ Helper functions to be used in the auth check functions ''' -from ckan.logic import NotFound +import ckan.logic as logic + def _get_object(context, data_dict, name, class_name): # return the named item if in the data_dict, or get it from # model.class_name + if not data_dict: + data_dict = {} + if not name in context: model = context['model'] id = data_dict.get('id', None) obj = getattr(model, class_name).get(id) if not obj: - raise NotFound + raise logic.NotFound else: obj = context[name] return obj -def get_related_object(context, data_dict = {}): + +def get_related_object(context, data_dict=None): return _get_object(context, data_dict, 'related', 'Related') -def get_package_object(context, data_dict = {}): + +def get_package_object(context, data_dict=None): return _get_object(context, data_dict, 'package', 'Package') -def get_resource_object(context, data_dict={}): + +def get_resource_object(context, data_dict=None): return _get_object(context, data_dict, 'resource', 'Resource') -def get_group_object(context, data_dict={}): + +def get_group_object(context, data_dict=None): return _get_object(context, data_dict, 'group', 'Group') -def get_user_object(context, data_dict={}): - return _get_object(context, data_dict, 'user_obj', 'User') +def get_user_object(context, data_dict=None): + return _get_object(context, data_dict, 'user_obj', 'User') From ba5063c17956b721c0ab209dc5fa9dcd4e7414d4 Mon Sep 17 00:00:00 2001 From: tobes Date: Sat, 13 Apr 2013 12:40:02 +0100 Subject: [PATCH 30/32] Slight refactor of logic/auth/__init__.py for more pythonic plus store found object --- ckan/logic/auth/__init__.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ckan/logic/auth/__init__.py b/ckan/logic/auth/__init__.py index 500be7fef3f..9ed00ec07ad 100644 --- a/ckan/logic/auth/__init__.py +++ b/ckan/logic/auth/__init__.py @@ -8,18 +8,19 @@ def _get_object(context, data_dict, name, class_name): # return the named item if in the data_dict, or get it from # model.class_name - if not data_dict: - data_dict = {} - - if not name in context: + try: + return context[name] + except KeyError: model = context['model'] + if not data_dict: + data_dict = {} id = data_dict.get('id', None) obj = getattr(model, class_name).get(id) if not obj: raise logic.NotFound - else: - obj = context[name] - return obj + # Save in case we need this again during the request + context[name] = obj + return obj def get_related_object(context, data_dict=None): From 48f37eff2cf96f2b6a1eb9be8816f3946daf9435 Mon Sep 17 00:00:00 2001 From: tobes Date: Sat, 13 Apr 2013 14:41:05 +0100 Subject: [PATCH 31/32] Saml2 - add check_access calls to some user actions --- ckan/controllers/user.py | 25 +++++++++++++++++++++++++ ckan/logic/auth/get.py | 8 ++++++++ 2 files changed, 33 insertions(+) diff --git a/ckan/controllers/user.py b/ckan/controllers/user.py index 087f03dbe25..3568f324047 100644 --- a/ckan/controllers/user.py +++ b/ckan/controllers/user.py @@ -141,6 +141,12 @@ def me(self, locale=None): id=user_ref) def register(self, data=None, errors=None, error_summary=None): + context = {'model': model, 'session': model.Session, 'user': c.user} + try: + check_access('user_create', context) + except NotAuthorized: + abort(401, _('Unauthorized to register as a user.')) + return self.new(data, errors, error_summary) def new(self, data=None, errors=None, error_summary=None): @@ -213,6 +219,8 @@ def _save_new(self, context): def edit(self, id=None, data=None, errors=None, error_summary=None): context = {'save': 'save' in request.params, 'schema': self._edit_form_to_db_schema(), + 'model': model, 'session': model.Session, + 'user': c.user, } if id is None: if c.userobj: @@ -221,6 +229,11 @@ def edit(self, id=None, data=None, errors=None, error_summary=None): abort(400, _('No user specified')) data_dict = {'id': id} + try: + check_access('user_update', context, data_dict) + except NotAuthorized: + abort(401, _('Unauthorized to edit a user.')) + if (context['save']) and not data: return self._save_edit(id, context) @@ -381,6 +394,13 @@ def logged_out_page(self): return render('user/logout.html') def request_reset(self): + context = {'model': model, 'session': model.Session, 'user': c.user} + data_dict = {'id': request.params.get('user')} + try: + check_access('request_reset', context) + except NotAuthorized: + abort(401, _('Unauthorized to request reset password.')) + if request.method == 'POST': id = request.params.get('user') @@ -435,6 +455,11 @@ def perform_reset(self, id): data_dict = {'id': id} + try: + check_access('user_reset', context) + except NotAuthorized: + abort(401, _('Unauthorized to reset password.')) + try: user_dict = get_action('user_show')(context, data_dict) diff --git a/ckan/logic/auth/get.py b/ckan/logic/auth/get.py index 3df5689c2ea..7a26c708c93 100644 --- a/ckan/logic/auth/get.py +++ b/ckan/logic/auth/get.py @@ -262,3 +262,11 @@ def dataset_followee_list(context, data_dict): def group_followee_list(context, data_dict): return _followee_list(context, data_dict) + + +def user_reset(context, data_dict): + return {'success': True} + + +def request_reset(context, data_dict): + return {'success': True} From 7a12178c569841027dd9e240adb16542c9e36b5e Mon Sep 17 00:00:00 2001 From: kindly Date: Wed, 17 Apr 2013 17:03:50 +0100 Subject: [PATCH 32/32] Fix for unauthorized user edit --- ckan/logic/auth/update.py | 6 +++++- ckan/tests/functional/test_user.py | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ckan/logic/auth/update.py b/ckan/logic/auth/update.py index 0a17ad43822..ac907f487d0 100644 --- a/ckan/logic/auth/update.py +++ b/ckan/logic/auth/update.py @@ -178,6 +178,10 @@ def group_edit_permissions(context, data_dict): def user_update(context, data_dict): user = context['user'] + if not user and 'reset_key' not in data_dict: + return {'success': False, + 'msg': _('Have to be logged in to edit user')} + user_obj = logic_auth.get_user_object(context, data_dict) user_reset = ('reset_key' in data_dict and data_dict['reset_key'] == user_obj.reset_key) @@ -185,7 +189,7 @@ def user_update(context, data_dict): if not (user == user_obj.name) and not user_reset: return {'success': False, 'msg': _('User %s not authorized to edit user %s') % - (str(user), user_obj.id)} + (user, user_obj.id)} return {'success': True} diff --git a/ckan/tests/functional/test_user.py b/ckan/tests/functional/test_user.py index 571e100ca39..1f496e21daf 100644 --- a/ckan/tests/functional/test_user.py +++ b/ckan/tests/functional/test_user.py @@ -820,8 +820,9 @@ def test_user_edit_no_user(self): def test_user_edit_unknown_user(self): offset = url_for(controller='user', action='edit', id='unknown_person') - res = self.app.get(offset, status=404) - assert 'User not found' in res, res + res = self.app.get(offset, status=302) # redirect to login page + res = res.follow() + assert 'Login' in res, res def test_user_edit_not_logged_in(self): # create user