all web tests OK
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Mon, 21 Dec 2009 20:28:08 +0100
changeset 4172 4d4cef034eec
parent 4171 f1b9f0ed1253
child 4173 cfd5d3270f99
all web tests OK
web/test/unittest_application.py
web/test/unittest_breadcrumbs.py
web/test/unittest_form.py
web/test/unittest_formfields.py
web/test/unittest_urlpublisher.py
web/test/unittest_views_basecontrollers.py
web/test/unittest_views_editforms.py
web/test/unittest_viewselector.py
--- a/web/test/unittest_application.py	Mon Dec 21 20:28:01 2009 +0100
+++ b/web/test/unittest_application.py	Mon Dec 21 20:28:08 2009 +0100
@@ -169,11 +169,11 @@
     def test_publish_validation_error(self):
         req = self.request()
         user = self.user()
+        eid = unicode(user.eid)
         req.form = {
-            'eid':       `user.eid`,
-            '__type:'+`user.eid`:    'CWUser',
-            'login:'+`user.eid`:     '', # ERROR: no login specified
-            'edits-login:'+`user.eid`: unicode(user.login),
+            'eid':       eid,
+            '__type:'+eid:    'CWUser', '_cw_edited_fields:'+eid: 'login-subject',
+            'login-subject:'+eid:     '', # ERROR: no login specified
              # just a sample, missing some necessary information for real life
             '__errorurl': 'view?vid=edition...'
             }
@@ -182,9 +182,8 @@
         eidmap = forminfo['eidmap']
         self.assertEquals(eidmap, {})
         values = forminfo['values']
-        self.assertEquals(values['login:'+`user.eid`], '')
-        self.assertEquals(values['edits-login:'+`user.eid`], user.login)
-        self.assertEquals(values['eid'], `user.eid`)
+        self.assertEquals(values['login-subject:'+eid], '')
+        self.assertEquals(values['eid'], eid)
         errors = forminfo['errors']
         self.assertEquals(errors.entity, user.eid)
         self.assertEquals(errors.errors['login'], 'required attribute')
@@ -194,16 +193,16 @@
         """test creation of two linked entities
         """
         req = self.request()
-        form = {'eid': ['X', 'Y'],
-                '__type:X': 'CWUser',
+        form = {'eid': ['X', 'Y'], '__maineid': 'X',
+                '__type:X': 'CWUser', '_cw_edited_fields:X': 'login-subject,surname-subject',
                 # missing required field
-                'login:X': u'', 'edits-login:X': '',
-                'surname:X': u'Mr Ouaoua', 'edits-surname:X': '',
-                '__type:Y': 'EmailAddress',
+                'login-subject:X': u'',
+                'surname-subject:X': u'Mr Ouaoua',
                 # but email address is set
-                'address:Y': u'bougloup@logilab.fr', 'edits-address:Y': '',
-                'alias:Y': u'', 'edits-alias:Y': '',
-                'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE,
+                '__type:Y': 'EmailAddress', '_cw_edited_fields:Y': 'address-subject,alias-subject,use_email-object',
+                'address-subject:Y': u'bougloup@logilab.fr',
+                'alias-subject:Y': u'',
+                'use_email-object:Y': 'X',
                 # necessary to get validation error handling
                 '__errorurl': 'view?vid=edition...',
                 }
@@ -212,7 +211,7 @@
         req.edited_eids = lambda : ('Y', 'X')
         path, params = self.expect_redirect(lambda x: self.app_publish(x, 'edit'), req)
         forminfo = req.get_session_data('view?vid=edition...')
-        self.assertUnorderedIterableEquals(forminfo['eidmap'].keys(), ['X', 'Y'])
+        self.assertEquals(set(forminfo['eidmap']), set('XY'))
         self.assertEquals(forminfo['errors'].entity, forminfo['eidmap']['X'])
         self.assertEquals(forminfo['errors'].errors, {'login': 'required attribute',
                                                       'upassword': 'required attribute'})
--- a/web/test/unittest_breadcrumbs.py	Mon Dec 21 20:28:01 2009 +0100
+++ b/web/test/unittest_breadcrumbs.py	Mon Dec 21 20:28:08 2009 +0100
@@ -3,8 +3,9 @@
 class BreadCrumbsTC(CubicWebTC):
 
     def test_base(self):
-        f1 = self.add_entity('Folder', name=u'par&ent')
-        f2 = self.add_entity('Folder', name=u'chi&ld')
+        req = self.request()
+        f1 = req.create_entity('Folder', name=u'par&ent')
+        f2 = req.create_entity('Folder', name=u'chi&ld')
         self.execute('SET F2 filed_under F1 WHERE F1 eid %(f1)s, F2 eid %(f2)s',
                      {'f1' : f1.eid, 'f2' : f2.eid})
         self.commit()
@@ -15,3 +16,7 @@
         self.assertEquals(ibc.render(),
                           """<span id="breadcrumbs" class="pathbar">&#160;&gt;&#160;<a href="http://testing.fr/cubicweb/Folder">folder_plural</a>&#160;&gt;&#160;<a href="http://testing.fr/cubicweb/folder/%s" title="">par&amp;ent</a>&#160;&gt;&#160;
 chi&amp;ld</span>""" % f1.eid)
+
+if __name__ == '__main__':
+    from logilab.common.testlib import unittest_main
+    unittest_main()
--- a/web/test/unittest_form.py	Mon Dec 21 20:28:01 2009 +0100
+++ b/web/test/unittest_form.py	Mon Dec 21 20:28:08 2009 +0100
@@ -15,7 +15,7 @@
 from cubicweb import Binary
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.web.formfields import (IntField, StringField, RichTextField,
-                                     DateTimeField, DateTimePicker,
+                                     PasswordField, DateTimeField, DateTimePicker,
                                      FileField, EditableFileField)
 from cubicweb.web.formwidgets import PasswordInput, Input
 from cubicweb.web.views.forms import EntityFieldsForm, FieldsForm
@@ -27,10 +27,10 @@
 
     def test_form_field_format(self):
         form = FieldsForm(self.request(), None)
-        self.assertEquals(form.form_field_format(None), 'text/html')
+        self.assertEquals(StringField().format(form), 'text/html')
         self.execute('INSERT CWProperty X: X pkey "ui.default-text-format", X value "text/rest", X for_user U WHERE U login "admin"')
         self.commit()
-        self.assertEquals(form.form_field_format(None), 'text/rest')
+        self.assertEquals(StringField().format(form), 'text/rest')
 
 
 class EntityFieldsFormTC(CubicWebTC):
@@ -41,42 +41,28 @@
         self.entity = self.user(self.req)
 
     def test_form_field_vocabulary_unrelated(self):
-        b = self.add_entity('BlogEntry', title=u'di mascii code', content=u'a best-seller')
-        t = self.add_entity('Tag', name=u'x')
-        form1 = EntityFieldsForm(self.request(), None, entity=t)
-        unrelated = [reid for rview, reid in form1.subject_relation_vocabulary('tags')]
+        b = self.req.create_entity('BlogEntry', title=u'di mascii code', content=u'a best-seller')
+        t = self.req.create_entity('Tag', name=u'x')
+        form1 = self.vreg['forms'].select('edition', self.req, entity=t)
+        unrelated = [reid for rview, reid in form1.field_by_name('tags', 'subject', t.e_schema).choices(form1)]
         self.failUnless(b.eid in unrelated, unrelated)
-        form2 = EntityFieldsForm(self.request(), None, entity=b)
-        unrelated = [reid for rview, reid in form2.object_relation_vocabulary('tags')]
+        form2 = self.vreg['forms'].select('edition', self.req, entity=b)
+        unrelated = [reid for rview, reid in form2.field_by_name('tags', 'object', t.e_schema).choices(form2)]
         self.failUnless(t.eid in unrelated, unrelated)
         self.execute('SET X tags Y WHERE X is Tag, Y is BlogEntry')
-        unrelated = [reid for rview, reid in form1.subject_relation_vocabulary('tags')]
+        unrelated = [reid for rview, reid in form1.field_by_name('tags', 'subject', t.e_schema).choices(form1)]
         self.failIf(b.eid in unrelated, unrelated)
-        unrelated = [reid for rview, reid in form2.object_relation_vocabulary('tags')]
+        unrelated = [reid for rview, reid in form2.field_by_name('tags', 'object', t.e_schema).choices(form2)]
         self.failIf(t.eid in unrelated, unrelated)
 
 
     def test_form_field_vocabulary_new_entity(self):
         e = self.vreg['etypes'].etype_class('CWUser')(self.request())
-        form = EntityFieldsForm(e.req, None, entity=e)
-        unrelated = [rview for rview, reid in form.subject_relation_vocabulary('in_group')]
+        form = self.vreg['forms'].select('edition', self.req, entity=e)
+        unrelated = [rview for rview, reid in form.field_by_name('in_group', 'subject').choices(form)]
         # should be default groups but owners, i.e. managers, users, guests
         self.assertEquals(unrelated, [u'guests', u'managers', u'users'])
 
-    # def test_subject_in_state_vocabulary(self):
-    #     # on a new entity
-    #     e = self.etype_instance('CWUser')
-    #     form = EntityFieldsForm(self.request(), None, entity=e)
-    #     states = list(form.subject_in_state_vocabulary('in_state'))
-    #     self.assertEquals(len(states), 1)
-    #     self.assertEquals(states[0][0], u'activated') # list of (combobox view, state eid)
-    #     # on an existant entity
-    #     e = self.user()
-    #     form = EntityFieldsForm(self.request(), None, entity=e)
-    #     states = list(form.subject_in_state_vocabulary('in_state'))
-    #     self.assertEquals(len(states), 1)
-    #     self.assertEquals(states[0][0], u'deactivated') # list of (combobox view, state eid)
-
     def test_consider_req_form_params(self):
         e = self.vreg['etypes'].etype_class('CWUser')(self.request())
         e.eid = 'A'
@@ -84,20 +70,20 @@
         field = StringField(name='login', eidparam=True)
         form.append_field(field)
         form.build_context({})
-        self.assertEquals(form.form_field_display_value(field, {}), 'toto')
+        self.assertEquals(field.display_value(form), 'toto')
 
 
     def test_linkto_field_duplication(self):
         e = self.vreg['etypes'].etype_class('CWUser')(self.request())
         e.eid = 'A'
-        e.req = self.req
+        e._cw = self.req
         geid = self.execute('CWGroup X WHERE X name "users"')[0][0]
         self.req.form['__linkto'] = 'in_group:%s:subject' % geid
         form = self.vreg['forms'].select('edition', self.req, entity=e)
         form.content_type = 'text/html'
         pageinfo = self._check_html(form.render(), form, template=None)
         inputs = pageinfo.find_tag('select', False)
-        self.failUnless(any(attrs for t, attrs in inputs if attrs.get('name') == 'in_group:A'))
+        self.failUnless(any(attrs for t, attrs in inputs if attrs.get('name') == 'in_group-subject:A'))
         inputs = pageinfo.find_tag('input', False)
         self.failIf(any(attrs for t, attrs in inputs if attrs.get('name') == '__linkto'))
 
@@ -106,8 +92,7 @@
         form = self.vreg['views'].select('doreledit', self.request(),
                                          rset=rset, row=0, rtype='content')
         data = form.render(row=0, rtype='content')
-        self.failUnless('edits-content' in data)
-        self.failUnless('edits-content_format' in data)
+        self.failUnless('content_format' in data)
 
     # form view tests #########################################################
 
@@ -140,98 +125,102 @@
     def _render_entity_field(self, name, form):
         form.build_context({})
         renderer = FormRenderer(self.req)
-        return form.field_by_name(name).render(form, renderer)
+        return form.field_by_name(name, 'subject').render(form, renderer)
 
     def _test_richtextfield(self, expected):
         class RTFForm(EntityFieldsForm):
-            description = RichTextField()
+            description = RichTextField(eidparam=True, role='subject')
         state = self.execute('State X WHERE X name "activated", X state_of WF, WF workflow_of ET, ET name "CWUser"').get_entity(0, 0)
         form = RTFForm(self.req, redirect_path='perdu.com', entity=state)
         # make it think it can use fck editor anyway
-        form.form_field_format = lambda x: 'text/html'
+        form.field_by_name('description', 'subject').format = lambda x: 'text/html'
         self.assertTextEquals(self._render_entity_field('description', form),
                               expected % {'eid': state.eid})
 
 
     def test_richtextfield_1(self):
         self.req.use_fckeditor = lambda: False
-        self._test_richtextfield('''<select id="description_format:%(eid)s" name="description_format:%(eid)s" size="1" style="display: block" tabindex="1">
+        self._test_richtextfield('''<select id="description_format-subject:%(eid)s" name="description_format-subject:%(eid)s" size="1" style="display: block" tabindex="1">
 <option value="text/cubicweb-page-template">text/cubicweb-page-template</option>
-<option value="text/html">text/html</option>
+<option selected="selected" value="text/html">text/html</option>
 <option value="text/plain">text/plain</option>
-<option selected="selected" value="text/rest">text/rest</option>
-</select><textarea cols="80" id="description:%(eid)s" name="description:%(eid)s" onkeyup="autogrow(this)" rows="2" tabindex="2"></textarea>''')
+<option value="text/rest">text/rest</option>
+</select><textarea cols="80" id="description-subject:%(eid)s" name="description-subject:%(eid)s" onkeyup="autogrow(this)" rows="2" tabindex="2"></textarea>''')
 
 
     def test_richtextfield_2(self):
         self.req.use_fckeditor = lambda: True
-        self._test_richtextfield('<input name="description_format:%(eid)s" type="hidden" value="text/rest" /><textarea cols="80" cubicweb:type="wysiwyg" id="description:%(eid)s" name="description:%(eid)s" onkeyup="autogrow(this)" rows="2" tabindex="1"></textarea>')
+        self._test_richtextfield('<input name="description_format-subject:%(eid)s" type="hidden" value="text/html" /><textarea cols="80" cubicweb:type="wysiwyg" id="description-subject:%(eid)s" name="description-subject:%(eid)s" onkeyup="autogrow(this)" rows="2" tabindex="1"></textarea>')
 
 
     def test_filefield(self):
         class FFForm(EntityFieldsForm):
-            data = FileField(format_field=StringField(name='data_format', max_length=50),
-                             encoding_field=StringField(name='data_encoding', max_length=20))
-        file = self.add_entity('File', data_name=u"pouet.txt", data_encoding=u'UTF-8',
+            data = FileField(
+                format_field=StringField(name='data_format', max_length=50,
+                                         eidparam=True, role='subject'),
+                encoding_field=StringField(name='data_encoding', max_length=20,
+                                           eidparam=True, role='subject'),
+                eidparam=True, role='subject')
+        file = self.req.create_entity('File', data_name=u"pouet.txt", data_encoding=u'UTF-8',
                                data=Binary('new widgets system'))
         form = FFForm(self.req, redirect_path='perdu.com', entity=file)
         self.assertTextEquals(self._render_entity_field('data', form),
-                              '''<input id="data:%(eid)s" name="data:%(eid)s" tabindex="1" type="file" value="" />
-<a href="javascript: toggleVisibility(&#39;data:%(eid)s-advanced&#39;)" title="show advanced fields"><img src="http://testing.fr/cubicweb/data/puce_down.png" alt="show advanced fields"/></a>
-<div id="data:%(eid)s-advanced" class="hidden">
-<label for="data_format:%(eid)s">data_format</label><input id="data_format:%(eid)s" maxlength="50" name="data_format:%(eid)s" size="45" tabindex="2" type="text" value="text/plain" /><br/>
-<label for="data_encoding:%(eid)s">data_encoding</label><input id="data_encoding:%(eid)s" maxlength="20" name="data_encoding:%(eid)s" size="20" tabindex="3" type="text" value="UTF-8" /><br/>
+                              '''<input id="data-subject:%(eid)s" name="data-subject:%(eid)s" tabindex="1" type="file" value="" />
+<a href="javascript: toggleVisibility(&#39;data-subject:%(eid)s-advanced&#39;)" title="show advanced fields"><img src="http://testing.fr/cubicweb/data/puce_down.png" alt="show advanced fields"/></a>
+<div id="data-subject:%(eid)s-advanced" class="hidden">
+<label for="data_format-subject:%(eid)s">data_format</label><input id="data_format-subject:%(eid)s" maxlength="50" name="data_format-subject:%(eid)s" size="45" tabindex="2" type="text" value="text/plain" /><br/>
+<label for="data_encoding-subject:%(eid)s">data_encoding</label><input id="data_encoding-subject:%(eid)s" maxlength="20" name="data_encoding-subject:%(eid)s" size="20" tabindex="3" type="text" value="UTF-8" /><br/>
 </div>
 <br/>
-<input name="data:%(eid)s__detach" type="checkbox" />
+<input name="data-subject__detach:%(eid)s" type="checkbox" />
 detach attached file
 ''' % {'eid': file.eid})
 
 
     def test_editablefilefield(self):
         class EFFForm(EntityFieldsForm):
-            data = EditableFileField(format_field=StringField(name='data_format', max_length=50),
-                                     encoding_field=StringField(name='data_encoding', max_length=20))
-            def form_field_encoding(self, field):
-                return 'ascii'
-            def form_field_format(self, field):
-                return 'text/plain'
-        file = self.add_entity('File', data_name=u"pouet.txt", data_encoding=u'UTF-8',
+            data = EditableFileField(
+                format_field=StringField('data_format', max_length=50,
+                                         eidparam=True, role='subject'),
+                encoding_field=StringField('data_encoding', max_length=20,
+                                           eidparam=True, role='subject'),
+                eidparam=True, role='subject')
+        file = self.req.create_entity('File', data_name=u"pouet.txt", data_encoding=u'UTF-8',
                                data=Binary('new widgets system'))
         form = EFFForm(self.req, redirect_path='perdu.com', entity=file)
         self.assertTextEquals(self._render_entity_field('data', form),
-                              '''<input id="data:%(eid)s" name="data:%(eid)s" tabindex="1" type="file" value="" />
-<a href="javascript: toggleVisibility(&#39;data:%(eid)s-advanced&#39;)" title="show advanced fields"><img src="http://testing.fr/cubicweb/data/puce_down.png" alt="show advanced fields"/></a>
-<div id="data:%(eid)s-advanced" class="hidden">
-<label for="data_format:%(eid)s">data_format</label><input id="data_format:%(eid)s" maxlength="50" name="data_format:%(eid)s" size="45" tabindex="2" type="text" value="text/plain" /><br/>
-<label for="data_encoding:%(eid)s">data_encoding</label><input id="data_encoding:%(eid)s" maxlength="20" name="data_encoding:%(eid)s" size="20" tabindex="3" type="text" value="UTF-8" /><br/>
+                              '''<input id="data-subject:%(eid)s" name="data-subject:%(eid)s" tabindex="1" type="file" value="" />
+<a href="javascript: toggleVisibility(&#39;data-subject:%(eid)s-advanced&#39;)" title="show advanced fields"><img src="http://testing.fr/cubicweb/data/puce_down.png" alt="show advanced fields"/></a>
+<div id="data-subject:%(eid)s-advanced" class="hidden">
+<label for="data_format-subject:%(eid)s">data_format</label><input id="data_format-subject:%(eid)s" maxlength="50" name="data_format-subject:%(eid)s" size="45" tabindex="2" type="text" value="text/plain" /><br/>
+<label for="data_encoding-subject:%(eid)s">data_encoding</label><input id="data_encoding-subject:%(eid)s" maxlength="20" name="data_encoding-subject:%(eid)s" size="20" tabindex="3" type="text" value="UTF-8" /><br/>
 </div>
 <br/>
-<input name="data:%(eid)s__detach" type="checkbox" />
+<input name="data-subject__detach:%(eid)s" type="checkbox" />
 detach attached file
 <p><b>You can either submit a new file using the browse button above, or choose to remove already uploaded file by checking the "detach attached file" check-box, or edit file content online with the widget below.</b></p>
-<textarea cols="80" name="data:%(eid)s" onkeyup="autogrow(this)" rows="3" tabindex="4">new widgets system</textarea>''' % {'eid': file.eid})
+<textarea cols="80" name="data-subject:%(eid)s" onkeyup="autogrow(this)" rows="3" tabindex="4">new widgets system</textarea>''' % {'eid': file.eid})
 
 
     def test_passwordfield(self):
         class PFForm(EntityFieldsForm):
-            upassword = StringField(widget=PasswordInput)
+            upassword = PasswordField(eidparam=True, role='subject')
         form = PFForm(self.req, redirect_path='perdu.com', entity=self.entity)
         self.assertTextEquals(self._render_entity_field('upassword', form),
-                              '''<input id="upassword:%(eid)s" name="upassword:%(eid)s" tabindex="1" type="password" value="__cubicweb_internal_field__" />
+                              '''<input id="upassword-subject:%(eid)s" name="upassword-subject:%(eid)s" tabindex="1" type="password" value="__cubicweb_internal_field__" />
 <br/>
-<input name="upassword-confirm:%(eid)s" tabindex="1" type="password" value="__cubicweb_internal_field__" />
+<input name="upassword-subject-confirm:%(eid)s" tabindex="1" type="password" value="__cubicweb_internal_field__" />
 &#160;
 <span class="emphasis">confirm password</span>''' % {'eid': self.entity.eid})
 
 
-    def test_datefield(self):
-        class DFForm(EntityFieldsForm):
-            creation_date = DateTimeField(widget=Input)
-        form = DFForm(self.req, entity=self.entity)
-        init, cur = (fromstring(self._render_entity_field(attr, form)).get('value')
-                     for attr in ('edits-creation_date', 'creation_date'))
-        self.assertEquals(init, cur)
+    # def test_datefield(self):
+    #     class DFForm(EntityFieldsForm):
+    #         creation_date = DateTimeField(widget=Input)
+    #     form = DFForm(self.req, entity=self.entity)
+    #     init, cur = (fromstring(self._render_entity_field(attr, form)).get('value')
+    #                  for attr in ('edits-creation_date', 'creation_date'))
+    #     self.assertEquals(init, cur)
 
 if __name__ == '__main__':
     unittest_main()
--- a/web/test/unittest_formfields.py	Mon Dec 21 20:28:01 2009 +0100
+++ b/web/test/unittest_formfields.py	Mon Dec 21 20:28:08 2009 +0100
@@ -46,7 +46,6 @@
         description_format_field = guess_field(schema['State'], schema['description_format'], skip_meta_attr=False)
         self.assertEquals(description_format_field.internationalizable, True)
         self.assertEquals(description_format_field.sort, True)
-        self.assertEquals(description_format_field.initial(None), 'text/rest')
 
 #         wikiid_field = guess_field(schema['State'], schema['wikiid'])
 #         self.assertIsInstance(wikiid_field, StringField)
@@ -92,9 +91,10 @@
 
     def test_constraints_priority(self):
         salesterm_field = guess_field(schema['Salesterm'], schema['reason'])
-        constraints = schema['reason'].rproperty('Salesterm', 'String', 'constraints')
+        constraints = schema['reason'].rdef('Salesterm', 'String').constraints
         self.assertEquals([c.__class__ for c in constraints],
                           [SizeConstraint, StaticVocabularyConstraint])
+        self.assertIsInstance(salesterm_field, StringField)
         self.assertIsInstance(salesterm_field.widget, Select)
 
 
@@ -102,7 +102,6 @@
         field = guess_field(schema['CWAttribute'], schema['indexed'])
         self.assertIsInstance(field, BooleanField)
         self.assertEquals(field.required, False)
-        self.assertEquals(field.initial(None), None)
         self.assertIsInstance(field.widget, Radio)
         self.assertEquals(field.vocabulary(mock(_cw=mock(_=unicode))),
                           [(u'yes', '1'), (u'no', '')])
@@ -126,10 +125,10 @@
         self.assertEquals(description_format_field.internationalizable, True)
         self.assertEquals(description_format_field.sort, True)
         # unlike below, initial is bound to form.form_field_format
-        self.assertEquals(description_format_field.initial(form), 'text/html')
+        self.assertEquals(description_format_field.value(form), 'text/html')
         self.execute('INSERT CWProperty X: X pkey "ui.default-text-format", X value "text/rest", X for_user U WHERE U login "admin"')
         self.commit()
-        self.assertEquals(description_format_field.initial(form), 'text/rest')
+        self.assertEquals(description_format_field.value(form), 'text/rest')
 
 
 class UtilsTC(TestCase):
--- a/web/test/unittest_urlpublisher.py	Mon Dec 21 20:28:01 2009 +0100
+++ b/web/test/unittest_urlpublisher.py	Mon Dec 21 20:28:08 2009 +0100
@@ -23,8 +23,9 @@
 
     def setup_database(self):
         self.create_user(u'ÿsaÿe')
-        b = self.add_entity('BlogEntry', title=u'hell\'o', content=u'blabla')
-        c = self.add_entity('Tag', name=u'yo') # take care: Tag's name normalized to lower case
+        req = self.request()
+        b = req.create_entity('BlogEntry', title=u'hell\'o', content=u'blabla')
+        c = req.create_entity('Tag', name=u'yo') # take care: Tag's name normalized to lower case
         self.execute('SET C tags B WHERE C eid %(c)s, B eid %(b)s', {'c':c.eid, 'b':b.eid}, 'b')
 
     def process(self, url):
--- a/web/test/unittest_views_basecontrollers.py	Mon Dec 21 20:28:01 2009 +0100
+++ b/web/test/unittest_views_basecontrollers.py	Mon Dec 21 20:28:08 2009 +0100
@@ -16,6 +16,8 @@
 from cubicweb.web import INTERNAL_FIELD_VALUE, Redirect, RequestError
 from cubicweb.entities.authobjs import CWUser
 
+u = unicode
+
 
 class EditControllerTC(CubicWebTC):
     def setUp(self):
@@ -29,7 +31,8 @@
     def test_noparam_edit(self):
         """check behaviour of this controller without any form parameter
         """
-        self.assertRaises(ValidationError, self.publish, self.request())
+        ex = self.assertRaises(ValidationError, self.ctrl_publish, self.request())
+        self.assertEquals(ex.errors, {None: u'no selected entities'})
 
     def test_validation_unique(self):
         """test creation of two linked entities
@@ -37,106 +40,98 @@
         user = self.user()
         req = self.request()
         req.form = {'eid': 'X', '__type:X': 'CWUser',
-                    'login:X': u'admin', 'edits-login:X': u'',
-                    'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
+                    '_cw_edited_fields:X': 'login-subject,upassword-subject',
+                    'login-subject:X': u'admin',
+                    'upassword-subject:X': u'toto',
+                    'upassword-subject-confirm:X': u'toto',
                     }
-        self.assertRaises(ValidationError, self.publish, req)
-
+        ex = self.assertRaises(ValidationError, self.ctrl_publish, req)
+        self.assertEquals(ex.errors, {'login': 'the value "admin" is already used, use another one'})
 
     def test_user_editing_itself(self):
         """checking that a manager user can edit itself
         """
         user = self.user()
-        basegroups = [str(eid) for eid, in self.execute('CWGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
+        basegroups = [u(eid) for eid, in self.execute('CWGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
         groupeids = [eid for eid, in self.execute('CWGroup G WHERE G name in ("managers", "users")')]
-        groups = [str(eid) for eid in groupeids]
-        stateeid = [eid for eid, in self.execute('State S WHERE S name "activated"')][0]
+        groups = [u(eid) for eid in groupeids]
         req = self.request()
+        eid = u(user.eid)
         req.form = {
-            'eid':       `user.eid`,
-            '__type:'+`user.eid`:    'CWUser',
-            'login:'+`user.eid`:     unicode(user.login),
-            'firstname:'+`user.eid`: u'Th\xe9nault',
-            'surname:'+`user.eid`:   u'Sylvain',
-            'in_group:'+`user.eid`:  groups,
-            'in_state:'+`user.eid`:  `stateeid`,
-            #
-            'edits-login:'+`user.eid`:     unicode(user.login),
-            'edits-firstname:'+`user.eid`: u'',
-            'edits-surname:'+`user.eid`:   u'',
-            'edits-in_group:'+`user.eid`:  basegroups,
-            'edits-in_state:'+`user.eid`:  `stateeid`,
+            'eid': eid, '__type:'+eid: 'CWUser',
+            '_cw_edited_fields:'+eid: 'login-subject,firstname-subject,surname-subject,in_group-subject',
+            'login-subject:'+eid:     u(user.login),
+            'surname-subject:'+eid: u'Th\xe9nault',
+            'firstname-subject:'+eid:   u'Sylvain',
+            'in_group-subject:'+eid:  groups,
             }
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         e = self.execute('Any X WHERE X eid %(x)s', {'x': user.eid}, 'x').get_entity(0, 0)
-        self.assertEquals(e.firstname, u'Th\xe9nault')
-        self.assertEquals(e.surname, u'Sylvain')
+        self.assertEquals(e.firstname, u'Sylvain')
+        self.assertEquals(e.surname, u'Th\xe9nault')
         self.assertEquals(e.login, user.login)
         self.assertEquals([g.eid for g in e.in_group], groupeids)
-        self.assertEquals(e.in_state[0].eid, stateeid)
 
     def test_user_can_change_its_password(self):
         user = self.create_user('user')
         cnx = self.login('user')
         req = self.request()
+        eid = u(user.eid)
         req.form = {
-            'eid': `user.eid`, '__type:'+`user.eid`: 'CWUser',
-            '__maineid' : str(user.eid),
-            'upassword:'+`user.eid`: 'tournicoton',
-            'upassword-confirm:'+`user.eid`: 'tournicoton',
-            'edits-upassword:'+`user.eid`:  '',
+            'eid': eid, '__maineid' : eid,
+            '__type:'+eid: 'CWUser',
+            '_cw_edited_fields:'+eid: 'upassword-subject',
+            'upassword-subject:'+eid: 'tournicoton',
+            'upassword-subject-confirm:'+eid: 'tournicoton',
             }
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         cnx.commit() # commit to check we don't get late validation error for instance
         self.assertEquals(path, 'cwuser/user')
         self.failIf('vid' in params)
 
-    def testr_user_editing_itself_no_relation(self):
+    def test_user_editing_itself_no_relation(self):
         """checking we can edit an entity without specifying some required
         relations (meaning no changes)
         """
         user = self.user()
-        groupeids = [eid for eid, in self.execute('CWGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
+        groupeids = [g.eid for g in user.in_group]
         req = self.request()
+        eid = u(user.eid)
         req.form = {
-            'eid':       `user.eid`,
-            '__type:'+`user.eid`:    'CWUser',
-            'login:'+`user.eid`:     unicode(user.login),
-            'firstname:'+`user.eid`: u'Th\xe9nault',
-            'surname:'+`user.eid`:   u'Sylvain',
-            #
-            'edits-login:'+`user.eid`:     unicode(user.login),
-            'edits-firstname:'+`user.eid`: u'',
-            'edits-surname:'+`user.eid`:   u'',
+            'eid':       eid,
+            '__type:'+eid:    'CWUser',
+            '_cw_edited_fields:'+eid: 'login-subject,firstname-subject,surname-subject',
+            'login-subject:'+eid:     u(user.login),
+            'firstname-subject:'+eid: u'Th\xe9nault',
+            'surname-subject:'+eid:   u'Sylvain',
             }
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         e = self.execute('Any X WHERE X eid %(x)s', {'x': user.eid}, 'x').get_entity(0, 0)
         self.assertEquals(e.login, user.login)
         self.assertEquals(e.firstname, u'Th\xe9nault')
         self.assertEquals(e.surname, u'Sylvain')
         self.assertEquals([g.eid for g in e.in_group], groupeids)
-        stateeids = [eid for eid, in self.execute('State S WHERE S name "activated"')]
-        self.assertEquals([s.eid for s in e.in_state], stateeids)
+        self.assertEquals(e.state, 'activated')
 
 
     def test_create_multiple_linked(self):
         gueid = self.execute('CWGroup G WHERE G name "users"')[0][0]
         req = self.request()
-        req.form = {'eid': ['X', 'Y'],
+        req.form = {'eid': ['X', 'Y'], '__maineid' : 'X',
 
                     '__type:X': 'CWUser',
-                    '__maineid' : 'X',
-                    'login:X': u'adim', 'edits-login:X': u'',
-                    'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
-                    'surname:X': u'Di Mascio', 'edits-surname:X': '',
-
-                    'in_group:X': `gueid`, 'edits-in_group:X': INTERNAL_FIELD_VALUE,
+                    '_cw_edited_fields:X': 'login-subject,upassword-subject,surname-subject,in_group-subject',
+                    'login-subject:X': u'adim',
+                    'upassword-subject:X': u'toto', 'upassword-subject-confirm:X': u'toto',
+                    'surname-subject:X': u'Di Mascio',
+                    'in_group-subject:X': u(gueid),
 
                     '__type:Y': 'EmailAddress',
-                    'address:Y': u'dima@logilab.fr', 'edits-address:Y': '',
-                    'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE,
+                    '_cw_edited_fields:Y': 'address-subject,use_email-object',
+                    'address-subject:Y': u'dima@logilab.fr',
+                    'use_email-object:Y': 'X',
                     }
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         # should be redirected on the created person
         self.assertEquals(path, 'cwuser/adim')
         e = self.execute('Any P WHERE P surname "Di Mascio"').get_entity(0, 0)
@@ -145,42 +140,41 @@
         self.assertEquals(email.address, 'dima@logilab.fr')
 
     def test_edit_multiple_linked(self):
-        peid = self.create_user('adim').eid
+        peid = u(self.create_user('adim').eid)
         req = self.request()
-        req.form = {'eid': [`peid`, 'Y'],
-                    '__type:%s'%peid: 'CWUser',
-                    'surname:%s'%peid: u'Di Masci', 'edits-surname:%s'%peid: '',
+        req.form = {'eid': [peid, 'Y'], '__maineid': peid,
+
+                    '__type:'+peid: u'CWUser',
+                    '_cw_edited_fields:'+peid: u'surname-subject',
+                    'surname-subject:'+peid: u'Di Masci',
 
-                    '__type:Y': 'EmailAddress',
-                    'address:Y': u'dima@logilab.fr', 'edits-address:Y': '',
-                    'use_email:%s'%peid: 'Y', 'edits-use_email:%s'%peid: INTERNAL_FIELD_VALUE,
-
-                    '__redirectrql': 'Any X WHERE X eid %s'%peid,
+                    '__type:Y': u'EmailAddress',
+                    '_cw_edited_fields:Y': u'address-subject,use_email-object',
+                    'address-subject:Y': u'dima@logilab.fr',
+                    'use_email-object:Y': peid,
                     }
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         # should be redirected on the created person
-        eid = params['rql'].split()[-1]
-        e = self.execute('Any X WHERE X eid %(x)s', {'x': eid}, 'x').get_entity(0, 0)
-        self.assertEquals(e.surname, 'Di Masci')
+        self.assertEquals(path, 'cwuser/adim')
+        e = self.execute('Any P WHERE P surname "Di Masci"').get_entity(0, 0)
         email = e.use_email[0]
         self.assertEquals(email.address, 'dima@logilab.fr')
 
-        emaileid = email.eid
+        emaileid = u(email.eid)
         req = self.request()
-        req.form = {'eid': [`peid`, `emaileid`],
-                         '__type:%s'%peid: 'CWUser',
-                         'surname:%s'%peid: u'Di Masci', 'edits-surname:%s'%peid: 'Di Masci',
-                         '__type:%s'%emaileid: 'EmailAddress',
-                         'address:%s'%emaileid: u'adim@logilab.fr', 'edits-address:%s'%emaileid: 'dima@logilab.fr',
-                         'use_email:%s'%peid: `emaileid`, 'edits-use_email:%s'%peid: `emaileid`,
-                         '__redirectrql': 'Any X WHERE X eid %s'%peid,
-                         }
-        path, params = self.expect_redirect_publish(req)
-        # should be redirected on the created person
-        eid = params['rql'].split()[-1]
-        e = self.execute('Any X WHERE X eid %(x)s', {'x': eid}, 'x').get_entity(0, 0)
-        self.assertEquals(e.surname, 'Di Masci')
-        email = e.use_email[0]
+        req.form = {'eid': [peid, emaileid],
+
+                    '__type:'+peid: u'CWUser',
+                    '_cw_edited_fields:'+peid: u'surname-subject',
+                    'surname-subject:'+peid: u'Di Masci',
+
+                    '__type:'+emaileid: u'EmailAddress',
+                    '_cw_edited_fields:'+emaileid: u'address-subject,use_email-object',
+                    'address-subject:'+emaileid: u'adim@logilab.fr',
+                    'use_email-object:'+emaileid: peid,
+                    }
+        path, params = self.expect_redirect_publish(req, 'edit')
+        email.clear_all_caches()
         self.assertEquals(email.address, 'adim@logilab.fr')
 
 
@@ -189,20 +183,24 @@
         """
         user = self.user()
         req = self.request()
-        req.form = {'__cloned_eid:X': user.eid,
-                    'eid': 'X', '__type:X': 'CWUser',
-                    'login:X': u'toto', 'edits-login:X': u'',
-                    'upassword:X': u'toto', 'edits-upassword:X': u'',
+        req.form = {'eid': 'X',
+                    '__cloned_eid:X': u(user.eid), '__type:X': 'CWUser',
+                    '_cw_edited_fields:X': 'login-subject,upassword-subject',
+                    'login-subject:X': u'toto',
+                    'upassword-subject:X': u'toto',
                     }
-        self.assertRaises(ValidationError, self.publish, req)
+        ex = self.assertRaises(ValidationError, self.ctrl_publish, req)
+        self.assertEquals(ex.errors, {'upassword': u'password and confirmation don\'t match'})
         req = self.request()
-        req.form = {'__cloned_eid:X': user.eid,
+        req.form = {'__cloned_eid:X': u(user.eid),
                     'eid': 'X', '__type:X': 'CWUser',
-                    'login:X': u'toto', 'edits-login:X': u'',
-                    'upassword:X': u'toto',
-                    'upassword-confirm:X': u'tutu', 'edits-upassword:X': u'',
+                    '_cw_edited_fields:X': 'login-subject,upassword-subject',
+                    'login-subject:X': u'toto',
+                    'upassword-subject:X': u'toto',
+                    'upassword-subject-confirm:X': u'tutu',
                     }
-        self.assertRaises(ValidationError, self.publish, req)
+        ex = self.assertRaises(ValidationError, self.ctrl_publish, req)
+        self.assertEquals(ex.errors, {'upassword': u'password and confirmation don\'t match'})
 
 
     def test_interval_bound_constraint_success(self):
@@ -211,24 +209,29 @@
         req = self.request()
         req.form = {'eid': ['X'],
                     '__type:X': 'Salesterm',
-                    'amount:X': u'-10', 'edits-amount:X': '',
-                    'described_by_test:X': str(feid), 'edits-described_by_test:X': INTERNAL_FIELD_VALUE,
+                    '_cw_edited_fields:X': 'amount-subject,described_by_test-subject',
+                    'amount-subject:X': u'-10',
+                    'described_by_test-subject:X': u(feid),
                 }
-        self.assertRaises(ValidationError, self.publish, req)
+        ex = self.assertRaises(ValidationError, self.ctrl_publish, req)
+        self.assertEquals(ex.errors, {'amount': 'value [0;100] constraint failed for value -10'})
         req = self.request()
         req.form = {'eid': ['X'],
                     '__type:X': 'Salesterm',
-                    'amount:X': u'110', 'edits-amount:X': '',
-                    'described_by_test:X': str(feid), 'edits-described_by_test:X': INTERNAL_FIELD_VALUE,
+                    '_cw_edited_fields:X': 'amount-subject,described_by_test-subject',
+                    'amount-subject:X': u'110',
+                    'described_by_test-subject:X': u(feid),
                     }
-        self.assertRaises(ValidationError, self.publish, req)
+        ex = self.assertRaises(ValidationError, self.ctrl_publish, req)
+        self.assertEquals(ex.errors, {'amount': 'value [0;100] constraint failed for value 110'})
         req = self.request()
         req.form = {'eid': ['X'],
                     '__type:X': 'Salesterm',
-                    'amount:X': u'10', 'edits-amount:X': '',
-                    'described_by_test:X': str(feid), 'edits-described_by_test:X': INTERNAL_FIELD_VALUE,
+                    '_cw_edited_fields:X': 'amount-subject,described_by_test-subject',
+                    'amount-subject:X': u'10',
+                    'described_by_test-subject:X': u(feid),
                     }
-        self.expect_redirect_publish(req)
+        self.expect_redirect_publish(req, 'edit')
         # should be redirected on the created
         #eid = params['rql'].split()[-1]
         e = self.execute('Salesterm X').get_entity(0, 0)
@@ -240,7 +243,7 @@
         user = self.user()
         req = self.request()
         req.set_session_data('pending_insert', set([(user.eid, 'in_group', tmpgroup.eid)]))
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         usergroups = [gname for gname, in
                       self.execute('Any N WHERE G name N, U in_group G, U eid %(u)s', {'u': user.eid})]
         self.assertUnorderedIterableEquals(usergroups, ['managers', 'test'])
@@ -259,48 +262,47 @@
         # now try to delete the relation
         req = self.request()
         req.set_session_data('pending_delete', set([(user.eid, 'in_group', groupeid)]))
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         usergroups = [gname for gname, in
                       self.execute('Any N WHERE G name N, U in_group G, U eid %(u)s', {'u': user.eid})]
         self.assertUnorderedIterableEquals(usergroups, ['managers'])
         self.assertEquals(req.get_pending_deletes(), [])
 
-    def test_custom_attribute_handler(self):
-        def custom_login_edit(self, formparams, value, relations):
-            formparams['login'] = value.upper()
-            relations.append('X login %(login)s')
-        CWUser.custom_login_edit = custom_login_edit
-        try:
-            user = self.user()
-            eid = repr(user.eid)
-            req = self.request()
-            req.form = {
-                'eid': eid,
-                '__type:'+eid:  'CWUser',
-                'login:'+eid: u'foo',
-                'edits-login:'+eid:  unicode(user.login),
-                }
-            path, params = self.expect_redirect_publish(req)
-            rset = self.execute('Any L WHERE X eid %(x)s, X login L', {'x': user.eid}, 'x')
-            self.assertEquals(rset[0][0], 'FOO')
-        finally:
-            del CWUser.custom_login_edit
+    # def test_custom_attribute_handler(self):
+    #     def custom_login_edit(self, formparams, value, relations):
+    #         formparams['login'] = value.upper()
+    #         relations.append('X login %(login)s')
+    #     CWUser.custom_login_edit = custom_login_edit
+    #     try:
+    #         user = self.user()
+    #         eid = repr(user.eid)
+    #         req = self.request()
+    #         req.form = {
+    #             'eid': eid,
+    #             '__type:'+eid:  'CWUser',
+    #             'login:'+eid: u'foo',
+    #             }
+    #         path, params = self.expect_redirect_publish(req, 'edit')
+    #         rset = self.execute('Any L WHERE X eid %(x)s, X login L', {'x': user.eid}, 'x')
+    #         self.assertEquals(rset[0][0], 'FOO')
+    #     finally:
+    #         del CWUser.custom_login_edit
 
     def test_redirect_apply_button(self):
         redirectrql = rql_for_eid(4012) # whatever
         req = self.request()
         req.form = {
-            'eid': 'A', '__type:A': 'BlogEntry',
-            '__maineid' : 'A',
-            'content:A': u'"13:03:43"', 'edits-content:A': '',
-            'title:A': u'huuu', 'edits-title:A': '',
+            'eid': 'A', '__maineid' : 'A',
+            '__type:A': 'BlogEntry', '_cw_edited_fields:A': 'content-subject,title-subject',
+            'content-subject:A': u'"13:03:43"',
+            'title-subject:A': u'huuu',
             '__redirectrql': redirectrql,
             '__redirectvid': 'primary',
             '__redirectparams': 'toto=tutu&tata=titi',
             '__form_id': 'edition',
             '__action_apply': '',
             }
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         self.failUnless(path.startswith('blogentry/'))
         eid = path.split('/')[1]
         self.assertEquals(params['vid'], 'edition')
@@ -313,16 +315,16 @@
         redirectrql = rql_for_eid(4012) # whatever
         req = self.request()
         req.form = {
-            'eid': 'A', '__type:A': 'BlogEntry',
-            '__maineid' : 'A',
-            'content:A': u'"13:03:43"', 'edits-content:A': '',
-            'title:A': u'huuu', 'edits-title:A': '',
+            'eid': 'A', '__maineid' : 'A',
+            '__type:A': 'BlogEntry', '_cw_edited_fields:A': 'content-subject,title-subject',
+            'content-subject:A': u'"13:03:43"',
+            'title-subject:A': u'huuu',
             '__redirectrql': redirectrql,
             '__redirectvid': 'primary',
             '__redirectparams': 'toto=tutu&tata=titi',
             '__form_id': 'edition',
             }
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         self.assertEquals(path, 'view')
         self.assertEquals(params['rql'], redirectrql)
         self.assertEquals(params['vid'], 'primary')
@@ -332,9 +334,9 @@
     def test_redirect_delete_button(self):
         req = self.request()
         eid = req.create_entity('BlogEntry', title=u'hop', content=u'hop').eid
-        req.form = {'eid': str(eid), '__type:%s'%eid: 'BlogEntry',
+        req.form = {'eid': u(eid), '__type:%s'%eid: 'BlogEntry',
                     '__action_delete': ''}
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         self.assertEquals(path, 'blogentry')
         self.assertEquals(params, {u'__message': u'entity deleted'})
         eid = req.create_entity('EmailAddress', address=u'hop@logilab.fr').eid
@@ -342,19 +344,19 @@
                      {'x': self.session.user.eid, 'e': eid}, 'x')
         self.commit()
         req = req
-        req.form = {'eid': str(eid), '__type:%s'%eid: 'EmailAddress',
+        req.form = {'eid': u(eid), '__type:%s'%eid: 'EmailAddress',
                     '__action_delete': ''}
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         self.assertEquals(path, 'cwuser/admin')
         self.assertEquals(params, {u'__message': u'entity deleted'})
         eid1 = req.create_entity('BlogEntry', title=u'hop', content=u'hop').eid
         eid2 = req.create_entity('EmailAddress', address=u'hop@logilab.fr').eid
         req = self.request()
-        req.form = {'eid': [str(eid1), str(eid2)],
+        req.form = {'eid': [u(eid1), u(eid2)],
                     '__type:%s'%eid1: 'BlogEntry',
                     '__type:%s'%eid2: 'EmailAddress',
                     '__action_delete': ''}
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         self.assertEquals(path, 'view')
         self.assertEquals(params, {u'__message': u'entities deleted'})
 
@@ -362,28 +364,22 @@
         """non-regression test checking that a manager user can edit a CWEType entity (CWGroup)
         """
         groupeids = [eid for eid, in self.execute('CWGroup G WHERE G name "managers"')]
-        groups = [str(eid) for eid in groupeids]
-        eeetypeeid = self.execute('CWEType X WHERE X name "CWGroup"')[0][0]
-        basegroups = [str(eid) for eid, in self.execute('CWGroup G WHERE X read_permission G, X eid %(x)s', {'x': eeetypeeid})]
+        groups = [u(eid) for eid in groupeids]
+        cwetypeeid = u(self.execute('CWEType X WHERE X name "CWGroup"')[0][0])
+        basegroups = [u(eid) for eid, in self.execute('CWGroup G WHERE X read_permission G, X eid %(x)s', {'x': cwetypeeid})]
         req = self.request()
         req.form = {
-            'eid':      `eeetypeeid`,
-            '__type:'+`eeetypeeid`:   'CWEType',
-            'name:'+`eeetypeeid`:     u'CWGroup',
-            'final:'+`eeetypeeid`:    False,
-            'meta:'+`eeetypeeid`:     True,
-            'description:'+`eeetypeeid`:     u'users group',
-            'read_permission:'+`eeetypeeid`:  groups,
-            #
-            'edits-name:'+`eeetypeeid`:     u'CWGroup',
-            'edits-final:'+`eeetypeeid`:    False,
-            'edits-meta:'+`eeetypeeid`:     True,
-            'edits-description:'+`eeetypeeid`:     u'users group',
-            'edits-read_permission:'+`eeetypeeid`:  basegroups,
+            'eid':      cwetypeeid,
+            '__type:'+cwetypeeid:   'CWEType',
+            '_cw_edited_fields:'+cwetypeeid: 'name-subject,final-subject,description-subject,read_permission-subject',
+            'name-subject:'+cwetypeeid:     u'CWGroup',
+            #'final-subject:'+cwetypeeid:    False,
+            'description-subject:'+cwetypeeid:     u'users group',
+            'read_permission-subject:'+cwetypeeid:  groups,
             }
         try:
-            path, params = self.expect_redirect_publish(req)
-            e = self.execute('Any X WHERE X eid %(x)s', {'x': eeetypeeid}, 'x').get_entity(0, 0)
+            path, params = self.expect_redirect_publish(req, 'edit')
+            e = self.execute('Any X WHERE X eid %(x)s', {'x': cwetypeeid}, 'x').get_entity(0, 0)
             self.assertEquals(e.name, 'CWGroup')
             self.assertEquals([g.eid for g in e.read_permission], groupeids)
         finally:
@@ -395,28 +391,23 @@
         """non-regression test checking that a manager user can edit a CWEType entity (CWEType)
         """
         groupeids = sorted(eid for eid, in self.execute('CWGroup G WHERE G name in ("managers", "users")'))
-        groups = [str(eid) for eid in groupeids]
-        eeetypeeid = self.execute('CWEType X WHERE X name "CWEType"')[0][0]
-        basegroups = [str(eid) for eid, in self.execute('CWGroup G WHERE X read_permission G, X eid %(x)s', {'x': eeetypeeid})]
+        groups = [u(eid) for eid in groupeids]
+        cwetypeeid = self.execute('CWEType X WHERE X name "CWEType"')[0][0]
+        basegroups = [u(eid) for eid, in self.execute('CWGroup G WHERE X read_permission G, X eid %(x)s', {'x': cwetypeeid})]
+        cwetypeeid = u(cwetypeeid)
         req = self.request()
         req.form = {
-            'eid':      `eeetypeeid`,
-            '__type:'+`eeetypeeid`:  'CWEType',
-            'name:'+`eeetypeeid`:     u'CWEType',
-            'final:'+`eeetypeeid`:    False,
-            'meta:'+`eeetypeeid`:     True,
-            'description:'+`eeetypeeid`:     u'users group',
-            'read_permission:'+`eeetypeeid`:  groups,
-
-            'edits-name:'+`eeetypeeid`:     u'CWEType',
-            'edits-final:'+`eeetypeeid`:    False,
-            'edits-meta:'+`eeetypeeid`:     True,
-            'edits-description:'+`eeetypeeid`:     u'users group',
-            'edits-read_permission:'+`eeetypeeid`:  basegroups,
+            'eid':      cwetypeeid,
+            '__type:'+cwetypeeid:  'CWEType',
+            '_cw_edited_fields:'+cwetypeeid: 'name-subject,final-subject,description-subject,read_permission-subject',
+            'name-subject:'+cwetypeeid:     u'CWEType',
+            #'final-subject:'+cwetypeeid:    False,
+            'description-subject:'+cwetypeeid:     u'users group',
+            'read_permission-subject:'+cwetypeeid:  groups,
             }
         try:
-            path, params = self.expect_redirect_publish(req)
-            e = self.execute('Any X WHERE X eid %(x)s', {'x': eeetypeeid}, 'x').get_entity(0, 0)
+            path, params = self.expect_redirect_publish(req, 'edit')
+            e = self.execute('Any X WHERE X eid %(x)s', {'x': cwetypeeid}, 'x').get_entity(0, 0)
             self.assertEquals(e.name, 'CWEType')
             self.assertEquals(sorted(g.eid for g in e.read_permission), groupeids)
         finally:
@@ -431,11 +422,11 @@
         """
         req = self.request()
         req.form = {
-            'eid': 'A', '__type:A': 'BlogEntry',
-            '__maineid' : 'A',
-            'title:A': u'"13:03:40"', 'edits-title:A': '',
-            'content:A': u'"13:03:43"', 'edits-content:A': ''}
-        path, params = self.expect_redirect_publish(req)
+            'eid': 'A', '__maineid' : 'A',
+            '__type:A': 'BlogEntry', '_cw_edited_fields:A': 'title-subject,content-subject',
+            'title-subject:A': u'"13:03:40"',
+            'content-subject:A': u'"13:03:43"',}
+        path, params = self.expect_redirect_publish(req, 'edit')
         self.failUnless(path.startswith('blogentry/'))
         eid = path.split('/')[1]
         e = self.execute('Any C, T WHERE C eid %(x)s, C content T', {'x': eid}, 'x').get_entity(0, 0)
@@ -449,27 +440,30 @@
         req.form = {'eid': ['X', 'Y'],
 
                     '__type:X': 'CWUser',
-                    'login:X': u'adim', 'edits-login:X': u'',
-                    'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
-                    'in_group:X': `gueid`, 'edits-in_group:X': INTERNAL_FIELD_VALUE,
+                    '_cw_edited_fields:X': 'login-subject,upassword-subject,in_group-subject',
+                    'login-subject:X': u'adim',
+                    'upassword-subject:X': u'toto', 'upassword-subject-confirm:X': u'toto',
+                    'in_group-subject:X': `gueid`,
 
                     '__type:Y': 'EmailAddress',
-                    'address:Y': u'', 'edits-address:Y': '',
-                    'alias:Y': u'', 'edits-alias:Y': '',
-                    'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE,
+                    '_cw_edited_fields:Y': 'address-subject,alias-subject,use_email-object',
+                    'address-subject:Y': u'',
+                    'alias-subject:Y': u'',
+                    'use_email-object:Y': 'X',
                     }
-        self.assertRaises(ValidationError, self.publish, req)
+        ex = self.assertRaises(ValidationError, self.ctrl_publish, req)
+        self.assertEquals(ex.errors, {'address': u'required attribute'})
 
     def test_nonregr_copy(self):
         user = self.user()
         req = self.request()
-        req.form = {'__cloned_eid:X': user.eid,
-                    'eid': 'X', '__type:X': 'CWUser',
-                    '__maineid' : 'X',
-                    'login:X': u'toto', 'edits-login:X': u'',
-                    'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'',
+        req.form = {'__maineid' : 'X', 'eid': 'X',
+                    '__cloned_eid:X': user.eid, '__type:X': 'CWUser',
+                    '_cw_edited_fields:X': 'login-subject,upassword-subject',
+                    'login-subject:X': u'toto',
+                    'upassword-subject:X': u'toto', 'upassword-subject-confirm:X': u'toto',
                     }
-        path, params = self.expect_redirect_publish(req)
+        path, params = self.expect_redirect_publish(req, 'edit')
         self.assertEquals(path, 'cwuser/toto')
         e = self.execute('Any X WHERE X is CWUser, X login "toto"').get_entity(0, 0)
         self.assertEquals(e.login, 'toto')
@@ -486,22 +480,24 @@
             self.execute('SET P use_email E, P primary_email E WHERE P eid %(p)s, E eid %(e)s',
                          {'p' : p.eid, 'e' : e.eid})
             req = self.request()
-            req.form = {'__cloned_eid:X': p.eid,
-                        'eid': 'X', '__type:X': 'CWUser',
-                        'login': u'dodo', 'edits-login': u'dodo',
-                        'surname:X': u'Boom', 'edits-surname:X': u'',
+            req.form = {'eid': 'X',
+                        '__cloned_eid:X': p.eid, '__type:X': 'CWUser',
+                        '_cw_edited_fields:X': 'login-subject,surname-subject',
+                        'login-subject': u'dodo',
+                        'surname-subject:X': u'Boom',
                         '__errorurl' : "whatever but required",
-                             }
+                        }
             # try to emulate what really happens in the web application
             # 1/ validate form => EditController.publish raises a ValidationError
             #    which fires a Redirect
             # 2/ When re-publishing the copy form, the publisher implicitly commits
             try:
-                self.app.publish('edit', req)
+                self.app_publish(req, 'edit')
             except Redirect:
+                req = self.request()
                 req.form['rql'] = 'Any X WHERE X eid %s' % p.eid
                 req.form['vid'] = 'copy'
-                self.app.publish('view', req)
+                self.app_publish(req, 'view')
             rset = self.execute('CWUser P WHERE P surname "Boom"')
             self.assertEquals(len(rset), 0)
         finally:
--- a/web/test/unittest_views_editforms.py	Mon Dec 21 20:28:01 2009 +0100
+++ b/web/test/unittest_views_editforms.py	Mon Dec 21 20:28:08 2009 +0100
@@ -25,7 +25,7 @@
                             {'widget': AutoCompletionWidget(autocomplete_initfunc='get_logins')})
         form = self.vreg['forms'].select('edition', self.request(),
                                          entity=self.user())
-        field = form.field_by_name('login')
+        field = form.field_by_name('login', 'subject')
         self.assertIsInstance(field.widget, AutoCompletionWidget)
         AFFK.del_rtag('CWUser', 'login', '*', 'subject')
 
--- a/web/test/unittest_viewselector.py	Mon Dec 21 20:28:01 2009 +0100
+++ b/web/test/unittest_viewselector.py	Mon Dec 21 20:28:08 2009 +0100
@@ -422,7 +422,8 @@
             req = self.request()
             rset = req.execute(rql)
         try:
-            self.vreg['views'].render(vid, req, rset=rset, **args)
+            obj = self['views'].select(vid, req, rset=rset, **args)
+            return obj.render(**args)
         except:
             print vid, rset, args
             raise