summaryrefslogtreecommitdiff
path: root/addons/website/models/website.py
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2019-09-13 18:07:59 +0200
committerChristophe Simonis <chs@odoo.com>2019-09-13 18:07:59 +0200
commit6c948c36bbf72dd896e0bff03aa7fbddadcbb0cd (patch)
treef8620905c011735d8a3c340c46d1417a54a75972 /addons/website/models/website.py
parent48503fcf08eac6fed12e080d4f501f9bbc8d257d (diff)
parent197474e5b9ad43ab2d587a98136a1a809ce9a2e7 (diff)
[MERGE] forward port branch saas-12.5 up to 197474e5b9a
Diffstat (limited to 'addons/website/models/website.py')
-rw-r--r--addons/website/models/website.py117
1 files changed, 81 insertions, 36 deletions
diff --git a/addons/website/models/website.py b/addons/website/models/website.py
index 8ce3d46568a..0ddca6810e2 100644
--- a/addons/website/models/website.py
+++ b/addons/website/models/website.py
@@ -7,7 +7,9 @@ import logging
import hashlib
import re
+
from werkzeug import urls
+from werkzeug.datastructures import OrderedMultiDict
from werkzeug.exceptions import NotFound
from odoo import api, fields, models, tools
@@ -56,7 +58,6 @@ class Website(models.Model):
company_id = fields.Many2one('res.company', string="Company", default=lambda self: self.env.company, required=True)
language_ids = fields.Many2many('res.lang', 'website_lang_rel', 'website_id', 'lang_id', 'Languages', default=_active_languages)
default_lang_id = fields.Many2one('res.lang', string="Default Language", default=_default_language, required=True)
- default_lang_code = fields.Char("Default language code", related='default_lang_id.code', store=True, readonly=False)
auto_redirect_lang = fields.Boolean('Autoredirect Language', default=True, help="Should users be redirected to their browser's language")
def _default_social_facebook(self):
@@ -156,7 +157,8 @@ class Website(models.Model):
public_user_to_change_websites = self.env['website']
self._handle_favicon(values)
- self._get_languages.clear_cache(self)
+ self.clear_caches()
+
if 'company_id' in values and 'user_id' not in values:
public_user_to_change_websites = self.filtered(lambda w: w.sudo().user_id.company_id.id != values['company_id'])
if public_user_to_change_websites:
@@ -437,45 +439,42 @@ class Website(models.Model):
# Languages
# ----------------------------------------------------------
- def get_languages(self):
+ def _get_alternate_languages(self, canonical_params):
self.ensure_one()
- return self._get_languages()
- @tools.cache('self.id')
- def _get_languages(self):
- return [(lg.code, lg.name) for lg in self.language_ids]
+ if not self._is_canonical_url(canonical_params=canonical_params):
+ # no hreflang on non-canonical pages
+ return []
+
+ languages = self.language_ids
+ if len(languages) <= 1:
+ # no hreflang if no alternate language
+ return []
- def get_alternate_languages(self, req=None):
langs = []
- if req is None:
- req = request.httprequest
- default = self.get_current_website().default_lang_code
shorts = []
- def get_url_localized(router, lang):
- arguments = dict(request.endpoint_arguments)
- for key, val in list(arguments.items()):
- if isinstance(val, models.BaseModel):
- arguments[key] = val.with_context(lang=lang)
- return router.build(request.endpoint, arguments)
-
- router = request.httprequest.app.get_db_router(request.db).bind('')
- for code, dummy in self.get_languages():
- lg_path = ('/' + code) if code != default else ''
- lg_codes = code.split('_')
- shorts.append(lg_codes[0])
- uri = get_url_localized(router, code) if request.endpoint else request.httprequest.path
- if req.query_string:
- uri += u'?' + req.query_string.decode('utf-8')
- lang = {
+ for lg in languages:
+ lg_codes = lg.code.split('_')
+ short = lg_codes[0]
+ shorts.append(short)
+ langs.append({
'hreflang': ('-'.join(lg_codes)).lower(),
- 'short': lg_codes[0],
- 'href': req.url_root[0:-1] + lg_path + uri,
- }
- langs.append(lang)
+ 'short': short,
+ 'href': self._get_canonical_url_localized(lang=lg, canonical_params=canonical_params),
+ })
+
+ # if there is only one region for a language, use only the language code
for lang in langs:
if shorts.count(lang['short']) == 1:
lang['hreflang'] = lang['short']
+
+ # add the default
+ langs.append({
+ 'hreflang': 'x-default',
+ 'href': self._get_canonical_url_localized(lang=self.default_lang_id, canonical_params=canonical_params),
+ })
+
return langs
# ----------------------------------------------------------
@@ -741,9 +740,6 @@ class Website(models.Model):
domain_part, url = rule.build(value, append_unknown=False)
if not query_string or query_string.lower() in url.lower():
page = {'loc': url}
- for key, val in value.items():
- if key.startswith('__'):
- page[key[2:]] = val
if url in ('/sitemap.xml',):
continue
if url in url_set:
@@ -767,9 +763,9 @@ class Website(models.Model):
for page in pages:
record = {'loc': page['url'], 'id': page['id'], 'name': page['name']}
if page.view_id and page.view_id.priority != 16:
- record['__priority'] = min(round(page.view_id.priority / 32.0, 1), 1)
+ record['priority'] = min(round(page.view_id.priority / 32.0, 1), 1)
if page['write_date']:
- record['__lastmod'] = page['write_date'].date()
+ record['lastmod'] = page['write_date'].date()
yield record
def get_website_pages(self, domain=[], order='name', limit=None):
@@ -835,6 +831,55 @@ class Website(models.Model):
self.ensure_one()
return self._get_http_domain() or super(BaseModel, self).get_base_url()
+ def _get_canonical_url_localized(self, lang, canonical_params):
+ """Returns the canonical URL for the current request with translatable
+ elements appropriately translated in `lang`.
+
+ If `request.endpoint` is not true, returns the current `path` instead.
+
+ `url_quote_plus` is applied on the returned path.
+ """
+ self.ensure_one()
+ if request.endpoint:
+ router = request.httprequest.app.get_db_router(request.db).bind('')
+ arguments = dict(request.endpoint_arguments)
+ for key, val in list(arguments.items()):
+ if isinstance(val, models.BaseModel):
+ if val.env.context.get('lang') != lang.url_code:
+ arguments[key] = val.with_context(lang=lang.url_code)
+ path = router.build(request.endpoint, arguments)
+ else:
+ # The build method returns a quoted URL so convert in this case for consistency.
+ path = urls.url_quote_plus(request.httprequest.path, safe='/')
+ lang_path = ('/' + lang.url_code) if lang != self.default_lang_id else ''
+ canonical_query_string = '?%s' % urls.url_encode(canonical_params) if canonical_params else ''
+ return self.get_base_url() + lang_path + path + canonical_query_string
+
+ def _get_canonical_url(self, canonical_params):
+ """Returns the canonical URL for the current request."""
+ self.ensure_one()
+ return self._get_canonical_url_localized(lang=request.lang, canonical_params=canonical_params)
+
+ def _is_canonical_url(self, canonical_params):
+ """Returns whether the current request URL is canonical."""
+ self.ensure_one()
+ # Compare OrderedMultiDict because the order is important, there must be
+ # only one canonical and not params permutations.
+ params = request.httprequest.args
+ canonical_params = canonical_params or OrderedMultiDict()
+ if params != canonical_params:
+ return False
+ # Compare URL at the first rerouting iteration (if available) because
+ # it's the one with the language in the path.
+ # It is important to also test the domain of the current URL.
+ current_url = request.httprequest.url_root[:-1] + (hasattr(request, 'rerouting') and request.rerouting[0] or request.httprequest.path)
+ canonical_url = self._get_canonical_url_localized(lang=request.lang, canonical_params=None)
+ # A request path with quotable characters (such as ",") is never
+ # canonical because request.httprequest.base_url is always unquoted,
+ # and canonical url is always quoted, so it is never possible to tell
+ # if the current URL is indeed canonical or not.
+ return current_url == canonical_url
+
class BaseModel(models.AbstractModel):
_inherit = 'base'