A custom MembershipTool
Create your own custom folderish type
======================================
I won't show you that, since this is a trivial thing for someone with advanced programming skill.
Just create your own folderish content type, ZPTs and what ever else you need for it.
There is nothing special needed for the registration, or anything else. Just do it like you would do for a normal product.
For the further document i'll call this custom folderish type MyFolder (to abbreviate things).
Creating a custom MembershipTool
================================
The code presented in this chapter should alltogether go into one file. It actually defines a class MembershipTool that is derived from the MembershipTool class of CMFMember. By defining createMemberarea(), CMFMember.MembershipTool.MembershipTool.createMemberarea() is overridden.
create a file MembershipTool.py in the root of your product.
Now place following imports into it::
from Globals import InitializeClass
from AccessControl import ClassSecurityInfo
from Products.CMFCore.utils import getToolByName
from Products.CMFMember.MembershipTool import MembershipTool as BaseTool
from Products.CMFPlone.PloneUtilities import translate
from Products.CMFPlone.PloneUtilities import _createObjectByType
from Products.Archetypes.utils import shasattr
from Products.CMFCore.CMFCorePermissions import View
The first two imports are quite usual for any content type. The third one imports an import utility that we'll need later on. The forth line imports the MembershipTool of CMFMember with a different name than MembershipTool since we're going to call our class like that. The fifth import is for internationalization since it's always a good idea to internationalize your code when you start writting it. Trying to internationalize it later can be cumbersome. The sixth line imports a function that is quite special, since it not only creates objects by it's type name, but it does so without performing some security checks. This is necessary so that the folder is created regardless if it's an allowed content type within the Members folder. Similair functions, but with additional security checks, are invokeFactory and
fti.constructInstance . The seventh line imports shasattr which is a saver form of hasattr, since the one in python is broken at the moment (for details see Archetypes/utils.py).
After the imports we start to define our class::
class MembershipTool( BaseTool ):
""" Replace the membership tool in order to change the process of
creating the member folder
"""
meta_type='<YOURPRODUCTNAME> Membership Tool'
security = ClassSecurityInfo()
plone_tool = 1
The first function added to this class is a helper function that eases the changing of the owner of a content type and
setting of a title and a description. Since this will most likely happen more than once when creating a custom member area, this is is stuffed into a helper function. So the helper function should look like::
def _changeFolderForMember(self, folder, user, folder_title, folder_description):
""" change ownership, title and description for a folder
"""
# Grant Ownership and Owner role to Member
folder.changeOwnership(user)
folder.__ac_local_roles__ = None
folder.manage_setLocalRoles(user.getId(), ['Owner'])
# set title and description (edit invokes reindexObject)
folder.edit(title=folder_title, description=folder_description)
The next step is to add a function to our MembershipTool class that overrides the one of CMFMember for creating the Membership area (most of the following code was taken from CMFPlone/MembershipTool.py)::
def createMemberarea(self, member_id):
""" creates a costum member folder
"""
# do not create member_area for groups
if shasattr(self, 'portal_groups') and member_id in self.portal_groups.listGroupIds():
return
catalog = getToolByName(self, 'portal_catalog')
membership = getToolByName(self, 'portal_membership')
members = self.getMembersFolder()
if not member_id:
# member_id is optional (see CMFCore.interfaces.portal_membership:
# Create a member area for 'member_id' or authenticated user.)
member = membership.getAuthenticatedMember()
member_id = member.getId()
if hasattr(members, 'aq_explicit'):
members=members.aq_explicit
if members is None:
# no members area
# XXX exception?
return
if shasattr(members, member_id):
# has already this member
# XXX exception
return
_createObjectByType('MyFolder', members, id=member_id)
# get the user object from acl_users
acl_users = self.__getPUS()
user = acl_users.getUser(member_id)
if user is not None:
user= user.__of__(acl_users)
else:
user= getSecurityManager().getUser()
# check that we do not do something wrong
if user.getId() != member_id:
raise NotImplementedError, \
'cannot get user for member area creation'
In order to avoid showing off too much code at once and for better understanding, i'll explain the above code a bit.
The first if statement simply prevents the creation of home areas for groups.
The following three lines try to acquire necessary tools for the creation and assignment of a folder for the authenticated user.
Since the member_id parameter is not part of the very basic MembershipTool, we can't count on it being set. In case it's not set, well fix that by acquiring the authenticated user (not member!! getAuthenticatedMember() anywhere before the first _createObjectType() within createMembershiparea would leed to loops.)
Some checks if the Member folder exists and the member's area of the authenticated user doesn't exist already.
The _createObjectType() creates the folder for the user in the Member folder. Exchange MyFolder with the name of your custom folderish type.
Then fetch the user object.
When this part of the code has run, most of the things is already done. Missing is now only the renaming of the folderish content type and the changing of the ownership. Since internationalization is always a good idea, the translation of strings for the folder titles and descriptions follows::
## get some translations
member_folder_title = translate(
'plone', 'title_member_folder',
{'member': member_id}, self,
default = "%s's Home" % member_id)
member_folder_description = translate(
'plone', 'description_member_folder',
{'member': member_id}, self,
default = 'Home page area that contains the items created ' \
'and collected by %s' % member_id)
member_folder_index_html_title = translate(
'plone', 'title_member_folder_index_html',
{'member': member_id}, self,
default = "Home page for %s" % member_id)
personal_folder_title = translate(
'plone', 'title_member_personal_folder',
{'member': member_id}, self,
default = "Personal Items for %s" % member_id)
personal_folder_description = translate(
'plone', 'description_member_personal_folder',
{'member': member_id}, self,
default = 'contains personal workarea items for %s' % member_id)
The following lines rename the folder and it's description using the acquired translations.::
## Modify member folder
member_folder = self.getHomeFolder(member_id)
# Grant Ownership and Owner role to Member
member_folder.changeOwnership(user)
member_folder.__ac_local_roles__ = None
member_folder.manage_setLocalRoles(member_id, ['Owner'])
# set title and description (edit invokes reindexObject)
member_folder.edit(title=member_folder_title,
description=member_folder_description)
member_folder.reindexObject()
Note the member_folder.reindexObject() is needed since the folder's title changed.
To complete a standard member's home area you'll also have to create the .personal folder.::
## Create personal folder for personal items
_createObjectByType('Folder', member_folder, id=self.personal_id)
personal = getattr(member_folder, self.personal_id)
_changeFolderForMember(personal, user, personal_folder_title,
personal_folder_description)
# Don't add .personal folders to catalog
catalog.unindexObject(personal)
MembershipTool.__doc__ = BaseTool.__doc__
InitializeClass(MembershipTool)
The only difference of the .personal folder to the home area folder is that it's index is removed.
The MembershipTool.__doc__ = BaseTool.__doc__ does ????
The InitializeClass() call is again normal as usual.
You can place any code within the createMemberarea(), but keep in mind that it's normally only executed once per member at his/her initial login. Calls to getAuthenticatedMember should under any circumstance be avoided, since that would lead to an endless loop (thx to wrapuser).

