Index: /main/waeup.uniben/trunk/src/waeup/uniben/students/browser.py
===================================================================
--- /main/waeup.uniben/trunk/src/waeup/uniben/students/browser.py	(revision 9209)
+++ /main/waeup.uniben/trunk/src/waeup/uniben/students/browser.py	(revision 9210)
@@ -18,7 +18,13 @@
 import grok
 from zope.i18n import translate
+from zope.schema.interfaces import ConstraintNotSatisfied
+from waeup.kofa.interfaces import REQUESTED
 from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
+from waeup.kofa.browser import KofaEditFormPage
+from waeup.kofa.browser.viewlets import ManageActionButton
+from waeup.kofa.browser.layout import action
 from waeup.kofa.students.browser import (
-    StudentClearPage, StudentRejectClearancePage)
+    StudentClearPage, StudentRejectClearancePage,
+    StudyCourseDisplayFormPage, msave, emit_lock_message)
 from kofacustom.nigeria.students.browser import (
     NigeriaOnlinePaymentDisplayFormPage,
@@ -26,5 +32,7 @@
     NigeriaExportPDFPaymentSlipPage)
 
-from waeup.uniben.students.interfaces import ICustomStudentOnlinePayment
+from waeup.uniben.students.interfaces import (
+    ICustomStudentOnlinePayment, ICustomStudentStudyCourse)
+from waeup.uniben.interfaces import MessageFactory as _
 
 class CustomOnlinePaymentDisplayFormPage(NigeriaOnlinePaymentDisplayFormPage):
@@ -70,2 +78,48 @@
 #        self.redirect(self.url(self.context,'view_clearance'))
 #        return
+
+class StudyCourseEditActionButton(ManageActionButton):
+    grok.order(1)
+    grok.context(ICustomStudentStudyCourse)
+    grok.view(StudyCourseDisplayFormPage)
+    grok.require('waeup.clearStudent')
+    text = _('Edit level')
+    target = 'edit_level'
+
+    @property
+    def target_url(self):
+        if self.context.is_current and self.context.student.state == REQUESTED:
+            return self.view.url(self.view.context, self.target)
+        return False
+
+class StudyCourseCOEditFormPage(KofaEditFormPage):
+    """ Page to edit the student study course data by clearance officers
+
+    This form page is available only in Uniben.
+    """
+    grok.context(ICustomStudentStudyCourse)
+    grok.name('edit_level')
+    grok.require('waeup.clearStudent')
+    label = _('Edit current level')
+    pnav = 4
+    form_fields = grok.AutoFields(
+        ICustomStudentStudyCourse).select('current_level')
+
+    def update(self):
+        if not (self.context.is_current and
+            self.context.student.state == REQUESTED):
+            emit_lock_message(self)
+            return
+        super(StudyCourseCOEditFormPage, self).update()
+        return
+
+    @action(_('Save'), style='primary')
+    def save(self, **data):
+        try:
+            msave(self, **data)
+        except ConstraintNotSatisfied:
+            # The selected level might not exist in certificate
+            self.flash(_('Current level not available for certificate.'))
+            return
+        #notify(grok.ObjectModifiedEvent(self.context.__parent__))
+        return
Index: /main/waeup.uniben/trunk/src/waeup/uniben/students/tests/test_browser.py
===================================================================
--- /main/waeup.uniben/trunk/src/waeup/uniben/students/tests/test_browser.py	(revision 9209)
+++ /main/waeup.uniben/trunk/src/waeup/uniben/students/tests/test_browser.py	(revision 9210)
@@ -20,8 +20,11 @@
 import tempfile
 from StringIO import StringIO
-from hurry.workflow.interfaces import IWorkflowState
+from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
+from zope.securitypolicy.interfaces import IPrincipalRoleManager
 from zope.component.hooks import setSite, clearSite
 from zope.component import getUtility, createObject
 from zope.interface import verify
+from zope.event import notify
+from waeup.kofa.authentication import LocalRoleSetEvent
 from waeup.kofa.app import University
 from waeup.kofa.students.tests.test_browser import StudentsFullSetup
@@ -372,2 +375,37 @@
         self.assertEqual(error, None)
         return
+
+    def test_edit_level_by_co(self):
+        # Create clearance officer
+        self.app['users'].addUser('mrclear', 'mrclearsecret')
+        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
+        self.app['users']['mrclear'].title = 'Carlo Pitter'
+        # Assign local ClearanceOfficer role
+        department = self.app['faculties']['fac1']['dep1']
+        prmlocal = IPrincipalRoleManager(department)
+        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
+        notify(LocalRoleSetEvent(
+            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
+        IWorkflowState(self.student).setState('clearance started')
+        # 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.assertMatches('...You logged in...', self.browser.contents)
+        # Only in state clearance requested the CO does see the
+        # 'Edit level' button ...
+        self.browser.open(self.studycourse_path)
+        self.assertFalse('Edit level' in self.browser.contents)
+        # ... and can open the edit_level view
+        self.browser.open(self.studycourse_path + '/edit_level')
+        self.assertMatches('...is locked...', self.browser.contents)
+        self.assertEqual(self.browser.url, self.studycourse_path)
+        IWorkflowInfo(self.student).fireTransition('request_clearance')
+        self.browser.open(self.studycourse_path)
+        self.assertTrue('Edit level' in self.browser.contents)
+        self.browser.getLink("Edit level").click()
+        self.browser.getControl(name="form.current_level").value = ['200']
+        self.browser.getControl("Save").click()
+        self.assertMatches('...has been saved...', self.browser.contents)
+        self.assertEqual(self.student.current_level, 200)
