1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
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.
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.
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/>.
20 ##############################################################################
23 from openerp import tools
24 from openerp.osv import osv, fields
25 from openerp.osv.orm import except_orm
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', ))
31 #: Possible slots to bind an action to with :meth:`~.set_action`
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
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
48 The purpose of each ``ir.values`` entry depends on its type, defined
49 by the ``key`` column:
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.
57 The ``key2`` column acts as a qualifier, further refining the type
58 of the entry. The possible values are:
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:
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
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
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
84 .. rubric:: Usage: default values
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.
93 .. rubric:: Usage: action bindings
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`.
103 def _value_unpickle(self, cursor, user, ids, name, arg, context=None):
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
110 value = str(pickle.loads(value))
113 res[record.id] = value
116 def _value_pickle(self, cursor, user, id, name, value, arg, context=None):
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)
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)
132 'value': {'model': act.model}
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)
139 'value': {'value_unpickle': act.type+','+str(act.id)}
143 'name': fields.char('Name', required=True),
144 'model': fields.char('Model Name', select=True, required=True,
145 help="Model to which this entry applies"),
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"),
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,
160 string='Default value or action reference'),
161 'key': fields.selection([('action','Action'),('default','Default')],
162 'Type', 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', 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"
171 "For defaults, an optional condition"
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")
183 'key2': 'tree_but_open',
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)')
192 def create(self, cr, uid, vals, context=None):
193 res = super(ir_values, self).create(cr, uid, vals, context=context)
194 self.get_defaults_dict.clear_cache(self)
197 def write(self, cr, uid, ids, vals, context=None):
198 res = super(ir_values, self).write(cr, uid, ids, vals, context=context)
199 self.get_defaults_dict.clear_cache(self)
202 def unlink(self, cr, uid, ids, context=None):
203 res = super(ir_values, self).unlink(cr, uid, ids, context=context)
204 self.get_defaults_dict.clear_cache(self)
207 def set_default(self, cr, uid, model, field_name, value, for_all_users=True, company_id=False, condition=False):
208 """Defines a default value for the given model and field_name. Any previous
209 default for the same scope (model, field_name, value, for_all_users, company_id, condition)
210 will be replaced and lost in the process.
212 Defaults can be later retrieved via :meth:`~.get_defaults`, which will return
213 the highest priority default for any given field. Defaults that are more specific
214 have a higher priority, in the following order (highest to lowest):
216 * specific to user and company
217 * specific to user only
218 * specific to company only
221 :param string model: model name
222 :param string field_name: field name to which the default applies
223 :param value: the default field value to set
224 :type value: any serializable Python value
225 :param bool for_all_users: whether the default should apply to everybody or only
226 the user calling the method
227 :param int company_id: optional ID of the company to which the default should
228 apply. If omitted, the default will be global. If True
229 is passed, the current user's company will be used.
230 :param string condition: optional condition specification that can be used to
231 restrict the applicability of the default values
232 (e.g. based on another field's value). This is an
233 opaque string as far as the API is concerned, but client
234 stacks typically use single-field conditions in the
235 form ``'key=stringified_value'``.
236 (Currently, the condition is trimmed to 200 characters,
237 so values that share the same first 200 characters always
239 :return: id of the newly created ir.values entry
241 if isinstance(value, unicode):
242 value = value.encode('utf8')
243 if company_id is True:
244 # should be company-specific, need to get company id
245 user = self.pool.get('res.users').browse(cr, uid, uid)
246 company_id = user.company_id.id
248 # remove existing defaults for the same scope
250 ('key', '=', 'default'),
251 ('key2', '=', condition and condition[:200]),
252 ('model', '=', model),
253 ('name', '=', field_name),
254 ('user_id', '=', False if for_all_users else uid),
255 ('company_id','=', company_id)
257 self.unlink(cr, uid, self.search(cr, uid, search_criteria))
259 return self.create(cr, uid, {
261 'value': pickle.dumps(value),
264 'key2': condition and condition[:200],
265 'user_id': False if for_all_users else uid,
266 'company_id': company_id,
269 def get_default(self, cr, uid, model, field_name, for_all_users=True, company_id=False, condition=False):
270 """ Return the default value defined for model, field_name, users, company and condition.
271 Return ``None`` if no such default exists.
274 ('key', '=', 'default'),
275 ('key2', '=', condition and condition[:200]),
276 ('model', '=', model),
277 ('name', '=', field_name),
278 ('user_id', '=', False if for_all_users else uid),
279 ('company_id','=', company_id)
281 defaults = self.browse(cr, uid, self.search(cr, uid, search_criteria))
282 return pickle.loads(defaults[0].value.encode('utf-8')) if defaults else None
284 def get_defaults(self, cr, uid, model, condition=False):
285 """Returns any default values that are defined for the current model and user,
286 (and match ``condition``, if specified), previously registered via
287 :meth:`~.set_default`.
289 Defaults are global to a model, not field-specific, but an optional
290 ``condition`` can be provided to restrict matching default values
291 to those that were defined for the same condition (usually based
292 on another field's value).
294 Default values also have priorities depending on whom they apply
295 to: only the highest priority value will be returned for any
296 field. See :meth:`~.set_default` for more details.
298 :param string model: model name
299 :param string condition: optional condition specification that can be used to
300 restrict the applicability of the default values
301 (e.g. based on another field's value). This is an
302 opaque string as far as the API is concerned, but client
303 stacks typically use single-field conditions in the
304 form ``'key=stringified_value'``.
305 (Currently, the condition is trimmed to 200 characters,
306 so values that share the same first 200 characters always
308 :return: list of default values tuples of the form ``(id, field_name, value)``
309 (``id`` is the ID of the default entry, usually irrelevant)
311 # use a direct SQL query for performance reasons,
312 # this is called very often
313 query = """SELECT v.id, v.name, v.value FROM ir_values v
314 LEFT JOIN res_users u ON (v.user_id = u.id)
315 WHERE v.key = %%s AND v.model = %%s
316 AND (v.user_id = %%s OR v.user_id IS NULL)
317 AND (v.company_id IS NULL OR
319 (SELECT company_id from res_users where id = %%s)
322 ORDER BY v.user_id, u.company_id"""
323 params = ('default', model, uid, uid)
325 query %= 'AND v.key2 = %s'
326 params += (condition[:200],)
328 query %= 'AND v.key2 is NULL'
329 cr.execute(query, params)
331 # keep only the highest priority default for each field
333 for row in cr.dictfetchall():
334 defaults.setdefault(row['name'],
335 (row['id'], row['name'], pickle.loads(row['value'].encode('utf-8'))))
336 return defaults.values()
338 # use ormcache: this is called a lot by BaseModel.default_get()!
339 @tools.ormcache(skiparg=2)
340 def get_defaults_dict(self, cr, uid, model, condition=False):
341 """ Returns a dictionary mapping field names with their corresponding
342 default value. This method simply improves the returned value of
343 :meth:`~.get_defaults`.
345 return dict((f, v) for i, f, v in self.get_defaults(cr, uid, model, condition))
347 def set_action(self, cr, uid, name, action_slot, model, action, res_id=False):
348 """Binds an the given action to the given model's action slot - for later
349 retrieval via :meth:`~.get_actions`. Any existing binding of the same action
350 to the same slot is first removed, allowing an update of the action's name.
351 See the class description for more details about the various action
352 slots: :class:`~ir_values`.
354 :param string name: action label, usually displayed by UI client
355 :param string action_slot: the action slot to which the action should be
356 bound to - one of ``client_action_multi``,
357 ``client_print_multi``, ``client_action_relate``,
359 :param string model: model name
360 :param string action: action reference, in the form ``'model,id'``
361 :param int res_id: optional record id - will bind the action only to a
362 specific record of the model, not all records.
363 :return: id of the newly created ir.values entry
365 assert isinstance(action, basestring) and ',' in action, \
366 'Action definition must be an action reference, e.g. "ir.actions.act_window,42"'
367 assert action_slot in ACTION_SLOTS, \
368 'Action slot (%s) must be one of: %r' % (action_slot, ACTION_SLOTS)
370 # remove existing action definition of same slot and value
372 ('key', '=', 'action'),
373 ('key2', '=', action_slot),
374 ('model', '=', model),
375 ('res_id', '=', res_id or 0), # int field -> NULL == 0
376 ('value', '=', action),
378 self.unlink(cr, uid, self.search(cr, uid, search_criteria))
380 return self.create(cr, uid, {
389 def get_actions(self, cr, uid, action_slot, model, res_id=False, context=None):
390 """Retrieves the list of actions bound to the given model's action slot.
391 See the class description for more details about the various action
392 slots: :class:`~.ir_values`.
394 :param string action_slot: the action slot to which the actions should be
395 bound to - one of ``client_action_multi``,
396 ``client_print_multi``, ``client_action_relate``,
398 :param string model: model name
399 :param int res_id: optional record id - will bind the action only to a
400 specific record of the model, not all records.
401 :return: list of action tuples of the form ``(id, name, action_def)``,
402 where ``id`` is the ID of the default entry, ``name`` is the
403 action label, and ``action_def`` is a dict containing the
404 action definition as obtained by calling
405 :meth:`~openerp.osv.osv.osv.read` on the action record.
407 assert action_slot in ACTION_SLOTS, 'Illegal action slot value: %s' % action_slot
408 # use a direct SQL query for performance reasons,
409 # this is called very often
410 query = """SELECT v.id, v.name, v.value FROM ir_values v
411 WHERE v.key = %s AND v.key2 = %s
417 cr.execute(query, ('action', action_slot, model, res_id or None))
419 for action in cr.dictfetchall():
420 if not action['value']:
421 continue # skip if undefined
422 action_model_name, action_id = action['value'].split(',')
423 if action_model_name not in self.pool:
424 continue # unknow model? skip it
425 action_model = self.pool[action_model_name]
426 fields = [field for field in action_model._fields if field not in EXCLUDED_FIELDS]
427 # FIXME: needs cleanup
429 action_def = action_model.read(cr, uid, int(action_id), fields, context)
431 if action_model_name in ('ir.actions.report.xml', 'ir.actions.act_window'):
432 groups = action_def.get('groups_id')
434 cr.execute('SELECT 1 FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',
435 (tuple(groups), uid))
436 if not cr.fetchone():
437 if action['name'] == 'Menuitem':
438 raise osv.except_osv('Error!',
439 'You do not have the permission to perform this operation!!!')
441 # keep only the first action registered for each action name
442 results[action['name']] = (action['id'], action['name'], action_def)
445 return sorted(results.values())
447 def _map_legacy_model_list(self, model_list, map_fn, merge_results=False):
448 """Apply map_fn to the various models passed, according to
449 legacy way to specify models/records.
451 assert isinstance(model_list, (list, tuple)), \
452 "model_list should be in the form [model,..] or [(model,res_id), ..]"
454 for model in model_list:
456 if isinstance(model, (list, tuple)):
457 model, res_id = model
458 result = map_fn(model, res_id)
459 # some of the functions return one result at a time (tuple or id)
460 # and some return a list of many of them - care for both
462 results.extend(result)
464 results.append(result)
467 # Backards-compatibility adapter layer to retrofit into split API
468 def set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=False, preserve_user=False, company=False):
469 """Deprecated legacy method to set default values and bind actions to models' action slots.
470 Now dispatches to the newer API methods according to the value of ``key``: :meth:`~.set_default`
471 (``key=='default'``) or :meth:`~.set_action` (``key == 'action'``).
473 :deprecated: As of v6.1, ``set_default()`` or ``set_action()`` should be used directly.
475 assert key in ['default', 'action'], "ir.values entry keys must be in ['default','action']"
477 def do_set(model,res_id):
478 return self.set_default(cr, uid, model, field_name=name, value=value,
479 for_all_users=(not preserve_user), company_id=company,
481 elif key == 'action':
482 def do_set(model,res_id):
483 return self.set_action(cr, uid, name, action_slot=key2, model=model, action=value, res_id=res_id)
484 return self._map_legacy_model_list(models, do_set)
486 def get(self, cr, uid, key, key2, models, meta=False, context=None, res_id_req=False, without_user=True, key2_req=True):
487 """Deprecated legacy method to get the list of default values or actions bound to models' action slots.
488 Now dispatches to the newer API methods according to the value of ``key``: :meth:`~.get_defaults`
489 (``key=='default'``) or :meth:`~.get_actions` (``key == 'action'``)
491 :deprecated: As of v6.1, ``get_defaults()`` or ``get_actions()`` should be used directly.
494 assert key in ['default', 'action'], "ir.values entry keys must be in ['default','action']"
496 def do_get(model,res_id):
497 return self.get_defaults(cr, uid, model, condition=key2)
498 elif key == 'action':
499 def do_get(model,res_id):
500 return self.get_actions(cr, uid, action_slot=key2, model=model, res_id=res_id, context=context)
501 return self._map_legacy_model_list(models, do_get, merge_results=True)
503 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: