Index: /main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py
===================================================================
--- /main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py	(revision 7157)
+++ /main/waeup.sirp/trunk/src/waeup/sirp/students/browser.py	(revision 7158)
@@ -49,5 +49,5 @@
 from waeup.sirp.students.catalog import search
 from waeup.sirp.students.workflow import (
-    CLEARANCE, RETURNING, CLEARED, REGISTERED, VALIDATED)
+    CLEARANCE, REQUESTED, RETURNING, CLEARED, REGISTERED, VALIDATED)
 from waeup.sirp.students.studylevel import StudentStudyLevel, CourseTicket
 from waeup.sirp.students.vocabularies import StudyLevelSource
@@ -454,4 +454,32 @@
     target = 'edit_clearance'
 
+class StudentClearActionButton(ManageActionButton):
+    grok.order(2)
+    grok.context(IStudent)
+    grok.view(StudentClearanceDisplayFormPage)
+    grok.require('waeup.clearStudent')
+    text = 'Clear student'
+    target = 'clear'
+
+    @property
+    def target_url(self):
+        if self.context.state != REQUESTED:
+            return ''
+        return self.view.url(self.view.context, self.target)
+
+class StudentRejectClearanceActionButton(ManageActionButton):
+    grok.order(2)
+    grok.context(IStudent)
+    grok.view(StudentClearanceDisplayFormPage)
+    grok.require('waeup.clearStudent')
+    text = 'Reject clearance'
+    target = 'reject_clearance'
+
+    @property
+    def target_url(self):
+        if self.context.state not in (REQUESTED, CLEARED):
+            return ''
+        return self.view.url(self.view.context, self.target)
+
 class StudentClearanceManageFormPage(WAeUPEditFormPage):
     """ Page to edit student clearance data
@@ -475,4 +503,47 @@
     def save(self, **data):
         msave(self, **data)
+        return
+
+class StudentClearPage(grok.View):
+    """ Clear student by clearance officer
+    """
+    grok.context(IStudent)
+    grok.name('clear')
+    grok.require('waeup.clearStudent')
+
+    def update(self):
+        if self.context.state == REQUESTED:
+            IWorkflowInfo(self.context).fireTransition('clear')
+            self.flash('Student has been cleared.')
+        else:
+            self.flash('Student is in the wrong state.')
+        self.redirect(self.url(self.context,'view_clearance'))
+        return
+
+    def render(self):
+        self.redirect(self.url(self.context, 'view_clearance'))
+        return
+
+class StudentRejectClearancePage(grok.View):
+    """ Reject clearance by clearance officers
+    """
+    grok.context(IStudent)
+    grok.name('reject_clearance')
+    grok.require('waeup.clearStudent')
+
+    def update(self):
+        if self.context.state == CLEARED:
+            IWorkflowInfo(self.context).fireTransition('reset4')
+            self.flash('Clearance has been annulled.')
+        elif self.context.state == REQUESTED:
+            IWorkflowInfo(self.context).fireTransition('reset3')
+            self.flash('Clearance request has been rejected.')
+        else:
+            self.flash('Student is in the wrong state.')
+        self.redirect(self.url(self.context,'view_clearance'))
+        return
+
+    def render(self):
+        self.redirect(self.url(self.context, 'view_clearance'))
         return
 
Index: /main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_browser.py
===================================================================
--- /main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_browser.py	(revision 7157)
+++ /main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_browser.py	(revision 7158)
@@ -32,4 +32,5 @@
 from zope.component.hooks import setSite, clearSite
 from zope.security.interfaces import Unauthorized
+from zope.securitypolicy.interfaces import IPrincipalRoleManager
 from zope.testbrowser.testing import Browser
 from hurry.workflow.interfaces import IWorkflowInfo
@@ -642,4 +643,47 @@
                         in self.browser.contents)
         return
+
+    def test_handle_clearance_by_co(self):
+        # Create clearance officer
+        self.app['users'].addUser('mrclear', 'mrclearsecret')
+        prmglobal = IPrincipalRoleManager(self.app)
+        prmglobal.assignRoleToPrincipal('waeup.StudentsOfficer', 'mrclear')
+        prmlocal = IPrincipalRoleManager(self.app['faculties']['fac1']['dep1'])
+        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
+        IWorkflowInfo(self.student).fireTransition('admit')
+        IWorkflowInfo(self.student).fireTransition('start_clearance')
+        # Login as clearance officer
+        self.browser.open(self.login_path)
+        self.browser.getControl(name="form.login").value = 'mrclear'
+        self.browser.getControl(name="form.password").value = 'mrclearsecret'
+        self.browser.getControl("Login").click()
+        self.assertTrue('You logged in' in self.browser.contents)
+        self.browser.open(self.clearance_student_path)
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.url, self.clearance_student_path)
+        # Only in state clearance requested the co does see the 'Clear' button
+        self.assertFalse('Clear student' in self.browser.contents)
+        IWorkflowInfo(self.student).fireTransition('request_clearance')
+        self.browser.open(self.clearance_student_path)
+        self.assertTrue('Clear student' in self.browser.contents)
+        self.browser.getLink("Clear student").click()
+        self.assertTrue('Student has been cleared' in self.browser.contents)
+        self.assertTrue('State: <span>cleared</span>' in self.browser.contents)
+        self.browser.getLink("Reject clearance").click()
+        self.assertTrue('Clearance has been annulled' in self.browser.contents)
+        self.assertTrue('State: <span>clearance started</span>'
+            in self.browser.contents)
+        IWorkflowInfo(self.student).fireTransition('request_clearance')
+        self.browser.open(self.clearance_student_path)
+        self.browser.getLink("Reject clearance").click()
+        self.assertTrue('Clearance request has been rejected'
+            in self.browser.contents)
+        self.assertTrue('State: <span>clearance started</span>'
+            in self.browser.contents)
+        # The clearance officer can't clear students if not in state
+        # clearance requested
+        self.browser.open(self.student_path + '/clear')
+        self.assertTrue('Student is in the wrong state'
+            in self.browser.contents)
 
     def test_student_change_password(self):
