Index: main/waeup.kofa/trunk/src/waeup/kofa/students/permissions.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/permissions.py	(revision 12843)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/permissions.py	(revision 12847)
@@ -24,7 +24,14 @@
 
 class HandleStudent(grok.Permission):
+    """
+    The HandleStudent permission is reserved for students.
+    Students 'handle' their data. Officers 'manage' the data.
+    """
     grok.name('waeup.handleStudent')
 
 class ViewStudent(grok.Permission):
+    """
+    The ViewStudent permission allows to view all student data.
+    """
     grok.name('waeup.viewStudent')
 
@@ -33,32 +40,63 @@
 
 class ViewStudentsContainer(grok.Permission):
+    """The ViewStudentsContainer permission allows to view the students root
+    container page.
+    """
     grok.name('waeup.viewStudentsContainer')
 
 class PayStudent(grok.Permission):
+    """The PayStudent permission allows to add an online payment ticket and to
+    manage tickets.
+    """
     grok.name('waeup.payStudent')
 
 class HandleAccommodation(grok.Permission):
+    """The HandleAccommodation allows to manage bed tickets.
+    """
     grok.name('waeup.handleAccommodation')
 
 class UploadStudentFile(grok.Permission):
+    """The UploadStudentFile permissions allows to upload the passport picture.
+    The respective page additionally checks the state of the student.
+    """
     grok.name('waeup.uploadStudentFile')
 
 class ManageStudent(grok.Permission):
+    """The ManageStudent permission allows to edit the data.
+    This permission is meant for clearance officers.
+    """
     grok.name('waeup.manageStudent')
 
 class ClearStudent(grok.Permission):
+    """The ClearStudent permission is needed to clear students
+    or to reject clearance. This permission is meant for course advisers.
+    """
     grok.name('waeup.clearStudent')
 
 class ValidateStudent(grok.Permission):
+    """The ValidateStudent permission is needed to validate or reject
+    course lists. This permission is not needed if users
+    already have the TriggerTransition permission.
+    """
     grok.name('waeup.validateStudent')
 
 class EditStudyLevel(grok.Permission):
+    """The EditStudyLevel permission is needed for editing course lists.
+    Students and course advisers do have this permission.
+    """
     grok.name('waeup.editStudyLevel')
 
 class LoginAsStudent(grok.Permission):
+    """The LoginAsStudent is needed to set temporary student passwords
+    and login as (impersonate) students.
+    """
     grok.name('waeup.loginAsStudent')
 
 # Local role
 class StudentRecordOwner(grok.Role):
+    """A student 'owns' her/his student object and subobjects and
+    gains permissions to handle all data, upload a passport picture,
+    add payment tickets, create and edit course lists and handle accommodation.
+    """
     grok.name('waeup.local.StudentRecordOwner')
     grok.title(u'Student Record Owner')
@@ -72,4 +110,7 @@
 # Site Roles
 class StudentRole(grok.Role):
+    """This role is dedicated to students only.
+    It defines the permissions a student gains portal-wide.
+    """
     grok.name('waeup.Student')
     grok.title(u'Student (do not assign)')
@@ -79,4 +120,6 @@
 
 class StudentsOfficer(grok.Role):
+    """The Students Officer is allowed to view all student data.
+    """
     grok.name('waeup.StudentsOfficer')
     grok.title(u'Students Officer (view only)')
@@ -85,4 +128,8 @@
 
 class StudentsManager(grok.Role):
+    """The Students Officer is allowed to edit all student data, to
+    create payment tickets, to handle bed tickets and to upload passport
+    pictures.
+    """
     grok.name('waeup.StudentsManager')
     grok.title(u'Students Manager')
@@ -104,4 +151,10 @@
 
 class StudentsClearanceOfficer(grok.Role):
+    """The global StudentsClearanceOfficer role enables users to view all
+    student data, to clear students and to reject clearance portal-wide.
+    Usually, this role is not assigned manually.
+    We are using the correspondent local role instead which assigns the
+    StudentsClearanceOfficer role dynamically.
+    """
     grok.name('waeup.StudentsClearanceOfficer')
     grok.title(u'Clearance Officer (all students)')
@@ -110,4 +163,10 @@
 
 class StudentsCourseAdviser(grok.Role):
+    """The global StudentsCourseAdviser role enables users to view all
+    student data, to edit, validate or reject course lists  portal-wide.
+    Usually, this role is not assigned manually.
+    We are using the correspondent local role instead which assigns the
+    StudentsCourseAdviser role dynamically.
+    """
     grok.name('waeup.StudentsCourseAdviser')
     grok.title(u'Course Adviser (all students)')
@@ -117,4 +176,7 @@
 
 class StudentImpersonator(grok.Role):
+    """The Student Impersonator gains the LoginAsStudent permission,
+    nothing else, see description above.
+    """
     grok.name('waeup.StudentImpersonator')
     grok.title(u'Student Impersonator')
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 12843)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py	(revision 12847)
@@ -688,4 +688,13 @@
         self.assertEqual(self.browser.headers['Content-Type'],
                          'application/pdf')
+        # We want to see the signature fields.
+        IWorkflowState(self.student).setState('cleared')
+        self.browser.open(self.student_path + '/clearance_slip.pdf')
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.headers['Content-Type'],
+                         'application/pdf')
+        path = os.path.join(samples_dir(), 'clearance_slip.pdf')
+        open(path, 'wb').write(self.browser.contents)
+        print "Sample PDF clearance_slip.pdf written to %s" % path
 
     def test_manage_course_lists(self):
