Work in progress: self-service registration (member registration)
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1220 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
c55d35f902
commit
a70fe7d830
5 changed files with 205 additions and 10 deletions
|
@ -1,5 +1,5 @@
|
||||||
<metal:block define-macro="render">
|
<metal:block define-macro="render">
|
||||||
<div>
|
<div tal:attributes="ondblclick python: item.openEditWindow('configure.html')">
|
||||||
<h3 tal:content="item/title">Title</h3>
|
<h3 tal:content="item/title">Title</h3>
|
||||||
<span tal:replace="structure item/render" />
|
<span tal:replace="structure item/render" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
|
|
||||||
<metal:block define-macro="image">
|
<metal:block define-macro="image">
|
||||||
<div>
|
<div tal:attributes="ondblclick python: item.openEditWindow('edit.html')">
|
||||||
<h3 tal:content="item/title">Title</h3>
|
<h3 tal:content="item/title">Title</h3>
|
||||||
<img src="#"
|
<img src="#"
|
||||||
tal:attributes="src string:${view/url}/.target${view/targetId}/view" />
|
tal:attributes="src string:${view/url}/.target${view/targetId}/view" />
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
|
|
||||||
<metal:block define-macro="download">
|
<metal:block define-macro="download">
|
||||||
<div>
|
<div tal:attributes="ondblclick python: item.openEditWindow('edit.html')">
|
||||||
<h3 tal:content="item/title">Title</h3>
|
<h3 tal:content="item/title">Title</h3>
|
||||||
<a href="#"
|
<a href="#"
|
||||||
tal:attributes="href string:${view/url}/.target${view/targetId}/view">
|
tal:attributes="href string:${view/url}/.target${view/targetId}/view">
|
||||||
|
|
|
@ -6,7 +6,7 @@ loops - Linked Objects for Organization and Processing Services
|
||||||
|
|
||||||
Note: This packages depends on cybertools.organize.
|
Note: This packages depends on cybertools.organize.
|
||||||
|
|
||||||
Letz's do some basic set up
|
Let's do some basic set up
|
||||||
|
|
||||||
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
|
||||||
>>> site = placefulSetUp(True)
|
>>> site = placefulSetUp(True)
|
||||||
|
@ -165,6 +165,72 @@ concept assigned we should get an error:
|
||||||
ValueError: ...
|
ValueError: ...
|
||||||
|
|
||||||
|
|
||||||
|
Member Registrations
|
||||||
|
====================
|
||||||
|
|
||||||
|
The member registration needs the whole pluggable authentication stuff
|
||||||
|
with a principal folder:
|
||||||
|
|
||||||
|
>>> from zope.app.appsetup.bootstrap import ensureUtility
|
||||||
|
>>> from zope.app.authentication.authentication import PluggableAuthentication
|
||||||
|
>>> ensureUtility(site, IAuthentication, '', PluggableAuthentication,
|
||||||
|
... copy_to_zlog=False)
|
||||||
|
''
|
||||||
|
>>> pau = component.getUtility(IAuthentication, context=site)
|
||||||
|
|
||||||
|
>>> from zope.app.component.site import UtilityRegistration
|
||||||
|
>>> from zope.app.authentication.principalfolder import PrincipalFolder
|
||||||
|
>>> from zope.app.authentication.interfaces import IAuthenticatorPlugin
|
||||||
|
>>> pFolder = PrincipalFolder('loops.')
|
||||||
|
>>> pau['loops'] = pFolder
|
||||||
|
>>> reg = UtilityRegistration('loops', IAuthenticatorPlugin, pFolder)
|
||||||
|
>>> pau.registrationManager.addRegistration(reg)
|
||||||
|
'UtilityRegistration'
|
||||||
|
>>> reg.status = u'Active'
|
||||||
|
>>> pau.authenticatorPlugins = ('loops',)
|
||||||
|
|
||||||
|
In addition, we have to create at least one node in the view space
|
||||||
|
and register an IMemberRegistrationManager adapter for the loops root object:
|
||||||
|
|
||||||
|
>>> from loops.view import ViewManager, Node
|
||||||
|
>>> views = loopsRoot['views'] = ViewManager()
|
||||||
|
>>> menu = views['menu'] = Node('Home')
|
||||||
|
>>> menu.nodeType = 'menu'
|
||||||
|
|
||||||
|
>>> from loops.organize.member import MemberRegistrationManager
|
||||||
|
>>> from loops.organize.interfaces import IMemberRegistrationManager
|
||||||
|
>>> from loops.interfaces import ILoops
|
||||||
|
>>> component.provideAdapter(MemberRegistrationManager)
|
||||||
|
|
||||||
|
Now we can enter the registration info for a new member (after having made
|
||||||
|
sure that a principal object can be served by a corresponding factory):
|
||||||
|
|
||||||
|
>>> from zope.app.authentication.principalfolder import FoundPrincipalFactory
|
||||||
|
>>> component.provideAdapter(FoundPrincipalFactory)
|
||||||
|
|
||||||
|
>>> form = {'field.userId': u'newuser',
|
||||||
|
... 'field.passwd': u'quack',
|
||||||
|
... 'field.passwdConfirm': u'quack',
|
||||||
|
... 'field.lastName': u'Sawyer',
|
||||||
|
... 'field.firstName': u'Tom'}
|
||||||
|
>>> from zope.publisher.browser import TestRequest
|
||||||
|
>>> request = TestRequest(form=form)
|
||||||
|
|
||||||
|
and register it
|
||||||
|
|
||||||
|
>>> from loops.organize.browser import MemberRegistration
|
||||||
|
>>> regView = MemberRegistration(menu, request)
|
||||||
|
>>> personAdapter = regView.register()
|
||||||
|
|
||||||
|
>>> personAdapter.context.__name__, personAdapter.lastName, personAdapter.userId
|
||||||
|
(u'newuser', u'Sawyer', u'loops.newuser')
|
||||||
|
|
||||||
|
Now we can also retrieve it from the authentication utility:
|
||||||
|
|
||||||
|
>>> pau.getPrincipal('loops.newuser').title
|
||||||
|
u'Tom Sawyer'
|
||||||
|
|
||||||
|
|
||||||
Fin de partie
|
Fin de partie
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|
|
@ -28,10 +28,14 @@ from zope.app import zapi
|
||||||
from zope.app.pagetemplate import ViewPageTemplateFile
|
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||||
from zope.app.principalannotation import annotations
|
from zope.app.principalannotation import annotations
|
||||||
from zope.cachedescriptors.property import Lazy
|
from zope.cachedescriptors.property import Lazy
|
||||||
|
from zope.i18nmessageid import MessageFactory
|
||||||
|
|
||||||
from loops.browser.common import BaseView
|
from loops.browser.common import BaseView
|
||||||
from loops.browser.concept import ConceptRelationView
|
from loops.browser.concept import ConceptRelationView
|
||||||
from loops.organize.interfaces import ANNOTATION_KEY
|
from loops.organize.interfaces import ANNOTATION_KEY, IMemberRegistrationManager
|
||||||
|
from loops.organize.interfaces import raiseValidationError
|
||||||
|
|
||||||
|
_ = MessageFactory('zope')
|
||||||
|
|
||||||
|
|
||||||
class MyConcepts(BaseView):
|
class MyConcepts(BaseView):
|
||||||
|
@ -65,6 +69,22 @@ class MyConcepts(BaseView):
|
||||||
for r in self.person.getResourceRelations():
|
for r in self.person.getResourceRelations():
|
||||||
yield ConceptRelationView(r, self.request, contextIsSecond=True)
|
yield ConceptRelationView(r, self.request, contextIsSecond=True)
|
||||||
|
|
||||||
@Lazy
|
|
||||||
def view(self):
|
class MemberRegistration(object):
|
||||||
return self
|
|
||||||
|
def __init__(self, context, request):
|
||||||
|
self.context = context
|
||||||
|
self.request = request
|
||||||
|
|
||||||
|
def register(self):
|
||||||
|
form = self.request.form
|
||||||
|
pw = form.get('field.passwd')
|
||||||
|
if form.get('field.passwdConfirm') != pw:
|
||||||
|
raiseValidationError(_(u'Password and password confirmation '
|
||||||
|
'do not match.'))
|
||||||
|
regMan = IMemberRegistrationManager(self.context.getLoopsRoot())
|
||||||
|
person = regMan.register(form.get('field.userId'), pw,
|
||||||
|
form.get('field.lastName'),
|
||||||
|
form.get('field.firstName'))
|
||||||
|
return person
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,8 @@ class UserId(schema.TextLine):
|
||||||
if person is not None and person != self.context:
|
if person is not None and person != self.context:
|
||||||
raiseValidationError(
|
raiseValidationError(
|
||||||
_(u'There is alread a person ($person) assigned to user $userId.',
|
_(u'There is alread a person ($person) assigned to user $userId.',
|
||||||
mapping={'person': zapi.getName(person), 'userId': userId}))
|
mapping=dict(person=zapi.getName(person),
|
||||||
|
userId=userId)))
|
||||||
|
|
||||||
|
|
||||||
class IPerson(IBasePerson):
|
class IPerson(IBasePerson):
|
||||||
|
@ -76,3 +77,23 @@ class IPerson(IBasePerson):
|
||||||
description=_(u'The principal id of a user that should '
|
description=_(u'The principal id of a user that should '
|
||||||
'be associated with this person.'),
|
'be associated with this person.'),
|
||||||
required=False,)
|
required=False,)
|
||||||
|
|
||||||
|
|
||||||
|
class IMemberRegistrationManager(Interface):
|
||||||
|
""" Knows what to do for registrating a new member (portal user).
|
||||||
|
"""
|
||||||
|
|
||||||
|
authPluginId = Attribute(u'The id of an authentication plugin to be '
|
||||||
|
'used for managing members of this loops site')
|
||||||
|
|
||||||
|
def register(userId, password, lastName, firstName=u'', **kw):
|
||||||
|
""" Register a new member for this loops site.
|
||||||
|
Return the person adapter for the concept created.
|
||||||
|
Raise ValidationError if the user could not be created.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def changePassword(oldPw, newPw):
|
||||||
|
""" Change the password of the user currently logged-in.
|
||||||
|
Raise a ValidationError if the oldPw does not match the
|
||||||
|
current password.
|
||||||
|
"""
|
||||||
|
|
88
organize/member.py
Normal file
88
organize/member.py
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
|
||||||
|
#
|
||||||
|
# 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; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Member registration adapter(s).
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
"""
|
||||||
|
|
||||||
|
from zope.app import zapi
|
||||||
|
from zope import interface, component
|
||||||
|
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 InternalPrincipal
|
||||||
|
from zope.app.security.interfaces import IAuthentication
|
||||||
|
from zope.i18nmessageid import MessageFactory
|
||||||
|
from zope.cachedescriptors.property import Lazy
|
||||||
|
|
||||||
|
from cybertools.typology.interfaces import IType
|
||||||
|
from loops.interfaces import ILoops
|
||||||
|
from loops.concept import Concept
|
||||||
|
from loops.organize.interfaces import IMemberRegistrationManager
|
||||||
|
from loops.organize.interfaces import raiseValidationError
|
||||||
|
|
||||||
|
_ = MessageFactory('zope')
|
||||||
|
|
||||||
|
|
||||||
|
class MemberRegistrationManager(object):
|
||||||
|
|
||||||
|
implements(IMemberRegistrationManager)
|
||||||
|
adapts(ILoops)
|
||||||
|
|
||||||
|
authPluginId = 'loops'
|
||||||
|
|
||||||
|
def __init__(self, context):
|
||||||
|
self.context = context
|
||||||
|
|
||||||
|
def register(self, userId, password, lastName, firstName=u'', **kw):
|
||||||
|
# step 1: create an internal principal in the loops principal folder:
|
||||||
|
pau = zapi.getUtility(IAuthentication, context=self.context)
|
||||||
|
if not IPluggableAuthentication.providedBy(pau):
|
||||||
|
raiseValidationError(_(u'There is no pluggable authentication '
|
||||||
|
'utility available.'))
|
||||||
|
if not self.authPluginId in pau.authenticatorPlugins:
|
||||||
|
raiseValidationError(_(u'There is no loops authenticator '
|
||||||
|
'plugin available.'))
|
||||||
|
pFolder = component.queryUtility(IAuthenticatorPlugin, self.authPluginId,
|
||||||
|
context=pau)
|
||||||
|
title = firstName and ' '.join((firstName, lastName)) or lastName
|
||||||
|
# TODO: encrypt password:
|
||||||
|
principal = InternalPrincipal(userId, password, title)
|
||||||
|
pFolder[userId] = principal
|
||||||
|
# step 1: create a corresponding person concept:
|
||||||
|
cm = self.context.getConceptManager()
|
||||||
|
id = userId
|
||||||
|
num = 0
|
||||||
|
while id in cm:
|
||||||
|
num +=1
|
||||||
|
id = userid + str(num)
|
||||||
|
person = cm[id] = Concept(title)
|
||||||
|
# TODO: the name of the person type object must be kept flexible!
|
||||||
|
person.conceptType = cm['person']
|
||||||
|
personAdapter = IType(person).typeInterface(person)
|
||||||
|
personAdapter.firstName = firstName
|
||||||
|
personAdapter.lastName = lastName
|
||||||
|
personAdapter.userId = '.'.join((self.authPluginId, userId))
|
||||||
|
return personAdapter
|
||||||
|
|
||||||
|
def changePassword(self, oldPw, newPw):
|
||||||
|
pass
|
||||||
|
|
Loading…
Add table
Reference in a new issue