Index: main/waeup.aaue/trunk/src/waeup/aaue/applicants/applicant.py
===================================================================
--- main/waeup.aaue/trunk/src/waeup/aaue/applicants/applicant.py	(revision 15524)
+++ main/waeup.aaue/trunk/src/waeup/aaue/applicants/applicant.py	(revision 15530)
@@ -34,4 +34,5 @@
     file_store = getUtility(IExtFileStore)
     file_store.deleteFileByContext(applicant, attr='stateresult.pdf')
+    file_store.deleteFileByContext(applicant, attr='verificationdoc.pdf')
     return
 
Index: main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser.py
===================================================================
--- main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser.py	(revision 15524)
+++ main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser.py	(revision 15530)
@@ -51,5 +51,6 @@
     ITranscriptApplicant,
     ICertificateRequest,
-    ICustomApplicant
+    ICustomApplicant,
+    IVerificationRequest
     )
 
@@ -260,4 +261,8 @@
         if pdf:
             html += '<a href="stateresult.pdf">Statement of Result</a>'
+        pdf = getUtility(IExtFileStore).getFileByContext(
+            self.context, attr='verificationdoc.pdf')
+        if pdf:
+            html += '<a href="verificationdoc.pdf">Result/Certificate Document</a>'
         return html
 
@@ -269,4 +274,9 @@
             form_fields['dispatch_address'].custom_widget = BytesDisplayWidget
             form_fields['perm_address'].custom_widget = BytesDisplayWidget
+            return form_fields
+        if self.target is not None and self.target == 'ver':
+            form_fields = grok.AutoFields(IVerificationRequest).omit(
+                'locked', 'suspended')
+            form_fields['body_address'].custom_widget = BytesDisplayWidget
             return form_fields
         if self.target is not None and self.target == 'cert':
@@ -318,8 +328,8 @@
         return ''
 
-    def update(self):
-        super(CustomApplicantDisplayFormPage, self).update()
-        self.extraform_url = self.url(self.context, 'stateresult.pdf')
-        return
+    #def update(self):
+    #    super(CustomApplicantDisplayFormPage, self).update()
+    #    #self.extraform_url = self.url(self.context, 'stateresult.pdf')
+    #    return
 
 class CustomPDFActionButton(PDFActionButton):
@@ -407,4 +417,8 @@
             form_fields['applicant_id'].for_display = True
             return form_fields
+        if self.target is not None and self.target == 'ver':
+            form_fields = grok.AutoFields(IVerificationRequest)
+            form_fields['applicant_id'].for_display = True
+            return form_fields
         # AAUE is using the same interface for all regular applications.
         form_fields = grok.AutoFields(ICustomUGApplicant)
@@ -428,4 +442,5 @@
         super(CustomApplicantManageFormPage, self).update()
         upload_stateresult = self.request.form.get('form.stateresult', None)
+        upload_verificationdoc = self.request.form.get('form.verificationdoc', None)
         if upload_stateresult:
             # We got a fresh stateresult upload
@@ -434,4 +449,12 @@
             if success:
                 self.context.writeLogMessage(self, 'saved: stateresult')
+            else:
+                self.upload_success = False
+        if upload_verificationdoc:
+            # We got a fresh verificationdoc upload
+            success = handle_file_upload(
+                upload_verificationdoc, self.context, self, attr='verificationdoc.pdf')
+            if success:
+                self.context.writeLogMessage(self, 'saved: verificationdoc')
             else:
                 self.upload_success = False
@@ -518,4 +541,9 @@
                 for field in ('firstname', 'middlename', 'lastname'):
                     form_fields[field].for_display = True
+            return form_fields
+        if self.target is not None and self.target == 'ver':
+            form_fields = grok.AutoFields(IVerificationRequest).omit(
+                'locked', 'suspended')
+            form_fields['applicant_id'].for_display = True
             return form_fields
         # AAUE is using the same interface for all regular applications.
@@ -563,8 +591,15 @@
         super(CustomApplicantEditFormPage, self).update()
         upload_stateresult = self.request.form.get('form.stateresult', None)
+        upload_verificationdoc = self.request.form.get('form.verificationdoc', None)
         if upload_stateresult:
             # We got a fresh stateresult upload
             success = handle_file_upload(
                 upload_stateresult, self.context, self, attr='stateresult.pdf')
+            if not success:
+                self.upload_success = False
+        if upload_verificationdoc:
+            # We got a fresh verificationdoc upload
+            success = handle_file_upload(
+                upload_verificationdoc, self.context, self, attr='verificationdoc.pdf')
             if not success:
                 self.upload_success = False
@@ -711,2 +746,15 @@
         self.response.setHeader('Content-Type', 'application/pdf')
         return pdf
+
+class VerificationDoc(grok.View):
+    """Renders the pdf form extension for applicants.
+    """
+    grok.name('verificationdoc.pdf')
+    grok.context(ICustomApplicant)
+    grok.require('waeup.viewApplication')
+
+    def render(self):
+        pdf = getUtility(IExtFileStore).getFileByContext(
+            self.context, attr='verificationdoc.pdf')
+        self.response.setHeader('Content-Type', 'application/pdf')
+        return pdf
Index: main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser_templates/applicantdisplaypage.pt
===================================================================
--- main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser_templates/applicantdisplaypage.pt	(revision 15524)
+++ main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser_templates/applicantdisplaypage.pt	(revision 15530)
@@ -37,5 +37,5 @@
       </td>
     <tr>
-    <tr tal:condition="python: view.target in ('trans', 'cert')">
+    <tr tal:condition="python: view.target in ('trans', 'cert', 'ver')">
       <td class="fieldname" i18n:translate="">
         Files:
Index: main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser_templates/applicanteditpage.pt
===================================================================
--- main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser_templates/applicanteditpage.pt	(revision 15524)
+++ main/waeup.aaue/trunk/src/waeup/aaue/applicants/browser_templates/applicanteditpage.pt	(revision 15530)
@@ -22,5 +22,5 @@
     <tbody>
       <tal:widgets content="structure provider:widgets" />
-      <tr>
+      <tr tal:condition="python: context.__parent__.with_picture">
         <td class="fieldname" i18n:translate="">
           Passport Photo:
@@ -68,4 +68,29 @@
           <span tal:replace="view/max_file_upload_size">10 KB</span>
       </tr>
+      <tr tal:condition="python: view.target in ('ver',)">
+        <td class="fieldname" i18n:translate="">
+          Scanned Document:
+        </td>
+        <td>
+          <p tal:condition="python: view.file_exists('verificationdoc.pdf')">
+            <a href="verificationdoc.pdf"
+               i18n:translate="">
+              Download pdf file
+            </a>
+          </p>
+          <div class="input-group half">
+            <div class="input-group-btn">
+              <div class="btn btn-default btn-file">
+                Select&hellip;
+              <input type="file" name="form.verificationdoc" />
+              </div>
+            </div>
+            <input type="text" class="form-control" readonly>
+          </div>
+          <span i18n:translate="">
+            PDF files only. Max. file size:
+          </span>
+          <span tal:replace="view/max_file_upload_size">10 KB</span>
+      </tr>
       <tr tal:condition="view/manage_applications">
         <td class="fieldname" i18n:translate="">Password:</td>
Index: main/waeup.aaue/trunk/src/waeup/aaue/applicants/interfaces.py
===================================================================
--- main/waeup.aaue/trunk/src/waeup/aaue/applicants/interfaces.py	(revision 15524)
+++ main/waeup.aaue/trunk/src/waeup/aaue/applicants/interfaces.py	(revision 15530)
@@ -60,4 +60,11 @@
     )
 
+document_types_vocab = SimpleKofaVocabulary(
+    (_('Certificate'), 'certificate'),
+    (_('Result'), 'result'),
+    (_('Transcript'), 'transcript'),
+    (_('Studentship'), 'studship'),
+    )
+
 class ICustomUGApplicant(IApplicantBaseData):
     """An undergraduate applicant.
@@ -793,6 +800,75 @@
 
 
+class IVerificationRequest(IKofaObject):
+    """A applicant asking for verification.
+    """
+
+    suspended = schema.Bool(
+        title = _(u'Account suspended'),
+        default = False,
+        required = False,
+        )
+
+    locked = schema.Bool(
+        title = _(u'Form locked'),
+        default = False,
+        required = False,
+        )
+
+    applicant_id = schema.TextLine(
+        title = _(u'Applicant Id'),
+        required = False,
+        readonly = False,
+        )
+
+    #reg_number = TextLineChoice(
+    #    title = _(u'Kofa Registration Number'),
+    #    readonly = False,
+    #    required = True,
+    #    source = contextual_reg_num_source,
+    #    )
+
+    firstname = schema.TextLine(
+        title = _(u'First Name'),
+        required = True,
+        )
+
+    middlename = schema.TextLine(
+        title = _(u'Middle Name'),
+        required = False,
+        )
+
+    lastname = schema.TextLine(
+        title = _(u'Last Name (Surname)'),
+        required = True,
+        )
+
+    email = schema.ASCIILine(
+        title = _(u'Email Address'),
+        required = True,
+        constraint=validate_email,
+        )
+
+    matric_number = schema.TextLine(
+        title = _(u'Verification Body Reference Number'),
+        readonly = False,
+        required = True,
+        )
+
+    body_address = schema.Text(
+        title = _(u'Verification Body Address'),
+        required = True,
+        )
+
+    document_type = schema.Choice(
+        title = _(u'Document Type'),
+        vocabulary = document_types_vocab,
+        required = True,
+        )
+
+
 class ICustomApplicant(ICustomUGApplicant, ICustomPGApplicant,
-                       ITranscriptApplicant, ICertificateRequest):
+                       ITranscriptApplicant, ICertificateRequest,
+                       IVerificationRequest):
     """An interface for all types of applicants.
 
Index: main/waeup.aaue/trunk/src/waeup/aaue/applicants/tests/test_browser.py
===================================================================
--- main/waeup.aaue/trunk/src/waeup/aaue/applicants/tests/test_browser.py	(revision 15524)
+++ main/waeup.aaue/trunk/src/waeup/aaue/applicants/tests/test_browser.py	(revision 15530)
@@ -381,2 +381,74 @@
         fd = storage.getFile(file_id)
         self.assertTrue(fd is None)
+
+    def test_upload_verificationdoc_by_manager(self):
+        # Add ver applicants container
+        self.vercontainer = ApplicantsContainer()
+        self.vercontainer.mode = 'create'
+        self.vercontainer.code = u'ver%s' % session_1
+        self.vercontainer.prefix = u'ver'
+        self.vercontainer.application_category = u'no'
+        self.vercontainer.year = session_1
+        self.vercontainer.application_fee = 300.0
+        self.vercontainer.title = u'This is the ver%s container' % session_1
+        self.app['applicants'][self.vercontainer.code] = self.vercontainer
+        delta = datetime.timedelta(days=10)
+        self.vercontainer.startdate = datetime.datetime.now(pytz.utc) - delta
+        self.vercontainer.enddate = datetime.datetime.now(pytz.utc) + delta
+        # Add applicant
+        verapplicant = createObject(u'waeup.Applicant')
+        verapplicant.firstname = u'Anna'
+        verapplicant.lastname = u'Post'
+        self.app['applicants'][self.vercontainer.code].addApplicant(verapplicant)
+        self.verapplicant = self.app['applicants'][self.vercontainer.code][
+            verapplicant.application_number]
+        self.verapplicant_view_path = ('http://localhost/app/applicants/ver%s/%s'
+            % (session_1, verapplicant.application_number))
+        self.verapplicant_manage_path = ('http://localhost/app/applicants/ver%s/%s/manage'
+            % (session_1, verapplicant.application_number))
+        # Login
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open(self.verapplicant_manage_path)
+        # Create a pseudo file with acceptable size
+        pdf_content = 'A' * 1024 * 300  # A string of 300 KB size
+        pseudo_pdf = StringIO(pdf_content)
+        ctrl = self.browser.getControl(name='form.verificationdoc')
+        file_ctrl = ctrl.mech_control
+        file_ctrl.add_file(pseudo_pdf, filename='myform.pdf')
+        self.browser.getControl("Save").click() # submit form
+        # Even though the form could not be saved ...
+        self.assertTrue('Required input is missing' in self.browser.contents)
+        # ... the file has been successfully uploaded
+        pdf_url = self.verapplicant_manage_path.replace('manage', 'verificationdoc.pdf')
+        self.browser.open(pdf_url)
+        self.assertEqual(
+            self.browser.headers['content-type'], 'application/pdf')
+        self.assertEqual(len(self.browser.contents), 307200)
+        # There is really a file stored for the applicant
+        storage = getUtility(IExtFileStore)
+        file_id = IFileStoreNameChooser(self.verapplicant).chooseName(
+            attr='verificationdoc.pdf')
+        # The stored file can be fetched
+        fd = storage.getFile(file_id)
+        file_len = len(fd.read())
+        self.assertEqual(file_len, 307200)
+        # A file link is displayed on the edit view ...
+        self.browser.open(self.verapplicant_manage_path)
+        self.assertTrue('<a href="verificationdoc.pdf">' in self.browser.contents)
+        # ... and on the dislay view
+        self.browser.open(self.verapplicant_view_path)
+        self.assertTrue('<a href="verificationdoc.pdf">Result/Certificate Document</a>'
+            in self.browser.contents)
+        # Adding file is properly logged
+        logfile = os.path.join(
+            self.app['datacenter'].storage, 'logs', 'applicants.log')
+        logcontent = open(logfile).read()
+        self.assertTrue(
+            'zope.mgr - waeup.aaue.applicants.browser.CustomApplicantManageFormPage'
+            ' - %s - saved: verificationdoc'
+            % (self.verapplicant.applicant_id)
+            in logcontent)
+        # When an applicant is removed, also the pdf files are gone.
+        del self.app['applicants'][self.vercontainer.code][self.verapplicant.application_number]
+        fd = storage.getFile(file_id)
+        self.assertTrue(fd is None)
Index: main/waeup.aaue/trunk/src/waeup/aaue/applicants/utils.py
===================================================================
--- main/waeup.aaue/trunk/src/waeup/aaue/applicants/utils.py	(revision 15524)
+++ main/waeup.aaue/trunk/src/waeup/aaue/applicants/utils.py	(revision 15530)
@@ -42,4 +42,5 @@
         'bridge': ['Bridge Programme', 'BRI'],
         'dsh': ['Science and Humanities Programme', 'DSH'],
+        'ver': ['Verification Request', 'VR'],
         }
 
