2013-11-15 13:26:26 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2013-12-03 11:46:57 +00:00
|
|
|
import logging
|
2014-01-24 10:46:10 +00:00
|
|
|
import re
|
2013-11-25 16:59:58 +00:00
|
|
|
import traceback
|
2013-11-27 17:04:32 +00:00
|
|
|
|
2013-12-03 17:17:14 +00:00
|
|
|
import werkzeug
|
2013-11-15 15:48:40 +00:00
|
|
|
import werkzeug.routing
|
2013-11-27 17:04:32 +00:00
|
|
|
|
2013-11-15 13:26:26 +00:00
|
|
|
import openerp
|
|
|
|
from openerp.addons.base import ir
|
2014-01-27 11:40:34 +00:00
|
|
|
from openerp.addons.base.ir import ir_qweb
|
2013-12-02 12:56:52 +00:00
|
|
|
from openerp.addons.website.models.website import slug
|
2013-11-25 16:59:58 +00:00
|
|
|
from openerp.http import request
|
|
|
|
from openerp.osv import orm
|
2013-11-15 13:26:26 +00:00
|
|
|
|
2013-12-03 11:46:57 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2014-01-24 10:46:10 +00:00
|
|
|
class RequestUID(object):
|
|
|
|
def __init__(self, **kw):
|
|
|
|
self.__dict__.update(kw)
|
|
|
|
|
2013-11-15 13:26:26 +00:00
|
|
|
class ir_http(orm.AbstractModel):
|
|
|
|
_inherit = 'ir.http'
|
|
|
|
|
2013-11-27 17:04:32 +00:00
|
|
|
rerouting_limit = 10
|
|
|
|
|
2013-11-15 13:26:26 +00:00
|
|
|
def _get_converters(self):
|
|
|
|
return dict(
|
|
|
|
super(ir_http, self)._get_converters(),
|
|
|
|
model=ModelConverter,
|
2013-11-15 15:48:40 +00:00
|
|
|
page=PageConverter,
|
2013-11-15 13:26:26 +00:00
|
|
|
)
|
|
|
|
|
2013-11-27 17:04:32 +00:00
|
|
|
def _dispatch(self):
|
2013-12-02 09:35:00 +00:00
|
|
|
first_pass = not hasattr(request, 'website')
|
2013-11-27 17:04:32 +00:00
|
|
|
request.website = None
|
|
|
|
func = None
|
|
|
|
try:
|
|
|
|
func, arguments = self._find_handler()
|
2014-01-20 15:37:33 +00:00
|
|
|
request.website_enabled = func.routing.get('website', False)
|
2013-11-27 17:04:32 +00:00
|
|
|
except werkzeug.exceptions.NotFound:
|
|
|
|
# either we have a language prefixed route, either a real 404
|
|
|
|
# in all cases, website processes them
|
2014-01-20 15:37:33 +00:00
|
|
|
request.website_enabled = True
|
2013-11-27 17:04:32 +00:00
|
|
|
|
2014-01-20 15:37:33 +00:00
|
|
|
if request.website_enabled:
|
2013-11-27 17:04:32 +00:00
|
|
|
if func:
|
2014-01-20 15:37:33 +00:00
|
|
|
self._authenticate(func.routing['auth'])
|
2013-11-27 17:04:32 +00:00
|
|
|
else:
|
|
|
|
self._auth_method_public()
|
|
|
|
request.website = request.registry['website'].get_current_website(request.cr, request.uid, context=request.context)
|
2013-12-02 09:35:00 +00:00
|
|
|
if first_pass:
|
2013-12-02 17:21:10 +00:00
|
|
|
request.lang = request.website.default_lang_code
|
2013-11-27 17:04:32 +00:00
|
|
|
request.context['lang'] = request.lang
|
|
|
|
request.website.preprocess_request(request)
|
|
|
|
if not func:
|
|
|
|
path = request.httprequest.path.split('/')
|
2013-12-02 17:21:10 +00:00
|
|
|
langs = [lg[0] for lg in request.website.get_languages()]
|
2013-11-27 17:04:32 +00:00
|
|
|
if path[1] in langs:
|
|
|
|
request.lang = request.context['lang'] = path.pop(1)
|
|
|
|
path = '/'.join(path) or '/'
|
|
|
|
return self.reroute(path)
|
2013-12-05 15:06:33 +00:00
|
|
|
return self._handle_exception(code=404)
|
2013-11-27 17:04:32 +00:00
|
|
|
return super(ir_http, self)._dispatch()
|
|
|
|
|
|
|
|
def reroute(self, path):
|
|
|
|
if not hasattr(request, 'rerouting'):
|
|
|
|
request.rerouting = []
|
|
|
|
if path in request.rerouting:
|
|
|
|
raise Exception("Rerouting loop is forbidden")
|
|
|
|
request.rerouting.append(path)
|
|
|
|
if len(request.rerouting) > self.rerouting_limit:
|
|
|
|
raise Exception("Rerouting limit exceeded")
|
|
|
|
request.httprequest.environ['PATH_INFO'] = path
|
|
|
|
# void werkzeug cached_property. TODO: find a proper way to do this
|
|
|
|
for key in ('path', 'full_path', 'url', 'base_url'):
|
|
|
|
request.httprequest.__dict__.pop(key, None)
|
|
|
|
|
|
|
|
return self._dispatch()
|
|
|
|
|
2014-01-24 10:46:10 +00:00
|
|
|
def _postprocess_args(self, arguments):
|
|
|
|
url = request.httprequest.url
|
|
|
|
for arg in arguments.itervalues():
|
|
|
|
if isinstance(arg, orm.browse_record) and isinstance(arg._uid, RequestUID):
|
|
|
|
placeholder = arg._uid
|
|
|
|
arg._uid = request.uid
|
|
|
|
try:
|
|
|
|
good_slug = slug(arg)
|
|
|
|
if str(arg.id) != placeholder.value and placeholder.value != good_slug:
|
|
|
|
# TODO: properly recompose the url instead of using replace()
|
|
|
|
url = url.replace(placeholder.value, good_slug)
|
|
|
|
except KeyError:
|
|
|
|
return self._handle_exception(werkzeug.exceptions.NotFound())
|
|
|
|
if url != request.httprequest.url:
|
|
|
|
werkzeug.exceptions.abort(werkzeug.utils.redirect(url))
|
|
|
|
|
2013-12-05 15:06:33 +00:00
|
|
|
def _handle_exception(self, exception=None, code=500):
|
2014-01-29 13:52:08 +00:00
|
|
|
if isinstance(exception, werkzeug.exceptions.HTTPException) and hasattr(exception, 'response') and exception.response:
|
2013-12-05 15:06:33 +00:00
|
|
|
return exception.response
|
2014-01-20 15:37:33 +00:00
|
|
|
if getattr(request, 'website_enabled', False) and request.website:
|
2013-12-05 15:06:33 +00:00
|
|
|
values = dict(
|
|
|
|
exception=exception,
|
|
|
|
traceback=traceback.format_exc(exception),
|
|
|
|
)
|
|
|
|
if exception:
|
2014-01-31 11:36:11 +00:00
|
|
|
code = getattr(exception, 'code', code)
|
2014-01-27 11:40:34 +00:00
|
|
|
if isinstance(exception, ir_qweb.QWebException):
|
|
|
|
values.update(qweb_exception=exception)
|
2014-01-31 11:36:11 +00:00
|
|
|
if isinstance(exception.qweb.get('cause'), openerp.exceptions.AccessError):
|
|
|
|
code = 403
|
2013-12-05 15:06:33 +00:00
|
|
|
if code == 500:
|
|
|
|
logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
|
2014-01-31 11:36:11 +00:00
|
|
|
if 'qweb_exception' in values:
|
2013-12-17 13:46:00 +00:00
|
|
|
view = request.registry.get("ir.ui.view")
|
2014-01-31 11:36:11 +00:00
|
|
|
views = view._views_get(request.cr, request.uid, exception.qweb['template'], request.context)
|
2014-01-27 11:40:34 +00:00
|
|
|
to_reset = [v for v in views if v.model_data_id.noupdate is True]
|
2013-12-17 13:46:00 +00:00
|
|
|
values['views'] = to_reset
|
2013-12-05 15:06:33 +00:00
|
|
|
elif code == 403:
|
|
|
|
logger.warn("403 Forbidden:\n\n%s", values['traceback'])
|
|
|
|
|
|
|
|
values.update(
|
|
|
|
status_message=werkzeug.http.HTTP_STATUS_CODES[code],
|
|
|
|
status_code=code,
|
|
|
|
)
|
|
|
|
|
|
|
|
if not request.uid:
|
|
|
|
self._auth_method_public()
|
2013-11-25 16:59:58 +00:00
|
|
|
|
2013-12-05 15:06:33 +00:00
|
|
|
try:
|
|
|
|
html = request.website._render('website.%s' % code, values)
|
2014-01-31 11:36:11 +00:00
|
|
|
except Exception:
|
2013-12-05 15:06:33 +00:00
|
|
|
html = request.website._render('website.http_error', values)
|
|
|
|
return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8')
|
|
|
|
|
|
|
|
return super(ir_http, self)._handle_exception(exception)
|
2013-11-15 13:26:26 +00:00
|
|
|
|
|
|
|
class ModelConverter(ir.ir_http.ModelConverter):
|
|
|
|
def __init__(self, url_map, model=False):
|
|
|
|
super(ModelConverter, self).__init__(url_map, model)
|
2013-11-19 11:32:05 +00:00
|
|
|
self.regex = r'(?:[A-Za-z0-9-_]+?-)?(\d+)(?=$|/)'
|
2013-11-15 13:26:26 +00:00
|
|
|
|
|
|
|
def to_url(self, value):
|
2013-12-02 12:56:52 +00:00
|
|
|
return slug(value)
|
2013-11-15 13:26:26 +00:00
|
|
|
|
2014-01-24 10:46:10 +00:00
|
|
|
def to_python(self, value):
|
|
|
|
m = re.match(self.regex, value)
|
|
|
|
_uid = RequestUID(value=value, match=m, converter=self)
|
|
|
|
return request.registry[self.model].browse(
|
|
|
|
request.cr, _uid, int(m.group(1)), context=request.context)
|
|
|
|
|
2013-11-25 10:08:13 +00:00
|
|
|
def generate(self, cr, uid, query=None, context=None):
|
2013-11-19 10:21:17 +00:00
|
|
|
return request.registry[self.model].name_search(
|
2013-11-25 10:08:13 +00:00
|
|
|
cr, uid, name=query or '', context=context)
|
2013-11-15 15:48:40 +00:00
|
|
|
|
|
|
|
class PageConverter(werkzeug.routing.PathConverter):
|
|
|
|
""" Only point of this converter is to bundle pages enumeration logic
|
|
|
|
|
|
|
|
Sads got: no way to get the view's human-readable name even if one exists
|
|
|
|
"""
|
2013-11-25 10:08:13 +00:00
|
|
|
def generate(self, cr, uid, query=None, context=None):
|
2013-11-15 15:48:40 +00:00
|
|
|
View = request.registry['ir.ui.view']
|
|
|
|
views = View.search_read(
|
2013-11-25 10:08:13 +00:00
|
|
|
cr, uid, [['page', '=', True]],
|
|
|
|
fields=[], order='name', context=context)
|
2013-11-15 15:48:40 +00:00
|
|
|
xids = View.get_external_id(
|
2013-11-25 10:08:13 +00:00
|
|
|
cr, uid, [view['id'] for view in views], context=context)
|
2013-11-15 15:48:40 +00:00
|
|
|
|
|
|
|
for view in views:
|
|
|
|
xid = xids[view['id']]
|
2014-02-07 15:06:23 +00:00
|
|
|
if xid and (not query or query.lower() in xid.lower()):
|
2013-11-15 15:48:40 +00:00
|
|
|
yield xid
|