X-Git-Url: https://scm.cri.minesparis.psl.eu/git/GroupUserFolder.git/blobdiff_plain/e9d14b6b5cc9cd4775c60cb340b5c4c787536fc3..3e1ba4932c34812cf2f6f3569b0f0dbea97b7a0b:/Products/GroupUserFolder/GroupDataTool.py?ds=inline diff --git a/Products/GroupUserFolder/GroupDataTool.py b/Products/GroupUserFolder/GroupDataTool.py new file mode 100644 index 0000000..fc1bb02 --- /dev/null +++ b/Products/GroupUserFolder/GroupDataTool.py @@ -0,0 +1,448 @@ +# -*- coding: utf-8 -*- +## GroupUserFolder +## Copyright (C)2006 Ingeniweb + +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. + +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +## You should have received a copy of the GNU General Public License +## along with this program; see the file COPYING. If not, write to the +## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +## Copyright (c) 2003 The Connexions Project, All Rights Reserved +## initially written by J Cameron Cooper, 11 June 2003 +## concept with Brent Hendricks, George Runyan +""" +Basic group data tool. +""" +__version__ = "$Revision: $" +# $Source: $ +# $Id: GroupDataTool.py 52136 2007-10-21 20:38:00Z encolpe $ +__docformat__ = 'restructuredtext' + +from Products.CMFCore.utils import UniqueObject, getToolByName +from OFS.SimpleItem import SimpleItem +from OFS.PropertyManager import PropertyManager +from Globals import DTMLFile +from Globals import InitializeClass +from AccessControl.Role import RoleManager +from BTrees.OOBTree import OOBTree +from ZPublisher.Converters import type_converters +from Acquisition import aq_inner, aq_parent, aq_base +from AccessControl import ClassSecurityInfo, Permissions, Unauthorized, getSecurityManager +from zope.interface import implements +from Products.CMFCore.interfaces import IActionProvider + +from Products.CMFCore.ActionProviderBase import ActionProviderBase + +# BBB CMF < 1.5 +try: + from Products.CMFCore.permissions import ManagePortal +except ImportError: + from Products.CMFCore.CMFCorePermissions import ManagePortal + +# from Products.CMFCore.MemberDataTool import CleanupTemp + +from interfaces.portal_groupdata import portal_groupdata as IGroupDataTool +from interfaces.portal_groupdata import GroupData as IGroupData +from Products.GroupUserFolder import postonly +from Products.GroupUserFolder.GRUFUser import GRUFGroup + +_marker = [] # Create a new marker object. + +from global_symbols import * + + +class GroupDataTool (UniqueObject, SimpleItem, PropertyManager, ActionProviderBase): + """ This tool wraps group objects, allowing transparent access to properties. + """ + # The latter will work only with Plone 1.1 => hence, the if + implements(IGroupDataTool, IActionProvider) + # __implements__ = (IGroupDataTool, ActionProviderBase.__implements__) + + id = 'portal_groupdata' + meta_type = 'CMF Group Data Tool' + _actions = () + + _v_temps = None + _properties=({'id':'title', 'type': 'string', 'mode': 'wd'},) + + security = ClassSecurityInfo() + + manage_options=( ActionProviderBase.manage_options + + ({ 'label' : 'Overview' + , 'action' : 'manage_overview' + }, + ) + + PropertyManager.manage_options + + SimpleItem.manage_options + ) + + # + # ZMI methods + # + security.declareProtected(ManagePortal, 'manage_overview') + manage_overview = DTMLFile('dtml/explainGroupDataTool', globals()) + + def __init__(self): + self._members = OOBTree() + # Create the default properties. + self._setProperty('description', '', 'text') + self._setProperty('email', '', 'string') + + # + # 'portal_groupdata' interface methods + # + security.declarePrivate('wrapGroup') + def wrapGroup(self, g): + """Returns an object implementing the GroupData interface""" + id = g.getId() + members = self._members + if not members.has_key(id): + # Get a temporary member that might be + # registered later via registerMemberData(). + temps = self._v_temps + if temps is not None and temps.has_key(id): + portal_group = temps[id] + else: + base = aq_base(self) + portal_group = GroupData(base, id) + if temps is None: + self._v_temps = {id:portal_group} +# XXX ClenupTemp doesn't exits +# if hasattr(self, 'REQUEST'): +# # No REQUEST during tests. +# self.REQUEST._hold(CleanupTemp(self)) + else: + temps[id] = portal_group + else: + portal_group = members[id] + # Return a wrapper with self as containment and + # the user as context. + return portal_group.__of__(self).__of__(g) + + security.declarePrivate('registerGroupData') + def registerGroupData(self, g, id): + ''' + Adds the given member data to the _members dict. + This is done as late as possible to avoid side effect + transactions and to reduce the necessary number of + entries. + ''' + self._members[id] = aq_base(g) + +InitializeClass(GroupDataTool) + + +class GroupData (SimpleItem): + + __implements__ = IGroupData + + security = ClassSecurityInfo() + + id = None + _tool = None + + def __init__(self, tool, id): + self.id = id + # Make a temporary reference to the tool. + # The reference will be removed by notifyModified(). + self._tool = tool + + def _getGRUF(self,): + return self.acl_users + + security.declarePrivate('notifyModified') + def notifyModified(self): + # Links self to parent for full persistence. + tool = getattr(self, '_tool', None) + if tool is not None: + del self._tool + tool.registerGroupData(self, self.getId()) + + security.declarePublic('getGroup') + def getGroup(self): + """ Returns the actual group implementation. Varies by group + implementation (GRUF/Nux/et al). In GRUF this is a user object.""" + # The user object is our context, but it's possible for + # restricted code to strip context while retaining + # containment. Therefore we need a simple security check. + parent = aq_parent(self) + bcontext = aq_base(parent) + bcontainer = aq_base(aq_parent(aq_inner(self))) + if bcontext is bcontainer or not hasattr(bcontext, 'getUserName'): + raise 'GroupDataError', "Can't find group data" + # Return the user object, which is our context. + return parent + + def getTool(self): + return aq_parent(aq_inner(self)) + + security.declarePublic("getGroupMemberIds") + def getGroupMemberIds(self,): + """ + Return a list of group member ids + """ + return map(lambda x: x.getMemberId(), self.getGroupMembers()) + + security.declarePublic("getAllGroupMemberIds") + def getAllGroupMemberIds(self,): + """ + Return a list of group member ids + """ + return map(lambda x: x.getMemberId(), self.getAllGroupMembers()) + + security.declarePublic('getGroupMembers') + def getGroupMembers(self, ): + """ + Returns a list of the portal_memberdata-ish members of the group. + This doesn't include TRANSITIVE groups/users. + """ + md = self.portal_memberdata + gd = self.portal_groupdata + ret = [] + for u_name in self.getGroup().getMemberIds(transitive = 0, ): + usr = self._getGRUF().getUserById(u_name) + if not usr: + raise AssertionError, "Cannot retreive a user by its id !" + if usr.isGroup(): + ret.append(gd.wrapGroup(usr)) + else: + ret.append(md.wrapUser(usr)) + return ret + + security.declarePublic('getAllGroupMembers') + def getAllGroupMembers(self, ): + """ + Returns a list of the portal_memberdata-ish members of the group. + This will include transitive groups / users + """ + md = self.portal_memberdata + gd = self.portal_groupdata + ret = [] + for u_name in self.getGroup().getMemberIds(): + usr = self._getGRUF().getUserById(u_name) + if not usr: + raise AssertionError, "Cannot retreive a user by its id !" + if usr.isGroup(): + ret.append(gd.wrapGroup(usr)) + else: + ret.append(md.wrapUser(usr)) + return ret + + def _getGroup(self,): + """ + _getGroup(self,) => Get the underlying group object + """ + return self._getGRUF().getGroupByName(self.getGroupName()) + + + security.declarePrivate("canAdministrateGroup") + def canAdministrateGroup(self,): + """ + Return true if the #current# user can administrate this group + """ + user = getSecurityManager().getUser() + tool = self.getTool() + portal = getToolByName(tool, 'portal_url').getPortalObject() + + # Has manager users pemission? + if user.has_permission(Permissions.manage_users, portal): + return True + + # Is explicitly mentioned as a group administrator? + managers = self.getProperty('delegated_group_member_managers', ()) + if user.getId() in managers: + return True + + # Belongs to a group which is explicitly mentionned as a group administrator + meth = getattr(user, "getAllGroupNames", None) + if meth: + groups = meth() + else: + groups = () + for v in groups: + if v in managers: + return True + + # No right to edit this: we complain. + return False + + security.declarePublic('addMember') + def addMember(self, id, REQUEST=None): + """ Add the existing member with the given id to the group""" + # We check if the current user can directly or indirectly administrate this group + if not self.canAdministrateGroup(): + raise Unauthorized, "You cannot add a member to the group." + self._getGroup().addMember(id) + + # Notify member that they've been changed + mtool = getToolByName(self, 'portal_membership') + member = mtool.getMemberById(id) + if member: + member.notifyModified() + addMember = postonly(addMember) + + security.declarePublic('removeMember') + def removeMember(self, id, REQUEST=None): + """Remove the member with the provided id from the group. + """ + # We check if the current user can directly or indirectly administrate this group + if not self.canAdministrateGroup(): + raise Unauthorized, "You cannot remove a member from the group." + self._getGroup().removeMember(id) + + # Notify member that they've been changed + mtool = getToolByName(self, 'portal_membership') + member = mtool.getMemberById(id) + if member: + member.notifyModified() + removeMember = postonly(removeMember) + + security.declareProtected(Permissions.manage_users, 'setProperties') + def setProperties(self, properties=None, **kw): + '''Allows the manager group to set his/her own properties. + Accepts either keyword arguments or a mapping for the "properties" + argument. + ''' + if properties is None: + properties = kw + return self.setGroupProperties(properties) + + security.declareProtected(Permissions.manage_users, 'setGroupProperties') + def setGroupProperties(self, mapping): + '''Sets the properties of the member. + ''' + # Sets the properties given in the MemberDataTool. + tool = self.getTool() + for id in tool.propertyIds(): + if mapping.has_key(id): + if not self.__class__.__dict__.has_key(id): + value = mapping[id] + if type(value)==type(''): + proptype = tool.getPropertyType(id) or 'string' + if type_converters.has_key(proptype): + value = type_converters[proptype](value) + setattr(self, id, value) + + # Hopefully we can later make notifyModified() implicit. + self.notifyModified() + + security.declarePublic('getProperties') + def getProperties(self, ): + """ Return the properties of this group. Properties are as usual in Zope.""" + tool = self.getTool() + ret = {} + for pty in tool.propertyIds(): + try: + ret[pty] = self.getProperty(pty) + except ValueError: + # We ignore missing ptys + continue + return ret + + security.declarePublic('getProperty') + def getProperty(self, id, default=_marker): + """ Returns the value of the property specified by 'id' """ + tool = self.getTool() + base = aq_base( self ) + + # First, check the wrapper (w/o acquisition). + value = getattr( base, id, _marker ) + if value is not _marker: + return value + + # Then, check the tool and the user object for a value. + tool_value = tool.getProperty( id, _marker ) + user_value = getattr( aq_base(self.getGroup()), id, _marker ) + + # If the tool doesn't have the property, use user_value or default + if tool_value is _marker: + if user_value is not _marker: + return user_value + elif default is not _marker: + return default + else: + raise ValueError, 'The property %s does not exist' % id + + # If the tool has an empty property and we have a user_value, use it + if not tool_value and user_value is not _marker: + return user_value + + # Otherwise return the tool value + return tool_value + + def __str__(self): + return self.getGroupId() + + security.declarePublic("isGroup") + def isGroup(self,): + """ + isGroup(self,) => Return true if this is a group. + Will always return true for groups. + As MemberData objects do not support this method, it is quite useless by now. + So one can use groupstool.isGroup(g) instead to get this information. + """ + return 1 + + ### Group object interface ### + + security.declarePublic('getGroupName') + def getGroupName(self): + """Return the name of the group, without any special decorations (like GRUF prefixes.)""" + return self.getGroup().getName() + + security.declarePublic('getGroupId') + def getGroupId(self): + """Get the ID of the group. The ID can be used, at least from + Python, to get the user from the user's UserDatabase. + Within Plone, all group ids are UNPREFIXED.""" + if isinstance(self, GRUFGroup): + return self.getGroup().getId(unprefixed = 1) + else: + return self.getGroup().getId() + + def getGroupTitleOrName(self): + """Get the Title property of the group. If there is none + then return the name """ + title = self.getProperty('title', None) + return title or self.getGroupName() + + security.declarePublic("getMemberId") + def getMemberId(self,): + """This exists only for a basic user/group API compatibility + """ + return self.getGroupId() + + security.declarePublic('getRoles') + def getRoles(self): + """Return the list of roles assigned to a user.""" + return self.getGroup().getRoles() + + security.declarePublic('getRolesInContext') + def getRolesInContext(self, object): + """Return the list of roles assigned to the user, including local + roles assigned in context of the passed in object.""" + return self.getGroup().getRolesInContext(object) + + security.declarePublic('getDomains') + def getDomains(self): + """Return the list of domain restrictions for a user""" + return self.getGroup().getDomains() + + security.declarePublic('has_role') + def has_role(self, roles, object=None): + """Check to see if a user has a given role or roles.""" + return self.getGroup().has_role(roles, object) + + # There are other parts of the interface but they are + # deprecated for use with CMF applications. + +InitializeClass(GroupData)