[Checkins] SVN: grokapps/LoginDemo/ moved login name validation from join form action to IUser interface; copied form

Luciano Ramalho luciano at ramalho.org
Thu Jan 3 13:07:05 EST 2008


Log message for revision 82647:
  moved login name validation from join form action to IUser interface; copied form
  template code from grok/templates/default_edit_form.pt to loginform/form.pt
  

Changed:
  U   grokapps/LoginDemo/README.txt
  U   grokapps/LoginDemo/src/logindemo/app.py
  U   grokapps/LoginDemo/src/logindemo/form.pt
  U   grokapps/LoginDemo/src/logindemo/interfaces.py

-=-
Modified: grokapps/LoginDemo/README.txt
===================================================================
--- grokapps/LoginDemo/README.txt	2008-01-03 16:46:40 UTC (rev 82646)
+++ grokapps/LoginDemo/README.txt	2008-01-03 18:07:04 UTC (rev 82647)
@@ -23,6 +23,8 @@
 
 - allow user to edit his data (password, real name, e-mail)
 
+- add a password confirmation field (and validation) to join form
+
 - password reset for users with a valid e-mail
 
 - allow manager to delete user accounts through the member listing

Modified: grokapps/LoginDemo/src/logindemo/app.py
===================================================================
--- grokapps/LoginDemo/src/logindemo/app.py	2008-01-03 16:46:40 UTC (rev 82646)
+++ grokapps/LoginDemo/src/logindemo/app.py	2008-01-03 18:07:04 UTC (rev 82647)
@@ -16,6 +16,7 @@
 from zope.annotation.interfaces import IAttributeAnnotatable
 from zope.i18n import MessageFactory
 
+
 from interfaces import IUser, UserDataAdapter  
 
 _ = MessageFactory('logindemo')
@@ -97,7 +98,7 @@
     form_fields = grok.AutoFields(IUser)
     # XXX: Failed attempt to display the password_encoding field
     #form_fields[u'password_encoding'].custom_widget = SourceDropdownWidget
-    form_title = u'User registration'
+    label = u'User registration'
     template = grok.PageTemplateFile('form.pt')
     
     @grok.action('Save')
@@ -105,24 +106,22 @@
         login = data['login']
         pau = getUtility(IAuthentication)
         principals = pau['principals']
-        if login in principals: # duplicate login name
-            ### XXX: find out how to display this message in the form template
-            msg = _(u'Duplicate login. Please choose a different one.')
-            self.redirect(self.url()+'?'+urlencode({'error_msg':msg}))
-        else:
-            principal = InternalPrincipal(login, data['password'], data['name'],
-                                          passwordManagerName='SHA1')
-            # add principal to principal folder
-            principals[login] = principal
-            # save the e-mail
-            user = IUser(principal)
-            user.email = data['email']
-            # grant the user permission to view the member listing
-            permission_mngr = IPrincipalPermissionManager(grok.getSite())
-            permission_mngr.grantPermissionToPrincipal(
-               'logindemo.ViewMemberListing', principals.prefix + login)
+        principal = InternalPrincipal(login, data['password'], data['name'],
+                                      passwordManagerName='SHA1')
+        # add principal to principal folder; we may assume that the login
+        # name is unique because of validation on the IUser interface
+        # but to be doubly sure, we assert this
+        assert(login not in principals)
+        principals[login] = principal
+        # save the e-mail
+        user = IUser(principal)
+        user.email = data['email']
+        # grant the user permission to view the member listing
+        permission_mngr = IPrincipalPermissionManager(grok.getSite())
+        permission_mngr.grantPermissionToPrincipal(
+           'logindemo.ViewMemberListing', principals.prefix + login)
 
-            self.redirect(self.url('login')+'?'+urlencode({'login':login}))
+        self.redirect(self.url('login')+'?'+urlencode({'login':login}))
                     
 class Account(grok.View):
     

Modified: grokapps/LoginDemo/src/logindemo/form.pt
===================================================================
--- grokapps/LoginDemo/src/logindemo/form.pt	2008-01-03 16:46:40 UTC (rev 82646)
+++ grokapps/LoginDemo/src/logindemo/form.pt	2008-01-03 18:07:04 UTC (rev 82647)
@@ -1,46 +1,75 @@
 <html metal:use-macro="context/@@master/macros/page">
 <body>
-
     
     <div metal:fill-slot="main">
-    
-        <h1 tal:content="view/form_title|string:Form"></h1>
 
-        <form class="edit-form" enctype="multipart/form-data" method="post"
-            action="." tal:attributes="action request/URL">
-        
-        <h1 tal:content="view/label" i18n:translate="">Edit something</h1>
-        
-        <div class="summary" tal:condition="view/status"
-             tal:content="view/status" i18n:translate="">Status</div>
-        
-        <div class="row" tal:repeat="widget view/widgets">
-          <div class="label">
-            <label for="field.name" title="The widget's hint"
-                   tal:attributes="for widget/name; title widget/hint"
-                   tal:content="widget/label" i18n:translate=""
-                   i18n:attributes="title">Label</label>
-          </div>
-        
-          <div tal:condition="widget/error"
-               tal:content="structure widget/error">Error</div>
-        
-          <div class="field">
-            <input tal:replace="structure widget" />
-          </div>
-        </div>
-        
-        <div class="row">
-          <div class="label" />
-          <div class="field">
-            <span class="actionButtons" tal:condition="view/availableActions">
-              <input tal:repeat="action view/actions"
-                     tal:replace="structure action/render"
-                     />
-            </span>
-          </div>
-        </div>    
-        </form>
+<!-- the code of the form element below was copied verbatim from
+     src/grok/templates/default_edit_form.pt-->
+<!-- XXX: is a better way of rendering Groks's default forms inside
+     a master template?-->
+
+<form action="." tal:attributes="action request/URL" method="post"
+      class="edit-form" enctype="multipart/form-data">
+
+  <h1 i18n:translate=""
+    tal:condition="view/label"
+    tal:content="view/label">Label</h1>
+
+  <div class="form-status"
+    tal:define="status view/status"
+    tal:condition="status">
+
+    <div i18n:translate="" tal:content="view/status">
+      Form status summary
     </div>
+
+    <ul class="errors" tal:condition="view/errors">
+      <li tal:repeat="error view/error_views">
+         <span tal:replace="structure error">Error Type</span>
+      </li>
+    </ul>
+  </div>
+
+  <table class="form-fields">
+    <tbody>
+      <tal:block repeat="widget view/widgets">
+        <tr>
+          <td class="label" tal:define="hint widget/hint">
+            <label tal:condition="python:hint"
+                   tal:attributes="for widget/name">
+              <span class="required" tal:condition="widget/required"
+              >*</span><span i18n:translate=""
+                             tal:content="widget/label">label</span>
+            </label>
+            <label tal:condition="python:not hint"
+                   tal:attributes="for widget/name">
+              <span class="required" tal:condition="widget/required"
+              >*</span><span i18n:translate=""
+                             tal:content="widget/label">label</span>
+            </label>
+          </td>
+          <td class="field">
+            <div class="widget" tal:content="structure widget">
+              <input type="text" />
+            </div>
+            <div class="error" tal:condition="widget/error">
+              <span tal:replace="structure widget/error">error</span>
+            </div>
+          </td>
+        </tr>
+      </tal:block>
+    </tbody>
+  </table>
+
+  <div id="actionsView">
+    <span class="actionButtons" tal:condition="view/availableActions">
+      <input tal:repeat="action view/actions"
+             tal:replace="structure action/render"
+             />
+    </span>
+  </div>
+</form>
+
+    </div><!--/slot main-->
 </body>
 </html>

Modified: grokapps/LoginDemo/src/logindemo/interfaces.py
===================================================================
--- grokapps/LoginDemo/src/logindemo/interfaces.py	2008-01-03 16:46:40 UTC (rev 82646)
+++ grokapps/LoginDemo/src/logindemo/interfaces.py	2008-01-03 18:07:04 UTC (rev 82647)
@@ -2,8 +2,9 @@
 
 from zope.interface import Interface, implements
 from zope import schema
-from zope.component import adapts, getUtilitiesFor
+from zope.component import adapts, getUtility, getUtilitiesFor
 from zope.annotation.interfaces import IAnnotations
+from zope.app.security.interfaces import IAuthentication
 from zope.app.authentication.principalfolder import IInternalPrincipal
 from zope.app.authentication.interfaces import IPasswordManager
 from persistent.dict import PersistentDict
@@ -11,17 +12,32 @@
 
 _ = MessageFactory('logindemo')
 
-USER_DATA_KEY = 'logindemo.iuser.data' 
+USER_DATA_KEY = 'logindemo.iuser.data'
 
+################################################## email validation
+
 class NotAnEmailAddress(schema.ValidationError):
     __doc__ = _(u"Invalid email address")
 
 check_email = re.compile(r"[a-zA-Z0-9._%-]+@([a-zA-Z0-9-]+\.)*[a-zA-Z]{2,4}").match
-def validate_email(value):
+def valid_email(value):
     if check_email(value):
         return True
     raise NotAnEmailAddress(value)
 
+################################################## login name validation
+
+class LoginNameTaken(schema.ValidationError):
+    __doc__ = _(u"Login name already in use. Please choose a different one.")
+
+def unique_login(login):
+    pau = getUtility(IAuthentication)
+    if login in pau['principals']:
+        raise LoginNameTaken
+    return True
+
+################################################## password manager selection
+
 class PasswordManagerChoices(object):
     implements(schema.interfaces.IIterableSource)
     
@@ -38,10 +54,13 @@
     def __contains__(self, value):
         return value in self.choices
 
+################################################## interfaces
+
 class IUser(Interface):
     """Basic user data."""
     login = schema.TextLine(title=_(u"Login"),
-                            required=True)
+                            required=True,
+                            constraint=unique_login)
     password = schema.Password(title=_(u"Password"),
                             required=True)
     # XXX: I have not managed yet to display this in the app.py join form
@@ -52,8 +71,10 @@
                             required=False)
     email = schema.ASCIILine(title=_(u"E-mail"),
                             required=False,
-                            constraint=validate_email)
+                            constraint=valid_email)
 
+################################################## adapters
+
 class UserDataAdapter(object): 
     """
     Principal Information Adapter



More information about the Checkins mailing list