2
0

blocks.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. from django import forms
  2. from django.db.models import BLANK_CHOICE_DASH
  3. from django.utils.dateparse import parse_datetime
  4. from django.utils.text import slugify
  5. from django.utils.translation import gettext_lazy as _
  6. from anyascii import anyascii
  7. from wagtail.blocks import (
  8. StructBlock, TextBlock, CharBlock, BooleanBlock, ListBlock, StreamBlock,
  9. DateBlock, TimeBlock, DateTimeBlock, ChoiceBlock, RichTextBlock,
  10. )
  11. class FormFieldBlock(StructBlock):
  12. field_label = CharBlock(label=_('Label'))
  13. help_text = TextBlock(required=False, label=_('Help text'))
  14. field_class = forms.CharField
  15. widget = None
  16. def get_slug(self, struct_value):
  17. return slugify(anyascii(struct_value['field_label']))
  18. def get_field_class(self, struct_value):
  19. return self.field_class
  20. def get_widget(self, struct_value):
  21. return self.widget
  22. def get_field_kwargs(self, struct_value):
  23. kwargs = {'label': struct_value['field_label'],
  24. 'help_text': struct_value['help_text'],
  25. 'required': struct_value.get('required', False)}
  26. if 'default_value' in struct_value:
  27. kwargs['initial'] = struct_value['default_value']
  28. form_widget = self.get_widget(struct_value)
  29. if form_widget is not None:
  30. kwargs['widget'] = form_widget
  31. return kwargs
  32. def get_field(self, struct_value):
  33. return self.get_field_class(struct_value)(
  34. **self.get_field_kwargs(struct_value))
  35. class OptionalFormFieldBlock(FormFieldBlock):
  36. required = BooleanBlock(label=_('Required'), required=False)
  37. CHARFIELD_FORMATS = [
  38. ('email', _('Email')),
  39. ('url', _('URL')),
  40. ]
  41. try:
  42. from phonenumber_field.formfields import PhoneNumberField
  43. except ImportError:
  44. pass
  45. else:
  46. CHARFIELD_FORMATS.append(('phone', _('Phone')))
  47. class CharFieldBlock(OptionalFormFieldBlock):
  48. format = ChoiceBlock(choices=CHARFIELD_FORMATS, required=False, label=_('Format'))
  49. default_value = CharBlock(required=False, label=_('Default value'))
  50. class Meta:
  51. label = _('Text field (single line)')
  52. def get_field_class(self, struct_value):
  53. text_format = struct_value['format']
  54. if text_format == 'url':
  55. return forms.URLField
  56. if text_format == 'email':
  57. return forms.EmailField
  58. if text_format == 'phone':
  59. return PhoneNumberField
  60. return super().get_field_class(struct_value)
  61. class TextFieldBlock(OptionalFormFieldBlock):
  62. default_value = TextBlock(required=False, label=_('Default value'))
  63. widget = forms.Textarea(attrs={'rows': 5})
  64. class Meta:
  65. label = _('Text field (multi line)')
  66. class NumberFieldBlock(OptionalFormFieldBlock):
  67. default_value = CharBlock(required=False, label=_('Default value'))
  68. widget = forms.NumberInput
  69. class Meta:
  70. label = _('Number field')
  71. class CheckboxFieldBlock(FormFieldBlock):
  72. default_value = BooleanBlock(required=False)
  73. field_class = forms.BooleanField
  74. class Meta:
  75. label = _('Checkbox field')
  76. icon = 'tick-inverse'
  77. class RadioButtonsFieldBlock(OptionalFormFieldBlock):
  78. choices = ListBlock(CharBlock(label=_('Choice')))
  79. field_class = forms.ChoiceField
  80. widget = forms.RadioSelect
  81. class Meta:
  82. label = _('Radio buttons')
  83. icon = 'radio-empty'
  84. def get_field_kwargs(self, struct_value):
  85. kwargs = super().get_field_kwargs(struct_value)
  86. kwargs['choices'] = [(choice, choice)
  87. for choice in struct_value['choices']]
  88. return kwargs
  89. class DropdownFieldBlock(RadioButtonsFieldBlock):
  90. widget = forms.Select
  91. class Meta:
  92. label = _('Dropdown field')
  93. icon = 'arrow-down-big'
  94. def get_field_kwargs(self, struct_value):
  95. kwargs = super(DropdownFieldBlock,
  96. self).get_field_kwargs(struct_value)
  97. kwargs['choices'].insert(0, BLANK_CHOICE_DASH[0])
  98. return kwargs
  99. class CheckboxesFieldBlock(OptionalFormFieldBlock):
  100. checkboxes = ListBlock(CharBlock(label=_('Checkbox')))
  101. field_class = forms.MultipleChoiceField
  102. widget = forms.CheckboxSelectMultiple
  103. class Meta:
  104. label = _('Multiple checkboxes field')
  105. icon = 'list-ul'
  106. def get_field_kwargs(self, struct_value):
  107. kwargs = super(CheckboxesFieldBlock,
  108. self).get_field_kwargs(struct_value)
  109. kwargs['choices'] = [(choice, choice)
  110. for choice in struct_value['checkboxes']]
  111. return kwargs
  112. class DatePickerInput(forms.DateInput):
  113. def __init__(self, *args, **kwargs):
  114. attrs = kwargs.get('attrs')
  115. if attrs is None:
  116. attrs = {}
  117. attrs.update({
  118. 'data-provide': 'datepicker',
  119. 'data-date-format': 'yyyy-mm-dd',
  120. })
  121. kwargs['attrs'] = attrs
  122. super().__init__(*args, **kwargs)
  123. class DateFieldBlock(OptionalFormFieldBlock):
  124. default_value = DateBlock(required=False)
  125. field_class = forms.DateField
  126. widget = DatePickerInput
  127. class Meta:
  128. label = _('Date field')
  129. icon = 'date'
  130. class HTML5TimeInput(forms.TimeInput):
  131. input_type = 'time'
  132. class TimeFieldBlock(OptionalFormFieldBlock):
  133. default_value = TimeBlock(required=False)
  134. field_class = forms.TimeField
  135. widget = HTML5TimeInput
  136. class Meta:
  137. label = _('Time field')
  138. icon = 'time'
  139. class DateTimePickerInput(forms.SplitDateTimeWidget):
  140. def __init__(self, attrs=None, date_format=None, time_format=None):
  141. super().__init__(attrs=attrs,
  142. date_format=date_format, time_format=time_format)
  143. self.widgets = (
  144. DatePickerInput(attrs=attrs, format=date_format),
  145. HTML5TimeInput(attrs=attrs, format=time_format),
  146. )
  147. def decompress(self, value):
  148. if isinstance(value, str):
  149. value = parse_datetime(value)
  150. return super().decompress(value)
  151. class DateTimeFieldBlock(OptionalFormFieldBlock):
  152. default_value = DateTimeBlock(required=False)
  153. field_class = forms.SplitDateTimeField
  154. widget = DateTimePickerInput
  155. class Meta:
  156. label = _('Date+time field')
  157. icon = 'date'
  158. class ImageFieldBlock(OptionalFormFieldBlock):
  159. field_class = forms.ImageField
  160. class Meta:
  161. label = _('Image field')
  162. icon = 'image'
  163. class FileFieldBlock(OptionalFormFieldBlock):
  164. field_class = forms.FileField
  165. class Meta:
  166. label = _('File field')
  167. icon = 'download'
  168. class FormFieldsBlock(StreamBlock):
  169. char = CharFieldBlock(group=_('Fields'))
  170. text = TextFieldBlock(group=_('Fields'))
  171. number = NumberFieldBlock(group=_('Fields'))
  172. checkbox = CheckboxFieldBlock(group=_('Fields'))
  173. radios = RadioButtonsFieldBlock(group=_('Fields'))
  174. dropdown = DropdownFieldBlock(group=_('Fields'))
  175. checkboxes = CheckboxesFieldBlock(group=_('Fields'))
  176. date = DateFieldBlock(group=_('Fields'))
  177. time = TimeFieldBlock(group=_('Fields'))
  178. datetime = DateTimeFieldBlock(group=_('Fields'))
  179. image = ImageFieldBlock(group=_('Fields'))
  180. file = FileFieldBlock(group=_('Fields'))
  181. text_markup = RichTextBlock(group=_('Other'))
  182. class Meta:
  183. label = _('Form fields')
  184. class FormStepBlock(StructBlock):
  185. name = CharBlock(label=_('Name'), required=False)
  186. form_fields = FormFieldsBlock()
  187. class Meta:
  188. label = _('Form step')
  189. class FormStepsBlock(StreamBlock):
  190. step = FormStepBlock()
  191. class Meta:
  192. label = _('Form steps')