web/form.py
branchtls-sprint
changeset 903 63a8ab7eeb9c
parent 902 e4de959c76af
child 904 4f1ce95aa686
--- a/web/form.py	Fri Feb 20 16:03:01 2009 +0100
+++ b/web/form.py	Fri Feb 20 16:04:04 2009 +0100
@@ -255,26 +255,25 @@
 
     def render(self, form, field):
         raise NotImplementedError
-    
+
+    def _render_attrs(self, form, field):
+        # name = form.form_field_name(field)
+        # values = form.form_field_value(field)
+        name = form.context[field]['name']
+        values = form.context[field]['value']
+        if not isinstance(values, (tuple, list)):
+            values = (values,)
+        return name, values, dict(self.attrs)
+
 class Input(FieldWidget):
     type = None
     
     def render(self, form, field):
-        name, value, attrs = self._render_attrs(form, field)
-        if attrs is None:
-            return tags.input(name=name, value=value)
-        return tags.input(name=name, value=value, type=self.type, **attrs)
+        name, values, attrs = self._render_attrs(form, field)
+        inputs = [tags.input(name=name, value=value, type=self.type, **attrs)
+                  for value in values]
+        return u'\n'.join(inputs)
 
-    def _render_attrs(self, form, field):
-        name = form.context[field]['name'] # qualified name
-        value = form.context[field]['value']
-        #fattrs = field.widget_attributes(self)
-        attrs = self.attrs.copy()
-        attrs['id'] = form.context[field]['id']
-        #attrs.update(fattrs)
-        # XXX id
-        return name, value, attrs
-    
 class TextInput(Input):
     type = 'text'
 
@@ -283,10 +282,6 @@
 
 class FileInput(Input):
     type = 'file'
-    
-    def _render_attrs(self, form, field):
-        name = form.context[field]['name'] # qualified name
-        return name, None, {}
 
 class HiddenInput(Input):
     type = 'hidden'
@@ -296,8 +291,14 @@
 
 class TextArea(FieldWidget):
     def render(self, form, field):
-        name, value, attrs = self._render_attrs(form, field)
+        name, values, attrs = self._render_attrs(form, field)
         attrs.setdefault('onkeypress', 'autogrow(this)')
+        if not values:
+            value = u''
+        elif len(values) == 1:
+            value = values[0]
+        else:
+            raise ValueError('a textarea is not supposed to be multivalued')
         return tags.textarea(value, name=name, **attrs)
 
 class FCKEditor(TextArea):
@@ -315,20 +316,19 @@
 #    pass
 
 class Select(FieldWidget):
-    def __init__(self, attrs=None, vocabulary=()):
+    def __init__(self, attrs=None, multiple=False):
         super(Select, self).__init__(attrs)
         self.multiple = multiple
         
     def render(self, form, field):
-        name, value, attrs = self._render_attrs(form, field)
-        if self.vocabulary:
-            # static vocabulary defined in form definition
-            vocab = self.vocabulary
-        else:
-            vocab = form.get_vocabulary(field)
+        name, curvalues, attrs = self._render_attrs(form, field)
+        vocab = field.vocabulary(form)
         options = []
         for label, value in vocab:
-            options.append(tags.option(label, value=value))
+            if value in curvalues:
+                options.append(tags.option(label, value=value, selected='selected'))
+            else:
+                options.append(tags.option(label, value=value))
         if attrs is None:
             return tags.select(name=name, options=options)
         return tags.select(name=name, multiple=self.multiple,
@@ -439,6 +439,13 @@
         return self.__unicode__().encode('utf-8')
     
     def format_value(self, req, value):
+        if isinstance(value, (list, tuple)):
+            return [self.format_single_value(req, val) for val in value]
+        return self.format_single_value(req, value)
+    
+    def format_single_value(self, req, value):
+        if value is None:
+            return u''
         return unicode(value)
 
     def get_widget(self, req):
@@ -457,12 +464,11 @@
         self.max_length = max_length
 
 class TextField(Field):
-    widget = TextArea
-    def __init__(self, row=None, col=None, **kwargs):
-        super(TextField, self).__init__(**kwargs)
-        self.row = row
-        self.col = col
-
+    def __init__(self, rows=10, cols=80, **kwargs):
+        widget = TextArea(dict(rows=rows, cols=cols))
+        super(TextField, self).__init__(widget=widget, **kwargs)
+        self.rows = rows
+        self.cols = cols
 
 class RichTextField(TextField):
     widget = None
@@ -519,8 +525,11 @@
         self.min = min
         self.max = max
 
+class BooleanField(Field):
+    widget = Radio
+
 class FloatField(IntField):    
-    def format_value(self, req, value):
+    def format_single_value(self, req, value):
         formatstr = entity.req.property_value('ui.float-format')
         if value is None:
             return u''
@@ -550,7 +559,7 @@
     
                  
 class RelationField(Field):
-    def __init__(self, role='subject', **kwargs):
+    def __init__(self, **kwargs):
         super(RelationField, self).__init__(**kwargs)
 
     @staticmethod
@@ -631,8 +640,8 @@
         return any(field.needs_multipart for field in self.fields) 
 
     def form_add_hidden(self, name, value=None, **kwargs):
-        self.fields.append(TextField(name=name, widget=HiddenInput,
-                                     initial=value, **kwargs))
+        self.fields.append(StringField(name=name, widget=HiddenInput,
+                                       initial=value, **kwargs))
 
     def form_render(self, **values):
         renderer = values.pop('renderer', FormRenderer())
@@ -738,7 +747,7 @@
         if fieldname.startswith('edits-') or fieldname.startswith('edito-'):
             # edit[s|o]- fieds must have the actual value stored on the entity
             if self.entity.has_eid():
-                value = self.form_field_entity_value(field, default_initial=False)
+                value = self.form_field_entity_value(field.visible_field, default_initial=False)
             else:
                 value = INTERNAL_FIELD_VALUE
         elif fieldname == '__type':
@@ -749,6 +758,8 @@
             value = values[fieldname]
         elif fieldname in self.req.form:
             value = self.req.form[fieldname]
+        elif isinstance(field, FileField):
+            return None # XXX manage FileField
         else:
             if self.entity.has_eid() and field.eidparam:
                 # use value found on the entity or field's initial value if it's
@@ -784,10 +795,14 @@
         if field.role == 'object':
             attr += '_object'
         if default_initial:
-            return getattr(self.entity, attr, field.initial)
+            value = getattr(self.entity, attr, field.initial)
         else:
-            return getattr(self.entity, attr)
-        
+            value = getattr(self.entity, attr)
+        if isinstance(field, RelationField):
+            # in this case, value is the list of related entities
+            value = [ent.eid for ent in value]
+        return value
+    
     def form_field_name(self, field):
         if field.eidparam:
             return eid_param(field.name, self.entity.eid)
@@ -799,12 +814,21 @@
         return field.id
         
     def form_field_vocabulary(self, field):
-        choices = self.vocabfunc(entity)
-        if self.sort:
-            choices = sorted(choices)
-        if self.rschema.rproperty(self.subjtype, self.objtype, 'internationalizable'):
-            return zip((entity.req._(v) for v in choices), choices)
-        return zip(choices, choices)
+        role, rtype = field.role, field.name
+        try:
+            vocabfunc = getattr(self.entity, '%s_%s_vocabulary' % (role, rtype))
+        except AttributeError:
+            vocabfunc = getattr(self, '%s_relation_vocabulary' % role)
+        else:
+            # XXX bw compat, default_<field name> on the entity
+            warn('found %s_%s_vocabulary on %s, should be set on a specific form'
+                 % (role, rtype, self.entity.id), DeprecationWarning)
+        return vocabfunc(rtype)
+## XXX BACKPORT ME
+##         if self.sort:
+##             choices = sorted(choices)
+##         if self.rschema.rproperty(self.subjtype, self.objtype, 'internationalizable'):
+##             return zip((entity.req._(v) for v in choices), choices)
 
     def subject_relation_vocabulary(self, rtype, limit=None):
         """defaut vocabulary method for the given relation, looking for