summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Lempereur <nle@odoo.com>2019-03-25 12:05:24 +0000
committerNicolas Lempereur <nle@odoo.com>2019-04-02 14:45:24 +0000
commit8557bcfa302de7aca4b5dadb35841cd52f9ebfff (patch)
treef05d8ab6540354fa8bc6b054c77b6aca81b62205
parentf1744a31a1fa3494345f9502bfd6cb5b8aa95ffa (diff)
[FIX] website: language prefixed URL on website bis
Reintroduces 5324284f5 that was reverted because in the case of an URL like /payment_stripe/static/src/js/stripe.js directly inside a QWeb template, we would possibly get: /fr_FR/payment_stripe/static/src/js/stripe.js Which is not accepted by werkzeug SharedDataMiddleware we use to serve static data in /{module_name}/static/ directories. Added test without changeset failed with: - test_process_att_no_request_lang: <AssertionError> line: self._test_att('/en_US/', {'href': '/'}) wrong result: {'href': '/en_US/'} - test_process_att_with_request_lang: <AssertionError> line: self._test_att('/', {'href': '/fr_FR/'}) wrong result: {'href': '/'} - test_process_att_matching_cdn_and_lang: <AssertionError> line: self._test_att('/en_US/a', {'href': 'http://test.cdn/a'}) wrong result: {'href': 'http://test.cdn/en_US/a'} - test_process_att_no_route: <AssertionError> line: self._test_att('/my-page', {'href': '/fr_FR/my-page'}) wrong result: {'href': '/my-page'} - test_process_att_url_crap: <IndexError: list index out of range> line: request.httprequest.app._log_call[-1], opw-1922051 closes #32095 Signed-off-by: Nicolas Lempereur (nle) <nle@odoo.com>
-rw-r--r--addons/http_routing/models/ir_http.py6
-rw-r--r--addons/website/models/ir_qweb.py9
-rw-r--r--addons/website/tests/test_qweb.py122
3 files changed, 135 insertions, 2 deletions
diff --git a/addons/http_routing/models/ir_http.py b/addons/http_routing/models/ir_http.py
index 0129bc90e6d..3388aabeb80 100644
--- a/addons/http_routing/models/ir_http.py
+++ b/addons/http_routing/models/ir_http.py
@@ -164,7 +164,7 @@ def is_multilang_url(local_url, langs=None):
local_url = '/'.join(spath)
try:
# Try to match an endpoint in werkzeug's routing table
- url = local_url.split('?')
+ url = local_url.partition('#')[0].split('?')
path = url[0]
query_string = url[1] if len(url) > 1 else None
router = request.httprequest.app.get_db_router(request.db).bind('')
@@ -173,6 +173,10 @@ def is_multilang_url(local_url, langs=None):
return (func.routing.get('website', False) and
func.routing.get('multilang', func.routing['type'] == 'http'))
except werkzeug.exceptions.NotFound:
+ # Consider /static/ files as non-multilang
+ static_index = path.find('/static/', 1)
+ if static_index != -1 and static_index == path.find('/', 1):
+ return False
return True
except Exception as e:
return False
diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py
index 0f73bc469f5..0c7900e9a47 100644
--- a/addons/website/models/ir_qweb.py
+++ b/addons/website/models/ir_qweb.py
@@ -4,6 +4,7 @@ import re
from collections import OrderedDict
from odoo import models
+from odoo.addons.http_routing.models.ir_http import url_for
from odoo.http import request
@@ -37,10 +38,16 @@ class QWeb(models.AbstractModel):
if not website and options.get('website_id'):
website = self.env['website'].browse(options['website_id'])
- if not website or not website.cdn_activated:
+ if not website:
return atts
name = self.URL_ATTRS.get(tagName)
+ if request and name and name in atts:
+ atts[name] = url_for(atts[name])
+
+ if not website.cdn_activated:
+ return atts
+
if name and name in atts:
atts = OrderedDict(atts)
atts[name] = website.get_cdn_url(atts[name])
diff --git a/addons/website/tests/test_qweb.py b/addons/website/tests/test_qweb.py
index e73083a66b2..5df4898487e 100644
--- a/addons/website/tests/test_qweb.py
+++ b/addons/website/tests/test_qweb.py
@@ -2,7 +2,9 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
+import werkzeug
+import odoo
from odoo import tools
from odoo.modules.module import get_module_resource
from odoo.tests.common import TransactionCase
@@ -63,3 +65,123 @@ class TestQweb(TransactionCase):
"css": attachments[1].url,
"user_id": demo.id,
}).encode('utf8'))
+
+class MockObject(object):
+ _log_call = []
+ def __init__(self, *args, **kwargs):
+ self.__dict__ = kwargs
+ def __call__(self, *args, **kwargs):
+ self._log_call.append((args, kwargs))
+ return self
+ def __getitem__(self, index):
+ return self
+
+def werkzeugRaiseNotFound(*args, **kwargs):
+ raise werkzeug.exceptions.NotFound()
+
+class MockRequest(object):
+ """ Class with context manager mocking odoo.http.request for tests """
+ def __init__(self, env, website=None, context=None, multilang=True, routing=True):
+ app = MockObject(routing={
+ 'type': 'http',
+ 'website': True,
+ 'multilang': multilang,
+ })
+ app.get_db_router = app.bind = app.match = app
+ if not routing:
+ app.match = werkzeugRaiseNotFound
+ self.request = MockObject(
+ env=env, context=context or {}, db=None, debug=False,
+ website=website, httprequest=MockObject(
+ path='/hello/',
+ app=app
+ )
+ )
+ odoo.http._request_stack.push(self.request)
+ def __enter__(self):
+ return self.request
+ def __exit__(self, exc_type, exc_value, traceback):
+ odoo.http._request_stack.pop()
+
+class TestQwebProcessAtt(TransactionCase):
+ def setUp(self):
+ super(TestQwebProcessAtt, self).setUp()
+ self.website = self.env['website'].browse(1)
+ self.website.language_ids = self.env.ref('base.lang_en') + self.env.ref('base.lang_fr')
+ self.website.default_lang_id = self.env.ref('base.lang_en')
+ self.website.cdn_activated = True
+ self.website.cdn_url = "http://test.cdn"
+ self.website.cdn_filters = "\n".join(["^(/[a-z]{2}_[A-Z]{2})?/a$", "^/b$"])
+
+ def _test_att(self, url, expect, tag='a', attribute='href'):
+ self.assertEqual(
+ self.env['ir.qweb']._post_processing_att(tag, {attribute: url}, {}),
+ expect
+ )
+
+ def test_process_att_no_request(self):
+ # no request so no URL rewriting
+ self._test_att('/', {'href': '/'})
+ self._test_att('/en_US/', {'href': '/en_US/'})
+ self._test_att('/fr_FR/', {'href': '/fr_FR/'})
+ # no URL rewritting for CDN
+ self._test_att('/a', {'href': '/a'})
+
+ def test_process_att_no_website(self):
+ with MockRequest(self.env) as request:
+ # no website so URL rewriting
+ self._test_att('/', {'href': '/'})
+ self._test_att('/en_US/', {'href': '/en_US/'})
+ self._test_att('/fr_FR/', {'href': '/fr_FR/'})
+ # no URL rewritting for CDN
+ self._test_att('/a', {'href': '/a'})
+
+ def test_process_att_monolang_route(self):
+ with MockRequest(self.env, website=self.website, multilang=False) as request:
+ # lang not changed in URL but CDN enabled
+ self._test_att('/a', {'href': 'http://test.cdn/a'})
+ self._test_att('/en_US/a', {'href': 'http://test.cdn/en_US/a'})
+ self._test_att('/b', {'href': 'http://test.cdn/b'})
+ self._test_att('/en_US/b', {'href': '/en_US/b'})
+
+ def test_process_att_no_request_lang(self):
+ with MockRequest(self.env, self.website) as request:
+ self._test_att('/', {'href': '/'})
+ self._test_att('/en_US/', {'href': '/'})
+ self._test_att('/fr_FR/', {'href': '/fr_FR/'})
+
+ def test_process_att_with_request_lang(self):
+ with MockRequest(self.env, self.website, context={'lang': 'fr_FR'}) as request:
+ self._test_att('/', {'href': '/fr_FR/'})
+ self._test_att('/en_US/', {'href': '/'})
+ self._test_att('/fr_FR/', {'href': '/fr_FR/'})
+
+ def test_process_att_matching_cdn_and_lang(self):
+ with MockRequest(self.env, self.website) as request:
+ # lang prefix is added before CDN
+ self._test_att('/a', {'href': 'http://test.cdn/a'})
+ self._test_att('/en_US/a', {'href': 'http://test.cdn/a'})
+ self._test_att('/fr_FR/a', {'href': 'http://test.cdn/fr_FR/a'})
+ self._test_att('/b', {'href': 'http://test.cdn/b'})
+ self._test_att('/en_US/b', {'href': 'http://test.cdn/b'})
+ self._test_att('/fr_FR/b', {'href': '/fr_FR/b'})
+
+ def test_process_att_no_route(self):
+ with MockRequest(self.env, self.website, context={'lang': 'fr_FR'}, routing=False) as request:
+ # default on multilang=True if route is not /{module}/static/
+ self._test_att('/web/static/hi', {'href': '/web/static/hi'})
+ self._test_att('/my-page', {'href': '/fr_FR/my-page'})
+
+ def test_process_att_url_crap(self):
+ with MockRequest(self.env, self.website) as request:
+ # #{fragment} is stripped from URL when testing route
+ self._test_att('/x#y?z', {'href': '/x#y?z'})
+ self.assertEqual(
+ request.httprequest.app._log_call[-1],
+ (('/x',), {'method': 'POST', 'query_args': None})
+ )
+ self._test_att('/x?y#z', {'href': '/x?y#z'})
+ self.assertEqual(
+ request.httprequest.app._log_call[-1],
+ (('/x',), {'method': 'POST', 'query_args': 'y'})
+ )