diff --git a/ckan/controllers/api.py b/ckan/controllers/api.py index cf22ba9ad78..f13e8eb7e14 100644 --- a/ckan/controllers/api.py +++ b/ckan/controllers/api.py @@ -3,6 +3,7 @@ import cgi import datetime import glob +import urllib from pylons import c, request, response from pylons.i18n import _, gettext @@ -545,7 +546,7 @@ def search(self, ver=None, register=None): def _get_search_params(cls, request_params): if 'qjson' in request_params: try: - qjson_param = request_params['qjson'].replace('\\\\u','\\u') + qjson_param = request_params['qjson'].replace('\\\\u', '\\u') params = h.json.loads(qjson_param, encoding='utf8') except ValueError, e: raise ValueError(gettext('Malformed qjson value') + ': %r' @@ -668,9 +669,8 @@ def group_exists(val): def dataset_autocomplete(self): q = request.params.get('incomplete', '') - q_lower = q.lower() limit = request.params.get('limit', 10) - tag_names = [] + package_dicts = [] if q: context = {'model': model, 'session': model.Session, 'user': c.user or c.author} @@ -752,9 +752,103 @@ def i18n_js_translations(self, lang): ''' translation strings for front end ''' ckan_path = os.path.join(os.path.dirname(__file__), '..') source = os.path.abspath(os.path.join(ckan_path, 'public', - 'base', 'i18n', '%s.js' % lang)) + 'base', 'i18n', '%s.js' % lang)) response.headers['Content-Type'] = CONTENT_TYPES['json'] if not os.path.exists(source): return '{}' f = open(source, 'r') return(f) + + @classmethod + def _get_request_data(cls, try_url_params=False): + '''Returns a dictionary, extracted from a request. + + If there is no data, None or "" is returned. + ValueError will be raised if the data is not a JSON-formatted dict. + + The data is retrieved as a JSON-encoded dictionary from the request + body. Or, if the `try_url_params` argument is True and the request is + a GET request, then an attempt is made to read the data from the url + parameters of the request. + + try_url_params + If try_url_params is False, then the data_dict is read from the + request body. + + If try_url_params is True and the request is a GET request then the + data is read from the url parameters. The resulting dict will only + be 1 level deep, with the url-param fields being the keys. If a + single key has more than one value specified, then the value will + be a list of strings, otherwise just a string. + + ''' + def make_unicode(entity): + '''Cast bare strings and strings in lists or dicts to Unicode. ''' + if isinstance(entity, str): + return unicode(entity) + elif isinstance(entity, list): + new_items = [] + for item in entity: + new_items.append(make_unicode(item)) + return new_items + elif isinstance(entity, dict): + new_dict = {} + for key, val in entity.items(): + new_dict[key] = make_unicode(val) + return new_dict + else: + return entity + + cls.log.debug('Retrieving request params: %r' % request.params) + cls.log.debug('Retrieving request POST: %r' % request.POST) + cls.log.debug('Retrieving request GET: %r' % request.GET) + request_data = None + if request.POST: + try: + keys = request.POST.keys() + # Parsing breaks if there is a = in the value, so for now + # we will check if the data is actually all in a single key + if keys and request.POST[keys[0]] in [u'1', u'']: + request_data = keys[0] + else: + request_data = urllib.unquote_plus(request.body) + except Exception, inst: + msg = "Could not find the POST data: %r : %s" % \ + (request.POST, inst) + raise ValueError(msg) + + elif try_url_params and request.GET: + return request.GET.mixed() + + else: + try: + if request.method in ['POST', 'PUT']: + request_data = request.body + else: + request_data = None + except Exception, inst: + msg = "Could not extract request body data: %s" % \ + (inst) + raise ValueError(msg) + cls.log.debug('Retrieved request body: %r' % request.body) + if not request_data: + msg = "No request body data" + raise ValueError(msg) + if request_data: + try: + request_data = h.json.loads(request_data, encoding='utf8') + except ValueError, e: + raise ValueError('Error decoding JSON data. ' + 'Error: %r ' + 'JSON data extracted from the request: %r' % + (e, request_data)) + if not isinstance(request_data, dict): + raise ValueError('Request data JSON decoded to %r but ' + 'it needs to be a dictionary.' % request_data) + # ensure unicode values + for key, val in request_data.items(): + # if val is str then assume it is ascii, since json converts + # utf8 encoded JSON to unicode + request_data[key] = make_unicode(val) + cls.log.debug('Request data extracted: %r' % request_data) + return request_data diff --git a/ckan/lib/base.py b/ckan/lib/base.py index 4fd2953ed21..33253d6e0d0 100644 --- a/ckan/lib/base.py +++ b/ckan/lib/base.py @@ -86,7 +86,7 @@ def render_jinja2(template_name, extra_vars): def render(template_name, extra_vars=None, cache_key=None, cache_type=None, cache_expire=None, method='xhtml', loader_class=MarkupTemplate, - cache_force = None, renderer=None): + cache_force=None, renderer=None): ''' Main template rendering function. ''' def render_template(): @@ -101,12 +101,12 @@ def render_template(): try: template_path, template_type = lib.render.template_info(template_name) except lib.render.TemplateNotFound: - template_type = 'genshi' + template_type = 'genshi' template_path = '' # snippets should not pass the context # but allow for legacy genshi templates - if renderer == 'snippet' and template_type != 'genshi': + if renderer == 'snippet' and template_type != 'genshi': del globs['c'] del globs['tmpl_context'] @@ -115,12 +115,12 @@ def render_template(): context_vars = globs.get('c') if context_vars: context_vars = dir(context_vars) - debug_info = {'template_name' : template_name, - 'template_path' : template_path, - 'template_type' : template_type, - 'vars' : globs, + debug_info = {'template_name': template_name, + 'template_path': template_path, + 'template_type': template_type, + 'vars': globs, 'c_vars': context_vars, - 'renderer' : renderer,} + 'renderer': renderer} if 'CKAN_DEBUG_INFO' not in request.environ: request.environ['CKAN_DEBUG_INFO'] = [] request.environ['CKAN_DEBUG_INFO'].append(debug_info) @@ -221,9 +221,9 @@ def __before__(self, action, **params): if c.userobj: from ckan.logic import get_action new_activities_count = get_action( - 'dashboard_new_activities_count') + 'dashboard_new_activities_count') context = {'model': model, 'session': model.Session, - 'user': c.user or c.author} + 'user': c.user or c.author} c.new_activities = new_activities_count(context, {}) def _identify_user(self): @@ -340,119 +340,6 @@ def _set_cors(self): response.headers['Access-Control-Allow-Headers'] = \ "X-CKAN-API-KEY, Authorization, Content-Type" - def _get_user(self, reference): - return model.User.by_name(reference) - - def _get_pkg(self, reference): - return model.Package.get(reference) - - def _get_group(self, reference): - return model.Group.get(reference) - - def _get_tag(self, reference): - return model.Tag.get(reference) - - @classmethod - def _get_request_data(cls, try_url_params=False): - '''Returns a dictionary, extracted from a request. - - If there is no data, None or "" is returned. - ValueError will be raised if the data is not a JSON-formatted dict. - - The data is retrieved as a JSON-encoded dictionary from the request - body. Or, if the `try_url_params` argument is True and the request is - a GET request, then an attempt is made to read the data from the url - parameters of the request. - - try_url_params - If try_url_params is False, then the data_dict is read from the - request body. - - If try_url_params is True and the request is a GET request then the - data is read from the url parameters. The resulting dict will only - be 1 level deep, with the url-param fields being the keys. If a - single key has more than one value specified, then the value will - be a list of strings, otherwise just a string. - - This function is only used by the API, so no strings need to be - translated. - - TODO: If this is only used by the API, then perhaps it should be - moved to the api controller class? - ''' - cls.log.debug('Retrieving request params: %r' % request.params) - cls.log.debug('Retrieving request POST: %r' % request.POST) - cls.log.debug('Retrieving request GET: %r' % request.GET) - request_data = None - if request.POST: - try: - keys = request.POST.keys() - # Parsing breaks if there is a = in the value, so for now - # we will check if the data is actually all in a single key - if keys and request.POST[keys[0]] in [u'1', u'']: - request_data = keys[0] - else: - request_data = urllib.unquote_plus(request.body) - except Exception, inst: - msg = "Could not find the POST data: %r : %s" % \ - (request.POST, inst) - raise ValueError(msg) - - elif try_url_params and request.GET: - return request.GET.mixed() - - else: - try: - if request.method in ['POST', 'PUT']: - request_data = request.body - else: - request_data = None - except Exception, inst: - msg = "Could not extract request body data: %s" % \ - (inst) - raise ValueError(msg) - cls.log.debug('Retrieved request body: %r' % request.body) - if not request_data: - msg = "No request body data" - raise ValueError(msg) - if request_data: - try: - request_data = json.loads(request_data, encoding='utf8') - except ValueError, e: - raise ValueError('Error decoding JSON data. ' - 'Error: %r ' - 'JSON data extracted from the request: %r' % - (e, request_data)) - if not isinstance(request_data, dict): - raise ValueError('Request data JSON decoded to %r but ' - 'it needs to be a dictionary.' % request_data) - # ensure unicode values - for key, val in request_data.items(): - # if val is str then assume it is ascii, since json converts - # utf8 encoded JSON to unicode - request_data[key] = cls._make_unicode(val) - cls.log.debug('Request data extracted: %r' % request_data) - return request_data - - @classmethod - def _make_unicode(cls, entity): - """Cast bare strings and strings in lists or dicts to Unicode - """ - if isinstance(entity, str): - return unicode(entity) - elif isinstance(entity, list): - new_items = [] - for item in entity: - new_items.append(cls._make_unicode(item)) - return new_items - elif isinstance(entity, dict): - new_dict = {} - for key, val in entity.items(): - new_dict[key] = cls._make_unicode(val) - return new_dict - else: - return entity - def _get_user_for_apikey(self): apikey_header_name = config.get(APIKEY_HEADER_NAME_KEY, APIKEY_HEADER_NAME_DEFAULT) @@ -475,115 +362,6 @@ def _get_user_for_apikey(self): user = query.filter_by(apikey=apikey).first() return user - def _get_timing_cache_path(self): - - return path - - def _handle_update_of_authz(self, domain_object): - '''In the event of a post request to a domain object\'s authz form, - work out which of the four possible actions is to be done, - and do it before displaying the page. - - Returns the updated roles for the domain_object. - ''' - from ckan.logic import NotFound, get_action - - context = {'model': model, 'session': model.Session, - 'user': c.user or c.author} - data_dict = {'domain_object': domain_object.id} - - # Work out actions needed, depending on which button was pressed - update_type = 'user' - if 'save' in request.POST: - update_or_add = 'update' - elif 'add' in request.POST: - update_or_add = 'add' - else: - update_type = None - update_or_add = None - - # Work out what role checkboxes are checked or unchecked - checked_roles = [box_id for (box_id, value) in request.params.items() - if (value == u'on')] - unchecked_roles = [box_id for (box_id, value) in request.params.items() - if (value == u'submitted')] - - action = None - if update_or_add is 'update': - # Get user_roles by decoding the checkbox grid - user$role strings - user_roles = {} - for checked_role in checked_roles: - obj_id, role = checked_role.split('$') - if obj_id not in user_roles: - user_roles[obj_id] = [] - user_roles[obj_id].append(role) - # Users without roles need adding to the user_roles too to make - # their roles be deleted - for unchecked_role in unchecked_roles: - obj_id, role = unchecked_role.split('$') - if obj_id not in user_roles: - user_roles[obj_id] = [] - # Convert user_roles to role dictionaries - role_dicts = [] - for user, roles in user_roles.items(): - role_dicts.append({update_type: user, 'roles': roles}) - data_dict['user_roles'] = role_dicts - - action = 'user_role_bulk_update' - success_message = _('Updated') - elif update_or_add is 'add': - # Roles for this new user is a simple list from the checkbox row - data_dict['roles'] = checked_roles - - # User comes from the input box. - new_user = request.params.get('new_user_name') - if new_user: - data_dict[update_type] = new_user - - action = 'user_role_update' - success_message = _('User role(s) added') - else: - h.flash_error(_('Please supply a user name')) - - if action: - try: - roles = get_action(action)(context, data_dict) - except NotFound, e: - h.flash_error(_('Not found') + (': %s' % e if str(e) else '')) - else: - h.flash_success(success_message) - - # Return roles for all users on this domain object - if update_or_add is 'add': - if update_type in data_dict: - del data_dict[update_type] - return get_action('roles_show')(context, data_dict) - - def _prepare_authz_info_for_render(self, user_object_roles): - # ================= - # Display the page - - # Find out all the possible roles. At the moment, any role can be - # associated with any object, so that's easy: - possible_roles = model.Role.get_all() - - # uniquify and sort - users = sorted(list(set([uor['user_id'] - for uor in user_object_roles['roles'] - if uor['user_id']]))) - - # make a dictionary from (user, role) to True, False - users_roles = [(uor['user_id'], uor['role']) - for uor in user_object_roles['roles'] - if uor['user_id']] - user_role_dict = {} - for u in users: - for r in possible_roles: - user_role_dict[(u, r)] = (u, r) in users_roles - - c.roles = possible_roles - c.users = users - c.user_role_dict = user_role_dict # Include the '_' function in the public names __all__ = [__name for __name in locals().keys() if not __name.startswith('_')