diff options
| author | Magnus Hagander | 2019-06-18 11:41:58 +0000 |
|---|---|---|
| committer | Magnus Hagander | 2019-06-18 11:44:15 +0000 |
| commit | 9899552d3398c0f9dca4540c23174d237c2a7517 (patch) | |
| tree | 425acca82f30147ae6e86658801b5b7ae063a901 /django/archives | |
| parent | 819e05a3845ffef1f0ee1148635648f5fb972c63 (diff) | |
Implement email resending in the list archives
This allows a logged-in user to get an email delivered to their mailbox,
thereby making it easy to reply to even if they haven't got it already
(and don't have a MUA capable of handling mbox files).
The email body will go out unmodified (including any list headers that
are stored in the archives, but this does not include for example the
unsubscribe link). Envelope sender is set to one configured in the ini
file, and envelope recipient is set to the email address of the user.
Diffstat (limited to 'django/archives')
| -rw-r--r-- | django/archives/mailarchives/migrations/0003_message_resend.py | 27 | ||||
| -rw-r--r-- | django/archives/mailarchives/models.py | 7 | ||||
| -rw-r--r-- | django/archives/mailarchives/templates/_message.html | 1 | ||||
| -rw-r--r-- | django/archives/mailarchives/templates/message_resend.html | 20 | ||||
| -rw-r--r-- | django/archives/mailarchives/templates/resend_complete.html | 17 | ||||
| -rw-r--r-- | django/archives/mailarchives/views.py | 52 | ||||
| -rw-r--r-- | django/archives/settings.py | 8 | ||||
| -rw-r--r-- | django/archives/urls.py | 4 |
8 files changed, 130 insertions, 6 deletions
diff --git a/django/archives/mailarchives/migrations/0003_message_resend.py b/django/archives/mailarchives/migrations/0003_message_resend.py new file mode 100644 index 0000000..5461502 --- /dev/null +++ b/django/archives/mailarchives/migrations/0003_message_resend.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.18 on 2019-06-18 09:39 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('mailarchives', '0002_list_permissions'), + ] + + operations = [ + migrations.CreateModel( + name='ResendMessage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('registeredat', models.DateTimeField(auto_now_add=True)), + ('message', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mailarchives.Message')), + ('sendto', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/django/archives/mailarchives/models.py b/django/archives/mailarchives/models.py index 88137f8..a9ca52e 100644 --- a/django/archives/mailarchives/models.py +++ b/django/archives/mailarchives/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.contrib.auth.models import User # Reason a message was hidden. # We're intentionally putting the prefix text in the array here, since @@ -120,6 +121,12 @@ class ListSubscriber(models.Model): db_table = 'listsubscribers' +class ResendMessage(models.Model): + message = models.ForeignKey(Message, null=False, blank=False) + sendto = models.ForeignKey(User, null=False, blank=False) + registeredat = models.DateTimeField(null=False, blank=False, auto_now_add=True) + + class ApiClient(models.Model): apikey = models.CharField(max_length=100, null=False, blank=False) postback = models.URLField(max_length=500, null=False, blank=False) diff --git a/django/archives/mailarchives/templates/_message.html b/django/archives/mailarchives/templates/_message.html index 7dea7f3..c90a80a 100644 --- a/django/archives/mailarchives/templates/_message.html +++ b/django/archives/mailarchives/templates/_message.html @@ -33,6 +33,7 @@ <a href="/message-id/raw/{{msg.messageid|urlencode}}">Raw Message</a> | <a href="/message-id/flat/{{msg.messageid|urlencode}}">Whole Thread</a> | <a href="/message-id/mbox/{{msg.messageid|urlencode}}">Download mbox</a> +{%if allow_resend %}| <a href="/message-id/resend/{{msg.messageid|urlencode}}">Resend email</a>{%endif%} </td> </tr> {% if not show_all %} diff --git a/django/archives/mailarchives/templates/message_resend.html b/django/archives/mailarchives/templates/message_resend.html new file mode 100644 index 0000000..0485726 --- /dev/null +++ b/django/archives/mailarchives/templates/message_resend.html @@ -0,0 +1,20 @@ +{%extends "page.html"%} +{%block title%}Resend - {{msg.subject}}{%endblock%} + +{%block contents%} +<h1 class="subject">Resend - {{msg.subject}}</h1> +<p> + The below message will be resent to <em>{{request.user.email}}</em>, + which is the email address of the account you are currently logged in with. +</p> + +<p> + <form method="post" action="/message-id/resend/{{msg.messageid|urlencode}}">{% csrf_token %} + <input type="hidden" name="resend" value="1"> + <input type="submit" value="Resend this message" class="btn btn-primary"> + </form> +</p> + +<h4>Message to resend</h4> +{% include '_message.html' with msg=msg lists=lists show_all=True %} +{%endblock%} diff --git a/django/archives/mailarchives/templates/resend_complete.html b/django/archives/mailarchives/templates/resend_complete.html new file mode 100644 index 0000000..9edab34 --- /dev/null +++ b/django/archives/mailarchives/templates/resend_complete.html @@ -0,0 +1,17 @@ +{%extends "page.html"%} +{%block title%}Resend - {{msg.subject}}{%endblock%} + +{%block contents%} +<h1 class="subject">Resend - {{msg.subject}}</h1> +<p> + The message <em>{{msg.subject}}</em> with messageid <em>{{msg.messageid}}</em> + has been scheduled for resending to <em>{{request.user.email}}</em>. +</p> +<p> + It will be delivered within a few minutes. +</p> + +<p> + <a class="btn btn-primary" href="/message-id/{{msg.messageid|urlencode}}">Return to message</a> +</p> +{%endblock%} diff --git a/django/archives/mailarchives/views.py b/django/archives/mailarchives/views.py index 8c82fc5..f19329f 100644 --- a/django/archives/mailarchives/views.py +++ b/django/archives/mailarchives/views.py @@ -1,10 +1,11 @@ from django.template import RequestContext from django.http import HttpResponse, HttpResponseForbidden, Http404 -from django.http import StreamingHttpResponse +from django.http import StreamingHttpResponse, HttpResponseRedirect from django.http import HttpResponsePermanentRedirect, HttpResponseNotModified from django.core.exceptions import PermissionDenied from django.shortcuts import render, get_object_or_404 from django.utils.http import http_date, parse_http_date_safe +from django.views.decorators.csrf import csrf_exempt from django.db import connection, transaction from django.db.models import Q from django.conf import settings @@ -169,7 +170,9 @@ def get_all_groups_and_lists(request, listid=None): class NavContext(object): def __init__(self, request, listid=None, listname=None, all_groups=None, expand_groupid=None): self.request = request - self.ctx = {} + self.ctx = { + 'allow_resend': settings.ALLOW_RESEND, + } if all_groups: groups = copy.deepcopy(all_groups) @@ -623,6 +626,51 @@ def mbox(request, listname, listname2, mboxyear, mboxmonth): return _build_mbox(query, params) +@transaction.atomic +def resend(request, messageid): + if not settings.ALLOW_RESEND: + raise PermissionDenied("Access denied.") + + if not (hasattr(request, 'user') and request.user.is_authenticated()): + raise ERedirect('%s?next=%s' % (settings.LOGIN_URL, request.path)) + + ensure_message_permissions(request, messageid) + + m = get_object_or_404(Message, messageid=messageid) + if m.hiddenstatus: + raise PermissionDenied("Access denied.") + + if request.method == 'POST': + if request.POST.get('resend', None) == '1': + ResendMessage(message=m, sendto=request.user).save() + connection.cursor().execute("NOTIFY archives_resend") + return HttpResponseRedirect('/message-id/resend/{0}/complete'.format(m.messageid)) + + lists = List.objects.extra(where=["listid IN (SELECT listid FROM list_threads WHERE threadid=%s)" % m.threadid]).order_by('listname') + + return render_nav(NavContext(request, lists[0].listid, lists[0].listname), 'message_resend.html', { + 'msg': m, + 'lists': lists, + }) + + +def resend_complete(request, messageid): + if not settings.ALLOW_RESEND: + raise PermissionDenied("Access denied.") + + m = get_object_or_404(Message, messageid=messageid) + if m.hiddenstatus: + raise PermissionDenied("Access denied.") + + lists = List.objects.extra(where=["listid IN (SELECT listid FROM list_threads WHERE threadid=%s)" % m.threadid]).order_by('listname') + + return render_nav(NavContext(request, lists[0].listid, lists[0].listname), 'resend_complete.html', { + 'msg': m, + 'lists': lists, + }) + + +@csrf_exempt def search(request): if not settings.PUBLIC_ARCHIVES: # We don't support searching of non-public archives at all at this point. diff --git a/django/archives/settings.py b/django/archives/settings.py index 80b990e..5761344 100644 --- a/django/archives/settings.py +++ b/django/archives/settings.py @@ -100,6 +100,8 @@ TEMPLATES = [{ INSTALLED_APPS = [ + 'django.contrib.auth', + 'django.contrib.contenttypes', 'archives.mailarchives', ] @@ -133,6 +135,7 @@ FORCE_SCRIPT_NAME = "" SEARCH_CLIENTS = ('127.0.0.1',) API_CLIENTS = ('127.0.0.1',) PUBLIC_ARCHIVES = False +ALLOW_RESEND = False try: from .settings_local import * @@ -140,16 +143,15 @@ except ImportError: pass # If this is a non-public site, enable middleware for handling logins etc -if not PUBLIC_ARCHIVES: +if ALLOW_RESEND or not PUBLIC_ARCHIVES: MIDDLEWARE_CLASSES = [ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', ] + MIDDLEWARE_CLASSES MIDDLEWARE_CLASSES.append('archives.mailarchives.redirecthandler.RedirectMiddleware') INSTALLED_APPS = [ - 'django.contrib.auth', - 'django.contrib.contenttypes', 'django.contrib.sessions', ] + INSTALLED_APPS diff --git a/django/archives/urls.py b/django/archives/urls.py index 542475d..67dd3e1 100644 --- a/django/archives/urls.py +++ b/django/archives/urls.py @@ -34,6 +34,8 @@ urlpatterns = [ url(r'^message-id/flat/(.+)$', archives.mailarchives.views.message_flat), url(r'^message-id/raw/(.+)$', archives.mailarchives.views.message_raw), url(r'^message-id/mbox/(.+)$', archives.mailarchives.views.message_mbox), + url(r'^message-id/resend/(.+)/complete$', archives.mailarchives.views.resend_complete), + url(r'^message-id/resend/(.+)$', archives.mailarchives.views.resend), url(r'^message-id/attachment/(\d+)/.*$', archives.mailarchives.views.attachment), url(r'^message-id/legacy/([\w-]+)/(\d+)-(\d+)/msg(\d+).php$', archives.mailarchives.views.legacy), url(r'^message-id/(.+)$', archives.mailarchives.views.message), @@ -62,7 +64,7 @@ urlpatterns = [ url(r'^dyncss/(?P<css>base|docs).css$', archives.mailarchives.views.dynamic_css), ] -if not settings.PUBLIC_ARCHIVES: +if settings.ALLOW_RESEND or not settings.PUBLIC_ARCHIVES: import archives.auth urlpatterns += [ |
