13 |
13 |
14 from cubicweb.schema import FormatConstraint |
14 from cubicweb.schema import FormatConstraint |
15 from cubicweb.utils import ustrftime |
15 from cubicweb.utils import ustrftime |
16 from cubicweb.common import tags, uilib |
16 from cubicweb.common import tags, uilib |
17 from cubicweb.web import INTERNAL_FIELD_VALUE |
17 from cubicweb.web import INTERNAL_FIELD_VALUE |
18 from cubicweb.web.formwidgets import (HiddenInput, TextInput, FileInput, PasswordInput, |
18 from cubicweb.web.formwidgets import ( |
19 TextArea, FCKEditor, Radio, Select, |
19 HiddenInput, TextInput, FileInput, PasswordInput, TextArea, FCKEditor, |
20 DateTimePicker) |
20 Radio, Select, DateTimePicker) |
21 |
21 |
22 class Field(object): |
22 class Field(object): |
23 """field class is introduced to control what's displayed in edition form |
23 """field class is introduced to control what's displayed in forms. It makes |
|
24 the link between something to edit and its display in the form. Actual |
|
25 display is handled by a widget associated to the field. |
|
26 |
|
27 Attributes |
|
28 ---------- |
|
29 all the attributes described below have sensible default value which may be |
|
30 overriden by value given to field's constructor. |
|
31 |
|
32 :name: |
|
33 name of the field (basestring), should be unique in a form. |
|
34 :id: |
|
35 dom identifier (default to the same value as `name`), should be unique in |
|
36 a form. |
|
37 :label: |
|
38 label of the field (default to the same value as `name`). |
|
39 :help: |
|
40 help message about this field. |
|
41 :widget: |
|
42 widget associated to the field. Each field class has a default widget |
|
43 class which may be overriden per instance. |
|
44 :required: |
|
45 bool flag telling if the field is required or not. |
|
46 :initial: |
|
47 initial value, used when no value specified by other means. |
|
48 :choices: |
|
49 static vocabulary for this field. May be a list of values or a list of |
|
50 (label, value) tuples if specified. |
|
51 :sort: |
|
52 bool flag telling if the vocabulary (either static vocabulary specified |
|
53 in `choices` or dynamic vocabulary fetched from the form) should be |
|
54 sorted on label. |
|
55 :internationalizable: |
|
56 bool flag telling if the vocabulary labels should be translated using the |
|
57 current request language. |
|
58 :eidparam: |
|
59 bool flag telling if this field is linked to a specific entity |
|
60 :role: |
|
61 when the field is linked to an entity attribute or relation, tells the |
|
62 role of the entity in the relation (eg 'subject' or 'object') |
|
63 |
24 """ |
64 """ |
|
65 # default widget associated to this class of fields. May be overriden per |
|
66 # instance |
25 widget = TextInput |
67 widget = TextInput |
|
68 # does this field requires a multipart form |
26 needs_multipart = False |
69 needs_multipart = False |
27 creation_rank = 0 |
70 # class attribute used for ordering of fields in a form |
28 |
71 __creation_rank = 0 |
29 def __init__(self, name=None, id=None, label=None, |
72 |
|
73 def __init__(self, name=None, id=None, label=None, help=None, |
30 widget=None, required=False, initial=None, |
74 widget=None, required=False, initial=None, |
31 choices=None, help=None, eidparam=False, role='subject'): |
75 choices=None, sort=True, internationalizable=False, |
|
76 eidparam=False, role='subject'): |
|
77 self.name = name |
|
78 self.id = id or name |
|
79 self.label = label or name |
|
80 self.help = help |
32 self.required = required |
81 self.required = required |
33 if widget is not None: |
82 if widget is not None: |
34 self.widget = widget |
83 self.widget = widget |
35 if isinstance(self.widget, type): |
84 if isinstance(self.widget, type): |
36 self.widget = self.widget() |
85 self.widget = self.widget() |
37 self.name = name |
|
38 self.label = label or name |
|
39 self.id = id or name |
|
40 self.initial = initial |
86 self.initial = initial |
41 self.choices = choices |
87 self.choices = choices |
42 self.help = help |
88 self.sort = sort |
|
89 self.internationalizable = internationalizable |
43 self.eidparam = eidparam |
90 self.eidparam = eidparam |
44 self.role = role |
91 self.role = role |
45 # global fields ordering in forms |
92 # ordering number for this field instance |
46 self.creation_rank = Field.creation_rank |
93 self.creation_rank = Field.__creation_rank |
47 Field.creation_rank += 1 |
94 Field.__creation_rank += 1 |
48 |
95 |
49 def __unicode__(self): |
96 def __unicode__(self): |
50 return u'<%s name=%r label=%r id=%r initial=%r @%x>' % ( |
97 return u'<%s name=%r label=%r id=%r initial=%r @%x>' % ( |
51 self.__class__.__name__, self.name, self.label, |
98 self.__class__.__name__, self.name, self.label, |
52 self.id, self.initial, id(self)) |
99 self.id, self.initial, id(self)) |
53 |
100 |
54 def __repr__(self): |
101 def __repr__(self): |
55 return self.__unicode__().encode('utf-8') |
102 return self.__unicode__().encode('utf-8') |
56 |
103 |
57 def set_name(self, name): |
104 def set_name(self, name): |
|
105 """automatically set .id and .label when name is set""" |
58 assert name |
106 assert name |
59 self.name = name |
107 self.name = name |
60 if not self.id: |
108 if not self.id: |
61 self.id = name |
109 self.id = name |
62 if not self.label: |
110 if not self.label: |
63 self.label = name |
111 self.label = name |
64 |
112 |
65 def is_visible(self): |
113 def is_visible(self): |
|
114 """return true if the field is not an hidden field""" |
66 return not isinstance(self.widget, HiddenInput) |
115 return not isinstance(self.widget, HiddenInput) |
67 |
116 |
68 def actual_fields(self, form): |
117 def actual_fields(self, form): |
|
118 """return actual fields composing this field in case of a compound |
|
119 field, usually simply return self |
|
120 """ |
69 yield self |
121 yield self |
70 |
122 |
71 def format_value(self, req, value): |
123 def format_value(self, req, value): |
|
124 """return value suitable for display where value may be a list or tuple |
|
125 of values |
|
126 """ |
72 if isinstance(value, (list, tuple)): |
127 if isinstance(value, (list, tuple)): |
73 return [self.format_single_value(req, val) for val in value] |
128 return [self.format_single_value(req, val) for val in value] |
74 return self.format_single_value(req, value) |
129 return self.format_single_value(req, value) |
75 |
130 |
76 def format_single_value(self, req, value): |
131 def format_single_value(self, req, value): |
|
132 """return value suitable for display""" |
77 if value is None: |
133 if value is None: |
78 return u'' |
134 return u'' |
79 return unicode(value) |
135 return unicode(value) |
80 |
136 |
81 def get_widget(self, form): |
137 def get_widget(self, form): |
|
138 """return the widget instance associated to this field""" |
82 return self.widget |
139 return self.widget |
83 |
140 |
84 def example_format(self, req): |
141 def example_format(self, req): |
|
142 """return a sample string describing what can be given as input for this |
|
143 field |
|
144 """ |
85 return u'' |
145 return u'' |
86 |
146 |
87 def render(self, form, renderer): |
147 def render(self, form, renderer): |
|
148 """render this field, which is part of form, using the given form |
|
149 renderer |
|
150 """ |
88 return self.get_widget(form).render(form, self) |
151 return self.get_widget(form).render(form, self) |
89 |
152 |
90 def vocabulary(self, form): |
153 def vocabulary(self, form): |
|
154 """return vocabulary for this field. This method will be called by |
|
155 widgets which desire it.""" |
91 if self.choices is not None: |
156 if self.choices is not None: |
92 if callable(self.choices): |
157 if callable(self.choices): |
93 vocab = self.choices(req=form.req) |
158 vocab = self.choices(req=form.req) |
94 else: |
159 else: |
95 vocab = self.choices |
160 vocab = self.choices |
96 if vocab and not isinstance(vocab[0], (list, tuple)): |
161 if vocab and not isinstance(vocab[0], (list, tuple)): |
97 vocab = [(x, x) for x in vocab] |
162 vocab = [(x, x) for x in vocab] |
98 return vocab |
163 else: |
99 return form.form_field_vocabulary(self) |
164 vocab = form.form_field_vocabulary(self) |
|
165 if self.internationalizable: |
|
166 vocab = [(form.req._(label), value) for label, value in vocab] |
|
167 if self.sort: |
|
168 vocab = sorted(vocab) |
|
169 return vocab |
100 |
170 |
101 |
171 |
102 class StringField(Field): |
172 class StringField(Field): |
103 def __init__(self, max_length=None, **kwargs): |
173 def __init__(self, max_length=None, **kwargs): |
104 super(StringField, self).__init__(**kwargs) |
174 super(StringField, self).__init__(**kwargs) |