Skip to content

Commit

Permalink
refactor: Update get block OLX view to support versions [FC-0062] (op…
Browse files Browse the repository at this point in the history
…enedx#35932)

* Deprecate `get_block_draft_olx`
* Deprecate get olx view in content libraries
* Create `get_block_olx` in xblock API with support of versions
* Create get olx view in xblock
  • Loading branch information
ChrisChV committed Dec 5, 2024
1 parent 38f7344 commit b31344b
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 10 deletions.
3 changes: 3 additions & 0 deletions openedx/core/djangoapps/content_libraries/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,9 @@ class LibraryBlockOlxView(APIView):
@convert_exceptions
def get(self, request, usage_key_str):
"""
DEPRECATED. Use get_block_olx_view() in xblock REST-API.
Can be removed post-Teak.
Get the block's OLX
"""
key = LibraryUsageLocatorV2.from_string(usage_key_str)
Expand Down
33 changes: 28 additions & 5 deletions openedx/core/djangoapps/xblock/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@
LearningCoreXBlockRuntime,
)
from .data import CheckPerm, LatestVersion
from .utils import get_secure_token_for_xblock_handler, get_xblock_id_for_anonymous_user
from .rest_api.url_converters import VersionConverter
from .utils import (
get_secure_token_for_xblock_handler,
get_xblock_id_for_anonymous_user,
get_auto_latest_version,
)

from .runtime.learning_core_runtime import LearningCoreXBlockRuntime

Expand Down Expand Up @@ -208,13 +213,26 @@ def get_component_from_usage_key(usage_key: UsageKeyV2) -> Component:
)


def get_block_draft_olx(usage_key: UsageKeyV2) -> str:
def get_block_olx(
usage_key: UsageKeyV2,
*,
version: int | LatestVersion = LatestVersion.AUTO
) -> str:
"""
Get the OLX source of the draft version of the given Learning-Core-backed XBlock.
Get the OLX source of the of the given Learning-Core-backed XBlock and a version.
"""
# Inefficient but simple approach. Optimize later if needed.
component = get_component_from_usage_key(usage_key)
component_version = component.versioning.draft
version = get_auto_latest_version(version)

if version == LatestVersion.DRAFT:
component_version = component.versioning.draft
elif version == LatestVersion.PUBLISHED:
component_version = component.versioning.published
else:
assert isinstance(version, int)
component_version = component.versioning.version_num(version)
if component_version is None:
raise NoSuchUsage(usage_key)

# TODO: we should probably make a method on ComponentVersion that returns
# a content based on the name. Accessing by componentversioncontent__key is
Expand All @@ -224,6 +242,11 @@ def get_block_draft_olx(usage_key: UsageKeyV2) -> str:
return content.text


def get_block_draft_olx(usage_key: UsageKeyV2) -> str:
""" DEPRECATED. Use get_block_olx(). Can be removed post-Teak. """
return get_block_olx(usage_key, version=LatestVersion.DRAFT)


def render_block_view(block, view_name, user): # pylint: disable=unused-argument
"""
Get the HTML, JS, and CSS needed to render the given XBlock view.
Expand Down
11 changes: 11 additions & 0 deletions openedx/core/djangoapps/xblock/rest_api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""
Serializers for the xblock REST API
"""
from rest_framework import serializers


class XBlockOlxSerializer(serializers.Serializer):
"""
Serializer for representing an XBlock's OLX
"""
olx = serializers.CharField()
2 changes: 2 additions & 0 deletions openedx/core/djangoapps/xblock/rest_api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
path('', views.block_metadata),
# get/post full json fields of an XBlock:
path('fields/', views.BlockFieldsView.as_view()),
# Get the OLX source code of the specified block
path('olx/', views.get_block_olx_view),
# render one of this XBlock's views (e.g. student_view)
path('view/<str:view_name>/', views.render_block_view),
# get the URL needed to call this XBlock's handlers
Expand Down
19 changes: 19 additions & 0 deletions openedx/core/djangoapps/xblock/rest_api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
get_handler_url as _get_handler_url,
load_block,
render_block_view as _render_block_view,
get_block_olx,
)
from ..utils import validate_secure_token_for_xblock_handler
from .url_converters import VersionConverter
from .serializers import XBlockOlxSerializer

User = get_user_model()

Expand Down Expand Up @@ -213,6 +215,23 @@ def xblock_handler(
return response


@api_view(['GET'])
@view_auth_classes(is_authenticated=False)
def get_block_olx_view(
request,
usage_key: UsageKeyV2,
version: LatestVersion | int = LatestVersion.AUTO,
):
"""
Get the OLX (XML serialization) of the specified XBlock
"""
context_impl = get_learning_context_impl(usage_key)
if not context_impl.can_view_block_for_editing(request.user, usage_key):
raise PermissionDenied(f"You don't have permission to access the OLX of component '{usage_key}'.")
olx = get_block_olx(usage_key, version=version)
return Response(XBlockOlxSerializer({"olx": olx}).data)


def cors_allow_xblock_handler(sender, request, **kwargs): # lint-amnesty, pylint: disable=unused-argument
"""
Sandboxed XBlocks need to be able to call XBlock handlers via POST,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from openedx.core.lib.xblock_serializer.api import serialize_modulestore_block_for_learning_core
from openedx.core.lib.xblock_serializer.data import StaticFile
from ..data import AuthoredDataMode, LatestVersion
from ..utils import get_auto_latest_version
from ..learning_context.manager import get_learning_context_impl
from .runtime import XBlockRuntime

Expand Down Expand Up @@ -178,11 +179,7 @@ def get_block(self, usage_key, for_parent=None, *, version: int | LatestVersion
# just get it the easy way.
component = self._get_component_from_usage_key(usage_key)

if version == LatestVersion.AUTO:
if self.authored_data_mode == AuthoredDataMode.DEFAULT_DRAFT:
version = LatestVersion.DRAFT
else:
version = LatestVersion.PUBLISHED
version = get_auto_latest_version(version)
if self.authored_data_mode == AuthoredDataMode.STRICTLY_PUBLISHED and version != LatestVersion.PUBLISHED:
raise ValidationError("This runtime only allows accessing the published version of components")
if version == LatestVersion.DRAFT:
Expand Down
19 changes: 19 additions & 0 deletions openedx/core/djangoapps/xblock/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
import crum
from django.conf import settings

from openedx.core.djangoapps.xblock.apps import get_xblock_app_config

from .data import AuthoredDataMode, LatestVersion


def get_secure_token_for_xblock_handler(user_id, block_key_str, time_idx=0):
"""
Expand Down Expand Up @@ -167,3 +171,18 @@ def get_xblock_id_for_anonymous_user(user):
return current_request.session["xblock_id_for_anonymous_user"]
else:
raise RuntimeError("Cannot get a user ID for an anonymous user outside of an HTTP request context.")


def get_auto_latest_version(version: int | LatestVersion) -> int | LatestVersion:
"""
Gets the actual LatestVersion if is `LatestVersion.AUTO`;
otherwise, returns the same value.
"""
if version == LatestVersion.AUTO:
authored_data_mode = get_xblock_app_config().get_runtime_params()["authored_data_mode"]
version = (
LatestVersion.DRAFT
if authored_data_mode == AuthoredDataMode.DEFAULT_DRAFT
else LatestVersion.PUBLISHED
)
return version

0 comments on commit b31344b

Please sign in to comment.