diff --git a/common/lib/xmodule/xmodule/contentstore/mongo.py b/common/lib/xmodule/xmodule/contentstore/mongo.py index 68261e1188..8894c9d176 100644 --- a/common/lib/xmodule/xmodule/contentstore/mongo.py +++ b/common/lib/xmodule/xmodule/contentstore/mongo.py @@ -147,7 +147,15 @@ class MongoContentStore(ContentStore): for asset in assets: asset_location = AssetLocation._from_deprecated_son(asset['_id'], course_key.run) # pylint: disable=protected-access - self.export(asset_location, output_directory) + # TODO: On 6/19/14, I had to put a try/except around this + # to export a course. The course failed on JSON files in + # the /static/ directory placed in it with an import. + # + # If this hasn't been looked at in a while, remove this comment. + # + # When debugging course exports, this might be a good place + # to look. -- pmitros + self.export(asset_location, output_directory) for attr, value in asset.iteritems(): if attr not in ['_id', 'md5', 'uploadDate', 'length', 'chunkSize']: policy.setdefault(asset_location.name, {})[attr] = value diff --git a/lms/djangoapps/courseware/tests/test_navigation.py b/lms/djangoapps/courseware/tests/test_navigation.py index e2e2cf5574..e447ee1657 100644 --- a/lms/djangoapps/courseware/tests/test_navigation.py +++ b/lms/djangoapps/courseware/tests/test_navigation.py @@ -28,17 +28,38 @@ class TestNavigation(ModuleStoreTestCase, LoginEnrollmentTestCase): self.test_course = CourseFactory.create(display_name='Robot_Sub_Course') self.course = CourseFactory.create(display_name='Robot_Super_Course') - self.chapter0 = ItemFactory.create(parent_location=self.course.location, + self.chapter0 = ItemFactory.create(parent=self.course, display_name='Overview') - self.chapter9 = ItemFactory.create(parent_location=self.course.location, + self.chapter9 = ItemFactory.create(parent=self.course, display_name='factory_chapter') - self.section0 = ItemFactory.create(parent_location=self.chapter0.location, + self.section0 = ItemFactory.create(parent=self.chapter0, display_name='Welcome') - self.section9 = ItemFactory.create(parent_location=self.chapter9.location, + self.section9 = ItemFactory.create(parent=self.chapter9, display_name='factory_section') - self.unit0 = ItemFactory.create(parent_location=self.section0.location, + self.unit0 = ItemFactory.create(parent=self.section0, display_name='New Unit') + self.chapterchrome = ItemFactory.create(parent=self.course, + display_name='Chrome') + self.chromelesssection = ItemFactory.create(parent=self.chapterchrome, + display_name='chromeless', + chrome='none') + self.accordionsection = ItemFactory.create(parent=self.chapterchrome, + display_name='accordion', + chrome='accordion') + self.tabssection = ItemFactory.create(parent=self.chapterchrome, + display_name='tabs', + chrome='tabs') + self.defaultchromesection = ItemFactory.create(parent=self.chapterchrome, + display_name='defaultchrome') + self.fullchromesection = ItemFactory.create(parent=self.chapterchrome, + display_name='fullchrome', + chrome='accordion,tabs') + self.tabtest = ItemFactory.create(parent=self.chapterchrome, + display_name='progress_tab', + default_tab = 'progress') + + # Create student accounts and activate them. for i in range(len(self.STUDENT_INFO)): email, password = self.STUDENT_INFO[i] @@ -48,6 +69,50 @@ class TestNavigation(ModuleStoreTestCase, LoginEnrollmentTestCase): self.staff_user = GlobalStaffFactory() + def test_chrome_settings(self): + ''' + Test settings for disabling and modifying navigation chrome in the courseware: + - Accordion enabled, or disabled + - Navigation tabs enabled, disabled, or redirected + ''' + email, password = self.STUDENT_INFO[0] + self.login(email, password) + self.enroll(self.course, True) + + test_data = ( + ('tabs', False, True), + ('none', False, False), + ('fullchrome', True, True), + ('accordion', True, False), + ('fullchrome', True, True)) + for (displayname, accordion, tabs) in test_data: + response = self.client.get(reverse('courseware_section', kwargs={ + 'course_id': self.course.id.to_deprecated_string(), + 'chapter': 'Chrome', + 'section': displayname, + })) + self.assertEquals('open_close_accordion' in response.content, accordion) + self.assertEquals('course-tabs' in response.content, tabs) + + def tab_active(tabname, response): + ''' Check if the progress tab is active in the tab set ''' + for line in response.content.split('\n'): + if tabname in line and 'active' in line: + return True + return False + + self.assertFalse(tab_active('progress', response)) + self.assertTrue(tab_active('courseware', response)) + + response = self.client.get(reverse('courseware_section', kwargs={ + 'course_id': self.course.id.to_deprecated_string(), + 'chapter': 'Chrome', + 'section': 'progress_tab', + })) + + self.assertTrue(tab_active('progress', response)) + self.assertFalse(tab_active('courseware', response)) + @override_settings(SESSION_INACTIVITY_TIMEOUT_IN_SECONDS=1) def test_inactive_session_timeout(self): """ diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index ce9a9d15a5..4a9a191998 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -338,6 +338,7 @@ def index(request, course_id, chapter=None, section=None, if section is not None: section_descriptor = chapter_descriptor.get_child_by(lambda m: m.location.name == section) + if section_descriptor is None: # Specifically asked-for section doesn't exist if masq == 'student': # if staff is masquerading as student be kinder, don't 404 @@ -345,6 +346,17 @@ def index(request, course_id, chapter=None, section=None, return redirect(reverse('courseware', args=[course.id.to_deprecated_string()])) raise Http404 + ## Allow chromeless operation + if section_descriptor.chrome: + chrome = [s.strip() for s in section_descriptor.chrome.lower().split(",")] + if 'accordion' not in chrome: + del context['accordion'] + if 'tabs' not in chrome: + context['disable_tabs'] = True + + if section_descriptor.default_tab: + context['default_tab'] = section_descriptor.default_tab + # cdodge: this looks silly, but let's refetch the section_descriptor with depth=None # which will prefetch the children more efficiently than doing a recursive load section_descriptor = modulestore().get_item(section_descriptor.location, depth=None) diff --git a/lms/lib/xblock/mixin.py b/lms/lib/xblock/mixin.py index edf84fbe6b..0f5ba0cfb2 100644 --- a/lms/lib/xblock/mixin.py +++ b/lms/lib/xblock/mixin.py @@ -18,5 +18,20 @@ class LmsBlockMixin(XBlockMixin): "grader to apply, and what to show in the TOC)", scope=Scope.settings, ) + chrome = String( + help="Which chrome to show. Options: " + "chromeless -- No chrome" + "tabs -- just tabs" + "accordion -- just accordion" + "tabs,accordion -- Full Chrome", + scope=Scope.settings, + default = None, + ) + default_tab = String( + help="Override which tab is selected." + "If not set, courseware tab is shown.", + scope=Scope.settings, + default = None, + ) source_file = String(help="source file name (eg for latex)", scope=Scope.settings) ispublic = Boolean(help="Whether this course is open to the public, or only to admins", scope=Scope.settings) diff --git a/lms/templates/courseware/course_navigation.html b/lms/templates/courseware/course_navigation.html index 3d9deb0483..fd9ab42eec 100644 --- a/lms/templates/courseware/course_navigation.html +++ b/lms/templates/courseware/course_navigation.html @@ -20,12 +20,13 @@ def url_class(is_active): <%! from courseware.views import notification_image_for_tab %> <% import waffle %> +% if disable_tabs is UNDEFINED: +%endif % if masquerade is not UNDEFINED: % if staff_access and masquerade is not None: diff --git a/lms/templates/courseware/courseware.html b/lms/templates/courseware/courseware.html index 4446b4ec7a..e0b386f075 100644 --- a/lms/templates/courseware/courseware.html +++ b/lms/templates/courseware/courseware.html @@ -179,8 +179,10 @@ ${fragment.foot_html()} % endif -% if accordion: <%include file="/dashboard/_dashboard_prompt_midcourse_reverify.html" /> +% if default_tab: + <%include file="/courseware/course_navigation.html" /> +% else: <%include file="/courseware/course_navigation.html" args="active_page='courseware'" /> % endif