From a696913364ffc4d5f1ce0675bc9be82f84f3ff93 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Thu, 26 Mar 2015 10:24:56 +0100 Subject: [PATCH] [FIX] website: ir_http - no redirect for bot and save lang in cookie Detect most of bots/crawlers to avoid auto redirect. Most bots fetch with lang en_US, so even if default website lang was not in en_US, googlebot was redirected to en_US page. Now we keep also the language selected by user into a cookie. If cookie exists but lang not in url, we redirect the user into his preferred language. Manage special case to allow to change the lang in url to set the default lang at fly in url and set the cookie... Many routes are not specified as multilang=False but should be. With the auto redirection, we need to update these routes to avoid useless redirects ! --- addons/website/controllers/main.py | 16 ++++++--- addons/website/models/ir_http.py | 38 +++++++++++++++++----- addons/website/static/src/js/website.js | 13 ++++++++ addons/website/views/website_templates.xml | 11 +++++-- 4 files changed, 62 insertions(+), 16 deletions(-) diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index 0fd0bd30ebf..f1f4f6d9992 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -51,6 +51,12 @@ class Website(openerp.addons.web.controllers.main.Home): # TODO: can't we just put auth=public, ... in web client ? return super(Website, self).web_login(*args, **kw) + @http.route('/website/lang/', type='http', auth="public", website=True, multilang=False) + def change_lang(self, lang, r, **kwargs): + redirect = werkzeug.utils.redirect(r or '/%s' % lang, 303) + redirect.set_cookie('website_lang', lang) + return redirect + @http.route('/page/', type='http', auth="public", website=True) def page(self, page, **opt): values = { @@ -232,14 +238,14 @@ class Website(openerp.addons.web.controllers.main.Home): request.cr, request.uid, xml_id, full=full, context=request.context) - @http.route('/website/get_view_translations', type='json', auth='public', website=True) + @http.route('/website/get_view_translations', type='json', auth='public', website=True, multilang=False) def get_view_translations(self, xml_id, lang=None): lang = lang or request.context.get('lang') return request.registry["ir.ui.view"].get_view_translations( request.cr, request.uid, xml_id, lang=lang, context=request.context) - @http.route('/website/set_translations', type='json', auth='public', website=True) + @http.route('/website/set_translations', type='json', auth='public', website=True, multilang=False) def set_translations(self, data, lang): irt = request.registry.get('ir.translation') for view_id, trans in data.items(): @@ -277,7 +283,7 @@ class Website(openerp.addons.web.controllers.main.Home): irt.create(request.cr, request.uid, new_trans) return True - @http.route('/website/translations', type='json', auth="public", website=True) + @http.route('/website/translations', type='json', auth="public", website=True, multilang=False) def get_website_translations(self, lang): module_obj = request.registry['ir.module.module'] module_ids = module_obj.search(request.cr, request.uid, [('name', 'ilike', 'website'), ('state', '=', 'installed')], context=request.context) @@ -293,7 +299,7 @@ class Website(openerp.addons.web.controllers.main.Home): website_url = url name = url.split("/").pop() attachment_id = Attachments.create(request.cr, request.uid, { - 'name':name, + 'name': name, 'type': 'url', 'url': url, 'res_model': 'ir.ui.view', @@ -330,7 +336,7 @@ class Website(openerp.addons.web.controllers.main.Home): window.parent['%s'](%s, %s); """ % (func, json.dumps(website_url), json.dumps(message)) - @http.route(['/website/publish'], type='json', auth="public", website=True) + @http.route(['/website/publish'], type='json', auth="public", website=True, multilang=False) def publish(self, id, object): _id = int(id) _object = request.registry[object] diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py index c45a203f4da..3f1ec566a2f 100644 --- a/addons/website/models/ir_http.py +++ b/addons/website/models/ir_http.py @@ -42,6 +42,16 @@ class ir_http(orm.AbstractModel): else: request.uid = request.session.uid + bots = "bot|crawl|slurp|spider|curl|wget".split("|") + def is_a_bot(self): + # We don't use regexp and ustr voluntarily + # timeit has been done to check the optimum method + ua = request.httprequest.environ.get('HTTP_USER_AGENT', '').lower() + try: + return any(bot in ua for bot in self.bots) + except UnicodeDecodeError: + return any(bot in ua.encode('ascii', 'ignore') for bot in self.bots) + def _dispatch(self): first_pass = not hasattr(request, 'website') request.website = None @@ -75,6 +85,7 @@ class ir_http(orm.AbstractModel): record = self.geo_ip_resolver.record_by_addr(request.httprequest.remote_addr) or {} request.session['geoip'] = record + cook_lang = request.httprequest.cookies.get('website_lang') if request.website_enabled: try: if func: @@ -88,10 +99,13 @@ class ir_http(orm.AbstractModel): request.website = request.registry['website'].get_current_website(request.cr, request.uid, context=request.context) langs = [lg[0] for lg in request.website.get_languages()] path = request.httprequest.path.split('/') + if first_pass: if request.website_multilang: + is_a_bot = self.is_a_bot() # If the url doesn't contains the lang and that it's the first connection, we to retreive the user preference if it exists. - if not path[1] in langs and not request.httprequest.cookies.get('session_id'): + if not path[1] in langs and not is_a_bot: + request.lang = cook_lang or request.lang if request.lang not in langs: # Try to find a similar lang. Eg: fr_BE and fr_FR short = request.lang.split('_')[0] @@ -100,14 +114,16 @@ class ir_http(orm.AbstractModel): request.lang = langs_withshort[0] else: request.lang = request.website.default_lang_code - # We redirect with the right language in url - if request.lang != request.website.default_lang_code: - path.insert(1, request.lang) - path = '/'.join(path) or '/' - return request.redirect(path + '?' + request.httprequest.query_string) else: request.lang = request.website.default_lang_code + if request.lang != request.website.default_lang_code: + path.insert(1, request.lang) + path = '/'.join(path) or '/' + redirect = request.redirect(path + '?' + request.httprequest.query_string) + redirect.set_cookie('website_lang', request.lang) + return redirect + request.context['lang'] = request.lang if not func: if path[1] in langs: @@ -116,11 +132,17 @@ class ir_http(orm.AbstractModel): if request.lang == request.website.default_lang_code: # If language is in the url and it is the default language, redirect # to url without language so google doesn't see duplicate content - return request.redirect(path + '?' + request.httprequest.query_string, code=301) + resp = request.redirect(path + '?' + request.httprequest.query_string, code=301) + if cook_lang != request.lang: # If default lang setted in url directly + resp.set_cookie('website_lang', request.lang) + return resp return self.reroute(path) # bind modified context request.website = request.website.with_context(request.context) - return super(ir_http, self)._dispatch() + resp = super(ir_http, self)._dispatch() + if not cook_lang: + resp.set_cookie('website_lang', request.lang) + return resp def reroute(self, path): if not hasattr(request, 'rerouting'): diff --git a/addons/website/static/src/js/website.js b/addons/website/static/src/js/website.js index e464a4e7697..6364550778a 100644 --- a/addons/website/static/src/js/website.js +++ b/addons/website/static/src/js/website.js @@ -350,6 +350,19 @@ }); }); + $(document).on('click', '.js_change_lang', function(e) { + e.preventDefault(); + + var self = $(this); + // retrieve the hash before the redirect + var redirect = { + lang: self.data('lang'), + url: self.attr('href'), + hash: location.hash + }; + location.href = _.str.sprintf("/website/lang/%(lang)s?r=%(url)s%(hash)s", redirect); + }) + /* ----- KANBAN WEBSITE ---- */ $('.js_kanban').each(function () { website.init_kanban(this); diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 3beda6a4d77..450687511f5 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -323,9 +323,14 @@