From 1e56d8964c491d8936670a92acf5b5f6730a6948 Mon Sep 17 00:00:00 2001 From: Jan Szejko Date: Tue, 4 Dec 2018 13:25:39 +0100 Subject: [PATCH] add update forms --- contact/forms.py | 101 ++++++++++++++++++++++++++++++---------------- contact/models.py | 8 ++++ contact/urls.py | 1 + contact/views.py | 46 +++++++++++++-------- 4 files changed, 104 insertions(+), 52 deletions(-) diff --git a/contact/forms.py b/contact/forms.py index 019b38e..73be1db 100644 --- a/contact/forms.py +++ b/contact/forms.py @@ -13,6 +13,7 @@ from . import mailing contact_forms = {} +update_forms = {} admin_list_width = 0 @@ -21,10 +22,14 @@ class ContactFormMeta(forms.Form.__class__): global admin_list_width model = super(ContactFormMeta, cls).__new__(cls, name, bases, attrs) if model.form_tag: - assert model.form_tag not in contact_forms, 'Duplicate form_tag.' - if model.admin_list: - admin_list_width = max(admin_list_width, len(model.admin_list)) - contact_forms[model.form_tag] = model + if model.form_type == 'create': + assert model.form_tag not in contact_forms, 'Duplicate form_tag.' + if model.admin_list: + admin_list_width = max(admin_list_width, len(model.admin_list)) + contact_forms[model.form_tag] = model + elif model.form_type == 'update': + assert model.form_tag not in update_forms, 'Duplicate form_tag.' + update_forms[model.form_tag] = model return model @@ -33,6 +38,7 @@ class ContactForm(forms.Form): __metaclass__ = ContactFormMeta form_tag = None + form_type = 'create' old_form_tags = [] form_title = _('Contact form') submit_label = _('Submit') @@ -45,6 +51,26 @@ class ContactForm(forms.Form): required_css_class = 'required' contact = NotImplemented + def __init__(self, *args, **kwargs): + self.instance = kwargs.pop('instance', None) + super(ContactForm, self).__init__(*args, **kwargs) + if not self.is_bound and self.instance: + # files are omitted (not necessary for now) + self.fields['contact'].initial = self.instance.contact + body = self.instance.body + for field, value in body.iteritems(): + if field in self.fields: + self.fields[field].initial = value + + def get_dictionary(self, contact): + site = Site.objects.get_current() + return { + 'form_tag': self.form_tag, + 'site_name': getattr(self, 'site_name', site.name), + 'site_domain': getattr(self, 'site_domain', site.domain), + 'contact': contact, + } + def save(self, request, formsets=None): from .models import Attachment, Contact body = {} @@ -61,11 +87,21 @@ class ContactForm(forms.Form): if sub_body: body.setdefault(f.form_tag, []).append(sub_body) - contact = Contact.objects.create( - body=body, - ip=request.META['REMOTE_ADDR'], - contact=self.cleaned_data['contact'], - form_tag=self.form_tag) + if self.instance: + contact = self.instance + contact.body = body + email_changed = contact.contact != self.cleaned_data['contact'] + contact.contact = self.cleaned_data['contact'] + assert contact.form_tag == self.form_tag + contact.save() + else: + contact = Contact.objects.create( + body=body, + ip=request.META['REMOTE_ADDR'], + contact=self.cleaned_data['contact'], + form_tag=self.form_tag) + email_changed = True + # not intended to be used with update forms for name, value in self.cleaned_data.items(): if isinstance(value, UploadedFile): attachment = Attachment(contact=contact, tag=name) @@ -73,12 +109,7 @@ class ContactForm(forms.Form): attachment.save() site = Site.objects.get_current() - dictionary = { - 'form_tag': self.form_tag, - 'site_name': getattr(self, 'site_name', site.name), - 'site_domain': getattr(self, 'site_domain', site.domain), - 'contact': contact, - } + dictionary = self.get_dictionary(contact) context = RequestContext(request) mail_managers_subject = render_to_string([ 'contact/%s/mail_managers_subject.txt' % self.form_tag, @@ -95,25 +126,25 @@ class ContactForm(forms.Form): except ValidationError: pass else: - mail_subject = render_to_string([ - 'contact/%s/mail_subject.txt' % self.form_tag, - 'contact/mail_subject.txt', - ], dictionary, context).strip() - if self.result_page: - mail_body = render_to_string( - 'contact/%s/results_email.txt' % contact.form_tag, - { - 'contact': contact, - 'results': self.results(contact), - }, context) - else: - mail_body = render_to_string([ - 'contact/%s/mail_body.txt' % self.form_tag, - 'contact/mail_body.txt', - ], dictionary, context) - send_mail(mail_subject, mail_body, 'no-reply@%s' % site.domain, [contact.contact], fail_silently=True) - if self.mailing or (self.mailing_field and self.cleaned_data[self.mailing_field]): - email = self.cleaned_data['contact'] - mailing.subscribe(email) + if not self.instance: + mail_subject = render_to_string([ + 'contact/%s/mail_subject.txt' % self.form_tag, + 'contact/mail_subject.txt', + ], dictionary, context).strip() + if self.result_page: + mail_body = render_to_string( + 'contact/%s/results_email.txt' % contact.form_tag, + { + 'contact': contact, + 'results': self.results(contact), + }, context) + else: + mail_body = render_to_string([ + 'contact/%s/mail_body.txt' % self.form_tag, + 'contact/mail_body.txt', + ], dictionary, context) + send_mail(mail_subject, mail_body, 'no-reply@%s' % site.domain, [contact.contact], fail_silently=True) + if email_changed and (self.mailing or (self.mailing_field and self.cleaned_data[self.mailing_field])): + mailing.subscribe(contact.contact) return contact diff --git a/contact/models.py b/contact/models.py index dbc48b9..f07b33a 100644 --- a/contact/models.py +++ b/contact/models.py @@ -3,6 +3,7 @@ import yaml from hashlib import sha1 from django.db import models from django.utils.encoding import smart_unicode, force_str +from django.db.models import permalink from django.utils.translation import ugettext_lazy as _ from jsonfield import JSONField from . import app_settings @@ -37,6 +38,13 @@ class Contact(models.Model): data = force_str(data) return sha1(data).hexdigest() + @permalink + def update_url(self): + from contact.forms import update_forms, contact_forms + form_class = update_forms.get(self.form_tag, contact_forms.get(self.form_tag)) + confirmation = form_class.confirmation_class.objects.get(contact=self) + return 'edit_form', [], {'form_tag': self.form_tag, 'contact_id': self.id, 'key': confirmation.key} + class Attachment(models.Model): contact = models.ForeignKey(Contact) diff --git a/contact/urls.py b/contact/urls.py index f2ef944..b0866ed 100644 --- a/contact/urls.py +++ b/contact/urls.py @@ -5,6 +5,7 @@ from . import views urlpatterns = patterns( 'contact.views', url(r'^(?P[^/]+)/$', views.form, name='contact_form'), + url(r'^(?P[^/]+)/edit/(?P[0-9]+)/(?P[0-9a-zA-Z]+)/$', views.form, name='edit_form'), url(r'^(?P[^/]+)/thanks/$', views.thanks, name='contact_thanks'), url(r'^attachment/(?P\d+)/(?P[^/]+)/$', views.attachment, name='contact_attachment'), url(r'^results/(?P\d+)/(?P[0-9a-f]+)/', views.results, name='contact_results'), diff --git a/contact/views.py b/contact/views.py index 82e0347..0316b20 100644 --- a/contact/views.py +++ b/contact/views.py @@ -4,34 +4,46 @@ from urllib import unquote from django.contrib.auth.decorators import permission_required from django.http import Http404 from django.shortcuts import get_object_or_404, redirect, render +from django.views.decorators.cache import never_cache from fnpdjango.utils.views import serve_file from honeypot.decorators import check_honeypot -from .forms import contact_forms +from .forms import contact_forms, update_forms from .models import Attachment, Contact @check_honeypot -def form(request, form_tag, force_enabled=False): +@never_cache +def form(request, form_tag, force_enabled=False, contact_id=None, key=None): + update = bool(contact_id and key) try: - form_class = contact_forms[form_tag] + if update and form_tag in update_forms: + form_class = update_forms[form_tag] + else: + form_class = contact_forms[form_tag] except KeyError: raise Http404 - if (getattr(form_class, 'disabled', False) and - not (force_enabled and request.user.is_superuser)): - template = getattr(form_class, 'disabled_template', None) - if template: - return render(request, template, {'title': form_class.form_title}) - raise Http404 + if not (force_enabled and request.user.is_superuser): + disabled = getattr(form_class, 'disabled', False) + if disabled: + template = getattr(form_class, 'disabled_template', None) + if template: + return render(request, template, {'title': form_class.form_title}) + raise Http404 + if contact_id: + contact = get_object_or_404(Contact, id=contact_id, form_tag=form_tag) + if form_tag != 'olimpiada': + raise Http404 + if key != contact.key: + raise Http404 + else: + contact = None if request.method == 'POST': - form = form_class(request.POST, request.FILES) + form = form_class(request.POST, request.FILES, instance=contact) else: - form = form_class(initial=request.GET) - formset_classes = getattr(form, 'form_formsets', {}) + form = form_class(initial=request.GET, instance=contact) if request.method == 'POST': - formsets = { - prefix: formset_class(request.POST, request.FILES, prefix=prefix) - for prefix, formset_class in formset_classes.iteritems()} + formsets = form.get_formsets(request) if form.is_valid() and all(formset.is_valid() for formset in formsets.itervalues()): contact = form.save(request, formsets.values()) if form.result_page: @@ -39,11 +51,11 @@ def form(request, form_tag, force_enabled=False): else: return redirect('contact_thanks', form_tag) else: - formsets = {prefix: formset_class(prefix=prefix) for prefix, formset_class in formset_classes.iteritems()} + formsets = form.get_formsets() return render( request, ['contact/%s/form.html' % form_tag, 'contact/form.html'], - {'form': form, 'formsets': formsets} + {'form': form, 'formsets': formsets, 'formset_errors': any(formset.errors for formset in formsets.values())} ) -- 2.20.1