[IMP] Added missing vim mode lines
[odoo/odoo.git] / openerp / addons / base / ir / ir_values.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from osv import osv,fields
23 from osv.orm import except_orm
24 import pickle
25 from tools.translate import _
26
27 EXCLUDED_FIELDS = set((
28     'report_sxw_content', 'report_rml_content', 'report_sxw', 'report_rml',
29     'report_sxw_content_data', 'report_rml_content_data', 'search_view', ))
30
31 #: Possible slots to bind an action to with :meth:`~.set_action`
32 ACTION_SLOTS = [
33                 "client_action_multi",  # sidebar wizard action
34                 "client_print_multi",   # sidebar report printing button
35                 "client_action_relate", # sidebar related link
36                 "tree_but_open",        # double-click on item in tree view
37                 "tree_but_action",      # deprecated: same as tree_but_open
38                ]
39
40
41 class ir_values(osv.osv):
42     """Holds internal model-specific action bindings and user-defined default
43        field values. definitions. This is a legacy internal model, mixing
44        two different concepts, and will likely be updated or replaced in a
45        future version by cleaner, separate models. You should not depend
46        explicitly on it.
47
48        The purpose of each ``ir.values`` entry depends on its type, defined
49        by the ``key`` column:
50
51         * 'default': user-defined default values, used when creating new
52           records of this model:
53         * 'action': binding of an action to a particular *action slot* of
54           this model, making the action easily available in the user
55           interface for this model.
56
57        The ``key2`` column acts as a qualifier, further refining the type
58        of the entry. The possible values are:
59
60         * for 'default' entries: an optional condition restricting the
61           cases where this particular default value will be applicable,
62           or ``False`` for no condition
63         * for 'action' entries: the ``key2`` qualifier is one of the available
64           action slots, defining how this action can be invoked:
65
66             * ``'client_print_multi'`` for report printing actions that will
67               be available on views displaying items from this model
68             * ``'client_action_multi'`` for assistants (wizards) actions
69               that will be available in views displaying objects of this model
70             * ``'client_action_relate'`` for links towards related documents
71               that should be available in views displaying objects of this model
72             * ``'tree_but_open'`` for actions that will be triggered when
73               double-clicking an item from this model in a hierarchical tree view
74
75        Each entry is specific to a model (``model`` column), and for ``'actions'``
76        type, may even be made specific to a given record of that model when the
77        ``res_id`` column contains a record ID (``False`` means it's global for
78        all records).
79
80        The content of the entry is defined by the ``value`` column, which may either
81        contain an arbitrary value, or a reference string defining the action that
82        should be executed.
83
84        .. rubric:: Usage: default values
85        
86        The ``'default'`` entries are usually defined manually by the
87        users, and set by their UI clients calling :meth:`~.set_default`.
88        These default values are then automatically used by the
89        ORM every time a new record is about to be created, i.e. when
90        :meth:`~openerp.osv.osv.osv.default_get`
91        or :meth:`~openerp.osv.osv.osv.create` are called.
92
93        .. rubric:: Usage: action bindings
94
95        Business applications will usually bind their actions during
96        installation, and OpenERP UI clients will apply them as defined,
97        based on the list of actions included in the result of
98        :meth:`~openerp.osv.osv.osv.fields_view_get`,
99        or directly returned by explicit calls to :meth:`~.get_actions`.
100     """
101     _name = 'ir.values'
102
103     def _value_unpickle(self, cursor, user, ids, name, arg, context=None):
104         res = {}
105         for record in self.browse(cursor, user, ids, context=context):
106             value = record[name[:-9]]
107             if record.key == 'default' and value:
108                 # default values are pickled on the fly
109                 try:
110                     value = str(pickle.loads(value))
111                 except Exception:
112                     pass
113             res[record.id] = value
114         return res
115
116     def _value_pickle(self, cursor, user, id, name, value, arg, context=None):
117         if context is None:
118             context = {}
119         ctx = context.copy()
120         if self.CONCURRENCY_CHECK_FIELD in ctx:
121             del ctx[self.CONCURRENCY_CHECK_FIELD]
122         record = self.browse(cursor, user, id, context=context)
123         if record.key == 'default':
124             # default values are pickled on the fly
125             value = pickle.dumps(value)
126         self.write(cursor, user, id, {name[:-9]: value}, context=ctx)
127
128     def onchange_object_id(self, cr, uid, ids, object_id, context=None):
129         if not object_id: return {}
130         act = self.pool.get('ir.model').browse(cr, uid, object_id, context=context)
131         return {
132                 'value': {'model': act.model}
133         }
134
135     def onchange_action_id(self, cr, uid, ids, action_id, context=None):
136         if not action_id: return {}
137         act = self.pool.get('ir.actions.actions').browse(cr, uid, action_id, context=context)
138         return {
139                 'value': {'value_unpickle': act.type+','+str(act.id)}
140         }
141
142     _columns = {
143         'name': fields.char('Name', size=128, required=True),
144         'model': fields.char('Model Name', size=128, select=True, required=True,
145                              help="Model to which this entry applies"),
146
147         # TODO: model_id and action_id should be read-write function fields
148         'model_id': fields.many2one('ir.model', 'Model (change only)', size=128,
149                                     help="Model to which this entry applies - "
150                                          "helper field for setting a model, will "
151                                          "automatically set the correct model name"),
152         'action_id': fields.many2one('ir.actions.actions', 'Action (change only)',
153                                      help="Action bound to this entry - "
154                                          "helper field for binding an action, will "
155                                          "automatically set the correct reference"),
156
157         'value': fields.text('Value', help="Default value (pickled) or reference to an action"),
158         'value_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle,
159                                           type='text',
160                                           string='Default value or action reference'),
161         'key': fields.selection([('action','Action'),('default','Default')],
162                                 'Type', size=128, select=True, required=True,
163                                 help="- Action: an action attached to one slot of the given model\n"
164                                      "- Default: a default value for a model field"),
165         'key2' : fields.char('Qualifier', size=128, select=True,
166                              help="For actions, one of the possible action slots: \n"
167                                   "  - client_action_multi\n"
168                                   "  - client_print_multi\n"
169                                   "  - client_action_relate\n"
170                                   "  - tree_but_open\n"
171                                   "For defaults, an optional condition"
172                              ,),
173         'res_id': fields.integer('Record ID', select=True,
174                                  help="Database identifier of the record to which this applies. "
175                                       "0 = for all records"),
176         'user_id': fields.many2one('res.users', 'User', ondelete='cascade', select=True,
177                                    help="If set, action binding only applies for this user."),
178         'company_id': fields.many2one('res.company', 'Company', ondelete='cascade', select=True,
179                                       help="If set, action binding only applies for this company")
180     }
181     _defaults = {
182         'key': 'action',
183         'key2': 'tree_but_open',
184     }
185
186     def _auto_init(self, cr, context=None):
187         super(ir_values, self)._auto_init(cr, context)
188         cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'ir_values_key_model_key2_res_id_user_id_idx\'')
189         if not cr.fetchone():
190             cr.execute('CREATE INDEX ir_values_key_model_key2_res_id_user_id_idx ON ir_values (key, model, key2, res_id, user_id)')
191
192     def set_default(self, cr, uid, model, field_name, value, for_all_users=True, company_id=False, condition=False):
193         """Defines a default value for the given model and field_name. Any previous
194            default for the same scope (model, field_name, value, for_all_users, company_id, condition)
195            will be replaced and lost in the process.
196
197            Defaults can be later retrieved via :meth:`~.get_defaults`, which will return
198            the highest priority default for any given field. Defaults that are more specific
199            have a higher priority, in the following order (highest to lowest):
200
201                * specific to user and company
202                * specific to user only
203                * specific to company only
204                * global to everyone
205
206            :param string model: model name
207            :param string field_name: field name to which the default applies
208            :param value: the default field value to set
209            :type value: any serializable Python value
210            :param bool for_all_users: whether the default should apply to everybody or only
211                                       the user calling the method
212            :param int company_id: optional ID of the company to which the default should
213                                   apply. If omitted, the default will be global. If True
214                                   is passed, the current user's company will be used.
215            :param string condition: optional condition specification that can be used to
216                                     restrict the applicability of the default values
217                                     (e.g. based on another field's value). This is an
218                                     opaque string as far as the API is concerned, but client
219                                     stacks typically use single-field conditions in the
220                                     form ``'key=stringified_value'``.
221                                     (Currently, the condition is trimmed to 200 characters,
222                                     so values that share the same first 200 characters always
223                                     match)
224            :return: id of the newly created ir.values entry
225         """
226         if isinstance(value, unicode):
227             value = value.encode('utf8')
228         if company_id is True:
229             # should be company-specific, need to get company id
230             user = self.pool.get('res.users').browse(cr, uid, uid)
231             company_id = user.company_id.id
232
233         # remove existing defaults for the same scope
234         search_criteria = [
235             ('key', '=', 'default'),
236             ('key2', '=', condition and condition[:200]),
237             ('model', '=', model),
238             ('name', '=', field_name),
239             ('user_id', '=', False if for_all_users else uid),
240             ('company_id','=', company_id)
241             ]
242         self.unlink(cr, uid, self.search(cr, uid, search_criteria))
243
244         return self.create(cr, uid, {
245             'name': field_name,
246             'value': pickle.dumps(value),
247             'model': model,
248             'key': 'default',
249             'key2': condition and condition[:200],
250             'user_id': False if for_all_users else uid,
251             'company_id': company_id,
252         })
253
254     def get_defaults(self, cr, uid, model, condition=False):
255         """Returns any default values that are defined for the current model and user,
256            (and match ``condition``, if specified), previously registered via
257            :meth:`~.set_default`.
258
259            Defaults are global to a model, not field-specific, but an optional
260            ``condition`` can be provided to restrict matching default values
261            to those that were defined for the same condition (usually based
262            on another field's value).
263
264            Default values also have priorities depending on whom they apply
265            to: only the highest priority value will be returned for any
266            field. See :meth:`~.set_default` for more details.
267
268            :param string model: model name
269            :param string condition: optional condition specification that can be used to
270                                     restrict the applicability of the default values
271                                     (e.g. based on another field's value). This is an
272                                     opaque string as far as the API is concerned, but client
273                                     stacks typically use single-field conditions in the
274                                     form ``'key=stringified_value'``.
275                                     (Currently, the condition is trimmed to 200 characters,
276                                     so values that share the same first 200 characters always
277                                     match)
278            :return: list of default values tuples of the form ``(id, field_name, value)``
279                     (``id`` is the ID of the default entry, usually irrelevant)
280         """
281         # use a direct SQL query for performance reasons,
282         # this is called very often
283         query = """SELECT v.id, v.name, v.value FROM ir_values v
284                       LEFT JOIN res_users u ON (v.user_id = u.id)
285                    WHERE v.key = %%s AND v.model = %%s
286                       AND (v.user_id = %%s OR v.user_id IS NULL)
287                       AND (v.company_id IS NULL OR
288                            v.company_id =
289                              (SELECT company_id from res_users where id = %%s)
290                           )
291                       %s
292                    ORDER BY v.user_id, u.company_id"""
293         query = query % ('AND v.key2 = %s' if condition else '')
294         params = ('default', model, uid, uid)
295         if condition:
296             params += (condition[:200],)
297         cr.execute(query, params)
298
299         # keep only the highest priority default for each field
300         defaults = {}
301         for row in cr.dictfetchall():
302             defaults.setdefault(row['name'],
303                 (row['id'], row['name'], pickle.loads(row['value'].encode('utf-8'))))
304         return defaults.values()
305
306     def set_action(self, cr, uid, name, action_slot, model, action, res_id=False):
307         """Binds an the given action to the given model's action slot - for later
308            retrieval via :meth:`~.get_actions`. Any existing binding of the same action
309            to the same slot is first removed, allowing an update of the action's name.
310            See the class description for more details about the various action
311            slots: :class:`~ir_values`.
312
313            :param string name: action label, usually displayed by UI client
314            :param string action_slot: the action slot to which the action should be
315                                       bound to - one of ``client_action_multi``,
316                                       ``client_print_multi``, ``client_action_relate``,
317                                       ``tree_but_open``.
318            :param string model: model name
319            :param string action: action reference, in the form ``'model,id'``
320            :param int res_id: optional record id - will bind the action only to a
321                               specific record of the model, not all records.
322            :return: id of the newly created ir.values entry
323         """
324         assert isinstance(action, basestring) and ',' in action, \
325                'Action definition must be an action reference, e.g. "ir.actions.act_window,42"'
326         assert action_slot in ACTION_SLOTS, \
327                'Action slot (%s) must be one of: %r' % (action_slot, ACTION_SLOTS)
328
329         # remove existing action definition of same slot and value
330         search_criteria = [
331             ('key', '=', 'action'),
332             ('key2', '=', action_slot),
333             ('model', '=', model),
334             ('res_id', '=', res_id or 0), # int field -> NULL == 0
335             ('value', '=', action),
336             ]
337         self.unlink(cr, uid, self.search(cr, uid, search_criteria))
338
339         return self.create(cr, uid, {
340             'key': 'action',
341             'key2': action_slot,
342             'model': model,
343             'res_id': res_id,
344             'name': name,
345             'value': action,
346         })
347
348     def get_actions(self, cr, uid, action_slot, model, res_id=False, context=None):
349         """Retrieves the list of actions bound to the given model's action slot.
350            See the class description for more details about the various action
351            slots: :class:`~.ir_values`.
352
353            :param string action_slot: the action slot to which the actions should be
354                                       bound to - one of ``client_action_multi``,
355                                       ``client_print_multi``, ``client_action_relate``,
356                                       ``tree_but_open``.
357            :param string model: model name
358            :param int res_id: optional record id - will bind the action only to a
359                               specific record of the model, not all records.
360            :return: list of action tuples of the form ``(id, name, action_def)``,
361                     where ``id`` is the ID of the default entry, ``name`` is the
362                     action label, and ``action_def`` is a dict containing the
363                     action definition as obtained by calling
364                     :meth:`~openerp.osv.osv.osv.read` on the action record.
365         """
366         assert action_slot in ACTION_SLOTS, 'Illegal action slot value: %s' % action_slot
367         # use a direct SQL query for performance reasons,
368         # this is called very often
369         query = """SELECT v.id, v.name, v.value FROM ir_values v
370                    WHERE v.key = %s AND v.key2 = %s
371                         AND v.model = %s
372                         AND (v.res_id = %s
373                              OR v.res_id IS NULL
374                              OR v.res_id = 0)
375                    ORDER BY v.id"""
376         cr.execute(query, ('action', action_slot, model, res_id or None))
377         results = {}
378         for action in cr.dictfetchall():
379             action_model,id = action['value'].split(',')
380             fields = [
381                     field
382                     for field in self.pool.get(action_model)._all_columns
383                     if field not in EXCLUDED_FIELDS]
384             # FIXME: needs cleanup
385             try:
386                 action_def = self.pool.get(action_model).read(cr, uid, int(id), fields, context)
387                 if action_def:
388                     if action_model in ('ir.actions.report.xml','ir.actions.act_window',
389                                         'ir.actions.wizard'):
390                         groups = action_def.get('groups_id')
391                         if groups:
392                             cr.execute('SELECT 1 FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',
393                                        (tuple(groups), uid))
394                             if not cr.fetchone():
395                                 if action['name'] == 'Menuitem':
396                                     raise osv.except_osv('Error !',
397                                                          'You do not have the permission to perform this operation !!!')
398                                 continue
399                 # keep only the first action registered for each action name
400                 results[action['name']] = (action['id'], action['name'], action_def)
401             except except_orm, e:
402                 continue
403         return results.values()
404
405     def _map_legacy_model_list(self, model_list, map_fn, merge_results=False):
406         """Apply map_fn to the various models passed, according to
407            legacy way to specify models/records.
408         """
409         assert isinstance(model_list, (list, tuple)), \
410             "model_list should be in the form [model,..] or [(model,res_id), ..]"
411         results = []
412         for model in model_list:
413             res_id = False
414             if isinstance(model, (list, tuple)):
415                 model, res_id = model
416             result = map_fn(model, res_id)
417             # some of the functions return one result at a time (tuple or id)
418             # and some return a list of many of them - care for both
419             if merge_results:
420                 results.extend(result)
421             else:
422                 results.append(result)
423         return results
424
425     # Backards-compatibility adapter layer to retrofit into split API
426     def set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=False, preserve_user=False, company=False):
427         """Deprecated legacy method to set default values and bind actions to models' action slots.
428            Now dispatches to the newer API methods according to the value of ``key``: :meth:`~.set_default`
429            (``key=='default'``) or :meth:`~.set_action` (``key == 'action'``).
430
431           :deprecated: As of v6.1, ``set_default()`` or ``set_action()`` should be used directly.
432         """
433         assert key in ['default', 'action'], "ir.values entry keys must be in ['default','action']"
434         if key == 'default':
435             def do_set(model,res_id):
436                 return self.set_default(cr, uid, model, field_name=name, value=value,
437                                         for_all_users=(not preserve_user), company_id=company,
438                                         condition=key2)
439         elif key == 'action':
440             def do_set(model,res_id):
441                 return self.set_action(cr, uid, name, action_slot=key2, model=model, action=value, res_id=res_id)
442         return self._map_legacy_model_list(models, do_set)
443
444     def get(self, cr, uid, key, key2, models, meta=False, context=None, res_id_req=False, without_user=True, key2_req=True):
445         """Deprecated legacy method to get the list of default values or actions bound to models' action slots.
446            Now dispatches to the newer API methods according to the value of ``key``: :meth:`~.get_defaults`
447            (``key=='default'``) or :meth:`~.get_actions` (``key == 'action'``)
448
449           :deprecated: As of v6.1, ``get_defaults()`` or ``get_actions()`` should be used directly.
450
451         """
452         assert key in ['default', 'action'], "ir.values entry keys must be in ['default','action']"
453         if key == 'default':
454             def do_get(model,res_id):
455                 return self.get_defaults(cr, uid, model, condition=key2)
456         elif key == 'action':
457             def do_get(model,res_id):
458                 return self.get_actions(cr, uid, action_slot=key2, model=model, res_id=res_id, context=context)
459         return self._map_legacy_model_list(models, do_get, merge_results=True)
460
461 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: