From dba11a967743cd07f3e7f472a19e8a556ce7ae9a Mon Sep 17 00:00:00 2001 From: Julia Hansbrough Date: Thu, 3 Oct 2013 17:27:21 +0000 Subject: [PATCH] Implemented bulk email interface for new dashboard --- lms/djangoapps/instructor/views/api.py | 46 +++++++++++- lms/djangoapps/instructor/views/api_urls.py | 3 +- .../instructor/views/instructor_dashboard.py | 20 ++++- lms/djangoapps/instructor/views/legacy.py | 1 - lms/envs/common.py | 2 +- .../instructor_dashboard.coffee | 3 + .../instructor_dashboard/send_email.coffee | 73 +++++++++++++++++++ .../sass/course/instructor/_instructor_2.scss | 4 + .../instructor_dashboard_2/email.html | 65 +++++++++++++++++ .../instructor_dashboard_2.html | 6 ++ .../instructor_dashboard_2/send_email.html | 35 +++++++++ 11 files changed, 252 insertions(+), 6 deletions(-) create mode 100644 lms/static/coffee/src/instructor_dashboard/send_email.coffee create mode 100644 lms/templates/instructor/instructor_dashboard_2/email.html create mode 100644 lms/templates/instructor/instructor_dashboard_2/send_email.html diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index e0b047604e..96f0225b4c 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -40,6 +40,12 @@ import analytics.distributions import analytics.csvs import csv +from bulk_email.models import CourseEmail +from html_to_text import html_to_text +from bulk_email import tasks + +from pudb import set_trace + log = logging.getLogger(__name__) @@ -705,6 +711,45 @@ def list_forum_members(request, course_id): } return JsonResponse(response_payload) +@cache_control(no_cache=True, no_store=True, must_revalidate=True) +# todo check if staff is the desired access level +# todo do html and plaintext messages +@require_level('staff') +@require_query_params(send_to="sending to whom", subject="subject line", message="message text") +def send_email(request, course_id): + """ + Send an email to self, staff, or everyone involved in a course. + Query Paramaters: + - 'send_to' specifies what group the email should be sent to + - 'subject' specifies email's subject + - 'message' specifies email's content + """ + set_trace() + course = get_course_by_id(course_id) + has_instructor_access = has_access(request.user, course, 'instructor') + send_to = request.GET.get("send_to") + subject = request.GET.get("subject") + message = request.GET.get("message") + text_message = html_to_text(message) + if subject == "": + return HttpResponseBadRequest("Operation requires instructor access.") + email = CourseEmail( + course_id = course_id, + sender=request.user, + to_option=send_to, + subject=subject, + html_message=message, + text_message=text_message + ) + email.save() + tasks.delegate_email_batches.delay( + email.id, + request.user.id + ) + response_payload = { + 'course_id': course_id, + } + return JsonResponse(response_payload) @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @@ -769,7 +814,6 @@ def update_forum_role_membership(request, course_id): } return JsonResponse(response_payload) - @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) @require_level('staff') diff --git a/lms/djangoapps/instructor/views/api_urls.py b/lms/djangoapps/instructor/views/api_urls.py index 07af69558f..79cdcf7e69 100644 --- a/lms/djangoapps/instructor/views/api_urls.py +++ b/lms/djangoapps/instructor/views/api_urls.py @@ -2,7 +2,6 @@ Instructor API endpoint urls. """ - from django.conf.urls import patterns, url urlpatterns = patterns('', # nopep8 @@ -34,4 +33,6 @@ urlpatterns = patterns('', # nopep8 'instructor.views.api.update_forum_role_membership', name="update_forum_role_membership"), url(r'^proxy_legacy_analytics$', 'instructor.views.api.proxy_legacy_analytics', name="proxy_legacy_analytics"), + url(r'^send_email$', + 'instructor.views.api.send_email', name="send_email") ) diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py index 35f7257902..d463460052 100644 --- a/lms/djangoapps/instructor/views/instructor_dashboard.py +++ b/lms/djangoapps/instructor/views/instructor_dashboard.py @@ -11,6 +11,10 @@ from django.utils.html import escape from django.http import Http404 from django.conf import settings +from xmodule_modifiers import wrap_xmodule +from xmodule.html_module import HtmlDescriptor +from xblock.field_data import DictFieldData +from xblock.fields import ScopeIds from courseware.access import has_access from courseware.courses import get_course_by_id from django_comment_client.utils import has_forum_access @@ -18,7 +22,6 @@ from django_comment_common.models import FORUM_ROLE_ADMINISTRATOR from xmodule.modulestore.django import modulestore from student.models import CourseEnrollment - @ensure_csrf_cookie @cache_control(no_cache=True, no_store=True, must_revalidate=True) def instructor_dashboard_2(request, course_id): @@ -43,7 +46,8 @@ def instructor_dashboard_2(request, course_id): _section_membership(course_id, access), _section_student_admin(course_id, access), _section_data_download(course_id), - _section_analytics(course_id), + _section_send_email(course_id, access,course), + _section_analytics(course_id) ] enrollment_count = sections[0]['enrollment_count'] @@ -149,6 +153,18 @@ def _section_data_download(course_id): } return section_data +def _section_send_email(course_id, access,course): + """ Provide data for the corresponding bulk email section """ + html_module = HtmlDescriptor(course.system, DictFieldData({'data': ''}), ScopeIds(None, None, None, None)) + section_data = { + 'section_key': 'send_email', + 'section_display_name': _('Email'), + 'access': access, + 'send_email': reverse('send_email',kwargs={'course_id': course_id}), + 'editor': wrap_xmodule(html_module.get_html, html_module, 'xmodule_edit.html')() + } + return section_data + def _section_analytics(course_id): """ Provide data for the corresponding dashboard section """ diff --git a/lms/djangoapps/instructor/views/legacy.py b/lms/djangoapps/instructor/views/legacy.py index 53be1d3347..0978d020bf 100644 --- a/lms/djangoapps/instructor/views/legacy.py +++ b/lms/djangoapps/instructor/views/legacy.py @@ -62,7 +62,6 @@ from bulk_email.models import CourseEmail from html_to_text import html_to_text from bulk_email import tasks - log = logging.getLogger(__name__) # internal commands for managing forum roles: diff --git a/lms/envs/common.py b/lms/envs/common.py index 6a9be412e1..8e32681177 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -114,7 +114,7 @@ MITX_FEATURES = { # analytics experiments 'ENABLE_INSTRUCTOR_ANALYTICS': False, - 'ENABLE_INSTRUCTOR_EMAIL': False, + 'ENABLE_INSTRUCTOR_EMAIL': True, # enable analytics server. # WARNING: THIS SHOULD ALWAYS BE SET TO FALSE UNDER NORMAL diff --git a/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee b/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee index a7c803f8ac..edbbe6a017 100644 --- a/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee +++ b/lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee @@ -156,6 +156,9 @@ setup_instructor_dashboard_sections = (idash_content) -> , constructor: window.InstructorDashboard.sections.StudentAdmin $element: idash_content.find ".#{CSS_IDASH_SECTION}#student_admin" + , + constructor: window.InstructorDashboard.sections.Email + $element: idash_content.find ".#{CSS_IDASH_SECTION}#send_email" , constructor: window.InstructorDashboard.sections.Analytics $element: idash_content.find ".#{CSS_IDASH_SECTION}#analytics" diff --git a/lms/static/coffee/src/instructor_dashboard/send_email.coffee b/lms/static/coffee/src/instructor_dashboard/send_email.coffee new file mode 100644 index 0000000000..4746f1ffed --- /dev/null +++ b/lms/static/coffee/src/instructor_dashboard/send_email.coffee @@ -0,0 +1,73 @@ +# Email Section + +# imports from other modules. +# wrap in (-> ... apply) to defer evaluation +# such that the value can be defined later than this assignment (file load order). +plantTimeout = -> window.InstructorDashboard.util.plantTimeout.apply this, arguments +std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments + +class SendEmail + constructor: (@$container) -> + # gather elements + @$emailEditor = XModule.loadModule($('.xmodule_edit')); + @$send_to = @$container.find("select[name='send_to']'") + @$subject = @$container.find("input[name='subject']'") + #message = emailEditor.save()['data'] + @$btn_send = @$container.find("input[name='send']'") + @$task_response = @$container.find(".request-response") + @$request_response_error = @$container.find(".request-response-error") + + # attach click handlers + + @$btn_send.click => + + send_data = + action: 'send' + send_to: @$send_to.val() + subject: @$subject.val() + message: @$emailEditor.save()['data'] + #message: @$message.val() + + $.ajax + dataType: 'json' + url: @$btn_send.data 'endpoint' + data: send_data + success: (data) => @display_response "Your email was successfully queued for sending." + error: std_ajax_err => @fail_with_error "Error sending email." + + fail_with_error: (msg) -> + console.warn msg + @$task_response.empty() + @$request_response_error.empty() + @$request_response_error.text msg + + display_response: (data_from_server) -> + @$task_response.empty() + @$request_response_error.empty() + @$task_response.text("Your email was successfully queued for sending.") + + +# Email Section +class Email + # enable subsections. + constructor: (@$section) -> + # attach self to html + # so that instructor_dashboard.coffee can find this object + # to call event handlers like 'onClickTitle' + @$section.data 'wrapper', @ + + # isolate # initialize SendEmail subsection + plantTimeout 0, => new SendEmail @$section.find '.send-email' + + # handler for when the section title is clicked. + onClickTitle: -> + + +# export for use +# create parent namespaces if they do not already exist. +# abort if underscore can not be found. +if _? + _.defaults window, InstructorDashboard: {} + _.defaults window.InstructorDashboard, sections: {} + _.defaults window.InstructorDashboard.sections, + Email: Email diff --git a/lms/static/sass/course/instructor/_instructor_2.scss b/lms/static/sass/course/instructor/_instructor_2.scss index 1a4777d0e0..f631f8f3f7 100644 --- a/lms/static/sass/course/instructor/_instructor_2.scss +++ b/lms/static/sass/course/instructor/_instructor_2.scss @@ -181,6 +181,10 @@ section.instructor-dashboard-content-2 { } } +.instructor-dashboard-wrapper-2 section.idash-section#email { + // todo +} + .instructor-dashboard-wrapper-2 section.idash-section#course_info { .course-errors-wrapper { diff --git a/lms/templates/instructor/instructor_dashboard_2/email.html b/lms/templates/instructor/instructor_dashboard_2/email.html new file mode 100644 index 0000000000..9eaefea8ad --- /dev/null +++ b/lms/templates/instructor/instructor_dashboard_2/email.html @@ -0,0 +1,65 @@ +<%! from django.utils.translation import ugettext as _ %> +<%page args="section_data"/> + +

Email

+ +
+ + +
+ ${_("Please try not to email students more than once a day. Important things to consider before sending:")} +
    +
  • ${_("Have you read over the email to make sure it says everything you want to say?")}
  • +
  • ${_("Have you sent the email to yourself first to make sure you're happy with how it's displayed?")}
  • +
+ +
+ +
\ No newline at end of file diff --git a/lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html b/lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html index c209db0103..527143b65b 100644 --- a/lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html +++ b/lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html @@ -31,6 +31,12 @@ + + + + + + <%static:js group='module-descriptor-js'/> ## NOTE that instructor is set as the active page so that the instructor button lights up, even though this is the instructor_2 page. diff --git a/lms/templates/instructor/instructor_dashboard_2/send_email.html b/lms/templates/instructor/instructor_dashboard_2/send_email.html new file mode 100644 index 0000000000..4b5681f251 --- /dev/null +++ b/lms/templates/instructor/instructor_dashboard_2/send_email.html @@ -0,0 +1,35 @@ +<%! from django.utils.translation import ugettext as _ %> +<%page args="section_data"/> + + +
+

${_("Send Email")}

+ + +
+ + +
+ + + +
+ +
+
+
\ No newline at end of file