diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9c405ed365a7..3bc3b6b9bf49 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,6 +11,9 @@ XModule: Don't delete generated xmodule asset files when compiling (for instance, when XModule provides a coffeescript file, don't delete the associated javascript) +Studio: For courses running on edx.org (marketing site), disable fields in +Course Settings that do not apply. + Common: Make asset watchers run as singletons (so they won't start if the watcher is already running in another shell). diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index 8c15b1ae95e6..6b8622f9926e 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -1,11 +1,16 @@ +""" +Tests for Studio Course Settings. +""" import datetime import json import copy +import mock from django.contrib.auth.models import User from django.test.client import Client from django.core.urlresolvers import reverse from django.utils.timezone import UTC +from django.test.utils import override_settings from xmodule.modulestore import Location from models.settings.course_details import (CourseDetails, CourseSettingsEncoder) @@ -21,6 +26,9 @@ class CourseTestCase(ModuleStoreTestCase): + """ + Base class for test classes below. + """ def setUp(self): """ These tests need a user in the DB so that the django Test Client @@ -51,6 +59,9 @@ def setUp(self): class CourseDetailsTestCase(CourseTestCase): + """ + Tests the first course settings page (course dates, overview, etc.). + """ def test_virgin_fetch(self): details = CourseDetails.fetch(self.course_location) self.assertEqual(details.course_location, self.course_location, "Location not copied into") @@ -81,9 +92,9 @@ def test_ooc_encoder(self): Test the encoder out of its original constrained purpose to see if it functions for general use """ details = {'location': Location(['tag', 'org', 'course', 'category', 'name']), - 'number': 1, - 'string': 'string', - 'datetime': datetime.datetime.now(UTC())} + 'number': 1, + 'string': 'string', + 'datetime': datetime.datetime.now(UTC())} jsondetails = json.dumps(details, cls=CourseSettingsEncoder) jsondetails = json.loads(jsondetails) @@ -118,8 +129,50 @@ def test_update_and_fetch(self): jsondetails.effort, "After set effort" ) + @override_settings(MKTG_URLS={'ROOT': 'dummy-root'}) + def test_marketing_site_fetch(self): + settings_details_url = reverse('settings_details', + kwargs={'org': self.course_location.org, 'name': self.course_location.name, + 'course': self.course_location.course}) + + with mock.patch.dict('django.conf.settings.MITX_FEATURES', {'ENABLE_MKTG_SITE': True}): + response = self.client.get(settings_details_url) + self.assertContains(response, "Course Summary Page") + self.assertContains(response, "course summary page will not be viewable") + + self.assertContains(response, "Course Start Date") + self.assertContains(response, "Course End Date") + self.assertNotContains(response, "Enrollment Start Date") + self.assertNotContains(response, "Enrollment End Date") + self.assertContains(response, "not the dates shown on your course summary page") + + self.assertNotContains(response, "Introducing Your Course") + self.assertNotContains(response, "Requirements") + + def test_regular_site_fetch(self): + settings_details_url = reverse('settings_details', + kwargs={'org': self.course_location.org, 'name': self.course_location.name, + 'course': self.course_location.course}) + + with mock.patch.dict('django.conf.settings.MITX_FEATURES', {'ENABLE_MKTG_SITE': False}): + response = self.client.get(settings_details_url) + self.assertContains(response, "Course Summary Page") + self.assertNotContains(response, "course summary page will not be viewable") + + self.assertContains(response, "Course Start Date") + self.assertContains(response, "Course End Date") + self.assertContains(response, "Enrollment Start Date") + self.assertContains(response, "Enrollment End Date") + self.assertNotContains(response, "not the dates shown on your course summary page") + + self.assertContains(response, "Introducing Your Course") + self.assertContains(response, "Requirements") + class CourseDetailsViewTest(CourseTestCase): + """ + Tests for modifying content on the first course settings page (course dates, overview, etc.). + """ def alter_field(self, url, details, field, val): setattr(details, field, val) # Need to partially serialize payload b/c the mock doesn't handle it correctly @@ -181,6 +234,9 @@ def compare_date_fields(self, details, encoded, context, field): class CourseGradingTest(CourseTestCase): + """ + Tests for the course settings grading page. + """ def test_initial_grader(self): descriptor = get_modulestore(self.course_location).get_item(self.course_location) test_grader = CourseGradingModel(descriptor) @@ -256,6 +312,9 @@ def test_update_grader_from_json(self): class CourseMetadataEditingTest(CourseTestCase): + """ + Tests for CourseMetadata. + """ def setUp(self): CourseTestCase.setUp(self) # add in the full class too diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 8a29a637b896..dd7573bad5fc 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -227,7 +227,8 @@ def get_course_settings(request, org, course, name): kwargs={"org": org, "course": course, "name": name, - "section": "details"}) + "section": "details"}), + 'about_page_editable': not settings.MITX_FEATURES.get('ENABLE_MKTG_SITE', False) }) diff --git a/cms/static/sass/elements/_system-help.scss b/cms/static/sass/elements/_system-help.scss index 7fcb21828278..a9a3e16128a1 100644 --- a/cms/static/sass/elements/_system-help.scss +++ b/cms/static/sass/elements/_system-help.scss @@ -1,2 +1,40 @@ // studio - elements - system help // ==================== + +// notices - in-context: to be used as notices to users within the context of a form/action +.notice-incontext { + @extend .ui-well; + @include border-radius(($baseline/10)); + + .title { + @extend .t-title7; + margin-bottom: ($baseline/4); + font-weight: 600; + } + + .copy { + @extend .t-copy-sub1; + @include transition(opacity 0.25s ease-in-out 0); + opacity: 0.75; + } + + strong { + font-weight: 600; + } + + &:hover { + + .copy { + opacity: 1.0; + } + } +} + +// particular warnings around a workflow for something +.notice-workflow { + background: $yellow-l5; + + .copy { + color: $gray-d1; + } +} diff --git a/cms/static/sass/views/_settings.scss b/cms/static/sass/views/_settings.scss index 735774511f41..cbb10346269a 100644 --- a/cms/static/sass/views/_settings.scss +++ b/cms/static/sass/views/_settings.scss @@ -21,7 +21,7 @@ body.course.settings { font-size: 14px; } - .message-status { + .message-status { display: none; @include border-top-radius(2px); @include box-sizing(border-box); @@ -52,6 +52,12 @@ body.course.settings { } } + // notices - used currently for edx mktg + .notice-workflow { + margin-top: ($baseline); + } + + // in form - elements .group-settings { margin: 0 0 ($baseline*2) 0; diff --git a/cms/templates/settings.html b/cms/templates/settings.html index 2adc0cd980b0..14c79e586afc 100644 --- a/cms/templates/settings.html +++ b/cms/templates/settings.html @@ -1,3 +1,5 @@ +<%! from django.utils.translation import ugettext as _ %> + <%inherit file="base.html" /> <%block name="title">Schedule & Details Settings <%block name="bodyclass">is-signedin course schedule settings @@ -50,8 +52,8 @@

- Settings - > Schedule & Details + ${_("Settings")} + > ${_("Schedule & Details")}

@@ -62,59 +64,68 @@

-

Basic Information

- The nuts and bolts of your course +

${_("Basic Information")}

+ ${_("The nuts and bolts of your course")}
  1. - - + +
  2. - - + +
  3. - - + +
-

Course Summary Page (for student enrollment and access)

+

${_("Course Summary Page")} ${_("(for student enrollment and access)")}

+ + % if not about_page_editable: +
+

${_("Promoting Your Course with edX")}

+
+

${_('Your course summary page will not be viewable until your course has been announced. To provide content for the page and preview it, follow the instructions provided by your PM or Conrad Warre (conrad@edx.org).')}

+
+
+ % endif

-

Course Schedule

- Important steps and segments of your course +

${_('Course Schedule')}

+ ${_('Dates that control when your course can be viewed.')}
  1. - + - First day the course begins + ${_("First day the course begins")}
    - +
    @@ -122,29 +133,30 @@

    Course Schedule

  2. - + - Last day your course is active + ${_("Last day your course is active")}
    - +
+ % if about_page_editable:
  1. - + - First day students can enroll + ${_("First day students can enroll")}
    - +
    @@ -152,91 +164,106 @@

    Course Schedule

  2. - + - Last day students can enroll + ${_("Last day students can enroll")}
    - +
-
- -
- -
-
-

Introducing Your Course

- Information for prospective students -
- -
    -
  1. - - - Introductions, prerequisites, FAQs that are used on your course summary page (formatted in HTML) -
  2. - -
  3. - -
    -
    - -
    - -
    + % endif -
    - - Enter your YouTube video's ID (along with any restriction parameters) -
    -
  4. -
+ % if not about_page_editable: +
+

${_("These Dates Are Not Used When Promoting Your Course")}

+
+

${_('These dates impact when your courseware can be viewed, but they are not the dates shown on your course summary page. To provide the course start and registration dates as shown on your course summary page, follow the instructions provided by your PM or Conrad Warre (conrad@edx.org).')}

+
+
+ % endif
-
- -
-
-

Requirements

- Expectations of the students taking this course -
- -
    -
  1. - - - Time spent on all course work -
  2. -
-
+ % if about_page_editable: +
+
+

${_("Introducing Your Course")}

+ ${_("Information for prospective students")} +
+ +
    +
  1. + + + <%def name='overview_text()'><% + a_link_start = '' + _("your course summary page") + '' + a_link = a_link_start + utils.get_lms_link_for_about_page(course_location) + a_link_end + text = _("Introductions, prerequisites, FAQs that are used on %s (formatted in HTML)") % a_link + %>${text} + ${overview_text()} +
  2. + +
  3. + + + +
    + + ${_("Enter your YouTube video's ID (along with any restriction parameters)")} +
    +
  4. +
+
+ +
+ +
+
+

${_("Requirements")}

+ ${_("Expectations of the students taking this course")} +
+ +
    +
  1. + + + ${_("Time spent on all course work")} +
  2. +
+
+ % endif
-