Index: /main/ikobacustom.uniben/trunk/CHANGES.txt
===================================================================
--- /main/ikobacustom.uniben/trunk/CHANGES.txt	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/CHANGES.txt	(revision 14174)
@@ -0,0 +1,8 @@
+Changes
+*******
+
+
+0.1 (never released)
+====================
+
+Initial version
Index: /main/ikobacustom.uniben/trunk/README.txt
===================================================================
--- /main/ikobacustom.uniben/trunk/README.txt	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/README.txt	(revision 14174)
@@ -0,0 +1,16 @@
+ikobacustom.skeleton
+********************
+
+Customizations of the `waeup.ikoba` package.
+
+For generic install instructions please see sources/waeup.ikoba/docs/INSTALL.txt.
+
+Bootstrap and buildout have to be run in two steps:
+
+  (py27)$ python bootstrap.py -c buildout-init.cfg
+
+  (py27)$ ./bin/buildout -c buildout-init.cfg
+
+  (py27)$ python bootstrap.py
+
+  (py27)$ ./bin/buildout
Index: /main/ikobacustom.uniben/trunk/bootstrap.py
===================================================================
--- /main/ikobacustom.uniben/trunk/bootstrap.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/bootstrap.py	(revision 14174)
@@ -0,0 +1,210 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+"""
+
+import os
+import shutil
+import sys
+import tempfile
+
+from optparse import OptionParser
+
+__version__ = '2015-07-01'
+# See zc.buildout's changelog if this version is up to date.
+
+tmpeggs = tempfile.mkdtemp(prefix='bootstrap-')
+
+usage = '''\
+[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
+
+Bootstraps a buildout-based project.
+
+Simply run this script in a directory containing a buildout.cfg, using the
+Python that you want bin/buildout to use.
+
+Note that by using --find-links to point to local resources, you can keep
+this script from going over the network.
+'''
+
+parser = OptionParser(usage=usage)
+parser.add_option("--version",
+                  action="store_true", default=False,
+                  help=("Return bootstrap.py version."))
+parser.add_option("-t", "--accept-buildout-test-releases",
+                  dest='accept_buildout_test_releases',
+                  action="store_true", default=False,
+                  help=("Normally, if you do not specify a --version, the "
+                        "bootstrap script and buildout gets the newest "
+                        "*final* versions of zc.buildout and its recipes and "
+                        "extensions for you.  If you use this flag, "
+                        "bootstrap and buildout will get the newest releases "
+                        "even if they are alphas or betas."))
+parser.add_option("-c", "--config-file",
+                  help=("Specify the path to the buildout configuration "
+                        "file to be used."))
+parser.add_option("-f", "--find-links",
+                  help=("Specify a URL to search for buildout releases"))
+parser.add_option("--allow-site-packages",
+                  action="store_true", default=False,
+                  help=("Let bootstrap.py use existing site packages"))
+parser.add_option("--buildout-version",
+                  help="Use a specific zc.buildout version")
+parser.add_option("--setuptools-version",
+                  help="Use a specific setuptools version")
+parser.add_option("--setuptools-to-dir",
+                  help=("Allow for re-use of existing directory of "
+                        "setuptools versions"))
+
+options, args = parser.parse_args()
+if options.version:
+    print("bootstrap.py version %s" % __version__)
+    sys.exit(0)
+
+
+######################################################################
+# load/install setuptools
+
+try:
+    from urllib.request import urlopen
+except ImportError:
+    from urllib2 import urlopen
+
+ez = {}
+if os.path.exists('ez_setup.py'):
+    exec(open('ez_setup.py').read(), ez)
+else:
+    exec(urlopen('https://bootstrap.pypa.io/ez_setup.py').read(), ez)
+
+if not options.allow_site_packages:
+    # ez_setup imports site, which adds site packages
+    # this will remove them from the path to ensure that incompatible versions
+    # of setuptools are not in the path
+    import site
+    # inside a virtualenv, there is no 'getsitepackages'.
+    # We can't remove these reliably
+    if hasattr(site, 'getsitepackages'):
+        for sitepackage_path in site.getsitepackages():
+            # Strip all site-packages directories from sys.path that
+            # are not sys.prefix; this is because on Windows
+            # sys.prefix is a site-package directory.
+            if sitepackage_path != sys.prefix:
+                sys.path[:] = [x for x in sys.path
+                               if sitepackage_path not in x]
+
+setup_args = dict(to_dir=tmpeggs, download_delay=0)
+
+if options.setuptools_version is not None:
+    setup_args['version'] = options.setuptools_version
+if options.setuptools_to_dir is not None:
+    setup_args['to_dir'] = options.setuptools_to_dir
+
+ez['use_setuptools'](**setup_args)
+import setuptools
+import pkg_resources
+
+# This does not (always?) update the default working set.  We will
+# do it.
+for path in sys.path:
+    if path not in pkg_resources.working_set.entries:
+        pkg_resources.working_set.add_entry(path)
+
+######################################################################
+# Install buildout
+
+ws = pkg_resources.working_set
+
+setuptools_path = ws.find(
+    pkg_resources.Requirement.parse('setuptools')).location
+
+# Fix sys.path here as easy_install.pth added before PYTHONPATH
+cmd = [sys.executable, '-c',
+       'import sys; sys.path[0:0] = [%r]; ' % setuptools_path +
+       'from setuptools.command.easy_install import main; main()',
+       '-mZqNxd', tmpeggs]
+
+find_links = os.environ.get(
+    'bootstrap-testing-find-links',
+    options.find_links or
+    ('http://downloads.buildout.org/'
+     if options.accept_buildout_test_releases else None)
+    )
+if find_links:
+    cmd.extend(['-f', find_links])
+
+requirement = 'zc.buildout'
+version = options.buildout_version
+if version is None and not options.accept_buildout_test_releases:
+    # Figure out the most recent final version of zc.buildout.
+    import setuptools.package_index
+    _final_parts = '*final-', '*final'
+
+    def _final_version(parsed_version):
+        try:
+            return not parsed_version.is_prerelease
+        except AttributeError:
+            # Older setuptools
+            for part in parsed_version:
+                if (part[:1] == '*') and (part not in _final_parts):
+                    return False
+            return True
+
+    index = setuptools.package_index.PackageIndex(
+        search_path=[setuptools_path])
+    if find_links:
+        index.add_find_links((find_links,))
+    req = pkg_resources.Requirement.parse(requirement)
+    if index.obtain(req) is not None:
+        best = []
+        bestv = None
+        for dist in index[req.project_name]:
+            distv = dist.parsed_version
+            if _final_version(distv):
+                if bestv is None or distv > bestv:
+                    best = [dist]
+                    bestv = distv
+                elif distv == bestv:
+                    best.append(dist)
+        if best:
+            best.sort()
+            version = best[-1].version
+if version:
+    requirement = '=='.join((requirement, version))
+cmd.append(requirement)
+
+import subprocess
+if subprocess.call(cmd) != 0:
+    raise Exception(
+        "Failed to execute command:\n%s" % repr(cmd)[1:-1])
+
+######################################################################
+# Import and run buildout
+
+ws.add_entry(tmpeggs)
+ws.require(requirement)
+import zc.buildout.buildout
+
+if not [a for a in args if '=' not in a]:
+    args.append('bootstrap')
+
+# if -c was provided, we push it back into args for buildout' main function
+if options.config_file is not None:
+    args[0:0] = ['-c', options.config_file]
+
+zc.buildout.buildout.main(args)
+shutil.rmtree(tmpeggs)
Index: /main/ikobacustom.uniben/trunk/buildout-init.cfg
===================================================================
--- /main/ikobacustom.uniben/trunk/buildout-init.cfg	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/buildout-init.cfg	(revision 14174)
@@ -0,0 +1,11 @@
+[buildout]
+# See http://pypi.python.org/pypi/mr.developer for details
+extensions = mr.developer
+auto-checkout = waeup.ikoba
+sources-dir = sources
+develop = .
+parts =
+
+[sources]
+waeup.ikoba = svn https://svn.waeup.org/repos/main/waeup.ikoba/trunk
+
Index: /main/ikobacustom.uniben/trunk/buildout.cfg
===================================================================
--- /main/ikobacustom.uniben/trunk/buildout.cfg	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/buildout.cfg	(revision 14174)
@@ -0,0 +1,217 @@
+[buildout]
+# See http://pypi.python.org/pypi/mr.developer for details
+extensions = mr.developer
+# Install waeup.ikoba as develop egg
+auto-checkout = waeup.ikoba
+sources-dir = sources
+develop = .
+parts =
+    eggbasket
+    app
+    i18n
+    test
+    mkdirs
+    zpasswd
+    zope_conf
+    site_zcml
+    zdaemon_conf
+    raw_deploy_ini
+    raw_debug_ini
+    themed_deploy_ini
+    themed_debug_ini
+    profile_ini
+# waeupdocs
+    coverage-detect
+    coverage-report
+# ikobactl creation must be _after_ app!
+    ikobactl
+    lxml
+    diazo
+# For backward compatibility, telling buildout not to throw away
+# the data and log subdirectories from the parts directory.
+    data
+    log
+# newest = false
+
+extends = versions.cfg
+show-picked-versions = true
+update-versions-file = versions.cfg
+
+
+# eggs will be installed in the default buildout location
+# (see .buildout/default.cfg in your home directory)
+# unless you specify an eggs-directory option here.
+
+[sources]
+waeup.ikoba = svn https://svn.waeup.org/repos/main/waeup.ikoba/trunk
+[ikoba_params]
+## Basic parameters for deployment.
+# default is 127.0.0.1
+host = 0.0.0.0
+# default is 8080
+base_port = 8080
+zeo1_port = 8080
+zeo2_port = 8081
+zeo3_port = 8082
+server_port = 8100
+# default 7. Number of pre-opened ZODB threads/client
+pool_size = 7
+# default 20MB. Cache size of each client (ZEO only)
+client_cache = 20MB
+# default 5000. Number of objects cached.
+object_cache = 5000
+# number of preopened paster threads, default 10
+threadpool_workers = 10
+devel_pkg = ikobacustom.skeleton
+
+[app]
+# This creates all scripts in bin/. The ikobactl created here is 'faulty'.
+recipe = zc.recipe.egg
+eggs = ikobacustom.skeleton [beaker, diazo]
+       z3c.evalexception>=2.0
+       Paste
+       PasteScript
+       PasteDeploy
+       repoze.profile
+       ZODB3
+interpreter = python-console
+
+[ikobactl]
+# See http://pypi.python.org/pypi/zc.recipe.egg for details...
+# Here we create the ikobactl script again, this time with a
+# fixed path to zdaemon.conf as argument.
+recipe = zc.recipe.egg
+eggs = ikobacustom.skeleton [beaker, diazo]
+       waeup.ikoba
+arguments = "${buildout:parts-directory}/etc/zdaemon.conf"
+scripts = ikobactl=ikobactl
+
+[mkdirs]
+recipe = z3c.recipe.mkdir
+paths =
+    ${zope_conf:filestorage}
+    ${zope_conf:logfiles}
+    ${zope_conf:blobstorage}
+    ${buildout:directory}/var/mailqueue/cur
+    ${buildout:directory}/var/mailqueue/tmp
+    ${buildout:directory}/var/mailqueue/new
+    ${buildout:directory}/var/datacenter/media
+    ${buildout:directory}/parts/test/datacenter
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = ikobacustom.skeleton [beaker, test]
+defaults = ['--tests-pattern', '^f?tests$', '-v']
+
+# this section named so that the i18n scripts are called bin/i18n...
+[i18n]
+recipe = z3c.recipe.i18n:i18n
+packages = ikobacustom.skeleton
+eggs = ikobacustom.skeleton
+domain = ikobacustom.skeleton
+output = src/ikobacustom/skeleton/locales
+zcml =
+
+# This section is named so that the zpasswd utility is
+# called `zpasswd`
+[zpasswd]
+recipe = z3c.recipe.dev:script
+eggs = ikobacustom.skeleton
+module = zope.app.server.zpasswd
+method = main
+
+[zope_conf]
+recipe = collective.recipe.template
+input = etc/zope.conf.in
+output = ${buildout:parts-directory}/etc/zope.conf
+filestorage = ${buildout:directory}/var/filestorage
+blobstorage = ${buildout:directory}/var/blobstorage
+logfiles = ${buildout:directory}/var/log
+extra =
+# extra = <grokwarnings>True</grokwarnings>
+# 'extra' is copied verbatim. Use it for product config sections and so.
+
+# The [data] and [log] parts are still in here to instruct buildout to not
+# unintentionally throw away the parts/data and parts/log subdirectories
+# that contain the Data.fs and the log files. These files should be
+# copied to the new locations. See the upgrade notes for more information.
+[data]
+recipe = zc.recipe.filestorage
+
+[log]
+recipe = zc.recipe.filestorage
+
+[mailqueue]
+recipe = zc.recipe.filestorage
+
+[site_zcml]
+recipe = collective.recipe.template
+input = etc/site.zcml.in
+output = ${buildout:parts-directory}/etc/site.zcml
+
+[zdaemon_conf]
+recipe = collective.recipe.template
+input = etc/zdaemon.conf.in
+output = ${buildout:parts-directory}/etc/zdaemon.conf
+
+[raw_debug_ini]
+recipe = collective.recipe.template
+input = etc/raw/debug.ini.in
+output = ${buildout:parts-directory}/etc/raw-debug.ini
+
+[raw_deploy_ini]
+recipe = collective.recipe.template
+input = etc/raw/deploy.ini.in
+output = ${buildout:parts-directory}/etc/raw-deploy.ini
+
+[themed_debug_ini]
+recipe = collective.recipe.template
+input = etc/themed/debug.ini.in
+output = ${buildout:parts-directory}/etc/themed-debug.ini
+
+[themed_deploy_ini]
+recipe = collective.recipe.template
+input = etc/themed/deploy.ini.in
+output = ${buildout:parts-directory}/etc/themed-deploy.ini
+
+[profile_ini]
+recipe = collective.recipe.template
+input = etc/profile.ini.in
+output = ${buildout:parts-directory}/etc/profile.ini
+
+[eggbasket]
+recipe = z3c.recipe.eggbasket
+eggs = grok
+url = http://grok.zope.org/releaseinfo/grok-eggs-1.1.tgz
+
+#[waeupdocs]
+#recipe = collective.recipe.sphinxbuilder
+#eggs = ikobacustom.skeleton[docs]
+#source = ${buildout:directory}/docs/source
+#build = ${buildout:directory}/docs/build
+
+# Collect test coverage data.
+[coverage-detect]
+recipe = zc.recipe.testrunner
+eggs = ikobacustom.skeleton[beaker]
+defaults = ['--tests-pattern', '^f?tests$', '-v', '--coverage', 'coverage']
+
+# Create a coverage report.
+# Make sure to run bin/coverage-detect to collect the data for the report
+# first!
+[coverage-report]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+scripts = coveragereport
+arguments = ('${buildout:parts-directory}/coverage-detect/coverage', '${buildout:directory}/coverage-report')
+
+[lxml]
+recipe = z3c.recipe.staticlxml
+egg = lxml
+
+[diazo]
+recipe = zc.recipe.egg
+eggs =
+    diazo [wsgi]
+    PasteScript
+    ikobacustom.skeleton [beaker,diazo]
Index: /main/ikobacustom.uniben/trunk/etc/README.txt
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/README.txt	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/README.txt	(revision 14174)
@@ -0,0 +1,26 @@
+In this directory you can find templates which are used by
+``zc.buildout`` to create the configuration files in the parts/etc/ subdir
+of your project.
+
+If you modify files in this directory, you have to run::
+
+  $ bin/buildout
+
+afterwards to rebuild the configuration files in parts/etc/.
+
+In the templates you can use placesholders recognized by zc.buildout
+to name local paths, etc. A zc.buildout placeholder looks like this::
+
+  ${buildout:directory}
+
+which gives you the path of the project directory and will be
+substituted with the real path when you run buildout the next
+time. The set of available placeholders depends on your
+buildout.cfg.
+
+You can also modify files in parts/etc directly, but those changes
+will be overwritten after running bin/buildout the next time.
+
+To run your project you can do::
+
+  $ bin/paster serve parts/etc/deploy.ini
Index: /main/ikobacustom.uniben/trunk/etc/paypal-testing.conf
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/paypal-testing.conf	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/paypal-testing.conf	(revision 14174)
@@ -0,0 +1,4 @@
+[rest-client]
+id = AW6P1xBRRCcFohbALZ8ax5e8i7IRgTJE8KEgUq2U8vXFtY8-w8oP86N3o2R5
+secret = EDmDSBAMzhLl0uM7PsfaOBsgXGuSoxnFsmZCYU2xmMfBkurc8mzxCndZi8IF
+mode = sandbox
Index: /main/ikobacustom.uniben/trunk/etc/paypal.conf
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/paypal.conf	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/paypal.conf	(revision 14174)
@@ -0,0 +1,4 @@
+[rest-client]
+id = AW6P1xBRRCcFohbALZ8ax5e8i7IRgTJE8KEgUq2U8vXFtY8-w8oP86N3o2R5
+secret = EDmDSBAMzhLl0uM7PsfaOBsgXGuSoxnFsmZCYU2xmMfBkurc8mzxCndZi8IF
+mode = sandbox
Index: /main/ikobacustom.uniben/trunk/etc/profile.ini.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/profile.ini.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/profile.ini.in	(revision 14174)
@@ -0,0 +1,72 @@
+# profile.ini
+#
+# Profiling configuration for use with paster/WSGI
+#
+# Call http://localhost:8080/__profile__ to see the profiler in action
+
+[loggers]
+keys = root, wsgi
+
+[handlers]
+keys = console, accesslog
+
+[formatters]
+keys = generic, accesslog
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_wsgi]
+level = INFO
+handlers = accesslog
+qualname = wsgi
+propagate = 0
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[handler_accesslog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'access.log'),
+        'a')
+level = INFO
+formatter = accesslog
+
+[formatter_generic]
+format = %(asctime)s %(levelname)s [%(name)s] %(message)s
+
+[formatter_accesslog]
+format = %(message)s
+
+[filter:translogger]
+use = egg:Paste#translogger
+setup_console_handler = False
+logger_name = wsgi
+
+[filter:profiler]
+use = egg:repoze.profile#profile
+log_filename = ikoba.profile
+#cachegrind_filename = cachegrind.out.ikoba
+discard_first_request = False
+path = /__profile__
+flush_at_shutdown = False
+
+[app:app]
+use = egg:ikobacustom.skeleton
+
+[pipeline:main]
+pipeline = translogger profiler app
+
+[server:main]
+use = egg:Paste#http
+host = ${ikoba_params:host}
+port = ${ikoba_params:base_port}
+
+[DEFAULT]
+# set the name of the zope.conf file
+zope_conf = %(here)s/zope.conf
+env_vars = ZC_ASYNC_UUID ${buildout:directory}/var/uuid1.txt
Index: /main/ikobacustom.uniben/trunk/etc/raw/debug.ini.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/raw/debug.ini.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/raw/debug.ini.in	(revision 14174)
@@ -0,0 +1,94 @@
+# debug.ini
+#
+# Debugging configuration for use with paster/WSGI
+#
+
+[loggers]
+keys = root, wsgi
+
+[handlers]
+keys = console, accesslog, asynclog, async_tracelog
+
+[formatters]
+keys = generic, accesslog
+
+[formatter_generic]
+format = %(asctime)s %(levelname)s [%(name)s] %(message)s
+
+[formatter_accesslog]
+format = %(message)s
+
+[handler_asynclog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'async.log'),
+        'a')
+level = INFO
+formatter = generic
+
+[handler_async_tracelog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'async_trace.log'),
+        'a')
+level = INFO
+formatter = generic
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[handler_accesslog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'access.log'),
+        'a')
+level = INFO
+formatter = accesslog
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_wsgi]
+level = INFO
+handlers = accesslog
+qualname = wsgi
+propagate = 0
+
+[logger_async]
+level = INFO
+handlers = asynclog
+qualname = zc.async
+propagate = 0
+
+[logger_async_trace]
+level = INFO
+handlers = async_tracelog
+qualname = zc.async.trace
+propagate = 0
+
+[filter:translogger]
+use = egg:Paste#translogger
+setup_console_handler = False
+logger_name = wsgi
+
+[filter-app:main]
+# Change the last part from 'ajax' to 'pdb' for a post-mortem debugger
+# on the console:
+use = egg:z3c.evalexception#ajax
+next = zope
+
+[app:zope]
+use = egg:${ikoba_params:devel_pkg}#debug
+filter-with = translogger
+exempt-exceptions = zope.security.interfaces.IUnauthorized
+
+[server:main]
+use = egg:Paste#http
+host = ${ikoba_params:host}
+port = ${ikoba_params:base_port}
+
+[DEFAULT]
+# set the name of the zope.conf file
+zope_conf = %(here)s/zope.conf
+env_vars = ZC_ASYNC_UUID ${buildout:directory}/var/uuid1.txt
Index: /main/ikobacustom.uniben/trunk/etc/raw/deploy.ini.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/raw/deploy.ini.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/raw/deploy.ini.in	(revision 14174)
@@ -0,0 +1,94 @@
+# deploy.ini
+#
+# Deployment configuration for use with paster/WSGI
+#
+
+[loggers]
+keys = root, wsgi, async, async_trace
+
+[handlers]
+keys = null, console, accesslog, asynclog, async_tracelog
+
+[formatters]
+keys = generic, accesslog
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_wsgi]
+level = INFO
+#handlers = accesslog
+handlers = null
+qualname = wsgi
+propagate = 0
+
+[logger_async]
+level = INFO
+handlers = asynclog
+qualname = zc.async
+propagate = 0
+
+[logger_async_trace]
+level = INFO
+handlers = async_tracelog
+qualname = zc.async.trace
+propagate = 0
+
+[handler_null]
+# you can use 'NullHandler' (w/o the path) if under Python 2.7+
+class = waeup.ikoba.utils.helpers.NullHandler
+args = ()
+level = NOTSET
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[handler_accesslog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'access.log'),
+        'a')
+level = INFO
+formatter = accesslog
+
+[handler_asynclog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'async.log'),
+        'a')
+level = INFO
+formatter = generic
+
+[handler_async_tracelog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'async_trace.log'),
+        'a')
+level = INFO
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)s [%(name)s] %(message)s
+
+[formatter_accesslog]
+format = %(message)s
+
+[filter:translogger]
+use = egg:Paste#translogger
+setup_console_handler = False
+logger_name = wsgi
+
+[app:main]
+use = egg:${ikoba_params:devel_pkg}
+filter-with = translogger
+
+[server:main]
+use = egg:Paste#http
+host = ${ikoba_params:host}
+port = ${ikoba_params:base_port}
+
+[DEFAULT]
+# set the name of the zope.conf file
+zope_conf = %(here)s/zope.conf
+env_vars = ZC_ASYNC_UUID ${buildout:directory}/var/uuid1.txt
Index: /main/ikobacustom.uniben/trunk/etc/site.zcml.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/site.zcml.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/site.zcml.in	(revision 14174)
@@ -0,0 +1,83 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:ikoba="http://namespaces.waeup.org/ikoba"
+           i18n_domain="ikobacustom.skeleton">
+
+  <include package="ikobacustom.skeleton" />
+  <include package="ikobacustom.skeleton" file="mail.zcml" />
+
+  <!-- install job container
+
+       install a job container for asynchronous jobs. Pick one of the
+       two possibilites: 'single' for installation in the one big ZODB
+       or 'multidb' for setup with a dedicated ZODB for job handling.
+  -->
+  <!-- include package="waeup.ikoba" file="async_single.zcml" / -->
+  <include package="waeup.ikoba" file="async_multidb.zcml" />
+
+  <!-- install dispatcher and other needed async components
+
+       install other needed components for asynchronous jobs. Pick one
+       of the two possibilities: 'basic_dispatcher' for single ZODB
+       setup, 'multidb_dispatcher' for setup with a dedicated ZODB for
+       job handling. Make sure the setting matches the setting picked
+       above. -->
+  <!-- include package="zc.async" file="basic_dispatcher_policy.zcml" / -->
+  <include package="zc.async" file="multidb_dispatcher_policy.zcml" />
+
+  <!-- Where should the datacenter reside by default? -->
+  <ikoba:datacenter
+      path="${buildout:directory}/var/datacenter" />
+
+  <!-- Where can the Paypal config be found? -->
+  <ikoba:paypalconf
+      path="${buildout:directory}/etc/paypal.conf" />
+
+    <configure i18n_domain="ikobacustom.skeleton">
+
+      <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" />
+      <principal id="zope.manager"
+                 title="Manager"
+                 login="grok"
+                 password_manager="SHA1"
+                 password="321b9cd3739bd029629243732eb682db005a7541f7509622"
+                 />
+
+      <!-- XMLRPC user for demo and testing
+           Plaintext password is xmlrpcuser1 -->
+      <principal id="zope.xmlrpcuser1"
+                 title="XMLRPC User 1"
+                 login="xmlrpcuser1"
+                 password="{SSHA}ZaL4-UiwmSu6AbLYY-yCS_8vtKpH5Hc6"
+                 password_manager="SSHA"
+                 />
+      <grant role="waeup.xmlrpcusers1"
+             principal="zope.xmlrpcuser1" />
+
+      <!-- Replace the following directive if you do not want
+           public access -->
+      <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" />
+      <grant permission="waeup.Authenticated"
+             principal="zope.Authenticated" />
+
+      <role id="zope.Manager" title="Site Manager" />
+      <grantAll role="zope.Manager" />
+      <grant role="zope.Manager"
+             principal="zope.manager" />
+   </configure>
+</configure>
Index: /main/ikobacustom.uniben/trunk/etc/themed/debug.ini.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/themed/debug.ini.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/themed/debug.ini.in	(revision 14174)
@@ -0,0 +1,125 @@
+# debug.ini
+#
+# Debugging configuration for use with paster/WSGI and diazo
+#
+
+[loggers]
+keys = root, wsgi
+
+[handlers]
+keys = console, accesslog, asynclog, async_tracelog
+
+[formatters]
+keys = generic, accesslog
+
+[formatter_generic]
+format = %(asctime)s %(levelname)s [%(name)s] %(message)s
+
+[formatter_accesslog]
+format = %(message)s
+
+[handler_asynclog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'async.log'),
+        'a')
+level = INFO
+formatter = generic
+
+[handler_async_tracelog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'async_trace.log'),
+        'a')
+level = INFO
+formatter = generic
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[handler_accesslog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'access.log'),
+        'a')
+level = INFO
+formatter = accesslog
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_wsgi]
+level = INFO
+handlers = accesslog
+qualname = wsgi
+propagate = 0
+
+[logger_async]
+level = INFO
+handlers = asynclog
+qualname = zc.async
+propagate = 0
+
+[logger_async_trace]
+level = INFO
+handlers = async_tracelog
+qualname = zc.async.trace
+propagate = 0
+
+[filter:translogger]
+use = egg:Paste#translogger
+setup_console_handler = False
+logger_name = wsgi
+
+[filter:debugger]
+# Change the last part from 'ajax' to 'pdb' for a post-mortem debugger
+# on the console:
+use = egg:z3c.evalexception#ajax
+
+[app:zope]
+use = egg:${ikoba_params:devel_pkg}#debug
+exempt-exceptions = zope.security.interfaces.IUnauthorized
+
+[app:static]
+use = egg:Paste#static
+document_root = %(LAYOUT_DIR)s/static
+
+[app:static_custom]
+use = egg:Paste#static
+document_root = %(LAYOUT_CUSTOM_DIR)s/static/
+
+[composite:main]
+use = egg:Paste#urlmap
+/static = static
+/static_custom = static_custom
+/ = default
+
+[filter:theme]
+use = egg:diazo
+rules = file:///%(LAYOUT_CUSTOM_DIR)s/rules.xml
+prefix = /
+doctype = <!DOCTYPE html>
+# set to false, if you don't want rules.xml being rebuild on each request
+debug = true
+
+[pipeline:default]
+pipeline = theme
+           translogger
+           debugger
+           zope
+
+[server:main]
+use = egg:Paste#http
+host = ${ikoba_params:host}
+port = ${ikoba_params:base_port}
+
+[DEFAULT]
+# set the name of the zope.conf file
+zope_conf = %(here)s/zope.conf
+env_vars = ZC_ASYNC_UUID ${buildout:directory}/var/uuid1.txt
+# the directory where all real diazo config is done (no trailing slash)
+LAYOUT_DIR = ${buildout:directory}/sources/waeup.ikoba/layout
+STATIC_DIR = %(LAYOUT_DIR)s/static
+LAYOUT_CUSTOM_DIR = ${buildout:directory}/layout
+STATIC_CUSTOM_DIR = %(LAYOUT_CUSTOM_DIR)s/static
Index: /main/ikobacustom.uniben/trunk/etc/themed/deploy.ini.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/themed/deploy.ini.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/themed/deploy.ini.in	(revision 14174)
@@ -0,0 +1,125 @@
+# deploy.ini
+#
+# Deployment configuration for use with paster/WSGI
+#
+
+[loggers]
+keys = root, wsgi, async, async_trace
+
+[handlers]
+keys = null, console, accesslog, asynclog, async_tracelog
+
+[formatters]
+keys = generic, accesslog
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_wsgi]
+level = INFO
+#handlers = accesslog
+handlers = null
+qualname = wsgi
+propagate = 0
+
+[logger_async]
+level = INFO
+handlers = asynclog
+qualname = zc.async
+propagate = 0
+
+[logger_async_trace]
+level = INFO
+handlers = async_tracelog
+qualname = zc.async.trace
+propagate = 0
+
+[handler_null]
+# you can use 'NullHandler' (w/o the path) if under Python 2.7+
+class = waeup.ikoba.utils.helpers.NullHandler
+args = ()
+level = NOTSET
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[handler_accesslog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'access.log'),
+        'a')
+level = INFO
+formatter = accesslog
+
+[handler_asynclog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'async.log'),
+        'a')
+level = INFO
+formatter = generic
+
+[handler_async_tracelog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'async_trace.log'),
+        'a')
+level = INFO
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)s [%(name)s] %(message)s
+
+[formatter_accesslog]
+format = %(message)s
+
+[filter:translogger]
+use = egg:Paste#translogger
+setup_console_handler = False
+logger_name = wsgi
+
+[app:content]
+use = egg:${ikoba_params:devel_pkg}
+filter-with = translogger
+
+[app:static]
+use = egg:Paste#static
+document_root = %(LAYOUT_DIR)s/static/
+
+[app:static_custom]
+use = egg:Paste#static
+document_root = %(LAYOUT_CUSTOM_DIR)s/static/
+
+[composite:main]
+use = egg:Paste#urlmap
+/static = static
+/static_custom = static_custom
+/ = default
+
+[server:main]
+use = egg:Paste#http
+host = ${ikoba_params:host}
+port = ${ikoba_params:base_port}
+
+[filter:theme]
+use = egg:diazo
+rules = file:///%(LAYOUT_CUSTOM_DIR)s/rules.xml
+prefix = /
+doctype = <!DOCTYPE html>
+# set to true, if you want rules.xml being rebuild on each request
+debug = false
+
+[pipeline:default]
+pipeline = theme
+           content
+
+[DEFAULT]
+# set the name of the zope.conf file
+zope_conf = %(here)s/zope.conf
+env_vars = ZC_ASYNC_UUID ${buildout:directory}/var/uuid1.txt
+# the directory where all real diazo config is done (no trailing slash)
+LAYOUT_DIR = ${buildout:directory}/sources/waeup.ikoba/layout
+STATIC_DIR = %(LAYOUT_DIR)s/static
+LAYOUT_CUSTOM_DIR = ${buildout:directory}/layout
+STATIC_CUSTOM_DIR = %(LAYOUT_CUSTOM_DIR)s/static
Index: /main/ikobacustom.uniben/trunk/etc/themed/zeo1.ini.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/themed/zeo1.ini.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/themed/zeo1.ini.in	(revision 14174)
@@ -0,0 +1,126 @@
+# deploy.ini
+#
+# Deployment configuration for use with paster/WSGI
+#
+
+[loggers]
+keys = root, wsgi, async, async_trace
+
+[handlers]
+keys = null, console, accesslog, asynclog, async_tracelog
+
+[formatters]
+keys = generic, accesslog
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_wsgi]
+level = INFO
+#handlers = accesslog
+handlers = null
+qualname = wsgi
+propagate = 0
+
+[logger_async]
+level = INFO
+handlers = asynclog
+qualname = zc.async
+propagate = 0
+
+[logger_async_trace]
+level = INFO
+handlers = async_tracelog
+qualname = zc.async.trace
+propagate = 0
+
+[handler_null]
+# you can use 'NullHandler' (w/o the path) if under Python 2.7+
+class = waeup.ikoba.utils.helpers.NullHandler
+args = ()
+level = NOTSET
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[handler_accesslog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'zeo1.log'),
+        'a')
+level = INFO
+formatter = accesslog
+
+[handler_asynclog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'zeo1_async.log'),
+        'a')
+level = INFO
+formatter = generic
+
+[handler_async_tracelog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'zeo1_async_trace.log'),
+        'a')
+level = INFO
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)s [%(name)s] %(message)s
+
+[formatter_accesslog]
+format = %(message)s
+
+[filter:translogger]
+use = egg:Paste#translogger
+setup_console_handler = False
+logger_name = wsgi
+
+[app:content]
+use = egg:${ikoba_params:devel_pkg}
+filter-with = translogger
+
+[app:static]
+use = egg:Paste#static
+document_root = %(LAYOUT_DIR)s/static/
+
+[app:static_custom]
+use = egg:Paste#static
+document_root = %(LAYOUT_CUSTOM_DIR)s/static/
+
+[composite:main]
+use = egg:Paste#urlmap
+/static = static
+/static_custom = static_custom
+/ = default
+
+[server:main]
+use = egg:Paste#http
+host = ${ikoba_params:host}
+port = ${ikoba_params:zeo1_port}
+threadpool_workers = ${ikoba_params:threadpool_workers}
+use_threadpool = True
+
+[filter:theme]
+use = egg:diazo
+rules = file:///%(LAYOUT_CUSTOM_DIR)s/rules.xml
+prefix = /
+doctype = <!DOCTYPE html>
+# set to true, if you want rules.xml being rebuild on each request
+debug = false
+
+[pipeline:default]
+pipeline = theme
+           content
+[DEFAULT]
+# set the name of the zope.conf file
+zope_conf = %(here)s/zope_zeo1.conf
+env_vars = ZC_ASYNC_UUID ${buildout:directory}/var/uuid1.txt
+# the directory where all real diazo config is done (no trailing slash)
+LAYOUT_DIR = ${buildout:directory}/sources/waeup.ikoba/layout
+STATIC_DIR = %(LAYOUT_DIR)s/static
+LAYOUT_CUSTOM_DIR = ${buildout:directory}/layout
+STATIC_CUSTOM_DIR = %(LAYOUT_CUSTOM_DIR)s/static
Index: /main/ikobacustom.uniben/trunk/etc/themed/zeo2.ini.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/themed/zeo2.ini.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/themed/zeo2.ini.in	(revision 14174)
@@ -0,0 +1,126 @@
+# deploy.ini
+#
+# Deployment configuration for use with paster/WSGI
+#
+
+[loggers]
+keys = root, wsgi, async, async_trace
+
+[handlers]
+keys = null, console, accesslog, asynclog, async_tracelog
+
+[formatters]
+keys = generic, accesslog
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_wsgi]
+level = INFO
+#handlers = accesslog
+handlers = null
+qualname = wsgi
+propagate = 0
+
+[logger_async]
+level = INFO
+handlers = asynclog
+qualname = zc.async
+propagate = 0
+
+[logger_async_trace]
+level = INFO
+handlers = async_tracelog
+qualname = zc.async.trace
+propagate = 0
+
+[handler_null]
+# you can use 'NullHandler' (w/o the path) if under Python 2.7+
+class = waeup.ikoba.utils.helpers.NullHandler
+args = ()
+level = NOTSET
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[handler_accesslog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'zeo2.log'),
+        'a')
+level = INFO
+formatter = accesslog
+
+[handler_asynclog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'zeo2_async.log'),
+        'a')
+level = INFO
+formatter = generic
+
+[handler_async_tracelog]
+class = FileHandler
+args = (os.path.join(r'${zope_conf:logfiles}', 'zeo2_async_trace.log'),
+        'a')
+level = INFO
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)s [%(name)s] %(message)s
+
+[formatter_accesslog]
+format = %(message)s
+
+[filter:translogger]
+use = egg:Paste#translogger
+setup_console_handler = False
+logger_name = wsgi
+
+[app:content]
+use = egg:${ikoba_params:devel_pkg}
+filter-with = translogger
+
+[app:static]
+use = egg:Paste#static
+document_root = %(LAYOUT_DIR)s/static/
+
+[app:static_custom]
+use = egg:Paste#static
+document_root = %(LAYOUT_CUSTOM_DIR)s/static/
+
+[composite:main]
+use = egg:Paste#urlmap
+/static = static
+/static_custom = static_custom
+/ = default
+
+[server:main]
+use = egg:Paste#http
+host = ${ikoba_params:host}
+port = ${ikoba_params:zeo2_port}
+threadpool_workers = ${ikoba_params:threadpool_workers}
+use_threadpool = True
+
+[filter:theme]
+use = egg:diazo
+rules = file:///%(LAYOUT_CUSTOM_DIR)s/rules.xml
+prefix = /
+doctype = <!DOCTYPE html>
+# set to true, if you want rules.xml being rebuild on each request
+debug = false
+
+[pipeline:default]
+pipeline = theme
+           content
+[DEFAULT]
+# set the name of the zope.conf file
+zope_conf = %(here)s/zope_zeo2.conf
+env_vars = ZC_ASYNC_UUID ${buildout:directory}/var/uuid2.txt
+# the directory where all real diazo config is done (no trailing slash)
+LAYOUT_DIR = ${buildout:directory}/sources/waeup.ikoba/layout
+STATIC_DIR = %(LAYOUT_DIR)s/static
+LAYOUT_CUSTOM_DIR = ${buildout:directory}/layout
+STATIC_CUSTOM_DIR = %(LAYOUT_CUSTOM_DIR)s/static
Index: /main/ikobacustom.uniben/trunk/etc/zdaemon.conf.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/zdaemon.conf.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/zdaemon.conf.in	(revision 14174)
@@ -0,0 +1,8 @@
+<runner>
+  program ${buildout:directory}/bin/paster serve ${buildout:directory}/parts/etc/themed-deploy.ini
+  daemon on
+  transcript ${zope_conf:logfiles}/zdaemon.log
+  socket-name ${zope_conf:logfiles}/zdaemonsock
+  # Enable this to run the child process as a different user
+  # user zope
+</runner>
Index: /main/ikobacustom.uniben/trunk/etc/zeo1.conf.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/zeo1.conf.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/zeo1.conf.in	(revision 14174)
@@ -0,0 +1,8 @@
+<runner>
+  program ${buildout:directory}/bin/paster serve --pid-file ${buildout:directory}/var/zeo1.pid ${buildout:directory}/parts/etc/themed-zeo1.ini
+  daemon on
+  transcript ${zope_conf:logfiles}/zdaemon-zeo1.log
+  socket-name ${zope_conf:logfiles}/zeo1.sock
+  # Enable this to run the child process as a different user
+  # user ikoba
+</runner>
Index: /main/ikobacustom.uniben/trunk/etc/zeo2.conf.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/zeo2.conf.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/zeo2.conf.in	(revision 14174)
@@ -0,0 +1,8 @@
+<runner>
+  program ${buildout:directory}/bin/paster serve --pid-file ${buildout:directory}/var/zeo2.pid ${buildout:directory}/parts/etc/themed-zeo2.ini
+  daemon on
+  transcript ${zope_conf:logfiles}/zdaemon-zeo2.log
+  socket-name ${zope_conf:logfiles}/zeo2.sock
+  # Enable this to run the child process as a different user
+  # user ikoba
+</runner>
Index: /main/ikobacustom.uniben/trunk/etc/zope.conf.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/zope.conf.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/zope.conf.in	(revision 14174)
@@ -0,0 +1,62 @@
+# Identify the component configuration used to define the site:
+site-definition ${site_zcml:output}
+
+<zodb>
+  # Standard blob storage
+  <blobstorage>
+    blob-dir ${zope_conf:blobstorage}
+    <filestorage>
+      path ${zope_conf:filestorage}/Data.fs
+    </filestorage>
+  </blobstorage>
+
+# Uncomment this if you want a blob-less standard file storage instead:
+#  <filestorage>
+#       path ${zope_conf:filestorage}
+#  </filestorage>
+
+# Uncomment this if you want to connect to a ZEO server instead:
+#  <zeoclient>
+#    server localhost:8100
+#    storage 1
+#    # ZEO client cache, in bytes
+#    cache-size 20MB
+#    # Uncomment to have a persistent disk cache
+#    #client zeo1
+#  </zeoclient>
+
+</zodb>
+
+<zodb async>
+  <filestorage>
+    # create true
+    path ${zope_conf:filestorage}/Data.async.fs
+  </filestorage>
+</zodb>
+
+<eventlog>
+  # This sets up logging to a file.
+  # The "path" setting can be a relative or absolute
+  # filesystem path.
+
+  <logfile>
+    path ${zope_conf:logfiles}/z3.log
+    formatter zope.exceptions.log.Formatter
+  </logfile>
+
+  # This sets up logging to to standard output.
+  # The "path" setting can be the tokens STDOUT or STDERR
+  
+#  <logfile>
+#    path STDOUT
+#    formatter zope.exceptions.log.Formatter
+#  </logfile>
+</eventlog>
+
+# Comment this line to disable developer mode.  This should be done in
+# production
+# devmode on
+
+# Extra configuration lines can be added to zope_conf's extra option. Put for 
+# instance productconf sections in here.
+${zope_conf:extra}
Index: /main/ikobacustom.uniben/trunk/etc/zope_zeo1.conf.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/zope_zeo1.conf.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/zope_zeo1.conf.in	(revision 14174)
@@ -0,0 +1,100 @@
+# Identify the component configuration used to define the site:
+site-definition ${site_zcml:output}
+
+<zodb>
+
+  #cache-size
+
+  # num of concurrent pre-opened ZODB threads; default: 7
+  # pool-size 25
+
+  # Standard blob storage
+#  <blobstorage>
+#    blob-dir ${zope_conf:blobstorage}
+#    <filestorage>
+#      path ${zope_conf:filestorage}/Data.fs
+#    </filestorage>
+#  </blobstorage>
+
+# Uncomment this if you want a blob-less standard file storage instead:
+#  <filestorage>
+#       path ${zope_conf:filestorage}
+#  </filestorage>
+
+# Uncomment this if you want to connect to a ZEO server instead:
+  <zeoclient>
+    server localhost:8010
+    storage 1
+    # ZEO client cache, in bytes
+    cache-size 20MB
+    # Uncomment to have a persistent disk cache
+    client ${zope_conf_zeo_1:filestorage}/zeo1
+  </zeoclient>
+
+# Uncomment this if you want to run a ZEO server instead:
+#  <zeo>
+#    # required; 'host:port' for TCP/IP, path to a socket file for UDP
+#    address localhost:8090
+#
+#    # optional; 'address' remarks apply
+#    #monitor-address localhost:8091
+#
+#    # optional; keep queue of last N transactions. Speeds up client cache verif.
+#    #invalidation-queue-size 10
+#
+#    # optional; is this a read-only instance? true or false
+#    #read-only false
+#
+#    # optional; seconds to wait for client to commit after acquiring lock
+#    #transaction-timeout 20
+#
+#    # optional; authentication protocol. With ZEO only 'digest' supported
+#    #authentication-protocol digest 
+#
+#    # optional; Path of the database containing authentication credentials.
+#    #authentication-database
+#
+#    # optional; the authentication realm of server
+#    #authentication-realm myrealm
+#  </zeo>
+
+</zodb>
+
+<zodb async>
+  <zeoclient>
+    server localhost:8010
+    storage async
+    name async
+    # ZEO client cache, in bytes
+    cache-size 20MB
+    # Uncomment to have a persistent disk cache
+    client ${zope_conf_zeo_1:filestorage}/zeo1_async
+  </zeoclient>
+</zodb>
+
+<eventlog>
+  # This sets up logging to a file.
+  # The "path" setting can be a relative or absolute
+  # filesystem path.
+
+  <logfile>
+    path ${zope_conf:logfiles}/zeo1_z3.log
+    formatter zope.exceptions.log.Formatter
+  </logfile>
+
+  # This sets up logging to to standard output.
+  # The "path" setting can be the tokens STDOUT or STDERR
+  
+#  <logfile>
+#    path STDOUT
+#    formatter zope.exceptions.log.Formatter
+#  </logfile>
+</eventlog>
+
+# Comment this line to disable developer mode.  This should be done in
+# production
+#devmode on
+
+# Extra configuration lines can be added to zope_conf's extra option. Put for 
+# instance productconf sections in here.
+${zope_conf:extra}
Index: /main/ikobacustom.uniben/trunk/etc/zope_zeo2.conf.in
===================================================================
--- /main/ikobacustom.uniben/trunk/etc/zope_zeo2.conf.in	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/etc/zope_zeo2.conf.in	(revision 14174)
@@ -0,0 +1,100 @@
+# Identify the component configuration used to define the site:
+site-definition ${site_zcml:output}
+
+<zodb>
+
+  #cache-size
+
+  # num of concurrent pre-opened ZODB threads; default: 7
+  pool-size 7
+
+  # Standard blob storage
+#  <blobstorage>
+#    blob-dir ${zope_conf:blobstorage}
+#    <filestorage>
+#      path ${zope_conf:filestorage}/Data.fs
+#    </filestorage>
+#  </blobstorage>
+
+# Uncomment this if you want a blob-less standard file storage instead:
+#  <filestorage>
+#       path ${zope_conf:filestorage}
+#  </filestorage>
+
+# Uncomment this if you want to connect to a ZEO server instead:
+  <zeoclient>
+    server localhost:8010
+    storage 1
+    # ZEO client cache, in bytes
+    cache-size 20MB
+    # Uncomment to have a persistent disk cache
+    client ${zope_conf_zeo_2:filestorage}/zeo2
+  </zeoclient>
+
+# Uncomment this if you want to run a ZEO server instead:
+#  <zeo>
+#    # required; 'host:port' for TCP/IP, path to a socket file for UDP
+#    address localhost:8090
+#
+#    # optional; 'address' remarks apply
+#    #monitor-address localhost:8091
+#
+#    # optional; keep queue of last N transactions. Speeds up client cache verif.
+#    #invalidation-queue-size 10
+#
+#    # optional; is this a read-only instance? true or false
+#    #read-only false
+#
+#    # optional; seconds to wait for client to commit after acquiring lock
+#    #transaction-timeout 20
+#
+#    # optional; authentication protocol. With ZEO only 'digest' supported
+#    #authentication-protocol digest 
+#
+#    # optional; Path of the database containing authentication credentials.
+#    #authentication-database
+#
+#    # optional; the authentication realm of server
+#    #authentication-realm myrealm
+#  </zeo>
+
+</zodb>
+
+<zodb async>
+  <zeoclient>
+    server localhost:8010
+    storage async
+    name async
+    # ZEO client cache, in bytes
+    cache-size 20MB
+    # Uncomment to have a persistent disk cache
+    client ${zope_conf_zeo_2:filestorage}/zeo2_async
+  </zeoclient>
+</zodb>
+
+<eventlog>
+  # This sets up logging to a file.
+  # The "path" setting can be a relative or absolute
+  # filesystem path.
+
+  <logfile>
+    path ${zope_conf:logfiles}/zeo2_z3.log
+    formatter zope.exceptions.log.Formatter
+  </logfile>
+
+  # This sets up logging to to standard output.
+  # The "path" setting can be the tokens STDOUT or STDERR
+  
+#  <logfile>
+#    path STDOUT
+#    formatter zope.exceptions.log.Formatter
+#  </logfile>
+</eventlog>
+
+# Comment this line to disable developer mode.  This should be done in
+# production
+#devmode on
+
+# Extra configuration lines can be added to zope_conf's extra option. Put for 
+# instance productconf sections in here.
+${zope_conf:extra}
Index: /main/ikobacustom.uniben/trunk/layout/error-zope.html
===================================================================
--- /main/ikobacustom.uniben/trunk/layout/error-zope.html	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/layout/error-zope.html	(revision 14174)
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>waeup.ikoba Portal - Error</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="description" content="">
+    <meta name="author" content="">
+    <link rel="shortcut icon" href="static/ico/favicon.ico">
+
+    <!-- stylesheets -->
+    <link href="static/bootstrap/css/bootstrap.min.css"
+	  rel="stylesheet">
+    <link href="static/css/base.css" rel="stylesheet">
+  </head>
+  <body id="ikoba-body" class="errorpage">
+    <div class="container">
+      <div class="jumbotron">
+        <h1 id="ikoba-error-heading">Error</h1>
+        <div id="ikoba-error-message">Error Message</div>
+        <div>We are terribly sorry.</div>
+      </div>
+    </div>
+
+  </body>
+</html>
Index: /main/ikobacustom.uniben/trunk/layout/rules.xml
===================================================================
--- /main/ikobacustom.uniben/trunk/layout/rules.xml	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/layout/rules.xml	(revision 14174)
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<rules
+    xmlns="http://namespaces.plone.org/diazo"
+    xmlns:css="http://namespaces.plone.org/diazo/css"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <!-- Cases where we do not want themes... -->
+  <notheme if-path="/++grokui++" />  <!-- do not theme GrokUI -->
+  <notheme if-path="/_debug" /><!-- output by debugger -->
+  <notheme css:if-content="div#error-area" />
+
+  <!-- use error page if there is a <table class="layout">
+       tag in content. This tag is rendered in Zope 3 errors. -->
+  <rules css:if-content="table#layout">
+    <theme href="error-zope.html" />
+    <replace css:theme-children="#ikoba-error-message"
+             css:content-children="h3" />
+  </rules>
+  <!-- use error page if there is no <div> tag in content.
+       This happens if something in the application went wrong. -->
+  <rules css:if-not-content="div">
+    <theme href="error-zope.html" />
+    <replace css:theme-children="#ikoba-error-message"
+             css:content-children="body" />
+  </rules>
+
+  <!-- regular rules -->
+  <rules css:if-content="body.ikoba-body">
+
+    <theme href="theme.html" />
+
+    <!-- head elements -->
+    <replace css:theme="html head title"
+             css:content="html head title" />
+
+    <!-- brand (upper left) -->
+    <copy attributes="href" css:theme="a.ikoba-brand-link"
+          css:content="a.ikoba-brand-link" />
+    <replace css:theme-children="a.ikoba-brand-link"
+             css:content-children="a.ikoba-brand-link" />
+
+    <!-- left top navigation -->
+    <replace css:theme-children="ul.ikoba-navbar-left"
+             css:content-children="ul.ikoba-navbar-left" />
+
+    <!-- right side top navigation -->
+    <replace css:theme-children="ul.ikoba-navbar-right"
+             css:content-children="ul.ikoba-navbar-right" />
+
+    <!-- maintenance mode warning -->
+    <drop css:theme="div.kofa-maintenance"
+          css:if-not-content="div.maintenance-warning" />
+    <replace css:theme-children="div.maintenance-warning"
+             css:content-children="div.maintenance-warning" />
+
+    <!-- breadcrumbs -->
+    <drop css:theme="div.ikoba-breadcrumbs-box-long"
+          css:if-not-content="ul.breadcrumb" />
+    <drop css:theme="div.ikoba-breadcrumbs-box-long"
+          css:if-content="div.wfstatus" />
+    <drop css:theme="div.ikoba-breadcrumbs-box-short"
+          css:if-not-content="ul.breadcrumb" />
+    <drop css:theme="div.ikoba-breadcrumbs-box-short"
+          css:if-not-content="div.wfstatus" />
+    <replace css:theme-children="ol.ikoba-breadcrumbs"
+             css:content-children="ul.breadcrumb" />
+
+    <!-- customer status -->
+    <drop css:theme="div.ikoba-customer-status-box"
+          css:if-not-content="div.wfstatus" />
+    <replace css:theme-children="div.wfstatus"
+             css:content-children="div.wfstatus" />
+
+    <!-- sidebar -->
+
+    <drop css:theme="div.ikoba-sidebar"
+          css:if-not-content="div.sidebar" />
+    <replace css:theme-children="div.sidebar-nav"
+             css:content-children="div.sidebar" />
+
+    <!-- content -->
+
+    <!-- drop one of the both content areas, depending on sidebar -->
+
+    <drop css:theme="div.ikoba-content-narrow"
+          css:if-not-content="div.sidebar" />
+    <drop css:theme="div.ikoba-content-wide"
+          css:if-content="div.sidebar" />
+    <replace css:theme-children="div.ikoba-content"
+             css:content-children="div.ikoba-content" />
+
+
+    <!-- scripts -->
+    <drop css:theme="script.ikoba-script-tooltip"
+          css:if-not-content="[data-toggle='tooltip']" />
+
+    <drop css:theme="script.ikoba-script-datepicker"
+          css:if-not-content="input.datepicker-le-year" />
+
+    <drop css:theme="script.ikoba-script-datatable"
+          css:if-not-content="table.ikoba-data-table" />
+
+<!--
+
+    <rules css:if-not-content="input.datepicker-le-year">
+        <drop css:theme="script.jquery-ui"
+              css:if-not-content="[data-toggle='tooltip']" />
+    </rules>
+
+-->
+<!--
+    <after css:theme-children="html head"
+           css:content="html head meta" />
+-->
+<!--
+    <after css:theme-children="html head"
+           css:content="html head base" />
+-->
+
+    <!-- top navigation -->
+    <!-- left top navigation -->
+<!--
+    <replace css:theme-children="ul.ikoba-navbar-left"
+             css:content-children="ul.ikoba-navbar-left" />
+-->
+
+    <!-- the language selector
+
+      quite tricky. We insert a <li> element at beginning of
+      ul.ikoba-navbar-right. This <li> has a header element
+      (the 'Language' button) and the a number of <li> elements
+      representing the supported languages.
+      We change the input content of format
+
+         <a href="foo" title="Lang">Abbrev</a>
+
+      to
+
+         <a href="foo">Lang</a>
+
+      creating new nodes (<xsl:element>) with attributes copied over.
+    -->
+    <before css:theme-children="ul.ikoba-navbar-right">
+      <li class="dropdown ikoba-langselect">
+        <a href="#" class="dropdown-toggle"
+           data-toggle="dropdown">
+           <img src="/static/img/earth.png" />
+        </a>
+        <ul class="dropdown-menu">
+          <xsl:for-each css:select=" .language a">
+            <li class="ikoba-language">
+              <xsl:element name="a">
+                <xsl:attribute name="href">
+                  <xsl:value-of select="@href" />
+                </xsl:attribute>
+                <xsl:value-of select="@title" />
+              </xsl:element>
+            </li>
+          </xsl:for-each>
+        </ul>
+      </li>
+    </before>
+
+    <!-- Make all data tables responsive -->
+
+    <replace css:content="table.ikoba-data-table">
+      <div class="table-responsive">
+        <xsl:apply-templates css:select="table.ikoba-data-table" mode="raw"/>
+      </div>
+    </replace>
+
+   </rules>
+
+</rules>
Index: /main/ikobacustom.uniben/trunk/layout/static/custom.css
===================================================================
--- /main/ikobacustom.uniben/trunk/layout/static/custom.css	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/layout/static/custom.css	(revision 14174)
@@ -0,0 +1,73 @@
+a {
+  color: #03418a;
+}
+
+a:hover {
+  color: #0d3664;
+}
+
+.topbar a {
+    color: white;
+}
+
+.navbar-inverse {
+  background-color: #0d3664;
+  background-image: -webkit-linear-gradient(top, #0d3664 0%, #03418a 100%);
+  background-image: linear-gradient(to bottom, #0d3664 0%, #03418a 100%);
+  background-repeat: repeat-x;
+}
+
+.navbar-inverse .navbar-brand,
+.navbar-inverse .navbar-nav > li > a {
+  color: #cccccc;
+}
+
+.navbar-inverse .navbar-nav > li > a:hover,
+.navbar-inverse .navbar-nav > .active > a,
+.navbar-inverse .navbar-nav > .active > a:hover,
+.navbar-inverse .navbar-nav > .active > a:focus {
+  color: #ffffff;
+  background-color: #03418a;
+  background-image: none;
+  background-image: none;
+  filter: none;
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+
+.navbar-inverse .navbar-toggle {
+  border-color: #03418a;
+}
+
+.navbar-inverse .navbar-toggle:hover,
+.navbar-inverse .navbar-toggle:focus {
+  background-color: #03418a;
+}
+
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .open > a:hover,
+.navbar-inverse .navbar-nav > .open > a:focus {
+  background-color: #03418a;
+  color: #fff;
+}
+
+/* Logo container customizations */
+
+#logo-container-narrow {
+  position: absolute;
+  top: -7px;
+  right: -11px;
+}
+
+#logo-container-wide {
+  position: absolute;
+  top: -7px;
+  right: -11px;
+}
+.ikoba-content-narrow .alert {
+  margin-right: 35px;
+}
+
+.ikoba-content-wide .alert {
+  margin-right: 50px;
+}
Index: /main/ikobacustom.uniben/trunk/layout/theme.html
===================================================================
--- /main/ikobacustom.uniben/trunk/layout/theme.html	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/layout/theme.html	(revision 14174)
@@ -0,0 +1,586 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Base · Twitter Bootstrap</title>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="description" content="">
+    <meta name="author" content="">
+
+    <!-- stylesheets -->
+    <link href="static/bootstrap/css/bootstrap.min.css"
+          rel="stylesheet">
+    <link href="static/bootstrap/css/bootstrap-theme.min.css"
+          rel="stylesheet">
+    <link href="static/css/jquery.dataTables.css"
+          rel="stylesheet">
+    <link href="static/css/jquery-ui.css"
+          rel="stylesheet">
+    <link href="static/css/base.css" rel="stylesheet">
+    <link href="static_custom/custom.css" rel="stylesheet">
+    <link href="static/css/file-upload.css" rel="stylesheet">
+
+    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
+    <!--[if lt IE 9]>
+      <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
+      <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
+      <link href="static/css/IE8.css" rel="stylesheet">
+    <![endif]-->
+
+    <script src="static/js/ikoba-functions.js"></script>
+
+    <!-- icons -->
+    <link rel="shortcut icon" href="static_custom/favicon.ico">
+
+
+  </head>
+
+  <body id="ikoba-body">
+
+    <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
+      <div class="navbar-header">
+        <button type="button" class="navbar-toggle" data-toggle="collapse"
+                  data-target="#ikoba-primary-nav">
+          <span class="icon-bar"></span>
+          <span class="icon-bar"></span>
+          <span class="icon-bar"></span>
+        </button>
+        <a class="navbar-brand active ikoba-brand-link" href="/">WAeUP.Ikoba</a>
+      </div>
+      <div class="navbar-collapse collapse"
+           id="ikoba-primary-nav">
+        <ul class="nav navbar-nav ikoba-navbar-left">
+          <li class="active"><a href="#">Products</a></li>
+          <li><a href="#about">Customers</a></li>
+          <li class="dropdown">
+            <a href="#" class="dropdown-toggle"
+               data-toggle="dropdown">My Data
+               <b class="caret"></b>
+            <ul class="dropdown-menu">
+              <li><a href="#">Base Data</a></li>
+              <li><a href="#">Documents</a></li>
+              <li><a href="#">Contracts</a></li>
+              <li><a href="#">History</a></li>
+            </ul>
+          </li>
+        </ul>
+
+        <ul class="nav navbar-nav navbar-right ikoba-navbar-right">
+          <li class="dropdown ikoba-langselect">
+            <a href="#" class="dropdown-toggle"
+               data-toggle="dropdown">
+               <img src="static/img/earth.png" />
+            <ul class="dropdown-menu">
+              <li class="ikoba-language"><a href="#">English</a></li>
+              <li class="ikoba-language"><a href="#">Français</a></li>
+              <li class="ikoba-language"><a href="#">Deutsch</a></li>
+              <li class="ikoba-language"><a href="#">Hausa</a></li>
+              <li class="ikoba-language"><a href="#">Yoruba</a></li>
+              <li class="ikoba-language"><a href="#">Igbo</a></li>
+            </ul>
+          </li>
+          <li><a href="#">Contact</a></li>
+          <li><a href="#">Logout</a></li>
+        </ul>
+      </div><!--/.nav-collapse -->
+    </div><!-- /.navbar -->
+
+    <div class="container">
+
+      <div class="row">
+        <div class="col-md-12 container kofa-maintenance">
+          <div class="well">
+            <div class="maintenance-warning">
+                M A I N T E N A N C E &nbsp;&nbsp;&nbsp; M O D E
+            </div> <!-- /maintenance-warning -->
+          </div> <!-- /well -->
+        </div> <!-- /kofa-maintenance -->
+      </div>
+
+      <div class="row">
+        <div class="col-md-12 container ikoba-breadcrumbs-box-long">
+          <div class="well">
+            <ol class="breadcrumb ikoba-breadcrumbs">
+              <li>
+                <a href="#">
+                  Home
+                </a>
+              </li>
+              <li>
+                <a href="#">
+                  Subtopic 1
+                </a>
+              </li>
+              <li>
+                <a href="#">
+                  Subtopic 2
+                </a>
+              </li>
+              <li>
+                <a href="#">
+                  Subtopic 3
+                </a>
+              </li>
+            </ol><!-- /ikoba-breadcrumbs -->
+          </div> <!-- /well -->
+        </div> <!-- /ikoba-breadcrumbs-box-long -->
+      </div>
+
+      <div class="row">
+        <div class="col-md-8 container ikoba-breadcrumbs-box-short">
+          <div class="well">
+            <ol class="breadcrumb ikoba-breadcrumbs">
+              <li>
+                <a href="#">
+                  Home
+                </a>
+              </li>
+              <li>
+                <a href="#">
+                  Subtopic 1
+                </a>
+              </li>
+              <li>
+                <a href="#">
+                  Subtopic 2
+                </a>
+              </li>
+              <li>
+                <a href="#">
+                  Subtopic 3
+                </a>
+              </li>
+            </ol><!-- /ikoba-breadcrumbs -->
+          </div> <!-- /well -->
+        </div> <!-- /ikoba-breadcrumbs-box-short -->
+
+        <div class="col-md-4 container ikoba-customer-status-box">
+          <div class="well">
+            <div class="wfstatus">
+                K1000000 &bull; quite long customer status
+            </div> <!-- /wfstatus -->
+          </div> <!-- /well -->
+        </div> <!-- /ikoba-customer-status-box -->
+
+      </div> <!-- /row -->
+
+      <div class="row">
+        <div class="col-md-2 ikoba-sidebar">
+          <div class="sidebar-nav well">
+            <div>
+              <div class="ikoba-sidebar-title">Customer Name</div>
+              <ul class="nav">
+                <li><a href="#">Base Data</a></li>
+                <li><a href="#">Documents</a></li>
+                <li><a href="#">Contracts</a></li>
+                <li><a href="#">History</a></li>
+              </ul>
+            </div>
+            <div>
+              <div class="ikoba-sidebar-title">Manager Name</div>
+              <ul class="nav">
+                <li><a href="#">Portal Configuration</a></li>
+                <li><a href="#">Portal Users</a></li>
+                <li><a href="#">Data Center</a></li>
+                <li><a href="#">Reports</a></li>
+                <li><a href="#">My Preferences</a></li>
+                <li><a href="#">My Roles</a></li>
+              </ul>
+            </div>
+          </div> <!-- /well -->
+        </div> <!-- /ikoba-sidebar -->
+        <div class="col-md-10 content ikoba-content-narrow">
+          <div id="logo-container-narrow">
+            <img src="static_custom/pcn_logo.png" width="65">
+          </div>
+          <div class="well">
+            <div class="ikoba-content">
+              <div class="actionbar">
+                <a class="btn btn-default" href="">
+                  <img src="static/img/actionicon_modify.png"
+                       alt="Manage" />
+                       Manage
+                </a>
+                <a class="btn btn-default" href="">
+                  <img src="static/img/actionicon_trigtrans.png"
+                       alt="Trigger transition" />
+                       Trigger transition
+                </a>
+              </div>
+              <div class="alert alert-danger">
+                  There were errors ...
+              </div>
+              <div class="alert alert-warning">
+                  Be careful ...
+              </div>
+              <div class="alert alert-success">
+                  You have successfully ...
+              </div>
+              <h1>WAeUP.Ikoba Theme</h1>
+
+              <p class="ikoba-content-desc">
+                A sample Diazo theme for WAeUP.Ikoba
+                <br />
+                Fusce dapibus, tellus ac cursus commodo.
+              </p>
+              <p>
+                Donec id elit non mi porta gravida at eget metus. Fusce
+                dapibus, tellus ac cursus commodo, tortor mauris
+                condimentum nibh, ut fermentum massa justo sit amet
+                risus. Etiam porta sem malesuada magna mollis
+                euismod. Donec sed odio dui.
+              </p>
+              <img src="static/img/bg.png" />
+
+              <!-- Tabs... -->
+              <br /><br />
+              <h3>Sample Tabs</h3>
+              <ul id="tabs" class="tabs nav nav-tabs" data-tabs="tabs">
+                <li class="active">
+                  <a href="#tab-1" data-toggle="tab">Tab 1</a>
+                </li>
+                <li class="">
+                  <a href="#tab-2" data-toggle="tab">Tab 2</a>
+                </li>
+                <li class="">
+                  <a href="#tab-3" data-toggle="tab">Tab 3</a>
+                </li>
+              </ul>
+              <div class="tab-content">
+                <div id="tab-1" class="tab-pane active">
+                  <br />
+                  <p>
+                    Tab 1 content...<br />  Donec id elit non mi porta
+                    gravida at eget metus. Fusce dapibus, tellus ac
+                    cursus commodo.
+                  </p>
+                </div>
+                <div id="tab-2" class="tab-pane">
+                  <br />
+                  <p>
+                    Tab 2 content...<br />  Tortor mauris condimentum
+                    nibh, ut fermentum massa justo sit amet
+                    risus. Etiam porta sem malesuada.
+                  </p>
+                </div>
+                <div id="tab-3" class="tab-pane">
+                  <br />
+                  <p>
+                    Tab 3 content...<br /> Magna mollis euismod. Donec
+                    sed odio dui.
+                  </p>
+                </div>
+
+                <br>
+
+                <h3>Sample Tooltip</h3>
+                <br>Move the mouse over the button:
+                <div class="btn btn-default" data-toggle="tooltip"
+                     title="This is a tooltip">Sample Tooltip
+                </div>
+
+                <br><br>
+
+                <h3>File Upload</h3>
+                <br>
+                <a href="#">Birth Certificate Scan</a>
+                <input type="submit"
+                       value="Delete file"
+                       class="btn btn-danger" />
+                <div class="btn btn-default btn-file">
+                  Select new file&hellip;
+                  <input type="file"/>
+                </div>
+                <input type="submit"
+                       value="Upload selected file"
+                       class="btn btn-primary" />
+                (10 kB max.)
+                <br><br>
+
+        				<div class="input-group">
+        					<div class="input-group-btn">
+        						<div class="btn btn-default btn-file">
+                      Select new file&hellip;
+                      <input type="file">
+                    </div>
+        					</div>
+        					<input type="text" class="form-control" readonly>
+                  <div class="input-group-btn">
+                    <input type="submit"
+                           value="Upload selected file"
+                           class="btn btn-primary" />
+                    (10 kB max.)
+                  </div>
+                </div>
+
+
+              </div><!-- /tab-content -->
+            </div> <!-- /ikoba-content -->
+          </div> <!-- /well -->
+        </div> <!-- /ikoba-content-narrow -->
+      </div> <!-- /row -->
+
+      <div class="row">
+        <div class="col-md-12 content ikoba-content-wide">
+          <div id="logo-container-wide">
+            <img src="static_custom/pcn_logo.png" width="80">
+          </div>
+          <div class="well ikoba-content">
+            <div class="actionbar">
+              <a class="btn btn-default" href="">
+                <img src="static/img/actionicon_modify.png"
+                     alt="Manage" />
+                     Manage
+              </a>
+              <a class="btn btn-default" href="">
+                <img src="static/img/actionicon_trigtrans.png"
+                     alt="Trigger transition" />
+                     Trigger transition
+              </a>
+            </div>
+            <div class="alert alert-danger">
+                There were errors ...
+            </div>
+            <div class="hero-unit">
+              <h1>Hello there!</h1>
+              <p>this is...</p>
+            </div>
+            <div>...content that spans the whole page.</div>
+            <br><br>
+
+            <h3>Accordions</h3>
+            <div class="panel-group" id="accordion" role="tablist"
+                 aria-multiselectable="true">
+              <div class="panel panel-default">
+                <div class="panel-heading" role="tab" id="headingTwo">
+                  <h4 class="panel-title">
+                    <a class="collapsed" data-toggle="collapse"
+                       data-parent="#accordion" href="#collapseTwo"
+                       aria-expanded="false" aria-controls="collapseTwo">
+                      Collapsible Group
+                    </a>
+                  </h4>
+                </div>
+                <div id="collapseTwo" class="panel-collapse collapse"
+                     role="tabpanel" aria-labelledby="headingTwo">
+                  <div class="panel-body">
+                    Anim pariatur cliche reprehenderit, enim eiusmod high life
+                    terry richardson ad squid. 3 wolf moon officia aute, non
+                    skateboard dolor brunch. Food truck quinoa laborum eiusmod.
+                  </div>
+                </div>
+              </div>
+            </div>
+
+            <br><br>
+            <h3>A Sample Data Table</h3>
+            <div class="table-responsive">
+              <table id="#datatable" class="table table-striped dataTable">
+                <thead>
+                  <tr>
+                    <th>Id</th>
+                    <th>Name</th>
+                    <th>Portal Roles</th>
+                    <th>Local Roles</th>
+                    <th></th>
+                  </tr>
+                </thead><tbody><tr><td>admin</td>
+                    <td>John</td>
+                    <td nowrap="nowrap">Portal Manager <br />
+                      Academics Officer (view only)
+                    </td>
+                    <td>Owner</td>
+                    <td class="text-right">
+                      <form method="post">
+                        <input type="hidden" name="userid" value="admin" />
+                        <input class="btn btn-primary btn-sm" type="submit"
+                               name="manage" value="Manage" />
+                        <input class="btn btn-default btn-sm" type="submit"
+                               name="delete" value="Remove" />
+                      </form>
+                    </td>
+                  </tr>
+                  <tr>
+                    <td>tester</td>
+                    <td>Vitali</td>
+                    <td nowrap="nowrap">Academics Officer (view only)</td>
+                    <td>Owner</td>
+                    <td class="text-right">
+                      <form method="post">
+                        <input type="hidden" name="userid" value="your_id" />
+                        <input class="btn btn-sm btn-primary" type="submit"
+                               name="manage" value="Manage" />
+                        <input class="btn btn-sm btn-default" type="submit"
+                               name="delete" value="Remove" />
+                      </form>
+                    </td>
+                  </tr>
+                </tbody>
+              </table>
+            </div>
+
+            <br><br>
+            <h3>A Sample Inline Form</h3>
+            <form method="POST" class="form-inline">
+            <br />
+              <div class="form-group">
+                <input class="btn btn-primary" type="submit" name="search"
+                  value="Find customers(s)" />
+              </div>
+              <div class="form-group">
+                <select name="searchtype" class="form-control">
+                  <option value="customer_id">with id</option>
+                  <option value="fullname">with name</option>
+                  <option value="reg_number">with registration number</option>
+                  <option value="suspended">
+                    with deactivated account (search term omitted)
+                  </option>
+                </select>
+              </div>
+              <div class="form-group">
+                <input type="text" class="form-control" name="searchterm" />
+              </div>
+            </form>
+
+            <br><br>
+            <h3>A Sample Display Form Table</h3>
+            <table class="form-table">
+              <tbody>
+                <tr>
+                  <td class="fieldname">Code:</td>
+                  <td>app2012</td>
+                </tr>
+                <tr>
+                 <td class="fieldname">Application Target:</td>
+                  <td>General Studies</td>
+                </tr>
+                 <tr>
+                  <td class="fieldname">
+                    Application Mode (Donec id elit non mi porta gravida.):
+                  </td>
+                  <td>Create Application Records</td>
+                </tr>
+              </tbody>
+            </table>
+
+            <br><br>
+            <h3>A Sample Edit/Manage Form Table</h3>
+            <table class="form-table">
+              <tbody>
+                <tr>
+                  <td class="fieldname">Code:</td>
+                  <td>app2012</td>
+                </tr>
+                <tr>
+                  <td class="fieldname">* Title:</td>
+                  <td>
+                    <input class="textType" id="form.title" name="form.title"
+                           size="20" type="text"
+                           value="General Studies 2012/2013" />
+                  </td>
+                </tr>
+                <tr>
+                  <td class="fieldname">Any phone number:</td>
+                  <td>
+                    <select id="form.phone.country" name="form.phone.country"
+                              size="1" class="phone-input">
+                      <option value="+234">Nigeria (+234)</option>
+                      <option value="+49">Germany (+49)</option>
+                      <option value="+1">U.S. (+1)</option>
+                    </select>-
+                    <input class=" phone-input textType" id="form.phone.area"
+                           name="form.phone.area" size="20" type="text"
+                           value="" />-
+                    <input class=" phone-input textType" id="form.phone.ext"
+                           name="form.phone.ext" size="20" type="text" value="" />
+                  </td>
+                </tr>
+                <tr>
+                  <td class="fieldname">Any text field:</td>
+                  <td>
+                      <textarea id="any_id"
+                          name="any_name" rows="5">Placeholder text</textarea>
+                  </td>
+                </tr>
+                <tr>
+                  <td class="fieldname">*
+                      A select field coming from zope.formlib:
+                  </td>
+                  <td>
+                    <div class="value">
+                      <select id="form.mode" name="form.mode" size="1">
+                        <option selected="selected" value="create">
+                          Create Records
+                        </option>
+                        <option value="update">Update Records
+                        </option>
+                      </select>
+                    </div>
+                    <input name="form.mode-empty-marker"
+                           type="hidden" value="1" />
+                  </td>
+                </tr>
+                <tr>
+                  <td class="fieldname">A datepicker field:</td>
+                  <td>
+                    <input class="datepicker-le-year textType"
+                                 id="form.any_date" name="form.any_date"
+                                 size="20" type="text" value="" />
+                  </td>
+                </tr>
+
+                <tr>
+                  <td class="fieldname">
+                      A select field in Ikoba pagetemplates:
+                  </td>
+                  <td>
+                      <select id="any_id" name="any_name" size="1"
+                              class="form-control half">
+                        <option selected="selected" value="create">
+                            Create Records
+                        </option>
+                        <option value="update">
+                            Update Records
+                        </option>
+                      </select>
+                  </td>
+                </tr>
+              </tbody>
+            </table>
+          </div> <!-- /well ikoba-content-->
+        </div> <!-- /ikoba-content-wide -->
+      </div> <!-- /row -->
+      <div id="bottom" class="footer">
+        <p class="credit muted text-right">
+          Ikoba - Application & Registration System<br />
+          Copyright &copy; WAeUP Group
+          <script type="text/javascript">
+          renderCurrentYear();
+          </script>
+        </p>
+      </div>
+    </div> <!-- /container -->
+
+    <script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
+    <script src="static/bootstrap/js/bootstrap.min.js"></script>
+    <script src="static/js/ikoba-scroll.js"></script>
+    <script src="static/js/ikoba-activate-tab.js"></script>
+    <script src="static/js/ikoba-scrollto.js"></script>
+    <script src="static/js/file-upload.js"></script>
+    <script class="ikoba-script-tooltip"
+        src="static/js/ikoba-tooltip.js">
+    </script>
+    <script class="ikoba-script-datatable"
+        src="static/js/jquery.dataTables.min.js">
+    </script>
+    <script class="ikoba-script-datatable"
+        src="static/js/ikoba-datatables.js">
+    </script>
+    <script class="jquery-ui"
+        src="static/js/jquery-ui.min.js">
+    </script>
+    <script class="ikoba-script-datepicker"
+        src="static/js/ikoba-datepicker.js">
+    </script>
+  </body>
+</html>
Index: /main/ikobacustom.uniben/trunk/setup.py
===================================================================
--- /main/ikobacustom.uniben/trunk/setup.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/setup.py	(revision 14174)
@@ -0,0 +1,100 @@
+import os
+from setuptools import setup, find_packages
+
+version = '0.1'
+
+install_requires =[
+    'setuptools',
+    'grok',
+    'waeup.ikoba >= 0.2dev',
+    ],
+
+# Having beaker installed additionally is a feature very recommended
+# for production use. The default buildout includes beaker for tests,
+# start scripts, and other parts that can benefit from it. The windows
+# buildout does not include it due to compiling problems with the
+# beaker package.
+beaker_require = [
+    'dolmen.beaker',
+    ]
+
+tests_require = [
+    'z3c.testsetup',
+    'zope.app.testing',
+    'zope.testbrowser',
+    'zope.testing',
+    'unittest2',
+    ]
+
+docs_require = [
+    'Sphinx',
+    'collective.recipe.sphinxbuilder',
+    'docutils',
+    'roman',
+    'repoze.sphinx.autointerface',
+    ]
+
+diazo_require = [
+    'diazo',
+    'webob',
+    ]
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+long_description = (
+    read('README.txt')
+    + '\n\n'
+    + read('CHANGES.txt')
+    + '\n\n'
+    + 'Download\n'
+    + '********\n'
+    )
+
+setup(name = 'ikobacustom.skeleton',
+      version = version,
+      description = "Customizations of the waeup.ikoba package",
+      long_description = long_description,
+
+      keywords = "portal waeup ikoba regstration application",
+      # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+      classifiers = [
+        'Development Status :: 3 - Alpha',
+        'Environment :: Web Environment',
+        'Intended Audience :: Education',
+        'License :: OSI Approved :: GNU General Public License (GPL)',
+        'Programming Language :: Python',
+        'Operating System :: POSIX',
+        'Operating System :: POSIX :: Linux',
+        'Framework :: Zope3',
+        'Topic :: Education',
+        'Topic :: Internet :: WWW/HTTP',
+        ],
+      author = "The WAeUP Team.",
+      author_email = "",
+      url = "http://www.waeup.org/",
+      license = "GPL",
+      package_dir = {'': 'src'},
+      packages= find_packages('src'),
+      namespace_packages = ['ikobacustom',],
+      include_package_data = True,
+      zip_safe = False,
+      install_requires = install_requires,
+      tests_require = tests_require,
+      extras_require = dict(
+        test = tests_require,
+        docs = docs_require,
+        beaker = beaker_require,
+        diazo = diazo_require,
+        ),
+      entry_points="""
+      # Add entry points here
+      [console_scripts]
+      ikoba-debug = grokcore.startup:interactive_debug_prompt
+      ikobactl = grokcore.startup:zdaemon_controller
+      [paste.app_factory]
+      main = waeup.ikoba.startup:env_app_factory
+      debug = waeup.ikoba.startup:env_debug_app_factory
+
+      """,
+      )
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/__init__.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/__init__.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/__init__.py	(revision 14174)
@@ -0,0 +1,7 @@
+# namespace package boilerplate
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError, e:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/browser/__init__.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/browser/__init__.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/browser/__init__.py	(revision 14174)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/browser/pdf.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/browser/pdf.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/browser/pdf.py	(revision 14174)
@@ -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.ikoba.browser.pdf import PDFCreator
+
+class SkeletonPDFCreator(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/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/configure.zcml
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/configure.zcml	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/configure.zcml	(revision 14174)
@@ -0,0 +1,20 @@
+<configure xmlns="http://namespaces.zope.org/zope"
+           xmlns:grok="http://namespaces.zope.org/grok">
+
+  <!-- Loading waeup.ikoba also configures a lot of other needed
+       packages correctly.
+  -->
+  <include package="waeup.ikoba" />
+  <include package="ikobacustom.skeleton" file="locales.zcml" />
+  <grok:grok package="." />
+
+  <!-- Register edit widget for PhoneNumbers -->
+  <adapter
+      for="waeup.ikoba.schema.interfaces.IPhoneNumber
+           zope.publisher.interfaces.browser.IBrowserRequest"
+      provides="zope.formlib.interfaces.ISimpleInputWidget"
+      factory="ikobacustom.skeleton.widgets.phonewidget.NigeriaPhoneWidget"
+      permission="zope.Public"
+      />
+
+</configure>
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/__init__.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/__init__.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/__init__.py	(revision 14174)
@@ -0,0 +1,3 @@
+"""This package contains everything regarding customers.
+"""
+# Make this a package.
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/batching.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/batching.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/batching.py	(revision 14174)
@@ -0,0 +1,56 @@
+## $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 skeleton documents.
+"""
+import grok
+from waeup.ikoba.customers.batching import (
+    CustomerProcessor,
+    CustomerDocumentProcessorBase, ContractProcessorBase)
+from ikobacustom.skeleton.customers.interfaces import (
+    ISkeletonCustomer,
+    ISkeletonCustomerDocument, ISkeletonContract)
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+
+class SkeletonCustomerProcessor(CustomerProcessor):
+    """A batch processor for ICustomer objects.
+    """
+    util_name = 'skeletoncustomerprocessor'
+    grok.name(util_name)
+    name = _('Skeleton Customer Processor')
+    iface = ISkeletonCustomer
+
+
+class SkeletonCustomerDocumentProcessor(CustomerDocumentProcessorBase):
+    """A batch processor for ISkeletonCustomerDocument objects.
+    """
+    util_name = 'skeletoncustomerdocumentprocessor'
+    grok.name(util_name)
+    name = _('Skeleton Customer Document Processor')
+    iface = ISkeletonCustomerDocument
+    factory_name = 'waeup.SkeletonCustomerDocument'
+
+
+class SkeletonContractProcessor(ContractProcessorBase):
+    """A batch processor for ISkeletonContract objects.
+    """
+    util_name = 'skeletoncontractprocessor'
+    grok.name(util_name)
+    name = _('Skeleton Customer Contract Processor')
+    iface = ISkeletonContract
+    factory_name = 'waeup.SkeletonContract'
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/browser.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/browser.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/browser.py	(revision 14174)
@@ -0,0 +1,46 @@
+## $Id$
+##
+## Copyright (C) 2015 Uli Fouquet & Henrik Bettermann
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+##
+
+from zope.component import getUtility
+from waeup.ikoba.interfaces import IIkobaUtils, IExtFileStore
+from waeup.ikoba.customers.browser import (
+    PDFContractSlip, CustomerBaseEditFormPage)
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+class PDFContractSlip(PDFContractSlip):
+    """Deliver pdf file including metadata.
+    """
+
+    @property
+    def label(self):
+        portal_language = getUtility(IIkobaUtils).PORTAL_LANGUAGE
+        return self.context.title
+
+
+class CustomerBaseEditFormPage(CustomerBaseEditFormPage):
+    """ View to edit customer base data
+    """
+
+    def dataNotComplete(self):
+        store = getUtility(IExtFileStore)
+        error = ''
+        if not store.getFileByContext(self.context, attr=u'passport.jpg'):
+            error += _('Passport picture is missing.')
+        if error:
+            return error
+        return
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/contracts.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/contracts.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/contracts.py	(revision 14174)
@@ -0,0 +1,70 @@
+## $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
+##
+"""
+Customer contract components.
+"""
+
+import grok
+from zope.component.interfaces import IFactory
+from zope.interface import implementedBy
+from waeup.ikoba.utils.helpers import attrs_to_fields
+from waeup.ikoba.customers.interfaces import ICustomerNavigation
+from waeup.ikoba.customers.contracts import ContractBase
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+from ikobacustom.skeleton.customers.interfaces import (
+    ISkeletonContract, ISkeletonContractEdit, ISkeletonContractProcess,
+    ISkeletonContractOfficialUse)
+
+
+class SkeletonContract(ContractBase):
+    """This is a sample contract.
+    """
+
+    grok.implements(
+        ISkeletonContractProcess,
+        ISkeletonContract,
+        ISkeletonContractEdit,
+        ICustomerNavigation)
+
+    contract_category = 'sample'
+
+    form_fields_interface = ISkeletonContract
+
+    edit_form_fields_interface = ISkeletonContractEdit
+
+    ou_form_fields_interface = ISkeletonContractOfficialUse
+
+    check_docs_interface = ISkeletonContract
+
+SkeletonContract = attrs_to_fields(SkeletonContract)
+
+
+# Contracts must be importable. So we might need a factory.
+class SkeletonContractFactory(grok.GlobalUtility):
+    """A factory for contracts.
+    """
+    grok.implements(IFactory)
+    grok.name(u'waeup.SkeletonContract')
+    title = u"Create a new contract.",
+    description = u"This factory instantiates new sample contract instances."
+
+    def __call__(self, *args, **kw):
+        return SkeletonContract(*args, **kw)
+
+    def getInterfaces(self):
+        return implementedBy(SkeletonContract)
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/customer.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/customer.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/customer.py	(revision 14174)
@@ -0,0 +1,55 @@
+## $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
+##
+"""
+Container for the various objects owned by customers.
+"""
+import grok
+from zope.component.interfaces import IFactory
+from zope.interface import implementedBy
+from waeup.ikoba.utils.helpers import attrs_to_fields
+from waeup.ikoba.customers.customer import Customer
+from waeup.ikoba.customers.interfaces import ICustomerNavigation
+from ikobacustom.skeleton.customers.interfaces import ISkeletonCustomer
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+
+class SkeletonCustomer(Customer):
+    """This is a customer container for the various objects
+    owned by customers.
+    """
+    grok.implements(ISkeletonCustomer, ICustomerNavigation)
+    grok.provides(ISkeletonCustomer)
+
+    form_fields_interface = ISkeletonCustomer
+
+SkeletonCustomer = attrs_to_fields(SkeletonCustomer)
+
+
+class SkeletonCustomerFactory(grok.GlobalUtility):
+    """A factory for customers.
+    """
+    grok.implements(IFactory)
+    grok.name(u'waeup.Customer')
+    title = u"Create a new skeleton customer.",
+    description = u"This factory instantiates new skeleton customer instances."
+
+    def __call__(self, *args, **kw):
+        return SkeletonCustomer()
+
+    def getInterfaces(self):
+        return implementedBy(SkeletonCustomer)
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/documents.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/documents.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/documents.py	(revision 14174)
@@ -0,0 +1,62 @@
+## $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
+##
+"""
+Customer document components.
+"""
+import grok
+from zope.component.interfaces import IFactory
+from zope.interface import implementedBy
+from waeup.ikoba.utils.helpers import attrs_to_fields
+from waeup.ikoba.customers.interfaces import ICustomerNavigation
+from waeup.ikoba.customers.documents import CustomerDocumentBase
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+from ikobacustom.skeleton.customers.interfaces import ISkeletonCustomerDocument
+
+
+class SkeletonCustomerDocument(CustomerDocumentBase):
+    """This is a sample customer document.
+    """
+
+    grok.implements(ISkeletonCustomerDocument, ICustomerNavigation)
+    grok.provides(ISkeletonCustomerDocument)
+
+    # Ikoba can store any number of files per Document object.
+    # However, we highly recommend to associate and store
+    # only one file per Document object. Thus the following
+    # tuple should contain only a single filename string.
+    filenames = ('sample',)
+
+    form_fields_interface = ISkeletonCustomerDocument
+
+SkeletonCustomerDocument = attrs_to_fields(SkeletonCustomerDocument)
+
+
+# Customer documents must be importable. So we need a factory.
+class SkeletonCustomerDocumentFactory(grok.GlobalUtility):
+    """A factory for customer documents.
+    """
+    grok.implements(IFactory)
+    grok.name(u'waeup.SkeletonCustomerDocument')
+    title = u"Create a new document.",
+    description = u"This factory instantiates new sample document instances."
+
+    def __call__(self, *args, **kw):
+        return SkeletonCustomerDocument(*args, **kw)
+
+    def getInterfaces(self):
+        return implementedBy(SkeletonCustomerDocument)
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/export.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/export.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/export.py	(revision 14174)
@@ -0,0 +1,49 @@
+## $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 skeleton customer objects and subobjects.
+"""
+import grok
+from waeup.ikoba.customers.export import (
+    CustomerExporter, CustomerDocumentExporterBase, ContractExporterBase)
+from ikobacustom.skeleton.customers.interfaces import (
+    ISkeletonCustomer, ISkeletonCustomerDocument, ISkeletonContractProcess)
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+class SkeletonCustomerExporter(CustomerExporter):
+    """Exporter for Customers.
+    """
+    iface = ISkeletonCustomer
+    title = _(u'Skeleton Customers')
+
+
+class SkeletonCustomerDocumentExporter(CustomerDocumentExporterBase):
+    """Exporter for documents.
+    """
+    grok.name('skeletoncustomerdocuments')
+    iface = ISkeletonCustomerDocument
+    title = _(u'Skeleton Customer Documents')
+    class_name = 'SkeletonCustomerDocument'
+
+
+class SkeletonContractExporter(ContractExporterBase):
+    """Exporter for Contract instances.
+    """
+    grok.name('skeletoncontracts')
+    iface = ISkeletonContractProcess
+    title = _(u'Skeleton Customer Contracts')
+    class_name = 'SkeletonContract'
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/fileviewlets.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/fileviewlets.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/fileviewlets.py	(revision 14174)
@@ -0,0 +1,84 @@
+## $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
+##
+
+import grok
+from waeup.ikoba.interfaces import MessageFactory as _
+
+from waeup.ikoba.browser.fileviewlets import (
+    FileDisplay, FileUpload, Image)
+
+from ikobacustom.skeleton.customers.documents import SkeletonCustomerDocument
+
+from waeup.ikoba.customers.browser import (
+    DocumentDisplayFormPage,
+    DocumentManageFormPage,
+    DocumentEditFormPage,
+    PDFDocumentSlip,)
+
+grok.templatedir('browser_templates')
+
+# File viewlets for customer documents
+
+class SampleScanManageUpload(FileUpload):
+    """Scan upload viewlet for officers.
+    """
+    grok.order(1)
+    grok.context(SkeletonCustomerDocument)
+    grok.view(DocumentManageFormPage)
+    grok.require('waeup.manageCustomer')
+    label = _(u'Sample Scan')
+    title = _(u'Sample Scan')
+    mus = 1024 * 200
+    download_name = u'sample'
+    download_filename = download_name
+    tab_redirect = '#tab2'
+
+
+class SampleScanEditUpload(SampleScanManageUpload):
+    """Scan upload viewlet for customer.
+    """
+    grok.view(DocumentEditFormPage)
+    grok.require('waeup.handleCustomer')
+
+
+class SampleScanDisplay(FileDisplay):
+    """Scan display viewlet.
+    """
+    grok.order(1)
+    grok.context(SkeletonCustomerDocument)
+    grok.require('waeup.viewCustomer')
+    grok.view(DocumentDisplayFormPage)
+    label = _(u'Sample Scan')
+    title = _(u'Sample Scan')
+    download_name = u'sample'
+    download_filename = download_name
+
+
+class SampleScanImage(Image):
+    """Scan document.
+    """
+    grok.name('sample')
+    grok.context(SkeletonCustomerDocument)
+    grok.require('waeup.viewCustomer')
+    download_name = u'sample'
+    download_filename = download_name
+
+
+class SampleScanPDFSlip(SampleScanDisplay):
+    grok.view(PDFDocumentSlip)
+
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/interfaces.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/interfaces.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/interfaces.py	(revision 14174)
@@ -0,0 +1,86 @@
+## $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
+##
+
+from zope import schema
+from waeup.ikoba.interfaces import IIkobaObject
+from waeup.ikoba.customers.interfaces import (
+    ICustomer, ICustomerDocument, IContract)
+from waeup.ikoba.customers.vocabularies import (
+    ConCatProductSource, CustomerDocumentSource)
+from waeup.ikoba.products.productoptions import ProductOptionField
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+
+class ISkeletonCustomer(ICustomer):
+    """Representation of a customer.
+
+    """
+
+# Customer document interfaces
+
+
+class ISkeletonCustomerDocument(ICustomerDocument):
+    """A customer document.
+
+    """
+
+# Customer contract interfaces
+
+class ISkeletonContract(IContract):
+    """A customer contract sample with document attached.
+
+    """
+
+    document_object = schema.Choice(
+        title = _(u'Document'),
+        source = CustomerDocumentSource(),
+        required = False,
+        )
+
+class ISkeletonContractOfficialUse(IIkobaObject):
+    """Interface for editing official use data.
+
+    """
+
+    comment = schema.Text(
+        title= _('Reason for rejection'),
+        required = False,
+        )
+
+class ISkeletonContractProcess(ISkeletonContract, ISkeletonContractOfficialUse):
+    """Interface for processing contract data.
+    """
+
+    product_options = schema.List(
+        title = _(u'Options/Fees'),
+        value_type = ProductOptionField(),
+        required = False,
+        readonly = False,
+        default = [],
+        )
+
+class ISkeletonContractEdit(ISkeletonContract):
+    """Interface for editing sample contract data by customers.
+
+    """
+
+    document_object = schema.Choice(
+        title = _(u'Document'),
+        source = CustomerDocumentSource(),
+        required = True,
+        )
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/__init__.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/__init__.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/__init__.py	(revision 14174)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/test_browser.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/test_browser.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/test_browser.py	(revision 14174)
@@ -0,0 +1,325 @@
+## $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
+##
+"""
+Document browser and functional tests.
+"""
+import os
+import grok
+import datetime
+from cStringIO import StringIO
+from zope.component import queryUtility, getUtility, createObject
+from zope.event import notify
+from zope.interface.verify import verifyObject, verifyClass
+from zope.testbrowser.testing import Browser
+from hurry.workflow.interfaces import IWorkflowState
+from waeup.ikoba.customers.tests.test_batching import CustomerImportExportSetup
+from waeup.ikoba.customers.tests.test_browser import CustomersFullSetup
+from ikobacustom.skeleton.customers.export import (
+    SkeletonCustomerExporter,
+    SkeletonCustomerDocumentExporter,
+    SkeletonContractExporter)
+from ikobacustom.skeleton.customers.batching import (
+    SkeletonCustomerProcessor,
+    SkeletonCustomerDocumentProcessor,
+    SkeletonContractProcessor)
+from ikobacustom.skeleton.testing import FunctionalLayer, samples_dir
+
+SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
+SAMPLE_IMAGE_BMP = os.path.join(os.path.dirname(__file__), 'test_image.bmp')
+SAMPLE_PDF = os.path.join(os.path.dirname(__file__), 'test_pdf.pdf')
+
+class CustomerImportExportTest(CustomerImportExportSetup):
+
+    layer = FunctionalLayer
+
+    def setup_customizable_params(self):
+        self._contract_category = u'sample'
+        return
+
+    def setup_for_export(self):
+        customer = createObject(u'waeup.Customer')
+        customer.firstname = u'Beate'
+        customer.lastname = u'Mueller'
+        customer.reg_number = u'123'
+        customer.sex = u'f'
+        customer.email = u'aa@aa.aa'
+        IWorkflowState(customer).setState('started')
+        self.app['customers'].addCustomer(customer)
+        document = createObject(u'waeup.SkeletonCustomerDocument')
+        document.title = u'My first document'
+        customer['documents'].addDocument(document)
+        contract = createObject(u'waeup.SkeletonContract')
+        contract.tc_dict = {'en':u'Hello World'}
+        customer['contracts'].addContract(contract)
+        self.customer = customer
+        self.document = document
+        self.contract = contract
+        self.outfile = os.path.join(self.workdir, 'myoutput.csv')
+        return
+
+    def test_export_reimport_customers(self):
+        # we can export all customers in a portal
+        # set values we can expect in export file
+        self.setup_for_export()
+        exporter = SkeletonCustomerExporter()
+        exporter.export_all(self.app, self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertEqual(result,
+            'customer_id,email,firstname,lastname,middlename,phone,'
+            'reg_number,sex,suspended,suspended_comment,password,state,history\r\n'
+            'K1000000,aa@aa.aa,Beate,Mueller,,,123,f,0,,,started,[]\r\n')
+        # We can reimport the file ...
+        processor = SkeletonCustomerProcessor()
+        result = processor.doImport(
+            self.outfile,
+            ['customer_id','email','firstname','lastname','middlename','phone',
+            'reg_number','sex','suspended','suspended_comment','password','state'],
+            mode='create')
+        num, num_fail, finished_path, failed_path = result
+        self.assertEqual(num_fail,1)
+        # ... if we remove the original customer.
+        del self.app['customers']['K1000000']
+        result = processor.doImport(
+            self.outfile,
+            ['customer_id','email','firstname','lastname','middlename','phone',
+            'reg_number','sex','suspended','suspended_comment','password','state'],
+            mode='create')
+        num_succ, num_fail, finished_path, failed_path = result
+        self.assertEqual(num_fail,0)
+        # We can import the same file in update mode if we ignore
+        # the reg_number and email address
+        result = processor.doImport(
+            self.outfile,
+            ['customer_id','xx_email','firstname','lastname','middlename','phone',
+            'xx_reg_number','sex','suspended','suspended_comment','password','state'],
+            mode='update')
+        num_succ, num_fail, finished_path, failed_path = result
+        self.assertEqual(num_succ,1)
+        self.assertEqual(num_fail,0)
+        return
+
+    def test_export_reimport_documents(self):
+        # we can export all documents in a portal
+        # set values we can expect in export file
+        self.setup_for_export()
+        exporter = SkeletonCustomerDocumentExporter()
+        exporter.export_all(self.app, self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertMatches(result,
+            'class_name,document_id,history,state,title,user_id\r\n'
+            'SkeletonCustomerDocument,%s,'
+            '[u\'2014-12-21 17:00:36 WAT - Document created by system\'],'
+            'created,My first document,K1000000\r\n'
+            % self.document.document_id)
+        # We can reimport the file if we change the header (user_id -> customer_id)
+        processor = SkeletonCustomerDocumentProcessor()
+        open(self.outfile, 'wb').write(
+            'customer_id,class_name,document_id,state,title\r\n'
+            'K1000000,SkeletonCustomerDocument,%s,started,My first title\r\n'
+            % self.document.document_id)
+        result = processor.doImport(
+            self.outfile,
+            ['customer_id','class_name','document_id','state','title'],
+            mode='create')
+        num, num_fail, finished_path, failed_path = result
+        # The object exists.
+        self.assertEqual(num_fail,1)
+        # We remove the original document.
+        del self.customer['documents'][self.document.document_id]
+        result = processor.doImport(
+            self.outfile,
+            ['customer_id','class_name','document_id','state','title'],
+            mode='create')
+        num_succ, num_fail, finished_path, failed_path = result
+        self.assertEqual(num_fail,0)
+        # We can import the same file in update mode.
+        result = processor.doImport(
+            self.outfile,
+            ['customer_id','class_name','document_id','state','title'],
+            mode='update')
+        num_succ, num_fail, finished_path, failed_path = result
+        self.assertEqual(num_succ,1)
+        self.assertEqual(num_fail,0)
+        return
+
+    def test_export_reimport_contracts(self):
+        # we can export all contracts in a portal
+        # set values we can expect in export file
+        self.setup_for_export()
+        exporter = SkeletonContractExporter()
+        exporter.export_all(self.app, self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertMatches(result,
+            'class_name,comment,contract_category,contract_id,document_object,'
+            'fee_based,history,last_product_id,product_object,product_options,'
+            'state,tc_dict,title,user_id,valid_from,valid_to\r\n'
+            'SkeletonContract,,sample,%s,,0,'
+            '[u\'2014-12-21 22:26:00 WAT - Contract created by system\']'
+            ',,,[],created,{\'en\': u\'Hello World\'},,K1000000,,\r\n'
+            % self.contract.contract_id)
+        # We can reimport the file if we change the header (user_id -> customer_id)
+        processor = SkeletonContractProcessor()
+        open(self.outfile, 'wb').write(
+            'class_name,contract_category,contract_id,document_object,'
+            'history,last_product_id,product_object,product_options,'
+            'state,tc_dict,title,user_id\r\n'
+            'SkeletonContract,sample,%s,,'
+            '[u\'2014-12-21 22:26:00 WAT - Contract created by system\']'
+            ',,,[],created,{\'en\': u\'Hello World\'},,K1000000\r\n'
+            % self.contract.contract_id)
+        result = processor.doImport(
+            self.outfile,
+            ['class_name','contract_category','contract_id','document_object',
+            'history','last_product_id','product_object','product_options',
+            'state','tc_dict','title','customer_id'],
+            mode='create')
+        num, num_fail, finished_path, failed_path = result
+        # The object exists.
+        self.assertEqual(num_fail,1)
+        # We remove the original contract.
+        del self.customer['contracts'][self.contract.contract_id]
+        result = processor.doImport(
+            self.outfile,
+            ['class_name','contract_category','contract_id','document_object',
+            'history','last_product_id','product_object','product_options',
+            'state','tc_dict','title','customer_id'],
+            mode='create')
+        num_succ, num_fail, finished_path, failed_path = result
+        self.assertEqual(num_fail,0)
+        # We can import the same file in update mode.
+        result = processor.doImport(
+            self.outfile,
+            ['class_name','contract_category','contract_id','document_object',
+            'history','last_product_id','product_object','product_options',
+            'state','tc_dict','title','customer_id'],
+            mode='update')
+        num_succ, num_fail, finished_path, failed_path = result
+        self.assertEqual(num_succ,1)
+        self.assertEqual(num_fail,0)
+        return
+
+class DocumentUITests(CustomersFullSetup):
+    # Tests for customer document related views and pages
+
+    layer = FunctionalLayer
+
+    def setup_customizable_params(self):
+        self._contract_category = u'sample'
+        self._document_factory = 'waeup.SkeletonCustomerDocument'
+        self._contract_factory = 'waeup.SkeletonContract'
+        return
+
+    def test_manage_upload_sample_file(self):
+        # Managers can upload a file via the DocumentManageFormPage
+        # The image is stored even if form has errors
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        self.browser.open(self.customer_path + '/documents/DOC1/manage')
+        # Create a pseudo image file and select it to be uploaded
+        image = open(SAMPLE_IMAGE, 'rb')
+        ctrl = self.browser.getControl(name='samplescanmanageupload')
+        file_ctrl = ctrl.mech_control
+        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
+        # The Save action does not upload files
+        self.browser.getControl("Save").click() # submit form
+        self.assertFalse(
+            'href="http://localhost/app/customers/K1000000/documents/DOC1/sample"'
+            in self.browser.contents)
+        # ... but the correct upload submit button does
+        image = open(SAMPLE_IMAGE)
+        ctrl = self.browser.getControl(name='samplescanmanageupload')
+        file_ctrl = ctrl.mech_control
+        file_ctrl.add_file(image, filename='my_sample_scan.jpg')
+        self.browser.getControl(
+            name='upload_samplescanmanageupload').click()
+        self.assertTrue(
+            'href="http://localhost/app/customers/K1000000/documents/DOC1/sample"'
+            in self.browser.contents)
+        # Browsing the link shows a real image
+        self.browser.open('sample')
+        self.assertEqual(
+            self.browser.headers['content-type'], 'image/jpeg')
+        self.assertEqual(len(self.browser.contents), 2787)
+        # We can't reupload a file. The existing file must be deleted first.
+        self.browser.open(self.customer_path + '/documents/DOC1/manage')
+        self.assertFalse(
+            'upload_samplescanmanageupload' in self.browser.contents)
+        # File must be deleted first
+        self.browser.getControl(name='delete_samplescanmanageupload').click()
+        self.assertTrue(
+            'sample deleted' in self.browser.contents)
+        # Uploading a file which is bigger than 150k will raise an error
+        big_image = StringIO(open(SAMPLE_IMAGE, 'rb').read() * 75)
+        ctrl = self.browser.getControl(name='samplescanmanageupload')
+        file_ctrl = ctrl.mech_control
+        file_ctrl.add_file(big_image, filename='my_sample_scan.jpg')
+        self.browser.getControl(
+            name='upload_samplescanmanageupload').click()
+        self.assertTrue(
+            'Uploaded file is too big' in self.browser.contents)
+        # We do not rely on filename extensions given by uploaders
+        image = open(SAMPLE_IMAGE, 'rb') # a jpg-file
+        ctrl = self.browser.getControl(name='samplescanmanageupload')
+        file_ctrl = ctrl.mech_control
+        # Tell uploaded file is bmp
+        file_ctrl.add_file(image, filename='my_sample_scan.bmp')
+        self.browser.getControl(
+            name='upload_samplescanmanageupload').click()
+        self.assertTrue(
+            # jpg file was recognized
+            'File sample.jpg uploaded.' in self.browser.contents)
+        # Delete file again
+        self.browser.getControl(name='delete_samplescanmanageupload').click()
+        self.assertTrue(
+            'sample deleted' in self.browser.contents)
+        # File names must meet several conditions
+        bmp_image = open(SAMPLE_IMAGE_BMP, 'rb')
+        ctrl = self.browser.getControl(name='samplescanmanageupload')
+        file_ctrl = ctrl.mech_control
+        file_ctrl.add_file(bmp_image, filename='my_sample_scan.bmp')
+        self.browser.getControl(
+            name='upload_samplescanmanageupload').click()
+        self.assertTrue('Only the following extensions are allowed'
+            in self.browser.contents)
+
+class ContractUITests(CustomersFullSetup):
+    # Tests for contract related views and pages
+
+    layer = FunctionalLayer
+
+    def setup_customizable_params(self):
+        self._contract_category = u'sample'
+        self._document_factory = 'waeup.SkeletonCustomerDocument'
+        self._contract_factory = 'waeup.SkeletonContract'
+        return
+
+    def test_view_slips(self):
+        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+        # Officers can open contract slips.
+        # First we add a submitted document and a product.
+        IWorkflowState(self.document).setState('submitted')
+        self.contract.document_object = self.document
+        self.contract.product_object = self.product
+        self.contract.tc_dict = {'en': u'<strong>Hello world</strong>'}
+        self.contract.title = u'Contract Title'
+        self.browser.open(self.customer_path + '/contracts/CON1')
+        self.browser.getLink("Download contract slip").click()
+        self.assertEqual(self.browser.headers['Status'], '200 Ok')
+        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
+        path = os.path.join(samples_dir(), 'contract_slip.pdf')
+        open(path, 'wb').write(self.browser.contents)
+        print "Sample contract_slip.pdf written to %s" % path
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/test_contract.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/test_contract.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/test_contract.py	(revision 14174)
@@ -0,0 +1,54 @@
+## $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
+##
+"""
+Tests for contracts.
+"""
+import tempfile
+import shutil
+from zope.interface.verify import verifyClass, verifyObject
+from zope.component import createObject
+from zope.component.hooks import setSite
+from zope.interface import verify
+from waeup.ikoba.customers.interfaces import ICustomerNavigation
+from waeup.ikoba.customers.contracts import ContractsContainer
+
+from ikobacustom.skeleton.testing import (FunctionalLayer, FunctionalTestCase)
+from ikobacustom.skeleton.customers.contracts import SkeletonContract
+from ikobacustom.skeleton.customers.interfaces import ISkeletonContract
+
+
+class ContractsContainerTestCase(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def test_interfaces(self):
+        verify.verifyClass(ISkeletonContract, SkeletonContract)
+        verify.verifyClass(ICustomerNavigation, SkeletonContract)
+        verify.verifyObject(ISkeletonContract, SkeletonContract())
+        verify.verifyObject(ICustomerNavigation, SkeletonContract())
+        return
+
+    def test_addContract(self):
+        container = ContractsContainer()
+        contract = createObject(u'waeup.SkeletonContract')
+        id = contract.contract_id
+        container.addContract(contract)
+        self.assertEqual(container[id], contract)
+        self.assertRaises(TypeError, container.addContract, object())
+        self.assertEqual(contract.class_name, 'SkeletonContract')
+        return
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/test_customer.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/test_customer.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/test_customer.py	(revision 14174)
@@ -0,0 +1,71 @@
+## $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
+##
+"""
+Tests for skeleton customers.
+"""
+import tempfile
+import shutil
+from zope.interface.verify import verifyClass, verifyObject
+from zope.component import createObject
+from zope.component.hooks import setSite
+from zope.interface import verify
+from waeup.ikoba.app import Company
+from waeup.ikoba.customers.interfaces import (
+    ICustomersContainer, ICustomerNavigation)
+from waeup.ikoba.customers.container import CustomersContainer
+
+from ikobacustom.skeleton.testing import (FunctionalLayer, FunctionalTestCase)
+from ikobacustom.skeleton.customers.customer import SkeletonCustomer
+from ikobacustom.skeleton.customers.interfaces import ISkeletonCustomer
+
+class SkeletonCustomerTestCase(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def setUp(self):
+        super(SkeletonCustomerTestCase, self).setUp()
+
+        # Prepopulate ZODB
+        app = Company()
+        self.dc_root = tempfile.mkdtemp()
+        app['datacenter'].setStoragePath(self.dc_root)
+
+        # Prepopulate the ZODB...
+        self.getRootFolder()['app'] = app
+        self.app = self.getRootFolder()['app']
+        setSite(self.app)
+
+    def test_interfaces(self):
+        verify.verifyClass(ISkeletonCustomer, SkeletonCustomer)
+        verify.verifyClass(ICustomerNavigation, SkeletonCustomer)
+        verify.verifyObject(ISkeletonCustomer, SkeletonCustomer())
+        verify.verifyObject(ICustomerNavigation, SkeletonCustomer())
+        return
+
+    def test_addSkeletonCustomer(self):
+        customer = createObject(u'waeup.Customer')
+        verify.verifyObject(ISkeletonCustomer, customer)
+        self.app['customers'].addCustomer(customer)
+        self.assertEqual(customer.customer_id, 'K1000000')
+        self.assertEqual(self.app['customers']['K1000000'], customer)
+        return
+
+    def tearDown(self):
+        shutil.rmtree(self.dc_root)
+        super(SkeletonCustomerTestCase, self).tearDown()
+        return
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/test_document.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/test_document.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/tests/test_document.py	(revision 14174)
@@ -0,0 +1,54 @@
+## $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
+##
+"""
+Tests for documents.
+"""
+import tempfile
+import shutil
+from zope.interface.verify import verifyClass, verifyObject
+from zope.component import createObject
+from zope.component.hooks import setSite
+from zope.interface import verify
+from waeup.ikoba.customers.interfaces import ICustomerNavigation
+from waeup.ikoba.customers.documents import CustomerDocumentsContainer
+
+from ikobacustom.skeleton.testing import (FunctionalLayer, FunctionalTestCase)
+from ikobacustom.skeleton.customers.documents import SkeletonCustomerDocument
+from ikobacustom.skeleton.customers.interfaces import ISkeletonCustomerDocument
+
+
+class DocumentsContainerTestCase(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def test_interfaces(self):
+        verify.verifyClass(ISkeletonCustomerDocument, SkeletonCustomerDocument)
+        verify.verifyClass(ICustomerNavigation, SkeletonCustomerDocument)
+        verify.verifyObject(ISkeletonCustomerDocument, SkeletonCustomerDocument())
+        verify.verifyObject(ICustomerNavigation, SkeletonCustomerDocument())
+        return
+
+    def test_addDocument(self):
+        container = CustomerDocumentsContainer()
+        document = createObject(u'waeup.SkeletonCustomerDocument')
+        id = document.document_id
+        container.addDocument(document)
+        self.assertEqual(container[id], document)
+        self.assertRaises(TypeError, container.addDocument, object())
+        self.assertEqual(document.class_name, 'SkeletonCustomerDocument')
+        return
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/utils.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/utils.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/customers/utils.py	(revision 14174)
@@ -0,0 +1,65 @@
+## $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
+##
+"""General helper functions and utilities for the customer section.
+"""
+
+from copy import deepcopy
+from waeup.ikoba.customers.utils import (
+    CustomersUtils,
+    STARTED, CREATED, REQUESTED, APPROVED,
+    SUBMITTED, VERIFIED, REJECTED,
+    EXPIRED, PROVISIONALLY)
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+class SkeletonCustomersUtils(CustomersUtils):
+    """A collection of methods subject to customization.
+    """
+
+    #: A prefix used when generating new customer ids. Each customer id will
+    #: start with this string. The default is 'K'.
+    CUSTOMER_ID_PREFIX = u'K'
+
+    DOCMANAGE_CUSTOMER_STATES = (REQUESTED, APPROVED, PROVISIONALLY)
+
+    DOCMANAGE_DOCUMENT_STATES = (CREATED,)
+
+    CONMANAGE_CUSTOMER_STATES = deepcopy(DOCMANAGE_CUSTOMER_STATES)
+
+    CONMANAGE_CONTRACT_STATES = (CREATED,)
+
+    DOCTYPES_DICT = {
+        #'CustomerSampleDocument': _('Sample Document'),
+        #'CustomerPDFDocument': _('PDF Document'),
+
+        'SkeletonCustomerDocument': _('Skeleton Document'),
+        }
+
+    CONTYPES_DICT = {
+        #'SampleContract': _('Sample Contract'),
+
+        'SkeletonContract': _('Skeleton Contract'),
+        }
+
+    SELECTABLE_DOCTYPES_DICT = deepcopy(DOCTYPES_DICT)
+
+    SELECTABLE_CONTYPES_DICT = deepcopy(CONTYPES_DICT)
+
+    EXPORTER_NAMES = (
+        'customers',
+        'skeletoncustomerdocuments',
+        'skeletoncontracts')
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/__init__.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/__init__.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/__init__.py	(revision 14174)
@@ -0,0 +1,8 @@
+"""This package contains everything regarding documents.
+"""
+# Make this a package.
+from ikobacustom.skeleton.documents.document import SkeletonDocument
+
+__all__ = [
+    'SkeletonDocument',
+    ]
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/batching.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/batching.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/batching.py	(revision 14174)
@@ -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
+##
+"""Batch processing for skeleton documents.
+"""
+import grok
+from waeup.ikoba.documents.batching import DocumentProcessorBase
+from ikobacustom.skeleton.documents.interfaces import ISkeletonDocument
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+
+class SkeletonDocumentProcessor(DocumentProcessorBase):
+    """A batch processor for ISkeletonDocument objects.
+    """
+    util_name = 'skeletondocumentprocessor'
+    grok.name(util_name)
+
+    name = _('Skeleton Document Processor')
+    iface = ISkeletonDocument
+
+    factory_name = 'waeup.SkeletonDocument'
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/document.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/document.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/document.py	(revision 14174)
@@ -0,0 +1,53 @@
+## $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
+##
+"""
+These are the documents.
+"""
+import grok
+from zope.component.interfaces import IFactory
+from zope.interface import implementedBy
+from waeup.ikoba.utils.helpers import attrs_to_fields
+from waeup.ikoba.documents.document import PublicDocumentBase
+from ikobacustom.skeleton.documents.interfaces import ISkeletonDocument
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+
+class SkeletonDocument(PublicDocumentBase):
+    """This is a document.
+    """
+    grok.implements(ISkeletonDocument)
+    grok.provides(ISkeletonDocument)
+
+    form_fields_interface = ISkeletonDocument
+
+SkeletonDocument = attrs_to_fields(SkeletonDocument)
+
+
+class SkeletonDocumentFactory(grok.GlobalUtility):
+    """A factory for documents.
+    """
+    grok.implements(IFactory)
+    grok.name(u'waeup.SkeletonDocument')
+    title = u"Create a new document.",
+    description = u"This factory instantiates new skeleton documents."
+
+    def __call__(self, *args, **kw):
+        return SkeletonDocument(*args, **kw)
+
+    def getInterfaces(self):
+        return implementedBy(SkeletonDocument)
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/export.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/export.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/export.py	(revision 14174)
@@ -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 skeleton documents.
+"""
+import grok
+from waeup.ikoba.documents.export import DocumentExporterBase
+from ikobacustom.skeleton.documents.interfaces import ISkeletonDocument
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+
+class SkeletonDocumentExporter(DocumentExporterBase):
+    """Exporter for documents.
+    """
+    grok.name('skeletondocuments')
+    iface = ISkeletonDocument
+    class_name = 'SkeletonDocument'
+    title = _(u'Skeleton Documents')
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/interfaces.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/interfaces.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/interfaces.py	(revision 14174)
@@ -0,0 +1,25 @@
+## $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
+##
+from waeup.ikoba.documents.interfaces import IPublicDocument
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+
+class ISkeletonDocument(IPublicDocument):
+    """A representation of a skeleton document.
+
+    """
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/tests/__init__.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/tests/__init__.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/tests/__init__.py	(revision 14174)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/tests/test_browser.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/tests/test_browser.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/tests/test_browser.py	(revision 14174)
@@ -0,0 +1,90 @@
+## $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
+##
+"""
+Document browser and functional tests.
+"""
+import os
+import grok
+import datetime
+from cStringIO import StringIO
+from zope.component import queryUtility, getUtility
+from zope.event import notify
+from zope.interface.verify import verifyObject, verifyClass
+from waeup.ikoba.documents.tests.test_batching import DocumentImportExportSetup
+from ikobacustom.skeleton.documents.document import SkeletonDocument
+from ikobacustom.skeleton.documents.export import SkeletonDocumentExporter
+from ikobacustom.skeleton.documents.batching import SkeletonDocumentProcessor
+from ikobacustom.skeleton.testing import FunctionalLayer
+
+
+class DocumentImportExportTest(DocumentImportExportSetup):
+
+    layer = FunctionalLayer
+
+    def setup_for_export(self):
+        document = SkeletonDocument()
+        document.document_id = u'DOC1'
+        document.title = u'My first document'
+        self.app['documents'][document.document_id] = self.document = document
+        self.outfile = os.path.join(self.workdir, 'myoutput.csv')
+        return
+
+    def test_export_reimport_all(self):
+        # we can export all documents in a portal
+        # set values we can expect in export file
+        self.setup_for_export()
+        exporter = SkeletonDocumentExporter()
+        exporter.export_all(self.app, self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertMatches(result,
+            'class_name,description,document_id,history,state,title,'
+            'users_with_local_roles\r\n'
+            'SkeletonDocument,,DOC1,'
+            '[u\'2014-12-21 23:27:15 WAT - Document created by system\'],'
+            'created,My first document,[]\r\n')
+        # We can import the same file.
+        processor = SkeletonDocumentProcessor()
+        result = processor.doImport(
+            self.outfile,
+            ['class_name','description','document_id','history','state',
+             'title','users_with_local_roles'],
+            mode='create')
+        num, num_fail, finished_path, failed_path = result
+        #content = open(failed_path).read()
+        # The object exists.
+        self.assertEqual(num_fail,1)
+        # We remove the original document.
+        del self.app['documents']['DOC1']
+        result = processor.doImport(
+            self.outfile,
+            ['class_name','description','document_id','history','state',
+             'title','users_with_local_roles'],
+            mode='create')
+        num_succ, num_fail, finished_path, failed_path = result
+        self.assertEqual(num_fail,0)
+        # We can import the same file in update mode.
+        processor = SkeletonDocumentProcessor()
+        result = processor.doImport(
+            self.outfile,
+            ['class_name','description','document_id','history','state',
+             'title','users_with_local_roles'],
+            mode='update')
+        num_succ, num_fail, finished_path, failed_path = result
+        self.assertEqual(num_succ,1)
+        self.assertEqual(num_fail,0)
+        return
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/tests/test_document.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/tests/test_document.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/tests/test_document.py	(revision 14174)
@@ -0,0 +1,53 @@
+## $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
+##
+"""
+Tests for skeleton documents.
+"""
+from zope.interface.verify import verifyClass, verifyObject
+from zope.component import createObject
+from waeup.ikoba.documents.container import DocumentsContainer
+
+from ikobacustom.skeleton.testing import (FunctionalLayer, FunctionalTestCase)
+from ikobacustom.skeleton.documents.document import SkeletonDocument
+from ikobacustom.skeleton.documents.interfaces import ISkeletonDocument
+
+class SkeletonDocumentTestCase(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def test_interfaces(self):
+        # Make sure the correct interfaces are implemented.
+        self.assertTrue(
+            verifyClass(
+                ISkeletonDocument, SkeletonDocument)
+            )
+        self.assertTrue(
+            verifyObject(
+                ISkeletonDocument, SkeletonDocument())
+            )
+        return
+
+    def test_addSkeletonDocument(self):
+        container = DocumentsContainer()
+        document = createObject(u'waeup.SkeletonDocument')
+        document.document_id = u'DOC1'
+        container.addDocument(document)
+        self.assertRaises(TypeError, container.addDocument, object())
+        self.assertEqual(document.document_id, 'DOC1')
+        self.assertEqual(container['DOC1'], document)
+        return
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/utils.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/utils.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/documents/utils.py	(revision 14174)
@@ -0,0 +1,35 @@
+## $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
+##
+"""General helper functions and utilities for the documents.
+"""
+from waeup.ikoba.documents.utils import DocumentsUtils
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+
+class SkeletonDocumentsUtils(DocumentsUtils):
+    """A collection of methods subject to customization.
+    """
+
+    DOCTYPES_DICT = {
+        'PDFDocument': _('PDF Document'),
+        'HTMLDocument': _('HTML Document'),
+        'RESTDocument': _('REST Document'),
+        'SkeletonDocument': _('Skeleton Document'),
+        }
+
+    SELECTABLE_DOCTYPES_DICT = DOCTYPES_DICT
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/ftesting.zcml
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/ftesting.zcml	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/ftesting.zcml	(revision 14174)
@@ -0,0 +1,91 @@
+<configure
+   xmlns="http://namespaces.zope.org/zope"
+   xmlns:ikoba="http://namespaces.waeup.org/ikoba"
+   i18n_domain="ikobacustom.skeleton"
+   package="ikobacustom.skeleton"
+   >
+
+  <include package="grok" />
+  <includeOverrides package="ikobacustom.skeleton" />
+  <include package="ikobacustom.skeleton" file="mail.zcml" />
+
+  <!-- Where should the datacenter reside by default?
+
+       The path is relative to the `package` defined in top
+       `configure` directive.
+  -->
+  <ikoba: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/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/interfaces.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/interfaces.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/interfaces.py	(revision 14174)
@@ -0,0 +1,40 @@
+## $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 ikobacustom.skeleton.utils.lgas import LGAS
+
+_ = MessageFactory = zope.i18nmessageid.MessageFactory('ikobacustom.skeleton')
+
+
+class LGASource(BasicSourceFactory):
+    """A source for school subjects used in exam documentation.
+    """
+    lga_dict = dict(LGAS)
+
+    def getValues(self):
+        return sorted(self.lga_dict.keys())
+
+    def getToken(self, value):
+        return str(value)
+
+    def getTitle(self, value):
+        return self.lga_dict.get(value,
+            _('Invalid key: ${a}', mapping = {'a':value}))
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/locales.zcml
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/locales.zcml	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/locales.zcml	(revision 14174)
@@ -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.ikoba" file="locales.zcml" />
+
+</configure>
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/locales/en/LC_MESSAGES/waeup.ikoba.po
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/locales/en/LC_MESSAGES/waeup.ikoba.po	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/locales/en/LC_MESSAGES/waeup.ikoba.po	(revision 14174)
@@ -0,0 +1,11 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: WAeUP.Ikoba\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"
+
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/mail.zcml
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/mail.zcml	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/mail.zcml	(revision 14174)
@@ -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 IKOBA 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.localhost"
+      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 localhost"
+      permission="zope.Public"
+      mailer="smtp.localhost"
+      queuePath="../../../var/mailqueue"
+      />
+
+  <mail:directDelivery
+      name="Direct SMTP on localhost"
+      permission="zope.Public"
+      mailer="smtp.localhost"
+      />
+
+</configure>
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/permissions.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/permissions.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/permissions.py	(revision 14174)
@@ -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.ikoba.permissions import CCOfficer
+
+
+class CCOfficer(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'CC Officer')
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/__init__.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/__init__.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/__init__.py	(revision 14174)
@@ -0,0 +1,8 @@
+"""This package contains everything regarding products.
+"""
+# Make this a package.
+from ikobacustom.skeleton.products.product import SkeletonProduct
+
+__all__ = [
+    'SkeletonProduct',
+    ]
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/batching.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/batching.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/batching.py	(revision 14174)
@@ -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
+##
+"""Batch processing for skeleton products.
+"""
+import grok
+from waeup.ikoba.products.batching import ProductProcessor
+from ikobacustom.skeleton.products.interfaces import ISkeletonProduct
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+class SkeletonProductProcessor(ProductProcessor):
+
+    iface = ISkeletonProduct
+    name = _('Skeleton Product Processor')
+    util_name = 'skeletonproductprocessor'
+    grok.name(util_name)
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/export.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/export.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/export.py	(revision 14174)
@@ -0,0 +1,35 @@
+## $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 skeleton products.
+"""
+import grok
+from waeup.ikoba.utils.helpers import iface_names
+from waeup.ikoba.products.export import ProductExporter
+from ikobacustom.skeleton.products.interfaces import ISkeletonProduct
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+
+class SkeletonProductExporter(ProductExporter):
+    """Exporter for products.
+    """
+
+    fields = tuple(sorted(iface_names(ISkeletonProduct))) + (
+        'users_with_local_roles',)
+
+    #: The title under which this exporter will be displayed
+    #title = _(u'Skeleton Products')
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/interfaces.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/interfaces.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/interfaces.py	(revision 14174)
@@ -0,0 +1,25 @@
+## $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
+##
+from waeup.ikoba.products.interfaces import IProduct
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+
+class ISkeletonProduct(IProduct):
+    """A representation of a skeleton product.
+
+    """
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/product.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/product.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/product.py	(revision 14174)
@@ -0,0 +1,53 @@
+## $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
+##
+"""
+These are the products (e.g. licenses).
+"""
+import grok
+from zope.component.interfaces import IFactory
+from zope.interface import implementedBy
+from waeup.ikoba.utils.helpers import attrs_to_fields
+from waeup.ikoba.products.product import Product
+from ikobacustom.skeleton.products.interfaces import ISkeletonProduct
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+
+class SkeletonProduct(Product):
+    """This is a product.
+    """
+    grok.implements(ISkeletonProduct)
+    grok.provides(ISkeletonProduct)
+
+    form_fields_interface = ISkeletonProduct
+
+SkeletonProduct = attrs_to_fields(SkeletonProduct)
+
+
+class SkeletonProductFactory(grok.GlobalUtility):
+    """A factory for products.
+    """
+    grok.implements(IFactory)
+    grok.name(u'waeup.Product')
+    title = u"Create a new product.",
+    description = u"This factory instantiates new skeleton products."
+
+    def __call__(self, *args, **kw):
+        return SkeletonProduct(*args, **kw)
+
+    def getInterfaces(self):
+        return implementedBy(SkeletonProduct)
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/tests/__init__.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/tests/__init__.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/tests/__init__.py	(revision 14174)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/tests/test_browser.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/tests/test_browser.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/tests/test_browser.py	(revision 14174)
@@ -0,0 +1,93 @@
+## $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
+##
+"""
+Product browser and functional tests.
+"""
+import os
+import grok
+import datetime
+from cStringIO import StringIO
+from zope.component import queryUtility, getUtility
+from zope.event import notify
+from zope.interface.verify import verifyObject, verifyClass
+from waeup.ikoba.products.tests.test_batching import ProductImportExportSetup
+from ikobacustom.skeleton.products.product import SkeletonProduct
+from ikobacustom.skeleton.products.export import SkeletonProductExporter
+from ikobacustom.skeleton.products.batching import SkeletonProductProcessor
+from ikobacustom.skeleton.testing import FunctionalLayer
+
+
+class ProductImportExportTest(ProductImportExportSetup):
+
+    layer = FunctionalLayer
+
+    def setup_for_export(self):
+        product = SkeletonProduct()
+        product.product_id = u'SAM'
+        product.contract_category = u'sample'
+        product.valid_from = datetime.date(2015, 12, 4)
+        self.app['products'][product.product_id] = self.product = product
+        self.outfile = os.path.join(self.workdir, 'myoutput.csv')
+        return
+
+    def test_export_reimport_all(self):
+        # we can export all products in a portal
+        # set values we can expect in export file
+        self.setup_for_export()
+        exporter = SkeletonProductExporter()
+        exporter.export_all(self.app, self.outfile)
+        result = open(self.outfile, 'rb').read()
+        self.assertEqual(result,
+            'contract_category,contract_title,description,options,'
+            'product_id,terms_and_conditions,title,valid_from,valid_to,'
+            'users_with_local_roles\r\n'
+            'sample,,,[],SAM,,Unnamed,2015-12-04#,,[]\r\n')
+        # We can import the same file.
+        processor = SkeletonProductProcessor()
+        result = processor.doImport(
+            self.outfile,
+            ['contract_category','contract_title', 'description', 'options',
+            'product_id', 'terms_and_conditions','title','valid_from','valid_to',
+            'users_with_local_roles'],
+            mode='create')
+        num, num_fail, finished_path, failed_path = result
+        #content = open(failed_path).read()
+        # The object exists.
+        self.assertEqual(num_fail,1)
+        # We remove the original product.
+        del self.app['products']['SAM']
+        result = processor.doImport(
+            self.outfile,
+            ['contract_category','contract_title', 'description', 'options',
+            'product_id', 'terms_and_conditions','title','valid_from','valid_to',
+            'users_with_local_roles'],
+            mode='create')
+        num, num_fail, finished_path, failed_path = result
+        self.assertEqual(num_fail,0)
+        # We can import the same file in update mode.
+        processor = SkeletonProductProcessor()
+        result = processor.doImport(
+            self.outfile,
+            ['contract_category','contract_title', 'description', 'options',
+            'product_id', 'terms_and_conditions','title','valid_from','valid_to',
+            'users_with_local_roles'],
+            mode='update')
+        num, num_fail, finished_path, failed_path = result
+        self.assertEqual(num,1)
+        self.assertEqual(num_fail,0)
+        return
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/tests/test_product.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/tests/test_product.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/products/tests/test_product.py	(revision 14174)
@@ -0,0 +1,53 @@
+## $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
+##
+"""
+Tests for skeleton products.
+"""
+from zope.interface.verify import verifyClass, verifyObject
+from zope.component import createObject
+from waeup.ikoba.products.interfaces import IProductsContainer
+from waeup.ikoba.products.container import ProductsContainer
+
+from ikobacustom.skeleton.testing import (FunctionalLayer, FunctionalTestCase)
+from ikobacustom.skeleton.products.product import SkeletonProduct
+from ikobacustom.skeleton.products.interfaces import ISkeletonProduct
+
+class SkeletonProductTestCase(FunctionalTestCase):
+
+    layer = FunctionalLayer
+
+    def test_interfaces(self):
+        # Make sure the correct interfaces are implemented.
+        self.assertTrue(
+            verifyClass(
+                ISkeletonProduct, SkeletonProduct)
+            )
+        self.assertTrue(
+            verifyObject(
+                ISkeletonProduct, SkeletonProduct())
+            )
+        return
+
+    def test_addSkeletonProduct(self):
+        container = ProductsContainer()
+        product = createObject(u'waeup.Product')
+        container.addProduct(product)
+        self.assertRaises(TypeError, container.addProduct, object())
+        self.assertEqual(product.product_id, 'NA')
+        self.assertEqual(container['NA'], product)
+        return
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/testing.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/testing.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/testing.py	(revision 14174)
@@ -0,0 +1,39 @@
+## $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
+##
+"""Testing support for :mod:`waeup.ikoba`.
+"""
+import os
+import tempfile
+import ikobacustom.skeleton
+from zope.app.testing.functional import ZCMLLayer
+from waeup.ikoba.testing import FunctionalTestCase
+
+# Some funcional layer that groks ikobacustom.skeleton.
+ftesting_zcml = os.path.join(
+    os.path.dirname(ikobacustom.skeleton.__file__), 'ftesting.zcml')
+FunctionalLayer = ZCMLLayer(ftesting_zcml, __name__, 'FunctionalLayer',
+                            allow_teardown=True)
+
+def samples_dir():
+    # create a directory for sample sheets, if it doesn't exist,
+    # and return its path.
+    tmp_dir = os.path.join(
+        tempfile.gettempdir(), 'ikobacustom.skeleton.pdf-samples')
+    if not os.path.exists(tmp_dir):
+        os.mkdir(tmp_dir)
+    return tmp_dir
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/utils/__init__.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/utils/__init__.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/utils/__init__.py	(revision 14174)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/utils/lgas.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/utils/lgas.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/utils/lgas.py	(revision 14174)
@@ -0,0 +1,878 @@
+LGAS = (
+    ('abia_aba_north','Abia / Aba North'),
+    ('abia_aba_south','Abia / Aba South'),
+    ('abia_arochukwu','Abia / Arochukwu'),
+    ('abia_bende','Abia / Bende'),
+    ('abia_ikawuno','Abia / Ikawuno'),
+    ('abia_ikwuano','Abia / Ikwuano'),
+    ('abia_isiala-ngwa-north','Abia / Isiala-Ngwa-North'),
+    ('abia_isiala-ngwa-south','Abia / Isiala-Ngwa-South'),
+    ('abia_isikwuato','Abia / Isikwuato'),
+    ('abia_isuikwuato','Abia / Isuikwuato'),
+    ('abia_nneochi','Abia / Nneochi'),
+    ('abia_obi_ngwa','Abia / Obi Ngwa'),
+    ('abia_obioma-ngwa','Abia / Obioma-Ngwa'),
+    ('abia_ohafia','Abia / Ohafia'),
+    ('abia_ohaozara','Abia / Ohaozara'),
+    ('abia_osisioma','Abia / Osisioma'),
+    ('abia_ugwunagbo','Abia / Ugwunagbo'),
+    ('abia_ukwa_west','Abia / Ukwa West'),
+    ('abia_ukwu_east','Abia / Ukwu East'),
+    ('abia_umuahia','Abia / Umuahia'),
+    ('abia_umuahia_south','Abia / Umuahia South'),
+    ('abia_umuahia-north','Abia / Umuahia-North'),
+    ('abuja_abaji','Abuja / Abaji'),
+    ('abuja_amac','Abuja / Amac'),
+    ('abuja_bwari','Abuja / Bwari'),
+    ('abuja_kuje','Abuja / Kuje'),
+    ('abuja_gwagwalada','Abuja / Gwagwalada'),
+    ('abuja_kwali','Abuja / Kwali'),
+    ('abuja_municipal','Abuja / Municipal'),
+    ('adamawa_demsa','Adamawa / Demsa'),
+    ('adamawa_fufore','Adamawa / Fufore'),
+    ('adamawa_ganye','Adamawa / Ganye'),
+    ('adamawa_girei','Adamawa / Girei'),
+    ('adamawa_gombi','Adamawa / Gombi'),
+    ('adamawa_guyuk','Adamawa / Guyuk'),
+    ('adamawa_hong','Adamawa / Hong'),
+    ('adamawa_jada','Adamawa / Jada'),
+    ('adamawa_jimeta','Adamawa / Jimeta'),
+    ('adamawa_lamurde','Adamawa / Lamurde'),
+    ('adamawa_madagali','Adamawa / Madagali'),
+    ('adamawa_maiha','Adamawa / Maiha'),
+    ('adamawa_mayo-belwa','Adamawa / Mayo-Belwa'),
+    ('adamawa_michika','Adamawa / Michika'),
+    ('adamawa_mubi-north','Adamawa / Mubi-North'),
+    ('adamawa_mubi-south','Adamawa / Mubi-South'),
+    ('adamawa_numan','Adamawa / Numan'),
+    ('adamawa_shelleng','Adamawa / Shelleng'),
+    ('adamawa_song','Adamawa / Song'),
+    ('adamawa_toungo','Adamawa / Toungo'),
+    ('adamawa_yola','Adamawa / Yola'),
+    ('akwa_ibom_abak','Akwa Ibom / Abak'),
+    ('akwa_ibom_eastern-obolo','Akwa Ibom / Eastern-Obolo'),
+    ('akwa_ibom_eket','Akwa Ibom / Eket'),
+    ('akwa_ibom_ekpe-atani','Akwa Ibom / Ekpe-Atani'),
+    ('akwa_ibom_esit_ekit','Akwa Ibom / Esit Ekit'),
+    ('akwa_ibom_essien-udim','Akwa Ibom / Essien-Udim'),
+    ('akwa_ibom_etim-ekpo','Akwa Ibom / Etim-Ekpo'),
+    ('akwa_ibom_etinam','Akwa Ibom / Etinam'),
+    ('akwa_ibom_ibeno','Akwa Ibom / Ibeno'),
+    ('akwa_ibom_ibesikpo-asutan','Akwa Ibom / Ibesikpo-Asutan'),
+    ('akwa_ibom_ibiono-ibom','Akwa Ibom / Ibiono-Ibom'),
+    ('akwa_ibom_ika','Akwa Ibom / Ika'),
+    ('akwa_ibom_ikono','Akwa Ibom / Ikono'),
+    ('akwa_ibom_ikot-abasi','Akwa Ibom / Ikot-Abasi'),
+    ('akwa_ibom_ikot-ekpene','Akwa Ibom / Ikot-Ekpene'),
+    ('akwa_ibom_ini','Akwa Ibom / Ini'),
+    ('akwa_ibom_itu','Akwa Ibom / Itu'),
+    ('akwa_ibom_mbo','Akwa Ibom / Mbo'),
+    ('akwa_ibom_mpat-enin','Akwa Ibom / Mpat-Enin'),
+    ('akwa_ibom_nsit-atai','Akwa Ibom / Nsit-Atai'),
+    ('akwa_ibom_nsit-ibom','Akwa Ibom / Nsit-Ibom'),
+    ('akwa_ibom_nsit-ubium','Akwa Ibom / Nsit-Ubium'),
+    ('akwa_ibom_obot-akara','Akwa Ibom / Obot-Akara'),
+    ('akwa_ibom_okobo','Akwa Ibom / Okobo'),
+    ('akwa_ibom_onna','Akwa Ibom / Onna'),
+    ('akwa_ibom_oron','Akwa Ibom / Oron'),
+    ('akwa_ibom_oruk_anam','Akwa Ibom / Oruk-Anam'),
+    ('akwa_ibom_udung-uko','Akwa Ibom / Udung-Uko'),
+    ('akwa_ibom_ukanafun','Akwa Ibom / Ukanafun'),
+    ('akwa_ibom_uquo_ibeno','Akwa Ibom / Uquo Ibeno'),
+    ('akwa_ibom_urue-offong-oruko','Akwa Ibom / Urue-Offong-Oruko'),
+    ('akwa_ibom_uruan','Akwa Ibom / Uruan'),
+    ('akwa_ibom_uruk-anam','Akwa Ibom / Uruk-Anam'),
+    ('akwa_ibom_uyo','Akwa Ibom / Uyo'),
+    ('anambra_aguata','Anambra / Aguata'),
+    ('anambra_anambra-east','Anambra / Anambra-East'),
+    ('anambra_anambra-west','Anambra / Anambra-West'),
+    ('anambra_anaocha','Anambra / Anaocha'),
+    ('anambra_awka-north','Anambra / Awka-North'),
+    ('anambra_awka-south','Anambra / Awka-South'),
+    ('anambra_ayamelum','Anambra / Ayamelum'),
+    ('anambra_dunukofia','Anambra / Dunukofia'),
+    ('anambra_ekwusigo','Anambra / Ekwusigo'),
+    ('anambra_idemili','Anambra / Idemili'),
+    ('anambra_idemili-north','Anambra / Idemili-North'),
+    ('anambra_idemili-south','Anambra / Idemili-South'),
+    ('anambra_ihiala','Anambra / Ihiala'),
+    ('anambra_nibo','Anambra / Nibo'),
+    ('anambra_njikoka','Anambra / Njikoka'),
+    ('anambra_nnewi','Anambra / Nnewi'),
+    ('anambra_nnewi-north','Anambra / Nnewi-North'),
+    ('anambra_nnewi-south','Anambra / Nnewi-South'),
+    ('anambra_ogbaru','Anambra / Ogbaru'),
+    ('anambra_olumba','Anambra / Olumba'),
+    ('anambra_onitsha-north','Anambra / Onitsha-North'),
+    ('anambra_onitsha-south','Anambra / Onitsha-South'),
+    ('anambra_orumba-north','Anambra / Orumba-North'),
+    ('anambra_orumba-south','Anambra / Orumba-South'),
+    ('anambra_oti','Anambra / Oti'),
+    ('anambra_oyi','Anambra / Oyi'),
+    ('anambra_ubuluizor-ihiala','Anambra / Ubuluizor-Ihiala'),
+    ('anambra_uyi','Anambra / Uyi'),
+    ('bauchi_alkaleri','Bauchi / Alkaleri'),
+    ('bauchi_bauchi','Bauchi / Bauchi'),
+    ('bauchi_bogoro','Bauchi / Bogoro'),
+    ('bauchi_damban','Bauchi / Damban'),
+    ('bauchi_darazo','Bauchi / Darazo'),
+    ('bauchi_dass','Bauchi / Dass'),
+    ('bauchi_gamawa','Bauchi / Gamawa'),
+    ('bauchi_ganjuwa','Bauchi / Ganjuwa'),
+    ('bauchi_giade','Bauchi / Giade'),
+    ('bauchi_itas/gadau','Bauchi / Itas/Gadau'),
+    ('bauchi_jamaare','Bauchi / Jamaare'),
+    ('bauchi_katagum','Bauchi / Katagum'),
+    ('bauchi_kirfi','Bauchi / Kirfi'),
+    ('bauchi_misau','Bauchi / Misau'),
+    ('bauchi_ningi','Bauchi / Ningi'),
+    ('bauchi_shira','Bauchi / Shira'),
+    ('bauchi_tafawa-balewa','Bauchi / Tafawa-Balewa'),
+    ('bauchi_toro','Bauchi / Toro'),
+    ('bauchi_warji','Bauchi / Warji'),
+    ('bauchi_zaki','Bauchi / Zaki'),
+    ('bayelsa_brass','Bayelsa / Brass'),
+    ('bayelsa_ekeremor','Bayelsa / Ekeremor'),
+    ('bayelsa_kembe','Bayelsa / Kembe'),
+    ('bayelsa_kolokuma','Bayelsa / Kolokuma'),
+    ('bayelsa_kolokuma/opkuma','Bayelsa / Kolokuma/Opkuma'),
+    ('bayelsa_nembe','Bayelsa / Nembe'),
+    ('bayelsa_ogbia','Bayelsa / Ogbia'),
+    ('bayelsa_sagbama','Bayelsa / Sagbama'),
+    ('bayelsa_southern-ijaw','Bayelsa / Southern-Ijaw'),
+    ('bayelsa_toru-abubo','Bayelsa / Toru-Abubo'),
+    ('bayelsa_yenegoa','Bayelsa / Yenegoa'),
+    ('benue_ado','Benue / Ado'),
+    ('benue_agatu','Benue / Agatu'),
+    ('benue_apa','Benue / Apa'),
+    ('benue_buruku','Benue / Buruku'),
+    ('benue_gboko','Benue / Gboko'),
+    ('benue_guma','Benue / Guma'),
+    ('benue_gwer-east','Benue / Gwer-East'),
+    ('benue_gwer-west','Benue / Gwer-West'),
+    ('benue_katsina-ala','Benue / Katsina-Ala'),
+    ('benue_konshisha','Benue / Konshisha'),
+    ('benue_kwande','Benue / Kwande'),
+    ('benue_logo','Benue / Logo'),
+    ('benue_makurdi','Benue / Makurdi'),
+    ('benue_obi','Benue / Obi'),
+    ('benue_ogbadibo','Benue / Ogbadibo'),
+    ('benue_ohimini','Benue / Ohimini'),
+    ('benue_oju','Benue / Oju'),
+    ('benue_okpokwu','Benue / Okpokwu'),
+    ('benue_otukpo','Benue / Otukpo'),
+    ('benue_oturkpa','Benue / Oturkpa'),
+    ('benue_tarka','Benue / Tarka'),
+    ('benue_ukum','Benue / Ukum'),
+    ('benue_ushongo','Benue / Ushongo'),
+    ('benue_vandeikya','Benue / Vandeikya'),
+    ('borno_abadan','Borno / Abadan'),
+    ('borno_askira-uba','Borno / Askira-Uba'),
+    ('borno_bama','Borno / Bama'),
+    ('borno_bayo','Borno / Bayo'),
+    ('borno_biu','Borno / Biu'),
+    ('borno_chibok','Borno / Chibok'),
+    ('borno_damboa','Borno / Damboa'),
+    ('borno_dikwa','Borno / Dikwa'),
+    ('borno_gubio','Borno / Gubio'),
+    ('borno_guzamala','Borno / Guzamala'),
+    ('borno_gwoza','Borno / Gwoza'),
+    ('borno_hawul','Borno / Hawul'),
+    ('borno_jere','Borno / Jere'),
+    ('borno_kaga','Borno / Kaga'),
+    ('borno_kala/balge','Borno / Kala/Balge'),
+    ('borno_konduga','Borno / Konduga'),
+    ('borno_kukawa','Borno / Kukawa'),
+    ('borno_kwaya-kusar','Borno / Kwaya-Kusar'),
+    ('borno_mafa','Borno / Mafa'),
+    ('borno_magumeri','Borno / Magumeri'),
+    ('borno_maiduguri','Borno / Maiduguri'),
+    ('borno_marte','Borno / Marte'),
+    ('borno_mobbar','Borno / Mobbar'),
+    ('borno_mongunu','Borno / Mongunu'),
+    ('borno_ngala','Borno / Ngala'),
+    ('borno_nganzai','Borno / Nganzai'),
+    ('borno_shani','Borno / Shani'),
+    ('cross_river_abi','Cross River / Abi'),
+    ('cross_river_abuochichie','Cross River / Abuochichie'),
+    ('cross_river_akamkpa','Cross River / Akamkpa'),
+    ('cross_river_akpabuyo','Cross River / Akpabuyo'),
+    ('cross_river_bakassi','Cross River / Bakassi'),
+    ('cross_river_bekwara','Cross River / Bekwara'),
+    ('cross_river_biasi','Cross River / Biasi'),
+    ('cross_river_boki','Cross River / Boki'),
+    ('cross_river_calabar-municipal','Cross River / Calabar-Municipal'),
+    ('cross_river_calabar-south','Cross River / Calabar-South'),
+    ('cross_river_etunk','Cross River / Etunk'),
+    ('cross_river_ikom','Cross River / Ikom'),
+    ('cross_river_obanliku','Cross River / Obanliku'),
+    ('cross_river_obubra','Cross River / Obubra'),
+    ('cross_river_obudu','Cross River / Obudu'),
+    ('cross_river_odukpani','Cross River / Odukpani'),
+    ('cross_river_ogoja','Cross River / Ogoja'),
+    ('cross_river_ugep-north','Cross River / Ugep-North'),
+    ('cross_river_yakurr','Cross River / Yakurr'),
+    ('cross_river_yala','Cross River / Yala'),
+    ('delta_aniocha-north','Delta / Aniocha-North'),
+    ('delta_aniocha-south','Delta / Aniocha-South'),
+    ('delta_bomadi','Delta / Bomadi'),
+    ('delta_burutu','Delta / Burutu'),
+    ('delta_effurun','Delta / Effurun'),
+    ('delta_ethiope-east','Delta / Ethiope-East'),
+    ('delta_ethiope-west','Delta / Ethiope-West'),
+    ('delta_idu','Delta / Idu'),
+    ('delta_ika-ne','Delta / Ika-Ne'),
+    ('delta_ika-north-east','Delta / Ika-North-East'),
+    ('delta_ika-south','Delta / Ika-South'),
+    ('delta_idemili-south','Delta / Idemili-South'),
+    ('delta_isoko-north','Delta / Isoko-North'),
+    ('delta_isoko-south','Delta / Isoko-South'),
+    ('delta_ndokwa-east','Delta / Ndokwa-East'),
+    ('delta_ndokwa-north','Delta / Ndokwa-North'),
+    ('delta_ndokwa-south','Delta / Ndokwa-South'),
+    ('delta_ndokwa-west','Delta / Ndokwa-West'),
+    ('delta_okpe','Delta / Okpe'),
+    ('delta_okwuani','Delta / Okwuani'),
+    ('delta_oleh','Delta / Oleh'),
+    ('delta_oshielli-north','Delta / Oshielli-North'),
+    ('delta_oshimili','Delta / Oshimili'),
+    ('delta_oshimili-north','Delta / Oshimili-North'),
+    ('delta_oshimili-south','Delta / Oshimili-South'),
+    ('delta_patani','Delta / Patani'),
+    ('delta_sapele','Delta / Sapele'),
+    ('delta_udokwa','Delta / Udokwa'),
+    ('delta_udu','Delta / Udu'),
+    ('delta_ughelli-north','Delta / Ughelli-North'),
+    ('delta_ughelli-south','Delta / Ughelli-South'),
+    ('delta_ukwuani','Delta / Ukwuani'),
+
+    ('delta_uvwie','Delta / Uvwie'),
+    ('delta_warri-central','Delta / Warri-Central'),
+    ('delta_warri-north','Delta / Warri-North'),
+    ('delta_warri-south','Delta / Warri-South'),
+    ('delta_warri-south-west','Delta / Warri-South-West'),
+    ('ebonyi_abakaliki','Ebonyi / Abakaliki'),
+    ('ebonyi_afikpo-north','Ebonyi / Afikpo-North'),
+    ('ebonyi_afikpo-south','Ebonyi / Afikpo-South'),
+    ('ebonyi_bomadim','Ebonyi / Bomadim'),
+    ('ebonyi_ebonyi','Ebonyi / Ebonyi'),
+    ('ebonyi_ezza-north','Ebonyi / Ezza-North'),
+    ('ebonyi_ezza-south','Ebonyi / Ezza-South'),
+    ('ebonyi_ikwo','Ebonyi / Ikwo'),
+    ('ebonyi_ishielu','Ebonyi / Ishielu'),
+    ('ebonyi_ivo','Ebonyi / Ivo'),
+    ('ebonyi_izzi','Ebonyi / Izzi'),
+    ('ebonyi_obaukwu','Ebonyi / Obaukwu'),
+    ('ebonyi_ohakwu','Ebonyi / Ohakwu'),
+    ('ebonyi_ohaozara','Ebonyi / Ohaozara'),
+    ('ebonyi_onicha','Ebonyi / Onicha'),
+    ('ebonyi_ukaba','Ebonyi / Ukaba'),
+    ('edo_afokpella','Edo / Afokpella'),
+    ('edo_afuze','Edo / Afuze'),
+    ('edo_agbazilo','Edo / Agbazilo'),
+    ('edo_akoko-edo','Edo / Akoko-Edo'),
+    ('edo_egor','Edo / Egor'),
+    ('edo_esan-central','Edo / Esan-Central'),
+    ('edo_esan-north-east','Edo / Esan-North-East'),
+    ('edo_esan-north-west','Edo / Esan-North-West'),
+    ('edo_esan-south-east','Edo / Esan-South-East'),
+    ('edo_esan-west','Edo / Esan-West'),
+    ('edo_etsako-central','Edo / Etsako-Central'),
+    ('edo_etsako-east','Edo / Etsako-East'),
+    ('edo_etsako-west','Edo / Etsako-West'),
+    ('edo_igueben','Edo / Igueben'),
+    ('edo_ikpoba-okha','Edo / Ikpoba-Okha'),
+    ('edo_ohunmwode','Edo / Ohunmwode'),
+    ('edo_ologbo','Edo / Ologbo'),
+    ('edo_opoji_irrua','Edo / Opoji Irrua'),
+    ('edo_oredo','Edo / Oredo'),
+    ('edo_orhionmwon','Edo / Orhionmwon'),
+    ('edo_ovia-north-east','Edo / Ovia-North-East'),
+    ('edo_ovia-north-west','Edo / Ovia-North-West'),
+    ('edo_ovia-south-east','Edo / Ovia-South-East'),
+    ('edo_ovia-south-west','Edo / Ovia-South-West'),
+    ('edo_owan','Edo / Owan'),
+    ('edo_owan-east','Edo / Owan-East'),
+    ('edo_owan-west','Edo / Owan-West'),
+    ('edo_ubiaja','Edo / Ubiaja'), 
+    ('edo_uhunmwonde','Edo / Uhunmwonde'),
+    ('ekiti_ado-ekiti','Ekiti / Ado-Ekiti'),
+    ('ekiti_aiyekire','Ekiti / Aiyekire'),
+    ('ekiti_efon','Ekiti / Efon'),
+    ('ekiti_ekiti-east','Ekiti / Ekiti-East'),
+    ('ekiti_ekiti-south-west','Ekiti / Ekiti-South-West'),
+    ('ekiti_ekiti-west','Ekiti / Ekiti-West'),
+    ('ekiti_emure/ise/orun','Ekiti / Emure/Ise/Orun'),
+    ('ekiti_gbonyin','Ekiti / Gbonyin'),
+    ('ekiti_ido-osi','Ekiti / Ido-Osi'),
+    ('ekiti_ijero','Ekiti / Ijero'),
+    ('ekiti_ikare','Ekiti / Ikare'),
+    ('ekiti_ikere','Ekiti / Ikere'),
+    ('ekiti_ikole','Ekiti / Ikole'),
+    ('ekiti_ilejemeje','Ekiti / Ilejemeje'),
+    ('ekiti_irepodun/ifelodun','Ekiti / Irepodun/Ifelodun'),
+    ('ekiti_ise-orun','Ekiti / Ise-Orun'),
+    ('ekiti_moba','Ekiti / Moba'),
+    ('ekiti_oye','Ekiti / Oye'),
+    ('enugu_aninri','Enugu / Aninri'),
+    ('enugu_awgu','Enugu / Awgu'),
+    ('enugu_enugu-east','Enugu / Enugu-East'),
+    ('enugu_enugu-north','Enugu / Enugu-North'),
+    ('enugu_enugu-south','Enugu / Enugu-South'),
+    ('enugu_ezeagu','Enugu / Ezeagu'),
+    ('enugu_igbo-etiti','Enugu / Igbo-Etiti'),
+    ('enugu_igbo-eze-north','Enugu / Igbo-Eze-North'),
+    ('enugu_igbo-eze-south','Enugu / Igbo-Eze-South'),
+    ('enugu_isi-uzo','Enugu / Isi-Uzo'),
+    ('enugu_nkanu-east','Enugu / Nkanu-East'),
+    ('enugu_nkanu-central','Enugu / Nkanu-Central'),
+    ('enugu_nkanu-west','Enugu / Nkanu-West'),
+    ('enugu_nsukka','Enugu / Nsukka'),
+    ('enugu_nukanu_east','Enugu / Nukanu East'),
+    ('enugu_oji-river','Enugu / Oji-River'),
+    ('enugu_udenu','Enugu / Udenu'),
+    ('enugu_udi','Enugu / Udi'),
+    ('enugu_uzo-uwani','Enugu / Uzo-Uwani'),
+    ('gombe_akko','Gombe / Akko'),
+    ('gombe_balanga','Gombe / Balanga'),
+    ('gombe_billiri','Gombe / Billiri'),
+    ('gombe_dukku','Gombe / Dukku'),
+    ('gombe_funakaye','Gombe / Funakaye'),
+    ('gombe_gombe','Gombe / Gombe'),
+    ('gombe_kaltungo','Gombe / Kaltungo'),
+    ('gombe_kwami','Gombe / Kwami'),
+    ('gombe_nafada/bajoga','Gombe / Nafada/Bajoga'),
+    ('gombe_shomgom','Gombe / Shomgom'),
+    ('gombe_yamaltu/deba','Gombe / Yamaltu/Deba'),
+    ('imo_aboh-mbaise','Imo / Aboh-Mbaise'),
+    ('imo_ahiazu-mbaise','Imo / Ahiazu-Mbaise'),
+    ('imo_dral-esat','Imo / Dral-Esat'),
+    ('imo_ehime-mbano','Imo / Ehime-Mbano'),
+    ('imo_ezinihitte','Imo / Ezinihitte'),
+    ('imo_ideato','Imo / Ideato'),
+    ('imo_ideato-north','Imo / Ideato-North'),
+    ('imo_ideato-south','Imo / Ideato-South'),
+    ('imo_ihitte/uboma','Imo / Ihitte/Uboma'),
+    ('imo_ikeduru','Imo / Ikeduru'),
+    ('imo_isiala-mbano','Imo / Isiala-Mbano'),
+    ('imo_isu','Imo / Isu'),
+    ('imo_mbaitoli','Imo / Mbaitoli'),
+    ('imo_mbano','Imo / Mbano'),
+    ('imo_ngor-okpala','Imo / Ngor-Okpala'),
+    ('imo_njaba','Imo / Njaba'),
+    ('imo_nkwerre','Imo / Nkwerre'),
+    ('imo_nwangele','Imo / Nwangele'),
+    ('imo_obowo','Imo / Obowo'),
+    ('imo_oguta','Imo / Oguta'),
+    ('imo_ohaji-egbema','Imo / Ohaji-Egbema'),
+    ('imo_okigwe','Imo / Okigwe'),
+    ('imo_onuimo','Imo / Onuimo'),
+    ('imo_orlu','Imo / Orlu'),
+    ('imo_orsu','Imo / Orsu'),
+    ('imo_oru-east','Imo / Oru-East'),
+    ('imo_oru-west','Imo / Oru-West'),
+    ('imo_owerri-municipal','Imo / Owerri-Municipal'),
+    ('imo_owerri-north','Imo / Owerri-North'),
+    ('imo_owerri-west','Imo / Owerri-West'),
+    ('imo_ugiri-ike_ikeduru','Imo / Ugiri-Ike Ikeduru'),
+    ('imo_ugiri-ikedikeduru','Imo / Ugiri-Ikedikeduru'),
+    ('imo_unbano','Imo / Unbano'),
+    ('imo_zinihitte','Imo / Zinihitte'),
+    ('jigawa_auyo','Jigawa / Auyo'),
+    ('jigawa_babura','Jigawa / Babura'),
+    ('jigawa_biriniwa','Jigawa / Biriniwa'),
+    ('jigawa_birnin-kudu','Jigawa / Birnin-Kudu'),
+    ('jigawa_bosuwa','Jigawa / Bosuwa'),
+    ('jigawa_buji','Jigawa / Buji'),
+    ('jigawa_dutse','Jigawa / Dutse'),
+    ('jigawa_gagarawa','Jigawa / Gagarawa'),
+    ('jigawa_garki','Jigawa / Garki'),
+    ('jigawa_gumel','Jigawa / Gumel'),
+    ('jigawa_guri','Jigawa / Guri'),
+    ('jigawa_gwaram','Jigawa / Gwaram'),
+    ('jigawa_gwiwa','Jigawa / Gwiwa'),
+    ('jigawa_hadejia','Jigawa / Hadejia'),
+    ('jigawa_jahun','Jigawa / Jahun'),
+    ('jigawa_kafin-hausa','Jigawa / Kafin-Hausa'),
+    ('jigawa_kaugama','Jigawa / Kaugama'),
+    ('jigawa_kazaure','Jigawa / Kazaure'),
+    ('jigawa_kirkasamma','Jigawa / Kirkasamma'),
+    ('jigawa_maigatari','Jigawa / Maigatari'),
+    ('jigawa_malam-maduri','Jigawa / Malam-Maduri'),
+    ('jigawa_miga','Jigawa / Miga'),
+    ('jigawa_ringim','Jigawa / Ringim'),
+    ('jigawa_roni','Jigawa / Roni'),
+    ('jigawa_sule-tankarkar','Jigawa / Sule-Tankarkar'),
+    ('jigawa_taura','Jigawa / Taura'),
+    ('jigawa_yankwashi','Jigawa / Yankwashi'),
+    ('kaduna_birnin-gwari','Kaduna / Birnin-Gwari'),
+    ('kaduna_chikun','Kaduna / Chikun'),
+    ('kaduna_giwa','Kaduna / Giwa'),
+    ('kaduna_gwagwada','Kaduna / Gwagwada'),
+    ('kaduna_igabi','Kaduna / Igabi'),
+    ('kaduna_ikara','Kaduna / Ikara'),
+    ('kaduna_jaba','Kaduna / Jaba'),
+    ('kaduna_jemaa','Kaduna / Jemaa'),
+    ('kaduna_kachia','Kaduna / Kachia'),
+    ('kaduna_kaduna-north','Kaduna / Kaduna-North'),
+    ('kaduna_kagarko','Kaduna / Kagarko'),
+    ('kaduna_kajuru','Kaduna / Kajuru'),
+    ('kaduna_kaura','Kaduna / Kaura'),
+    ('kaduna_kauru','Kaduna / Kauru'),
+    ('kaduna_koka/kawo','Kaduna / Koka/Kawo'),
+    ('kaduna_kubah','Kaduna / Kubah'),
+    ('kaduna_kudan','Kaduna / Kudan'),
+    ('kaduna_lere','Kaduna / Lere'),
+    ('kaduna_makarfi','Kaduna / Makarfi'),
+    ('kaduna_sabon-gari','Kaduna / Sabon-Gari'),
+    ('kaduna_sanga','Kaduna / Sanga'),
+    ('kaduna_soba','Kaduna / Soba'),
+    ('kaduna_tudun-wada/makera','Kaduna / Tudun-Wada/Makera'),
+    ('kaduna_zango-kataf','Kaduna / Zango-Kataf'),
+    ('kaduna_zaria','Kaduna / Zaria'),
+    ('kano_ajingi','Kano / Ajingi'),
+    ('kano_albasu','Kano / Albasu'),
+    ('kano_bagwai','Kano / Bagwai'),
+    ('kano_bebeji','Kano / Bebeji'),
+    ('kano_bichi','Kano / Bichi'),
+    ('kano_bunkure','Kano / Bunkure'),
+    ('kano_dala','Kano / Dala'),
+    ('kano_dambatta','Kano / Dambatta'),
+    ('kano_dawakin-kudu','Kano / Dawakin-Kudu'),
+    ('kano_dawakin-tofa','Kano / Dawakin-Tofa'),
+    ('kano_doguwa','Kano / Doguwa'),
+    ('kano_fagge','Kano / Fagge'),
+    ('kano_gabasawa','Kano / Gabasawa'),
+    ('kano_garko','Kano / Garko'),
+    ('kano_garun-mallam','Kano / Garun-Mallam'),
+    ('kano_gaya','Kano / Gaya'),
+    ('kano_gezawa','Kano / Gezawa'),
+    ('kano_gwale','Kano / Gwale'),
+    ('kano_gwarzo','Kano / Gwarzo'),
+    ('kano_kano-municipal','Kano / Kano-Municipal'),
+    ('kano_karaye','Kano / Karaye'),
+    ('kano_kibiya','Kano / Kibiya'),
+    ('kano_kiru','Kano / Kiru'),
+    ('kano_kumbotso','Kano / Kumbotso'),
+    ('kano_kunchi','Kano / Kunchi'),
+    ('kano_kura','Kano / Kura'),
+    ('kano_madobi','Kano / Madobi'),
+    ('kano_makoda','Kano / Makoda'),
+    ('kano_minjibir','Kano / Minjibir'),
+    ('kano_nasarawa','Kano / Nasarawa'),
+    ('kano_rano','Kano / Rano'),
+    ('kano_rimin-gado','Kano / Rimin-Gado'),
+    ('kano_rogo','Kano / Rogo'),
+    ('kano_shanono','Kano / Shanono'),
+    ('kano_sumaila','Kano / Sumaila'),
+    ('kano_takai','Kano / Takai'),
+    ('kano_tarauni','Kano / Tarauni'),
+    ('kano_tofa','Kano / Tofa'),
+    ('kano_tsanyawa','Kano / Tsanyawa'),
+    ('kano_tudun-wada','Kano / Tudun-Wada'),
+    ('kano_ungogo','Kano / Ungogo'),
+    ('kano_warawa','Kano / Warawa'),
+    ('kano_wudil','Kano / Wudil'),
+    ('katsina_bakori','Katsina / Bakori'),
+    ('katsina_batagarawa','Katsina / Batagarawa'),
+    ('katsina_batsari','Katsina / Batsari'),
+    ('katsina_baure','Katsina / Baure'),
+    ('katsina_bindawa','Katsina / Bindawa'),
+    ('katsina_charanchi','Katsina / Charanchi'),
+    ('katsina_dan-musa','Katsina / Dan-Musa'),
+    ('katsina_dandume','Katsina / Dandume'),
+    ('katsina_danji','Katsina / Danji'),
+    ('katsina_daura','Katsina / Daura'),
+    ('katsina_dutsi','Katsina / Dutsi'),
+    ('katsina_dutsinma','Katsina / Dutsinma'),
+    ('katsina_faskari','Katsina / Faskari'),
+    ('katsina_funtua','Katsina / Funtua'),
+    ('katsina_ingawa','Katsina / Ingawa'),
+    ('katsina_jibia','Katsina / Jibia'),
+    ('katsina_kafur','Katsina / Kafur'),
+    ('katsina_kaita','Katsina / Kaita'),
+    ('katsina_kankara','Katsina / Kankara'),
+    ('katsina_kankia','Katsina / Kankia'),
+    ('katsina_katsina','Katsina / Katsina'),
+    ('katsina_kurfi','Katsina / Kurfi'),
+    ('katsina_kusada','Katsina / Kusada'),
+    ('katsina_mai-adua','Katsina / Mai-Adua'),
+    ('katsina_malumfashi','Katsina / Malumfashi'),
+    ('katsina_mani','Katsina / Mani'),
+    ('katsina_mashi','Katsina / Mashi'),
+    ('katsina_matazu','Katsina / Matazu'),
+    ('katsina_musawa','Katsina / Musawa'),
+    ('katsina_rimi','Katsina / Rimi'),
+    ('katsina_sabuwa','Katsina / Sabuwa'),
+    ('katsina_safana','Katsina / Safana'),
+    ('katsina_sandamu','Katsina / Sandamu'),
+    ('katsina_zango','Katsina / Zango'),
+    ('kebbi_aleiro','Kebbi / Aleiro'),
+    ('kebbi_arewa-dandi','Kebbi / Arewa-Dandi'),
+    ('kebbi_argungu','Kebbi / Argungu'),
+    ('kebbi_augie','Kebbi / Augie'),
+    ('kebbi_bagudo','Kebbi / Bagudo'),
+    ('kebbi_birnin-kebbi','Kebbi / Birnin-Kebbi'),
+    ('kebbi_bumza','Kebbi / Bumza'),
+    ('kebbi_dandi','Kebbi / Dandi'),
+    ('kebbi_danko','Kebbi / Danko'),
+    ('kebbi_fakai','Kebbi / Fakai'),
+    ('kebbi_gwandu','Kebbi / Gwandu'),
+    ('kebbi_jega','Kebbi / Jega'),
+    ('kebbi_kalgo','Kebbi / Kalgo'),
+    ('kebbi_koko-besse','Kebbi / Koko-Besse'),
+    ('kebbi_maiyama','Kebbi / Maiyama'),
+    ('kebbi_ngaski','Kebbi / Ngaski'),
+    ('kebbi_sakaba','Kebbi / Sakaba'),
+    ('kebbi_shanga','Kebbi / Shanga'),
+    ('kebbi_suru','Kebbi / Suru'),
+    ('kebbi_wasagu','Kebbi / Wasagu'),
+    ('kebbi_yauri','Kebbi / Yauri'),
+    ('kebbi_zuru','Kebbi / Zuru'),
+    ('kogi_adavi','Kogi / Adavi'),
+    ('kogi_ajaokuta','Kogi / Ajaokuta'),
+    ('kogi_ankpa','Kogi / Ankpa'),
+    ('kogi_bassa','Kogi / Bassa'),
+    ('kogi_dekina','Kogi / Dekina'),
+    ('kogi_ibaji','Kogi / Ibaji'),
+    ('kogi_idah','Kogi / Idah'),
+    ('kogi_igalamela','Kogi / Igalamela'),
+    ('kogi_ijumu','Kogi / Ijumu'),
+    ('kogi_ikoyi-ijumu','Kogi / Ikoyi-Ijumu'),
+    ('kogi_kabba/bunu','Kogi / Kabba/Bunu'),
+    ('kogi_kogi','Kogi / Kogi'),
+    ('kogi_lokoja','Kogi / Lokoja'),
+    ('kogi_mopa-muro','Kogi / Mopa-Muro'),
+    ('kogi_obaji','Kogi / Obaji'),
+    ('kogi_ofu','Kogi / Ofu'),
+    ('kogi_ogori/magongo','Kogi / Ogori/Magongo'),
+    ('kogi_okehi','Kogi / Okehi'),
+    ('kogi_okene','Kogi / Okene'),
+    ('kogi_olamaboro','Kogi / Olamaboro'),
+    ('kogi_omala','Kogi / Omala'),
+    ('kogi_oru','Kogi / Oru'),
+    ('kogi_oyi','Kogi / Oyi'),
+    ('kogi_yagba-east','Kogi / Yagba-East'),
+    ('kogi_yagba-west','Kogi / Yagba-West'),
+    ('kwara_asa','Kwara / Asa'),
+    ('kwara_baruten','Kwara / Baruten'),
+    ('kwara_edu','Kwara / Edu'),
+    ('kwara_ekiti','Kwara / Ekiti'),
+    ('kwara_ilorin-east','Kwara / Ilorin-East'),
+    ('kwara_ilorin-south','Kwara / Ilorin-South'),
+    ('kwara_ilorin-west','Kwara / Ilorin-West'),
+    ('kwara_isin','Kwara / Isin'),
+    ('kwara_kaiama','Kwara / Kaiama'),
+    ('kwara_moro','Kwara / Moro'),
+    ('kwara_offa','Kwara / Offa'),
+    ('kwara_oke-ero','Kwara / Oke-Ero'),
+    ('kwara_oyun','Kwara / Oyun'),
+    ('kwara_pategi','Kwara / Pategi'),
+    ('kwara_ifelodun','Kwara / Ifelodun'),
+    ('kwara_irepodun','Kwara / Irepodun'),
+    ('lagos_agege','Lagos / Agege'),
+    ('lagos_ajeromi-ifelodun','Lagos / Ajeromi-Ifelodun'),
+    ('lagos_alimosho','Lagos / Alimosho'),
+    ('lagos_amuwo-odofin','Lagos / Amuwo-Odofin'),
+    ('lagos_apapa','Lagos / Apapa'),
+    ('lagos_badagry','Lagos / Badagry'),
+    ('lagos_epe','Lagos / Epe'),
+    ('lagos_eti-osa','Lagos / Eti-Osa'),
+    ('lagos_ibeju-lekki','Lagos / Ibeju-Lekki'),
+    ('lagos_ifako-ijaiye','Lagos / Ifako-Ijaiye'),
+    ('lagos_ikeja','Lagos / Ikeja'),
+    ('lagos_ikorodu','Lagos / Ikorodu'),
+    ('lagos_island','Lagos / Island'),
+    ('lagos_kosofe','Lagos / Kosofe'),
+    ('lagos_lagos-island','Lagos / Lagos-Island'),
+    ('lagos_lagos-mainland','Lagos / Lagos-Mainland'),
+    ('lagos_mushin','Lagos / Mushin'),
+    ('lagos_ojo','Lagos / Ojo'),
+    ('lagos_oshodi-isolo','Lagos / Oshodi-Isolo'),
+    ('lagos_shomolu','Lagos / Shomolu'),
+    ('lagos_somolu','Lagos / Somolu'),
+    ('lagos_suru-lere','Lagos / Suru-Lere'),
+    ('nassarawa_akwanga','Nassarawa / Akwanga'),
+    ('nassarawa_awe','Nassarawa / Awe'),
+    ('nassarawa_doma','Nassarawa / Doma'),
+    ('nassarawa_karu','Nassarawa / Karu'),
+    ('nassarawa_keana','Nassarawa / Keana'),
+    ('nassarawa_keffi','Nassarawa / Keffi'),
+    ('nassarawa_kokona','Nassarawa / Kokona'),
+    ('nassarawa_lafia','Nassarawa / Lafia'),
+    ('nassarawa_nassawara','Nassarawa / Nassawara'),
+    ('nassarawa_nassawara_eggon','Nassarawa / Nassawara Eggon'),
+    ('nassarawa_obi','Nassarawa / Obi'),
+    ('nassarawa_toto','Nassarawa / Toto'),
+    ('nassarawa_wambu','Nassarawa / Wambu'),
+    ('niger_agaie','Niger / Agaie'),
+    ('niger_agwara','Niger / Agwara'),
+    ('niger_bida','Niger / Bida'),
+    ('niger_borgu','Niger / Borgu'),
+    ('niger_bosso','Niger / Bosso'),
+    ('niger_chanchaga','Niger / Chanchaga'),
+    ('niger_edati','Niger / Edati'),
+    ('niger_gbako','Niger / Gbako'),
+    ('niger_gurara','Niger / Gurara'),
+    ('niger_katcha','Niger / Katcha'),
+    ('niger_kontagora','Niger / Kontagora'),
+    ('niger_lapai','Niger / Lapai'),
+    ('niger_lavun','Niger / Lavun'),
+    ('niger_magama','Niger / Magama'),
+    ('niger_mariga','Niger / Mariga'),
+    ('niger_mashegu','Niger / Mashegu'),
+    ('niger_mokwa','Niger / Mokwa'),
+    ('niger_muya','Niger / Muya'),
+    ('niger_paikoro','Niger / Paikoro'),
+    ('niger_rafi','Niger / Rafi'),
+    ('niger_rijau','Niger / Rijau'),
+    ('niger_shiroro','Niger / Shiroro'),
+    ('niger_suleja','Niger / Suleja'),
+    ('niger_tafa','Niger / Tafa'),
+    ('niger_wushishi','Niger / Wushishi'),
+    ('ogun_abeokuta-north','Ogun / Abeokuta-North'),
+    ('ogun_abeokuta-south','Ogun / Abeokuta-South'),
+    ('ogun_ado-odo/ota','Ogun / Ado-Odo/Ota'),
+    ('ogun_adoodo-ota','Ogun / Adoodo-Ota'),
+    ('ogun_ewekoro','Ogun / Ewekoro'),
+    ('ogun_idarapo','Ogun / Idarapo'),
+    ('ogun_iffo-otta','Ogun / Iffo-Otta'),
+    ('ogun_ifo','Ogun / Ifo'),
+    ('ogun_igbado-north','Ogun / Igbado-North'),
+    ('ogun_igbado-south','Ogun / Igbado-South'),
+    ('ogun_ijebu-east','Ogun / Ijebu-East'),
+    ('ogun_ijebu-north','Ogun / Ijebu-North'),
+    ('ogun_ijebu-north-east','Ogun / Ijebu-North-East'),
+    ('ogun_ijebu-ode','Ogun / Ijebu-Ode'),
+    ('ogun_ikenne','Ogun / Ikenne'),
+    ('ogun_ilishan_remo','Ogun / Ilishan Remo'),
+    ('ogun_ilugun-alaro','Ogun / Ilugun-Alaro'),
+    ('ogun_imeko-afon','Ogun / Imeko-Afon'),
+    ('ogun_ipokia','Ogun / Ipokia'),
+    ('ogun_lgayekpe_','Ogun / Lgayekpe'),
+    ('ogun_obafemi-owode','Ogun / Obafemi-Owode'),
+    ('ogun_odedah','Ogun / Odedah'),
+    ('ogun_odogbolu','Ogun / Odogbolu'),
+    ('ogun_ogun-waterside','Ogun / Ogun-Waterside'),
+    ('ogun_remo-north','Ogun / Remo-North'),
+    ('ogun_sagamu','Ogun / Sagamu'),
+    ('ogun_yewa','Ogun / Yewa'),
+    ('ondo_akoko','Ondo / Akoko'),
+    ('ondo_akoko-north','Ondo / Akoko-North'),
+    ('ondo_akoko-north-east','Ondo / Akoko-North-East'),
+    ('ondo_akoko-north-west','Ondo / Akoko-North-West'),
+    ('ondo_akoko-south','Ondo / Akoko-South'),
+    ('ondo_akoko-south-east','Ondo / Akoko-South-East'),
+    ('ondo_akoko-south-west','Ondo / Akoko-South-West'),
+    ('ondo_akure','Ondo / Akure'),
+    ('ondo_akure-north','Ondo / Akure-North'),
+    ('ondo_akure-south','Ondo / Akure-South'),
+    ('ondo_ese-odo','Ondo / Ese-Odo'),
+    ('ondo_idanre','Ondo / Idanre'),
+    ('ondo_ifedore','Ondo / Ifedore'),
+    ('ondo_ikale','Ondo / Ikale'),
+    ('ondo_ilaje','Ondo / Ilaje'),
+    ('ondo_ilaje-west','Ondo / Ilaje-West'),
+    ('ondo_ile-oluji-okeigbo','Ondo / Ile-Oluji-Okeigbo'),
+    ('ondo_irele','Ondo / Irele'),
+    ('ondo_odigbo','Ondo / Odigbo'),
+    ('ondo_okiti-pupa','Ondo / Okiti-Pupa'),
+    ('ondo_ondo','Ondo / Ondo'),
+    ('ondo_ondo-east','Ondo / Ondo-East'),
+    ('ondo_ondo-west','Ondo / Ondo-West'),
+    ('ondo_ose','Ondo / Ose'),
+    ('ondo_owo','Ondo / Owo'),
+    ('osun_atakumosa','Osun / Atakumosa'),
+    ('osun_atakumosa_east','Osun / Atakumosa East'),
+    ('osun_ayeda-ade','Osun / Ayeda-Ade'),
+    ('osun_ayedire','Osun / Ayedire'),
+    ('osun_boluwaduro','Osun / Boluwaduro'),
+    ('osun_boripe','Osun / Boripe'),
+    ('osun_ede','Osun / Ede'),
+    ('osun_ede-north','Osun / Ede North'),
+    ('osun_egbedore','Osun / Egbedore'),
+    ('osun_ejigbo','Osun / Ejigbo'),
+    ('osun_ife','Osun / Ife'),
+    ('osun_ife-north','Osun / Ife-North'),
+    ('osun_ife-south','Osun / Ife-South'),
+    ('osun_ife-central','Osun / Ife-Central'),
+    ('osun_ife-east','Osun / Ife-East'),
+    ('osun_ifelodun','Osun / Ifelodun'),
+    ('osun_ila','Osun / Ila'),
+    ('osun_ilesa-east','Osun / Ilesa-East'),
+    ('osun_ilesa-west','Osun / Ilesa-West'),
+    ('osun_ilesha','Osun / Ilesha'),
+    ('osun_ilesha_west','Osun / Ilesha West'),
+    ('osun_irepodun','Osun / Irepodun'),
+    ('osun_irewole','Osun / Irewole'),
+    ('osun_isokan','Osun / Isokan'),
+    ('osun_iwo','Osun / Iwo'),
+    ('osun_obokun','Osun / Obokun'),
+    ('osun_odo-otin','Osun / Odo-Otin'),
+    ('osun_ola_oluwa','Osun / Ola Oluwa'),
+    ('osun_olorunda','Osun / Olorunda'),
+    ('osun_ori-ade','Osun / Ori-Ade'),
+    ('osun_orolu','Osun / Orolu'),
+    ('osun_osogbo','Osun / Osogbo'),
+    ('oyo_afijio','Oyo / Afijio'),
+    ('oyo_akinyele','Oyo / Akinyele'),
+    ('oyo_atiba','Oyo / Atiba'),
+    ('oyo_atigbo','Oyo / Atigbo'),
+    ('oyo_egbeda','Oyo / Egbeda'),
+    ('oyo_ibadan-central','Oyo / Ibadan-Central'),
+    ('oyo_ibadan-north-east','Oyo / Ibadan-North-East'),
+    ('oyo_ibadan-north-west','Oyo / Ibadan-North-West'),
+    ('oyo_ibadan-south-east','Oyo / Ibadan-South-East'),
+    ('oyo_ibadan-south-west','Oyo / Ibadan-South-West'),
+    ('oyo_ibarapa-central','Oyo / Ibarapa-Central'),
+    ('oyo_ibarapa-east','Oyo / Ibarapa-East'),
+    ('oyo_ibarapa-north','Oyo / Ibarapa-North'),
+    ('oyo_ido','Oyo / Ido'),
+    ('oyo_ifedayo','Oyo / Ifedayo'),
+    ('oyo_ifeloju','Oyo / Ifeloju'),
+    ('oyo_irepo','Oyo / Irepo'),
+    ('oyo_iseyin','Oyo / Iseyin'),
+    ('oyo_itseiwaju','Oyo / Itseiwaju'),
+    ('oyo_iwajowa','Oyo / Iwajowa'),
+    ('oyo_kajola','Oyo / Kajola'),
+    ('oyo_lagelu','Oyo / Lagelu'),
+    ('oyo_odo-oluwa','Oyo / Odo-Oluwa'),
+    ('oyo_ogbomosho-north','Oyo / Ogbomosho-North'),
+    ('oyo_ogbomosho-south','Oyo / Ogbomosho-South'),
+    ('oyo_olorunsogo','Oyo / Olorunsogo'),
+    ('oyo_oluyole','Oyo / Oluyole'),
+    ('oyo_ona-ara','Oyo / Ona-Ara'),
+    ('oyo_orelope','Oyo / Orelope'),
+    ('oyo_ori-ire','Oyo / Ori-Ire'),
+    ('oyo_oyo-west','Oyo / Oyo-West'),
+    ('oyo_saki-east','Oyo / Saki-East'),
+    ('oyo_saki-west','Oyo / Saki-West'),
+    ('oyo_surulere','Oyo / Surulere'),
+    ('plateau_barkin-ladi','Plateau / Barkin-Ladi'),
+    ('plateau_bassa','Plateau / Bassa'),
+    ('plateau_bokkos','Plateau / Bokkos'),
+    ('plateau_jos-east','Plateau / Jos-East'),
+    ('plateau_jos-north','Plateau / Jos-North'),
+    ('plateau_jos-south','Plateau / Jos-South'),
+    ('plateau_kanam','Plateau / Kanam'),
+    ('plateau_kanke','Plateau / Kanke'),
+    ('plateau_langtang-north','Plateau / Langtang-North'),
+    ('plateau_langtang-south','Plateau / Langtang-South'),
+    ('plateau_mangu','Plateau / Mangu'),
+    ('plateau_mikang','Plateau / Mikang'),
+    ('plateau_pankshin','Plateau / Pankshin'),
+    ('plateau_quananpan','Plateau / Quananpan'),
+    ('plateau_riyom','Plateau / Riyom'),
+    ('plateau_shendam','Plateau / Shendam'),
+    ('plateau_wase','Plateau / Wase'),
+    ('rivers_aboa/odual','Rivers / Aboa/Odual'),
+    ('rivers_ahoada-east','Rivers / Ahoada-East'),
+    ('rivers_ahoada-west','Rivers / Ahoada-West'),
+    ('rivers_akukutoru','Rivers / Akukutoru'),
+    ('rivers_andoni','Rivers / Andoni'),
+    ('rivers_asari-toru','Rivers / Asari-Toru'),
+    ('rivers_bonny','Rivers / Bonny'),
+    ('rivers_buguma','Rivers / Buguma'),
+    ('rivers_degema','Rivers / Degema'),
+    ('rivers_eleme','Rivers / Eleme'),
+    ('rivers_elfane','Rivers / Elfane'),
+    ('rivers_emuoha','Rivers / Emuoha'),
+    ('rivers_etche','Rivers / Etche'),
+    ('rivers_gokana','Rivers / Gokana'),
+    ('rivers_ikwerre','Rivers / Ikwerre'),
+    ('rivers_khana','Rivers / Khana'),
+    ('rivers_obia/akpor','Rivers / Obia/Akpor'),
+    ('rivers_ogba-egbema-ndoni','Rivers / Ogba-Egbema-Ndoni'),
+    ('rivers_ogu/bolo','Rivers / Ogu/Bolo'),
+    ('rivers_okirika','Rivers / Okirika'),
+    ('rivers_omuma','Rivers / Omuma'),
+    ('rivers_opobo/nkoro','Rivers / Opobo/Nkoro'),
+    ('rivers_oyigbo','Rivers / Oyigbo'),
+    ('rivers_port-harcourt','Rivers / Port-Harcourt'),
+    ('rivers_tai','Rivers / Tai'),
+    ('sokoto_binji','Sokoto / Binji'),
+    ('sokoto_bodinga','Sokoto / Bodinga'),
+    ('sokoto_dange-shuni','Sokoto / Dange-Shuni'),
+    ('sokoto_gada','Sokoto / Gada'),
+    ('sokoto_goronyo','Sokoto / Goronyo'),
+    ('sokoto_gudu','Sokoto / Gudu'),
+    ('sokoto_gwadabawa','Sokoto / Gwadabawa'),
+    ('sokoto_illela','Sokoto / Illela'),
+    ('sokoto_kebbe','Sokoto / Kebbe'),
+    ('sokoto_kware','Sokoto / Kware'),
+    ('sokoto_raba','Sokoto / Raba'),
+    ('sokoto_sabo-birni','Sokoto / Sabo-Birni'),
+    ('sokoto_shagari','Sokoto / Shagari'),
+    ('sokoto_silame','Sokoto / Silame'),
+    ('sokoto_sokoto-north','Sokoto / Sokoto-North'),
+    ('sokoto_sokoto-south','Sokoto / Sokoto-South'),
+    ('sokoto_tambuwal','Sokoto / Tambuwal'),
+    ('sokoto_tangaza','Sokoto / Tangaza'),
+    ('sokoto_tureta','Sokoto / Tureta'),
+    ('sokoto_wamakko','Sokoto / Wamakko'),
+    ('sokoto_wurno','Sokoto / Wurno'),
+    ('sokoto_yabo','Sokoto / Yabo'),
+    ('taraba_ardo-kola','Taraba / Ardo-Kola'),
+    ('taraba_bali','Taraba / Bali'),
+    ('taraba_donga','Taraba / Donga'),
+    ('taraba_gashaka','Taraba / Gashaka'),
+    ('taraba_gassol','Taraba / Gassol'),
+    ('taraba_ibi','Taraba / Ibi'),
+    ('taraba_jalingo','Taraba / Jalingo'),
+    ('taraba_karim-lamido','Taraba / Karim-Lamido'),
+    ('taraba_kurmi','Taraba / Kurmi'),
+    ('taraba_lau','Taraba / Lau'),
+    ('taraba_oilingo','Taraba / Oilingo'),
+    ('taraba_sardauna','Taraba / Sardauna'),
+    ('taraba_takum','Taraba / Takum'),
+    ('taraba_ussa','Taraba / Ussa'),
+    ('taraba_wukari','Taraba / Wukari'),
+    ('taraba_yorro','Taraba / Yorro'),
+    ('taraba_zing','Taraba / Zing'),
+    ('yobe_bade','Yobe / Bade'),
+    ('yobe_borsali','Yobe / Borsali'),
+    ('yobe_damaturu','Yobe / Damaturu'),
+    ('yobe_fika','Yobe / Fika'),
+    ('yobe_fune','Yobe / Fune'),
+    ('yobe_geidam','Yobe / Geidam'),
+    ('yobe_gogaram','Yobe / Gogaram'),
+    ('yobe_gujba','Yobe / Gujba'),
+    ('yobe_gulani','Yobe / Gulani'),
+    ('yobe_jakusko','Yobe / Jakusko'),
+    ('yobe_karasuwa','Yobe / Karasuwa'),
+    ('yobe_machina','Yobe / Machina'),
+    ('yobe_nangere','Yobe / Nangere'),
+    ('yobe_nguru','Yobe / Nguru'),
+    ('yobe_potiskum','Yobe / Potiskum'),
+    ('yobe_tarmua','Yobe / Tarmua'),
+    ('yobe_yunusari','Yobe / Yunusari'),
+    ('zamfara_anka','Zamfara / Anka'),
+    ('zamfara_bakura','Zamfara / Bakura'),
+    ('zamfara_bukkuyum','Zamfara / Bukkuyum'),
+    ('zamfara_bungudu','Zamfara / Bungudu'),
+    ('zamfara_gumi','Zamfara / Gumi'),
+    ('zamfara_gusau','Zamfara / Gusau'),
+    ('zamfara_isa','Zamfara / Isa'),
+    ('zamfara_kaura-namoda','Zamfara / Kaura-Namoda'),
+    ('zamfara_kiyawa','Zamfara / Kiyawa'),
+    ('zamfara_maradun','Zamfara / Maradun'),
+    ('zamfara_maru','Zamfara / Maru'),
+    ('zamfara_shinkafi','Zamfara / Shinkafi'),
+    ('zamfara_talata-mafara','Zamfara / Talata-Mafara'),
+    ('zamfara_tsafe','Zamfara / Tsafe'),
+    ('zamfara_zurmi','Zamfara / Zurmi'),
+    ('osun_aiyedire','Osun / Aiyedire'),
+    ('cross_river_biase','Cross River / Biase'),
+    ('ogun_egbado-north','Ogun / Egbado-North'),
+    ('ogun_egbado-south','Ogun / Egbado-South'),
+    ('oyo_itesiwaju','Oyo / Itesiwaju'),
+    ('oyo_ogo-oluwa','Oyo / Ogo-Oluwa'),
+    ('ebonyi_ohaukwu','Ebonyi / Ohaukwu'),
+    ('ondo_okitipupa','Ondo / Okitipupa'),
+    ('osun_oriade','Osun / Oriade'),
+    ('benue_oturkpo','Benue / Oturkpo'),
+    ('oyo_oyo-east','Oyo / Oyo-East'),
+    ('abia_ukwa-east','Abia / Ukwa-East'),
+    ('akwa_ibom_urue-ffong-oruko','Akwa Ibom / Urue-Offong-Oruko'),
+    ('foreigner','Foreigner')
+    )
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/utils/utils.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/utils/utils.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/utils/utils.py	(revision 14174)
@@ -0,0 +1,88 @@
+## $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 Ikoba.
+"""
+import pytz
+from waeup.ikoba.utils.utils import IkobaUtils, sorted_phone_prefixes
+from ikobacustom.skeleton.interfaces import MessageFactory as _
+
+INT_PHONE_PREFIXES = [
+    (1, _('Nigeria'), '234'),
+    (99, _('Germany'), '49'),
+    (99, _('United States'), '1'),
+    (99, _('Niger'), '227'),
+    (99, _('Benin'), '229'),
+    (99, _('Cameroon'), '237'),
+    (99, _('United Kingdom'), '44'),
+    (99, _('France'), '33'),
+    ]
+
+class SkeletonIkobaUtils(IkobaUtils):
+    """A collection of methods subject to customization.
+    """
+    PORTAL_LANGUAGE = 'en'
+
+    # CURRENCIES must be equal to or an excerpt of ISO_4217_CURRENCIES
+    CURRENCIES = {
+        'EUR': ('EUROPE', _('Euro'), 2, '978'),
+        'USD': ('U.S.', _('US Dollar'), 2, '840'),
+        'NGN': ('NIGERIA', _('Naira'), 2, '566'),
+        }
+
+    CON_CATS_DICT = {
+        'sample': 'Sample Category',
+        'no': 'no contract',
+        }
+
+    PREFERRED_LANGUAGES_DICT = {
+        'en':(1, u'English'),
+        'fr':(2, u'Fran&ccedil;ais'),
+        'de':(3, u'Deutsch'),
+        'ha':(4, u'Hausa'),
+        'yo':(5, u'Yoruba'),
+        'ig':(6, u'Igbo'),
+        }
+
+    EXPORTER_NAMES = (
+        'pdfdocuments',
+        'htmldocuments',
+        'restdocuments',
+        'users',
+        'products',
+        'customers',
+        'skeletoncustomerdocuments',
+        'skeletoncontracts')
+
+    BATCH_PROCESSOR_NAMES = (
+        'skeletoncustomerprocessor',
+        'skeletoncustomerdocumentprocessor',
+        'skeletoncontractprocessor',
+        'skeletonproductprocessor',
+        'pdfdocumentprocessor',
+        'htmldocumentprocessor',
+        'restdocumentprocessor',
+        'userprocessor')
+
+    #: A function to return
+    @classmethod
+    def sorted_phone_prefixes(cls, data=INT_PHONE_PREFIXES, request=None):
+        return sorted_phone_prefixes(data, request)
+
+    @property
+    def tzinfo(self):
+        return pytz.timezone('Africa/Lagos')
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/widgets/__init__.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/widgets/__init__.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/widgets/__init__.py	(revision 14174)
@@ -0,0 +1,1 @@
+# Make this a package.
Index: /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/widgets/phonewidget.py
===================================================================
--- /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/widgets/phonewidget.py	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/src/ikobacustom/skeleton/widgets/phonewidget.py	(revision 14174)
@@ -0,0 +1,28 @@
+## $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
+##
+"""
+A customized phone widget
+"""
+
+from waeup.ikoba.widgets.phonewidget import PhoneWidget
+
+class NigeriaPhoneWidget(PhoneWidget):
+    """A phone widget without area prefix.
+    """
+    subwidget_names = ('country', 'ext')
+    bootstrap_span = 'span3'
Index: /main/ikobacustom.uniben/trunk/versions.cfg
===================================================================
--- /main/ikobacustom.uniben/trunk/versions.cfg	(revision 14174)
+++ /main/ikobacustom.uniben/trunk/versions.cfg	(revision 14174)
@@ -0,0 +1,8 @@
+# Versions of packages used by kofacustom.skeleton
+[buildout]
+extends = sources/waeup.ikoba/versions.cfg
+versions = versions
+
+[versions]
+# Added by buildout at 2016-03-25 07:46:37.907671
+idna = 2.1
