diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 206be44c87..f865c09840 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 8c15b1ae95..6b8622f992 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 @@ from xmodule.fields import Date 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 @@ class CourseTestCase(ModuleStoreTestCase): 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 @@ class CourseDetailsTestCase(CourseTestCase): 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 @@ class CourseDetailsTestCase(CourseTestCase): 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 @@ class CourseDetailsViewTest(CourseTestCase): 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 @@ class CourseGradingTest(CourseTestCase): 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 8a29a637b8..dd7573bad5 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 7fcb218282..a9a3e16128 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 735774511f..cbb1034626 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 2adc0cd980..14c79e586a 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 @@ from contentstore import utils

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

@@ -62,59 +64,68 @@ from contentstore import utils
-

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 @@ from contentstore import utils
  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 @@ from contentstore import utils
  2. - + - Last day students can enroll + ${_("Last day students can enroll")}
    - +
-
+ % endif + % 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 +
+ % if about_page_editable: +
+
+

${_("Introducing Your Course")}

+ ${_("Information for prospective students")} +
-
-
-

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. -
      -
    1. - - - Introductions, prerequisites, FAQs that are used on your course summary page (formatted in HTML) -
    2. +
    3. + + -
    4. - -
      -
      - -
      - -
      +
      + + ${_("Enter your YouTube video's ID (along with any restriction parameters)")} +
      +
    5. +
    +
-
- - Enter your YouTube video's ID (along with any restriction parameters) -
- - -
+
-
+
+
+

${_("Requirements")}

+ ${_("Expectations of the students taking this course")} +
-
-
-

Requirements

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