Merge pull request #1359 from edx/brian/bulk-email-rc

Bulk Email improvements for release
This commit is contained in:
brianhw
2013-10-16 10:26:28 -07:00
56 changed files with 3988 additions and 817 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}