def test_replace_jump_to_id_urls_not_called(self): """ Test replace_jump_to_id_urls method called jump_to_id_base_url is not provided. """ replace_url_service = ReplaceURLService(course_id=COURSE_KEY) return_text = replace_url_service.replace_urls("text") assert not self.mock_replace_jump_to_id_urls.called
def test_replace_course_urls_called(self): """ Test replace_course_urls method called static_replace_only is passed as False. """ replace_url_service = ReplaceURLService(course_id=COURSE_KEY) return_text = replace_url_service.replace_urls("text") assert self.mock_replace_course_urls.called
def test_replace_static_url_only(self): """ Test only replace_static_urls method called when static_replace_only is passed as True. """ replace_url_service = ReplaceURLService(course_id=COURSE_KEY) return_text = replace_url_service.replace_urls( "text", static_replace_only=True) assert self.mock_replace_static_urls.called assert not self.mock_replace_course_urls.called assert not self.mock_replace_jump_to_id_urls.called
def replace_urls(self, html_str): """ Deprecated in favor of the replace_urls service. """ warnings.warn( 'replace_urls is deprecated. Please use ReplaceURLService instead.', DeprecationWarning, stacklevel=3, ) return ReplaceURLService( xblock=self._active_block, lookup_asset_url=self._lookup_asset_url).replace_urls(html_str)
def service(self, block, service_name): """ Return a service, or None. Services are objects implementing arbitrary other interfaces. """ # TODO: Do these declarations actually help with anything? Maybe this check should # be removed from here and from XBlock.runtime declaration = block.service_declaration(service_name) if declaration is None: raise NoSuchServiceError(f"Service {service_name!r} was not requested.") # Most common service is field-data so check that first: if service_name == "field-data": if block.scope_ids not in self.block_field_datas: try: self.block_field_datas[block.scope_ids] = self._init_field_data_for_block(block) except: # Don't try again pointlessly every time another field is accessed self.block_field_datas[block.scope_ids] = None raise return self.block_field_datas[block.scope_ids] elif service_name == "completion": context_key = block.scope_ids.usage_id.context_key return CompletionService(user=self.user, context_key=context_key) elif service_name == "user": return DjangoXBlockUserService( self.user, # The value should be updated to whether the user is staff in the context when Blockstore runtime adds # support for courses. user_is_staff=self.user.is_staff, anonymous_user_id=self.anonymous_student_id, ) elif service_name == "mako": if self.system.student_data_mode == XBlockRuntimeSystem.STUDENT_DATA_EPHEMERAL: return MakoService(namespace_prefix='lms.') return MakoService() elif service_name == "i18n": return ModuleI18nService(block=block) elif service_name == 'sandbox': context_key = block.scope_ids.usage_id.context_key return SandboxService(contentstore=contentstore, course_id=context_key) elif service_name == 'cache': return CacheService(cache) elif service_name == 'replace_urls': return ReplaceURLService(xblock=block, lookup_asset_url=self._lookup_asset_url) # Check if the XBlockRuntimeSystem wants to handle this: service = self.system.get_service(block, service_name) # Otherwise, fall back to the base implementation which loads services # defined in the constructor: if service is None: service = super().service(block, service_name) return service
def test_replace_course_urls(self, course_id, anchor_tag): """ Verify that the course URL has been replaced. """ course = getattr(self, course_id) replace_url_service = ReplaceURLService(course_id=course.id) test_replace = replace_urls_wrapper( block=course, view='baseview', frag=Fragment('<a href="/course/id">'), context=None, replace_url_service=replace_url_service) assert isinstance(test_replace, Fragment) assert test_replace.content == anchor_tag
def test_replace_jump_to_id_urls(self, course_id): """ Verify that the jump-to URL has been replaced. """ course = getattr(self, course_id) replace_url_service = ReplaceURLService( course_id=course.id, jump_to_id_base_url='/base_url/') test_replace = replace_urls_wrapper( block=course, view='baseview', frag=Fragment('<a href="/jump_to_id/id">'), context=None, replace_url_service=replace_url_service) assert isinstance(test_replace, Fragment) assert test_replace.content == '<a href="/base_url/id">'
def render(self, block, view_name, context=None): """ Render a specific view of an XBlock. """ # Users who aren't logged in are not allowed to view any views other # than public_view. They may call any handlers though. if (self.user is None or self.user.is_anonymous) and view_name != 'public_view': raise PermissionDenied # We also need to override this method because some XBlocks in the # edx-platform codebase use methods like add_webpack_to_fragment() # which create relative URLs (/static/studio/bundles/webpack-foo.js). # We want all resource URLs to be absolute, such as is done when # local_resource_url() is used. fragment = super().render(block, view_name, context) needs_fix = False for resource in fragment.resources: if resource.kind == 'url' and resource.data.startswith('/'): needs_fix = True break if needs_fix: log.warning("XBlock %s returned relative resource URLs, which are deprecated", block.scope_ids.usage_id) # The Fragment API is mostly immutable, so changing a resource requires this: frag_data = fragment.to_dict() for resource in frag_data['resources']: if resource['kind'] == 'url' and resource['data'].startswith('/'): log.debug("-> Relative resource URL: %s", resource['data']) resource['data'] = get_xblock_app_config().get_site_root_url() + resource['data'] fragment = Fragment.from_dict(frag_data) # Apply any required transforms to the fragment. # We could move to doing this in wrap_xblock() and/or use an array of # wrapper methods like the ConfigurableFragmentWrapper mixin does. fragment = wrap_fragment( fragment, ReplaceURLService(xblock=block, lookup_asset_url=self._lookup_asset_url).replace_urls(fragment.content) ) return fragment
def _preview_module_system(request, descriptor, field_data): """ Returns a ModuleSystem for the specified descriptor that is specialized for rendering module previews. request: The active django request descriptor: An XModuleDescriptor """ course_id = descriptor.location.course_key display_name_only = (descriptor.category == 'static_tab') replace_url_service = ReplaceURLService(course_id=course_id) wrappers = [ # This wrapper wraps the module in the template specified above partial(wrap_xblock, 'PreviewRuntime', display_name_only=display_name_only, usage_id_serializer=str, request_token=request_token(request)), # This wrapper replaces urls in the output that start with /static # with the correct course-specific url for the static content partial(replace_urls_wrapper, replace_url_service=replace_url_service, static_replace_only=True), _studio_wrap_xblock, ] wrappers_asides = [ partial(wrap_xblock_aside, 'PreviewRuntime', usage_id_serializer=str, request_token=request_token(request)) ] mako_service = MakoService(namespace_prefix='lms.') if settings.FEATURES.get("LICENSING", False): # stick the license wrapper in front wrappers.insert(0, partial(wrap_with_license, mako_service=mako_service)) return PreviewModuleSystem( static_url=settings.STATIC_URL, # TODO (cpennington): Do we want to track how instructors are using the preview problems? track_function=lambda event_type, event: None, get_module=partial(_load_preview_module, request), debug=True, mixins=settings.XBLOCK_MIXINS, course_id=course_id, # Set up functions to modify the fragment produced by student_view wrappers=wrappers, wrappers_asides=wrappers_asides, error_descriptor_class=ErrorBlock, # 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, anonymous_user_id='student', user_role=get_user_role(request.user, course_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 }, )