Index: main/waeup.kofa/trunk/CHANGES.txt
===================================================================
--- main/waeup.kofa/trunk/CHANGES.txt	(revision 13958)
+++ main/waeup.kofa/trunk/CHANGES.txt	(revision 13959)
@@ -4,4 +4,6 @@
 1.4.2.dev0 (unreleased)
 =======================
+
+* Count mandates on configuration page and provide 'Purge' button.
 
 * Start `ApplicantExporter` and `ApplicantPaymentExporter` at the same
Index: main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py	(revision 13958)
+++ main/waeup.kofa/trunk/src/waeup/kofa/browser/pages.py	(revision 13959)
@@ -1049,5 +1049,5 @@
     pnav = 0
     label = _(u'Edit portal configuration')
-    taboneactions = [_('Save'), _('Update plugins')]
+    taboneactions = [_('Save'), _('Update plugins'), _('Purge mandates')]
     tabtwoactions = [
         _('Add session configuration'),
@@ -1100,4 +1100,12 @@
         grok.getSite().updatePlugins()
         self.flash(_('Plugins were updated. See log file for details.'))
+        return
+
+    @action(_('Purge mandates'),
+              tooltip=_('For experts only!'),
+              validator=NullValidator)
+    def purgeMandates(self, **data):
+        num = grok.getSite()['mandates'].removeExpired()
+        self.flash(_('${a} mandate(s) were purged.', mapping = {'a': num}))
         return
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/browser/templates/configurationmanagepage.pt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/browser/templates/configurationmanagepage.pt	(revision 13958)
+++ main/waeup.kofa/trunk/src/waeup/kofa/browser/templates/configurationmanagepage.pt	(revision 13959)
@@ -16,4 +16,15 @@
         <tbody>
           <tal:widgets content="structure provider:widgets" />
+          <tr>
+            <td>Mandates:</td>
+            <td>
+              <span tal:content="python:layout.site['mandates'].count[0]"></span>
+              <span i18n:translate="">active</span>,
+              <span tal:content="python:layout.site['mandates'].count[0]"></span>
+              <span i18n:translate="">expired</span>,
+              <span tal:content="python:layout.site['mandates'].count[0]"></span>
+              <span i18n:translate="">total</span>
+            </td>
+          </tr>
         </tbody>
       </table>
Index: main/waeup.kofa/trunk/src/waeup/kofa/mandates/container.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/mandates/container.py	(revision 13958)
+++ main/waeup.kofa/trunk/src/waeup/kofa/mandates/container.py	(revision 13959)
@@ -30,4 +30,14 @@
     grok.implements(IMandatesContainer)
     grok.provides(IMandatesContainer)
+
+    @property
+    def count(self):
+        """Count active and expired mandates.
+        """
+        now = datetime.utcnow()
+        total = len(self)
+        expired = len([i for i in self.values() if i.expires < now])
+        active = total - expired
+        return active, expired, total
 
     def addMandate(self, mandate):
Index: main/waeup.kofa/trunk/src/waeup/kofa/mandates/interfaces.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/mandates/interfaces.py	(revision 13958)
+++ main/waeup.kofa/trunk/src/waeup/kofa/mandates/interfaces.py	(revision 13959)
@@ -25,4 +25,6 @@
     """
 
+    count = Attribute('Count active and expired mandates.')
+
     def addMandate(mandate):
         """Add mandate.
Index: main/waeup.kofa/trunk/src/waeup/kofa/mandates/tests.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/mandates/tests.py	(revision 13958)
+++ main/waeup.kofa/trunk/src/waeup/kofa/mandates/tests.py	(revision 13959)
@@ -153,4 +153,5 @@
         self.app['mandates'].addMandate(mandate2)
         self.assertEqual(len(self.app['mandates'].keys()), 2)
+        self.assertEqual(self.app['mandates'].count, (1, 1, 2))
         num_deleted = self.app['mandates'].removeExpired()
         self.assertEqual(num_deleted, 1)
@@ -158,4 +159,20 @@
         self.assertEqual(len(self.app['mandates'].keys()), 1)
         self.assertEqual([i for i in self.app['mandates'].keys()], [u'23456'])
+
+    def test_purge_mandates(self):
+        # mandate1 is an old mandate which just expired.
+        mandate1 = PasswordMandate(days=0)
+        self.app['mandates'].addMandate(mandate1)
+        # mandate2 is a new mandate with default time delta.
+        mandate2 = PasswordMandate(mandate_id='23456')
+        self.app['mandates'].addMandate(mandate2)
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open('http://localhost/app/configuration')
+        self.assertEqual(self.app['mandates'].count, (1, 1, 2))
+        self.assertTrue('<span>expired</span>' in self.browser.contents)
+        self.browser.getControl("Purge mandates").click()
+        self.assertTrue('1 mandate(s) were purged' in self.browser.contents)
+        self.assertEqual(self.app['mandates'].count, (1, 0, 1))
+        return
 
     def test_browser(self):
