Files
edx-platform/lms/lib/xblock/runtime.py

138 lines
3.9 KiB
Python

"""
Module implementing `xblock.runtime.Runtime` functionality for the LMS
"""
import re
from django.core.urlresolvers import reverse
from xmodule.x_module import ModuleSystem
def _quote_slashes(match):
"""
Helper function for `quote_slashes`
"""
matched = match.group(0)
# We have to escape ';', because that is our
# escape sequence identifier (otherwise, the escaping)
# couldn't distinguish between us adding ';_' to the string
# and ';_' appearing naturally in the string
if matched == ';':
return ';;'
elif matched == '/':
return ';_'
else:
return matched
def quote_slashes(text):
"""
Quote '/' characters so that they aren't visible to
django's url quoting, unquoting, or url regex matching.
Escapes '/'' to the sequence ';_', and ';' to the sequence
';;'. By making the escape sequence fixed length, and escaping
identifier character ';', we are able to reverse the escaping.
"""
return re.sub(r'[;/]', _quote_slashes, text)
def _unquote_slashes(match):
"""
Helper function for `unquote_slashes`
"""
matched = match.group(0)
if matched == ';;':
return ';'
elif matched == ';_':
return '/'
else:
return matched
def unquote_slashes(text):
"""
Unquote slashes quoted by `quote_slashes`
"""
return re.sub(r'(;;|;_)', _unquote_slashes, text)
def handler_url(course_id, block, handler, suffix='', query='', thirdparty=False):
"""
Return an XBlock handler url for the specified course, block and handler.
If handler is an empty string, this function is being used to create a
prefix of the general URL, which is assumed to be followed by handler name
and suffix.
If handler is specified, then it is checked for being a valid handler
function, and ValueError is raised if not.
"""
view_name = 'xblock_handler'
if handler:
# Be sure this is really a handler.
func = getattr(block, handler, None)
if not func:
raise ValueError("{!r} is not a function name".format(handler))
if not getattr(func, "_is_xblock_handler", False):
raise ValueError("{!r} is not a handler name".format(handler))
if thirdparty:
view_name = 'xblock_handler_noauth'
url = reverse(view_name, kwargs={
'course_id': course_id,
'usage_id': quote_slashes(str(block.scope_ids.usage_id)),
'handler': handler,
'suffix': suffix,
})
# If suffix is an empty string, remove the trailing '/'
if not suffix:
url = url.rstrip('/')
# If there is a query string, append it
if query:
url += '?' + query
return url
def handler_prefix(course_id, block):
"""
Returns a prefix for use by the Javascript handler_url function.
The prefix is a valid handler url after the handler name is slash-appended
to it.
"""
# This depends on handler url having the handler_name as the final piece of the url
# so that leaving an empty handler_name really does leave the opportunity to append
# the handler_name on the frontend
# This is relied on by the xblock/runtime.v1.coffee frontend handlerUrl function
return handler_url(course_id, block, '').rstrip('/?')
class LmsHandlerUrls(object):
"""
A runtime mixin that provides a handler_url function that routes
to the LMS' xblock handler view.
This must be mixed in to a runtime that already accepts and stores
a course_id
"""
# pylint: disable=unused-argument
# pylint: disable=no-member
def handler_url(self, block, handler_name, suffix='', query='', thirdparty=False):
"""See :method:`xblock.runtime:Runtime.handler_url`"""
return handler_url(self.course_id, block, handler_name, suffix='', query='', thirdparty=thirdparty)
class LmsModuleSystem(LmsHandlerUrls, ModuleSystem): # pylint: disable=abstract-method
"""
ModuleSystem specialized to the LMS
"""
pass