Index: /main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py
===================================================================
--- /main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py	(revision 7362)
+++ /main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py	(revision 7363)
@@ -34,6 +34,14 @@
     Table, Spacer)
 from reportlab.platypus.tables import TableStyle
+from waeup.sirp.applicants.interfaces import (
+    IApplicant, IApplicantEdit, IApplicantsRoot,
+    IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab,
+    MAX_UPLOAD_SIZE, IApplicantOnlinePayment,
+    )
+from waeup.sirp.applicants.workflow import INITIALIZED, STARTED, PAID, SUBMITTED
 from waeup.sirp.browser import (
-    SIRPPage, SIRPEditFormPage, SIRPAddFormPage, SIRPDisplayFormPage)
+    SIRPPage, SIRPEditFormPage, SIRPAddFormPage, SIRPDisplayFormPage,
+    DEFAULT_PASSPORT_IMAGE_PATH)
+from waeup.sirp.browser.interfaces import ICaptchaManager
 from waeup.sirp.browser.breadcrumbs import Breadcrumb
 from waeup.sirp.browser.layout import NullValidator, jsaction, JSAction
@@ -45,5 +53,6 @@
     IFileStoreNameChooser, IPasswordValidator, IUserAccount)
 from waeup.sirp.permissions import get_users_with_local_roles
-from waeup.sirp.browser import DEFAULT_PASSPORT_IMAGE_PATH
+from waeup.sirp.students.viewlets import PrimaryStudentNavTab
+from waeup.sirp.students.interfaces import IStudentsUtils
 from waeup.sirp.university.interfaces import ICertificate
 from waeup.sirp.utils.helpers import string_from_bytes, file_size
@@ -51,13 +60,6 @@
     FriendlyDateWidget, FriendlyDateDisplayWidget,
     FriendlyDatetimeDisplayWidget)
+from waeup.sirp.widgets.phonewidget import PhoneWidget
 from waeup.sirp.widgets.restwidget import ReSTDisplayWidget
-from waeup.sirp.applicants.interfaces import (
-    IApplicant, IApplicantEdit, IApplicantsRoot,
-    IApplicantsContainer, IApplicantsContainerAdd, application_types_vocab,
-    MAX_UPLOAD_SIZE, IApplicantOnlinePayment,
-    )
-from waeup.sirp.applicants.workflow import INITIALIZED, STARTED, PAID, SUBMITTED
-from waeup.sirp.students.viewlets import PrimaryStudentNavTab
-from waeup.sirp.students.interfaces import IStudentsUtils
 
 grok.context(ISIRPObject) # Make ISIRPObject the default context
@@ -303,13 +305,4 @@
     text = 'Manage applicants container'
 
-#class ApplicantLoginActionButton(ManageActionButton):
-#    grok.order(2)
-#    grok.context(IApplicantsContainer)
-#    grok.view(ApplicantsContainerPage)
-#    grok.require('waeup.Anonymous')
-#    icon = 'login.png'
-#    text = 'Login for applicants'
-#    target = 'login'
-
 class ApplicantsContainerManageFormPage(SIRPEditFormPage):
     grok.context(IApplicantsContainer)
@@ -429,5 +422,6 @@
         self.context.addApplicant(applicant)
         self.flash('Applicant record created.')
-        self.redirect(self.url(self.context[applicant.application_number], 'index'))
+        self.redirect(
+            self.url(self.context[applicant.application_number], 'index'))
         return
 
@@ -520,5 +514,4 @@
             return
         timestamp = "%d" % int(time()*1000)
-        #order_id = "%s%s" % (student_id[1:],timestamp)
         for key in self.context.keys():
             ticket = self.context[key]
@@ -557,6 +550,8 @@
     grok.require('waeup.viewApplication')
     form_fields = grok.AutoFields(IApplicantOnlinePayment)
-    form_fields['creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
-    form_fields['payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
+    form_fields[
+        'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
+    form_fields[
+        'payment_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
     pnav = 3
 
@@ -952,5 +947,6 @@
             self.flash('Successfully removed: %s' % ', '.join(deleted))
             ob_class = self.__implemented__.__name__.replace('waeup.sirp.','')
-            self.context.loggerInfo(ob_class, 'removed: % s' % ', '.join(deleted))
+            self.context.loggerInfo(
+                ob_class, 'removed: % s' % ', '.join(deleted))
         return
 
@@ -980,4 +976,5 @@
         )
     form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
+    #form_fields['phone'].custom_widget = PhoneWidget
     grok.template('applicanteditpage')
     manage_applications = False
@@ -1091,2 +1088,66 @@
             return open(DEFAULT_PASSPORT_IMAGE_PATH, 'rb').read()
         return image
+
+class ApplicantRegistrationSuccessful(SIRPPage):
+    """Info page when applicant registration was successful.
+    """
+    grok.context(IApplicantsContainer)
+    grok.name('register_success')
+    grok.require('waeup.Public')
+    grok.template('applicantregister_succ')
+    label = 'Applicant Registration Successful'
+    title = label
+
+    def update(self, app_id=None):
+        if app_id is None:
+            self.redirect(self.url(self.context, 'register'))
+        self.app_id = app_id
+        self.login_url = self.url(grok.getSite(), 'login')
+
+class ApplicantRegistrationPage(SIRPAddFormPage):
+    """Captcha'd registration page for applicants.
+    """
+    grok.context(IApplicantsContainer)
+    grok.name('register')
+    grok.require('waeup.Public')
+    grok.template('applicantregister')
+    label = 'Applicant Registration'
+    title = label
+    form_fields = grok.AutoFields(IApplicantEdit).select(
+        'firstname', 'middlename', 'lastname', 'email', 'phone')
+    form_fields['phone'].custom_widget = PhoneWidget
+
+    def update(self):
+        # handle captcha
+        self.captcha = getUtility(ICaptchaManager).getCaptcha()
+        self.captcha_result = self.captcha.verify(self.request)
+        self.captcha_code = self.captcha.display(self.captcha_result.error_code)
+        return
+
+    @grok.action('Register')
+    def register(self, **data):
+        if not self.captcha_result.is_valid:
+            # captcha will display error messages automatically.
+            # No need to flash something.
+            return
+
+        # handle password field manually
+        form = self.request.form
+        password = form.get('password', None)
+        password_ctl = form.get('control_password', None)
+        if password:
+            validator = getUtility(IPasswordValidator)
+            errors = validator.validate_password(password, password_ctl)
+            if errors:
+                self.flash( ' '.join(errors))
+                return
+
+        # add applicant and redirect to success page
+        applicant = createObject('waeup.Applicant')
+        self.applyData(applicant, **data)
+        self.context.addApplicant(applicant)
+        IUserAccount(applicant).setPassword(password)
+        app_id = applicant.applicant_id
+        self.redirect(self.url(self.context, 'register_success',
+                               data=dict(app_id=app_id)))
+        return
