CleanupTemp n'existe plus.
[GroupUserFolder.git] / GroupDataTool.py
1 # -*- coding: utf-8 -*-
2 ## GroupUserFolder
3 ## Copyright (C)2006 Ingeniweb
4
5 ## This program is free software; you can redistribute it and/or modify
6 ## it under the terms of the GNU General Public License as published by
7 ## the Free Software Foundation; either version 2 of the License, or
8 ## (at your option) any later version.
9
10 ## This program is distributed in the hope that it will be useful,
11 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ## GNU General Public License for more details.
14
15 ## You should have received a copy of the GNU General Public License
16 ## along with this program; see the file COPYING. If not, write to the
17 ## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19 ## Copyright (c) 2003 The Connexions Project, All Rights Reserved
20 ## initially written by J Cameron Cooper, 11 June 2003
21 ## concept with Brent Hendricks, George Runyan
22 """
23 Basic group data tool.
24 """
25 __version__ = "$Revision: $"
26 # $Source: $
27 # $Id: GroupDataTool.py 52136 2007-10-21 20:38:00Z encolpe $
28 __docformat__ = 'restructuredtext'
29
30 from Products.CMFCore.utils import UniqueObject, getToolByName
31 from OFS.SimpleItem import SimpleItem
32 from OFS.PropertyManager import PropertyManager
33 from Globals import DTMLFile
34 from Globals import InitializeClass
35 from AccessControl.Role import RoleManager
36 from BTrees.OOBTree import OOBTree
37 from ZPublisher.Converters import type_converters
38 from Acquisition import aq_inner, aq_parent, aq_base
39 from AccessControl import ClassSecurityInfo, Permissions, Unauthorized, getSecurityManager
40 from zope.interface import implements
41 from Products.CMFCore.interfaces import IActionProvider
42
43 from Products.CMFCore.ActionProviderBase import ActionProviderBase
44
45 # BBB CMF < 1.5
46 try:
47 from Products.CMFCore.permissions import ManagePortal
48 except ImportError:
49 from Products.CMFCore.CMFCorePermissions import ManagePortal
50
51 # from Products.CMFCore.MemberDataTool import CleanupTemp
52
53 from interfaces.portal_groupdata import portal_groupdata as IGroupDataTool
54 from interfaces.portal_groupdata import GroupData as IGroupData
55 from Products.GroupUserFolder import postonly
56 from Products.GroupUserFolder.GRUFUser import GRUFGroup
57
58 _marker = [] # Create a new marker object.
59
60 from global_symbols import *
61
62
63 class GroupDataTool (UniqueObject, SimpleItem, PropertyManager, ActionProviderBase):
64 """ This tool wraps group objects, allowing transparent access to properties.
65 """
66 # The latter will work only with Plone 1.1 => hence, the if
67 implements(IGroupDataTool, IActionProvider)
68 # __implements__ = (IGroupDataTool, ActionProviderBase.__implements__)
69
70 id = 'portal_groupdata'
71 meta_type = 'CMF Group Data Tool'
72 _actions = ()
73
74 _v_temps = None
75 _properties=({'id':'title', 'type': 'string', 'mode': 'wd'},)
76
77 security = ClassSecurityInfo()
78
79 manage_options=( ActionProviderBase.manage_options +
80 ({ 'label' : 'Overview'
81 , 'action' : 'manage_overview'
82 },
83 )
84 + PropertyManager.manage_options
85 + SimpleItem.manage_options
86 )
87
88 #
89 # ZMI methods
90 #
91 security.declareProtected(ManagePortal, 'manage_overview')
92 manage_overview = DTMLFile('dtml/explainGroupDataTool', globals())
93
94 def __init__(self):
95 self._members = OOBTree()
96 # Create the default properties.
97 self._setProperty('description', '', 'text')
98 self._setProperty('email', '', 'string')
99
100 #
101 # 'portal_groupdata' interface methods
102 #
103 security.declarePrivate('wrapGroup')
104 def wrapGroup(self, g):
105 """Returns an object implementing the GroupData interface"""
106 id = g.getId()
107 members = self._members
108 if not members.has_key(id):
109 # Get a temporary member that might be
110 # registered later via registerMemberData().
111 temps = self._v_temps
112 if temps is not None and temps.has_key(id):
113 portal_group = temps[id]
114 else:
115 base = aq_base(self)
116 portal_group = GroupData(base, id)
117 if temps is None:
118 self._v_temps = {id:portal_group}
119 # XXX ClenupTemp doesn't exits
120 # if hasattr(self, 'REQUEST'):
121 # # No REQUEST during tests.
122 # self.REQUEST._hold(CleanupTemp(self))
123 else:
124 temps[id] = portal_group
125 else:
126 portal_group = members[id]
127 # Return a wrapper with self as containment and
128 # the user as context.
129 return portal_group.__of__(self).__of__(g)
130
131 security.declarePrivate('registerGroupData')
132 def registerGroupData(self, g, id):
133 '''
134 Adds the given member data to the _members dict.
135 This is done as late as possible to avoid side effect
136 transactions and to reduce the necessary number of
137 entries.
138 '''
139 self._members[id] = aq_base(g)
140
141 InitializeClass(GroupDataTool)
142
143
144 class GroupData (SimpleItem):
145
146 __implements__ = IGroupData
147
148 security = ClassSecurityInfo()
149
150 id = None
151 _tool = None
152
153 def __init__(self, tool, id):
154 self.id = id
155 # Make a temporary reference to the tool.
156 # The reference will be removed by notifyModified().
157 self._tool = tool
158
159 def _getGRUF(self,):
160 return self.acl_users
161
162 security.declarePrivate('notifyModified')
163 def notifyModified(self):
164 # Links self to parent for full persistence.
165 tool = getattr(self, '_tool', None)
166 if tool is not None:
167 del self._tool
168 tool.registerGroupData(self, self.getId())
169
170 security.declarePublic('getGroup')
171 def getGroup(self):
172 """ Returns the actual group implementation. Varies by group
173 implementation (GRUF/Nux/et al). In GRUF this is a user object."""
174 # The user object is our context, but it's possible for
175 # restricted code to strip context while retaining
176 # containment. Therefore we need a simple security check.
177 parent = aq_parent(self)
178 bcontext = aq_base(parent)
179 bcontainer = aq_base(aq_parent(aq_inner(self)))
180 if bcontext is bcontainer or not hasattr(bcontext, 'getUserName'):
181 raise 'GroupDataError', "Can't find group data"
182 # Return the user object, which is our context.
183 return parent
184
185 def getTool(self):
186 return aq_parent(aq_inner(self))
187
188 security.declarePublic("getGroupMemberIds")
189 def getGroupMemberIds(self,):
190 """
191 Return a list of group member ids
192 """
193 return map(lambda x: x.getMemberId(), self.getGroupMembers())
194
195 security.declarePublic("getAllGroupMemberIds")
196 def getAllGroupMemberIds(self,):
197 """
198 Return a list of group member ids
199 """
200 return map(lambda x: x.getMemberId(), self.getAllGroupMembers())
201
202 security.declarePublic('getGroupMembers')
203 def getGroupMembers(self, ):
204 """
205 Returns a list of the portal_memberdata-ish members of the group.
206 This doesn't include TRANSITIVE groups/users.
207 """
208 md = self.portal_memberdata
209 gd = self.portal_groupdata
210 ret = []
211 for u_name in self.getGroup().getMemberIds(transitive = 0, ):
212 usr = self._getGRUF().getUserById(u_name)
213 if not usr:
214 raise AssertionError, "Cannot retreive a user by its id !"
215 if usr.isGroup():
216 ret.append(gd.wrapGroup(usr))
217 else:
218 ret.append(md.wrapUser(usr))
219 return ret
220
221 security.declarePublic('getAllGroupMembers')
222 def getAllGroupMembers(self, ):
223 """
224 Returns a list of the portal_memberdata-ish members of the group.
225 This will include transitive groups / users
226 """
227 md = self.portal_memberdata
228 gd = self.portal_groupdata
229 ret = []
230 for u_name in self.getGroup().getMemberIds():
231 usr = self._getGRUF().getUserById(u_name)
232 if not usr:
233 raise AssertionError, "Cannot retreive a user by its id !"
234 if usr.isGroup():
235 ret.append(gd.wrapGroup(usr))
236 else:
237 ret.append(md.wrapUser(usr))
238 return ret
239
240 def _getGroup(self,):
241 """
242 _getGroup(self,) => Get the underlying group object
243 """
244 return self._getGRUF().getGroupByName(self.getGroupName())
245
246
247 security.declarePrivate("canAdministrateGroup")
248 def canAdministrateGroup(self,):
249 """
250 Return true if the #current# user can administrate this group
251 """
252 user = getSecurityManager().getUser()
253 tool = self.getTool()
254 portal = getToolByName(tool, 'portal_url').getPortalObject()
255
256 # Has manager users pemission?
257 if user.has_permission(Permissions.manage_users, portal):
258 return True
259
260 # Is explicitly mentioned as a group administrator?
261 managers = self.getProperty('delegated_group_member_managers', ())
262 if user.getId() in managers:
263 return True
264
265 # Belongs to a group which is explicitly mentionned as a group administrator
266 meth = getattr(user, "getAllGroupNames", None)
267 if meth:
268 groups = meth()
269 else:
270 groups = ()
271 for v in groups:
272 if v in managers:
273 return True
274
275 # No right to edit this: we complain.
276 return False
277
278 security.declarePublic('addMember')
279 def addMember(self, id, REQUEST=None):
280 """ Add the existing member with the given id to the group"""
281 # We check if the current user can directly or indirectly administrate this group
282 if not self.canAdministrateGroup():
283 raise Unauthorized, "You cannot add a member to the group."
284 self._getGroup().addMember(id)
285
286 # Notify member that they've been changed
287 mtool = getToolByName(self, 'portal_membership')
288 member = mtool.getMemberById(id)
289 if member:
290 member.notifyModified()
291 addMember = postonly(addMember)
292
293 security.declarePublic('removeMember')
294 def removeMember(self, id, REQUEST=None):
295 """Remove the member with the provided id from the group.
296 """
297 # We check if the current user can directly or indirectly administrate this group
298 if not self.canAdministrateGroup():
299 raise Unauthorized, "You cannot remove a member from the group."
300 self._getGroup().removeMember(id)
301
302 # Notify member that they've been changed
303 mtool = getToolByName(self, 'portal_membership')
304 member = mtool.getMemberById(id)
305 if member:
306 member.notifyModified()
307 removeMember = postonly(removeMember)
308
309 security.declareProtected(Permissions.manage_users, 'setProperties')
310 def setProperties(self, properties=None, **kw):
311 '''Allows the manager group to set his/her own properties.
312 Accepts either keyword arguments or a mapping for the "properties"
313 argument.
314 '''
315 if properties is None:
316 properties = kw
317 return self.setGroupProperties(properties)
318
319 security.declareProtected(Permissions.manage_users, 'setGroupProperties')
320 def setGroupProperties(self, mapping):
321 '''Sets the properties of the member.
322 '''
323 # Sets the properties given in the MemberDataTool.
324 tool = self.getTool()
325 for id in tool.propertyIds():
326 if mapping.has_key(id):
327 if not self.__class__.__dict__.has_key(id):
328 value = mapping[id]
329 if type(value)==type(''):
330 proptype = tool.getPropertyType(id) or 'string'
331 if type_converters.has_key(proptype):
332 value = type_converters[proptype](value)
333 setattr(self, id, value)
334
335 # Hopefully we can later make notifyModified() implicit.
336 self.notifyModified()
337
338 security.declarePublic('getProperties')
339 def getProperties(self, ):
340 """ Return the properties of this group. Properties are as usual in Zope."""
341 tool = self.getTool()
342 ret = {}
343 for pty in tool.propertyIds():
344 try:
345 ret[pty] = self.getProperty(pty)
346 except ValueError:
347 # We ignore missing ptys
348 continue
349 return ret
350
351 security.declarePublic('getProperty')
352 def getProperty(self, id, default=_marker):
353 """ Returns the value of the property specified by 'id' """
354 tool = self.getTool()
355 base = aq_base( self )
356
357 # First, check the wrapper (w/o acquisition).
358 value = getattr( base, id, _marker )
359 if value is not _marker:
360 return value
361
362 # Then, check the tool and the user object for a value.
363 tool_value = tool.getProperty( id, _marker )
364 user_value = getattr( aq_base(self.getGroup()), id, _marker )
365
366 # If the tool doesn't have the property, use user_value or default
367 if tool_value is _marker:
368 if user_value is not _marker:
369 return user_value
370 elif default is not _marker:
371 return default
372 else:
373 raise ValueError, 'The property %s does not exist' % id
374
375 # If the tool has an empty property and we have a user_value, use it
376 if not tool_value and user_value is not _marker:
377 return user_value
378
379 # Otherwise return the tool value
380 return tool_value
381
382 def __str__(self):
383 return self.getGroupId()
384
385 security.declarePublic("isGroup")
386 def isGroup(self,):
387 """
388 isGroup(self,) => Return true if this is a group.
389 Will always return true for groups.
390 As MemberData objects do not support this method, it is quite useless by now.
391 So one can use groupstool.isGroup(g) instead to get this information.
392 """
393 return 1
394
395 ### Group object interface ###
396
397 security.declarePublic('getGroupName')
398 def getGroupName(self):
399 """Return the name of the group, without any special decorations (like GRUF prefixes.)"""
400 return self.getGroup().getName()
401
402 security.declarePublic('getGroupId')
403 def getGroupId(self):
404 """Get the ID of the group. The ID can be used, at least from
405 Python, to get the user from the user's UserDatabase.
406 Within Plone, all group ids are UNPREFIXED."""
407 if isinstance(self, GRUFGroup):
408 return self.getGroup().getId(unprefixed = 1)
409 else:
410 return self.getGroup().getId()
411
412 def getGroupTitleOrName(self):
413 """Get the Title property of the group. If there is none
414 then return the name """
415 title = self.getProperty('title', None)
416 return title or self.getGroupName()
417
418 security.declarePublic("getMemberId")
419 def getMemberId(self,):
420 """This exists only for a basic user/group API compatibility
421 """
422 return self.getGroupId()
423
424 security.declarePublic('getRoles')
425 def getRoles(self):
426 """Return the list of roles assigned to a user."""
427 return self.getGroup().getRoles()
428
429 security.declarePublic('getRolesInContext')
430 def getRolesInContext(self, object):
431 """Return the list of roles assigned to the user, including local
432 roles assigned in context of the passed in object."""
433 return self.getGroup().getRolesInContext(object)
434
435 security.declarePublic('getDomains')
436 def getDomains(self):
437 """Return the list of domain restrictions for a user"""
438 return self.getGroup().getDomains()
439
440 security.declarePublic('has_role')
441 def has_role(self, roles, object=None):
442 """Check to see if a user has a given role or roles."""
443 return self.getGroup().has_role(roles, object)
444
445 # There are other parts of the interface but they are
446 # deprecated for use with CMF applications.
447
448 InitializeClass(GroupData)