summaryrefslogtreecommitdiff
path: root/addons/website
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2018-06-28 19:01:52 +0200
committerChristophe Simonis <chs@odoo.com>2018-06-28 19:01:52 +0200
commitc8bb02141d2801c243ae65ba6800b07f090bb5c6 (patch)
tree2d099c5577df4783a6db3d853cf0141bf41c1aa9 /addons/website
parent4e2733e83a1f42773fb28f66431a0d6fb0393be9 (diff)
parent3cdcbce93cbde548d2d24426ee8de1d93f06706e (diff)
[MERGE] forward port branch saas-11.3 up to 3cdcbce93cb
Diffstat (limited to 'addons/website')
-rw-r--r--addons/website/models/ir_qweb.py100
-rw-r--r--addons/website/models/website.py22
-rw-r--r--addons/website/tests/__init__.py1
-rw-r--r--addons/website/tests/template_qweb_test.xml36
-rw-r--r--addons/website/tests/test_qweb.py65
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">
+&lt;!DOCTYPE html&gt;
+<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'))