# -*- coding: utf-8 -*- import collections import json from markupsafe import Markup from webhelpers.html import tags import webob.dec import webob.exc from . import templates N_ = lambda message: message errors_explanation = { 400: N_("Request is faulty"), 401: N_("Access is restricted to authorized persons."), 403: N_("Access is forbidden."), 404: N_("The requested page was not found."), } errors_message = { 401: N_("You must login to access this page."), } errors_title = { 400: N_("Unable to Access"), 401: N_("Access Denied"), 403: N_("Access Denied"), 404: N_("Unable to Access"), } def bad_request(ctx, **kw): return error(ctx, 400, **kw) def discard_empty_items(data): if isinstance(data, collections.Mapping): # Use type(data) to keep OrderedDicts. data = type(data)( (name, discard_empty_items(value)) for name, value in data.iteritems() if value is not None ) return data def error(ctx, code, **kw): response = webob.exc.status_map[code](headers=kw.pop('headers', None)) if code != 204: # No content body = kw.pop('body', None) if body is None: template_path = kw.pop('template_path', '/http-error.mako') explanation = kw.pop('explanation', None) if explanation is None: explanation = errors_explanation.get(code) explanation = ctx._(explanation) if explanation is not None else response.explanation message = kw.pop('message', None) if message is None: message = errors_message.get(code) if message is not None: message = ctx._(message) comment = kw.pop('comment', None) if isinstance(comment, dict): comment = tags.ul(u'{0} : {1}'.format(key, value) for key, value in comment.iteritems()) elif isinstance(comment, list): comment = tags.ul(comment) title = kw.pop('title', None) if title is None: title = errors_title.get(code) title = ctx._(title) if title is not None else response.status body = templates.render(ctx, template_path, comment=comment, explanation=explanation, message=message, response=response, title=title, **kw) response.body = body.encode('utf-8') if isinstance(body, unicode) else body return response def forbidden(ctx, **kw): return error(ctx, 403, **kw) def method_not_allowed(ctx, **kw): return error(ctx, 405, **kw) def no_content(ctx, headers=None): return error(ctx, 204, headers=headers) def not_found(ctx, **kw): return error(ctx, 404, **kw) def redirect(ctx, code=302, location=None, **kw): assert location is not None location_str = location.encode('utf-8') if isinstance(location, unicode) else location response = webob.exc.status_map[code](headers=kw.pop('headers', None), location=location_str) body = kw.pop('body', None) if body is None: template_path = kw.pop('template_path', '/http-error.mako') explanation = kw.pop('explanation', None) if explanation is None: explanation = Markup(u'{0} {1}.').format(ctx._(u"You'll be redirected to page"), location) message = kw.pop('message', None) if message is None: message = errors_message.get(code) if message is not None: message = ctx._(message) title = kw.pop('title', None) if title is None: title = ctx._("Redirection in progress...") body = templates.render(ctx, template_path, comment=kw.pop('comment', None), explanation=explanation, message=message, response=response, title=title, **kw) response.body = body.encode('utf-8') if isinstance(body, unicode) else body return response def respond_json(ctx, data, code=None, headers=None, jsonp=None): """Return a JSON response. This function is optimized for JSON following `Google JSON Style Guide `_, but will handle any JSON except for HTTP errors. """ if isinstance(data, collections.Mapping): # Remove null properties as recommended by Google JSON Style Guide. data = discard_empty_items(data) error = data.get('error') else: error = None if headers is None: headers = [] if jsonp: headers.append(('Content-Type', 'application/javascript; charset=utf-8')) else: headers.append(('Content-Type', 'application/json; charset=utf-8')) if error: code = code or error['code'] assert isinstance(code, int) response = webob.exc.status_map[code](headers=headers) if error.get('code') is None: error['code'] = code if error.get('message') is None: error['message'] = response.title else: response = ctx.req.response if code is not None: response.status = code response.headers.update(headers) text = unicode(json.dumps(data, encoding='utf-8', ensure_ascii=False, indent=2, sort_keys=True)) if jsonp: text = u'{0}({1})'.format(jsonp, text) response.text = text return response def unauthorized(ctx, **kw): return error(ctx, 401, **kw)