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:
parent
55b231e8f9
commit
6a45e33890
8 changed files with 78 additions and 46 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
renderer field/displayRenderer"
|
||||
tal:condition="nocall:value">
|
||||
<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>
|
||||
</tal:row>
|
||||
</table>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
pFolder = getPrincipalFolder(self.context, pfName)
|
||||
if IPersonBasedAuthenticator.providedBy(pFolder):
|
||||
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
|
||||
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(':')
|
||||
gFolder = getGroupsFolder(gfName)
|
||||
if not groups:
|
||||
groups = groupNames.split(',')
|
||||
groups = options(self.groups_key, ())
|
||||
for groupInfo in groups:
|
||||
names = groupInfo.split(':')
|
||||
if len(names) == 1:
|
||||
gName, gfName = names[0], None
|
||||
else:
|
||||
gFolder = getGroupsFolder()
|
||||
gName, gfName = names
|
||||
gFolder = getGroupsFolder(gfName)
|
||||
if gFolder is not None:
|
||||
for g in groups:
|
||||
group = gFolder.get(g)
|
||||
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
|
||||
|
|
|
@ -59,7 +59,8 @@ def getGroupsFolder(context=None, name='gloops'):
|
|||
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)
|
||||
if not IPluggableAuthentication.providedBy(pau):
|
||||
raise ValueError(u'There is no pluggable authentication '
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue