diff --git a/conservancy/contacts/admin.py b/conservancy/contacts/admin.py index f6c68b1cd1631a2c2a5dcf9e60c4aa8af0143690..9a61da2b92c2c855c1202c38f791bf94acdb907a 100644 --- a/conservancy/contacts/admin.py +++ b/conservancy/contacts/admin.py @@ -5,4 +5,5 @@ from .models import Unsubscription @admin.register(Unsubscription) class UnsubscriptionAdmin(admin.ModelAdmin): - list_display = ['created', 'email'] + list_display = ['created', 'email', 'mailout'] + search_fields = ['email', 'mailout'] diff --git a/conservancy/contacts/migrations/0002_unsubscription_mailout.py b/conservancy/contacts/migrations/0002_unsubscription_mailout.py new file mode 100644 index 0000000000000000000000000000000000000000..7ec1cc9bf8fdda141a4ede9b5ac00531426d8b53 --- /dev/null +++ b/conservancy/contacts/migrations/0002_unsubscription_mailout.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.11 on 2024-04-10 02:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contacts', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='unsubscription', + name='mailout', + field=models.SlugField(default=''), + preserve_default=False, + ), + ] diff --git a/conservancy/contacts/models.py b/conservancy/contacts/models.py index 58e89a75abb655072e7b663fb004f8eb8d3a6587..729b1c5c4581a6229a880e2ad68f85267394240f 100644 --- a/conservancy/contacts/models.py +++ b/conservancy/contacts/models.py @@ -4,6 +4,7 @@ from django.db import models class Unsubscription(models.Model): created = models.DateTimeField(auto_now_add=True, blank=True) email = models.EmailField() + mailout = models.SlugField() class Meta: ordering = ['created'] diff --git a/conservancy/contacts/views.py b/conservancy/contacts/views.py index daaf4d6712a48cb14fa8fcc66e9e927fe13c5864..ed5281202d96d95825b38e12f8c7c0bfb090a416 100644 --- a/conservancy/contacts/views.py +++ b/conservancy/contacts/views.py @@ -11,17 +11,27 @@ logger = logging.getLogger(__name__) class UnsubscribeForm(ModelForm): class Meta: model = Unsubscription - fields = ['email'] + fields = ['email', 'mailout'] -# Exempt from CSRF protection so that it can be triggered by Gmail's on-click -# unsubscribe. -@csrf_exempt +@csrf_exempt # Submitted directly by Gmail and similar - no CSRF token. def unsubscribe(request): + """Endpoint for use with Gmail one-click unsubscribe or similar. + + Gmail now requires "List-Unsubscribe" headers for senders over a certain + monthly volume (currently 5000 emails). Add the following headers to your + mailout: + + List-Unsubscribe: + List-Unsubscribe-Post: List-Unsubscribe=One-Click + + Interfaces like Gmail will then provide a user interface to unsubscribe + which will hit this endpoint. + """ if request.method == 'POST': logger.debug('Unsubscribe GET: %s', request.GET) logger.debug('Unsubscribe POST: %s', request.POST) - form = UnsubscribeForm(request.GET | request.POST) + form = UnsubscribeForm(request.GET) if form.is_valid(): form.save() logger.info('Unsubscribed %s', form.cleaned_data['email'])