[ADD] CDN support for website
authorFabien Meghazi <fme@openerp.com>
Tue, 21 Oct 2014 13:37:40 +0000 (15:37 +0200)
committerFabien Meghazi <fme@openerp.com>
Wed, 22 Oct 2014 08:00:36 +0000 (10:00 +0200)
addons/website/models/ir_qweb.py
addons/website/models/res_config.py
addons/website/models/website.py
addons/website/views/res_config.xml

index 019eedd..14b3571 100644 (file)
@@ -53,24 +53,28 @@ class QWeb(orm.AbstractModel):
         'style',
     ]
 
-    def add_template(self, qcontext, name, node):
-        # preprocessing for multilang static urls
-        if request.website:
-            for tag, attr in self.URL_ATTRS.iteritems():
-                for e in node.iterdescendants(tag=tag):
-                    url = e.get(attr)
-                    if url:
-                        e.set(attr, qcontext.get('url_for')(url))
-        super(QWeb, self).add_template(qcontext, name, node)
-
-    def render_att_att(self, element, attribute_name, attribute_value, qwebcontext):
-        URL_ATTRS = self.URL_ATTRS.get(element.tag)
-        is_website = request.website
-        for att, val in super(QWeb, self).render_att_att(element, attribute_name, attribute_value, qwebcontext):
-            if is_website and att == URL_ATTRS and isinstance(val, basestring):
-                val = qwebcontext.get('url_for')(val)
-            yield (att, val)
+    CDN_TRIGGERS = {
+        'link':    'href',
+        'script':  'src',
+        'img':     'src',
+    }
 
+    def render_attribute(self, element, name, value, qwebcontext):
+        context = qwebcontext.context or {}
+        if not context.get('rendering_bundle'):
+            if name == self.URL_ATTRS.get(element.tag):
+                value = qwebcontext.get('url_for')(value)
+            if request and request.website and request.website.cdn_activated and name == self.CDN_TRIGGERS.get(element.tag):
+                value = request.website.get_cdn_url(value)
+
+        return super(QWeb, self).render_attribute(element, name, value, qwebcontext)
+
+    def render_tag_call_assets(self, element, template_attributes, generated_attributes, qwebcontext):
+        if request and request.website and request.website.cdn_activated:
+            if qwebcontext.context is None:
+                qwebcontext.context = {}
+            qwebcontext.context['url_for'] = request.website.get_cdn_url
+        return super(QWeb, self).render_tag_call_assets(element, template_attributes, generated_attributes, qwebcontext)
 
     def get_converter_for(self, field_type):
         return self.pool.get(
index 4b757eb..bec1a44 100644 (file)
@@ -20,6 +20,9 @@ class website_config_settings(osv.osv_memory):
         'social_youtube': fields.related('website_id', 'social_youtube', type="char", string='Youtube Account'),
         'social_googleplus': fields.related('website_id', 'social_googleplus', type="char", string='Google+ Account'),
         'compress_html': fields.related('website_id', 'compress_html', type="boolean", string='Compress HTML'),
+        'cdn_activated': fields.related('website_id', 'cdn_activated', type="boolean", string='Use CDN'),
+        'cdn_url': fields.related('website_id', 'cdn_url', type="char", string='CDN Base URL'),
+        'cdn_filters': fields.related('website_id', 'cdn_filters', type="text", string='CDN Filters'),
     }
 
     def on_change_website_id(self, cr, uid, ids, website_id, context=None):
index dab91bd..bfc8e8a 100644 (file)
@@ -124,6 +124,12 @@ def slug(value):
 # NOTE: as the pattern is used as it for the ModelConverter (ir_http.py), do not use any flags
 _UNSLUG_RE = re.compile(r'(?:(\w{1,2}|\w[A-Za-z0-9-_]+?\w)-)?(-?\d+)(?=$|/)')
 
+DEFAULT_CDN_FILTERS = [
+    "^/[^/]+/static/",
+    "^/web/(css|js)/",
+    "^/website/image/",
+]
+
 def unslug(s):
     """Extract slug and id from a string.
         Always return un 2-tuple (str|None, int|None)
@@ -163,6 +169,9 @@ class website(osv.osv):
         'google_analytics_key': fields.char('Google Analytics Key'),
         'user_id': fields.many2one('res.users', string='Public User'),
         'compress_html': fields.boolean('Compress HTML'),
+        'cdn_activated': fields.boolean('Activate CDN for assets'),
+        'cdn_url': fields.char('CDN Base URL'),
+        'cdn_filters': fields.text('CDN Filters', help="URL matching those filters will be rewritten using the CDN Base URL"),
         'partner_id': fields.related('user_id','partner_id', type='many2one', relation='res.partner', string='Public Partner'),
         'menu_id': fields.function(_get_menu, relation='website.menu', type='many2one', string='Main Menu')
     }
@@ -170,6 +179,9 @@ class website(osv.osv):
         'user_id': lambda self,cr,uid,c: self.pool['ir.model.data'].xmlid_to_res_id(cr, openerp.SUPERUSER_ID, 'base.public_user'),
         'company_id': lambda self,cr,uid,c: self.pool['ir.model.data'].xmlid_to_res_id(cr, openerp.SUPERUSER_ID,'base.main_company'),
         'compress_html': False,
+        'cdn_activated': False,
+        'cdn_url': '//localhost:8069/',
+        'cdn_filters': '\n'.join(DEFAULT_CDN_FILTERS),
     }
 
     # cf. Wizard hack in website_views.xml
@@ -225,6 +237,16 @@ class website(osv.osv):
         website = self.browse(cr, uid, id)
         return [(lg.code, lg.name) for lg in website.language_ids]
 
+    def get_cdn_url(self, cr, uid, uri, context=None):
+        # Currently only usable in a website_enable request context
+        if request and request.website and not request.debug:
+            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 urlparse.urljoin(cdn_url, uri)
+        return uri
+
     def get_languages(self, cr, uid, ids, context=None):
         return self._get_languages(cr, uid, ids[0], context=context)
 
index 452dda3..8762ec7 100644 (file)
@@ -92,6 +92,9 @@
                                     <field name="compress_html"/> Compress rendered html for a better google pagespeed result
                                 </div>
                             </div>
+                            <field name="cdn_activated"/>
+                            <field name="cdn_url" attrs="{'invisible': [('cdn_activated', '=', False)] }"/>
+                            <field name="cdn_filters" attrs="{'invisible': [('cdn_activated', '=', False)] }"/>
                         </group>
                     </div>
                 </form>