add a /jump_to_id/ shortcut for producing more durable links between courseware in Studio

This commit is contained in:
Chris Dodge
2013-07-20 21:02:24 -04:00
parent 8c94be8083
commit 1c79b9c874
9 changed files with 82 additions and 3 deletions

View File

@@ -171,7 +171,7 @@
<div class="window unit-location">
<h4 class="header">${_("Unit Location")}</h4>
<div class="window-contents">
<div><input type="text" class="url" value="/courseware/${section.url_name}/${subsection.url_name}" disabled /></div>
<div>${_("Unit Identifier:")}&nbsp;<input type="text" class="url" value="${unit.location.name}" disabled /></div>
<ol>
<li>
<a href="#" class="section-item">${section.display_name_with_default}</a>

View File

@@ -43,6 +43,26 @@ def try_staticfiles_lookup(path):
return url
def replace_jump_to_id_urls(text, course_id, jump_to_id_base_url):
"""
This will replace a link to another piece of courseware to a 'jump_to'
URL that will redirect to the right place in the courseware
NOTE: This is similar to replace_course_urls in terms of functionality
but it is intended to be used when we only have a 'id' that the
course author provides. This is much more helpful when using
Studio authored courses since they don't need to know the path. This
is also durable with respect to item moves.
"""
def replace_jump_to_id_url(match):
quote = match.group('quote')
rest = match.group('rest')
return "".join([quote, jump_to_id_base_url + rest, quote])
return re.sub(_url_replace_regex('/jump_to_id/'), replace_jump_to_id_url, text)
def replace_course_urls(text, course_id):
"""
Replace /course/$stuff urls with /courses/$course_id/$stuff urls
@@ -53,7 +73,6 @@ def replace_course_urls(text, course_id):
returns: text with the links replaced
"""
def replace_course_url(match):
quote = match.group('quote')
rest = match.group('rest')

View File

@@ -42,6 +42,19 @@ def wrap_xmodule(get_html, module, template, context=None):
return _get_html
def replace_jump_to_id_urls(get_html, course_id, jump_to_id_base_url):
"""
This will replace a link between courseware in the format
/jump_to/<id> with a URL for a page that will correctly redirect
This is similar to replace_course_urls, but much more flexible and
durable for Studio authored courses. See more comments in static_replace.replace_jump_to_urls
"""
@wraps(get_html)
def _get_html():
return static_replace.replace_jump_to_id_urls(get_html(), course_id, jump_to_id_base_url)
return _get_html
def replace_course_urls(get_html, course_id):
"""
Updates the supplied module with a new get_html function that wraps

View File

@@ -0,0 +1 @@
<a href="/jump_to_id/vertical_test">This is a link to another page</a>

View File

@@ -0,0 +1 @@
<html filename="toyjumpto.html"/>

View File

@@ -27,7 +27,7 @@ from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.x_module import ModuleSystem
from xmodule_modifiers import replace_course_urls, replace_static_urls, add_histogram, wrap_xmodule, save_module # pylint: disable=F0401
from xmodule_modifiers import replace_course_urls, replace_jump_to_id_urls, replace_static_urls, add_histogram, wrap_xmodule, save_module # pylint: disable=F0401
import static_replace
from psychometrics.psychoanalyze import make_psychometrics_data_update_handler
@@ -393,6 +393,15 @@ def get_module_for_descriptor_internal(user, descriptor, model_data_cache, cours
# hierarchy of this course
module.get_html = replace_course_urls(module.get_html, course_id)
# this will rewrite intra-courseware links
# that use the shorthand /jump_to_id/<id>. This is very helpful
# for studio authored courses (compared to the /course/... format) since it is
# is durable with respect to moves and the author doesn't need to
# know the hierarchy
module.get_html = replace_jump_to_id_urls(module.get_html, course_id,
reverse('jump_to_id',
kwargs={'course_id': course_id, 'module_id': ''}))
if settings.MITX_FEATURES.get('DISPLAY_HISTOGRAMS_TO_STAFF'):
if has_access(user, module, 'staff', course_id):
module.get_html = add_histogram(module.get_html, module, user)

View File

@@ -44,6 +44,19 @@ class TestJumpTo(TestCase):
response = self.client.get(jumpto_url)
self.assertRedirects(response, expected, status_code=302, target_status_code=302)
def test_jumpto_id(self):
location = Location('i4x', 'edX', 'toy', 'chapter', 'Overview')
jumpto_url = '%s/%s/jump_to_id/%s' % ('/courses', self.course_name, location.name)
expected = 'courses/edX/toy/2012_Fall/courseware/Overview/'
response = self.client.get(jumpto_url)
self.assertRedirects(response, expected, status_code=302, target_status_code=302)
def test_jumpto_id_invalid_location(self):
location = Location('i4x', 'edX', 'toy', 'NoSuchPlace', None)
jumpto_url = '%s/%s/jump_to/%s' % ('/courses', self.course_name, location.name)
response = self.client.get(jumpto_url)
self.assertEqual(response.status_code, 404)
class ViewsTestCase(TestCase):
def setUp(self):

View File

@@ -33,6 +33,7 @@ from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import InvalidLocationError, ItemNotFoundError, NoPathToItem
from xmodule.modulestore.search import path_to_location
from xmodule.course_module import CourseDescriptor
import comment_client
@@ -446,6 +447,26 @@ def index(request, course_id, chapter=None, section=None,
return result
@ensure_csrf_cookie
def jump_to_id(request, course_id, module_id):
"""
This entry point allows for a shorter version of a jump to where just the id of the element is
passed in. This assumes that id is unique within the course_id namespace
"""
course_location = CourseDescriptor.id_to_location(course_id)
items = modulestore().get_items(['i4x', course_location.org, course_location.course, None, module_id])
if len(items) == 0:
raise Http404("Could not find id = {0} in course_id = {1}".format(module_id, course_id))
if len(items) > 1:
logging.warning("Multiple items found with id = {0} in course_id = {1}. Using first found {2}...".
format(module_id, course_id, items[0].location.url()))
return jump_to(request, course_id, items[0].location.url())
@ensure_csrf_cookie
def jump_to(request, course_id, location):
"""

View File

@@ -177,6 +177,8 @@ if settings.COURSEWARE_ENABLED:
urlpatterns += (
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/jump_to/(?P<location>.*)$',
'courseware.views.jump_to', name="jump_to"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/jump_to_id/(?P<module_id>.*)$',
'courseware.views.jump_to_id', name="jump_to_id"),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/modx/(?P<location>.*?)/(?P<dispatch>[^/]*)$',
'courseware.module_render.modx_dispatch',
name='modx_dispatch'),