You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
5.5 KiB

  1. # Copyright 2016 Therp BV <https://therp.nl>
  2. # Copyright 2018 Tecnativa - Sergio Teruel
  3. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
  4. from lxml import etree
  5. from odoo import api, models, tools
  6. from odoo.tools.safe_eval import safe_eval
  7. class UnquoteObject(str):
  8. def __getattr__(self, name):
  9. return UnquoteObject('%s.%s' % (self, name))
  10. def __repr__(self):
  11. return self
  12. def __call__(self, *args, **kwargs):
  13. return UnquoteObject(
  14. '%s(%s)' % (
  15. self,
  16. ','.join(
  17. [
  18. UnquoteObject(
  19. a if not isinstance(a, str)
  20. else "'%s'" % a
  21. )
  22. for a in args
  23. ] +
  24. [
  25. '%s=%s' % (UnquoteObject(k), v)
  26. for (k, v) in kwargs.items()
  27. ]
  28. )
  29. )
  30. )
  31. class UnquoteEvalObjectContext(tools.misc.UnquoteEvalContext):
  32. def __missing__(self, key):
  33. return UnquoteObject(key)
  34. class IrUiView(models.Model):
  35. _inherit = 'ir.ui.view'
  36. @api.model
  37. def apply_inheritance_specs(self, source, specs_tree, inherit_id):
  38. for specs, handled_by in self._iter_inheritance_specs(specs_tree):
  39. source = handled_by(source, specs, inherit_id)
  40. return source
  41. @api.model
  42. def _iter_inheritance_specs(self, spec):
  43. if spec.tag == 'data':
  44. for child in spec:
  45. for node, handler in self._iter_inheritance_specs(child):
  46. yield node, handler
  47. return
  48. if spec.get('position') == 'attributes':
  49. if all(not c.get('operation') for c in spec):
  50. yield spec, self._get_inheritance_handler(spec)
  51. return
  52. for child in spec:
  53. node = etree.Element(spec.tag, **spec.attrib)
  54. node.insert(0, child)
  55. yield node, self._get_inheritance_handler_attributes(
  56. child
  57. )
  58. return
  59. yield spec, self._get_inheritance_handler(spec)
  60. @api.model
  61. def _get_inheritance_handler(self, node):
  62. handler = super(IrUiView, self).apply_inheritance_specs
  63. if hasattr(
  64. self, 'inheritance_handler_%s' % node.tag
  65. ):
  66. handler = getattr(
  67. self,
  68. 'inheritance_handler_%s' % node.tag
  69. )
  70. return handler
  71. @api.model
  72. def _get_inheritance_handler_attributes(self, node):
  73. handler = super(IrUiView, self).apply_inheritance_specs
  74. if hasattr(
  75. self, 'inheritance_handler_attributes_%s' % node.get('operation')
  76. ):
  77. handler = getattr(
  78. self,
  79. 'inheritance_handler_attributes_%s' % node.get('operation')
  80. )
  81. return handler
  82. @api.model
  83. def inheritance_handler_attributes_python_dict(
  84. self, source, specs, inherit_id
  85. ):
  86. """Implement
  87. <$node position="attributes">
  88. <attribute name="$attribute" operation="python_dict" key="$key">
  89. $keyvalue
  90. </attribute>
  91. </$node>"""
  92. node = self.locate_node(source, specs)
  93. for attribute_node in specs:
  94. python_dict = safe_eval(
  95. node.get(attribute_node.get('name')) or '{}',
  96. UnquoteEvalObjectContext(),
  97. nocopy=True
  98. )
  99. python_dict[attribute_node.get('key')] = UnquoteObject(
  100. attribute_node.text
  101. )
  102. node.attrib[attribute_node.get('name')] = str(python_dict)
  103. return source
  104. @api.model
  105. def inheritance_handler_xpath(self, source, specs, inherit_id):
  106. if not specs.get('position') == 'move':
  107. return super(IrUiView, self).apply_inheritance_specs(
  108. source, specs, inherit_id
  109. )
  110. node = self.locate_node(source, specs)
  111. target_node = self.locate_node(
  112. source, etree.Element(specs.tag, expr=specs.get('target'))
  113. )
  114. target_node.append(node)
  115. return source
  116. @api.model
  117. def inheritance_handler_attributes_list_add(
  118. self, source, specs, inherit_id
  119. ):
  120. """Implement
  121. <$node position="attributes">
  122. <attribute name="$attribute" operation="list_add">
  123. $new_value
  124. </attribute>
  125. </$node>"""
  126. node = self.locate_node(source, specs)
  127. for attribute_node in specs:
  128. attribute_name = attribute_node.get('name')
  129. old_value = node.get(attribute_name) or ''
  130. new_value = old_value + ',' + attribute_node.text
  131. node.attrib[attribute_name] = new_value
  132. return source
  133. @api.model
  134. def inheritance_handler_attributes_list_remove(
  135. self, source, specs, inherit_id
  136. ):
  137. """Implement
  138. <$node position="attributes">
  139. <attribute name="$attribute" operation="list_remove">
  140. $value_to_remove
  141. </attribute>
  142. </$node>"""
  143. node = self.locate_node(source, specs)
  144. for attribute_node in specs:
  145. attribute_name = attribute_node.get('name')
  146. old_values = (node.get(attribute_name) or '').split(',')
  147. remove_values = attribute_node.text.split(',')
  148. new_values = [x for x in old_values if x not in remove_values]
  149. node.attrib[attribute_name] = ','.join(
  150. [_f for _f in new_values if _f])
  151. return source