From 3eff2fd6d7b430cf1e1eca949e2910979c4021cb Mon Sep 17 00:00:00 2001 From: amercader Date: Mon, 25 Sep 2017 11:47:59 +0100 Subject: [PATCH 01/21] Update version number for 2.6.4b --- ckan/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckan/__init__.py b/ckan/__init__.py index 8d28b50c110..cd3b6e4717f 100644 --- a/ckan/__init__.py +++ b/ckan/__init__.py @@ -1,6 +1,6 @@ # encoding: utf-8 -__version__ = '2.6.3' +__version__ = '2.6.4b' __description__ = 'CKAN Software' __long_description__ = \ From df514999f19fcb554f798f58b9e026ae05111362 Mon Sep 17 00:00:00 2001 From: Jinfei Fan Date: Thu, 22 Jun 2017 13:11:12 -0400 Subject: [PATCH 02/21] support non root for fanstatic --- ckan/config/middleware/pylons_app.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ckan/config/middleware/pylons_app.py b/ckan/config/middleware/pylons_app.py index 5a1218d4b4f..0a63e4fbd07 100644 --- a/ckan/config/middleware/pylons_app.py +++ b/ckan/config/middleware/pylons_app.py @@ -1,6 +1,7 @@ # encoding: utf-8 import os +import re from pylons.wsgiapp import PylonsApp @@ -87,6 +88,10 @@ def make_pylons_stack(conf, full_stack=True, static_files=True, 'bottom': True, 'bundle': True, } + root_path = config.get('ckan.root_path', None) + if root_path: + root_path = re.sub('/{{LANG}}', '', root_path) + fanstatic_config['base_url'] = root_path app = Fanstatic(app, **fanstatic_config) for plugin in PluginImplementations(IMiddleware): From a75e2a147126e0613ed1b61e92ad8a2360facbf8 Mon Sep 17 00:00:00 2001 From: Jinfei Fan Date: Fri, 23 Jun 2017 10:52:12 -0400 Subject: [PATCH 03/21] add test case for none root fanstatic --- ckan/tests/test_none_root.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 ckan/tests/test_none_root.py diff --git a/ckan/tests/test_none_root.py b/ckan/tests/test_none_root.py new file mode 100644 index 00000000000..7ee1d220697 --- /dev/null +++ b/ckan/tests/test_none_root.py @@ -0,0 +1,19 @@ +# encoding: utf-8 + +from ckan.common import config +import ckan.plugins as p +import ckan.config.middleware as middleware +import webtest +import ckan.tests.helpers as helpers + + +class TestNoneRootCKAN(): + @helpers.change_config(u'ckan.root_path', u'/data/{{LANG}}') + def test_resource_url(self): + wsgiapp = middleware.make_app(config[u'global_conf'], **config) + app = webtest.TestApp(wsgiapp) + p.load(u'example_theme_v15_fanstatic') + content = app.get(u'/en/base.html') + assert u'example_theme.css' in content + assert u'href="/data/fanstatic/example_theme' in content + p.unload(u'example_theme_v15_fanstatic') From b936fa575ac928c0467f5c97e25842fd257b9732 Mon Sep 17 00:00:00 2001 From: Gabriel Gimenez Catuogno <12-11006@usb.ve> Date: Fri, 25 Aug 2017 17:00:23 +0200 Subject: [PATCH 04/21] Fix replace, add --- ckan/lib/mailer.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ckan/lib/mailer.py b/ckan/lib/mailer.py index c75e3435e96..738eb19ec6e 100644 --- a/ckan/lib/mailer.py +++ b/ckan/lib/mailer.py @@ -33,7 +33,10 @@ def _mail_recipient(recipient_name, recipient_email, mail_from = config.get('smtp.mail_from') msg = MIMEText(body.encode('utf-8'), 'plain', 'utf-8') for k, v in headers.items(): - msg[k] = v + if k in msg.keys(): + msg.replace_header(k, v) + else: + msg.add_header(k, v) subject = Header(subject.encode('utf-8'), 'utf-8') msg['Subject'] = subject msg['From'] = _("%s <%s>") % (sender_name, mail_from) From df3760eea7d65624fc3788a0e455a3cc7dfd63f3 Mon Sep 17 00:00:00 2001 From: Gabriel Gimenez Catuogno <12-11006@usb.ve> Date: Fri, 25 Aug 2017 17:03:40 +0200 Subject: [PATCH 05/21] Unused import --- ckan/lib/mailer.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ckan/lib/mailer.py b/ckan/lib/mailer.py index 738eb19ec6e..4979ea8eb77 100644 --- a/ckan/lib/mailer.py +++ b/ckan/lib/mailer.py @@ -8,7 +8,6 @@ from email.mime.text import MIMEText from email.header import Header from email import Utils -from urlparse import urljoin from ckan.common import config import paste.deploy.converters From 0a711fd3364a08229b0313c89e1624b94ed2f692 Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Thu, 10 Aug 2017 16:18:05 +0300 Subject: [PATCH 06/21] Skip url parsing in redirect if url is full url or starts with root_path --- ckan/lib/helpers.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index 21ce5c1c360..c82421c152c 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -148,6 +148,15 @@ def redirect_to(*args, **kw): toolkit.redirect_to('dataset_read', id='changed') ''' + # Remove LANG from root_path so that we do not need to parse locales + root_path = config.get('ckan.root_path', None) + if root_path: + root_path = re.sub('/{{LANG}}', '', root_path) + + # If args contain full url eg. http://example.com or url starting with root_path skip url parsing + if args and (is_url(args[0]) or ( root_path and args[0].startswith(root_path))) : + _routes_redirect_to(args[0]) + if are_there_flash_messages(): kw['__no_cache__'] = True return _redirect_to(url_for(*args, **kw)) From f1fcb24ce9f44df96f4059658b3afd9623fdfc4f Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Thu, 10 Aug 2017 16:27:32 +0300 Subject: [PATCH 07/21] Add missing return --- ckan/lib/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index c82421c152c..9b435f7ffbd 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -155,7 +155,7 @@ def redirect_to(*args, **kw): # If args contain full url eg. http://example.com or url starting with root_path skip url parsing if args and (is_url(args[0]) or ( root_path and args[0].startswith(root_path))) : - _routes_redirect_to(args[0]) + return _routes_redirect_to(args[0]) if are_there_flash_messages(): kw['__no_cache__'] = True From e5963c38cacd6fb5b50f54c60f3a8f6fbe15ee2b Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Thu, 10 Aug 2017 16:52:32 +0300 Subject: [PATCH 08/21] Move url checks after unicode conversion --- ckan/lib/helpers.py | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index 9b435f7ffbd..0561854525b 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -19,16 +19,19 @@ from urllib import urlencode from paste.deploy import converters -from webhelpers.html import HTML, literal, tags +from webhelpers.html import HTML, literal, tags, tools from webhelpers import paginate import webhelpers.text as whtext import webhelpers.date as date from markdown import markdown from bleach import clean as clean_html, ALLOWED_TAGS, ALLOWED_ATTRIBUTES from pylons import url as _pylons_default_url -from ckan.common import config -from routes import redirect_to as _redirect_to +from ckan.common import config, is_flask_request +from flask import redirect as _flask_redirect +from routes import redirect_to as _routes_redirect_to from routes import url_for as _routes_default_url_for +from flask import url_for as _flask_default_url_for +from werkzeug.routing import BuildError as FlaskRouteBuildError import i18n import ckan.exceptions @@ -148,18 +151,30 @@ def redirect_to(*args, **kw): toolkit.redirect_to('dataset_read', id='changed') ''' + if are_there_flash_messages(): + kw['__no_cache__'] = True + + # Routes router doesn't like unicode args + uargs = map(lambda arg: str(arg) if isinstance(arg, unicode) else arg, + args) + # Remove LANG from root_path so that we do not need to parse locales root_path = config.get('ckan.root_path', None) if root_path: root_path = re.sub('/{{LANG}}', '', root_path) # If args contain full url eg. http://example.com or url starting with root_path skip url parsing - if args and (is_url(args[0]) or ( root_path and args[0].startswith(root_path))) : - return _routes_redirect_to(args[0]) + if uargs and (is_url(uargs[0]) or ( root_path and uargs[0].startswith(root_path))) : + return _routes_redirect_to(uargs[0]) - if are_there_flash_messages(): - kw['__no_cache__'] = True - return _redirect_to(url_for(*args, **kw)) + _url = url_for(*uargs, **kw) + if _url.startswith('/'): + _url = str(config['ckan.site_url'].rstrip('/') + _url) + + if is_flask_request(): + return _flask_redirect(_url) + else: + return _routes_redirect_to(_url) @maintain.deprecated('h.url is deprecated please use h.url_for') From 1ccdc5728a337c6e9827bd3eaaebbaf892843c03 Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Thu, 10 Aug 2017 17:05:37 +0300 Subject: [PATCH 09/21] pep8 --- ckan/lib/helpers.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index 0561854525b..2a375d64212 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -157,14 +157,16 @@ def redirect_to(*args, **kw): # Routes router doesn't like unicode args uargs = map(lambda arg: str(arg) if isinstance(arg, unicode) else arg, args) - + # Remove LANG from root_path so that we do not need to parse locales root_path = config.get('ckan.root_path', None) if root_path: root_path = re.sub('/{{LANG}}', '', root_path) - # If args contain full url eg. http://example.com or url starting with root_path skip url parsing - if uargs and (is_url(uargs[0]) or ( root_path and uargs[0].startswith(root_path))) : + # If args contain full url eg. http://example.com + # or url starting with root_path skip url parsing + if uargs and (is_url(uargs[0]) + or (root_path and uargs[0].startswith(root_path))): return _routes_redirect_to(uargs[0]) _url = url_for(*uargs, **kw) From 45b58eb3aacd2c2778d0f4458d8f722235733ecc Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Fri, 11 Aug 2017 09:19:23 +0300 Subject: [PATCH 10/21] Redirect if given a single string --- ckan/lib/helpers.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index 2a375d64212..09d1a46d928 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -150,6 +150,11 @@ def redirect_to(*args, **kw): toolkit.redirect_to('dataset_read', id='changed') + If given a single string as argument, this redirects without url parsing + + toolkit.redirect_to('http://example.com') + toolkit.redirect_to('/dataset') + ''' if are_there_flash_messages(): kw['__no_cache__'] = True @@ -158,15 +163,14 @@ def redirect_to(*args, **kw): uargs = map(lambda arg: str(arg) if isinstance(arg, unicode) else arg, args) - # Remove LANG from root_path so that we do not need to parse locales - root_path = config.get('ckan.root_path', None) - if root_path: - root_path = re.sub('/{{LANG}}', '', root_path) + # Repoze.who redirects with single strings + # which need to be parsed by url_for + exempt_urls = ['/user/logout', '/user/logged_out_redirect', + '/user/logged_in'] + matching = [s for s in uargs if any(xs in s for xs in exempt_urls)] - # If args contain full url eg. http://example.com - # or url starting with root_path skip url parsing - if uargs and (is_url(uargs[0]) - or (root_path and uargs[0].startswith(root_path))): + if uargs and len(uargs) is 1 and isinstance(uargs[0], basestring) \ + and len(matching) is 0: return _routes_redirect_to(uargs[0]) _url = url_for(*uargs, **kw) From 0ae06c87ff20dcdc8d59f41e413aa9009f3b2873 Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Fri, 11 Aug 2017 09:56:31 +0300 Subject: [PATCH 11/21] only redirect those single strings that start with / --- ckan/lib/helpers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index 09d1a46d928..d9f478f4791 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -154,6 +154,7 @@ def redirect_to(*args, **kw): toolkit.redirect_to('http://example.com') toolkit.redirect_to('/dataset') + toolkit.redirect_to('/some/other/path') ''' if are_there_flash_messages(): @@ -170,7 +171,7 @@ def redirect_to(*args, **kw): matching = [s for s in uargs if any(xs in s for xs in exempt_urls)] if uargs and len(uargs) is 1 and isinstance(uargs[0], basestring) \ - and len(matching) is 0: + and uargs[0].startswith('/') and len(matching) is 0: return _routes_redirect_to(uargs[0]) _url = url_for(*uargs, **kw) From d8bb76994d55bcdc1bacac0e93e615e05a4185c8 Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Fri, 11 Aug 2017 10:18:39 +0300 Subject: [PATCH 12/21] skip url_for only --- ckan/lib/helpers.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index d9f478f4791..51fa5a116a2 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -170,11 +170,16 @@ def redirect_to(*args, **kw): '/user/logged_in'] matching = [s for s in uargs if any(xs in s for xs in exempt_urls)] + _url = '' + skip_url_parsing = False if uargs and len(uargs) is 1 and isinstance(uargs[0], basestring) \ and uargs[0].startswith('/') and len(matching) is 0: - return _routes_redirect_to(uargs[0]) + skip_url_parsing = True + _url = uargs[0] + + if skip_url_parsing is False: + _url = url_for(*uargs, **kw) - _url = url_for(*uargs, **kw) if _url.startswith('/'): _url = str(config['ckan.site_url'].rstrip('/') + _url) From 2e3108c3293720d7e7896792306a5e67523954da Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Fri, 11 Aug 2017 10:34:31 +0300 Subject: [PATCH 13/21] pep8 --- ckan/lib/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index 51fa5a116a2..ecfa6baccb5 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -173,7 +173,7 @@ def redirect_to(*args, **kw): _url = '' skip_url_parsing = False if uargs and len(uargs) is 1 and isinstance(uargs[0], basestring) \ - and uargs[0].startswith('/') and len(matching) is 0: + and uargs[0].startswith('/') and len(matching) is 0: skip_url_parsing = True _url = uargs[0] From daad60ec731ed016b46ec26e33989bf66cc02d4d Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Tue, 15 Aug 2017 16:02:10 +0300 Subject: [PATCH 14/21] Check if the given string is url --- ckan/lib/helpers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index ecfa6baccb5..d171707cb69 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -173,7 +173,8 @@ def redirect_to(*args, **kw): _url = '' skip_url_parsing = False if uargs and len(uargs) is 1 and isinstance(uargs[0], basestring) \ - and uargs[0].startswith('/') and len(matching) is 0: + and (uargs[0].startswith('/') or is_url(uargs[0])) \ + and len(matching) is 0: skip_url_parsing = True _url = uargs[0] From c3ff043d42ebab09be8ba546586128ae5f2c833a Mon Sep 17 00:00:00 2001 From: Jari Voutilainen Date: Thu, 24 Aug 2017 13:57:47 +0300 Subject: [PATCH 15/21] Try to handle repoze who urls without hard coding them to helper --- ckan/controllers/user.py | 8 +++----- ckan/lib/helpers.py | 9 ++------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/ckan/controllers/user.py b/ckan/controllers/user.py index ebc305d19f0..69491c267b2 100644 --- a/ckan/controllers/user.py +++ b/ckan/controllers/user.py @@ -397,8 +397,7 @@ def login(self, error=None): if not c.user: came_from = request.params.get('came_from') if not came_from: - came_from = h.url_for(controller='user', action='logged_in', - __ckan_no_root=True) + came_from = h.url_for(controller='user', action='logged_in') c.login_handler = h.url_for( self._get_repoze_handler('login_handler_path'), came_from=came_from) @@ -436,10 +435,9 @@ def logout(self): # Do any plugin logout stuff for item in p.PluginImplementations(p.IAuthenticator): item.logout() - url = h.url_for(controller='user', action='logged_out_page', - __ckan_no_root=True) + url = h.url_for(controller='user', action='logged_out_page') h.redirect_to(self._get_repoze_handler('logout_handler_path') + - '?came_from=' + url) + '?came_from=' + url, parse_url=True) def logged_out(self): # redirect if needed diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index d171707cb69..4231541e360 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -164,17 +164,12 @@ def redirect_to(*args, **kw): uargs = map(lambda arg: str(arg) if isinstance(arg, unicode) else arg, args) - # Repoze.who redirects with single strings - # which need to be parsed by url_for - exempt_urls = ['/user/logout', '/user/logged_out_redirect', - '/user/logged_in'] - matching = [s for s in uargs if any(xs in s for xs in exempt_urls)] - _url = '' skip_url_parsing = False + parse_url = kw.pop('parse_url', False) if uargs and len(uargs) is 1 and isinstance(uargs[0], basestring) \ and (uargs[0].startswith('/') or is_url(uargs[0])) \ - and len(matching) is 0: + and parse_url is False: skip_url_parsing = True _url = uargs[0] From 9ba6f0824aa9e90132fa20951201e4bdf93d64a4 Mon Sep 17 00:00:00 2001 From: Konstantin Sivakov Date: Tue, 26 Sep 2017 12:12:36 +0200 Subject: [PATCH 16/21] add patch --- ckan/controllers/package.py | 2 +- ckan/lib/helpers.py | 9 +++++++ ckan/templates/ajax_snippets/api_info.html | 25 +++++++++---------- ckan/templates/package/resource_read.html | 2 +- .../package/snippets/data_api_button.html | 3 +-- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/ckan/controllers/package.py b/ckan/controllers/package.py index ee3d5b08ae2..ef0c9305b43 100644 --- a/ckan/controllers/package.py +++ b/ckan/controllers/package.py @@ -1092,7 +1092,7 @@ def resource_read(self, id, resource_id): except KeyError: c.package['isopen'] = False - # TODO: find a nicer way of doing this + # Deprecated: c.datastore_api - use h.action_url instead c.datastore_api = '%s/api/action' % \ config.get('ckan.site_url', '').rstrip('/') diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index 4231541e360..84cd7313e50 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -17,6 +17,7 @@ import copy import urlparse from urllib import urlencode +import uuid from paste.deploy import converters from webhelpers.html import HTML, literal, tags, tools @@ -2353,6 +2354,14 @@ def radio(selected, id, checked): value="%s" type="radio">') % (selected, id, selected, id)) +@core_helper +def sanitize_id(id_): + '''Given an id (uuid4), if it has any invalid characters it raises + ValueError. + ''' + return str(uuid.UUID(id_)) + + core_helper(flash, name='flash') core_helper(localised_number) core_helper(localised_SI_number) diff --git a/ckan/templates/ajax_snippets/api_info.html b/ckan/templates/ajax_snippets/api_info.html index daba81038e6..6273865b266 100644 --- a/ckan/templates/ajax_snippets/api_info.html +++ b/ckan/templates/ajax_snippets/api_info.html @@ -1,18 +1,18 @@ {# Displays information about accessing a resource via the API. -datastore_root_url - The root API url. resource_id - The resource id embedded - If true will not include the "modal" classes on the snippet. Example - {% snippet 'ajax_snippets/api_info.html', datastore_root_url=datastore_root_url, resource_id=resource_id, embedded=true %} + {% snippet 'ajax_snippets/api_info.html', resource_id=resource_id, embedded=true %} #} -{% set sql_example_url = datastore_root_url + '/datastore_search_sql?sql=SELECT * from "' + resource_id + '" WHERE title LIKE \'jones\'' %} - +{% set resource_id = h.sanitize_id(resource_id) %} +{% set sql_example_url = h.url_for(controller='api', action='action', ver=3, logic_function='datastore_search_sql', qualified=True) + '?sql=SELECT * from "' + resource_id + '" WHERE title LIKE \'jones\'' %} +{# not urlencoding the sql because its clearer #}