Commit dfd9b463 by asadiqbal

SOL-1455

parent 4f131fec
......@@ -737,7 +737,7 @@ class CertificatesTest(BaseInstructorDashboardTest):
self.certificates_section.add_certificate_exception(self.user_name, '')
self.assertIn(
'User (username/email={user}) already in exception list.'.format(user=self.user_name),
'{user} already in exception list.'.format(user=self.user_name),
self.certificates_section.message.text
)
......@@ -784,8 +784,7 @@ class CertificatesTest(BaseInstructorDashboardTest):
self.certificates_section.wait_for_ajax()
self.assertIn(
"We can't find the user (username/email={user}) you've entered. "
"Make sure the username or email address is correct, then try again.".format(user=invalid_user),
"{user} does not exist in the LMS. Please check your spelling and retry.".format(user=invalid_user),
self.certificates_section.message.text
)
......@@ -818,8 +817,7 @@ class CertificatesTest(BaseInstructorDashboardTest):
self.certificates_section.wait_for_ajax()
self.assertIn(
"The user (username/email={user}) you have entered is not enrolled in this course. "
"Make sure the username or email address is correct, then try again.".format(user=new_user),
"{user} is not enrolled in this course. Please check your spelling and retry.".format(user=new_user),
self.certificates_section.message.text
)
......@@ -840,6 +838,7 @@ class CertificatesTest(BaseInstructorDashboardTest):
self.certificates_section.wait_for_ajax()
self.assertIn(
'Certificate generation started for white listed students.',
self.user_name + ' has been successfully added to the exception list. Click Generate Exception Certificate'
' below to send the certificate.',
self.certificates_section.message.text
)
......@@ -474,8 +474,7 @@ class CertificateExceptionViewInstructorApiTest(SharedModuleStoreTestCase):
# Assert Error Message
self.assertEqual(
res_json['message'],
u"We can't find the user (username/email={user}) you've entered. "
u"Make sure the username or email address is correct, then try again.".format(user=invalid_user)
u"{user} does not exist in the LMS. Please check your spelling and retry.".format(user=invalid_user)
)
def test_certificate_exception_missing_username_and_email_error(self):
......@@ -501,7 +500,7 @@ class CertificateExceptionViewInstructorApiTest(SharedModuleStoreTestCase):
self.assertEqual(
res_json['message'],
u'Student username/email field is required and can not be empty. '
u'Kindly fill in username/email and then press "Add Exception" button.'
u'Kindly fill in username/email and then press "Add to Exception List" button.'
)
def test_certificate_exception_duplicate_user_error(self):
......@@ -591,8 +590,7 @@ class CertificateExceptionViewInstructorApiTest(SharedModuleStoreTestCase):
# Assert Error Message
self.assertEqual(
res_json['message'],
"The user (username/email={user}) you have entered is not enrolled in this course. "
"Make sure the username or email address is correct, then try again.".format(
"{user} is not enrolled in this course. Please check your spelling and retry.".format(
user=self.certificate_exception['user_name']
)
)
......@@ -646,7 +644,7 @@ class CertificateExceptionViewInstructorApiTest(SharedModuleStoreTestCase):
# Assert Error Message
self.assertEqual(
res_json['message'],
u"Invalid Json data, Please refresh the page and then try again."
u"The record is not in the correct format. Please add a valid username or email address."
)
def test_remove_certificate_exception_non_existing_error(self):
......
......@@ -2839,22 +2839,21 @@ def parse_request_data_and_get_user(request, course_key):
try:
certificate_exception = json.loads(request.body or '{}')
except ValueError:
raise ValueError(_('Invalid Json data, Please refresh the page and then try again.'))
raise ValueError(_('The record is not in the correct format. Please add a valid username or email address.'))
user = certificate_exception.get('user_name', '') or certificate_exception.get('user_email', '')
if not user:
raise ValueError(_('Student username/email field is required and can not be empty. '
'Kindly fill in username/email and then press "Add Exception" button.'))
'Kindly fill in username/email and then press "Add to Exception List" button.'))
try:
db_user = get_user_by_username_or_email(user)
except ObjectDoesNotExist:
raise ValueError(_("We can't find the user (username/email={user}) you've entered. "
"Make sure the username or email address is correct, then try again.").format(user=user))
raise ValueError(_("{user} does not exist in the LMS. Please check your spelling and retry.").format(user=user))
# Make Sure the given student is enrolled in the course
if not CourseEnrollment.is_enrolled(db_user, course_key):
raise ValueError(_("The user (username/email={user}) you have entered is not enrolled in this course. "
"Make sure the username or email address is correct, then try again.").format(user=user))
raise ValueError(_("{user} is not enrolled in this course. Please check your spelling and retry.")
.format(user=user))
return certificate_exception, db_user
......
......@@ -32,7 +32,8 @@
el: DOM_SELECTORS.bulk_exception,
events: {
'change #browseBtn': 'chooseFile',
'click .upload-csv-button': 'uploadCSV'
'click .upload-csv-button': 'uploadCSV',
'click a.arrow': 'toggleMessageDetails'
},
initialize: function(options){
......@@ -72,19 +73,22 @@
},
display_response: function(data_from_server) {
$(".results").empty();
$(".bulk-exception-results").removeClass('hidden').empty();
// Display general error messages
if (data_from_server.general_errors.length) {
var errors = data_from_server.general_errors;
generate_div('msg-error', MESSAGE_GROUP.general_errors, gettext('Errors!'), errors);
generate_div(
MESSAGE_GROUP.general_errors,
gettext('Uploaded file issues. Click on "+" to view.'),
errors
);
}
// Display success message
if (data_from_server.success.length) {
var success_data = data_from_server.success;
generate_div(
'msg-success',
MESSAGE_GROUP.successfully_added,
get_text(success_data.length, MESSAGE_GROUP.successfully_added),
success_data
......@@ -98,7 +102,6 @@
if (row_errors.data_format_error.length) {
var format_errors = row_errors.data_format_error;
generate_div(
'msg-error',
MESSAGE_GROUP.data_format_error,
get_text(format_errors.length, MESSAGE_GROUP.data_format_error),
format_errors
......@@ -107,7 +110,6 @@
if (row_errors.user_not_exist.length) {
var user_not_exist = row_errors.user_not_exist;
generate_div(
'msg-error',
MESSAGE_GROUP.user_not_exist,
get_text(user_not_exist.length, MESSAGE_GROUP.user_not_exist),
user_not_exist
......@@ -116,7 +118,6 @@
if (row_errors.user_already_white_listed.length) {
var user_already_white_listed = row_errors.user_already_white_listed;
generate_div(
'msg-error',
MESSAGE_GROUP.user_already_white_listed,
get_text(user_already_white_listed.length, MESSAGE_GROUP.user_already_white_listed),
user_already_white_listed
......@@ -125,7 +126,6 @@
if (row_errors.user_not_enrolled.length) {
var user_not_enrolled = row_errors.user_not_enrolled;
generate_div(
'msg-error',
MESSAGE_GROUP.user_not_enrolled,
get_text(user_not_enrolled.length, MESSAGE_GROUP.user_not_enrolled),
user_not_enrolled
......@@ -133,17 +133,22 @@
}
}
function generate_div(div_class, group, heading, display_data) {
function generate_div(group, heading, display_data) {
// inner function generate div and display response messages.
$('<div/>', {
class: 'message ' + div_class + ' ' + group
}).appendTo('.results').prepend( "<b>" + heading + "</b>" );
class: 'message ' + group
}).appendTo('.bulk-exception-results').prepend(
"<a id= '" + group + "' href='javascript:void(0);' class='arrow'> + </a>" + heading
).append($('<ul/>', {
class: group
}));
for(var i = 0; i < display_data.length; i++){
$('<div/>', {
$('<li/>', {
text: display_data[i]
}).appendTo('.results > .' + div_class + '.' + group);
}).appendTo('div.message > .' + group);
}
$("div.message > ." + group).hide();
}
function get_text(qty, group) {
......@@ -179,10 +184,23 @@
}
},
toggleMessageDetails: function(event) {
if (event && event.preventDefault) { event.preventDefault(); }
var group = event.target.id;
$("div.message > ." + group).slideToggle( "fast", function() {
if ($(this).is(':visible')) {
event.target.text = ' -- ';
} else {
event.target.text = ' + ';
}
});
},
chooseFile: function(event) {
if (event && event.preventDefault) { event.preventDefault(); }
if (event.currentTarget.files.length === 1) {
this.$el.find(DOM_SELECTORS.upload_csv_button).removeClass('is-disabled');
this.$el.find(DOM_SELECTORS.upload_csv_button).removeClass('is-disabled')
.addClass('button-blue');
this.$el.find(DOM_SELECTORS.browse_file).val(
event.currentTarget.value.substring(event.currentTarget.value.lastIndexOf("\\") + 1));
}
......
......@@ -14,7 +14,7 @@
function($, _, gettext, Backbone){
return Backbone.View.extend({
el: "#white-listed-students",
message_div: '#certificate-white-list-editor .message',
message_div: 'div.white-listed-students > div.message',
generate_exception_certificates_radio:
'input:radio[name=generate-exception-certificates-radio]:checked',
......@@ -42,11 +42,27 @@
},
removeException: function(event){
// Delegate remove exception event to certificate white-list editor view
this.certificateWhiteListEditorView.trigger('removeException', $(event.target).data());
// avoid default click behavior of link by returning false.
return false;
var certificate = $(event.target).data();
var model = this.collection.findWhere(certificate);
var self = this;
if(model){
model.destroy(
{
success: function() {
self.showMessage('Student Removed from certificate white list successfully.');
},
error: this.showError(this),
wait: true,
data: JSON.stringify(model.attributes)
}
);
}
else{
this.showMessage(
'Could not find Certificate Exception in white list. ' +
'Please refresh the page and try again'
);
}
},
generateExceptionCertificates: function(){
......@@ -56,17 +72,15 @@
);
},
showMessage: function(message, messageClass){
$(this.message_div).text(message).
removeClass('msg-error msg-success').addClass(messageClass).focus();
$('html, body').animate({
scrollTop: $(this.message_div).offset().top - 20
}, 1000);
showMessage: function(message){
$(this.message_div + ">p" ).remove();
$(this.message_div).removeClass('hidden').append("<p>"+ gettext(message) + "</p>").focus();
$(this.message_div).fadeOut(6000, "linear");
},
showSuccess: function(caller_object){
return function(xhr){
caller_object.showMessage(xhr.message, 'msg-success');
caller_object.showMessage(xhr.message);
};
},
......@@ -74,12 +88,11 @@
return function(xhr){
try{
var response = JSON.parse(xhr.responseText);
caller_object.showMessage(response.message, 'msg-error');
caller_object.showMessage(response.message);
}
catch(exception){
caller_object.showMessage(
"Server Error, Please refresh the page and try again.", 'msg-error'
);
"Server Error, Please refresh the page and try again.");
}
};
}
......
......@@ -19,11 +19,6 @@
'click #add-exception': 'addException'
},
initialize: function(){
this.on('removeException', this.removeException);
},
render: function(){
var template = this.loadTemplate('certificate-white-list-editor');
this.$el.html(template());
......@@ -59,8 +54,7 @@
if(this.collection.findWhere(model)){
this.showMessage(
"User (username/email=" + (user_name || user_email) + ") already in exception list.",
'msg-error'
(user_name || user_email) + " already in exception list."
);
}
else if(certificate_exception.isValid()){
......@@ -70,7 +64,8 @@
success: this.showSuccess(
this,
true,
'Student added to Certificate white list successfully.'
(user_name || user_email) + ' has been successfully added to the exception list.' +
' Click Generate Exception Certificate below to send the certificate.'
),
error: this.showError(this)
}
......@@ -78,34 +73,7 @@
}
else{
this.showMessage(certificate_exception.validationError, 'msg-error');
}
},
removeException: function(certificate){
var model = this.collection.findWhere(certificate);
if(model){
model.destroy(
{
success: this.showSuccess(
this,
false,
'Student Removed from certificate white list successfully.'
),
error: this.showError(this),
wait: true,
//emulateJSON: true,
data: JSON.stringify(model.attributes)
}
);
this.showMessage('Exception is being removed from server.', 'msg-success');
}
else{
this.showMessage(
'Could not find Certificate Exception in white list. ' +
'Please refresh the page and try again',
'msg-error'
);
this.showMessage(certificate_exception.validationError);
}
},
......@@ -114,12 +82,9 @@
return re.test(email);
},
showMessage: function(message, messageClass){
this.$(this.message_div).text(message).
removeClass('msg-error msg-success').addClass(messageClass).focus();
$('html, body').animate({
scrollTop: this.$el.offset().top - 20
}, 1000);
showMessage: function(message){
$(this.message_div + ">p" ).remove();
this.$(this.message_div).removeClass('hidden').append("<p>"+ gettext(message) + "</p>");
},
showSuccess: function(caller, add_model, message){
......@@ -127,7 +92,7 @@
if(add_model){
caller.collection.add(model);
}
caller.showMessage(message, 'msg-success');
caller.showMessage(message);
};
},
......@@ -135,11 +100,11 @@
return function(model, response){
try{
var response_data = JSON.parse(response.responseText);
caller.showMessage(response_data.message, 'msg-error');
caller.showMessage(response_data.message);
}
catch(exception){
caller.showMessage("" +
"Server Error, Please refresh the page and try again.", 'msg-error'
"Server Error, Please refresh the page and try again."
);
}
};
......
......@@ -330,16 +330,15 @@ define([
it("verifies success and error messages", function() {
var message_selector='.message',
error_class = 'msg-error',
success_class = 'msg-success',
success_message = 'Student added to Certificate white list successfully.',
success_message = 'test_user has been successfully added to the exception list. Click Generate' +
' Exception Certificate below to send the certificate.',
requests = AjaxHelpers.requests(this),
duplicate_user='test_user';
var error_messages = {
empty_user_name_email: 'Student username/email field is required and can not be empty. ' +
'Kindly fill in username/email and then press "Add Exception" button.',
duplicate_user: "User (username/email=" + (duplicate_user) + ") already in exception list."
duplicate_user: "<p>" + (duplicate_user) + " already in exception list.</p>"
};
// click 'Add Exception' button with empty username/email field
......@@ -347,7 +346,6 @@ define([
view.$el.find('#add-exception').click();
// Verify error message for missing username/email
expect(view.$el.find(message_selector)).toHaveClass(error_class);
expect(view.$el.find(message_selector).html()).toMatch(error_messages.empty_user_name_email);
// Add a new Exception to list
......@@ -369,7 +367,6 @@ define([
);
// Verify success message
expect(view.$el.find(message_selector)).toHaveClass(success_class);
expect(view.$el.find(message_selector).html()).toMatch(success_message);
// Add a duplicate Certificate Exception
......@@ -378,7 +375,6 @@ define([
view.$el.find('#add-exception').click();
// Verify success message
expect(view.$el.find(message_selector)).toHaveClass(error_class);
expect(view.$el.find(message_selector).html()).toEqual(error_messages.duplicate_user);
});
......
......@@ -1802,15 +1802,6 @@ input[name="subject"] {
width: 75%;
}
.certificate-exception-container {
h3 {
border-bottom: 1px groove black;
display: inline-block;
}
p.under-heading-text {
margin: 12px 0 12px 0;
}
}
}
input[name="subject"] {
......@@ -2110,103 +2101,174 @@ input[name="subject"] {
@include right(auto);
}
#certificate-white-list-editor{
.certificate-exception-inputs{
.student-username-or-email{
width: 300px;
}
.notes-field{
width: 400px;
}
p+p{
margin-top: 5px;
}
.message{
margin-top: 10px;
}
}
}
.white-listed-students {
table {
width: 100%;
word-wrap: break-word;
// view - certificates
// --------------------
.instructor-dashboard-wrapper-2 section.idash-section#certificates {
th {
@extend %t-copy-sub2;
background-color: $gray-l5;
padding: ($baseline*0.75) ($baseline/2) ($baseline*0.75) ($baseline/2);
vertical-align: middle;
text-align: left;
color: $gray;
%btn-blue {
@extend %btn-primary-blue;
padding: ($baseline/2.5) ($baseline/2);
text-shadow: none;
margin-bottom: 10px;
}
&.date, &.email{
width: 230px;
%exception-message {
margin-top: 15px;
background-color: $gray-l4;
border-top-style: groove;
color: $black;
}
&.user-id{
width: 60px;
#certificate-white-list-editor {
.certificate-exception-inputs {
.student-username-or-email {
width: 300px;
margin-bottom: 10px;
}
&.user-name{
width: 150px;
.notes-field {
width: 400px;
}
&.action{
width: 150px;
p + p {
margin-top: 5px;
}
.message {
@extend %exception-message;
}
.button-blue {
@extend %btn-blue;
}
}
}
td {
padding: ($baseline/2);
vertical-align: middle;
text-align: left;
}
.white-listed-students {
margin-top: 10px;
table {
width: 100%;
word-wrap: break-word;
tbody {
box-shadow: 0 2px 2px $shadow-l1;
border: 1px solid $gray-l4;
background: $white;
th {
@extend %t-copy-sub2;
background-color: $gray-l5;
padding: ($baseline*0.75) ($baseline/2) ($baseline*0.75) ($baseline/2);
vertical-align: middle;
text-align: left;
color: $gray;
tr {
@include transition(all $tmg-f2 ease-in-out 0s);
border-top: 1px solid $gray-l4;
&.date, &.email {
width: 230px;
}
&:first-child {
border-top: none;
&.user-id {
width: 60px;
}
&:nth-child(odd) {
background-color: $gray-l6;
&.user-name {
width: 150px;
}
a {
color: $gray-d1;
&.action {
width: 150px;
}
}
td {
padding: ($baseline/2);
vertical-align: middle;
text-align: left;
}
tbody {
box-shadow: 0 2px 2px $shadow-l1;
border: 1px solid $gray-l4;
background: $white;
tr {
@include transition(all $tmg-f2 ease-in-out 0s);
border-top: 1px solid $gray-l4;
&:first-child {
border-top: none;
}
&:nth-child(odd) {
background-color: $gray-l6;
}
a {
color: $gray-d1;
&:hover {
color: $blue;
}
}
&:hover {
color: $blue;
background-color: $blue-l5;
}
}
}
}
.button-blue {
@extend %btn-blue;
}
.message {
@extend %exception-message;
}
}
&:hover {
background-color: $blue-l5;
.certificate-exception-container {
h3 {
border-bottom: 1px groove black;
display: inline-block;
}
p.under-heading {
margin: 12px 0 12px 0;
line-height: 23px;
}
}
.bulk-white-list-exception {
margin-top: 10px;
.white-list-csv {
.bulk-exception-results {
margin-top: 10px;
background-color: $gray-l4;
border-top-style: groove;
color: $black;
margin-bottom: 15px;
.message {
padding: 5px 10px;
margin: 2px;
}
}
.arrow {
font-weight: bold;
}
.button-blue {
@extend %btn-blue;
}
}
}
}
// Custom File upload
.customBrowseBtn {
margin: ($baseline/2) 0;
display: inline-block;
.file-browse {
position:relative;
overflow:hidden;
position: relative;
overflow: hidden;
display: inline;
@include margin-left(-5px);
span.browse{
span.browse {
@include button(simple, $blue);
@include margin-right($baseline);
padding: 6px ($baseline/2);
......@@ -2214,17 +2276,17 @@ input[name="subject"] {
border-radius: 0 3px 3px 0;
}
input.file_field {
position:absolute;
position: absolute;
@include right(0);
top:0;
margin:0;
padding:0;
cursor:pointer;
opacity:0;
filter:alpha(opacity=0);
top: 0;
margin: 0;
padding: 0;
cursor: pointer;
opacity: 0;
filter: alpha(opacity=0);
}
}
& > span, & input[disabled]{
& > span, & input[disabled] {
vertical-align: middle;
}
input[disabled] {
......@@ -2233,4 +2295,4 @@ input[name="subject"] {
border: 1px solid $lightGrey1;
cursor: not-allowed;
}
}
}
\ No newline at end of file
<h3><%= gettext("Bulk Exceptions") %></h3>
<div class="bulk_white_list_csv">
<p class="under-heading-text">
<%= gettext("You can upload a CSV file of usernames or email addresses to be added to the certificate exceptions white list.") %>
<div class="white-list-csv">
<p class="under-heading">
<%= gettext("Upload a comma separated values (.csv) file that contains the usernames or email addresses of learners who have been given exceptions. Include the username or email address in the first comma separated field. You can include an optional note describing the reason for the exception in the second comma separated field.") %>
</p>
<form id="bulk-white-list-exception-form" enctype="multipart/form-data">
<div class="customBrowseBtn">
......@@ -11,7 +11,7 @@
<input class="file_field" id="browseBtn" name="students_list" type="file" accept=".csv"/>
</div>
</div>
<button class="is-disabled upload-csv-button" type="submit"><%= gettext("Upload CSV") %></button>
<div><button class="is-disabled upload-csv-button" type="submit"><%= gettext('Add to Exception List') %></button></div>
</form>
<div class="results"></div>
<div class="bulk-exception-results hidden"></div>
</div>
\ No newline at end of file
<h3><%= gettext("Individual Exceptions") %></h3>
<p class="under-heading-text"> <%= gettext("You can add a username or email address to be added to the certificate exceptions white list.") %></p>
<p class="under-heading"> <%= gettext("Enter the username or email address of each learner that you want to add as an exception.") %></p>
<div class='certificate-exception-inputs'>
<div class="">
<input class='student-username-or-email' id="certificate-exception" type="text" placeholder="Student email or username" aria-describedby='student-user-name-or-email-tip'>
<textarea class='notes-field' id="notes" rows="10" placeholder="Free text notes" aria-describedby='notes-field-tip'></textarea>
<input type="button" id="add-exception" value="Add Exception">
<div class='message'></div>
</div>
<div>
<button type="button" class="button-blue" id="add-exception" ><%= gettext("Add to Exception List") %> </button>
</div>
<div class='message hidden'></div>
</div>
<h3><%= gettext("Generate Exception Certificates") %></h3>
<p class="under-heading">
<label>
<input type='radio' name='generate-exception-certificates-radio' checked="checked" value='new' aria-describedby='generate-exception-certificates-radio-new-tip'>
<span id='generate-exception-certificates-radio-new-tip'><%- gettext('Generate a Certificate for all ') %><strong><%- gettext('New') %></strong> <%- gettext('additions to the Exception list') %></span>
......@@ -7,8 +9,8 @@
<input type='radio' name='generate-exception-certificates-radio' value='all' aria-describedby='generate-exception-certificates-radio-all-tip'>
<span id='generate-exception-certificates-radio-all-tip'><%- gettext('Generate a Certificate for all users on the Exception list') %></span>
</label>
<br/>
<input type="button" id="generate-exception-certificates" value="<%- gettext('Generate Exception Certificates') %>" />
</p>
<button id="generate-exception-certificates" class="button-blue" type="button"><%= gettext('Generate Exception Certificates') %></button>
<br/>
<% if (certificates.length === 0) { %>
<p><%- gettext("No results") %></p>
......@@ -37,4 +39,5 @@
<% } %>
</tbody>
</table>
<div class='message hidden'></div>
<% } %>
......@@ -117,14 +117,15 @@ import json
<div class="certificate-exception-container">
<hr>
<h2> ${_("Certificate Exceptions")} </h2>
<p class="under-heading-text">
${_("Use this to generate certificates for users who did not pass the course but have been given an exception by the Course Team to earn a certificate.")}
<h2> ${_("SET CERTIFICATE EXCEPTIONS")} </h2>
<p class="under-heading info">
${_("Set exceptions to generate certificates for learners who did not qualify for a certificate but have " \
"been given an exception by the course team. After you add learners to the exception list, click Generate " \
"Exception Certificates below.")}
</p>
<div class="certificate-exception-section">
<div class="bulk-white-list-exception"></div>
<br />
<div id="certificate-white-list-editor"></div>
<div class="bulk-white-list-exception"></div>
<div class="white-listed-students" id="white-listed-students"></div>
<br/>
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment