diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index 2d684a7a84..c9d43eb6a6 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -245,6 +245,10 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin): self.assertContains(response, "Introducing Your Course") self.assertContains(response, "Course 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") @@ -355,7 +359,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") @@ -369,6 +374,10 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin): self.assertContains(response, "Introducing Your Course") self.assertContains(response, "Course 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") diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index aa60bcadca..0fcf6714c5 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -976,11 +976,15 @@ 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 = { @@ -1001,6 +1005,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/static/js/models/settings/course_details.js b/cms/static/js/models/settings/course_details.js index 99db4d68a1..8742a8fe67 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, @@ -32,9 +36,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..202ff4cf0d 100644 --- a/cms/static/js/spec/views/settings/main_spec.js +++ b/cms/static/js/spec/views/settings/main_spec.js @@ -21,6 +21,10 @@ define([ course_id : '', run : '', syllabus : null, + title: '', + subtitle: '', + duration: '', + description: '', short_description : '', overview : '', intro_video : null, diff --git a/cms/static/js/views/settings/main.js b/cms/static/js/views/settings/main.js index 82703ac734..4bdefc23e6 100644 --- a/cms/static/js/views/settings/main.js +++ b/cms/static/js/views/settings/main.js @@ -71,6 +71,16 @@ var DetailsView = ValidatingView.extend({ this.$el.find('#' + this.fieldToSelectorMap['overview']).val(this.model.get('overview')); this.codeMirrorize(null, $('#course-overview')[0]); + if (this.model.get('title') !== '') { + this.$el.find('#' + this.fieldToSelectorMap.title).val(this.model.get('title')); + } else { + var displayName = this.$el.find('#' + this.fieldToSelectorMap.title).attr('data-display-name'); + this.$el.find('#' + this.fieldToSelectorMap.title).val(displayName); + } + this.$el.find('#' + this.fieldToSelectorMap.subtitle).val(this.model.get('subtitle')); + this.$el.find('#' + this.fieldToSelectorMap.duration).val(this.model.get('duration')); + this.$el.find('#' + this.fieldToSelectorMap.description).val(this.model.get('description')); + this.$el.find('#' + this.fieldToSelectorMap['short_description']).val(this.model.get('short_description')); this.$el.find('.current-course-introduction-video iframe').attr('src', this.model.videosourceSample()); @@ -126,6 +136,10 @@ var DetailsView = ValidatingView.extend({ 'enrollment_start' : 'enrollment-start', 'enrollment_end' : 'enrollment-end', 'overview' : 'course-overview', + 'title': 'course-title', + 'subtitle': 'course-subtitle', + 'duration': 'course-duration', + 'description': 'course-description', 'short_description' : 'course-short-description', 'intro_video' : 'course-introduction-video', 'effort' : "course-effort", @@ -194,9 +208,6 @@ var DetailsView = ValidatingView.extend({ updateModel: function(event) { switch (event.currentTarget.id) { - case 'course-language': - this.setField(event); - break; case 'course-image-url': this.setField(event); var url = $(event.currentTarget).val(); @@ -208,9 +219,6 @@ var DetailsView = ValidatingView.extend({ $('#course-image').attr('src', $(event.currentTarget).val()); }, 1000); break; - case 'course-effort': - this.setField(event); - break; case 'entrance-exam-enabled': if($(event.currentTarget).is(":checked")){ this.$('.div-grade-requirements').show(); @@ -228,9 +236,6 @@ var DetailsView = ValidatingView.extend({ this.setField(event); } break; - case 'course-short-description': - this.setField(event); - break; case 'pre-requisite-course': var value = $(event.currentTarget).val(); value = value == "" ? [] : [value]; @@ -257,6 +262,15 @@ var DetailsView = ValidatingView.extend({ case 'course-pace-instructor-paced': this.model.set('self_paced', JSON.parse(event.currentTarget.value)); break; + case 'course-language': + case 'course-effort': + case 'course-title': + case 'course-subtitle': + case 'course-duration': + case 'course-description': + case 'course-short-description': + this.setField(event); + break; default: // Everything else is handled by datepickers and CodeMirror. break; } diff --git a/cms/templates/settings.html b/cms/templates/settings.html index c10e9ebc8b..f5df1caddb 100644 --- a/cms/templates/settings.html +++ b/cms/templates/settings.html @@ -27,7 +27,7 @@ %block> @@ -292,9 +292,33 @@ CMS.URL.UPLOAD_ASSET = '${upload_asset_url}'; ${_("Information for prospective students")}