diff --git a/cms/djangoapps/contentstore/views/course.py b/cms/djangoapps/contentstore/views/course.py index 60d88027d3..5d36db91fc 100644 --- a/cms/djangoapps/contentstore/views/course.py +++ b/cms/djangoapps/contentstore/views/course.py @@ -16,8 +16,10 @@ from django.core.exceptions import PermissionDenied from django.core.urlresolvers import reverse from django.http import HttpResponseBadRequest, HttpResponseNotFound, HttpResponse from util.json_request import JsonResponse +from util.date_utils import get_default_time_display from edxmako.shortcuts import render_to_response +from xmodule.course_module import DEFAULT_START_DATE from xmodule.error_module import ErrorDescriptor from xmodule.modulestore.django import modulestore from xmodule.contentstore.content import StaticContent @@ -376,6 +378,8 @@ def course_index(request, course_key): sections = course_module.get_children() course_structure = _course_outline_json(request, course_module) locator_to_show = request.REQUEST.get('show', None) + course_release_date = get_default_time_display(course_module.start) if course_module.start != DEFAULT_START_DATE else _("Unscheduled") + settings_url = reverse_course_url('settings_handler', course_key) try: current_action = CourseRerunState.objects.find_first(course_key=course_key, should_display=True) @@ -392,6 +396,8 @@ def course_index(request, course_key): CourseGradingModel.fetch(course_key).graders ), 'rerun_notification_id': current_action.id if current_action else None, + 'course_release_date': course_release_date, + 'settings_url': settings_url, }) diff --git a/cms/djangoapps/contentstore/views/tests/test_course_index.py b/cms/djangoapps/contentstore/views/tests/test_course_index.py index e92ef9053d..cf9d99da39 100644 --- a/cms/djangoapps/contentstore/views/tests/test_course_index.py +++ b/cms/djangoapps/contentstore/views/tests/test_course_index.py @@ -3,6 +3,7 @@ Unit tests for getting the list of courses and the course outline. """ import json import lxml +import datetime from contentstore.tests.utils import CourseTestCase from contentstore.utils import reverse_course_url, add_instructor @@ -10,6 +11,8 @@ from contentstore.views.access import has_course_access from contentstore.views.course import course_outline_initial_state from contentstore.views.item import create_xblock_info, VisibilityState from course_action_state.models import CourseRerunState +from util.date_utils import get_default_time_display +from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from opaque_keys.edx.locator import CourseLocator @@ -273,3 +276,35 @@ class TestCourseOutline(CourseTestCase): expanded_locators = initial_state['expanded_locators'] self.assertIn(unicode(self.sequential.location), expanded_locators) self.assertIn(unicode(self.vertical.location), expanded_locators) + + def test_start_date_on_page(self): + """ + Verify that the course start date is included on the course outline page. + """ + def _get_release_date(response): + """Return the release date from the course page""" + parsed_html = lxml.html.fromstring(response.content) + return parsed_html.find_class('course-status')[0].find_class('status-release-value')[0].text_content() + + def _assert_settings_link_present(response): + """ + Asserts there's a course settings link on the course page by the course release date. + """ + parsed_html = lxml.html.fromstring(response.content) + settings_link = parsed_html.find_class('course-status')[0].find_class('action-edit')[0].find('a') + self.assertIsNotNone(settings_link) + self.assertEqual(settings_link.get('href'), reverse_course_url('settings_handler', self.course.id)) + + outline_url = reverse_course_url('course_handler', self.course.id) + response = self.client.get(outline_url, {}, HTTP_ACCEPT='text/html') + + # A course with the default release date should display as "Unscheduled" + self.assertEqual(_get_release_date(response), 'Unscheduled') + _assert_settings_link_present(response) + + self.course.start = datetime.datetime(2014, 1, 1) + modulestore().update_item(self.course, ModuleStoreEnum.UserID.test) + response = self.client.get(outline_url, {}, HTTP_ACCEPT='text/html') + + self.assertEqual(_get_release_date(response), get_default_time_display(self.course.start)) + _assert_settings_link_present(response) diff --git a/cms/static/sass/views/_outline.scss b/cms/static/sass/views/_outline.scss index fa2d382181..0a909ddf5b 100644 --- a/cms/static/sass/views/_outline.scss +++ b/cms/static/sass/views/_outline.scss @@ -171,6 +171,49 @@ @extend %expand-collapse; } + // course status + // -------------------- + .course-status { + margin-bottom: $baseline; + + .status-release { + @extend %t-copy-base; + display: inline-block; + color: $color-copy-base; + } + + .status-release-label, + .status-release-value, + .status-actions { + display: inline-block; + vertical-align: middle; + margin-bottom: 0; + } + + .status-release-label { + margin-right: ($baseline/4); + } + + .status-release-value { + @extend %t-strong; + } + + .status-actions { + @extend %actions-list; + @include transition(opacity $tmg-f1 ease-in-out 0); + margin-left: ($baseline/4); + opacity: 0.0; + } + + // STATE: hover + &:hover { + + .status-actions { + opacity: 1.0; + } + } + } + // outline // -------------------- .outline { diff --git a/cms/templates/course_outline.html b/cms/templates/course_outline.html index 6c928e0942..f3ffe0e6b6 100644 --- a/cms/templates/course_outline.html +++ b/cms/templates/course_outline.html @@ -70,10 +70,27 @@ from contentstore.utils import reverse_usage_url