10 from simplejson import dumps |
10 from simplejson import dumps |
11 from mx.DateTime import today |
11 from mx.DateTime import today |
12 |
12 |
13 from logilab.common.compat import any |
13 from logilab.common.compat import any |
14 from logilab.mtconverter import html_escape |
14 from logilab.mtconverter import html_escape |
|
15 |
|
16 from yams.constraints import SizeConstraint, StaticVocabularyConstraint |
15 |
17 |
16 from cubicweb import typed_eid |
18 from cubicweb import typed_eid |
17 from cubicweb.utils import ustrftime |
19 from cubicweb.utils import ustrftime |
18 from cubicweb.selectors import match_form_params |
20 from cubicweb.selectors import match_form_params |
19 from cubicweb.view import NOINDEX, NOFOLLOW, View, EntityView, AnyRsetView |
21 from cubicweb.view import NOINDEX, NOFOLLOW, View, EntityView, AnyRsetView |
248 # widgets ############ |
250 # widgets ############ |
249 |
251 |
250 class FieldWidget(object): |
252 class FieldWidget(object): |
251 def __init__(self, attrs=None): |
253 def __init__(self, attrs=None): |
252 self.attrs = attrs or {} |
254 self.attrs = attrs or {} |
253 |
255 |
254 def render(self, form, field): |
256 def render(self, form, field): |
255 raise NotImplementedError |
257 raise NotImplementedError |
256 |
258 |
257 class Input(FieldWidget): |
259 class Input(FieldWidget): |
258 type = None |
260 type = None |
286 name = form.context[field]['name'] # qualified name |
288 name = form.context[field]['name'] # qualified name |
287 return name, None, {} |
289 return name, None, {} |
288 |
290 |
289 class HiddenInput(Input): |
291 class HiddenInput(Input): |
290 type = 'hidden' |
292 type = 'hidden' |
291 |
293 |
292 class Button(Input): |
294 class Button(Input): |
293 type = 'button' |
295 type = 'button' |
294 |
296 |
295 class TextArea(FieldWidget): |
297 class TextArea(FieldWidget): |
296 def render(self, form, field): |
298 def render(self, form, field): |
439 class StringField(Field): |
441 class StringField(Field): |
440 def __init__(self, max_length=None, **kwargs): |
442 def __init__(self, max_length=None, **kwargs): |
441 super(StringField, self).__init__(**kwargs) |
443 super(StringField, self).__init__(**kwargs) |
442 self.max_length = max_length |
444 self.max_length = max_length |
443 |
445 |
444 |
|
445 class TextField(Field): |
446 class TextField(Field): |
446 widget = TextArea |
447 widget = TextArea |
447 def __init__(self, row=None, col=None, **kwargs): |
448 def __init__(self, row=None, col=None, **kwargs): |
448 super(TextField, self).__init__(**kwargs) |
449 super(TextField, self).__init__(**kwargs) |
449 self.row = row |
450 self.row = row |
528 class HiddenInitialValueField(Field): |
529 class HiddenInitialValueField(Field): |
529 def __init__(self, visible_field, name): |
530 def __init__(self, visible_field, name): |
530 super(HiddenInitialValueField, self).__init__(name=name, |
531 super(HiddenInitialValueField, self).__init__(name=name, |
531 widget=HiddenInput) |
532 widget=HiddenInput) |
532 self.visible_field = visible_field |
533 self.visible_field = visible_field |
|
534 |
533 |
535 |
534 class RelationField(Field): |
536 class RelationField(Field): |
535 def __init__(self, role='subject', **kwargs): |
537 def __init__(self, role='subject', **kwargs): |
536 super(RelationField, self).__init__(**kwargs) |
538 super(RelationField, self).__init__(**kwargs) |
537 self.role = role |
539 |
|
540 @staticmethod |
|
541 def fromcardinality(card, role, **kwargs): |
|
542 return RelationField(widget=Select(multiple=card in '*+'), |
|
543 **kwargs) |
538 |
544 |
539 # forms ############ |
545 # forms ############ |
540 class metafieldsform(type): |
546 class metafieldsform(type): |
541 def __new__(mcs, name, bases, classdict): |
547 def __new__(mcs, name, bases, classdict): |
542 allfields = [] |
548 allfields = [] |
619 value = values[field.name] |
625 value = values[field.name] |
620 elif field.name in self.req.form: |
626 elif field.name in self.req.form: |
621 value = self.req.form[field.name] |
627 value = self.req.form[field.name] |
622 else: |
628 else: |
623 value = field.initial |
629 value = field.initial |
624 return value # field.format_value(self.req, value) |
630 return value |
625 |
631 |
626 def form_format_field_value(self, field, values): |
632 def form_format_field_value(self, field, values): |
627 return self.req.property_value('ui.default-text-format') |
633 return self.req.property_value('ui.default-text-format') |
628 |
634 |
629 def form_field_name(self, field): |
635 def form_field_name(self, field): |
630 return field.name |
636 return field.name |
723 else: |
729 else: |
724 # use field's initial value |
730 # use field's initial value |
725 value = field.initial |
731 value = field.initial |
726 if callable(value): |
732 if callable(value): |
727 values = value() |
733 values = value() |
728 return value # field.format_value(self.req, value) |
734 return value |
729 |
735 |
730 def form_format_field_value(self, field, values): |
736 def form_format_field_value(self, field, values): |
731 entity = self.entity |
737 entity = self.entity |
732 if field.eidparam and entity.has_format(field.name) and ( |
738 if field.eidparam and entity.has_format(field.name) and ( |
733 entity.has_eid() or '%s_format' % field.name in entity): |
739 entity.has_eid() or '%s_format' % field.name in entity): |
838 label = form.req._(field.label) |
844 label = form.req._(field.label) |
839 attrs = {'for': form.context[field]['id']} |
845 attrs = {'for': form.context[field]['id']} |
840 if field.required: |
846 if field.required: |
841 attrs['class'] = 'required' |
847 attrs['class'] = 'required' |
842 return tags.label(label, **attrs) |
848 return tags.label(label, **attrs) |
843 |
849 |
|
850 |
|
851 def stringfield_from_constraints(constraints, **kwargs): |
|
852 field = None |
|
853 for cstr in constraints: |
|
854 if isinstance(cstr, StaticVocabularyConstraint): |
|
855 return StringField(widget=Select(vocabulary=cstr.vocabulary), |
|
856 **kwargs) |
|
857 if isinstance(cstr, SizeConstraint) and cstr.max is not None: |
|
858 if cstr.max > 257: |
|
859 field = textfield_from_constraint(cstr, **kwargs) |
|
860 else: |
|
861 field = StringField(max_length=cstr.max, **kwargs) |
|
862 return field or TextField(**kwargs) |
|
863 |
|
864 |
|
865 def textfield_from_constraint(constraint, **kwargs): |
|
866 if 256 < constraint.max < 513: |
|
867 rows, cols = 5, 60 |
|
868 else: |
|
869 rows, cols = 10, 80 |
|
870 return TextField(rows, cols, **kwargs) |
|
871 |
|
872 |
|
873 def find_field(eclass, subjschema, rschema, role='subject'): |
|
874 """return the most adapated widget to edit the relation |
|
875 'subjschema rschema objschema' according to information found in the schema |
|
876 """ |
|
877 fieldclass = None |
|
878 if role == 'subject': |
|
879 objschema = rschema.objects(subjschema)[0] |
|
880 cardidx = 0 |
|
881 else: |
|
882 objschema = rschema.subjects(subjschema)[0] |
|
883 cardidx = 1 |
|
884 card = rschema.rproperty(subjschema, objschema, 'cardinality')[cardidx] |
|
885 required = card in '1+' |
|
886 if rschema in eclass.widgets: |
|
887 fieldclass = eclass.widgets[rschema] |
|
888 if isinstance(fieldclass, basestring): |
|
889 return StringField(name=rschema.type) |
|
890 elif not rschema.is_final(): |
|
891 return RelationField.fromcardinality(card, role,name=rschema.type, |
|
892 required=required) |
|
893 else: |
|
894 fieldclass = FIELDS[objschema] |
|
895 if fieldclass is StringField: |
|
896 constraints = rschema.rproperty(subjschema, objschema, 'constraints') |
|
897 return stringfield_from_constraints(constraints, name=rschema.type, |
|
898 required=required) |
|
899 return fieldclass(name=rschema.type, required=required) |
|
900 |
|
901 FIELDS = { |
|
902 'Boolean': BooleanField, |
|
903 'Bytes': FileField, |
|
904 'Date': DateField, |
|
905 'Datetime': DateTimeField, |
|
906 'Int': IntField, |
|
907 'Float': FloatField, |
|
908 'Decimal': StringField, |
|
909 'Password': StringField, |
|
910 'String' : StringField, |
|
911 'Time': TimeField, |
|
912 } |