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
Beispiel #2
0
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)
Beispiel #3
0
    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