Index: /main/waeup.kofa/trunk/src/waeup/kofa/applicants/interfaces.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/applicants/interfaces.py	(revision 14010)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/applicants/interfaces.py	(revision 14011)
@@ -34,4 +34,6 @@
 from waeup.kofa.payments.interfaces import IOnlinePayment
 from waeup.kofa.schema import PhoneNumber
+from waeup.kofa.schoolgrades import ResultEntryField
+from waeup.kofa.refereeentries import RefereeEntryField
 from waeup.kofa.students.vocabularies import GenderSource, RegNumberSource
 from waeup.kofa.university.vocabularies import (
@@ -411,11 +413,4 @@
         )
 
-    #school_grades = schema.List(
-    #    title = _(u'School Grades'),
-    #    value_type = ResultEntryField(),
-    #    required = False,
-    #    default = [],
-    #    )
-
     notice = schema.Text(
         title = _(u'Notice'),
@@ -444,5 +439,26 @@
         )
 
-class IApplicant(IApplicantBaseData):
+class IApplicantTestData(IKofaObject):
+    """This interface is for demonstration and test purposes only.
+    It can be omitted in customized versions of Kofa.
+    """
+
+    school_grades = schema.List(
+        title = _(u'School Grades'),
+        value_type = ResultEntryField(),
+        required = False,
+        defaultFactory=list,
+        )
+
+    referees = schema.List(
+        title = _(u'Referees'),
+        value_type = RefereeEntryField(),
+        required = False,
+        defaultFactory=list,
+        )
+
+IApplicantTestData['school_grades'].order = IApplicantBaseData['course2'].order
+
+class IApplicant(IApplicantBaseData, IApplicantTestData):
     """This is basically the applicant base data. Here we repeat the
     fields from base data if we have to set the `required` attribute
Index: /main/waeup.kofa/trunk/src/waeup/kofa/interfaces.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/interfaces.py	(revision 14010)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/interfaces.py	(revision 14011)
@@ -273,4 +273,27 @@
     """
 
+class IRefereeEntry(Interface):
+    """A referee entry.
+    """
+    name = schema.TextLine(
+        title = _(u'Name'),
+        required = True,
+        description = _(u'Name'),
+        )
+    email = schema.ASCIILine(
+        title = _(u'Email Address'),
+        default = None,
+        required = True,
+        constraint=validate_email,
+        description = _(u'Email Address'),
+        )
+
+class IRefereeEntryField(IObject):
+    """A zope.schema-like field for usage in interfaces.
+
+    Marker interface to distuingish result entries from ordinary
+    object fields. Needed for registration of widgets.
+    """
+
 class IKofaUtils(Interface):
     """A collection of methods which are subject to customization.
@@ -792,5 +815,5 @@
             ),
         required = False,
-        default = [],
+        defaultFactory=list,
         )
 
Index: /main/waeup.kofa/trunk/src/waeup/kofa/refereeentries.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/refereeentries.py	(revision 14011)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/refereeentries.py	(revision 14011)
@@ -0,0 +1,121 @@
+## $Id: schoolgrades.py 12426 2015-01-08 14:25:54Z uli $
+##
+## 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
+##
+"""Components representing and aggregating referee entries.
+"""
+import grok
+from zope.formlib.interfaces import IInputWidget, IDisplayWidget
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.schema.fieldproperty import FieldProperty
+from zope.schema import Object
+from waeup.kofa.interfaces import IRefereeEntry, IRefereeEntryField
+from waeup.kofa.widgets.objectwidget import (
+    KofaObjectWidget, KofaObjectDisplayWidget
+    )
+
+
+#: A unique default value.
+DEFAULT_VALUE = object()
+
+
+class RefereeEntry(grok.Model):
+    """A referee entry contains a name and a email.
+    """
+    grok.implements(IRefereeEntry)
+    name = FieldProperty(IRefereeEntry['name'])
+    email = FieldProperty(IRefereeEntry['email'])
+
+    def __init__(self, name=None, email=None):
+        super(RefereeEntry, self).__init__()
+        if name is not None:
+            self.name = name
+        if email is not None:
+            self.email = email
+        return
+
+    def __eq__(self, obj):
+        """Two RefereeEntry objects are equal if their `name` and
+           `email` are equal.
+        """
+        for name in ('name', 'email',):
+            if getattr(self, name) != getattr(obj, name, DEFAULT_VALUE):
+                return False
+        return True
+
+    def __ne__(self, other):
+        """Two RefereeEntries are not equal, if their equality test fails.
+
+        a != b <-> not(a == b). Python doc tell, that __ne__ should
+        also be rovided, whenever __eq__ is implemented.
+        """
+        return not self.__eq__(other)
+
+    def to_string(self):
+        """A string representation that can be used in exports.
+
+        Returned is a unicode string of format ``(u'<NAME>',u'<EMAIL>')``.
+        """
+        return unicode((self.name, self.email))
+
+    @classmethod
+    def from_string(cls, string):
+        """Create new RefereeEntry instance based on `string`.
+
+        The string is expected to be in format as delivered by
+        meth:`to_string`.
+
+        This is a classmethod. This means, you normally will call::
+
+          RefereeEntry.from_string(mystring)
+
+        i.e. use the `RefereeEntry` class, not an instance thereof.
+        """
+        string = string.replace("u''", "None")
+        name, email = eval(string)
+        return cls(name, email)
+
+
+class RefereeEntryField(Object):
+    """A zope.schema-like field for usage in interfaces.
+
+    If you want to define an interface containing referee entries, you
+    can do so like this::
+
+      class IMyInterface(Interface):
+          my_referee_entry = RefereeEntryField()
+
+    Default widgets are registered to render referee entry fields.
+    """
+    grok.implements(IRefereeEntryField)
+
+    def __init__(self, **kw):
+        super(RefereeEntryField, self).__init__(IRefereeEntry, **kw)
+        return
+
+
+# register KofaObjectWidgets as default widgets for IRefereeEntryFields
+@grok.adapter(IRefereeEntryField, IBrowserRequest)
+@grok.implementer(IInputWidget)
+def referee_entry_input_widget(obj, req):
+    return KofaObjectWidget(obj, req, RefereeEntry)
+
+
+# register a display widget for IRefereeEntryFields
+@grok.adapter(IRefereeEntryField, IBrowserRequest)
+@grok.implementer(IDisplayWidget)
+def referee_entry_display_widget(obj, req):
+    return KofaObjectDisplayWidget(obj, req, RefereeEntry)
Index: /main/waeup.kofa/trunk/src/waeup/kofa/widgets/objectwidget.pt
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/widgets/objectwidget.pt	(revision 14010)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/widgets/objectwidget.pt	(revision 14011)
@@ -2,4 +2,7 @@
   <span tal:define="error widget/error"
     tal:replace="structure error" tal:condition="error" />
+  <span class="hint" tal:condition="widget/hint">
+    <span tal:content="widget/hint" />:
+  </span>
   <span tal:replace="structure widget" />
 </span>
