Index: main/waeup.kofa/trunk/CHANGES.txt
===================================================================
--- main/waeup.kofa/trunk/CHANGES.txt	(revision 16058)
+++ main/waeup.kofa/trunk/CHANGES.txt	(revision 16059)
@@ -4,4 +4,6 @@
 1.6.1.dev0 (unreleased)
 =======================
+
+* Improve referee reports.
 
 * Use consistent flow value formatting for CGPA.
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser.py	(revision 16058)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser.py	(revision 16059)
@@ -594,4 +594,10 @@
 
     @property
+    def display_refereereports(self):
+        if self.context.refereereports:
+            return True
+        return False
+
+    @property
     def file_links(self):
         html = ''
@@ -1190,4 +1196,5 @@
     manage_applications = False
     submit_state = PAID
+    mandate_days = 7
 
     @property
@@ -1289,5 +1296,5 @@
             if referee.email_sent:
                 continue
-            mandate = RefereeReportMandate(days=7)
+            mandate = RefereeReportMandate(days=self.mandate_days)
             mandate.params['name'] = referee.name
             mandate.params['email'] = referee.email
@@ -1296,4 +1303,6 @@
                     self.context.__parent__.code,
                     self.context.application_number)
+            mandate.params['redirect_path2'] = ''
+            mandate.params['applicant_id'] = self.context.applicant_id
             site['mandates'].addMandate(mandate)
             # Send invitation email
@@ -1358,10 +1367,42 @@
         # for file storage.
         image = getUtility(IExtFileStore).getFileByContext(self.context)
-        self.response.setHeader(
-            'Content-Type', 'image/jpeg')
+        self.response.setHeader('Content-Type', 'image/jpeg')
         if image is None:
             # show placeholder image
             return open(DEFAULT_PASSPORT_IMAGE_PATH, 'rb').read()
         return image
+
+class PassportImageForReport(PassportImage):
+    """Renders the passport image for applicants for referee reports.
+    """
+    grok.name('passport_for_report.jpg')
+    grok.context(IApplicant)
+    grok.require('waeup.Public')
+
+    def render(self):
+        # Check mandate
+        form = self.request.form
+        self.mandate_id = form.get('mandate_id', None)
+        self.mandates = grok.getSite()['mandates']
+        mandate = self.mandates.get(self.mandate_id, None)
+        if mandate is None:
+            self.flash(_('No mandate.'), type='warning')
+            self.redirect(self.application_url())
+            return
+        if mandate:
+            # Check the mandate expiration date after redirect again
+            if mandate.expires < datetime.utcnow():
+                self.flash(_('Mandate expired.'),
+                           type='warning')
+                self.redirect(self.application_url())
+                return
+            # Check if mandate allows access
+            if mandate.params.get('applicant_id') != self.context.applicant_id:
+                self.flash(_('Wrong mandate.'),
+                           type='warning')
+                self.redirect(self.application_url())
+                return
+            return super(PassportImageForReport, self).render()
+        return
 
 class ApplicantRegistrationPage(KofaAddFormPage):
@@ -1686,4 +1727,23 @@
         'creation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le')
 
+class RemoveRefereeReportPage(UtilityView, grok.View):
+    """
+    """
+    grok.context(IApplicantRefereeReport)
+    grok.name('remove')
+    grok.require('waeup.manageApplication')
+
+    def update(self):
+        redirect_url = self.url(self.context.__parent__)
+        self.context.__parent__.writeLogMessage(
+            self, 'removed: %s' % self.context.r_id)
+        del self.context.__parent__[self.context.r_id]
+        self.flash(_('Referee report removed.'))
+        self.redirect(redirect_url)
+        return
+
+    def render(self):
+        return
+
 class RefereeReportAddFormPage(KofaAddFormPage):
     """Add-form to add an referee report. This form
@@ -1696,5 +1756,5 @@
         IApplicantRefereeReport).omit('creation_date')
     grok.template('refereereportpage')
-    label = _('Add referee report')
+    label = _('Referee Report Form')
     pnav = 3
     #doclink = DOCLINK + '/refereereports.html'
@@ -1717,5 +1777,4 @@
             self.redirect(self.application_url())
             return
-        self.passport_url = self.url(self.context, 'passport.jpg')
         if mandate:
             # Check the mandate expiration date after redirect again
@@ -1725,4 +1784,13 @@
                 self.redirect(self.application_url())
                 return
+            args = {'mandate_id':mandate.mandate_id}
+            # Check if report exists.
+            # If so, redirect to the pdf file.
+            if mandate.params.get('redirect_path2'):
+                self.redirect(
+                    self.application_url() +
+                    mandate.params.get('redirect_path2') +
+                    '?%s' % urlencode(args))
+                return
             # Prefill form with mandate params
             self.form_fields.get(
@@ -1730,4 +1798,6 @@
             self.form_fields.get(
                 'email').field.default = mandate.params['email']
+            self.passport_url = self.url(
+                self.context, 'passport_for_report.jpg') + '?%s' % urlencode(args)
         super(RefereeReportAddFormPage, self).update()
         return
@@ -1744,9 +1814,17 @@
         self.applyData(report, **data)
         self.context[report.r_id] = report
-        self.flash(_('Referee report has been saved. Thank you!'))
+        # self.flash(_('Referee report has been saved. Thank you!'))
         self.context.writeLogMessage(self, 'added: %s' % report.r_id)
-        # Delete mandate
-        del self.mandates[self.mandate_id]
-        self.redirect(self.application_url())
+        # Changed on 19/04/20: We do no longer delete the mandate
+        # but set path to redirect to the pdf file
+        self.mandates[self.mandate_id].params[
+            'redirect_path2'] = '/applicants/%s/%s/%s/referee_report.pdf' % (
+                self.context.__parent__.code,
+                self.context.application_number,
+                report.r_id)
+        notify(grok.ObjectModifiedEvent(self.mandates[self.mandate_id]))
+        args = {'mandate_id':self.mandate_id}
+        self.redirect(self.url(report, 'referee_report.pdf')
+                      + '?%s' % urlencode(args))
         return
 
@@ -1783,4 +1861,38 @@
             self.context.__parent__, applicantview, note=self.note)
 
+class ExportPDFReportSlipPage2(ExportPDFReportSlipPage):
+    """Deliver a PDF slip of the context to referees.
+    """
+    grok.name('referee_report.pdf')
+    grok.require('waeup.Public')
+
+    def update(self):
+        # Check mandate
+        form = self.request.form
+        self.mandate_id = form.get('mandate_id', None)
+        self.mandates = grok.getSite()['mandates']
+        mandate = self.mandates.get(self.mandate_id, None)
+        if mandate is None:
+            self.flash(_('No mandate.'), type='warning')
+            self.redirect(self.application_url())
+            return
+        if mandate:
+            # Check the mandate expiration date after redirect again
+            if mandate.expires < datetime.utcnow():
+                self.flash(_('Mandate expired.'),
+                           type='warning')
+                self.redirect(self.application_url())
+                return
+            # Check if form has really been submitted
+            if not mandate.params.get('redirect_path2') \
+                or mandate.params.get(
+                    'applicant_id') != self.context.__parent__.applicant_id:
+                self.flash(_('Wrong mandate.'),
+                           type='warning')
+                self.redirect(self.application_url())
+                return
+            super(ExportPDFReportSlipPage2, self).update()
+        return
+
 class AdditionalFile(grok.View):
     """Renders additional pdf files for applicants.
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser_templates/applicantdisplaypage.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser_templates/applicantdisplaypage.pt	(revision 16058)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser_templates/applicantdisplaypage.pt	(revision 16059)
@@ -51,4 +51,31 @@
   </tbody>
 </table>
+
+<tal:refereereports condition="view/display_refereereports">
+  <br /><br />
+  <h3 i18n:translate="">
+      Referee Reports
+  </h3>
+  <table i18n:domain="waeup.kofa" class="table table-condensed">
+    <thead>
+    <tr>
+      <th i18n:translate="">Report Id</th>
+      <th i18n:translate="">Creation Date</th>
+      <th i18n:translate="">Referee</th>
+      <th i18n:translate="">Email Address</th>
+    </tr>
+    </thead>
+    <tbody>
+      <tr tal:repeat="cl context/refereereports">
+        <td> <a tal:attributes="href python:view.url(cl)">
+          <span tal:content="cl/r_id">RID</span></a></td>
+        <td tal:content="python: layout.formatDatetime(cl.creation_date)">CREATION DATE</td>
+        <td tal:content="cl/name">REFEREE</td>
+        <td tal:content="cl/email">EMAIL</td>
+      </tr>
+    </tbody>
+  </table>
+</tal:refereereports>
+
 <tal:payments condition="view/display_payments">
   <h3 i18n:domain="waeup.kofa" i18n:translate="">
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser_templates/refereereportpage.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser_templates/refereereportpage.pt	(revision 16058)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/browser_templates/refereereportpage.pt	(revision 16059)
@@ -1,2 +1,4 @@
+
+
 <p i18n:domain="waeup.kofa" i18n:translate="referee_welcome">
   You have been invited to report about
@@ -14,15 +16,15 @@
   </strong>
   of the <span i18n:name="app_title" tal:replace="layout/getAppTitle"></span>.
-  
-  <br /><br />
+</p>
 
-  <img tal:condition="python: context.__parent__.with_picture"
-       src="" height="180px" tal:attributes="src view/passport_url" />
+<img tal:condition="python: context.__parent__.with_picture"
+src="" height="180px" tal:attributes="src view/passport_url" />
 
-  <br /><br />
+<br /><br />
 
-  Please fill and submit the following referee report form. After submission
-  the form will be locked. You can neither view nor edit
-  your report after submission.
+<p i18n:translate="">
+Please fill and submit the following referee report form.
+After submission the form will be locked and a report pdf
+file will be available for download.
 </p>
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_browser.py	(revision 16058)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_browser.py	(revision 16059)
@@ -1661,4 +1661,5 @@
         mandate.params['name'] = u'John Referee'
         mandate.params['email'] = 'aa@aa.aa'
+        mandate.params['applicant_id'] = self.applicant.applicant_id
         mandate.params[
             'redirect_path'] = '/applicants/%s/%s/addrefereereport' % (
@@ -1692,4 +1693,5 @@
             in self.browser.contents)
         self.assertEqual(self.browser.url, 'http://localhost/app')
+        return
 
     def test_add_and_view_reports(self):
@@ -1697,7 +1699,8 @@
         mandate.params['name'] = u'John Referee'
         mandate.params['email'] = 'aa@aa.aa'
-        mandate.params[
-            'redirect_path'] = '/applicants/%s/%s/addrefereereport' % (
+        mandate.params['applicant_id'] = self.applicant.applicant_id
+        mandate.params['redirect_path'] = '/applicants/%s/%s/addrefereereport' % (
                 container_name_1, self.applicant.application_number)
+        mandate.params['redirect_path2'] = ''
         self.app['mandates'].addMandate(mandate)
         self.assertEqual(len(self.app['mandates'].keys()), 1)
@@ -1712,9 +1715,20 @@
         self.browser.getControl(name="form.email").value = 'bb@bb.bb'
         self.browser.getControl("Submit").click()
-        self.assertTrue('Referee report has been saved' in self.browser.contents)
-        self.assertEqual(self.browser.url, 'http://localhost/app')
+        # Referee wil be redirected to a pdf file
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.headers['Content-Type'],
+                         'application/pdf')
+        path = os.path.join(samples_dir(), 'referee_report.pdf')
+        open(path, 'wb').write(self.browser.contents)
+        print "Sample PDF referee_report.pdf written to %s" % path
         # Report has been created
         self.assertEqual(len(self.applicant.refereereports), 1)
         report = self.applicant.refereereports[0]
+        # Referee can use mandate again to download the pdf report
+        self.browser.open('http://localhost/app/mandate?mandate_id=%s'
+            % mandate.mandate_id)
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.headers['Content-Type'],
+                         'application/pdf')
         # Managers can view the report
         self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
@@ -1725,5 +1739,5 @@
         self.assertTrue('John Referee' in self.browser.contents)
         # Managers can download a pdf slip
-        self.browser.getLink("Download report").click()
+        self.browser.getLink("Download referee report").click()
         self.assertEqual(self.browser.headers['Status'], '200 Ok')
         self.assertEqual(self.browser.headers['Content-Type'],
@@ -1732,5 +1746,23 @@
         open(path, 'wb').write(self.browser.contents)
         print "Sample PDF referee_report_slip.pdf written to %s" % path
-        # Report creation is logged
+        # Mandate is not deleted ...
+        self.assertEqual(len(self.app['mandates'].keys()), 1)
+        # ... but redirect_path2 attribute has been set
+        redirect_path2 = '/applicants/%s/%s/%s/referee_report.pdf' % (
+                container_name_1,
+                self.applicant.application_number,
+                report.r_id)
+        self.assertEqual(
+            self.app['mandates'].values()[0].params['redirect_path2'],
+            redirect_path2)
+        # Managers can delete referee reports
+        self.browser.open(self.manage_path)
+        self.browser.getLink("%s" % report.r_id).click()
+        self.assertEqual(len(self.applicant.refereereports), 1)
+        self.browser.getLink("Delete").click()
+        self.assertEqual(len(self.applicant.refereereports), 0)
+        self.assertTrue('Referee report removed.' in self.browser.contents)
+        self.assertEqual(self.browser.url, self.view_path)
+        # Report creation and deletion is logged
         logfile = os.path.join(
             self.app['datacenter'].storage, 'logs', 'applicants.log')
@@ -1739,8 +1771,10 @@
             'zope.anybody - applicants.browser.RefereeReportAddFormPage - '
             '%s - added: %s\n' % (self.applicant.applicant_id, report.r_id)
-            in logcontent
-            )
-        # Mandate is deleted
-        self.assertEqual(len(self.app['mandates'].keys()), 0)
+            in logcontent)
+        self.assertTrue(
+            'zope.mgr - applicants.browser.RemoveRefereeReportPage - '
+            '%s - removed: %s\n' % (self.applicant.applicant_id, report.r_id)
+            in logcontent)
+        return
 
     def test_final_submit_with_referees(self):
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/utils.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/utils.py	(revision 16058)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/utils.py	(revision 16059)
@@ -47,4 +47,6 @@
       'form.course1': _(u'Desired Study Courses'),
       'form.notice': _(u'Application Process Data'),
+      'form.referees': _(u'Referees (automatically invited by email '
+                          'after final submission of this form)'),
       }
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/viewlets.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/viewlets.py	(revision 16058)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/viewlets.py	(revision 16059)
@@ -286,17 +286,4 @@
         return self.view.url(self.view.context, self.target)
 
-class ReportSlipActionButton(ManageActionButton):
-    grok.order(1) 
-    grok.context(IApplicantRefereeReport)
-    grok.view(RefereeReportDisplayFormPage)
-    grok.require('waeup.manageApplication')
-    icon = 'actionicon_pdf.png'
-    text = _('Download report slip')
-    target = 'referee_report_slip.pdf'
-
-    @property
-    def target_url(self):
-        return self.view.url(self.view.context, self.target)
-
 class ApprovePaymentActionButton(ManageActionButton):
     grok.order(8)
@@ -313,2 +300,31 @@
             return ''
         return self.view.url(self.view.context, self.target)
+
+class ReportSlipActionButton(ManageActionButton):
+    grok.order(1)
+    grok.context(IApplicantRefereeReport)
+    grok.view(RefereeReportDisplayFormPage)
+    grok.require('waeup.manageApplication')
+    icon = 'actionicon_pdf.png'
+    text = _('Download referee report slip')
+    target = 'referee_report_slip.pdf'
+
+    @property
+    def target_url(self):
+        return self.view.url(self.view.context, self.target)
+
+deletion_warning = _(
+    "'The report will be irrevocably deleted. Are you sure?'")
+
+class ReportRemoveActionButton(ManageActionButton):
+    grok.order(2)
+    grok.context(IApplicantRefereeReport)
+    grok.view(RefereeReportDisplayFormPage)
+    grok.require('waeup.manageApplication')
+    icon = 'actionicon_reject.png'
+    text = _('Delete referee report')
+    target = 'remove'
+
+    @property
+    def onclick(self):
+        return "return window.confirm(%s);" % deletion_warning
Index: main/waeup.kofa/trunk/src/waeup/kofa/interfaces.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/interfaces.py	(revision 16058)
+++ main/waeup.kofa/trunk/src/waeup/kofa/interfaces.py	(revision 16059)
@@ -296,6 +296,5 @@
         constraint=validate_email,
         description = _(
-            u'Email Address (referees will be automatically invited by email '
-             'after final submission of this form)'),
+            u'Email Address'),
         )
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/mandates/mandate.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/mandates/mandate.py	(revision 16058)
+++ main/waeup.kofa/trunk/src/waeup/kofa/mandates/mandate.py	(revision 16059)
@@ -112,6 +112,5 @@
 class RefereeReportMandate(Mandate):
     """This is a mandate which can unlock a `RefereeReportAddFormPage`.
-    The mandate is not automatically deleted. This has to be done
-    by the submit method of the add form page.
+    The mandate is not automatically deleted.
     """
 
