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.

-