diff options
author | Magnus Hagander | 2014-04-28 16:04:52 +0000 |
---|---|---|
committer | Magnus Hagander | 2014-04-28 16:04:52 +0000 |
commit | a059717d3c831afe047a3ecda8e2610eb0e6fd57 (patch) | |
tree | df49b4fac472388de89827313c01302d659aff89 | |
parent | 4a4e8c46ec9834c43b0a0705905d5ee12574694a (diff) |
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.
-rw-r--r-- | pgcommitfest/commitfest/forms.py | 11 | ||||
-rw-r--r-- | pgcommitfest/commitfest/templates/commitfest.html | 48 | ||||
-rw-r--r-- | pgcommitfest/commitfest/templates/patch_commands.inc | 11 | ||||
-rw-r--r-- | pgcommitfest/commitfest/views.py | 63 | ||||
-rw-r--r-- | 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 @@ <th>Reviewers</th> <th>{%if p.is_open%}<a href="#" style="color:#333333;" onclick="return sortpatches(1);">Latest activity</a>{%if sortkey = 1%}<div style="float:right;"><i class="icon-arrow-down"></i></div>{%endif%}{%else%}Latest activity{%endif%}</th> <th>{%if p.is_open%}<a href="#" style="color:#333333;" onclick="return sortpatches(2);">Latest mail</a>{%if sortkey = 2%}<div style="float:right;"><i class="icon-arrow-down"></i></div>{%endif%}{%else%}Latest mail{%endif%}</th> +{%if user.is_staff%} + <th>Select</th> +{%endif%} </tr> </thead> <tbody> @@ -60,7 +63,7 @@ {%if grouping%} {%ifchanged p.topic%} - <tr><th colspan="6">{{p.topic}}</th></tr> + <tr><th colspan="{%if user.is_staff%}7{%else%}6{%endif%}">{{p.topic}}</th></tr> {%endifchanged%} {%endif%} <tr> @@ -70,14 +73,53 @@ <td>{{p.reviewer_names|default:''}}</td> <td style="white-space: nowrap;">{{p.modified|date:"Y-m-d"}}<br/>{{p.modified|date:"H:i"}}</td> <td style="white-space: nowrap;">{{p.lastmail|date:"Y-m-d"}}<br/>{{p.lastmail|date:"H:i"}}</td> +{%if user.is_staff%} + <td style="white-space: nowrap;"><input type="checkbox" class="sender_checkbox" id="send_authors_{{p.id}}">Author<br/><input type="checkbox" class="sender_checkbox" id="send_reviewers_{{p.id}}">Reviewer</td> +{%endif%} </tr> {%endfor%} </tbody> </table> +<div> {%if cf.isopen or user.is_staff %} -<p> <a class="btn btn-default" href="new/">New patch</a> -</p> {%endif%} +{%if user.is_staff%} + <div class="btn-group dropup"> + <button type="button" class="btn btn-default dropdown-toggle " data-toggle="dropdown" href="#">Send mail <span class="caret"></span></button> + <ul class="dropdown-menu"> + <li><a href="javascript:send_selected()">Selected</a></li> + <li><a href="send_email/?reviewers={{openpatchids|join:","}}">All reviewers (open patches)</a></li> + <li><a href="send_email/?authors={{openpatchids|join:","}}">All authors (open patches)</a></li> + <li><a href="send_email/?authors={{openpatchids|join:","}}&reviewers={{openpatchids|join:","}}">All authors and reviewers (open patches)</a></li> + </ul> + </div> +{%endif%} +</div> +{%endblock%} + +{%block morescript%} +<script language="javascript"> +{%if user.is_staff%} + function send_selected() { + var authors = []; + var reviewers = []; + $('input.sender_checkbox').each(function(index, el) { + if (el.checked) { + if (el.id.indexOf('send_authors_') == 0) { + authors.push(el.id.substring(13)); + } else { + reviewers.push(el.id.substring(15)); + } + } + }); + if (authors.length==0 && reviewers.length==0) { + alert('Nothing to send.'); + return; + } + document.location.href = 'send_email/?authors=' + authors.join(',') + '&reviewers=' + reviewers.join(','); + } +{%endif%} +</script> {%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 @@ </ul> </div> +{%if request.user.is_staff%} +<div class="btn-group"> + <a class="btn btn-default dropdown-toggle" data-toggle="dropdown" href="#">Send private mail <span class="caret"></span></a> + <ul class="dropdown-menu"> + <li><a href="send_email/?authors={{patch.id}}">Send mail to authors</a></li> + <li><a href="send_email/?reviewers={{patch.id}}">Send mail to reviewers</a></li> + <li><a href="send_email/?authors={{patch.id}}&reviewers={{patch.id}}">Send mail to authors and reviewers</a></li> + </ul> +</div> +{%endif%} + </div>
\ 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 [email protected]_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')), |