--- /dev/null
+# -*- 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 usergroup tool.
+"""
+__version__ = "$Revision$"
+# $Source: $
+# $Id: GroupsTool.py 50142 2007-09-25 13:13:12Z wichert $
+__docformat__ = 'restructuredtext'
+
+from Products.CMFCore.utils import UniqueObject
+from Products.CMFCore.utils import getToolByName
+from Products.CMFCore.utils import _checkPermission
+from OFS.SimpleItem import SimpleItem
+from Globals import InitializeClass, DTMLFile, MessageDialog
+from Acquisition import aq_base
+from AccessControl.User import nobody
+from AccessControl import ClassSecurityInfo
+from ZODB.POSException import ConflictError
+# BBB CMF < 1.5
+try:
+ from Products.CMFCore.permissions import ManagePortal
+ from Products.CMFCore.permissions import View
+ from Products.CMFCore.permissions import ViewManagementScreens
+except ImportError:
+ from Products.CMFCore.CMFCorePermissions import ManagePortal
+ from Products.CMFCore.CMFCorePermissions import View
+ from Products.CMFCore.CMFCorePermissions import ViewManagementScreens
+
+from Products.GroupUserFolder import postonly
+from GroupsToolPermissions import AddGroups
+from GroupsToolPermissions import ManageGroups
+from GroupsToolPermissions import DeleteGroups
+from GroupsToolPermissions import ViewGroups
+from GroupsToolPermissions import SetGroupOwnership
+from Products.CMFCore.ActionProviderBase import ActionProviderBase
+from interfaces.portal_groups import portal_groups as IGroupsTool
+from global_symbols import *
+
+# Optional feature-preview support
+import PloneFeaturePreview
+
+class GroupsTool (UniqueObject, SimpleItem, ActionProviderBase, ):
+ """ This tool accesses group data through a GRUF acl_users object.
+
+ It can be replaced with something that groups member data in a
+ different way.
+ """
+ # Show implementation only if IGroupsTool is defined
+ # The latter will work only with Plone 1.1 => hence, the if
+ if hasattr(ActionProviderBase, '__implements__'):
+ __implements__ = (IGroupsTool, ActionProviderBase.__implements__)
+
+ id = 'portal_groups'
+ meta_type = 'CMF Groups Tool'
+ _actions = ()
+
+ security = ClassSecurityInfo()
+
+ groupworkspaces_id = "groups"
+ groupworkspaces_title = "Groups"
+ groupWorkspacesCreationFlag = 1
+ groupWorkspaceType = "Folder"
+ groupWorkspaceContainerType = "Folder"
+
+ manage_options=(
+ ( { 'label' : 'Configure'
+ , 'action' : 'manage_config'
+ },
+ ) + ActionProviderBase.manage_options +
+ ( { 'label' : 'Overview'
+ , 'action' : 'manage_overview'
+ },
+ ) + SimpleItem.manage_options)
+
+ # #
+ # ZMI methods #
+ # #
+ security.declareProtected(ViewManagementScreens, 'manage_overview')
+ manage_overview = DTMLFile('dtml/explainGroupsTool', globals()) # unlike MembershipTool
+ security.declareProtected(ViewManagementScreens, 'manage_config')
+ manage_config = DTMLFile('dtml/configureGroupsTool', globals())
+
+ security.declareProtected(ManagePortal, 'manage_setGroupWorkspacesFolder')
+ def manage_setGroupWorkspacesFolder(self, id='groups', title='Groups', REQUEST=None):
+ """ZMI method for workspace container name set."""
+ self.setGroupWorkspacesFolder(id, title)
+ return self.manage_config(manage_tabs_message="Workspaces folder name set to %s" % id)
+
+ security.declareProtected(ManagePortal, 'manage_setGroupWorkspaceType')
+ def manage_setGroupWorkspaceType(self, type='Folder', REQUEST=None):
+ """ZMI method for workspace type set."""
+ self.setGroupWorkspaceType(type)
+ return self.manage_config(manage_tabs_message="Group Workspaces type set to %s" % type)
+
+ security.declareProtected(ManagePortal, 'manage_setGroupWorkspaceContainerType')
+ def manage_setGroupWorkspaceContainerType(self, type='Folder', REQUEST=None):
+ """ZMI method for workspace type set."""
+ self.setGroupWorkspaceContainerType(type)
+ return self.manage_config(manage_tabs_message="Group Workspaces container type set to %s" % type)
+
+ security.declareProtected(ViewGroups, 'getGroupById')
+ def getGroupById(self, id):
+ """
+ Returns the portal_groupdata-ish object for a group corresponding to this id.
+ """
+ if id==None:
+ return None
+ g = self.acl_users.getGroupByName(id, None)
+ if g is not None:
+ g = self.wrapGroup(g)
+ return g
+
+ security.declareProtected(ViewGroups, 'getGroupsByUserId')
+ def getGroupsByUserId(self, userid):
+ """Return a list of the groups the user corresponding to 'userid' belongs to."""
+ #log("getGroupsByUserId(%s)" % userid)
+ user = self.acl_users.getUser(userid)
+ #log("user '%s' is in groups %s" % (userid, user.getGroups()))
+ if user:
+ groups = user.getGroups() or []
+ else:
+ groups = []
+ return [self.getGroupById(elt) for elt in groups]
+
+ security.declareProtected(ViewGroups, 'listGroups')
+ def listGroups(self):
+ """Return a list of the available portal_groupdata-ish objects."""
+ return [ self.wrapGroup(elt) for elt in self.acl_users.getGroups() ]
+
+ security.declareProtected(ViewGroups, 'listGroupIds')
+ def listGroupIds(self):
+ """Return a list of the available groups' ids as entered (without group prefixes)."""
+ return self.acl_users.getGroupNames()
+
+ security.declareProtected(ViewGroups, 'listGroupNames')
+ def listGroupNames(self):
+ """Return a list of the available groups' ids as entered (without group prefixes)."""
+ return self.acl_users.getGroupNames()
+
+ security.declarePublic("isGroup")
+ def isGroup(self, u):
+ """Test if a user/group object is a group or not.
+ You must pass an object you get earlier with wrapUser() or wrapGroup()
+ """
+ base = aq_base(u)
+ if hasattr(base, "isGroup") and base.isGroup():
+ return 1
+ return 0
+
+ security.declareProtected(View, 'searchForGroups')
+ def searchForGroups(self, REQUEST = {}, **kw):
+ """Return a list of groups meeting certain conditions. """
+ # arguments need to be better refined?
+ if REQUEST:
+ dict = REQUEST
+ else:
+ dict = kw
+
+ name = dict.get('name', None)
+ email = dict.get('email', None)
+ roles = dict.get('roles', None)
+ title = dict.get('title', None)
+ title_or_name = dict.get('title_or_name', None)
+
+ last_login_time = dict.get('last_login_time', None)
+ #is_manager = self.checkPermission('Manage portal', self)
+
+ if name:
+ name = name.strip().lower()
+ if not name:
+ name = None
+ if email:
+ email = email.strip().lower()
+ if not email:
+ email = None
+ if title:
+ title = title.strip().lower()
+ if title_or_name:
+ title_or_name = title_or_name.strip().lower()
+ if not title:
+ title = None
+
+ res = []
+ portal = self.portal_url.getPortalObject()
+ for g in portal.portal_groups.listGroups():
+ #if not (g.listed or is_manager):
+ # continue
+ if name:
+ if (g.getGroupName().lower().find(name) == -1) and (g.getGroupId().lower().find(name) == -1):
+ continue
+ if email:
+ if g.email.lower().find(email) == -1:
+ continue
+ if roles:
+ group_roles = g.getRoles()
+ found = 0
+ for r in roles:
+ if r in group_roles:
+ found = 1
+ break
+ if not found:
+ continue
+ if title:
+ if g.title.lower().find(title) == -1:
+ continue
+ if title_or_name:
+ # first search for title
+ if g.title.lower().find(title_or_name) == -1:
+ # not found, now search for name
+ if (g.getGroupName().lower().find(title_or_name) == -1) and (g.getGroupId().lower().find(title_or_name) == -1):
+ continue
+
+ if last_login_time:
+ if g.last_login_time < last_login_time:
+ continue
+ res.append(g)
+
+ return res
+
+ security.declareProtected(AddGroups, 'addGroup')
+ def addGroup(self, id, roles = [], groups = [], REQUEST=None, *args, **kw):
+ """Create a group, and a group workspace if the toggle is on, with the supplied id, roles, and domains.
+
+ Underlying user folder must support adding users via the usual Zope API.
+ Passwords for groups ARE irrelevant in GRUF."""
+ if id in self.listGroupIds():
+ raise ValueError, "Group '%s' already exists." % (id, )
+ self.acl_users.userFolderAddGroup(id, roles = roles, groups = groups )
+ self.createGrouparea(id)
+ self.getGroupById(id).setProperties(**kw)
+ addGroup = postonly(addGroup)
+
+ security.declareProtected(ManageGroups, 'editGroup')
+ def editGroup(self, id, roles = None, groups = None, REQUEST=None, *args, **kw):
+ """Edit the given group with the supplied password, roles, and domains.
+
+ Underlying user folder must support editing users via the usual Zope API.
+ Passwords for groups seem to be currently irrelevant in GRUF."""
+ self.acl_users.userFolderEditGroup(id, roles = roles, groups = groups, )
+ self.getGroupById(id).setProperties(**kw)
+ editGroup = postonly(editGroup)
+
+ security.declareProtected(DeleteGroups, 'removeGroups')
+ def removeGroups(self, ids, keep_workspaces=0, REQUEST=None):
+ """Remove the group in the provided list (if possible).
+
+ Will by default remove this group's GroupWorkspace if it exists. You may
+ turn this off by specifying keep_workspaces=true.
+ Underlying user folder must support removing users via the usual Zope API."""
+ for gid in ids:
+ gdata = self.getGroupById(gid)
+ gusers = gdata.getGroupMembers()
+ for guser in gusers:
+ gdata.removeMember(guser.id)
+
+ self.acl_users.userFolderDelGroups(ids)
+ gwf = self.getGroupWorkspacesFolder()
+ if not gwf: # _robert_
+ return
+ if not keep_workspaces:
+ for id in ids:
+ if hasattr(aq_base(gwf), id):
+ gwf._delObject(id)
+ removeGroups = postonly(removeGroups)
+
+ security.declareProtected(SetGroupOwnership, 'setGroupOwnership')
+ def setGroupOwnership(self, group, object, REQUEST=None):
+ """Make the object 'object' owned by group 'group' (a portal_groupdata-ish object).
+
+ For GRUF this is easy. Others may have to re-implement."""
+ user = group.getGroup()
+ if user is None:
+ raise ValueError, "Invalid group: '%s'." % (group, )
+ object.changeOwnership(user)
+ object.manage_setLocalRoles(user.getId(), ['Owner'])
+ setGroupOwnership = postonly(setGroupOwnership)
+
+ security.declareProtected(ManagePortal, 'setGroupWorkspacesFolder')
+ def setGroupWorkspacesFolder(self, id="", title=""):
+ """ Set the location of the Group Workspaces folder by id.
+
+ The Group Workspaces Folder contains all the group workspaces, just like the
+ Members folder contains all the member folders.
+
+ If anyone really cares, we can probably make the id work as a path as well,
+ but for the moment it's only an id for a folder in the portal root, just like the
+ corresponding MembershipTool functionality. """
+ self.groupworkspaces_id = id.strip()
+ self.groupworkspaces_title = title
+
+ security.declareProtected(ManagePortal, 'getGroupWorkspacesFolderId')
+ def getGroupWorkspacesFolderId(self):
+ """ Get the Group Workspaces folder object's id.
+
+ The Group Workspaces Folder contains all the group workspaces, just like the
+ Members folder contains all the member folders. """
+ return self.groupworkspaces_id
+
+ security.declareProtected(ManagePortal, 'getGroupWorkspacesFolderTitle')
+ def getGroupWorkspacesFolderTitle(self):
+ """ Get the Group Workspaces folder object's title.
+ """
+ return self.groupworkspaces_title
+
+ security.declarePublic('getGroupWorkspacesFolder')
+ def getGroupWorkspacesFolder(self):
+ """ Get the Group Workspaces folder object.
+
+ The Group Workspaces Folder contains all the group workspaces, just like the
+ Members folder contains all the member folders. """
+ parent = self.aq_inner.aq_parent
+ folder = getattr(parent, self.getGroupWorkspacesFolderId(), None)
+ return folder
+
+ security.declareProtected(ManagePortal, 'toggleGroupWorkspacesCreation')
+ def toggleGroupWorkspacesCreation(self, REQUEST=None):
+ """ Toggles the flag for creation of a GroupWorkspaces folder upon creation of the group. """
+ if not hasattr(self, 'groupWorkspacesCreationFlag'):
+ self.groupWorkspacesCreationFlag = 0
+
+ self.groupWorkspacesCreationFlag = not self.groupWorkspacesCreationFlag
+
+ m = self.groupWorkspacesCreationFlag and 'turned on' or 'turned off'
+
+ return self.manage_config(manage_tabs_message="Workspaces creation %s" % m)
+
+ security.declareProtected(ManagePortal, 'getGroupWorkspacesCreationFlag')
+ def getGroupWorkspacesCreationFlag(self):
+ """Return the (boolean) flag indicating whether the Groups Tool will create a group workspace
+ upon the creation of the group (if one doesn't exist already). """
+ return self.groupWorkspacesCreationFlag
+
+ security.declareProtected(AddGroups, 'createGrouparea')
+ def createGrouparea(self, id):
+ """Create a space in the portal for the given group, much like member home
+ folders."""
+ parent = self.aq_inner.aq_parent
+ workspaces = self.getGroupWorkspacesFolder()
+ pt = getToolByName( self, 'portal_types' )
+
+ if id and self.getGroupWorkspacesCreationFlag():
+ if workspaces is None:
+ # add GroupWorkspaces folder
+ pt.constructContent(
+ type_name = self.getGroupWorkspaceContainerType(),
+ container = parent,
+ id = self.getGroupWorkspacesFolderId(),
+ )
+ workspaces = self.getGroupWorkspacesFolder()
+ workspaces.setTitle(self.getGroupWorkspacesFolderTitle())
+ workspaces.setDescription("Container for " + self.getGroupWorkspacesFolderId())
+ # how about ownership?
+
+ # this stuff like MembershipTool...
+ portal_catalog = getToolByName( self, 'portal_catalog' )
+ portal_catalog.unindexObject(workspaces) # unindex GroupWorkspaces folder
+ workspaces._setProperty('right_slots', (), 'lines')
+
+ if workspaces is not None and not hasattr(workspaces.aq_base, id):
+ # add workspace to GroupWorkspaces folder
+ pt.constructContent(
+ type_name = self.getGroupWorkspaceType(),
+ container = workspaces,
+ id = id,
+ )
+ space = self.getGroupareaFolder(id)
+ space.setTitle("%s workspace" % id)
+ space.setDescription("Container for objects shared by this group")
+
+ if hasattr(space, 'setInitialGroup'):
+ # GroupSpaces can have their own policies regarding the group
+ # that they are created for.
+ user = self.getGroupById(id).getGroup()
+ if user is not None:
+ space.setInitialGroup(user)
+ else:
+ space.manage_delLocalRoles(space.users_with_local_role('Owner'))
+ self.setGroupOwnership(self.getGroupById(id), space)
+
+ # Hook to allow doing other things after grouparea creation.
+ notify_script = getattr(workspaces, 'notifyGroupAreaCreated', None)
+ if notify_script is not None:
+ notify_script()
+
+ # Re-indexation
+ portal_catalog = getToolByName( self, 'portal_catalog' )
+ portal_catalog.reindexObject(space)
+
+ security.declareProtected(ManagePortal, 'getGroupWorkspaceType')
+ def getGroupWorkspaceType(self):
+ """Return the Type (as in TypesTool) to make the GroupWorkspace."""
+ return self.groupWorkspaceType
+
+ security.declareProtected(ManagePortal, 'setGroupWorkspaceType')
+ def setGroupWorkspaceType(self, type):
+ """Set the Type (as in TypesTool) to make the GroupWorkspace."""
+ self.groupWorkspaceType = type
+
+ security.declareProtected(ManagePortal, 'getGroupWorkspaceContainerType')
+ def getGroupWorkspaceContainerType(self):
+ """Return the Type (as in TypesTool) to make the GroupWorkspace."""
+ return self.groupWorkspaceContainerType
+
+ security.declareProtected(ManagePortal, 'setGroupWorkspaceContainerType')
+ def setGroupWorkspaceContainerType(self, type):
+ """Set the Type (as in TypesTool) to make the GroupWorkspace."""
+ self.groupWorkspaceContainerType = type
+
+ security.declarePublic('getGroupareaFolder')
+ def getGroupareaFolder(self, id=None, verifyPermission=0):
+ """Returns the object of the group's work area."""
+ if id is None:
+ group = self.getAuthenticatedMember()
+ if not hasattr(member, 'getGroupId'):
+ return None
+ id = group.getGroupId()
+ workspaces = self.getGroupWorkspacesFolder()
+ if workspaces:
+ try:
+ folder = workspaces[id]
+ if verifyPermission and not _checkPermission('View', folder):
+ # Don't return the folder if the user can't get to it.
+ return None
+ return folder
+ except KeyError: pass
+ return None
+
+ security.declarePublic('getGroupareaURL')
+ def getGroupareaURL(self, id=None, verifyPermission=0):
+ """Returns the full URL to the group's work area."""
+ ga = self.getGroupareaFolder(id, verifyPermission)
+ if ga is not None:
+ return ga.absolute_url()
+ else:
+ return None
+
+ security.declarePrivate('wrapGroup')
+ def wrapGroup(self, g, wrap_anon=0):
+ ''' Sets up the correct acquisition wrappers for a group
+ object and provides an opportunity for a portal_memberdata
+ tool to retrieve and store member data independently of
+ the user object.
+ '''
+ b = getattr(g, 'aq_base', None)
+ if b is None:
+ # u isn't wrapped at all. Wrap it in self.acl_users.
+ b = g
+ g = g.__of__(self.acl_users)
+ if (b is nobody and not wrap_anon) or hasattr(b, 'getMemberId'):
+ # This user is either not recognized by acl_users or it is
+ # already registered with something that implements the
+ # member data tool at least partially.
+ return g
+
+ parent = self.aq_inner.aq_parent
+ base = getattr(parent, 'aq_base', None)
+ if hasattr(base, 'portal_groupdata'):
+ # Get portal_groupdata to do the wrapping.
+ Log(LOG_DEBUG, "parent", parent)
+ gd = getToolByName(parent, 'portal_groupdata')
+ Log(LOG_DEBUG, "group data", gd)
+ try:
+ #log("wrapping group %s" % g)
+ portal_group = gd.wrapGroup(g)
+ return portal_group
+ except ConflictError:
+ raise
+ except:
+ import logging
+ logger = logging.getLogger('GroupUserFolder.GroupsTool')
+ logger.exception('Error during wrapGroup')
+ # Failed.
+ return g
+
+InitializeClass(GroupsTool)