diff --git a/openerp/addons/base/ir/ir_qweb.py b/openerp/addons/base/ir/ir_qweb.py index 12c5dff139c..6194c6fcb48 100644 --- a/openerp/addons/base/ir/ir_qweb.py +++ b/openerp/addons/base/ir/ir_qweb.py @@ -1073,7 +1073,8 @@ class AssetsBundle(object): response = [] if debug: if css and self.stylesheets: - self.preprocess_css() + compiled = self.preprocess_css() + self.recompose_css(compiled) for style in self.stylesheets: response.append(style.to_html()) if js: @@ -1119,13 +1120,13 @@ class AssetsBundle(object): return self.cache[key][1] def css(self): + """Generate css content from given bundle""" key = 'css_%s' % self.xmlid if key in self.cache and self.cache[key][0] != self.version: # Invalidate cache on version mismach self.cache.pop(key) if key not in self.cache: - self.preprocess_css() - content = '\n'.join(asset.minify() for asset in self.stylesheets) + content = self.preprocess_css() if self.css_errors: msg = '\n'.join(self.css_errors) @@ -1162,32 +1163,45 @@ class AssetsBundle(object): def preprocess_css(self): """ Checks if the bundle contains any sass/less content, then compiles it to css. + Returns the bundle's flat css. """ - to_preprocess = [asset for asset in self.stylesheets if isinstance(asset, PreprocessedCSS)] - if not to_preprocess: - return - to_compile = [asset for asset in to_preprocess if type(asset) == type(to_preprocess[0])] - if len(to_preprocess) != len(to_compile): - self.css_errors.append("You can't mix css preprocessors languages in the same bundle. (%s)" % self.xmlid) - command = to_compile[0].get_command() - source = '\n'.join([asset.get_source() for asset in to_compile]) + sass = [asset for asset in self.stylesheets if isinstance(asset, SassStylesheetAsset)] + if sass: + cmd = sass[0].get_command() + source = '\n'.join([asset.get_source() for asset in sass]) + compiled = self.compile_css(cmd, source) + self.recompose_css(compiled) - # move up all @import rules to the top and exclude file imports + less = [asset for asset in self.stylesheets if isinstance(asset, LessStylesheetAsset)] + if less: + cmd = less[0].get_command() + source = '' + for asset in self.stylesheets: + if isinstance(asset, LessStylesheetAsset): + source += asset.get_source() + else: + source += asset.content + compiled = self.compile_css(cmd, source) + return compiled + + return '\n'.join(asset.minify() for asset in self.stylesheets) + + def compile_css(self, cmd, source): + """Sanitizes @import rules, remove duplicates @import rules, then compile""" imports = [] - def push(matchobj): + def sanitize(matchobj): ref = matchobj.group(2) line = '@import "%s"%s' % (ref, matchobj.group(3)) if '.' not in ref and line not in imports and not ref.startswith(('.', '/', '~')): imports.append(line) + return line return '' - source = re.sub(self.rx_preprocess_imports, push, source) - imports.append(source) - source = u'\n'.join(imports) + source = re.sub(self.rx_preprocess_imports, sanitize, source) try: - compiler = Popen(command, stdin=PIPE, stdout=PIPE, stderr=PIPE) + compiler = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) except Exception: - msg = "Could not execute command %r" % command[0] + msg = "Could not execute command %r" % cmd[0] _logger.error(msg) self.css_errors.append(msg) return @@ -1198,14 +1212,18 @@ class AssetsBundle(object): self.css_errors.append(error) return compiled = result[0].strip().decode('utf8') + return compiled + + def recompose_css(self, compiled): + """Flattenize StylesheetAsset's content from compiled source""" fragments = self.rx_css_split.split(compiled)[1:] while fragments: asset_id = fragments.pop(0) - asset = next(asset for asset in to_compile if asset.id == asset_id) + asset = next(asset for asset in self.stylesheets if asset.id == asset_id) asset._content = fragments.pop(0) def get_preprocessor_error(self, stderr, source=None): - # TODO: try to find out which asset the error belongs to + """Improve and remove sensitive information from sass/less compilator error messages""" error = stderr.split('Load paths')[0].replace(' Use --trace for backtrace.', '') if 'Cannot load compass' in error: error += "Maybe you should install the compass gem using this extra argument:\n\n" \