18.0 vanilla

This commit is contained in:
Ernad Husremovic 2025-10-03 18:06:50 +02:00
parent d72e748793
commit 0a7ae8db93
337 changed files with 399651 additions and 232598 deletions

View file

@ -1,18 +1,25 @@
from lxml import etree
from lxml.builder import E
import copy
import itertools
import logging
import re
from odoo.tools.translate import _
from odoo.tools import SKIPPED_ELEMENT_TYPES, html_escape
from odoo.exceptions import ValidationError
from lxml import etree
from lxml.builder import E
from odoo.tools.translate import LazyTranslate
from odoo.exceptions import ValidationError
from .misc import SKIPPED_ELEMENT_TYPES, html_escape
__all__ = []
_lt = LazyTranslate('base')
_logger = logging.getLogger(__name__)
RSTRIP_REGEXP = re.compile(r'\n[ \t]*$')
# attribute names that contain Python expressions
PYTHON_ATTRIBUTES = {'readonly', 'required', 'invisible', 'column_invisible', 't-if', 't-elif'}
def add_stripped_items_before(node, spec, extract):
text = spec.text or ''
@ -36,7 +43,9 @@ def add_stripped_items_before(node, spec, extract):
for child in spec:
if child.get('position') == 'move':
tail = child.tail
child = extract(child)
child.tail = tail
node.addprevious(child)
@ -77,7 +86,7 @@ def locate_node(arch, spec):
try:
xPath = etree.ETXPath(expr)
except etree.XPathSyntaxError as e:
raise ValidationError(_("Invalid Expression while parsing xpath %r", expr)) from e
raise ValidationError(_lt("Invalid Expression while parsing xpath %s", expr)) from e
nodes = xPath(arch)
return nodes[0] if nodes else None
elif spec.tag == 'field':
@ -122,7 +131,7 @@ def apply_inheritance_specs(source, specs_tree, inherit_branding=False, pre_loca
"""
if len(spec):
raise ValueError(
_("Invalid specification for moved nodes: %r", etree.tostring(spec, encoding='unicode'))
_lt("Invalid specification for moved nodes: %s", etree.tostring(spec, encoding='unicode'))
)
pre_locate(spec)
to_extract = locate_node(source, spec)
@ -131,7 +140,7 @@ def apply_inheritance_specs(source, specs_tree, inherit_branding=False, pre_loca
return to_extract
else:
raise ValueError(
_("Element %r cannot be located in parent view", etree.tostring(spec, encoding='unicode'))
_lt("Element “%s cannot be located in parent view", etree.tostring(spec, encoding='unicode'))
)
while len(specs):
@ -210,26 +219,80 @@ def apply_inheritance_specs(source, specs_tree, inherit_branding=False, pre_loca
node.text = spec.text
else:
raise ValueError(_("Invalid mode attribute:") + " '%s'" % mode)
raise ValueError(_lt("Invalid mode attribute:%s", mode))
elif pos == 'attributes':
for child in spec.getiterator('attribute'):
attribute = child.get('name')
value = child.text or ''
if child.get('add') or child.get('remove'):
assert not child.text
separator = child.get('separator', ',')
if separator == ' ':
separator = None # squash spaces
to_add = (
s for s in (s.strip() for s in child.get('add', '').split(separator))
if s
)
to_remove = {s.strip() for s in child.get('remove', '').split(separator)}
values = (s.strip() for s in node.get(attribute, '').split(separator))
value = (separator or ' ').join(itertools.chain(
(v for v in values if v not in to_remove),
to_add
# The element should only have attributes:
# - name (mandatory),
# - add, remove, separator
# - any attribute that starts with data-oe-*
unknown = [
key
for key in child.attrib
if key not in ('name', 'add', 'remove', 'separator')
and not key.startswith('data-oe-')
]
if unknown:
raise ValueError(_lt(
"Invalid attributes %s in element <attribute>",
", ".join(map(repr, unknown)),
))
attribute = child.get('name')
value = None
if child.get('add') or child.get('remove'):
if child.text:
raise ValueError(_lt(
"Element <attribute> with 'add' or 'remove' cannot contain text %s",
repr(child.text),
))
value = node.get(attribute, '')
add = child.get('add', '')
remove = child.get('remove', '')
separator = child.get('separator')
if attribute in PYTHON_ATTRIBUTES or attribute.startswith('decoration-'):
# attribute containing a python expression
separator = separator.strip()
if separator not in ('and', 'or'):
raise ValueError(_lt(
"Invalid separator %(separator)s for python expression %(expression)s; "
"valid values are 'and' and 'or'",
separator=repr(separator), expression=repr(attribute),
))
if remove:
if re.match(rf'^\(*{remove}\)*$', value):
value = ''
else:
patterns = [
f"({remove}) {separator} ",
f" {separator} ({remove})",
f"{remove} {separator} ",
f" {separator} {remove}",
]
for pattern in patterns:
index = value.find(pattern)
if index != -1:
value = value[:index] + value[index + len(pattern):]
break
if add:
value = f"({value}) {separator} ({add})" if value else add
else:
if separator is None:
separator = ','
elif separator == ' ':
separator = None # squash spaces
values = (s.strip() for s in value.split(separator))
to_add = filter(None, (s.strip() for s in add.split(separator)))
to_remove = {s.strip() for s in remove.split(separator)}
value = (separator or ' ').join(itertools.chain(
(v for v in values if v and v not in to_remove),
to_add
))
else:
value = child.text or ''
if value:
node.set(attribute, value)
elif attribute in node.attrib:
@ -255,10 +318,7 @@ def apply_inheritance_specs(source, specs_tree, inherit_branding=False, pre_loca
add_stripped_items_before(node, spec, extract)
else:
raise ValueError(
_("Invalid position attribute: '%s'") %
pos
)
raise ValueError(_lt("Invalid position attribute: '%s'", pos))
else:
attrs = ''.join([
@ -268,7 +328,7 @@ def apply_inheritance_specs(source, specs_tree, inherit_branding=False, pre_loca
])
tag = "<%s%s>" % (spec.tag, attrs)
raise ValueError(
_("Element '%s' cannot be located in parent view", tag)
_lt("Element '%s' cannot be located in parent view", tag)
)
return source