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 = '''''' \
+ '''
"
+ elif view_type == 'tree':
+ field = 'id'
+ if self._rec_name in self._columns:
+ field = self._rec_name
+ xml = '''''' \
+ '''''' \
+ % (self._description, field)
+ elif view_type == 'calendar':
+ xml = '''''' \
+ '''''' \
+ '''''' \
+ % (self._description, self._date_name, self._rec_name)
+ else:
+ xml = ''
+ result['type'] = view_type
+ result['arch'] = xml
+ result['field_childs'] = False
+ result['view_id'] = 0
+
+ # Update arch and compute fields from arch
+ parser = etree.XMLParser(remove_blank_text=True)
+ tree = etree.fromstring(result['arch'], parser)
+ xarch, xfields = self._view_look_dom_arch(cursor, user, tree,
+ result['type'], context=context)
+ result['arch'] = xarch
+ result['fields'] = xfields
+
+ # Add toolbar
+ if toolbar:
+ action_obj = self.pool.get('ir.action.keyword')
+ prints = action_obj.get_keyword(cursor, user, 'form_print',
+ (self._name, 0), context=context)
+ actions = action_obj.get_keyword(cursor, user, 'form_action',
+ (self._name, 0), context=context)
+ relates = action_obj.get_keyword(cursor, user, 'form_relate',
+ (self._name, 0), context=context)
+ result['toolbar'] = {
+ 'print': prints,
+ 'action': actions,
+ 'relate': relates,
+ }
+
+ # Compute md5
+ result['md5'] = md5.new(str(result)).hexdigest()
+ if hexmd5 == result['md5']:
+ return True
+ return result
+
+ fields_view_get = Cache('orm.fields_view_get')(fields_view_get)
+
+ def view_header_get(self, cursor, user, value, view_type='form',
+ context=None):
+ """
+ Overload this method if you need a window title
+ which depends on the context
+
+ :param cursor: the database cursor
+ :param user: the user id
+ :param value: the default header string
+ :param view_type: the type of the view
+ :param context: the context
+ :return: the header string of the view
+ """
+ return value
+
+ def _view_look_dom_arch(self, cursor, user, tree, type, context=None):
+ fields_width = {}
+ tree_root = tree.getroottree().getroot()
+
+ if type == 'tree':
+ viewtreewidth_obj = self.pool.get('ir.ui.view_tree_width')
+ viewtreewidth_ids = viewtreewidth_obj.search(cursor, user, [
+ ('model', '=', self._name),
+ ('user', '=', user),
+ ], context=context)
+ for viewtreewidth in viewtreewidth_obj.browse(cursor, user,
+ viewtreewidth_ids, context=context):
+ if viewtreewidth.width > 0:
+ fields_width[viewtreewidth.field] = viewtreewidth.width
+
+ fields_def = self.__view_look_dom(cursor, user, tree_root, type,
+ fields_width=fields_width, context=context)
+
+ for field_name in fields_def.keys():
+ if field_name in self._columns:
+ field = self._columns[field_name]
+ elif field_name in self._inherit_fields:
+ field = self._inherit_fields[field_name][2]
+ else:
+ continue
+ for depend in field.depends:
+ fields_def.setdefault(depend, {'name': depend})
+
+ if ('active' in self._columns) or ('active' in self._inherit_fields):
+ fields_def.setdefault('active', {'name': 'active', 'select': "2"})
+
+ arch = etree.tostring(tree, encoding='utf-8', pretty_print=False)
+ fields2 = self.fields_get(cursor, user, fields_def.keys(), context)
+ for field in fields_def:
+ if field in fields2:
+ fields2[field].update(fields_def[field])
+ return arch, fields2
+
+ def __view_look_dom(self, cursor, user, element, type, fields_width=None,
+ context=None):
+ translation_obj = self.pool.get('ir.translation')
+
+ if fields_width is None:
+ fields_width = {}
+ if context is None:
+ context = {}
+ result = False
+ fields_attrs = {}
+ childs = True
+
+ if element.tag in ('field', 'label', 'separator', 'group'):
+ for attr in ('name', 'icon'):
+ if element.get(attr):
+ attrs = {}
+ try:
+ if element.get(attr) in self._columns:
+ relation = self._columns[element.get(attr)]._obj
+ else:
+ relation = self._inherit_fields[element.get(attr)][2]._obj
+ except:
+ relation = False
+ if relation and element.tag == 'field':
+ childs = False
+ views = {}
+ for field in element:
+ if field.tag in ('form', 'tree', 'graph'):
+ field2 = copy.copy(field)
+
+ def _translate_field(field):
+ if field.get('string'):
+ trans = translation_obj._get_source(
+ cursor, self._name, 'view',
+ context['language'],
+ field.get('string'))
+ if trans:
+ field.set('string', trans)
+ if field.get('sum'):
+ trans = translation_obj._get_source(
+ cursor, self._name, 'view',
+ context['language'],
+ field.get('sum'))
+ if trans:
+ field.set('sum', trans)
+ for field_child in field:
+ _translate_field(field_child)
+ if 'language' in context:
+ _translate_field(field2)
+
+ relation_obj = self.pool.get(relation)
+ if hasattr(relation_obj, '_view_look_dom_arch'):
+ xarch, xfields = \
+ relation_obj._view_look_dom_arch(
+ cursor, user,
+ field2, field.tag,
+ context=context)
+ views[field.tag] = {
+ 'arch': xarch,
+ 'fields': xfields
+ }
+ element.remove(field)
+ attrs = {'views': views}
+ fields_attrs[element.get(attr)] = attrs
+ if element.get('name') in fields_width:
+ element.set('width', str(fields_width[element.get('name')]))
+
+ # translate view
+ if ('language' in context) and not result:
+ for attr in ('string', 'sum', 'confirm', 'help'):
+ if element.get(attr):
+ trans = translation_obj._get_source(cursor,
+ self._name, 'view', context['language'],
+ element.get(attr))
+ if trans:
+ element.set(attr, trans)
+
+ # Set header string
+ if element.tag in ('form', 'tree', 'graph'):
+ element.set('string', self.view_header_get(cursor, user,
+ element.get('string') or '', view_type=element.tag,
+ context=context))
+
+ if element.tag == 'tree' and element.get('sequence'):
+ fields_attrs.setdefault(element.get('sequence'), {})
+
+ if childs:
+ for field in element:
+ fields_attrs.update(self.__view_look_dom(cursor, user, field,
+ type, fields_width=fields_width, context=context))
+ return fields_attrs
diff --git a/trytond/osv/orm.py b/trytond/osv/orm.py
index 86f276a557..31e0600d7f 100644
--- a/trytond/osv/orm.py
+++ b/trytond/osv/orm.py
@@ -1,15 +1,13 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
# -*- coding: utf-8 -*-
-from trytond.model import Model
+from trytond.model import ModelView
from trytond.netsvc import LocalService
import fields
from trytond.tools import Cache
-import md5
import time
import traceback
import datetime
-from lxml import etree
import copy
from trytond.sql_db import table_handler
from decimal import Decimal
@@ -283,7 +281,7 @@ class EvalEnvironment(dict):
def __nonzero__(self):
return bool(self.record)
-class ORM(Model):
+class ORM(ModelView):
"""
Object relationnal mapping to postgresql module
. Hierarchical structure
@@ -445,7 +443,6 @@ class ORM(Model):
'write',
'create',
'delete',
- 'fields_view_get',
'search',
'name_get',
'name_search',
@@ -1541,340 +1538,6 @@ class ORM(Model):
wf_service.trg_create(user, self._name, id_new, cursor, context=context)
return id_new
-
- def view_header_get(self, cursor, user, value, view_type='form',
- context=None):
- """
- Overload this method if you need a window title
- which depends on the context
- """
- return False
-
- def __view_look_dom(self, cursor, user, element, type, fields_width=None,
- context=None):
- translation_obj = self.pool.get('ir.translation')
-
- if fields_width is None:
- fields_width = {}
- if context is None:
- context = {}
- result = False
- fields_attrs = {}
- childs = True
-
- if element.tag in ('field', 'label', 'separator', 'group'):
- for attr in ('name', 'icon'):
- if element.get(attr):
- attrs = {}
- try:
- if element.get(attr) in self._columns:
- relation = self._columns[element.get(attr)]._obj
- else:
- relation = self._inherit_fields[element.get(attr)][2]._obj
- except:
- relation = False
- if relation and element.tag == 'field':
- childs = False
- views = {}
- for field in element:
- if field.tag in ('form', 'tree', 'graph'):
- field2 = copy.copy(field)
-
- def _translate_field(field):
- if field.get('string'):
- trans = translation_obj._get_source(
- cursor, self._name, 'view',
- context['language'],
- field.get('string'))
- if trans:
- field.set('string', trans)
- if field.get('sum'):
- trans = translation_obj._get_source(
- cursor, self._name, 'view',
- context['language'],
- field.get('sum'))
- if trans:
- field.set('sum', trans)
- for field_child in field:
- _translate_field(field_child)
- if 'language' in context:
- _translate_field(field2)
-
- xarch, xfields = self.pool.get(relation
- )._view_look_dom_arch(cursor, user,
- field2, field.tag,
- context=context)
- views[field.tag] = {
- 'arch': xarch,
- 'fields': xfields
- }
- element.remove(field)
- attrs = {'views': views}
- fields_attrs[element.get(attr)] = attrs
- if element.get('name') in fields_width:
- element.set('width', str(fields_width[element.get('name')]))
-
- # translate view
- if ('language' in context) and not result:
- for attr in ('string', 'sum', 'confirm', 'help'):
- if element.get(attr):
- trans = translation_obj._get_source(cursor,
- self._name, 'view', context['language'],
- element.get(attr))
- if trans:
- element.set(attr, trans)
-
- if element.tag in ('form', 'tree', 'graph'):
- value = ''
- if element.get('string'):
- value = element.get('string')
- result = self.view_header_get(cursor, user, value, element.tag,
- context)
- if result:
- element.set('string', result)
- if element.tag == 'tree' and element.get('sequence'):
- fields_attrs.setdefault(element.get('sequence'), {})
-
- if childs:
- for field in element:
- fields_attrs.update(self.__view_look_dom(cursor, user, field,
- type, fields_width=fields_width, context=context))
- return fields_attrs
-
- def _view_look_dom_arch(self, cursor, user, tree, type, context=None):
- fields_width = {}
- tree_root = tree.getroottree().getroot()
-
- if type == 'tree':
- viewtreewidth_obj = self.pool.get('ir.ui.view_tree_width')
- viewtreewidth_ids = viewtreewidth_obj.search(cursor, user, [
- ('model', '=', self._name),
- ('user', '=', user),
- ], context=context)
- for viewtreewidth in viewtreewidth_obj.browse(cursor, user,
- viewtreewidth_ids, context=context):
- if viewtreewidth.width > 0:
- fields_width[viewtreewidth.field] = viewtreewidth.width
-
- fields_def = self.__view_look_dom(cursor, user, tree_root, type,
- fields_width=fields_width, context=context)
-
- for field_name in fields_def.keys():
- if field_name in self._columns:
- field = self._columns[field_name]
- elif field_name in self._inherit_fields:
- field = self._inherit_fields[field_name][2]
- else:
- continue
- for depend in field.depends:
- fields_def.setdefault(depend, {'name': depend})
-
- if ('active' in self._columns) or ('active' in self._inherit_fields):
- fields_def.setdefault('active', {'name': 'active', 'select': "2"})
-
- arch = etree.tostring(tree, encoding='utf-8', pretty_print=False)
- fields2 = self.fields_get(cursor, user, fields_def.keys(), context)
- for field in fields_def:
- if field in fields2:
- fields2[field].update(fields_def[field])
- return arch, fields2
-
- def fields_view_get(self, cursor, user, view_id=None, view_type='form',
- context=None, toolbar=False, hexmd5=None):
- '''
- Return a dict with keys:
- - arch: the xml description of the view.
- - fields: a dict with the definition of each fields.
- - toolbar: if toolbar is True, a dict with 'print', 'action', 'relate'
- keyword action defintion for the view.
- - md5: the check sum of the above dict that will be used for caching.
- view_id can specify the id of the view, if empty the system
- will select the first view
- view_type specify the type of the view if view_id is empty
- If hexmd5 is fill, the function will return True if the view have the same
- md5 or the dict.
- '''
-
- if context is None:
- context = {}
-
- def _inherit_apply(src, inherit):
-
- def _find(tree, element):
- if element.tag == 'xpath':
- res = tree.xpath(element.get('expr'))
- if res:
- return res[0]
- return None
-
- 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')
-
- result = {'type': view_type, '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]
-
- 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
-
- def _inherit_apply_rec(result, inherit_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',
- (inherit_id, self._name, inherit_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 = _inherit_apply(result, arch)
- #result = _inherit_apply_rec(result, view_id)
- return result
-
- result['arch'] = _inherit_apply_rec(result['arch'], view_id)
-
- result['field_childs'] = sql_res[1] or False
- # otherwise, build some kind of default view
- else:
- if view_type == 'form':
- res = self.fields_get(cursor, user, context=context)
- xml = '''''' \
- '''"
- elif view_type == 'tree':
- field = 'id'
- if self._rec_name in self._columns:
- field = self._rec_name
- xml = '''''' \
- '''''' \
- % (self._description, field)
- elif view_type == 'calendar':
- xml = '''''' \
- '''''' \
- '''''' \
- % (self._description, self._date_name, self._rec_name)
- else:
- xml = ''
- result['type'] = view_type
- result['arch'] = xml
- result['field_childs'] = False
- result['view_id'] = 0
-
- parser = etree.XMLParser(remove_blank_text=True)
- tree = etree.fromstring(result['arch'], parser)
- xarch, xfields = self._view_look_dom_arch(cursor, user, tree,
- result['type'], context=context)
- result['arch'] = xarch
- result['fields'] = xfields
- if toolbar:
- action_obj = self.pool.get('ir.action.keyword')
- prints = action_obj.get_keyword(cursor, user, 'form_print',
- (self._name, 0), context=context)
- actions = action_obj.get_keyword(cursor, user, 'form_action',
- (self._name, 0), context=context)
- relates = action_obj.get_keyword(cursor, user, 'form_relate',
- (self._name, 0), context=context)
- result['toolbar'] = {
- 'print': prints,
- 'action': actions,
- 'relate': relates,
- }
- result['md5'] = md5.new(str(result)).hexdigest()
- if hexmd5 == result['md5']:
- return True
- return result
-
- fields_view_get = Cache('orm.fields_view_get')(fields_view_get)
-
def _where_calc(self, cursor, user, args, active_test=True, context=None):
if context is None:
context = {}