more on registration; extend concept view to show the attributes of the typeInterface
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1222 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
		
							parent
							
								
									00e27595ca
								
							
						
					
					
						commit
						6ab11a50e9
					
				
					 8 changed files with 110 additions and 52 deletions
				
			
		| 
						 | 
				
			
			@ -68,7 +68,18 @@ class ConceptView(BaseView):
 | 
			
		|||
    
 | 
			
		||||
    @Lazy
 | 
			
		||||
    def macro(self):
 | 
			
		||||
        return self.template.macros['conceptlisting']
 | 
			
		||||
        return self.template.macros['conceptdata']
 | 
			
		||||
 | 
			
		||||
    def fieldData(self):
 | 
			
		||||
        ti = IType(self.context).typeInterface
 | 
			
		||||
        if not ti: return 
 | 
			
		||||
        adapter = ti(self.context)
 | 
			
		||||
        for f in ti:
 | 
			
		||||
            title = getattr(ti[f], 'title', '')
 | 
			
		||||
            value = getattr(adapter, f, '')
 | 
			
		||||
            if not (value and title):
 | 
			
		||||
                continue
 | 
			
		||||
            yield {'title': title, 'value': value, 'id': f}
 | 
			
		||||
 | 
			
		||||
    def children(self):
 | 
			
		||||
        for r in self.context.getChildRelations():
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,23 @@
 | 
			
		|||
<metal:body define-macro="conceptlisting">
 | 
			
		||||
<metal:data define-macro="conceptdata">
 | 
			
		||||
  <div tal:attributes="class string:content-$level;
 | 
			
		||||
                       ondblclick item/openEditWindow">
 | 
			
		||||
    <h3>
 | 
			
		||||
      <span i18n:translate="">Concept</span>:
 | 
			
		||||
      <span tal:content="item/title">Title</span>
 | 
			
		||||
    </h3>
 | 
			
		||||
    <table>
 | 
			
		||||
      <tr tal:repeat="field item/fieldData">
 | 
			
		||||
        <td><span tal:content="field/title"
 | 
			
		||||
                  i18n:translate="" />:</td>
 | 
			
		||||
        <td tal:content="nocall:field/value"></td>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
    <metal:listing use-macro="item/template/macros/conceptlisting2" />
 | 
			
		||||
  </div>
 | 
			
		||||
</metal:data>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<metal:listing define-macro="conceptlisting">
 | 
			
		||||
  <div>
 | 
			
		||||
    <h3 tal:attributes="class string:content-$level;
 | 
			
		||||
                        ondblclick item/openEditWindow">
 | 
			
		||||
| 
						 | 
				
			
			@ -12,7 +31,7 @@
 | 
			
		|||
        <div tal:repeat="related item/parents">
 | 
			
		||||
          <a href="#"
 | 
			
		||||
             tal:attributes="href string:${view/url}/.target${related/uniqueId}"
 | 
			
		||||
             tal:content="related/title">Resource Title</a>
 | 
			
		||||
             tal:content="related/title">Concept Title</a>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div tal:attributes="class string:content-$level;
 | 
			
		||||
| 
						 | 
				
			
			@ -21,7 +40,7 @@
 | 
			
		|||
        <div tal:repeat="related item/children">
 | 
			
		||||
          <a href="#"
 | 
			
		||||
             tal:attributes="href string:${view/url}/.target${related/uniqueId}"
 | 
			
		||||
             tal:content="related/title">Resource Title</a>
 | 
			
		||||
             tal:content="related/title">Concept Title</a>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div tal:attributes="class string:content-$level;
 | 
			
		||||
| 
						 | 
				
			
			@ -35,4 +54,4 @@
 | 
			
		|||
      </div>
 | 
			
		||||
    </metal:listing>
 | 
			
		||||
  </div>
 | 
			
		||||
</metal:body>
 | 
			
		||||
</metal:listing>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -223,7 +223,7 @@ and register it
 | 
			
		|||
  >>> personAdapter = regView.register(data)
 | 
			
		||||
 | 
			
		||||
  >>> personAdapter.context.__name__, personAdapter.lastName, personAdapter.userId
 | 
			
		||||
  (u'newuser', u'Sawyer', u'loops.newuser')
 | 
			
		||||
  (u'person.newuser', u'Sawyer', u'loops.newuser')
 | 
			
		||||
 | 
			
		||||
Now we can also retrieve it from the authentication utility:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,22 +34,17 @@ from zope.formlib.form import Form, FormFields, action
 | 
			
		|||
from zope.formlib.namedtemplate import NamedTemplate
 | 
			
		||||
from zope.i18nmessageid import MessageFactory
 | 
			
		||||
 | 
			
		||||
from loops.browser.common import BaseView
 | 
			
		||||
from cybertools.typology.interfaces import IType
 | 
			
		||||
from loops.browser.concept import ConceptView
 | 
			
		||||
from loops.browser.node import NodeView
 | 
			
		||||
from loops.browser.concept import ConceptRelationView
 | 
			
		||||
from loops.organize.interfaces import ANNOTATION_KEY, IMemberRegistrationManager
 | 
			
		||||
from loops.organize.interfaces import IMemberRegistration, raiseValidationError
 | 
			
		||||
from loops.organize.interfaces import IMemberRegistration
 | 
			
		||||
 | 
			
		||||
_ = MessageFactory('zope')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MyConcepts(BaseView):
 | 
			
		||||
 | 
			
		||||
    template = NamedTemplate('loops.concept_macros')
 | 
			
		||||
 | 
			
		||||
    @Lazy
 | 
			
		||||
    def macro(self):
 | 
			
		||||
        return self.template.macros['conceptlisting']
 | 
			
		||||
class MyStuff(ConceptView):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, context, request):
 | 
			
		||||
        self.context = context
 | 
			
		||||
| 
						 | 
				
			
			@ -59,23 +54,9 @@ class MyConcepts(BaseView):
 | 
			
		|||
        if self.person is not None:
 | 
			
		||||
            self.context = self.person
 | 
			
		||||
 | 
			
		||||
    def children(self):
 | 
			
		||||
        if self.person is None:
 | 
			
		||||
            return
 | 
			
		||||
        for r in self.person.getChildRelations():
 | 
			
		||||
            yield ConceptRelationView(r, self.request, contextIsSecond=True)
 | 
			
		||||
 | 
			
		||||
    def parents(self):
 | 
			
		||||
        if self.person is None:
 | 
			
		||||
            return
 | 
			
		||||
        for r in self.person.getParentRelations():
 | 
			
		||||
            yield ConceptRelationView(r, self.request)
 | 
			
		||||
 | 
			
		||||
    def resources(self):
 | 
			
		||||
        if self.person is None:
 | 
			
		||||
            return
 | 
			
		||||
        for r in self.person.getResourceRelations():
 | 
			
		||||
            yield ConceptRelationView(r, self.request, contextIsSecond=True)
 | 
			
		||||
    @Lazy
 | 
			
		||||
    def view(self):
 | 
			
		||||
        return self
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PasswordWidget(BasePasswordWidget):
 | 
			
		||||
| 
						 | 
				
			
			@ -109,11 +90,13 @@ class MemberRegistration(Form, NodeView):
 | 
			
		|||
        pw = form.get('password')
 | 
			
		||||
        if form.get('passwordConfirm') != pw:
 | 
			
		||||
            raise ValueError(u'Password and password confirmation do not match.')
 | 
			
		||||
        login = form.get('loginName')
 | 
			
		||||
        regMan = IMemberRegistrationManager(self.context.getLoopsRoot())
 | 
			
		||||
        person = regMan.register(form.get('loginName'), pw,
 | 
			
		||||
        person = regMan.register(login, pw,
 | 
			
		||||
                                 form.get('lastName'),
 | 
			
		||||
                                 form.get('firstName'))
 | 
			
		||||
        message = _(u'You have been registered and can now login.')
 | 
			
		||||
        self.request.response.redirect('%s?message=%s' % (self.url, message))
 | 
			
		||||
        self.request.response.redirect('%s/login.html?login=%s&message=%s'
 | 
			
		||||
                            % (self.url, login, message))
 | 
			
		||||
        return person
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,11 +28,11 @@
 | 
			
		|||
  <!-- my stuff and other views -->
 | 
			
		||||
 | 
			
		||||
  <zope:adapter
 | 
			
		||||
        name="myconcepts"
 | 
			
		||||
        name="mystuff"
 | 
			
		||||
        for="loops.interfaces.IConcept
 | 
			
		||||
            zope.publisher.interfaces.browser.IBrowserRequest"
 | 
			
		||||
        provides="zope.interface.Interface"
 | 
			
		||||
        factory="loops.organize.browser.MyConcepts"
 | 
			
		||||
        factory="loops.organize.browser.MyStuff"
 | 
			
		||||
        permission="zope.View"
 | 
			
		||||
        />
 | 
			
		||||
  
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,10 @@ from zope.app import zapi
 | 
			
		|||
from zope.app.principalannotation import annotations
 | 
			
		||||
from zope.app.security.interfaces import IAuthentication, PrincipalLookupError
 | 
			
		||||
from zope.i18nmessageid import MessageFactory
 | 
			
		||||
from zope.security.proxy import removeSecurityProxy
 | 
			
		||||
 | 
			
		||||
from cybertools.organize.interfaces import IPerson as IBasePerson
 | 
			
		||||
from loops.organize.util import getPrincipalFolder, authPluginId
 | 
			
		||||
 | 
			
		||||
_ = MessageFactory('zope')
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +53,8 @@ class UserId(schema.TextLine):
 | 
			
		|||
    def _validate(self, userId):
 | 
			
		||||
        if not userId:
 | 
			
		||||
            return
 | 
			
		||||
        auth = component.getUtility(IAuthentication, context=self.context)
 | 
			
		||||
        context = removeSecurityProxy(self.context).context
 | 
			
		||||
        auth = component.getUtility(IAuthentication, context=context)
 | 
			
		||||
        try:
 | 
			
		||||
            principal = auth.getPrincipal(userId)
 | 
			
		||||
        except PrincipalLookupError:
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +62,7 @@ class UserId(schema.TextLine):
 | 
			
		|||
                                   mapping={'userId': userId}))
 | 
			
		||||
        pa = annotations(principal)
 | 
			
		||||
        person = pa.get(ANNOTATION_KEY, None)
 | 
			
		||||
        if person is not None and person != self.context:
 | 
			
		||||
        if person is not None and person != context:
 | 
			
		||||
            raiseValidationError(
 | 
			
		||||
                _(u'There is alread a person ($person) assigned to user $userId.',
 | 
			
		||||
                  mapping=dict(person=zapi.getName(person),
 | 
			
		||||
| 
						 | 
				
			
			@ -69,6 +73,10 @@ class LoginName(schema.TextLine):
 | 
			
		|||
 | 
			
		||||
    def _validate(self, userId):
 | 
			
		||||
        super(LoginName, self)._validate(userId)
 | 
			
		||||
        if userId in getPrincipalFolder(self.context):
 | 
			
		||||
            raiseValidationError(
 | 
			
		||||
                _(u'There is alread a user with ID $userId.',
 | 
			
		||||
                  mapping=dict(userId=userId)))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Password(schema.Password):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,6 +39,7 @@ 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.util import getPrincipalFolder, authPluginId
 | 
			
		||||
 | 
			
		||||
_ = MessageFactory('zope')
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,40 +49,32 @@ 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):
 | 
			
		||||
            raise ValueError(u'There is no pluggable authentication '
 | 
			
		||||
                                    'utility available.')
 | 
			
		||||
        if not self.authPluginId in pau.authenticatorPlugins:
 | 
			
		||||
            raise ValueError(u'There is no loops authenticator '
 | 
			
		||||
                                    'plugin available.')
 | 
			
		||||
        pFolder = component.queryUtility(IAuthenticatorPlugin, self.authPluginId,
 | 
			
		||||
                                         context=pau)
 | 
			
		||||
        pFolder = getPrincipalFolder(self.context)
 | 
			
		||||
        title = firstName and ' '.join((firstName, lastName)) or lastName
 | 
			
		||||
        # TODO: care for password encryption:
 | 
			
		||||
        principal = InternalPrincipal(userId, password, title)
 | 
			
		||||
        pFolder[userId] = principal
 | 
			
		||||
        # step 2: create a corresponding person concept:
 | 
			
		||||
        cm = self.context.getConceptManager()
 | 
			
		||||
        id = userId
 | 
			
		||||
        id = baseId = 'person.' + userId
 | 
			
		||||
        num = 0
 | 
			
		||||
        while id in cm:
 | 
			
		||||
            num +=1
 | 
			
		||||
            id = userId + str(num)
 | 
			
		||||
            id = baseId + str(num)
 | 
			
		||||
        person = cm[id] = Concept(title)
 | 
			
		||||
        # TODO: the name of the person type object must be kept flexible!
 | 
			
		||||
        # So we have to search for a type concept that has IPerson as
 | 
			
		||||
        # its typeInterface...
 | 
			
		||||
        person.conceptType = cm['person']
 | 
			
		||||
        personAdapter = IType(person).typeInterface(person)
 | 
			
		||||
        personAdapter.firstName = firstName
 | 
			
		||||
        personAdapter.lastName = lastName
 | 
			
		||||
        personAdapter.userId = '.'.join((self.authPluginId, userId))
 | 
			
		||||
        personAdapter.userId = '.'.join((authPluginId, userId))
 | 
			
		||||
        notify(ObjectCreatedEvent(person))
 | 
			
		||||
        notify(ObjectModifiedEvent(person))
 | 
			
		||||
        return personAdapter
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										44
									
								
								organize/util.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								organize/util.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
#
 | 
			
		||||
#  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
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
Utilities for the loops.organize package.
 | 
			
		||||
 | 
			
		||||
$Id$
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from zope.app import zapi
 | 
			
		||||
from zope import interface, component, schema
 | 
			
		||||
from zope.app.authentication.interfaces import IPluggableAuthentication
 | 
			
		||||
from zope.app.authentication.interfaces import IAuthenticatorPlugin
 | 
			
		||||
from zope.app.security.interfaces import IAuthentication
 | 
			
		||||
 | 
			
		||||
authPluginId = 'loops'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def getPrincipalFolder(context=None):
 | 
			
		||||
        pau = zapi.getUtility(IAuthentication, context=context)
 | 
			
		||||
        if not IPluggableAuthentication.providedBy(pau):
 | 
			
		||||
            raise ValueError(u'There is no pluggable authentication '
 | 
			
		||||
                              'utility available.')
 | 
			
		||||
        if not authPluginId in pau.authenticatorPlugins:
 | 
			
		||||
            raise ValueError(u'There is no loops authenticator '
 | 
			
		||||
                              'plugin available.')
 | 
			
		||||
        return component.queryUtility(IAuthenticatorPlugin, authPluginId,
 | 
			
		||||
                                         context=pau)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue