Merge pull request #1359 from edx/brian/bulk-email-rc
Bulk Email improvements for release
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
# Analytics Section
|
||||
###
|
||||
Analytics 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).
|
||||
###
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
# Course Info Section
|
||||
# This is the implementation of the simplest section
|
||||
# of the instructor dashboard.
|
||||
###
|
||||
Course Info Section
|
||||
This is the implementation of the simplest section
|
||||
of the instructor dashboard.
|
||||
|
||||
imports from other modules.
|
||||
wrap in (-> ... apply) to defer evaluation
|
||||
such that the value can be defined later than this assignment (file load order).
|
||||
###
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# Data Download Section
|
||||
###
|
||||
Data Download 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).
|
||||
###
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
# Instructor Dashboard Tab Manager
|
||||
# The instructor dashboard is broken into sections.
|
||||
# Only one section is visible at a time,
|
||||
# and is responsible for its own functionality.
|
||||
#
|
||||
# NOTE: plantTimeout (which is just setTimeout from util.coffee)
|
||||
# is used frequently in the instructor dashboard to isolate
|
||||
# failures. If one piece of code under a plantTimeout fails
|
||||
# then it will not crash the rest of the dashboard.
|
||||
#
|
||||
# NOTE: The instructor dashboard currently does not
|
||||
# use backbone. Just lots of jquery. This should be fixed.
|
||||
#
|
||||
# NOTE: Server endpoints in the dashboard are stored in
|
||||
# the 'data-endpoint' attribute of relevant html elements.
|
||||
# The urls are rendered there by a template.
|
||||
#
|
||||
# NOTE: For an example of what a section object should look like
|
||||
# see course_info.coffee
|
||||
###
|
||||
Instructor Dashboard Tab Manager
|
||||
|
||||
The instructor dashboard is broken into sections.
|
||||
|
||||
Only one section is visible at a time,
|
||||
and is responsible for its own functionality.
|
||||
|
||||
NOTE: plantTimeout (which is just setTimeout from util.coffee)
|
||||
is used frequently in the instructor dashboard to isolate
|
||||
failures. If one piece of code under a plantTimeout fails
|
||||
then it will not crash the rest of the dashboard.
|
||||
|
||||
NOTE: The instructor dashboard currently does not
|
||||
use backbone. Just lots of jquery. This should be fixed.
|
||||
|
||||
NOTE: Server endpoints in the dashboard are stored in
|
||||
the 'data-endpoint' attribute of relevant html elements.
|
||||
The urls are rendered there by a template.
|
||||
|
||||
NOTE: For an example of what a section object should look like
|
||||
see course_info.coffee
|
||||
|
||||
imports from other modules
|
||||
wrap in (-> ... apply) to defer evaluation
|
||||
such that the value can be defined later than this assignment (file load order).
|
||||
###
|
||||
|
||||
# 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
|
||||
|
||||
@@ -156,6 +161,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"
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# Membership Section
|
||||
###
|
||||
Membership 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).
|
||||
###
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
101
lms/static/coffee/src/instructor_dashboard/send_email.coffee
Normal file
101
lms/static/coffee/src/instructor_dashboard/send_email.coffee
Normal file
@@ -0,0 +1,101 @@
|
||||
###
|
||||
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']'")
|
||||
@$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 =>
|
||||
if @$subject.val() == ""
|
||||
alert gettext("Your message must have a subject.")
|
||||
else if @$emailEditor.save()['data'] == ""
|
||||
alert gettext("Your message cannot be blank.")
|
||||
else
|
||||
success_message = gettext("Your email was successfully queued for sending.")
|
||||
send_to = @$send_to.val().toLowerCase()
|
||||
if send_to == "myself"
|
||||
send_to = gettext("yourself")
|
||||
else if send_to == "staff"
|
||||
send_to = gettext("everyone who is staff or instructor on this course")
|
||||
else
|
||||
send_to = gettext("ALL (everyone who is enrolled in this course as student, staff, or instructor)")
|
||||
success_message = gettext("Your email was successfully queued for sending. Please note that for large public classes (~10k), it may take 1-2 hours to send all emails.")
|
||||
subject = gettext(@$subject.val())
|
||||
confirm_message = gettext("You are about to send an email titled \"#{subject}\" to #{send_to}. Is this OK?")
|
||||
if confirm confirm_message
|
||||
|
||||
send_data =
|
||||
action: 'send'
|
||||
send_to: @$send_to.val()
|
||||
subject: @$subject.val()
|
||||
message: @$emailEditor.save()['data']
|
||||
|
||||
$.ajax
|
||||
type: 'POST'
|
||||
dataType: 'json'
|
||||
url: @$btn_send.data 'endpoint'
|
||||
data: send_data
|
||||
success: (data) =>
|
||||
@display_response success_message
|
||||
|
||||
error: std_ajax_err =>
|
||||
@fail_with_error gettext('Error sending email.')
|
||||
|
||||
else
|
||||
@$task_response.empty()
|
||||
@$request_response_error.empty()
|
||||
|
||||
fail_with_error: (msg) ->
|
||||
console.warn msg
|
||||
@$task_response.empty()
|
||||
@$request_response_error.empty()
|
||||
@$request_response_error.text gettext(msg)
|
||||
$(".msg-confirm").css({"display":"none"})
|
||||
|
||||
display_response: (data_from_server) ->
|
||||
@$task_response.empty()
|
||||
@$request_response_error.empty()
|
||||
@$task_response.text(data_from_server)
|
||||
$(".msg-confirm").css({"display":"block"})
|
||||
|
||||
|
||||
# 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
|
||||
@@ -1,8 +1,11 @@
|
||||
# Student Admin Section
|
||||
###
|
||||
Student Admin 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).
|
||||
###
|
||||
|
||||
# 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
|
||||
plantInterval = -> window.InstructorDashboard.util.plantInterval.apply this, arguments
|
||||
std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments
|
||||
|
||||
@@ -219,6 +219,11 @@ $action-secondary-disabled-fg: $white;
|
||||
$header-graphic-super-color: $m-blue-d1;
|
||||
$header-graphic-sub-color: $m-gray-d2;
|
||||
|
||||
// State-based colors
|
||||
$error-color: $error-red;
|
||||
$warning-color: $m-pink;
|
||||
$confirm-color: $m-green;
|
||||
|
||||
// ====================
|
||||
|
||||
// MISC: visual horizontal rules
|
||||
|
||||
@@ -18,6 +18,74 @@
|
||||
right: 15px;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
// system feedback - messages
|
||||
.msg {
|
||||
border-radius: 1px;
|
||||
padding: $baseline/2 $baseline*0.75;
|
||||
margin-bottom: $baseline;
|
||||
font-weight: 600;
|
||||
|
||||
.copy {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
// TYPE: warning
|
||||
.msg-warning {
|
||||
border-top: 2px solid $warning-color;
|
||||
background: tint($warning-color,95%);
|
||||
|
||||
.copy {
|
||||
color: $warning-color;
|
||||
}
|
||||
}
|
||||
|
||||
// TYPE: confirm
|
||||
.msg-confirm {
|
||||
border-top: 2px solid $confirm-color;
|
||||
background: tint($confirm-color,95%);
|
||||
display: none;
|
||||
color: $confirm-color;
|
||||
}
|
||||
|
||||
// TYPE: confirm
|
||||
.msg-error {
|
||||
border-top: 2px solid $error-color;
|
||||
background: tint($error-color,95%);
|
||||
|
||||
.copy {
|
||||
color: $error-color;
|
||||
}
|
||||
}
|
||||
|
||||
// inline copy
|
||||
.copy-confirm {
|
||||
color: $confirm-color;
|
||||
}
|
||||
|
||||
.copy-warning {
|
||||
color: $warning-color;
|
||||
}
|
||||
|
||||
.copy-error {
|
||||
color: $error-color;
|
||||
}
|
||||
|
||||
.list-advice {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: $baseline 0;
|
||||
|
||||
.item {
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section.instructor-dashboard-content-2 {
|
||||
@@ -110,7 +178,6 @@ section.instructor-dashboard-content-2 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.instructor-dashboard-wrapper-2 section.idash-section#course_info {
|
||||
.course-errors-wrapper {
|
||||
margin-top: 2em;
|
||||
@@ -173,6 +240,24 @@ section.instructor-dashboard-content-2 {
|
||||
}
|
||||
}
|
||||
|
||||
.instructor-dashboard-wrapper-2 section.idash-section#send_email {
|
||||
// form fields
|
||||
.list-fields {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
.field {
|
||||
margin-bottom: $baseline;
|
||||
padding: 0;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.instructor-dashboard-wrapper-2 section.idash-section#membership {
|
||||
$half_width: $baseline * 20;
|
||||
@@ -470,3 +555,7 @@ section.instructor-dashboard-content-2 {
|
||||
right: $baseline;
|
||||
}
|
||||
}
|
||||
|
||||
input[name="subject"] {
|
||||
width:600px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user