prefixed form methods to avoid potential conflicts with field names, button related method, a bit more serious renderer tls-sprint
authorsylvain.thenault@logilab.fr
Thu, 19 Feb 2009 18:55:13 +0100
branchtls-sprint
changeset 847 27c4ebe90d03
parent 846 e449f1da7d09
child 848 f5aca5c5f6ca
prefixed form methods to avoid potential conflicts with field names, button related method, a bit more serious renderer
web/form.py
web/test/unittest_form.py
--- a/web/form.py	Thu Feb 19 18:54:26 2009 +0100
+++ b/web/form.py	Thu Feb 19 18:55:13 2009 +0100
@@ -422,22 +422,58 @@
         self.context = {}
         
     @property
-    def needs_multipart(self):
+    def form_needs_multipart(self):
         return any(field.needs_multipart for field in self.fields) 
 
-    def render(self, **values):
+    def form_render(self, **values):
         renderer = values.pop('renderer', FormRenderer())
-        self.build_context(values)
+        self.form_build_context(values)
         return renderer.render(self)
 
-    def build_context(self, values):
+    def form_build_context(self, values):
         self.context = context = {}
-        for name, field in self.fields:
-            value = values.get(field.name, field.initial)
-            context[field] = {'value': field.format_value(self.req, value)}
+        for field in self.fields:
+            context[field] = {'value': self.form_field_value(field, values),
+                              'name': self.form_field_name(field, values),
+                              'id': self.form_field_id(field, values),
+                              }
+
+    def form_field_value(self, field, values):
+        value = values.get(field.name, field.initial)
+        return field.format_value(self.req, value) 
+
+    def form_field_name(self, field, values):
+        return field.name
 
-    def get_vocabulary(self, field):
+    def form_field_id(self, field, values):
+        return field.id
+   
+    def form_field_vocabulary(self, field):
         raise NotImplementedError
+    
+    BUTTON_STR = u'<input class="validateButton" type="submit" name="%s" value="%s" tabindex="%s"/>'
+    ACTION_SUBMIT_STR = u'<input class="validateButton" type="button" onclick="postForm(\'%s\', \'%s\', \'%s\')" value="%s" tabindex="%s"/>'
+
+    def button_ok(self, label=None, tabindex=None):
+        label = self.req._(label or stdmsgs.BUTTON_OK).capitalize()
+        return self.BUTTON_STR % ('defaultsubmit', label, tabindex or 2)
+    
+    def button_apply(self, label=None, tabindex=None):
+        label = self.req._(label or stdmsgs.BUTTON_APPLY).capitalize()
+        return self.ACTION_SUBMIT_STR % ('__action_apply', label, self.id, label, tabindex or 3)
+
+    def button_delete(self, label=None, tabindex=None):
+        label = self.req._(label or stdmsgs.BUTTON_DELETE).capitalize()
+        return self.ACTION_SUBMIT_STR % ('__action_delete', label, self.id, label, tabindex or 3)
+    
+    def button_cancel(self, label=None, tabindex=None):
+        label = self.req._(label or stdmsgs.BUTTON_CANCEL).capitalize()
+        return self.ACTION_SUBMIT_STR % ('__action_cancel', label, self.id, label, tabindex or 4)
+    
+    def button_reset(self, label=None, tabindex=None):
+        label = self.req._(label or stdmsgs.BUTTON_CANCEL).capitalize()
+        return u'<input class="validateButton" type="reset" value="%s" tabindex="%s"/>' % (
+            label, tabindex or 4)
 
     
 class EntityFieldsForm(FieldsForm):
@@ -447,25 +483,28 @@
         self.fields.append(TextField(name='__type', widget=HiddenInput))
         self.fields.append(TextField(name='eid', widget=HiddenInput))
         
-    def render(self, entity, **values):
+    def form_render(self, entity, **values):
         self.entity = entity
-        return super(EntityFieldsForm, self).render(**values)
+        return super(EntityFieldsForm, self).form_render(**values)
+
+    def form_field_value(self, field, values):
+        try:
+            value = values[field.name]
+        except KeyError:
+            value = getattr(self.entity, field.name, field.initial)
+        return field.format_value(self.req, value) 
 
-    def build_context(self, values):
-        self.context = context = {}
-        for field in self.fields:
-            try:
-                value = values[field.name]
-            except KeyError:
-                value = getattr(self.entity, field.name, field.initial)
-            if field.eidparam:
-                name = eid_param(field.name, self.entity.eid)
-            else:
-                name = field.name
-            context[field] = {'value': field.format_value(self.req, value),
-                              'name': name}
+    def form_field_name(self, field, values):
+        if field.eidparam:
+            return eid_param(field.name, self.entity.eid)
+        return field.name
+
+    def form_field_id(self, field, values):
+        if field.eidparam:
+            return eid_param(field.id, self.entity.eid)
+        return field.id
         
-    def get_vocabulary(self, field):
+    def form_field_vocabulary(self, field):
         choices = self.vocabfunc(entity)
         if self.sort:
             choices = sorted(choices)
@@ -477,9 +516,11 @@
 # form renderers ############
 
 class FormRenderer(object):
+    
     def render(self, form):
         data = []
         w = data.append
+        # XXX form_needs_multipart
         w(u'<form action="%s" onsubmit="return freezeFormButtons(\'%s\');" method="post" id="%s">'
           % (form.req.build_url(form.action), form.id, form.id))
         w(u'<div id="progress">%s</div>' % _('validating...'))
@@ -487,10 +528,30 @@
         w(tags.input(type='hidden', name='__form_id', value=form.id))
         if form.redirect_path:
             w(tags.input(type='hidden', name='__redirect_path', value=form.redirect_path))
-        for field in form.fields:
-            w(field.render(form))
-        for button in form.buttons():
-            w(button.render())
+        self.render_fields(w, form)
+        self.render_buttons(w, form)
         w(u'</fieldset>')
         w(u'</form>')
         return '\n'.join(data)
+
+    def render_fields(self, w, form):
+        w(u'<table>')
+        for field in form.fields:
+            w(u'<tr>')
+            w('<th>%s</th>' % self.render_label(form, field))
+            w(u'<td style="width:100%;">')
+            w(field.render(form))
+            w(u'</td></tr>')
+        w(u'</table>')
+
+    def render_buttons(self, w, form):
+        for button in form.buttons():
+            w(button)
+        
+    def render_label(self, form, field):
+        label = form.req._(field.label)
+        attrs = {'for': form.context[field]['id']}
+        if field.required:
+            attrs['class'] = 'required'
+        return tags.label(label, **attrs)
+        
--- a/web/test/unittest_form.py	Thu Feb 19 18:54:26 2009 +0100
+++ b/web/test/unittest_form.py	Thu Feb 19 18:55:13 2009 +0100
@@ -1,31 +1,30 @@
 from logilab.common.testlib import TestCase, unittest_main, mock_object
-from cubicweb.web.form import *
 
-class ChangeStateForm(EntityFieldsForm):
-    state = TextField(widget=HiddenInput, eidparam=False)
-    __method = TextField(widget=HiddenInput, initial='set_state', eidparam=False)
-    trcomment = RichTextField()
-
-    def buttons(self):
-        return []
+from cubicweb.web.form import *
+from cubicweb.web.views.baseforms import ChangeStateForm
 
 class CustomChangeStateForm(ChangeStateForm):
     hello = IntField(name='youlou')
 
 class EntityFieldsFormTC(TestCase):
 
+    def setUp(self):
+        def next_tabindex(self):
+            self.count += 1
+            return self.count
+        self.req = mock_object(build_url=lambda *args,**kwargs: 'myurl.com',
+                               _=lambda s,x: x,
+                               next_tabindex=next_tabindex, count=0)
+        self.entity = mock_object(eid=1)
+        
     def test(self):
-        req = mock_object(build_url=lambda *args,**kwargs: 'myurl.com')
-        form = ChangeStateForm(req, redirect_path='perdu.com')
-        entity = mock_object(eid=1)
-        self.assertEquals(form.render(entity, state=123),
+        form = ChangeStateForm(self.req, redirect_path='perdu.com')
+        self.assertEquals(form.form_render(self.entity, state=123),
                           '''''')
 
     def test_form_inheritance(self):
-        req = mock_object(build_url=lambda *args,**kwargs: 'myurl.com')
-        form = CustomChangeStateForm(req, redirect_path='perdu.com')
-        entity = mock_object(eid=1)
-        self.assertEquals(form.render(entity, state=123),
+        form = CustomChangeStateForm(self.req, redirect_path='perdu.com')
+        self.assertEquals(form.form_render(self.entity, state=123),
                           '''''')