From 49217ebe8bd8eba20fb167831c8157a52b4d8b97 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Fri, 13 Dec 2013 07:47:21 -0500 Subject: [PATCH] Allow multiple client-side runtimes on a single page Make XBlock client-side runtimes proper classes, so that handlerUrl can be defined in a per-runtime way, and we can have multiple runtimes on a single page. [LMS-1630][LMS-1421][LMS-1517] --- cms/djangoapps/contentstore/views/item.py | 9 +- cms/djangoapps/contentstore/views/preview.py | 22 ++--- cms/lib/xblock/runtime.py | 2 - cms/static/coffee/spec/main.coffee | 1 + cms/static/coffee/spec/main_squire.coffee | 1 + .../coffee/src/views/module_edit.coffee | 2 +- .../coffee/src/xblock/cms.runtime.v1.coffee | 22 +++++ cms/static/js_test.yml | 3 +- cms/static/js_test_squire.yml | 2 + common/djangoapps/xmodule_modifiers.py | 14 +-- common/static/coffee/src/xblock/core.coffee | 9 +- .../coffee/src/xblock/runtime.v1.coffee | 29 ++----- lms/djangoapps/courseware/module_render.py | 7 +- .../instructor/views/instructor_dashboard.py | 3 +- lms/djangoapps/instructor/views/legacy.py | 3 +- lms/envs/common.py | 12 +-- lms/lib/xblock/runtime.py | 86 ++++++------------- .../coffee/src/xblock/lms.runtime.v1.coffee | 18 ++++ lms/static/js_test.yml | 1 + 19 files changed, 122 insertions(+), 124 deletions(-) create mode 100644 cms/static/coffee/src/xblock/cms.runtime.v1.coffee create mode 100644 lms/static/coffee/src/xblock/lms.runtime.v1.coffee diff --git a/cms/djangoapps/contentstore/views/item.py b/cms/djangoapps/contentstore/views/item.py index 3d8f81a6ef..725f394d14 100644 --- a/cms/djangoapps/contentstore/views/item.py +++ b/cms/djangoapps/contentstore/views/item.py @@ -10,6 +10,7 @@ from xmodule_modifiers import wrap_xblock from django.core.exceptions import PermissionDenied from django.contrib.auth.decorators import login_required from django.http import HttpResponseBadRequest +from django.utils.translation import ugettext as _ from django.views.decorators.http import require_http_methods from xblock.fields import Scope @@ -34,7 +35,7 @@ from .helpers import _xmodule_recurse from preview import handler_prefix, get_preview_html from edxmako.shortcuts import render_to_response, render_to_string from models.settings.course_grading import CourseGradingModel -from django.utils.translation import ugettext as _ +from cms.lib.xblock.runtime import handler_url __all__ = ['orphan_handler', 'xblock_handler'] @@ -43,6 +44,12 @@ log = logging.getLogger(__name__) CREATE_IF_NOT_FOUND = ['course_info'] +# In order to allow descriptors to use a handler url, we need to +# monkey-patch the x_module library. +# TODO: Remove this code when Runtimes are no longer created by modulestores +xmodule.x_module.descriptor_global_handler_url = handler_url + + # pylint: disable=unused-argument @require_http_methods(("DELETE", "GET", "PUT", "POST")) @login_required diff --git a/cms/djangoapps/contentstore/views/preview.py b/cms/djangoapps/contentstore/views/preview.py index 11bea40f9f..732323e924 100644 --- a/cms/djangoapps/contentstore/views/preview.py +++ b/cms/djangoapps/contentstore/views/preview.py @@ -33,20 +33,6 @@ __all__ = ['preview_handler'] log = logging.getLogger(__name__) -def handler_prefix(block, handler='', suffix=''): - """ - Return a url prefix for XBlock handler_url. The full handler_url - should be '{prefix}/{handler}/{suffix}?{query}'. - - Trailing `/`s are removed from the returned url. - """ - return reverse('preview_handler', kwargs={ - 'usage_id': quote_slashes(unicode(block.scope_ids.usage_id).encode('utf-8')), - 'handler': handler, - 'suffix': suffix, - }).rstrip('/?') - - @login_required def preview_handler(request, usage_id, handler, suffix=''): """ @@ -91,7 +77,11 @@ class PreviewModuleSystem(ModuleSystem): # pylint: disable=abstract-method An XModule ModuleSystem for use in Studio previews """ def handler_url(self, block, handler_name, suffix='', query='', thirdparty=False): - return handler_prefix(block, handler_name, suffix) + '?' + query + return reverse('preview_handler', kwargs={ + 'usage_id': quote_slashes(unicode(block.scope_ids.usage_id).encode('utf-8')), + 'handler': handler_name, + 'suffix': suffix, + }) + '?' + query def _preview_module_system(request, descriptor): @@ -123,7 +113,7 @@ def _preview_module_system(request, descriptor): # Set up functions to modify the fragment produced by student_view wrappers=( # This wrapper wraps the module in the template specified above - partial(wrap_xblock, handler_prefix, display_name_only=descriptor.location.category == 'static_tab'), + partial(wrap_xblock, 'PreviewRuntime', display_name_only=descriptor.location.category == 'static_tab'), # This wrapper replaces urls in the output that start with /static # with the correct course-specific url for the static content diff --git a/cms/lib/xblock/runtime.py b/cms/lib/xblock/runtime.py index 5c749a34c6..36437c4750 100644 --- a/cms/lib/xblock/runtime.py +++ b/cms/lib/xblock/runtime.py @@ -4,7 +4,6 @@ XBlock runtime implementations for edX Studio from django.core.urlresolvers import reverse -import xmodule.x_module from lms.lib.xblock.runtime import quote_slashes @@ -27,4 +26,3 @@ def handler_url(block, handler_name, suffix='', query='', thirdparty=False): return url -xmodule.x_module.descriptor_global_handler_url = handler_url diff --git a/cms/static/coffee/spec/main.coffee b/cms/static/coffee/spec/main.coffee index 2cbc85d7c1..705ff08238 100644 --- a/cms/static/coffee/spec/main.coffee +++ b/cms/static/coffee/spec/main.coffee @@ -28,6 +28,7 @@ requirejs.config({ "tinymce": "xmodule_js/common_static/js/vendor/tiny_mce/tiny_mce", "jquery.tinymce": "xmodule_js/common_static/js/vendor/tiny_mce/jquery.tinymce", "xmodule": "xmodule_js/src/xmodule", + "xblock/cms.runtime.v1": "coffee/src/xblock/cms.runtime.v1", "xblock": "xmodule_js/common_static/coffee/src/xblock", "utility": "xmodule_js/common_static/js/src/utility", "accessibility": "xmodule_js/common_static/js/src/accessibility_tools", diff --git a/cms/static/coffee/spec/main_squire.coffee b/cms/static/coffee/spec/main_squire.coffee index a8e052fd2e..bc19272283 100644 --- a/cms/static/coffee/spec/main_squire.coffee +++ b/cms/static/coffee/spec/main_squire.coffee @@ -27,6 +27,7 @@ requirejs.config({ "tinymce": "xmodule_js/common_static/js/vendor/tiny_mce/tiny_mce", "jquery.tinymce": "xmodule_js/common_static/js/vendor/tiny_mce/jquery.tinymce", "xmodule": "xmodule_js/src/xmodule", + "xblock/cms.runtime.v1": "coffee/src/xblock/cms.runtime.v1", "xblock": "xmodule_js/common_static/coffee/src/xblock", "utility": "xmodule_js/common_static/js/src/utility", "sinon": "xmodule_js/common_static/js/vendor/sinon-1.7.1", diff --git a/cms/static/coffee/src/views/module_edit.coffee b/cms/static/coffee/src/views/module_edit.coffee index a9628b628f..a30fbe9db1 100644 --- a/cms/static/coffee/src/views/module_edit.coffee +++ b/cms/static/coffee/src/views/module_edit.coffee @@ -1,6 +1,6 @@ define ["backbone", "jquery", "underscore", "gettext", "xblock/runtime.v1", "js/views/feedback_notification", "js/views/metadata", "js/collections/metadata" - "js/utils/modal", "jquery.inputnumber", "xmodule", "coffee/src/main"], + "js/utils/modal", "jquery.inputnumber", "xmodule", "coffee/src/main", "xblock/cms.runtime.v1"], (Backbone, $, _, gettext, XBlock, NotificationView, MetadataView, MetadataCollection, ModalUtils) -> class ModuleEdit extends Backbone.View tagName: 'li' diff --git a/cms/static/coffee/src/xblock/cms.runtime.v1.coffee b/cms/static/coffee/src/xblock/cms.runtime.v1.coffee new file mode 100644 index 0000000000..4eddd6ef3a --- /dev/null +++ b/cms/static/coffee/src/xblock/cms.runtime.v1.coffee @@ -0,0 +1,22 @@ +define ["jquery", "xblock/runtime.v1", "URI"], ($, XBlock, URI) -> + @PreviewRuntime = {} + + class PreviewRuntime.v1 extends XBlock.Runtime.v1 + handlerUrl: (element, handlerName, suffix, query, thirdparty) -> + uri = URI("/preview/xblock").segment($(@element).data('usage-id')) + .segment('handler') + .segment(handlerName) + if suffix? then uri.segment(suffix) + if query? then uri.search(query) + uri.toString() + + @StudioRuntime = {} + + class StudioRuntime.v1 extends XBlock.Runtime.v1 + handlerUrl: (element, handlerName, suffix, query, thirdparty) -> + uri = URI("/xblock").segment($(@element).data('usage-id')) + .segment('handler') + .segment(handlerName) + if suffix? then uri.segment(suffix) + if query? then uri.search(query) + uri.toString() diff --git a/cms/static/js_test.yml b/cms/static/js_test.yml index 17d4805be0..ce247967a9 100644 --- a/cms/static/js_test.yml +++ b/cms/static/js_test.yml @@ -59,7 +59,8 @@ lib_paths: - xmodule_js/common_static/js/vendor/URI.min.js - xmodule_js/common_static/js/vendor/jquery.smooth-scroll.min.js - xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js - - xmodule_js/common_static/coffee/src/xblock + - xmodule_js/common_static/coffee/src/xblock/ + - xmodule_js/common_static/js/vendor/URI.min.js # Paths to source JavaScript files src_paths: diff --git a/cms/static/js_test_squire.yml b/cms/static/js_test_squire.yml index 8f57291b3d..c8a5ff439d 100644 --- a/cms/static/js_test_squire.yml +++ b/cms/static/js_test_squire.yml @@ -54,6 +54,8 @@ lib_paths: - xmodule_js/src/xmodule.js - xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js - xmodule_js/common_static/js/test/i18n.js + - xmodule_js/common_static/coffee/src/xblock/ + - xmodule_js/common_static/js/vendor/URI.min.js # Paths to source JavaScript files src_paths: diff --git a/common/djangoapps/xmodule_modifiers.py b/common/djangoapps/xmodule_modifiers.py index f7b314d775..1b095bf917 100644 --- a/common/djangoapps/xmodule_modifiers.py +++ b/common/djangoapps/xmodule_modifiers.py @@ -15,6 +15,7 @@ from xblock.fragment import Fragment from xmodule.seq_module import SequenceModule from xmodule.vertical_module import VerticalModule from xmodule.x_module import shim_xmodule_js, XModuleDescriptor, XModule +from lms.lib.xblock.runtime import quote_slashes log = logging.getLogger(__name__) @@ -29,26 +30,28 @@ def wrap_fragment(fragment, new_content): return wrapper_frag -def wrap_xblock(handler_prefix, block, view, frag, context, display_name_only=False): # pylint: disable=unused-argument +def wrap_xblock(runtime_class, block, view, frag, context, display_name_only=False, extra_data=None): # pylint: disable=unused-argument """ Wraps the results of rendering an XBlock view in a standard
with identifying data so that the appropriate javascript module can be loaded onto it. - :param handler_prefix: A function that takes a block and returns the url prefix for - the javascript handler_url. This prefix should be able to have {handler_name}/{suffix}?{query} - appended to it to return a valid handler_url + :param runtime_class: The name of the javascript runtime class to use to load this block :param block: An XBlock (that may be an XModule or XModuleDescriptor) :param view: The name of the view that rendered the fragment being wrapped :param frag: The :class:`Fragment` to be wrapped :param context: The context passed to the view being rendered :param display_name_only: If true, don't render the fragment content at all. Instead, just render the `display_name` of `block` + :param extra_data: A dictionary with extra data values to be set on the wrapper """ + if extra_data is None: + extra_data = {} # If any mixins have been applied, then use the unmixed class class_name = getattr(block, 'unmixed_class', block.__class__).__name__ data = {} + data.update(extra_data) css_classes = ['xblock', 'xblock-' + view] if isinstance(block, (XModule, XModuleDescriptor)): @@ -65,9 +68,10 @@ def wrap_xblock(handler_prefix, block, view, frag, context, display_name_only=Fa if frag.js_init_fn: data['init'] = frag.js_init_fn + data['runtime-class'] = runtime_class data['runtime-version'] = frag.js_init_version - data['handler-prefix'] = handler_prefix(block) data['block-type'] = block.scope_ids.block_type + data['usage-id'] = quote_slashes(str(block.scope_ids.usage_id)) template_context = { 'content': block.display_name if display_name_only else frag.content, diff --git a/common/static/coffee/src/xblock/core.coffee b/common/static/coffee/src/xblock/core.coffee index 71fbfd291e..9c5cc0961c 100644 --- a/common/static/coffee/src/xblock/core.coffee +++ b/common/static/coffee/src/xblock/core.coffee @@ -1,18 +1,19 @@ @XBlock = - runtime: {} + Runtime: {} initializeBlock: (element) -> $element = $(element) children = @initializeBlocks($element) + runtime = $element.data("runtime-class") version = $element.data("runtime-version") initFnName = $element.data("init") - if version? and initFnName? - runtime = @runtime["v#{version}"](element, children) + if runtime? and version? and initFnName? + runtime = new window[runtime]["v#{version}"](element, children) initFn = window[initFnName] block = initFn(runtime, element) ? {} else elementTag = $('
').append($element.clone()).html(); - console.log("Block #{elementTag} is missing data-runtime-version or data-init, and can't be initialized") + console.log("Block #{elementTag} is missing data-runtime, data-runtime-version or data-init, and can't be initialized") block = {} block.element = element diff --git a/common/static/coffee/src/xblock/runtime.v1.coffee b/common/static/coffee/src/xblock/runtime.v1.coffee index efb9944bd2..b3f28242ab 100644 --- a/common/static/coffee/src/xblock/runtime.v1.coffee +++ b/common/static/coffee/src/xblock/runtime.v1.coffee @@ -1,24 +1,5 @@ -@XBlock.runtime.v1 = (element, children) -> - childMap = {} - $.each children, (idx, child) -> - childMap[child.name] = child - - return { - # Generate the handler url for the specified handler. - # - # element is the html element containing the xblock requesting the url - # handlerName is the name of the handler - # suffix is the optional url suffix to include in the handler url - # query is an optional query-string (note, this should not include a preceding ? or &) - handlerUrl: (element, handlerName, suffix, query) -> - handlerPrefix = $(element).data("handler-prefix") - suffix = if suffix? then "/#{suffix}" else '' - query = if query? then "?#{query}" else '' - "#{handlerPrefix}/#{handlerName}#{suffix}#{query}" - - # A list of xblock children of this element - children: children - - # A map of name -> child for the xblock children of this element - childMap: childMap - } +class XBlock.Runtime.v1 + constructor: (@element, @children) -> + @childMap = {} + $.each @children, (idx, child) => + @childMap[child.name] = child diff --git a/lms/djangoapps/courseware/module_render.py b/lms/djangoapps/courseware/module_render.py index 207e46f638..952d3734df 100644 --- a/lms/djangoapps/courseware/module_render.py +++ b/lms/djangoapps/courseware/module_render.py @@ -21,7 +21,7 @@ from courseware.access import has_access, get_user_role from courseware.masquerade import setup_masquerade from courseware.model_data import FieldDataCache, DjangoKeyValueStore from lms.lib.xblock.field_data import LmsFieldData -from lms.lib.xblock.runtime import LmsModuleSystem, handler_prefix, unquote_slashes +from lms.lib.xblock.runtime import LmsModuleSystem, unquote_slashes from edxmako.shortcuts import render_to_string from psychometrics.psychoanalyze import make_psychometrics_data_update_handler from student.models import anonymous_id_for_user, user_by_anonymous_id @@ -339,7 +339,7 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours # Wrap the output display in a single div to allow for the XModule # javascript to be bound correctly if wrap_xmodule_display is True: - block_wrappers.append(partial(wrap_xblock, partial(handler_prefix, course_id))) + block_wrappers.append(partial(wrap_xblock, 'LmsRuntime', extra_data={'course-id': course_id})) # TODO (cpennington): When modules are shared between courses, the static # prefix is going to have to be specific to the module, not the directory @@ -379,7 +379,8 @@ def get_module_for_descriptor_internal(user, descriptor, field_data_cache, cours # As we have the time to manually test more modules, we can add to the list # of modules that get the per-course anonymized id. is_pure_xblock = isinstance(descriptor, XBlock) and not isinstance(descriptor, XModuleDescriptor) - is_lti_module = not is_pure_xblock and issubclass(descriptor.module_class, LTIModule) + module_class = getattr(descriptor, 'module_class', None) + is_lti_module = not is_pure_xblock and issubclass(module_class, LTIModule) if is_pure_xblock or is_lti_module: anonymous_student_id = anonymous_id_for_user(user, course_id) else: diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py index 2000c83900..ef58761c54 100644 --- a/lms/djangoapps/instructor/views/instructor_dashboard.py +++ b/lms/djangoapps/instructor/views/instructor_dashboard.py @@ -24,7 +24,6 @@ from django_comment_client.utils import has_forum_access from django_comment_common.models import FORUM_ROLE_ADMINISTRATOR from student.models import CourseEnrollment from bulk_email.models import CourseAuthorization -from lms.lib.xblock.runtime import handler_prefix from .tools import get_units_with_due_date, title_or_url @@ -206,7 +205,7 @@ def _section_send_email(course_id, access, course): ScopeIds(None, None, None, 'i4x://dummy_org/dummy_course/html/dummy_name') ) fragment = course.system.render(html_module, 'studio_view') - fragment = wrap_xblock(partial(handler_prefix, course_id), html_module, 'studio_view', fragment, None) + fragment = wrap_xblock('LmsRuntime', html_module, 'studio_view', fragment, None, extra_data={"course-id": course_id}) email_editor = fragment.content section_data = { 'section_key': 'send_email', diff --git a/lms/djangoapps/instructor/views/legacy.py b/lms/djangoapps/instructor/views/legacy.py index c671b28922..0fabf5d376 100644 --- a/lms/djangoapps/instructor/views/legacy.py +++ b/lms/djangoapps/instructor/views/legacy.py @@ -61,7 +61,6 @@ import track.views from xblock.field_data import DictFieldData from xblock.fields import ScopeIds from django.utils.translation import ugettext as _u -from lms.lib.xblock.runtime import handler_prefix from microsite_configuration.middleware import MicrositeConfiguration @@ -848,7 +847,7 @@ def instructor_dashboard(request, course_id): ScopeIds(None, None, None, 'i4x://dummy_org/dummy_course/html/dummy_name') ) fragment = html_module.render('studio_view') - fragment = wrap_xblock(partial(handler_prefix, course_id), html_module, 'studio_view', fragment, None) + fragment = wrap_xblock('LmsRuntime', html_module, 'studio_view', fragment, None, extra_data={"course-id": course_id}) email_editor = fragment.content # Enable instructor email only if the following conditions are met: diff --git a/lms/envs/common.py b/lms/envs/common.py index 5bc8f27d32..7ffd64b4ea 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -754,6 +754,7 @@ main_vendor_js = [ 'js/vendor/ova/ova.js', 'js/vendor/ova/catch/js/catch.js', 'js/vendor/ova/catch/js/handlebars-1.1.2.js' + 'js/vendor/URI.min.js' ] discussion_js = sorted(rooted_glob(COMMON_ROOT / 'static', 'coffee/src/discussion/**/*.js')) @@ -819,17 +820,18 @@ PIPELINE_CSS = { } +common_js = set(rooted_glob(COMMON_ROOT / 'static', 'coffee/src/**/*.js')) - set(courseware_js + discussion_js + staff_grading_js + open_ended_js + notes_js + instructor_dash_js) +project_js = set(rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/**/*.js')) - set(courseware_js + discussion_js + staff_grading_js + open_ended_js + notes_js + instructor_dash_js) + + + # test_order: Determines the position of this chunk of javascript on # the jasmine test page PIPELINE_JS = { 'application': { # Application will contain all paths not in courseware_only_js - 'source_filenames': sorted( - set(rooted_glob(COMMON_ROOT / 'static', 'coffee/src/**/*.js') + - rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/**/*.js')) - - set(courseware_js + discussion_js + staff_grading_js + open_ended_js + notes_js + instructor_dash_js) - ) + [ + 'source_filenames': sorted(common_js) + sorted(project_js) + [ 'js/form.ext.js', 'js/my_courses_dropdown.js', 'js/toggle_login_modal.js', diff --git a/lms/lib/xblock/runtime.py b/lms/lib/xblock/runtime.py index 74066a6af1..a339a0a98a 100644 --- a/lms/lib/xblock/runtime.py +++ b/lms/lib/xblock/runtime.py @@ -58,63 +58,6 @@ def unquote_slashes(text): 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(unicode(block.scope_ids.usage_id).encode('utf-8')), - '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 @@ -127,7 +70,34 @@ class LmsHandlerUrls(object): # 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) + view_name = 'xblock_handler' + if handler_name: + # Be sure this is really a handler. + func = getattr(block, handler_name, None) + if not func: + raise ValueError("{!r} is not a function name".format(handler_name)) + if not getattr(func, "_is_xblock_handler", False): + raise ValueError("{!r} is not a handler name".format(handler_name)) + + if thirdparty: + view_name = 'xblock_handler_noauth' + + url = reverse(view_name, kwargs={ + 'course_id': self.course_id, + 'usage_id': quote_slashes(unicode(block.scope_ids.usage_id).encode('utf-8')), + 'handler': handler_name, + '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 class LmsModuleSystem(LmsHandlerUrls, ModuleSystem): # pylint: disable=abstract-method diff --git a/lms/static/coffee/src/xblock/lms.runtime.v1.coffee b/lms/static/coffee/src/xblock/lms.runtime.v1.coffee new file mode 100644 index 0000000000..9a999727a3 --- /dev/null +++ b/lms/static/coffee/src/xblock/lms.runtime.v1.coffee @@ -0,0 +1,18 @@ +@LmsRuntime = {} + +class LmsRuntime.v1 extends XBlock.Runtime.v1 + handlerUrl: (element, handlerName, suffix, query, thirdparty) -> + courseId = $(@element).data("course-id") + usageId = $(@element).data("usage-id") + handlerAuth = if thirdparty then "handler_noauth" else "handler" + + uri = URI('/courses').segment(courseId) + .segment('xblock') + .segment(usageId) + .segment(handlerAuth) + .segment(handlerName) + + if suffix? then uri.segment(suffix) + if query? then uri.search(query) + + uri.toString() diff --git a/lms/static/js_test.yml b/lms/static/js_test.yml index 6fa88250ff..4cec8c06e7 100644 --- a/lms/static/js_test.yml +++ b/lms/static/js_test.yml @@ -40,6 +40,7 @@ lib_paths: - xmodule_js/common_static/js/vendor/jquery.cookie.js - xmodule_js/common_static/js/vendor/flot/jquery.flot.js - xmodule_js/common_static/js/vendor/CodeMirror/codemirror.js + - xmodule_js/common_static/js/vendor/URI.min.js - xmodule_js/common_static/coffee/src/jquery.immediateDescendents.js - xmodule_js/common_static/coffee/src/xblock - xmodule_js/src/capa/