[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