[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 !
This commit is contained in:
Jeremy Kersten 2015-03-26 10:24:56 +01:00
parent b10180887e
commit a696913364
4 changed files with 62 additions and 16 deletions

View File

@ -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/<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/<page: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);
</script>""" % (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]

View File

@ -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'):

View File

@ -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);

View File

@ -323,9 +323,14 @@
</div>
<ul class="list-inline js_language_selector mt16" t-if="(request.website_multilang and len(languages) &gt; 1) or editable">
<li t-foreach="languages" t-as="lg">
<a t-att-href="url_for(request.httprequest.path + '?' + keep_query(), lang=lg[0])"
t-att-data-default-lang="editable and 'true' if lg[0] == website.default_lang_code else None">
<t t-esc="lg[1].split('/').pop()"/></a>
<a
t-att-href="url_for(request.httprequest.path + '?' + keep_query(), lang=lg[0])"
t-att-data-default-lang="editable and 'true' if lg[0] == website.default_lang_code else None"
t-att-data-lang="lg[0]"
class="js_change_lang"
>
<t t-esc="lg[1].split('/').pop()"/>
</a>
</li>
<li groups="base.group_website_publisher">
<t t-set="url_return" t-value="url_for('', '[lang]') + '?' + keep_query()"/>