Holger Brunn
8 years ago
committed by
Raphaël Valyi
9 changed files with 288 additions and 0 deletions
-
83base_view_inheritance_extension/README.rst
-
4base_view_inheritance_extension/__init__.py
-
17base_view_inheritance_extension/__openerp__.py
-
22base_view_inheritance_extension/demo/ir_ui_view.xml
-
4base_view_inheritance_extension/models/__init__.py
-
124base_view_inheritance_extension/models/ir_ui_view.py
-
BINbase_view_inheritance_extension/static/description/icon.png
-
4base_view_inheritance_extension/tests/__init__.py
-
30base_view_inheritance_extension/tests/test_base_view_inheritance_extension.py
@ -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 |
After Width: 128 | Height: 128 | 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] |
||||
|
) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue