Commit 1c79b9c8 by Chris Dodge

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

parent 8c94be80
......@@ -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>
......
......@@ -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')
......
......@@ -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
......
<a href="/jump_to_id/vertical_test">This is a link to another page</a>
<html filename="toyjumpto.html"/>
\ No newline at end of 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)
......
......@@ -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):
......
......@@ -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
......@@ -447,6 +448,26 @@ def index(request, course_id, chapter=None, section=None,
@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):
"""
Show the page that contains a specific location.
......
......@@ -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'),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment