2020-01-05 12:37:51 +00:00
|
|
|
|
import swapper
|
2019-03-27 15:49:14 +00:00
|
|
|
|
from django import forms
|
2019-12-31 12:05:12 +00:00
|
|
|
|
from django.contrib.contenttypes.models import ContentType
|
2020-01-06 11:51:10 +00:00
|
|
|
|
from django.core.mail import EmailMessage
|
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
2019-12-31 12:05:12 +00:00
|
|
|
|
|
2020-01-02 00:56:15 +00:00
|
|
|
|
Page = swapper.load_model('cms', 'Page')
|
2019-12-31 12:05:12 +00:00
|
|
|
|
Section = swapper.load_model('cms', 'Section')
|
2019-03-27 15:49:14 +00:00
|
|
|
|
|
|
|
|
|
class PageForm(forms.ModelForm):
|
2020-03-10 22:55:46 +00:00
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
2020-03-11 10:48:37 +00:00
|
|
|
|
extra = 1 if self.instance.pk else 2
|
|
|
|
|
self.formsets = [forms.inlineformset_factory(
|
|
|
|
|
parent_model = Page,
|
|
|
|
|
model = Section,
|
|
|
|
|
form = SectionForm,
|
|
|
|
|
formset = BaseSectionFormSet,
|
|
|
|
|
extra=extra,
|
|
|
|
|
)(
|
2020-03-10 22:55:46 +00:00
|
|
|
|
data=self.data if self.is_bound else None,
|
|
|
|
|
files=self.files if self.is_bound else None,
|
|
|
|
|
instance=self.instance,
|
|
|
|
|
form_kwargs={'label_suffix': self.label_suffix},
|
|
|
|
|
)]
|
2020-03-11 10:48:37 +00:00
|
|
|
|
if not self.instance.pk:
|
|
|
|
|
self.formsets[0][0].empty_permitted = False
|
2020-03-10 22:55:46 +00:00
|
|
|
|
|
|
|
|
|
def is_valid(self):
|
|
|
|
|
return super().is_valid() and self.formsets[0].is_valid()
|
|
|
|
|
|
|
|
|
|
def clean(self):
|
|
|
|
|
super().clean()
|
|
|
|
|
if not self.formsets[0].is_valid():
|
|
|
|
|
self.add_error(None, _('There’s a problem saving one of the sections'))
|
|
|
|
|
if not self.instance and not self.formsets[0].has_changed():
|
|
|
|
|
self.add_error(None, _('You can’t save a new page without adding any sections!'))
|
|
|
|
|
|
|
|
|
|
def save(self, *args, **kwargs):
|
|
|
|
|
page = super().save()
|
|
|
|
|
formset = self.formsets[0]
|
|
|
|
|
formset.instance = page
|
|
|
|
|
formset.save()
|
|
|
|
|
if page.slug and not page.sections.exists():
|
|
|
|
|
page.delete()
|
|
|
|
|
return None
|
|
|
|
|
return page
|
|
|
|
|
|
2019-03-27 15:49:14 +00:00
|
|
|
|
class Meta:
|
|
|
|
|
model = Page
|
|
|
|
|
fields = '__all__'
|
|
|
|
|
|
2019-08-23 15:19:40 +00:00
|
|
|
|
class SectionForm(forms.ModelForm):
|
2020-01-02 00:56:15 +00:00
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
# Repopulate the 'choices' attribute of the type field from
|
|
|
|
|
# the child model.
|
|
|
|
|
self.fields['type'].choices = self._meta.model.TYPES
|
2020-02-14 16:20:41 +00:00
|
|
|
|
self.fields['type'].initial = self._meta.model.TYPES[0][0]
|
2020-01-02 00:56:15 +00:00
|
|
|
|
|
2020-02-14 16:20:41 +00:00
|
|
|
|
def delete(self):
|
|
|
|
|
instance = super().save()
|
|
|
|
|
instance.delete()
|
|
|
|
|
|
|
|
|
|
def save(self, commit=True):
|
2019-12-31 12:05:12 +00:00
|
|
|
|
section = super().save()
|
2020-01-05 12:37:51 +00:00
|
|
|
|
|
|
|
|
|
# Explanation: get the content type of the model that the user
|
|
|
|
|
# supplied when filling in this form, and save it's id to the
|
|
|
|
|
# 'polymorphic_ctype_id' field. The next time the object is
|
|
|
|
|
# requested from the database, django-polymorphic will convert
|
|
|
|
|
# it to the correct subclass.
|
2019-12-31 12:05:12 +00:00
|
|
|
|
section.polymorphic_ctype = ContentType.objects.get(
|
|
|
|
|
app_label=section._meta.app_label,
|
|
|
|
|
model=section.type.lower(),
|
|
|
|
|
)
|
|
|
|
|
|
2020-02-14 16:20:41 +00:00
|
|
|
|
if commit:
|
|
|
|
|
section.save()
|
2019-12-31 12:05:12 +00:00
|
|
|
|
return section
|
|
|
|
|
|
2019-08-23 15:19:40 +00:00
|
|
|
|
class Meta:
|
|
|
|
|
model = Section
|
|
|
|
|
exclude = ['page']
|
2020-01-05 12:37:51 +00:00
|
|
|
|
#field_classes = {
|
|
|
|
|
# 'type': forms.ChoiceField,
|
|
|
|
|
#}
|
|
|
|
|
|
|
|
|
|
# There is definitely a bug in Django, since the above 'field_classes' gets
|
|
|
|
|
# ignored entirely. Workaround to force a ChoiceField anyway:
|
|
|
|
|
type = forms.ChoiceField()
|
2020-02-14 16:20:41 +00:00
|
|
|
|
|
2020-03-08 21:19:39 +00:00
|
|
|
|
class BaseSectionFormSet(forms.BaseInlineFormSet):
|
2020-03-09 17:24:39 +00:00
|
|
|
|
'''If a swappable Section model defines one-to-many fields, (i.e. has
|
|
|
|
|
foreign keys pointing to it) formsets will be generated for the
|
|
|
|
|
related models and stored in the form.formsets array.
|
2020-03-08 21:19:39 +00:00
|
|
|
|
|
2020-03-09 17:24:39 +00:00
|
|
|
|
Based on this logic for nested formsets:
|
|
|
|
|
https://www.yergler.net/2013/09/03/nested-formsets-redux/
|
2020-03-08 21:19:39 +00:00
|
|
|
|
|
|
|
|
|
Typical usecases could be:
|
|
|
|
|
- an images section that displays multiple images
|
|
|
|
|
- a column section that displays separate colums
|
|
|
|
|
- a calendar section that displays calendar events
|
|
|
|
|
- etc...
|
|
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
def add_fields(self, form, index):
|
|
|
|
|
super().add_fields(form, index)
|
|
|
|
|
section = form.instance
|
2020-03-09 17:24:39 +00:00
|
|
|
|
form.formsets = []
|
|
|
|
|
for field in section._meta.get_fields():
|
|
|
|
|
if field.one_to_many:
|
2020-03-11 10:48:37 +00:00
|
|
|
|
extra = 1 if getattr(section, field.name).exists() else 2
|
|
|
|
|
|
|
|
|
|
formset = forms.inlineformset_factory(
|
|
|
|
|
Section, field.related_model,
|
|
|
|
|
fields='__all__',
|
|
|
|
|
extra=extra,
|
|
|
|
|
)(
|
2020-03-09 17:24:39 +00:00
|
|
|
|
instance=section,
|
|
|
|
|
data=form.data if self.is_bound else None,
|
|
|
|
|
files=form.files if self.is_bound else None,
|
2020-03-10 22:55:46 +00:00
|
|
|
|
prefix=f'{form.prefix}-{field.name}',
|
|
|
|
|
form_kwargs=self.form_kwargs,
|
|
|
|
|
)
|
2020-03-09 17:24:39 +00:00
|
|
|
|
formset.name = field.name
|
|
|
|
|
form.formsets.append(formset)
|
2020-03-08 21:19:39 +00:00
|
|
|
|
|
|
|
|
|
def is_valid(self):
|
|
|
|
|
result = super().is_valid()
|
|
|
|
|
if self.is_bound:
|
|
|
|
|
for form in self.forms:
|
2020-03-09 17:24:39 +00:00
|
|
|
|
for formset in form.formsets:
|
|
|
|
|
result = result and formset.is_valid()
|
2020-03-08 21:19:39 +00:00
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def save(self, commit=True):
|
|
|
|
|
result = super().save(commit=commit)
|
|
|
|
|
for form in self:
|
2020-03-10 22:55:46 +00:00
|
|
|
|
for formset in form.formsets:
|
|
|
|
|
formset.save(commit=commit)
|
2020-03-08 21:19:39 +00:00
|
|
|
|
return result
|
|
|
|
|
|
2020-03-11 10:48:37 +00:00
|
|
|
|
class ContactForm(forms.Form):
|
|
|
|
|
sender = forms.EmailField(label=_('Your email address'))
|
|
|
|
|
spam_protection = forms.CharField(label=_('Your message'), widget=forms.Textarea())
|
|
|
|
|
message = forms.CharField(label=_('Your message'), widget=forms.Textarea(), initial='Hi there!')
|
|
|
|
|
|
|
|
|
|
def save(self, request):
|
|
|
|
|
hostname = request.get_host()
|
|
|
|
|
body = self.cleaned_data.get('spam_protection')
|
|
|
|
|
if len(body.split()) < 7:
|
|
|
|
|
return
|
|
|
|
|
spamcheck = self.cleaned_data.get('message')
|
|
|
|
|
if spamcheck != 'Hi there!':
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
email = EmailMessage(
|
|
|
|
|
to = ['info@' + hostname],
|
|
|
|
|
from_email = 'noreply@' + hostname,
|
|
|
|
|
body = body,
|
|
|
|
|
subject = _('Contact form at %(hostname)s.') % {'hostname': hostname},
|
|
|
|
|
headers = {'Reply-To': self.cleaned_data.get('sender')},
|
|
|
|
|
)
|
|
|
|
|
email.send()
|