Skip to content

Commit

Permalink
Merge branch 'release-v2.4.8' into release-v2.4-latest
Browse files Browse the repository at this point in the history
  • Loading branch information
smotornyuk committed Aug 2, 2017
2 parents 1fee81b + d7942d5 commit 3e4708d
Show file tree
Hide file tree
Showing 30 changed files with 373 additions and 97 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -7,6 +7,25 @@
Changelog
---------

v2.4.8 2017-08-02
=================

* Fix in organization / group form image URL field (#3661)
* Fix activity test to use utcnow (#3644)
* Changed required permission from 'update' to 'manage_group' (#3631)
* Catch invalid sort param exception (#3630)
* Choose direction of recreated package relationship depending on its type (#3626)
* Fix render_datetime for dates before year 1900 (#3611)
* Fix KeyError in 'package_create' (#3027)
* Allow slug preview to work with autocomplete fields (#2501)
* Fix filter results button not working for organization/group (#3620)
* Allow underscores in URL slug preview on create dataset (#3612)
* Create new resource view if resource format changed (#3515)
* Fixed incorrect escaping in `mail_to`
* Autocomplete fields are more responsive - 300ms timeout instead of 1s (#3693)
* Fixed dataset count display for groups (#3711)
* Restrict access to form pages (#3684)

v2.4.7 2017-03-22
=================

Expand Down
2 changes: 1 addition & 1 deletion ckan/__init__.py
@@ -1,4 +1,4 @@
__version__ = '2.4.7'
__version__ = '2.4.8'

__description__ = 'CKAN Software'
__long_description__ = \
Expand Down
46 changes: 33 additions & 13 deletions ckan/controllers/group.py
Expand Up @@ -170,14 +170,24 @@ def index(self):
context['user_id'] = c.userobj.id
context['user_is_admin'] = c.userobj.sysadmin

data_dict_global_results = {
'all_fields': False,
'q': q,
'sort': sort_by,
'type': group_type or 'group',
}
global_results = self._action('group_list')(context,
data_dict_global_results)
try:
data_dict_global_results = {
'all_fields': False,
'q': q,
'sort': sort_by,
'type': group_type or 'group',
}
global_results = self._action('group_list')(
context, data_dict_global_results)
except ValidationError as e:
if e.error_dict and e.error_dict.get('message'):
msg = e.error_dict['message']
else:
msg = str(e)
h.flash_error(msg)
c.page = h.Page([], 0)
return render(self._index_template(group_type),
extra_vars={'group_type': group_type})

data_dict_page_results = {
'all_fields': True,
Expand Down Expand Up @@ -405,6 +415,7 @@ def bulk_process(self, id):
data_dict = {'id': id}

try:
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
Expand All @@ -413,7 +424,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",
Expand Down Expand Up @@ -673,16 +684,21 @@ def members(self, id):
'user': c.user or c.author}

try:
data_dict = {'id': id}
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):
Expand All @@ -691,7 +707,11 @@ def member_new(self, id):
context = {'model': model, 'session': model.Session,
'user': c.user or c.author}

#self._check_access('group_delete', context, {'id': id})
try:
self._check_access('group_member_create', context, {'id': id})
except NotAuthorized:
abort(403, _('Unauthorized to create group %s members') % '')

try:
data_dict = {'id': id}
data_dict['include_datasets'] = False
Expand Down
17 changes: 9 additions & 8 deletions ckan/controllers/package.py
Expand Up @@ -583,17 +583,22 @@ def new(self, data=None, errors=None, error_summary=None):

def resource_edit(self, id, resource_id, data=None, errors=None,
error_summary=None):
context = {'model': model, 'session': model.Session,
'api_version': 3, 'for_edit': True,
'user': c.user, 'auth_user_obj': c.userobj}
data_dict = {'id': id}

try:
check_access('package_update', context, data_dict)
except NotAuthorized:
abort(403, _('User %r not authorized to edit %s') % (c.user, id))

if request.method == 'POST' and not data:
data = data or clean_dict(dict_fns.unflatten(tuplize_dict(parse_params(
request.POST))))
# we don't want to include save as it is part of the form
del data['save']

context = {'model': model, 'session': model.Session,
'api_version': 3, 'for_edit': True,
'user': c.user or c.author, 'auth_user_obj': c.userobj}

data['package_id'] = id
try:
if resource_id:
Expand All @@ -610,10 +615,6 @@ def resource_edit(self, id, resource_id, data=None, errors=None,
abort(401, _('Unauthorized to edit this resource'))
redirect(h.url_for(controller='package', action='resource_read',
id=id, resource_id=resource_id))

context = {'model': model, 'session': model.Session,
'api_version': 3, 'for_edit': True,
'user': c.user or c.author, 'auth_user_obj': c.userobj}
pkg_dict = get_action('package_show')(context, {'id': id})
if pkg_dict['state'].startswith('draft'):
# dataset has not yet been fully created
Expand Down
37 changes: 34 additions & 3 deletions ckan/lib/helpers.py
Expand Up @@ -17,8 +17,7 @@
from urllib import urlencode

from paste.deploy.converters import asbool
from webhelpers.html import escape, HTML, literal, url_escape
from webhelpers.html.tools import mail_to
from webhelpers.html import HTML, literal, url_escape
from webhelpers.html.tags import *
from lib.markdown import markdown
from webhelpers import paginate
Expand All @@ -45,6 +44,7 @@
from ckan.common import (
_, ungettext, g, c, request, session, json, OrderedDict
)
from markupsafe import Markup, escape

get_available_locales = i18n.get_available_locales
get_locales_dict = i18n.get_locales_dict
Expand Down Expand Up @@ -116,7 +116,7 @@ def get_site_protocol_and_host():
If `ckan.site_url` is set like this::
ckan.site_url = http://example.com
Then this function would return a tuple `('http', 'example.com')`
If the setting is missing, `(None, None)` is returned instead.
Expand Down Expand Up @@ -307,6 +307,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')
Expand Down Expand Up @@ -994,6 +999,24 @@ def render_datetime(datetime_, date_format=None, with_hours=False):
return ''
# if date_format was supplied we use it
if date_format:

# See http://bugs.python.org/issue1777412
if datetime_.year < 1900:
year = str(datetime_.year)

date_format = re.sub('(?<!%)((%%)*)%y',
r'\g<1>{year}'.format(year=year[-2:]),
date_format)
date_format = re.sub('(?<!%)((%%)*)%Y',
r'\g<1>{year}'.format(year=year),
date_format)

datetime_ = datetime.datetime(2016, datetime_.month, datetime_.day,
datetime_.hour, datetime_.minute,
datetime_.second)

return datetime_.strftime(date_format)

return datetime_.strftime(date_format)
# the localised date
return formatters.localised_nice_date(datetime_, show_date=True,
Expand Down Expand Up @@ -2076,6 +2099,13 @@ def license_options(existing_license_id=None):
for license_id in license_ids]


def mail_to(email_address, name):
email = escape(email_address)
author = escape(name)
html = Markup(u'<a href=mailto:{0}>{1}</a>'.format(email, author))
return html


# these are the functions that will end up in `h` template helpers
__allowed_functions__ = [
# functions defined in ckan.lib.helpers
Expand Down Expand Up @@ -2137,6 +2167,7 @@ def license_options(existing_license_id=None):
'debug_inspect',
'dict_list_reduce',
'full_current_url',
'current_url',
'popular',
'debug_full_info_as_list',
'get_facet_title',
Expand Down
6 changes: 4 additions & 2 deletions ckan/logic/action/create.py
Expand Up @@ -306,8 +306,10 @@ def resource_create(context, data_dict):
_get_action('package_update')(context, pkg_dict)
context.pop('defer_commit')
except ValidationError, e:
errors = e.error_dict['resources'][-1]
raise ValidationError(errors)
try:
raise ValidationError(e.error_dict['resources'][-1])
except (KeyError, IndexError):
raise ValidationError(e.error_dict)

## Get out resource_id resource from model as it will not appear in
## package_show until after commit
Expand Down
14 changes: 12 additions & 2 deletions ckan/logic/action/update.py
Expand Up @@ -127,6 +127,7 @@ def resource_update(context, data_dict):

resource = model.Resource.get(id)
context["resource"] = resource
old_resource_format = resource.format

if not resource:
log.error('Could not find resource ' + id)
Expand Down Expand Up @@ -158,14 +159,23 @@ def resource_update(context, data_dict):
updated_pkg_dict = _get_action('package_update')(context, pkg_dict)
context.pop('defer_commit')
except ValidationError, e:
errors = e.error_dict['resources'][n]
raise ValidationError(errors)
try:
raise ValidationError(e.error_dict['resources'][-1])
except (KeyError, IndexError):
raise ValidationError(e.error_dict)

upload.upload(id, uploader.get_max_resource_size())
model.repo.commit()

resource = _get_action('resource_show')(context, {'id': id})

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,
'resource': resource})

for plugin in plugins.PluginImplementations(plugins.IResourceController):
plugin.after_update(context, resource)

Expand Down
2 changes: 1 addition & 1 deletion ckan/logic/auth/create.py
Expand Up @@ -211,7 +211,7 @@ def _check_group_auth(context, data_dict):
groups = groups - set(pkg_groups)

for group in groups:
if not authz.has_user_permission_for_group_or_org(group.id, user, 'update'):
if not authz.has_user_permission_for_group_or_org(group.id, user, 'manage_group'):
return False

return True
Expand Down
13 changes: 7 additions & 6 deletions ckan/logic/auth/update.py
Expand Up @@ -173,14 +173,15 @@ 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}

Expand Down
4 changes: 3 additions & 1 deletion ckan/model/package.py
Expand Up @@ -227,16 +227,18 @@ def add_relationship(self, type_, related_package, comment=u''):
if type_ in package_relationship.PackageRelationship.get_forward_types():
subject = self
object_ = related_package
direction = "forward"
elif type_ in package_relationship.PackageRelationship.get_reverse_types():
type_ = package_relationship.PackageRelationship.reverse_to_forward_type(type_)
assert type_
subject = related_package
object_ = self
direction = "reverse"
else:
raise KeyError, 'Package relationship type: %r' % type_

rels = self.get_relationships(with_package=related_package,
type=type_, active=False, direction="forward")
type=type_, active=False, direction=direction)
if rels:
rel = rels[0]
if comment:
Expand Down
5 changes: 0 additions & 5 deletions ckan/model/user.py
Expand Up @@ -53,13 +53,8 @@ def by_email(cls, email):

@classmethod
def get(cls, user_reference):
# double slashes in an openid often get turned into single slashes
# by browsers, so correct that for the openid lookup
corrected_openid_user_ref = cls.DOUBLE_SLASH.sub('://\\1',
user_reference)
query = meta.Session.query(cls).autoflush(False)
query = query.filter(or_(cls.name == user_reference,
cls.openid == corrected_openid_user_ref,
cls.id == user_reference))
return query.first()

Expand Down
2 changes: 1 addition & 1 deletion ckan/public/base/javascript/modules/autocomplete.js
Expand Up @@ -24,7 +24,7 @@ this.ckan.module('autocomplete', function (jQuery, _) {
label: false,
items: 10,
source: null,
interval: 1000,
interval: 300,
dropdownClass: '',
containerClass: '',
i18n: {
Expand Down
2 changes: 1 addition & 1 deletion ckan/public/base/javascript/modules/autocomplete.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion ckan/public/base/javascript/modules/slug-preview.js
Expand Up @@ -18,7 +18,7 @@ this.ckan.module('slug-preview-target', {

// Watch for updates to the target field and update the hidden slug field
// triggering the "change" event manually.
el.on('keyup.slug-preview', function (event) {
el.on('keyup.slug-preview input.slug-preview', function (event) {
sandbox.publish('slug-target-changed', this.value);
//slug.val(this.value).trigger('change');
});
Expand Down
2 changes: 1 addition & 1 deletion ckan/public/base/javascript/modules/slug-preview.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3e4708d

Please sign in to comment.