171 lines
5.5 KiB
Python
171 lines
5.5 KiB
Python
|
# -*- 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} <a href="{1}">{1}</a>.').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 <http://google-styleguide.googlecode.com/svn/trunk/jsoncstyleguide.xml>`_, 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)
|