def _lookup_asset_url(self, block, asset_path): """ Return an absolute URL for the specified static asset file that may belong to this XBlock. e.g. if the XBlock settings have a field value like "/static/foo.png" then this method will be called with asset_path="foo.png" and should return a URL like https://cdn.none/xblock/f843u89789/static/foo.png If the asset file is not recognized, return None """ if '..' in asset_path: return None # Illegal path definition_key = block.scope_ids.def_id # Compute the full path to the static file in the bundle, # e.g. "problem/prob1/static/illustration.svg" expanded_path = os.path.dirname( definition_key.olx_path) + '/static/' + asset_path try: metadata = get_bundle_file_metadata_with_cache( bundle_uuid=definition_key.bundle_uuid, path=expanded_path, bundle_version=definition_key.bundle_version, draft_name=definition_key.draft_name, ) except blockstore_api.BundleFileNotFound: log.warning("XBlock static file not found: %s for %s", asset_path, block.scope_ids.usage_id) return None # Make sure the URL is one that will work from the user's browser, # not one that only works from within a docker container: url = blockstore_api.force_browser_url(metadata.url) return url
def add_library_block_static_asset_file(usage_key, file_name, file_content): """ Upload a static asset file into the library, to be associated with the specified XBlock. Will silently overwrite an existing file of the same name. file_name should be a name like "doc.pdf". It may optionally contain slashes like 'en/doc.pdf' file_content should be a binary string. Returns a LibraryXBlockStaticFile object. Example: video_block = UsageKey.from_string("lb:VideoTeam:python-intro:video:1") add_library_block_static_asset_file(video_block, "subtitles-en.srt", subtitles.encode('utf-8')) """ assert isinstance(file_content, six.binary_type) def_key, lib_bundle = _lookup_usage_key(usage_key) if file_name != file_name.strip().strip('/'): raise InvalidNameError("file name cannot start/end with / or whitespace.") if '//' in file_name or '..' in file_name: raise InvalidNameError("Invalid sequence (// or ..) in filename.") file_path = lib_bundle.get_static_prefix_for_definition(def_key) + file_name # Write the new static file into the library bundle's draft draft = get_or_create_bundle_draft(def_key.bundle_uuid, DRAFT_NAME) write_draft_file(draft.uuid, file_path, file_content) # Clear the bundle cache so everyone sees the new file immediately: lib_bundle.cache.clear() file_metadata = blockstore_cache.get_bundle_file_metadata_with_cache( bundle_uuid=def_key.bundle_uuid, path=file_path, draft_name=DRAFT_NAME, ) LIBRARY_BLOCK_UPDATED.send(sender=None, library_key=lib_bundle.library_key, usage_key=usage_key) return LibraryXBlockStaticFile(path=file_metadata.path, url=file_metadata.url, size=file_metadata.size)
def definition_for_usage(self, usage_key): """ Given the usage key for an XBlock in this library bundle, return the BundleDefinitionLocator which specifies the actual XBlock definition (as a path to an OLX in a specific blockstore bundle). Must return a BundleDefinitionLocator if the XBlock exists in this context, or None otherwise. For a content library, the rules are simple: * If the usage key points to a block in this library, the filename (definition) of the OLX file is always {block_type}/{usage_id}/definition.xml Each library has exactly one usage per definition for its own blocks. * However, block definitions from other content libraries may be linked into this library via <xblock-include ... /> directives. In that case, it's necessary to inspect every OLX file in this library that might have an <xblock-include /> directive in order to find what external block the usage ID refers to. """ # Now that we know the library/bundle, find the block's definition if self.draft_name: version_arg = {"draft_name": self.draft_name} else: version_arg = { "bundle_version": get_bundle_version_number(self.bundle_uuid) } olx_path = "{}/{}/definition.xml".format(usage_key.block_type, usage_key.usage_id) try: get_bundle_file_metadata_with_cache(self.bundle_uuid, olx_path, **version_arg) return BundleDefinitionLocator(self.bundle_uuid, usage_key.block_type, olx_path, **version_arg) except blockstore_api.BundleFileNotFound: # This must be a usage of a block from a linked bundle. One of the # OLX files in this bundle contains an <xblock-include usage="..."/> bundle_includes = self.get_bundle_includes() try: return bundle_includes[usage_key] except KeyError: return None