Index: main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py	(revision 9821)
+++ main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py	(revision 9822)
@@ -194,4 +194,38 @@
     return
 
+def doll_up(view, user=None):
+    """Doll up export jobs for displaying in table.
+    """
+    job_entries = view.context.get_running_export_jobs(user)
+    job_manager = getUtility(IJobManager)
+    entries = []
+    for job_id, exporter_name, user_id in job_entries:
+        job = job_manager.get(job_id)
+        exporter = getUtility(ICSVExporter, name=exporter_name)
+        exporter_title = getattr(exporter, 'title', 'Unknown')
+        args = ', '.join([str(x) for x in job.kwargs.values()])
+        descr = '%s <br> (%s)' % (exporter_title, args)
+        status = job.finished and 'ready' or 'running'
+        status = job.failed and 'FAILED' or status
+        start_time = getattr(job, 'begin_after', None)
+        if start_time:
+            start_time = start_time.astimezone(
+                getUtility(
+                    IKofaUtils).tzinfo).strftime("%Y-%m-%d %H:%M:%S %Z")
+        download_url = view.url(view.context, 'download_export',
+                                data=dict(job_id=job_id))
+        new_entry = dict(
+            job=job_id,
+            descr=descr,
+            creator=user_id,
+            status=status,
+            start_time=start_time,
+            download_url=download_url,
+            show_download_button = (job.finished and not job.failed),
+            show_refresh_button = not job.finished,
+            show_discard_button = job.finished,)
+        entries.append(new_entry)
+    return entries
+
 #
 # Login/logout and language switch pages...
@@ -1505,5 +1539,4 @@
     pnav = 0
     export_button = _(u'Create CSV file')
-    _running_exports = None
 
     def getExporters(self):
@@ -1516,66 +1549,21 @@
         return sorted(title_name_tuples)
 
-    def job_finished(self, status):
-        return status == 'completed'
-
-    def getRunningExports(self):
-        """Returns running exports as list of tuples.
-
-        Only exports triggered by the current user (identified by
-        principal.id) are returned.
-
-        Each tuple has the form (<STATUS>, <STATUS_TITLE>, <EXPORTER_NAME>).
-
-        ``STATUS``:
-           the status as machine readable string (something like
-           ``'completed'``)
-
-        ``STATUS_TITLE``:
-           status of export as translated string.
-
-        ``EXPORTER_NAME``:
-           string representing the exporter title used when triggering
-           the export job.
-        """
-        if self._running_exports is None:
-            self._running_exports = self._getRunningExports()
-        return self._running_exports
-
-    def _getRunningExports(self):
-        result = self.context.get_export_jobs_status(self.user_id)
-        return result
-
-    def update(self, export=None, start_export=None, exporter=None,
-               discard=None, download=None):
-        self.user_id = self.request.principal.id
-        if discard:
-            myjobs = self.context.get_running_export_jobs(self.user_id)
-            for entry in myjobs:
-                self.context.delete_export_entry(entry)
-                self.flash(_('Discarded export result'))
-            return
-        if download:
-            myjobs = self.context.get_running_export_jobs(self.user_id)
-            if not len(myjobs):
-                self.flash(_('This export was already discarded.'))
-                return
-            job_id = myjobs[0][0]
-            self.redirect(
-                self.url(self.context, 'export.csv') + '?job_id=%s' % job_id)
-            return
-        if None in (start_export, exporter):
-            return
-        job_id = self.context.start_export_job(
-            exporter, self.request.principal.id)
-        self.redirect(self.url(self.context, 'export'))
+    def update(self, CREATE=None, DISCARD=None, exporter=None, job_id=None):
+        if CREATE:
+            job_id = self.context.start_export_job(
+                exporter, self.request.principal.id)
+        if DISCARD and job_id:
+            entry = self.context.entry_from_job_id(job_id)
+            self.context.delete_export_entry(entry)
+            self.flash(_('Discarded export') + ' %s' % job_id)
+        self.entries = doll_up(self, user=None)
         return
 
 class ExportCSVView(grok.View):
     grok.context(IDataCenter)
-    grok.name('export.csv')
+    grok.name('download_export')
     grok.require('waeup.manageDataCenter')
 
     def render(self, job_id=None):
-
         manager = getUtility(IJobManager)
         job = manager.get(job_id)
@@ -1597,6 +1585,6 @@
             'Content-Disposition', 'attachment; filename="%s' % filename)
         # remove job and running_exports entry from context
-        self.context.delete_export_entry(
-            self.context.entry_from_job_id(job_id))
+        #self.context.delete_export_entry(
+        #    self.context.entry_from_job_id(job_id))
         ob_class = self.__implemented__.__name__.replace('waeup.kofa.','')
         self.context.logger.info('%s - exported: %s' % (ob_class, filename))
Index: main/waeup.kofa/trunk/src/waeup/kofa/browser/templates/datacenterexportpage.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/browser/templates/datacenterexportpage.pt	(revision 9821)
+++ main/waeup.kofa/trunk/src/waeup/kofa/browser/templates/datacenterexportpage.pt	(revision 9822)
@@ -1,81 +1,82 @@
-<div i18n:domain="waeup.kofa" class="row">
-  <div class="span8 columns">
-    <div class="span8 columns">
-      <p i18n:translate="">
-        Here you can create CSV files from parts of portal data.
-      </p>
-      <p i18n:translate="">
-        Please pick the type of objects you want to export from the
-        selection below.
-      </p>
-      <p i18n:translate="">
-        The file will be generated and then be made available for you
-        to download. You must download or discard any existing export
-        file before creating a new one.
-      </p>
-    </div>
-    <form  method="POST">
-      <fieldset>
-        <label for="running_exports"
-          tal:condition="view/getRunningExports">Running Export:
-        </label>
+<p i18n:translate="">
+  Here you can create CSV files from parts of portal data.
+  Please pick the type of objects you want to export from the
+  selection below. The file will be generated and then be
+  made available for you to download in the table below.
+</p>
 
+<br />
 
-        <div class="input span6">
-          <div tal:repeat="items view/getRunningExports">
-            Data type:
-            <span tal:content="python: items[2]">exporter
-            </span><br />
-              Status:
-            <span tal:content="python: items[1]">status
-            </span>
-            <br /><br /><br />
-            <span tal:condition="not: python: view.job_finished(items[0])">
-              <img src="" tal:attributes="src static/ajax-loader.gif"
-                alt="Loading..."  class="spinner" />
-              <input type="submit" name="reload" value="Reload"
-                class="btn primary" />
-            </span>
-            <div>
-              <span tal:condition="python: view.job_finished(items[0])">
-                <input type="submit" name="download" value="Download"
-                  class="btn primary" />
-                <input type="submit" name="discard" value="Discard"
-                  class="btn secondary" />
-              </span>
-            </div>
-          </div>
-          <div>
-            <tal:loading_bar content="structure provider:loadingbar" />
-          </div>
-        </div>
+<form  method="POST">
+  <label for="exporter">
+    Exporter (Data Type):
+  </label>
+  <div class="input">
+    <select name="exporter">
+      <span tal:repeat="items view/getExporters" tal:omit-tag="">
+        <option tal:define="name python: items[1]; title python: items[0]"
+          tal:attributes="value name">
+        <span tal:replace="title">TITLE
+        </span>
+        </option>
+      </span>
+    </select>
+  </div>
+  <br />
+  <div class="input">
+    <input i18n:translate="" type="submit" class="btn primary"
+      name="CREATE" tal:attributes="value view/export_button" />
+  </div>
+</form>
 
+<br /><br />
 
-        <div class="clearfix" tal:condition="not: view/getRunningExports">
-          <label for="exporter">Data Type:
-          </label>
-          <div class="input">
-            <select name="exporter">
-              <span tal:repeat="items view/getExporters" tal:omit-tag="">
-                <option tal:define="name python: items[1]; title python: items[0]"
-                  tal:attributes="value name">
-                <span tal:replace="title">TITLE
-                </span>
-                </option>
-              </span>
-            </select>
-            <span class="help-inline" i18n:translate="">
-              Type of objects to export
-            </span>
-          </div>
-        </div>
-
-
-        <div class="input" tal:condition="not: view/getRunningExports">
-          <input i18n:translate="" type="submit" class="btn primary"
-            name="start_export" tal:attributes="value view/export_button" />
-        </div>
-      </fieldset>
-    </form>
-  </div>
-</div>
+<table i18n:domain="waeup.kofa">
+  <thead>
+    <tr>
+      <th i18n:translate="">Export Number</th>
+      <th i18n:translate="">Description</th>
+      <th i18n:translate="">Creator</th>
+      <th i18n:translate="">Creation Date</th>
+      <th i18n:translate="">Status</th>
+      <th>&nbsp;</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr tal:repeat="job view/entries">
+      <td>
+        <span tal:replace="job/job">12</span>
+      </td>
+      <td>
+        <span tal:replace="structure job/descr">DESCRIPTION</span>
+      </td>
+      <td>
+        <span tal:replace="job/creator">CREATOR</span>
+      </td>
+      <td nowrap>
+        <span tal:replace="job/start_time">DATETIME</span>
+      </td>
+      <td>
+        <span tal:replace="job/status">STATUS</span>
+      </td>
+      <td nowrap>
+        <a href="" class="btn" i18n:translate=""
+           tal:condition="job/show_refresh_button">
+          <img tal:attributes="src static/actionicon_reload.png" />
+          Reload
+        </a>
+        <a href="" class="btn primary"
+               tal:attributes="href job/download_url"
+               tal:condition="job/show_download_button">
+          Download</a>
+        <form method="POST">
+          <input type="hidden" name="job_id"
+                 tal:attributes="value job/job" />
+          <input type="submit" class="btn secondary"
+                 name="DISCARD" value="Discard"
+                 tal:condition="job/show_discard_button" />
+        </form>
+      </td>
+    </tr>
+  </tbody>
+</table>
Index: main/waeup.kofa/trunk/src/waeup/kofa/browser/tests/test_browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/browser/tests/test_browser.py	(revision 9821)
+++ main/waeup.kofa/trunk/src/waeup/kofa/browser/tests/test_browser.py	(revision 9822)
@@ -200,16 +200,12 @@
         # while the export file is created, we get a reload button
         # (or a loading bar if javascript is enabled)...
-        self.browser.getControl("Reload").click()
+        self.assertTrue('Reload' in self.browser.contents)
         # ...which is displayed as long as the job is not finished.
         # When the job is finished and we reload the page...
         job_id = self.wait_for_export_job_completed()
-        try:
-            self.browser.getControl("Reload").click()
-        except LookupError:
-            # if the job completed very fast, we will get the download
-            # link immediately
-            pass
+        self.browser.open(self.datacenter_path + '/export')
+        self.assertFalse('Reload' in self.browser.contents)
         # ...we can download the result
-        self.browser.getControl("Download").click()
+        self.browser.getLink("Download").click()
         self.assertEqual(self.browser.headers['content-type'],
                          'text/csv; charset=UTF-8')
@@ -221,8 +217,8 @@
 
         # after download, the job and the result file are removed
-        manager = getUtility(IJobManager)
-        result = manager.get(job_id)
-        self.assertEqual(result, None)
-        self.assertEqual(self.stored_in_datacenter(job_id), False)
+        #manager = getUtility(IJobManager)
+        #result = manager.get(job_id)
+        #self.assertEqual(result, None)
+        #self.assertEqual(self.stored_in_datacenter(job_id), False)
         logfile = os.path.join(
             self.app['datacenter'].storage, 'logs', 'datacenter.log')
@@ -259,9 +255,6 @@
         self.browser.getControl("Create CSV file").click()
         job_id = self.wait_for_export_job_completed()
-        try:
-            self.browser.getControl("Reload").click()
-        except LookupError:
-            pass
-        self.browser.getControl("Download").click()
+        self.browser.open(self.datacenter_path + '/@@export')
+        self.browser.getLink("Download").click()
         self.assertEqual(self.browser.headers['content-type'],
                          'text/csv; charset=UTF-8')
@@ -291,5 +284,5 @@
         self.browser.open(self.datacenter_path + '/@@export')
         self.browser.getControl("Discard").click()
-        self.assertTrue('Discarded export result' in self.browser.contents)
+        self.assertTrue('Discarded export' in self.browser.contents)
         return
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 9821)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser.py	(revision 9822)
@@ -37,5 +37,5 @@
     KofaForm, NullValidator)
 from waeup.kofa.browser.breadcrumbs import Breadcrumb
-from waeup.kofa.browser.pages import ContactAdminForm
+from waeup.kofa.browser.pages import ContactAdminForm, ExportCSVView, doll_up
 from waeup.kofa.browser.resources import (
     datepicker, datatable, tabs, warning, toggleall)
@@ -2661,38 +2661,4 @@
     pnav = 1
 
-    def doll_up(self):
-        job_entries = self.context.get_running_export_jobs(
-            self.request.principal.id)
-        job_manager = getUtility(IJobManager)
-        entries = []
-        for job_id, exporter_name, user_id in job_entries:
-            job = job_manager.get(job_id)
-            exporter = getUtility(ICSVExporter, name=exporter_name)
-            exporter_title = getattr(exporter, 'title', 'Unknown')
-            args = ', '.join([str(x) for x in job.kwargs.values()])
-            descr = '%s (%s)' % (exporter_title, args)
-            status = job.finished and 'ready' or 'running'
-            status = job.failed and 'FAILED' or status
-            start_time = getattr(job, 'begin_after', None)
-            if start_time:
-                start_time = start_time.astimezone(
-                    getUtility(
-                        IKofaUtils).tzinfo).strftime("%Y-%m-%d %H:%M:%S %Z")
-            download_url = self.url(self.context, 'download',
-                                    data=dict(job_id=job_id))
-            new_entry = dict(
-                job=job_id,
-                descr=descr,
-                creator=user_id,
-                status=status,
-                start_time=start_time,
-                download_url=download_url,
-                show_download_button = (job.finished and not job.failed),
-                show_refresh_button = not job.finished,
-                show_discard_button = job.finished,)
-            entries.append(new_entry)
-        self.entries = entries
-        pass
-
     def update(self, CREATE=None, DISCARD=None, job_id=None):
         if CREATE:
@@ -2703,5 +2669,5 @@
             self.context.delete_export_entry(entry)
             self.flash(_('Discarded export') + ' %s' % job_id)
-        self.doll_up()
+        self.entries = doll_up(self, user=self.request.principal.id)
         return
 
@@ -2771,21 +2737,7 @@
         return
 
-class ExportJobContainerDownload(grok.View):
+class ExportJobContainerDownload(ExportCSVView):
     """Page that configures a students export job.
     """
     grok.context(VirtualExportJobContainer)
     grok.require('waeup.showStudents')
-    grok.name('download')
-
-    def update(self, job_id=None):
-        self.job_id=job_id
-        return
-
-    def render(self):
-        job = getUtility(IJobManager).get(self.job_id)
-        self.response.setHeader(
-            'Content-Type', 'text/csv; charset=UTF-8')
-        self.response.setHeader(
-            'Content-Disposition:', 'attachment; filename="%s' % (
-                'students.csv',))
-        return open(job.result, 'rb')
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/exportjobsindex.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/exportjobsindex.pt	(revision 9821)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/browser_templates/exportjobsindex.pt	(revision 9822)
@@ -16,5 +16,5 @@
       </td>
       <td>
-        <span tal:replace="job/descr">DESCRIPTION</span>
+        <span tal:replace="structure job/descr">DESCRIPTION</span>
       </td>
       <td>
@@ -28,19 +28,19 @@
       </td>
       <td nowrap>
+        <a href="" class="btn" i18n:translate=""
+           tal:condition="job/show_refresh_button">
+          <img tal:attributes="src static/actionicon_reload.png" />
+          Reload
+        </a>
+        <a href="" class="btn primary"
+               tal:attributes="href job/download_url"
+               tal:condition="job/show_download_button">
+          Download</a>
         <form method="POST">
           <input type="hidden" name="job_id"
                  tal:attributes="value job/job" />
-          <a href="" class="btn primary"
-                 tal:attributes="href job/download_url"
-                 tal:condition="job/show_download_button">
-            Download</a>
           <input type="submit" class="btn secondary"
                  name="DISCARD" value="Discard"
                  tal:condition="job/show_discard_button" />
-          <a href="" class="btn" i18n:translate=""
-             tal:condition="job/show_refresh_button">
-            <img tal:attributes="src static/actionicon_reload.png" />
-            Reload
-          </a>
         </form>
       </td>
