Index: main/waeup.kofa/trunk/src/waeup/kofa/utils/converters.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/utils/converters.py	(revision 7917)
+++ main/waeup.kofa/trunk/src/waeup/kofa/utils/converters.py	(revision 7932)
@@ -24,8 +24,12 @@
 from zope.formlib.form import (
     _widgetKey, WidgetInputError, ValidationError, InputErrors, expandPrefix)
-from zope.formlib.interfaces import IInputWidget, ISimpleInputWidget
+from zope.formlib.interfaces import IInputWidget
 from zope.interface import Interface
 from zope.publisher.browser import TestRequest
-from waeup.kofa.interfaces import IObjectConverter
+from zope.schema.interfaces import IList
+from waeup.kofa.interfaces import (
+    IObjectConverter, IResultEntryField, IFieldConverter, SubjectSource,
+    GradeSource)
+from waeup.kofa.schoolgrades import ResultEntry
 
 class ExtendedCheckBoxWidget(CheckBoxWidget):
@@ -85,5 +89,4 @@
     store the fieldname for which a validation error happened in the
     returned error list (what the original does not do).
-
     """
     errors = []
@@ -108,4 +111,62 @@
     return errors
 
+class DefaultFieldConverter(grok.Adapter):
+    grok.context(Interface)
+    grok.provides(IFieldConverter)
+
+    def request_data(self, name, value, schema_field, prefix=''):
+        return {prefix: value}
+
+class ListFieldConverter(grok.Adapter):
+    grok.context(IList)
+    grok.provides(IFieldConverter)
+
+    def request_data(self, name, value, schema_field, prefix=''):
+        value_type = schema_field.value_type
+        try:
+            items = eval(value)
+        except:
+            return {prefix: value}
+        result = {'%s.count' % prefix: len(items)}
+        for num, item in enumerate(items):
+            sub_converter = IFieldConverter(value_type)
+            result.update(sub_converter.request_data(
+                unicode(num), unicode(item),
+                value_type, "%s.%s." % (prefix, num)))
+        return result
+
+class ResultEntryConverter(grok.Adapter):
+    grok.context(IResultEntryField)
+    grok.provides(IFieldConverter)
+
+    def request_data(self, name, value, schema_field, prefix=''):
+        """Turn CSV values into ResultEntry-compatible form data.
+
+        Expects as `value` a _string_ like ``(u'mysubject',
+        u'mygrade')`` and turns it into some dict like::
+
+          {
+            'form.grade.subject': u'9234896395...',
+            'form.grade.grade': u'7e67e9e777..'
+            }
+
+        where the values are tokens from appropriate sources.
+
+        Such dicts can be transformed into real ResultEntry objects by
+        input widgets used in converters.
+        """
+        try:
+            entry = ResultEntry.from_string(value)
+            subj, grade = entry.subject, entry.grade
+        except:
+            return {prefix: value}
+        # web forms send tokens instead of real values
+        s_token = SubjectSource().factory.getToken(subj)
+        g_token = GradeSource().factory.getToken(grade)
+        result = {
+            "%ssubject" % (prefix): s_token,
+            "%sgrade" % (prefix): g_token,
+            }
+        return result
 
 class DefaultObjectConverter(grok.Adapter):
@@ -172,5 +233,16 @@
         request = TestRequest(form={})
         for key, val in data_dict.items():
-            request.form['form.%s' % key] = val
+            field = form_fields.get(key, None)
+            if field is not None:
+                # let adapters to the respective schema fields do the
+                # further fake-request processing
+                schema_field = field.interface[field.__name__]
+                field_converter = IFieldConverter(schema_field)
+                request.form.update(
+                    field_converter.request_data(
+                        key, val, schema_field, 'form.%s' % key)
+                    )
+            else:
+                request.form['form.%s' % key] = val
 
         obj = context
@@ -185,4 +257,5 @@
 
         invariant_errors = form.checkInvariants(form_fields, new_data)
+
         if errors or invariant_errors:
             err_messages = [(key, err.args[0]) for key, err in errors]
Index: main/waeup.kofa/trunk/src/waeup/kofa/utils/tests/test_converters.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/utils/tests/test_converters.py	(revision 7917)
+++ main/waeup.kofa/trunk/src/waeup/kofa/utils/tests/test_converters.py	(revision 7932)
@@ -33,8 +33,10 @@
 from waeup.kofa.app import University
 from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase
+from waeup.kofa.interfaces import (
+    SimpleKofaVocabulary, SubjectSource, GradeSource)
+from waeup.kofa.schoolgrades import ResultEntryField
 from waeup.kofa.university import Faculty
 from waeup.kofa.utils.converters import IObjectConverter
 from waeup.kofa.utils.helpers import attrs_to_fields
-from waeup.kofa.interfaces import SimpleKofaVocabulary
 
 colors = SimpleKofaVocabulary(
@@ -85,4 +87,17 @@
         vocabulary = car_nums,
         )
+    grades = schema.List(
+        title = u'School Grades',
+        value_type = ResultEntryField(),
+        required = True,
+        default = [],
+        )
+    friends = schema.List(
+        title = u'Friends',
+        value_type = schema.TextLine(
+            title = u'Name',
+            )
+        )
+
     @invariant
     def kevinIsYoung(contact):
@@ -330,2 +345,31 @@
         assert data['num_cars'] == 1
         return
+
+    def test_list_of_textlines(self):
+        # We can convert lists of text lines
+        converter = IObjectConverter(IContact)
+        err, inv_err, data = converter.fromStringDict(
+            {"friends": "['Fred', 'Wilma']"}, 'contact')
+        self.assertEqual(
+            data, {'friends': ['Fred', 'Wilma']})
+        return
+
+    def test_list_of_resultentries(self):
+        # We can handle lists of result entries
+        converter = IObjectConverter(IContact)
+        # get currently valid values
+        s_src, g_src = SubjectSource(), GradeSource()
+        s_val1, s_val2 = list(s_src.factory.getValues())[0:2]
+        g_val1, g_val2 = list(g_src.factory.getValues())[0:2]
+        req_string = u"[('%s', '%s'), ('%s', '%s')]" % (
+                s_val1, g_val1, s_val2, g_val2)
+        err, inv_err, data = converter.fromStringDict(
+            {"grades": req_string,
+             },
+            'contact')
+        result_grades = data['grades']
+        self.assertTrue(isinstance(result_grades, list))
+        self.assertEqual(len(result_grades), 2)
+        self.assertEqual(result_grades[0].subject, s_val1)
+        self.assertEqual(result_grades[0].grade, g_val1)
+        return
