[FIX] whitespace/indent lost by RTE
authorXavier Morel <xmo@openerp.com>
Thu, 27 Feb 2014 12:59:34 +0000 (13:59 +0100)
committerXavier Morel <xmo@openerp.com>
Thu, 27 Feb 2014 12:59:34 +0000 (13:59 +0100)
Didn't manage to find RTE settings to avoid losing leading whitespace of
lines, so reindeint arch after doing all integration, right before saving back
to view's field.

* html.fromstring(parser=HTMLParser(remove_blank_text=True) does not seem to
  work, so serialize to XML, and parse back with
  remove_blank_text. remove_blank_text necessary for lxml's pretty_print to
  work correctly.

* pretty_print only & always uses 2 spaces/indent level. Our files (and the
  HTML editor's Format button) uses 4 spaces -> need a second pass to double
  indents.

bzr revid: xmo@openerp.com-20140227125934-q8j3z440px2ic6kx

addons/website/models/ir_ui_view.py
addons/website/tests/test_views.py

index 27af033..857b99d 100644 (file)
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 import copy
+import re
 import simplejson
 import werkzeug
 
@@ -158,6 +159,29 @@ class view(osv.osv):
 
         return super(view, self).render(cr, uid, id_or_xml_id, values=values, engine=engine, context=context)
 
+    def _pretty_arch(self, arch):
+        # remove_blank_string does not seem to work on HTMLParser, and
+        # pretty-printing with lxml more or less requires stripping
+        # whitespace: http://lxml.de/FAQ.html#why-doesn-t-the-pretty-print-option-reformat-my-xml-output
+        # so serialize to XML, parse as XML (remove whitespace) then serialize
+        # as XML (pretty print)
+        arch_no_whitespace = etree.fromstring(
+            etree.tostring(arch, encoding='utf-8'),
+            parser=etree.XMLParser(encoding='utf-8', remove_blank_text=True))
+        arch_pretty_indent_2 = etree.tostring(
+            arch_no_whitespace, encoding='unicode', pretty_print=True)
+
+        # pretty_print uses a fixed indent level of 2, we want an indent of 4,
+        # double up leading spaces.
+        def repl(m):
+            indent = len(m.group(0)) / 2
+            return u' ' * 4 * indent
+        # FIXME: If py2.7 only, can use re.M in sub and don't have to do replacement line by line
+        return u'\n'.join(
+            re.sub(ur'^((?:  )+)', repl, line)
+            for line in arch_pretty_indent_2.split(u'\n')
+        )
+
     def save(self, cr, uid, res_id, value, xpath=None, context=None):
         """ Update a view section. The view section may embed fields to write
 
@@ -183,5 +207,5 @@ class view(osv.osv):
 
         arch = self.replace_arch_section(cr, uid, res_id, xpath, arch_section, context=context)
         self.write(cr, uid, res_id, {
-            'arch': etree.tostring(arch, encoding='utf-8').decode('utf-8')
+            'arch': self._pretty_arch(arch)
         }, context=context)
index 8041a7e..1adb117 100644 (file)
@@ -13,8 +13,8 @@ class TestViewSaving(common.TransactionCase):
     def eq(self, a, b):
         self.assertEqual(a.tag, b.tag)
         self.assertEqual(a.attrib, b.attrib)
-        self.assertEqual(a.text, b.text)
-        self.assertEqual(a.tail, b.tail)
+        self.assertEqual((a.text or '').strip(), (b.text or '').strip())
+        self.assertEqual((a.tail or '').strip(), (b.tail or '').strip())
         for ca, cb in itertools.izip_longest(a, b):
             self.eq(ca, cb)