We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Send wagtail form emails asynchronously 
Currently wagtail form emails are sent synchronously because the `send` attributes are not serializable. see #671 for the temporary fix.
Rather then sending sync we should fix the problem and provide the required data as a serializable type

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `meinberlin/apps/cms/models.py`
Content:
```
1 from django.db import models
2 from modelcluster.fields import ParentalKey
3 from modelcluster.models import ClusterableModel
4 from wagtail.wagtailadmin import edit_handlers
5 from wagtail.wagtailcore import blocks
6 from wagtail.wagtailcore import fields
7 from wagtail.wagtailcore.models import Orderable
8 from wagtail.wagtailcore.models import Page
9 from wagtail.wagtailforms.models import AbstractEmailForm
10 from wagtail.wagtailforms.models import AbstractFormField
11 from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
12 from wagtail.wagtailsnippets.models import register_snippet
13 
14 from meinberlin.apps.actions import blocks as actions_blocks
15 
16 from . import blocks as cms_blocks
17 from . import emails
18 
19 
20 class SimplePage(Page):
21     body = fields.RichTextField(blank=True)
22 
23     content_panels = [
24         edit_handlers.FieldPanel('title'),
25         edit_handlers.FieldPanel('body'),
26     ]
27 
28     subpage_types = []
29 
30 
31 class HomePage(Page):
32     body = fields.StreamField([
33         ('paragraph', blocks.RichTextBlock(
34             template='meinberlin_cms/blocks/richtext_block.html'
35         )),
36         ('call_to_action', cms_blocks.CallToActionBlock()),
37         ('columns_text', cms_blocks.ColumnsBlock()),
38         ('projects', cms_blocks.ProjectsWrapperBlock()),
39         ('activities', actions_blocks.PlatformActivityBlock()),
40     ])
41 
42     subtitle = models.CharField(max_length=120)
43 
44     header_image = models.ForeignKey(
45         'wagtailimages.Image',
46         null=True,
47         blank=False,
48         on_delete=models.SET_NULL,
49         related_name='+'
50     )
51 
52     content_panels = Page.content_panels + [
53         edit_handlers.FieldPanel('subtitle'),
54         ImageChooserPanel('header_image'),
55         edit_handlers.StreamFieldPanel('body'),
56     ]
57 
58 
59 class MenuItem(models.Model):
60     title = models.CharField(max_length=255)
61     link_page = models.ForeignKey('wagtailcore.Page')
62 
63     @property
64     def url(self):
65         return self.link_page.url
66 
67     def __str__(self):
68         return self.title
69 
70     panels = [
71         edit_handlers.FieldPanel('title'),
72         edit_handlers.PageChooserPanel('link_page')
73     ]
74 
75 
76 @register_snippet
77 class NavigationMenu(ClusterableModel):
78     title = models.CharField(max_length=255, null=False, blank=False)
79 
80     def __str__(self):
81         return self.title
82 
83     panels = [
84         edit_handlers.FieldPanel('title'),
85         edit_handlers.InlinePanel('items')
86     ]
87 
88 
89 class NavigationMenuItem(Orderable, MenuItem):
90     parent = ParentalKey('meinberlin_cms.NavigationMenu', related_name='items')
91 
92 
93 class EmailFormField(AbstractFormField):
94     page = ParentalKey('EmailFormPage', related_name='form_fields')
95 
96 
97 class EmailFormPage(AbstractEmailForm):
98     intro = fields.RichTextField(
99         help_text='Introduction text shown above the form'
100     )
101     thank_you = fields.RichTextField(
102         help_text='Text shown after form submission',
103     )
104     email_content = models.CharField(
105         max_length=200,
106         help_text='Email content message',
107     )
108     attach_as = models.CharField(
109         max_length=3,
110         choices=(
111             ('xls', 'XLSX Document'),
112             ('txt', 'Text'),
113         ),
114         default='xls',
115         help_text='Form results are send in this document format',
116     )
117 
118     content_panels = AbstractEmailForm.content_panels + [
119         edit_handlers.MultiFieldPanel([
120             edit_handlers.FieldPanel('intro', classname='full'),
121             edit_handlers.FieldPanel('thank_you', classname='full'),
122         ], 'Page'),
123         edit_handlers.MultiFieldPanel([
124             edit_handlers.FieldPanel('to_address'),
125             edit_handlers.FieldPanel('subject'),
126             edit_handlers.FieldPanel('email_content', classname='full'),
127             edit_handlers.FieldPanel('attach_as'),
128         ], 'Email'),
129         edit_handlers.InlinePanel('form_fields', label='Form fields'),
130     ]
131 
132     def send_mail(self, form):
133         self.form = form
134         if self.attach_as == 'xls':
135             emails.XlsxFormEmail.send(self)
136         elif self.attach_as == 'txt':
137             emails.TextFormEmail.send(self)
138 
139     @property
140     def field_values(self):
141         fields = {}
142         for field in self.form:
143             value = field.value()
144             if isinstance(value, list):
145                 value = ', '.join(value)
146             fields[field.label] = value
147         return fields
148
```

Path: `meinberlin/apps/cms/emails.py`
Content:
```
1 import io
2 from email.mime.application import MIMEApplication
3 from email.mime.text import MIMEText
4 
5 import xlsxwriter
6 from django.utils import timezone
7 from django.utils.translation import ugettext as _
8 
9 from adhocracy4.emails.mixins import SyncEmailMixin
10 from meinberlin.apps.contrib.emails import Email
11 
12 
13 class FormEmail(SyncEmailMixin, Email):
14     template_name = 'meinberlin_cms/emails/form_submission'
15 
16     def get_receivers(self):
17         return [x.strip() for x in self.object.to_address.split(',')]
18 
19 
20 class XlsxFormEmail(FormEmail):
21 
22     def _generate_xlsx(self):
23         stream = io.BytesIO()
24         workbook = xlsxwriter.Workbook(stream, {'in_memory': True})
25         worksheet = workbook.add_worksheet()
26 
27         worksheet.write(0, 0, _('Form Field'))
28         worksheet.write(0, 1, _('Response'))
29 
30         rows = self.object.field_values.items()
31         for rownum, row in enumerate(rows, start=1):
32             worksheet.write(rownum, 0, row[0])
33             worksheet.write(rownum, 1, self._fix_newline_if_string(row[1]))
34 
35         workbook.close()
36         return stream.getvalue()
37 
38     def _fix_newline_if_string(self, value):
39         if isinstance(value, str):
40             return value.replace('\r', '')
41         return value
42 
43     def get_attachments(self):
44         attachments = super().get_attachments()
45         xlsx_data = self._generate_xlsx()
46         mime_doc = MIMEApplication(
47             _data=xlsx_data,
48             _subtype='vnd.openxmlformats-officedocument.spreadsheetml.sheet')
49         timestamp = timezone.now().strftime("%Y-%m-%d")
50         form_title = self.object.title.replace(' ', '_')
51         submission_pk = self.object.get_submission_class().objects.last().pk
52         filename = '{}_{}_{}.xlsx'.format(timestamp, form_title, submission_pk)
53         mime_doc.add_header(
54             'Content-Disposition',
55             'attachment; filename="{}"'.format(filename)
56         )
57         return attachments + [mime_doc]
58 
59 
60 class TextFormEmail(FormEmail):
61 
62     def get_attachments(self):
63         attachments = super().get_attachments()
64         text = ''
65         for field, value in self.object.field_values.items():
66             text += '{}:\n{}\n\n'.format(field, value)
67         mime_doc = MIMEText(_text=text, _charset='utf-8')
68         return attachments + [mime_doc]
69
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/meinberlin/apps/cms/emails.py b/meinberlin/apps/cms/emails.py
--- a/meinberlin/apps/cms/emails.py
+++ b/meinberlin/apps/cms/emails.py
@@ -6,15 +6,14 @@
 from django.utils import timezone
 from django.utils.translation import ugettext as _
 
-from adhocracy4.emails.mixins import SyncEmailMixin
 from meinberlin.apps.contrib.emails import Email
 
 
-class FormEmail(SyncEmailMixin, Email):
+class FormEmail(Email):
     template_name = 'meinberlin_cms/emails/form_submission'
 
     def get_receivers(self):
-        return [x.strip() for x in self.object.to_address.split(',')]
+        return [x.strip() for x in self.kwargs.get('to_addresses')]
 
 
 class XlsxFormEmail(FormEmail):
@@ -27,8 +26,8 @@
         worksheet.write(0, 0, _('Form Field'))
         worksheet.write(0, 1, _('Response'))
 
-        rows = self.object.field_values.items()
-        for rownum, row in enumerate(rows, start=1):
+        field_values = self.kwargs.get('field_values')
+        for rownum, row in enumerate(field_values.items(), start=1):
             worksheet.write(rownum, 0, row[0])
             worksheet.write(rownum, 1, self._fix_newline_if_string(row[1]))
 
@@ -47,8 +46,8 @@
             _data=xlsx_data,
             _subtype='vnd.openxmlformats-officedocument.spreadsheetml.sheet')
         timestamp = timezone.now().strftime("%Y-%m-%d")
-        form_title = self.object.title.replace(' ', '_')
-        submission_pk = self.object.get_submission_class().objects.last().pk
+        form_title = self.kwargs.get('title')
+        submission_pk = self.kwargs.get('submission_pk')
         filename = '{}_{}_{}.xlsx'.format(timestamp, form_title, submission_pk)
         mime_doc.add_header(
             'Content-Disposition',
@@ -62,7 +61,8 @@
     def get_attachments(self):
         attachments = super().get_attachments()
         text = ''
-        for field, value in self.object.field_values.items():
+        field_values = self.kwargs.get('field_values')
+        for field, value in field_values.items():
             text += '{}:\n{}\n\n'.format(field, value)
         mime_doc = MIMEText(_text=text, _charset='utf-8')
         return attachments + [mime_doc]
diff --git a/meinberlin/apps/cms/models.py b/meinberlin/apps/cms/models.py
--- a/meinberlin/apps/cms/models.py
+++ b/meinberlin/apps/cms/models.py
@@ -130,16 +130,20 @@
     ]
 
     def send_mail(self, form):
-        self.form = form
+        kwargs = {
+            'title': self.title.replace(' ', '_'),
+            'to_addresses': self.to_address.split(','),
+            'field_values': self.get_field_values(form),
+            'submission_pk': self.get_submission_class().objects.last().pk
+        }
         if self.attach_as == 'xls':
-            emails.XlsxFormEmail.send(self)
+            emails.XlsxFormEmail.send(self, **kwargs)
         elif self.attach_as == 'txt':
-            emails.TextFormEmail.send(self)
+            emails.TextFormEmail.send(self, **kwargs)
 
-    @property
-    def field_values(self):
+    def get_field_values(self, form):
         fields = {}
-        for field in self.form:
+        for field in form:
             value = field.value()
             if isinstance(value, list):
                 value = ', '.join(value)

{"golden_diff": "diff --git a/meinberlin/apps/cms/emails.py b/meinberlin/apps/cms/emails.py\n--- a/meinberlin/apps/cms/emails.py\n+++ b/meinberlin/apps/cms/emails.py\n@@ -6,15 +6,14 @@\n from django.utils import timezone\n from django.utils.translation import ugettext as _\n \n-from adhocracy4.emails.mixins import SyncEmailMixin\n from meinberlin.apps.contrib.emails import Email\n \n \n-class FormEmail(SyncEmailMixin, Email):\n+class FormEmail(Email):\n     template_name = 'meinberlin_cms/emails/form_submission'\n \n     def get_receivers(self):\n-        return [x.strip() for x in self.object.to_address.split(',')]\n+        return [x.strip() for x in self.kwargs.get('to_addresses')]\n \n \n class XlsxFormEmail(FormEmail):\n@@ -27,8 +26,8 @@\n         worksheet.write(0, 0, _('Form Field'))\n         worksheet.write(0, 1, _('Response'))\n \n-        rows = self.object.field_values.items()\n-        for rownum, row in enumerate(rows, start=1):\n+        field_values = self.kwargs.get('field_values')\n+        for rownum, row in enumerate(field_values.items(), start=1):\n             worksheet.write(rownum, 0, row[0])\n             worksheet.write(rownum, 1, self._fix_newline_if_string(row[1]))\n \n@@ -47,8 +46,8 @@\n             _data=xlsx_data,\n             _subtype='vnd.openxmlformats-officedocument.spreadsheetml.sheet')\n         timestamp = timezone.now().strftime(\"%Y-%m-%d\")\n-        form_title = self.object.title.replace(' ', '_')\n-        submission_pk = self.object.get_submission_class().objects.last().pk\n+        form_title = self.kwargs.get('title')\n+        submission_pk = self.kwargs.get('submission_pk')\n         filename = '{}_{}_{}.xlsx'.format(timestamp, form_title, submission_pk)\n         mime_doc.add_header(\n             'Content-Disposition',\n@@ -62,7 +61,8 @@\n     def get_attachments(self):\n         attachments = super().get_attachments()\n         text = ''\n-        for field, value in self.object.field_values.items():\n+        field_values = self.kwargs.get('field_values')\n+        for field, value in field_values.items():\n             text += '{}:\\n{}\\n\\n'.format(field, value)\n         mime_doc = MIMEText(_text=text, _charset='utf-8')\n         return attachments + [mime_doc]\ndiff --git a/meinberlin/apps/cms/models.py b/meinberlin/apps/cms/models.py\n--- a/meinberlin/apps/cms/models.py\n+++ b/meinberlin/apps/cms/models.py\n@@ -130,16 +130,20 @@\n     ]\n \n     def send_mail(self, form):\n-        self.form = form\n+        kwargs = {\n+            'title': self.title.replace(' ', '_'),\n+            'to_addresses': self.to_address.split(','),\n+            'field_values': self.get_field_values(form),\n+            'submission_pk': self.get_submission_class().objects.last().pk\n+        }\n         if self.attach_as == 'xls':\n-            emails.XlsxFormEmail.send(self)\n+            emails.XlsxFormEmail.send(self, **kwargs)\n         elif self.attach_as == 'txt':\n-            emails.TextFormEmail.send(self)\n+            emails.TextFormEmail.send(self, **kwargs)\n \n-    @property\n-    def field_values(self):\n+    def get_field_values(self, form):\n         fields = {}\n-        for field in self.form:\n+        for field in form:\n             value = field.value()\n             if isinstance(value, list):\n                 value = ', '.join(value)\n", "issue": "Send wagtail form emails asynchronously \nCurrently wagtail form emails are sent synchronously because the `send` attributes are not serializable. see #671 for the temporary fix.\r\nRather then sending sync we should fix the problem and provide the required data as a serializable type\n", "before_files": [{"content": "from django.db import models\nfrom modelcluster.fields import ParentalKey\nfrom modelcluster.models import ClusterableModel\nfrom wagtail.wagtailadmin import edit_handlers\nfrom wagtail.wagtailcore import blocks\nfrom wagtail.wagtailcore import fields\nfrom wagtail.wagtailcore.models import Orderable\nfrom wagtail.wagtailcore.models import Page\nfrom wagtail.wagtailforms.models import AbstractEmailForm\nfrom wagtail.wagtailforms.models import AbstractFormField\nfrom wagtail.wagtailimages.edit_handlers import ImageChooserPanel\nfrom wagtail.wagtailsnippets.models import register_snippet\n\nfrom meinberlin.apps.actions import blocks as actions_blocks\n\nfrom . import blocks as cms_blocks\nfrom . import emails\n\n\nclass SimplePage(Page):\n    body = fields.RichTextField(blank=True)\n\n    content_panels = [\n        edit_handlers.FieldPanel('title'),\n        edit_handlers.FieldPanel('body'),\n    ]\n\n    subpage_types = []\n\n\nclass HomePage(Page):\n    body = fields.StreamField([\n        ('paragraph', blocks.RichTextBlock(\n            template='meinberlin_cms/blocks/richtext_block.html'\n        )),\n        ('call_to_action', cms_blocks.CallToActionBlock()),\n        ('columns_text', cms_blocks.ColumnsBlock()),\n        ('projects', cms_blocks.ProjectsWrapperBlock()),\n        ('activities', actions_blocks.PlatformActivityBlock()),\n    ])\n\n    subtitle = models.CharField(max_length=120)\n\n    header_image = models.ForeignKey(\n        'wagtailimages.Image',\n        null=True,\n        blank=False,\n        on_delete=models.SET_NULL,\n        related_name='+'\n    )\n\n    content_panels = Page.content_panels + [\n        edit_handlers.FieldPanel('subtitle'),\n        ImageChooserPanel('header_image'),\n        edit_handlers.StreamFieldPanel('body'),\n    ]\n\n\nclass MenuItem(models.Model):\n    title = models.CharField(max_length=255)\n    link_page = models.ForeignKey('wagtailcore.Page')\n\n    @property\n    def url(self):\n        return self.link_page.url\n\n    def __str__(self):\n        return self.title\n\n    panels = [\n        edit_handlers.FieldPanel('title'),\n        edit_handlers.PageChooserPanel('link_page')\n    ]\n\n\n@register_snippet\nclass NavigationMenu(ClusterableModel):\n    title = models.CharField(max_length=255, null=False, blank=False)\n\n    def __str__(self):\n        return self.title\n\n    panels = [\n        edit_handlers.FieldPanel('title'),\n        edit_handlers.InlinePanel('items')\n    ]\n\n\nclass NavigationMenuItem(Orderable, MenuItem):\n    parent = ParentalKey('meinberlin_cms.NavigationMenu', related_name='items')\n\n\nclass EmailFormField(AbstractFormField):\n    page = ParentalKey('EmailFormPage', related_name='form_fields')\n\n\nclass EmailFormPage(AbstractEmailForm):\n    intro = fields.RichTextField(\n        help_text='Introduction text shown above the form'\n    )\n    thank_you = fields.RichTextField(\n        help_text='Text shown after form submission',\n    )\n    email_content = models.CharField(\n        max_length=200,\n        help_text='Email content message',\n    )\n    attach_as = models.CharField(\n        max_length=3,\n        choices=(\n            ('xls', 'XLSX Document'),\n            ('txt', 'Text'),\n        ),\n        default='xls',\n        help_text='Form results are send in this document format',\n    )\n\n    content_panels = AbstractEmailForm.content_panels + [\n        edit_handlers.MultiFieldPanel([\n            edit_handlers.FieldPanel('intro', classname='full'),\n            edit_handlers.FieldPanel('thank_you', classname='full'),\n        ], 'Page'),\n        edit_handlers.MultiFieldPanel([\n            edit_handlers.FieldPanel('to_address'),\n            edit_handlers.FieldPanel('subject'),\n            edit_handlers.FieldPanel('email_content', classname='full'),\n            edit_handlers.FieldPanel('attach_as'),\n        ], 'Email'),\n        edit_handlers.InlinePanel('form_fields', label='Form fields'),\n    ]\n\n    def send_mail(self, form):\n        self.form = form\n        if self.attach_as == 'xls':\n            emails.XlsxFormEmail.send(self)\n        elif self.attach_as == 'txt':\n            emails.TextFormEmail.send(self)\n\n    @property\n    def field_values(self):\n        fields = {}\n        for field in self.form:\n            value = field.value()\n            if isinstance(value, list):\n                value = ', '.join(value)\n            fields[field.label] = value\n        return fields\n", "path": "meinberlin/apps/cms/models.py"}, {"content": "import io\nfrom email.mime.application import MIMEApplication\nfrom email.mime.text import MIMEText\n\nimport xlsxwriter\nfrom django.utils import timezone\nfrom django.utils.translation import ugettext as _\n\nfrom adhocracy4.emails.mixins import SyncEmailMixin\nfrom meinberlin.apps.contrib.emails import Email\n\n\nclass FormEmail(SyncEmailMixin, Email):\n    template_name = 'meinberlin_cms/emails/form_submission'\n\n    def get_receivers(self):\n        return [x.strip() for x in self.object.to_address.split(',')]\n\n\nclass XlsxFormEmail(FormEmail):\n\n    def _generate_xlsx(self):\n        stream = io.BytesIO()\n        workbook = xlsxwriter.Workbook(stream, {'in_memory': True})\n        worksheet = workbook.add_worksheet()\n\n        worksheet.write(0, 0, _('Form Field'))\n        worksheet.write(0, 1, _('Response'))\n\n        rows = self.object.field_values.items()\n        for rownum, row in enumerate(rows, start=1):\n            worksheet.write(rownum, 0, row[0])\n            worksheet.write(rownum, 1, self._fix_newline_if_string(row[1]))\n\n        workbook.close()\n        return stream.getvalue()\n\n    def _fix_newline_if_string(self, value):\n        if isinstance(value, str):\n            return value.replace('\\r', '')\n        return value\n\n    def get_attachments(self):\n        attachments = super().get_attachments()\n        xlsx_data = self._generate_xlsx()\n        mime_doc = MIMEApplication(\n            _data=xlsx_data,\n            _subtype='vnd.openxmlformats-officedocument.spreadsheetml.sheet')\n        timestamp = timezone.now().strftime(\"%Y-%m-%d\")\n        form_title = self.object.title.replace(' ', '_')\n        submission_pk = self.object.get_submission_class().objects.last().pk\n        filename = '{}_{}_{}.xlsx'.format(timestamp, form_title, submission_pk)\n        mime_doc.add_header(\n            'Content-Disposition',\n            'attachment; filename=\"{}\"'.format(filename)\n        )\n        return attachments + [mime_doc]\n\n\nclass TextFormEmail(FormEmail):\n\n    def get_attachments(self):\n        attachments = super().get_attachments()\n        text = ''\n        for field, value in self.object.field_values.items():\n            text += '{}:\\n{}\\n\\n'.format(field, value)\n        mime_doc = MIMEText(_text=text, _charset='utf-8')\n        return attachments + [mime_doc]\n", "path": "meinberlin/apps/cms/emails.py"}], "after_files": [{"content": "from django.db import models\nfrom modelcluster.fields import ParentalKey\nfrom modelcluster.models import ClusterableModel\nfrom wagtail.wagtailadmin import edit_handlers\nfrom wagtail.wagtailcore import blocks\nfrom wagtail.wagtailcore import fields\nfrom wagtail.wagtailcore.models import Orderable\nfrom wagtail.wagtailcore.models import Page\nfrom wagtail.wagtailforms.models import AbstractEmailForm\nfrom wagtail.wagtailforms.models import AbstractFormField\nfrom wagtail.wagtailimages.edit_handlers import ImageChooserPanel\nfrom wagtail.wagtailsnippets.models import register_snippet\n\nfrom meinberlin.apps.actions import blocks as actions_blocks\n\nfrom . import blocks as cms_blocks\nfrom . import emails\n\n\nclass SimplePage(Page):\n    body = fields.RichTextField(blank=True)\n\n    content_panels = [\n        edit_handlers.FieldPanel('title'),\n        edit_handlers.FieldPanel('body'),\n    ]\n\n    subpage_types = []\n\n\nclass HomePage(Page):\n    body = fields.StreamField([\n        ('paragraph', blocks.RichTextBlock(\n            template='meinberlin_cms/blocks/richtext_block.html'\n        )),\n        ('call_to_action', cms_blocks.CallToActionBlock()),\n        ('columns_text', cms_blocks.ColumnsBlock()),\n        ('projects', cms_blocks.ProjectsWrapperBlock()),\n        ('activities', actions_blocks.PlatformActivityBlock()),\n    ])\n\n    subtitle = models.CharField(max_length=120)\n\n    header_image = models.ForeignKey(\n        'wagtailimages.Image',\n        null=True,\n        blank=False,\n        on_delete=models.SET_NULL,\n        related_name='+'\n    )\n\n    content_panels = Page.content_panels + [\n        edit_handlers.FieldPanel('subtitle'),\n        ImageChooserPanel('header_image'),\n        edit_handlers.StreamFieldPanel('body'),\n    ]\n\n\nclass MenuItem(models.Model):\n    title = models.CharField(max_length=255)\n    link_page = models.ForeignKey('wagtailcore.Page')\n\n    @property\n    def url(self):\n        return self.link_page.url\n\n    def __str__(self):\n        return self.title\n\n    panels = [\n        edit_handlers.FieldPanel('title'),\n        edit_handlers.PageChooserPanel('link_page')\n    ]\n\n\n@register_snippet\nclass NavigationMenu(ClusterableModel):\n    title = models.CharField(max_length=255, null=False, blank=False)\n\n    def __str__(self):\n        return self.title\n\n    panels = [\n        edit_handlers.FieldPanel('title'),\n        edit_handlers.InlinePanel('items')\n    ]\n\n\nclass NavigationMenuItem(Orderable, MenuItem):\n    parent = ParentalKey('meinberlin_cms.NavigationMenu', related_name='items')\n\n\nclass EmailFormField(AbstractFormField):\n    page = ParentalKey('EmailFormPage', related_name='form_fields')\n\n\nclass EmailFormPage(AbstractEmailForm):\n    intro = fields.RichTextField(\n        help_text='Introduction text shown above the form'\n    )\n    thank_you = fields.RichTextField(\n        help_text='Text shown after form submission',\n    )\n    email_content = models.CharField(\n        max_length=200,\n        help_text='Email content message',\n    )\n    attach_as = models.CharField(\n        max_length=3,\n        choices=(\n            ('xls', 'XLSX Document'),\n            ('txt', 'Text'),\n        ),\n        default='xls',\n        help_text='Form results are send in this document format',\n    )\n\n    content_panels = AbstractEmailForm.content_panels + [\n        edit_handlers.MultiFieldPanel([\n            edit_handlers.FieldPanel('intro', classname='full'),\n            edit_handlers.FieldPanel('thank_you', classname='full'),\n        ], 'Page'),\n        edit_handlers.MultiFieldPanel([\n            edit_handlers.FieldPanel('to_address'),\n            edit_handlers.FieldPanel('subject'),\n            edit_handlers.FieldPanel('email_content', classname='full'),\n            edit_handlers.FieldPanel('attach_as'),\n        ], 'Email'),\n        edit_handlers.InlinePanel('form_fields', label='Form fields'),\n    ]\n\n    def send_mail(self, form):\n        kwargs = {\n            'title': self.title.replace(' ', '_'),\n            'to_addresses': self.to_address.split(','),\n            'field_values': self.get_field_values(form),\n            'submission_pk': self.get_submission_class().objects.last().pk\n        }\n        if self.attach_as == 'xls':\n            emails.XlsxFormEmail.send(self, **kwargs)\n        elif self.attach_as == 'txt':\n            emails.TextFormEmail.send(self, **kwargs)\n\n    def get_field_values(self, form):\n        fields = {}\n        for field in form:\n            value = field.value()\n            if isinstance(value, list):\n                value = ', '.join(value)\n            fields[field.label] = value\n        return fields\n", "path": "meinberlin/apps/cms/models.py"}, {"content": "import io\nfrom email.mime.application import MIMEApplication\nfrom email.mime.text import MIMEText\n\nimport xlsxwriter\nfrom django.utils import timezone\nfrom django.utils.translation import ugettext as _\n\nfrom meinberlin.apps.contrib.emails import Email\n\n\nclass FormEmail(Email):\n    template_name = 'meinberlin_cms/emails/form_submission'\n\n    def get_receivers(self):\n        return [x.strip() for x in self.kwargs.get('to_addresses')]\n\n\nclass XlsxFormEmail(FormEmail):\n\n    def _generate_xlsx(self):\n        stream = io.BytesIO()\n        workbook = xlsxwriter.Workbook(stream, {'in_memory': True})\n        worksheet = workbook.add_worksheet()\n\n        worksheet.write(0, 0, _('Form Field'))\n        worksheet.write(0, 1, _('Response'))\n\n        field_values = self.kwargs.get('field_values')\n        for rownum, row in enumerate(field_values.items(), start=1):\n            worksheet.write(rownum, 0, row[0])\n            worksheet.write(rownum, 1, self._fix_newline_if_string(row[1]))\n\n        workbook.close()\n        return stream.getvalue()\n\n    def _fix_newline_if_string(self, value):\n        if isinstance(value, str):\n            return value.replace('\\r', '')\n        return value\n\n    def get_attachments(self):\n        attachments = super().get_attachments()\n        xlsx_data = self._generate_xlsx()\n        mime_doc = MIMEApplication(\n            _data=xlsx_data,\n            _subtype='vnd.openxmlformats-officedocument.spreadsheetml.sheet')\n        timestamp = timezone.now().strftime(\"%Y-%m-%d\")\n        form_title = self.kwargs.get('title')\n        submission_pk = self.kwargs.get('submission_pk')\n        filename = '{}_{}_{}.xlsx'.format(timestamp, form_title, submission_pk)\n        mime_doc.add_header(\n            'Content-Disposition',\n            'attachment; filename=\"{}\"'.format(filename)\n        )\n        return attachments + [mime_doc]\n\n\nclass TextFormEmail(FormEmail):\n\n    def get_attachments(self):\n        attachments = super().get_attachments()\n        text = ''\n        field_values = self.kwargs.get('field_values')\n        for field, value in field_values.items():\n            text += '{}:\\n{}\\n\\n'.format(field, value)\n        mime_doc = MIMEText(_text=text, _charset='utf-8')\n        return attachments + [mime_doc]\n", "path": "meinberlin/apps/cms/emails.py"}]}