Merge lp:~openerp-dev/openobject-server/trunk-project-task-stage-access-rights-ima into lp:openobject-server

Proposed by Ishwar Malvi(OpenERP)
Status: Needs review
Proposed branch: lp:~openerp-dev/openobject-server/trunk-project-task-stage-access-rights-ima
Merge into: lp:openobject-server
Diff against target: 279 lines (+269/-0)
1 file modified
openerp/osv/orm.py (+269/-0)
To merge this branch: bzr merge lp:~openerp-dev/openobject-server/trunk-project-task-stage-access-rights-ima
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+191981@code.launchpad.net

Description of the change

Hello,
     I have updated __view_look_dom_arch method in orm for kanban view to set groups right in xml.

Thanks,
Ishwar Malvi

To post a comment you must log in.
4952. By Ishwar Malvi(OpenERP)

[MERGE]with latest.

4953. By Bharat Devnani (Open ERP)

[MERGE] merged with main server

Unmerged revisions

4953. By Bharat Devnani (Open ERP)

[MERGE] merged with main server

4952. By Ishwar Malvi(OpenERP)

[MERGE]with latest.

4951. By Ishwar Malvi(OpenERP)

[MERGE]with trunk.

4950. By Vidhin Mehta (OpenERP)

[IMP]set group right in xml for kanban view.

4949. By Ishwar Malvi(OpenERP)

[IMP]improved code.

4948. By Ishwar Malvi(OpenERP)

[IMP]hide the link add a new column,edit and delete in kanban if the user has not the right to do it.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openerp/osv/orm.py'
2--- openerp/osv/orm.py 2014-02-20 16:05:48 +0000
3+++ openerp/osv/orm.py 2014-03-04 06:27:49 +0000
4@@ -1704,6 +1704,275 @@
5 return any([self.pool.get('res.users').has_group(cr, uid, group_ext_id)
6 for group_ext_id in groups.split(',')])
7
8+ def __view_look_dom(self, cr, user, node, view_id, in_tree_view, model_fields, context=None):
9+ """Return the description of the fields in the node.
10+
11+ In a normal call to this method, node is a complete view architecture
12+ but it is actually possible to give some sub-node (this is used so
13+ that the method can call itself recursively).
14+
15+ Originally, the field descriptions are drawn from the node itself.
16+ But there is now some code calling fields_get() in order to merge some
17+ of those information in the architecture.
18+
19+ """
20+ if context is None:
21+ context = {}
22+ result = False
23+ fields = {}
24+ children = True
25+
26+ modifiers = {}
27+
28+ def encode(s):
29+ if isinstance(s, unicode):
30+ return s.encode('utf8')
31+ return s
32+
33+ def check_group(node):
34+ """Apply group restrictions, may be set at view level or model level::
35+ * at view level this means the element should be made invisible to
36+ people who are not members
37+ * at model level (exclusively for fields, obviously), this means
38+ the field should be completely removed from the view, as it is
39+ completely unavailable for non-members
40+
41+ :return: True if field should be included in the result of fields_view_get
42+ """
43+ if node.tag == 'field' and node.get('name') in self._all_columns:
44+ column = self._all_columns[node.get('name')].column
45+ if column.groups and not self.user_has_groups(cr, user,
46+ groups=column.groups,
47+ context=context):
48+ node.getparent().remove(node)
49+ fields.pop(node.get('name'), None)
50+ # no point processing view-level ``groups`` anymore, return
51+ return False
52+ if node.get('groups'):
53+ can_see = self.user_has_groups(cr, user,
54+ groups=node.get('groups'),
55+ context=context)
56+ if not can_see:
57+ node.set('invisible', '1')
58+ modifiers['invisible'] = True
59+ if 'attrs' in node.attrib:
60+ del(node.attrib['attrs']) #avoid making field visible later
61+ del(node.attrib['groups'])
62+ return True
63+
64+ if node.tag in ('field', 'node', 'arrow'):
65+ if node.get('object'):
66+ attrs = {}
67+ views = {}
68+ xml = "<form>"
69+ for f in node:
70+ if f.tag == 'field':
71+ xml += etree.tostring(f, encoding="utf-8")
72+ xml += "</form>"
73+ new_xml = etree.fromstring(encode(xml))
74+ ctx = context.copy()
75+ ctx['base_model_name'] = self._name
76+ xarch, xfields = self.pool[node.get('object')].__view_look_dom_arch(cr, user, new_xml, view_id, ctx)
77+ views['form'] = {
78+ 'arch': xarch,
79+ 'fields': xfields
80+ }
81+ attrs = {'views': views}
82+ fields = xfields
83+ if node.get('name'):
84+ attrs = {}
85+ try:
86+ if node.get('name') in self._columns:
87+ column = self._columns[node.get('name')]
88+ else:
89+ column = self._inherit_fields[node.get('name')][2]
90+ except Exception:
91+ column = False
92+
93+ if column:
94+ relation = self.pool[column._obj] if column._obj else None
95+
96+ children = False
97+ views = {}
98+ for f in node:
99+ if f.tag in ('form', 'tree', 'graph', 'kanban'):
100+ node.remove(f)
101+ ctx = context.copy()
102+ ctx['base_model_name'] = self._name
103+ xarch, xfields = relation.__view_look_dom_arch(cr, user, f, view_id, ctx)
104+ views[str(f.tag)] = {
105+ 'arch': xarch,
106+ 'fields': xfields
107+ }
108+ attrs = {'views': views}
109+ if node.get('widget') and node.get('widget') == 'selection':
110+ # Prepare the cached selection list for the client. This needs to be
111+ # done even when the field is invisible to the current user, because
112+ # other events could need to change its value to any of the selectable ones
113+ # (such as on_change events, refreshes, etc.)
114+
115+ # If domain and context are strings, we keep them for client-side, otherwise
116+ # we evaluate them server-side to consider them when generating the list of
117+ # possible values
118+ # TODO: find a way to remove this hack, by allow dynamic domains
119+ dom = []
120+ if column._domain and not isinstance(column._domain, basestring):
121+ dom = list(column._domain)
122+ dom += eval(node.get('domain', '[]'), {'uid': user, 'time': time})
123+ search_context = dict(context)
124+ if column._context and not isinstance(column._context, basestring):
125+ search_context.update(column._context)
126+ attrs['selection'] = relation._name_search(cr, user, '', dom, context=search_context, limit=None, name_get_uid=1)
127+ if (node.get('required') and not int(node.get('required'))) or not column.required:
128+ attrs['selection'].append((False, ''))
129+ fields[node.get('name')] = attrs
130+
131+ field = model_fields.get(node.get('name'))
132+ if field:
133+ transfer_field_to_modifiers(field, modifiers)
134+
135+
136+ elif node.tag in ('form', 'tree'):
137+ result = self.view_header_get(cr, user, False, node.tag, context)
138+ if result:
139+ node.set('string', result)
140+ in_tree_view = node.tag == 'tree'
141+
142+ elif node.tag == 'calendar':
143+ for additional_field in ('date_start', 'date_delay', 'date_stop', 'color'):
144+ if node.get(additional_field):
145+ fields[node.get(additional_field)] = {}
146+
147+ if not check_group(node):
148+ # node must be removed, no need to proceed further with its children
149+ return fields
150+
151+ # The view architeture overrides the python model.
152+ # Get the attrs before they are (possibly) deleted by check_group below
153+ transfer_node_to_modifiers(node, modifiers, context, in_tree_view)
154+
155+ # TODO remove attrs couterpart in modifiers when invisible is true ?
156+
157+ # translate view
158+ if 'lang' in context:
159+ if node.text and node.text.strip():
160+ trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.text.strip())
161+ if trans:
162+ node.text = node.text.replace(node.text.strip(), trans)
163+ if node.tail and node.tail.strip():
164+ trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.tail.strip())
165+ if trans:
166+ node.tail = node.tail.replace(node.tail.strip(), trans)
167+
168+ if node.get('string') and not result:
169+ trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.get('string'))
170+ if trans == node.get('string') and ('base_model_name' in context):
171+ # If translation is same as source, perhaps we'd have more luck with the alternative model name
172+ # (in case we are in a mixed situation, such as an inherited view where parent_view.model != model
173+ trans = self.pool.get('ir.translation')._get_source(cr, user, context['base_model_name'], 'view', context['lang'], node.get('string'))
174+ if trans:
175+ node.set('string', trans)
176+
177+ for attr_name in ('confirm', 'sum', 'avg', 'help', 'placeholder'):
178+ attr_value = node.get(attr_name)
179+ if attr_value:
180+ trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], attr_value)
181+ if trans:
182+ node.set(attr_name, trans)
183+
184+ for f in node:
185+ if children or (node.tag == 'field' and f.tag in ('filter','separator')):
186+ fields.update(self.__view_look_dom(cr, user, f, view_id, in_tree_view, model_fields, context))
187+
188+ transfer_modifiers_to_node(modifiers, node)
189+ return fields
190+
191+ def _disable_workflow_buttons(self, cr, user, node):
192+ """ Set the buttons in node to readonly if the user can't activate them. """
193+ if user == 1:
194+ # admin user can always activate workflow buttons
195+ return node
196+
197+ # TODO handle the case of more than one workflow for a model or multiple
198+ # transitions with different groups and same signal
199+ usersobj = self.pool.get('res.users')
200+ buttons = (n for n in node.getiterator('button') if n.get('type') != 'object')
201+ for button in buttons:
202+ user_groups = usersobj.read(cr, user, [user], ['groups_id'])[0]['groups_id']
203+ cr.execute("""SELECT DISTINCT t.group_id
204+ FROM wkf
205+ INNER JOIN wkf_activity a ON a.wkf_id = wkf.id
206+ INNER JOIN wkf_transition t ON (t.act_to = a.id)
207+ WHERE wkf.osv = %s
208+ AND t.signal = %s
209+ AND t.group_id is NOT NULL
210+ """, (self._name, button.get('name')))
211+ group_ids = [x[0] for x in cr.fetchall() if x[0]]
212+ can_click = not group_ids or bool(set(user_groups).intersection(group_ids))
213+ button.set('readonly', str(int(not can_click)))
214+ return node
215+
216+ def __view_look_dom_arch(self, cr, user, node, view_id, context=None):
217+ """ Return an architecture and a description of all the fields.
218+
219+ The field description combines the result of fields_get() and
220+ __view_look_dom().
221+
222+ :param node: the architecture as as an etree
223+ :return: a tuple (arch, fields) where arch is the given node as a
224+ string and fields is the description of all the fields.
225+
226+ """
227+ fields = {}
228+ if node.tag == 'diagram':
229+ if node.getchildren()[0].tag == 'node':
230+ node_model = self.pool[node.getchildren()[0].get('object')]
231+ node_fields = node_model.fields_get(cr, user, None, context)
232+ fields.update(node_fields)
233+ if not node.get("create") and not node_model.check_access_rights(cr, user, 'create', raise_exception=False):
234+ node.set("create", 'false')
235+ if node.getchildren()[1].tag == 'arrow':
236+ arrow_fields = self.pool[node.getchildren()[1].get('object')].fields_get(cr, user, None, context)
237+ fields.update(arrow_fields)
238+ else:
239+ fields = self.fields_get(cr, user, None, context)
240+ fields_def = self.__view_look_dom(cr, user, node, view_id, False, fields, context=context)
241+ node = self._disable_workflow_buttons(cr, user, node)
242+ if node.tag in ('kanban', 'tree', 'form', 'gantt'):
243+ for action, operation in (('create', 'create'), ('delete', 'unlink'), ('edit', 'write')):
244+ if not node.get(action) and not self.check_access_rights(cr, user, operation, raise_exception=False):
245+ node.set(action, 'false')
246+ if node.tag in ('kanban'):
247+ group_by_field = node.get('default_group_by')
248+ if group_by_field:
249+ group_by_object = self.fields_get(cr, user, None, context)[group_by_field]
250+ if (group_by_object['type'] == 'many2one'):
251+ group_by_model = self.pool.get(group_by_object['relation'])
252+ for action, operation in (('group_create', 'create'), ('group_delete', 'unlink'), ('group_edit', 'write')):
253+ if not node.get(action) and not group_by_model.check_access_rights(cr, user, operation, raise_exception=False):
254+ node.set(action, 'false')
255+
256+ arch = etree.tostring(node, encoding="utf-8").replace('\t', '')
257+ for k in fields.keys():
258+ if k not in fields_def:
259+ del fields[k]
260+ for field in fields_def:
261+ if field == 'id':
262+ # sometime, the view may contain the (invisible) field 'id' needed for a domain (when 2 objects have cross references)
263+ fields['id'] = {'readonly': True, 'type': 'integer', 'string': 'ID'}
264+ elif field in fields:
265+ fields[field].update(fields_def[field])
266+ else:
267+ cr.execute('select name, model from ir_ui_view where (id=%s or inherit_id=%s) and arch like %s', (view_id, view_id, '%%%s%%' % field))
268+ res = cr.fetchall()[:]
269+ model = res[0][1]
270+ res.insert(0, ("Can't find field '%s' in the following view parts composing the view of object model '%s':" % (field, model), None))
271+ msg = "\n * ".join([r[0] for r in res])
272+ msg += "\n\nEither you wrongly customized this view, or some modules bringing those views are not compatible with your current data model"
273+ _logger.error(msg)
274+ raise except_orm('View error', msg)
275+ return arch, fields
276+
277 def _get_default_form_view(self, cr, user, context=None):
278 """ Generates a default single-line form view using all fields
279 of the current model except the m2m and o2m ones.