diff --git a/cms/static/js/models/settings/course_details.js b/cms/static/js/models/settings/course_details.js
index 2e192e066b..202d1097e7 100644
--- a/cms/static/js/models/settings/course_details.js
+++ b/cms/static/js/models/settings/course_details.js
@@ -29,7 +29,9 @@ var CourseDetails = Backbone.Model.extend({
video_thumbnail_image_asset_path: '',
pre_requisite_courses: [],
entrance_exam_enabled : '',
- entrance_exam_minimum_score_pct: '50'
+ entrance_exam_minimum_score_pct: '50',
+ learning_info: [],
+ instructor_info: {}
},
validate: function(newattrs) {
diff --git a/cms/static/js/spec/views/settings/main_spec.js b/cms/static/js/spec/views/settings/main_spec.js
index 5c686f4ac9..e87d706c9e 100644
--- a/cms/static/js/spec/views/settings/main_spec.js
+++ b/cms/static/js/spec/views/settings/main_spec.js
@@ -1,13 +1,17 @@
define([
'jquery', 'js/models/settings/course_details', 'js/views/settings/main',
- 'common/js/spec_helpers/ajax_helpers'
-], function($, CourseDetailsModel, MainView, AjaxHelpers) {
+ 'common/js/spec_helpers/ajax_helpers', 'common/js/spec_helpers/template_helpers',
+], function($, CourseDetailsModel, MainView, AjaxHelpers, TemplateHelpers) {
'use strict';
var SELECTORS = {
entrance_exam_min_score: '#entrance-exam-minimum-score-pct',
entrance_exam_enabled_field: '#entrance-exam-enabled',
- grade_requirement_div: '.div-grade-requirements div'
+ grade_requirement_div: '.div-grade-requirements div',
+ add_course_learning_info: '.add-course-learning-info',
+ delete_course_learning_info: '.delete-course-learning-info',
+ add_course_instructor_info: '.add-course-instructor-info',
+ remove_instructor_data: '.remove-instructor-data'
};
describe('Settings/Main', function () {
@@ -39,14 +43,32 @@ define([
entrance_exam_enabled : '',
entrance_exam_minimum_score_pct: '50',
license: null,
- language: ''
+ language: '',
+ learning_info: [''],
+ instructor_info: {
+ 'instructors': [{"name": "","title": "","organization": "","image": "","bio": ""}]
+ }
},
- mockSettingsPage = readFixtures('mock/mock-settings-page.underscore');
+
+ mockSettingsPage = readFixtures('mock/mock-settings-page.underscore'),
+ learningInfoTpl = readFixtures('course-settings-learning-fields.underscore'),
+ instructorInfoTpl = readFixtures('course-instructor-details.underscore');
beforeEach(function () {
- setFixtures(mockSettingsPage);
+ TemplateHelpers.installTemplates(['course-settings-learning-fields', 'course-instructor-details'], true);
+ appendSetFixtures(mockSettingsPage);
+ appendSetFixtures(
+ $("
@@ -448,10 +448,44 @@ CMS.URL.UPLOAD_ASSET = '${upload_asset_url}'
${_("Enter your YouTube video's ID (along with any restriction parameters)")}
- % endif
+ % endif
+ % if enable_extended_course_details:
+
+
+
+ ${_("Learning Outcomes")}
+ ${_("Add the learning outcomes for this course")}
+
+
+
+
+
+
+
+
+
+
+
+
+ ${_("Instructors")}
+ ${_("Add details about the instructors for this course")}
+
+
+
+
+
+
+
+
+ % endif
+
% if about_page_editable or is_prerequisite_courses_enabled or is_entrance_exams_enabled:
diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py
index 6ed3385032..136d2f963b 100644
--- a/common/lib/xmodule/xmodule/course_module.py
+++ b/common/lib/xmodule/xmodule/course_module.py
@@ -794,6 +794,36 @@ class CourseFields(object):
scope=Scope.settings
)
+ learning_info = List(
+ display_name=_("Course Learning Information"),
+ help=_("Specify what student can learn from the course."),
+ default=[],
+ scope=Scope.settings
+ )
+
+ """
+ instructor_info dict structure:
+ {
+ "instructors": [
+ {
+ "name": "",
+ "title": "",
+ "organization": "",
+ "image": "",
+ "bio": ""
+ }
+ ]
+ }
+ """
+ instructor_info = Dict(
+ display_name=_("Course Instructor"),
+ help=_("Enter the details for Course Instructor"),
+ default={
+ "instructors": []
+ },
+ scope=Scope.settings
+ )
+
class CourseModule(CourseFields, SequenceModule): # pylint: disable=abstract-method
"""
diff --git a/common/test/acceptance/pages/studio/settings_advanced.py b/common/test/acceptance/pages/studio/settings_advanced.py
index 7f189a4cd9..87acaf1785 100644
--- a/common/test/acceptance/pages/studio/settings_advanced.py
+++ b/common/test/acceptance/pages/studio/settings_advanced.py
@@ -219,4 +219,6 @@ class AdvancedSettingsPage(CoursePage):
'enable_proctored_exams',
'enable_timed_exams',
'enable_subsection_gating',
+ 'learning_info',
+ 'instructor_info'
]
diff --git a/common/test/acceptance/tests/studio/test_studio_settings.py b/common/test/acceptance/tests/studio/test_studio_settings.py
index 9b67b5dcd4..3e63ee7167 100644
--- a/common/test/acceptance/tests/studio/test_studio_settings.py
+++ b/common/test/acceptance/tests/studio/test_studio_settings.py
@@ -571,42 +571,3 @@ class StudioSubsectionSettingsA11yTest(StudioCourseTest):
include=['section.edit-settings-timed-examination']
)
self.course_outline.a11y_audit.check_for_accessibility_errors()
-
-
-class StudioSettingsImageUploadTest(StudioCourseTest):
-
- """
- Class to test course settings image uploads.
- """
-
- def setUp(self): # pylint: disable=arguments-differ
- super(StudioSettingsImageUploadTest, self).setUp()
- self.settings_page = SettingsPage(self.browser, self.course_info['org'], self.course_info['number'],
- self.course_info['run'])
-
- def test_upload_course_card_image(self):
- self.settings_page.visit()
- self.settings_page.wait_for_page()
-
- # upload image
- file_to_upload = 'image.jpg'
- self.settings_page.upload_image('#upload-course-image', file_to_upload)
- self.assertIn(file_to_upload, self.settings_page.get_uploaded_image_path('#course-image'))
-
- def test_upload_course_banner_image(self):
- self.settings_page.visit()
- self.settings_page.wait_for_page()
-
- # upload image
- file_to_upload = 'image.jpg'
- self.settings_page.upload_image('#upload-banner-image', file_to_upload)
- self.assertIn(file_to_upload, self.settings_page.get_uploaded_image_path('#banner-image'))
-
- def test_upload_course_video_thumbnail_image(self):
- self.settings_page.visit()
- self.settings_page.wait_for_page()
-
- # upload image
- file_to_upload = 'image.jpg'
- self.settings_page.upload_image('#upload-video-thumbnail-image', file_to_upload)
- self.assertIn(file_to_upload, self.settings_page.get_uploaded_image_path('#video-thumbnail-image'))
diff --git a/openedx/core/djangoapps/models/course_details.py b/openedx/core/djangoapps/models/course_details.py
index c818642def..0db50a3803 100644
--- a/openedx/core/djangoapps/models/course_details.py
+++ b/openedx/core/djangoapps/models/course_details.py
@@ -70,6 +70,8 @@ class CourseDetails(object):
'50'
) # minimum passing score for entrance exam content module/tree,
self.self_paced = None
+ self.learning_info = []
+ self.instructor_info = []
@classmethod
def fetch_about_attribute(cls, course_key, attribute):
@@ -108,6 +110,8 @@ class CourseDetails(object):
course_details.video_thumbnail_image_asset_path = course_image_url(descriptor, 'video_thumbnail_image')
course_details.language = descriptor.language
course_details.self_paced = descriptor.self_paced
+ course_details.learning_info = descriptor.learning_info
+ course_details.instructor_info = descriptor.instructor_info
# Default course license is "All Rights Reserved"
course_details.license = getattr(descriptor, "license", "all-rights-reserved")
@@ -243,6 +247,14 @@ class CourseDetails(object):
descriptor.license = jsondict['license']
dirty = True
+ if 'learning_info' in jsondict:
+ descriptor.learning_info = jsondict['learning_info']
+ dirty = True
+
+ if 'instructor_info' in jsondict:
+ descriptor.instructor_info = jsondict['instructor_info']
+ dirty = True
+
if 'language' in jsondict and jsondict['language'] != descriptor.language:
descriptor.language = jsondict['language']
dirty = True
diff --git a/openedx/core/djangoapps/models/tests/test_course_details.py b/openedx/core/djangoapps/models/tests/test_course_details.py
index fd778ee10a..661ddfe456 100644
--- a/openedx/core/djangoapps/models/tests/test_course_details.py
+++ b/openedx/core/djangoapps/models/tests/test_course_details.py
@@ -110,6 +110,26 @@ class CourseDetailsTestCase(ModuleStoreTestCase):
CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).language,
jsondetails.language
)
+ jsondetails.learning_info = ["test", "test"]
+ self.assertEqual(
+ CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).learning_info,
+ jsondetails.learning_info
+ )
+ jsondetails.instructor_info = {
+ "instructors": [
+ {
+ "name": "test",
+ "title": "test",
+ "organization": "test",
+ "image": "test",
+ "bio": "test"
+ }
+ ]
+ }
+ self.assertEqual(
+ CourseDetails.update_from_json(self.course.id, jsondetails.__dict__, self.user).instructor_info,
+ jsondetails.instructor_info
+ )
def test_toggle_pacing_during_course_run(self):
SelfPacedConfiguration(enabled=True).save()