Course Reruns UI
Studio: adding course re-run-centric static template rendering * initial HTML for dashboard states * initial HTML for new course re-run view/form * initial HTML placeholder for outline alert UI Conflicts: cms/templates/index.html Studio: adding styling for course re-run-centric views * adding new view/page mast-wizard type * refactoring create course/element form styling * adding course re-run view specific styling * adding courses processing styling (w/ alerts and status) Course rerun server-side updates: support display_name and DuplicateCourseError. Studio: further design revisions and tweaks from feedback * removing new window attribute from re-run control * removing links from processing courses * revising look/feel of dismiss action on dashboard + alert * correcting font-weight of dashboard processing title * adding extra space to course rerun action on dashboard * re-wording secondary cancel action on rerun view Conflicts: cms/templates/index.html Added interation on unsucceeded courses in dashboard Studio: removing 'rel=external' property from course re-run actions Studio: removing hover styles for processing courses Fixed value bug in split and set course listing to display run moved task.py for rerun
This commit is contained in:
committed by
Ben McMorran
parent
a986b46cb1
commit
9f8f64cffe
@@ -5,6 +5,7 @@ This file contains celery tasks for contentstore views
|
||||
from celery.task import task
|
||||
from django.contrib.auth.models import User
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
from xmodule.modulestore.exceptions import DuplicateCourseError, ItemNotFoundError
|
||||
from course_action_state.models import CourseRerunState
|
||||
from contentstore.utils import initialize_permissions
|
||||
@@ -32,13 +33,11 @@ def rerun_course(source_course_key_string, destination_course_key_string, user_i
|
||||
|
||||
# update state: Succeeded
|
||||
CourseRerunState.objects.succeeded(course_key=destination_course_key)
|
||||
|
||||
return "succeeded"
|
||||
|
||||
except DuplicateCourseError as exc:
|
||||
# do NOT delete the original course, only update the status
|
||||
CourseRerunState.objects.failed(course_key=destination_course_key, exception=exc)
|
||||
|
||||
return "duplicate course"
|
||||
|
||||
# catch all exceptions so we can update the state and properly cleanup the course.
|
||||
@@ -1580,9 +1580,12 @@ class RerunCourseTest(ContentStoreTestCase):
|
||||
json_resp = parse_json(response)
|
||||
self.assertNotIn('ErrMsg', json_resp)
|
||||
destination_course_key = CourseKey.from_string(json_resp['destination_course_key'])
|
||||
|
||||
return destination_course_key
|
||||
|
||||
def create_course_listing_html(self, course_key):
|
||||
"""Creates html fragment that is created for the given course_key in the course listing section"""
|
||||
return '<a class="course-link" href="/course/{}"'.format(course_key)
|
||||
|
||||
def create_unsucceeded_course_action_html(self, course_key):
|
||||
"""Creates html fragment that is created for the given course_key in the unsucceeded course action section"""
|
||||
# TODO Update this once the Rerun UI LMS-11011 is implemented.
|
||||
@@ -1615,6 +1618,7 @@ class RerunCourseTest(ContentStoreTestCase):
|
||||
rerun_state = CourseRerunState.objects.find_first(course_key=destination_course_key)
|
||||
expected_states = {
|
||||
'state': CourseRerunUIStateManager.State.SUCCEEDED,
|
||||
'display_name': self.destination_course_data['display_name'],
|
||||
'source_course_key': source_course.id,
|
||||
'course_key': destination_course_key,
|
||||
'should_display': True,
|
||||
|
||||
@@ -329,7 +329,9 @@ class TestCourseListing(ModuleStoreTestCase):
|
||||
|
||||
# simulate initiation of course actions
|
||||
for course in courses_in_progress:
|
||||
CourseRerunState.objects.initiated(sourse_course_key, destination_course_key=course.id, user=self.user)
|
||||
CourseRerunState.objects.initiated(
|
||||
sourse_course_key, destination_course_key=course.id, user=self.user, display_name="test course"
|
||||
)
|
||||
|
||||
# verify return values
|
||||
for method in (_accessible_courses_list_from_groups, _accessible_courses_list):
|
||||
|
||||
@@ -5,11 +5,9 @@ import json
|
||||
import random
|
||||
import string # pylint: disable=W0402
|
||||
import logging
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
import django.utils
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django_future.csrf import ensure_csrf_cookie
|
||||
from django.conf import settings
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.core.exceptions import PermissionDenied
|
||||
@@ -25,11 +23,14 @@ from xmodule.modulestore.django import modulestore
|
||||
from xmodule.contentstore.content import StaticContent
|
||||
from xmodule.tabs import PDFTextbookTabs
|
||||
from xmodule.partitions.partitions import UserPartition, Group
|
||||
|
||||
from xmodule.modulestore.exceptions import ItemNotFoundError, DuplicateCourseError
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.locations import Location
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from django_future.csrf import ensure_csrf_cookie
|
||||
from util.json_request import JsonResponse
|
||||
from edxmako.shortcuts import render_to_response
|
||||
from contentstore.course_info_model import get_course_updates, update_course_updates, delete_course_update
|
||||
from contentstore.utils import (
|
||||
add_instructor,
|
||||
@@ -47,7 +48,6 @@ from models.settings.course_grading import CourseGradingModel
|
||||
from models.settings.course_metadata import CourseMetadata
|
||||
from util.json_request import expect_json
|
||||
from util.string_utils import _has_non_ascii_characters
|
||||
|
||||
from .access import has_course_access
|
||||
from .component import (
|
||||
OPEN_ENDED_COMPONENT_TYPES,
|
||||
@@ -56,10 +56,8 @@ from .component import (
|
||||
SPLIT_TEST_COMPONENT_TYPE,
|
||||
ADVANCED_COMPONENT_TYPES,
|
||||
)
|
||||
from .tasks import rerun_course
|
||||
from contentstore.tasks import rerun_course
|
||||
from .item import create_xblock_info
|
||||
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from course_creators.views import get_course_creator_status, add_user_with_status_unrequested
|
||||
from contentstore import utils
|
||||
from student.roles import (
|
||||
@@ -68,11 +66,11 @@ from student.roles import (
|
||||
from student import auth
|
||||
from course_action_state.models import CourseRerunState, CourseRerunUIStateManager
|
||||
from course_action_state.managers import CourseActionStateItemNotFoundError
|
||||
|
||||
from microsite_configuration import microsite
|
||||
|
||||
|
||||
__all__ = ['course_info_handler', 'course_handler', 'course_info_update_handler',
|
||||
'course_rerun_handler',
|
||||
'settings_handler',
|
||||
'grading_handler',
|
||||
'advanced_settings_handler',
|
||||
@@ -233,6 +231,25 @@ def course_handler(request, course_key_string=None):
|
||||
else:
|
||||
return HttpResponseNotFound()
|
||||
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
@require_http_methods(["GET"])
|
||||
def course_rerun_handler(request, course_key_string):
|
||||
"""
|
||||
The restful handler for course reruns.
|
||||
GET
|
||||
html: return html page with form to rerun a course for the given course id
|
||||
"""
|
||||
course_key = CourseKey.from_string(course_key_string)
|
||||
course_module = _get_course_module(course_key, request.user, depth=3)
|
||||
if request.method == 'GET':
|
||||
return render_to_response('course-create-rerun.html', {
|
||||
'source_course_key': course_key,
|
||||
'display_name': course_module.display_name,
|
||||
'user': request.user,
|
||||
'course_creator_status': _get_course_creator_status(request.user),
|
||||
'allow_unicode_course_id': settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID', False)
|
||||
})
|
||||
|
||||
def _course_outline_json(request, course_module):
|
||||
"""
|
||||
@@ -340,9 +357,26 @@ def course_listing(request):
|
||||
course.display_name,
|
||||
reverse_course_url('course_handler', course.id),
|
||||
get_lms_link_for_item(course.location),
|
||||
_get_rerun_link_for_item(course.id),
|
||||
course.display_org_with_default,
|
||||
course.display_number_with_default,
|
||||
course.location.name
|
||||
course.location.run
|
||||
)
|
||||
|
||||
def format_unsucceeded_course_for_view(uca):
|
||||
"""
|
||||
return tuple of the data which the view requires for each unsucceeded course
|
||||
"""
|
||||
return (
|
||||
uca.display_name,
|
||||
uca.course_key.org,
|
||||
uca.course_key.course,
|
||||
uca.course_key.run,
|
||||
True if uca.state == CourseRerunUIStateManager.State.FAILED else False,
|
||||
True if uca.state == CourseRerunUIStateManager.State.IN_PROGRESS else False,
|
||||
reverse_course_url('course_notifications_handler', uca.course_key, kwargs={
|
||||
'action_state_id': uca.id,
|
||||
}) if uca.state == CourseRerunUIStateManager.State.FAILED else ''
|
||||
)
|
||||
|
||||
# remove any courses in courses that are also in the unsucceeded_course_actions list
|
||||
@@ -353,6 +387,8 @@ def course_listing(request):
|
||||
if not isinstance(c, ErrorDescriptor) and (c.id not in unsucceeded_action_course_keys)
|
||||
]
|
||||
|
||||
unsucceeded_course_actions = [format_unsucceeded_course_for_view(uca) for uca in unsucceeded_course_actions]
|
||||
|
||||
return render_to_response('index.html', {
|
||||
'courses': courses,
|
||||
'unsucceeded_course_actions': unsucceeded_course_actions,
|
||||
@@ -363,6 +399,10 @@ def course_listing(request):
|
||||
})
|
||||
|
||||
|
||||
def _get_rerun_link_for_item(course_key):
|
||||
return '/course_rerun/{}/{}/{}'.format(course_key.org, course_key.course, course_key.run)
|
||||
|
||||
|
||||
@login_required
|
||||
@ensure_csrf_cookie
|
||||
def course_index(request, course_key):
|
||||
@@ -398,6 +438,9 @@ def course_index(request, course_key):
|
||||
'rerun_notification_id': current_action.id if current_action else None,
|
||||
'course_release_date': course_release_date,
|
||||
'settings_url': settings_url,
|
||||
'notification_dismiss_url': reverse_course_url('course_notifications_handler', current_action.course_key, kwargs={
|
||||
'action_state_id': current_action.id,
|
||||
}) if current_action else None,
|
||||
})
|
||||
|
||||
|
||||
@@ -554,7 +597,7 @@ def _rerun_course(request, org, number, run, fields):
|
||||
add_instructor(destination_course_key, request.user, request.user)
|
||||
|
||||
# Mark the action as initiated
|
||||
CourseRerunState.objects.initiated(source_course_key, destination_course_key, request.user)
|
||||
CourseRerunState.objects.initiated(source_course_key, destination_course_key, request.user, fields['display_name'])
|
||||
|
||||
# Rerun the course as a new celery task
|
||||
rerun_course.delay(unicode(source_course_key), unicode(destination_course_key), request.user.id, fields)
|
||||
|
||||
@@ -38,8 +38,15 @@ FEATURES['ALLOW_ALL_ADVANCED_COMPONENTS'] = True
|
||||
################################# CELERY ######################################
|
||||
|
||||
# By default don't use a worker, execute tasks as if they were local functions
|
||||
|
||||
# TODO BEWARE: UNCOMMENT THIS BEFORE MERGING INTO MASTER
|
||||
CELERY_ALWAYS_EAGER = True
|
||||
|
||||
# TODO BEWARE: DO NOT COMMIT THE REST OF THIS SECTION INTO MASTER - FOR LOCAL TESTING ONLY
|
||||
# Test with Celery threads
|
||||
# CELERY_ALWAYS_EAGER = False
|
||||
# BROKER_URL = 'redis://'
|
||||
|
||||
################################ DEBUG TOOLBAR ################################
|
||||
INSTALLED_APPS += ('debug_toolbar', 'debug_toolbar_mongo')
|
||||
MIDDLEWARE_CLASSES += ('debug_toolbar.middleware.DebugToolbarMiddleware',)
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
require(["domReady", "jquery", "underscore", "js/utils/cancel_on_escape"],
|
||||
function (domReady, $, _, CancelOnEscape) {
|
||||
|
||||
var dismissNotification = function (e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: $('.dismiss-button').data('dismiss-link'),
|
||||
type: 'DELETE',
|
||||
success: function(result) {
|
||||
window.location.reload()
|
||||
}
|
||||
});
|
||||
};
|
||||
var saveNewCourse = function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -164,5 +175,6 @@ require(["domReady", "jquery", "underscore", "js/utils/cancel_on_escape"],
|
||||
|
||||
domReady(function () {
|
||||
$('.new-course-button').bind('click', addNewCourse);
|
||||
$('.dismiss-button').bind('click', dismissNotification);
|
||||
});
|
||||
});
|
||||
|
||||
161
cms/static/js/views/course_rerun.js
Normal file
161
cms/static/js/views/course_rerun.js
Normal file
@@ -0,0 +1,161 @@
|
||||
require(["domReady", "jquery", "underscore", "js/utils/cancel_on_escape"],
|
||||
function (domReady, $, _, CancelOnEscape) {
|
||||
|
||||
var saveRerunCourse = function (e) {
|
||||
e.preventDefault();
|
||||
// One final check for empty values
|
||||
var errors = _.reduce(
|
||||
['.rerun-course-name', '.rerun-course-org', '.rerun-course-number', '.rerun-course-run'],
|
||||
function (acc, ele) {
|
||||
var $ele = $(ele);
|
||||
var error = validateRequiredField($ele.val());
|
||||
setNewCourseFieldInErr($ele.parent('li'), error);
|
||||
return error ? true : acc;
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
if (errors) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $newCourseForm = $(this).closest('#rerun-course-form');
|
||||
var display_name = $newCourseForm.find('.rerun-course-name').val();
|
||||
var org = $newCourseForm.find('.rerun-course-org').val();
|
||||
var number = $newCourseForm.find('.rerun-course-number').val();
|
||||
var run = $newCourseForm.find('.rerun-course-run').val();
|
||||
|
||||
analytics.track('Reran a Course', {
|
||||
'source_course_key': source_course_key,
|
||||
'org': org,
|
||||
'number': number,
|
||||
'display_name': display_name,
|
||||
'run': run
|
||||
});
|
||||
$.postJSON('/course/', {
|
||||
'source_course_key': source_course_key,
|
||||
'org': org,
|
||||
'number': number,
|
||||
'display_name': display_name,
|
||||
'run': run
|
||||
},
|
||||
function (data) {
|
||||
if (data.url !== undefined) {
|
||||
window.location = data.url
|
||||
} else if (data.ErrMsg !== undefined) {
|
||||
$('.wrapper-error').addClass('is-shown').removeClass('is-hidden');
|
||||
$('#course_rerun_error').html('<p>' + data.ErrMsg + '</p>');
|
||||
$('.rerun-course-save').addClass('is-disabled').removeClass('is-processing').html(gettext('Create Re-run'));
|
||||
$('.action-cancel').removeClass('is-hidden');
|
||||
}
|
||||
}
|
||||
);
|
||||
// Go into creating re-run state
|
||||
$('.rerun-course-save').addClass('is-disabled').addClass('is-processing').html(
|
||||
'<i class="icon-refresh icon-spin"></i>' + gettext('Processing Re-run Request')
|
||||
);
|
||||
$('.action-cancel').addClass('is-hidden');
|
||||
};
|
||||
|
||||
var cancelRerunCourse = function (e) {
|
||||
e.preventDefault();
|
||||
// Clear out existing fields and errors
|
||||
$('.rerun-course-run').val('');
|
||||
$('#course_rerun_error').html('');
|
||||
$('wrapper-error').removeClass('is-shown').addClass('is-hidden');
|
||||
$('.rerun-course-save').off('click');
|
||||
window.location.href = '/course/'
|
||||
};
|
||||
|
||||
var validateRequiredField = function (msg) {
|
||||
return msg.length === 0 ? gettext('Required field.') : '';
|
||||
};
|
||||
|
||||
var setNewCourseFieldInErr = function (el, msg) {
|
||||
if(msg) {
|
||||
el.addClass('error');
|
||||
el.children('span.tip-error').addClass('is-shown').removeClass('is-hidden').text(msg);
|
||||
$('.rerun-course-save').addClass('is-disabled');
|
||||
}
|
||||
else {
|
||||
el.removeClass('error');
|
||||
el.children('span.tip-error').addClass('is-hidden').removeClass('is-shown');
|
||||
// One "error" div is always present, but hidden or shown
|
||||
if($('.error').length === 1) {
|
||||
$('.rerun-course-save').removeClass('is-disabled');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
domReady(function () {
|
||||
var $cancelButton = $('.rerun-course-cancel');
|
||||
var $courseRun = $('.rerun-course-run');
|
||||
$courseRun.focus().select();
|
||||
$('.rerun-course-save').on('click', saveRerunCourse);
|
||||
$cancelButton.bind('click', cancelRerunCourse);
|
||||
CancelOnEscape($cancelButton);
|
||||
$('.cancel-button').bind('click', cancelRerunCourse);
|
||||
|
||||
// Check that a course (org, number, run) doesn't use any special characters
|
||||
var validateCourseItemEncoding = function (item) {
|
||||
var required = validateRequiredField(item);
|
||||
if (required) {
|
||||
return required;
|
||||
}
|
||||
if ($('.allow-unicode-course-id').val() === 'True'){
|
||||
if (/\s/g.test(item)) {
|
||||
return gettext('Please do not use any spaces in this field.');
|
||||
}
|
||||
}
|
||||
else{
|
||||
if (item !== encodeURIComponent(item)) {
|
||||
return gettext('Please do not use any spaces or special characters in this field.');
|
||||
}
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
// Ensure that org/course_num/run < 65 chars.
|
||||
var validateTotalCourseItemsLength = function () {
|
||||
var totalLength = _.reduce(
|
||||
['.rerun-course-org', '.rerun-course-number', '.rerun-course-run'],
|
||||
function (sum, ele) {
|
||||
return sum + $(ele).val().length;
|
||||
}, 0
|
||||
);
|
||||
if (totalLength > 65) {
|
||||
$('.wrap-error').addClass('is-shown');
|
||||
$('#course_creation_error').html('<p>' + gettext('The combined length of the organization, course number, and course run fields cannot be more than 65 characters.') + '</p>');
|
||||
$('.rerun-course-save').addClass('is-disabled');
|
||||
}
|
||||
else {
|
||||
$('.wrap-error').removeClass('is-shown');
|
||||
}
|
||||
};
|
||||
|
||||
// Handle validation asynchronously
|
||||
_.each(
|
||||
['.rerun-course-org', '.rerun-course-number', '.rerun-course-run'],
|
||||
function (ele) {
|
||||
var $ele = $(ele);
|
||||
$ele.on('keyup', function (event) {
|
||||
// Don't bother showing "required field" error when
|
||||
// the user tabs into a new field; this is distracting
|
||||
// and unnecessary
|
||||
if (event.keyCode === 9) {
|
||||
return;
|
||||
}
|
||||
var error = validateCourseItemEncoding($ele.val());
|
||||
setNewCourseFieldInErr($ele.parent(), error);
|
||||
validateTotalCourseItemsLength();
|
||||
});
|
||||
}
|
||||
);
|
||||
var $name = $('.rerun-course-name');
|
||||
$name.on('keyup', function () {
|
||||
var error = validateRequiredField($name.val());
|
||||
setNewCourseFieldInErr($name.parent(), error);
|
||||
validateTotalCourseItemsLength();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,6 +5,17 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
|
||||
|
||||
var modalSelector = '.edit-section-publish-settings';
|
||||
|
||||
var dismissNotification = function (e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: $('.dismiss-button').data('dismiss-link'),
|
||||
type: 'GET',
|
||||
success: function(result) {
|
||||
$('.wrapper-alert-announcement').remove()
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var toggleSections = function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -222,6 +233,8 @@ define(["domReady", "jquery", "jquery.ui", "underscore", "gettext", "js/views/fe
|
||||
$('.toggle-button-sections').bind('click', toggleSections);
|
||||
$('.expand-collapse').bind('click', toggleSubmodules);
|
||||
|
||||
$('.dismiss-button').bind('click', dismissNotification);
|
||||
|
||||
var $body = $('body');
|
||||
$body.on('click', '.section-published-date .edit-release-date', editSectionPublishDate);
|
||||
$body.on('click', '.edit-section-publish-settings .action-save', saveSetSectionScheduleDate);
|
||||
|
||||
@@ -240,6 +240,282 @@ p, ul, ol, dl {
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// layout - basic
|
||||
.wrapper-view {
|
||||
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// layout - basic page header
|
||||
.wrapper-mast {
|
||||
margin: ($baseline*1.5) 0 0 0;
|
||||
padding: 0 $baseline;
|
||||
position: relative;
|
||||
|
||||
.mast, .metadata {
|
||||
@include clearfix();
|
||||
position: relative;
|
||||
max-width: $fg-max-width;
|
||||
min-width: $fg-min-width;
|
||||
width: flex-grid(12);
|
||||
margin: 0 auto $baseline auto;
|
||||
color: $gray-d2;
|
||||
}
|
||||
|
||||
.mast {
|
||||
border-bottom: 1px solid $gray-l4;
|
||||
padding-bottom: ($baseline/2);
|
||||
|
||||
// layout with actions
|
||||
.page-header {
|
||||
width: flex-grid(12);
|
||||
}
|
||||
|
||||
// layout with actions
|
||||
&.has-actions {
|
||||
@include clearfix();
|
||||
|
||||
.page-header {
|
||||
float: left;
|
||||
width: flex-grid(6,12);
|
||||
margin-right: flex-gutter();
|
||||
}
|
||||
|
||||
.nav-actions {
|
||||
position: relative;
|
||||
bottom: -($baseline*0.75);
|
||||
float: right;
|
||||
width: flex-grid(6,12);
|
||||
text-align: right;
|
||||
|
||||
.nav-item {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-right: ($baseline/2);
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// buttons
|
||||
.button {
|
||||
padding: ($baseline/4) ($baseline/2) ($baseline/3) ($baseline/2);
|
||||
}
|
||||
|
||||
.new-button {
|
||||
|
||||
}
|
||||
|
||||
.view-button {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// layout with actions
|
||||
&.has-subtitle {
|
||||
|
||||
.nav-actions {
|
||||
bottom: -($baseline*1.5);
|
||||
}
|
||||
}
|
||||
|
||||
// layout with navigation
|
||||
&.has-navigation {
|
||||
|
||||
.nav-actions {
|
||||
bottom: -($baseline*1.5);
|
||||
}
|
||||
|
||||
.navigation-link {
|
||||
@extend %cont-truncated;
|
||||
display: inline-block;
|
||||
vertical-align: bottom; // correct for extra padding in FF
|
||||
max-width: 250px;
|
||||
|
||||
&.navigation-current {
|
||||
@extend %ui-disabled;
|
||||
color: $gray;
|
||||
max-width: 250px;
|
||||
|
||||
&:before {
|
||||
color: $gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-link:before {
|
||||
content: " / ";
|
||||
margin: ($baseline/4);
|
||||
color: $gray;
|
||||
|
||||
&:hover {
|
||||
color: $gray;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation .navigation-link:first-child:before {
|
||||
content: "";
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CASE: wizard-based mast
|
||||
.mast-wizard {
|
||||
|
||||
.page-header-sub {
|
||||
@extend %t-title4;
|
||||
color: $gray;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.page-header-super {
|
||||
@extend %t-title4;
|
||||
float: left;
|
||||
width: flex-grid(12,12);
|
||||
margin-top: ($baseline/2);
|
||||
border-top: 1px solid $gray-l4;
|
||||
padding-top: ($baseline/2);
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
// page metadata/action bar
|
||||
.metadata {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// layout - basic page content
|
||||
.wrapper-content {
|
||||
margin: 0;
|
||||
padding: 0 $baseline;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.content {
|
||||
@include clearfix();
|
||||
@extend %t-copy-base;
|
||||
max-width: $fg-max-width;
|
||||
min-width: $fg-min-width;
|
||||
width: flex-grid(12);
|
||||
margin: 0 auto;
|
||||
color: $gray-d2;
|
||||
|
||||
header {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
border-bottom: 1px solid $gray-l4;
|
||||
padding-bottom: ($baseline/2);
|
||||
|
||||
.title-sub {
|
||||
@extend %t-copy-sub1;
|
||||
display: block;
|
||||
margin: 0;
|
||||
color: $gray-l2;
|
||||
}
|
||||
|
||||
.title-1 {
|
||||
@extend %t-title3;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: 600;
|
||||
color: $gray-d3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-primary, .content-supplementary {
|
||||
@include box-sizing(border-box);
|
||||
}
|
||||
|
||||
// layout - primary content
|
||||
.content-primary {
|
||||
|
||||
.title-1 {
|
||||
@extend %t-title3;
|
||||
}
|
||||
|
||||
.title-2 {
|
||||
@extend %t-title4;
|
||||
margin: 0 0 ($baseline/2) 0;
|
||||
}
|
||||
|
||||
.title-3 {
|
||||
@extend %t-title6;
|
||||
margin: 0 0 ($baseline/2) 0;
|
||||
}
|
||||
|
||||
header {
|
||||
@include clearfix();
|
||||
|
||||
.title-2 {
|
||||
width: flex-grid(5, 12);
|
||||
margin: 0 flex-gutter() 0 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.tip {
|
||||
@extend %t-copy-sub2;
|
||||
width: flex-grid(7, 12);
|
||||
float: right;
|
||||
margin-top: ($baseline/2);
|
||||
text-align: right;
|
||||
color: $gray-l2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// layout - supplemental content
|
||||
.content-supplementary {
|
||||
|
||||
> section {
|
||||
margin: 0 0 $baseline 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// layout - grandfathered
|
||||
.main-wrapper {
|
||||
position: relative;
|
||||
margin: 0 ($baseline*2);
|
||||
}
|
||||
|
||||
.inner-wrapper {
|
||||
@include clearfix();
|
||||
position: relative;
|
||||
max-width: 1280px;
|
||||
margin: auto;
|
||||
|
||||
> article {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
.main-column {
|
||||
clear: both;
|
||||
float: left;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
float: right;
|
||||
width: 28%;
|
||||
}
|
||||
|
||||
.left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
|
||||
@@ -133,6 +133,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
// white secondary button
|
||||
%btn-secondary-white {
|
||||
@extend %ui-btn-secondary;
|
||||
border-color: $white-t2;
|
||||
color: $white-t3;
|
||||
|
||||
&:hover, &:active {
|
||||
border-color: $white;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&.current, &.active {
|
||||
background: $gray-d2;
|
||||
color: $gray-l5;
|
||||
|
||||
&:hover, &:active {
|
||||
background: $gray-d2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// green secondary button
|
||||
%btn-secondary-green {
|
||||
@extend %ui-btn-secondary;
|
||||
@@ -213,17 +234,6 @@
|
||||
|
||||
// ====================
|
||||
|
||||
// calls-to-action
|
||||
|
||||
// ====================
|
||||
|
||||
// specific buttons - view live
|
||||
%view-live-button {
|
||||
@extend %t-action4;
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// UI: element actions list
|
||||
|
||||
%actions-list {
|
||||
|
||||
@@ -137,28 +137,10 @@ form {
|
||||
}
|
||||
}
|
||||
|
||||
// ELEM: form wrapper
|
||||
.wrapper-create-element {
|
||||
height: 0;
|
||||
margin-bottom: $baseline;
|
||||
opacity: 0.0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
|
||||
&.animate {
|
||||
@include transition(opacity $tmg-f1 ease-in-out 0s, height $tmg-f1 ease-in-out 0s);
|
||||
}
|
||||
|
||||
&.is-shown {
|
||||
height: auto; // define a specific height for the animating version of this UI to work properly
|
||||
opacity: 1.0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// ELEM: form
|
||||
// form styling for creating a new content item (course, user, textbook)
|
||||
form[class^="create-"] {
|
||||
// TODO: refactor this into a placeholder to extend.
|
||||
.form-create {
|
||||
@extend %ui-window;
|
||||
|
||||
.title {
|
||||
@@ -253,12 +235,19 @@ form[class^="create-"] {
|
||||
|
||||
.tip {
|
||||
@extend %t-copy-sub2;
|
||||
@include transition(color, 0.15s, ease-in-out);
|
||||
@include transition(color 0.15s ease-in-out);
|
||||
|
||||
|
||||
display: block;
|
||||
margin-top: ($baseline/4);
|
||||
color: $gray-l3;
|
||||
}
|
||||
|
||||
.tip-note {
|
||||
display: block;
|
||||
margin-top: ($baseline/4);
|
||||
}
|
||||
|
||||
.tip-error {
|
||||
display: none;
|
||||
float: none;
|
||||
@@ -365,7 +354,6 @@ form[class^="create-"] {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// form - inline xblock name edit on unit, container, outline
|
||||
|
||||
// TOOD: abstract this out into a Sass placeholder
|
||||
@@ -402,6 +390,25 @@ form[class^="create-"] {
|
||||
}
|
||||
}
|
||||
|
||||
// ELEM: form wrapper
|
||||
.wrapper-create-element {
|
||||
height: 0;
|
||||
margin-bottom: $baseline;
|
||||
opacity: 0.0;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
|
||||
&.animate {
|
||||
@include transition(opacity $tmg-f1 ease-in-out 0s, height $tmg-f1 ease-in-out 0s);
|
||||
}
|
||||
|
||||
&.is-shown {
|
||||
height: auto; // define a specific height for the animating version of this UI to work properly
|
||||
opacity: 1.0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// forms - grandfathered
|
||||
|
||||
@@ -527,7 +527,7 @@
|
||||
&.wrapper-alert-warning {
|
||||
box-shadow: 0 1px 1px $white, inset 0 2px 2px $shadow-d1, inset 0 -4px 1px $orange;
|
||||
|
||||
[class^="icon"] {
|
||||
.alert-symbol {
|
||||
color: $orange;
|
||||
}
|
||||
}
|
||||
@@ -535,7 +535,7 @@
|
||||
&.wrapper-alert-error {
|
||||
box-shadow: 0 1px 1px $white, inset 0 2px 2px $shadow-d1, inset 0 -4px 1px $red-l1;
|
||||
|
||||
[class^="icon"] {
|
||||
.alert-symbol {
|
||||
color: $red-l1;
|
||||
}
|
||||
}
|
||||
@@ -543,7 +543,7 @@
|
||||
&.wrapper-alert-confirmation {
|
||||
box-shadow: 0 1px 1px $white, inset 0 2px 2px $shadow-d1, inset 0 -4px 1px $green;
|
||||
|
||||
[class^="icon"] {
|
||||
.alert-symbol {
|
||||
color: $green;
|
||||
}
|
||||
}
|
||||
@@ -551,7 +551,7 @@
|
||||
&.wrapper-alert-announcement {
|
||||
box-shadow: 0 1px 1px $white, inset 0 2px 2px $shadow-d1, inset 0 -4px 1px $blue;
|
||||
|
||||
[class^="icon"] {
|
||||
.alert-symbol {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
@@ -559,7 +559,7 @@
|
||||
&.wrapper-alert-step-required {
|
||||
box-shadow: 0 1px 1px $white, inset 0 2px 2px $shadow-d1, inset 0 -4px 1px $pink;
|
||||
|
||||
[class^="icon"] {
|
||||
.alert-symbol {
|
||||
color: $pink;
|
||||
}
|
||||
}
|
||||
@@ -579,11 +579,11 @@
|
||||
@extend %t-strong;
|
||||
}
|
||||
|
||||
[class^="icon"], .copy {
|
||||
.alert-symbol, .copy {
|
||||
float: left;
|
||||
}
|
||||
|
||||
[class^="icon"] {
|
||||
.alert-symbol {
|
||||
@include transition (color 0.50s ease-in-out 0s);
|
||||
@extend %t-icon3;
|
||||
width: flex-grid(1, 12);
|
||||
@@ -605,7 +605,7 @@
|
||||
// with actions
|
||||
&.has-actions {
|
||||
|
||||
[class^="icon"] {
|
||||
.alert-symbol {
|
||||
width: flex-grid(1, 12);
|
||||
}
|
||||
|
||||
@@ -667,6 +667,28 @@
|
||||
background: $gray-d1;
|
||||
}
|
||||
}
|
||||
|
||||
// with dismiss (to sunset action-alert-clos)
|
||||
.action-dismiss {
|
||||
|
||||
.button {
|
||||
@extend %btn-secondary-white;
|
||||
}
|
||||
|
||||
.icon,.button-copy {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.icon {
|
||||
@extend %t-icon4;
|
||||
margin-right: ($baseline/4);
|
||||
}
|
||||
|
||||
.button-copy {
|
||||
@extend %t-copy-sub1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
@import 'views/dashboard';
|
||||
@import 'views/export';
|
||||
@import 'views/index';
|
||||
@import 'views/course-create';
|
||||
@import 'views/import';
|
||||
@import 'views/outline';
|
||||
@import 'views/settings';
|
||||
|
||||
116
cms/static/sass/views/_course-create.scss
Normal file
116
cms/static/sass/views/_course-create.scss
Normal file
@@ -0,0 +1,116 @@
|
||||
// studio - views - course creation page
|
||||
// ====================
|
||||
|
||||
.view-course-create {
|
||||
|
||||
// basic layout
|
||||
// --------------------
|
||||
.content-primary, .content-supplementary {
|
||||
@include box-sizing(border-box);
|
||||
float: left;
|
||||
}
|
||||
|
||||
.content-primary {
|
||||
width: flex-grid(9, 12);
|
||||
margin-right: flex-gutter();
|
||||
}
|
||||
|
||||
.content-supplementary {
|
||||
width: flex-grid(3, 12);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// header/masthead
|
||||
// --------------------
|
||||
.mast .page-header-super {
|
||||
|
||||
.course-original-title-id, .course-original-title {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.course-original-title-id {
|
||||
@extend %t-title5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// course re-run form
|
||||
// --------------------
|
||||
.rerun-course {
|
||||
|
||||
.row {
|
||||
@include clearfix();
|
||||
margin-bottom: ($baseline*0.75);
|
||||
}
|
||||
|
||||
.column {
|
||||
float: left;
|
||||
width: 48%;
|
||||
}
|
||||
|
||||
.column:first-child {
|
||||
margin-right: 4%;
|
||||
}
|
||||
|
||||
label {
|
||||
@extend %t-title7;
|
||||
display: block;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.rerun-course-org,
|
||||
.rerun-course-number,
|
||||
.rerun-course-name,
|
||||
.rerun-course-run {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rerun-course-name {
|
||||
@extend %t-title5;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.rerun-course-save {
|
||||
@include blue-button;
|
||||
}
|
||||
|
||||
.rerun-course-cancel {
|
||||
@include white-button;
|
||||
}
|
||||
|
||||
.item-details {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.wrap-error {
|
||||
@include transition(opacity $tmg-f2 ease 0s);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.wrap-error.is-shown {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.message-status {
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
padding: ($baseline*.5) ($baseline*1.5) 8px ($baseline*1.5);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
// NOTE: override for modern button styling until all buttons (in _forms.scss) can be converted
|
||||
.actions {
|
||||
|
||||
.action-primary {
|
||||
@include blue-button;
|
||||
@extend %t-action2;
|
||||
}
|
||||
|
||||
.action-secondary {
|
||||
@include grey-button;
|
||||
@extend %t-action2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -294,120 +294,298 @@
|
||||
// ELEM: course listings
|
||||
.courses {
|
||||
margin: $baseline 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
@extend %t-title6;
|
||||
margin-bottom: $baseline;
|
||||
border-bottom: 1px solid $gray-l3;
|
||||
padding-bottom: ($baseline/2);
|
||||
color: $gray-l2;
|
||||
}
|
||||
}
|
||||
|
||||
.list-courses {
|
||||
margin-top: $baseline;
|
||||
border-radius: 3px;
|
||||
border: 1px solid $gray;
|
||||
border: 1px solid $gray-l2;
|
||||
background: $white;
|
||||
box-shadow: 0 1px 2px $shadow-l1;
|
||||
box-shadow: 0 1px 1px $shadow-l1;
|
||||
|
||||
.course-item {
|
||||
@include box-sizing(border-box);
|
||||
width: flex-grid(9, 9);
|
||||
position: relative;
|
||||
border-bottom: 1px solid $gray-l1;
|
||||
padding: $baseline;
|
||||
li:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// STATE: hover/focus
|
||||
&:hover {
|
||||
background: $paleYellow;
|
||||
|
||||
.course-actions .view-live-button {
|
||||
opacity: 1.0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
// UI: course wrappers (needed for status messages)
|
||||
.wrapper-course {
|
||||
|
||||
.course-title {
|
||||
color: $orange-d1;
|
||||
}
|
||||
// CASE: has status
|
||||
&.has-status {
|
||||
|
||||
.course-metadata {
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
.course-link, .course-actions {
|
||||
.course-status {
|
||||
@include box-sizing(border-box);
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// encompassing course link
|
||||
.course-link {
|
||||
@extend %ui-depth2;
|
||||
width: flex-grid(7, 9);
|
||||
margin-right: flex-gutter();
|
||||
}
|
||||
|
||||
// course title
|
||||
.course-title {
|
||||
@extend %t-title4;
|
||||
@extend %t-light;
|
||||
margin: 0 ($baseline*2) ($baseline/4) 0;
|
||||
}
|
||||
|
||||
// course metadata
|
||||
.course-metadata {
|
||||
@extend %t-copy-sub1;
|
||||
@include transition(opacity $tmg-f1 ease-in-out 0);
|
||||
color: $gray;
|
||||
opacity: 0.75;
|
||||
|
||||
.metadata-item {
|
||||
display: inline-block;
|
||||
|
||||
&:after {
|
||||
content: "/";
|
||||
margin-left: ($baseline/10);
|
||||
margin-right: ($baseline/10);
|
||||
color: $gray-l4;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
@extend %cont-text-sr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.course-actions {
|
||||
@extend %ui-depth3;
|
||||
position: static;
|
||||
width: flex-grid(2, 9);
|
||||
width: flex-grid(3, 9);
|
||||
padding-right: ($baseline/2);
|
||||
text-align: right;
|
||||
|
||||
// view live button
|
||||
.view-live-button {
|
||||
@extend %ui-depth3;
|
||||
@include transition(opacity $tmg-f2 ease-in-out 0);
|
||||
@include box-sizing(border-box);
|
||||
padding: ($baseline/2);
|
||||
opacity: 0.0;
|
||||
pointer-events: none;
|
||||
.value {
|
||||
|
||||
&:hover {
|
||||
opacity: 1.0;
|
||||
pointer-events: auto;
|
||||
.copy, *[class^="icon"] {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
*[class^="icon"] {
|
||||
@extend %t-icon4;
|
||||
margin-right: ($baseline/2);
|
||||
}
|
||||
|
||||
.copy {
|
||||
@extend %t-copy-sub1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
.status-message {
|
||||
@extend %t-copy-sub1;
|
||||
background-color: $gray-l5;
|
||||
box-shadow: 0 2px 2px 0 $shadow inset;
|
||||
padding: ($baseline*0.75) $baseline;
|
||||
|
||||
&.has-actions {
|
||||
|
||||
.copy, .status-actions {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.copy {
|
||||
width: 65%;
|
||||
margin: 0 $baseline 0 0;
|
||||
}
|
||||
|
||||
.status-actions {
|
||||
width: 30%;
|
||||
text-align: right;
|
||||
|
||||
.button {
|
||||
@extend %btn-secondary-white;
|
||||
}
|
||||
|
||||
.icon,.button-copy {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.icon {
|
||||
@extend %t-icon4;
|
||||
margin-right: ($baseline/4);
|
||||
}
|
||||
|
||||
.button-copy {
|
||||
@extend %t-copy-sub1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UI: individual course listings
|
||||
.course-item {
|
||||
@include box-sizing(border-box);
|
||||
width: flex-grid(9, 9);
|
||||
position: relative;
|
||||
border-bottom: 1px solid $gray-l2;
|
||||
padding: $baseline;
|
||||
|
||||
// STATE: hover/focus
|
||||
&:hover {
|
||||
background: $paleYellow;
|
||||
|
||||
.course-actions {
|
||||
opacity: 1.0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.course-title {
|
||||
color: $orange-d1;
|
||||
}
|
||||
|
||||
.course-metadata {
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
.course-link, .course-actions {
|
||||
@include box-sizing(border-box);
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// encompassing course link
|
||||
.course-link {
|
||||
@extend %ui-depth2;
|
||||
width: flex-grid(6, 9);
|
||||
margin-right: flex-gutter();
|
||||
}
|
||||
|
||||
// course title
|
||||
.course-title {
|
||||
@extend %t-title4;
|
||||
margin: 0 ($baseline*2) ($baseline/4) 0;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
// course metadata
|
||||
.course-metadata {
|
||||
@extend %t-copy-sub1;
|
||||
@include transition(opacity $tmg-f1 ease-in-out 0);
|
||||
color: $gray;
|
||||
opacity: 0.75;
|
||||
|
||||
.metadata-item {
|
||||
display: inline-block;
|
||||
|
||||
&:after {
|
||||
content: "/";
|
||||
margin-left: ($baseline/10);
|
||||
margin-right: ($baseline/10);
|
||||
color: $gray-l4;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
@extend %cont-text-sr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.course-actions {
|
||||
@include transition(opacity $tmg-f2 ease-in-out 0);
|
||||
@extend %ui-depth3;
|
||||
position: static;
|
||||
width: flex-grid(3, 9);
|
||||
text-align: right;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
|
||||
.action {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-right: ($baseline/2);
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
@extend %t-action3;
|
||||
}
|
||||
|
||||
// view live button
|
||||
.view-button {
|
||||
@include box-sizing(border-box);
|
||||
padding: ($baseline/2);
|
||||
}
|
||||
|
||||
// course re-run button
|
||||
.action-rerun {
|
||||
margin-right: $baseline;
|
||||
}
|
||||
|
||||
.rerun-button {
|
||||
font-weight: 600;
|
||||
// TODO: sync up button styling and add secondary style here
|
||||
}
|
||||
}
|
||||
|
||||
// CASE: is processing
|
||||
&.is-processing {
|
||||
|
||||
.course-status .value {
|
||||
color: $gray-l2;
|
||||
}
|
||||
}
|
||||
|
||||
// CASE: has an error
|
||||
&.has-error {
|
||||
|
||||
.course-status {
|
||||
color: $red; // TODO: abstract this out to an error-based color variable
|
||||
}
|
||||
|
||||
~ .status-message {
|
||||
background: $red-l1; // TODO: abstract this out to an error-based color variable
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
// CASE: last course in listing
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// CASE: courses that are being processed
|
||||
.courses-processing {
|
||||
margin-bottom: ($baseline*2);
|
||||
border-bottom: 1px solid $gray-l3;
|
||||
padding-bottom: ($baseline*2);
|
||||
|
||||
// TODO: abstract this case out better with normal course listings
|
||||
.list-courses {
|
||||
border: none;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.wrapper-course {
|
||||
@extend %ui-window;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.course-item {
|
||||
border: none;
|
||||
|
||||
// STATE: hover/focus
|
||||
&:hover {
|
||||
background: inherit;
|
||||
|
||||
.course-title {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// course details (replacement for course-link when a course cannot be linked)
|
||||
.course-details {
|
||||
@extend %ui-depth2;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: flex-grid(6, 9);
|
||||
margin-right: flex-gutter();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ====================
|
||||
|
||||
// ELEM: new user form
|
||||
.wrapper-create-course {
|
||||
|
||||
@@ -494,6 +672,5 @@
|
||||
margin-bottom: 0;
|
||||
padding: ($baseline*.5) ($baseline*1.5) 8px ($baseline*1.5);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,7 +343,9 @@
|
||||
<% online_help_token = self.online_help_token() if hasattr(self, 'online_help_token') else None %>
|
||||
<%include file="widgets/header.html" args="online_help_token=online_help_token" />
|
||||
|
||||
<div id="page-alert"></div>
|
||||
<div id="page-alert">
|
||||
<%block name="page_alert"></%block>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<%block name="content"></%block>
|
||||
|
||||
173
cms/templates/course-create-rerun.html
Normal file
173
cms/templates/course-create-rerun.html
Normal file
@@ -0,0 +1,173 @@
|
||||
<!--
|
||||
|
||||
DESIGN/UI NOTES:
|
||||
|
||||
* changed tip-based UI text to have is-hidden/is-shown stateful classes rather than is-hiding/is-showing
|
||||
* create-course and new-course prefixed classes have been changed to use rerun-courses
|
||||
* changed form <input /> elements to <button> elements
|
||||
|
||||
- - -
|
||||
|
||||
TODO:
|
||||
|
||||
* sync up styling of stateful classes
|
||||
* need to add support for allow_unicode_course_id in real view's template
|
||||
|
||||
-->
|
||||
|
||||
<%inherit file="base.html" />
|
||||
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
|
||||
<%block name="title">${_("Create a Course Rerun of:")}</%block>
|
||||
<%block name="bodyclass">is-signedin view-course-create view-course-create-rerun</%block>
|
||||
|
||||
<%block name="jsextra">
|
||||
<script type="text/javascript">
|
||||
require(["domReady!", "jquery", "jquery.form", "js/views/course_rerun"], function(doc, $) {
|
||||
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
var source_course_key = "${source_course_key}"
|
||||
</script>
|
||||
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
<div id="content">
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast mast-wizard has-actions">
|
||||
<h1 class="page-header">
|
||||
<span class="page-header-sub">${_("Create a re-run of a course")}</span>
|
||||
</h1>
|
||||
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">Page Actions</h3>
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<a href="" class="button cancel-button">${_("Cancel")}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<h2 class="page-header-super course-original">
|
||||
<span class="sr">${_("You are creating a re-run from:")}</span>
|
||||
<span class="course-original-title-id">${source_course_key.org} ${source_course_key.course} ${source_course_key.run}</span>
|
||||
<span class="course-original-title">${display_name}</span>
|
||||
</h2>
|
||||
</header>
|
||||
</div> <!-- /mast -->
|
||||
|
||||
<div class="wrapper-content wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<section class="content">
|
||||
<article class="content-primary">
|
||||
<div class="introduction">
|
||||
<div class="copy">
|
||||
<p>
|
||||
${_("Provide identifying information for this re-run of the course. The original course is not affected in any way by a re-run.")}
|
||||
<strong>${_("Note: Together, the organization, course number, and course run must uniquely identify this new course instance.")}</strong>
|
||||
<p>
|
||||
</div>
|
||||
</div><!-- /introduction -->
|
||||
|
||||
<!-- - - - -->
|
||||
|
||||
<div class="wrapper-rerun-course">
|
||||
<form class="form-create rerun-course course-info" id="rerun-course-form" name="rerun-course-form">
|
||||
<!-- NOTE: this element's contents should be only included when they are needed and not kept in the DOM for all states -->
|
||||
<div class="wrapper-error is-hidden">
|
||||
<div id="course_rerun_error" name="course_rerun_error" class="message message-status error" role="alert">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-form">
|
||||
<fieldset>
|
||||
<legend class="sr">${_("Required Information to Create a re-run of a course")}</legend>
|
||||
|
||||
<ol class="list-input">
|
||||
<li class="field text required" id="field-course-name">
|
||||
<label for="rerun-course-name">${_("Course Name")}</label>
|
||||
<input class="rerun-course-name" id="rerun-course-name" type="text" name="rerun-course-name" aria-required="true" value="${display_name}" placeholder="${_('e.g. Introduction to Computer Science')}" />
|
||||
<span class="tip">
|
||||
${_("The public display name for the new course. (This name is often the same as the original course name.)")}
|
||||
</span>
|
||||
<span class="tip tip-error is-hidden"></span>
|
||||
</li>
|
||||
<li class="field text required" id="field-organization">
|
||||
<label for="rerun-course-org">${_("Organization")}</label>
|
||||
<input class="rerun-course-org" id="rerun-course-org" type="text" name="rerun-course-org" aria-required="true" value="${source_course_key.org}" placeholder="${_('e.g. UniversityX or OrganizationX')}" />
|
||||
<span class="tip">
|
||||
${_("The name of the organization sponsoring the new course. (This name is often the same as the original organization name.)")}
|
||||
<strong class="tip-note" class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-hidden"></span>
|
||||
</li>
|
||||
|
||||
<li class="row">
|
||||
<div class="column field text required" id="field-course-number">
|
||||
<label for="rerun-course-number">${_("Course Number")}</label>
|
||||
<input class="rerun-course-number" id="rerun-course-number" type="text" name="rerun-course-number" aria-required="true" value="${source_course_key.course}" placeholder="${_('e.g. CS101')}" />
|
||||
<span class="tip">
|
||||
${_("The unique number that identifies the new course within the organization. (This number is often the same as the original course number.)")}
|
||||
<strong class="tip-note" class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-hidden"></span>
|
||||
</div>
|
||||
|
||||
<div class="column field text required" id="field-course-run">
|
||||
<label for="rerun-course-run">${_("Course Run")}</label>
|
||||
<input class="rerun-course-run" id="rerun-course-run" type="text" name="rerun-course-run" aria-required="true"placeholder="${_('e.g. 2014_T1')}" />
|
||||
<span class="tip">
|
||||
${_("The term in which the new course will run. (This value is often different than the original course run value.)")}
|
||||
<strong class="tip-note" class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-hidden"></span>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<input type="hidden" value="" class="allow-unicode-course-id" /> <!-- TODO: need to add support for allow_unicode_course_id in real view's template -->
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button type="submit" class="action action-primary rerun-course-save is-disabled">${_('Create Re-run')}</button>
|
||||
<button type="button" class="action action-secondary action-cancel rerun-course-cancel">${_('Cancel')}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</article><!-- /content-primary -->
|
||||
|
||||
<aside class="content-supplementary" role="complimentary">
|
||||
<div class="bit">
|
||||
<h3 class="title-3">${_("When will my course re-run start?")}</h3>
|
||||
<ul class="list-details">
|
||||
<li class="item-detail">${_("Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum.")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="bit">
|
||||
<h3 class="title-3">${_("What transfers from the original course?")}</h3>
|
||||
<ul class="list-details">
|
||||
<li class="item-detail">${_("Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum.")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="bit">
|
||||
<h3 class="title-3">${_("What does not transfer from the original course?")}</h3>
|
||||
<ul class="list-details">
|
||||
<li class="item-detail">${_("Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum.")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside><!-- /content-supplementary -->
|
||||
|
||||
</section>
|
||||
</div><!-- /content -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</%block>
|
||||
@@ -36,6 +36,31 @@ from contentstore.utils import reverse_usage_url
|
||||
% endfor
|
||||
</%block>
|
||||
|
||||
<%block name="page_alert">
|
||||
%if notification_dismiss_url is not None:
|
||||
<div class="wrapper wrapper-alert wrapper-alert-announcement is-shown">
|
||||
<div class="alert announcement has-actions">
|
||||
<i class="alert-symbol icon-bullhorn"></i>
|
||||
|
||||
<div class="copy">
|
||||
<h2 class="title title-3">This course was created as a re-run. Some manual configuration is needed.</h2>
|
||||
|
||||
<p>Be sure to review and reset all dates (the Course Start Date was set to January 1, 2030); set up the course team; review course updates and other assets for dated material; and seed the discussions and wiki.</p>
|
||||
</div>
|
||||
|
||||
<ul class="nav-actions">
|
||||
<li class="action action-dismiss">
|
||||
<a href="#" class="button dismiss-button" data-dismiss-link='${notification_dismiss_url}'>
|
||||
<i class="icon icon-remove-sign"></i>
|
||||
<span class="button-copy">${_("Dimiss")}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast has-actions has-subtitle">
|
||||
|
||||
@@ -77,7 +77,7 @@ require(["domReady!", "jquery", "jquery.form", "js/index"], function(doc, $) {
|
||||
|
||||
% if course_creator_status=='granted':
|
||||
<div class="wrapper-create-element wrapper-create-course">
|
||||
<form class="create-course course-info" id="create-course-form" name="create-course-form">
|
||||
<form class="form-create create-course course-info" id="create-course-form" name="create-course-form">
|
||||
<div class="wrap-error">
|
||||
<div id="course_creation_error" name="course_creation_error" class="message message-status message-status error" role="alert">
|
||||
<p>${_("Please correct the highlighted fields below.")}</p>
|
||||
@@ -131,10 +131,104 @@ require(["domReady!", "jquery", "jquery.form", "js/index"], function(doc, $) {
|
||||
</div>
|
||||
% endif
|
||||
|
||||
<!-- STATE: processing courses -->
|
||||
%if len(unsucceeded_course_actions) > 0:
|
||||
<div class="courses courses-processing">
|
||||
<h3 class="title">Courses Being Processed</h3>
|
||||
|
||||
<ul class="list-courses">
|
||||
%for display_name, org, num, run, state_failed, state_in_progress, dismiss_link in sorted(unsucceeded_course_actions, key=lambda s: s[0].lower() if s[0] is not None else ''):
|
||||
<!-- STATE: re-run is processing -->
|
||||
%if state_in_progress:
|
||||
<li class="wrapper-course has-status">
|
||||
<div class="course-item course-rerun is-processing">
|
||||
<div class="course-details" href="#">
|
||||
<h3 class="course-title">${display_name}</h3>
|
||||
|
||||
<div class="course-metadata">
|
||||
<span class="course-org metadata-item">
|
||||
<span class="label">${_("Organization:")}</span> <span class="value">${org}</span>
|
||||
</span>
|
||||
<span class="course-num metadata-item">
|
||||
<span class="label">${_("Course Number:")}</span>
|
||||
<span class="value">${num}</span>
|
||||
</span>
|
||||
<span class="course-run metadata-item">
|
||||
<span class="label">${_("Course Run:")}</span> <span class="value">${run}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dl class="course-status">
|
||||
<dt class="label sr">This re-run processing status:</dt>
|
||||
<dd class="value">
|
||||
<i class="icon-refresh icon-spin"></i>
|
||||
<span class="copy">Configuring as re-run</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="status-message">
|
||||
<p class="copy">${_("The new course will be added to your course list in 5-10 minutes. Return to this page or refresh it to update the course list. The new course will need some manual configuration.")}</p>
|
||||
</div>
|
||||
</li>
|
||||
%endif
|
||||
|
||||
<!-- - - - -->
|
||||
|
||||
<!-- STATE: re-run has error -->
|
||||
%if state_failed:
|
||||
<li class="wrapper-course has-status">
|
||||
<div class="course-item course-rerun has-error">
|
||||
<div class="course-details" href="#">
|
||||
<h3 class="course-title">${display_name}</h3>
|
||||
|
||||
<div class="course-metadata">
|
||||
<span class="course-org metadata-item">
|
||||
<span class="label">${_("Organization:")}</span> <span class="value">${org}</span>
|
||||
</span>
|
||||
<span class="course-num metadata-item">
|
||||
<span class="label">${_("Course Number:")}</span>
|
||||
<span class="value">${num}</span>
|
||||
</span>
|
||||
<span class="course-run metadata-item">
|
||||
<span class="label">${_("Course Run:")}</span> <span class="value">${run}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dl class="course-status">
|
||||
<dt class="label sr">This re-run processing status:</dt>
|
||||
<dd class="value">
|
||||
<i class="icon-warning-sign"></i>
|
||||
<span class="copy">Configuration Error</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div class="status-message has-actions">
|
||||
<p class="copy">${_("A system error occurred while your course was being processed. Please go to the original course to try the re-run again, or contact your PM for assistance.")}</p>
|
||||
|
||||
<ul class="status-actions">
|
||||
<li class="action action-dismiss">
|
||||
<a href="#" class="button dismiss-button" data-dismiss-link="${dismiss_link}">
|
||||
<i class="icon icon-remove-sign"></i>
|
||||
<span class="button-copy">${_("Dismiss")}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
%endif
|
||||
%endfor
|
||||
</ul>
|
||||
</div>
|
||||
%endif
|
||||
|
||||
%if len(courses) > 0:
|
||||
<div class="courses">
|
||||
<ul class="list-courses">
|
||||
%for course, url, lms_link, org, num, run in sorted(courses, key=lambda s: s[0].lower() if s[0] is not None else ''):
|
||||
%for course, url, lms_link, rerun_link, org, num, run in sorted(courses, key=lambda s: s[0].lower() if s[0] is not None else ''):
|
||||
<li class="course-item">
|
||||
<a class="course-link" href="${url}">
|
||||
<h3 class="course-title">${course}</h3>
|
||||
@@ -154,12 +248,22 @@ require(["domReady!", "jquery", "jquery.form", "js/index"], function(doc, $) {
|
||||
</a>
|
||||
|
||||
<ul class="item-actions course-actions">
|
||||
<li class="action">
|
||||
<a href="${lms_link}" rel="external" class="button view-button view-live-button">${_("View Live")}</a>
|
||||
% if course_creator_status=='granted':
|
||||
<li class="action action-rerun">
|
||||
<a href="${rerun_link}" class="button rerun-button">${_("Re-run Course")}</a>
|
||||
</li>
|
||||
% endif
|
||||
<li class="action action-view">
|
||||
<a href="${lms_link}" rel="external" class="button view-button">${_("View Live")}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
%endfor
|
||||
% if course_creator_status=='granted':
|
||||
<script type="text/javascript">
|
||||
$('.course-item').addClass('can-rerun');
|
||||
</script>
|
||||
% endif
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<article class="content-primary" role="main">
|
||||
%if allow_actions:
|
||||
<div class="wrapper-create-element animate wrapper-create-user">
|
||||
<form class="create-user" id="create-user-form" name="create-user-form">
|
||||
<form class="form-create create-user" id="create-user-form" name="create-user-form">
|
||||
<div class="wrapper-form">
|
||||
<h3 class="title">${_("Add a User to Your Course's Team")}</h3>
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="bit">
|
||||
<h3 class="title-3">${_("Course Team Roles")}</h3>
|
||||
<p>${_("Course team members, or staff, are course co-authors. They have full writing and editing privileges on all course content.")}</p>
|
||||
<p>${_("Admins are course team members who can add and remove other course team members.")}</p>
|
||||
<p>${_("Admins are course team members who can add and remove other course team members.")}</p>
|
||||
</div>
|
||||
|
||||
% if user_is_instuctor and len(instructors) == 1:
|
||||
|
||||
368
cms/templates/ux/reference/course-create-rerun.html
Normal file
368
cms/templates/ux/reference/course-create-rerun.html
Normal file
@@ -0,0 +1,368 @@
|
||||
<!--
|
||||
|
||||
DESIGN/UI NOTES:
|
||||
|
||||
* create-course and new-course prefixed classes have been changed to use rerun-courses plus generic form-create class
|
||||
* changed form <input /> elements to <button> elements
|
||||
|
||||
- - -
|
||||
|
||||
TODO:
|
||||
|
||||
* sync up styling of stateful classes
|
||||
* need to add support for allow_unicode_course_id in real view's template
|
||||
|
||||
-->
|
||||
|
||||
<%inherit file="../../base.html" />
|
||||
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%! from django.core.urlresolvers import reverse %>
|
||||
|
||||
<%block name="title">[template] ${_("Create a Course Rerun of HarvardX SW12.2x T2_2014")}</%block>
|
||||
<%block name="bodyclass">is-signedin view-course-create view-course-create-rerun</%block>
|
||||
|
||||
<%block name="content">
|
||||
<div id="content">
|
||||
|
||||
<div class="wrapper-mast wrapper">
|
||||
<header class="mast mast-wizard has-actions">
|
||||
<h1 class="page-header">
|
||||
<span class="page-header-sub">${_("Create a re-run of a course")}</span>
|
||||
</h1>
|
||||
|
||||
<nav class="nav-actions">
|
||||
<h3 class="sr">Page Actions</h3>
|
||||
<ul>
|
||||
<li class="nav-item">
|
||||
<a href="" rel="external" class="button cancel-button">${_("Cancel")}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<h2 class="page-header-super course-original">
|
||||
<span class="sr">${_("You are creating a re-run from:")}</span>
|
||||
<span class="course-original-title-id">HarvardX SW12.2x T2_2014</span>
|
||||
<span class="course-original-title">China (Part 2): The Creation and End of a Centralized Empire</span>
|
||||
</h2>
|
||||
</header>
|
||||
</div> <!-- /mast -->
|
||||
|
||||
<div class="wrapper-content wrapper">
|
||||
<div class="inner-wrapper">
|
||||
<section class="content">
|
||||
<article class="content-primary">
|
||||
<div class="introduction">
|
||||
<div class="copy">
|
||||
<p>
|
||||
${_("Provide identifying information for this re-run of the course. The original course is not affected in any way by a re-run.")}
|
||||
<strong>${_("Note: Together, the organization, course number, and course run must uniquely identify this new course instance.")}</strong>
|
||||
<p>
|
||||
</div>
|
||||
</div><!-- /introduction -->
|
||||
|
||||
<!-- - - - -->
|
||||
|
||||
<!-- STATE: initial form -->
|
||||
<div class="wrapper-rerun-course">
|
||||
<form class="form-create rerun-course course-info" id="rerun-course-form" name="rerun-course-form">
|
||||
<!-- NOTE: this element's contents should be only included when they are needed and not kept in the DOM for all states -->
|
||||
<div class="wrapper-error">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="wrapper-form">
|
||||
<fieldset>
|
||||
<legend class="sr">${_("Required Information to Create a re-run of a course")}</legend>
|
||||
|
||||
<ol class="list-input">
|
||||
<li class="field text required" id="field-course-name">
|
||||
<label for="rerun-course-name">${_("Course Name")}</label>
|
||||
<input class="rerun-course-name" id="rerun-course-name" type="text" name="rerun-course-name" aria-required="true" placeholder="${_('e.g. Introduction to Computer Science')}" />
|
||||
<span class="tip">
|
||||
${_("The public display name for the new course. (This name is often the same as the original course name.)")}
|
||||
</span>
|
||||
<span class="tip tip-error is-hidden"></span>
|
||||
</li>
|
||||
<li class="field text required" id="field-organization">
|
||||
<label for="rerun-course-org">${_("Organization")}</label>
|
||||
<input class="rerun-course-org" id="rerun-course-org" type="text" name="rerun-course-org" aria-required="true" placeholder="${_('e.g. UniversityX or OrganizationX')}" />
|
||||
<span class="tip">
|
||||
${_("The name of the organization sponsoring the new course. (This name is often the same as the original organization name.)")}
|
||||
<strong class="tip-note" class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-hidden"></span>
|
||||
</li>
|
||||
|
||||
<li class="row">
|
||||
<div class="column field text required" id="field-course-number">
|
||||
<label for="rerun-course-number">${_("Course Number")}</label>
|
||||
<input class="rerun-course-number" id="rerun-course-number" type="text" name="rerun-course-number" aria-required="true" placeholder="${_('e.g. CS101')}" />
|
||||
<span class="tip">
|
||||
${_("The unique number that identifies the new course within the organization. (This number is often the same as the original course number.)")}
|
||||
<strong class="tip-note" class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-hidden"></span>
|
||||
</div>
|
||||
|
||||
<div class="column field text required" id="field-course-run">
|
||||
<label for="rerun-course-run">${_("Course Run")}</label>
|
||||
<input class="rerun-course-run" id="rerun-course-run" type="text" name="rerun-course-run" aria-required="true"placeholder="${_('e.g. 2014_T1')}" />
|
||||
<span class="tip">
|
||||
${_("The term in which the new course will run. (This value is often different than the original course run value.)")}
|
||||
<strong class="tip-note" class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-hidden"></span>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<input type="hidden" value="" class="allow-unicode-course-id" /> <!-- TODO: need to add support for allow_unicode_course_id in real view's template -->
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button type="submit" class="action action-primary rerun-course-save is-disabled">${_('Create Re-run')}</button>
|
||||
<button type="button" class="action action-secondary action-cancel rerun-course-cancel">${_('Cancel')}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /rerun-course -->
|
||||
|
||||
<!-- - - - -->
|
||||
|
||||
<!-- STATE: error - all attributes match an existing course -->
|
||||
<div class="wrapper-rerun-course">
|
||||
<form class="form-create rerun-course course-info" id="rerun-course-form" name="rerun-course-form">
|
||||
<div class="wrapper-error is-shown">
|
||||
<!-- NOTE: this element's contents should be only included when they are needed and not kept in the DOM for all states -->
|
||||
<div id="course_rerun_error" name="course_rerun_error" class="message message-status error" role="alert">
|
||||
<p>${_("A course already has that organization, course number, and course run. Change one or more of these values to give the new course a unique URL.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-form">
|
||||
<fieldset>
|
||||
<legend class="sr">${_("Required Information to Create a re-run of a course")}</legend>
|
||||
|
||||
<ol class="list-input">
|
||||
<li class="field text required error" id="field-course-name">
|
||||
<label for="rerun-course-name">${_("Course Name")}</label>
|
||||
<input class="rerun-course-name" id="rerun-course-name" type="text" name="rerun-course-name" aria-required="true" placeholder="${_('e.g. Introduction to Computer Science')}" />
|
||||
<span class="tip">
|
||||
${_("The public display name for the new course. (This name is often the same as the original course name.)")}
|
||||
</span>
|
||||
<span class="tip tip-error is-hiding"></span>
|
||||
</li>
|
||||
<li class="field text required error" id="field-organization">
|
||||
<label for="rerun-course-org">${_("Organization")}</label>
|
||||
<input class="rerun-course-org" id="rerun-course-org" type="text" name="rerun-course-org" aria-required="true" placeholder="${_('e.g. UniversityX or OrganizationX')}" />
|
||||
<span class="tip">
|
||||
${_("The name of the organization sponsoring the new course. (This name is often the same as the original organization name.)")}
|
||||
<strong class="tip-note" class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-hiding"></span>
|
||||
</li>
|
||||
|
||||
<li class="row">
|
||||
<div class="column field text required error" id="field-course-number">
|
||||
<label for="rerun-course-number">${_("Course Number")}</label>
|
||||
<input class="rerun-course-number" id="rerun-course-number" type="text" name="rerun-course-number" aria-required="true" placeholder="${_('e.g. CS101')}" />
|
||||
<span class="tip">
|
||||
${_("The unique number that identifies the new course within the organization. (This number is often the same as the original course number.)")}
|
||||
<strong class="tip-note" class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-hiding"></span>
|
||||
</div>
|
||||
|
||||
<div class="column field text required error" id="field-course-run">
|
||||
<label for="rerun-course-run">${_("Course Run")}</label>
|
||||
<input class="rerun-course-run" id="rerun-course-run" type="text" name="rerun-course-run" aria-required="true"placeholder="${_('e.g. 2014_T1')}" />
|
||||
<span class="tip">
|
||||
${_("The term in which the new course will run. (This value is often different than the original course run value.)")}
|
||||
<strong class="tip-note" class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-hiding"></span>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<input type="hidden" value="" class="allow-unicode-course-id" /> <!-- TODO: need to add support for allow_unicode_course_id in real view's template -->
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button type="submit" class="action action-primary rerun-course-save is-disabled">${_('Create Re-run')}</button>
|
||||
<button type="button" class="action action-secondary action-cancel rerun-course-cancel">${_('Cancel and Return to Dashboard')}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /rerun-course -->
|
||||
|
||||
<!-- - - - -->
|
||||
|
||||
<!-- STATE: error - specific error fields -->
|
||||
<div class="wrapper-rerun-course">
|
||||
<form class="form-create rerun-course course-info" id="rerun-course-form" name="rerun-course-form">
|
||||
<div class="wrapper-error is-shown">
|
||||
<!-- NOTE: this element's contents should be only included when they are needed and not kept in the DOM for all states -->
|
||||
<div id="course_rerun_error" name="course_rerun_error" class="message message-status error" role="alert">
|
||||
<p>${_("Please correct the highlighted fields below.")}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wrapper-form">
|
||||
<fieldset>
|
||||
<legend class="sr">${_("Required Information to Create a re-run of a course")}</legend>
|
||||
|
||||
<ol class="list-input">
|
||||
<li class="field text required error" id="field-course-name">
|
||||
<label for="rerun-course-name">${_("Course Name")}</label>
|
||||
<input class="rerun-course-name" id="rerun-course-name" type="text" name="rerun-course-name" aria-required="true" placeholder="${_('e.g. Introduction to Computer Science')}" />
|
||||
<span class="tip">
|
||||
${_("The public display name for the new course. (This name is often the same as the original course name.)")}
|
||||
</span>
|
||||
<span class="tip tip-error is-showing">Required field.</span>
|
||||
</li>
|
||||
<li class="field text required error" id="field-organization">
|
||||
<label for="rerun-course-org">${_("Organization")}</label>
|
||||
<input class="rerun-course-org" id="rerun-course-org" type="text" name="rerun-course-org" aria-required="true" placeholder="${_('e.g. UniversityX or OrganizationX')}" />
|
||||
<span class="tip">
|
||||
${_("The name of the organization sponsoring the new course. (This name is often the same as the original organization name.)")}
|
||||
<strong class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-showing">Please do not use any spaces or special characters in this field.</span>
|
||||
</li>
|
||||
|
||||
<li class="row">
|
||||
<div class="column field text required error" id="field-course-number">
|
||||
<label for="rerun-course-number">${_("Course Number")}</label>
|
||||
<input class="rerun-course-number" id="rerun-course-number" type="text" name="rerun-course-number" aria-required="true" placeholder="${_('e.g. CS101')}" />
|
||||
<span class="tip">
|
||||
${_("The unique number that identifies the new course within the organization. (This number is often the same as the original course number.)")}
|
||||
<strong class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-showing">Please do not use any spaces or special characters in this field.</span>
|
||||
</div>
|
||||
|
||||
<div class="column field text required error" id="field-course-run">
|
||||
<label for="rerun-course-run">${_("Course Run")}</label>
|
||||
<input class="rerun-course-run" id="rerun-course-run" type="text" name="rerun-course-run" aria-required="true"placeholder="${_('e.g. 2014_T1')}" />
|
||||
<span class="tip">
|
||||
${_("The term in which the new course will run. (This value is often different than the original course run value.)")}
|
||||
<strong class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-showing">Required field.</span>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<input type="hidden" value="" class="allow-unicode-course-id" /> <!-- TODO: need to add support for allow_unicode_course_id in real view's template -->
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button type="submit" class="action action-primary rerun-course-save is-disabled">${_('Create Re-run')}</button>
|
||||
<button type="button" class="action action-secondary action-cancel rerun-course-cancel">${_('Cancel and Return to Dashboard')}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /rerun-course -->
|
||||
|
||||
<!-- - - - -->
|
||||
|
||||
<!-- STATE: processing successful submission -->
|
||||
<div class="wrapper-rerun-course">
|
||||
<form class="form-create rerun-course course-info" id="rerun-course-form" name="rerun-course-form">
|
||||
<div class="wrapper-error">
|
||||
</div>
|
||||
|
||||
<div class="wrapper-form">
|
||||
<fieldset>
|
||||
<legend class="sr">${_("Required Information to Create a re-run of a course")}</legend>
|
||||
|
||||
<ol class="list-input">
|
||||
<li class="field text required" id="field-course-name">
|
||||
<label for="rerun-course-name">${_("Course Name")}</label>
|
||||
<input class="rerun-course-name" id="rerun-course-name" type="text" name="rerun-course-name" aria-required="true" placeholder="${_('e.g. Introduction to Computer Science')}" />
|
||||
<span class="tip">
|
||||
${_("The public display name for the new course. (This name is often the same as the original course name.)")}
|
||||
</span>
|
||||
<span class="tip tip-error is-hiding"></span>
|
||||
</li>
|
||||
<li class="field text required" id="field-organization">
|
||||
<label for="rerun-course-org">${_("Organization")}</label>
|
||||
<input class="rerun-course-org" id="rerun-course-org" type="text" name="rerun-course-org" aria-required="true" placeholder="${_('e.g. UniversityX or OrganizationX')}" />
|
||||
<span class="tip">
|
||||
${_("The name of the organization sponsoring the new course. (This name is often the same as the original organization name.)")}
|
||||
<strong class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-hiding"></span>
|
||||
</li>
|
||||
|
||||
<li class="row">
|
||||
<div class="column field text required" id="field-course-number">
|
||||
<label for="rerun-course-number">${_("Course Number")}</label>
|
||||
<input class="rerun-course-number" id="rerun-course-number" type="text" name="rerun-course-number" aria-required="true" placeholder="${_('e.g. CS101')}" />
|
||||
<span class="tip">
|
||||
${_("The unique number that identifies the new course within the organization. (This number is often the same as the original course number.)")}
|
||||
<strong class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-hiding"></span>
|
||||
</div>
|
||||
|
||||
<div class="column field text required" id="field-course-run">
|
||||
<label for="rerun-course-run">${_("Course Run")}</label>
|
||||
<input class="rerun-course-run" id="rerun-course-run" type="text" name="rerun-course-run" aria-required="true"placeholder="${_('e.g. 2014_T1')}" />
|
||||
<span class="tip">
|
||||
${_("The term in which the new course will run. (This value is often different than the original course run value.)")}
|
||||
<strong class="tip-note">${_("Note: No spaces or special characters are allowed.")}</strong>
|
||||
</span>
|
||||
<span class="tip tip-error is-hiding"></span>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<input type="hidden" value="" class="allow-unicode-course-id" /> <!-- TODO: need to add support for allow_unicode_course_id in real view's template -->
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button type="submit" class="action action-primary rerun-course-save is-disabled is-processing">
|
||||
<i class="icon-refresh icon-spin"></i>
|
||||
${_('Processing Re-run Request')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /rerun-course -->
|
||||
|
||||
</article><!-- /content-primary -->
|
||||
|
||||
<aside class="content-supplementary" role="complimentary">
|
||||
<div class="bit">
|
||||
<h3 class="title-3">${_("When will my course re-run start?")}</h3>
|
||||
<ul class="list-details">
|
||||
<li class="item-detail">${_("Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum.")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="bit">
|
||||
<h3 class="title-3">${_("What transfers from the original course?")}</h3>
|
||||
<ul class="list-details">
|
||||
<li class="item-detail">${_("Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum.")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="bit">
|
||||
<h3 class="title-3">${_("What does not transfer from the original course?")}</h3>
|
||||
<ul class="list-details">
|
||||
<li class="item-detail">${_("Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Sed posuere consectetur est at lobortis. Maecenas faucibus mollis interdum.")}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</aside><!-- /content-supplementary -->
|
||||
|
||||
</section>
|
||||
</div><!-- /content -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</%block>
|
||||
@@ -74,6 +74,7 @@ urlpatterns += patterns(
|
||||
),
|
||||
url(r'^course/{}?$'.format(settings.COURSE_KEY_PATTERN), 'course_handler', name='course_handler'),
|
||||
url(r'^course_notifications/{}/(?P<action_state_id>\d+)?$'.format(settings.COURSE_KEY_PATTERN), 'course_notifications_handler'),
|
||||
url(r'^course_rerun/{}$'.format(settings.COURSE_KEY_PATTERN), 'course_rerun_handler', name='course_rerun_handler'),
|
||||
url(r'^container/{}$'.format(settings.USAGE_KEY_PATTERN), 'container_handler'),
|
||||
url(r'^checklists/{}/(?P<checklist_index>\d+)?$'.format(settings.COURSE_KEY_PATTERN), 'checklists_handler'),
|
||||
url(r'^orphan/{}$'.format(settings.COURSE_KEY_PATTERN), 'orphan_handler'),
|
||||
|
||||
@@ -113,7 +113,7 @@ class CourseRerunUIStateManager(CourseActionUIStateManager):
|
||||
FAILED = "failed"
|
||||
SUCCEEDED = "succeeded"
|
||||
|
||||
def initiated(self, source_course_key, destination_course_key, user):
|
||||
def initiated(self, source_course_key, destination_course_key, user, display_name):
|
||||
"""
|
||||
To be called when a new rerun is initiated for the given course by the given user.
|
||||
"""
|
||||
@@ -123,6 +123,7 @@ class CourseRerunUIStateManager(CourseActionUIStateManager):
|
||||
user=user,
|
||||
allow_not_found=True,
|
||||
source_course_key=source_course_key,
|
||||
display_name=display_name,
|
||||
)
|
||||
|
||||
def succeeded(self, course_key):
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding field 'CourseRerunState.display_name'
|
||||
db.add_column('course_action_state_coursererunstate', 'display_name',
|
||||
self.gf('django.db.models.fields.CharField')(default='', max_length=255),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'CourseRerunState.display_name'
|
||||
db.delete_column('course_action_state_coursererunstate', 'display_name')
|
||||
|
||||
|
||||
models = {
|
||||
'auth.group': {
|
||||
'Meta': {'object_name': 'Group'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
|
||||
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
|
||||
},
|
||||
'auth.permission': {
|
||||
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
|
||||
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
|
||||
},
|
||||
'auth.user': {
|
||||
'Meta': {'object_name': 'User'},
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
|
||||
},
|
||||
'contenttypes.contenttype': {
|
||||
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
|
||||
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
|
||||
},
|
||||
'course_action_state.coursererunstate': {
|
||||
'Meta': {'unique_together': "(('course_key', 'action'),)", 'object_name': 'CourseRerunState'},
|
||||
'action': ('django.db.models.fields.CharField', [], {'max_length': '100', 'db_index': 'True'}),
|
||||
'course_key': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'created_time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'created_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_by_user+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}),
|
||||
'display_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'message': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
|
||||
'should_display': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source_course_key': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'state': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
|
||||
'updated_time': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'updated_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updated_by_user+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_action_state']
|
||||
@@ -109,6 +109,9 @@ class CourseRerunState(CourseActionUIState):
|
||||
# Original course that is being rerun
|
||||
source_course_key = CourseKeyField(max_length=255, db_index=True)
|
||||
|
||||
# Display name for destination course
|
||||
display_name = models.CharField(max_length=255, default="")
|
||||
|
||||
# MANAGERS
|
||||
# Override the abstract class' manager with a Rerun-specific manager that inherits from the base class' manager.
|
||||
objects = CourseRerunUIStateManager()
|
||||
|
||||
@@ -17,10 +17,13 @@ class TestCourseRerunStateManager(TestCase):
|
||||
self.source_course_key = CourseLocator("source_org", "source_course_num", "source_run")
|
||||
self.course_key = CourseLocator("test_org", "test_course_num", "test_run")
|
||||
self.created_user = UserFactory()
|
||||
self.display_name = "destination course name"
|
||||
self.expected_rerun_state = {
|
||||
'created_user': self.created_user,
|
||||
'updated_user': self.created_user,
|
||||
'course_key': self.course_key,
|
||||
'source_course_key': self.source_course_key,
|
||||
"display_name": self.display_name,
|
||||
'action': CourseRerunUIStateManager.ACTION,
|
||||
'should_display': True,
|
||||
'message': "",
|
||||
@@ -53,10 +56,16 @@ class TestCourseRerunStateManager(TestCase):
|
||||
})
|
||||
self.verify_rerun_state()
|
||||
|
||||
def test_rerun_initiated(self):
|
||||
def initiate_rerun(self):
|
||||
CourseRerunState.objects.initiated(
|
||||
source_course_key=self.source_course_key, destination_course_key=self.course_key, user=self.created_user
|
||||
source_course_key=self.source_course_key,
|
||||
destination_course_key=self.course_key,
|
||||
user=self.created_user,
|
||||
display_name=self.display_name,
|
||||
)
|
||||
|
||||
def test_rerun_initiated(self):
|
||||
self.initiate_rerun()
|
||||
self.expected_rerun_state.update(
|
||||
{'state': CourseRerunUIStateManager.State.IN_PROGRESS}
|
||||
)
|
||||
@@ -64,9 +73,7 @@ class TestCourseRerunStateManager(TestCase):
|
||||
|
||||
def test_rerun_succeeded(self):
|
||||
# initiate
|
||||
CourseRerunState.objects.initiated(
|
||||
source_course_key=self.source_course_key, destination_course_key=self.course_key, user=self.created_user
|
||||
)
|
||||
self.initiate_rerun()
|
||||
|
||||
# set state to succeed
|
||||
CourseRerunState.objects.succeeded(course_key=self.course_key)
|
||||
@@ -80,9 +87,7 @@ class TestCourseRerunStateManager(TestCase):
|
||||
|
||||
def test_rerun_failed(self):
|
||||
# initiate
|
||||
CourseRerunState.objects.initiated(
|
||||
source_course_key=self.source_course_key, destination_course_key=self.course_key, user=self.created_user
|
||||
)
|
||||
self.initiate_rerun()
|
||||
|
||||
# set state to fail
|
||||
exception = Exception("failure in rerunning")
|
||||
|
||||
@@ -1794,7 +1794,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
|
||||
xblock_class = self.mixologist.mix(xblock_class)
|
||||
|
||||
for field_name, value in fields.iteritems():
|
||||
if value:
|
||||
if value is not None:
|
||||
if isinstance(xblock_class.fields[field_name], Reference):
|
||||
fields[field_name] = value.block_id
|
||||
elif isinstance(xblock_class.fields[field_name], ReferenceList):
|
||||
|
||||
Reference in New Issue
Block a user