62 :widget: |
63 :widget: |
63 widget associated to the field. Each field class has a default widget |
64 widget associated to the field. Each field class has a default widget |
64 class which may be overriden per instance. |
65 class which may be overriden per instance. |
65 :required: |
66 :required: |
66 bool flag telling if the field is required or not. |
67 bool flag telling if the field is required or not. |
67 :initial: |
68 :value: |
68 initial value, used when no value specified by other means. |
69 field's value, used when no value specified by other means. XXX explain |
69 :choices: |
70 :choices: |
70 static vocabulary for this field. May be a list of values or a list of |
71 static vocabulary for this field. May be a list of values or a list of |
71 (label, value) tuples if specified. |
72 (label, value) tuples if specified. |
72 :sort: |
73 :sort: |
73 bool flag telling if the vocabulary (either static vocabulary specified |
74 bool flag telling if the vocabulary (either static vocabulary specified |
93 # does this field requires a multipart form |
94 # does this field requires a multipart form |
94 needs_multipart = False |
95 needs_multipart = False |
95 # class attribute used for ordering of fields in a form |
96 # class attribute used for ordering of fields in a form |
96 __creation_rank = 0 |
97 __creation_rank = 0 |
97 |
98 |
98 def __init__(self, name=None, id=None, label=None, help=None, |
99 eidparam = False |
99 widget=None, required=False, initial=None, |
100 role = None |
100 choices=None, sort=True, internationalizable=False, |
101 id = None |
101 eidparam=False, role='subject', fieldset=None, order=None): |
102 help = None |
|
103 required = False |
|
104 choices = None |
|
105 sort = True |
|
106 internationalizable = False |
|
107 fieldset = None |
|
108 order = None |
|
109 value = _MARKER |
|
110 |
|
111 def __init__(self, name=None, label=None, widget=None, **kwargs): |
|
112 for key, val in kwargs.items(): |
|
113 if key == 'initial': |
|
114 warn('[3.6] use value instead of initial', DeprecationWarning, |
|
115 stacklevel=3) |
|
116 key = 'value' |
|
117 assert hasattr(self.__class__, key) and not key[0] == '_', key |
|
118 setattr(self, key, val) |
102 self.name = name |
119 self.name = name |
103 self.id = id or name |
|
104 self.label = label or name |
120 self.label = label or name |
105 self.help = help |
121 # has to be done after other attributes initialization |
106 self.required = required |
|
107 self.initial = initial |
|
108 self.choices = choices |
|
109 self.sort = sort |
|
110 self.internationalizable = internationalizable |
|
111 self.eidparam = eidparam |
|
112 self.role = role |
|
113 self.fieldset = fieldset |
|
114 self.init_widget(widget) |
122 self.init_widget(widget) |
115 self.order = order |
|
116 # ordering number for this field instance |
123 # ordering number for this field instance |
117 self.creation_rank = Field.__creation_rank |
124 self.creation_rank = Field.__creation_rank |
118 Field.__creation_rank += 1 |
125 Field.__creation_rank += 1 |
119 |
126 |
120 def __unicode__(self): |
127 def __unicode__(self): |
121 return u'<%s name=%r label=%r id=%r initial=%r visible=%r @%x>' % ( |
128 return u'<%s name=%r eidparam=%s role=%r id=%r value=%r visible=%r @%x>' % ( |
122 self.__class__.__name__, self.name, self.label, |
129 self.__class__.__name__, self.name, self.eidparam, self.role, |
123 self.id, self.initial, self.is_visible(), id(self)) |
130 self.id, self.value, self.is_visible(), id(self)) |
124 |
131 |
125 def __repr__(self): |
132 def __repr__(self): |
126 return self.__unicode__().encode('utf-8') |
133 return self.__unicode__().encode('utf-8') |
127 |
134 |
128 def init_widget(self, widget): |
135 def init_widget(self, widget): |
132 self.widget = Select() |
139 self.widget = Select() |
133 if isinstance(self.widget, type): |
140 if isinstance(self.widget, type): |
134 self.widget = self.widget() |
141 self.widget = self.widget() |
135 |
142 |
136 def set_name(self, name): |
143 def set_name(self, name): |
137 """automatically set .id and .label when name is set""" |
144 """automatically set .label when name is set""" |
138 assert name |
145 assert name |
139 self.name = name |
146 self.name = name |
140 if not self.id: |
|
141 self.id = name |
|
142 if not self.label: |
147 if not self.label: |
143 self.label = name |
148 self.label = name |
144 |
149 |
145 def is_visible(self): |
150 def is_visible(self): |
146 """return true if the field is not an hidden field""" |
151 """return true if the field is not an hidden field""" |
197 """return an html dom identifier for this field""" |
202 """return an html dom identifier for this field""" |
198 id = self.id or self.role_name() |
203 id = self.id or self.role_name() |
199 if self.eidparam: |
204 if self.eidparam: |
200 return eid_param(id, form.edited_entity.eid) |
205 return eid_param(id, form.edited_entity.eid) |
201 return id |
206 return id |
|
207 |
|
208 def display_value(self, form): |
|
209 """return field's *string* value to use for display |
|
210 |
|
211 looks in |
|
212 1. previously submitted form values if any (eg on validation error) |
|
213 2. req.form |
|
214 3. extra form args given to render_form |
|
215 4. field's typed value |
|
216 |
|
217 values found in 1. and 2. are expected te be already some 'display' |
|
218 value while those found in 3. and 4. are expected to be correctly typed. |
|
219 """ |
|
220 qname = self.input_name(form) |
|
221 if qname in form.form_previous_values: |
|
222 return form.form_previous_values[qname] |
|
223 if qname in form._cw.form: |
|
224 return form._cw.form[qname] |
|
225 if self.name != qname and self.name in form._cw.form: |
|
226 return form._cw.form[self.name] |
|
227 for key in (self, qname): |
|
228 try: |
|
229 value = form.formvalues[key] |
|
230 break |
|
231 except: |
|
232 continue |
|
233 else: |
|
234 if self.name != qname and self.name in form.formvalues: |
|
235 value = form.formvalues[self.name] |
|
236 else: |
|
237 value = self.typed_value(form) |
|
238 if value != INTERNAL_FIELD_VALUE: |
|
239 value = self.format_value(form._cw, value) |
|
240 return value |
|
241 |
|
242 def typed_value(self, form, load_bytes=False): |
|
243 if self.value is not _MARKER: |
|
244 if callable(self.value): |
|
245 return self.value(form) |
|
246 return self.value |
|
247 return self._typed_value(form, load_bytes) |
|
248 |
|
249 def _typed_value(self, form, load_bytes=False): |
|
250 if self.eidparam: |
|
251 assert form._cw.vreg.schema.rschema(self.name).final |
|
252 entity = form.edited_entity |
|
253 if entity.has_eid() or self.name in entity: |
|
254 return getattr(entity, self.name) |
|
255 formattr = '%s_%s_default' % (self.role, self.name) |
|
256 if hasattr(form, formattr): |
|
257 warn('[3.6] %s.%s deprecated, use field.value' % ( |
|
258 form.__class__.__name__, formattr), DeprecationWarning) |
|
259 return getattr(form, formattr)() |
|
260 if self.eidparam: |
|
261 return entity.e_schema.default(self.name) |
|
262 return None |
202 |
263 |
203 def example_format(self, req): |
264 def example_format(self, req): |
204 """return a sample string describing what can be given as input for this |
265 """return a sample string describing what can be given as input for this |
205 field |
266 field |
206 """ |
267 """ |
290 if self.max_length < 513: |
351 if self.max_length < 513: |
291 widget.attrs.setdefault('cols', 60) |
352 widget.attrs.setdefault('cols', 60) |
292 widget.attrs.setdefault('rows', 5) |
353 widget.attrs.setdefault('rows', 5) |
293 |
354 |
294 |
355 |
|
356 class PasswordField(StringField): |
|
357 widget = PasswordInput |
|
358 |
|
359 def _typed_value(self, form, load_bytes=False): |
|
360 if self.eidparam: |
|
361 # no way to fetch actual password value with cw |
|
362 if form.edited_entity.has_eid(): |
|
363 return INTERNAL_FIELD_VALUE |
|
364 return form.edited_entity.e_schema.default(self.name) |
|
365 return super(PasswordField, self)._typed_value(form, load_bytes) |
|
366 |
|
367 |
295 class RichTextField(StringField): |
368 class RichTextField(StringField): |
296 widget = None |
369 widget = None |
297 def __init__(self, format_field=None, **kwargs): |
370 def __init__(self, format_field=None, **kwargs): |
298 super(RichTextField, self).__init__(**kwargs) |
371 super(RichTextField, self).__init__(**kwargs) |
299 self.format_field = format_field |
372 self.format_field = format_field |
322 fkwargs = {'eidparam': self.eidparam} |
395 fkwargs = {'eidparam': self.eidparam} |
323 if self.use_fckeditor(form): |
396 if self.use_fckeditor(form): |
324 # if fckeditor is used and format field isn't explicitly |
397 # if fckeditor is used and format field isn't explicitly |
325 # deactivated, we want an hidden field for the format |
398 # deactivated, we want an hidden field for the format |
326 fkwargs['widget'] = HiddenInput() |
399 fkwargs['widget'] = HiddenInput() |
327 fkwargs['initial'] = 'text/html' |
400 fkwargs['value'] = 'text/html' |
328 else: |
401 else: |
329 # else we want a format selector |
402 # else we want a format selector |
330 fkwargs['widget'] = Select() |
403 fkwargs['widget'] = Select() |
331 fcstr = FormatConstraint() |
404 fcstr = FormatConstraint() |
332 fkwargs['choices'] = fcstr.vocabulary(form=form) |
405 fkwargs['choices'] = fcstr.vocabulary(form=form) |
333 fkwargs['internationalizable'] = True |
406 fkwargs['internationalizable'] = True |
334 fkwargs['initial'] = lambda f: f.form_field_format(self) |
407 fkwargs['value'] = self.format |
335 fkwargs['eidparam'] = self.eidparam |
408 fkwargs['eidparam'] = self.eidparam |
336 field = StringField(name=self.name + '_format', **fkwargs) |
409 field = StringField(name=self.name + '_format', **fkwargs) |
337 req.data[self] = field |
410 req.data[self] = field |
338 return field |
411 return field |
339 |
412 |
380 yield self.format_field |
453 yield self.format_field |
381 if self.encoding_field: |
454 if self.encoding_field: |
382 yield self.encoding_field |
455 yield self.encoding_field |
383 if self.name_field: |
456 if self.name_field: |
384 yield self.name_field |
457 yield self.name_field |
|
458 |
|
459 def _typed_value(self, form, load_bytes=False): |
|
460 if self.eidparam: |
|
461 if form.edited_entity.has_eid(): |
|
462 if load_bytes: |
|
463 return getattr(form.edited_entity, self.name) |
|
464 # don't actually load data |
|
465 # XXX value should reflect if some file is already attached |
|
466 # * try to display name metadata |
|
467 # * check length(data) / data != null |
|
468 return True |
|
469 return False |
|
470 return super(FileField, self)._typed_value(form, load_bytes) |
385 |
471 |
386 def render(self, form, renderer): |
472 def render(self, form, renderer): |
387 wdgs = [self.get_widget(form).render(form, self, renderer)] |
473 wdgs = [self.get_widget(form).render(form, self, renderer)] |
388 if self.format_field or self.encoding_field: |
474 if self.format_field or self.encoding_field: |
389 divid = '%s-advanced' % self.input_name(form) |
475 divid = '%s-advanced' % self.input_name(form) |
398 if self.format_field: |
484 if self.format_field: |
399 wdgs.append(self.render_subfield(form, self.format_field, renderer)) |
485 wdgs.append(self.render_subfield(form, self.format_field, renderer)) |
400 if self.encoding_field: |
486 if self.encoding_field: |
401 wdgs.append(self.render_subfield(form, self.encoding_field, renderer)) |
487 wdgs.append(self.render_subfield(form, self.encoding_field, renderer)) |
402 wdgs.append(u'</div>') |
488 wdgs.append(u'</div>') |
403 if not self.required and form.context[self]['value']: |
489 if not self.required and self.display_value(form): |
404 # trick to be able to delete an uploaded file |
490 # trick to be able to delete an uploaded file |
405 wdgs.append(u'<br/>') |
491 wdgs.append(u'<br/>') |
406 wdgs.append(tags.input(name=self.input_name(form, u'__detach'), |
492 wdgs.append(tags.input(name=self.input_name(form, u'__detach'), |
407 type=u'checkbox')) |
493 type=u'checkbox')) |
408 wdgs.append(form._cw._('detach attached file')) |
494 wdgs.append(form._cw._('detach attached file')) |
437 class EditableFileField(FileField): |
523 class EditableFileField(FileField): |
438 editable_formats = ('text/plain', 'text/html', 'text/rest') |
524 editable_formats = ('text/plain', 'text/html', 'text/rest') |
439 |
525 |
440 def render(self, form, renderer): |
526 def render(self, form, renderer): |
441 wdgs = [super(EditableFileField, self).render(form, renderer)] |
527 wdgs = [super(EditableFileField, self).render(form, renderer)] |
442 if form.form_field_format(self) in self.editable_formats: |
528 if self.format(form) in self.editable_formats: |
443 data = form.form_field_value(self, load_bytes=True) |
529 data = self.typed_value(form, load_bytes=True) |
444 if data: |
530 if data: |
445 encoding = form.form_field_encoding(self) |
531 encoding = form.form_field_encoding(self) |
446 try: |
532 try: |
447 form.context[self]['value'] = unicode(data.getvalue(), encoding) |
533 form.formvalues[self] = unicode(data.getvalue(), encoding) |
448 except UnicodeError: |
534 except UnicodeError: |
449 pass |
535 pass |
450 else: |
536 else: |
451 if not self.required: |
537 if not self.required: |
452 msg = form._cw._( |
538 msg = form._cw._( |
590 vocab = res + form.form_field_vocabulary(self) + relatedvocab |
676 vocab = res + form.form_field_vocabulary(self) + relatedvocab |
591 if self.sort: |
677 if self.sort: |
592 vocab = vocab_sort(vocab) |
678 vocab = vocab_sort(vocab) |
593 return vocab |
679 return vocab |
594 |
680 |
|
681 def form_init(self, form): |
|
682 if not self.display_value(form): |
|
683 value = form.edited_entity.linked_to(self.name, self.role) |
|
684 if value: |
|
685 searchedvalues = ['%s:%s:%s' % (self.name, eid, self.role) |
|
686 for eid in value] |
|
687 # remove associated __linkto hidden fields |
|
688 for field in form.root_form.fields_by_name('__linkto'): |
|
689 if field.value in searchedvalues: |
|
690 form.root_form.remove_field(field) |
|
691 form.formvalues[self] = value |
|
692 |
|
693 def _typed_value(self, form, load_bytes=False): |
|
694 entity = form.edited_entity |
|
695 # non final relation field |
|
696 if entity.has_eid() or entity.relation_cached(self.name, self.role): |
|
697 return [r[0] for r in entity.related(self.name, self.role)] |
|
698 return () |
|
699 |
595 def format_single_value(self, req, value): |
700 def format_single_value(self, req, value): |
596 return value |
701 return value |
597 |
702 |
598 def process_form_value(self, form): |
703 def process_form_value(self, form): |
599 """process posted form and return correctly typed value""" |
704 """process posted form and return correctly typed value""" |
626 if role == 'subject': |
731 if role == 'subject': |
627 targetschema = rdef.object |
732 targetschema = rdef.object |
628 if rschema.final: |
733 if rschema.final: |
629 if rdef.get('internationalizable'): |
734 if rdef.get('internationalizable'): |
630 kwargs.setdefault('internationalizable', True) |
735 kwargs.setdefault('internationalizable', True) |
631 def get_default(form, es=eschema, rs=rschema): |
|
632 return es.default(rs) |
|
633 kwargs.setdefault('initial', get_default) |
|
634 else: |
736 else: |
635 targetschema = rdef.subject |
737 targetschema = rdef.subject |
636 card = rdef.role_cardinality(role) |
738 card = rdef.role_cardinality(role) |
637 kwargs['required'] = card in '1+' |
739 kwargs['required'] = card in '1+' |
638 kwargs['name'] = rschema.type |
740 kwargs['name'] = rschema.type |
645 if rschema.final: |
747 if rschema.final: |
646 if skip_meta_attr and rschema in eschema.meta_attributes(): |
748 if skip_meta_attr and rschema in eschema.meta_attributes(): |
647 return None |
749 return None |
648 fieldclass = FIELDS[targetschema] |
750 fieldclass = FIELDS[targetschema] |
649 if fieldclass is StringField: |
751 if fieldclass is StringField: |
650 if targetschema == 'Password': |
|
651 # special case for Password field: specific PasswordInput widget |
|
652 kwargs.setdefault('widget', PasswordInput()) |
|
653 return StringField(**kwargs) |
|
654 if eschema.has_metadata(rschema, 'format'): |
752 if eschema.has_metadata(rschema, 'format'): |
655 # use RichTextField instead of StringField if the attribute has |
753 # use RichTextField instead of StringField if the attribute has |
656 # a "format" metadata. But getting information from constraints |
754 # a "format" metadata. But getting information from constraints |
657 # may be useful anyway... |
755 # may be useful anyway... |
658 for cstr in rdef.constraints: |
756 for cstr in rdef.constraints: |