Index: /main/waeup.uniben/trunk/src/waeup/uniben/hostels/__init__.py
===================================================================
--- /main/waeup.uniben/trunk/src/waeup/uniben/hostels/__init__.py	(revision 13322)
+++ /main/waeup.uniben/trunk/src/waeup/uniben/hostels/__init__.py	(revision 13322)
@@ -0,0 +1,4 @@
+"""This package contains everything regarding hostels.
+"""
+# Make this a package.
+
Index: /main/waeup.uniben/trunk/src/waeup/uniben/hostels/browser.py
===================================================================
--- /main/waeup.uniben/trunk/src/waeup/uniben/hostels/browser.py	(revision 13322)
+++ /main/waeup.uniben/trunk/src/waeup/uniben/hostels/browser.py	(revision 13322)
@@ -0,0 +1,53 @@
+## $Id: browser.py 13177 2015-07-18 06:07:14Z henrik $
+##
+## Copyright (C) 2015 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+import grok
+from waeup.kofa.hostels.interfaces import IHostelsContainer
+from waeup.kofa.hostels.browser import ReleaseExpiredAllocationsPage, HostelsContainerPage
+from waeup.kofa.browser.viewlets import ManageActionButton
+
+
+class ReleaseExpiredAllocationsActionButton(ManageActionButton):
+    grok.order(2)
+    grok.context(IHostelsContainer)
+    grok.view(HostelsContainerPage)
+    grok.require('waeup.manageHostels')
+    icon = 'actionicon_sweep.png'
+    target = 'releaseexpired'
+    text = 'Release all expired bed space allocations'
+
+    @property
+    def target_url(self):
+        if self.target and self.request.principal.id == 'admin':
+            return self.view.url(self.view.context, self.target)
+        return
+
+    @property
+    def onclick(self):
+        msg = "'All expired bed space allocations will be annulled. " + \
+              "Bed space allocations expire four (4) days after accommodation " + \
+              "booking if maintenance fee has not been paid. Are you sure?'"
+        return "return window.confirm(%s);" % msg
+
+
+class CustomReleaseExpiredAllocationsPage(ReleaseExpiredAllocationsPage):
+    """Release all expired allocated beds.
+    """
+
+    def update(self):
+        super(CustomReleaseExpiredAllocationsPage, self).update(4)
+        return
Index: /main/waeup.uniben/trunk/src/waeup/uniben/hostels/tests.py
===================================================================
--- /main/waeup.uniben/trunk/src/waeup/uniben/hostels/tests.py	(revision 13322)
+++ /main/waeup.uniben/trunk/src/waeup/uniben/hostels/tests.py	(revision 13322)
@@ -0,0 +1,72 @@
+## $Id: tests.py 13319 2015-10-14 17:07:35Z henrik $
+##
+## Copyright (C) 2015 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+"""
+Tests for hostels and their UI components.
+"""
+import grok
+from datetime import datetime, timedelta
+from zope.event import notify
+from zope.testbrowser.testing import Browser
+from zope.catalog.interfaces import ICatalog
+from zope.component import queryUtility
+from waeup.kofa.hostels.vocabularies import NOT_OCCUPIED
+from waeup.kofa.students.accommodation import BedTicket
+from waeup.kofa.hostels.tests import HostelsFullSetup
+
+from waeup.uniben.testing import FunctionalLayer
+
+
+class HostelsContainerTests(HostelsFullSetup):
+
+    layer = FunctionalLayer
+
+    def test_release_expired_allocations(self):
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        cat = queryUtility(ICatalog, name='beds_catalog')
+        bedticket = BedTicket()
+        bedticket.booking_session = 2004
+        bedticket.bed_coordinates = u'anything'
+        self.student['accommodation'].addBedTicket(bedticket)
+        self.app[
+            'hostels']['hall-x']['hall_block_room_bed'].owner = self.student_id
+        notify(grok.ObjectModifiedEvent(
+            self.app['hostels']['hall-x']['hall_block_room_bed']))
+        results = cat.searchResults(owner=(self.student_id, self.student_id))
+        self.assertEqual(len(results), 1)
+        self.browser.open(self.container_path + '/releaseexpired')
+        self.assertTrue('No bed released' in self.browser.contents)
+        delta = timedelta(days=5)   # 4 days expiration time span in Uniben
+        bedticket.booking_date = datetime.utcnow() - delta
+        self.browser.open(self.container_path + '/releaseexpired')
+        self.assertTrue(
+            'Successfully released beds: hall_block_room_bed (B1000000)'
+            in self.browser.contents)
+        results = cat.searchResults(owner=(self.student_id, self.student_id))
+        self.assertEqual(len(results), 0)
+        self.assertMatches(bedticket.display_coordinates,
+            '-- booking expired (2015-10-14 08:35:38 WAT) --')
+        self.assertEqual(
+            self.app['hostels']['hall-x']['hall_block_room_bed'].owner,
+            NOT_OCCUPIED)
+        # Releasing is logged.
+        logcontent = open(self.logfile).read()
+        self.assertTrue(
+            'hostels.browser.CustomReleaseExpiredAllocationsPage - hostels - '
+            'released: hall_block_room_bed (B1000000)'
+            in logcontent)
+        return
