diff --git a/cms/djangoapps/contentstore/views.py b/cms/djangoapps/contentstore/views.py
index 6375f90d4e..e2b5c6b51e 100644
--- a/cms/djangoapps/contentstore/views.py
+++ b/cms/djangoapps/contentstore/views.py
@@ -109,6 +109,7 @@ def index(request):
courses = filter(lambda course: has_access(request.user, course.location), courses)
return render_to_response('index.html', {
+ 'new_course_template' : Location('i4x', 'edx', 'templates', 'course', 'Empty'),
'courses': [(course.metadata.get('display_name'),
reverse('course_index', args=[
course.location.org,
@@ -597,7 +598,32 @@ def unpublish_unit(request):
return HttpResponse()
+@login_required
+@expect_json
+def create_new_course(request):
+ template = Location(request.POST['template'])
+ org = request.POST.get('org')
+ number = request.POST.get('number')
+ display_name = request.POST.get('display_name')
+ dest_location = Location('i4x', org, number, 'course', Location.clean(display_name))
+
+ logging.debug(dest_location)
+ logging.debug(template)
+
+ new_course = modulestore('direct').clone_item(template, dest_location)
+
+ if display_name is not None:
+ new_course.metadata['display_name'] = display_name
+
+ # we need a 'data_dir' for legacy reasons
+ new_course.metadata['data_dir'] = uuid4().hex
+
+ modulestore('direct').update_metadata(new_course.location.url(), new_course.own_metadata)
+
+ create_all_course_groups(request.user, new_course.location)
+
+ return HttpResponse(json.dumps({'id': new_course.location.url()}))
@login_required
@expect_json
diff --git a/cms/static/js/base.js b/cms/static/js/base.js
index 8602770d24..12f1a3ecc3 100644
--- a/cms/static/js/base.js
+++ b/cms/static/js/base.js
@@ -58,6 +58,8 @@ $(document).ready(function() {
e.preventDefault();
$('.import .file-input').click();
});
+
+ $('.new-course-button').bind('click', addNewCourse);
});
function showImportSubmit(e) {
@@ -406,6 +408,7 @@ function addNewSection(e) {
$newSection.find('.new-section-name-cancel').bind('click', cancelNewSection);
}
+
function saveNewSection(e) {
e.preventDefault();
@@ -430,6 +433,42 @@ function cancelNewSection(e) {
$(this).parents('section.new-section').remove();
}
+
+function addNewCourse(e) {
+ e.preventDefault();
+ var $newCourse = $($('#new-course-template').html());
+ $('.new-course-button').after($newCourse);
+ $newCourse.find('.new-course-org').focus().select();
+ $newCourse.find('.new-course-save').bind('click', saveNewCourse);
+ $newCourse.find('.new-course-cancel').bind('click', cancelNewCourse);
+}
+
+function saveNewCourse(e) {
+ e.preventDefault();
+
+ template = $(this).data('template');
+
+ org = $(this).prevAll('.new-course-org').val();
+ number = $(this).prevAll('.new-course-number').val();
+ display_name = $(this).prevAll('.new-course-name').val();
+
+ $.post('/create_new_course',
+ { 'template' : template,
+ 'org' : org,
+ 'number' : number,
+ 'display_name': display_name,
+ },
+ function(data) {
+ if (data.id != undefined)
+ location.reload();
+ });
+}
+
+function cancelNewCourse(e) {
+ e.preventDefault();
+ $(this).parents('section.new-course').remove();
+}
+
function addNewSubsection(e) {
e.preventDefault();
var $section = $(this).closest('.courseware-section');
diff --git a/cms/templates/index.html b/cms/templates/index.html
index 4b721a0865..69058e4db3 100644
--- a/cms/templates/index.html
+++ b/cms/templates/index.html
@@ -2,13 +2,28 @@
<%block name="bodyclass">index%block>
<%block name="title">Courses%block>
-<%block name="content">
+<%block name="header_extras">
+
+ %block>
+<%block name="content">
My Courses
- New Course
+ New Course
%for course, url in courses:
-
diff --git a/cms/urls.py b/cms/urls.py
index 1c2e70b35d..d9f75d159e 100644
--- a/cms/urls.py
+++ b/cms/urls.py
@@ -16,8 +16,7 @@ urlpatterns = ('',
url(r'^create_draft$', 'contentstore.views.create_draft', name='create_draft'),
url(r'^publish_draft$', 'contentstore.views.publish_draft', name='publish_draft'),
url(r'^unpublish_unit$', 'contentstore.views.unpublish_unit', name='unpublish_unit'),
-
-
+ url(r'^create_new_course', 'contentstore.views.create_new_course', name='create_new_course'),
url(r'^(?P[^/]+)/(?P[^/]+)/course/(?P[^/]+)$',
'contentstore.views.course_index', name='course_index'),
diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py
index e7e3e4e519..4883677bf4 100644
--- a/common/lib/xmodule/xmodule/course_module.py
+++ b/common/lib/xmodule/xmodule/course_module.py
@@ -16,6 +16,8 @@ log = logging.getLogger(__name__)
class CourseDescriptor(SequenceDescriptor):
module_class = SequenceModule
+ template_dir_name = 'course'
+
class Textbook:
def __init__(self, title, book_url):
self.title = title
@@ -64,7 +66,6 @@ class CourseDescriptor(SequenceDescriptor):
def __init__(self, system, definition=None, **kwargs):
super(CourseDescriptor, self).__init__(system, definition, **kwargs)
-
self.textbooks = []
for title, book_url in self.definition['data']['textbooks']:
try:
diff --git a/common/lib/xmodule/xmodule/templates.py b/common/lib/xmodule/xmodule/templates.py
index 41b1523709..dcb731b135 100644
--- a/common/lib/xmodule/xmodule/templates.py
+++ b/common/lib/xmodule/xmodule/templates.py
@@ -31,6 +31,8 @@ def all_templates():
templates = defaultdict(list)
for category, descriptor in XModuleDescriptor.load_classes():
+ if category == 'course':
+ logging.debug(descriptor.templates())
templates[category] = descriptor.templates()
return templates
@@ -65,8 +67,9 @@ def update_templates():
template_location = Location('i4x', 'edx', 'templates', category, Location.clean_for_url_name(template.metadata['display_name']))
try:
- json_data = template._asdict()
+ json_data = {'definition': {'data': template.data, 'children' : template.children}}
json_data['location'] = template_location.dict()
+
XModuleDescriptor.load_from_json(json_data, TemplateTestSystem())
except:
log.warning('Unable to instantiate {cat} from template {template}, skipping'.format(