diff --git a/cms/djangoapps/contentstore/features/course-settings.feature b/cms/djangoapps/contentstore/features/course-settings.feature
index 0aeb95dbd9..ca8b3ec4cb 100644
--- a/cms/djangoapps/contentstore/features/course-settings.feature
+++ b/cms/djangoapps/contentstore/features/course-settings.feature
@@ -79,7 +79,6 @@ Feature: CMS.Course Settings
| Course Start Time | 11:00 |
| Course Introduction Video | 4r7wHMg5Yjg |
| Course Effort | 200:00 |
- | Course Image URL | image.jpg |
# Special case because we have to type in code mirror
Scenario: Changes in Course Overview show a confirmation
@@ -94,11 +93,3 @@ Feature: CMS.Course Settings
When I select Schedule and Details
And I change the "Course Start Date" field to ""
Then the save notification button is disabled
-
- Scenario: User can upload course image
- Given I have opened a new course in Studio
- When I select Schedule and Details
- And I click the "Upload Course Image" button
- And I upload a new course image
- Then I should see the new course image
- And the image URL should be present in the field
diff --git a/cms/djangoapps/contentstore/features/course-settings.py b/cms/djangoapps/contentstore/features/course-settings.py
index 4e11b393d1..350eb0392e 100644
--- a/cms/djangoapps/contentstore/features/course-settings.py
+++ b/cms/djangoapps/contentstore/features/course-settings.py
@@ -132,37 +132,6 @@ def test_change_course_overview(_step):
type_in_codemirror(0, "
Overview
")
-@step('I click the "Upload Course Image" button')
-def click_upload_button(_step):
- button_css = '.action-upload-image'
- world.css_click(button_css)
-
-
-@step('I upload a new course image$')
-def upload_new_course_image(_step):
- upload_file('image.jpg', sub_path="uploads")
-
-
-@step('I should see the new course image$')
-def i_see_new_course_image(_step):
- img_css = '#course-image'
- images = world.css_find(img_css)
- assert len(images) == 1
- img = images[0]
- expected_src = 'image.jpg'
-
- # Don't worry about the domain in the URL
- success_func = lambda _: img['src'].endswith(expected_src)
- world.wait_for(success_func)
-
-
-@step('the image URL should be present in the field')
-def image_url_present(_step):
- field_css = '#course-image-url'
- expected_value = 'image.jpg'
- assert world.css_value(field_css).endswith(expected_value)
-
-
############### HELPER METHODS ####################
def set_date_or_time(css, date_or_time):
"""
diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py
index 99dff6acd8..5171d59059 100644
--- a/cms/djangoapps/contentstore/tests/test_course_settings.py
+++ b/cms/djangoapps/contentstore/tests/test_course_settings.py
@@ -255,11 +255,17 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
self.assertContains(response, "not the dates shown on your course summary page")
self.assertContains(response, "Introducing Your Course")
- self.assertContains(response, "Course Image")
+ self.assertContains(response, "Course Card Image")
self.assertContains(response, "Course Short Description")
+ self.assertNotContains(response, "Course Title")
+ self.assertNotContains(response, "Course Subtitle")
+ self.assertNotContains(response, "Course Duration")
+ self.assertNotContains(response, "Course Description")
self.assertNotContains(response, "Course Overview")
self.assertNotContains(response, "Course Introduction Video")
self.assertNotContains(response, "Requirements")
+ self.assertNotContains(response, "Course Banner Image")
+ self.assertNotContains(response, "Course Video Thumbnail Image")
@unittest.skipUnless(settings.FEATURES.get('ENTRANCE_EXAMS', False), True)
def test_entrance_exam_created_updated_and_deleted_successfully(self):
@@ -367,7 +373,8 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
def test_regular_site_fetch(self):
settings_details_url = get_url(self.course.id)
- with mock.patch.dict('django.conf.settings.FEATURES', {'ENABLE_MKTG_SITE': False}):
+ with mock.patch.dict('django.conf.settings.FEATURES', {'ENABLE_MKTG_SITE': False,
+ 'ENABLE_EXTENDED_COURSE_DETAILS': True}):
response = self.client.get_html(settings_details_url)
self.assertContains(response, "Course Summary Page")
self.assertContains(response, "Send a note to students via email")
@@ -380,11 +387,17 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
self.assertNotContains(response, "not the dates shown on your course summary page")
self.assertContains(response, "Introducing Your Course")
- self.assertContains(response, "Course Image")
+ self.assertContains(response, "Course Card Image")
+ self.assertContains(response, "Course Title")
+ self.assertContains(response, "Course Subtitle")
+ self.assertContains(response, "Course Duration")
+ self.assertContains(response, "Course Description")
self.assertContains(response, "Course Short Description")
self.assertContains(response, "Course Overview")
self.assertContains(response, "Course Introduction Video")
self.assertContains(response, "Requirements")
+ self.assertContains(response, "Course Banner Image")
+ self.assertContains(response, "Course Video Thumbnail Image")
@ddt.ddt
diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py
index 530b2f9d6b..476cab45da 100644
--- a/cms/djangoapps/contentstore/views/course.py
+++ b/cms/djangoapps/contentstore/views/course.py
@@ -976,18 +976,24 @@ def settings_handler(request, course_key_string):
'ENABLE_MKTG_SITE',
settings.FEATURES.get('ENABLE_MKTG_SITE', False)
)
+ enable_extended_course_details = microsite.get_value_for_org(
+ course_module.location.org,
+ 'ENABLE_EXTENDED_COURSE_DETAILS',
+ settings.FEATURES.get('ENABLE_EXTENDED_COURSE_DETAILS', False)
+ )
about_page_editable = not marketing_site_enabled
enrollment_end_editable = GlobalStaff().has_user(request.user) or not marketing_site_enabled
short_description_editable = settings.FEATURES.get('EDITABLE_SHORT_DESCRIPTION', True)
-
self_paced_enabled = SelfPacedConfiguration.current().enabled
settings_context = {
'context_course': course_module,
'course_locator': course_key,
'lms_link_for_about_page': utils.get_lms_link_for_about_page(course_key),
- 'course_image_url': course_image_url(course_module),
+ 'course_image_url': course_image_url(course_module, 'course_image'),
+ 'banner_image_url': course_image_url(course_module, 'banner_image'),
+ 'video_thumbnail_image_url': course_image_url(course_module, 'video_thumbnail_image'),
'details_url': reverse_course_url('settings_handler', course_key),
'about_page_editable': about_page_editable,
'short_description_editable': short_description_editable,
@@ -1001,6 +1007,7 @@ def settings_handler(request, course_key_string):
'is_prerequisite_courses_enabled': is_prerequisite_courses_enabled(),
'is_entrance_exams_enabled': is_entrance_exams_enabled(),
'self_paced_enabled': self_paced_enabled,
+ 'enable_extended_course_details': enable_extended_course_details
}
if is_prerequisite_courses_enabled():
courses, in_process_course_actions = get_courses_accessible_to_user(request)
diff --git a/cms/envs/bok_choy.env.json b/cms/envs/bok_choy.env.json
index 14115e3612..2e9c6086b6 100644
--- a/cms/envs/bok_choy.env.json
+++ b/cms/envs/bok_choy.env.json
@@ -76,7 +76,8 @@
"ALLOW_ALL_ADVANCED_COMPONENTS": true,
"ENABLE_CONTENT_LIBRARIES": true,
"ENABLE_SPECIAL_EXAMS": true,
- "SHOW_LANGUAGE_SELECTOR": true
+ "SHOW_LANGUAGE_SELECTOR": true,
+ "ENABLE_EXTENDED_COURSE_DETAILS": true
},
"FEEDBACK_SUBMISSION_EMAIL": "",
"GITHUB_REPO_ROOT": "** OVERRIDDEN **",
diff --git a/cms/static/js/models/settings/course_details.js b/cms/static/js/models/settings/course_details.js
index 99db4d68a1..202d1097e7 100644
--- a/cms/static/js/models/settings/course_details.js
+++ b/cms/static/js/models/settings/course_details.js
@@ -12,6 +12,10 @@ var CourseDetails = Backbone.Model.extend({
enrollment_start: null,
enrollment_end: null,
syllabus: null,
+ title: "",
+ subtitle: "",
+ duration: "",
+ description: "",
short_description: "",
overview: "",
intro_video: null,
@@ -19,9 +23,15 @@ var CourseDetails = Backbone.Model.extend({
license: null,
course_image_name: '', // the filename
course_image_asset_path: '', // the full URL (/c4x/org/course/num/asset/filename)
+ banner_image_name: '',
+ banner_image_asset_path: '',
+ video_thumbnail_image_name: '',
+ 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) {
@@ -32,9 +42,30 @@ var CourseDetails = Backbone.Model.extend({
newattrs, ["start_date", "end_date", "enrollment_start", "enrollment_end"]
);
+ if (newattrs.title.length > 50) {
+ errors.title = gettext("The title field must be limited to 50 characters.");
+ }
+
+ if (newattrs.subtitle.length > 150) {
+ errors.subtitle = gettext("The subtitle field must be limited to 150 characters.");
+ }
+
+ if (newattrs.duration.length > 50) {
+ errors.duration = gettext("The duration field must be limited to 50 characters.");
+ }
+
+ if (newattrs.short_description.length > 150) {
+ errors.short_description = gettext("The short description field must be limited to 150 characters.");
+ }
+
+ if (newattrs.description.length > 1000) {
+ errors.description = gettext("The description field must be limited to 1000 characters.");
+ }
+
if (newattrs.start_date === null) {
errors.start_date = gettext("The course must have an assigned start date.");
}
+
if (newattrs.start_date && newattrs.end_date && newattrs.start_date >= newattrs.end_date) {
errors.end_date = gettext("The course end date must be later than the course start date.");
}
diff --git a/cms/static/js/spec/views/settings/main_spec.js b/cms/static/js/spec/views/settings/main_spec.js
index f39d669c5d..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 () {
@@ -21,24 +25,50 @@ define([
course_id : '',
run : '',
syllabus : null,
+ title: '',
+ subtitle: '',
+ duration: '',
+ description: '',
short_description : '',
overview : '',
intro_video : null,
effort : null,
course_image_name : '',
course_image_asset_path : '',
+ banner_image_name : '',
+ banner_image_asset_path : '',
+ video_thumbnail_image_name : '',
+ video_thumbnail_image_asset_path : '',
pre_requisite_courses : [],
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(
+ $("
@@ -27,7 +27,7 @@
%block>
@@ -292,9 +292,33 @@ CMS.URL.UPLOAD_ASSET = '${upload_asset_url}';
${_("Information for prospective students")}
+
+ % if enable_extended_course_details:
+
+
+
+ ${_("Displayed as title on the course details page. Limit to 50 characters.")}
+
+
+
+
+ ${_("Displayed as subtitle on the course details page. Limit to 150 characters.")}
+
+
+
+
+ ${_("Displayed on the course details page. Limit to 50 characters.")}
+
+
+
+
+ ${_("Displayed on the course details page. Limit to 1000 characters.")}
+
+ % endif
+
% if short_description_editable:
-
+
${_("Appears on the course catalog page when students roll over the course name. Limit to ~150 characters")}
% if context_course.course_image:
-
+
@@ -328,7 +352,7 @@ CMS.URL.UPLOAD_ASSET = '${upload_asset_url}';
% else:
-
+ ${_("Your course currently does not have an image. Please upload one (JPEG or PNG format, and minimum suggested dimensions are 375px wide by 200px tall)")}
% endif
@@ -340,13 +364,75 @@ CMS.URL.UPLOAD_ASSET = '${upload_asset_url}';
${_("Please provide a valid path and name to your course image (Note: only JPEG or PNG format supported)")}
-
+
+ % if enable_extended_course_details:
+
+
+
+ % if context_course.banner_image:
+
+
+
+
+
+ ${_("You can manage this image along with all of your other files & uploads").format(upload_asset_url)}
+
+
+ % else:
+
+
+
+ ${_("Your course currently does not have an image. Please upload one (JPEG or PNG format, and minimum suggested dimensions are 1440px wide by 400px tall)")}
+ % endif
+
+
+
+
+ ## Translators: This is the placeholder text for a field that requests the URL for a course banner image
+
+ ${_("Please provide a valid path and name to your banner image (Note: only JPEG or PNG format supported)")}
+
+
+
+
+
+
+
+
+ % if context_course.video_thumbnail_image:
+
+
+
+
+
+ ${_("You can manage this image along with all of your other files & uploads").format(upload_asset_url)}
+
+
+ % else:
+
+
+
+ ${_("Your course currently does not have a video thumbnail image. Please upload one (JPEG or PNG format, and minimum suggested dimensions are 375px wide by 200px tall)")}
+ % endif
+
+
+
+
+ ## Translators: This is the placeholder text for a field that requests the URL for a course video thumbnail image
+
+ ${_("Please provide a valid path and name to your video thumbnail image (Note: only JPEG or PNG format supported)")}
+
+
+
+
+ % endif
+
% if about_page_editable:
-
+
@@ -362,10 +448,44 @@ CMS.URL.UPLOAD_ASSET = '${upload_asset_url}';
${_("Enter your YouTube video's ID (along with any restriction parameters)")}