[REVERT] re-apply the 4 commits that were reverted just after, 4 commits ago.

bzr revid: vmt@openerp.com-20110705122222-bhim9uft3l3sqee8
This commit is contained in:
Vo Minh Thu 2011-07-05 14:22:22 +02:00
parent dae2957394
commit a13109ea93
1 changed files with 131 additions and 8 deletions

View File

@ -52,6 +52,7 @@ import re
import time import time
import traceback import traceback
import types import types
import simplejson
import openerp.netsvc as netsvc import openerp.netsvc as netsvc
from lxml import etree from lxml import etree
@ -75,6 +76,88 @@ module_class_list = {}
# Super-user identifier (aka Administrator aka root) # Super-user identifier (aka Administrator aka root)
ROOT_USER_ID = 1 ROOT_USER_ID = 1
def transfer_field_to_modifiers(field, modifiers):
for a in ('invisible', 'readonly', 'required'):
if field.get(a):
modifiers[a] = bool(field.get(a))
# Don't deal with groups, it is done by check_group().
# Need the context to evaluate the invisible attribute on tree views.
# For non-tree views, the context shouldn't be given.
def transfer_node_to_modifiers(node, modifiers, context=None, in_tree_view=False):
if node.get('attrs'):
modifiers.update(eval(node.get('attrs')))
if node.get('states'):
if 'invisible' in modifiers and isinstance(modifiers['invisible'], list):
# TODO combine with AND or OR, use implicit AND for now.
modifiers['invisible'].append(('state', 'not in', node.get('states').split(',')))
else:
modifiers['invisible'] = [('state', 'not in', node.get('states').split(','))]
for a in ('invisible', 'readonly', 'required'):
if node.get(a):
v = bool(eval(node.get(a), {'context': context or {}}))
if in_tree_view and a == 'invisible':
# Invisible in a tree view has a specific meaning, make it a
# new key in the modifiers attribute.
modifiers['tree_invisible'] = v
elif v or (a not in modifiers or not isinstance(modifiers[a], list)):
# Don't set the attribute to False if a dynamic value was
# provided (i.e. a domain from attrs or states).
modifiers[a] = v
def simplify_modifiers(modifiers):
for a in ('invisible', 'readonly', 'required'):
if a in modifiers and not modifiers[a]:
del modifiers[a]
def transfer_modifiers_to_node(modifiers, node):
if modifiers:
simplify_modifiers(modifiers)
node.set('modifiers', simplejson.dumps(modifiers))
def test_modifiers(what, expected):
modifiers = {}
if isinstance(what, basestring):
node = etree.fromstring(what)
transfer_node_to_modifiers(node, modifiers)
simplify_modifiers(modifiers)
json = simplejson.dumps(modifiers)
assert json == expected, "%s != %s" % (json, expected)
elif isinstance(what, dict):
transfer_field_to_modifiers(what, modifiers)
simplify_modifiers(modifiers)
json = simplejson.dumps(modifiers)
assert json == expected, "%s != %s" % (json, expected)
# To use this test:
# import openerp
# openerp.osv.orm.modifiers_tests()
def modifiers_tests():
test_modifiers('<field name="a"/>', '{}')
test_modifiers('<field name="a" invisible="1"/>', '{"invisible": true}')
test_modifiers('<field name="a" readonly="1"/>', '{"readonly": true}')
test_modifiers('<field name="a" required="1"/>', '{"required": true}')
test_modifiers('<field name="a" invisible="0"/>', '{}')
test_modifiers('<field name="a" readonly="0"/>', '{}')
test_modifiers('<field name="a" required="0"/>', '{}')
test_modifiers('<field name="a" invisible="1" required="1"/>', '{"invisible": true, "required": true}') # TODO order is not guaranteed
test_modifiers('<field name="a" invisible="1" required="0"/>', '{"invisible": true}')
test_modifiers('<field name="a" invisible="0" required="1"/>', '{"required": true}')
test_modifiers("""<field name="a" attrs="{'invisible': [('b', '=', 'c')]}"/>""", '{"invisible": [["b", "=", "c"]]}')
# The dictionary is supposed to be the result of fields_get().
test_modifiers({}, '{}')
test_modifiers({"invisible": True}, '{"invisible": true}')
test_modifiers({"invisible": False}, '{}')
def check_object_name(name): def check_object_name(name):
""" Check if the given name is a valid openerp object name. """ Check if the given name is a valid openerp object name.
@ -1346,13 +1429,26 @@ class orm_template(object):
def view_header_get(self, cr, user, view_id=None, view_type='form', context=None): def view_header_get(self, cr, user, view_id=None, view_type='form', context=None):
return False return False
def __view_look_dom(self, cr, user, node, view_id, context=None): def __view_look_dom(self, cr, user, node, view_id, in_tree_view, model_fields, context=None):
""" Return the description of the fields in the node.
In a normal call to this method, node is a complete view architecture
but it is actually possible to give some sub-node (this is used so
that the method can call itself recursively).
Originally, the field descriptions are drawn from the node itself.
But there is now some code calling fields_get() in order to merge some
of those information in the architecture.
"""
if context is None: if context is None:
context = {} context = {}
result = False result = False
fields = {} fields = {}
children = True children = True
modifiers = {}
def encode(s): def encode(s):
if isinstance(s, unicode): if isinstance(s, unicode):
return s.encode('utf8') return s.encode('utf8')
@ -1366,6 +1462,7 @@ class orm_template(object):
can_see = any(access_pool.check_groups(cr, user, group) for group in groups) can_see = any(access_pool.check_groups(cr, user, group) for group in groups)
if not can_see: if not can_see:
node.set('invisible', '1') node.set('invisible', '1')
modifiers['invisible'] = True
if 'attrs' in node.attrib: if 'attrs' in node.attrib:
del(node.attrib['attrs']) #avoid making field visible later del(node.attrib['attrs']) #avoid making field visible later
del(node.attrib['groups']) del(node.attrib['groups'])
@ -1437,10 +1534,14 @@ class orm_template(object):
attrs['selection'].append((False, '')) attrs['selection'].append((False, ''))
fields[node.get('name')] = attrs fields[node.get('name')] = attrs
field = model_fields[node.get('name')]
transfer_field_to_modifiers(field, modifiers)
elif node.tag in ('form', 'tree'): elif node.tag in ('form', 'tree'):
result = self.view_header_get(cr, user, False, node.tag, context) result = self.view_header_get(cr, user, False, node.tag, context)
if result: if result:
node.set('string', result) node.set('string', result)
in_tree_view = node.tag == 'tree'
elif node.tag == 'calendar': elif node.tag == 'calendar':
for additional_field in ('date_start', 'date_delay', 'date_stop', 'color'): for additional_field in ('date_start', 'date_delay', 'date_stop', 'color'):
@ -1449,6 +1550,12 @@ class orm_template(object):
check_group(node) check_group(node)
# The view architeture overrides the python model.
# Get the attrs before they are (possibly) deleted by check_group below
transfer_node_to_modifiers(node, modifiers, context, in_tree_view)
# TODO remove attrs couterpart in modifiers when invisible is true ?
# translate view # translate view
if 'lang' in context: if 'lang' in context:
if node.get('string') and not result: if node.get('string') and not result:
@ -1474,8 +1581,9 @@ class orm_template(object):
for f in node: for f in node:
if children or (node.tag == 'field' and f.tag in ('filter','separator')): if children or (node.tag == 'field' and f.tag in ('filter','separator')):
fields.update(self.__view_look_dom(cr, user, f, view_id, context)) fields.update(self.__view_look_dom(cr, user, f, view_id, in_tree_view, model_fields, context))
transfer_modifiers_to_node(modifiers, node)
return fields return fields
def _disable_workflow_buttons(self, cr, user, node): def _disable_workflow_buttons(self, cr, user, node):
@ -1504,19 +1612,32 @@ class orm_template(object):
return node return node
def __view_look_dom_arch(self, cr, user, node, view_id, context=None): def __view_look_dom_arch(self, cr, user, node, view_id, context=None):
fields_def = self.__view_look_dom(cr, user, node, view_id, context=context) """ Return an architecture and a description of all the fields.
node = self._disable_workflow_buttons(cr, user, node)
arch = etree.tostring(node, encoding="utf-8").replace('\t', '') The field description combines the result of fields_get() and
__view_look_dom().
:param node: the architecture as as an etree
:return: a tuple (arch, fields) where arch is the given node as a
string and fields is the description of all the fields.
"""
fields = {} fields = {}
if node.tag == 'diagram': if node.tag == 'diagram':
if node.getchildren()[0].tag == 'node': if node.getchildren()[0].tag == 'node':
node_fields = self.pool.get(node.getchildren()[0].get('object')).fields_get(cr, user, fields_def.keys(), context) node_fields = self.pool.get(node.getchildren()[0].get('object')).fields_get(cr, user, None, context)
fields.update(node_fields) fields.update(node_fields)
if node.getchildren()[1].tag == 'arrow': if node.getchildren()[1].tag == 'arrow':
arrow_fields = self.pool.get(node.getchildren()[1].get('object')).fields_get(cr, user, fields_def.keys(), context) arrow_fields = self.pool.get(node.getchildren()[1].get('object')).fields_get(cr, user, None, context)
fields.update(arrow_fields) fields.update(arrow_fields)
else: else:
fields = self.fields_get(cr, user, fields_def.keys(), context) fields = self.fields_get(cr, user, None, context)
fields_def = self.__view_look_dom(cr, user, node, view_id, False, fields, context=context)
node = self._disable_workflow_buttons(cr, user, node)
arch = etree.tostring(node, encoding="utf-8").replace('\t', '')
for k in fields.keys():
if k not in fields_def:
del fields[k]
for field in fields_def: for field in fields_def:
if field == 'id': if field == 'id':
# sometime, the view may contain the (invisible) field 'id' needed for a domain (when 2 objects have cross references) # sometime, the view may contain the (invisible) field 'id' needed for a domain (when 2 objects have cross references)
@ -1537,6 +1658,7 @@ class orm_template(object):
def __get_default_calendar_view(self): def __get_default_calendar_view(self):
"""Generate a default calendar view (For internal use only). """Generate a default calendar view (For internal use only).
""" """
# TODO could return an etree instead of a string
arch = ('<?xml version="1.0" encoding="utf-8"?>\n' arch = ('<?xml version="1.0" encoding="utf-8"?>\n'
'<calendar string="%s"') % (self._description) '<calendar string="%s"') % (self._description)
@ -1604,6 +1726,7 @@ class orm_template(object):
for field_name in fields_to_search: for field_name in fields_to_search:
field_group.append(etree.Element("field", attrib={'name': field_name})) field_group.append(etree.Element("field", attrib={'name': field_name}))
#TODO tostring can be removed as fromstring is call directly after...
return etree.tostring(search_view, encoding="utf-8").replace('\t', '') return etree.tostring(search_view, encoding="utf-8").replace('\t', '')
# #