diff options
| author | Christophe Simonis <chs@odoo.com> | 2018-06-28 19:01:52 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2018-06-28 19:01:52 +0200 |
| commit | c8bb02141d2801c243ae65ba6800b07f090bb5c6 (patch) | |
| tree | 2d099c5577df4783a6db3d853cf0141bf41c1aa9 /addons/website | |
| parent | 4e2733e83a1f42773fb28f66431a0d6fb0393be9 (diff) | |
| parent | 3cdcbce93cbde548d2d24426ee8de1d93f06706e (diff) | |
[MERGE] forward port branch saas-11.3 up to 3cdcbce93cb
Diffstat (limited to 'addons/website')
| -rw-r--r-- | addons/website/models/ir_qweb.py | 100 | ||||
| -rw-r--r-- | addons/website/models/website.py | 22 | ||||
| -rw-r--r-- | addons/website/tests/__init__.py | 1 | ||||
| -rw-r--r-- | addons/website/tests/template_qweb_test.xml | 36 | ||||
| -rw-r--r-- | addons/website/tests/test_qweb.py | 65 |
5 files changed, 144 insertions, 80 deletions
diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py index 943a603d211..0f73bc469f5 100644 --- a/addons/website/models/ir_qweb.py +++ b/addons/website/models/ir_qweb.py @@ -1,93 +1,51 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. - -import ast +import re +from collections import OrderedDict from odoo import models from odoo.http import request +re_background_image = re.compile(r"(background-image\s*:\s*url\(\s*['\"]?\s*)([^)'\"]+)") + + class QWeb(models.AbstractModel): """ QWeb object for rendering stuff in the website context """ _inherit = 'ir.qweb' URL_ATTRS = { - 'form': 'action', - 'a': 'href', + 'form': 'action', + 'a': 'href', + 'link': 'href', + 'script': 'src', + 'img': 'src', } - CDN_TRIGGERS = { - 'link': 'href', - 'script': 'src', - 'img': 'src', - } - - def _get_asset(self, xmlid, options, css=True, js=True, debug=False, async=False, values=None): - website = getattr(request, 'website', None) if request else None - if website and website.cdn_activated: - values = dict(values or {}, url_for=website.get_cdn_url) - return super(QWeb, self)._get_asset(xmlid, options, css, js, debug, async, values) - - def _website_build_attribute(self, tagName, name, value, options, values): - """ Compute the value of an attribute while rendering the template. """ - if name == self.URL_ATTRS.get(tagName) and values.get('url_for'): - return values.get('url_for')(value or '') - elif request and getattr(request, 'website', None) and request.website.cdn_activated and (name == self.URL_ATTRS.get(tagName) or name == self.CDN_TRIGGERS.get(tagName)): - return request.website.get_cdn_url(value or '') - return value - - def _wrap_build_attributes(self, el, items, options): - """ Map items corresponding to URL and CDN attributes to an ast expression. """ - if options.get('rendering_bundle'): - return items - - url_att = self.URL_ATTRS.get(el.tag) - cdn_att = self.CDN_TRIGGERS.get(el.tag) + def _post_processing_att(self, tagName, atts, options): + if atts.get('data-no-post-process'): + return atts - def process(item): - if isinstance(item, tuple) and (item[0] in (url_att, cdn_att)): - return (item[0], ast.Call( - func=ast.Attribute( - value=ast.Name(id='self', ctx=ast.Load()), - attr='_website_build_attribute', - ctx=ast.Load() - ), - args=[ - ast.Str(el.tag), - ast.Str(item[0]), - item[1], - ast.Name(id='options', ctx=ast.Load()), - ast.Name(id='values', ctx=ast.Load()), - ], keywords=[], - starargs=None, kwargs=None - )) - else: - return item + atts = super(QWeb, self)._post_processing_att(tagName, atts, options) - return [process(it) for it in items] + if options.get('inherit_branding') or options.get('rendering_bundle') or \ + options.get('edit_translations') or options.get('debug') or (request and request.debug): + return atts - def _compile_static_attributes(self, el, options): - items = super(QWeb, self)._compile_static_attributes(el, options) - return self._wrap_build_attributes(el, items, options) + website = request and getattr(request, 'website', None) + if not website and options.get('website_id'): + website = self.env['website'].browse(options['website_id']) - def _compile_dynamic_attributes(self, el, options): - items = super(QWeb, self)._compile_dynamic_attributes(el, options) - return self._wrap_build_attributes(el, items, options) + if not website or not website.cdn_activated: + return atts - # method called by computing code + name = self.URL_ATTRS.get(tagName) + if name and name in atts: + atts = OrderedDict(atts) + atts[name] = website.get_cdn_url(atts[name]) + if isinstance(atts.get('style'), str) and 'background-image' in atts['style']: + atts = OrderedDict(atts) + atts['style'] = re_background_image.sub(lambda m: '%s%s' % (m.group(1), website.get_cdn_url(m.group(2))), atts['style']) - def _get_dynamic_att(self, tagName, atts, options, values): - atts = super(QWeb, self)._get_dynamic_att(tagName, atts, options, values) - if options.get('rendering_bundle'): - return atts - for name, value in atts.items(): - atts[name] = self._website_build_attribute(tagName, name, value, options, values) return atts - - def _is_static_node(self, el): - url_att = self.URL_ATTRS.get(el.tag) - cdn_att = self.CDN_TRIGGERS.get(el.tag) - return super(QWeb, self)._is_static_node(el) and \ - (not url_att or not el.get(url_att)) and \ - (not cdn_att or not el.get(cdn_att)) diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 106aee8b208..40c1ce0ae6e 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -85,7 +85,11 @@ class Website(models.Model): @api.multi def write(self, values): self._get_languages.clear_cache(self) - return super(Website, self).write(values) + result = super(Website, self).write(values) + if 'cdn_activated' in values or 'cdn_url' in values or 'cdn_filters' in values: + # invalidate the caches from static node at compile time + self.env['ir.qweb'].clear_caches() + return result #---------------------------------------------------------- # Page Management @@ -563,15 +567,15 @@ class Website(models.Model): size = '' if size is None else '/%s' % size return '/web/image/%s/%s/%s%s?unique=%s' % (record._name, record.id, field, size, sha) - @api.model def get_cdn_url(self, uri): - # Currently only usable in a website_enable request context - if request and request.website and not request.debug and request.website.user_id.id == request.uid: - cdn_url = request.website.cdn_url - cdn_filters = (request.website.cdn_filters or '').splitlines() - for flt in cdn_filters: - if flt and re.match(flt, uri): - return urls.url_join(cdn_url, uri) + self.ensure_one() + if not uri: + return '' + cdn_url = self.cdn_url + cdn_filters = (self.cdn_filters or '').splitlines() + for flt in cdn_filters: + if flt and re.match(flt, uri): + return urls.url_join(cdn_url, uri) return uri @api.model diff --git a/addons/website/tests/__init__.py b/addons/website/tests/__init__.py index 121eff4ff44..a4415acbb7d 100644 --- a/addons/website/tests/__init__.py +++ b/addons/website/tests/__init__.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import test_converter +from . import test_qweb from . import test_crawl from . import test_ui from . import test_views diff --git a/addons/website/tests/template_qweb_test.xml b/addons/website/tests/template_qweb_test.xml new file mode 100644 index 00000000000..423c8d3212e --- /dev/null +++ b/addons/website/tests/template_qweb_test.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <template id="website.test_bundle"> + <script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script> + <script type="text/javascript" src="http://test.external.link/javascript1.js"></script> + + <link rel="stylesheet" type="text/css" href="/web/static/lib/jquery.ui/jquery-ui.css"/> + <link rel="stylesheet" href="http://test.external.link/style1.css"/> + + <script type="text/javascript" src="/web/static/src/js/boot.js"></script> + <script type="text/javascript" src="http://test.external.link/javascript2.js"></script> + + <link rel="stylesheet" href="http://test.external.link/style2.css"/> + </template> + + <template id="website.test_template" name="test template 2"> +<!DOCTYPE html> +<html> + <head> + <t t-call-assets="website.test_bundle" t-js="False"/> + <meta/> + <t t-call-assets="website.test_bundle" t-css="False"/> + </head> + <body> + <img src="http://test.external.link/img.png"/> + <img src="/website/static/img.png"/> + <a href="http://test.external.link/link">x</a> + <a href="/web/content/local_link">x</a> + <span t-attf-style="background-image: url('/web/image/2')" t-att-empty="False">xxx</span> + <div widget="html" t-field="user.signature"/> + <div widget="image" t-field="user.image" t-options="{'widget': 'image'}"/> + </body> +</html> + </template> + +</odoo>
\ No newline at end of file diff --git a/addons/website/tests/test_qweb.py b/addons/website/tests/test_qweb.py new file mode 100644 index 00000000000..e73083a66b2 --- /dev/null +++ b/addons/website/tests/test_qweb.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import re + +from odoo import tools +from odoo.modules.module import get_module_resource +from odoo.tests.common import TransactionCase + + +class TestQweb(TransactionCase): + def _load(self, module, *args): + tools.convert_file(self.cr, 'website', + get_module_resource(module, *args), + {}, 'init', False, 'test', self.registry._assertion_report) + + def test_qweb_cdn(self): + self._load('website', 'tests', 'template_qweb_test.xml') + + website = self.env['website'].browse(1) + website.write({ + "cdn_activated": True, + "cdn_url": "http://test.cdn" + }) + + demo = self.env['res.users'].search([('login', '=', 'demo')])[0] + demo.write({"signature": '''<span class="toto"> + span<span class="fa"></span><img src="/web/image/1"/> + </span>'''}) + + demo_env = self.env(user=demo) + + html = demo_env['ir.qweb'].render('website.test_template', {"user": demo}, website_id= website.id) + html = html.strip().decode('utf8') + html = re.sub(r'\?unique=[^"]+', '', html).encode('utf8') + + attachments = demo_env['ir.attachment'].search([('url', '=like', '/web/content/%-%/website.test_bundle.%')]) + self.assertEqual(len(attachments), 2) + self.assertEqual(html, ("""<!DOCTYPE html> +<html> + <head> + <link rel="stylesheet" href="http://test.external.link/style1.css"/> + <link rel="stylesheet" href="http://test.external.link/style2.css"/> + <link type="text/css" rel="stylesheet" href="http://test.cdn%(css)s"/> + <meta/> + <script type="text/javascript" src="http://test.external.link/javascript1.js"></script> + <script type="text/javascript" src="http://test.external.link/javascript2.js"></script> + <script type="text/javascript" src="http://test.cdn%(js)s"></script> + </head> + <body> + <img src="http://test.external.link/img.png"/> + <img src="http://test.cdn/website/static/img.png"/> + <a href="http://test.external.link/link">x</a> + <a href="http://test.cdn/web/content/local_link">x</a> + <span style="background-image: url('http://test.cdn/web/image/2')">xxx</span> + <div widget="html"><span class="toto"> + span<span class="fa"></span><img src="http://test.cdn/web/image/1"> + </span></div> + <div widget="image"><img src="http://test.cdn/web/image/res.users/%(user_id)s/image" class="img img-responsive"/></div> + </body> +</html>""" % { + "js": attachments[0].url, + "css": attachments[1].url, + "user_id": demo.id, + }).encode('utf8')) |
