diff --git a/common/lib/xmodule/xmodule/modulestore/search.py b/common/lib/xmodule/xmodule/modulestore/search.py index 14cea26498..0ee4e37d41 100644 --- a/common/lib/xmodule/xmodule/modulestore/search.py +++ b/common/lib/xmodule/xmodule/modulestore/search.py @@ -44,8 +44,8 @@ def path_to_location(modulestore, usage_key): If no path exists, return None. - If a path exists, return it as a list with target location first, and - the starting location last. + If a path exists, return it as a tuple with root location first, and + the target location last. ''' # Standard DFS @@ -86,6 +86,7 @@ def path_to_location(modulestore, usage_key): # pull out the location names chapter = path[1].name if n > 1 else None section = path[2].name if n > 2 else None + vertical = path[3].name if n > 3 else None # Figure out the position position = None @@ -109,7 +110,7 @@ def path_to_location(modulestore, usage_key): position_list.append(str(child_locs.index(path[path_index + 1]) + 1)) position = "_".join(position_list) - return (course_id, chapter, section, position) + return (course_id, chapter, section, vertical, position, path[-1]) def navigation_index(position): diff --git a/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py b/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py index 74aa23066b..30d6404a93 100644 --- a/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py +++ b/common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py @@ -1221,15 +1221,16 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup): should_work = ( (self.problem_x1a_2, - (course_key, u"Chapter_x", u"Sequential_x1", '1')), + (course_key, u"Chapter_x", u"Sequential_x1", u'Vertical_x1a', '1', self.problem_x1a_2)), (self.chapter_x, - (course_key, "Chapter_x", None, None)), + (course_key, "Chapter_x", None, None, None, self.chapter_x)), ) for location, expected in should_work: # each iteration has different find count, pop this iter's find count with check_mongo_calls(num_finds.pop(0), num_sends): - self.assertEqual(path_to_location(self.store, location), expected) + path = path_to_location(self.store, location) + self.assertEqual(path, expected) not_found = ( course_key.make_usage_key('video', 'WelcomeX'), @@ -1259,11 +1260,13 @@ class TestMixedModuleStore(CommonMixedModuleStoreSetup): # only needs course_locations set self.initdb('draft') course_key = self.course_locations[self.XML_COURSEID1].course_key + video_key = course_key.make_usage_key('video', 'Welcome') + chapter_key = course_key.make_usage_key('chapter', 'Overview') should_work = ( - (course_key.make_usage_key('video', 'Welcome'), - (course_key, "Overview", "Welcome", None)), - (course_key.make_usage_key('chapter', 'Overview'), - (course_key, "Overview", None, None)), + (video_key, + (course_key, "Overview", "Welcome", None, None, video_key)), + (chapter_key, + (course_key, "Overview", None, None, None, chapter_key)), ) for location, expected in should_work: diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 8dbfcb42d0..4ece972ea6 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -3,6 +3,7 @@ Tests courseware views.py """ import cgi +from urllib import urlencode import ddt import json import unittest @@ -83,10 +84,11 @@ class TestJumpTo(ModuleStoreTestCase): course = CourseFactory.create() chapter = ItemFactory.create(category='chapter', parent_location=course.location) section = ItemFactory.create(category='sequential', parent_location=chapter.location) - expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/'.format( + expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/?{activate_block_id}'.format( course_id=unicode(course.id), chapter_id=chapter.url_name, section_id=section.url_name, + activate_block_id=urlencode({'activate_block_id': unicode(section.location)}) ) jumpto_url = '{0}/{1}/jump_to/{2}'.format( '/courses', @@ -105,10 +107,11 @@ class TestJumpTo(ModuleStoreTestCase): module1 = ItemFactory.create(category='html', parent_location=vertical1.location) module2 = ItemFactory.create(category='html', parent_location=vertical2.location) - expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/1'.format( + expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/1?{activate_block_id}'.format( course_id=unicode(course.id), chapter_id=chapter.url_name, section_id=section.url_name, + activate_block_id=urlencode({'activate_block_id': unicode(module1.location)}) ) jumpto_url = '{0}/{1}/jump_to/{2}'.format( '/courses', @@ -118,10 +121,11 @@ class TestJumpTo(ModuleStoreTestCase): response = self.client.get(jumpto_url) self.assertRedirects(response, expected, status_code=302, target_status_code=302) - expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/2'.format( + expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/2?{activate_block_id}'.format( course_id=unicode(course.id), chapter_id=chapter.url_name, section_id=section.url_name, + activate_block_id=urlencode({'activate_block_id': unicode(module2.location)}) ) jumpto_url = '{0}/{1}/jump_to/{2}'.format( '/courses', @@ -145,10 +149,11 @@ class TestJumpTo(ModuleStoreTestCase): # internal position of module2 will be 1_2 (2nd item withing 1st item) - expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/1'.format( + expected = 'courses/{course_id}/courseware/{chapter_id}/{section_id}/1?{activate_block_id}'.format( course_id=unicode(course.id), chapter_id=chapter.url_name, section_id=section.url_name, + activate_block_id=urlencode({'activate_block_id': unicode(module2.location)}) ) jumpto_url = '{0}/{1}/jump_to/{2}'.format( '/courses', @@ -1084,6 +1089,23 @@ class GenerateUserCertTests(ModuleStoreTestCase): ), resp.content) +class ActivateIDCheckerBlock(XBlock): + """ + XBlock for checking for an activate_block_id entry in the render context. + """ + # We don't need actual children to test this. + has_children = False + + def student_view(self, context): + """ + A student view that displays the activate_block_id context variable. + """ + result = Fragment() + if 'activate_block_id' in context: + result.add_content(u"Activate Block ID: {block_id}

".format(block_id=context['activate_block_id'])) + return result + + class ViewCheckerBlock(XBlock): """ XBlock for testing user state in views. @@ -1157,6 +1179,34 @@ class TestIndexView(ModuleStoreTestCase): response = views.index(request, unicode(course.id), chapter=chapter.url_name, section=section.url_name) self.assertEquals(response.content.count("ViewCheckerPassed"), 3) + @XBlock.register_temp_plugin(ActivateIDCheckerBlock, 'id_checker') + def test_activate_block_id(self): + user = UserFactory() + + course = CourseFactory.create() + chapter = ItemFactory.create(parent=course, category='chapter') + section = ItemFactory.create(parent=chapter, category='sequential', display_name="Sequence") + vertical = ItemFactory.create(parent=section, category='vertical', display_name="Vertical") + ItemFactory.create(parent=vertical, category='id_checker', display_name="ID Checker") + + CourseEnrollmentFactory(user=user, course_id=course.id) + + request = RequestFactory().get( + reverse( + 'courseware_section', + kwargs={ + 'course_id': unicode(course.id), + 'chapter': chapter.url_name, + 'section': section.url_name, + } + ) + '?activate_block_id=test_block_id' + ) + request.user = user + mako_middleware_process_request(request) + + response = views.index(request, unicode(course.id), chapter=chapter.url_name, section=section.url_name) + self.assertIn("Activate Block ID: test_block_id", response.content) + class TestRenderXBlock(RenderXBlockTestMixin, ModuleStoreTestCase): """ diff --git a/lms/djangoapps/courseware/url_helpers.py b/lms/djangoapps/courseware/url_helpers.py index 22f4619396..62ba41e42e 100644 --- a/lms/djangoapps/courseware/url_helpers.py +++ b/lms/djangoapps/courseware/url_helpers.py @@ -1,6 +1,7 @@ """ Module to define url helpers functions """ +from urllib import urlencode from xmodule.modulestore.search import path_to_location, navigation_index from xmodule.modulestore.django import modulestore from django.core.urlresolvers import reverse @@ -20,7 +21,10 @@ def get_redirect_url(course_key, usage_key): Redirect url string """ - (course_key, chapter, section, position) = path_to_location(modulestore(), usage_key) + ( + course_key, chapter, section, vertical_unused, + position, final_target_id + ) = path_to_location(modulestore(), usage_key) # choose the appropriate view (and provide the necessary args) based on the # args provided by the redirect. @@ -43,4 +47,7 @@ def get_redirect_url(course_key, usage_key): 'courseware_position', args=(unicode(course_key), chapter, section, navigation_index(position)) ) + + redirect_url += "?{}".format(urlencode({'activate_block_id': unicode(final_target_id)})) + return redirect_url diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index 398b5bb465..ba2673c3a9 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -552,7 +552,8 @@ def _index_bulk_op(request, course_key, chapter, section, position): # Save where we are in the chapter. save_child_position(chapter_module, section) - context['fragment'] = section_module.render(STUDENT_VIEW) + section_render_context = {'activate_block_id': request.GET.get('activate_block_id')} + context['fragment'] = section_module.render(STUDENT_VIEW, section_render_context) context['section_title'] = section_descriptor.display_name_with_default else: # section is none, so display a message diff --git a/lms/djangoapps/open_ended_grading/utils.py b/lms/djangoapps/open_ended_grading/utils.py index 7728ab8d7f..fac12a2cda 100644 --- a/lms/djangoapps/open_ended_grading/utils.py +++ b/lms/djangoapps/open_ended_grading/utils.py @@ -1,4 +1,5 @@ import logging +from urllib import urlencode from xmodule.modulestore import search from xmodule.modulestore.django import modulestore @@ -33,6 +34,8 @@ def generate_problem_url(problem_url_parts, base_course_url): @param base_course_url: Base url of a given course @return: A path to the problem """ + activate_block_id = problem_url_parts[-1] + problem_url_parts = problem_url_parts[0:-1] problem_url = base_course_url + "/" for i, part in enumerate(problem_url_parts): if part is not None: @@ -44,6 +47,7 @@ def generate_problem_url(problem_url_parts, base_course_url): if i == 1: problem_url += "courseware/" problem_url += part + "/" + problem_url += '?{}'.format(urlencode({'activate_block_id': unicode(activate_block_id)})) return problem_url