from datetime import date from decimal import Decimal from itertools import chain from catalog.models import Comment, SiteConfig from django import forms from django.db import models from django.utils.translation import gettext_lazy as _ from modelcluster.fields import ParentalKey from wagtail.admin.edit_handlers import (FieldPanel, InlinePanel,StreamFieldPanel) from wagtail.contrib.forms.forms import FormBuilder from wagtail.contrib.forms.models import (FORM_FIELD_CHOICES, AbstractForm, AbstractFormField) from wagtail.core import blocks from wagtail.core.fields import RichTextField, StreamField from wagtail.core.models import Page from wagtail.embeds.blocks import EmbedBlock from .blocks import ImageListBlock, ModalBlock, YellowStripBlock, NewsletterBlock from .utils import get_current_site, TranslatedField, get_language_codes class CustomFormBuilder(FormBuilder): def __init__(self, fields): super().__init__(fields) def create_file_field(self, field, options): return forms.FileField(**options) def create_multiselect_field(self, field, options): return super().create_checkboxes_field(field, options) class CustomPageMixin(Page): class Meta: abstract = True menu_link_text = models.CharField( max_length=255, blank=True ) promote_panels = Page.promote_panels + [ FieldPanel("menu_link_text") ] preview_modes = [] class ImageGallery(Page): template = "pages/image_gallery.html" preview_modes = [] class InfoPage(CustomPageMixin): template = "pages/info.html" body = StreamField([ ("header_video", EmbedBlock()), ("paragraph", blocks.RichTextBlock()), ("medium_text", blocks.RichTextBlock()), ("h1_big_text", blocks.TextBlock()), ("image_list", ImageListBlock()), ("yellow_strip", YellowStripBlock()) ]) footer = StreamField([("modal", ModalBlock())], blank=True) newsletter_strip = StreamField([("Newletter", NewsletterBlock())], max_num=1, blank=True) content_panels = CustomPageMixin.content_panels + [ StreamFieldPanel("body"), StreamFieldPanel("footer"), StreamFieldPanel("newsletter_strip") ] def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) site_config = SiteConfig.objects.get(site=get_current_site(request)) context["site_config"] = site_config return context class GenericPage(CustomPageMixin): template = "pages/generic.html" body = StreamField([ ("paragraph", blocks.RichTextBlock()), ("medium_text", blocks.RichTextBlock()), ("h1_big_text", blocks.TextBlock()), ("image_list", ImageListBlock()), ("modal", ModalBlock()) ]) content_panels = CustomPageMixin.content_panels + [ StreamFieldPanel("body") ] class CustomAbstractForm(CustomPageMixin, AbstractForm): class Meta: abstract = True TRANSLATED_FIELDS = { 'help_text': models.CharField(verbose_name=_('help text'), max_length=255, blank=True), 'choices': models.TextField( verbose_name='choices', blank=True, help_text=_('Comma separated list of choices. Only applicable in checkboxes, radio and dropdown.') ) } class ContributeField(AbstractFormField): CHOICES = FORM_FIELD_CHOICES + (('file', 'Upload File'),) page = ParentalKey("ContributePage", on_delete=models.CASCADE, related_name="form_fields") field_type = models.CharField( verbose_name='field type', max_length=16, choices=CHOICES ) choices = TranslatedField("choices") help_text = TranslatedField("help_text") AbstractFormField.panels = [ field for field in AbstractFormField.panels if field.field_name not in TRANSLATED_FIELDS.keys() ] panels = AbstractFormField.panels + list( chain.from_iterable([ [ FieldPanel(f"{field}_{language_code}") for language_code in get_language_codes() ] for field in TRANSLATED_FIELDS.keys() ]) ) for field in TRANSLATED_FIELDS: for language_code in get_language_codes(): _, path, args, kwargs = TRANSLATED_FIELDS[field].deconstruct() kwargs['verbose_name'] = f"{kwargs['verbose_name']} ({language_code})" ContributeField.add_to_class( f"{field}_{language_code}", TRANSLATED_FIELDS[field].__class__(*args, **kwargs) ) class ContributePage(CustomAbstractForm): template = "pages/contribute.html" landing_page_template = "pages/contribute_landing.html" form_builder = CustomFormBuilder details = RichTextField(blank=True) bottom_details = StreamField([ ("paragraph", blocks.RichTextBlock()), ("medium_text", blocks.RichTextBlock()), ("h1_big_text", blocks.TextBlock()), ("image_list", ImageListBlock()), ("modal", ModalBlock()) ], blank=True) content_panels = CustomAbstractForm.content_panels + [ FieldPanel("details", classname="full"), InlinePanel("form_fields", label="Form Fields"), FieldPanel("bottom_details", classname="full") ] def get_context(self, request, *args, **kwargs): context = super().get_context(request, *args, **kwargs) site_config = SiteConfig.objects.get(site=get_current_site(request)) context["site_config"] = site_config return context def process_form_submission(self, form): """ Processes the form submission, if an Image upload is found, pull out the files data, create an actual Wgtail Image and reference its ID only in the stored form response. """ cleaned_data = form.cleaned_data matching_fields = map(lambda f: f.name, Comment._meta.get_fields()) & cleaned_data.keys() non_matching_fields = cleaned_data.keys() - matching_fields for field in non_matching_fields: if isinstance(cleaned_data[field], date): cleaned_data[field] = str(cleaned_data[field]) elif isinstance(cleaned_data[field], Decimal): cleaned_data[field] = float(cleaned_data[field]) submission = Comment( **{k: cleaned_data[k] for k in matching_fields}, site=get_current_site(form.page.url), misc_data={k: cleaned_data[k] for k in non_matching_fields} ) submission.save() return submission