Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Update get block OLX view to support versions [FC-0062] #35932

Merged
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 @@ -710,6 +710,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:
ChrisChV marked this conversation as resolved.
Show resolved Hide resolved
"""
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,
):
ChrisChV marked this conversation as resolved.
Show resolved Hide resolved
"""
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
Loading