diff options
author | luxagraf <sng@luxagraf.net> | 2019-02-12 14:31:53 -0600 |
---|---|---|
committer | luxagraf <sng@luxagraf.net> | 2019-02-12 14:31:53 -0600 |
commit | 0ccf2f7b61ea9889b1c03e51dfc847ca16cd98e7 (patch) | |
tree | 3c379d541c5aa96870a54796718078668fc8263f /app/lttr | |
parent | ee2803d2f85888ce42fd6371ef606f4a9c6fd88b (diff) |
finished up subscribe functions for lttr
Diffstat (limited to 'app/lttr')
-rw-r--r-- | app/lttr/admin.py | 13 | ||||
-rw-r--r-- | app/lttr/forms.py | 25 | ||||
-rw-r--r-- | app/lttr/migrations/0003_newsletter_slug.py | 19 | ||||
-rw-r--r-- | app/lttr/migrations/0004_auto_20190212_1529.py | 21 | ||||
-rw-r--r-- | app/lttr/models.py | 95 | ||||
-rw-r--r-- | app/lttr/urls.py | 33 | ||||
-rw-r--r-- | app/lttr/views.py | 50 |
7 files changed, 198 insertions, 58 deletions
diff --git a/app/lttr/admin.py b/app/lttr/admin.py new file mode 100644 index 0000000..1b919d6 --- /dev/null +++ b/app/lttr/admin.py @@ -0,0 +1,13 @@ +from django.contrib import admin + +from .models import NewsletterMailing, Subscriber + + +@admin.register(Subscriber) +class SubscriberAdmin(admin.ModelAdmin): + list_display = ('email_field', 'user', 'date_created', 'subscribed', 'subscribe_date', 'unsubscribed') + search_fields = ['email_field'] + list_filter = ['unsubscribed'] + + class Media: + js = ('next-prev-links.js',) diff --git a/app/lttr/forms.py b/app/lttr/forms.py index e1d4709..ad41d66 100644 --- a/app/lttr/forms.py +++ b/app/lttr/forms.py @@ -73,3 +73,28 @@ class SubscribeRequestForm(NewsletterForm): pass return data + + +class UpdateForm(NewsletterForm): + """ + This form allows one to actually update to or unsubscribe from the + newsletter. To do this, a correct activation code is required. + """ + + email_field = forms.EmailField( + label=("e-mail"), validators=[validate_email_nouser], disabled=True + ) + + def clean_user_activation_code(self): + data = self.cleaned_data['user_activation_code'] + + if data != self.instance.activation_code: + raise ValidationError( + ('The validation code supplied by you does not match.') + ) + + return data + + user_activation_code = forms.CharField( + label=("Activation code"), max_length=40 + ) diff --git a/app/lttr/migrations/0003_newsletter_slug.py b/app/lttr/migrations/0003_newsletter_slug.py new file mode 100644 index 0000000..ea20b4b --- /dev/null +++ b/app/lttr/migrations/0003_newsletter_slug.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.5 on 2019-02-11 22:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('lttr', '0002_subscriber_email_field'), + ] + + operations = [ + migrations.AddField( + model_name='newsletter', + name='slug', + field=models.SlugField(default='friends', unique=True), + preserve_default=False, + ), + ] diff --git a/app/lttr/migrations/0004_auto_20190212_1529.py b/app/lttr/migrations/0004_auto_20190212_1529.py new file mode 100644 index 0000000..4b810c9 --- /dev/null +++ b/app/lttr/migrations/0004_auto_20190212_1529.py @@ -0,0 +1,21 @@ +# Generated by Django 2.1.5 on 2019-02-12 15:29 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('lttr', '0003_newsletter_slug'), + ] + + operations = [ + migrations.RemoveField( + model_name='subscriber', + name='create_date', + ), + migrations.RemoveField( + model_name='subscriber', + name='ip', + ), + ] diff --git a/app/lttr/models.py b/app/lttr/models.py index 029022c..ec5e897 100644 --- a/app/lttr/models.py +++ b/app/lttr/models.py @@ -28,6 +28,7 @@ def make_activation_code(): class Newsletter(models.Model): """ A model for Newletters. Might I one day have two? I might. """ title = models.CharField(max_length=250) + slug = models.SlugField(db_index=True, unique=True) def __str__(self): return self.title @@ -36,7 +37,7 @@ class Newsletter(models.Model): return reverse("lttr:detail", kwargs={"slug": self.slug}) def subscribe_url(self): - return reverse('newsletter_subscribe_request', kwargs={'newsletter_slug': self.slug}) + return reverse('lttr:newsletter_subscribe_request', kwargs={'newsletter_slug': self.slug}) def unsubscribe_url(self): return reverse('newsletter_unsubscribe_request', kwargs={'newsletter_slug': self.slug}) @@ -50,6 +51,44 @@ class Newsletter(models.Model): def get_subscriptions(self): return Subscriber.objects.filter(newsletter=self, subscribed=True) + def get_templates(self, action): + """ + Return a subject, text, HTML tuple with e-mail templates for + a particular action. Returns a tuple with subject, text and e-mail + template. + """ + + assert action in ACTIONS + ('message', ), 'Unknown action: %s' % action + + # Common substitutions for filenames + tpl_subst = { + 'action': action, + 'newsletter': self.slug + } + + # Common root path for all the templates + tpl_root = 'lttr/message/' + + subject_template = select_template([ + tpl_root + '%(newsletter)s/%(action)s_subject.txt' % tpl_subst, + tpl_root + '%(action)s_subject.txt' % tpl_subst, + ]) + + text_template = select_template([ + tpl_root + '%(newsletter)s/%(action)s.txt' % tpl_subst, + tpl_root + '%(action)s.txt' % tpl_subst, + ]) + + html_template = select_template([ + tpl_root + '%(newsletter)s/%(action)s.html' % tpl_subst, + tpl_root + '%(action)s.html' % tpl_subst, + ]) + + return (subject_template, text_template, html_template) + + def get_sender(self): + return 'Scott Gilbertson <sng@luxagraf.net>' + @classmethod def get_default(cls): try: @@ -85,45 +124,6 @@ class NewsletterMailing(models.Model): self.date_created = timezone.now() super(NewsletterMailing, self).save() - def get_templates(self, action): - """ - Return a subject, text, HTML tuple with e-mail templates for - a particular action. Returns a tuple with subject, text and e-mail - template. - """ - - assert action in ACTIONS + ('message', ), 'Unknown action: %s' % action - - # Common substitutions for filenames - tpl_subst = { - 'action': action, - 'newsletter': self.slug - } - - # Common root path for all the templates - tpl_root = 'newsletter/message/' - - subject_template = select_template([ - tpl_root + '%(newsletter)s/%(action)s_subject.txt' % tpl_subst, - tpl_root + '%(action)s_subject.txt' % tpl_subst, - ]) - - text_template = select_template([ - tpl_root + '%(newsletter)s/%(action)s.txt' % tpl_subst, - tpl_root + '%(action)s.txt' % tpl_subst, - ]) - - if self.send_html: - html_template = select_template([ - tpl_root + '%(newsletter)s/%(action)s.html' % tpl_subst, - tpl_root + '%(action)s.html' % tpl_subst, - ]) - else: - # HTML templates are not required - html_template = None - - return (subject_template, text_template, html_template) - class Subscriber(models.Model): """ A model for Newletter Subscriber """ @@ -131,9 +131,7 @@ class Subscriber(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) date_created = models.DateTimeField(blank=True, auto_now_add=True, editable=False) date_updated = models.DateTimeField(blank=True, auto_now=True, editable=False) - ip = models.GenericIPAddressField(blank=True, null=True) newsletter = models.ForeignKey(Newsletter, on_delete=models.CASCADE) - create_date = models.DateTimeField(editable=False, default=datetime.datetime.now) activation_code = models.CharField(max_length=40, default=make_activation_code) subscribed = models.BooleanField(default=False, db_index=True) subscribe_date = models.DateTimeField(null=True, blank=True) @@ -278,25 +276,22 @@ class Subscriber(models.Model): message.send() def subscribe_activate_url(self): - return reverse('newsletter_update_activate', kwargs={ - 'newsletter_slug': self.newsletter.slug, - 'email': self.email, - 'action': 'subscribe', + return reverse('lttr:newsletter_activate', kwargs={ + 'slug': self.newsletter.slug, 'activation_code': self.activation_code }) def unsubscribe_activate_url(self): return reverse('newsletter_update_activate', kwargs={ - 'newsletter_slug': self.newsletter.slug, + 'slug': self.newsletter.slug, 'email': self.email, 'action': 'unsubscribe', 'activation_code': self.activation_code }) def update_activate_url(self): - return reverse('newsletter_update_activate', kwargs={ - 'newsletter_slug': self.newsletter.slug, - 'email': self.email, + return reverse('lttr:newsletter_update_activate', kwargs={ + 'slug': self.newsletter.slug, 'action': 'update', 'activation_code': self.activation_code }) diff --git a/app/lttr/urls.py b/app/lttr/urls.py index 84b0bca..e0166f1 100644 --- a/app/lttr/urls.py +++ b/app/lttr/urls.py @@ -1,4 +1,4 @@ -from django.urls import path +from django.urls import path, re_path from . import views @@ -11,11 +11,40 @@ urlpatterns = [ name="detail" ), path( - r'<int:page>', + r'<str:slug>/<int:page>', views.NewsletterListView.as_view(), name="list" ), path( + '<str:slug>/activate/<str:activation_code>/', + views.ConfirmSubscriptionView.as_view(), name='newsletter_activate' + ), + #path( + # '/subscribe/confirm/', + # views.SubscribeRequestView.as_view(confirm=True), + # name='newsletter_subscribe_confirm' + #), + #path( + # '^<newsletter_slug:s>/update/$', + # views.UpdateRequestView.as_view(), + # name='newsletter_update_request' + #), + #path( + # '^<newsletter_slug:s>/unsubscribe/$', + # views.UnsubscribeRequestView.as_view(), + # name='newsletter_unsubscribe_request' + #), + #path( + # '^<newsletter_slug:s>/unsubscribe/confirm/$', + # views.UnsubscribeRequestView.as_view(confirm=True), + # name='newsletter_unsubscribe_confirm' + #), + path( + r'subscribed/', + views.NewsletterSubscribedView.as_view(), + name="subscribed" + ), + path( r'', views.NewsletterListView.as_view(), {'page': 1}, diff --git a/app/lttr/views.py b/app/lttr/views.py index 786bc5c..0d3dbea 100644 --- a/app/lttr/views.py +++ b/app/lttr/views.py @@ -1,14 +1,22 @@ -from django.views.generic import ListView, CreateView +import socket +from django.views.generic import ListView, CreateView, TemplateView, FormView from django.views.generic.detail import DetailView from django.views.generic.dates import YearArchiveView, MonthArchiveView from django.contrib.syndication.views import Feed +from django.template.response import TemplateResponse +from django.contrib.auth import get_user_model from django.db.models import Q +from django.shortcuts import get_object_or_404, redirect from django.conf import settings +from django.urls import reverse, reverse_lazy from utils.views import PaginatedListView +from smtplib import SMTPException from .models import NewsletterMailing, Subscriber, Newsletter -from .forms import SubscribeRequestForm +from .forms import SubscribeRequestForm, UpdateForm + +ACTIONS = ('subscribe', 'unsubscribe', 'update') class NewsletterMailingDetail(DetailView): @@ -29,12 +37,17 @@ class NewsletterMailingDetail(DetailView): return context +class NewsletterSubscribedView(TemplateView): + template_name = "lttr/subscribed.html" + + class NewsletterListView(CreateView): - model = Subscriber - form_class = SubscribeRequestForm """ Return a subscribe form and list of Newsletter posts in reverse chronological order """ + model = Subscriber + form_class = SubscribeRequestForm + action = 'subscribe' def get_form_kwargs(self): kwargs = super(NewsletterListView, self).get_form_kwargs() @@ -46,9 +59,34 @@ class NewsletterListView(CreateView): context['mailings'] = NewsletterMailing.objects.filter(status=1) return context + def get_success_url(self): + return reverse_lazy('lttr:subscribed') + def form_valid(self, form, **kwargs): - form.instance.user = settings.AUTH_USER_MODEL.objects.get_or_create(email=form.instance.email) + form.instance.user, created = get_user_model().objects.get_or_create( + email=form.cleaned_data['email_field'], + username=form.cleaned_data['email_field'] + ) self.object = form.save() + try: + self.object.send_activation_email(action=self.action) + + except (SMTPException, socket.error) as e: + print(e) + self.error = True + + # Although form was valid there was error while sending email, + # so stay at the same url. + return super(NewsletterListView, self).form_invalid(form) return super(NewsletterListView, self).form_valid(form) - #super(NewsletterListView, self).form_valid() + +class ConfirmSubscriptionView(DetailView): + model = Subscriber + template_name = "lttr/confirm_activate.html" + + def get_object(self): + obj = Subscriber.objects.get(newsletter__slug=self.kwargs['slug'], activation_code=self.kwargs['activation_code']) + if obj.subscribed is False: + obj.update('subscribe') + return obj |