[IMP] tools: xpath - support mode inner for position replace

This commit adds a new attribute mode for position 'replace' to the xpath feature.

This mode can take 2 values:

- 'outer' (default mode if not provided) that will replace the sibling target
- 'inner' that will preserve the sibling target

If base arch is:
```html
<p>
   <field name='x'>yyy</field>
</p>
```

`<field name='x' position="replace" (mode="outer")>zzz</field>`
```html
<p>
   zzz
</p>
```

`<field name='x' position="replace" mode="inner">zzz</field>`
```html
<p>
   <field name='x'>
      zzz
   </field>
</p>
```

Mode inner is useful for theme or render flat content, but not recommanded to
be used in page editable since we ignore the inherit-branding part until now.

task-2172208

closes odoo/odoo#48679

Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com>
Co-authored-by: Okan SUMER (osu) <osu@odoo.com>
Co-authored-by: Jérémy Kersten <jke@odoo.com>
This commit is contained in:
Jeremy Kersten
2021-07-29 07:04:55 +00:00
co-authored by Okan SUMER
parent 73a821abf6
commit be43710ab8
2 changed files with 88 additions and 38 deletions
+37 -1
View File
@@ -300,8 +300,18 @@ class TestApplyInheritanceSpecs(ViewCase):
self.base_arch = E.form(
E.field(name="target"),
string="Title")
self.adv_arch = E.form(
E.field(
"TEXT1",
E.field(name="subtarget"),
"TEXT2",
E.field(name="anothersubtarget"),
"TEXT3",
name="target",
),
string="Title")
def test_replace(self):
def test_replace_outer(self):
spec = E.field(
E.field(name="replacement"),
name="target", position="replace")
@@ -366,6 +376,32 @@ class TestApplyInheritanceSpecs(ViewCase):
name="target"),
string="Title"))
def test_replace_inner(self):
spec = E.field(
"TEXT 4",
E.field(name="replacement"),
"TEXT 5",
E.field(name="replacement2"),
"TEXT 6",
name="target", position="replace", mode="inner")
expected = E.form(
E.field(
"TEXT 4",
E.field(name="replacement"),
"TEXT 5",
E.field(name="replacement2"),
"TEXT 6",
name="target"),
string="Title")
# applying spec to both base_arch and adv_arch is expected to give the same result
self.View.apply_inheritance_specs(self.base_arch, spec)
self.assertEqual(self.base_arch, expected)
self.View.apply_inheritance_specs(self.adv_arch, spec)
self.assertEqual(self.adv_arch, expected)
def test_unpack_data(self):
spec = E.data(
E.field(E.field(name="inserted 0"), name="target"),
+51 -37
View File
@@ -134,44 +134,58 @@ def apply_inheritance_specs(source, specs_tree, inherit_branding=False, pre_loca
if node is not None:
pos = spec.get('position', 'inside')
if pos == 'replace':
for loc in spec.xpath(".//*[text()='$0']"):
loc.text = ''
loc.append(copy.deepcopy(node))
if node.getparent() is None:
spec_content = None
comment = None
for content in spec:
if content.tag is not etree.Comment:
spec_content = content
break
else:
comment = content
source = copy.deepcopy(spec_content)
# only keep the t-name of a template root node
t_name = node.get('t-name')
if t_name:
source.set('t-name', t_name)
if comment is not None:
text = source.text
source.text = None
comment.tail = text
source.insert(0, comment)
else:
replaced_node_tag = None
mode = spec.get('mode', 'outer')
if mode == "outer":
for loc in spec.xpath(".//*[text()='$0']"):
loc.text = ''
loc.append(copy.deepcopy(node))
if node.getparent() is None:
spec_content = None
comment = None
for content in spec:
if content.tag is not etree.Comment:
spec_content = content
break
else:
comment = content
source = copy.deepcopy(spec_content)
# only keep the t-name of a template root node
t_name = node.get('t-name')
if t_name:
source.set('t-name', t_name)
if comment is not None:
text = source.text
source.text = None
comment.tail = text
source.insert(0, comment)
else:
replaced_node_tag = None
for child in spec:
if child.get('position') == 'move':
child = extract(child)
if inherit_branding and not replaced_node_tag and child.tag is not etree.Comment:
# To make a correct branding, we need to
# - know exactly which node has been replaced
# - store it before anything else has altered the Tree
# Do it exactly here :D
child.set('meta-oe-xpath-replacing', node.tag)
# We just store the replaced node tag on the first
# child of the xpath replacing it
replaced_node_tag = node.tag
node.addprevious(child)
node.getparent().remove(node)
elif mode == "inner":
# Replace the entire content of an element
for child in node:
node.remove(child)
node.text = None
for child in spec:
if child.get('position') == 'move':
child = extract(child)
if inherit_branding and not replaced_node_tag and child.tag is not etree.Comment:
# To make a correct branding, we need to
# - know exactly which node has been replaced
# - store it before anything else has altered the Tree
# Do it exactly here :D
child.set('meta-oe-xpath-replacing', node.tag)
# We just store the replaced node tag on the first
# child of the xpath replacing it
replaced_node_tag = node.tag
node.addprevious(child)
node.getparent().remove(node)
node.append(copy.deepcopy(child))
node.text = spec.text
else:
raise ValueError(_("Invalid mode attribute:") + " '%s'" % mode)
elif pos == 'attributes':
for child in spec.getiterator('attribute'):
attribute = child.get('name')