Index: main/waeup.kofa/trunk/src/waeup/kofa/app.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/app.py	(revision 8845)
+++ main/waeup.kofa/trunk/src/waeup/kofa/app.py	(revision 8846)
@@ -25,4 +25,5 @@
 from waeup.kofa.students.container import StudentsContainer
 from waeup.kofa.hostels.container import HostelsContainer
+from waeup.kofa.mandates.container import MandatesContainer
 from waeup.kofa.interfaces import (
     IUniversity, IKofaPluggable, IObjectUpgradeEvent, )
@@ -59,4 +60,5 @@
         self['configuration'] = ConfigurationContainer()
         self['hostels'] = HostelsContainer()
+        self['mandates'] = MandatesContainer()
         self._createPlugins()
 
Index: main/waeup.kofa/trunk/src/waeup/kofa/mandates/__init__.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/mandates/__init__.py	(revision 8846)
+++ main/waeup.kofa/trunk/src/waeup/kofa/mandates/__init__.py	(revision 8846)
@@ -0,0 +1,10 @@
+"""This package contains everything regarding temporary jobs (mandates).
+"""
+# Make this a package.
+from waeup.kofa.mandates.container import MandatesContainer
+from waeup.kofa.mandates.mandate import Mandate
+
+__all__ = [
+    'MandatesContainer',
+    'Mandate',
+    ]
Index: main/waeup.kofa/trunk/src/waeup/kofa/mandates/container.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/mandates/container.py	(revision 8846)
+++ main/waeup.kofa/trunk/src/waeup/kofa/mandates/container.py	(revision 8846)
@@ -0,0 +1,64 @@
+## $Id$
+##
+## Copyright (C) 2012 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
+##
+"""
+Containers which contain mandate objects.
+"""
+import grok
+from datetime import datetime
+from grok import index
+from waeup.kofa.interfaces import IKofaPluggable
+from waeup.kofa.mandates.interfaces import IMandatesContainer, IMandate
+
+class MandatesContainer(grok.Container):
+    """This is a container for all kind of mandates.
+    """
+    grok.implements(IMandatesContainer)
+    grok.provides(IMandatesContainer)
+
+    def addMandate(self, mandate):
+        if not IMandate.providedBy(mandate):
+            raise TypeError(
+                'MandateContainers contain only IMandate instances')
+        self[mandate.mandate_id] = mandate
+        return
+
+    def removeExpired(self):
+        """Remove all expired mandates.
+        """
+        for mandate in self.keys():
+            if self[mandate].expires < datetime.utcnow():
+                del self[mandate]
+        return
+
+class MandatesPlugin(grok.GlobalUtility):
+    """A plugin that creates container for mandates inside a university.
+    """
+    grok.implements(IKofaPluggable)
+    grok.name('mandates')
+
+    def setup(self, site, name, logger):
+        if 'mandates' in site.keys():
+            logger.warn('Could not create container for faculties in Kofa.')
+            return
+        site['mandates'] = MandatesContainer()
+        logger.info('Container for mandates created')
+        return
+
+    def update(self, site, name, logger):
+        if not 'mandates' in site.keys():
+            self.setup(site, name, logger)
Index: main/waeup.kofa/trunk/src/waeup/kofa/mandates/interfaces.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/mandates/interfaces.py	(revision 8846)
+++ main/waeup.kofa/trunk/src/waeup/kofa/mandates/interfaces.py	(revision 8846)
@@ -0,0 +1,42 @@
+## $Id$
+##
+## Copyright (C) 2012 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
+##
+from zope.interface import Attribute
+from zope import schema
+from waeup.kofa.interfaces import IKofaObject
+
+class IMandatesContainer(IKofaObject):
+    """A container for all kind of mandate objects.
+
+    """
+
+    def addMandate(mandate):
+        """Add mandate.
+        """
+
+    def removeExpired():
+        """Remove all expired mandates.
+        """
+
+class IMandate(IKofaObject):
+    """A base representation of mandates.
+
+    """
+    mandate_id = Attribute('Mandate Identifier')
+    expires = Attribute('Expiration Datetime')
+    params = Attribute('Dictionary with mandate parameters')
+    category = Attribute('Mandate Category')
Index: main/waeup.kofa/trunk/src/waeup/kofa/mandates/mandate.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/mandates/mandate.py	(revision 8846)
+++ main/waeup.kofa/trunk/src/waeup/kofa/mandates/mandate.py	(revision 8846)
@@ -0,0 +1,61 @@
+## $Id$
+##
+## Copyright (C) 2012 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
+##
+"""
+These are the mandates.
+"""
+import grok
+from datetime import datetime, timedelta
+from grok import index
+from waeup.kofa.interfaces import IUserAccount
+from waeup.kofa.interfaces import MessageFactory as _
+from waeup.kofa.mandates.interfaces import IMandate
+
+class Mandate(grok.Container):
+    """This is a mandate which can set a student password.
+    """
+    grok.implements(IMandate)
+    grok.provides(IMandate)
+
+    def __init__(self, days=1):
+        super(Mandate, self).__init__()
+        self.creation_date = datetime.utcnow() # offset-naive datetime
+        delta = timedelta(days=days)
+        self.expires = datetime.utcnow() + delta
+        self.mandate_id = None
+        self.category = None
+        self.params = {}
+        return
+
+    def _setStudentPassword(self):
+        student_id = self.params.get('student_id', None)
+        pwd = self.params.get('password', None)
+        student = grok.getSite()['students'].get(student_id, None)
+        if student and pwd:
+            IUserAccount(student).setPassword(pwd)
+            return True
+        return False
+
+    def execute(self):
+        msg = _('Wrong mandate parameters.')
+        if self.expires < datetime.utcnow():
+            msg = _('Mandate expired.')
+        if self.category == 'student_password':
+            if self._setStudentPassword():
+                msg = _('Password has been successfully set.')
+        del self.__parent__[self.mandate_id]
+        return msg
Index: main/waeup.kofa/trunk/src/waeup/kofa/mandates/tests.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/mandates/tests.py	(revision 8846)
+++ main/waeup.kofa/trunk/src/waeup/kofa/mandates/tests.py	(revision 8846)
@@ -0,0 +1,126 @@
+## $Id$
+##
+## Copyright (C) 2012 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 mandates.
+"""
+import tempfile
+import shutil
+from datetime import datetime, timedelta
+from zope.interface.verify import verifyClass, verifyObject
+from zope.component import createObject
+from zope.component.hooks import setSite, clearSite
+from waeup.kofa.app import University
+from waeup.kofa.interfaces import IUserAccount
+from waeup.kofa.mandates.interfaces import (
+    IMandatesContainer, IMandate)
+from waeup.kofa.mandates.container import MandatesContainer
+from waeup.kofa.mandates.mandate import Mandate
+from waeup.kofa.testing import (FunctionalLayer, FunctionalTestCase)
+
+class MandatesContainerTestCase(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def test_interfaces(self):
+        # Make sure the correct interfaces are implemented.
+        self.assertTrue(
+            verifyClass(
+                IMandatesContainer, MandatesContainer)
+            )
+        self.assertTrue(
+            verifyObject(
+                IMandatesContainer, MandatesContainer())
+            )
+        self.assertTrue(
+            verifyClass(
+                IMandate, Mandate)
+            )
+        self.assertTrue(
+            verifyObject(
+                IMandate, Mandate())
+            )
+        return
+
+    def setUp(self):
+        super(MandatesContainerTestCase, self).setUp()
+
+        # Setup a sample site for each test
+        app = University()
+        self.dc_root = tempfile.mkdtemp()
+        app['datacenter'].setStoragePath(self.dc_root)
+
+        # Prepopulate the ZODB...
+        self.getRootFolder()['app'] = app
+        # we add the site immediately after creation to the
+        # ZODB. Catalogs and other local utilities are not setup
+        # before that step.
+        self.app = self.getRootFolder()['app']
+        # Set site here. Some of the following setup code might need
+        # to access grok.getSite() and should get our new app then
+        setSite(app)
+
+    def tearDown(self):
+        super(MandatesContainerTestCase, self).tearDown()
+        clearSite()
+        shutil.rmtree(self.dc_root)
+
+    def test_set_password(self):
+        student = createObject('waeup.Student')
+        self.app['students'].addStudent(student)
+        # Add and execute a mandate with missing parameters.
+        mandate = Mandate()
+        mandate.mandate_id = '12345'
+        mandate.category = 'student_password'
+        self.app['mandates'].addMandate(mandate)
+        msg = mandate.execute()
+        self.assertEqual(msg, u'Wrong mandate parameters.')
+        # Add and execute an expired mandate.
+        mandate = Mandate(days=0)
+        mandate.mandate_id = '12345'
+        mandate.category = 'student_password'
+        self.app['mandates'].addMandate(mandate)
+        msg = mandate.execute()
+        self.assertEqual(msg, u'Mandate expired.')
+        # Ad and execute a perfect mandate
+        mandate = Mandate(days=0)
+        mandate.mandate_id = '12345'
+        mandate.category = 'student_password'
+        mandate.params['student_id'] = student.student_id
+        mandate.params['password'] = 'mypwd1'
+        self.app['mandates'].addMandate(mandate)
+        msg = mandate.execute()
+        # Password has been set.
+        self.assertEqual(msg, u'Password has been successfully set.')
+        self.assertTrue(IUserAccount(student).checkPassword('mypwd1'))
+        # All mandates have been removed.
+        self.assertEqual(len(self.app['mandates'].keys()), 0)
+
+    def test_remove_expired(self):
+        # mandate1 is an old mandate which just expired.
+        mandate1 = Mandate(days=0)
+        mandate1.mandate_id = '12345'
+        self.app['mandates'].addMandate(mandate1)
+        # mandate2 is a new mandate with default time delta.
+        mandate2 = Mandate()
+        mandate2.mandate_id = '23456'
+        self.app['mandates'].addMandate(mandate2)
+        self.assertEqual(len(self.app['mandates'].keys()), 2)
+        self.app['mandates'].removeExpired()
+        # Only the new mandate remains in the container.
+        self.assertEqual(len(self.app['mandates'].keys()), 1)
+        self.assertEqual([i for i in self.app['mandates'].keys()], [u'23456'])
