[ADD] base_view_inheritance_extension
parent
4cce747cc3
commit
7cd2002b84
|
@ -0,0 +1,83 @@
|
|||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
|
||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||
:alt: License: AGPL-3
|
||||
|
||||
=========================
|
||||
Extended view inheritance
|
||||
=========================
|
||||
|
||||
This module was written to make it simple to add custom operators for view inheritance.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
|
||||
:alt: Try me on Runbot
|
||||
:target: https://runbot.odoo-community.org/runbot/149/8.0
|
||||
|
||||
Change a python dictionary (context for example)
|
||||
------------------------------------------------
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<attribute name="$attribute" operation="python_dict" key="$key">
|
||||
$new_value
|
||||
</attribute>
|
||||
|
||||
Note that views are subject to evaluation of xmlids anyways, so if you need to refer to some xmlid, say ``%(xmlid)s``.
|
||||
|
||||
Move an element in the view
|
||||
---------------------------
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<xpath expr="$xpath" position="move" target="$targetxpath" />
|
||||
|
||||
This can also be used to wrap some element into another, create the target element first, then move the node youwant to wrap there.
|
||||
|
||||
Known issues / Roadmap
|
||||
======================
|
||||
|
||||
* add ``<attribute operation="python_list_add">$value</attribute>``
|
||||
* add ``<attribute operation="python_list_remove">$index</attribute>``
|
||||
* add ``<attribute operation="json_dict" key="$key">$value</attribute>``
|
||||
* support ``<xpath expr="$xpath" position="move" target="xpath" target_position="position" />``
|
||||
* support an ``eval`` attribute for our new node types
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues
|
||||
<https://github.com/OCA/server-tools/issues>`_. In case of trouble, please
|
||||
check there if your issue has already been reported. If you spotted it first,
|
||||
help us smashing it by providing a detailed and welcomed feedback.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Images
|
||||
------
|
||||
|
||||
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
* Holger Brunn <hbrunn@therp.nl>
|
||||
|
||||
Do not contact contributors directly about help with questions or problems concerning this addon, but use the `community mailing list <mailto:community@mail.odoo.com>`_ or the `appropriate specialized mailinglist <https://odoo-community.org/groups>`_ for help, and the bug tracker linked in `Bug Tracker`_ above for technical issues.
|
||||
|
||||
Maintainer
|
||||
----------
|
||||
|
||||
.. image:: https://odoo-community.org/logo.png
|
||||
:alt: Odoo Community Association
|
||||
:target: https://odoo-community.org
|
||||
|
||||
This module is maintained by the OCA.
|
||||
|
||||
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.
|
||||
|
||||
To contribute to this module, please visit https://odoo-community.org.
|
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# © 2016 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from . import models
|
|
@ -0,0 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# © 2016 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
{
|
||||
"name": "Extended view inheritance",
|
||||
"version": "8.0.1.0.0",
|
||||
"author": "Therp BV,Odoo Community Association (OCA)",
|
||||
"license": "AGPL-3",
|
||||
"category": "Hidden/Dependency",
|
||||
"summary": "Adds more operators for view inheritance",
|
||||
"depends": [
|
||||
'base',
|
||||
],
|
||||
"demo": [
|
||||
"demo/ir_ui_view.xml",
|
||||
],
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="view_partner_form" model="ir.ui.view">
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="." position="attributes">
|
||||
<attribute name="string">Partner form</attribute>
|
||||
</xpath>
|
||||
<field name="parent_id" position="attributes">
|
||||
<attribute name="context" operation="python_dict" key="default_name">'The company name'</attribute>
|
||||
<attribute name="context" operation="python_dict" key="default_company_id">context.get('company_id', context.get('company'))</attribute>
|
||||
</field>
|
||||
<notebook position="inside">
|
||||
<page string="A new page" name="my_new_page" />
|
||||
</notebook>
|
||||
<xpath expr="//field[@name='child_ids']" position="move" target="//page[@name='my_new_page']" />
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# © 2016 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from . import ir_ui_view
|
|
@ -0,0 +1,124 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# © 2016 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from lxml import etree
|
||||
from openerp import api, models, tools
|
||||
|
||||
|
||||
class UnquoteObject(str):
|
||||
def __getattr__(self, name):
|
||||
return UnquoteObject('%s.%s' % (self, name))
|
||||
|
||||
def __repr__(self):
|
||||
return self
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return UnquoteObject(
|
||||
'%s(%s)' % (
|
||||
self,
|
||||
','.join(
|
||||
[
|
||||
UnquoteObject(
|
||||
a if not isinstance(a, basestring)
|
||||
else "'%s'" % a
|
||||
)
|
||||
for a in args
|
||||
] +
|
||||
[
|
||||
'%s=%s' % (UnquoteObject(k), v)
|
||||
for (k, v) in kwargs.iteritems()
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class UnquoteEvalObjectContext(tools.misc.UnquoteEvalContext):
|
||||
def __missing__(self, key):
|
||||
return UnquoteObject(key)
|
||||
|
||||
|
||||
class IrUiView(models.Model):
|
||||
_inherit = 'ir.ui.view'
|
||||
|
||||
@api.model
|
||||
def apply_inheritance_specs(self, source, specs_tree, inherit_id):
|
||||
for specs, handled_by in self._iter_inheritance_specs(specs_tree):
|
||||
source = handled_by(source, specs, inherit_id)
|
||||
return source
|
||||
|
||||
@api.model
|
||||
def _iter_inheritance_specs(self, spec):
|
||||
if spec.tag == 'data':
|
||||
for child in spec:
|
||||
for node, handler in self._iter_inheritance_specs(child):
|
||||
yield node, handler
|
||||
return
|
||||
if spec.get('position') == 'attributes':
|
||||
for child in spec:
|
||||
node = etree.Element(spec.tag, **spec.attrib)
|
||||
node.insert(0, child)
|
||||
yield node, self._get_inheritance_handler_attributes(
|
||||
child
|
||||
)
|
||||
return
|
||||
yield spec, self._get_inheritance_handler(spec)
|
||||
|
||||
@api.model
|
||||
def _get_inheritance_handler(self, node):
|
||||
handler = super(IrUiView, self).apply_inheritance_specs
|
||||
if hasattr(
|
||||
self, 'inheritance_handler_%s' % node.tag
|
||||
):
|
||||
handler = getattr(
|
||||
self,
|
||||
'inheritance_handler_%s' % node.tag
|
||||
)
|
||||
return handler
|
||||
|
||||
@api.model
|
||||
def _get_inheritance_handler_attributes(self, node):
|
||||
handler = super(IrUiView, self).apply_inheritance_specs
|
||||
if hasattr(
|
||||
self, 'inheritance_handler_attributes_%s' % node.get('operation')
|
||||
):
|
||||
handler = getattr(
|
||||
self,
|
||||
'inheritance_handler_attributes_%s' % node.get('operation')
|
||||
)
|
||||
return handler
|
||||
|
||||
@api.model
|
||||
def inheritance_handler_attributes_python_dict(
|
||||
self, source, specs, inherit_id
|
||||
):
|
||||
"""Implement
|
||||
<$node position="attributes">
|
||||
<attribute name="$attribute" operation="python_dict" key="$key">
|
||||
$keyvalue
|
||||
</attribute>
|
||||
</$node>"""
|
||||
node = self.locate_node(source, specs)
|
||||
for attribute_node in specs:
|
||||
python_dict = tools.safe_eval(
|
||||
node.get(attribute_node.get('name')) or '{}',
|
||||
UnquoteEvalObjectContext()
|
||||
)
|
||||
python_dict[attribute_node.get('key')] = UnquoteObject(
|
||||
attribute_node.text
|
||||
)
|
||||
node.attrib[attribute_node.get('name')] = str(python_dict)
|
||||
return source
|
||||
|
||||
@api.model
|
||||
def inheritance_handler_xpath(self, source, specs, inherit_id):
|
||||
if not specs.get('position') == 'move':
|
||||
return super(IrUiView, self).apply_inheritance_specs(
|
||||
source, specs, inherit_id
|
||||
)
|
||||
node = self.locate_node(source, specs)
|
||||
target_node = self.locate_node(
|
||||
source, etree.Element(specs.tag, expr=specs.get('target'))
|
||||
)
|
||||
target_node.append(node)
|
||||
return source
|
Binary file not shown.
After Width: | Height: | Size: 9.2 KiB |
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# © 2016 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from . import test_base_view_inheritance_extension
|
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# © 2016 Therp BV <http://therp.nl>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||
from lxml import etree
|
||||
from openerp.tests.common import TransactionCase
|
||||
|
||||
|
||||
class TestBaseViewInheritanceExtension(TransactionCase):
|
||||
def test_base_view_inheritance_extension(self):
|
||||
view_id = self.env.ref('base.view_partner_form').id
|
||||
fields_view_get = self.env['res.partner'].fields_view_get(
|
||||
view_id=view_id
|
||||
)
|
||||
view = etree.fromstring(fields_view_get['arch'])
|
||||
# verify normal attributes work
|
||||
self.assertEqual(view.xpath('//form')[0].get('string'), 'Partner form')
|
||||
# verify our extra context key worked
|
||||
self.assertTrue(
|
||||
'default_name' in
|
||||
view.xpath('//field[@name="parent_id"]')[0].get('context')
|
||||
)
|
||||
self.assertTrue(
|
||||
"context.get('company_id', context.get('company'))" in
|
||||
view.xpath('//field[@name="parent_id"]')[0].get('context')
|
||||
)
|
||||
# verify we moved the child_ids field
|
||||
self.assertEqual(
|
||||
view.xpath('//field[@name="child_ids"]')[0].getparent(),
|
||||
view.xpath('//page[@name="my_new_page"]')[0]
|
||||
)
|
Loading…
Reference in New Issue