[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:
co-authored by
Okan SUMER
parent
73a821abf6
commit
be43710ab8
@@ -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"),
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user