Commit 6219ecac by Andy Armstrong Committed by cahrens

Support adding cohorts from the instructor dashboard

TNL-162
parent b58f7ad7
......@@ -5,6 +5,12 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected.
LMS: Support adding cohorts from the instructor dashboard. TNL-162
LMS: Support adding students to a cohort via the instructor dashboard. TNL-163
LMS: Show cohorts on the new instructor dashboard. TNL-161
LMS: Mobile API available for courses that opt in using the Course Advanced
Setting "Mobile Course Available" (only used in limited closed beta).
......
......@@ -3,11 +3,12 @@ This file contains the logic for cohort groups, as exposed internally to the
forums, and to the cohort admin views.
"""
from django.http import Http404
import logging
import random
from django.http import Http404
from django.utils.translation import ugettext as _
from courseware import courses
from student.models import get_user_by_username_or_email
from .models import CourseUserGroup
......@@ -140,7 +141,7 @@ def get_cohorted_commentables(course_key):
def get_cohort(user, course_key):
"""
Given a django User and a CourseKey, return the user's cohort in that
Given a Django user and a CourseKey, return the user's cohort in that
cohort.
Arguments:
......@@ -180,7 +181,7 @@ def get_cohort(user, course_key):
# Use the "default cohort".
group_name = DEFAULT_COHORT_NAME
group, _ = CourseUserGroup.objects.get_or_create(
group, __ = CourseUserGroup.objects.get_or_create(
course_id=course_key,
group_type=CourseUserGroup.COHORT,
name=group_name
......@@ -250,7 +251,7 @@ def add_cohort(course_key, name):
if CourseUserGroup.objects.filter(course_id=course_key,
group_type=CourseUserGroup.COHORT,
name=name).exists():
raise ValueError("Can't create two cohorts with the same name")
raise ValueError(_("You cannot create two cohorts with the same name"))
try:
course = courses.get_course_by_id(course_key)
......@@ -296,9 +297,10 @@ def add_user_to_cohort(cohort, username_or_email):
)
if course_cohorts.exists():
if course_cohorts[0] == cohort:
raise ValueError("User {0} already present in cohort {1}".format(
user.username,
cohort.name))
raise ValueError("User {user_name} already present in cohort {cohort_name}".format(
user_name=user.username,
cohort_name=cohort.name
))
else:
previous_cohort = course_cohorts[0].name
course_cohorts[0].users.remove(user)
......@@ -313,8 +315,9 @@ def delete_empty_cohort(course_key, name):
"""
cohort = get_cohort_by_name(course_key, name)
if cohort.users.exists():
raise ValueError(
"Can't delete non-empty cohort {0} in course {1}".format(
name, course_key))
raise ValueError(_("You cannot delete non-empty cohort {cohort_name} in course {course_key}").format(
cohort_name=name,
course_key=course_key
))
cohort.delete()
......@@ -9,6 +9,9 @@ from xmodule.modulestore import ModuleStoreEnum
class CohortFactory(DjangoModelFactory):
"""
Factory for constructing mock cohorts.
"""
FACTORY_FOR = CourseUserGroup
name = Sequence("cohort{}".format)
......@@ -17,6 +20,9 @@ class CohortFactory(DjangoModelFactory):
@post_generation
def users(self, create, extracted, **kwargs): # pylint: disable=W0613
"""
Returns the users associated with the cohort.
"""
if extracted:
self.users.add(*extracted)
......
......@@ -277,7 +277,7 @@ class AddCohortTestCase(CohortViewsTestCase):
self.verify_contains_added_cohort(
self.request_add_cohort(cohort_name, self.course),
cohort_name,
expected_error_msg="Can't create two cohorts with the same name"
expected_error_msg="You cannot create two cohorts with the same name"
)
......
......@@ -37,7 +37,7 @@ class MembershipPage(PageObject):
def _get_cohort_options(self):
"""
Returns the available options in the cohort dropdown, including the initial "Select a cohort".
Returns the available options in the cohort dropdown, including the initial "Select a cohort group".
"""
return self.q(css=".cohort-management #cohort-select option")
......@@ -55,7 +55,7 @@ class MembershipPage(PageObject):
def get_cohorts(self):
"""
Returns, as a list, the names of the available cohorts in the drop-down, filtering out "Select a cohort".
Returns, as a list, the names of the available cohorts in the drop-down, filtering out "Select a cohort group".
"""
return [
self._cohort_name(opt.text)
......@@ -86,6 +86,15 @@ class MembershipPage(PageObject):
lambda el: self._cohort_name(el.text) == cohort_name
).first.click()
def add_cohort(self, cohort_name):
"""
Adds a new manual cohort with the specified name.
"""
self.q(css="div.cohort-management-nav .action-create").first.click()
textinput = self.q(css="#cohort-create-name").results[0]
textinput.send_keys(cohort_name)
self.q(css="div.form-actions .action-save").first.click()
def get_cohort_group_setup(self):
"""
Returns the description of the current cohort
......
......@@ -11,6 +11,8 @@ from ...pages.lms.auto_auth import AutoAuthPage
from ...pages.lms.instructor_dashboard import InstructorDashboardPage
from ...pages.studio.settings_advanced import AdvancedSettingsPage
import uuid
class CohortConfigurationTest(UniqueCourseTest, CohortTestMixin):
"""
......@@ -149,6 +151,30 @@ class CohortConfigurationTest(UniqueCourseTest, CohortTestMixin):
self.assertEqual("There was an error when trying to add students:", error_messages[0])
self.assertEqual("Unknown user: unknown_user", error_messages[1])
self.assertEqual(
self.student_name+",unknown_user,",
self.student_name + ",unknown_user,",
self.membership_page.get_cohort_student_input_field_value()
)
def test_add_new_cohort(self):
"""
Scenario: A new manual cohort can be created, and a student assigned to it.
Given I have a course with a user in the course
When I add a new manual cohort to the course via the LMS instructor dashboard
Then the new cohort is displayed and has no users in it
And when I add the user to the new cohort
Then the cohort has 1 user
"""
new_cohort = str(uuid.uuid4().get_hex()[0:20])
self.assertFalse(new_cohort in self.membership_page.get_cohorts())
self.membership_page.add_cohort(new_cohort)
# After adding the cohort, it should automatically be selected
EmptyPromise(
lambda: new_cohort == self.membership_page.get_selected_cohort(), "Waiting for new cohort to appear"
).fulfill()
self.assertEqual(0, self.membership_page.get_selected_cohort_count())
self.membership_page.add_students_to_selected_cohort([self.instructor_name])
# Wait for the number of users in the cohort to change, indicating that the add operation is complete.
EmptyPromise(
lambda: 1 == self.membership_page.get_selected_cohort_count(), 'Waiting for student to be added'
).fulfill()
......@@ -992,6 +992,7 @@ courseware_js = (
base_vendor_js = [
'js/vendor/jquery.min.js',
'js/vendor/jquery.cookie.js',
'js/vendor/underscore-min.js'
]
main_vendor_js = base_vendor_js + [
......@@ -999,7 +1000,6 @@ main_vendor_js = base_vendor_js + [
'js/RequireJS-namespace-undefine.js',
'js/vendor/json2.js',
'js/vendor/jquery-ui.min.js',
'js/vendor/jquery.cookie.js',
'js/vendor/jquery.qtip.min.js',
'js/vendor/swfobject/swfobject.js',
'js/vendor/jquery.ba-bbq.min.js',
......
(function(Backbone) {
var NotificationModel = Backbone.Model.extend({
defaults: {
// Supported types are "confirmation" and "error".
/**
* The type of notification to be shown.
* Supported types are "confirmation", "warning" and "error".
*/
type: "confirmation",
/**
* The title to be shown for the notification. This string should be short so
* that it can be shown on a single line.
*/
title: "",
/**
* An optional message giving more details for the notification. This string can be as long
* as needed and will wrap.
*/
message: "",
/**
* An optional array of detail messages to be shown beneath the title and message. This is
* typically used to enumerate a set of warning or error conditions that occurred.
*/
details: [],
actionText: "",
/**
* The text label to be shown for an action button, or null if there is no associated action.
*/
actionText: null,
/**
* The class to be added to the action button. This allows selectors to be written that can
* target the action button directly.
*/
actionClass: "",
/**
* An optional icon class to be shown before the text on the action button.
*/
actionIconClass: "",
/**
* An optional callback that will be invoked when the user clicks on the action button.
*/
actionCallback: null
}
});
......
define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification', 'js/common_helpers/template_helpers'],
function (Backbone, $, NotificationModel, NotificationView, TemplateHelpers) {
describe("NotificationView", function () {
var createNotification, verifyTitle, verifyDetails, verifyAction, notificationView;
var createNotification, verifyTitle, verifyMessage, verifyDetails, verifyAction, notificationView;
createNotification = function (modelVals) {
var notificationModel = new NotificationModel(modelVals);
......@@ -16,6 +16,10 @@ define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification',
expect(notificationView.$('.message-title').text().trim()).toBe(expectedTitle);
};
verifyMessage = function (expectedMessage) {
expect(notificationView.$('.message-copy').text().trim()).toBe(expectedMessage);
};
verifyDetails = function (expectedDetails) {
var details = notificationView.$('.summary-item');
expect(details.length).toBe(expectedDetails.length);
......@@ -41,7 +45,7 @@ define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification',
it('has default values', function () {
createNotification({});
expect(notificationView.$('div.message').hasClass('message-confirmation')).toBe(true);
expect(notificationView.$('div.message')).toHaveClass('message-confirmation');
verifyTitle('');
verifyDetails([]);
verifyAction(null);
......@@ -49,8 +53,8 @@ define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification',
it('can use an error type', function () {
createNotification({type: 'error'});
expect(notificationView.$('div.message').hasClass('message-error')).toBe(true);
expect(notificationView.$('div.message').hasClass('message-confirmation')).toBe(false);
expect(notificationView.$('div.message')).toHaveClass('message-error');
expect(notificationView.$('div.message')).not.toHaveClass('message-confirmation');
});
it('can specify a title', function () {
......@@ -58,6 +62,11 @@ define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification',
verifyTitle('notification title');
});
it('can specify a message', function () {
createNotification({message: 'This is a dummy message'});
verifyMessage('This is a dummy message');
});
it('can specify details', function () {
var expectedDetails = ['detail 1', 'detail 2'];
createNotification({details: expectedDetails});
......@@ -69,9 +78,9 @@ define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification',
verifyAction('action text');
});
it ('does not show an action button if callback is not provided', function () {
it ('shows an action button if only text is provided', function () {
createNotification({actionText: 'action text'});
verifyAction(null);
verifyAction('action text');
});
it ('does not show an action button if text is not provided', function () {
......@@ -82,7 +91,7 @@ define(['backbone', 'jquery', 'js/models/notification', 'js/views/notification',
it ('triggers the callback when the action button is clicked', function () {
var actionCallback = jasmine.createSpy('Spy on callback');
var view = createNotification({actionText: 'action text', actionCallback: actionCallback});
notificationView.$('a.action-primary').click();
notificationView.$('button.action-primary').click();
expect(actionCallback).toHaveBeenCalledWith(view);
});
});
......
......@@ -101,7 +101,8 @@
addNotifications: function(modifiedUsers) {
var oldCohort, title, details, numPresent, numUsersAdded, numErrors,
createErrorDetails, errorActionCallback, errorModel;
createErrorDetails, errorActionCallback, errorModel,
errorLimit = 5;
// Show confirmation messages.
this.undelegateViewEvents(this.confirmationNotifications);
......@@ -165,7 +166,7 @@
numErrors = modifiedUsers.unknown.length;
if (numErrors > 0) {
createErrorDetails = function (unknownUsers, showAllErrors) {
var numErrors = unknownUsers.length, details = [], errorLimit = 5;
var numErrors = unknownUsers.length, details = [];
for (var i = 0; i < (showAllErrors ? numErrors : Math.min(errorLimit, numErrors)); i++) {
details.push(interpolate_text(gettext("Unknown user: {user}"), {user: unknownUsers[i]}));
......@@ -181,15 +182,16 @@
details = createErrorDetails(modifiedUsers.unknown, false);
errorActionCallback = function (view) {
view.model.set("actionCallback", null);
view.model.set("actionText", null);
view.model.set("details", createErrorDetails(modifiedUsers.unknown, true));
view.render();
};
errorModel = new NotificationModel({
details: details,
actionText: gettext("View all errors"),
actionCallback: numErrors > 5 ? errorActionCallback : null
actionText: numErrors > errorLimit ? gettext("View all errors") : null,
actionCallback: errorActionCallback,
actionClass: 'action-expand'
});
this.showErrorMessage(title, false, errorModel);
......
(function(Backbone, CohortEditorView) {
var CohortsView = Backbone.View.extend({
(function($, _, Backbone, gettext, interpolate_text, CohortEditorView, NotificationModel, NotificationView) {
var hiddenClass = 'is-hidden',
disabledClass = 'is-disabled';
this.CohortsView = Backbone.View.extend({
events : {
"change .cohort-select": "showCohortEditor"
'change .cohort-select': 'onCohortSelected',
'click .action-create': 'showAddCohortForm',
'click .action-cancel': 'cancelAddCohortForm',
'click .action-save': 'saveAddCohortForm'
},
initialize: function(options) {
this.template = _.template($('#cohorts-tpl').text());
this.selectorTemplate = _.template($('#cohort-selector-tpl').text());
this.addCohortFormTemplate = _.template($('#add-cohort-form-tpl').text());
this.advanced_settings_url = options.advanced_settings_url;
this.model.on('sync', this.onSync, this);
},
......@@ -15,7 +22,7 @@
this.$el.html(this.template({
cohorts: this.model.models
}));
this.renderSelector();
this.onSync();
return this;
},
......@@ -27,7 +34,25 @@
},
onSync: function() {
this.renderSelector(this.getSelectedCohort());
var selectedCohort = this.lastSelectedCohortId && this.model.get(this.lastSelectedCohortId),
hasCohorts = this.model.length > 0;
this.hideAddCohortForm();
if (hasCohorts) {
this.$('.cohort-management-nav').removeClass(hiddenClass);
this.renderSelector(selectedCohort);
if (selectedCohort) {
this.showCohortEditor(selectedCohort);
}
} else {
this.$('.cohort-management-nav').addClass(hiddenClass);
this.showNotification({
type: 'warning',
title: gettext('You currently have no cohort groups configured'),
actionText: gettext('Add Cohort Group'),
actionClass: 'action-create',
actionIconClass: 'icon-plus'
});
}
},
getSelectedCohort: function() {
......@@ -35,22 +60,116 @@
return id && this.model.get(parseInt(id));
},
showCohortEditor: function(event) {
onCohortSelected: function(event) {
event.preventDefault();
var selectedCohort = this.getSelectedCohort();
this.lastSelectedCohortId = selectedCohort.get('id');
this.showCohortEditor(selectedCohort);
},
showCohortEditor: function(cohort) {
this.removeNotification();
if (this.editor) {
this.editor.setCohort(selectedCohort);
this.editor.setCohort(cohort);
} else {
this.editor = new CohortEditorView({
el: this.$('.cohort-management-group'),
model: selectedCohort,
model: cohort,
cohorts: this.model,
advanced_settings_url: this.advanced_settings_url
});
this.editor.render();
}
},
showNotification: function(options, beforeElement) {
var model = new NotificationModel(options);
this.removeNotification();
this.notification = new NotificationView({
model: model
});
this.notification.render();
if (!beforeElement) {
beforeElement = this.$('.cohort-management-group');
}
beforeElement.before(this.notification.$el);
},
removeNotification: function() {
if (this.notification) {
this.notification.remove();
}
},
showAddCohortForm: function(event) {
event.preventDefault();
this.removeNotification();
this.addCohortForm = $(this.addCohortFormTemplate({}));
this.addCohortForm.insertAfter(this.$('.cohort-management-nav'));
this.setCohortEditorVisibility(false);
},
hideAddCohortForm: function() {
this.setCohortEditorVisibility(true);
if (this.addCohortForm) {
this.addCohortForm.remove();
this.addCohortForm = null;
}
},
setCohortEditorVisibility: function(showEditor) {
if (showEditor) {
this.$('.cohort-management-group').removeClass(hiddenClass);
this.$('.cohort-management-nav').removeClass(disabledClass);
} else {
this.$('.cohort-management-group').addClass(hiddenClass);
this.$('.cohort-management-nav').addClass(disabledClass);
}
},
saveAddCohortForm: function(event) {
event.preventDefault();
var self = this,
showAddError,
cohortName = this.$('.cohort-create-name').val().trim();
showAddError = function(message) {
self.showNotification(
{type: 'error', title: message},
self.$('.cohort-management-create-form-name label')
);
};
this.removeNotification();
if (cohortName.length > 0) {
$.post(
this.model.url + '/add',
{name: cohortName}
).done(function(result) {
if (result.success) {
self.lastSelectedCohortId = result.cohort.id;
self.model.fetch().done(function() {
self.showNotification({
type: 'confirmation',
title: interpolate_text(
gettext('The {cohortGroupName} cohort group has been created. You can manually add students to this group below.'),
{cohortGroupName: cohortName}
)
});
});
} else {
showAddError(result.msg);
}
}).fail(function() {
showAddError(gettext("We've encountered an error. Please refresh your browser and then try again."));
});
} else {
showAddError(gettext('Please enter a name for your new cohort group.'));
}
},
cancelAddCohortForm: function(event) {
event.preventDefault();
this.removeNotification();
this.onSync();
}
});
this.CohortsView = CohortsView;
}).call(this, Backbone, CohortEditorView);
}).call(this, $, _, Backbone, gettext, interpolate_text, CohortEditorView, NotificationModel, NotificationView);
......@@ -9,17 +9,14 @@
},
render: function() {
var actionText = null;
// If no actionText is passed to the template, the action button
// will not be shown.
if (this.model.get("actionText") && this.model.get("actionCallback")) {
actionText = this.model.get("actionText");
}
this.$el.html(this.template({
type: this.model.get("type"),
title: this.model.get("title"),
message: this.model.get("message"),
details: this.model.get("details"),
actionText: actionText
actionText: this.model.get("actionText"),
actionClass: this.model.get("actionClass"),
actionIconClass: this.model.get("actionIconClass")
}));
return this;
},
......
......@@ -454,8 +454,11 @@ section.instructor-dashboard-content-2 {
.form-submit {
@include idashbutton($blue);
@include font-size(14);
@include line-height(14);
margin-right: ($baseline/2);
margin-bottom: 0;
text-shadow: none;
}
.form-cancel {
......@@ -481,6 +484,21 @@ section.instructor-dashboard-content-2 {
@include idashbutton($blue);
float: right;
text-align: right;
text-shadow: none;
font-weight: 600;
}
// STATE: is disabled
&.is-disabled {
.cohort-select {
opacity: 0.25;
}
.action-create {
opacity: 0.50;
}
}
}
......
......@@ -11,7 +11,6 @@
<script type="text/javascript" src="${static.url('js/src/jquery.timeago.locale.js')}"></script>
<script type="text/javascript" src="${static.url('js/mustache.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/URI.min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/underscore-min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/backbone-min.js')}"></script>
<script type="text/javascript" src="${static.url('js/src/tooltip_manager.js')}"></script>
......
<div class="cohort-management-create">
<form action="" method="post" name="" id="cohort-management-create-form" class="cohort-management-create-form">
<h3 class="form-title"><%- gettext('Add a New Cohort Group') %></h3>
<div class="form-fields">
<div class="cohort-management-create-form-name field field-text">
<label for="cohort-create-name" class="label">
<%- gettext('New Cohort Name') %> *
<span class="sr"><%- gettext('(Required Field)')%></span>
</label>
<input type="text" name="cohort-create-name" value="" class="input cohort-create-name"
id="cohort-create-name"
placeholder="<%- gettext("Enter Your New Cohort Group's Name") %>" required="required" />
</div>
</div>
<div class="form-actions">
<button class="form-submit button action-primary action-save">
<i class="icon icon-plus"></i>
<%- gettext('Save') %>
</button>
<a href="" class="form-cancel action-secondary action-cancel"><%- gettext('Cancel') %></a>
</div>
</form>
</div>
<option value=""><%- gettext('Select a cohort') %></option>
<% if (!selectedCohort) { %>
<option value=""><%- gettext('Select a cohort group') %></option>
<% } %>
<% _.each(cohorts, function(cohort) { %>
<%
var label = interpolate(
......
......@@ -4,19 +4,24 @@
</h2>
<!-- nav -->
<% if (cohorts.length > 0) { %>
<div class="cohort-management-nav">
<form action="" method="post" name="" id="cohort-management-nav-form" class="cohort-management-nav-form">
<div class="cohort-management-nav-form-select field field-select">
<label for="cohort-select" class="label sr">${_("Select a cohort to manage")}</label>
<select class="input cohort-select" name="cohort-select" id="cohort-select">
</select>
</div>
<div class="cohort-management-nav">
<form action="" method="post" name="" id="cohort-management-nav-form" class="cohort-management-nav-form">
<div class="cohort-management-nav-form-select field field-select">
<label for="cohort-select" class="label sr">${_("Select a cohort group to manage")}</label>
<select class="input cohort-select" name="cohort-select" id="cohort-select">
</select>
</div>
<button class="form-submit button action-primary action-view sr"><%- gettext('View cohort group') %></button>
</form>
</div>
<div class="form-actions">
<button class="form-submit button action-primary action-view sr"><%- gettext('View Cohort Group') %></button>
</div>
</form>
<!-- individual group -->
<div class="cohort-management-group"></div>
<% } %>
<a href="" class="action-primary action-create">
<i class="icon icon-plus"></i>
<%- gettext('Add Cohort Group') %>
</a>
</div>
<!-- individual group -->
<div class="cohort-management-group"></div>
......@@ -32,7 +32,6 @@
window.Range.prototype = { };
}
</script>
<script type="text/javascript" src="${static.url('js/vendor/underscore-min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/backbone-min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/mustache.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.js')}"></script>
......@@ -63,7 +62,7 @@
## Include Underscore templates
<%block name="header_extras">
% for template_name in ["cohorts", "cohort-editor", "cohort-selector", "notification"]:
% for template_name in ["cohorts", "cohort-editor", "cohort-selector", "add-cohort-form", "notification"]:
<script type="text/template" id="${template_name}-tpl">
<%static:include path="instructor/instructor_dashboard_2/${template_name}.underscore" />
</script>
......
......@@ -220,31 +220,30 @@
</div>
%if course.is_cohorted:
<hr class="divider" />
<div class="cohort-management membership-section"
data-ajax_url="${section_data['cohorts_ajax_url']}"
data-advanced-settings-url="${section_data['advanced_settings_url']}"
>
</div>
% endif
<hr class="divider" />
<div class="cohort-management membership-section"
data-ajax_url="${section_data['cohorts_ajax_url']}"
data-advanced-settings-url="${section_data['advanced_settings_url']}"
>
</div>
<%block name="headextra">
<script>
$(document).ready(function() {
var cohortManagementElement = $('.cohort-management');
if (cohortManagementElement.length > 0) {
var cohorts = new CohortCollection();
cohorts.url = cohortManagementElement.data('ajax_url');
var cohortsView = new CohortsView({
el: cohortManagementElement,
model: cohorts,
advanced_settings_url: cohortManagementElement.data('advanced-settings-url')
});
cohortsView.render();
cohorts.fetch().done(function() {
cohortsView.render();
});
}
});
</script>
</%block>
<%block name="headextra">
<script>
$(document).ready(function() {
var cohortManagementElement = $('.cohort-management');
if (cohortManagementElement.length > 0) {
var cohorts = new CohortCollection();
cohorts.url = cohortManagementElement.data('ajax_url');
var cohortsView = new CohortsView({
el: cohortManagementElement,
model: cohorts,
advanced_settings_url: cohortManagementElement.data('advanced-settings-url')
});
cohorts.fetch().done(function() {
cohortsView.render();
});
}
});
</script>
</%block>
% endif
......@@ -3,22 +3,29 @@
<%- title %>
</h3>
<% if (details.length > 0) { %>
<% if (details.length > 0 || message) { %>
<div class="message-copy">
<ul class="list-summary summary-items">
<% for (var i = 0; i < details.length; i++) { %>
<li class="summary-item">
<%- details[i] %>
</li>
<% } %>
</ul>
<% if (message) { %>
<p><%- message %></p>
<% } %>
<% if (details.length > 0) { %>
<ul class="list-summary summary-items">
<% for (var i = 0; i < details.length; i++) { %>
<li class="summary-item"><%- details[i] %></li>
<% } %>
</ul>
<% } %>
</div>
<% } %>
<% if (actionText) { %>
<div class="message-actions">
<a href="" class="action-primary"><%- actionText %></a>
<button class="action-primary <%- actionClass %>">
<% if (actionIconClass) { %>
<i class="icon <%- actionIconClass %>"></i>
<% } %>
<%- actionText %>
</button>
</div>
<% } %>
</div>
......@@ -6,7 +6,6 @@
<%block name="headextra">
<%static:css group='style-course-vendor'/>
<%static:css group='style-course'/>
<script type="text/javascript" src="${static.url('js/vendor/underscore-min.js')}"></script>
</%block>
<%block name="pagetitle">${_("{course_number} Staff Grading").format(course_number=course.display_number_with_default) | h}</%block>
......
......@@ -19,7 +19,6 @@
window.Range.prototype = { };
}
</script>
<script type="text/javascript" src="${static.url('js/vendor/underscore-min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/mustache.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.axislabels.js')}"></script>
......@@ -244,9 +243,10 @@
<div class="form-fields">
<div class="cohort-management-nav-form-select field field-select">
<label for="cohort-select" class="label sr">Select a cohort to manage</label>
<label for="cohort-select" class="label sr">Select a cohort group to manage</label>
<select class="input cohort-select" name="cohort-select" id="cohort-select">
<option>Select a cohort group</option>
<option value="cohort-name-1">Cohort Name is Placed Here and Should Accommodate Almost Everything (12,546)</option>
<option value="cohort-name-2" selected>Cras mattis consectetur purus sit amet fermentum (8,546)</option>
<option value="cohort-name-3">Donec id elit non mi porta gravida at eget metus. (4)
......@@ -310,7 +310,6 @@
<span class="sr">(Required Field)</span>
</label>
<input type="text" name="cohort-create-name" value="" class=" input cohort-create-name" id="cohort-create-name" placeholder="Enter Your New Cohort Group's Name" required="required" />
<span class="tip">No special characters or symbols are allowed in group names</span>
</div>
</div>
......@@ -337,7 +336,6 @@
<span class="sr">(Required Field)</span>
</label>
<input type="text" name="cohort-edit-name" value="" class=" input cohort-edit-name" id="cohort-edit-name" placeholder="Enter Your Cohort Group's Name" required="required" />
<span class="tip">No special characters or symbols are allowed in group names</span>
</div>
</div>
......@@ -356,10 +354,6 @@
<h3 class="message-title">New Cohort Name has been created. You can manually add students to this group below.</h3>
</div>
<div class="message message-error">
<h3 class="message-title">Special characters are not allowed in cohort group names</h3>
</div>
<!-- individual group -->
<div class="cohort-management-group">
<header class="cohort-management-group-header">
......
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