Opt-out
authorRadek Czajka <rczajka@rczajka.pl>
Wed, 29 Jan 2020 09:48:35 +0000 (10:48 +0100)
committerRadek Czajka <rczajka@rczajka.pl>
Wed, 29 Jan 2020 09:48:35 +0000 (10:48 +0100)
src/messaging/locale/pl/LC_MESSAGES/django.mo
src/messaging/locale/pl/LC_MESSAGES/django.po
src/messaging/migrations/0004_auto_20200129_1035.py [new file with mode: 0644]
src/messaging/models.py
src/messaging/templates/messaging/contact_form.html [new file with mode: 0644]
src/messaging/templates/messaging/email_body.html [new file with mode: 0644]
src/messaging/urls.py
src/messaging/views.py

index 6ee4e30..f57284f 100644 (file)
Binary files a/src/messaging/locale/pl/LC_MESSAGES/django.mo and b/src/messaging/locale/pl/LC_MESSAGES/django.mo differ
index d66ee74..45da1b5 100644 (file)
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2020-01-29 10:10+0100\n"
-"PO-Revision-Date: 2020-01-29 10:11+0100\n"
+"PO-Revision-Date: 2020-01-29 10:18+0100\n"
 "Last-Translator: \n"
 "Language-Team: \n"
 "Language: pl\n"
@@ -31,7 +31,7 @@ msgstr "Ograniczenia wysyłki"
 #: admin.py:41
 #, python-format
 msgid "Test e-mail has been sent to %(email)s."
-msgstr "Na adres %(email)s zostały wysłany testowy e-mail."
+msgstr "Na adres %(email)s została wysłana testowa wiadomość."
 
 #: admin.py:43
 msgid "You have no email set. Test e-mail not sent."
diff --git a/src/messaging/migrations/0004_auto_20200129_1035.py b/src/messaging/migrations/0004_auto_20200129_1035.py
new file mode 100644 (file)
index 0000000..edaaf05
--- /dev/null
@@ -0,0 +1,74 @@
+# Generated by Django 2.2.9 on 2020-01-29 09:35
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('messaging', '0003_auto_20200128_2230'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='contact',
+            name='key',
+            field=models.CharField(blank=True, default='', max_length=64),
+            preserve_default=False,
+        ),
+        migrations.AlterField(
+            model_name='contact',
+            name='level',
+            field=models.PositiveSmallIntegerField(choices=[(20, 'Would-be donor'), (30, 'One-time donor'), (40, 'Recurring donor'), (10, 'Cold'), (50, 'Opt out')]),
+        ),
+        migrations.AlterField(
+            model_name='emailtemplate',
+            name='dow_1',
+            field=models.BooleanField(default=True, verbose_name='Monday'),
+        ),
+        migrations.AlterField(
+            model_name='emailtemplate',
+            name='dow_2',
+            field=models.BooleanField(default=True, verbose_name='Tuesday'),
+        ),
+        migrations.AlterField(
+            model_name='emailtemplate',
+            name='dow_3',
+            field=models.BooleanField(default=True, verbose_name='Wednesday'),
+        ),
+        migrations.AlterField(
+            model_name='emailtemplate',
+            name='dow_4',
+            field=models.BooleanField(default=True, verbose_name='Thursday'),
+        ),
+        migrations.AlterField(
+            model_name='emailtemplate',
+            name='dow_5',
+            field=models.BooleanField(default=True, verbose_name='Friday'),
+        ),
+        migrations.AlterField(
+            model_name='emailtemplate',
+            name='dow_6',
+            field=models.BooleanField(default=True, verbose_name='Saturday'),
+        ),
+        migrations.AlterField(
+            model_name='emailtemplate',
+            name='dow_7',
+            field=models.BooleanField(default=True, verbose_name='Sunday'),
+        ),
+        migrations.AlterField(
+            model_name='emailtemplate',
+            name='max_days_since',
+            field=models.SmallIntegerField(blank=True, null=True, verbose_name='max days since'),
+        ),
+        migrations.AlterField(
+            model_name='emailtemplate',
+            name='min_days_since',
+            field=models.SmallIntegerField(blank=True, null=True, verbose_name='min days since'),
+        ),
+        migrations.AlterField(
+            model_name='emailtemplate',
+            name='state',
+            field=models.CharField(choices=[('cold', 'cold group'), ('club-payment-unfinished', 'club would-be donors'), ('club-single', 'club one-time donors'), ('club-membership-expiring', 'club one-time donors with donation expiring'), ('club-recurring', 'club recurring donors'), ('club-recurring-payment-problem', 'club recurring donors with donation expired')], help_text='?', max_length=128, verbose_name='state'),
+        ),
+    ]
index 6f158e9..24cf623 100644 (file)
@@ -3,8 +3,10 @@ from django.conf import settings
 from django.core.mail import send_mail
 from django.db import models
 from django.template import Template, Context
+from django.urls import reverse
 from django.utils.translation import ugettext_lazy as _
 from sentry_sdk import capture_exception
+from catalogue.utils import get_random_hash
 from .recipient import Recipient
 from .states import states
 
@@ -54,8 +56,24 @@ class EmailTemplate(models.Model):
         raise ValueError('Unknown state', s.state)
 
     def send(self, recipient, verbose=False, dry_run=False, test=False):
-        subject = Template(self.subject).render(Context(recipient.context))
-        body = Template(self.body).render(Context(recipient.context))
+        ctx = Context(recipient.context)
+
+        if test:
+            contact = Contact(email=recipient.email, key='test')
+        else:
+            # TODO: actually, we should just use Contacts instead of recipients.
+            contact = Contact.objects.get(email=recipient.email)
+
+        ctx['contact'] = contact
+
+        subject = Template(self.subject).render(ctx)
+
+        if test:
+            subject = "[test] " + subject
+
+        body_template = '{% extends "messaging/email_body.html" %}{% block body %}' + self.body + '{% endblock %}'
+
+        body = Template(body_template).render(ctx)
         if verbose:
             print(recipient.email, subject)
         if not dry_run:
@@ -113,6 +131,15 @@ class Contact(models.Model):
         ])
     since = models.DateTimeField()
     expires_at = models.DateTimeField(null=True, blank=True)
+    key = models.CharField(max_length=64, blank=True)
+
+    def save(self, *args, **kwargs):
+        if not self.key:
+            self.key = get_random_hash(self.email)
+        super().save(*args, **kwargs)
+
+    def get_optout_url(self):
+        return reverse('messaging_optout', args=[self.key])
 
     @classmethod
     def update(cls, email, level, since, expires_at=None):
diff --git a/src/messaging/templates/messaging/contact_form.html b/src/messaging/templates/messaging/contact_form.html
new file mode 100644 (file)
index 0000000..bbe9f37
--- /dev/null
@@ -0,0 +1,11 @@
+{% extends "base/base.html" %}
+
+{% block body %}
+
+<h1>Czy na pewno chcesz zrezygnować z otrzymywania e-maili na adres {{ object.email }}?</h1>
+
+<form method="POST" action="">
+       <button type="submit">Tak, wypisz mnie</button>
+</form>
+
+{% endblock %}
diff --git a/src/messaging/templates/messaging/email_body.html b/src/messaging/templates/messaging/email_body.html
new file mode 100644 (file)
index 0000000..b6f9202
--- /dev/null
@@ -0,0 +1,6 @@
+{% load i18n %}{% block body %}
+{% endblock %}
+
+-- 
+{% trans "Visit this address if you don't want to be contacted in the future:" %}
+https://wolnelektury.pl{{ contact.get_optout_url }}
index 8f1fc1f..4990008 100644 (file)
@@ -4,4 +4,5 @@ from . import views
 
 urlpatterns = [
     path('states/<slug>/info.json', views.state_info),
+    path('opt-out/<key>/', views.OptOutView.as_view(), name='messaging_optout'),
 ]
index 31debe1..f3881bb 100644 (file)
@@ -3,6 +3,8 @@ from django.http import JsonResponse
 from django.urls import reverse
 from django.shortcuts import render
 from django.utils.translation import ugettext as _
+from django.views.generic import UpdateView
+from . import models
 from .states import states
 
 
@@ -28,3 +30,10 @@ def state_info(request, slug):
         "help": help_text,
     })
 
+
+class OptOutView(UpdateView):
+    model = models.Contact
+    slug_url_kwarg = 'key'
+    slug_field = 'key'
+    fields = ['level']
+