Back to index

plone3  3.1.7
GroupDataTool.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 ## GroupUserFolder
00003 ## Copyright (C)2006 Ingeniweb
00004 
00005 ## This program is free software; you can redistribute it and/or modify
00006 ## it under the terms of the GNU General Public License as published by
00007 ## the Free Software Foundation; either version 2 of the License, or
00008 ## (at your option) any later version.
00009 
00010 ## This program is distributed in the hope that it will be useful,
00011 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 ## GNU General Public License for more details.
00014 
00015 ## You should have received a copy of the GNU General Public License
00016 ## along with this program; see the file COPYING. If not, write to the
00017 ## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00018 
00019 ## Copyright (c) 2003 The Connexions Project, All Rights Reserved
00020 ## initially written by J Cameron Cooper, 11 June 2003
00021 ## concept with Brent Hendricks, George Runyan
00022 """
00023 Basic group data tool.
00024 """
00025 __version__ = "$Revision:  $"
00026 # $Source:  $
00027 # $Id: GroupDataTool.py 52136 2007-10-21 20:38:00Z encolpe $
00028 __docformat__ = 'restructuredtext'
00029 
00030 from Products.CMFCore.utils import UniqueObject, getToolByName
00031 from OFS.SimpleItem import SimpleItem
00032 from OFS.PropertyManager import PropertyManager
00033 from Globals import DTMLFile
00034 from Globals import InitializeClass
00035 from AccessControl.Role import RoleManager
00036 from BTrees.OOBTree import OOBTree
00037 from ZPublisher.Converters import type_converters
00038 from Acquisition import aq_inner, aq_parent, aq_base
00039 from AccessControl import ClassSecurityInfo, Permissions, Unauthorized, getSecurityManager
00040 
00041 from Products.CMFCore.ActionProviderBase import ActionProviderBase
00042 # BBB CMF < 1.5
00043 try:
00044     from Products.CMFCore.permissions import ManagePortal
00045 except ImportError:
00046     from Products.CMFCore.CMFCorePermissions import ManagePortal
00047 
00048 from Products.CMFCore.MemberDataTool import CleanupTemp
00049 
00050 from interfaces.portal_groupdata import portal_groupdata as IGroupDataTool
00051 from interfaces.portal_groupdata import GroupData as IGroupData
00052 from Products.GroupUserFolder import postonly
00053 from Products.GroupUserFolder.GRUFUser import GRUFGroup
00054 
00055 _marker = []  # Create a new marker object.
00056 
00057 from global_symbols import *
00058 
00059 
00060 class GroupDataTool (UniqueObject, SimpleItem, PropertyManager, ActionProviderBase):
00061     """ This tool wraps group objects, allowing transparent access to properties.
00062     """
00063     # The latter will work only with Plone 1.1 => hence, the if
00064     __implements__ = (IGroupDataTool, ActionProviderBase.__implements__)
00065 
00066     id = 'portal_groupdata'
00067     meta_type = 'CMF Group Data Tool'
00068     _actions = ()
00069 
00070     _v_temps = None
00071     _properties=({'id':'title', 'type': 'string', 'mode': 'wd'},)
00072 
00073     security = ClassSecurityInfo()
00074 
00075     manage_options=( ActionProviderBase.manage_options +
00076                      ({ 'label' : 'Overview'
00077                        , 'action' : 'manage_overview'
00078                        },
00079                      )
00080                    + PropertyManager.manage_options
00081                    + SimpleItem.manage_options
00082                    )
00083 
00084     #
00085     #   ZMI methods
00086     #
00087     security.declareProtected(ManagePortal, 'manage_overview')
00088     manage_overview = DTMLFile('dtml/explainGroupDataTool', globals())
00089 
00090     def __init__(self):
00091         self._members = OOBTree()
00092         # Create the default properties.
00093         self._setProperty('description', '', 'text')
00094         self._setProperty('email', '', 'string')
00095 
00096     #
00097     #   'portal_groupdata' interface methods
00098     #
00099     security.declarePrivate('wrapGroup')
00100     def wrapGroup(self, g):
00101         """Returns an object implementing the GroupData interface"""
00102         id = g.getId()
00103         members = self._members
00104         if not members.has_key(id):
00105             # Get a temporary member that might be
00106             # registered later via registerMemberData().
00107             temps = self._v_temps
00108             if temps is not None and temps.has_key(id):
00109                 portal_group = temps[id]
00110             else:
00111                 base = aq_base(self)
00112                 portal_group = GroupData(base, id)
00113                 if temps is None:
00114                     self._v_temps = {id:portal_group}
00115                     if hasattr(self, 'REQUEST'):
00116                         # No REQUEST during tests.
00117                         self.REQUEST._hold(CleanupTemp(self))
00118                 else:
00119                     temps[id] = portal_group
00120         else:
00121             portal_group = members[id]
00122         # Return a wrapper with self as containment and
00123         # the user as context.
00124         return portal_group.__of__(self).__of__(g)
00125 
00126     security.declarePrivate('registerGroupData')
00127     def registerGroupData(self, g, id):
00128         '''
00129         Adds the given member data to the _members dict.
00130         This is done as late as possible to avoid side effect
00131         transactions and to reduce the necessary number of
00132         entries.
00133         '''
00134         self._members[id] = aq_base(g)
00135 
00136 InitializeClass(GroupDataTool)
00137 
00138 
00139 class GroupData (SimpleItem):
00140 
00141     __implements__ = IGroupData
00142 
00143     security = ClassSecurityInfo()
00144 
00145     id = None
00146     _tool = None
00147 
00148     def __init__(self, tool, id):
00149         self.id = id
00150         # Make a temporary reference to the tool.
00151         # The reference will be removed by notifyModified().
00152         self._tool = tool
00153 
00154     def _getGRUF(self,):
00155         return self.acl_users
00156 
00157     security.declarePrivate('notifyModified')
00158     def notifyModified(self):
00159         # Links self to parent for full persistence.
00160         tool = getattr(self, '_tool', None)
00161         if tool is not None:
00162             del self._tool
00163             tool.registerGroupData(self, self.getId())
00164 
00165     security.declarePublic('getGroup')
00166     def getGroup(self):
00167         """ Returns the actual group implementation. Varies by group
00168         implementation (GRUF/Nux/et al). In GRUF this is a user object."""
00169         # The user object is our context, but it's possible for
00170         # restricted code to strip context while retaining
00171         # containment.  Therefore we need a simple security check.
00172         parent = aq_parent(self)
00173         bcontext = aq_base(parent)
00174         bcontainer = aq_base(aq_parent(aq_inner(self)))
00175         if bcontext is bcontainer or not hasattr(bcontext, 'getUserName'):
00176             raise 'GroupDataError', "Can't find group data"
00177         # Return the user object, which is our context.
00178         return parent
00179 
00180     def getTool(self):
00181         return aq_parent(aq_inner(self))
00182 
00183     security.declarePublic("getGroupMemberIds")
00184     def getGroupMemberIds(self,):
00185         """
00186         Return a list of group member ids
00187         """
00188         return map(lambda x: x.getMemberId(), self.getGroupMembers())
00189 
00190     security.declarePublic("getAllGroupMemberIds")
00191     def getAllGroupMemberIds(self,):
00192         """
00193         Return a list of group member ids
00194         """
00195         return map(lambda x: x.getMemberId(), self.getAllGroupMembers())
00196 
00197     security.declarePublic('getGroupMembers')
00198     def getGroupMembers(self, ):
00199         """
00200         Returns a list of the portal_memberdata-ish members of the group.
00201         This doesn't include TRANSITIVE groups/users.
00202         """
00203         md = self.portal_memberdata
00204         gd = self.portal_groupdata
00205         ret = []
00206         for u_name in self.getGroup().getMemberIds(transitive = 0, ):
00207             usr = self._getGRUF().getUserById(u_name)
00208             if not usr:
00209                 raise AssertionError, "Cannot retreive a user by its id !"
00210             if usr.isGroup():
00211                 ret.append(gd.wrapGroup(usr))
00212             else:
00213                 ret.append(md.wrapUser(usr))
00214         return ret
00215 
00216     security.declarePublic('getAllGroupMembers')
00217     def getAllGroupMembers(self, ):
00218         """
00219         Returns a list of the portal_memberdata-ish members of the group.
00220         This will include transitive groups / users
00221         """
00222         md = self.portal_memberdata
00223         gd = self.portal_groupdata
00224         ret = []
00225         for u_name in self.getGroup().getMemberIds():
00226             usr = self._getGRUF().getUserById(u_name)
00227             if not usr:
00228                 raise AssertionError, "Cannot retreive a user by its id !"
00229             if usr.isGroup():
00230                 ret.append(gd.wrapGroup(usr))
00231             else:
00232                 ret.append(md.wrapUser(usr))
00233         return ret
00234 
00235     def _getGroup(self,):
00236         """
00237         _getGroup(self,) => Get the underlying group object
00238         """
00239         return self._getGRUF().getGroupByName(self.getGroupName())
00240 
00241 
00242     security.declarePrivate("canAdministrateGroup")
00243     def canAdministrateGroup(self,):
00244         """
00245         Return true if the #current# user can administrate this group
00246         """
00247         user = getSecurityManager().getUser()
00248         tool = self.getTool()
00249         portal = getToolByName(tool, 'portal_url').getPortalObject()
00250         
00251         # Has manager users pemission? 
00252         if user.has_permission(Permissions.manage_users, portal):
00253             return True
00254 
00255         # Is explicitly mentioned as a group administrator?
00256         managers = self.getProperty('delegated_group_member_managers', ())
00257         if user.getId() in managers:
00258             return True
00259 
00260         # Belongs to a group which is explicitly mentionned as a group administrator
00261         meth = getattr(user, "getAllGroupNames", None)
00262         if meth:
00263             groups = meth()
00264         else:
00265             groups = ()
00266         for v in groups:
00267             if v in managers:
00268                 return True
00269 
00270         # No right to edit this: we complain.
00271         return False
00272 
00273     security.declarePublic('addMember')
00274     def addMember(self, id, REQUEST=None):
00275         """ Add the existing member with the given id to the group"""
00276         # We check if the current user can directly or indirectly administrate this group
00277         if not self.canAdministrateGroup():
00278             raise Unauthorized, "You cannot add a member to the group."
00279         self._getGroup().addMember(id)
00280 
00281         # Notify member that they've been changed
00282         mtool = getToolByName(self, 'portal_membership')
00283         member = mtool.getMemberById(id)
00284         if member:
00285             member.notifyModified()
00286     addMember = postonly(addMember)
00287 
00288     security.declarePublic('removeMember')
00289     def removeMember(self, id, REQUEST=None):
00290         """Remove the member with the provided id from the group.
00291         """
00292         # We check if the current user can directly or indirectly administrate this group
00293         if not self.canAdministrateGroup():
00294             raise Unauthorized, "You cannot remove a member from the group."
00295         self._getGroup().removeMember(id)
00296 
00297         # Notify member that they've been changed
00298         mtool = getToolByName(self, 'portal_membership')
00299         member = mtool.getMemberById(id)
00300         if member:
00301             member.notifyModified()
00302     removeMember = postonly(removeMember)
00303 
00304     security.declareProtected(Permissions.manage_users, 'setProperties')
00305     def setProperties(self, properties=None, **kw):
00306         '''Allows the manager group to set his/her own properties.
00307         Accepts either keyword arguments or a mapping for the "properties"
00308         argument.
00309         '''
00310         if properties is None:
00311             properties = kw
00312         return self.setGroupProperties(properties)
00313 
00314     security.declareProtected(Permissions.manage_users, 'setGroupProperties')
00315     def setGroupProperties(self, mapping):
00316         '''Sets the properties of the member.
00317         '''
00318         # Sets the properties given in the MemberDataTool.
00319         tool = self.getTool()
00320         for id in tool.propertyIds():
00321             if mapping.has_key(id):
00322                 if not self.__class__.__dict__.has_key(id):
00323                     value = mapping[id]
00324                     if type(value)==type(''):
00325                         proptype = tool.getPropertyType(id) or 'string'
00326                         if type_converters.has_key(proptype):
00327                             value = type_converters[proptype](value)
00328                     setattr(self, id, value)
00329                     
00330         # Hopefully we can later make notifyModified() implicit.
00331         self.notifyModified()
00332 
00333     security.declarePublic('getProperties')
00334     def getProperties(self, ):
00335         """ Return the properties of this group. Properties are as usual in Zope."""
00336         tool = self.getTool()
00337         ret = {}
00338         for pty in tool.propertyIds():
00339             try:
00340                 ret[pty] = self.getProperty(pty)
00341             except ValueError:
00342                 # We ignore missing ptys
00343                 continue
00344         return ret
00345 
00346     security.declarePublic('getProperty')
00347     def getProperty(self, id, default=_marker):
00348         """ Returns the value of the property specified by 'id' """
00349         tool = self.getTool()
00350         base = aq_base( self )
00351 
00352         # First, check the wrapper (w/o acquisition).
00353         value = getattr( base, id, _marker )
00354         if value is not _marker:
00355             return value
00356 
00357         # Then, check the tool and the user object for a value.
00358         tool_value = tool.getProperty( id, _marker )
00359         user_value = getattr( aq_base(self.getGroup()), id, _marker )
00360 
00361         # If the tool doesn't have the property, use user_value or default
00362         if tool_value is _marker:
00363             if user_value is not _marker:
00364                 return user_value
00365             elif default is not _marker:
00366                 return default
00367             else:
00368                 raise ValueError, 'The property %s does not exist' % id
00369 
00370         # If the tool has an empty property and we have a user_value, use it
00371         if not tool_value and user_value is not _marker:
00372             return user_value
00373 
00374         # Otherwise return the tool value
00375         return tool_value
00376 
00377     def __str__(self):
00378         return self.getGroupId()
00379 
00380     security.declarePublic("isGroup")
00381     def isGroup(self,):
00382         """
00383         isGroup(self,) => Return true if this is a group.
00384         Will always return true for groups.
00385         As MemberData objects do not support this method, it is quite useless by now.
00386         So one can use groupstool.isGroup(g) instead to get this information.
00387         """
00388         return 1
00389 
00390     ### Group object interface ###
00391 
00392     security.declarePublic('getGroupName')
00393     def getGroupName(self):
00394         """Return the name of the group, without any special decorations (like GRUF prefixes.)"""
00395         return self.getGroup().getName()
00396 
00397     security.declarePublic('getGroupId')
00398     def getGroupId(self):
00399         """Get the ID of the group. The ID can be used, at least from
00400         Python, to get the user from the user's UserDatabase.
00401         Within Plone, all group ids are UNPREFIXED."""
00402         if isinstance(self, GRUFGroup):
00403             return self.getGroup().getId(unprefixed = 1)
00404         else:
00405             return self.getGroup().getId()
00406 
00407     def getGroupTitleOrName(self):
00408         """Get the Title property of the group. If there is none
00409         then return the name """
00410         title = self.getProperty('title', None)
00411         return title or self.getGroupName()
00412 
00413     security.declarePublic("getMemberId")
00414     def getMemberId(self,):
00415         """This exists only for a basic user/group API compatibility
00416         """
00417         return self.getGroupId()
00418 
00419     security.declarePublic('getRoles')
00420     def getRoles(self):
00421         """Return the list of roles assigned to a user."""
00422         return self.getGroup().getRoles()
00423 
00424     security.declarePublic('getRolesInContext')
00425     def getRolesInContext(self, object):
00426         """Return the list of roles assigned to the user,  including local
00427         roles assigned in context of the passed in object."""
00428         return self.getGroup().getRolesInContext(object)
00429 
00430     security.declarePublic('getDomains')
00431     def getDomains(self):
00432         """Return the list of domain restrictions for a user"""
00433         return self.getGroup().getDomains()
00434 
00435     security.declarePublic('has_role')
00436     def has_role(self, roles, object=None):
00437         """Check to see if a user has a given role or roles."""
00438         return self.getGroup().has_role(roles, object)
00439 
00440     # There are other parts of the interface but they are
00441     # deprecated for use with CMF applications.
00442 
00443 InitializeClass(GroupData)