Merge lp:~openerp-dev/openobject-server/6.0-opw-574351-nep into lp:openobject-server/6.0

Proposed by Nehal Panchal (OpenERP)
Status: Rejected
Rejected by: Naresh(OpenERP)
Proposed branch: lp:~openerp-dev/openobject-server/6.0-opw-574351-nep
Merge into: lp:openobject-server/6.0
Diff against target: 430 lines (+195/-156)
3 files modified
bin/addons/base/ir/ir_values.py (+187/-154)
bin/addons/base/security/base_security.xml (+7/-0)
bin/addons/base/security/ir.model.access.csv (+1/-2)
To merge this branch: bzr merge lp:~openerp-dev/openobject-server/6.0-opw-574351-nep
Reviewer Review Type Date Requested Status
Naresh(OpenERP) (community) Disapprove
Review via email: mp+105194@code.launchpad.net

Description of the change

Hello,

Non-admin user cannot set default value with "only for you" option.

This fixes the issue.

Thanks.

To post a comment you must log in.
Revision history for this message
Naresh(OpenERP) (nch-openerp) wrote :

Hello

The changes are huge and very risky to be merged to stable version. This may cause regression and effect the existing customers. I would reject this merge proposal.

Thanks,

review: Disapprove

Unmerged revisions

3618. By Nehal Panchal (OpenERP)

[FIX] base : Non-admin user cannot set default value with 'only for you' option

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/addons/base/ir/ir_values.py'
2--- bin/addons/base/ir/ir_values.py 2012-02-09 08:53:51 +0000
3+++ bin/addons/base/ir/ir_values.py 2012-05-09 12:18:18 +0000
4@@ -27,20 +27,28 @@
5 EXCLUDED_FIELDS = set((
6 'report_sxw_content', 'report_rml_content', 'report_sxw', 'report_rml',
7 'report_sxw_content_data', 'report_rml_content_data', 'search_view', ))
8+
9+ACTION_SLOTS = [
10+ "client_action_multi", # sidebar wizard action
11+ "client_print_multi", # sidebar report printing button
12+ "client_action_relate", # sidebar related link
13+ "tree_but_open", # double-click on item in tree view
14+ "tree_but_action", # deprecated: same as tree_but_open
15+ ]
16
17 class ir_values(osv.osv):
18 _name = 'ir.values'
19
20 def _value_unpickle(self, cursor, user, ids, name, arg, context=None):
21 res = {}
22- for report in self.browse(cursor, user, ids, context=context):
23- value = report[name[:-9]]
24- if not report.object and value:
25+ for record in self.browse(cursor, user, ids, context=context):
26+ value = record[name[:-9]]
27+ if record.key == 'default' and value:
28 try:
29 value = str(pickle.loads(value))
30- except:
31+ except Exception:
32 pass
33- res[report.id] = value
34+ res[record.id] = value
35 return res
36
37 def _value_pickle(self, cursor, user, id, name, value, arg, context=None):
38@@ -49,18 +57,19 @@
39 ctx = context.copy()
40 if self.CONCURRENCY_CHECK_FIELD in ctx:
41 del ctx[self.CONCURRENCY_CHECK_FIELD]
42- if not self.browse(cursor, user, id, context=context).object:
43+ record = self.browse(cursor, user, id, context=context)
44+ if record.key == 'default':
45 value = pickle.dumps(value)
46 self.write(cursor, user, id, {name[:-9]: value}, context=ctx)
47
48- def onchange_object_id(self, cr, uid, ids, object_id, context={}):
49+ def onchange_object_id(self, cr, uid, ids, object_id, context=None):
50 if not object_id: return {}
51 act = self.pool.get('ir.model').browse(cr, uid, object_id, context=context)
52 return {
53 'value': {'model': act.model}
54 }
55
56- def onchange_action_id(self, cr, uid, ids, action_id, context={}):
57+ def onchange_action_id(self, cr, uid, ids, action_id, context=None):
58 if not action_id: return {}
59 act = self.pool.get('ir.actions.actions').browse(cr, uid, action_id, context=context)
60 return {
61@@ -68,29 +77,44 @@
62 }
63
64 _columns = {
65- 'name': fields.char('Name', size=128),
66- 'model_id': fields.many2one('ir.model', 'Object', size=128,
67- help="This field is not used, it only helps you to select a good model."),
68- 'model': fields.char('Object Name', size=128, select=True),
69- 'action_id': fields.many2one('ir.actions.actions', 'Action',
70- help="This field is not used, it only helps you to select the right action."),
71- 'value': fields.text('Value'),
72+ 'name': fields.char('Name', size=128, required=True),
73+ 'model': fields.char('Model Name', size=128, select=True, required=True,
74+ help="Model to which this entry applies"),
75+ 'model_id': fields.many2one('ir.model', 'Model (change only)', size=128,
76+ help="Model to which this entry applies - "
77+ "helper field for setting a model, will "
78+ "automatically set the correct model name"),
79+ 'action_id': fields.many2one('ir.actions.actions', 'Action (change only)',
80+ help="Action bound to this entry - "
81+ "helper field for binding an action, will "
82+ "automatically set the correct reference"),
83+ 'value': fields.text('Value', help="Default value (pickled) or reference to an action"),
84 'value_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle,
85- method=True, type='text', string='Value'),
86- 'object': fields.boolean('Is Object'),
87- 'key': fields.selection([('action','Action'),('default','Default')], 'Type', size=128, select=True),
88- 'key2' : fields.char('Event Type',help="The kind of action or button in the client side that will trigger the action.", size=128, select=True),
89- 'meta': fields.text('Meta Datas'),
90- 'meta_unpickle': fields.function(_value_unpickle, fnct_inv=_value_pickle,
91- method=True, type='text', string='Metadata'),
92- 'res_id': fields.integer('Object ID', help="Keep 0 if the action must appear on all resources.", select=True),
93- 'user_id': fields.many2one('res.users', 'User', ondelete='cascade', select=True),
94- 'company_id': fields.many2one('res.company', 'Company', select=True)
95+ type='text',
96+ string='Default value or action reference'),
97+ 'key': fields.selection([('action','Action'),('default','Default')],
98+ 'Type', size=128, select=True, required=True,
99+ help="- Action: an action attached to one slot of the given model\n"
100+ "- Default: a default value for a model field"),
101+ 'key2' : fields.char('Qualifier', size=128, select=True,
102+ help="For actions, one of the possible action slots: \n"
103+ " - client_action_multi\n"
104+ " - client_print_multi\n"
105+ " - client_action_relate\n"
106+ " - tree_but_open\n"
107+ "For defaults, an optional condition"
108+ ,),
109+ 'res_id': fields.integer('Record ID', select=True,
110+ help="Database identifier of the record to which this applies. "
111+ "0 = for all records"),
112+ 'user_id': fields.many2one('res.users', 'User', ondelete='cascade', select=True,
113+ help="If set, action binding only applies for this user."),
114+ 'company_id': fields.many2one('res.company', 'Company', ondelete='cascade', select=True,
115+ help="If set, action binding only applies for this company")
116 }
117 _defaults = {
118- 'key': lambda *a: 'action',
119- 'key2': lambda *a: 'tree_but_open',
120- 'company_id': lambda *a: False
121+ 'key': 'action',
122+ 'key2': 'tree_but_open',
123 }
124
125 def _auto_init(self, cr, context=None):
126@@ -99,137 +123,146 @@
127 if not cr.fetchone():
128 cr.execute('CREATE INDEX ir_values_key_model_key2_res_id_user_id_idx ON ir_values (key, model, key2, res_id, user_id)')
129
130- def set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=False, preserve_user=False, company=False):
131+ def set_default(self, cr, uid, model, field_name, value, for_all_users=True, company_id=False, condition=False):
132 if isinstance(value, unicode):
133 value = value.encode('utf8')
134- if not isobject:
135- value = pickle.dumps(value)
136- if meta:
137- meta = pickle.dumps(meta)
138- assert isinstance(models, (list, tuple)), models
139- assert not company or isinstance(company, int), "Parameter 'company' must be an integer (company ID)!"
140- if company and company is True:
141- current_user_obj = self.pool.get('res.users').browse(cr, uid, uid, context={})
142- company = current_user_obj.company_id.id
143-
144- ids_res = []
145- for model in models:
146+ if company_id is True:
147+ user = self.pool.get('res.users').browse(cr, uid, uid)
148+ company_id = user.company_id.id
149+ search_criteria = [
150+ ('key', '=', 'default'),
151+ ('key2', '=', condition and condition[:200]),
152+ ('model', '=', model),
153+ ('name', '=', field_name),
154+ ('user_id', '=', False if for_all_users else uid),
155+ ('company_id','=', company_id)
156+ ]
157+ self.unlink(cr, uid, self.search(cr, uid, search_criteria))
158+ return self.create(cr, uid, {
159+ 'name': field_name,
160+ 'value': pickle.dumps(value),
161+ 'model': model,
162+ 'key': 'default',
163+ 'key2': condition and condition[:200],
164+ 'user_id': False if for_all_users else uid,
165+ 'company_id': company_id,
166+ })
167+
168+ def get_defaults(self, cr, uid, model, condition=False):
169+ query = """SELECT v.id, v.name, v.value FROM ir_values v
170+ LEFT JOIN res_users u ON (v.user_id = u.id)
171+ WHERE v.key = %%s AND v.model = %%s
172+ AND (v.user_id = %%s OR v.user_id IS NULL)
173+ AND (v.company_id IS NULL OR
174+ v.company_id =
175+ (SELECT company_id from res_users where id = %%s)
176+ )
177+ %s
178+ ORDER BY v.user_id, u.company_id"""
179+ query = query % ('AND v.key2 = %s' if condition else '')
180+ params = ('default', model, uid, uid)
181+ if condition:
182+ params += (condition[:200],)
183+ cr.execute(query, params)
184+ defaults = {}
185+ for row in cr.dictfetchall():
186+ defaults.setdefault(row['name'],
187+ (row['id'], row['name'], pickle.loads(row['value'].encode('utf-8'))))
188+ return defaults.values()
189+
190+ def set_action(self, cr, uid, name, action_slot, model, action, res_id=False):
191+ assert isinstance(action, basestring) and ',' in action, \
192+ 'Action definition must be an action reference, e.g. "ir.actions.act_window,42"'
193+ assert action_slot in ACTION_SLOTS, \
194+ 'Action slot (%s) must be one of: %r' % (action_slot, ACTION_SLOTS)
195+ search_criteria = [
196+ ('key', '=', 'action'),
197+ ('key2', '=', action_slot),
198+ ('model', '=', model),
199+ ('res_id', '=', res_id or 0), # int field -> NULL == 0
200+ ('value', '=', action),
201+ ]
202+ self.unlink(cr, uid, self.search(cr, uid, search_criteria))
203+ return self.create(cr, uid, {
204+ 'key': 'action',
205+ 'key2': action_slot,
206+ 'model': model,
207+ 'res_id': res_id,
208+ 'name': name,
209+ 'value': action,
210+ })
211+
212+ def get_actions(self, cr, uid, action_slot, model, res_id=False, context=None):
213+ assert action_slot in ACTION_SLOTS, 'Illegal action slot value: %s' % action_slot
214+ query = """SELECT v.id, v.name, v.value FROM ir_values v
215+ WHERE v.key = %s AND v.key2 = %s
216+ AND v.model = %s
217+ AND (v.res_id = %s
218+ OR v.res_id IS NULL
219+ OR v.res_id = 0)
220+ ORDER BY v.id"""
221+ cr.execute(query, ('action', action_slot, model, res_id or None))
222+ results = {}
223+ for action in cr.dictfetchall():
224+ action_model,id = action['value'].split(',')
225+ fields = [
226+ field
227+ for field in self.pool.get(action_model)._all_columns
228+ if field not in EXCLUDED_FIELDS]
229+ try:
230+ action_def = self.pool.get(action_model).read(cr, uid, int(id), fields, context)
231+ if action_def:
232+ if action_model in ('ir.actions.report.xml','ir.actions.act_window',
233+ 'ir.actions.wizard'):
234+ groups = action_def.get('groups_id')
235+ if groups:
236+ cr.execute('SELECT 1 FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',
237+ (tuple(groups), uid))
238+ if not cr.fetchone():
239+ if action['name'] == 'Menuitem':
240+ raise osv.except_osv('Error !',
241+ 'You do not have the permission to perform this operation !!!')
242+ continue
243+ results[action['name']] = (action['id'], action['name'], action_def)
244+ except except_orm, e:
245+ continue
246+ return results.values()
247+
248+ def _map_legacy_model_list(self, model_list, map_fn, merge_results=False):
249+ assert isinstance(model_list, (list, tuple)), \
250+ "model_list should be in the form [model,..] or [(model,res_id), ..]"
251+ results = []
252+ for model in model_list:
253+ res_id = False
254 if isinstance(model, (list, tuple)):
255- model,res_id = model
256+ model, res_id = model
257+ result = map_fn(model, res_id)
258+ if merge_results:
259+ results.extend(result)
260 else:
261- res_id=False
262- if replace:
263- search_criteria = [
264- ('key', '=', key),
265- ('key2', '=', key2),
266- ('model', '=', model),
267- ('res_id', '=', res_id),
268- ('user_id', '=', preserve_user and uid),
269- ('company_id' ,'=', company)
270-
271- ]
272- if key in ('meta', 'default'):
273- search_criteria.append(('name', '=', name))
274- else:
275- search_criteria.append(('value', '=', value))
276+ results.append(result)
277+ return results
278
279- self.unlink(cr, uid, self.search(cr, uid, search_criteria))
280- vals = {
281- 'name': name,
282- 'value': value,
283- 'model': model,
284- 'object': isobject,
285- 'key': key,
286- 'key2': key2 and key2[:200],
287- 'meta': meta,
288- 'user_id': preserve_user and uid,
289- 'company_id':company
290- }
291- if res_id:
292- vals['res_id'] = res_id
293- ids_res.append(self.create(cr, uid, vals))
294- return ids_res
295+ def set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=False, preserve_user=False, company=False):
296+ assert key in ['default', 'action'], "ir.values entry keys must be in ['default','action']"
297+ if key == 'default':
298+ def do_set(model,res_id):
299+ return self.set_default(cr, uid, model, field_name=name, value=value,
300+ for_all_users=(not preserve_user), company_id=company,
301+ condition=key2)
302+ elif key == 'action':
303+ def do_set(model,res_id):
304+ return self.set_action(cr, uid, name, action_slot=key2, model=model, action=value, res_id=res_id)
305+ return self._map_legacy_model_list(models, do_set)
306
307 def get(self, cr, uid, key, key2, models, meta=False, context={}, res_id_req=False, without_user=True, key2_req=True):
308- result = []
309- for m in models:
310- if isinstance(m, (list, tuple)):
311- m, res_id = m
312- else:
313- res_id=False
314-
315- where = ['key=%s','model=%s']
316- params = [key, str(m)]
317- if key2:
318- where.append('key2=%s')
319- params.append(key2[:200])
320- elif key2_req and not meta:
321- where.append('key2 is null')
322- if res_id_req and (models[-1][0]==m):
323- if res_id:
324- where.append('res_id=%s')
325- params.append(res_id)
326- else:
327- where.append('(res_id is NULL)')
328- elif res_id:
329- if (models[-1][0]==m):
330- where.append('(res_id=%s or (res_id is null))')
331- params.append(res_id)
332- else:
333- where.append('res_id=%s')
334- params.append(res_id)
335-
336- order = 'id, company_id'
337- where.append('''(user_id=%s or (user_id IS NULL))
338- and (company_id is null or
339- company_id = (SELECT company_id FROM res_users WHERE id = %s)) order by '''+ order)
340- params += [uid, uid]
341- clause = ' and '.join(where)
342- cr.execute('select id,name,value,object,meta, key from ir_values where ' + clause, params)
343- result = cr.fetchall()
344- if result:
345- break
346-
347- if not result:
348- return []
349-
350- def _result_get(x, keys):
351- if x[1] in keys:
352- return False
353- keys.append(x[1])
354- if x[3]:
355- model,id = x[2].split(',')
356- # FIXME: It might be a good idea to opt-in that kind of stuff
357- # FIXME: instead of arbitrarily removing random fields
358- fields = [
359- field
360- for field in self.pool.get(model).fields_get_keys(cr, uid)
361- if field not in EXCLUDED_FIELDS]
362-
363- try:
364- datas = self.pool.get(model).read(cr, uid, [int(id)], fields, context)
365- except except_orm, e:
366- return False
367- datas = datas and datas[0]
368- if not datas:
369- return False
370- else:
371- datas = pickle.loads(x[2].encode('utf-8'))
372- if meta:
373- return (x[0], x[1], datas, pickle.loads(x[4]))
374- return (x[0], x[1], datas)
375- keys = []
376- res = filter(None, map(lambda x: _result_get(x, keys), result))
377- res2 = res[:]
378- for r in res:
379- if isinstance(r[2], dict) and r[2].get('type') in ('ir.actions.report.xml','ir.actions.act_window','ir.actions.wizard'):
380- groups = r[2].get('groups_id')
381- if groups:
382- cr.execute('SELECT COUNT(1) FROM res_groups_users_rel WHERE gid IN %s AND uid=%s',(tuple(groups), uid))
383- cnt = cr.fetchone()[0]
384- if not cnt:
385- res2.remove(r)
386- if r[1] == 'Menuitem' and not res2:
387- raise osv.except_osv('Error !','You do not have the permission to perform this operation !!!')
388- return res2
389+ assert key in ['default', 'action'], "ir.values entry keys must be in ['default','action']"
390+ if key == 'default':
391+ def do_get(model,res_id):
392+ return self.get_defaults(cr, uid, model, condition=key2)
393+ elif key == 'action':
394+ def do_get(model,res_id):
395+ return self.get_actions(cr, uid, action_slot=key2, model=model, res_id=res_id, context=context)
396+ return self._map_legacy_model_list(models, do_get, merge_results=True)
397+
398 ir_values()
399
400=== modified file 'bin/addons/base/security/base_security.xml'
401--- bin/addons/base/security/base_security.xml 2011-03-03 15:59:18 +0000
402+++ bin/addons/base/security/base_security.xml 2012-05-09 12:18:18 +0000
403@@ -62,6 +62,13 @@
404 <field name="global" eval="True"/>
405 <field name="domain_force">[('company_id','child_of',[user.company_id.id])]</field>
406 </record>
407+
408+ <record model="ir.rule" id="ir_values_default_rule">
409+ <field name="name">Defaults: alter personal values only</field>
410+ <field name="model_id" ref="model_ir_values"/>
411+ <field name="domain_force">[('key','=','default'),('user_id','=',user.id)]</field>
412+ <field name="perm_read" eval="False"/>
413+ </record>
414
415 </data>
416 </openerp>
417
418=== modified file 'bin/addons/base/security/ir.model.access.csv'
419--- bin/addons/base/security/ir.model.access.csv 2011-02-23 15:58:33 +0000
420+++ bin/addons/base/security/ir.model.access.csv 2012-05-09 12:18:18 +0000
421@@ -38,8 +38,7 @@
422 "access_ir_ui_view_custom_group_user","ir_ui_view_custom_group_user","model_ir_ui_view_custom",,1,0,0,0
423 "access_ir_ui_view_custom_group_system","ir_ui_view_custom_group_system","model_ir_ui_view_custom","group_system",1,1,1,1
424 "access_ir_ui_view_sc_group_user","ir_ui_view_sc group_user","model_ir_ui_view_sc",,1,1,1,1
425-"access_ir_values_group_erp_manager","ir_values group_erp_manager","model_ir_values","group_erp_manager",1,1,1,1
426-"access_ir_values_group_all","ir_values group_all","model_ir_values",,1,0,1,0
427+"access_ir_values_group_all","ir_values group_all","model_ir_values",,1,1,1,1
428 "access_res_company_group_erp_manager","res_company group_erp_manager","model_res_company","group_erp_manager",1,1,1,1
429 "access_res_company_group_user","res_company group_user","model_res_company",,1,0,0,0
430 "access_res_country_group_all","res_country group_user_all","model_res_country",,1,0,0,0