From 3dd7468365452083ee0bd4090cfa38c0749418fb Mon Sep 17 00:00:00 2001 From: James Tauber Date: Thu, 6 Sep 2012 22:36:40 -0400 Subject: [PATCH 01/22] first pass at models and view for table with selection --- symposion/reviews/models.py | 18 +++ symposion/reviews/urls.py | 3 +- symposion/reviews/views.py | 14 ++ .../reviews/result_notification.html | 132 ++++++++++++++++++ 4 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 symposion/templates/reviews/result_notification.html diff --git a/symposion/reviews/models.py b/symposion/reviews/models.py index 3fb405d3..8a7db88b 100644 --- a/symposion/reviews/models.py +++ b/symposion/reviews/models.py @@ -284,6 +284,24 @@ class Comment(models.Model): commented_at = models.DateTimeField(default=datetime.now) +class NotificationTemplate(models.Model): + + label = models.CharField(max_length=100) + subject = models.CharField(max_length=100) + body = models.TextField() + + +class ResultNotification(models.Model): + + proposal = models.ForeignKey("proposals.ProposalBase", related_name="notifications") + template = models.ForeignKey(NotificationTemplate, null=True, blank=True, on_delete=models.SET_NULL) + timestamp = models.DateTimeField(default=datetime.now) + to_address = models.EmailField() + from_address = models.EmailField() + subject = models.CharField(max_length=100) + body = models.TextField() + + def promote_proposal(proposal): if hasattr(proposal, "presentation") and proposal.presentation: diff --git a/symposion/reviews/urls.py b/symposion/reviews/urls.py index 6770cb88..8336dc04 100644 --- a/symposion/reviews/urls.py +++ b/symposion/reviews/urls.py @@ -5,10 +5,11 @@ urlpatterns = patterns("symposion.reviews.views", url(r"^section/(?P[\w\-]+)/$", "review_section", name="review_section"), url(r"^section/(?P[\w\-]+)/assignments/$", "review_section", {"assigned": True}, name="review_section_assignments"), url(r"^section/(?P[\w\-]+)/status/$", "review_status", name="review_status"), - url(r"^section/(?P[\w\-]+)/status/(?P[\w]+)/$", "review_status", name="review_status"), + url(r"^section/(?P[\w\-]+)/status/(?P\w+)/$", "review_status", name="review_status"), url(r"^section/(?P[\w\-]+)/list/(?P\d+)/$", "review_list", name="review_list_user"), url(r"^section/(?P[\w\-]+)/admin/$", "review_admin", name="review_admin"), url(r"^section/(?P[\w\-]+)/admin/accept/$", "review_bulk_accept", name="review_bulk_accept"), + url(r"^section/(?P[\w\-]+)/notification/(?P\w+)/$", "result_notification", name="result_notification"), url(r"^review/(?P\d+)/$", "review_detail", name="review_detail"), diff --git a/symposion/reviews/views.py b/symposion/reviews/views.py index 71cac478..ef90c2b0 100644 --- a/symposion/reviews/views.py +++ b/symposion/reviews/views.py @@ -369,3 +369,17 @@ def review_bulk_accept(request, section_slug): return render(request, "reviews/review_bulk_accept.html", { "form": form, }) + + +@login_required +def result_notification(request, section_slug, status): + if not request.user.has_perm("reviews.can_manage_%s" % section_slug): + return access_not_permitted(request) + + proposals = ProposalBase.objects.filter(kind__section__slug=section_slug, result__status=status).select_related("speaker__user", "result").select_subclasses() + + ctx = { + "section_slug": section_slug, + "proposals": proposals, + } + return render(request, "reviews/result_notification.html", ctx) diff --git a/symposion/templates/reviews/result_notification.html b/symposion/templates/reviews/result_notification.html new file mode 100644 index 00000000..f16993c3 --- /dev/null +++ b/symposion/templates/reviews/result_notification.html @@ -0,0 +1,132 @@ +{% extends "reviews/base.html" %} + +{% load i18n %} + +{% block extra_style %} + +{% endblock %} + +{% block body %} +

Result Notification

+ + 0 selected + + + + + + + + + + + + + {% for proposal in proposals %} + + + + + + + + + {% endfor %} + +
#{% trans "Speaker / Title" %}{% trans "Category" %}{% trans "Status" %}{% trans "Notified?" %}
{{ proposal.number }} + + {{ proposal.speaker }} +
+ {{ proposal.title }} +
+
{{ proposal.track }} + {% with proposal.result.status as status %} +
+ {% if status != "undecided" %} + {{ status }} + {% endif %} +
+ {% endwith %} +
+
+{% endblock %} + +{% block extra_script %} + +{% endblock %} From 4435d957fc7d9e1fa55baa260beb5f5b8e0979fa Mon Sep 17 00:00:00 2001 From: James Tauber Date: Thu, 6 Sep 2012 22:54:52 -0400 Subject: [PATCH 02/22] more tweaks to selection javascript but still has select all bug --- symposion/templates/reviews/result_notification.html | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/symposion/templates/reviews/result_notification.html b/symposion/templates/reviews/result_notification.html index f16993c3..6e29facc 100644 --- a/symposion/templates/reviews/result_notification.html +++ b/symposion/templates/reviews/result_notification.html @@ -62,10 +62,6 @@ var options = $.extend({}, $.fn.actions.defaults, opts); var actionCheckboxes = $(this); checker = function(checked) { - if (checked) { - } else { - reset(); - } $(actionCheckboxes).attr("checked", checked) .parent().parent().toggleClass(options.selectedClass, checked); } @@ -81,17 +77,12 @@ return value; }); } - reset = function() { - $(options.counterContainer).show(); - } - // Show counter by default - $(options.counterContainer).show(); // Check state of checkboxes and reinit state if needed $(this).filter(":checked").each(function(i) { $(this).parent().parent().toggleClass(options.selectedClass); updateCounter(); }); - $(options.allToggle).show().click(function() { + $(options.allToggle).click(function() { checker($(this).attr("checked")); updateCounter(); }); From 3c2a0de89deee0e532815c60ea8de470b705ce5a Mon Sep 17 00:00:00 2001 From: Luke Hatcher Date: Thu, 6 Sep 2012 23:51:54 -0400 Subject: [PATCH 03/22] change attr to prop --- symposion/templates/reviews/result_notification.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/symposion/templates/reviews/result_notification.html b/symposion/templates/reviews/result_notification.html index 6e29facc..b5467a06 100644 --- a/symposion/templates/reviews/result_notification.html +++ b/symposion/templates/reviews/result_notification.html @@ -62,13 +62,13 @@ var options = $.extend({}, $.fn.actions.defaults, opts); var actionCheckboxes = $(this); checker = function(checked) { - $(actionCheckboxes).attr("checked", checked) + $(actionCheckboxes).prop("checked", checked) .parent().parent().toggleClass(options.selectedClass, checked); } updateCounter = function() { var sel = $(actionCheckboxes).filter(":checked").length; $(options.counterContainer).html(sel); - $(options.allToggle).attr("checked", function() { + $(options.allToggle).prop("checked", function() { if (sel == actionCheckboxes.length) { value = true; } else { @@ -83,7 +83,7 @@ updateCounter(); }); $(options.allToggle).click(function() { - checker($(this).attr("checked")); + checker($(this).prop("checked")); updateCounter(); }); lastChecked = null; @@ -92,14 +92,14 @@ var target = event.target ? event.target : event.srcElement; if (lastChecked && $.data(lastChecked) != $.data(target) && event.shiftKey == true) { var inrange = false; - $(lastChecked).attr("checked", target.checked) + $(lastChecked).prop("checked", target.checked) .parent().parent().toggleClass(options.selectedClass, target.checked); $(actionCheckboxes).each(function() { if ($.data(this) == $.data(lastChecked) || $.data(this) == $.data(target)) { inrange = (inrange) ? false : true; } if (inrange) { - $(this).attr("checked", target.checked) + $(this).prop("checked", target.checked) .parent().parent().toggleClass(options.selectedClass, target.checked); } }); From 5fefff0a3dbd11c313b0973333e97d63182b7a23 Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Fri, 7 Sep 2012 18:11:43 -0600 Subject: [PATCH 04/22] added result_notification_prepare view and stubbed out result_notification_send --- symposion/reviews/urls.py | 1 + symposion/reviews/views.py | 38 ++++++++- .../reviews/result_notification.html | 82 ++++++++++--------- 3 files changed, 83 insertions(+), 38 deletions(-) diff --git a/symposion/reviews/urls.py b/symposion/reviews/urls.py index 8336dc04..0e2a2c6c 100644 --- a/symposion/reviews/urls.py +++ b/symposion/reviews/urls.py @@ -10,6 +10,7 @@ urlpatterns = patterns("symposion.reviews.views", url(r"^section/(?P[\w\-]+)/admin/$", "review_admin", name="review_admin"), url(r"^section/(?P[\w\-]+)/admin/accept/$", "review_bulk_accept", name="review_bulk_accept"), url(r"^section/(?P[\w\-]+)/notification/(?P\w+)/$", "result_notification", name="result_notification"), + url(r"^section/(?P[\w\-]+)/notification/(?P\w+)/prepare/$", "result_notification_prepare", name="result_notification_prepare"), url(r"^review/(?P\d+)/$", "review_detail", name="review_detail"), diff --git a/symposion/reviews/views.py b/symposion/reviews/views.py index ef90c2b0..8313a2bc 100644 --- a/symposion/reviews/views.py +++ b/symposion/reviews/views.py @@ -1,4 +1,5 @@ from django.db.models import Q +from django.http import HttpResponseBadRequest, HttpResponseNotAllowed from django.shortcuts import render, redirect, get_object_or_404 from django.views.decorators.http import require_POST @@ -377,9 +378,44 @@ def result_notification(request, section_slug, status): return access_not_permitted(request) proposals = ProposalBase.objects.filter(kind__section__slug=section_slug, result__status=status).select_related("speaker__user", "result").select_subclasses() - + ctx = { "section_slug": section_slug, + "status": status, "proposals": proposals, } return render(request, "reviews/result_notification.html", ctx) + + +@login_required +def result_notification_prepare(request, section_slug, status): + if request.method != "POST": + return HttpResponseNotAllowed(["POST"]) + + proposal_pks = [] + try: + for pk in request.POST.getlist("_selected_action"): + proposal_pks.append(int(pk)) + except ValueError: + return HttpResponseBadRequest() + proposals = ProposalBase.objects.filter( + kind__section__slug=section_slug, + result__status=status, + ) + proposals = proposals.filter(pk__in=proposal_pks) + proposals = proposals.select_related("speaker__user", "result") + proposals = proposals.select_subclasses() + + ctx = { + "section_slug": section_slug, + "status": status, + "proposals": proposals, + } + return render(request, "reviews/result_notification_prepare.html", ctx) + + +def result_notification_send(request, section_slug, status): + if request.method != "POST": + return HttpResponseNotAllowed(["POST"]) + + # @@@ discuss with jtauber about how to handle this diff --git a/symposion/templates/reviews/result_notification.html b/symposion/templates/reviews/result_notification.html index b5467a06..d0960589 100644 --- a/symposion/templates/reviews/result_notification.html +++ b/symposion/templates/reviews/result_notification.html @@ -15,44 +15,52 @@ 0 selected - - - - - - - - - + - - {% for proposal in proposals %} - - - - - - - - - {% endfor %} - -
#{% trans "Speaker / Title" %}{% trans "Category" %}{% trans "Status" %}{% trans "Notified?" %}
{{ proposal.number }} - - {{ proposal.speaker }} -
- {{ proposal.title }} -
-
{{ proposal.track }} - {% with proposal.result.status as status %} -
- {% if status != "undecided" %} - {{ status }} - {% endif %} -
- {% endwith %} -
-
+ {% csrf_token %} + + + + + + + + + + + + + {% for proposal in proposals %} + + + + + + + + + {% endfor %} + +
#{% trans "Speaker / Title" %}{% trans "Category" %}{% trans "Status" %}{% trans "Notified?" %}
{{ proposal.number }} + + {{ proposal.speaker }} +
+ {{ proposal.title }} +
+
{{ proposal.track }} + {% with proposal.result.status as status %} +
+ {% if status != "undecided" %} + {{ status }} + {% endif %} +
+ {% endwith %} +
+
+ + + + {% endblock %} {% block extra_script %} From aa8db3c919a1922f2b0c519f6cc3e2e5de6edad3 Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Sat, 8 Sep 2012 16:13:20 -0600 Subject: [PATCH 05/22] added notification_templates to result_notification context --- symposion/reviews/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/symposion/reviews/views.py b/symposion/reviews/views.py index 8313a2bc..098adbcf 100644 --- a/symposion/reviews/views.py +++ b/symposion/reviews/views.py @@ -378,11 +378,13 @@ def result_notification(request, section_slug, status): return access_not_permitted(request) proposals = ProposalBase.objects.filter(kind__section__slug=section_slug, result__status=status).select_related("speaker__user", "result").select_subclasses() + notification_templates = NotificationTemplate.objects.all() ctx = { "section_slug": section_slug, "status": status, "proposals": proposals, + "notification_templates": notification_templates, } return render(request, "reviews/result_notification.html", ctx) From 1c727a989bfb2d82b10f6b108e4339993a389851 Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Sat, 8 Sep 2012 16:14:27 -0600 Subject: [PATCH 06/22] added missing NotificationTemplate import --- symposion/reviews/views.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/symposion/reviews/views.py b/symposion/reviews/views.py index 098adbcf..06f8b8a6 100644 --- a/symposion/reviews/views.py +++ b/symposion/reviews/views.py @@ -12,7 +12,9 @@ from symposion.utils.mail import send_email from symposion.reviews.forms import ReviewForm, SpeakerCommentForm from symposion.reviews.forms import BulkPresentationForm -from symposion.reviews.models import ReviewAssignment, Review, LatestVote, ProposalResult +from symposion.reviews.models import ( + ReviewAssignment, Review, LatestVote, ProposalResult, NotificationTemplate +) def access_not_permitted(request): From 6186a913df15c8c2316bff6372cf12ad5dba76a4 Mon Sep 17 00:00:00 2001 From: James Tauber Date: Sat, 8 Sep 2012 18:19:06 -0400 Subject: [PATCH 07/22] initial template selection tweaks to result notification --- .../templates/reviews/result_notification.html | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/symposion/templates/reviews/result_notification.html b/symposion/templates/reviews/result_notification.html index d0960589..83e75bba 100644 --- a/symposion/templates/reviews/result_notification.html +++ b/symposion/templates/reviews/result_notification.html @@ -13,12 +13,25 @@ {% block body %}

Result Notification

- 0 selected
{% csrf_token %} +

+ Select one or more proposals (0 currently selected) +
+ then pick an email template + +
+ +

+ @@ -57,9 +70,6 @@ {% endfor %}
- - -
{% endblock %} From df0ea0655cf333934e58b790dee0c44a8a75f95e Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Sat, 8 Sep 2012 16:29:17 -0600 Subject: [PATCH 08/22] implemented more result notification behavior --- symposion/reviews/urls.py | 1 + symposion/reviews/views.py | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/symposion/reviews/urls.py b/symposion/reviews/urls.py index 0e2a2c6c..b0968bca 100644 --- a/symposion/reviews/urls.py +++ b/symposion/reviews/urls.py @@ -11,6 +11,7 @@ urlpatterns = patterns("symposion.reviews.views", url(r"^section/(?P[\w\-]+)/admin/accept/$", "review_bulk_accept", name="review_bulk_accept"), url(r"^section/(?P[\w\-]+)/notification/(?P\w+)/$", "result_notification", name="result_notification"), url(r"^section/(?P[\w\-]+)/notification/(?P\w+)/prepare/$", "result_notification_prepare", name="result_notification_prepare"), + url(r"^section/(?P[\w\-]+)/notification/(?P\w+)/send/$", "result_notification_send", name="result_notification_send"), url(r"^review/(?P\d+)/$", "review_detail", name="review_detail"), diff --git a/symposion/reviews/views.py b/symposion/reviews/views.py index 06f8b8a6..f7c4fe9c 100644 --- a/symposion/reviews/views.py +++ b/symposion/reviews/views.py @@ -414,6 +414,7 @@ def result_notification_prepare(request, section_slug, status): "section_slug": section_slug, "status": status, "proposals": proposals, + "proposal_pks": " ".join(proposal_pks), } return render(request, "reviews/result_notification_prepare.html", ctx) @@ -422,4 +423,22 @@ def result_notification_send(request, section_slug, status): if request.method != "POST": return HttpResponseNotAllowed(["POST"]) - # @@@ discuss with jtauber about how to handle this + if "proposal_pks" not in request.POST: + return HttpResponseBadRequest() + + try: + proposal_pks = [int(pk) for pk in request.POST["proposal_pks"]] + except ValueError: + return HttpResponseBadRequest() + + proposals = ProposalBase.objects.filter( + kind__section__slug=section_slug, + result__status=status, + ) + proposals = proposals.filter(pk__in=proposal_pks) + proposals = proposals.select_related("speaker__user", "result") + proposals = proposals.select_subclasses() + + # create ResultNotification objects and send + + return redirect("result_notification", section_slug=section_slug, status=status) From 2b0b9865304c1afad98190ed784b29ae2fc3d87b Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Sat, 8 Sep 2012 16:30:35 -0600 Subject: [PATCH 09/22] fixed pk joining --- symposion/reviews/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/symposion/reviews/views.py b/symposion/reviews/views.py index f7c4fe9c..9544c886 100644 --- a/symposion/reviews/views.py +++ b/symposion/reviews/views.py @@ -414,7 +414,7 @@ def result_notification_prepare(request, section_slug, status): "section_slug": section_slug, "status": status, "proposals": proposals, - "proposal_pks": " ".join(proposal_pks), + "proposal_pks": " ".join([str(pk) for pk in proposal_pks]), } return render(request, "reviews/result_notification_prepare.html", ctx) From 5f8e8f195cdbef872fddf9824ed9730f70cbb621 Mon Sep 17 00:00:00 2001 From: James Tauber Date: Sat, 8 Sep 2012 18:32:06 -0400 Subject: [PATCH 10/22] get template select sending proper name/value --- symposion/templates/reviews/result_notification.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/symposion/templates/reviews/result_notification.html b/symposion/templates/reviews/result_notification.html index 83e75bba..89be268a 100644 --- a/symposion/templates/reviews/result_notification.html +++ b/symposion/templates/reviews/result_notification.html @@ -22,8 +22,8 @@ Select one or more proposals (0 currently selected)
then pick an email template - + {% for template in notification_templates %} {% endfor %} From 1f9ceed0e1f2723138aadb6d1c8fec361732a051 Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Sat, 8 Sep 2012 16:35:38 -0600 Subject: [PATCH 11/22] added notification template handling in result_notification_prepare --- symposion/reviews/views.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/symposion/reviews/views.py b/symposion/reviews/views.py index 9544c886..fc319456 100644 --- a/symposion/reviews/views.py +++ b/symposion/reviews/views.py @@ -396,6 +396,12 @@ def result_notification_prepare(request, section_slug, status): if request.method != "POST": return HttpResponseNotAllowed(["POST"]) + notification_template_pk = request.POST.get("notification_template", "") + if notification_template_pk: + notification_template = NotificationTemplate.objects.get(pk=notification_template_pk) + else: + notification_template = None + proposal_pks = [] try: for pk in request.POST.getlist("_selected_action"): @@ -413,6 +419,7 @@ def result_notification_prepare(request, section_slug, status): ctx = { "section_slug": section_slug, "status": status, + "notification_template": notification_template, "proposals": proposals, "proposal_pks": " ".join([str(pk) for pk in proposal_pks]), } From 8cb9a41f410a03c11ca2e0e0414cfbdbe3304ead Mon Sep 17 00:00:00 2001 From: James Tauber Date: Sat, 8 Sep 2012 18:39:38 -0400 Subject: [PATCH 12/22] first pass at result notification prepare template --- .../reviews/result_notification.html | 1 - .../reviews/result_notification_prepare.html | 42 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 symposion/templates/reviews/result_notification_prepare.html diff --git a/symposion/templates/reviews/result_notification.html b/symposion/templates/reviews/result_notification.html index 89be268a..37e29462 100644 --- a/symposion/templates/reviews/result_notification.html +++ b/symposion/templates/reviews/result_notification.html @@ -13,7 +13,6 @@ {% block body %}

Result Notification

-
{% csrf_token %} diff --git a/symposion/templates/reviews/result_notification_prepare.html b/symposion/templates/reviews/result_notification_prepare.html new file mode 100644 index 00000000..f87cc5bc --- /dev/null +++ b/symposion/templates/reviews/result_notification_prepare.html @@ -0,0 +1,42 @@ +{% extends "reviews/base.html" %} + +{% load i18n %} + +{% block body %} +

Result Notification Prepare

+ +
+
+

Proposals

+ + {% for proposal in proposals %} + + + + {% endfor %} +
+ {{ proposal.speaker }} ({{ proposal.speaker.email }}) +
+ {{ proposal.title }} +
+
+
+

Email

+ + + + Subject: + +
+ Body: + +
+ + + + +
+ +{% endblock %} From b754bb9addf2ca31b0da3ca52f7bd07c71391d56 Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Sat, 8 Sep 2012 16:40:00 -0600 Subject: [PATCH 13/22] fixed proposal pks and added notification template to send --- symposion/reviews/views.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/symposion/reviews/views.py b/symposion/reviews/views.py index fc319456..eb1fba2f 100644 --- a/symposion/reviews/views.py +++ b/symposion/reviews/views.py @@ -396,12 +396,6 @@ def result_notification_prepare(request, section_slug, status): if request.method != "POST": return HttpResponseNotAllowed(["POST"]) - notification_template_pk = request.POST.get("notification_template", "") - if notification_template_pk: - notification_template = NotificationTemplate.objects.get(pk=notification_template_pk) - else: - notification_template = None - proposal_pks = [] try: for pk in request.POST.getlist("_selected_action"): @@ -416,12 +410,18 @@ def result_notification_prepare(request, section_slug, status): proposals = proposals.select_related("speaker__user", "result") proposals = proposals.select_subclasses() + notification_template_pk = request.POST.get("notification_template", "") + if notification_template_pk: + notification_template = NotificationTemplate.objects.get(pk=notification_template_pk) + else: + notification_template = None + ctx = { "section_slug": section_slug, "status": status, "notification_template": notification_template, "proposals": proposals, - "proposal_pks": " ".join([str(pk) for pk in proposal_pks]), + "proposal_pks": ",".join([str(pk) for pk in proposal_pks]), } return render(request, "reviews/result_notification_prepare.html", ctx) @@ -434,7 +434,7 @@ def result_notification_send(request, section_slug, status): return HttpResponseBadRequest() try: - proposal_pks = [int(pk) for pk in request.POST["proposal_pks"]] + proposal_pks = [int(pk) for pk in request.POST["proposal_pks"].split(",")] except ValueError: return HttpResponseBadRequest() @@ -446,6 +446,12 @@ def result_notification_send(request, section_slug, status): proposals = proposals.select_related("speaker__user", "result") proposals = proposals.select_subclasses() + notification_template_pk = request.POST.get("notification_template", "") + if notification_template_pk: + notification_template = NotificationTemplate.objects.get(pk=notification_template_pk) + else: + notification_template = None + # create ResultNotification objects and send return redirect("result_notification", section_slug=section_slug, status=status) From 3d362300bb02d00e9904fe37a9d91467636bd9af Mon Sep 17 00:00:00 2001 From: James Tauber Date: Sat, 8 Sep 2012 18:49:08 -0400 Subject: [PATCH 14/22] improved styling on email form --- .../templates/reviews/result_notification_prepare.html | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/symposion/templates/reviews/result_notification_prepare.html b/symposion/templates/reviews/result_notification_prepare.html index f87cc5bc..a6a29a57 100644 --- a/symposion/templates/reviews/result_notification_prepare.html +++ b/symposion/templates/reviews/result_notification_prepare.html @@ -25,13 +25,11 @@
- Subject: - + +
- Body: - + +
From ce3009d57374edb8f9ab1636526ba4306cf20d05 Mon Sep 17 00:00:00 2001 From: James Tauber Date: Sat, 8 Sep 2012 18:56:38 -0400 Subject: [PATCH 15/22] help text on {{ proposal }} and more explicit send button --- .../templates/reviews/result_notification_prepare.html | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/symposion/templates/reviews/result_notification_prepare.html b/symposion/templates/reviews/result_notification_prepare.html index a6a29a57..c417a18a 100644 --- a/symposion/templates/reviews/result_notification_prepare.html +++ b/symposion/templates/reviews/result_notification_prepare.html @@ -23,7 +23,6 @@

Email

- @@ -33,7 +32,11 @@
- +

+ If the Body includes the string {% templatetag openvariable %} proposal {% templatetag closevariable %} then it will be + replaced with the title of the proposal when the email is sent. +

+
From 733c4adc530b056552c5e11e357814e774a0aa39 Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Sat, 8 Sep 2012 17:02:51 -0600 Subject: [PATCH 16/22] create ResultNotification objects and send mail --- symposion/reviews/models.py | 4 ++++ symposion/reviews/views.py | 23 ++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/symposion/reviews/models.py b/symposion/reviews/models.py index 8a7db88b..a6b16d9f 100644 --- a/symposion/reviews/models.py +++ b/symposion/reviews/models.py @@ -300,6 +300,10 @@ class ResultNotification(models.Model): from_address = models.EmailField() subject = models.CharField(max_length=100) body = models.TextField() + + @property + def email_args(self): + return (self.subject, self.body, self.from_address, [self.to_address]) def promote_proposal(proposal): diff --git a/symposion/reviews/views.py b/symposion/reviews/views.py index eb1fba2f..b4902ee4 100644 --- a/symposion/reviews/views.py +++ b/symposion/reviews/views.py @@ -1,3 +1,6 @@ +import re + +from django.core.mail import send_mass_mail from django.db.models import Q from django.http import HttpResponseBadRequest, HttpResponseNotAllowed from django.shortcuts import render, redirect, get_object_or_404 @@ -13,7 +16,8 @@ from symposion.utils.mail import send_email from symposion.reviews.forms import ReviewForm, SpeakerCommentForm from symposion.reviews.forms import BulkPresentationForm from symposion.reviews.models import ( - ReviewAssignment, Review, LatestVote, ProposalResult, NotificationTemplate + ReviewAssignment, Review, LatestVote, ProposalResult, NotificationTemplate, + ResultNotification ) @@ -430,7 +434,7 @@ def result_notification_send(request, section_slug, status): if request.method != "POST": return HttpResponseNotAllowed(["POST"]) - if "proposal_pks" not in request.POST: + if not all([k in request.POST for k in ["proposal_pks", "subject", "body"]]) return HttpResponseBadRequest() try: @@ -452,6 +456,19 @@ def result_notification_send(request, section_slug, status): else: notification_template = None - # create ResultNotification objects and send + emails = [] + + for proposal in proposals: + rn = ResultNotification() + rn.proposal = proposal + rn.template = notification_template + rn.to_address = proposal.speaker_email + rn.from_address = settings.DEFAULT_FROM_EMAIL + rn.subject = request.POST["subject"] + rn.body = re.sub(r"{{\s*proposal\s*}}", proposal.title, request.POST["body"]) + rn.save() + emails.append(rn.email_args) + + send_mass_mail(emails) return redirect("result_notification", section_slug=section_slug, status=status) From 7b25f580872582550076b1cca53bf0d4b2e6ba4d Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Sat, 8 Sep 2012 17:04:28 -0600 Subject: [PATCH 17/22] fixed syntax error --- symposion/reviews/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/symposion/reviews/views.py b/symposion/reviews/views.py index b4902ee4..6cd3846f 100644 --- a/symposion/reviews/views.py +++ b/symposion/reviews/views.py @@ -434,7 +434,7 @@ def result_notification_send(request, section_slug, status): if request.method != "POST": return HttpResponseNotAllowed(["POST"]) - if not all([k in request.POST for k in ["proposal_pks", "subject", "body"]]) + if not all([k in request.POST for k in ["proposal_pks", "subject", "body"]]): return HttpResponseBadRequest() try: From e7c6055b3fc6f9910d59791d4f34967b15e5927d Mon Sep 17 00:00:00 2001 From: James Tauber Date: Sat, 8 Sep 2012 19:07:34 -0400 Subject: [PATCH 18/22] added cancel and disable next --- symposion/templates/reviews/result_notification.html | 7 ++++++- .../templates/reviews/result_notification_prepare.html | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/symposion/templates/reviews/result_notification.html b/symposion/templates/reviews/result_notification.html index 37e29462..655a3cb5 100644 --- a/symposion/templates/reviews/result_notification.html +++ b/symposion/templates/reviews/result_notification.html @@ -28,7 +28,7 @@ {% endfor %}
- +

@@ -93,6 +93,11 @@ } return value; }); + if (sel == 0) { + $("#next-button").prop("disabled", true); + } else { + $("#next-button").prop("disabled", false); + } } // Check state of checkboxes and reinit state if needed $(this).filter(":checked").each(function(i) { diff --git a/symposion/templates/reviews/result_notification_prepare.html b/symposion/templates/reviews/result_notification_prepare.html index c417a18a..1a14d563 100644 --- a/symposion/templates/reviews/result_notification_prepare.html +++ b/symposion/templates/reviews/result_notification_prepare.html @@ -37,6 +37,7 @@ replaced with the title of the proposal when the email is sent.

+ Cancel From aafc4e003eacf138e7d76b945bfa64f7b43204c8 Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Sat, 8 Sep 2012 17:17:00 -0600 Subject: [PATCH 19/22] protected views from being used by wrong people --- symposion/reviews/views.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/symposion/reviews/views.py b/symposion/reviews/views.py index 6cd3846f..0174b28d 100644 --- a/symposion/reviews/views.py +++ b/symposion/reviews/views.py @@ -400,6 +400,9 @@ def result_notification_prepare(request, section_slug, status): if request.method != "POST": return HttpResponseNotAllowed(["POST"]) + if not request.user.has_perm("reviews.can_manage_%s" % section_slug): + return access_not_permitted(request) + proposal_pks = [] try: for pk in request.POST.getlist("_selected_action"): @@ -430,10 +433,14 @@ def result_notification_prepare(request, section_slug, status): return render(request, "reviews/result_notification_prepare.html", ctx) +@login_required def result_notification_send(request, section_slug, status): if request.method != "POST": return HttpResponseNotAllowed(["POST"]) + if not request.user.has_perm("reviews.can_manage_%s" % section_slug): + return access_not_permitted(request) + if not all([k in request.POST for k in ["proposal_pks", "subject", "body"]]): return HttpResponseBadRequest() From 4c38b66d638698185f0a6495c4090830b03ae897 Mon Sep 17 00:00:00 2001 From: James Tauber Date: Sat, 8 Sep 2012 19:21:08 -0400 Subject: [PATCH 20/22] csrf token --- symposion/templates/reviews/result_notification_prepare.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/symposion/templates/reviews/result_notification_prepare.html b/symposion/templates/reviews/result_notification_prepare.html index 1a14d563..b6e8ff24 100644 --- a/symposion/templates/reviews/result_notification_prepare.html +++ b/symposion/templates/reviews/result_notification_prepare.html @@ -24,6 +24,9 @@

Email

+ + {% csrf_token %} +
From e37b395b853f2498c87e7f498b5e6daf585230ef Mon Sep 17 00:00:00 2001 From: James Tauber Date: Sat, 8 Sep 2012 19:21:16 -0400 Subject: [PATCH 21/22] nav for result notification --- symposion/templates/reviews/base.html | 5 +++++ symposion/templates/reviews/result_notification.html | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/symposion/templates/reviews/base.html b/symposion/templates/reviews/base.html index 4344f06a..f9334f1a 100644 --- a/symposion/templates/reviews/base.html +++ b/symposion/templates/reviews/base.html @@ -84,6 +84,11 @@ {% trans "Voting Status" %} + {% if request.user.is_staff %} +
  • + Result Notification +
  • + {% endif %} {% endfor %} {% endblock %} diff --git a/symposion/templates/reviews/result_notification.html b/symposion/templates/reviews/result_notification.html index 655a3cb5..cbd5c4c2 100644 --- a/symposion/templates/reviews/result_notification.html +++ b/symposion/templates/reviews/result_notification.html @@ -11,6 +11,13 @@ {% endblock %} {% block body %} + + +

    Result Notification

    @@ -64,6 +71,7 @@ {% endwith %}
    {% endfor %} From c87225674f6eadc7f2cd1c3d5b11a3fe88a03124 Mon Sep 17 00:00:00 2001 From: Brian Rosner Date: Sat, 8 Sep 2012 17:29:18 -0600 Subject: [PATCH 22/22] added admin for NotificationTemplate --- symposion/reviews/admin.py | 6 ++++++ symposion/reviews/models.py | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 symposion/reviews/admin.py diff --git a/symposion/reviews/admin.py b/symposion/reviews/admin.py new file mode 100644 index 00000000..c7273ad1 --- /dev/null +++ b/symposion/reviews/admin.py @@ -0,0 +1,6 @@ +from django.contrib import admin + +from symposion.reviews.models import NotificationTemplate + + +admin.site.register(NotificationTemplate) diff --git a/symposion/reviews/models.py b/symposion/reviews/models.py index a6b16d9f..3de03fa5 100644 --- a/symposion/reviews/models.py +++ b/symposion/reviews/models.py @@ -288,7 +288,13 @@ class NotificationTemplate(models.Model): label = models.CharField(max_length=100) subject = models.CharField(max_length=100) - body = models.TextField() + body = models.TextField( + help_text=( + "If the Body includes the string {{ proposal }} " + "then it will be replaced with the title of the proposal when the " + "email is sent." + ) + ) class ResultNotification(models.Model):
    + {% if proposal.notifications.exists %}yes{% endif %}