Index: /main/kofacustom.pcn/trunk/CHANGES.txt
===================================================================
--- /main/kofacustom.pcn/trunk/CHANGES.txt	(revision 11831)
+++ /main/kofacustom.pcn/trunk/CHANGES.txt	(revision 11832)
@@ -2,19 +2,6 @@
 *******
 
-1.2dev (unreleased)
+1.0dev (unreleased)
 ===================
 
-* Adjust to layout changes made in base package.
 
-1.1 (2014-06-24)
-================
-
-* Remove waeup.kofa from sources. Use waeup.kofa PyPI package instead.
-
-* Copy layout folder from waeup.kofa. kofacustom.sampleuni uses its own Diazo
-  layout.
-
-1.0 (2014-06-16)
-================
-
-* Initial version.
Index: /main/kofacustom.pcn/trunk/README.txt
===================================================================
--- /main/kofacustom.pcn/trunk/README.txt	(revision 11831)
+++ /main/kofacustom.pcn/trunk/README.txt	(revision 11832)
@@ -1,6 +1,6 @@
-waeup.sampleuni
+waeup.pcn
 ***************
 
-`waeup.sampleuni` is a Python package providing a student information
+`waeup.pcn` is a Python package providing a student information
 portal based on WAeUP Kofa.
 
@@ -16,5 +16,5 @@
 The Subversion_ repository location of this package is
 
-  https://svn.waeup.org/repos/main/waeup.sampleuni/
+  https://svn.waeup.org/repos/main/waeup.pcn/
 
 .. _Subversion: http://subversion.apache.org/
Index: /main/kofacustom.pcn/trunk/buildout-zeo.cfg
===================================================================
--- /main/kofacustom.pcn/trunk/buildout-zeo.cfg	(revision 11831)
+++ /main/kofacustom.pcn/trunk/buildout-zeo.cfg	(revision 11832)
@@ -118,5 +118,5 @@
 # See http://pypi.python.org/pypi/zc.recipe.egg for details...
 recipe = zc.recipe.egg
-eggs = kofacustom.sampleuni [beaker, diazo]
+eggs = kofacustom.pcn [beaker, diazo]
 arguments = "${buildout:parts-directory}/etc/zeo1.conf"
 scripts = kofactl=zeo_client1
@@ -125,5 +125,5 @@
 # See http://pypi.python.org/pypi/zc.recipe.egg for details...
 recipe = zc.recipe.egg
-eggs = kofacustom.sampleuni [beaker, diazo]
+eggs = kofacustom.pcn [beaker, diazo]
 arguments = "${buildout:parts-directory}/etc/zeo2.conf"
 scripts = kofactl=zeo_client2
Index: /main/kofacustom.pcn/trunk/buildout.cfg
===================================================================
--- /main/kofacustom.pcn/trunk/buildout.cfg	(revision 11831)
+++ /main/kofacustom.pcn/trunk/buildout.cfg	(revision 11832)
@@ -57,10 +57,10 @@
 # number of preopened paster threads, default 10
 threadpool_workers = 10
-devel_pkg = kofacustom.sampleuni
+devel_pkg = kofacustom.pcn
 
 [app]
 # This creates all scripts in bin/. The kofactl created here is 'faulty'.
 recipe = zc.recipe.egg
-eggs = kofacustom.sampleuni [beaker, diazo]
+eggs = kofacustom.pcn [beaker, diazo]
        z3c.evalexception>=2.0
        Paste
@@ -76,5 +76,5 @@
 # fixed path to zdaemon.conf as argument.
 recipe = zc.recipe.egg
-eggs = kofacustom.sampleuni [beaker, diazo]
+eggs = kofacustom.pcn [beaker, diazo]
 arguments = "${buildout:parts-directory}/etc/zdaemon.conf"
 scripts = kofactl=kofactl
@@ -91,5 +91,5 @@
 [test]
 recipe = zc.recipe.testrunner
-eggs = kofacustom.sampleuni [beaker, test]
+eggs = kofacustom.pcn [beaker, test]
 defaults = ['--tests-pattern', '^f?tests$', '-v']
 
@@ -97,8 +97,8 @@
 [i18n]
 recipe = z3c.recipe.i18n:i18n
-packages = kofacustom.sampleuni
-eggs = kofacustom.sampleuni
-domain = kofacustom.sampleuni
-output = src/kofacustom/sampleuni/locales
+packages = kofacustom.pcn
+eggs = kofacustom.pcn
+domain = kofacustom.pcn
+output = src/kofacustom/pcn/locales
 zcml =
 
@@ -107,5 +107,5 @@
 [zpasswd]
 recipe = z3c.recipe.dev:script
-eggs = kofacustom.sampleuni
+eggs = kofacustom.pcn
 module = zope.app.server.zpasswd
 method = main
@@ -175,5 +175,5 @@
 [coverage-detect]
 recipe = zc.recipe.testrunner
-eggs = kofacustom.sampleuni[beaker]
+eggs = kofacustom.pcn[beaker]
 defaults = ['--tests-pattern', '^f?tests$', '-v', '--coverage', 'coverage']
 
@@ -196,3 +196,3 @@
     diazo [wsgi]
     PasteScript
-    kofacustom.sampleuni [beaker, diazo]
+    kofacustom.pcn [beaker, diazo]
Index: /main/kofacustom.pcn/trunk/etc/site.zcml.in
===================================================================
--- /main/kofacustom.pcn/trunk/etc/site.zcml.in	(revision 11831)
+++ /main/kofacustom.pcn/trunk/etc/site.zcml.in	(revision 11832)
@@ -1,8 +1,8 @@
 <configure xmlns="http://namespaces.zope.org/zope"
            xmlns:kofa="http://namespaces.waeup.org/kofa"
-           i18n_domain="kofacustom.sampleuni">
+           i18n_domain="kofacustom.pcn">
 
-  <include package="kofacustom.sampleuni" />
-  <include package="kofacustom.sampleuni" file="mail.zcml" />
+  <include package="kofacustom.pcn" />
+  <include package="kofacustom.pcn" file="mail.zcml" />
 
   <!-- install job container
@@ -29,5 +29,5 @@
       path="${buildout:directory}/var/datacenter" />
 
-    <configure i18n_domain="kofacustom.sampleuni">
+    <configure i18n_domain="kofacustom.pcn">
 
       <unauthenticatedPrincipal id="zope.anybody"
Index: /main/kofacustom.pcn/trunk/setup.py
===================================================================
--- /main/kofacustom.pcn/trunk/setup.py	(revision 11831)
+++ /main/kofacustom.pcn/trunk/setup.py	(revision 11832)
@@ -46,5 +46,5 @@
     )
 
-setup(name = 'kofacustom.sampleuni',
+setup(name = 'kofacustom.pcn',
       version = version,
       description = "A customized waeup.kofa",
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/__init__.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/__init__.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/__init__.py	(revision 11832)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/__init__.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/__init__.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/__init__.py	(revision 11832)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/applicant.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/applicant.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/applicant.py	(revision 11832)
@@ -0,0 +1,46 @@
+## $Id$
+##
+## Copyright (C) 2011 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 zope.interface import implementedBy
+from waeup.kofa.applicants.applicant import ApplicantFactory
+from waeup.kofa.utils.helpers import attrs_to_fields
+from waeup.kofa.applicants.applicant import Applicant
+from kofacustom.pcn.applicants.interfaces import(
+    ICustomApplicant, ICustomApplicantEdit)
+
+class CustomApplicant(Applicant):
+
+    grok.implements(ICustomApplicant, ICustomApplicantEdit)
+    grok.provides(ICustomApplicant)
+
+# Set all attributes of CustomApplicant required in ICustomApplicant as field
+# properties. Doing this, we do not have to set initial attributes
+# ourselves and as a bonus we get free validation when an attribute is
+# set.
+CustomApplicant = attrs_to_fields(CustomApplicant)
+
+class CustomApplicantFactory(ApplicantFactory):
+    """A factory for customized applicants.
+    """
+
+    def __call__(self, *args, **kw):
+        return CustomApplicant()
+
+    def getInterfaces(self):
+        return implementedBy(CustomApplicant)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/batching.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/batching.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/batching.py	(revision 11832)
@@ -0,0 +1,36 @@
+## $Id$
+##
+## Copyright (C) 2011 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
+##
+"""Batch processing for applicants.
+"""
+from waeup.kofa.utils.batching import BatchProcessor
+from waeup.kofa.applicants.batching import ApplicantProcessor
+from kofacustom.pcn.applicants.interfaces import ICustomApplicant
+
+class CustomApplicantProcessor(ApplicantProcessor):
+    """A batch processor for IApplicant objects.
+
+    In create mode container_code is required. If application_number is given
+    an applicant with this number is created in the designated container.
+    If application_number is not given a random application_number is assigned.
+    applicant_id is being determined by the system and can't be imported.
+
+    In update or remove mode container_code and application_number columns
+    must not exist. The applicant object is solely searched by its applicant_id
+    or reg_number.
+    """
+    iface = ICustomApplicant
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/browser.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/browser.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/browser.py	(revision 11832)
@@ -0,0 +1,24 @@
+## $Id$
+##
+## Copyright (C) 2011 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
+##
+"""UI components for basic applicants and related components.
+"""
+import grok
+from waeup.kofa.applicants.browser import (
+    ApplicantRegistrationPage, ApplicantsContainerPage)
+from kofacustom.pcn.interfaces import MessageFactory as _
+
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/export.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/export.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/export.py	(revision 11832)
@@ -0,0 +1,32 @@
+## $Id$
+##
+## Copyright (C) 2014 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
+##
+"""Exporters for applicant-related stuff.
+"""
+import grok
+from waeup.kofa.applicants.interfaces import IApplicantBaseData
+from waeup.kofa.applicants.export import ApplicantsExporter
+from kofacustom.pcn.applicants.interfaces import ICustomApplicant
+
+class CustomApplicantsExporter(ApplicantsExporter):
+    """Exporter for Custom Applicants.
+    """
+
+    fields = tuple(sorted(set(
+        ICustomApplicant.names() +
+        IApplicantBaseData.names()
+        ))) + ('container_code',)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/interfaces.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/interfaces.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/interfaces.py	(revision 11832)
@@ -0,0 +1,49 @@
+## $Id$
+##
+## Copyright (C) 2011 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
+##
+"""Customized interfaces of the university application package.
+"""
+
+from waeup.kofa.applicants.interfaces import (
+    IApplicantBaseData,
+    IApplicantOnlinePayment,
+    IApplicantEdit,
+    )
+from kofacustom.pcn.interfaces import MessageFactory as _
+
+class ICustomApplicant(IApplicantBaseData):
+    """A custom applicant.
+
+    """
+
+class ICustomApplicantEdit(IApplicantEdit):
+    """A custom applicant interface for editing.
+
+    Here we can repeat the fields from base data and set the
+    `required` and `readonly` attributes to True to further restrict
+    the data access. Or we can allow only certain certificates to be
+    selected by choosing the appropriate source.
+
+    We cannot omit fields here. This has to be done in the
+    respective form page.
+    """
+
+class ICustomApplicantOnlinePayment(IApplicantOnlinePayment):
+    """A custom applicant payment via payment gateways.
+
+    """
+
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/payment.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/payment.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/payment.py	(revision 11832)
@@ -0,0 +1,59 @@
+## $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
+##
+"""
+Application fee payment.
+"""
+import grok
+from zope.component.interfaces import IFactory
+from zope.interface import implementedBy
+from waeup.kofa.applicants.payment import ApplicantOnlinePayment
+from waeup.kofa.utils.helpers import attrs_to_fields
+from kofacustom.pcn.applicants.interfaces import ICustomApplicantOnlinePayment
+
+class CustomApplicantOnlinePayment(ApplicantOnlinePayment):
+    """This is a custom online payment for applicants.
+
+    Since this class inherits from ApplicantOnlinePayment, it automatically
+    implements the interfaces of the parent class which means
+    all viewlets and views meant for StudentOnlinePayment
+    can also be accessed in the customized system.
+    """
+    grok.implements(ICustomApplicantOnlinePayment)
+    grok.provides(ICustomApplicantOnlinePayment)
+
+    def __init__(self):
+        super(CustomApplicantOnlinePayment, self).__init__()
+        return
+
+CustomApplicantOnlinePayment = attrs_to_fields(
+    CustomApplicantOnlinePayment, omit=['display_item'])
+
+# Applicant online payments must be importable. So we might need a factory.
+class CustomApplicantOnlinePaymentFactory(grok.GlobalUtility):
+    """A factory for applicant online payments.
+    """
+    grok.implements(IFactory)
+    grok.name(u'waeup.ApplicantOnlinePayment')
+    title = u"Create a new online payment.",
+    description = u"This factory instantiates new online payment instances."
+
+    def __call__(self, *args, **kw):
+        return CustomApplicantOnlinePayment()
+
+    def getInterfaces(self):
+        return implementedBy(CustomApplicantOnlinePayment)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/tests/__init__.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/tests/__init__.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/tests/__init__.py	(revision 11832)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/tests/test_applicant.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/tests/test_applicant.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/tests/test_applicant.py	(revision 11832)
@@ -0,0 +1,79 @@
+## $Id$
+##
+## Copyright (C) 2011 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 applicants and related.
+"""
+import grok
+import shutil
+import tempfile
+from StringIO import StringIO
+from zope.component import adapts, queryUtility
+from zope.component.interfaces import IFactory
+from zope.interface import verify, implements
+from zope.location.interfaces import ILocation
+from waeup.kofa.image.interfaces import IKofaImageFile
+from waeup.kofa.imagestorage import DefaultStorage
+from waeup.kofa.interfaces import IFileStoreHandler, IFileStoreNameChooser
+from waeup.kofa.testing import FunctionalTestCase
+from kofacustom.pcn.applicants.applicant import (
+    CustomApplicant, CustomApplicantFactory,
+    )
+from kofacustom.pcn.applicants.interfaces import ICustomApplicant
+from kofacustom.pcn.testing import FunctionalLayer
+
+class CustomApplicantTest(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(CustomApplicantTest, self).setUp()
+        self.applicant = CustomApplicant()
+        return
+
+    def tearDown(self):
+        super(CustomApplicantTest, self).tearDown()
+        return
+
+    def test_interfaces(self):
+        verify.verifyClass(ICustomApplicant, CustomApplicant)
+        verify.verifyObject(ICustomApplicant, self.applicant)
+        return
+
+class CustomApplicantFactoryTest(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(CustomApplicantFactoryTest, self).setUp()
+        self.factory = CustomApplicantFactory()
+        return
+
+    def tearDown(self):
+        super(CustomApplicantFactoryTest, self).tearDown()
+        return
+
+    def test_interfaces(self):
+        verify.verifyClass(IFactory, CustomApplicantFactory)
+        verify.verifyObject(IFactory, self.factory)
+
+    def test_factory(self):
+        obj = self.factory()
+        assert isinstance(obj, CustomApplicant)
+
+    def test_getInterfaces(self):
+        implemented_by = self.factory.getInterfaces()
+        assert implemented_by.isOrExtends(ICustomApplicant)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/tests/test_browser.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/tests/test_browser.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/tests/test_browser.py	(revision 11832)
@@ -0,0 +1,114 @@
+## $Id$
+##
+## Copyright (C) 2013 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
+##
+"""
+Test the applicant-related UI components.
+"""
+import os
+import datetime
+from zope.component import createObject, getUtility
+from zope.catalog.interfaces import ICatalog
+from zope.intid.interfaces import IIntIds
+from kofacustom.pcn.testing import FunctionalLayer
+from waeup.kofa.schoolgrades import ResultEntry
+from waeup.kofa.utils.utils import KofaUtils
+from waeup.kofa.applicants.tests.test_browser import ApplicantsFullSetup
+from waeup.kofa.applicants.tests.test_batching import ApplicantImportExportSetup
+from kofacustom.pcn.applicants.export import CustomApplicantsExporter
+from kofacustom.pcn.applicants.batching import CustomApplicantProcessor
+
+class CustomApplicantUITests(ApplicantsFullSetup):
+    # Tests for uploading/browsing the passport image of appplicants
+
+    layer = FunctionalLayer
+
+class ApplicantsExporterTest(ApplicantImportExportSetup):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(ApplicantsExporterTest, self).setUp()
+        self.outfile = os.path.join(self.workdir, 'myoutput.csv')
+        self.cat = getUtility(ICatalog, name='applicants_catalog')
+        self.intids = getUtility(IIntIds)
+        return
+
+    def setup_applicant(self, applicant):
+        # set predictable values for `applicant`
+        applicant.reg_number = u'123456'
+        applicant.applicant_id = u'dp2011_654321'
+        applicant.firstname = u'Anna'
+        applicant.lastname = u'Tester'
+        applicant.middlename = u'M.'
+        applicant.date_of_birth = datetime.date(1981, 2, 4)
+        applicant.sex = 'f'
+        applicant.email = 'anna@sample.com'
+        applicant.phone = u'+234-123-12345'
+        applicant.course1 = self.certificate
+        applicant.course2 = self.certificate
+        applicant.course_admitted = self.certificate
+        applicant.notice = u'Some notice\nin lines.'
+        applicant.password = 'any password'
+        result_entry = ResultEntry(
+            KofaUtils.EXAM_SUBJECTS_DICT.keys()[0],
+            KofaUtils.EXAM_GRADES[0][0]
+            )
+        applicant.school_grades = [
+            result_entry]
+        return applicant
+
+    def test_export_reimport_all(self):
+        # we can export all applicants in a portal
+        # set values we can expect in export file
+        self.applicant = self.setup_applicant(self.applicant)
+        exporter = CustomApplicantsExporter()
+        exporter.export_all(self.app, self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertTrue(
+            'applicant_id,application_date,application_number,course1,course2,'
+            'course_admitted,date_of_birth,display_fullname,email,firstname,'
+            'history,lastname,locked,middlename,notice,password,phone,'
+            'reg_number,sex,special_application,state,'
+            'student_id,suspended,container_code\r\n'
+            'dp2011_654321,,654321,CERT1,CERT1,CERT1,1981-02-04#,'
+            'Anna M. Tester,anna@sample.com,Anna,'
+            in result)
+        self.assertTrue(
+            'Application initialized by system\'],'
+            'Tester,0,M.,"Some notice\nin lines.",any password,'
+            '+234-123-12345#,123456,f,,initialized,,0,dp2011\r\n'
+            in result)
+        # We can import the same file if we ignore some columns.
+        # Since the applicants_catalog hasn't been notified, the same
+        # record with same reg_number can be imported twice.
+        processor = CustomApplicantProcessor()
+        result = processor.doImport(
+            self.outfile,
+            ['ignore_applicant_id','application_date',
+            'ignore_application_number','course1','course2',
+            'course_admitted','date_of_birth',
+            'ignore_display_fullname','email','firstname',
+            'ignore_history','lastname','locked','middlename',
+            'notice','password','phone',
+            'reg_number','sex','special_application','state',
+            'student_id','suspended','container_code'],
+            mode='create')
+        num_succ, num_fail, finished_path, failed_path = result
+        #content = open(failed_path).read()
+        self.assertEqual(num_succ,1)
+        self.assertEqual(num_fail,0)
+        return
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/utils.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/utils.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/applicants/utils.py	(revision 11832)
@@ -0,0 +1,26 @@
+## $Id$
+##
+## Copyright (C) 2011 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
+##
+"""General helper functions and utilities for the application section.
+"""
+
+from waeup.kofa.applicants.utils import ApplicantsUtils
+from kofacustom.pcn.interfaces import MessageFactory as _
+
+class ApplicantsUtils(ApplicantsUtils):
+    """A collection of parameters and methods subject to customization.
+    """
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/browser/__init__.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/browser/__init__.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/browser/__init__.py	(revision 11832)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/browser/pages.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/browser/pages.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/browser/pages.py	(revision 11832)
@@ -0,0 +1,35 @@
+## $Id$
+##
+## Copyright (C) 2011 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.browser.pages import (
+    SessionConfigurationAddFormPage, SessionConfigurationManageFormPage,
+    LoginPage, CertificatePage, CertificateManageFormPage)
+from waeup.kofa.university.interfaces import ICertificate
+from kofacustom.pcn.interfaces import (
+    ICustomSessionConfiguration, ICustomSessionConfigurationAdd)
+
+class CustomSessionConfigurationAddFormPage(SessionConfigurationAddFormPage):
+    """Add a session configuration object to configuration container.
+    """
+    form_fields = grok.AutoFields(ICustomSessionConfigurationAdd)
+
+class CustomSessionConfigurationManageFormPage(SessionConfigurationManageFormPage):
+    """Manage session configuration object.
+    """
+    form_fields = grok.AutoFields(ICustomSessionConfiguration)
+
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/browser/pdf.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/browser/pdf.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/browser/pdf.py	(revision 11832)
@@ -0,0 +1,35 @@
+## $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
+##
+"""
+Reusable components for pdf generation.
+"""
+
+import os
+from waeup.kofa.browser.pdf import PDFCreator
+
+class CustomPDFCreator(PDFCreator):
+    """A utility to help with generating PDF docs.
+    """
+
+    #header_logo_path = os.path.join(
+    #    os.path.dirname(__file__), 'static', 'pdf_logo.jpg')
+    #watermark_path = os.path.join(
+    #    os.path.dirname(__file__), 'static', 'pdf_watermark.jpg')
+
+    watermark_pos = [-25, 100]
+    logo_pos = [507, 720, 40]
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/browser/tests.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/browser/tests.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/browser/tests.py	(revision 11832)
@@ -0,0 +1,49 @@
+## $Id$
+##
+## Copyright (C) 2011 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 tempfile
+import shutil
+from zope.testbrowser.testing import Browser
+from zope.component.hooks import setSite, clearSite
+from zope.security.interfaces import Unauthorized
+from waeup.kofa.testing import FunctionalTestCase
+from kofacustom.pcn.testing import FunctionalLayer
+from waeup.kofa.app import University
+
+class CustomPackageBrowserTests(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(CustomPackageBrowserTests, self).setUp()
+
+        # Setup a sample site for each test
+        app = University()
+        self.dc_root = tempfile.mkdtemp()
+        app['datacenter'].setStoragePath(self.dc_root)
+
+        # Put the prepopulated site into test ZODB and prepare test
+        # browser
+        self.getRootFolder()['app'] = app
+        setSite(app)
+        self.browser = Browser()
+        self.browser.handleErrors = False
+
+    def tearDown(self):
+        super(CustomPackageBrowserTests, self).tearDown()
+        clearSite()
+        shutil.rmtree(self.dc_root)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/configuration.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/configuration.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/configuration.py	(revision 11832)
@@ -0,0 +1,47 @@
+## $Id$
+##
+## Copyright (C) 2011 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 for session configuration objects.
+"""
+import grok
+from zope.interface import implementedBy
+from waeup.kofa.utils.helpers import attrs_to_fields
+from waeup.kofa.configuration import (
+    SessionConfigurationFactory, SessionConfiguration)
+from kofacustom.pcn.interfaces import (
+    ICustomSessionConfiguration, ICustomSessionConfigurationAdd)
+
+
+class CustomSessionConfiguration(SessionConfiguration):
+    """
+    Customized session configuration model
+    """
+
+    grok.implements(ICustomSessionConfiguration, ICustomSessionConfigurationAdd)
+
+CustomSessionConfiguration = attrs_to_fields(CustomSessionConfiguration)
+
+class CustomSessionConfigurationFactory(SessionConfigurationFactory):
+    """A customized factory for session configuration objects.
+    """
+
+    def __call__(self, *args, **kw):
+        return CustomSessionConfiguration(*args, **kw)
+
+    def getInterfaces(self):
+        return implementedBy(CustomSessionConfiguration)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/configure.zcml
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/configure.zcml	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/configure.zcml	(revision 11832)
@@ -0,0 +1,8 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:grok="http://namespaces.zope.org/grok">
+  <include package="kofacustom.pcn" file="locales.zcml" />
+
+  <include package="waeup.kofa" />
+  <grok:grok package="." />
+
+</configure>
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/ftesting.zcml
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/ftesting.zcml	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/ftesting.zcml	(revision 11832)
@@ -0,0 +1,67 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:kofa="http://namespaces.waeup.org/kofa"
+   i18n_domain="kofacustom.pcn"
+   package="kofacustom.pcn"
+   >
+
+  <include package="kofacustom.pcn" />
+  <include package="kofacustom.pcn" file="mail.zcml"/>
+
+  <!-- Where should the datacenter reside by default? -->
+  <kofa:datacenter
+      path="../../../parts/test/datacenter" />
+
+  <!-- Typical functional testing security setup -->
+  <securityPolicy
+      component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy"
+      />
+
+  <unauthenticatedPrincipal
+      id="zope.anybody"
+      title="Unauthenticated User"
+      />
+  <unauthenticatedGroup id="zope.Anybody"
+      title="Unauthenticated Users" />
+  <authenticatedGroup id="zope.Authenticated"
+      title="Authenticated Users" />
+  <everybodyGroup id="zope.Everybody"
+      title="All Users" />
+
+  <!-- grant
+      permission="zope.View"
+      principal="zope.Anybody"
+      / -->
+  <grant
+      permission="zope.app.dublincore.view"
+      principal="zope.Anybody" />
+  <grant
+      permission="zope.View"
+      principal="zope.Authenticated"
+      />
+  <grant
+      permission="zope.app.dublincore.view"
+      principal="zope.Authenticated"
+      />
+  <grant
+      permission="waeup.Public"
+      principal="zope.Everybody"
+      />
+  <grant 
+      permission="waeup.Anonymous"
+      principal="zope.Anybody"
+      />
+
+
+  <principal
+      id="zope.mgr"
+      title="Manager"
+      login="mgr"
+      password="mgrpw"
+      />
+
+  <role id="zope.Manager" title="Site Manager" />
+  <grantAll role="zope.Manager" />
+  <grant role="zope.Manager" principal="zope.mgr" />
+
+</configure>
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/interfaces.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/interfaces.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/interfaces.py	(revision 11832)
@@ -0,0 +1,92 @@
+## $Id$
+##
+## Copyright (C) 2011 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 zope.i18nmessageid
+from zope import schema
+from zc.sourcefactory.basic import BasicSourceFactory
+from waeup.kofa.interfaces import (SimpleKofaVocabulary,
+    ISessionConfiguration, academic_sessions_vocab)
+
+_ = MessageFactory = zope.i18nmessageid.MessageFactory('kofacustom.pcn')
+
+# It's recommended to replicate all fields from the base package here.
+class ICustomSessionConfiguration(ISessionConfiguration):
+    """A session configuration object.
+    """
+
+    # Base fees, do not remove.
+
+    school_fee_base = schema.Float(
+        title = _(u'School Fee (ignored)'),
+        default = 0.0,
+        required = False,
+        )
+
+    application_fee = schema.Float(
+        title = _(u'Application Fee (fallback)'),
+        default = 0.0,
+        required = False,
+        )
+
+    clearance_fee = schema.Float(
+        title = _(u'Clearance Fee (ignored)'),
+        default = 0.0,
+        required = False,
+        )
+
+    booking_fee = schema.Float(
+        title = _(u'Booking Fee'),
+        default = 0.0,
+        required = False,
+        )
+
+    maint_fee = schema.Float(
+        title = _(u'Maintenance Fee (ignored)'),
+        default = 0.0,
+        required = False,
+        )
+
+    transcript_fee = schema.Float(
+        title = _(u'Transcript Fee'),
+        default = 0.0,
+        required = False,
+        )
+
+    # Additional fees in custom package add here
+
+
+    def getSessionString():
+        """Returns the session string from the vocabulary.
+        """
+
+
+class ICustomSessionConfigurationAdd(ICustomSessionConfiguration):
+    """A session configuration object in add mode.
+    """
+
+    academic_session = schema.Choice(
+        title = _(u'Academic Session'),
+        source = academic_sessions_vocab,
+        default = None,
+        required = True,
+        readonly = False,
+        )
+
+ICustomSessionConfigurationAdd[
+    'academic_session'].order =  ICustomSessionConfiguration[
+    'academic_session'].order
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/locales.zcml
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/locales.zcml	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/locales.zcml	(revision 11832)
@@ -0,0 +1,8 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:i18n="http://namespaces.zope.org/i18n">
+
+  <include package="zope.i18n" file="meta.zcml" />
+  <i18n:registerTranslations directory="locales" />
+  <include package="waeup.kofa" file="locales.zcml" />
+
+</configure>
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/locales/en/LC_MESSAGES/waeup.kofa.po
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/locales/en/LC_MESSAGES/waeup.kofa.po	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/locales/en/LC_MESSAGES/waeup.kofa.po	(revision 11832)
@@ -0,0 +1,14 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: \n"
+"Last-Translator: Henrik Bettermann <henrik@waeup.org>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=iso-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Academics"
+msgstr "Institutions"
+
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/mail.zcml
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/mail.zcml	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/mail.zcml	(revision 11832)
@@ -0,0 +1,82 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:mail="http://namespaces.zope.org/mail">
+  <!--
+
+   SMTP Settings
+   *************
+
+   In here we define the mailers and mail deliveries we want to be
+   used by the portal.
+
+   They are set here (and not dynamically in the portal) to increase
+   security. Even portal users with all permissions cannot change mail
+   server credentials and related. You need filesystem access to
+   change/override the settings in here.
+
+   In the portal you can pick one of the deliveries for use with all
+   SMTP (aka mail) related functionality.
+
+   Each delivery (direct or queued) is bound to a certain mailer,
+   defined by a 'mail:smtpMailer' directive.
+
+   The smtpMailer directives define real mail hosts (local or remote)
+   where mail from the portal should be processed.
+
+   The 'mail:directDelivery' and 'mail:queuedDelivery' directives send
+   local mail to the mailer set in their 'mailer' attribute. While
+   directDelivery sends mail immediately when a request is finished
+   (see zope.sendmail for more info), queuedDelivery handles mail in a
+   background thread and sends mail independently from requests.
+
+   By default in Kofa we have a fake mailer activated, that does not
+   send real mail to the outside world.
+  -->
+
+  <!-- A mail server on localhost -->
+  <!-- Mail server definitions require the following attributes:
+
+        - name: (string) name to be used by deliveries
+
+       Mail server definitions allow the following additional attribs:
+
+        - hostname   (string) hostname or IP(?) of mail server to use
+                              ('localhost' by default)
+        - port       (int number) port to connect to SMTP mailer
+                              (25 by default)
+        - username   (string) Username to log in to mail server
+        - password   (string) Password to log in to mail server
+
+  -->
+  <mail:smtpMailer
+      name="smtp.mailserver"
+      hostname="localhost"
+      port="25"
+      />
+
+  <!-- The mail deliveries -->
+  <!-- Mail deliveries require the following attributes:
+
+        - name:       (string) this string will appear in portal select
+                               boxes
+        - permission: (string) zope default perms only
+        - mailer:     (string) name of some mailer registered above or
+                               'smtp'
+        - queuePath:  (string) must exist and contain cur/, new/, and
+                               tmp/ dirs. Relative paths are relative
+                               to the directory this files lives in.
+
+   -->
+  <mail:queuedDelivery
+      name="Queued SMTP on mail server"
+      permission="zope.Public"
+      mailer="smtp.mailserver"
+      queuePath="../../../var/mailqueue"
+      />
+
+  <mail:directDelivery
+      name="Direct SMTP on mail server"
+      permission="zope.Public"
+      mailer="smtp.mailserver"
+      />
+
+</configure>
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/meta.zcml
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/meta.zcml	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/meta.zcml	(revision 11832)
@@ -0,0 +1,12 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:meta="http://namespaces.zope.org/meta"
+    xmlns:grok="http://namespaces.zope.org/grok">
+
+  <include package="grok" file="meta.zcml" />
+  <!-- include package="z3c.autoinclude" file="meta.zcml" / -->
+
+  <!-- Load the grokkers -->
+  <grok:grok package=".meta" />
+
+</configure>
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/payments/__init__.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/payments/__init__.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/payments/__init__.py	(revision 11832)
@@ -0,0 +1,3 @@
+"""This package contains everything regarding payments.
+"""
+# Make this a package.
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/payments/interfaces.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/payments/interfaces.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/payments/interfaces.py	(revision 11832)
@@ -0,0 +1,26 @@
+## $Id$
+##
+## Copyright (C) 2011 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.payments.interfaces import IOnlinePayment
+from kofacustom.pcn.interfaces import MessageFactory as _
+
+class ICustomOnlinePayment(IOnlinePayment):
+    """A payment via payment gateways.
+
+    """
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/permissions.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/permissions.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/permissions.py	(revision 11832)
@@ -0,0 +1,30 @@
+## $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
+##
+import grok
+from waeup.kofa.permissions import CCOfficer
+
+
+class CustomCCOfficer(CCOfficer):
+    """ Permissions of Computer Center members.
+
+    This is basically a copy of the the PortalManager. We exclude some
+    permissions by commenting them out.
+    """
+
+    grok.name('waeup.CCOfficer')
+    grok.title(u'Our CC Officer')
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/__init__.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/__init__.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/__init__.py	(revision 11832)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/batching.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/batching.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/batching.py	(revision 11832)
@@ -0,0 +1,44 @@
+## $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
+##
+"""Batch processing components for student objects.
+
+Batch processors eat CSV files to add, update or remove large numbers
+of certain kinds of objects at once.
+
+Here we define the processors for students specific objects like
+students, studycourses, payment tickets and accommodation tickets.
+"""
+from kofacustom.pcn.students.interfaces import (
+    ICustomStudent, ICustomStudentUpdateByRegNo,
+    ICustomStudentUpdateByMatricNo,
+    ICustomStudentOnlinePayment
+    )
+from waeup.kofa.students.batching import (
+    StudentProcessor, StudentOnlinePaymentProcessor)
+
+class CustomStudentProcessor(StudentProcessor):
+    """A batch processor for ICustomStudent objects.
+    """
+    iface = ICustomStudent
+    iface_byregnumber = ICustomStudentUpdateByRegNo
+    iface_bymatricnumber = ICustomStudentUpdateByMatricNo
+
+class CustomStudentOnlinePaymentProcessor(StudentOnlinePaymentProcessor):
+    """A batch processor for ICustomStudentOnlinePayment objects.
+    """
+    iface = ICustomStudentOnlinePayment
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/browser.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/browser.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/browser.py	(revision 11832)
@@ -0,0 +1,38 @@
+## $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
+##
+import grok
+from zope.i18n import translate
+from zope.schema.interfaces import ConstraintNotSatisfied
+from zope.component import getUtility
+from hurry.workflow.interfaces import IWorkflowInfo
+from waeup.kofa.interfaces import REQUESTED, IExtFileStore, IKofaUtils
+from waeup.kofa.widgets.datewidget import FriendlyDatetimeDisplayWidget
+from waeup.kofa.browser.layout import KofaEditFormPage
+from waeup.kofa.browser.layout import action, jsaction
+from waeup.kofa.students.browser import (
+    StudyLevelEditFormPage, StudyLevelDisplayFormPage,
+    StudentBasePDFFormPage, ExportPDFCourseRegistrationSlipPage,
+    CourseTicketDisplayFormPage, StudentTriggerTransitionFormPage,
+    msave, emit_lock_message)
+from waeup.kofa.students.interfaces import IStudentsUtils, ICourseTicket
+from waeup.kofa.students.workflow import FORBIDDEN_POSTGRAD_TRANS
+from kofacustom.pcn.students.interfaces import (
+    ICustomStudentOnlinePayment, ICustomStudentStudyCourse,
+    ICustomStudentStudyLevel)
+from kofacustom.pcn.interfaces import MessageFactory as _
+
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/export.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/export.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/export.py	(revision 11832)
@@ -0,0 +1,75 @@
+## $Id$
+##
+## Copyright (C) 2011 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
+##
+"""Exporters for student related stuff.
+"""
+from kofacustom.pcn.students.interfaces import (
+    ICustomStudent,
+    ICustomStudentStudyCourse,
+    ICustomStudentStudyLevel,
+    ICustomCourseTicket,
+    ICustomStudentOnlinePayment)
+from waeup.kofa.students.export import (
+    StudentsExporter,
+    StudentStudyCourseExporter,
+    StudentStudyLevelExporter,
+    CourseTicketExporter,
+    StudentPaymentsExporter)
+from waeup.kofa.utils.helpers import iface_names
+
+class CustomStudentsExporter(StudentsExporter):
+    """Exporter for Students.
+    """
+
+    fields = tuple(sorted(iface_names(
+        ICustomStudent, omit=['loggerInfo']))) + (
+        'password', 'state', 'history', 'certcode', 'is_postgrad',
+        'current_level', 'current_session')
+
+class CustomStudentStudyCourseExporter(StudentStudyCourseExporter):
+    """Exporter for StudentStudyCourses.
+    """
+
+    fields = tuple(
+        sorted(iface_names(ICustomStudentStudyCourse))) + ('student_id',)
+
+class CustomStudentStudyLevelExporter(StudentStudyLevelExporter):
+    """Exporter for StudentStudyLevels.
+    """
+    #: Fieldnames considered by this exporter
+    fields = tuple(sorted(iface_names(
+        ICustomStudentStudyLevel) + ['level'])) + (
+        'student_id', 'number_of_tickets','certcode')
+
+class CustomCourseTicketExporter(CourseTicketExporter):
+    """Exporter for CourseTickets.
+    """
+
+    fields = tuple(sorted(iface_names(ICustomCourseTicket) +
+        ['level', 'code', 'level_session'])) + ('student_id',
+        'certcode', 'display_fullname')
+
+class CustomStudentPaymentsExporter(StudentPaymentsExporter):
+    """Exporter for OnlinePayment instances.
+    """
+
+    fields = tuple(
+        sorted(iface_names(
+            ICustomStudentOnlinePayment, exclude_attribs=False,
+            omit=['display_item']))) + (
+            'student_id','state','current_session')
+
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/interfaces.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/interfaces.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/interfaces.py	(revision 11832)
@@ -0,0 +1,100 @@
+## $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 import schema
+from waeup.kofa.students.interfaces import (
+    IStudentBase, IUGStudentClearance, IPGStudentClearance,
+    IStudentPersonal, IStudentStudyLevel,
+    IStudentStudyCourse, ICourseTicket,
+    IStudentUpdateByRegNo, IStudentUpdateByMatricNo,
+    )
+from kofacustom.pcn.payments.interfaces import ICustomOnlinePayment
+from kofacustom.pcn.interfaces import MessageFactory as _
+
+class ICustomStudentBase(IStudentBase):
+    """Representation of student base data.
+
+    """
+
+class ICustomStudentPersonal(IStudentPersonal):
+    """Student personal data.
+
+    """
+
+class ICustomUGStudentClearance(IUGStudentClearance):
+    """Representation of ug student clearance data.
+
+    """
+
+class ICustomPGStudentClearance(IPGStudentClearance):
+    """Representation of pg student clearance data.
+
+    """
+
+
+class ICustomStudent(ICustomStudentBase,ICustomUGStudentClearance,
+    ICustomPGStudentClearance,ICustomStudentPersonal):
+    """Representation of a student.
+
+    """
+
+class ICustomStudentStudyCourse(IStudentStudyCourse):
+    """A container for student study levels.
+
+    """
+
+class ICustomStudentStudyLevel(IStudentStudyLevel):
+    """A container for course tickets.
+
+    """
+
+class ICustomStudentOnlinePayment(ICustomOnlinePayment):
+    """A student payment via payment gateways.
+
+    This Interface does not inherit from IStudentOnlinePayment.
+    Thus all fields from IStudentOnlinePayment have to be repeated here.
+    """
+
+    p_current = schema.Bool(
+        title = _(u'Current Session Payment'),
+        default = True,
+        required = False,
+        )
+
+    p_level = schema.Int(
+        title = _(u'Payment Level'),
+        required = False,
+        )
+
+ICustomStudentOnlinePayment['p_level'].order = ICustomStudentOnlinePayment[
+    'p_session'].order
+
+class ICustomCourseTicket(ICourseTicket):
+    """A course ticket.
+
+    """
+
+class ICustomStudentUpdateByRegNo(IStudentUpdateByRegNo):
+    """Representation of a student. Skip regular reg_number validation.
+
+    """
+
+class ICustomStudentUpdateByMatricNo(IStudentUpdateByMatricNo):
+    """Representation of a student. Skip regular matric_number validation.
+
+    """
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/payments.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/payments.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/payments.py	(revision 11832)
@@ -0,0 +1,60 @@
+## $Id$
+##
+## Copyright (C) 2011 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
+##
+"""
+Student payment components.
+"""
+import grok
+from zope.component.interfaces import IFactory
+from zope.interface import implementedBy
+from waeup.kofa.students.interfaces import IStudentNavigation
+from waeup.kofa.students.payments import (
+    StudentOnlinePayment, StudentOnlinePaymentFactory)
+from waeup.kofa.utils.helpers import attrs_to_fields
+from kofacustom.pcn.students.interfaces import ICustomStudentOnlinePayment
+
+class CustomStudentOnlinePayment(StudentOnlinePayment):
+    """This is a custom online payment for students.
+
+    Since this class inherits from StudentOnlinePayment, it automatically
+    implements the interfaces of the parent class which means
+    all viewlets and views meant for StudentOnlinePayment
+    can also be accessed in the customized system.
+    """
+    grok.implements(ICustomStudentOnlinePayment, IStudentNavigation)
+    grok.provides(ICustomStudentOnlinePayment)
+
+    def __init__(self):
+        super(CustomStudentOnlinePayment, self).__init__()
+        return
+
+    @property
+    def student(self):
+        return self.__parent__.__parent__
+
+CustomStudentOnlinePayment = attrs_to_fields(
+    CustomStudentOnlinePayment, omit=['display_item'])
+
+class CustomStudentOnlinePaymentFactory(StudentOnlinePaymentFactory):
+    """A factory for student online payments.
+    """
+
+    def __call__(self, *args, **kw):
+        return CustomStudentOnlinePayment()
+
+    def getInterfaces(self):
+        return implementedBy(CustomStudentOnlinePayment)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/student.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/student.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/student.py	(revision 11832)
@@ -0,0 +1,60 @@
+## $Id$
+##
+## Copyright (C) 2011 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
+##
+"""
+Container for the various objects owned by students.
+"""
+import grok
+from zope.interface import implementedBy
+from waeup.kofa.utils.helpers import attrs_to_fields
+from waeup.kofa.students.student import StudentFactory
+from waeup.kofa.students.interfaces import IStudentNavigation
+from waeup.kofa.utils.helpers import get_current_principal
+from waeup.kofa.students.student import Student
+from kofacustom.pcn.students.interfaces import ICustomStudent
+
+
+class CustomStudent(Student):
+    """This is a student container for the various objects
+    owned by students.
+    """
+    grok.implements(ICustomStudent, IStudentNavigation)
+    grok.provides(ICustomStudent)
+
+    @property
+    def transcript_enabled(self):
+        user = get_current_principal()
+        if user.id in ('admin'):
+            return True
+        return False
+
+
+# Set all attributes of Student required in IStudent as field
+# properties. Doing this, we do not have to set initial attributes
+# ourselves and as a bonus we get free validation when an attribute is
+# set.
+CustomStudent = attrs_to_fields(CustomStudent)
+
+class CustomStudentFactory(StudentFactory):
+    """A factory for students.
+    """
+
+    def __call__(self, *args, **kw):
+        return CustomStudent()
+
+    def getInterfaces(self):
+        return implementedBy(CustomStudent)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/studycourse.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/studycourse.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/studycourse.py	(revision 11832)
@@ -0,0 +1,47 @@
+## $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
+##
+"""
+Container which holds the data of the student study courses
+and contains the (student) study level objects.
+"""
+import grok
+from zope.interface import implementedBy
+from waeup.kofa.students.studycourse import StudentStudyCourseFactory
+from waeup.kofa.utils.helpers import attrs_to_fields
+from waeup.kofa.students.interfaces import IStudentNavigation
+from waeup.kofa.students.workflow import CLEARED, RETURNING, PAID
+from waeup.kofa.students.studycourse import StudentStudyCourse
+from kofacustom.pcn.students.interfaces import ICustomStudentStudyCourse
+
+class CustomStudentStudyCourse(StudentStudyCourse):
+    """This is a container for study levels.
+    """
+    grok.implements(ICustomStudentStudyCourse, IStudentNavigation)
+    grok.provides(ICustomStudentStudyCourse)
+
+CustomStudentStudyCourse = attrs_to_fields(CustomStudentStudyCourse)
+
+class CustomStudentStudyCourseFactory(StudentStudyCourseFactory):
+    """A factory for student study courses.
+    """
+
+    def __call__(self, *args, **kw):
+        return CustomStudentStudyCourse()
+
+    def getInterfaces(self):
+        return implementedBy(CustomStudentStudyCourse)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/studylevel.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/studylevel.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/studylevel.py	(revision 11832)
@@ -0,0 +1,77 @@
+## $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
+##
+"""
+Container which holds the data of a student study level
+and contains the course tickets.
+"""
+import grok
+from zope.component.interfaces import IFactory
+from zope.interface import implementedBy
+from waeup.kofa.utils.helpers import attrs_to_fields
+from waeup.kofa.students.studylevel import (
+    StudentStudyLevel, CourseTicket,
+    CourseTicketFactory, StudentStudyLevelFactory)
+from waeup.kofa.students.interfaces import IStudentNavigation
+from kofacustom.pcn.students.interfaces import (
+    ICustomStudentStudyLevel, ICustomCourseTicket)
+
+
+class CustomStudentStudyLevel(StudentStudyLevel):
+    """This is a container for course tickets.
+    """
+    grok.implements(ICustomStudentStudyLevel, IStudentNavigation)
+    grok.provides(ICustomStudentStudyLevel)
+
+CustomStudentStudyLevel = attrs_to_fields(
+    CustomStudentStudyLevel, omit=['total_credits', 'gpa'])
+
+class CustomStudentStudyLevelFactory(StudentStudyLevelFactory):
+    """A factory for student study levels.
+    """
+
+    def __call__(self, *args, **kw):
+        return CustomStudentStudyLevel()
+
+    def getInterfaces(self):
+        return implementedBy(CustomStudentStudyLevel)
+
+class CustomCourseTicket(CourseTicket):
+    """This is a course ticket which allows the
+    student to attend the course. Lecturers will enter scores and more at
+    the end of the term.
+
+    A course ticket contains a copy of the original course and
+    course referrer data. If the courses and/or their referrers are removed, the
+    corresponding tickets remain unchanged. So we do not need any event
+    triggered actions on course tickets.
+    """
+    grok.implements(ICustomCourseTicket, IStudentNavigation)
+    grok.provides(ICustomCourseTicket)
+
+
+CustomCourseTicket = attrs_to_fields(CustomCourseTicket)
+
+class CustomCourseTicketFactory(CourseTicketFactory):
+    """A factory for student study levels.
+    """
+
+    def __call__(self, *args, **kw):
+        return CustomCourseTicket()
+
+    def getInterfaces(self):
+        return implementedBy(CustomCourseTicket)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/tests/__init__.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/tests/__init__.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/tests/__init__.py	(revision 11832)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/tests/test_browser.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/tests/test_browser.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/tests/test_browser.py	(revision 11832)
@@ -0,0 +1,37 @@
+## $Id$
+##
+## Copyright (C) 2011 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 os
+import shutil
+import tempfile
+from StringIO import StringIO
+from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
+from zope.securitypolicy.interfaces import IPrincipalRoleManager
+from zope.component.hooks import setSite, clearSite
+from zope.component import getUtility, createObject
+from zope.interface import verify
+from zope.event import notify
+from waeup.kofa.authentication import LocalRoleSetEvent
+from waeup.kofa.app import University
+from waeup.kofa.students.tests.test_browser import StudentsFullSetup
+from waeup.kofa.students.accommodation import BedTicket
+from waeup.kofa.testing import FunctionalTestCase
+from waeup.kofa.interfaces import (
+    IExtFileStore, IFileStoreNameChooser)
+from waeup.kofa.students.interfaces import IStudentsUtils
+from kofacustom.pcn.testing import FunctionalLayer
+
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/tests/test_export.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/tests/test_export.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/tests/test_export.py	(revision 11832)
@@ -0,0 +1,97 @@
+import datetime
+from zope.component import queryUtility
+from zope.interface.verify import verifyObject, verifyClass
+from waeup.kofa.interfaces import ICSVExporter
+from waeup.kofa.schoolgrades import ResultEntry
+from waeup.kofa.students.tests.test_batching import StudentImportExportSetup
+from kofacustom.pcn.utils.utils import CustomKofaUtils
+from kofacustom.pcn.students.export import (
+    CustomStudentsExporter, CustomStudentPaymentsExporter)
+from kofacustom.pcn.testing import FunctionalLayer
+
+
+class CustomStudentsExporterTest(StudentImportExportSetup):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(CustomStudentsExporterTest, self).setUp()
+        self.setup_for_export()
+        result_entry = ResultEntry(
+            CustomKofaUtils.EXAM_SUBJECTS_DICT.keys()[0],
+            CustomKofaUtils.EXAM_GRADES[0][0]
+            )
+        self.student.alr_results = [
+            result_entry]
+        self.student.fst_sit_results = [
+            result_entry]
+        self.student.scd_sit_results = [
+            result_entry]
+        return
+
+    def test_ifaces(self):
+        # make sure we fullfill interface contracts
+        obj = CustomStudentsExporter()
+        verifyObject(ICSVExporter, obj)
+        verifyClass(ICSVExporter, CustomStudentsExporter)
+        return
+
+
+    def test_export_all(self):
+        # we can really export students
+        # set values we can expect in export file
+        self.setup_student(self.student)
+        exporter = CustomStudentsExporter()
+        exporter.export_all(self.app, self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertMatches(
+            'adm_code,clearance_locked,clr_code,date_of_birth,email,'
+            'employer,firstname,lastname,matric_number,middlename,'
+            'nationality,officer_comment,perm_address,personal_updated,'
+            'phone,reg_number,sex,student_id,suspended,suspended_comment,'
+            'password,state,history,certcode,is_postgrad,current_level,'
+            'current_session\r\nmy adm code,0,my clr code,1981-02-04#,'
+            'anna@sample.com,,Anna,Tester,234,M.,NG,,'
+            '"Studentroad 21\nLagos 123456\n",,+234-123-12345#,123,'
+            'f,A111111,0,,,created,'
+            '[u\'2014-06-16 08:23:55 UTC - Record created by system\'],'
+            'CERT1,0,200,2012\r\n',
+            result
+            )
+        return
+
+class CustomStudentPaymentsExporterTest(StudentImportExportSetup):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(CustomStudentPaymentsExporterTest, self).setUp()
+        self.setup_for_export()
+        return
+
+    def test_ifaces(self):
+        # make sure we fullfill interface contracts
+        obj = CustomStudentPaymentsExporter()
+        verifyObject(ICSVExporter, obj)
+        verifyClass(ICSVExporter, CustomStudentPaymentsExporter)
+        return
+
+    def test_export_all(self):
+        # we can really export students
+        # set values we can expect in export file
+        self.setup_student(self.student)
+        self.student['payments']['my-payment'].r_company = 'interswatch'
+        self.student['payments']['my-payment'].r_card_num = '789'
+        exporter = CustomStudentPaymentsExporter()
+        exporter.export_all(self.app, self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertMatches(
+            'ac,amount_auth,creation_date,p_category,p_current,p_id,'
+            'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,'
+            'r_code,r_desc,student_id,state,current_session\r\n666,'
+            '12.12,2012-04-01 13:12:01,schoolfee,1,my-id,p-item,'
+            '100,2012,paid,2012-04-01 14:12:01,12.12,r-code,,'
+            'A111111,created,2012\r\n',
+            result
+            )
+        return
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/tests/test_student.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/tests/test_student.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/tests/test_student.py	(revision 11832)
@@ -0,0 +1,127 @@
+## $Id$
+##
+## Copyright (C) 2011 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 students and related.
+"""
+from datetime import tzinfo
+from zope.component.interfaces import IFactory
+from zope.interface import verify
+from waeup.kofa.testing import FunctionalTestCase
+from kofacustom.pcn.students.student import CustomStudent, CustomStudentFactory
+from kofacustom.pcn.students.studycourse import (
+    CustomStudentStudyCourse, CustomStudentStudyCourseFactory)
+from kofacustom.pcn.students.studylevel import (
+    CustomStudentStudyLevel, CustomCourseTicket,
+    CustomStudentStudyLevelFactory,
+    CustomCourseTicketFactory)
+from kofacustom.pcn.students.interfaces import (
+    ICustomStudent, ICustomStudentStudyCourse,
+    ICustomStudentStudyLevel, ICustomCourseTicket)
+from kofacustom.pcn.testing import FunctionalLayer
+
+
+class CustomStudentFactoryTest(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(CustomStudentFactoryTest, self).setUp()
+        self.factory = CustomStudentFactory()
+
+    def tearDown(self):
+        super(CustomStudentFactoryTest, self).tearDown()
+
+    def test_interfaces(self):
+        verify.verifyClass(IFactory, CustomStudentFactory)
+        verify.verifyObject(IFactory, self.factory)
+
+    def test_factory(self):
+        obj = self.factory()
+        assert isinstance(obj, CustomStudent)
+
+    def test_getInterfaces(self):
+        implemented_by = self.factory.getInterfaces()
+        assert implemented_by.isOrExtends(ICustomStudent)
+
+class CustomStudentStudyCourseFactoryTest(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(CustomStudentStudyCourseFactoryTest, self).setUp()
+        self.factory = CustomStudentStudyCourseFactory()
+
+    def tearDown(self):
+        super(CustomStudentStudyCourseFactoryTest, self).tearDown()
+
+    def test_interfaces(self):
+        verify.verifyClass(IFactory, CustomStudentStudyCourseFactory)
+        verify.verifyObject(IFactory, self.factory)
+
+    def test_factory(self):
+        obj = self.factory()
+        assert isinstance(obj, CustomStudentStudyCourse)
+
+    def test_getInterfaces(self):
+        implemented_by = self.factory.getInterfaces()
+        assert implemented_by.isOrExtends(ICustomStudentStudyCourse)
+
+class CustomStudentStudyLevelFactoryTest(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(CustomStudentStudyLevelFactoryTest, self).setUp()
+        self.factory = CustomStudentStudyLevelFactory()
+
+    def tearDown(self):
+        super(CustomStudentStudyLevelFactoryTest, self).tearDown()
+
+    def test_interfaces(self):
+        verify.verifyClass(IFactory, CustomStudentStudyLevelFactory)
+        verify.verifyObject(IFactory, self.factory)
+
+    def test_factory(self):
+        obj = self.factory()
+        assert isinstance(obj, CustomStudentStudyLevel)
+
+    def test_getInterfaces(self):
+        implemented_by = self.factory.getInterfaces()
+        assert implemented_by.isOrExtends(ICustomStudentStudyLevel)
+
+class CustomCourseTicketFactoryTest(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(CustomCourseTicketFactoryTest, self).setUp()
+        self.factory = CustomCourseTicketFactory()
+
+    def tearDown(self):
+        super(CustomCourseTicketFactoryTest, self).tearDown()
+
+    def test_interfaces(self):
+        verify.verifyClass(IFactory, CustomCourseTicketFactory)
+        verify.verifyObject(IFactory, self.factory)
+
+    def test_factory(self):
+        obj = self.factory()
+        assert isinstance(obj, CustomCourseTicket)
+
+    def test_getInterfaces(self):
+        implemented_by = self.factory.getInterfaces()
+        assert implemented_by.isOrExtends(ICustomCourseTicket)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/utils.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/utils.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/utils.py	(revision 11832)
@@ -0,0 +1,31 @@
+## $Id$
+##
+## Copyright (C) 2011 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 time import time
+from zope.component import createObject, getUtility
+from waeup.kofa.interfaces import (IKofaUtils,
+    CLEARED, RETURNING, PAID, REGISTERED, VALIDATED)
+from waeup.kofa.students.utils import StudentsUtils
+from kofacustom.pcn.interfaces import MessageFactory as _
+
+class CustomStudentsUtils(StudentsUtils):
+    """A collection of customized methods.
+
+    """
+
+    # refix
+    STUDENT_ID_PREFIX = u'X'
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/viewlets.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/viewlets.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/students/viewlets.py	(revision 11832)
@@ -0,0 +1,30 @@
+## $Id$
+##
+## Copyright (C) 2011 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.interfaces import REQUESTED
+from waeup.kofa.browser.viewlets import ManageActionButton
+from kofacustom.pcn.students.interfaces import (
+    ICustomStudentStudyCourse, ICustomStudentStudyLevel)
+from waeup.kofa.students.viewlets import (
+    FileDisplay, FileUpload, Image)
+from waeup.kofa.students.browser import (
+    ExportPDFClearanceSlipPage, StudyCourseDisplayFormPage,
+    StudyLevelDisplayFormPage)
+
+from waeup.kofa.interfaces import MessageFactory as _
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/testing.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/testing.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/testing.py	(revision 11832)
@@ -0,0 +1,27 @@
+## $Id$
+##
+## Copyright (C) 2011 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
+##
+"""Testing support for custom package.
+"""
+import os.path
+import kofacustom.pcn
+from zope.app.testing.functional import ZCMLLayer
+
+ftesting_zcml = os.path.join(
+    os.path.dirname(kofacustom.pcn.__file__), 'ftesting.zcml')
+FunctionalLayer = ZCMLLayer(ftesting_zcml, __name__, 'FunctionalLayer',
+                            allow_teardown=True)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/tests/__init__.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/tests/__init__.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/tests/__init__.py	(revision 11832)
@@ -0,0 +1,2 @@
+# This is a package
+
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/tests/test_configuration.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/tests/test_configuration.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/tests/test_configuration.py	(revision 11832)
@@ -0,0 +1,76 @@
+## $Id$
+##
+## Copyright (C) 2011 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 configuration containers and related.
+"""
+from zope.component.interfaces import IFactory
+from zope.interface import verify
+from waeup.kofa.testing import FunctionalTestCase
+from waeup.kofa.configuration import ConfigurationContainer
+from waeup.kofa.interfaces import IConfigurationContainer
+from waeup.kofa.tests.test_configuration import (
+    ConfigurationTest, SessionConfigurationFactoryTest)
+from kofacustom.pcn.configuration import (
+    CustomSessionConfiguration, CustomSessionConfigurationFactory)
+from kofacustom.pcn.interfaces import(
+    ICustomSessionConfiguration, ICustomSessionConfigurationAdd)
+from kofacustom.pcn.testing import FunctionalLayer
+
+class ConfigurationTest(ConfigurationTest):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(ConfigurationTest, self).setUp()
+        self.confcontainer = ConfigurationContainer()
+        self.sessionconf = CustomSessionConfiguration()
+        return
+
+    #def tearDown(self):
+    #    super(ConfigurationTest, self).tearDown()
+    #    return
+
+    def test_interfaces(self):
+        #verify.verifyClass(IConfigurationContainer, ConfigurationContainer)
+        #verify.verifyObject(IConfigurationContainer, self.confcontainer)
+        verify.verifyClass(ICustomSessionConfiguration, CustomSessionConfiguration)
+        verify.verifyObject(ICustomSessionConfiguration, self.sessionconf)
+        verify.verifyClass(ICustomSessionConfigurationAdd, CustomSessionConfiguration)
+        verify.verifyObject(ICustomSessionConfigurationAdd, self.sessionconf)
+
+class SessionConfigurationFactoryTest(SessionConfigurationFactoryTest):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(SessionConfigurationFactoryTest, self).setUp()
+        self.factory = CustomSessionConfigurationFactory()
+
+    #def tearDown(self):
+    #    super(SessionConfigurationFactoryTest, self).tearDown()
+
+    def test_interfaces(self):
+        verify.verifyClass(IFactory, CustomSessionConfigurationFactory)
+        verify.verifyObject(IFactory, self.factory)
+
+    def test_factory(self):
+        obj = self.factory()
+        assert isinstance(obj, CustomSessionConfiguration)
+
+    def test_getInterfaces(self):
+        implemented_by = self.factory.getInterfaces()
+        assert implemented_by.isOrExtends(ICustomSessionConfiguration)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/tests/test_datacenter.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/tests/test_datacenter.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/tests/test_datacenter.py	(revision 11832)
@@ -0,0 +1,30 @@
+## $Id$
+##
+## Copyright (C) 2011 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 waeup.kofa.datacenter import DataCenter
+from waeup.kofa.testing import FunctionalTestCase
+from kofacustom.pcn.testing import FunctionalLayer
+
+
+class DatacenterTests(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def test_storage_path(self):
+        mydatacenter = DataCenter()
+        storagepath = mydatacenter.storage
+        self.assertTrue('/parts/test/datacenter' in storagepath)
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/tests/test_smtp.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/tests/test_smtp.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/tests/test_smtp.py	(revision 11832)
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+## $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 email-related components
+import base64
+import logging
+import tempfile
+import shutil
+import unittest
+from StringIO import StringIO
+from zope.component import getUtility
+from zope.component.hooks import setSite
+from zope.sendmail.interfaces import IMailDelivery
+from waeup.kofa.app import University
+from waeup.kofa.interfaces import IMailService
+from waeup.kofa.smtp import (
+    encode_header_item, encode_address, encode_body, FakeSMTPDelivery,
+    send_mail)
+from waeup.kofa.testing import FunctionalTestCase
+from kofacustom.pcn.testing import FunctionalLayer
+
+#
+# SMTP test config
+#
+
+# Also run tests that send mail to external servers?
+#   If you enable this, please make sure the external smtp hosts and receivers
+#   do exist really and are not bothered by being spammed by a test programme.
+EXTERNAL_MAIL_TESTS = False
+
+# Maybe existing receiver of externally sent mail. If this mail account
+# exists really, it might receive mail from the tests!
+EXTERNAL_MAIL_RECEIVER = 'existing_address@waeup.org'
+
+# Names of mail deliveries to use when external mail tests are enabled.
+#   See local mail.zcml for names of available deliveries.
+EXTERNAL_DIRECT_DELIVERY = 'Direct SMTP on mail server'
+EXTERNAL_QUEUED_DELIVERY = 'Queued SMTP on mail server'
+
+#
+# end of SMTP test config
+#
+
+def external_mail_test(func):
+    if not EXTERNAL_MAIL_TESTS:
+        myself = __file__
+        if myself.endswith('.pyc'):
+            myself = myself[:-2]
+        print "WARNING: external mail tests are skipped!"
+        print "WARNING: edit %s to enable them." % myself
+        return
+    return func
+
+class ExternalMailerTests(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(ExternalMailerTests, 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
+        self.app = self.getRootFolder()['app']
+        return
+
+    def tearDown(self):
+        super(ExternalMailerTests, self).tearDown()
+        shutil.rmtree(self.dc_root)
+        return
+
+    def test_config_default_mailer(self):
+        # The default mailer set in config is 'no mailer'.
+        self.assertEqual(
+            getattr(self.app.get('configuration', None), 'smtp_mailer'),
+            u'No email service')
+        return
+
+    @external_mail_test
+    def test_send_direct_mail(self):
+        # send mail using direct mail delivery
+        self.app['configuration'].smtp_mailer = EXTERNAL_DIRECT_DELIVERY
+        setSite(self.app)
+        result = send_mail(
+            'test program', 'no-reply@waeup.org',
+            'test mail receiver', EXTERNAL_MAIL_RECEIVER,
+            'Test Mail from WAeUP Kofa',
+            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
+            )
+        import transaction
+        transaction.commit() # The mail is really sent when transactions is
+                             # committed
+        self.assertTrue('@' in result)
+        return
+
+    @external_mail_test
+    def test_send_queued_mail(self):
+        # send mail using queued mail delivery
+        self.app['configuration'].smtp_mailer = EXTERNAL_QUEUED_DELIVERY
+        setSite(self.app)
+        result = send_mail(
+            'test program', 'no-reply@waeup.org',
+            'test mail receiver', EXTERNAL_MAIL_RECEIVER,
+            'Test Mail from WAeUP Kofa',
+            'Hi from test mailer!\n\nRegards,\nTest Programme\n'
+            )
+        import transaction
+        transaction.commit() # The mail is really sent when transactions is
+                             # committed
+        self.assertTrue('@' in result)
+        return
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/university/__init__.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/university/__init__.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/university/__init__.py	(revision 11832)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/utils/__init__.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/utils/__init__.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/utils/__init__.py	(revision 11832)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/utils/tests.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/utils/tests.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/utils/tests.py	(revision 11832)
@@ -0,0 +1,29 @@
+## $Id$
+##
+## Copyright (C) 2011 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 waeup.kofa.testing import FunctionalTestCase
+from kofacustom.pcn.testing import FunctionalLayer
+
+
+class CustomPackageUtilsTests(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def test_foo(self):
+        '''Test method foo.
+        '''
+        return
Index: /main/kofacustom.pcn/trunk/src/kofacustom/pcn/utils/utils.py
===================================================================
--- /main/kofacustom.pcn/trunk/src/kofacustom/pcn/utils/utils.py	(revision 11832)
+++ /main/kofacustom.pcn/trunk/src/kofacustom/pcn/utils/utils.py	(revision 11832)
@@ -0,0 +1,25 @@
+## $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
+##
+"""Customize general helper utilities for Kofa.
+"""
+from waeup.kofa.utils.utils import KofaUtils
+
+class CustomKofaUtils(KofaUtils):
+    """A collection of methods subject to customization.
+    """
+
