Index: main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py	(revision 7366)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py	(revision 7369)
@@ -26,4 +26,6 @@
 from zope.catalog.interfaces import ICatalog
 from zope.component import queryUtility, getUtility
+#from hurry.query import Eq
+#from hurry.query.query import Query
 from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
 from zope.component import createObject
@@ -33,5 +35,6 @@
 from waeup.sirp.browser import (
     SIRPPage, SIRPEditFormPage, SIRPAddFormPage, SIRPDisplayFormPage,
-    ContactAdminForm)
+    ContactAdminForm, SIRPForm)
+from waeup.sirp.browser.interfaces import ICaptchaManager
 from waeup.sirp.browser.breadcrumbs import Breadcrumb
 from waeup.sirp.browser.resources import datepicker, datatable, tabs, warning
@@ -41,5 +44,5 @@
 from waeup.sirp.interfaces import (
     ISIRPObject, IUserAccount, IExtFileStore, IPasswordValidator, IContactForm,
-    ISIRPUtils)
+    ISIRPUtils, IUniversity)
 from waeup.sirp.widgets.datewidget import (
     FriendlyDateWidget, FriendlyDateDisplayWidget,
@@ -51,5 +54,5 @@
     IStudentAccommodation, IStudentClearanceEdit, IStudentStudyLevel,
     ICourseTicket, ICourseTicketAdd, IStudentPaymentsContainer,
-    IStudentOnlinePayment, IBedTicket, IStudentsUtils
+    IStudentOnlinePayment, IBedTicket, IStudentsUtils, IStudentChangePassword
     )
 from waeup.sirp.students.catalog import search
@@ -193,5 +196,5 @@
     grok.context(ISIRPObject)
     grok.name('setpassword')
-    grok.require('waeup.Public')
+    grok.require('waeup.Anonymous')
     grok.template('setpassword')
     title = ''
@@ -1469,4 +1472,8 @@
         students_utils = getUtility(IStudentsUtils)
         acc_details  = students_utils.getAccommodationDetails(student)
+        if not acc_details:
+            self.flash("Your data are incomplete.")
+            self.redirect(self.url(self.context))
+            return
         if not student.state in acc_details['allowed_states']:
             self.flash("You are in the wrong registration state.")
@@ -2186,2 +2193,57 @@
         self.redirect(self.url(self.context, u'@@edit'))
         return
+
+class ChangePasswordRequestPage(SIRPForm):
+    """Captcha'd page for students to request a password change.
+    """
+    grok.context(IUniversity)
+    grok.name('changepw')
+    grok.require('waeup.Anonymous')
+    grok.template('changepw')
+    title = 'Change Password Request'
+    label = 'Change my password'
+    form_fields = grok.AutoFields(IStudentChangePassword)
+
+    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('Send request')
+    def request(self, **data):
+        if not self.captcha_result.is_valid:
+            # Captcha will display error messages automatically.
+            # No need to flash something.
+            return
+        # Search student
+        cat = queryUtility(ICatalog, name='students_catalog')
+        reg_number = data['reg_number']
+        email = data['email']
+        results = cat.searchResults(
+            reg_number=(reg_number, reg_number),
+            email=(email,email))
+        if len(results) == 0:
+            self.flash('No student record found.')
+            return
+        student = list(results)[0]
+        # Change password
+        sirp_utils = getUtility(ISIRPUtils)
+        pwd = sirp_utils.genPassword()
+        IUserAccount(student).setPassword(pwd)
+        # Send email with new redentials
+        username = student.student_id
+        fullname = student.display_fullname
+        subject = 'Your SIRP credentials'
+        msg = 'You have successfully changed your password for the'
+        email_to = student.email
+        login_url = self.url(grok.getSite(), 'login')
+        success = sirp_utils.sendPassword(fullname,msg,username,
+            pwd,login_url,email_to,subject)
+        if success:
+            self.flash('An email with your user name and password ' +
+                'has been sent to %s.' % email_to)
+        else:
+            self.flash('An smtp server error occurred.')
+        return
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/changepw.pt
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/changepw.pt	(revision 7369)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/browser_templates/changepw.pt	(revision 7369)
@@ -0,0 +1,58 @@
+<form action="." tal:attributes="action request/URL" method="post"
+      class="edit-form" enctype="multipart/form-data">
+
+  <h2 i18n:translate=""
+    tal:condition="view/label"
+    tal:content="view/label">Label</h2>
+
+  <div class="form-status"
+    tal:define="status view/status"
+    tal:condition="status">
+    Form Status:
+    <span i18n:translate="" tal:content="view/status">
+      Form status summary
+    </span>
+  </div>
+
+  <table class="zebra">
+    <tbody>
+      <tal:block repeat="widget view/widgets">
+        <tr>
+          <td class="label">
+            <label 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">
+            <span class="widget" tal:content="structure widget">
+              <input type="text" />
+            </span>
+            <tal:error tal:condition="widget/error">
+              <span tal:replace="structure widget/error">error</span>
+            </tal:error>
+            <tal:hint tal:condition="widget/hint">
+              <span class="hint" tal:content="structure widget/hint">hint</span>
+            </tal:hint>
+          </td>
+        </tr>
+      </tal:block>
+      <tr tal:condition="view/captcha_code">
+        <td i18n:translate="">
+          Please solve the captcha<br />
+          to prevent misuse of this service:
+        </td>
+        <td>
+          <div tal:content="structure view/captcha_code"></div>
+        </td>
+      </tr>
+
+    </tbody>
+  </table>
+
+  <div class="actionButtons" tal:condition="view/availableActions">
+    <input tal:repeat="action view/actions"
+           tal:replace="structure action/render"
+           />
+  </div>
+</form>
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/catalog.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/catalog.py	(revision 7366)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/catalog.py	(revision 7369)
@@ -38,4 +38,5 @@
     student_id = index.Field(attribute='student_id')
     fullname = index.Text(attribute='fullname')
+    email = index.Field(attribute='email')
     reg_number = index.Field(attribute='reg_number')
     matric_number = index.Field(attribute='matric_number')
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/interfaces.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/interfaces.py	(revision 7366)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/interfaces.py	(revision 7369)
@@ -453,2 +453,20 @@
 
     """
+
+class IStudentChangePassword(ISIRPObject):
+    """Interface needed for change pasword page.
+
+    """
+
+    reg_number = schema.TextLine(
+        title = u'Registration Number',
+        default = None,
+        required = True,
+        readonly = False,
+        )
+
+    email = schema.ASCIILine(
+        title = u'Email',
+        required = True,
+        constraint=validate_email,
+        )
Index: main/waeup.sirp/trunk/src/waeup/sirp/students/utils.py
===================================================================
--- main/waeup.sirp/trunk/src/waeup/sirp/students/utils.py	(revision 7366)
+++ main/waeup.sirp/trunk/src/waeup/sirp/students/utils.py	(revision 7369)
@@ -191,7 +191,10 @@
         # Determine bed type
         studycourse = student['studycourse']
+        certificate = getattr(studycourse,'certificate',None)
         entry_session = studycourse.entry_session
         current_level = studycourse.current_level
-        end_level = studycourse.certificate.end_level
+        if not (entry_session and current_level and certificate):
+            return
+        end_level = certificate.end_level
         if entry_session == grok.getSite()['configuration'].accommodation_session:
             bt = 'fr'
