diff --git a/trytond/model/__init__.py b/trytond/model/__init__.py index 642ee16c14..8ffaffa57b 100644 --- a/trytond/model/__init__.py +++ b/trytond/model/__init__.py @@ -1,3 +1,4 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. from model import * +from modelview import * diff --git a/trytond/model/modelview.py b/trytond/model/modelview.py new file mode 100644 index 0000000000..ab4c8c0ce0 --- /dev/null +++ b/trytond/model/modelview.py @@ -0,0 +1,368 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. +from trytond.model import Model +from trytond.tools import Cache +from lxml import etree +import md5 +import copy + +def _find(tree, element): + if element.tag == 'xpath': + res = tree.xpath(element.get('expr')) + if res: + return res[0] + return None + + +def _inherit_apply(src, inherit): + tree_src = etree.fromstring(src) + tree_inherit = etree.fromstring(inherit) + root_inherit = tree_inherit.getroottree().getroot() + for element2 in root_inherit: + element = _find(tree_src, element2) + if element is not None: + pos = element2.get('position', 'inside') + if pos == 'replace': + parent = element.getparent() + next = element.getnext() + if next is not None: + for child in element2: + index = parent.index(next) + parent.insert(index, child) + else: + parent.extend(element2.getchildren()) + parent.remove(element) + elif pos == 'replace_attributes': + child = element2.getchildren()[0] + for attr in child.attrib: + element.set(attr, child.get(attr)) + elif pos == 'inside': + element.extend(element2.getchildren()) + elif pos == 'after': + parent = element.getparent() + next = element.getnext() + if next is not None: + for child in element2: + index = parent.index(next) + parent.insert(index, child) + else: + parent.extend(element2.getchildren()) + elif pos == 'before': + parent = element.getparent() + for child in element2: + index = parent.index(element) + parent.insert(index, child) + else: + raise AttributeError('Unknown position ' \ + 'in inherited view %s!' % pos) + else: + raise AttributeError( + 'Couldn\'t find tag (%s: %s) in parent view!' % \ + (element2.tag, element2.get('expr'))) + return etree.tostring(tree_src, encoding='utf-8') + + +class ModelView(Model): + """ + Define a model with views in Tryton. + """ + + def __init__(self): + super(ModelView, self).__init__() + self._rpc_allowed += [ + 'fields_view_get', + ] + + def fields_view_get(self, cursor, user, view_id=None, view_type='form', + context=None, toolbar=False, hexmd5=None): + ''' + Return a view definition + + :param cursor: the database cursor + :param user: the user id + :param view_id: the id of the view, if None the first one will be used + :param view_type: the type of the view if view_id is None + :param context: the context + :param toolbar: if True the result will contain a toolbar key with + keyword action definitions for the view + :param hexmd5: if fill, the function will return True if the result + have the same md5 + :return: a dictionnary with keys: + - model: the model name + - arch: the xml description of the view + - fields: a dictionnary with the definition of each fields + in the view + - toolbar: a dictionnary with the keyword action definitions + - md5: the check sum of the dictionnary without this checksum + ''' + + if context is None: + context = {} + + result = {'model': self._name} + + test = True + model = True + sql_res = False + inherit_view_id = False + while test: + if view_id: + where = (model and (" and model='%s'" % (self._name,))) or '' + cursor.execute('SELECT arch, field_childs, id, type, ' \ + 'inherit, model ' \ + 'FROM ir_ui_view WHERE id = %s ' + where, (view_id,)) + else: + cursor.execute('SELECT arch, field_childs, id, type, ' \ + 'inherit, model ' \ + 'FROM ir_ui_view ' \ + 'WHERE model = %s AND type = %s' \ + 'ORDER BY inherit DESC, priority ASC, id ASC', + (self._name, view_type)) + sql_res = cursor.fetchone() + if not sql_res: + break + test = sql_res[4] + if test: + inherit_view_id = sql_res[2] + view_id = test or sql_res[2] + model = False + + # if a view was found + if sql_res: + result['type'] = sql_res[3] + result['view_id'] = view_id + result['arch'] = sql_res[0] + result['field_childs'] = sql_res[1] or False + + # Check if view is not from an inherited model + if sql_res[5] != self._name: + inherit_obj = self.pool.get(sql_res[5]) + result['arch'] = inherit_obj.fields_view_get(cursor, user, + result['view_id'], context=context)['arch'] + view_id = inherit_view_id + + # get all views which inherit from (ie modify) this view + cursor.execute('SELECT arch, domain, id FROM ir_ui_view ' \ + 'WHERE (inherit = %s AND model = %s) OR ' \ + ' (id = %s AND inherit IS NOT NULL) ' + 'ORDER BY priority ASC, id ASC', + (view_id, self._name, view_id)) + sql_inherit = cursor.fetchall() + for (arch, domain, view_id) in sql_inherit: + if domain: + if not eval(domain, {'context': context}): + continue + if not arch or not arch.strip(): + continue + result['arch'] = _inherit_apply(result['arch'], arch) + + # otherwise, build some kind of default view + else: + if view_type == 'form': + res = self.fields_get(cursor, user, context=context) + xml = '''''' \ + '''
''' % (self._description,) + for i in res: + if i in ('create_uid', 'create_date', + 'write_uid', 'write_date', 'id'): + continue + if res[i]['type'] not in ('one2many', 'many2many'): + xml += '