From a059717d3c831afe047a3ecda8e2610eb0e6fd57 Mon Sep 17 00:00:00 2001 From: Magnus Hagander Date: Mon, 28 Apr 2014 18:04:52 +0200 Subject: [PATCH] Add support for (bulk) emailing CF managers can now email authors and reviewers both individually on a patch, and in the full set of search results. --- pgcommitfest/commitfest/forms.py | 11 ++++ .../commitfest/templates/commitfest.html | 48 +++++++++++++- .../commitfest/templates/patch_commands.inc | 11 ++++ pgcommitfest/commitfest/views.py | 63 ++++++++++++++++++- pgcommitfest/urls.py | 2 + 5 files changed, 129 insertions(+), 6 deletions(-) diff --git a/pgcommitfest/commitfest/forms.py b/pgcommitfest/commitfest/forms.py index a083588..7f820e4 100644 --- a/pgcommitfest/commitfest/forms.py +++ b/pgcommitfest/commitfest/forms.py @@ -1,5 +1,6 @@ from django import forms from django.forms import ValidationError +from django.forms.widgets import HiddenInput from django.db.models import Q from django.contrib.auth.models import User @@ -114,3 +115,13 @@ class CommentForm(forms.Form): if '1' in self.cleaned_data[fn] and not '0' in self.cleaned_data[fn]: self.errors[fn] = (('Cannot pass a test without performing it!'),) return self.cleaned_data + +class BulkEmailForm(forms.Form): + reviewers = forms.CharField(required=False, widget=HiddenInput()) + authors = forms.CharField(required=False, widget=HiddenInput()) + subject = forms.CharField(required=True) + body = forms.CharField(required=True, widget=forms.Textarea) + confirm = forms.BooleanField(required=True, label='Check to confirm sending') + + def __init__(self, *args, **kwargs): + super(BulkEmailForm, self).__init__(*args, **kwargs) diff --git a/pgcommitfest/commitfest/templates/commitfest.html b/pgcommitfest/commitfest/templates/commitfest.html index 3b5ae02..6aea4b0 100644 --- a/pgcommitfest/commitfest/templates/commitfest.html +++ b/pgcommitfest/commitfest/templates/commitfest.html @@ -53,6 +53,9 @@ Reviewers {%if p.is_open%}Latest activity{%if sortkey = 1%}
{%endif%}{%else%}Latest activity{%endif%} {%if p.is_open%}Latest mail{%if sortkey = 2%}
{%endif%}{%else%}Latest mail{%endif%} +{%if user.is_staff%} + Select +{%endif%} @@ -60,7 +63,7 @@ {%if grouping%} {%ifchanged p.topic%} - {{p.topic}} + {{p.topic}} {%endifchanged%} {%endif%} @@ -70,14 +73,53 @@ {{p.reviewer_names|default:''}} {{p.modified|date:"Y-m-d"}}
{{p.modified|date:"H:i"}} {{p.lastmail|date:"Y-m-d"}}
{{p.lastmail|date:"H:i"}} +{%if user.is_staff%} + Author
Reviewer +{%endif%} {%endfor%} +
{%if cf.isopen or user.is_staff %} -

New patch -

{%endif%} +{%if user.is_staff%} + +{%endif%} +
+{%endblock%} + +{%block morescript%} + {%endblock%} diff --git a/pgcommitfest/commitfest/templates/patch_commands.inc b/pgcommitfest/commitfest/templates/patch_commands.inc index 50d9301..aeb96e2 100644 --- a/pgcommitfest/commitfest/templates/patch_commands.inc +++ b/pgcommitfest/commitfest/templates/patch_commands.inc @@ -27,4 +27,15 @@ +{%if request.user.is_staff%} +
+ Send private mail + +
+{%endif%} + \ No newline at end of file diff --git a/pgcommitfest/commitfest/views.py b/pgcommitfest/commitfest/views.py index 650b29c..2556cc3 100644 --- a/pgcommitfest/commitfest/views.py +++ b/pgcommitfest/commitfest/views.py @@ -5,6 +5,7 @@ from django.db import transaction from django.db.models import Q from django.contrib import messages from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import User import settings @@ -12,10 +13,11 @@ from datetime import datetime from email.mime.text import MIMEText from email.utils import formatdate, make_msgid -from mailqueue.util import send_mail +from mailqueue.util import send_mail, send_simple_mail from models import CommitFest, Patch, PatchOnCommitFest, PatchHistory, Committer from forms import PatchForm, NewPatchForm, CommentForm, CommitFestFilterForm +from forms import BulkEmailForm from ajax import doAttachThread def home(request): @@ -82,12 +84,12 @@ def commitfest(request, cfid): # Redirect to get rid of the ugly url return HttpResponseRedirect('/%s/' % cf.id) - patches = cf.patch_set.filter(q).select_related().extra(select={ + patches = list(cf.patch_set.filter(q).select_related().extra(select={ 'status':'commitfest_patchoncommitfest.status', 'author_names':"SELECT string_agg(first_name || ' ' || last_name || ' (' || username || ')', ', ') FROM auth_user INNER JOIN commitfest_patch_authors cpa ON cpa.user_id=auth_user.id WHERE cpa.patch_id=commitfest_patch.id", 'reviewer_names':"SELECT string_agg(first_name || ' ' || last_name || ' (' || username || ')', ', ') FROM auth_user INNER JOIN commitfest_patch_reviewers cpr ON cpr.user_id=auth_user.id WHERE cpr.patch_id=commitfest_patch.id", 'is_open':'commitfest_patchoncommitfest.status IN (%s)' % ','.join([str(x) for x in PatchOnCommitFest.OPEN_STATUSES]), - }).order_by(*ordering) + }).order_by(*ordering)) # Generates a fairly expensive query, which we shouldn't do unless # the user is logged in. XXX: Figure out how to avoid doing that.. @@ -101,6 +103,7 @@ def commitfest(request, cfid): 'title': cf.title, 'grouping': sortkey==0, 'sortkey': sortkey, + 'openpatchids': [p.id for p in patches if p.is_open], }, context_instance=RequestContext(request)) def patch(request, cfid, patchid): @@ -414,3 +417,57 @@ def committer(request, cfid, patchid, status): PatchHistory(patch=patch, by=request.user, what='Removed self from committers').save() patch.save() return HttpResponseRedirect('../../') + +@login_required +@transaction.commit_on_success +def send_email(request, cfid): + cf = get_object_or_404(CommitFest, pk=cfid) + if not request.user.is_staff: + raise Http404("Only CF managers can do that.") + + if request.method == 'POST': + authoridstring = request.POST['authors'] + revieweridstring = request.POST['reviewers'] + form = BulkEmailForm(data=request.POST) + if form.is_valid(): + q = Q() + if authoridstring: + q = q | Q(patch_author__in=[int(x) for x in authoridstring.split(',')]) + if revieweridstring: + q = q | Q(patch_reviewer__in=[int(x) for x in revieweridstring.split(',')]) + + recipients = User.objects.filter(q).distinct() + + for r in recipients: + send_simple_mail(request.user.email, r.email, form.cleaned_data['subject'], form.cleaned_data['body']) + messages.add_message(request, messages.INFO, "Sent email to %s" % r.email) + return HttpResponseRedirect('..') + else: + authoridstring = request.GET.get('authors', None) + revieweridstring = request.GET.get('reviewers', None) + form = BulkEmailForm(initial={'authors': authoridstring, 'reviewers': revieweridstring}) + + if authoridstring: + authors = list(User.objects.filter(patch_author__in=[int(x) for x in authoridstring.split(',')]).distinct()) + else: + authors = [] + if revieweridstring: + reviewers = list(User.objects.filter(patch_reviewer__in=[int(x) for x in revieweridstring.split(',')]).distinct()) + else: + reviewers = [] + + messages.add_message(request, messages.INFO, "Email will be sent from: %s" % request.user.email) + def _user_and_mail(u): + return "%s %s (%s)" % (u.first_name, u.last_name, u.email) + + if len(authors): + messages.add_message(request, messages.INFO, "The email will be sent to the following authors: %s" % ", ".join([_user_and_mail(u) for u in authors])) + if len(reviewers): + messages.add_message(request, messages.INFO, "The email will be sent to the following reviewers: %s" % ", ".join([_user_and_mail(u) for u in reviewers])) + + return render_to_response('base_form.html', { + 'cf': cf, + 'form': form, + 'title': 'Send email', + 'breadcrumbs': [{'title': cf.title, 'href': '/%s/' % cf.pk},], + }, context_instance=RequestContext(request)) diff --git a/pgcommitfest/urls.py b/pgcommitfest/urls.py index 488041d..b9e063c 100644 --- a/pgcommitfest/urls.py +++ b/pgcommitfest/urls.py @@ -16,6 +16,8 @@ urlpatterns = patterns('', url(r'^(\d+)/(\d+)/reviewer/(become|remove)/$', 'commitfest.views.reviewer'), url(r'^(\d+)/(\d+)/committer/(become|remove)/$', 'commitfest.views.committer'), url(r'^(\d+)/(\d+)/(comment|review)/', 'commitfest.views.comment'), + url(r'^(\d+)/send_email/$', 'commitfest.views.send_email'), + url(r'^(\d+)/\d+/send_email/$', 'commitfest.views.send_email'), url(r'^ajax/(\w+)/$', 'commitfest.ajax.main'), url(r'^selectable/', include('selectable.urls')), -- 2.39.5