extend/improve form/processing for registering users, clean up options

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@3329 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2009-04-11 10:07:14 +00:00
parent 55b231e8f9
commit 6a45e33890
8 changed files with 78 additions and 46 deletions

View file

@ -8,6 +8,8 @@ $Id$
New features New features
- form for user registration (``register_user.html``), controlled by options:
``registration_principalfolder``, ``registration_groups``
- new action: ``create task`` - new action: ``create task``
- quick search field - quick search field
- external file/media asset type: options for hiding or showing fields on editing - external file/media asset type: options for hiding or showing fields on editing

View file

@ -197,7 +197,7 @@ class ConceptView(BaseView):
if (cont is not None and not IUnauthenticatedPrincipal.providedBy( if (cont is not None and not IUnauthenticatedPrincipal.providedBy(
self.request.principal)): self.request.principal)):
cont.macros.register('portlet_right', 'parents', title=_(u'Parents'), cont.macros.register('portlet_right', 'parents', title=_(u'Parents'),
subMacro=self.template.macros['parents'], subMacro=concept_macros.macros['parents'],
priority=20, info=self) priority=20, info=self)
@Lazy @Lazy

View file

@ -48,7 +48,7 @@
renderer field/displayRenderer" renderer field/displayRenderer"
tal:condition="nocall:value"> tal:condition="nocall:value">
<td><b tal:content="field/title" i18n:translate="" />:</td> <td><b tal:content="field/title" i18n:translate="" />:</td>
<td><span metal:use-macro="item/template/macros/?renderer" /></td> <td><span metal:use-macro="view/concept_macros/?renderer" /></td>
</tr> </tr>
</tal:row> </tal:row>
</table> </table>

View file

@ -407,7 +407,8 @@ Send Email to Members
>>> from loops.organize.browser.party import SendEmailForm >>> from loops.organize.browser.party import SendEmailForm
>>> form = SendEmailForm(menu, TestRequest()) >>> form = SendEmailForm(menu, TestRequest())
>>> form.members >>> form.members
[{'email': 'john@loopz.org', 'title': u'John'}] [{'email': 'john@loopz.org', 'title': u'John'},
{'email': u'tommy@sawyer.com', 'title': u'Tom Sawyer'}]
>>> form.subject >>> form.subject
u"loops Notification from '$site'" u"loops Notification from '$site'"
>>> form.mailBody >>> form.mailBody

View file

@ -27,6 +27,7 @@ from zope.app.container.contained import Contained
from zope import component from zope import component
from zope.interface import Interface, implements from zope.interface import Interface, implements
from zope.app.authentication.interfaces import IAuthenticatorPlugin from zope.app.authentication.interfaces import IAuthenticatorPlugin
from zope.app.authentication.principalfolder import IInternalPrincipal
from zope.app.authentication.principalfolder import PrincipalInfo from zope.app.authentication.principalfolder import PrincipalInfo
from zope.app.principalannotation.interfaces import IPrincipalAnnotationUtility from zope.app.principalannotation.interfaces import IPrincipalAnnotationUtility
from zope.app.security.interfaces import IAuthentication from zope.app.security.interfaces import IAuthentication

View file

@ -76,15 +76,20 @@ class PersonalInfo(ConceptView):
class MemberRegistration(NodeView, CreateForm): class MemberRegistration(NodeView, CreateForm):
interface = IMemberRegistration interface = IMemberRegistration
message = _(u'You have been registered.') message = _(u'The user account has been created.')
formErrors = dict( formErrors = dict(
confirm_nomatch=FormError(_(u'Password and password confirmation do not match.')), confirm_nomatch=FormError(_(u'Password and password confirmation do not match.')),
duplicate_loginname=FormError(_('Login name already taken.')),
) )
label = _(u'Member Registration') label = _(u'Member Registration')
label_submit = _(u'Register') label_submit = _(u'Register')
permissions_key = u'registration.permissions'
roles_key = u'registration.roles'
registration_adapter_key = u'registration.adapter'
@Lazy @Lazy
def macro(self): def macro(self):
return schema_macros.macros['form'] return schema_macros.macros['form']
@ -123,11 +128,20 @@ class MemberRegistration(NodeView, CreateForm):
return True return True
login = form.get('loginName') login = form.get('loginName')
regMan = IMemberRegistrationManager(self.context.getLoopsRoot()) regMan = IMemberRegistrationManager(self.context.getLoopsRoot())
self.object = regMan.register(login, pw, result = regMan.register(login, pw,
form.get('lastName'), form.get('firstName')) form.get('lastName'), form.get('firstName'),
email=form.get('email'),
phoneNumbers=form.get('phoneNumbers'))
if isinstance(result, dict):
fi = formState.fieldInstances[result['fieldName']]
fi.setError(result['error'], self.formErrors)
formState.severity = max(formState.severity, fi.severity)
return True
self.object = result
msg = self.message msg = self.message
self.request.response.redirect('%s/login.html?login=%s&message=%s' #self.request.response.redirect('%s/login.html?login=%s&message=%s'
% (self.url, login, msg)) # % (self.url, login, msg))
self.request.response.redirect('%s?message=%s' % (self.url, msg))
return False return False

View file

@ -24,10 +24,13 @@ $Id$
from zope import interface, component, schema from zope import interface, component, schema
from zope.app.component import queryNextUtility from zope.app.component import queryNextUtility
from zope.app.container.interfaces import INameChooser
from zope.cachedescriptors.property import Lazy
from zope.component import adapts from zope.component import adapts
from zope.interface import implements from zope.interface import implements
from zope.app.authentication.interfaces import IPluggableAuthentication from zope.app.authentication.interfaces import IPluggableAuthentication
from zope.app.authentication.interfaces import IAuthenticatorPlugin from zope.app.authentication.interfaces import IAuthenticatorPlugin
from zope.app.authentication.principalfolder import IInternalPrincipal
from zope.app.authentication.principalfolder import InternalPrincipal from zope.app.authentication.principalfolder import InternalPrincipal
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.app.security.interfaces import IAuthentication, PrincipalLookupError from zope.app.security.interfaces import IAuthentication, PrincipalLookupError
@ -35,10 +38,12 @@ from zope.event import notify
from zope.i18nmessageid import MessageFactory from zope.i18nmessageid import MessageFactory
from zope.cachedescriptors.property import Lazy from zope.cachedescriptors.property import Lazy
from cybertools.meta.interfaces import IOptions
from cybertools.typology.interfaces import IType from cybertools.typology.interfaces import IType
from loops.common import adapted from loops.common import adapted
from loops.concept import Concept from loops.concept import Concept
from loops.interfaces import ILoops from loops.interfaces import ILoops
from loops.organize.auth import IPersonBasedAuthenticator
from loops.organize.interfaces import IMemberRegistrationManager from loops.organize.interfaces import IMemberRegistrationManager
from loops.organize.util import getPrincipalFolder, getGroupsFolder from loops.organize.util import getPrincipalFolder, getGroupsFolder
from loops.organize.util import getInternalPrincipal from loops.organize.util import getInternalPrincipal
@ -51,54 +56,60 @@ class MemberRegistrationManager(object):
implements(IMemberRegistrationManager) implements(IMemberRegistrationManager)
adapts(ILoops) adapts(ILoops)
person_typeName = 'person'
default_principalfolder = 'loops'
principalfolder_key = u'registration.principalfolder'
groups_key = u'registration.groups'
def __init__(self, context): def __init__(self, context):
self.context = context self.context = context
def register(self, userId, password, lastName, firstName=u'', def register(self, userId, password, lastName, firstName=u'',
groups=[], useExisting=False, **kw): groups=[], useExisting=False, **kw):
concepts = self.context.getConceptManager()
personType = adapted(concepts[self.person_typeName])
options = IOptions(personType)
pfName = options(self.principalfolder_key,
(self.default_principalfolder,))[0]
# step 1: create an internal principal in the loops principal folder: # step 1: create an internal principal in the loops principal folder:
pFolder = getPrincipalFolder(self.context) pFolder = getPrincipalFolder(self.context, pfName)
# if isinstance(pFolder, PersonBasedAuthenticator): if IPersonBasedAuthenticator.providedBy(pFolder):
# pFolder.setPassword(userId, password) pFolder.setPassword(userId, password)
# else: else:
title = firstName and ' '.join((firstName, lastName)) or lastName title = firstName and ' '.join((firstName, lastName)) or lastName
principal = InternalPrincipal(userId, password, title) principal = InternalPrincipal(userId, password, title)
if useExisting: if useExisting:
if userId not in pFolder: if userId not in pFolder:
pFolder[userId] = principal pFolder[userId] = principal
else:
if userId in pFolder:
return dict(fieldName='loginName', error='duplicate_loginname')
else: else:
pFolder[userId] = principal pFolder[userId] = principal
# step 2 (optional): assign to group(s) # step 2 (optional): assign to group(s)
personType = self.context.getLoopsRoot().getConceptManager()['person'] groups = options(self.groups_key, ())
od = getOptionsDict(adapted(personType).options) for groupInfo in groups:
groupInfo = od.get('group') names = groupInfo.split(':')
if groupInfo: if len(names) == 1:
gfName, groupNames = groupInfo.split(':') gName, gfName = names[0], None
gFolder = getGroupsFolder(gfName)
if not groups:
groups = groupNames.split(',')
else: else:
gFolder = getGroupsFolder() gName, gfName = names
gFolder = getGroupsFolder(gfName)
if gFolder is not None: if gFolder is not None:
for g in groups: group = gFolder.get(gName)
group = gFolder.get(g)
if group is not None: if group is not None:
members = list(group.principals) members = list(group.principals)
members.append(pFolder.prefix + userId) members.append(pFolder.prefix + userId)
group.principals = members group.principals = members
# step 3: create a corresponding person concept: # step 3: create a corresponding person concept:
cm = self.context.getConceptManager() name = baseId = 'person.' + userId
id = baseId = 'person.' + userId if useExisting and name in concepts:
# TODO: use NameChooser person = concepts[name]
if useExisting and id in cm:
person = cm[id]
else: else:
num = 0 person = Concept(title)
while id in cm: name = INameChooser(concepts).chooseName(name, person)
num +=1 concepts[name] = person
id = baseId + str(num) person.conceptType = personType.context
person = cm[id] = Concept(title)
person.conceptType = cm['person']
personAdapter = adapted(person) personAdapter = adapted(person)
personAdapter.firstName = firstName personAdapter.firstName = firstName
personAdapter.lastName = lastName personAdapter.lastName = lastName
@ -110,7 +121,7 @@ class MemberRegistrationManager(object):
return personAdapter return personAdapter
def changePassword(self, principal, oldPw, newPw): def changePassword(self, principal, oldPw, newPw):
if not isinstance(principal, InternalPrincipal): if not IInternalPrincipal.providedBy(principal):
principal = getInternalPrincipal(principal.id) principal = getInternalPrincipal(principal.id)
if not principal.checkPassword(oldPw): if not principal.checkPassword(oldPw):
return False return False

View file

@ -59,7 +59,8 @@ def getGroupsFolder(context=None, name='gloops'):
return getPrincipalFolder(authPluginId=name, ignoreErrors=True) return getPrincipalFolder(authPluginId=name, ignoreErrors=True)
def getInternalPrincipal(id, context=None): def getInternalPrincipal(id, context=None, pau=None):
if pau is None:
pau = component.getUtility(IAuthentication, context=context) pau = component.getUtility(IAuthentication, context=context)
if not IPluggableAuthentication.providedBy(pau): if not IPluggableAuthentication.providedBy(pau):
raise ValueError(u'There is no pluggable authentication ' raise ValueError(u'There is no pluggable authentication '
@ -68,7 +69,8 @@ def getInternalPrincipal(id, context=None):
next = queryNextUtility(pau, IAuthentication) next = queryNextUtility(pau, IAuthentication)
if next is None: if next is None:
raise PrincipalLookupError(id) raise PrincipalLookupError(id)
return next.getPrincipal(id) #return next.getPrincipal(id)
return getInternalPrincipal(id, context, pau=next)
id = id[len(pau.prefix):] id = id[len(pau.prefix):]
for name, authplugin in pau.getAuthenticatorPlugins(): for name, authplugin in pau.getAuthenticatorPlugins():
if not id.startswith(authplugin.prefix): if not id.startswith(authplugin.prefix):
@ -79,7 +81,8 @@ def getInternalPrincipal(id, context=None):
return principal return principal
next = queryNextUtility(pau, IAuthentication) next = queryNextUtility(pau, IAuthentication)
if next is not None: if next is not None:
return next.getPrincipal(pau.prefix + id) #return next.getPrincipal(pau.prefix + id)
return getInternalPrincipal(id, context, pau=next)
raise PrincipalLookupError(id) raise PrincipalLookupError(id)