Index: main/waeup.kofa/trunk/CHANGES.txt
===================================================================
--- main/waeup.kofa/trunk/CHANGES.txt	(revision 13907)
+++ main/waeup.kofa/trunk/CHANGES.txt	(revision 13908)
@@ -4,4 +4,7 @@
 1.4.2.dev0 (unreleased)
 =======================
+
+* Add `LecturerLandingPage` and `LecturerCourses` `ManageLink`
+  viewlet.
 
 * Add `ExportPDFScoresSlip`.
Index: main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py	(revision 13907)
+++ main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py	(revision 13908)
@@ -365,4 +365,9 @@
                     self.redirect(self.application_url() + rel_link)
                     return
+                # Redirect lecturer after login
+                local_roles = get_user_account(self.request).getLocalRoles()
+                if local_roles.has_key('waeup.local.Lecturer'):
+                    self.redirect(self.application_url() + '/my_courses')
+                    return
                 if not self.camefrom:
                     self.redirect(self.application_url() + '/index')
@@ -978,4 +983,28 @@
         return site_roles_userfriendly
 
+class LecturerLandingPage(KofaPage):
+    """Display courses when lecturer logs in.
+    """
+    grok.name('my_courses')
+    grok.require('waeup.Authenticated')
+    grok.context(IUniversity)
+    grok.template('mycourses')
+    label =_('My Courses')
+
+    @property
+    def academic_session(self):
+        current_academic_session = grok.getSite()[
+            'configuration'].current_academic_session
+        if current_academic_session:
+            session = academic_sessions_vocab.getTerm(
+                current_academic_session).title
+            return session
+        return _('no academic session set')
+
+    @property
+    def getCourses(self):
+        local_roles = get_user_account(self.request).getLocalRoles()
+        return local_roles['waeup.local.Lecturer']
+
 #
 # Search pages...
Index: main/waeup.kofa/trunk/src/waeup/kofa/browser/templates/mycourses.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/browser/templates/mycourses.pt	(revision 13908)
+++ main/waeup.kofa/trunk/src/waeup/kofa/browser/templates/mycourses.pt	(revision 13908)
@@ -0,0 +1,12 @@
+<h3 i18n:domain="waeup.kofa" i18n:translate="">
+  Academic Session: <span tal:content="view/academic_session">SESSION</span>
+</h3>
+
+<div tal:repeat="course view/getCourses">
+    <a tal:attributes="href python:view.url(course)">
+      <span tal:content="course/title">ROLE</span>
+      (<span tal:content="course/code">ROLE</span>)
+    </a>
+</div>
+
+
Index: main/waeup.kofa/trunk/src/waeup/kofa/browser/viewlets.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/browser/viewlets.py	(revision 13907)
+++ main/waeup.kofa/trunk/src/waeup/kofa/browser/viewlets.py	(revision 13908)
@@ -300,6 +300,5 @@
         url = self.view.url(grok.getSite(), self.link)
         text = translate(self.text, context=self.request)
-        return u'<li><a href="%s">%s</a></li>' % (
-                url, text)
+        return u'<li><a href="%s">%s</a></li>' % (url, text)
 
 class ManagePortalConfiguration(ManageLink):
@@ -339,8 +338,27 @@
     text = _(u'Reports')
 
+class LecturerCourses(ManageLink):
+    """A link to courses of lecturers.
+    """
+    grok.order(6)
+    grok.require('waeup.Authenticated')
+
+    link = u'my_courses'
+    text = _(u'My Courses')
+
+    def render(self):
+        user_account = get_user_account(self.request)
+        if user_account:
+            local_roles = user_account.getLocalRoles()
+            if local_roles.has_key('waeup.local.Lecturer'):
+                url = self.view.url(grok.getSite(), self.link)
+                text = translate(self.text, context=self.request)
+                return u'<li><a href="%s">%s</a></li>' % (url, text)
+        return ''
+
 class MyPreferences(ManageLink):
     """A link to personal preferences, placed in upper left box.
     """
-    grok.order(6)
+    grok.order(7)
     grok.require('waeup.Public')
     text = _(u'My Preferences')
@@ -351,6 +369,5 @@
             url = self.view.url(account_object)
             text = translate(self.text, context=self.request)
-            return u'<li><a href="%s">%s</a></li>' % (
-                    url, text)
+            return u'<li><a href="%s">%s</a></li>' % (url, text)
         return ''
 
@@ -358,5 +375,5 @@
     """A link to display site and local roles.
     """
-    grok.order(7)
+    grok.order(8)
     grok.require('waeup.Public')
     text = _(u'My Roles')
@@ -367,6 +384,5 @@
             url = self.view.url(account_object) + '/my_roles'
             text = translate(self.text, context=self.request)
-            return u'<li><a href="%s">%s</a></li>' % (
-                    url, text)
+            return u'<li><a href="%s">%s</a></li>' % (url, text)
         return ''
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 13907)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 13908)
@@ -3209,5 +3209,5 @@
             code=(self.context.code, self.context.code)
             )
-        result = [[_('Matric No.'),
+        header = [[_('Matric No.'),
                    _('Reg. No.'),
                    _('Fullname'),
@@ -3216,4 +3216,5 @@
                    _('Level'),
                    _('Score') ],]
+        tickets = []
         for ticket in list(coursetickets):
             row = [ticket.student.matric_number,
@@ -3224,6 +3225,6 @@
                   ticket.level,
                   ticket.score]
-            result.append(row)
-        return result
+            tickets.append(row)
+        return header + sorted(tickets, key=lambda value: value[0])
 
     def render(self):
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 13907)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 13908)
@@ -1821,29 +1821,31 @@
         prmlocal = IPrincipalRoleManager(course)
         prmlocal.assignRoleToPrincipal('waeup.local.Lecturer', 'mrslecturer')
+        notify(LocalRoleSetEvent(
+            course, 'waeup.local.Lecturer', 'mrslecturer', granted=True))
         # Login as lecturer.
         self.browser.open(self.login_path)
         self.browser.getControl(name="form.login").value = 'mrslecturer'
-        self.browser.getControl(name="form.password").value = 'mrslecturersecret'
+        self.browser.getControl(
+            name="form.password").value = 'mrslecturersecret'
         self.browser.getControl("Login").click()
         self.assertMatches('...You logged in...', self.browser.contents)
-        # CO can see her roles.
+        # Lecturer is on landing page
+        self.assertTrue('(<span>COURSE1</span>)' in self.browser.contents)
+        # Lecturer can click the 'My Roles' link.
         self.browser.getLink("My Roles").click()
         self.assertMatches(
             '...<div>Academics Officer (view only)</div>...',
             self.browser.contents)
-        # But not her local role ...
-        self.assertFalse('Lecturer' in self.browser.contents)
-        # ... because we forgot to notify the course that the local role
-        # has changed.
-        notify(LocalRoleSetEvent(
-            course, 'waeup.local.Lecturer', 'mrslecturer', granted=True))
-        self.browser.open('http://localhost/app/users/mrslecturer/my_roles')
-        self.assertTrue('Lecturer' in self.browser.contents)
         self.assertMatches(
-            '...<a href="http://localhost/app/faculties/fac1/dep1/courses/COURSE1">...',
+            '...<a href="http://localhost/app/'
+            'faculties/fac1/dep1/courses/COURSE1">...',
             self.browser.contents)
+        self.assertFalse('(<span>COURSE1</span>)' in self.browser.contents)
+        # Lecturer can click the 'My Courses' link ...
+        self.browser.getLink("My Courses").click()
+        # ... and is is back on landing page
+        self.assertTrue('(<span>COURSE1</span>)' in self.browser.contents)
         # The lecturer can go to her course ...
-        self.browser.getLink(
-            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1").click()
+        self.browser.getLink("COURSE1").click()
         # Lecturers can neither filter students ...
         self.assertRaises(
@@ -1861,10 +1863,12 @@
         # Course results can be batch edited via the edit_courses view.
         self.app['faculties']['fac1']['dep1'].score_editing_disabled = True
-        self.browser.open("http://localhost/app/faculties/fac1/dep1/courses/COURSE1")
+        self.browser.open(
+            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1")
         self.browser.getLink("Update scores").click()
         self.assertTrue('Score editing disabled' in self.browser.contents)
         self.app['faculties']['fac1']['dep1'].score_editing_disabled = False
         self.browser.getLink("Update scores").click()
-        self.assertTrue('Current academic session not set' in self.browser.contents)
+        self.assertTrue(
+            'Current academic session not set' in self.browser.contents)
         self.app['configuration'].current_academic_session = 2004
         self.browser.getLink("Update scores").click()
@@ -1875,5 +1879,6 @@
         # Student must be in state 'courses validated'.
         self.browser.open(
-            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
+            'http://localhost/app/faculties/fac1/'
+            'dep1/courses/COURSE1/edit_scores')
         self.assertTrue(
             'input type="text" name="scores"' in self.browser.contents)
@@ -1888,8 +1893,9 @@
         logcontent = open(logfile).read()
         self.assertTrue('mrslecturer - students.browser.EditScoresPage - '
-                        'K1000000 100/COURSE1 score updated (55)' in logcontent)
+            'K1000000 100/COURSE1 score updated (55)' in logcontent)
         # Non-integer scores won't be accepted.
         self.browser.open(
-            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
+            'http://localhost/app/faculties/fac1/'
+            'dep1/courses/COURSE1/edit_scores')
         self.assertTrue('value="55" />' in self.browser.contents)
         self.browser.getControl(name="scores", index=0).value = 'abc'
@@ -1899,5 +1905,6 @@
         # Scores can be removed.
         self.browser.open(
-            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/edit_scores")
+            'http://localhost/app/faculties/fac1/'
+            'dep1/courses/COURSE1/edit_scores')
         self.browser.getControl(name="scores", index=0).value = ''
         self.browser.getControl("Update scores").click()
@@ -1906,8 +1913,9 @@
         logcontent = open(logfile).read()
         self.assertTrue('mrslecturer - students.browser.EditScoresPage - '
-                        'K1000000 100/COURSE1 score updated (None)' in logcontent)
+            'K1000000 100/COURSE1 score updated (None)' in logcontent)
         # A course ticket slip can be downloaded
         self.browser.open(
-            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1/coursetickets.pdf")
+            'http://localhost/app/faculties/fac1/'
+            'dep1/courses/COURSE1/coursetickets.pdf')
         self.assertEqual(self.browser.headers['Status'], '200 Ok')
         self.assertEqual(self.browser.headers['Content-Type'],
@@ -1916,5 +1924,4 @@
         open(path, 'wb').write(self.browser.contents)
         print "Sample PDF coursetickets.pdf written to %s" % path
-
 
     def test_change_current_mode(self):
