diff --git a/CHANGES.txt b/CHANGES.txt
index ffa33bb..1f4a5dc 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -8,6 +8,8 @@ $Id$
New features
+- form for user registration (``register_user.html``), controlled by options:
+ ``registration_principalfolder``, ``registration_groups``
- new action: ``create task``
- quick search field
- external file/media asset type: options for hiding or showing fields on editing
diff --git a/browser/concept.py b/browser/concept.py
index 9e958c2..e9f47d4 100644
--- a/browser/concept.py
+++ b/browser/concept.py
@@ -197,7 +197,7 @@ class ConceptView(BaseView):
if (cont is not None and not IUnauthenticatedPrincipal.providedBy(
self.request.principal)):
cont.macros.register('portlet_right', 'parents', title=_(u'Parents'),
- subMacro=self.template.macros['parents'],
+ subMacro=concept_macros.macros['parents'],
priority=20, info=self)
@Lazy
diff --git a/browser/concept_macros.pt b/browser/concept_macros.pt
index 261cc8f..15f8fca 100644
--- a/browser/concept_macros.pt
+++ b/browser/concept_macros.pt
@@ -48,7 +48,7 @@
renderer field/displayRenderer"
tal:condition="nocall:value">
: |
- |
+ |
diff --git a/organize/README.txt b/organize/README.txt
index a97a541..1096d85 100644
--- a/organize/README.txt
+++ b/organize/README.txt
@@ -407,7 +407,8 @@ Send Email to Members
>>> from loops.organize.browser.party import SendEmailForm
>>> form = SendEmailForm(menu, TestRequest())
>>> 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
u"loops Notification from '$site'"
>>> form.mailBody
diff --git a/organize/auth.py b/organize/auth.py
index fa50f01..cd94083 100644
--- a/organize/auth.py
+++ b/organize/auth.py
@@ -27,6 +27,7 @@ from zope.app.container.contained import Contained
from zope import component
from zope.interface import Interface, implements
from zope.app.authentication.interfaces import IAuthenticatorPlugin
+from zope.app.authentication.principalfolder import IInternalPrincipal
from zope.app.authentication.principalfolder import PrincipalInfo
from zope.app.principalannotation.interfaces import IPrincipalAnnotationUtility
from zope.app.security.interfaces import IAuthentication
diff --git a/organize/browser/member.py b/organize/browser/member.py
index 4875298..38f6cad 100644
--- a/organize/browser/member.py
+++ b/organize/browser/member.py
@@ -76,15 +76,20 @@ class PersonalInfo(ConceptView):
class MemberRegistration(NodeView, CreateForm):
interface = IMemberRegistration
- message = _(u'You have been registered.')
+ message = _(u'The user account has been created.')
formErrors = dict(
confirm_nomatch=FormError(_(u'Password and password confirmation do not match.')),
+ duplicate_loginname=FormError(_('Login name already taken.')),
)
label = _(u'Member Registration')
label_submit = _(u'Register')
+ permissions_key = u'registration.permissions'
+ roles_key = u'registration.roles'
+ registration_adapter_key = u'registration.adapter'
+
@Lazy
def macro(self):
return schema_macros.macros['form']
@@ -123,11 +128,20 @@ class MemberRegistration(NodeView, CreateForm):
return True
login = form.get('loginName')
regMan = IMemberRegistrationManager(self.context.getLoopsRoot())
- self.object = regMan.register(login, pw,
- form.get('lastName'), form.get('firstName'))
+ result = regMan.register(login, pw,
+ 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
- self.request.response.redirect('%s/login.html?login=%s&message=%s'
- % (self.url, login, msg))
+ #self.request.response.redirect('%s/login.html?login=%s&message=%s'
+ # % (self.url, login, msg))
+ self.request.response.redirect('%s?message=%s' % (self.url, msg))
return False
diff --git a/organize/member.py b/organize/member.py
index 4d7907b..37c5045 100644
--- a/organize/member.py
+++ b/organize/member.py
@@ -24,10 +24,13 @@ $Id$
from zope import interface, component, schema
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.interface import implements
from zope.app.authentication.interfaces import IPluggableAuthentication
from zope.app.authentication.interfaces import IAuthenticatorPlugin
+from zope.app.authentication.principalfolder import IInternalPrincipal
from zope.app.authentication.principalfolder import InternalPrincipal
from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.app.security.interfaces import IAuthentication, PrincipalLookupError
@@ -35,10 +38,12 @@ from zope.event import notify
from zope.i18nmessageid import MessageFactory
from zope.cachedescriptors.property import Lazy
+from cybertools.meta.interfaces import IOptions
from cybertools.typology.interfaces import IType
from loops.common import adapted
from loops.concept import Concept
from loops.interfaces import ILoops
+from loops.organize.auth import IPersonBasedAuthenticator
from loops.organize.interfaces import IMemberRegistrationManager
from loops.organize.util import getPrincipalFolder, getGroupsFolder
from loops.organize.util import getInternalPrincipal
@@ -51,54 +56,60 @@ class MemberRegistrationManager(object):
implements(IMemberRegistrationManager)
adapts(ILoops)
+ person_typeName = 'person'
+ default_principalfolder = 'loops'
+ principalfolder_key = u'registration.principalfolder'
+ groups_key = u'registration.groups'
+
def __init__(self, context):
self.context = context
def register(self, userId, password, lastName, firstName=u'',
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:
- pFolder = getPrincipalFolder(self.context)
- # if isinstance(pFolder, PersonBasedAuthenticator):
- # pFolder.setPassword(userId, password)
- # else:
- title = firstName and ' '.join((firstName, lastName)) or lastName
- principal = InternalPrincipal(userId, password, title)
- if useExisting:
- if userId not in pFolder:
- pFolder[userId] = principal
+ pFolder = getPrincipalFolder(self.context, pfName)
+ if IPersonBasedAuthenticator.providedBy(pFolder):
+ pFolder.setPassword(userId, password)
else:
- pFolder[userId] = principal
+ title = firstName and ' '.join((firstName, lastName)) or lastName
+ principal = InternalPrincipal(userId, password, title)
+ if useExisting:
+ if userId not in pFolder:
+ pFolder[userId] = principal
+ else:
+ if userId in pFolder:
+ return dict(fieldName='loginName', error='duplicate_loginname')
+ else:
+ pFolder[userId] = principal
# step 2 (optional): assign to group(s)
- personType = self.context.getLoopsRoot().getConceptManager()['person']
- od = getOptionsDict(adapted(personType).options)
- groupInfo = od.get('group')
- if groupInfo:
- gfName, groupNames = groupInfo.split(':')
+ groups = options(self.groups_key, ())
+ for groupInfo in groups:
+ names = groupInfo.split(':')
+ if len(names) == 1:
+ gName, gfName = names[0], None
+ else:
+ gName, gfName = names
gFolder = getGroupsFolder(gfName)
- if not groups:
- groups = groupNames.split(',')
- else:
- gFolder = getGroupsFolder()
- if gFolder is not None:
- for g in groups:
- group = gFolder.get(g)
+ if gFolder is not None:
+ group = gFolder.get(gName)
if group is not None:
members = list(group.principals)
members.append(pFolder.prefix + userId)
group.principals = members
# step 3: create a corresponding person concept:
- cm = self.context.getConceptManager()
- id = baseId = 'person.' + userId
- # TODO: use NameChooser
- if useExisting and id in cm:
- person = cm[id]
+ name = baseId = 'person.' + userId
+ if useExisting and name in concepts:
+ person = concepts[name]
else:
- num = 0
- while id in cm:
- num +=1
- id = baseId + str(num)
- person = cm[id] = Concept(title)
- person.conceptType = cm['person']
+ person = Concept(title)
+ name = INameChooser(concepts).chooseName(name, person)
+ concepts[name] = person
+ person.conceptType = personType.context
personAdapter = adapted(person)
personAdapter.firstName = firstName
personAdapter.lastName = lastName
@@ -110,7 +121,7 @@ class MemberRegistrationManager(object):
return personAdapter
def changePassword(self, principal, oldPw, newPw):
- if not isinstance(principal, InternalPrincipal):
+ if not IInternalPrincipal.providedBy(principal):
principal = getInternalPrincipal(principal.id)
if not principal.checkPassword(oldPw):
return False
diff --git a/organize/util.py b/organize/util.py
index 78a9459..e31f7fc 100644
--- a/organize/util.py
+++ b/organize/util.py
@@ -59,8 +59,9 @@ def getGroupsFolder(context=None, name='gloops'):
return getPrincipalFolder(authPluginId=name, ignoreErrors=True)
-def getInternalPrincipal(id, context=None):
- pau = component.getUtility(IAuthentication, context=context)
+def getInternalPrincipal(id, context=None, pau=None):
+ if pau is None:
+ pau = component.getUtility(IAuthentication, context=context)
if not IPluggableAuthentication.providedBy(pau):
raise ValueError(u'There is no pluggable authentication '
'utility available.')
@@ -68,7 +69,8 @@ def getInternalPrincipal(id, context=None):
next = queryNextUtility(pau, IAuthentication)
if next is None:
raise PrincipalLookupError(id)
- return next.getPrincipal(id)
+ #return next.getPrincipal(id)
+ return getInternalPrincipal(id, context, pau=next)
id = id[len(pau.prefix):]
for name, authplugin in pau.getAuthenticatorPlugins():
if not id.startswith(authplugin.prefix):
@@ -79,7 +81,8 @@ def getInternalPrincipal(id, context=None):
return principal
next = queryNextUtility(pau, IAuthentication)
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)