[FIX] website_forum: correct syntax error left during previous forward-port
[odoo/odoo.git] / openerp / osv / orm.py
1 import simplejson
2 from lxml import etree
3
4 from ..exceptions import except_orm
5 from ..models import (
6     MetaModel,
7     BaseModel,
8     Model, TransientModel, AbstractModel,
9
10     MAGIC_COLUMNS,
11     LOG_ACCESS_COLUMNS,
12 )
13
14 # extra definitions for backward compatibility
15 browse_record_list = BaseModel
16
17 class browse_record(object):
18     """ Pseudo-class for testing record instances """
19     class __metaclass__(type):
20         def __instancecheck__(self, inst):
21             return isinstance(inst, BaseModel) and len(inst) <= 1
22
23 class browse_null(object):
24     """ Pseudo-class for testing null instances """
25     class __metaclass__(type):
26         def __instancecheck__(self, inst):
27             return isinstance(inst, BaseModel) and not inst
28
29
30 def transfer_field_to_modifiers(field, modifiers):
31     default_values = {}
32     state_exceptions = {}
33     for attr in ('invisible', 'readonly', 'required'):
34         state_exceptions[attr] = []
35         default_values[attr] = bool(field.get(attr))
36     for state, modifs in (field.get("states",{})).items():
37         for modif in modifs:
38             if default_values[modif[0]] != modif[1]:
39                 state_exceptions[modif[0]].append(state)
40
41     for attr, default_value in default_values.items():
42         if state_exceptions[attr]:
43             modifiers[attr] = [("state", "not in" if default_value else "in", state_exceptions[attr])]
44         else:
45             modifiers[attr] = default_value
46
47
48 # Don't deal with groups, it is done by check_group().
49 # Need the context to evaluate the invisible attribute on tree views.
50 # For non-tree views, the context shouldn't be given.
51 def transfer_node_to_modifiers(node, modifiers, context=None, in_tree_view=False):
52     if node.get('attrs'):
53         modifiers.update(eval(node.get('attrs')))
54
55     if node.get('states'):
56         if 'invisible' in modifiers and isinstance(modifiers['invisible'], list):
57             # TODO combine with AND or OR, use implicit AND for now.
58             modifiers['invisible'].append(('state', 'not in', node.get('states').split(',')))
59         else:
60             modifiers['invisible'] = [('state', 'not in', node.get('states').split(','))]
61
62     for a in ('invisible', 'readonly', 'required'):
63         if node.get(a):
64             v = bool(eval(node.get(a), {'context': context or {}}))
65             if in_tree_view and a == 'invisible':
66                 # Invisible in a tree view has a specific meaning, make it a
67                 # new key in the modifiers attribute.
68                 modifiers['tree_invisible'] = v
69             elif v or (a not in modifiers or not isinstance(modifiers[a], list)):
70                 # Don't set the attribute to False if a dynamic value was
71                 # provided (i.e. a domain from attrs or states).
72                 modifiers[a] = v
73
74
75 def simplify_modifiers(modifiers):
76     for a in ('invisible', 'readonly', 'required'):
77         if a in modifiers and not modifiers[a]:
78             del modifiers[a]
79
80
81 def transfer_modifiers_to_node(modifiers, node):
82     if modifiers:
83         simplify_modifiers(modifiers)
84         node.set('modifiers', simplejson.dumps(modifiers))
85
86 def setup_modifiers(node, field=None, context=None, in_tree_view=False):
87     """ Processes node attributes and field descriptors to generate
88     the ``modifiers`` node attribute and set it on the provided node.
89
90     Alters its first argument in-place.
91
92     :param node: ``field`` node from an OpenERP view
93     :type node: lxml.etree._Element
94     :param dict field: field descriptor corresponding to the provided node
95     :param dict context: execution context used to evaluate node attributes
96     :param bool in_tree_view: triggers the ``tree_invisible`` code
97                               path (separate from ``invisible``): in
98                               tree view there are two levels of
99                               invisibility, cell content (a column is
100                               present but the cell itself is not
101                               displayed) with ``invisible`` and column
102                               invisibility (the whole column is
103                               hidden) with ``tree_invisible``.
104     :returns: nothing
105     """
106     modifiers = {}
107     if field is not None:
108         transfer_field_to_modifiers(field, modifiers)
109     transfer_node_to_modifiers(
110         node, modifiers, context=context, in_tree_view=in_tree_view)
111     transfer_modifiers_to_node(modifiers, node)
112
113 def test_modifiers(what, expected):
114     modifiers = {}
115     if isinstance(what, basestring):
116         node = etree.fromstring(what)
117         transfer_node_to_modifiers(node, modifiers)
118         simplify_modifiers(modifiers)
119         json = simplejson.dumps(modifiers)
120         assert json == expected, "%s != %s" % (json, expected)
121     elif isinstance(what, dict):
122         transfer_field_to_modifiers(what, modifiers)
123         simplify_modifiers(modifiers)
124         json = simplejson.dumps(modifiers)
125         assert json == expected, "%s != %s" % (json, expected)
126
127
128 # To use this test:
129 # import openerp
130 # openerp.osv.orm.modifiers_tests()
131 def modifiers_tests():
132     test_modifiers('<field name="a"/>', '{}')
133     test_modifiers('<field name="a" invisible="1"/>', '{"invisible": true}')
134     test_modifiers('<field name="a" readonly="1"/>', '{"readonly": true}')
135     test_modifiers('<field name="a" required="1"/>', '{"required": true}')
136     test_modifiers('<field name="a" invisible="0"/>', '{}')
137     test_modifiers('<field name="a" readonly="0"/>', '{}')
138     test_modifiers('<field name="a" required="0"/>', '{}')
139     test_modifiers('<field name="a" invisible="1" required="1"/>', '{"invisible": true, "required": true}') # TODO order is not guaranteed
140     test_modifiers('<field name="a" invisible="1" required="0"/>', '{"invisible": true}')
141     test_modifiers('<field name="a" invisible="0" required="1"/>', '{"required": true}')
142     test_modifiers("""<field name="a" attrs="{'invisible': [('b', '=', 'c')]}"/>""", '{"invisible": [["b", "=", "c"]]}')
143
144     # The dictionary is supposed to be the result of fields_get().
145     test_modifiers({}, '{}')
146     test_modifiers({"invisible": True}, '{"invisible": true}')
147     test_modifiers({"invisible": False}, '{}')