#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 lxml import etree from functools import wraps import copy from trytond.model import Model from trytond.tools import safe_eval, ClassProperty from trytond.pyson import PYSONEncoder, CONTEXT from trytond.transaction import Transaction from trytond.cache import Cache from trytond.pool import Pool from trytond.exceptions import UserError from trytond.rpc import RPC __all__ = ['ModelView'] 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: if element2.tag != 'xpath': continue element = _find(tree_src, element2) if element is not None: pos = element2.get('position', 'inside') if pos == 'replace': parent = element.getparent() enext = element.getnext() if enext is not None: for child in element2: index = parent.index(enext) 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() enext = element.getnext() if enext is not None: for child in element2: index = parent.index(enext) 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. """ __modules_list = None # Cache for the modules list sorted by dependency _fields_view_get_cache = Cache('modelview.fields_view_get') _view_toolbar_get_cache = Cache('modelview.view_toolbar_get') @staticmethod def _reset_modules_list(): ModelView.__modules_list = None @ClassProperty @classmethod def _modules_list(cls): from trytond.modules import create_graph, get_module_list if ModelView.__modules_list: return ModelView.__modules_list graph = create_graph(get_module_list())[0] ModelView.__modules_list = [x.name for x in graph] + [None] return ModelView.__modules_list @classmethod def __setup__(cls): super(ModelView, cls).__setup__() cls.__rpc__['fields_view_get'] = RPC() cls.__rpc__['view_toolbar_get'] = RPC() cls._buttons = {} @classmethod def fields_view_get(cls, view_id=None, view_type='form'): ''' Return a view definition. If view_id is None the first one will be used of view_type. The definition is a dictionary with keys: - model: the model name - arch: the xml description of the view - fields: a dictionary with the definition of each field in the view ''' key = (cls.__name__, view_id, view_type) result = cls._fields_view_get_cache.get(key) if result: return result result = {'model': cls.__name__} pool = Pool() View = pool.get('ir.ui.view') test = True model = True view = None inherit_view_id = False while test: if view_id: domain = [('id', '=', view_id)] if model: domain.append(('model', '=', cls.__name__)) views = View.search(domain, order=[]) else: domain = [ ('model', '=', cls.__name__), ('type', '=', view_type), ] order = [ ('inherit', 'DESC'), ('priority', 'ASC'), ('id', 'ASC'), ] views = View.search(domain, order=order) if not views: break view = views[0] test = view.inherit if test: inherit_view_id = view.id view_id = test.id if test else view.id model = False # if a view was found if view: result['type'] = view.type result['view_id'] = view_id result['arch'] = view.arch result['field_childs'] = view.field_childs # Check if view is not from an inherited model if view.model != cls.__name__: Inherit = pool.get(view.model) result['arch'] = Inherit.fields_view_get( result['view_id'])['arch'] view_id = inherit_view_id # get all views which inherit from (ie modify) this view views = View.search([ 'OR', [ ('inherit', '=', view_id), ('model', '=', cls.__name__), ], [ ('id', '=', view_id), ('inherit', '!=', None), ], ], order=[ ('priority', 'ASC'), ('id', 'ASC'), ]) raise_p = False while True: try: views.sort(key=lambda x: cls._modules_list.index(x.module or None)) break except ValueError: if raise_p: raise # There is perhaps a new module in the directory ModelView._reset_modules_list() raise_p = True for view in views: if view.domain: if not safe_eval(view.domain, {'context': Transaction().context}): continue if not view.arch or not view.arch.strip(): continue result['arch'] = _inherit_apply(result['arch'], view.arch) # otherwise, build some kind of default view else: if view_type == 'form': res = cls.fields_get() xml = '''''' \ '''
''' % (cls.__doc__,) for i in res: if i in ('create_uid', 'create_date', 'write_uid', 'write_date', 'id', 'rec_name'): continue if res[i]['type'] not in ('one2many', 'many2many'): xml += '