summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMagnus Hagander2014-04-28 16:04:52 +0000
committerMagnus Hagander2014-04-28 16:04:52 +0000
commita059717d3c831afe047a3ecda8e2610eb0e6fd57 (patch)
treedf49b4fac472388de89827313c01302d659aff89
parent4a4e8c46ec9834c43b0a0705905d5ee12574694a (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.py11
-rw-r--r--pgcommitfest/commitfest/templates/commitfest.html48
-rw-r--r--pgcommitfest/commitfest/templates/patch_commands.inc11
-rw-r--r--pgcommitfest/commitfest/views.py63
-rw-r--r--pgcommitfest/urls.py2
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
+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')),