[ADD] custom website ir.http#dispatch and rerouting

bzr revid: fme@openerp.com-20131127170432-z7d1w4ck03d7wwia
This commit is contained in:
Fabien Meghazi 2013-11-27 18:04:32 +01:00
parent d1d1754ff4
commit 07eaace3ee
3 changed files with 88 additions and 69 deletions

View File

@ -1,17 +1,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import traceback import traceback
import werkzeug.routing import werkzeug.routing
import openerp import openerp
from openerp.addons.base import ir from openerp.addons.base import ir
from openerp.http import request from openerp.http import request
from openerp.osv import orm from openerp.osv import orm
from ..utils import slugify from ..utils import slugify
from website import get_current_website
class ir_http(orm.AbstractModel): class ir_http(orm.AbstractModel):
_inherit = 'ir.http' _inherit = 'ir.http'
rerouting_limit = 10
def _get_converters(self): def _get_converters(self):
return dict( return dict(
super(ir_http, self)._get_converters(), super(ir_http, self)._get_converters(),
@ -26,29 +29,82 @@ class ir_http(orm.AbstractModel):
else: else:
request.uid = request.session.uid request.uid = request.session.uid
def _handle_403(self, exception): def _dispatch(self):
return self._render_error(403, { request.website = None
'error': exception.message 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): if request.cms:
return self._render_error(404) 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): def _handle_500(self, exception):
# TODO: proper logging if getattr(request, 'cms', False) and request.website:
return self._render_error(500, { return self._render_error(500, {
'exception': exception, 'exception': exception,
'traceback': traceback.format_exc(), 'traceback': traceback.format_exc(),
'qweb_template': getattr(exception, 'qweb_template', None), 'qweb_template': getattr(exception, 'qweb_template', None),
'qweb_node': getattr(exception, 'qweb_node', None), 'qweb_node': getattr(exception, 'qweb_node', None),
'qweb_eval': getattr(exception, 'qweb_eval', None), 'qweb_eval': getattr(exception, 'qweb_eval', None),
}) })
raise exception
def _render_error(self, code, values=None): 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( return werkzeug.wrappers.Response(
request.website._render('website.%s' % code, values), request.website._render('website.%s' % code, values),
status=code, status=code,

View File

@ -3,7 +3,6 @@ import fnmatch
import functools import functools
import inspect import inspect
import logging import logging
import psycopg2
import math import math
import itertools import itertools
import urllib import urllib
@ -15,7 +14,7 @@ import werkzeug.exceptions
import werkzeug.wrappers import werkzeug.wrappers
import openerp 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.tools.safe_eval import safe_eval
from openerp.addons.web import http from openerp.addons.web import http
@ -26,44 +25,20 @@ logger = logging.getLogger(__name__)
def route(routes, *route_args, **route_kwargs): def route(routes, *route_args, **route_kwargs):
def decorator(f): def decorator(f):
new_routes = routes if isinstance(routes, list) else [routes]
f.cms = True f.cms = True
f.multilang = route_kwargs.get('multilang', False) f.multilang = route_kwargs.pop('multilang', False)
if f.multilang: return http.route(routes, *route_args, **route_kwargs)(f)
route_kwargs.pop('multilang')
for r in list(new_routes):
new_routes.append('/<string(length=5):lang_code>' + 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
return decorator 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): def url_for(path_or_uri, lang=None, keep_query=None):
location = path_or_uri.strip() location = path_or_uri.strip()
url = urlparse.urlparse(location) url = urlparse.urlparse(location)
if request and not url.netloc and not url.scheme: if request and not url.netloc and not url.scheme:
location = urlparse.urljoin(request.httprequest.path, location) location = urlparse.urljoin(request.httprequest.path, location)
lang = lang or request.context.get('lang')
langs = request.context.get('langs') 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('/') ps = location.split('/')
lang = lang or request.context.get('lang')
if ps[1] in langs: if ps[1] in langs:
ps[1] = lang ps[1] = lang
else: else:
@ -164,38 +139,27 @@ class website(osv.osv):
self.public_user = self.pool[ref[0]].browse(cr, uid, ref[1]) self.public_user = self.pool[ref[0]].browse(cr, uid, ref[1])
return self.public_user 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 preprocess_request(self, cr, uid, ids, request, context=None):
def redirect(url): def redirect(url):
return werkzeug.utils.redirect(url_for(url)) return werkzeug.utils.redirect(url_for(url))
request.redirect = redirect 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 is_public_user = request.uid == self.get_public_user(cr, uid, context).id
user = self.pool['res.users'].browse(cr, uid, uid, context=context) 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] 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] is_website_publisher = website_publisher_id in [g.id for g in user.groups_id]
# Select current language lang = request.context['lang']
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
is_master_lang = lang == request.website.default_lang_id.code is_master_lang = lang == request.website.default_lang_id.code
request.context.update({ 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_public_user': is_public_user,
'is_master_lang': is_master_lang, 'is_master_lang': is_master_lang,
'editable': is_website_publisher, '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): def get_template(self, cr, uid, ids, template, context=None):

View File

@ -188,8 +188,7 @@
their performance. their performance.
</p> </p>
</div> </div>
<ul class="nav nav-pills js_language_selector" t-if="request.multilang and <ul class="nav nav-pills js_language_selector" t-if="(len(website.language_ids) &gt; 1 or editable)">
(len(website.language_ids) &gt; 1 or editable)">
<li t-foreach="website.language_ids" t-as="lg"> <li t-foreach="website.language_ids" t-as="lg">
<a t-att-href="url_for('', lang=lg.code)" <a t-att-href="url_for('', lang=lg.code)"
t-att-data-default-lang="editable and 'true' if lg.code == website.default_lang_id.code else None"> t-att-data-default-lang="editable and 'true' if lg.code == website.default_lang_id.code else None">
@ -197,7 +196,7 @@
</a> </a>
</li> </li>
<li t-if="editable"> <li t-if="editable">
<t t-set="url_return" t-value="request.multilang and url_for(request.httprequest.path, '[lang]') or request.httprequest.path"/> <t t-set="url_return" t-value="url_for(request.httprequest.path, '[lang]')"/>
<a t-attf-href="/web#action=base.action_view_base_language_install&amp;website_id=#{website.id}&amp;url_return=#{url_return}"> <a t-attf-href="/web#action=base.action_view_base_language_install&amp;website_id=#{website.id}&amp;url_return=#{url_return}">
<i class="icon-plus-sign"/> <i class="icon-plus-sign"/>
Add a language... Add a language...