Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/browser.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/browser.py	(revision 9562)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/browser.py	(revision 9564)
@@ -39,4 +39,5 @@
 from kofacustom.nigeria.students.interfaces import (
     INigeriaStudentBase, INigeriaStudent, INigeriaStudentPersonal,
+    INigeriaStudentPersonalEdit,
     INigeriaUGStudentClearance,INigeriaPGStudentClearance,
     INigeriaStudentOnlinePayment
@@ -83,5 +84,5 @@
     """ Page to edit personal data
     """
-    form_fields = grok.AutoFields(INigeriaStudentPersonal).omit('personal_updated')
+    form_fields = grok.AutoFields(INigeriaStudentPersonalEdit).omit('personal_updated')
 
 class NigeriaStudentPersonalManageFormPage(StudentPersonalManageFormPage):
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/interfaces.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/interfaces.py	(revision 9562)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/interfaces.py	(revision 9564)
@@ -58,4 +58,316 @@
     'reg_number'].order
 
+
+class INigeriaUGStudentClearance(IUGStudentClearance):
+    """Representation of ug student clearance data.
+
+    """
+    officer_comment = schema.Text(
+        title = _(u"Officer's Comment"),
+        required = False,
+        )
+
+    clearance_locked = schema.Bool(
+        title = _(u'Clearance form locked'),
+        default = False,
+        required = False,
+        )
+
+    clr_code = schema.TextLine(
+        title = _(u'CLR Activation Code'),
+        required = False,
+        readonly = False,
+        )
+
+    date_of_birth = FormattedDate(
+        title = _(u'Date of Birth'),
+        required = False,
+        show_year = True,
+        )
+
+    nationality = schema.Choice(
+        source = nats_vocab,
+        title = _(u'Nationality'),
+        required = True,
+        )
+
+    lga = schema.Choice(
+        source = LGASource(),
+        title = _(u'State/LGA (Nigerians only)'),
+        required = False,
+        )
+
+    def_adm = schema.Bool(
+        title = _(u'Deferent of Admission'),
+        required = False,
+        readonly = False,
+        )
+
+    fst_sit_fname = schema.TextLine(
+        title = _(u'Full Name'),
+        required = False,
+        readonly = False,
+        )
+    fst_sit_no = schema.TextLine(
+        title = _(u'Exam Number'),
+        required = False,
+        readonly = False,
+        )
+
+    fst_sit_date = FormattedDate(
+        title = _(u'Exam Date'),
+        required = False,
+        readonly = False,
+        show_year = True,
+        )
+
+    fst_sit_type = schema.Choice(
+        title = _(u'Exam Type'),
+        required = False,
+        readonly = False,
+        vocabulary = exam_types,
+        )
+
+    fst_sit_results = schema.List(
+        title = _(u'Exam Results'),
+        value_type = ResultEntryField(),
+        required = False,
+        readonly = False,
+        default = [],
+        )
+
+    scd_sit_fname = schema.TextLine(
+        title = _(u'Full Name'),
+        required = False,
+        readonly = False,
+        )
+    scd_sit_no = schema.TextLine(
+        title = _(u'Exam Number'),
+        required = False,
+        readonly = False,
+        )
+
+    scd_sit_date = FormattedDate(
+        title = _(u'Exam Date'),
+        required = False,
+        readonly = False,
+        show_year = True,
+        )
+
+    scd_sit_type = schema.Choice(
+        title = _(u'Exam Type'),
+        required = False,
+        readonly = False,
+        vocabulary = exam_types,
+        )
+
+    scd_sit_results = schema.List(
+        title = _(u'Exam Results'),
+        value_type = ResultEntryField(),
+        required = False,
+        readonly = False,
+        default = [],
+        )
+
+    alr_fname = schema.TextLine(
+        title = _(u'Full Name'),
+        required = False,
+        readonly = False,
+        )
+    alr_no = schema.TextLine(
+        title = _(u'Exam Number'),
+        required = False,
+        readonly = False,
+        )
+
+    alr_date = FormattedDate(
+        title = _(u'Exam Date'),
+        required = False,
+        readonly = False,
+        show_year = True,
+        )
+
+    alr_results = schema.List(
+        title = _(u'Exam Results'),
+        value_type = ResultEntryField(),
+        required = False,
+        readonly = False,
+        default = [],
+        )
+
+    hq_type = schema.Choice(
+        title = _(u'Qualification Obtained'),
+        required = False,
+        readonly = False,
+        vocabulary = high_qual,
+        )
+
+    hq_fname = schema.TextLine(
+        title = _(u'Full Name'),
+        required = False,
+        readonly = False,
+        )
+
+    hq_matric_no = schema.TextLine(
+        title = _(u'Former Matric Number'),
+        required = False,
+        readonly = False,
+        )
+
+    hq_degree = schema.Choice(
+        title = _(u'Class of Degree'),
+        required = False,
+        readonly = False,
+        vocabulary = high_grade,
+        )
+
+    hq_school = schema.TextLine(
+        title = _(u'Institution Attended'),
+        required = False,
+        readonly = False,
+        )
+
+    hq_session = schema.TextLine(
+        title = _(u'Years Attended'),
+        required = False,
+        readonly = False,
+        )
+
+    hq_disc = schema.TextLine(
+        title = _(u'Discipline'),
+        required = False,
+        readonly = False,
+        )
+
+
+class INigeriaPGStudentClearance(INigeriaUGStudentClearance):
+    """Representation of pg student clearance data.
+
+    """
+
+    hq2_type = schema.Choice(
+        title = _(u'Qualification Obtained'),
+        required = False,
+        readonly = False,
+        vocabulary = high_qual,
+        )
+
+    hq2_matric_no = schema.TextLine(
+        title = _(u'Former Matric Number'),
+        required = False,
+        readonly = False,
+        )
+
+    hq2_degree = schema.Choice(
+        title = _(u'Class of Degree'),
+        required = False,
+        readonly = False,
+        vocabulary = high_grade,
+        )
+
+    hq2_school = schema.TextLine(
+        title = _(u'Institution Attended'),
+        required = False,
+        readonly = False,
+        )
+
+    hq2_session = schema.TextLine(
+        title = _(u'Years Attended'),
+        required = False,
+        readonly = False,
+        )
+
+    hq2_disc = schema.TextLine(
+        title = _(u'Discipline'),
+        required = False,
+        readonly = False,
+        )
+
+    nysc_year = schema.Int(
+        title = _(u'Nysc Year'),
+        required = False,
+        readonly = False,
+        )
+
+    nysc_location = schema.TextLine(
+        title = _(u'Nysc Location'),
+        required = False,
+        )
+
+    nysc_lga = schema.Choice(
+        source = LGASource(),
+        title = _(u'Nysc LGA'),
+        required = False,
+        )
+
+    employer = schema.TextLine(
+        title = _(u'Employer'),
+        required = False,
+        readonly = False,
+        )
+
+    emp_position = schema.TextLine(
+        title = _(u'Employer Position'),
+        required = False,
+        readonly = False,
+        )
+
+    emp_start = FormattedDate(
+        title = _(u'Start Date'),
+        required = False,
+        readonly = False,
+        show_year = True,
+        )
+
+    emp_end = FormattedDate(
+        title = _(u'End Date'),
+        required = False,
+        readonly = False,
+        show_year = True,
+        )
+
+    emp_reason = schema.TextLine(
+        title = _(u'Reason for Leaving'),
+        required = False,
+        readonly = False,
+        )
+
+    employer2 = schema.TextLine(
+        title = _(u'2nd Employer'),
+        required = False,
+        readonly = False,
+        )
+
+    emp2_position = schema.TextLine(
+        title = _(u'2nd Employer Position'),
+        required = False,
+        readonly = False,
+        )
+
+    emp2_start = FormattedDate(
+        title = _(u'Start Date'),
+        required = False,
+        readonly = False,
+        show_year = True,
+        )
+    emp2_end = FormattedDate(
+        title = _(u'End Date'),
+        required = False,
+        readonly = False,
+        show_year = True,
+        )
+
+    emp2_reason = schema.TextLine(
+        title = _(u'Reason for Leaving'),
+        required = False,
+        readonly = False,
+        )
+
+    former_matric = schema.TextLine(
+        title = _(u'If yes, matric number'),
+        required = False,
+        readonly = False,
+        )
+
 class INigeriaStudentPersonal(IStudentPersonal):
     """Student personal data.
@@ -64,4 +376,23 @@
 
     is_foreigner = Attribute('True if student is non-Nigerian')
+
+    next_kin_name = schema.TextLine(
+        title = _(u'Next of Kin Name'),
+        required = False,
+        readonly = False,
+        )
+
+    next_kin_relation = schema.TextLine(
+        title = _(u'Next of Kin Relationship'),
+        required = False,
+        readonly = False,
+        )
+
+    next_kin_address = schema.Text(
+        title = _(u'Next of Kin Address'),
+        required = False,
+        readonly = False,
+        description = _(u'Please provide email address and/or phone number.'),
+        )
 
     marit_stat = schema.Choice(
@@ -85,23 +416,4 @@
         )
 
-    next_kin_name = schema.TextLine(
-        title = _(u'Next of Kin Name'),
-        required = False,
-        readonly = False,
-        )
-
-    next_kin_relation = schema.TextLine(
-        title = _(u'Next of Kin Relationship'),
-        required = False,
-        readonly = False,
-        )
-
-    next_kin_address = schema.Text(
-        title = _(u'Next of Kin Address'),
-        required = False,
-        readonly = False,
-        description = _(u'Please provide email address and/or phone number.'),
-        )
-
     disabled = schema.Bool(
         title = u'Disabled',
@@ -110,315 +422,4 @@
         )
 
-class INigeriaUGStudentClearance(IUGStudentClearance):
-    """Representation of ug student clearance data.
-
-    """
-    officer_comment = schema.Text(
-        title = _(u"Officer's Comment"),
-        required = False,
-        )
-
-    clearance_locked = schema.Bool(
-        title = _(u'Clearance form locked'),
-        default = False,
-        required = False,
-        )
-
-    clr_code = schema.TextLine(
-        title = _(u'CLR Activation Code'),
-        required = False,
-        readonly = False,
-        )
-
-    date_of_birth = FormattedDate(
-        title = _(u'Date of Birth'),
-        required = False,
-        show_year = True,
-        )
-
-    nationality = schema.Choice(
-        source = nats_vocab,
-        title = _(u'Nationality'),
-        required = True,
-        )
-
-    lga = schema.Choice(
-        source = LGASource(),
-        title = _(u'State/LGA (Nigerians only)'),
-        required = False,
-        )
-
-    def_adm = schema.Bool(
-        title = _(u'Deferent of Admission'),
-        required = False,
-        readonly = False,
-        )
-
-    fst_sit_fname = schema.TextLine(
-        title = _(u'Full Name'),
-        required = False,
-        readonly = False,
-        )
-    fst_sit_no = schema.TextLine(
-        title = _(u'Exam Number'),
-        required = False,
-        readonly = False,
-        )
-
-    fst_sit_date = FormattedDate(
-        title = _(u'Exam Date'),
-        required = False,
-        readonly = False,
-        show_year = True,
-        )
-
-    fst_sit_type = schema.Choice(
-        title = _(u'Exam Type'),
-        required = False,
-        readonly = False,
-        vocabulary = exam_types,
-        )
-
-    fst_sit_results = schema.List(
-        title = _(u'Exam Results'),
-        value_type = ResultEntryField(),
-        required = False,
-        readonly = False,
-        default = [],
-        )
-
-    scd_sit_fname = schema.TextLine(
-        title = _(u'Full Name'),
-        required = False,
-        readonly = False,
-        )
-    scd_sit_no = schema.TextLine(
-        title = _(u'Exam Number'),
-        required = False,
-        readonly = False,
-        )
-
-    scd_sit_date = FormattedDate(
-        title = _(u'Exam Date'),
-        required = False,
-        readonly = False,
-        show_year = True,
-        )
-
-    scd_sit_type = schema.Choice(
-        title = _(u'Exam Type'),
-        required = False,
-        readonly = False,
-        vocabulary = exam_types,
-        )
-
-    scd_sit_results = schema.List(
-        title = _(u'Exam Results'),
-        value_type = ResultEntryField(),
-        required = False,
-        readonly = False,
-        default = [],
-        )
-
-    alr_fname = schema.TextLine(
-        title = _(u'Full Name'),
-        required = False,
-        readonly = False,
-        )
-    alr_no = schema.TextLine(
-        title = _(u'Exam Number'),
-        required = False,
-        readonly = False,
-        )
-
-    alr_date = FormattedDate(
-        title = _(u'Exam Date'),
-        required = False,
-        readonly = False,
-        show_year = True,
-        )
-
-    alr_results = schema.List(
-        title = _(u'Exam Results'),
-        value_type = ResultEntryField(),
-        required = False,
-        readonly = False,
-        default = [],
-        )
-
-    hq_type = schema.Choice(
-        title = _(u'Qualification Obtained'),
-        required = False,
-        readonly = False,
-        vocabulary = high_qual,
-        )
-
-    hq_fname = schema.TextLine(
-        title = _(u'Full Name'),
-        required = False,
-        readonly = False,
-        )
-
-    hq_matric_no = schema.TextLine(
-        title = _(u'Former Matric Number'),
-        required = False,
-        readonly = False,
-        )
-
-    hq_degree = schema.Choice(
-        title = _(u'Class of Degree'),
-        required = False,
-        readonly = False,
-        vocabulary = high_grade,
-        )
-
-    hq_school = schema.TextLine(
-        title = _(u'Institution Attended'),
-        required = False,
-        readonly = False,
-        )
-
-    hq_session = schema.TextLine(
-        title = _(u'Years Attended'),
-        required = False,
-        readonly = False,
-        )
-
-    hq_disc = schema.TextLine(
-        title = _(u'Discipline'),
-        required = False,
-        readonly = False,
-        )
-
-
-class INigeriaPGStudentClearance(INigeriaUGStudentClearance):
-    """Representation of pg student clearance data.
-
-    """
-
-    hq2_type = schema.Choice(
-        title = _(u'Qualification Obtained'),
-        required = False,
-        readonly = False,
-        vocabulary = high_qual,
-        )
-
-    hq2_matric_no = schema.TextLine(
-        title = _(u'Former Matric Number'),
-        required = False,
-        readonly = False,
-        )
-
-    hq2_degree = schema.Choice(
-        title = _(u'Class of Degree'),
-        required = False,
-        readonly = False,
-        vocabulary = high_grade,
-        )
-
-    hq2_school = schema.TextLine(
-        title = _(u'Institution Attended'),
-        required = False,
-        readonly = False,
-        )
-
-    hq2_session = schema.TextLine(
-        title = _(u'Years Attended'),
-        required = False,
-        readonly = False,
-        )
-
-    hq2_disc = schema.TextLine(
-        title = _(u'Discipline'),
-        required = False,
-        readonly = False,
-        )
-
-    nysc_year = schema.Int(
-        title = _(u'Nysc Year'),
-        required = False,
-        readonly = False,
-        )
-
-    nysc_location = schema.TextLine(
-        title = _(u'Nysc Location'),
-        required = False,
-        )
-
-    nysc_lga = schema.Choice(
-        source = LGASource(),
-        title = _(u'Nysc LGA'),
-        required = False,
-        )
-
-    employer = schema.TextLine(
-        title = _(u'Employer'),
-        required = False,
-        readonly = False,
-        )
-
-    emp_position = schema.TextLine(
-        title = _(u'Employer Position'),
-        required = False,
-        readonly = False,
-        )
-
-    emp_start = FormattedDate(
-        title = _(u'Start Date'),
-        required = False,
-        readonly = False,
-        show_year = True,
-        )
-
-    emp_end = FormattedDate(
-        title = _(u'End Date'),
-        required = False,
-        readonly = False,
-        show_year = True,
-        )
-
-    emp_reason = schema.TextLine(
-        title = _(u'Reason for Leaving'),
-        required = False,
-        readonly = False,
-        )
-
-    employer2 = schema.TextLine(
-        title = _(u'2nd Employer'),
-        required = False,
-        readonly = False,
-        )
-
-    emp2_position = schema.TextLine(
-        title = _(u'2nd Employer Position'),
-        required = False,
-        readonly = False,
-        )
-
-    emp2_start = FormattedDate(
-        title = _(u'Start Date'),
-        required = False,
-        readonly = False,
-        show_year = True,
-        )
-    emp2_end = FormattedDate(
-        title = _(u'End Date'),
-        required = False,
-        readonly = False,
-        show_year = True,
-        )
-
-    emp2_reason = schema.TextLine(
-        title = _(u'Reason for Leaving'),
-        required = False,
-        readonly = False,
-        )
-
-    former_matric = schema.TextLine(
-        title = _(u'If yes, matric number'),
-        required = False,
-        readonly = False,
-        )
-
 
 class INigeriaStudent(INigeriaStudentBase,INigeriaUGStudentClearance,
@@ -428,4 +429,44 @@
     """
 
+class INigeriaStudentPersonalEdit(INigeriaStudentPersonal):
+    """Interface for editing personal data by students.
+
+    Here we can repeat the fields from IStudentPersonal and set the
+    `required` if necessary.
+    """
+
+    perm_address = schema.Text(
+        title = _(u'Permanent Address'),
+        required = True,
+        )
+
+    next_kin_name = schema.TextLine(
+        title = _(u'Next of Kin Name'),
+        required = True,
+        readonly = False,
+        )
+
+    next_kin_relation = schema.TextLine(
+        title = _(u'Next of Kin Relationship'),
+        required = True,
+        readonly = False,
+        )
+
+    next_kin_address = schema.Text(
+        title = _(u'Next of Kin Address'),
+        required = True,
+        readonly = False,
+        description = _(u'Please provide email address and/or phone number.'),
+        )
+
+INigeriaStudentPersonalEdit['next_kin_name'].order = INigeriaStudentPersonal[
+    'next_kin_name'].order
+INigeriaStudentPersonalEdit['next_kin_relation'].order = INigeriaStudentPersonal[
+    'next_kin_relation'].order
+INigeriaStudentPersonalEdit['next_kin_address'].order = INigeriaStudentPersonal[
+    'next_kin_address'].order
+INigeriaStudentPersonalEdit['perm_address'].order = INigeriaStudentPersonal[
+    'perm_address'].order
+
 class INigeriaStudentStudyCourse(IStudentStudyCourse):
     """A container for student study levels.
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/student.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/student.py	(revision 9562)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/student.py	(revision 9564)
@@ -23,5 +23,6 @@
 from waeup.kofa.utils.helpers import attrs_to_fields
 from waeup.kofa.students.student import Student, StudentFactory
-from kofacustom.nigeria.students.interfaces import INigeriaStudent, IStudentNavigation
+from kofacustom.nigeria.students.interfaces import (
+    INigeriaStudent, IStudentNavigation, INigeriaStudentPersonalEdit)
 
 
@@ -30,5 +31,6 @@
     owned by students.
     """
-    grok.implements(INigeriaStudent, IStudentNavigation)
+    grok.implements(
+        INigeriaStudent, INigeriaStudentPersonalEdit, IStudentNavigation)
     grok.provides(INigeriaStudent)
 
Index: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/tests/test_browser.py
===================================================================
--- main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/tests/test_browser.py	(revision 9562)
+++ main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/tests/test_browser.py	(revision 9564)
@@ -186,4 +186,9 @@
         self.assertEqual(self.browser.headers['Status'], '200 Ok')
         self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
+        # Managers can edit personal data. No fields are required.
+        self.browser.open(self.manage_personal_path)
+        self.browser.getControl("Save").click()
+        self.assertMatches('...Form has been saved...',
+                           self.browser.contents)
 
     def test_student_access(self):
@@ -226,4 +231,25 @@
         self.browser.getControl("Save and request clearance").click()
         self.assertMatches('...Clearance has been requested...',
+                           self.browser.contents)
+
+        # Students can edit personal data. Some fields are required.
+        self.browser.open(self.personal_path)
+        self.assertTrue('Updated' in self.browser.contents)
+        self.browser.getLink("Edit").click()
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.url, self.edit_personal_path)
+        self.browser.getControl("Save").click()
+        self.assertMatches('...Required input is missing...',
+                           self.browser.contents)
+        self.browser.getControl(name="form.perm_address").value = 'My address!'
+        self.browser.getControl("Save").click()
+        self.assertMatches('...Required input is missing...',
+                           self.browser.contents)
+        # Ok, let's give up and fill the rest.
+        self.browser.getControl(name="form.next_kin_name").value = 'My Mutti'
+        self.browser.getControl(name="form.next_kin_relation").value = 'mother'
+        self.browser.getControl(name="form.next_kin_address").value = 'sweet home'
+        self.browser.getControl("Save").click()
+        self.assertMatches('...Form has been saved...',
                            self.browser.contents)
 
