Commit 9126777e by Calen Pennington

Add test of local_resource_url in AcidBlock

parent 39563c32
import unittest
from django.conf import settings
from xmodule import templates
from xmodule.modulestore.tests import persistent_factories
from xmodule.course_module import CourseDescriptor
......@@ -9,7 +11,6 @@ from xmodule.modulestore.locator import CourseLocator, BlockUsageLocator, LocalI
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.html_module import HtmlDescriptor
from xmodule.modulestore import inheritance
from xmodule.x_module import prefer_xmodules
from xblock.core import XBlock
......@@ -252,7 +253,7 @@ class TemplateTests(unittest.TestCase):
class_ = XBlock.load_class(
json_data.get('category', json_data.get('location', {}).get('category')),
default_class,
select=prefer_xmodules
select=settings.XBLOCK_SELECT_FUNCTION
)
usage_id = json_data.get('_id', None)
if not '_inherited_settings' in json_data and parent_xblock is not None:
......
from __future__ import absolute_import
import json
import logging
from collections import defaultdict
......@@ -147,7 +149,7 @@ def _load_mixed_class(category):
"""
Load an XBlock by category name, and apply all defined mixins
"""
component_class = XBlock.load_class(category, select=prefer_xmodules)
component_class = XBlock.load_class(category, select=settings.XBLOCK_SELECT_FUNCTION)
mixologist = Mixologist(settings.XBLOCK_MIXINS)
return mixologist.mix(component_class)
......
"""Views for items (modules)."""
from __future__ import absolute_import
import hashlib
import logging
......@@ -37,7 +38,7 @@ from .helpers import _xmodule_recurse
from contentstore.views.preview import get_preview_fragment
from edxmako.shortcuts import render_to_string
from models.settings.course_grading import CourseGradingModel
from cms.lib.xblock.runtime import handler_url
from cms.lib.xblock.runtime import handler_url, local_resource_url
__all__ = ['orphan_handler', 'xblock_handler', 'xblock_view_handler']
......@@ -50,6 +51,7 @@ CREATE_IF_NOT_FOUND = ['course_info']
# 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
xmodule.x_module.descriptor_global_local_resource_url = local_resource_url
def hash_resource(resource):
......
from __future__ import absolute_import
import logging
import hashlib
from functools import partial
......@@ -21,6 +23,7 @@ from xblock.fragment import Fragment
from lms.lib.xblock.field_data import LmsFieldData
from lms.lib.xblock.runtime import quote_slashes, unquote_slashes
from cms.lib.xblock.runtime import local_resource_url
from util.sandboxing import can_execute_unsafe_code
......@@ -87,6 +90,9 @@ class PreviewModuleSystem(ModuleSystem): # pylint: disable=abstract-method
'suffix': suffix,
}) + '?' + query
def local_resource_url(self, block, uri):
return local_resource_url(block, uri)
def _preview_module_system(request, descriptor):
"""
......
"""
An :class:`~xblock.runtime.KeyValueStore` that stores data in the django session
"""
from __future__ import absolute_import
from xblock.runtime import KeyValueStore
......
"""
Views dedicated to rendering xblocks.
"""
from __future__ import absolute_import
import logging
import mimetypes
from xblock.core import XBlock
from django.conf import settings
from django.http import Http404, HttpResponse
log = logging.getLogger(__name__)
def xblock_resource(request, block_type, uri): # pylint: disable=unused-argument
"""
Return a package resource for the specified XBlock.
"""
try:
xblock_class = XBlock.load_class(block_type, settings.XBLOCK_SELECT_FUNCTION)
content = xblock_class.open_local_resource(uri)
except IOError:
log.info('Failed to load xblock resource', exc_info=True)
raise Http404
except Exception: # pylint: disable-msg=broad-except
log.error('Failed to load xblock resource', exc_info=True)
raise Http404
mimetype, _ = mimetypes.guess_type(uri)
return HttpResponse(content, mimetype=mimetype)
......@@ -91,7 +91,6 @@ GITHUB_REPO_ROOT = ENV_ROOT / "data"
sys.path.append(REPO_ROOT)
sys.path.append(PROJECT_ROOT / 'djangoapps')
sys.path.append(PROJECT_ROOT / 'lib')
sys.path.append(COMMON_ROOT / 'djangoapps')
sys.path.append(COMMON_ROOT / 'lib')
......
......@@ -26,3 +26,12 @@ def handler_url(block, handler_name, suffix='', query='', thirdparty=False):
return url
def local_resource_url(block, uri):
"""
local_resource_url for Studio
"""
return reverse('xblock_resource_url', kwargs={
'block_type': block.scope_ids.block_type,
'uri': uri,
})
......@@ -22,6 +22,9 @@ urlpatterns = patterns('', # nopep8
url(r'^xblock/(?P<usage_id>.*?)/handler/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$',
'contentstore.views.component_handler', name='component_handler'),
url(r'^xblock/resource/(?P<block_type>[^/]*)/(?P<uri>.*)$',
'contentstore.views.xblock.xblock_resource', name='xblock_resource_url'),
# temporary landing page for a course
url(r'^edge/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$',
'contentstore.views.landing', name='landing'),
......
......@@ -1496,7 +1496,7 @@ class SplitMongoModuleStore(ModuleStoreWriteBase):
"""
if fields is None:
return {}
cls = self.mixologist.mix(XBlock.load_class(category, select=prefer_xmodules))
cls = self.mixologist.mix(XBlock.load_class(category, select=self.xblock_select))
result = collections.defaultdict(dict)
for field_name, value in fields.iteritems():
field = getattr(cls, field_name)
......
......@@ -47,6 +47,9 @@ class TestModuleSystem(ModuleSystem): # pylint: disable=abstract-method
def handler_url(self, block, handler, suffix='', query='', thirdparty=False):
return str(block.scope_ids.usage_id) + '/' + handler + '/' + suffix + '?' + query
def local_resource_url(self, block, uri):
return 'resource/' + str(block.scope_ids.block_type) + '/' + uri
def get_test_system(course_id=''):
"""
......
......@@ -143,6 +143,9 @@ class XModuleMixin(XBlockMixin):
@property
def system(self):
"""
Return the XBlock runtime (backwards compatibility alias provided for XModules).
"""
return self.runtime
@property
......@@ -893,8 +896,21 @@ class ConfigurableFragmentWrapper(object): # pylint: disable=abstract-method
# This function exists to give applications (LMS/CMS) a place to monkey-patch until
# we can refactor modulestore to split out the FieldData half of its interface from
# the Runtime part of its interface. This function matches the Runtime.handler_url interface
def descriptor_global_handler_url(block, handler_name, suffix='', query='', thirdparty=False):
raise NotImplementedError("Applications must monkey-patch this function before using handler-urls for studio_view")
def descriptor_global_handler_url(block, handler_name, suffix='', query='', thirdparty=False): # pylint: disable=invalid-name, unused-argument
"""
See :meth:`xblock.runtime.Runtime.handler_url`.
"""
raise NotImplementedError("Applications must monkey-patch this function before using handler_url for studio_view")
# This function exists to give applications (LMS/CMS) a place to monkey-patch until
# we can refactor modulestore to split out the FieldData half of its interface from
# the Runtime part of its interface. This function matches the Runtime.local_resource_url interface
def descriptor_global_local_resource_url(block, uri): # pylint: disable=invalid-name, unused-argument
"""
See :meth:`xblock.runtime.Runtime.local_resource_url`.
"""
raise NotImplementedError("Applications must monkey-patch this function before using local_resource_url for studio_view")
class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable=abstract-method
......@@ -943,6 +959,8 @@ class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable
get_policy: a function that takes a usage id and returns a dict of
policy to apply.
local_resource_url: an implementation of :meth:`xblock.runtime.Runtime.local_resource_url`
"""
super(DescriptorSystem, self).__init__(**kwargs)
......@@ -1010,13 +1028,30 @@ class DescriptorSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable
# global function that the application can override.
return descriptor_global_handler_url(block, handler_name, suffix, query, thirdparty)
def resource_url(self, resource):
raise NotImplementedError("edX Platform doesn't currently implement XBlock resource urls")
def local_resource_url(self, block, uri):
"""
See :meth:`xblock.runtime.Runtime:local_resource_url` for documentation.
"""
xmodule_runtime = getattr(block, 'xmodule_runtime', None)
if xmodule_runtime is not None:
return xmodule_runtime.local_resource_url(block, uri)
else:
# Currently, Modulestore is responsible for instantiating DescriptorSystems
# This means that LMS/CMS don't have a way to define a subclass of DescriptorSystem
# that implements the correct local_resource_url. So, for now, instead, we will reference a
# global function that the application can override.
return descriptor_global_local_resource_url(block, uri)
def resource_url(self, resource):
"""
See :meth:`xblock.runtime.Runtime:resource_url` for documentation.
"""
raise NotImplementedError("edX Platform doesn't currently implement XBlock resource urls")
def publish(self, block, event):
"""
See :meth:`xblock.runtime.Runtime:publish` for documentation.
"""
raise NotImplementedError("edX Platform doesn't currently implement XBlock publish")
def add_block_as_child_node(self, block, node):
......@@ -1182,9 +1217,6 @@ class ModuleSystem(ConfigurableFragmentWrapper, Runtime): # pylint: disable=abs
def resource_url(self, resource):
raise NotImplementedError("edX Platform doesn't currently implement XBlock resource urls")
def local_resource_url(self, block, uri):
raise NotImplementedError("edX Platform doesn't currently implement XBlock resource urls")
def publish(self, block, event):
pass
......
......@@ -61,6 +61,13 @@ class AcidView(PageObject):
self.test_passed('.child-values-match')
])
@property
def resource_url_passed(self):
"""
Whether the resource-url test passed in this view of the :class:`.AcidBlock`.
"""
return self.test_passed('.local-resource-test')
def scope_passed(self, scope):
return all(
self.test_passed('.scope-storage-test.scope-{} {}'.format(scope, test))
......
......@@ -371,6 +371,7 @@ class XBlockAcidBase(UniqueCourseTest):
self.assertTrue(acid_block.init_fn_passed)
self.assertTrue(acid_block.doc_ready_passed)
self.assertTrue(acid_block.child_tests_passed)
self.assertTrue(acid_block.resource_url_passed)
self.assertTrue(acid_block.scope_passed('user_state'))
......
......@@ -159,6 +159,7 @@ class XBlockAcidBase(WebAppTest):
self.assertTrue(acid_block.init_fn_passed)
self.assertTrue(acid_block.doc_ready_passed)
self.assertTrue(acid_block.child_tests_passed)
self.assertTrue(acid_block.resource_url_passed)
self.assertTrue(acid_block.scope_passed('user_state'))
def test_acid_block_editor(self):
......@@ -175,6 +176,7 @@ class XBlockAcidBase(WebAppTest):
self.assertTrue(acid_block.init_fn_passed)
self.assertTrue(acid_block.doc_ready_passed)
self.assertTrue(acid_block.child_tests_passed)
self.assertTrue(acid_block.resource_url_passed)
self.assertTrue(acid_block.scope_passed('content'))
self.assertTrue(acid_block.scope_passed('settings'))
......
import json
import logging
import mimetypes
import static_replace
......@@ -545,6 +546,23 @@ def handle_xblock_callback(request, course_id, usage_id, handler, suffix=None):
return _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, request.user)
def xblock_resource(request, block_type, uri): # pylint: disable=unused-argument
"""
Return a package resource for the specified XBlock.
"""
try:
xblock_class = XBlock.load_class(block_type, select=settings.XBLOCK_SELECT_FUNCTION)
content = xblock_class.open_local_resource(uri)
except IOError:
log.info('Failed to load xblock resource', exc_info=True)
raise Http404
except Exception: # pylint: disable-msg=broad-except
log.error('Failed to load xblock resource', exc_info=True)
raise Http404
mimetype, _ = mimetypes.guess_type(uri)
return HttpResponse(content, mimetype=mimetype)
def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, user):
"""
Invoke an XBlock handler, either authenticated or not.
......
......@@ -99,6 +99,15 @@ class LmsHandlerUrls(object):
return url
def local_resource_url(self, block, uri):
"""
local_resource_url for Studio
"""
return reverse('xblock_resource_url', kwargs={
'block_type': block.scope_ids.block_type,
'uri': uri,
})
class LmsModuleSystem(LmsHandlerUrls, ModuleSystem): # pylint: disable=abstract-method
"""
......
......@@ -190,6 +190,9 @@ if settings.COURSEWARE_ENABLED:
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/xblock/(?P<usage_id>[^/]*)/handler_noauth/(?P<handler>[^/]*)(?:/(?P<suffix>.*))?$',
'courseware.module_render.handle_xblock_callback_noauth',
name='xblock_handler_noauth'),
url(r'xblock/resource/(?P<block_type>[^/]+)/(?P<uri>.*)$',
'courseware.module_render.xblock_resource',
name='xblock_resource_url'),
# Software Licenses
......
......@@ -23,4 +23,4 @@
-e git+https://github.com/edx/event-tracking.git@f0211d702d#egg=event-tracking
-e git+https://github.com/edx/bok-choy.git@62de7b576a08f36cde5b030c52bccb1a2f3f8df1#egg=bok_choy
-e git+https://github.com/edx-solutions/django-splash.git@15bf143b15714e22fc451ff1b0f8a7a2a9483172#egg=django-splash
-e git+https://github.com/edx/acid-block.git@9c832513f0c01f79227bea894fba11c66fe4c08c#egg=acid-xblock
-e git+https://github.com/edx/acid-block.git@ce4c09615055a3cc14e39689e881c3422ff3162c#egg=acid-xblock
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