From 789d0c8c843afef49218724fc68ef2fa30fae15d Mon Sep 17 00:00:00 2001 From: Ben Sturmfels Date: Tue, 9 Apr 2024 22:50:06 +1000 Subject: [PATCH] contacts: Remove ContactEntry and add Unsubscription This change removes the unused `ContactEntry` model and the `subscribe` view and replaces it with an `Unsubscription` model and an `unsubscribe` view. It works similarly, but is intended to be used with the `list-unsubscribe` and `list-unsubscribe-post` headers. --- conservancy/contacts/admin.py | 10 ++--- .../contacts/migrations/0001_initial.py | 32 ++++++++++++++++ conservancy/contacts/migrations/__init__.py | 0 conservancy/contacts/models.py | 13 ++----- .../templates/contacts/unsubscribe.html | 10 +++++ .../contacts/unsubscribe_success.html | 6 +++ conservancy/contacts/urls.py | 4 +- conservancy/contacts/views.py | 38 +++++++++++-------- conservancy/settings/base.py | 5 +++ conservancy/urls.py | 1 + 10 files changed, 86 insertions(+), 33 deletions(-) create mode 100644 conservancy/contacts/migrations/0001_initial.py create mode 100644 conservancy/contacts/migrations/__init__.py create mode 100644 conservancy/contacts/templates/contacts/unsubscribe.html create mode 100644 conservancy/contacts/templates/contacts/unsubscribe_success.html diff --git a/conservancy/contacts/admin.py b/conservancy/contacts/admin.py index 94048d4b..f6c68b1c 100644 --- a/conservancy/contacts/admin.py +++ b/conservancy/contacts/admin.py @@ -1,10 +1,8 @@ from django.contrib import admin -from .models import ContactEntry - - -@admin.register(ContactEntry) -class ContactEntryAdmin(admin.ModelAdmin): - list_display = ('email', 'subscribe_conservancy') +from .models import Unsubscription +@admin.register(Unsubscription) +class UnsubscriptionAdmin(admin.ModelAdmin): + list_display = ['created', 'email'] diff --git a/conservancy/contacts/migrations/0001_initial.py b/conservancy/contacts/migrations/0001_initial.py new file mode 100644 index 00000000..e10b2fab --- /dev/null +++ b/conservancy/contacts/migrations/0001_initial.py @@ -0,0 +1,32 @@ +# Generated by Django 4.2.11 on 2024-04-09 08:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name='Unsubscription', + fields=[ + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID', + ), + ), + ('created', models.DateTimeField(auto_now_add=True)), + ('email', models.EmailField(max_length=254)), + ], + options={ + 'ordering': ['created'], + }, + ), + ] diff --git a/conservancy/contacts/migrations/__init__.py b/conservancy/contacts/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/conservancy/contacts/models.py b/conservancy/contacts/models.py index 14ac7484..58e89a75 100644 --- a/conservancy/contacts/models.py +++ b/conservancy/contacts/models.py @@ -1,14 +1,9 @@ from django.db import models -class ContactEntry(models.Model): - """Conservancy contact system - - Hopefully this will be deprecated soon""" - - email = models.EmailField() # should make it unique, but we really cannot - subscribe_conservancy = models.BooleanField(default=False) +class Unsubscription(models.Model): + created = models.DateTimeField(auto_now_add=True, blank=True) + email = models.EmailField() class Meta: - ordering = ('email',) - + ordering = ['created'] diff --git a/conservancy/contacts/templates/contacts/unsubscribe.html b/conservancy/contacts/templates/contacts/unsubscribe.html new file mode 100644 index 00000000..86de5a67 --- /dev/null +++ b/conservancy/contacts/templates/contacts/unsubscribe.html @@ -0,0 +1,10 @@ +{% extends "base_conservancy.html" %} +{% block outercontent %} +
+

Unsubscribe

+
+ {{ form.as_p }} +

+
+
+{% endblock %} diff --git a/conservancy/contacts/templates/contacts/unsubscribe_success.html b/conservancy/contacts/templates/contacts/unsubscribe_success.html new file mode 100644 index 00000000..e4938995 --- /dev/null +++ b/conservancy/contacts/templates/contacts/unsubscribe_success.html @@ -0,0 +1,6 @@ +{% extends "base_conservancy.html" %} +{% block outercontent %} +
+

Unsubscribe successful

+
+{% endblock %} diff --git a/conservancy/contacts/urls.py b/conservancy/contacts/urls.py index 661e6891..74f1c84d 100644 --- a/conservancy/contacts/urls.py +++ b/conservancy/contacts/urls.py @@ -1,7 +1,7 @@ from django.urls import path -from .views import subscribe +from .views import unsubscribe urlpatterns = [ - path('', subscribe), + path('unsubscribe/', unsubscribe), ] diff --git a/conservancy/contacts/views.py b/conservancy/contacts/views.py index 95ccfc09..65ea3445 100644 --- a/conservancy/contacts/views.py +++ b/conservancy/contacts/views.py @@ -1,25 +1,31 @@ +import logging + from django.forms import ModelForm +from django.views.decorators.csrf import csrf_exempt from django.shortcuts import render -from .models import ContactEntry +from .models import Unsubscription + +logger = logging.getLogger(__name__) + +class UnsubscribeForm(ModelForm): + class Meta: + model = Unsubscription + fields = ['email'] -def subscribe(request): - """Mailing list subscription form - """ - - class ContactEntryForm(ModelForm): - class Meta: - model = ContactEntry - - ContactEntryForm.base_fields['subscribe_conservancy'].label = 'Receive Software Freedom Conservancy updates' - +# Exempt from CSRF protection so that it can be triggered by Gmail's on-click +# unsubscribe. +@csrf_exempt +def unsubscribe(request): if request.method == 'POST': - form = ContactEntryForm(request.POST) + logger.debug('Unsubscribe GET: %s', request.GET) + logger.debug('Unsubscribe POST: %s', request.POST) + form = UnsubscribeForm(request.POST) if form.is_valid(): form.save() - return render(request, 'contacts/subscribe_success.html', {'form': form.cleaned_data}) + logger.info('Unsubscribed %s', form.cleaned_data['email']) + return render(request, 'contacts/unsubscribe_success.html') else: - form = ContactEntryForm() - - return render(request, 'contacts/subscribe.html', {'form': form}) + form = UnsubscribeForm() + return render(request, 'contacts/unsubscribe.html', {'form': form}) diff --git a/conservancy/settings/base.py b/conservancy/settings/base.py index 1d5c3132..6ca284d6 100644 --- a/conservancy/settings/base.py +++ b/conservancy/settings/base.py @@ -59,6 +59,11 @@ LOGGING = { 'handlers': ['console'], 'propagate': False, }, + 'conservancy.contacts': { + 'handlers': ['console'], + 'level': 'DEBUG', + 'propagate': False, + } }, 'root': { 'handlers': ['console'], diff --git a/conservancy/urls.py b/conservancy/urls.py index a23c6baa..dc2fe862 100644 --- a/conservancy/urls.py +++ b/conservancy/urls.py @@ -30,6 +30,7 @@ urlpatterns = [ path('assignment/', include('conservancy.assignment.urls')), path('blog/', include('conservancy.blog.urls')), path('casts/the-corresponding-source/', include('conservancy.podjango.urls')), + path('contacts/', include('conservancy.contacts.urls')), path('contractpatch/', include('conservancy.contractpatch.urls')), path('feeds/', feeds.view), path('feeds/blog/', feeds.BlogFeed()),