diff --git a/conservancy/contacts/admin.py b/conservancy/contacts/admin.py
index 94048d4bdeea0bd9b66d2afd18e14ff455a726b4..f6c68b1cd1631a2c2a5dcf9e60c4aa8af0143690 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 0000000000000000000000000000000000000000..e10b2fabcaf6db905cc477b786bd0279efbe5714
--- /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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/conservancy/contacts/models.py b/conservancy/contacts/models.py
index 14ac74848d12e310d4ab824e8337b1c65810fccd..58e89a75abb655072e7b663fb004f8eb8d3a6587 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 0000000000000000000000000000000000000000..86de5a67543d2b6ce3148446cbb03caf5b4e3cbe
--- /dev/null
+++ b/conservancy/contacts/templates/contacts/unsubscribe.html
@@ -0,0 +1,10 @@
+{% extends "base_conservancy.html" %}
+{% block outercontent %}
+
+{% endblock %}
diff --git a/conservancy/contacts/templates/contacts/unsubscribe_success.html b/conservancy/contacts/templates/contacts/unsubscribe_success.html
new file mode 100644
index 0000000000000000000000000000000000000000..e4938995a0590b31f559cc128342bbc5ffb6e402
--- /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 661e68911f8d37eaf233eebb0b5de3bde303ac1c..74f1c84d6557d92674783ad92c8db8de0e0b4b02 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 95ccfc09340d49cbf7a6e7cd3ffc76a2bdfbc93b..65ea3445d0b0e0d2c9ad8126a6fb202413643d4c 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
-def subscribe(request):
- """Mailing list subscription form
- """
+logger = logging.getLogger(__name__)
- class ContactEntryForm(ModelForm):
- class Meta:
- model = ContactEntry
+class UnsubscribeForm(ModelForm):
+ class Meta:
+ model = Unsubscription
+ fields = ['email']
- 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 1d5c3132691ef081ab3b7b5d4b778041e0a08515..6ca284d6148f2d2797b3161e692603dc1b97531a 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 a23c6baa2cb5a7603e2b0b3c73097a6b0f531024..dc2fe8624e881af5d5f35f9241ebc5984fb875da 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()),