Skip to content

Commit

Permalink
temp: add implementations for delete_library_block_static_asset_file,…
Browse files Browse the repository at this point in the history
… add_library_block_static_asset_file
  • Loading branch information
ormsbee committed Oct 11, 2024
1 parent d0e0a55 commit 9c36732
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 13 deletions.
10 changes: 10 additions & 0 deletions cms/envs/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,13 @@
"SECRET": "***",
"URL": "***",
}

############## openedx-learning (Learning Core) config ##############
OPENEDX_LEARNING = {
'MEDIA': {
'BACKEND': 'django.core.files.storage.InMemoryStorage',
'OPTIONS': {
'location': MEDIA_ROOT + "_private"
}
}
}
76 changes: 71 additions & 5 deletions openedx/core/djangoapps/content_libraries/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import base64
import hashlib
import logging
import mimetypes

import attr
import requests
Expand Down Expand Up @@ -1042,12 +1043,12 @@ def get_library_block_static_asset_files(usage_key) -> list[LibraryXBlockStaticF
]


def add_library_block_static_asset_file(usage_key, file_name, file_content) -> LibraryXBlockStaticFile:
def add_library_block_static_asset_file(usage_key, file_path, file_content, user=None) -> LibraryXBlockStaticFile:
"""
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
file_path should be a name like "doc.pdf". It may optionally contain slashes
like 'en/doc.pdf'
file_content should be a binary string.
Expand All @@ -1059,10 +1060,58 @@ def add_library_block_static_asset_file(usage_key, file_name, file_content) -> L
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'))
"""
raise NotImplementedError("Static assets not yet implemented for Learning Core")
# File path validations copied over from v1 library logic...
if file_path != file_path.strip().strip('/'):
raise InvalidNameError("file_path cannot start/end with / or whitespace.")
if '//' in file_path or '..' in file_path:
raise InvalidNameError("Invalid sequence (// or ..) in file_path.")

component = get_component_from_usage_key(usage_key)

media_type_str, _encoding = mimetypes.guess_type(file_path)
media_type = authoring_api.get_or_create_media_type(media_type_str)
now = datetime.now(tz=timezone.utc)

def delete_library_block_static_asset_file(usage_key, file_name):
with transaction.atomic():
content = authoring_api.get_or_create_file_content(
component.publishable_entity.learning_package.id,
media_type.id,
data=file_content,
created=now,
)
component_version = authoring_api.create_next_component_version(
component.pk,
content_to_replace={file_path: content.id},
created=now,
created_by=user.id if user else None,
)
transaction.on_commit(
lambda: LIBRARY_BLOCK_UPDATED.send_event(
library_block=LibraryBlockData(
library_key=usage_key.context_key,
usage_key=usage_key,
)
)
)

# Now figure out the URL for the newly created asset...
site_root_url = get_xblock_app_config().get_site_root_url()
local_path = reverse(
'content_libraries:library-assets',
kwargs={
'component_version_uuid': component_version.uuid,
'asset_path': file_path,
}
)

return LibraryXBlockStaticFile(
path=file_path,
url=site_root_url + local_path,
size=content.size,
)


def delete_library_block_static_asset_file(usage_key, file_path, user=None):
"""
Delete a static asset file from the library.
Expand All @@ -1072,7 +1121,24 @@ def delete_library_block_static_asset_file(usage_key, file_name):
video_block = UsageKey.from_string("lb:VideoTeam:python-intro:video:1")
delete_library_block_static_asset_file(video_block, "subtitles-en.srt")
"""
raise NotImplementedError("Static assets not yet implemented for Learning Core")
component = get_component_from_usage_key(usage_key)
now = datetime.now(tz=timezone.utc)

with transaction.atomic():
component_version = authoring_api.create_next_component_version(
component.pk,
content_to_replace={file_path: None},
created=now,
created_by=user.id if user else None,
)
transaction.on_commit(
lambda: LIBRARY_BLOCK_UPDATED.send_event(
library_block=LibraryBlockData(
library_key=usage_key.context_key,
usage_key=usage_key,
)
)
)


def get_allowed_block_types(library_key): # pylint: disable=unused-argument
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,9 @@
"""


@skip("Assets are being reimplemented in Learning Core. Disable until that's ready.")
class ContentLibrariesStaticAssetsTest(ContentLibrariesRestApiTest):
"""
Tests for static asset files in Learning-Core-based Content Libraries
WARNING: every test should have a unique library slug, because even though
the django/mysql database gets reset for each test case, the lookup between
library slug and bundle UUID does not because it's assumed to be immutable
and cached forever.
"""

def test_asset_filenames(self):
Expand Down
4 changes: 2 additions & 2 deletions openedx/core/djangoapps/content_libraries/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,7 +759,7 @@ def put(self, request, usage_key_str, file_path):
raise ValidationError("File too big")
file_content = file_wrapper.read()
try:
result = api.add_library_block_static_asset_file(usage_key, file_path, file_content)
result = api.add_library_block_static_asset_file(usage_key, file_path, file_content, request.user)
except ValueError:
raise ValidationError("Invalid file path") # lint-amnesty, pylint: disable=raise-missing-from
return Response(LibraryXBlockStaticFileSerializer(result).data)
Expand All @@ -774,7 +774,7 @@ def delete(self, request, usage_key_str, file_path):
usage_key.lib_key, request.user, permissions.CAN_EDIT_THIS_CONTENT_LIBRARY,
)
try:
api.delete_library_block_static_asset_file(usage_key, file_path)
api.delete_library_block_static_asset_file(usage_key, file_path, request.user)
except ValueError:
raise ValidationError("Invalid file path") # lint-amnesty, pylint: disable=raise-missing-from
return Response(status=status.HTTP_204_NO_CONTENT)
Expand Down

0 comments on commit 9c36732

Please sign in to comment.