');
+ $taskResSection.append($idsList);
+ for (j = 0, len1 = ids.length; j < len1; j++) {
+ identifier = ids[j];
+ $idsList.append($('', {
+ text: identifier
+ }));
+ }
+ return displayResponse.$task_response.append($taskResSection);
+ };
+ if (successes.length && dataFromServer.action === 'add') {
+ // Translators: A list of users appears after this sentence;
+ renderList(gettext('These users were successfully added as beta testers:'), (function() {
+ var j, len1, results;
+ results = [];
+ for (j = 0, len1 = successes.length; j < len1; j++) {
+ sr = successes[j];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (successes.length && dataFromServer.action === 'remove') {
+ // Translators: A list of users appears after this sentence;
+ renderList(gettext('These users were successfully removed as beta testers:'), (function() {
+ var j, len1, results;
+ results = [];
+ for (j = 0, len1 = successes.length; j < len1; j++) {
+ sr = successes[j];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (errors.length && dataFromServer.action === 'add') {
+ // Translators: A list of users appears after this sentence;
+ renderList(gettext('These users were not added as beta testers:'), (function() {
+ var j, len1, results;
+ results = [];
+ for (j = 0, len1 = errors.length; j < len1; j++) {
+ sr = errors[j];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (errors.length && dataFromServer.action === 'remove') {
+ // Translators: A list of users appears after this sentence;
+ renderList(gettext('These users were not removed as beta testers:'), (function() {
+ var j, len1, results;
+ results = [];
+ for (j = 0, len1 = errors.length; j < len1; j++) {
+ sr = errors[j];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (noUsers.length) {
+ noUsers.push($(
+ gettext('Users must create and activate their account before they can be promoted to beta tester.'))
+ );
+ return renderList(gettext('Could not find users associated with the following identifiers:'), (function() { // eslint-disable-line max-len
+ var j, len1, results;
+ results = [];
+ for (j = 0, len1 = noUsers.length; j < len1; j++) {
+ sr = noUsers[j];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ return renderList();
+ };
- return false
+ return betaTesterBulkAddition;
+ }());
- display_response: (data_from_server) ->
- @$results.empty()
- errors = []
- warnings = []
- result_from_server_is_success = true
+ BatchEnrollment = (function() {
+ function batchEnrollment($container) {
+ var batchEnroll = this;
+ this.$container = $container;
+ this.$identifier_input = this.$container.find("textarea[name='student-ids']");
+ this.$enrollment_button = this.$container.find('.enrollment-button');
+ this.$is_course_white_label = this.$container.find('#is_course_white_label').val();
+ this.$reason_field = this.$container.find("textarea[name='reason-field']");
+ this.$checkbox_autoenroll = this.$container.find("input[name='auto-enroll']");
+ this.$checkbox_emailstudents = this.$container.find("input[name='email-students']");
+ this.$task_response = this.$container.find('.request-response');
+ this.$request_response_error = this.$container.find('.request-response-error');
+ this.$enrollment_button.click(function(event) {
+ var sendData;
+ if (batchEnroll.$is_course_white_label === 'True') {
+ if (!batchEnroll.$reason_field.val()) {
+ batchEnroll.fail_with_error(gettext('Reason field should not be left blank.'));
+ return false;
+ }
+ }
+ emailStudents = batchEnroll.$checkbox_emailstudents.is(':checked');
+ sendData = {
+ action: $(event.target).data('action'),
+ identifiers: batchEnroll.$identifier_input.val(),
+ auto_enroll: batchEnroll.$checkbox_autoenroll.is(':checked'),
+ email_students: emailStudents,
+ reason: batchEnroll.$reason_field.val()
+ };
+ return $.ajax({
+ dataType: 'json',
+ type: 'POST',
+ url: $(event.target).data('endpoint'),
+ data: sendData,
+ success: function(data) {
+ return batchEnroll.display_response(data);
+ },
+ error: statusAjaxError(function() {
+ return batchEnroll.fail_with_error(gettext('Error enrolling/unenrolling users.'));
+ })
+ });
+ });
+ }
- if data_from_server.general_errors.length
- result_from_server_is_success = false
- for general_error in data_from_server.general_errors
- general_error['is_general_error'] = true
- errors.push general_error
+ batchEnrollment.prototype.clear_input = function() {
+ this.$identifier_input.val('');
+ this.$reason_field.val('');
+ this.$checkbox_emailstudents.attr('checked', true);
+ return this.$checkbox_autoenroll.attr('checked', true);
+ };
- if data_from_server.row_errors.length
- result_from_server_is_success = false
- for error in data_from_server.row_errors
- error['is_general_error'] = false
- errors.push error
+ batchEnrollment.prototype.fail_with_error = function(msg) {
+ this.clear_input();
+ this.$task_response.empty();
+ this.$request_response_error.empty();
+ return this.$request_response_error.text(msg);
+ };
- if data_from_server.warnings.length
- result_from_server_is_success = false
- for warning in data_from_server.warnings
- warning['is_general_error'] = false
- warnings.push warning
+ batchEnrollment.prototype.display_response = function(dataFromServer) {
+ var allowed, autoenrolled, enrolled, errors, errorsLabel,
+ invalidIdentifier, notenrolled, notunenrolled, renderList, sr, studentResults,
+ i, j, len, len1, ref, renderIdsLists,
+ displayResponse = this;
+ this.clear_input();
+ this.$task_response.empty();
+ this.$request_response_error.empty();
+ invalidIdentifier = [];
+ errors = [];
+ enrolled = [];
+ allowed = [];
+ autoenrolled = [];
+ notenrolled = [];
+ notunenrolled = [];
+ ref = dataFromServer.results;
+ for (i = 0, len = ref.length; i < len; i++) {
+ studentResults = ref[i];
+ if (studentResults.invalidIdentifier) {
+ invalidIdentifier.push(studentResults);
+ } else if (studentResults.error) {
+ errors.push(studentResults);
+ } else if (studentResults.after.enrollment) {
+ enrolled.push(studentResults);
+ } else if (studentResults.after.allowed) {
+ if (studentResults.after.auto_enroll) {
+ autoenrolled.push(studentResults);
+ } else {
+ allowed.push(studentResults);
+ }
+ } else if (dataFromServer.action === 'unenroll' &&
+ !studentResults.before.enrollment &&
+ !studentResults.before.allowed) {
+ notunenrolled.push(studentResults);
+ } else if (!studentResults.after.enrollment) {
+ notenrolled.push(studentResults);
+ } else {
+ console.warn('student results not reported to user'); // eslint-disable-line no-console
+ }
+ }
+ renderList = function(label, ids) {
+ var identifier, $idsList, $taskResSection, h, len3;
+ $taskResSection = $('', {
+ class: 'request-res-section'
+ });
+ $taskResSection.append($('', {
+ text: label
+ }));
+ $idsList = $('
');
+ $taskResSection.append($idsList);
+ for (h = 0, len3 = ids.length; h < len3; h++) {
+ identifier = ids[h];
+ $idsList.append($('', {
+ text: identifier
+ }));
+ }
+ return displayResponse.$task_response.append($taskResSection);
+ };
+ if (invalidIdentifier.length) {
+ renderList(gettext('The following email addresses and/or usernames are invalid:'), (function() {
+ var m, len4, results;
+ results = [];
+ for (m = 0, len4 = invalidIdentifier.length; m < len4; m++) {
+ sr = invalidIdentifier[m];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (errors.length) {
+ errorsLabel = (function() {
+ if (dataFromServer.action === 'enroll') {
+ return 'There was an error enrolling:';
+ } else if (dataFromServer.action === 'unenroll') {
+ return 'There was an error unenrolling:';
+ } else {
+ console.warn("unknown action from server '" + dataFromServer.action + "'"); // eslint-disable-line no-console, max-len
+ return 'There was an error processing:';
+ }
+ }());
+ renderIdsLists = function(errs) {
+ var srItem,
+ k = 0,
+ results = [];
+ for (k = 0, len = errs.length; k < len; k++) {
+ srItem = errs[k];
+ results.push(srItem.identifier);
+ }
+ return results;
+ };
+ for (j = 0, len1 = errors.length; j < len1; j++) {
+ studentResults = errors[j];
+ renderList(errorsLabel, renderIdsLists(errors));
+ }
+ }
+ if (enrolled.length && emailStudents) {
+ renderList(gettext('Successfully enrolled and sent email to the following users:'), (function() {
+ var k, len2, results;
+ results = [];
+ for (k = 0, len2 = enrolled.length; k < len2; k++) {
+ sr = enrolled[k];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (enrolled.length && !emailStudents) {
+ // Translators: A list of users appears after this sentence;
+ renderList(gettext('Successfully enrolled the following users:'), (function() {
+ var k, len2, results;
+ results = [];
+ for (k = 0, len2 = enrolled.length; k < len2; k++) {
+ sr = enrolled[k];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (allowed.length && emailStudents) {
+ // Translators: A list of users appears after this sentence;
+ renderList(gettext('Successfully sent enrollment emails to the following users. They will be allowed to enroll once they register:'), (function() { // eslint-disable-line max-len
+ var k, len2, results;
+ results = [];
+ for (k = 0, len2 = allowed.length; k < len2; k++) {
+ sr = allowed[k];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (allowed.length && !emailStudents) {
+ // Translators: A list of users appears after this sentence;
+ renderList(gettext('These users will be allowed to enroll once they register:'), (function() {
+ var k, len2, results;
+ results = [];
+ for (k = 0, len2 = allowed.length; k < len2; k++) {
+ sr = allowed[k];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (autoenrolled.length && emailStudents) {
+ // Translators: A list of users appears after this sentence;
+ renderList(gettext('Successfully sent enrollment emails to the following users. They will be enrolled once they register:'), (function() { // eslint-disable-line max-len
+ var k, len2, results;
+ results = [];
+ for (k = 0, len2 = autoenrolled.length; k < len2; k++) {
+ sr = autoenrolled[k];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (autoenrolled.length && !emailStudents) {
+ // Translators: A list of users appears after this sentence;
+ renderList(gettext('These users will be enrolled once they register:'), (function() {
+ var k, len2, results;
+ results = [];
+ for (k = 0, len2 = autoenrolled.length; k < len2; k++) {
+ sr = autoenrolled[k];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (notenrolled.length && emailStudents) {
+ // Translators: A list of users appears after this sentence;
+ renderList(gettext('Emails successfully sent. The following users are no longer enrolled in the course:'), (function() { // eslint-disable-line max-len
+ var k, len2, results;
+ results = [];
+ for (k = 0, len2 = notenrolled.length; k < len2; k++) {
+ sr = notenrolled[k];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (notenrolled.length && !emailStudents) {
+ // Translators: A list of users appears after this sentence;
+ renderList(gettext('The following users are no longer enrolled in the course:'), (function() {
+ var k, len2, results;
+ results = [];
+ for (k = 0, len2 = notenrolled.length; k < len2; k++) {
+ sr = notenrolled[k];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ if (notunenrolled.length) {
+ return renderList(gettext('These users were not affiliated with the course so could not be unenrolled:'), (function() { // eslint-disable-line max-len
+ var k, len2, results;
+ results = [];
+ for (k = 0, len2 = notunenrolled.length; k < len2; k++) {
+ sr = notunenrolled[k];
+ results.push(sr.identifier);
+ }
+ return results;
+ }()));
+ }
+ return renderList();
+ };
- render_response = (title, message, type, student_results) =>
- details = []
- for student_result in student_results
- if student_result.is_general_error
- details.push student_result.response
- else
- response_message = student_result.username + ' ('+ student_result.email + '): ' + ' (' + student_result.response + ')'
- details.push response_message
+ return batchEnrollment;
+ }());
- @$results.append @render_notification_view type, title, message, details
+ this.AuthList = (function() {
+ function authList($container, rolename) {
+ var authlist = this;
+ this.$container = $container;
+ this.rolename = rolename;
+ this.$display_table = this.$container.find('.auth-list-table');
+ this.$request_response_error = this.$container.find('.request-response-error');
+ this.$add_section = this.$container.find('.auth-list-add');
+ this.$allow_field = this.$add_section.find("input[name='email']");
+ this.$allow_button = this.$add_section.find("input[name='allow']");
+ this.$allow_button.click(function() {
+ authlist.access_change(authlist.$allow_field.val(), 'allow', function() {
+ return authlist.reload_auth_list();
+ });
+ return authlist.$allow_field.val('');
+ });
+ this.reload_auth_list();
+ }
- if errors.length
- render_response gettext('Errors'), gettext("The following errors were generated:"), 'error', errors
- if warnings.length
- render_response gettext('Warnings'), gettext("The following warnings were generated:"), 'warning', warnings
- if result_from_server_is_success
- render_response gettext('Success'), gettext("All accounts were created successfully."), 'confirmation', []
+ authList.prototype.reload_auth_list = function() {
+ var loadAuthList,
+ ths = this;
+ loadAuthList = function(data) {
+ var $tablePlaceholder, WHICH_CELL_IS_REVOKE, columns, grid, options, tableData;
+ ths.$request_response_error.empty();
+ ths.$display_table.empty();
+ options = {
+ enableCellNavigation: true,
+ enableColumnReorder: false,
+ forceFitColumns: true
+ };
+ WHICH_CELL_IS_REVOKE = 3;
+ columns = [
+ {
+ id: 'username',
+ field: 'username',
+ name: 'Username'
+ }, {
+ id: 'email',
+ field: 'email',
+ name: 'Email'
+ }, {
+ id: 'first_name',
+ field: 'first_name',
+ name: 'First Name'
+ }, {
+ id: 'revoke',
+ field: 'revoke',
+ name: 'Revoke',
+ formatter: function() {
+ return "Revoke Access";
+ }
+ }
+ ];
+ tableData = data[ths.rolename];
+ $tablePlaceholder = $('', {
+ class: 'slickgrid'
+ });
+ ths.$display_table.append($tablePlaceholder);
+ grid = new window.Slick.Grid($tablePlaceholder, tableData, columns, options);
+ return grid.onClick.subscribe(function(e, args) {
+ var item;
+ item = args.grid.getDataItem(args.row);
+ if (args.cell === WHICH_CELL_IS_REVOKE) {
+ return ths.access_change(item.email, 'revoke', function() {
+ return ths.reload_auth_list();
+ });
+ }
+ return false;
+ });
+ };
+ return $.ajax({
+ dataType: 'json',
+ type: 'POST',
+ url: this.$display_table.data('endpoint'),
+ data: {
+ rolename: this.rolename
+ },
+ success: loadAuthList,
+ error: statusAjaxError(function() {
+ return ths.$request_response_error.text("Error fetching list for '" + ths.rolename + "'");
+ })
+ });
+ };
- render_notification_view: (type, title, message, details) ->
- notification_model = new NotificationModel()
- notification_model.set({
- 'type': type,
- 'title': title,
- 'message': message,
- 'details': details,
+ authList.prototype.refresh = function() {
+ this.$display_table.empty();
+ return this.reload_auth_list();
+ };
+
+ authList.prototype.access_change = function(email, action, cb) {
+ var ths = this;
+ return $.ajax({
+ dataType: 'json',
+ type: 'POST',
+ url: this.$add_section.data('endpoint'),
+ data: {
+ email: email,
+ rolename: this.rolename,
+ action: action
+ },
+ success: function(data) {
+ return typeof cb === 'function' ? cb(data) : undefined;
+ },
+ error: statusAjaxError(function() {
+ return ths.$request_response_error.text(gettext("Error changing user's permissions."));
+ })
+ });
+ };
+
+ return authList;
+ }());
+
+ Membership = (function() {
+ function membership($section) {
+ var authList, i, len, ref,
+ thismembership = this;
+ this.$section = $section;
+ this.$section.data('wrapper', this);
+ plantTimeout(0, function() {
+ return new BatchEnrollment(thismembership.$section.find('.batch-enrollment'));
+ });
+ plantTimeout(0, function() {
+ return new AutoEnrollmentViaCsv(thismembership.$section.find('.auto_enroll_csv'));
+ });
+ plantTimeout(0, function() {
+ return new BetaTesterBulkAddition(thismembership.$section.find('.batch-beta-testers'));
+ });
+ this.$list_selector = this.$section.find('select#member-lists-selector');
+ this.$auth_list_containers = this.$section.find('.auth-list-container');
+ this.$auth_list_errors = this.$section.find('.member-lists-management .request-response-error');
+ this.auth_lists = _.map(this.$auth_list_containers, function(authListContainer) {
+ var rolename;
+ rolename = $(authListContainer).data('rolename');
+ return new AuthListWidget($(authListContainer), rolename, thismembership.$auth_list_errors);
+ });
+ this.$list_selector.empty();
+ ref = this.auth_lists;
+ for (i = 0, len = ref.length; i < len; i++) {
+ authList = ref[i];
+ this.$list_selector.append($('', {
+ text: authList.$container.data('display-name'),
+ data: {
+ auth_list: authList
+ }
+ }));
+ }
+ if (this.auth_lists.length === 0) {
+ this.$list_selector.hide();
+ }
+ this.$list_selector.change(function() {
+ var $opt, j, len1, ref1;
+ $opt = thismembership.$list_selector.children('option:selected');
+ if (!($opt.length > 0)) {
+ return;
+ }
+ ref1 = thismembership.auth_lists;
+ for (j = 0, len1 = ref1.length; j < len1; j++) {
+ authList = ref1[j];
+ authList.$container.removeClass('active');
+ }
+ authList = $opt.data('auth_list');
+ authList.$container.addClass('active');
+ authList.re_view();
+ });
+ this.$list_selector.change();
+ }
+
+ membership.prototype.onClickTitle = function() {};
+
+ return membership;
+ }());
+
+ _.defaults(window, {
+ InstructorDashboard: {}
});
- view = new NotificationView(model:notification_model);
- view.render()
- return view.$el.html()
-class BetaTesterBulkAddition
- constructor: (@$container) ->
- # gather elements
- @$identifier_input = @$container.find("textarea[name='student-ids-for-beta']")
- @$btn_beta_testers = @$container.find("input[name='beta-testers']")
- @$checkbox_autoenroll = @$container.find("input[name='auto-enroll']")
- @$checkbox_emailstudents = @$container.find("input[name='email-students-beta']")
- @$task_response = @$container.find(".request-response")
- @$request_response_error = @$container.find(".request-response-error")
+ _.defaults(window.InstructorDashboard, {
+ sections: {}
+ });
- # click handlers
- @$btn_beta_testers.click (event) =>
- emailStudents = @$checkbox_emailstudents.is(':checked')
- autoEnroll = @$checkbox_autoenroll.is(':checked')
- send_data =
- action: $(event.target).data('action') # 'add' or 'remove'
- identifiers: @$identifier_input.val()
- email_students: emailStudents
- auto_enroll: autoEnroll
-
- $.ajax
- dataType: 'json'
- type: 'POST'
- url: @$btn_beta_testers.data 'endpoint'
- data: send_data
- success: (data) => @display_response data
- error: std_ajax_err => @fail_with_error gettext "Error adding/removing users as beta testers."
-
- # clear the input text field
- clear_input: ->
- @$identifier_input.val ''
- # default for the checkboxes should be checked
- @$checkbox_emailstudents.attr('checked', true)
- @$checkbox_autoenroll.attr('checked', true)
-
- fail_with_error: (msg) ->
- console.warn msg
- @clear_input()
- @$task_response.empty()
- @$request_response_error.empty()
- @$request_response_error.text msg
-
- display_response: (data_from_server) ->
- @clear_input()
- @$task_response.empty()
- @$request_response_error.empty()
- errors = []
- successes = []
- no_users = []
- for student_results in data_from_server.results
- if student_results.userDoesNotExist
- no_users.push student_results
- else if student_results.error
- errors.push student_results
- else
- successes.push student_results
-
- render_list = (label, ids) =>
- task_res_section = $ '', class: 'request-res-section'
- task_res_section.append $ '', text: label
- ids_list = $ '
'
- task_res_section.append ids_list
-
- for identifier in ids
- ids_list.append $ '', text: identifier
-
- @$task_response.append task_res_section
-
- if successes.length and data_from_server.action is 'add'
- `// Translators: A list of users appears after this sentence`
- render_list gettext("These users were successfully added as beta testers:"), (sr.identifier for sr in successes)
-
- if successes.length and data_from_server.action is 'remove'
- `// Translators: A list of users appears after this sentence`
- render_list gettext("These users were successfully removed as beta testers:"), (sr.identifier for sr in successes)
-
- if errors.length and data_from_server.action is 'add'
- `// Translators: A list of users appears after this sentence`
- render_list gettext("These users were not added as beta testers:"), (sr.identifier for sr in errors)
-
- if errors.length and data_from_server.action is 'remove'
- `// Translators: A list of users appears after this sentence`
- render_list gettext("These users were not removed as beta testers:"), (sr.identifier for sr in errors)
-
- if no_users.length
- no_users.push $ gettext("Users must create and activate their account before they can be promoted to beta tester.")
- `// Translators: A list of identifiers (which are email addresses and/or usernames) appears after this sentence`
- render_list gettext("Could not find users associated with the following identifiers:"), (sr.identifier for sr in no_users)
-
-# Wrapper for the batch enrollment subsection.
-# This object handles buttons, success and failure reporting,
-# and server communication.
-class BatchEnrollment
- constructor: (@$container) ->
- # gather elements
- @$identifier_input = @$container.find("textarea[name='student-ids']")
- @$enrollment_button = @$container.find(".enrollment-button")
- @$is_course_white_label = @$container.find("#is_course_white_label").val()
- @$reason_field = @$container.find("textarea[name='reason-field']")
- @$checkbox_autoenroll = @$container.find("input[name='auto-enroll']")
- @$checkbox_emailstudents = @$container.find("input[name='email-students']")
- @$task_response = @$container.find(".request-response")
- @$request_response_error = @$container.find(".request-response-error")
-
- # attach click handler for enrollment buttons
- @$enrollment_button.click (event) =>
- if @$is_course_white_label == 'True'
- if not @$reason_field.val()
- @fail_with_error gettext "Reason field should not be left blank."
- return false
-
- emailStudents = @$checkbox_emailstudents.is(':checked')
- send_data =
- action: $(event.target).data('action') # 'enroll' or 'unenroll'
- identifiers: @$identifier_input.val()
- auto_enroll: @$checkbox_autoenroll.is(':checked')
- email_students: emailStudents
- reason: @$reason_field.val()
-
- $.ajax
- dataType: 'json'
- type: 'POST'
- url: $(event.target).data 'endpoint'
- data: send_data
- success: (data) => @display_response data
- error: std_ajax_err => @fail_with_error gettext "Error enrolling/unenrolling users."
-
-
- # clear the input text field
- clear_input: ->
- @$identifier_input.val ''
- @$reason_field.val ''
- # default for the checkboxes should be checked
- @$checkbox_emailstudents.attr('checked', true)
- @$checkbox_autoenroll.attr('checked', true)
-
- fail_with_error: (msg) ->
- console.warn msg
- @clear_input()
- @$task_response.empty()
- @$request_response_error.empty()
- @$request_response_error.text msg
-
- display_response: (data_from_server) ->
- @clear_input()
- @$task_response.empty()
- @$request_response_error.empty()
-
- # these results arrays contain student_results
- # only populated arrays will be rendered
- #
- # invalid identifiers
- invalid_identifier = []
- # students for which there was an error during the action
- errors = []
- # students who are now enrolled in the course
- enrolled = []
- # students who are now allowed to enroll in the course
- allowed = []
- # students who will be autoenrolled on registration
- autoenrolled = []
- # students who are now not enrolled in the course
- notenrolled = []
- # students who were not enrolled or allowed prior to unenroll action
- notunenrolled = []
-
- # categorize student results into the above arrays.
- for student_results in data_from_server.results
- # for a successful action.
- # student_results is of the form {
- # "identifier": "jd405@edx.org",
- # "before": {
- # "enrollment": true,
- # "auto_enroll": false,
- # "user": true,
- # "allowed": false
- # }
- # "after": {
- # "enrollment": true,
- # "auto_enroll": false,
- # "user": true,
- # "allowed": false
- # },
- # }
- #
- # for an action error.
- # student_results is of the form {
- # 'identifier': identifier,
- # # then one of:
- # 'error': True,
- # 'invalidIdentifier': True # if identifier can't find a valid User object and doesn't pass validate_email
- # }
-
- if student_results.invalidIdentifier
- invalid_identifier.push student_results
-
- else if student_results.error
- errors.push student_results
-
- else if student_results.after.enrollment
- enrolled.push student_results
-
- else if student_results.after.allowed
- if student_results.after.auto_enroll
- autoenrolled.push student_results
- else
- allowed.push student_results
-
- # The instructor is trying to unenroll someone who is not enrolled or allowed to enroll; non-sensical action.
- else if data_from_server.action is 'unenroll' and not (student_results.before.enrollment) and not (student_results.before.allowed)
- notunenrolled.push student_results
-
- else if not student_results.after.enrollment
- notenrolled.push student_results
-
- else
- console.warn 'student results not reported to user'
- console.warn student_results
-
- # render populated result arrays
- render_list = (label, ids) =>
- task_res_section = $ '', class: 'request-res-section'
- task_res_section.append $ '', text: label
- ids_list = $ '
'
- task_res_section.append ids_list
-
- for identifier in ids
- ids_list.append $ '', text: identifier
-
- @$task_response.append task_res_section
-
- if invalid_identifier.length
- render_list gettext("The following email addresses and/or usernames are invalid:"), (sr.identifier for sr in invalid_identifier)
-
- if errors.length
- errors_label = do ->
- if data_from_server.action is 'enroll'
- "There was an error enrolling:"
- else if data_from_server.action is 'unenroll'
- "There was an error unenrolling:"
- else
- console.warn "unknown action from server '#{data_from_server.action}'"
- "There was an error processing:"
-
- for student_results in errors
- render_list errors_label, (sr.identifier for sr in errors)
-
- if enrolled.length and emailStudents
- render_list gettext("Successfully enrolled and sent email to the following users:"), (sr.identifier for sr in enrolled)
-
- if enrolled.length and not emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("Successfully enrolled the following users:"), (sr.identifier for sr in enrolled)
-
- # Student hasn't registered so we allow them to enroll
- if allowed.length and emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("Successfully sent enrollment emails to the following users. They will be allowed to enroll once they register:"),
- (sr.identifier for sr in allowed)
-
- # Student hasn't registered so we allow them to enroll
- if allowed.length and not emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("These users will be allowed to enroll once they register:"),
- (sr.identifier for sr in allowed)
-
- # Student hasn't registered so we allow them to enroll with autoenroll
- if autoenrolled.length and emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("Successfully sent enrollment emails to the following users. They will be enrolled once they register:"),
- (sr.identifier for sr in autoenrolled)
-
- # Student hasn't registered so we allow them to enroll with autoenroll
- if autoenrolled.length and not emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("These users will be enrolled once they register:"),
- (sr.identifier for sr in autoenrolled)
-
- if notenrolled.length and emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("Emails successfully sent. The following users are no longer enrolled in the course:"),
- (sr.identifier for sr in notenrolled)
-
- if notenrolled.length and not emailStudents
- `// Translators: A list of users appears after this sentence`
- render_list gettext("The following users are no longer enrolled in the course:"),
- (sr.identifier for sr in notenrolled)
-
- if notunenrolled.length
- `// Translators: A list of users appears after this sentence. This situation arises when a staff member tries to unenroll a user who is not currently enrolled in this course.`
- render_list gettext("These users were not affiliated with the course so could not be unenrolled:"),
- (sr.identifier for sr in notunenrolled)
-
-# Wrapper for auth list subsection.
-# manages a list of users who have special access.
-# these could be instructors, staff, beta users, or forum roles.
-# uses slickgrid to display list.
-class AuthList
- # rolename is one of ['instructor', 'staff'] for instructor_staff endpoints
- # rolename is the name of Role for forums for the forum endpoints
- constructor: (@$container, @rolename) ->
- # gather elements
- @$display_table = @$container.find('.auth-list-table')
- @$request_response_error = @$container.find('.request-response-error')
- @$add_section = @$container.find('.auth-list-add')
- @$allow_field = @$add_section.find("input[name='email']")
- @$allow_button = @$add_section.find("input[name='allow']")
-
- # attach click handler
- @$allow_button.click =>
- @access_change @$allow_field.val(), 'allow', => @reload_auth_list()
- @$allow_field.val ''
-
- @reload_auth_list()
-
- # fetch and display list of users who match criteria
- reload_auth_list: ->
- # helper function to display server data in the list
- load_auth_list = (data) =>
- # clear existing data
- @$request_response_error.empty()
- @$display_table.empty()
-
- # setup slickgrid
- options =
- enableCellNavigation: true
- enableColumnReorder: false
- # autoHeight: true
- forceFitColumns: true
-
- # this is a hack to put a button/link in a slick grid cell
- # if you change columns, then you must update
- # WHICH_CELL_IS_REVOKE to have the index
- # of the revoke column (left to right).
- WHICH_CELL_IS_REVOKE = 3
- columns = [
- id: 'username'
- field: 'username'
- name: 'Username'
- ,
- id: 'email'
- field: 'email'
- name: 'Email'
- ,
- id: 'first_name'
- field: 'first_name'
- name: 'First Name'
- ,
- # id: 'last_name'
- # field: 'last_name'
- # name: 'Last Name'
- # ,
- id: 'revoke'
- field: 'revoke'
- name: 'Revoke'
- formatter: (row, cell, value, columnDef, dataContext) ->
- "Revoke Access"
- ]
-
- table_data = data[@rolename]
-
- $table_placeholder = $ '', class: 'slickgrid'
- @$display_table.append $table_placeholder
- grid = new Slick.Grid($table_placeholder, table_data, columns, options)
-
- # click handler part of the revoke button/link hack.
- grid.onClick.subscribe (e, args) =>
- item = args.grid.getDataItem(args.row)
- if args.cell is WHICH_CELL_IS_REVOKE
- @access_change item.email, 'revoke', => @reload_auth_list()
-
- # fetch data from the endpoint
- # the endpoint comes from data-endpoint of the table
- $.ajax
- dataType: 'json'
- type: 'POST'
- url: @$display_table.data 'endpoint'
- data: rolename: @rolename
- success: load_auth_list
- error: std_ajax_err => @$request_response_error.text "Error fetching list for '#{@rolename}'"
-
-
- # slickgrid's layout collapses when rendered
- # in an invisible div. use this method to reload
- # the AuthList widget
- refresh: ->
- @$display_table.empty()
- @reload_auth_list()
-
- # update the access of a user.
- # (add or remove them from the list)
- # action should be one of ['allow', 'revoke']
- access_change: (email, action, cb) ->
- $.ajax
- dataType: 'json'
- type: 'POST'
- url: @$add_section.data 'endpoint'
- data:
- email: email
- rolename: @rolename
- action: action
- success: (data) -> cb?(data)
- error: std_ajax_err => @$request_response_error.text gettext "Error changing user's permissions."
-
-
-# Membership Section
-class Membership
- # 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 BatchEnrollment subsection
- plantTimeout 0, => new BatchEnrollment @$section.find '.batch-enrollment'
-
- # isolate # initialize AutoEnrollmentViaCsv subsection
- plantTimeout 0, => new AutoEnrollmentViaCsv @$section.find '.auto_enroll_csv'
-
- # initialize BetaTesterBulkAddition subsection
- plantTimeout 0, => new BetaTesterBulkAddition @$section.find '.batch-beta-testers'
-
- # gather elements
- @$list_selector = @$section.find 'select#member-lists-selector'
- @$auth_list_containers = @$section.find '.auth-list-container'
- @$auth_list_errors = @$section.find '.member-lists-management .request-response-error'
-
- # initialize & store AuthList subsections
- # one for each .auth-list-container in the section.
- @auth_lists = _.map (@$auth_list_containers), (auth_list_container) =>
- rolename = $(auth_list_container).data 'rolename'
- new AuthListWidget $(auth_list_container), rolename, @$auth_list_errors
-
- # populate selector
- @$list_selector.empty()
- for auth_list in @auth_lists
- @$list_selector.append $ '',
- text: auth_list.$container.data 'display-name'
- data:
- auth_list: auth_list
- if @auth_lists.length is 0
- @$list_selector.hide()
-
- @$list_selector.change =>
- $opt = @$list_selector.children('option:selected')
- return unless $opt.length > 0
- for auth_list in @auth_lists
- auth_list.$container.removeClass 'active'
- auth_list = $opt.data('auth_list')
- auth_list.$container.addClass 'active'
- auth_list.re_view()
-
- # one-time first selection of top list.
- @$list_selector.change()
-
- # handler for when the section title is clicked.
- onClickTitle: ->
-
-
-# export for use
-# create parent namespaces if they do not already exist.
-_.defaults window, InstructorDashboard: {}
-_.defaults window.InstructorDashboard, sections: {}
-_.defaults window.InstructorDashboard.sections,
- Membership: Membership
+ _.defaults(window.InstructorDashboard.sections, {
+ Membership: Membership
+ });
+}).call(this);
diff --git a/lms/static/js/instructor_dashboard/metrics.js b/lms/static/js/instructor_dashboard/metrics.js
index ec28e48670..775f966bca 100644
--- a/lms/static/js/instructor_dashboard/metrics.js
+++ b/lms/static/js/instructor_dashboard/metrics.js
@@ -1,25 +1,17 @@
-# METRICS Section
+(function() {
+ 'use strict';
+ var Metrics;
-# 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
+ Metrics = (function() {
+ function metrics($section) {
+ this.$section = $section;
+ this.$section.data('wrapper', this);
+ }
-#Metrics Section
-class Metrics
- constructor: (@$section) ->
- @$section.data 'wrapper', @
-
-
- # handler for when the section title is clicked.
- onClickTitle: ->
+ metrics.prototype.onClickTitle = function() {};
-# 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,
- Metrics: Metrics
+ return metrics;
+ }());
+
+ window.InstructorDashboard.sections.Metrics = Metrics;
+}).call(this);
diff --git a/lms/static/js/instructor_dashboard/send_email.js b/lms/static/js/instructor_dashboard/send_email.js
index dab903ca16..d20a54b42c 100644
--- a/lms/static/js/instructor_dashboard/send_email.js
+++ b/lms/static/js/instructor_dashboard/send_email.js
@@ -1,197 +1,256 @@
-###
-Email Section
+/* globals _, SendEmail */
-imports from other modules.
-wrap in (-> ... apply) to defer evaluation
-such that the value can be defined later than this assignment (file load order).
-###
+(function() {
+ 'use strict';
+ var KeywordValidator, PendingInstructorTasks,
+ createEmailContentTable, createEmailMessageViews, createTaskListTable,
+ plantTimeout, statusAjaxError;
-# Load utilities
-plantTimeout = -> window.InstructorDashboard.util.plantTimeout.apply this, arguments
-std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments
-PendingInstructorTasks = -> window.InstructorDashboard.util.PendingInstructorTasks
-create_task_list_table = -> window.InstructorDashboard.util.create_task_list_table.apply this, arguments
-create_email_content_table = -> window.InstructorDashboard.util.create_email_content_table.apply this, arguments
-create_email_message_views = -> window.InstructorDashboard.util.create_email_message_views.apply this, arguments
-KeywordValidator = -> window.InstructorDashboard.util.KeywordValidator
+ plantTimeout = function() {
+ return window.InstructorDashboard.util.plantTimeout.apply(this, arguments);
+ };
-class @SendEmail
- constructor: (@$container) ->
- # gather elements
- @$emailEditor = XBlock.initializeBlock($('.xblock-studio_view'));
- @$send_to = @$container.find("input[name='send_to']")
- @$cohort_targets = @$send_to.filter('[value^="cohort:"]')
- @$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")
- @$content_request_response_error = @$container.find(".content-request-response-error")
- @$history_request_response_error = @$container.find(".history-request-response-error")
- @$btn_task_history_email = @$container.find("input[name='task-history-email']")
- @$btn_task_history_email_content = @$container.find("input[name='task-history-email-content']")
- @$table_task_history_email = @$container.find(".task-history-email-table")
- @$table_email_content_history = @$container.find(".content-history-email-table")
- @$email_content_table_inner = @$container.find(".content-history-table-inner")
- @$email_messages_wrapper = @$container.find(".email-messages-wrapper")
+ statusAjaxError = function() {
+ return window.InstructorDashboard.util.statusAjaxError.apply(this, arguments);
+ };
- # attach click handlers
+ PendingInstructorTasks = function() {
+ return window.InstructorDashboard.util.PendingInstructorTasks;
+ };
- @$btn_send.click =>
- subject = @$subject.val()
- body = @$emailEditor.save()['data']
- targets = []
- @$send_to.filter(':checked').each ->
- targets.push(this.value)
+ createTaskListTable = function() {
+ return window.InstructorDashboard.util.createTaskListTable.apply(this, arguments);
+ };
- if subject == ""
- alert gettext("Your message must have a subject.")
+ createEmailContentTable = function() {
+ return window.InstructorDashboard.util.createEmailContentTable.apply(this, arguments);
+ };
- else if body == ""
- alert gettext("Your message cannot be blank.")
+ createEmailMessageViews = function() {
+ return window.InstructorDashboard.util.createEmailMessageViews.apply(this, arguments);
+ };
- else if targets.length == 0
- alert gettext("Your message must have at least one target.")
+ KeywordValidator = function() {
+ return window.InstructorDashboard.util.KeywordValidator;
+ };
- else
- # Validation for keyword substitution
- validation = KeywordValidator().validate_string body
- if not validation.is_valid
- message = gettext("There are invalid keywords in your email. Check the following keywords and try again.")
- message += "\n" + validation.invalid_keywords.join('\n')
- alert message
- return
+ this.SendEmail = (function() {
+ function SendEmail($container) {
+ var sendemail = this;
+ this.$container = $container;
+ this.$emailEditor = XBlock.initializeBlock($('.xblock-studio_view'));
+ this.$send_to = this.$container.find("input[name='send_to']");
+ this.$cohort_targets = this.$send_to.filter('[value^="cohort:"]');
+ this.$subject = this.$container.find("input[name='subject']");
+ this.$btn_send = this.$container.find("input[name='send']");
+ this.$task_response = this.$container.find('.request-response');
+ this.$request_response_error = this.$container.find('.request-response-error');
+ this.$content_request_response_error = this.$container.find('.content-request-response-error');
+ this.$history_request_response_error = this.$container.find('.history-request-response-error');
+ this.$btn_task_history_email = this.$container.find("input[name='task-history-email']");
+ this.$btn_task_history_email_content = this.$container.find("input[name='task-history-email-content']");
+ this.$table_task_history_email = this.$container.find('.task-history-email-table');
+ this.$table_email_content_history = this.$container.find('.content-history-email-table');
+ this.$email_content_table_inner = this.$container.find('.content-history-table-inner');
+ this.$email_messages_wrapper = this.$container.find('.email-messages-wrapper');
+ this.$btn_send.click(function() {
+ var body, confirmMessage, displayTarget, fullConfirmMessage, message,
+ sendData, subject, successMessage, target, targets, validation, i, len;
+ subject = sendemail.$subject.val();
+ body = sendemail.$emailEditor.save().data;
+ targets = [];
+ sendemail.$send_to.filter(':checked').each(function() {
+ return targets.push(this.value);
+ });
+ if (subject === '') {
+ return alert(gettext('Your message must have a subject.')); // eslint-disable-line no-alert
+ } else if (body === '') {
+ return alert(gettext('Your message cannot be blank.')); // eslint-disable-line no-alert
+ } else if (targets.length === 0) {
+ return alert(gettext( // eslint-disable-line no-alert
+ 'Your message must have at least one target.'));
+ } else {
+ validation = KeywordValidator().validate_string(body);
+ if (!validation.isValid) {
+ message = gettext(
+ 'There are invalid keywords in your email. Check the following keywords and try again.');
+ message += '\n' + validation.invalidKeywords.join('\n');
+ alert(message); // eslint-disable-line no-alert
+ return false;
+ }
+ displayTarget = function(value) {
+ if (value === 'myself') {
+ return gettext('Yourself');
+ } else if (value === 'staff') {
+ return gettext('Everyone who has staff privileges in this course');
+ } else if (value === 'learners') {
+ return gettext('All learners who are enrolled in this course');
+ } else {
+ return gettext('All learners in the {cohort_name} cohort')
+ .replace('{cohort_name}', value.slice(value.indexOf(':') + 1));
+ }
+ };
+ successMessage = gettext('Your email message was successfully queued for sending. In courses with a large number of learners, email messages to learners might take up to an hour to be sent.'); // eslint-disable-line max-len
+ confirmMessage = gettext(
+ 'You are sending an email message with the subject {subject} to the following recipients.');
+ for (i = 0, len = targets.length; i < len; i++) {
+ target = targets[i];
+ confirmMessage += '\n-' + displayTarget(target);
+ }
+ confirmMessage += '\n\n' + gettext('Is this OK?');
+ fullConfirmMessage = confirmMessage.replace('{subject}', subject);
+ if (confirm(fullConfirmMessage)) { // eslint-disable-line no-alert
+ sendData = {
+ action: 'send',
+ send_to: JSON.stringify(targets),
+ subject: subject,
+ message: body
+ };
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: sendemail.$btn_send.data('endpoint'),
+ data: sendData,
+ success: function() {
+ return sendemail.display_response(successMessage);
+ },
+ error: statusAjaxError(function() {
+ return sendemail.fail_with_error(gettext('Error sending email.'));
+ })
+ });
+ } else {
+ sendemail.task_response.empty();
+ return sendemail.$request_response_error.empty();
+ }
+ }
+ });
+ this.$btn_task_history_email.click(function() {
+ var url = sendemail.$btn_task_history_email.data('endpoint');
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: url,
+ success: function(data) {
+ if (data.tasks.length) {
+ return createTaskListTable(sendemail.$table_task_history_email, data.tasks);
+ } else {
+ sendemail.$history_request_response_error.text(
+ gettext('There is no email history for this course.')
+ );
+ return sendemail.$history_request_response_error.css({
+ display: 'block'
+ });
+ }
+ },
+ error: statusAjaxError(function() {
+ return sendemail.$history_request_response_error.text(
+ gettext('There was an error obtaining email task history for this course.')
+ );
+ })
+ });
+ });
+ this.$btn_task_history_email_content.click(function() {
+ var url = sendemail.$btn_task_history_email_content.data('endpoint');
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: url,
+ success: function(data) {
+ if (data.emails.length) {
+ createEmailContentTable(sendemail.$table_email_content_history,
+ sendemail.$email_content_table_inner, data.emails
+ );
+ return createEmailMessageViews(sendemail.$email_messages_wrapper, data.emails);
+ } else {
+ sendemail.$content_request_response_error.text(
+ gettext('There is no email history for this course.')
+ );
+ return sendemail.$content_request_response_error.css({
+ display: 'block'
+ });
+ }
+ },
+ error: statusAjaxError(function() {
+ return sendemail.$content_request_response_error.text(
+ gettext('There was an error obtaining email content history for this course.')
+ );
+ })
+ });
+ });
+ this.$send_to.change(function() {
+ var targets;
+ if ($('input#target_learners:checked').length) {
+ sendemail.$cohort_targets.each(function() {
+ this.checked = false;
+ this.disabled = true;
+ return true;
+ });
+ } else {
+ sendemail.$cohort_targets.each(function() {
+ this.disabled = false;
+ return true;
+ });
+ }
+ targets = [];
+ $('input[name="send_to"]:checked+label').each(function() {
+ return targets.push(this.innerText.replace(/\s*\n.*/g, ''));
+ });
+ return $('.send_to_list').text(gettext('Send to:') + ' ' + targets.join(', '));
+ });
+ }
- display_target = (value) ->
- if value == "myself"
- gettext("Yourself")
- else if value == "staff"
- gettext("Everyone who has staff privileges in this course")
- else if value == "learners"
- gettext("All learners who are enrolled in this course")
- else
- gettext("All learners in the {cohort_name} cohort").replace('{cohort_name}', value.slice(value.indexOf(':')+1))
- success_message = gettext("Your email message was successfully queued for sending. In courses with a large number of learners, email messages to learners might take up to an hour to be sent.")
- confirm_message = gettext("You are sending an email message with the subject {subject} to the following recipients.")
- for target in targets
- confirm_message += "\n-" + display_target(target)
- confirm_message += "\n\n" + gettext("Is this OK?")
- full_confirm_message = confirm_message.replace('{subject}', subject)
+ SendEmail.prototype.fail_with_error = function(msg) {
+ this.$task_response.empty();
+ this.$request_response_error.empty();
+ this.$request_response_error.text(msg);
+ return $('.msg-confirm').css({
+ display: 'none'
+ });
+ };
- if confirm full_confirm_message
+ SendEmail.prototype.display_response = function(dataFromServer) {
+ this.$task_response.empty();
+ this.$request_response_error.empty();
+ this.$task_response.text(dataFromServer);
+ return $('.msg-confirm').css({
+ display: 'block'
+ });
+ };
- send_data =
- action: 'send'
- send_to: JSON.stringify(targets)
- subject: subject
- message: body
+ return SendEmail;
+ }());
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_send.data 'endpoint'
- data: send_data
- success: (data) =>
- @display_response success_message
+ this.Email = (function() {
+ function email($section) {
+ var eml = this;
+ this.$section = $section;
+ this.$section.data('wrapper', this);
+ plantTimeout(0, function() {
+ return new SendEmail(eml.$section.find('.send-email'));
+ });
+ this.instructor_tasks = new (PendingInstructorTasks())(this.$section);
+ }
- error: std_ajax_err =>
- @fail_with_error gettext('Error sending email.')
+ email.prototype.onClickTitle = function() {
+ return this.instructor_tasks.task_poller.start();
+ };
- else
- @task_response.empty()
- @$request_response_error.empty()
+ email.prototype.onExit = function() {
+ return this.instructor_tasks.task_poller.stop();
+ };
- # list task history for email
- @$btn_task_history_email.click =>
- url = @$btn_task_history_email.data 'endpoint'
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: url
- success: (data) =>
- if data.tasks.length
- create_task_list_table @$table_task_history_email, data.tasks
- else
- @$history_request_response_error.text gettext("There is no email history for this course.")
- # Enable the msg-warning css display
- @$history_request_response_error.css({"display":"block"})
- error: std_ajax_err =>
- @$history_request_response_error.text gettext("There was an error obtaining email task history for this course.")
+ return email;
+ }());
- # List content history for emails sent
- @$btn_task_history_email_content.click =>
- url = @$btn_task_history_email_content.data 'endpoint'
- $.ajax
- type: 'POST'
- dataType: 'json'
- url : url
- success: (data) =>
- if data.emails.length
- create_email_content_table @$table_email_content_history, @$email_content_table_inner, data.emails
- create_email_message_views @$email_messages_wrapper, data.emails
- else
- @$content_request_response_error.text gettext("There is no email history for this course.")
- @$content_request_response_error.css({"display":"block"})
- error: std_ajax_err =>
- @$content_request_response_error.text gettext("There was an error obtaining email content history for this course.")
+ _.defaults(window, {
+ InstructorDashboard: {}
+ });
- @$send_to.change =>
- # Ensure invalid combinations are disabled
- if $('input#target_learners:checked').length
- # If all is selected, cohorts can't be
- @$cohort_targets.each ->
- this.checked = false
- this.disabled = true
- true
- else
- @$cohort_targets.each ->
- this.disabled = false
- true
+ _.defaults(window.InstructorDashboard, {
+ sections: {}
+ });
- # Also, keep the sent_to_list div updated
- targets = []
- $('input[name="send_to"]:checked+label').each ->
- # Only use the first line, even if a subheading is present
- targets.push(this.innerText.replace(/\s*\n.*/g,''))
- $(".send_to_list").text(gettext("Send to:") + " " + targets.join(", "))
-
-
- fail_with_error: (msg) ->
- console.warn msg
- @$task_response.empty()
- @$request_response_error.empty()
- @$request_response_error.text 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'
-
- @instructor_tasks = new (PendingInstructorTasks()) @$section
-
- # handler for when the section title is clicked.
- onClickTitle: -> @instructor_tasks.task_poller.start()
-
- # handler for when the section is closed
- onExit: -> @instructor_tasks.task_poller.stop()
-
-
-# export for use
-# create parent namespaces if they do not already exist.
-_.defaults window, InstructorDashboard: {}
-_.defaults window.InstructorDashboard, sections: {}
-_.defaults window.InstructorDashboard.sections,
- Email: Email
+ _.defaults(window.InstructorDashboard.sections, {
+ Email: this.Email
+ });
+}).call(this);
diff --git a/lms/static/js/instructor_dashboard/student_admin.js b/lms/static/js/instructor_dashboard/student_admin.js
index c9de7cba74..9c65b24380 100644
--- a/lms/static/js/instructor_dashboard/student_admin.js
+++ b/lms/static/js/instructor_dashboard/student_admin.js
@@ -1,398 +1,570 @@
-###
-Student Admin Section
+/* globals _, interpolate_text */
-imports from other modules.
-wrap in (-> ... apply) to defer evaluation
-such that the value can be defined later than this assignment (file load order).
-###
+(function() {
+ 'use strict';
+ var PendingInstructorTasks, createTaskListTable, findAndAssert, statusAjaxError;
-# Load utilities
-std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments
-create_task_list_table = -> window.InstructorDashboard.util.create_task_list_table.apply this, arguments
-PendingInstructorTasks = -> window.InstructorDashboard.util.PendingInstructorTasks
+ statusAjaxError = function() {
+ return window.InstructorDashboard.util.statusAjaxError.apply(this, arguments);
+ };
+ createTaskListTable = function() {
+ return window.InstructorDashboard.util.createTaskListTable.apply(this, arguments);
+ };
-# get jquery element and assert its existance
-find_and_assert = ($root, selector) ->
- item = $root.find selector
- if item.length != 1
- console.error "element selection failed for '#{selector}' resulted in length #{item.length}"
- throw "Failed Element Selection"
- else
- item
+ PendingInstructorTasks = function() {
+ return window.InstructorDashboard.util.PendingInstructorTasks;
+ };
+ findAndAssert = function($root, selector) {
+ var item, msg;
+ item = $root.find(selector);
+ if (item.length !== 1) {
+ msg = 'Failed Element Selection';
+ throw msg;
+ } else {
+ return item;
+ }
+ };
-class @StudentAdmin
- constructor: (@$section) ->
- # attach self to html so that instructor_dashboard.coffee can find
- # this object to call event handlers like 'onClickTitle'
- @$section.data 'wrapper', @
+ this.StudentAdmin = (function() {
+ function StudentAdmin($section) {
+ var studentadmin = this;
+ this.$section = $section;
+ this.$section.data('wrapper', this);
+ this.$field_student_select_progress = findAndAssert(this.$section, "input[name='student-select-progress']");
+ this.$field_student_select_grade = findAndAssert(this.$section, "input[name='student-select-grade']");
+ this.$progress_link = findAndAssert(this.$section, 'a.progress-link');
+ this.$field_problem_select_single = findAndAssert(this.$section, "input[name='problem-select-single']");
+ this.$btn_reset_attempts_single = findAndAssert(this.$section, "input[name='reset-attempts-single']");
+ this.$btn_delete_state_single = this.$section.find("input[name='delete-state-single']");
+ this.$btn_rescore_problem_single = this.$section.find("input[name='rescore-problem-single']");
+ this.$btn_task_history_single = this.$section.find("input[name='task-history-single']");
+ this.$table_task_history_single = this.$section.find('.task-history-single-table');
+ this.$field_exam_grade = this.$section.find("input[name='entrance-exam-student-select-grade']");
+ this.$btn_reset_entrance_exam_attempts = this.$section.find("input[name='reset-entrance-exam-attempts']");
+ this.$btn_delete_entrance_exam_state = this.$section.find("input[name='delete-entrance-exam-state']");
+ this.$btn_rescore_entrance_exam = this.$section.find("input[name='rescore-entrance-exam']");
+ this.$btn_skip_entrance_exam = this.$section.find("input[name='skip-entrance-exam']");
+ this.$btn_entrance_exam_task_history = this.$section.find("input[name='entrance-exam-task-history']");
+ this.$table_entrance_exam_task_history = this.$section.find('.entrance-exam-task-history-table');
+ this.$field_problem_select_all = this.$section.find("input[name='problem-select-all']");
+ this.$btn_reset_attempts_all = this.$section.find("input[name='reset-attempts-all']");
+ this.$btn_rescore_problem_all = this.$section.find("input[name='rescore-problem-all']");
+ this.$btn_task_history_all = this.$section.find("input[name='task-history-all']");
+ this.$table_task_history_all = this.$section.find('.task-history-all-table');
+ this.instructor_tasks = new (PendingInstructorTasks())(this.$section);
+ this.$request_err = findAndAssert(this.$section, '.student-specific-container .request-response-error');
+ this.$request_err_grade = findAndAssert(this.$section, '.student-grade-container .request-response-error');
+ this.$request_err_ee = this.$section.find('.entrance-exam-grade-container .request-response-error');
+ this.$request_response_error_all = this.$section.find('.course-specific-container .request-response-error');
+ this.$progress_link.click(function(e) {
+ var errorMessage, fullErrorMessage, uniqStudentIdentifier;
+ e.preventDefault();
+ uniqStudentIdentifier = studentadmin.$field_student_select_progress.val();
+ if (!uniqStudentIdentifier) {
+ return studentadmin.$request_err.text(
+ gettext('Please enter a student email address or username.')
+ );
+ }
+ errorMessage = gettext("Error getting student progress url for '<%- student_id %>'. Make sure that the student identifier is spelled correctly."); // eslint-disable-line max-len
+ fullErrorMessage = _.template(errorMessage)({
+ student_id: uniqStudentIdentifier
+ });
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: studentadmin.$progress_link.data('endpoint'),
+ data: {
+ unique_student_identifier: uniqStudentIdentifier
+ },
+ success: studentadmin.clear_errors_then(function(data) {
+ window.location = data.progress_url;
+ return window.location;
+ }),
+ error: statusAjaxError(function() {
+ return studentadmin.$request_err.text(fullErrorMessage);
+ })
+ });
+ });
+ this.$btn_reset_attempts_single.click(function() {
+ var errorMessage, fullErrorMessage, fullSuccessMessage,
+ problemToReset, sendData, successMessage, uniqStudentIdentifier;
+ uniqStudentIdentifier = studentadmin.$field_student_select_grade.val();
+ problemToReset = studentadmin.$field_problem_select_single.val();
+ if (!uniqStudentIdentifier) {
+ return studentadmin.$request_err_grade.text(
+ gettext('Please enter a student email address or username.')
+ );
+ }
+ if (!problemToReset) {
+ return studentadmin.$request_err_grade.text(gettext('Please enter a problem location.'));
+ }
+ sendData = {
+ unique_student_identifier: uniqStudentIdentifier,
+ problem_to_reset: problemToReset,
+ delete_module: false
+ };
+ successMessage = gettext("Success! Problem attempts reset for problem '<%- problem_id %>' and student '<%- student_id %>'."); // eslint-disable-line max-len
+ errorMessage = gettext("Error resetting problem attempts for problem '<%= problem_id %>' and student '<%- student_id %>'. Make sure that the problem and student identifiers are complete and correct."); // eslint-disable-line max-len
+ fullSuccessMessage = _.template(successMessage)({
+ problem_id: problemToReset,
+ student_id: uniqStudentIdentifier
+ });
+ fullErrorMessage = _.template(errorMessage)({
+ problem_id: problemToReset,
+ student_id: uniqStudentIdentifier
+ });
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: studentadmin.$btn_reset_attempts_single.data('endpoint'),
+ data: sendData,
+ success: studentadmin.clear_errors_then(function() {
+ return alert(fullSuccessMessage); // eslint-disable-line no-alert
+ }),
+ error: statusAjaxError(function() {
+ return studentadmin.$request_err_grade.text(fullErrorMessage);
+ })
+ });
+ });
+ this.$btn_delete_state_single.click(function() {
+ var confirmMessage, errorMessage, fullConfirmMessage,
+ fullErrorMessage, problemToReset, sendData, uniqStudentIdentifier;
+ uniqStudentIdentifier = studentadmin.$field_student_select_grade.val();
+ problemToReset = studentadmin.$field_problem_select_single.val();
+ if (!uniqStudentIdentifier) {
+ return studentadmin.$request_err_grade.text(
+ gettext('Please enter a student email address or username.')
+ );
+ }
+ if (!problemToReset) {
+ return studentadmin.$request_err_grade.text(
+ gettext('Please enter a problem location.')
+ );
+ }
+ confirmMessage = gettext("Delete student '<%- student_id %>'s state on problem '<%- problem_id %>'?");
+ fullConfirmMessage = _.template(confirmMessage)({
+ student_id: uniqStudentIdentifier,
+ problem_id: problemToReset
+ });
+ if (window.confirm(fullConfirmMessage)) { // eslint-disable-line no-alert
+ sendData = {
+ unique_student_identifier: uniqStudentIdentifier,
+ problem_to_reset: problemToReset,
+ delete_module: true
+ };
+ errorMessage = gettext("Error deleting student '<%- student_id %>'s state on problem '<%- problem_id %>'. Make sure that the problem and student identifiers are complete and correct."); // eslint-disable-line max-len
+ fullErrorMessage = _.template(errorMessage)({
+ student_id: uniqStudentIdentifier,
+ problem_id: problemToReset
+ });
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: studentadmin.$btn_delete_state_single.data('endpoint'),
+ data: sendData,
+ success: studentadmin.clear_errors_then(function() {
+ return alert(gettext('Module state successfully deleted.')); // eslint-disable-line no-alert, max-len
+ }),
+ error: statusAjaxError(function() {
+ return studentadmin.$request_err_grade.text(fullErrorMessage);
+ })
+ });
+ } else {
+ return studentadmin.clear_errors();
+ }
+ });
+ this.$btn_rescore_problem_single.click(function() {
+ var errorMessage, fullErrorMessage, fullSuccessMessage,
+ problemToReset, sendData, successMessage, uniqStudentIdentifier;
+ uniqStudentIdentifier = studentadmin.$field_student_select_grade.val();
+ problemToReset = studentadmin.$field_problem_select_single.val();
+ if (!uniqStudentIdentifier) {
+ return studentadmin.$request_err_grade.text(
+ gettext('Please enter a student email address or username.')
+ );
+ }
+ if (!problemToReset) {
+ return studentadmin.$request_err_grade.text(
+ gettext('Please enter a problem location.')
+ );
+ }
+ sendData = {
+ unique_student_identifier: uniqStudentIdentifier,
+ problem_to_reset: problemToReset
+ };
+ successMessage = gettext("Started rescore problem task for problem '<%- problem_id %>' and student '<%- student_id %>'. Click the 'Show Background Task History for Student' button to see the status of the task."); // eslint-disable-line max-len
+ fullSuccessMessage = _.template(successMessage)({
+ student_id: uniqStudentIdentifier,
+ problem_id: problemToReset
+ });
+ errorMessage = gettext("Error starting a task to rescore problem '<%- problem_id %>' for student '<%- student_id %>'. Make sure that the the problem and student identifiers are complete and correct."); // eslint-disable-line max-len
+ fullErrorMessage = _.template(errorMessage)({
+ student_id: uniqStudentIdentifier,
+ problem_id: problemToReset
+ });
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: studentadmin.$btn_rescore_problem_single.data('endpoint'),
+ data: sendData,
+ success: studentadmin.clear_errors_then(function() {
+ return alert(fullSuccessMessage); // eslint-disable-line no-alert
+ }),
+ error: statusAjaxError(function() {
+ return studentadmin.$request_err_grade.text(fullErrorMessage);
+ })
+ });
+ });
+ this.$btn_task_history_single.click(function() {
+ var errorMessage, fullErrorMessage, problemToReset, sendData, uniqStudentIdentifier;
+ uniqStudentIdentifier = studentadmin.$field_student_select_grade.val();
+ problemToReset = studentadmin.$field_problem_select_single.val();
+ if (!uniqStudentIdentifier) {
+ return studentadmin.$request_err_grade.text(
+ gettext('Please enter a student email address or username.')
+ );
+ }
+ if (!problemToReset) {
+ return studentadmin.$request_err_grade.text(
+ gettext('Please enter a problem location.')
+ );
+ }
+ sendData = {
+ unique_student_identifier: uniqStudentIdentifier,
+ problem_location_str: problemToReset
+ };
+ errorMessage = gettext("Error getting task history for problem '<%- problem_id %>' and student '<%- student_id %>'. Make sure that the problem and student identifiers are complete and correct."); // eslint-disable-line max-len
+ fullErrorMessage = _.template(errorMessage)({
+ student_id: uniqStudentIdentifier,
+ problem_id: problemToReset
+ });
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: studentadmin.$btn_task_history_single.data('endpoint'),
+ data: sendData,
+ success: studentadmin.clear_errors_then(function(data) {
+ return createTaskListTable(studentadmin.$table_task_history_single, data.tasks);
+ }),
+ error: statusAjaxError(function() {
+ return studentadmin.$request_err_grade.text(fullErrorMessage);
+ })
+ });
+ });
+ this.$btn_reset_entrance_exam_attempts.click(function() {
+ var sendData, uniqStudentIdentifier;
+ uniqStudentIdentifier = studentadmin.$field_exam_grade.val();
+ if (!uniqStudentIdentifier) {
+ return studentadmin.$request_err_ee.text(gettext(
+ 'Please enter a student email address or username.')
+ );
+ }
+ sendData = {
+ unique_student_identifier: uniqStudentIdentifier,
+ delete_module: false
+ };
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: studentadmin.$btn_reset_entrance_exam_attempts.data('endpoint'),
+ data: sendData,
+ success: studentadmin.clear_errors_then(function() {
+ var fullSuccessMessage, successMessage;
+ successMessage = gettext("Entrance exam attempts is being reset for student '{student_id}'.");
+ fullSuccessMessage = interpolate_text(successMessage, {
+ student_id: uniqStudentIdentifier
+ });
+ return alert(fullSuccessMessage); // eslint-disable-line no-alert
+ }),
+ error: statusAjaxError(function() {
+ var errorMessage, fullErrorMessage;
+ errorMessage = gettext("Error resetting entrance exam attempts for student '{student_id}'. Make sure student identifier is correct."); // eslint-disable-line max-len
+ fullErrorMessage = interpolate_text(errorMessage, {
+ student_id: uniqStudentIdentifier
+ });
+ return studentadmin.$request_err_ee.text(fullErrorMessage);
+ })
+ });
+ });
+ this.$btn_rescore_entrance_exam.click(function() {
+ var sendData, uniqStudentIdentifier;
+ uniqStudentIdentifier = studentadmin.$field_exam_grade.val();
+ if (!uniqStudentIdentifier) {
+ return studentadmin.$request_err_ee.text(gettext(
+ 'Please enter a student email address or username.')
+ );
+ }
+ sendData = {
+ unique_student_identifier: uniqStudentIdentifier
+ };
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: studentadmin.$btn_rescore_entrance_exam.data('endpoint'),
+ data: sendData,
+ success: studentadmin.clear_errors_then(function() {
+ var fullSuccessMessage, successMessage;
+ successMessage = gettext("Started entrance exam rescore task for student '{student_id}'. Click the 'Show Background Task History for Student' button to see the status of the task."); // eslint-disable-line max-len
+ fullSuccessMessage = interpolate_text(successMessage, {
+ student_id: uniqStudentIdentifier
+ });
+ return alert(fullSuccessMessage); // eslint-disable-line no-alert
+ }),
+ error: statusAjaxError(function() {
+ var errorMessage, fullErrorMessage;
+ errorMessage = gettext("Error starting a task to rescore entrance exam for student '{student_id}'. Make sure that entrance exam has problems in it and student identifier is correct."); // eslint-disable-line max-len
+ fullErrorMessage = interpolate_text(errorMessage, {
+ student_id: uniqStudentIdentifier
+ });
+ return studentadmin.$request_err_ee.text(fullErrorMessage);
+ })
+ });
+ });
+ this.$btn_skip_entrance_exam.click(function() {
+ var confirmMessage, fullConfirmMessage, sendData, uniqStudentIdentifier;
+ uniqStudentIdentifier = studentadmin.$field_exam_grade.val();
+ if (!uniqStudentIdentifier) {
+ return studentadmin.$request_err_ee.text(gettext("Enter a student's username or email address."));
+ }
+ confirmMessage = gettext("Do you want to allow this student ('{student_id}') to skip the entrance exam?"); // eslint-disable-line max-len
+ fullConfirmMessage = interpolate_text(confirmMessage, {
+ student_id: uniqStudentIdentifier
+ });
+ if (window.confirm(fullConfirmMessage)) { // eslint-disable-line no-alert
+ sendData = {
+ unique_student_identifier: uniqStudentIdentifier
+ };
+ return $.ajax({
+ dataType: 'json',
+ url: studentadmin.$btn_skip_entrance_exam.data('endpoint'),
+ data: sendData,
+ type: 'POST',
+ success: studentadmin.clear_errors_then(function(data) {
+ return alert(data.message); // eslint-disable-line no-alert
+ }),
+ error: statusAjaxError(function() {
+ var errorMessage;
+ errorMessage = gettext("An error occurred. Make sure that the student's username or email address is correct and try again."); // eslint-disable-line max-len
+ return studentadmin.$request_err_ee.text(errorMessage);
+ })
+ });
+ }
+ return false;
+ });
+ this.$btn_delete_entrance_exam_state.click(function() {
+ var sendData, uniqStudentIdentifier;
+ uniqStudentIdentifier = studentadmin.$field_exam_grade.val();
+ if (!uniqStudentIdentifier) {
+ return studentadmin.$request_err_ee.text(
+ gettext('Please enter a student email address or username.')
+ );
+ }
+ sendData = {
+ unique_student_identifier: uniqStudentIdentifier,
+ delete_module: true
+ };
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: studentadmin.$btn_delete_entrance_exam_state.data('endpoint'),
+ data: sendData,
+ success: studentadmin.clear_errors_then(function() {
+ var fullSuccessMessage, successMessage;
+ successMessage = gettext("Entrance exam state is being deleted for student '{student_id}'.");
+ fullSuccessMessage = interpolate_text(successMessage, {
+ student_id: uniqStudentIdentifier
+ });
+ return alert(fullSuccessMessage); // eslint-disable-line no-alert
+ }),
+ error: statusAjaxError(function() {
+ var errorMessage, fullErrorMessage;
+ errorMessage = gettext("Error deleting entrance exam state for student '{student_id}'. Make sure student identifier is correct."); // eslint-disable-line max-len
+ fullErrorMessage = interpolate_text(errorMessage, {
+ student_id: uniqStudentIdentifier
+ });
+ return studentadmin.$request_err_ee.text(fullErrorMessage);
+ })
+ });
+ });
+ this.$btn_entrance_exam_task_history.click(function() {
+ var sendData, uniqStudentIdentifier;
+ uniqStudentIdentifier = studentadmin.$field_exam_grade.val();
+ if (!uniqStudentIdentifier) {
+ return studentadmin.$request_err_ee.text(
+ gettext("Enter a student's username or email address.")
+ );
+ }
+ sendData = {
+ unique_student_identifier: uniqStudentIdentifier
+ };
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: studentadmin.$btn_entrance_exam_task_history.data('endpoint'),
+ data: sendData,
+ success: studentadmin.clear_errors_then(function(data) {
+ return createTaskListTable(studentadmin.$table_entrance_exam_task_history, data.tasks);
+ }),
+ error: statusAjaxError(function() {
+ var errorMessage, fullErrorMessage;
+ errorMessage = gettext("Error getting entrance exam task history for student '{student_id}'. Make sure student identifier is correct."); // eslint-disable-line max-len
+ fullErrorMessage = interpolate_text(errorMessage, {
+ student_id: uniqStudentIdentifier
+ });
+ return studentadmin.$request_err_ee.text(fullErrorMessage);
+ })
+ });
+ });
+ this.$btn_reset_attempts_all.click(function() {
+ var confirmMessage, errorMessage, fullConfirmMessage,
+ fullErrorMessage, fullSuccessMessage, problemToReset, sendData, successMessage;
+ problemToReset = studentadmin.$field_problem_select_all.val();
+ if (!problemToReset) {
+ return studentadmin.$request_response_error_all.text(
+ gettext('Please enter a problem location.')
+ );
+ }
+ confirmMessage = gettext("Reset attempts for all students on problem '<%- problem_id %>'?");
+ fullConfirmMessage = _.template(confirmMessage)({
+ problem_id: problemToReset
+ });
+ if (window.confirm(fullConfirmMessage)) { // eslint-disable-line no-alert
+ sendData = {
+ all_students: true,
+ problem_to_reset: problemToReset
+ };
+ successMessage = gettext("Successfully started task to reset attempts for problem '<%- problem_id %>'. Click the 'Show Background Task History for Problem' button to see the status of the task."); // eslint-disable-line max-len
+ fullSuccessMessage = _.template(successMessage)({
+ problem_id: problemToReset
+ });
+ errorMessage = gettext("Error starting a task to reset attempts for all students on problem '<%- problem_id %>'. Make sure that the problem identifier is complete and correct."); // eslint-disable-line max-len
+ fullErrorMessage = _.template(errorMessage)({
+ problem_id: problemToReset
+ });
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: studentadmin.$btn_reset_attempts_all.data('endpoint'),
+ data: sendData,
+ success: studentadmin.clear_errors_then(function() {
+ return alert(fullSuccessMessage); // eslint-disable-line no-alert
+ }),
+ error: statusAjaxError(function() {
+ return studentadmin.$request_response_error_all.text(fullErrorMessage);
+ })
+ });
+ } else {
+ return studentadmin.clear_errors();
+ }
+ });
+ this.$btn_rescore_problem_all.click(function() {
+ var confirmMessage, errorMessage, fullConfirmMessage,
+ fullErrorMessage, fullSuccessMessage, problemToReset, sendData, successMessage;
+ problemToReset = studentadmin.$field_problem_select_all.val();
+ if (!problemToReset) {
+ return studentadmin.$request_response_error_all.text(
+ gettext('Please enter a problem location.')
+ );
+ }
+ confirmMessage = gettext("Rescore problem '<%- problem_id %>' for all students?");
+ fullConfirmMessage = _.template(confirmMessage)({
+ problem_id: problemToReset
+ });
+ if (window.confirm(fullConfirmMessage)) { // eslint-disable-line no-alert
+ sendData = {
+ all_students: true,
+ problem_to_reset: problemToReset
+ };
+ successMessage = gettext("Successfully started task to rescore problem '<%- problem_id %>' for all students. Click the 'Show Background Task History for Problem' button to see the status of the task."); // eslint-disable-line max-len
+ fullSuccessMessage = _.template(successMessage)({
+ problem_id: problemToReset
+ });
+ errorMessage = gettext("Error starting a task to rescore problem '<%- problem_id %>'. Make sure that the problem identifier is complete and correct."); // eslint-disable-line max-len
+ fullErrorMessage = _.template(errorMessage)({
+ problem_id: problemToReset
+ });
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: studentadmin.$btn_rescore_problem_all.data('endpoint'),
+ data: sendData,
+ success: studentadmin.clear_errors_then(function() {
+ return alert(fullSuccessMessage); // eslint-disable-line no-alert
+ }),
+ error: statusAjaxError(function() {
+ return studentadmin.$request_response_error_all.text(fullErrorMessage);
+ })
+ });
+ } else {
+ return studentadmin.clear_errors();
+ }
+ });
+ this.$btn_task_history_all.click(function() {
+ var sendData;
+ sendData = {
+ problem_location_str: studentadmin.$field_problem_select_all.val()
+ };
+ if (!sendData.problem_location_str) {
+ return studentadmin.$request_response_error_all.text(
+ gettext('Please enter a problem location.')
+ );
+ }
+ return $.ajax({
+ type: 'POST',
+ dataType: 'json',
+ url: studentadmin.$btn_task_history_all.data('endpoint'),
+ data: sendData,
+ success: studentadmin.clear_errors_then(function(data) {
+ return createTaskListTable(studentadmin.$table_task_history_all, data.tasks);
+ }),
+ error: statusAjaxError(function() {
+ return studentadmin.$request_response_error_all.text(
+ gettext('Error listing task history for this student and problem.')
+ );
+ })
+ });
+ });
+ }
- # gather buttons
- # some buttons are optional because they can be flipped by the instructor task feature switch
- # student-specific
- @$field_student_select_progress = find_and_assert @$section, "input[name='student-select-progress']"
- @$field_student_select_grade = find_and_assert @$section, "input[name='student-select-grade']"
- @$progress_link = find_and_assert @$section, "a.progress-link"
- @$field_problem_select_single = find_and_assert @$section, "input[name='problem-select-single']"
- @$btn_reset_attempts_single = find_and_assert @$section, "input[name='reset-attempts-single']"
- @$btn_delete_state_single = @$section.find "input[name='delete-state-single']"
- @$btn_rescore_problem_single = @$section.find "input[name='rescore-problem-single']"
- @$btn_task_history_single = @$section.find "input[name='task-history-single']"
- @$table_task_history_single = @$section.find ".task-history-single-table"
+ StudentAdmin.prototype.clear_errors_then = function(cb) {
+ this.$request_err.empty();
+ this.$request_err_grade.empty();
+ this.$request_err_ee.empty();
+ this.$request_response_error_all.empty();
+ return function() {
+ return cb != null ? cb.apply(this, arguments) : void 0;
+ };
+ };
- # entrance-exam-specific
- @$field_entrance_exam_student_select_grade = @$section.find "input[name='entrance-exam-student-select-grade']"
- @$btn_reset_entrance_exam_attempts = @$section.find "input[name='reset-entrance-exam-attempts']"
- @$btn_delete_entrance_exam_state = @$section.find "input[name='delete-entrance-exam-state']"
- @$btn_rescore_entrance_exam = @$section.find "input[name='rescore-entrance-exam']"
- @$btn_skip_entrance_exam = @$section.find "input[name='skip-entrance-exam']"
- @$btn_entrance_exam_task_history = @$section.find "input[name='entrance-exam-task-history']"
- @$table_entrance_exam_task_history = @$section.find ".entrance-exam-task-history-table"
+ StudentAdmin.prototype.clear_errors = function() {
+ this.$request_err.empty();
+ this.$request_err_grade.empty();
+ this.$request_err_ee.empty();
+ return this.$request_response_error_all.empty();
+ };
- # course-specific
- @$field_problem_select_all = @$section.find "input[name='problem-select-all']"
- @$btn_reset_attempts_all = @$section.find "input[name='reset-attempts-all']"
- @$btn_rescore_problem_all = @$section.find "input[name='rescore-problem-all']"
- @$btn_task_history_all = @$section.find "input[name='task-history-all']"
- @$table_task_history_all = @$section.find ".task-history-all-table"
- @instructor_tasks = new (PendingInstructorTasks()) @$section
+ StudentAdmin.prototype.onClickTitle = function() {
+ return this.instructor_tasks.task_poller.start();
+ };
- # response areas
- @$request_response_error_progress = find_and_assert @$section, ".student-specific-container .request-response-error"
- @$request_response_error_grade = find_and_assert @$section, ".student-grade-container .request-response-error"
- @$request_response_error_ee = @$section.find ".entrance-exam-grade-container .request-response-error"
- @$request_response_error_all = @$section.find ".course-specific-container .request-response-error"
+ StudentAdmin.prototype.onExit = function() {
+ return this.instructor_tasks.task_poller.stop();
+ };
- # attach click handlers
+ return StudentAdmin;
+ }());
- # go to student progress page
- @$progress_link.click (e) =>
- e.preventDefault()
- unique_student_identifier = @$field_student_select_progress.val()
- if not unique_student_identifier
- return @$request_response_error_progress.text gettext("Please enter a student email address or username.")
- error_message = gettext("Error getting student progress url for '<%= student_id %>'. Make sure that the student identifier is spelled correctly.")
- full_error_message = _.template(error_message)({student_id: unique_student_identifier})
+ _.defaults(window, {
+ InstructorDashboard: {}
+ });
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$progress_link.data 'endpoint'
- data: unique_student_identifier: unique_student_identifier
- success: @clear_errors_then (data) ->
- window.location = data.progress_url
- error: std_ajax_err => @$request_response_error_progress.text full_error_message
+ _.defaults(window.InstructorDashboard, {
+ sections: {}
+ });
- # reset attempts for student on problem
- @$btn_reset_attempts_single.click =>
- unique_student_identifier = @$field_student_select_grade.val()
- problem_to_reset = @$field_problem_select_single.val()
- if not unique_student_identifier
- return @$request_response_error_grade.text gettext("Please enter a student email address or username.")
- if not problem_to_reset
- return @$request_response_error_grade.text gettext("Please enter a problem location.")
- send_data =
- unique_student_identifier: unique_student_identifier
- problem_to_reset: problem_to_reset
- delete_module: false
- success_message = gettext("Success! Problem attempts reset for problem '<%= problem_id %>' and student '<%= student_id %>'.")
- error_message = gettext("Error resetting problem attempts for problem '<%= problem_id %>' and student '<%= student_id %>'. Make sure that the problem and student identifiers are complete and correct.")
- full_success_message = _.template(success_message)({problem_id: problem_to_reset, student_id: unique_student_identifier})
- full_error_message = _.template(error_message)({problem_id: problem_to_reset, student_id: unique_student_identifier})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_reset_attempts_single.data 'endpoint'
- data: send_data
- success: @clear_errors_then -> alert full_success_message
- error: std_ajax_err => @$request_response_error_grade.text full_error_message
-
- # delete state for student on problem
- @$btn_delete_state_single.click =>
- unique_student_identifier = @$field_student_select_grade.val()
- problem_to_reset = @$field_problem_select_single.val()
- if not unique_student_identifier
- return @$request_response_error_grade.text gettext("Please enter a student email address or username.")
- if not problem_to_reset
- return @$request_response_error_grade.text gettext("Please enter a problem location.")
- confirm_message = gettext("Delete student '<%= student_id %>'s state on problem '<%= problem_id %>'?")
- full_confirm_message = _.template(confirm_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
-
- if window.confirm full_confirm_message
- send_data =
- unique_student_identifier: unique_student_identifier
- problem_to_reset: problem_to_reset
- delete_module: true
- error_message = gettext("Error deleting student '<%= student_id %>'s state on problem '<%= problem_id %>'. Make sure that the problem and student identifiers are complete and correct.")
- full_error_message = _.template(error_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_delete_state_single.data 'endpoint'
- data: send_data
- success: @clear_errors_then -> alert gettext('Module state successfully deleted.')
- error: std_ajax_err => @$request_response_error_grade.text full_error_message
- else
- # Clear error messages if "Cancel" was chosen on confirmation alert
- @clear_errors()
-
- # start task to rescore problem for student
- @$btn_rescore_problem_single.click =>
- unique_student_identifier = @$field_student_select_grade.val()
- problem_to_reset = @$field_problem_select_single.val()
- if not unique_student_identifier
- return @$request_response_error_grade.text gettext("Please enter a student email address or username.")
- if not problem_to_reset
- return @$request_response_error_grade.text gettext("Please enter a problem location.")
- send_data =
- unique_student_identifier: unique_student_identifier
- problem_to_reset: problem_to_reset
- success_message = gettext("Started rescore problem task for problem '<%= problem_id %>' and student '<%= student_id %>'. Click the 'Show Background Task History for Student' button to see the status of the task.")
- full_success_message = _.template(success_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
- error_message = gettext("Error starting a task to rescore problem '<%= problem_id %>' for student '<%= student_id %>'. Make sure that the the problem and student identifiers are complete and correct.")
- full_error_message = _.template(error_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_rescore_problem_single.data 'endpoint'
- data: send_data
- success: @clear_errors_then -> alert full_success_message
- error: std_ajax_err => @$request_response_error_grade.text full_error_message
-
- # list task history for student+problem
- @$btn_task_history_single.click =>
- unique_student_identifier = @$field_student_select_grade.val()
- problem_to_reset = @$field_problem_select_single.val()
- if not unique_student_identifier
- return @$request_response_error_grade.text gettext("Please enter a student email address or username.")
- if not problem_to_reset
- return @$request_response_error_grade.text gettext("Please enter a problem location.")
- send_data =
- unique_student_identifier: unique_student_identifier
- problem_location_str: problem_to_reset
- error_message = gettext("Error getting task history for problem '<%= problem_id %>' and student '<%= student_id %>'. Make sure that the problem and student identifiers are complete and correct.")
- full_error_message = _.template(error_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_task_history_single.data 'endpoint'
- data: send_data
- success: @clear_errors_then (data) =>
- create_task_list_table @$table_task_history_single, data.tasks
- error: std_ajax_err => @$request_response_error_grade.text full_error_message
-
- # reset entrance exam attempts for student
- @$btn_reset_entrance_exam_attempts.click =>
- unique_student_identifier = @$field_entrance_exam_student_select_grade.val()
- if not unique_student_identifier
- return @$request_response_error_ee.text gettext("Please enter a student email address or username.")
- send_data =
- unique_student_identifier: unique_student_identifier
- delete_module: false
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_reset_entrance_exam_attempts.data 'endpoint'
- data: send_data
- success: @clear_errors_then ->
- success_message = gettext("Entrance exam attempts is being reset for student '{student_id}'.")
- full_success_message = interpolate_text(success_message, {student_id: unique_student_identifier})
- alert full_success_message
- error: std_ajax_err =>
- error_message = gettext("Error resetting entrance exam attempts for student '{student_id}'. Make sure student identifier is correct.")
- full_error_message = interpolate_text(error_message, {student_id: unique_student_identifier})
- @$request_response_error_ee.text full_error_message
-
- # start task to rescore entrance exam for student
- @$btn_rescore_entrance_exam.click =>
- unique_student_identifier = @$field_entrance_exam_student_select_grade.val()
- if not unique_student_identifier
- return @$request_response_error_ee.text gettext("Please enter a student email address or username.")
- send_data =
- unique_student_identifier: unique_student_identifier
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_rescore_entrance_exam.data 'endpoint'
- data: send_data
- success: @clear_errors_then ->
- success_message = gettext("Started entrance exam rescore task for student '{student_id}'. Click the 'Show Background Task History for Student' button to see the status of the task.")
- full_success_message = interpolate_text(success_message, {student_id: unique_student_identifier})
- alert full_success_message
- error: std_ajax_err =>
- error_message = gettext("Error starting a task to rescore entrance exam for student '{student_id}'. Make sure that entrance exam has problems in it and student identifier is correct.")
- full_error_message = interpolate_text(error_message, {student_id: unique_student_identifier})
- @$request_response_error_ee.text full_error_message
-
- # Mark a student to skip entrance exam
- @$btn_skip_entrance_exam.click =>
- unique_student_identifier = @$field_entrance_exam_student_select_grade.val()
- if not unique_student_identifier
- return @$request_response_error_ee.text gettext("Enter a student's username or email address.")
- confirm_message = gettext("Do you want to allow this student ('{student_id}') to skip the entrance exam?")
- full_confirm_message = interpolate_text(confirm_message, {student_id: unique_student_identifier})
- if window.confirm full_confirm_message
- send_data =
- unique_student_identifier: unique_student_identifier
-
- $.ajax
- dataType: 'json'
- url: @$btn_skip_entrance_exam.data 'endpoint'
- data: send_data
- type: 'POST'
- success: @clear_errors_then (data) ->
- alert data.message
- error: std_ajax_err =>
- error_message = gettext("An error occurred. Make sure that the student's username or email address is correct and try again.")
- @$request_response_error_ee.text error_message
-
- # delete student state for entrance exam
- @$btn_delete_entrance_exam_state.click =>
- unique_student_identifier = @$field_entrance_exam_student_select_grade.val()
- if not unique_student_identifier
- return @$request_response_error_ee.text gettext("Please enter a student email address or username.")
- send_data =
- unique_student_identifier: unique_student_identifier
- delete_module: true
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_delete_entrance_exam_state.data 'endpoint'
- data: send_data
- success: @clear_errors_then ->
- success_message = gettext("Entrance exam state is being deleted for student '{student_id}'.")
- full_success_message = interpolate_text(success_message, {student_id: unique_student_identifier})
- alert full_success_message
- error: std_ajax_err =>
- error_message = gettext("Error deleting entrance exam state for student '{student_id}'. Make sure student identifier is correct.")
- full_error_message = interpolate_text(error_message, {student_id: unique_student_identifier})
- @$request_response_error_ee.text full_error_message
-
- # list entrance exam task history for student
- @$btn_entrance_exam_task_history.click =>
- unique_student_identifier = @$field_entrance_exam_student_select_grade.val()
- if not unique_student_identifier
- return @$request_response_error_ee.text gettext("Enter a student's username or email address.")
- send_data =
- unique_student_identifier: unique_student_identifier
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_entrance_exam_task_history.data 'endpoint'
- data: send_data
- success: @clear_errors_then (data) =>
- create_task_list_table @$table_entrance_exam_task_history, data.tasks
- error: std_ajax_err =>
- error_message = gettext("Error getting entrance exam task history for student '{student_id}'. Make sure student identifier is correct.")
- full_error_message = interpolate_text(error_message, {student_id: unique_student_identifier})
- @$request_response_error_ee.text full_error_message
-
- # start task to reset attempts on problem for all students
- @$btn_reset_attempts_all.click =>
- problem_to_reset = @$field_problem_select_all.val()
- if not problem_to_reset
- return @$request_response_error_all.text gettext("Please enter a problem location.")
- confirm_message = gettext("Reset attempts for all students on problem '<%= problem_id %>'?")
- full_confirm_message = _.template(confirm_message)({problem_id: problem_to_reset})
- if window.confirm full_confirm_message
- send_data =
- all_students: true
- problem_to_reset: problem_to_reset
- success_message = gettext("Successfully started task to reset attempts for problem '<%= problem_id %>'. Click the 'Show Background Task History for Problem' button to see the status of the task.")
- full_success_message = _.template(success_message)({problem_id: problem_to_reset})
- error_message = gettext("Error starting a task to reset attempts for all students on problem '<%= problem_id %>'. Make sure that the problem identifier is complete and correct.")
- full_error_message = _.template(error_message)({problem_id: problem_to_reset})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_reset_attempts_all.data 'endpoint'
- data: send_data
- success: @clear_errors_then -> alert full_success_message
- error: std_ajax_err => @$request_response_error_all.text full_error_message
- else
- # Clear error messages if "Cancel" was chosen on confirmation alert
- @clear_errors()
-
- # start task to rescore problem for all students
- @$btn_rescore_problem_all.click =>
- problem_to_reset = @$field_problem_select_all.val()
- if not problem_to_reset
- return @$request_response_error_all.text gettext("Please enter a problem location.")
- confirm_message = gettext("Rescore problem '<%= problem_id %>' for all students?")
- full_confirm_message = _.template(confirm_message)({problem_id: problem_to_reset})
- if window.confirm full_confirm_message
- send_data =
- all_students: true
- problem_to_reset: problem_to_reset
- success_message = gettext("Successfully started task to rescore problem '<%= problem_id %>' for all students. Click the 'Show Background Task History for Problem' button to see the status of the task.")
- full_success_message = _.template(success_message)({problem_id: problem_to_reset})
- error_message = gettext("Error starting a task to rescore problem '<%= problem_id %>'. Make sure that the problem identifier is complete and correct.")
- full_error_message = _.template(error_message)({problem_id: problem_to_reset})
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_rescore_problem_all.data 'endpoint'
- data: send_data
- success: @clear_errors_then -> alert full_success_message
- error: std_ajax_err => @$request_response_error_all.text full_error_message
- else
- # Clear error messages if "Cancel" was chosen on confirmation alert
- @clear_errors()
-
- # list task history for problem
- @$btn_task_history_all.click =>
- send_data =
- problem_location_str: @$field_problem_select_all.val()
-
- if not send_data.problem_location_str
- return @$request_response_error_all.text gettext("Please enter a problem location.")
-
- $.ajax
- type: 'POST'
- dataType: 'json'
- url: @$btn_task_history_all.data 'endpoint'
- data: send_data
- success: @clear_errors_then (data) =>
- create_task_list_table @$table_task_history_all, data.tasks
- error: std_ajax_err => @$request_response_error_all.text gettext("Error listing task history for this student and problem.")
-
- # wraps a function, but first clear the error displays
- clear_errors_then: (cb) ->
- @$request_response_error_progress.empty()
- @$request_response_error_grade.empty()
- @$request_response_error_ee.empty()
- @$request_response_error_all.empty()
- ->
- cb?.apply this, arguments
-
-
- clear_errors: ->
- @$request_response_error_progress.empty()
- @$request_response_error_grade.empty()
- @$request_response_error_ee.empty()
- @$request_response_error_all.empty()
-
- # handler for when the section title is clicked.
- onClickTitle: -> @instructor_tasks.task_poller.start()
-
- # handler for when the section is closed
- onExit: -> @instructor_tasks.task_poller.stop()
-
-
-# export for use
-# create parent namespaces if they do not already exist.
-_.defaults window, InstructorDashboard: {}
-_.defaults window.InstructorDashboard, sections: {}
-_.defaults window.InstructorDashboard.sections,
- StudentAdmin: StudentAdmin
+ _.defaults(window.InstructorDashboard.sections, {
+ StudentAdmin: this.StudentAdmin
+ });
+}).call(this);
diff --git a/lms/static/js/instructor_dashboard/util.js b/lms/static/js/instructor_dashboard/util.js
index 5782107b22..2acb3b7295 100644
--- a/lms/static/js/instructor_dashboard/util.js
+++ b/lms/static/js/instructor_dashboard/util.js
@@ -1,463 +1,537 @@
-# Common utilities for instructor dashboard components.
+/* globals _, Logger, Slick, tinyMCE, InstructorDashboard */
-# reverse arguments on common functions to enable
-# better coffeescript with callbacks at the end.
-plantTimeout = (ms, cb) -> setTimeout cb, ms
-plantInterval = (ms, cb) -> setInterval cb, ms
+(function() {
+ 'use strict';
+ var IntervalManager, KeywordValidator,
+ createEmailContentTable, createEmailMessageViews,
+ findAndAssert, pWrapper, plantInterval, plantTimeout,
+ sentToFormatter, setupCopyEmailButton, subjectFormatter,
+ unknownIfNullFormatter, unknownP,
+ anyOf = [].indexOf || function(item) {
+ var i, l;
+ for (i = 0, l = this.length; i < l; i++) {
+ if (i in this && this[i] === item) {
+ return i;
+ }
+ }
+ return -1;
+ };
+ plantTimeout = function(ms, cb) {
+ return setTimeout(cb, ms);
+ };
-# get jquery element and assert its existance
-find_and_assert = ($root, selector) ->
- item = $root.find selector
- if item.length != 1
- console.error "element selection failed for '#{selector}' resulted in length #{item.length}"
- throw "Failed Element Selection"
- else
- item
+ plantInterval = function(ms, cb) {
+ return setInterval(cb, ms);
+ };
-# standard ajax error wrapper
-#
-# wraps a `handler` function so that first
-# it prints basic error information to the console.
-@std_ajax_err = (handler) -> (jqXHR, textStatus, errorThrown) ->
- console.warn """ajax error
- textStatus: #{textStatus}
- errorThrown: #{errorThrown}"""
- handler.apply this, arguments
+ findAndAssert = function($root, selector) {
+ var item, msg;
+ item = $root.find(selector);
+ if (item.length !== 1) {
+ msg = 'Failed Element Selection';
+ throw msg;
+ } else {
+ return item;
+ }
+ };
+ this.statusAjaxError = function(handler) {
+ return function(jqXHR, textStatus, errorThrown) { // eslint-disable-line no-unused-vars
+ return handler.apply(this, arguments);
+ };
+ };
-# render a task list table to the DOM
-# `$table_tasks` the $element in which to put the table
-# `tasks_data`
-@create_task_list_table = ($table_tasks, tasks_data) ->
- $table_tasks.empty()
+ this.createTaskListTable = function($tableTasks, tasksData) {
+ var $tablePlaceholder, columns, options, tableData;
+ $tableTasks.empty();
+ options = {
+ enableCellNavigation: true,
+ enableColumnReorder: false,
+ autoHeight: true,
+ rowHeight: 100,
+ forceFitColumns: true
+ };
+ columns = [
+ {
+ id: 'task_type',
+ field: 'task_type',
+ /*
+ Translators: a "Task" is a background process such as grading students or sending email
+ */
- options =
- enableCellNavigation: true
- enableColumnReorder: false
- autoHeight: true
- rowHeight: 100
- forceFitColumns: true
+ name: gettext('Task Type'),
+ minWidth: 102
+ }, {
+ id: 'task_input',
+ field: 'task_input',
+ /*
+ Translators: a "Task" is a background process such as grading students or sending email
+ */
- columns = [
- id: 'task_type'
- field: 'task_type'
- ###
- Translators: a "Task" is a background process such as grading students or sending email
- ###
- name: gettext('Task Type')
- minWidth: 102
- ,
- id: 'task_input'
- field: 'task_input'
- ###
- Translators: a "Task" is a background process such as grading students or sending email
- ###
- name: gettext('Task inputs')
- minWidth: 150
- ,
- id: 'task_id'
- field: 'task_id'
- ###
- Translators: a "Task" is a background process such as grading students or sending email
- ###
- name: gettext('Task ID')
- minWidth: 150
- ,
- id: 'requester'
- field: 'requester'
- ###
- Translators: a "Requester" is a username that requested a task such as sending email
- ###
- name: gettext('Requester')
- minWidth: 80
- ,
- id: 'created'
- field: 'created'
- ###
- Translators: A timestamp of when a task (eg, sending email) was submitted appears after this
- ###
- name: gettext('Submitted')
- minWidth: 120
- ,
- id: 'duration_sec'
- field: 'duration_sec'
- ###
- Translators: The length of a task (eg, sending email) in seconds appears this
- ###
- name: gettext('Duration (sec)')
- minWidth: 80
- ,
- id: 'task_state'
- field: 'task_state'
- ###
- Translators: The state (eg, "In progress") of a task (eg, sending email) appears after this.
- ###
- name: gettext('State')
- minWidth: 80
- ,
- id: 'status'
- field: 'status'
- ###
- Translators: a "Task" is a background process such as grading students or sending email
- ###
- name: gettext('Task Status')
- minWidth: 80
- ,
- id: 'task_message'
- field: 'task_message'
- ###
- Translators: a "Task" is a background process such as grading students or sending email
- ###
- name: gettext('Task Progress')
- minWidth: 120
- ]
+ name: gettext('Task inputs'),
+ minWidth: 150
+ }, {
+ id: 'task_id',
+ field: 'task_id',
+ /*
+ Translators: a "Task" is a background process such as grading students or sending email
+ */
- table_data = tasks_data
+ name: gettext('Task ID'),
+ minWidth: 150
+ }, {
+ id: 'requester',
+ field: 'requester',
+ /*
+ Translators: a "Requester" is a username that requested a task such as sending email
+ */
- $table_placeholder = $ '', class: 'slickgrid'
- $table_tasks.append($table_placeholder)
- grid = new Slick.Grid($table_placeholder, table_data, columns, options)
+ name: gettext('Requester'),
+ minWidth: 80
+ }, {
+ id: 'created',
+ field: 'created',
+ /*
+ Translators: A timestamp of when a task (eg, sending email) was submitted appears after this
+ */
-# Formats the subject field for email content history table
-subject_formatter = (row, cell, value, columnDef, dataContext) ->
- if value is null then return gettext("An error occurred retrieving your email. Please try again later, and contact technical support if the problem persists.")
- subject_text = $('').text(value['subject']).html()
- return edx.HtmlUtils.joinHtml(
- edx.HtmlUtils.HTML('