feat: remove usage of LmsModuleSystem and PreviewModuleSystem

This commit is contained in:
Kaustav Banerjee
2023-01-08 22:56:04 +05:30
committed by Agrendalath
parent 017f8469de
commit 18fea868a9
4 changed files with 462 additions and 201 deletions

View File

@@ -41,8 +41,7 @@ from openedx.core.lib.xblock_utils import (
request_token,
wrap_fragment,
wrap_xblock,
wrap_xblock_aside,
xblock_local_resource_url
wrap_xblock_aside
)
from ..utils import get_visibility_partition_info
@@ -94,61 +93,144 @@ def preview_handler(request, usage_key_string, handler, suffix=''):
return webob_to_django_response(resp)
def handler_url(block, handler_name, suffix='', query='', thirdparty=False):
"""
Handler URL function for Preview
"""
return reverse('preview_handler', kwargs={
'usage_key_string': str(block.scope_ids.usage_id),
'handler': handler_name,
'suffix': suffix,
}) + '?' + query
def preview_applicable_aside_types(block, applicable_aside_types=None):
"""
Remove acid_aside and honor the config record
"""
if not StudioConfig.asides_enabled(block.scope_ids.block_type):
return []
# TODO: aside_type != 'acid_aside' check should be removed once AcidBlock is only installed during tests
# (see https://openedx.atlassian.net/browse/TE-811)
return [
aside_type
for aside_type in applicable_aside_types(block)
if aside_type != 'acid_aside'
]
def render_child_placeholder(block, view_name, context, wrap_xblock=None):
"""
Renders a placeholder XBlock.
"""
return wrap_xblock(block, view_name, Fragment(), context)
def preview_layout_asides(block, context, frag, view_name, aside_frag_fns, wrap_aside=None):
position_for_asides = '<!-- footer for xblock_aside -->'
result = Fragment()
result.add_fragment_resources(frag)
for aside, aside_fn in aside_frag_fns:
aside_frag = aside_fn(block, context)
if aside_frag.content != '':
aside_frag_wrapped = wrap_aside(block, aside, view_name, aside_frag, context)
aside.save()
result.add_fragment_resources(aside_frag_wrapped)
replacement = position_for_asides + aside_frag_wrapped.content
frag.content = frag.content.replace(position_for_asides, replacement)
result.add_content(frag.content)
return result
class PreviewModuleSystem(DescriptorSystem): # pylint: disable=abstract-method
"""
An XModule ModuleSystem for use in Studio previews
"""
# xblocks can check for this attribute during rendering to determine if
# they are being rendered for preview (i.e. in Studio)
is_author_mode = True
#######################
#######################
## Set directly to system below
#######################
#######################
# is_author_mode = True
def handler_url(self, block, handler_name, suffix='', query='', thirdparty=False):
return reverse('preview_handler', kwargs={
'usage_key_string': str(block.scope_ids.usage_id),
'handler': handler_name,
'suffix': suffix,
}) + '?' + query
#######################
#######################
## Implemented as handler_url above
#######################
#######################
# def handler_url(self, block, handler_name, suffix='', query='', thirdparty=False):
# return reverse('preview_handler', kwargs={
# 'usage_key_string': str(block.scope_ids.usage_id),
# 'handler': handler_name,
# 'suffix': suffix,
# }) + '?' + query
def local_resource_url(self, block, uri):
return xblock_local_resource_url(block, uri)
#######################
#######################
## Being monkey patched in Descriptor system from cms.djangoapps.xblock_config.apps.py
#######################
#######################
# def local_resource_url(self, block, uri):
# return xblock_local_resource_url(block, uri)
def applicable_aside_types(self, block):
"""
Remove acid_aside and honor the config record
"""
if not StudioConfig.asides_enabled(block.scope_ids.block_type):
return []
#######################
#######################
## Implemented as preview_applicable_aside_types above
#######################
#######################
# def applicable_aside_types(self, block):
# """
# Remove acid_aside and honor the config record
# """
# if not StudioConfig.asides_enabled(block.scope_ids.block_type):
# return []
# TODO: aside_type != 'acid_aside' check should be removed once AcidBlock is only installed during tests
# (see https://openedx.atlassian.net/browse/TE-811)
return [
aside_type
for aside_type in super().applicable_aside_types(block)
if aside_type != 'acid_aside'
]
# # TODO: aside_type != 'acid_aside' check should be removed once AcidBlock is only installed during tests
# # (see https://openedx.atlassian.net/browse/TE-811)
# return [
# aside_type
# for aside_type in super().applicable_aside_types(block)
# if aside_type != 'acid_aside'
# ]
def render_child_placeholder(self, block, view_name, context):
"""
Renders a placeholder XBlock.
"""
return self.wrap_xblock(block, view_name, Fragment(), context)
#######################
#######################
## Implemented as render_child_placeholder above
#######################
#######################
# def render_child_placeholder(self, block, view_name, context):
# """
# Renders a placeholder XBlock.
# """
# return self.wrap_xblock(block, view_name, Fragment(), context)
def layout_asides(self, block, context, frag, view_name, aside_frag_fns):
position_for_asides = '<!-- footer for xblock_aside -->'
result = Fragment()
result.add_fragment_resources(frag)
#######################
#######################
## Implemented as preview_layout_asides above
#######################
#######################
# def layout_asides(self, block, context, frag, view_name, aside_frag_fns):
# position_for_asides = '<!-- footer for xblock_aside -->'
# result = Fragment()
# result.add_fragment_resources(frag)
for aside, aside_fn in aside_frag_fns:
aside_frag = aside_fn(block, context)
if aside_frag.content != '':
aside_frag_wrapped = self.wrap_aside(block, aside, view_name, aside_frag, context)
aside.save()
result.add_fragment_resources(aside_frag_wrapped)
replacement = position_for_asides + aside_frag_wrapped.content
frag.content = frag.content.replace(position_for_asides, replacement)
# for aside, aside_fn in aside_frag_fns:
# aside_frag = aside_fn(block, context)
# if aside_frag.content != '':
# aside_frag_wrapped = self.wrap_aside(block, aside, view_name, aside_frag, context)
# aside.save()
# result.add_fragment_resources(aside_frag_wrapped)
# replacement = position_for_asides + aside_frag_wrapped.content
# frag.content = frag.content.replace(position_for_asides, replacement)
result.add_content(frag.content)
return result
# result.add_content(frag.content)
# return result
def _preview_module_system(request, descriptor, field_data):
@@ -206,35 +288,74 @@ def _preview_module_system(request, descriptor, field_data):
else:
preview_anonymous_user_id = anonymous_id_for_user(request.user, course_id)
return PreviewModuleSystem(
load_item=descriptor._runtime.load_item,
resources_fs=descriptor._runtime.resources_fs,
error_tracker=descriptor._runtime.error_tracker,
get_block=partial(_load_preview_block, request),
mixins=settings.XBLOCK_MIXINS,
services={
"field-data": field_data,
"i18n": XBlockI18nService,
'mako': mako_service,
"settings": SettingsService(),
"user": DjangoXBlockUserService(
request.user,
user_role=get_user_role(request.user, course_id),
anonymous_user_id=preview_anonymous_user_id,
),
"partitions": StudioPartitionService(course_id=course_id),
"teams_configuration": TeamsConfigurationService(),
"sandbox": SandboxService(contentstore=contentstore, course_id=course_id),
"cache": CacheService(cache),
'replace_urls': replace_url_service
}
# Set up functions to modify the fragment produced by student_view
wrappers=wrappers,
wrappers_asides=wrappers_asides,
# Get the raw DescriptorSystem, not the CombinedSystem
descriptor_runtime=descriptor._runtime, # pylint: disable=protected-access
services={
"field-data": field_data,
"i18n": XBlockI18nService,
'mako': mako_service,
"settings": SettingsService(),
"user": DjangoXBlockUserService(
request.user,
user_role=get_user_role(request.user, course_id),
anonymous_user_id=preview_anonymous_user_id,
),
"partitions": StudioPartitionService(course_id=course_id),
"teams_configuration": TeamsConfigurationService(),
"sandbox": SandboxService(contentstore=contentstore, course_id=course_id),
"cache": CacheService(cache),
'replace_urls': replace_url_service
},
# system = PreviewModuleSystem(
# load_item=descriptor._runtime.load_item,
# resources_fs=descriptor._runtime.resources_fs,
# error_tracker=descriptor._runtime.error_tracker,
# # get_module=partial(_load_preview_module, request),
# # mixins=settings.XBLOCK_MIXINS,
# # Set up functions to modify the fragment produced by student_view
# # wrappers=wrappers,
# # wrappers_asides=wrappers_asides,
# # Get the raw DescriptorSystem, not the CombinedSystem
# # descriptor_runtime=descriptor._runtime, # pylint: disable=protected-access
# # services={
# # "field-data": field_data,
# # "i18n": ModuleI18nService,
# # 'mako': mako_service,
# # "settings": SettingsService(),
# # "user": DjangoXBlockUserService(
# # request.user,
# # user_role=get_user_role(request.user, course_id),
# # anonymous_user_id=preview_anonymous_user_id,
# # ),
# # "partitions": StudioPartitionService(course_id=course_id),
# # "teams_configuration": TeamsConfigurationService(),
# # "sandbox": SandboxService(contentstore=contentstore, course_id=course_id),
# # "cache": CacheService(cache),
# # 'replace_urls': replace_url_service
# # },
# )
descriptor._runtime.get_block = partial(_load_preview_block, request),
descriptor._runtime.mixins = settings.XBLOCK_MIXINS
# Set up functions to modify the fragment produced by student_view
descriptor._runtime.wrappers = wrappers
descriptor._runtime.wrappers_asides=wrappers_asides
descriptor._runtime._services.update(services)
descriptor._runtime.is_author_mode = True
descriptor._runtime.handler_url_override = handler_url
descriptor._runtime.applicable_aside_types_override = preview_applicable_aside_types
descriptor._runtime.render_child_placeholder = partial(
render_child_placeholder,
wrap_xblock = descriptor._runtime.wrap_xblock
)
descriptor._runtime.layout_asides_override = partial(
preview_layout_asides,
wrap_aside = descriptor._runtime.wrap_aside
)
return descriptor._runtime
class StudioPartitionService(PartitionService):

View File

@@ -67,7 +67,7 @@ from lms.djangoapps.courseware.field_overrides import OverrideFieldData
from lms.djangoapps.courseware.services import UserStateService
from lms.djangoapps.grades.api import GradesUtilService
from lms.djangoapps.lms_xblock.field_data import LmsFieldData
from lms.djangoapps.lms_xblock.runtime import LmsModuleSystem, UserTagsService
from lms.djangoapps.lms_xblock.runtime import UserTagsService, lms_wrappers_aside, lms_applicable_aside_types
from lms.djangoapps.verify_student.services import XBlockVerificationService
from openedx.core.djangoapps.bookmarks.api import BookmarksService
from openedx.core.djangoapps.crawlers.models import CrawlersConfig
@@ -587,50 +587,94 @@ def get_module_system_for_user(
store = modulestore()
system = LmsModuleSystem(
load_item=descriptor._runtime.load_item,
resources_fs=descriptor._runtime.resources_fs,
error_tracker=descriptor._runtime.error_tracker,
get_block=inner_get_block,
# TODO: When we merge the descriptor and module systems, we can stop reaching into the mixologist (cpennington)
mixins=descriptor.runtime.mixologist._mixins, # pylint: disable=protected-access
wrappers=block_wrappers,
services={
'fs': FSService(),
'field-data': field_data,
'mako': mako_service,
'user': user_service,
'verification': XBlockVerificationService(),
'proctoring': ProctoringService(),
'milestones': milestones_helpers.get_service(),
'credit': CreditService(),
'bookmarks': BookmarksService(user=user),
'gating': GatingService(),
'grade_utils': GradesUtilService(course_id=course_id),
'user_state': UserStateService(),
'content_type_gating': ContentTypeGatingService(),
'cache': CacheService(cache),
'sandbox': SandboxService(contentstore=contentstore, course_id=course_id),
'xqueue': xqueue_service,
'replace_urls': replace_url_service,
'rebind_user': rebind_user_service,
'completion': CompletionService(user=user, context_key=course_id)
if user and user.is_authenticated
else None,
'i18n': XBlockI18nService,
'library_tools': LibraryToolsService(store, user_id=user.id if user else None),
'partitions': PartitionService(course_id=course_id, cache=DEFAULT_REQUEST_CACHE.data),
'settings': SettingsService(),
'user_tags': UserTagsService(user=user, course_id=course_id),
'badging': BadgingService(course_id=course_id, modulestore=store) if badges_enabled() else None,
'teams': TeamsService(),
'teams_configuration': TeamsConfigurationService(),
'call_to_action': CallToActionService(),
'publish': EventPublishingService(user, course_id, track_function),
},
descriptor_runtime=descriptor._runtime, # pylint: disable=protected-access
request_token=request_token,
)
services={
'fs': FSService(),
'field-data': field_data,
'mako': mako_service,
'user': user_service,
'verification': XBlockVerificationService(),
'proctoring': ProctoringService(),
'milestones': milestones_helpers.get_service(),
'credit': CreditService(),
'bookmarks': BookmarksService(user=user),
'gating': GatingService(),
'grade_utils': GradesUtilService(course_id=course_id),
'user_state': UserStateService(),
'content_type_gating': ContentTypeGatingService(),
'cache': CacheService(cache),
'sandbox': SandboxService(contentstore=contentstore, course_id=course_id),
'xqueue': xqueue_service,
'replace_urls': replace_url_service,
'rebind_user': rebind_user_service,
'completion': CompletionService(user=user, context_key=course_id)
if user and user.is_authenticated
else None,
'i18n': XBlockI18nService,
'library_tools': LibraryToolsService(store, user_id=user.id if user else None),
'partitions': PartitionService(course_id=course_id, cache=DEFAULT_REQUEST_CACHE.data),
'settings': SettingsService(),
'user_tags': UserTagsService(user=user, course_id=course_id),
'badging': BadgingService(course_id=course_id, modulestore=store) if badges_enabled() else None,
'teams': TeamsService(),
'teams_configuration': TeamsConfigurationService(),
'call_to_action': CallToActionService(),
'publish': EventPublishingService(user, course_id, track_function),
}
# system = LmsModuleSystem(
# load_item=descriptor._runtime.load_item,
# resources_fs=descriptor._runtime.resources_fs,
# error_tracker=descriptor._runtime.error_tracker,
# # get_module=inner_get_module,
# # TODO: When we merge the descriptor and module systems, we can stop reaching into the mixologist (cpennington)
# # mixins=descriptor.runtime.mixologist._mixins, # pylint: disable=protected-access
# # wrappers=block_wrappers,
# # services={
# # 'fs': FSService(),
# # 'field-data': field_data,
# # 'mako': mako_service,
# # 'user': user_service,
# # 'verification': XBlockVerificationService(),
# # 'proctoring': ProctoringService(),
# # 'milestones': milestones_helpers.get_service(),
# # 'credit': CreditService(),
# # 'bookmarks': BookmarksService(user=user),
# # 'gating': GatingService(),
# # 'grade_utils': GradesUtilService(course_id=course_id),
# # 'user_state': UserStateService(),
# # 'content_type_gating': ContentTypeGatingService(),
# # 'cache': CacheService(cache),
# # 'sandbox': SandboxService(contentstore=contentstore, course_id=course_id),
# # 'xqueue': xqueue_service,
# # 'replace_urls': replace_url_service,
# # 'rebind_user': rebind_user_service,
# # 'completion': CompletionService(user=user, context_key=course_id)
# # if user and user.is_authenticated
# # else None,
# # 'i18n': ModuleI18nService,
# # 'library_tools': LibraryToolsService(store, user_id=user.id if user else None),
# # 'partitions': PartitionService(course_id=course_id, cache=DEFAULT_REQUEST_CACHE.data),
# # 'settings': SettingsService(),
# # 'user_tags': UserTagsService(user=user, course_id=course_id),
# # 'badging': BadgingService(course_id=course_id, modulestore=store) if badges_enabled() else None,
# # 'teams': TeamsService(),
# # 'teams_configuration': TeamsConfigurationService(),
# # 'call_to_action': CallToActionService(),
# # 'publish': EventPublishingService(user, course_id, track_function),
# # },
# # descriptor_runtime=descriptor._runtime, # pylint: disable=protected-access
# # request_token=request_token,
# )
descriptor._runtime.get_block = inner_get_block
descriptor._runtime.mixins = descriptor.runtime.mixologist._mixins
descriptor._runtime.wrappers = block_wrappers
descriptor._runtime._services.update(services)
descriptor._runtime.request_token = request_token
descriptor._runtime.wrap_asides_override = lms_wrappers_aside
descriptor._runtime.applicable_aside_types_override = lms_applicable_aside_types
# pass position specified in URL to module through ModuleSystem
if position is not None:
@@ -640,14 +684,14 @@ def get_module_system_for_user(
log.exception('Non-integer %r passed as position.', position)
position = None
system.set('position', position)
descriptor._runtime.set('position', position)
system.set('user_is_staff', user_is_staff)
system.set('user_is_admin', bool(has_access(user, 'staff', 'global')))
system.set('user_is_beta_tester', CourseBetaTesterRole(course_id).has_user(user))
system.set('days_early_for_beta', descriptor.days_early_for_beta)
descriptor._runtime.set('user_is_staff', user_is_staff)
descriptor._runtime.set('user_is_admin', bool(has_access(user, 'staff', 'global')))
descriptor._runtime.set('user_is_beta_tester', CourseBetaTesterRole(course_id).has_user(user))
descriptor._runtime.set('days_early_for_beta', descriptor.days_early_for_beta)
return system, field_data
return descriptor._runtime, field_data
# TODO: Find all the places that this method is called and figure out how to

View File

@@ -72,6 +72,64 @@ def local_resource_url(block, uri):
return xblock_local_resource_url(block, uri)
def lms_wrappers_aside(block, aside, view, frag, context, request_token=None):
"""
Creates a div which identifies the aside, points to the original block,
and writes out the json_init_args into a script tag.
The default implementation creates a frag to wraps frag w/ a div identifying the xblock. If you have
javascript, you'll need to override this impl
"""
if not frag.content:
return frag
runtime_class = 'LmsRuntime'
extra_data = {
'block-id': quote_slashes(str(block.scope_ids.usage_id)),
'course-id': quote_slashes(str(block.scope_ids.usage_id.context_key)),
'url-selector': 'asideBaseUrl',
'runtime-class': runtime_class,
}
if request_token:
extra_data['request-token'] = request_token
return wrap_xblock_aside(
runtime_class,
aside,
view,
frag,
context,
usage_id_serializer=str,
request_token=request_token,
extra_data=extra_data,
)
def lms_applicable_aside_types(block, applicable_aside_types=None):
"""
Return all of the asides which might be decorating this `block`.
Arguments:
block (:class:`.XBlock`): The block to render retrieve asides for.
"""
config = XBlockAsidesConfig.current()
if not config.enabled:
return []
if block.scope_ids.block_type in config.disabled_blocks.split():
return []
# TODO: aside_type != 'acid_aside' check should be removed once AcidBlock is only installed during tests
# (see https://openedx.atlassian.net/browse/TE-811)
return [
aside_type
for aside_type in applicable_aside_types(block)
if aside_type != 'acid_aside'
]
class UserTagsService:
"""
A runtime class that provides an interface to the user service. It handles filling in
@@ -121,85 +179,114 @@ class LmsModuleSystem(DescriptorSystem): # pylint: disable=abstract-method
ModuleSystem specialized to the LMS
"""
def __init__(self, **kwargs):
self.request_token = kwargs.pop('request_token', None)
#######################
#######################
## Set Directly from module_render
#######################
#######################
# self.request_token = kwargs.pop('request_token', None)
super().__init__(**kwargs)
def handler_url(self, *args, **kwargs): # lint-amnesty, pylint: disable=signature-differs
"""
Implement the XBlock runtime handler_url interface.
#######################
#######################
## Being monkey patched in Descriptor system from lms.djangoapps.lms_xblock.apps.py
#######################
#######################
This is mostly just proxying to the module level `handler_url` function
defined higher up in this file.
# def handler_url(self, *args, **kwargs): # lint-amnesty, pylint: disable=signature-differs
# """
# Implement the XBlock runtime handler_url interface.
We're doing this indirection because the module level `handler_url`
logic is also needed by the `DescriptorSystem`. The particular
`handler_url` that a `DescriptorSystem` needs will be different when
running an LMS process or a CMS/Studio process. That's accomplished by
monkey-patching a global. It's a long story, but please know that you
can't just refactor and fold that logic into here without breaking
things.
# This is mostly just proxying to the module level `handler_url` function
# defined higher up in this file.
https://openedx.atlassian.net/wiki/display/PLAT/Convert+from+Storage-centric+runtimes+to+Application-centric+runtimes
# We're doing this indirection because the module level `handler_url`
# logic is also needed by the `DescriptorSystem`. The particular
# `handler_url` that a `DescriptorSystem` needs will be different when
# running an LMS process or a CMS/Studio process. That's accomplished by
# monkey-patching a global. It's a long story, but please know that you
# can't just refactor and fold that logic into here without breaking
# things.
See :method:`xblock.runtime:Runtime.handler_url`
"""
return handler_url(*args, **kwargs)
# https://openedx.atlassian.net/wiki/display/PLAT/Convert+from+Storage-centric+runtimes+to+Application-centric+runtimes
def local_resource_url(self, *args, **kwargs):
return local_resource_url(*args, **kwargs)
# See :method:`xblock.runtime:Runtime.handler_url`
# """
# return handler_url(*args, **kwargs)
def wrap_aside(self, block, aside, view, frag, context):
"""
Creates a div which identifies the aside, points to the original block,
and writes out the json_init_args into a script tag.
#######################
#######################
## Being monkey patched in Descriptor system from lms.djangoapps.lms_xblock.apps.py
#######################
#######################
The default implementation creates a frag to wraps frag w/ a div identifying the xblock. If you have
javascript, you'll need to override this impl
"""
if not frag.content:
return frag
# def local_resource_url(self, *args, **kwargs):
# return local_resource_url(*args, **kwargs)
runtime_class = 'LmsRuntime'
extra_data = {
'block-id': quote_slashes(str(block.scope_ids.usage_id)),
'course-id': quote_slashes(str(block.course_id)),
'url-selector': 'asideBaseUrl',
'runtime-class': runtime_class,
}
if self.request_token:
extra_data['request-token'] = self.request_token
#######################
#######################
## Implemented as lms_wrappers_aside above
#######################
#######################
return wrap_xblock_aside(
runtime_class,
aside,
view,
frag,
context,
usage_id_serializer=str,
request_token=self.request_token,
extra_data=extra_data,
)
# def wrap_aside(self, block, aside, view, frag, context):
# """
# Creates a div which identifies the aside, points to the original block,
# and writes out the json_init_args into a script tag.
def applicable_aside_types(self, block):
"""
Return all of the asides which might be decorating this `block`.
# The default implementation creates a frag to wraps frag w/ a div identifying the xblock. If you have
# javascript, you'll need to override this impl
# """
# if not frag.content:
# return frag
Arguments:
block (:class:`.XBlock`): The block to render retrieve asides for.
"""
# runtime_class = 'LmsRuntime'
# extra_data = {
# 'block-id': quote_slashes(str(block.scope_ids.usage_id)),
# 'course-id': quote_slashes(str(block.course_id)),
# 'url-selector': 'asideBaseUrl',
# 'runtime-class': runtime_class,
# }
# if self.request_token:
# extra_data['request-token'] = self.request_token
config = XBlockAsidesConfig.current()
# return wrap_xblock_aside(
# runtime_class,
# aside,
# view,
# frag,
# context,
# usage_id_serializer=str,
# request_token=self.request_token,
# extra_data=extra_data,
# )
if not config.enabled:
return []
#######################
#######################
## Implemented as lms_applicable_aside_types above
#######################
#######################
if block.scope_ids.block_type in config.disabled_blocks.split():
return []
# def applicable_aside_types(self, block):
# """
# Return all of the asides which might be decorating this `block`.
# TODO: aside_type != 'acid_aside' check should be removed once AcidBlock is only installed during tests
# (see https://openedx.atlassian.net/browse/TE-811)
return [
aside_type
for aside_type in super().applicable_aside_types(block)
if aside_type != 'acid_aside'
]
# Arguments:
# block (:class:`.XBlock`): The block to render retrieve asides for.
# """
# config = XBlockAsidesConfig.current()
# if not config.enabled:
# return []
# if block.scope_ids.block_type in config.disabled_blocks.split():
# return []
# # TODO: aside_type != 'acid_aside' check should be removed once AcidBlock is only installed during tests
# # (see https://openedx.atlassian.net/browse/TE-811)
# return [
# aside_type
# for aside_type in super().applicable_aside_types(block)
# if aside_type != 'acid_aside'
# ]

View File

@@ -1420,17 +1420,8 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemSh
"""
Base class for :class:`Runtime`s to be used with :class:`XModuleDescriptor`s
"""
def get(self, attr):
""" provide uniform access to attributes (like etree)."""
return self.__dict__.get(attr)
def set(self, attr, val):
"""provide uniform access to attributes (like etree)"""
self.__dict__[attr] = val
def __init__(
self, load_item, resources_fs, error_tracker, descriptor_runtime=None, get_policy=None, disabled_xblock_types=lambda: [], get_module=None, **kwargs
self, load_item, resources_fs, error_tracker, get_policy=None, disabled_xblock_types=lambda: [], get_module=None, **kwargs
):
"""
load_item: Takes a Location and returns an XModuleDescriptor
@@ -1467,7 +1458,10 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemSh
self.disabled_xblock_types = disabled_xblock_types
self.get_module = get_module
self.descriptor_runtime = descriptor_runtime
# self.handler_url_override = None
# self.applicable_aside_types_override = None
# self.wrap_asides_override = None
# self.layout_asides_override = None
def get(self, attr):
""" provide uniform access to attributes (like etree)."""
@@ -1479,11 +1473,10 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemSh
def get_block(self, usage_id, for_parent=None):
"""See documentation for `xblock.runtime:Runtime.get_block`"""
block = self.load_item(usage_id, for_parent=for_parent)
if self.get_module:
# return self.get_module(block)
return self.get_module(self.descriptor_runtime.get_block(usage_id, for_parent=for_parent))
return self.load_item(usage_id, for_parent=for_parent)
return self.get_module(block)
return block
def load_block_type(self, block_type):
"""
@@ -1522,6 +1515,8 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemSh
# This means that LMS/CMS don't have a way to define a subclass of DescriptorSystem
# that implements the correct handler url. So, for now, instead, we will reference a
# global function that the application can override.
if getattr(self, 'handler_url_override', None):
return self.handler_url_override(block, handler_name, suffix, query, thirdparty)
return descriptor_global_handler_url(block, handler_name, suffix, query, thirdparty)
def local_resource_url(self, block, uri):
@@ -1538,6 +1533,9 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemSh
"""
See :meth:`xblock.runtime.Runtime:applicable_aside_types` for documentation.
"""
if getattr(self, 'applicable_aside_types_override', None):
return self.applicable_aside_types_override(block, applicable_aside_types=super().applicable_aside_types)
potential_set = set(super().applicable_aside_types(block))
if getattr(block, 'xmodule_runtime', None) is not None:
if hasattr(block.xmodule_runtime, 'applicable_aside_types'):
@@ -1586,6 +1584,16 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, ModuleSystemSh
return service(block)
return service
def wrap_aside(self, block, aside, view, frag, context):
if getattr(self, 'wrap_asides_override', None):
return self.wrap_asides_override(block, aside, view, frag, context, request_token=self.request_token)
return super().wrap_aside(block, aside, view, frag, context)
def layout_asides(self, block, context, frag, view_name, aside_frag_fns):
if getattr(self, 'layout_asides_override', None):
return self.layout_asides_override(block, context, frag, view_name, aside_frag_fns)
return super().layout_asides(block, context, frag, view_name, aside_frag_fns)
class XMLParsingSystem(DescriptorSystem): # lint-amnesty, pylint: disable=abstract-method, missing-class-docstring
def __init__(self, process_xml, **kwargs):
@@ -1702,6 +1710,7 @@ class XMLParsingSystem(DescriptorSystem): # lint-amnesty, pylint: disable=abstr
field_value[key] = self._make_usage_key(course_key, subvalue)
setattr(xblock, field.name, field_value)
class CombinedSystem:
"""
This class is a shim to allow both pure XBlocks and XModuleDescriptors