From 07eaace3ee711e5d61e281f38076d412af93d289 Mon Sep 17 00:00:00 2001
From: Fabien Meghazi
Date: Wed, 27 Nov 2013 18:04:32 +0100
Subject: [PATCH] [ADD] custom website ir.http#dispatch and rerouting
bzr revid: fme@openerp.com-20131127170432-z7d1w4ck03d7wwia
---
addons/website/models/ir_http.py | 94 +++++++++++++++++-----
addons/website/models/website.py | 58 +++----------
addons/website/views/website_templates.xml | 5 +-
3 files changed, 88 insertions(+), 69 deletions(-)
diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py
index 005b45f1d6a..8eadebf676d 100644
--- a/addons/website/models/ir_http.py
+++ b/addons/website/models/ir_http.py
@@ -1,17 +1,20 @@
# -*- coding: utf-8 -*-
import traceback
+
import werkzeug.routing
+
import openerp
from openerp.addons.base import ir
from openerp.http import request
from openerp.osv import orm
from ..utils import slugify
-from website import get_current_website
class ir_http(orm.AbstractModel):
_inherit = 'ir.http'
+ rerouting_limit = 10
+
def _get_converters(self):
return dict(
super(ir_http, self)._get_converters(),
@@ -26,29 +29,82 @@ class ir_http(orm.AbstractModel):
else:
request.uid = request.session.uid
- def _handle_403(self, exception):
- return self._render_error(403, {
- 'error': exception.message
- })
+ def _dispatch(self):
+ request.website = None
+ func = None
+ try:
+ func, arguments = self._find_handler()
+ request.cms = getattr(func, 'cms', False)
+ except werkzeug.exceptions.NotFound:
+ # either we have a language prefixed route, either a real 404
+ # in all cases, website processes them
+ request.cms = True
- def _handle_404(self, exception):
- return self._render_error(404)
+ if request.cms:
+ if func:
+ self._authenticate(getattr(func, 'auth', None))
+ else:
+ self._auth_method_public()
+ request.website = request.registry['website'].get_current_website(request.cr, request.uid, context=request.context)
+ langs = request.context['langs'] = [lg.code for lg in request.website.language_ids]
+ lang_cookie = request.httprequest.cookies.get('lang', None)
+ if lang_cookie in langs:
+ request.context['lang_cookie'] = lang_cookie
+ request.context['lang_default'] = request.website.default_lang_id.code
+ if not hasattr(request, 'lang'):
+ request.lang = request.context['lang_default']
+ request.context['lang'] = request.lang
+ request.website.preprocess_request(request)
+ if not func:
+ path = request.httprequest.path.split('/')
+ if path[1] in langs:
+ request.lang = request.context['lang'] = path.pop(1)
+ path = '/'.join(path) or '/'
+ return self.reroute(path)
+ return self._handle_404()
+
+ 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()
+
+ def _handle_403(self, exception):
+ if getattr(request, 'cms', False) and request.website:
+ self._auth_method_public()
+ return self._render_error(403, {
+ 'error': exception.message
+ })
+ raise exception
+
+ def _handle_404(self, exception=None):
+ if getattr(request, 'cms', False) and request.website:
+ return self._render_error(404)
+ raise request.not_found()
def _handle_500(self, exception):
- # TODO: proper logging
- return self._render_error(500, {
- 'exception': exception,
- 'traceback': traceback.format_exc(),
- 'qweb_template': getattr(exception, 'qweb_template', None),
- 'qweb_node': getattr(exception, 'qweb_node', None),
- 'qweb_eval': getattr(exception, 'qweb_eval', None),
- })
+ if getattr(request, 'cms', False) and request.website:
+ return self._render_error(500, {
+ 'exception': exception,
+ 'traceback': traceback.format_exc(),
+ 'qweb_template': getattr(exception, 'qweb_template', None),
+ 'qweb_node': getattr(exception, 'qweb_node', None),
+ 'qweb_eval': getattr(exception, 'qweb_eval', None),
+ })
+ raise exception
def _render_error(self, code, values=None):
- self._auth_method_public()
- if not hasattr(request, 'website'):
- request.website = get_current_website()
- request.website.preprocess_request(request)
return werkzeug.wrappers.Response(
request.website._render('website.%s' % code, values),
status=code,
diff --git a/addons/website/models/website.py b/addons/website/models/website.py
index 62844305403..a442f95bd95 100644
--- a/addons/website/models/website.py
+++ b/addons/website/models/website.py
@@ -3,7 +3,6 @@ import fnmatch
import functools
import inspect
import logging
-import psycopg2
import math
import itertools
import urllib
@@ -15,7 +14,7 @@ import werkzeug.exceptions
import werkzeug.wrappers
import openerp
-from openerp.osv import orm, osv, fields
+from openerp.osv import osv, fields
from openerp.tools.safe_eval import safe_eval
from openerp.addons.web import http
@@ -26,44 +25,20 @@ logger = logging.getLogger(__name__)
def route(routes, *route_args, **route_kwargs):
def decorator(f):
- new_routes = routes if isinstance(routes, list) else [routes]
f.cms = True
- f.multilang = route_kwargs.get('multilang', False)
- if f.multilang:
- route_kwargs.pop('multilang')
- for r in list(new_routes):
- new_routes.append('/' + r)
- @http.route(new_routes, *route_args, **route_kwargs)
- @functools.wraps(f, assigned=functools.WRAPPER_ASSIGNMENTS + ('func_name',))
- def wrap(*args, **kwargs):
- request.route_lang = kwargs.pop('lang_code', None)
- if not hasattr(request, 'website'):
- request.multilang = f.multilang
- request.website = get_current_website()
-
- if request.route_lang:
- lang_ok = [lg.code for lg in request.website.language_ids if lg.code == request.route_lang]
- if not lang_ok:
- return request.not_found()
- request.website.preprocess_request(request)
- return f(*args, **kwargs)
- return wrap
+ f.multilang = route_kwargs.pop('multilang', False)
+ return http.route(routes, *route_args, **route_kwargs)(f)
return decorator
-def get_current_website():
- # TODO: Select website, currently hard coded
- return request.registry['website'].browse(request.cr, request.uid, 1, context=request.context)
-
-
def url_for(path_or_uri, lang=None, keep_query=None):
location = path_or_uri.strip()
url = urlparse.urlparse(location)
if request and not url.netloc and not url.scheme:
location = urlparse.urljoin(request.httprequest.path, location)
+ lang = lang or request.context.get('lang')
langs = request.context.get('langs')
- if location[0] == '/' and (len(langs) > 1 or lang):
+ if location[0] == '/' and len(langs) > 1 and lang != request.context['lang_default']:
ps = location.split('/')
- lang = lang or request.context.get('lang')
if ps[1] in langs:
ps[1] = lang
else:
@@ -164,38 +139,27 @@ class website(osv.osv):
self.public_user = self.pool[ref[0]].browse(cr, uid, ref[1])
return self.public_user
+ def get_current_website(self, cr, uid, context=None):
+ # TODO: Select website, currently hard coded
+ return self.pool['website'].browse(cr, uid, 1, context=context)
+
def preprocess_request(self, cr, uid, ids, request, context=None):
def redirect(url):
return werkzeug.utils.redirect(url_for(url))
request.redirect = redirect
- if not hasattr(request, 'multilang'):
- # TODO: try to get rid of this multilang attribute
- request.multilang = False
-
is_public_user = request.uid == self.get_public_user(cr, uid, context).id
user = self.pool['res.users'].browse(cr, uid, uid, context=context)
website_publisher_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_website_publisher')[1]
is_website_publisher = website_publisher_id in [g.id for g in user.groups_id]
- # Select current language
- if hasattr(request, 'route_lang'):
- lang = request.route_lang
- else:
- lang = request.params.get('lang', None) or request.httprequest.cookies.get('lang', None)
- if lang not in [lg.code for lg in request.website.language_ids]:
- lang = request.website.default_lang_id.code
-
+ lang = request.context['lang']
is_master_lang = lang == request.website.default_lang_id.code
request.context.update({
- 'lang': lang,
- 'lang_selected': [lg for lg in request.website.language_ids if lg.code == lang],
- 'langs': [lg.code for lg in request.website.language_ids],
- 'multilang': request.multilang,
'is_public_user': is_public_user,
'is_master_lang': is_master_lang,
'editable': is_website_publisher,
- 'translatable': not is_public_user and not is_master_lang and request.multilang,
+ 'translatable': not is_public_user and not is_master_lang,
})
def get_template(self, cr, uid, ids, template, context=None):
diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml
index cc0379ce4d4..f5536b86374 100644
--- a/addons/website/views/website_templates.xml
+++ b/addons/website/views/website_templates.xml
@@ -188,8 +188,7 @@
their performance.
-