Implemented bulk email interface for new dashboard
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
|
||||
@@ -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 """
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
73
lms/static/coffee/src/instructor_dashboard/send_email.coffee
Normal file
73
lms/static/coffee/src/instructor_dashboard/send_email.coffee
Normal file
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
65
lms/templates/instructor/instructor_dashboard_2/email.html
Normal file
65
lms/templates/instructor/instructor_dashboard_2/email.html
Normal file
@@ -0,0 +1,65 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%page args="section_data"/>
|
||||
|
||||
<h2>Email</h2>
|
||||
|
||||
<form>
|
||||
<ul class="list-fields">
|
||||
<li class="field">
|
||||
<label for="id_to">${_("Send to:")}</label>
|
||||
<select id="id_to" name="to_option">
|
||||
<option value="myself">${_("Myself")}</option>
|
||||
%if to_option == "staff":
|
||||
<option value="staff" selected="selected">${_("Staff and instructors")}</option>
|
||||
%else:
|
||||
<option value="staff">${_("Staff and instructors")}</option>
|
||||
%endif
|
||||
%if to_option == "all":
|
||||
<option value="all" selected="selected">${_("All (students, staff and instructors)")}</option>
|
||||
%else:
|
||||
<option value="all">${_("All (students, staff and instructors)")}</option>
|
||||
%endif
|
||||
</select>
|
||||
</li>
|
||||
<li class="field">
|
||||
<label for="id_subject">${_("Subject: ")}</label>
|
||||
%if subject:
|
||||
<input type="text" id="id_subject" name="subject" maxlength="100" size="75" value="${subject}">
|
||||
%else:
|
||||
<input type="text" id="id_subject" name="subject" maxlength="100" size="75">
|
||||
%endif
|
||||
</li>
|
||||
|
||||
<li class="field">
|
||||
<label>Message:</label>
|
||||
<div class="email-editor">
|
||||
|
||||
<!--todo make this render the real way-->
|
||||
|
||||
<section class="xmodule_edit xmodule_HtmlDescriptor" data-type="HTMLEditingDescriptor">
|
||||
|
||||
|
||||
<section class="html-editor editor">
|
||||
<div class="row">
|
||||
<textarea class="tiny-mce"><p>hihihi</p></textarea>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
</div>
|
||||
<input type="hidden" name="message" value="">
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="submit-email-action">
|
||||
${_("Please try not to email students more than once a day. Important things to consider before sending:")}
|
||||
<ul class="list-advice">
|
||||
<li class="item">${_("Have you read over the email to make sure it says everything you want to say?")}</li>
|
||||
<li class="item">${_("Have you sent the email to yourself first to make sure you're happy with how it's displayed?")}</li>
|
||||
</ul>
|
||||
<input type="submit" name="send-email" value="Send email" data-endpoint="${ section_data[get_send_email_url']}">
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@@ -31,6 +31,12 @@
|
||||
<script type="text/javascript" src="${static.url('js/vendor/slick.grid.js')}"></script>
|
||||
<link rel="stylesheet" href="${static.url('css/vendor/slickgrid/smoothness/jquery-ui-1.8.16.custom.css')}">
|
||||
<link rel="stylesheet" href="${static.url('css/vendor/slickgrid/slick.grid.css')}">
|
||||
<script type="text/javascript" src="${static.url('js/vendor/CodeMirror/htmlmixed.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/vendor/CodeMirror/css.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/vendor/codemirror-compressed.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/vendor/tiny_mce/tiny_mce.js')}"></script>
|
||||
<script type="text/javascript" src="${static.url('js/vendor/tiny_mce/jquery.tinymce.js')}"></script>
|
||||
<%static:js group='module-descriptor-js'/>
|
||||
</%block>
|
||||
|
||||
## NOTE that instructor is set as the active page so that the instructor button lights up, even though this is the instructor_2 page.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%page args="section_data"/>
|
||||
|
||||
<script language="JavaScript" type="text/javascript">
|
||||
</script>
|
||||
<div class="vert-left send-email">
|
||||
<h2> ${_("Send Email")} </h2>
|
||||
<label for="id_to">${_("Send to:")}</label>
|
||||
<select id="id_to" name="send_to">
|
||||
<option value="myself">${_("Myself")}</option>
|
||||
%if to_option == "staff":
|
||||
<option value="staff" selected="selected">${_("Staff and instructors")}</option>
|
||||
%else:
|
||||
<option value="staff">${_("Staff and instructors")}</option>
|
||||
%endif
|
||||
%if to_option == "all":
|
||||
<option value="all" selected="selected">${_("All (students, staff and instructors)")}</option>
|
||||
%else:
|
||||
<option value="all">${_("All (students, staff and instructors)")}</option>
|
||||
%endif
|
||||
</select>
|
||||
<br/>
|
||||
<label for="id_subject">${_("Subject: ")}</label>
|
||||
<input type="text" id="id_subject" name="subject">
|
||||
<br/>
|
||||
<label>Message:</label>
|
||||
<div class="email-editor">
|
||||
${ section_data['editor'] }
|
||||
</div>
|
||||
<input type="hidden" name="message" value="">
|
||||
<br/>
|
||||
<input type="button" name="send" value="${_("Send")}" data-endpoint="${ section_data['send_email'] }" >
|
||||
<div class="request-response"></div>
|
||||
<div class="request-response-error"></div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user