Index: main/waeup.kofa/trunk/docs/source/userdocs/security.rst
===================================================================
--- main/waeup.kofa/trunk/docs/source/userdocs/security.rst	(revision 12846)
+++ main/waeup.kofa/trunk/docs/source/userdocs/security.rst	(revision 12847)
@@ -13,5 +13,5 @@
 ===========
 
-The whole set of permissions and roles are described in the :py:mod:`Permissions and Roles Module<waeup.kofa.permissions>`. Here we describe only a subset of permission classes which are essential for the security settings configuration.
+The whole set of permission and role classes are described in the :py:mod:`Permissions and Roles Module<waeup.kofa.permissions>`. Here we describe only a subset of permission classes which are essential for the security settings configuration.
 
 General Permissions
@@ -130,4 +130,6 @@
 Many global roles do only bundle one or two permissions. The objective behind is to share responsibilities and distribute tasks.
 
+Global roles are being assigned via the user manage form page.
+
 Global General Roles
 --------------------
@@ -171,4 +173,6 @@
 --------------------------------
 
+Global Application Section Roles are assigned portal-wide (globally) but do actually only allocate permissions in the Application Section.
+
 .. autoclass:: waeup.kofa.applicants.permissions.ApplicantRole()
    :noindex:
@@ -183,4 +187,6 @@
 ----------------------------
 
+Global Student Section Roles are assigned portal-wide (globally) but do actually only allocate permissions in the Student Section.
+
 .. autoclass:: waeup.kofa.students.permissions.StudentRole()
    :noindex:
@@ -201,7 +207,53 @@
    :noindex:
 
-Local Roles
-===========
-
-Dynamic Roles
-=============
+Local Roles and Dynamic Role Assignment
+=======================================
+
+In contrast to global roles, which are assigned portal-wide, local role permissions are gained for a specific context.
+
+Some local roles serve a second purpose. At first glance it appears strange that some of these 'odd' roles do not give more permissions than the user already has due to other roles. Their real purpose is to delegate permissions to the students or application section. If a user has for example the LocalStudentsManager role described below at department level, s/he automatically gets the StudentsManager role for those students studying in this department. We call this a **dynamic role**. In contrast to static global or local roles, dynamic roles are not stored in the database, they are dynamically assigned.
+
+Local roles are assigned either automatically by the system during user object setup or manually through the web interface. The automatically assigned local roles are:
+
+.. autoclass:: waeup.kofa.permissions.Owner()
+   :noindex:
+
+.. autoclass:: waeup.kofa.applicants.permissions.ApplicationOwner()
+   :noindex:
+
+.. autoclass:: waeup.kofa.students.permissions.StudentRecordOwner()
+   :noindex:
+
+All other local roles must be assigned manually via context manage form pages.
+
+.. autoclass:: waeup.kofa.permissions.ApplicationManager()
+   :noindex:
+
+.. autoclass:: waeup.kofa.permissions.DepartmentOfficer()
+   :noindex:
+
+.. autoclass:: waeup.kofa.permissions.DepartmentManager()
+   :noindex:
+
+.. autoclass:: waeup.kofa.permissions.Lecturer()
+   :noindex:
+
+The following local roles do also delegate permissions to the student section. In other words, dynamic roles are assigned.
+
+.. autoclass:: waeup.kofa.permissions.ClearanceOfficer()
+   :noindex:
+
+.. autoclass:: waeup.kofa.permissions.LocalStudentsManager()
+   :noindex:
+
+.. autoclass:: waeup.kofa.permissions.LocalWorkflowManager()
+   :noindex:
+
+.. autoclass:: waeup.kofa.permissions.UGClearanceOfficer()
+   :noindex:
+
+.. autoclass:: waeup.kofa.permissions.PGClearanceOfficer()
+   :noindex:
+
+.. autoclass:: waeup.kofa.permissions.CourseAdviser100()
+   :noindex:
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/permissions.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/permissions.py	(revision 12846)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/permissions.py	(revision 12847)
@@ -24,7 +24,12 @@
 
 class HandleApplication(grok.Permission):
+    """The HandleApplication permission is reserved for applicants.
+    Applicants 'handle' their data. Officers 'manage' the data.
+    """
     grok.name('waeup.handleApplication')
 
 class ViewApplication(grok.Permission):
+    """The ViewApplication permission allows to view application records.
+    """
     grok.name('waeup.viewApplication')
 
@@ -36,22 +41,39 @@
 
 class ManageApplication(grok.Permission):
+    """The ManageApplication permission allows to edit the data. This
+    permission is reserved for officers and portal managers.
+    """
     grok.name('waeup.manageApplication')
 
 class ViewApplicationStatistics(grok.Permission):
+    """The ViewApplicationStatistics permission allows to perform statistical
+    evaluations. Only portal managers have this permission.
+    """
     grok.name('waeup.viewApplicationStatistics')
 
 class PayApplicant(grok.Permission):
+    """The PayApplicant permission allows to add an online payment ticket.
+    """
     grok.name('waeup.payApplicant')
 
 # Local role
+
 class ApplicationOwner(grok.Role):
+    """An applicant 'owns' her/his application record and
+    gains permissions to handle the record, upload a passport picture or
+    add payment tickets.
+    """
     grok.name('waeup.local.ApplicationOwner')
     grok.title(u'Application Owner')
-    grok.permissions('waeup.handleApplication', 'waeup.viewApplication',
+    grok.permissions('waeup.handleApplication',
+                     'waeup.viewApplication',
                      'waeup.payApplicant')
 
-# Site role
+# Site roles
 
 class ApplicantRole(grok.Role):
+    """This role is dedicated to applicants only. It defines the permissions
+    an applicant gains portal-wide.
+    """
     grok.name('waeup.Applicant')
     grok.permissions('waeup.viewAcademics', 'waeup.viewMyApplicationDataTab',
@@ -59,4 +81,6 @@
 
 class ApplicationsOfficer(grok.Role):
+    """The Applications Officer is allowed to view all application records.
+    """
     grok.name('waeup.ApplicationsOfficer')
     grok.title(u'Applications Officer (view only)')
@@ -64,4 +88,7 @@
 
 class ApplicationsManager(grok.Role):
+    """The Applications Officer is allowed to edit all application records.
+    The role allows also to add payment tickets.
+    """
     grok.name('waeup.ApplicationsManager')
     grok.title(u'Applications Manager')
Index: main/waeup.kofa/trunk/src/waeup/kofa/permissions.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/permissions.py	(revision 12846)
+++ main/waeup.kofa/trunk/src/waeup/kofa/permissions.py	(revision 12847)
@@ -115,5 +115,5 @@
     """The ManageDataCenter permission allows to access all pages
     in the Data Center and to upload files. It does not automatically
-    allow to process uploaded data.
+    allow to process uploaded data files.
     """
     grok.name('waeup.manageDataCenter')
@@ -121,5 +121,5 @@
 class ImportData(grok.Permission):
     """The ImportData permission allows to batch process (import) any kind of 
-    portal data except for user data. This User Data processor
+    portal data except for user data. The User Data processor
     requires also the ManageUsers permission.
     """
@@ -164,6 +164,9 @@
 
 # Local Roles
+
 class ApplicationsManager(grok.Role):
-    """
+    """The local ApplicationsManager role can be assigned at department level.
+    Local Applications Managers do not gain further permissions. This role is
+    meant for the assignment of dynamic roles only, see below.
     """
     grok.name('waeup.local.ApplicationsManager')
@@ -172,5 +175,10 @@
 
 class DepartmentManager(grok.Role):
-    """
+    """The local DepartmentManager role can be assigned at faculty or
+    department level. The role allows to edit all data within this container.
+    It does not automatically allow to remove sub-containers.
+
+    Department Managers (Dean of Faculty or Head of Department respectively)
+    can also list student data but not access student pages.
     """
     grok.name('waeup.local.DepartmentManager')
@@ -181,5 +189,11 @@
 
 class DepartmentOfficer(grok.Role):
-    """
+    """The local DepartmentOfficer role can be assigned at faculty or
+    department level. The role allows to list all student data within the
+    faculty/department the local role is assigned.
+
+    Department Managers (Dean of Faculty or Head of Department respectively)
+    can also list student data but not access student pages. They can
+    furthermore export payment overviews.
     """
     grok.name('waeup.local.DepartmentOfficer')
@@ -190,6 +204,11 @@
 
 class ClearanceOfficer(grok.Role):
-    """The clearance officer role is meant for the
-    assignment of dynamic roles only.
+    """The local ClearanceOfficer role can be assigned at faculty or
+    department level. The role allows to list or export all student
+    data within the faculty/department the local role is assigned.
+
+    Clearance Officers can furthermore clear all students or reject clearance
+    of all students in their faculty/department. They get the
+    StudentsClearanceOfficer role for this subset of students.
     """
     grok.name('waeup.local.ClearanceOfficer')
@@ -201,6 +220,11 @@
 
 class LocalStudentsManager(grok.Role):
-    """The local students manager role is meant for the
-    assignment of dynamic roles only.
+    """The local LocalStudentsManager role can be assigned at faculty or
+    department level. The role allows to view all data and to view or export
+    all student data within the faculty/department the local role is assigned.
+
+    Local Students Managers can furthermore manage data of students
+    in their faculty/department. They get the StudentsManager role for
+    this subset of students.
     """
     grok.name('waeup.local.LocalStudentsManager')
@@ -211,6 +235,11 @@
 
 class LocalWorkflowManager(grok.Role):
-    """The local workflow manager role is meant for the
-    assignment of dynamic roles only.
+    """The local LocalWorkflowManager role can be assigned at faculty level.
+    The role allows to view all data and to list or export
+    all student data within the faculty the local role is assigned.
+
+    Local Workflow Managers can trigger transition of students in their
+    faculty/department. They get the WorkflowManager role for
+    this subset of students.
     """
     grok.name('waeup.local.LocalWorkflowManager')
@@ -221,6 +250,7 @@
 
 class UGClearanceOfficer(grok.Role):
-    """The clearance officer role is meant for the
-    assignment of dynamic roles only.
+    """UG Clearance Officers are regular Clearance Officers with restricted
+    dynamic permission assignment. They can only access undergraduate
+    students.
     """
     grok.name('waeup.local.UGClearanceOfficer')
@@ -232,6 +262,7 @@
 
 class PGClearanceOfficer(grok.Role):
-    """The clearance officer role is meant for the
-    assignment of dynamic roles only.
+    """PG Clearance Officers are regular Clearance Officers with restricted
+    dynamic permission assignment. They can only access postgraduate
+    students.
     """
     grok.name('waeup.local.PGClearanceOfficer')
@@ -243,6 +274,12 @@
 
 class CourseAdviser100(grok.Role):
-    """The 100 level course adviser role is meant for the
-    assignment of dynamic roles only.
+    """The local CourseAdviser100 role can be assigned at faculty,
+    department or certificate level. The role allows to view all data and 
+    to list or export all student data within the faculty, department 
+    or certificate the local role is assigned.
+
+    Local Course Advisers can validate or reject course lists of students
+    in ther faculty/department/certificate at level 100.
+    They get the StudentsCourseAdviser role for this subset of students.
     """
     grok.name('waeup.local.CourseAdviser100')
@@ -253,6 +290,5 @@
 
 class CourseAdviser200(grok.Role):
-    """The course 200 level adviser role is meant for the
-    assignment of dynamic roles only.
+    """Same as CourseAdviser100 but for level 200.
     """
     grok.name('waeup.local.CourseAdviser200')
@@ -263,6 +299,5 @@
 
 class CourseAdviser300(grok.Role):
-    """The 300 level course adviser role is meant for the
-    assignment of dynamic roles only.
+    """Same as CourseAdviser100 but for level 300.
     """
     grok.name('waeup.local.CourseAdviser300')
@@ -273,6 +308,5 @@
 
 class CourseAdviser400(grok.Role):
-    """The 400 level course adviser role is meant for the
-    assignment of dynamic roles only.
+    """Same as CourseAdviser100 but for level 400.
     """
     grok.name('waeup.local.CourseAdviser400')
@@ -283,6 +317,5 @@
 
 class CourseAdviser500(grok.Role):
-    """The 500 level course adviser role is meant for the
-    assignment of dynamic roles only.
+    """Same as CourseAdviser100 but for level 500.
     """
     grok.name('waeup.local.CourseAdviser500')
@@ -293,6 +326,5 @@
 
 class CourseAdviser600(grok.Role):
-    """The 600 level course adviser role is meant for the
-    assignment of dynamic roles only.
+    """Same as CourseAdviser100 but for level 600.
     """
     grok.name('waeup.local.CourseAdviser600')
@@ -303,6 +335,5 @@
 
 class CourseAdviser700(grok.Role):
-    """The 700 level course adviser role is meant for the
-    assignment of dynamic roles only.
+    """Same as CourseAdviser100 but for level 700.
     """
     grok.name('waeup.local.CourseAdviser700')
@@ -313,6 +344,5 @@
 
 class CourseAdviser800(grok.Role):
-    """The 800 level course adviser role is meant for the
-    assignment of dynamic roles only.
+    """Same as CourseAdviser100 but for level 800.
     """
     grok.name('waeup.local.CourseAdviser800')
@@ -323,6 +353,8 @@
 
 class Lecturer(grok.Role):
-    """The lecturer role is meant for the
-    assignment of dynamic roles only.
+    """The local Lecturer role can be assigned at course level.
+    The role allows to view all data and to list or export all student
+    ata within course the local role is assigned. Lecturers can't access
+    student data directly but they can edit the scores in course tickets.
     """
     grok.name('waeup.local.Lecturer')
@@ -334,4 +366,7 @@
 
 class Owner(grok.Role):
+    """Each user 'owns' her/his user object and gains permission to edit
+    some of the user attributes.
+    """
     grok.name('waeup.local.Owner')
     grok.title(u'Owner')
@@ -339,4 +374,5 @@
 
 # Site Roles
+
 class AcademicsOfficer(grok.Role):
     """An Academics Officer can view but not edit data in the
@@ -364,5 +400,5 @@
 class ACManager(grok.Role):
     """This is the role for Access Code Managers.
-    An ACManager can view and manage the Accesscodes Section, see
+    An AC Manager can view and manage the Accesscodes Section, see
     ManageACBatches permission above.
     """
@@ -374,5 +410,5 @@
     """This single-permission role is dedicated to those users
     who are charged with batch processing of portal data.
-    A DataCenterManager manager can access all pages in the Data Center,
+    A Data Center Manager can access all pages in the Data Center,
     see ManageDataCenter permission above.
     """
@@ -382,8 +418,8 @@
 
 class ImportManager(grok.Role):
-    """An ImportManager is a DataCenterManager who is also allowed
+    """An Import Manager is a Data Center Manager who is also allowed
     to batch process (import) data. All batch processors (importers) are 
     available except for the User Processor. This processor requires the
-    UsersManager role too. The ImportManager role includes the
+    Users Manager role too. The ImportManager role includes the
     DataCenterManager role but not vice versa.
     """
@@ -394,5 +430,5 @@
 
 class ExportManager(grok.Role):
-    """An ExportManager is a DataCenterManager who is also allowed
+    """An Export Manager is a Data Center Manager who is also allowed
     to export all kind of portal data. The ExportManager role includes the
     DataCenterManager role but not vice versa.
@@ -404,5 +440,5 @@
 
 class BursaryOfficer(grok.Role):
-    """BursaryOfficers can export bursary data. They can't access the
+    """Bursary Officers can export bursary data. They can't access the
     Data Center but see student data export buttons in the Academic Section.
     """
@@ -414,5 +450,5 @@
 
 class UsersManager(grok.Role):
-    """A UsersManager can add, remove or edit
+    """A Users Manager can add, remove or edit
     user accounts, see ManageUsers permission for further information.
     Be very careful with this role.
@@ -424,5 +460,5 @@
 
 class WorkflowManager(grok.Role):
-    """The WorkflowManager can trigger workflow transitions
+    """The Workflow Manager can trigger workflow transitions
     of student and document objects, see TriggerTransition permission
     for further information.
@@ -433,5 +469,5 @@
 
 class PortalManager(grok.Role):
-    """The portal manager role is the maximum set of Kofa permissions
+    """The PortalManager role is the maximum set of Kofa permissions
     which are needed to manage the entire portal. This set must not
     be customized. It is recommended to assign this role only
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/permissions.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/permissions.py	(revision 12846)
+++ 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 12846)
+++ 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):
