Skip to content

Commit

Permalink
feat: new unstable URL to render a v2 library xblock in an iframe (#3…
Browse files Browse the repository at this point in the history
  • Loading branch information
rpenido authored Sep 13, 2024
1 parent 4e5a3b2 commit bb6ac5a
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 3 deletions.
7 changes: 6 additions & 1 deletion cms/templates/content_libraries/xblock_iframe.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
<base target="_blank">
<meta charset="UTF-8">
<!-- gettext & XBlock JS i18n code -->
<script type="text/javascript" src="{{ lms_root_url }}/static/js/i18n/en/djangojs.js"></script>
{% if is_development %}
<!-- in development, the djangojs file isn't available so use fallback-->
<script type="text/javascript" src="{{ lms_root_url }}/static/js/src/gettext_fallback.js"></script>
{% else %}
<script type="text/javascript" src="{{ lms_root_url }}/static/js/i18n/en/djangojs.js"></script>
{% endif %}
<!-- Most XBlocks require jQuery: -->
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<!-- The Video XBlock requires "ajaxWithPrefix" -->
Expand Down
1 change: 1 addition & 0 deletions lms/templates/xblock_v2/xblock_iframe.html
5 changes: 5 additions & 0 deletions openedx/core/djangoapps/xblock/rest_api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,9 @@
),
])),
])),
path('xblocks/v2/<str:usage_key_str>/', include([
# render one of this XBlock's views (e.g. student_view) for embedding in an iframe
# NOTE: this endpoint is **unstable** and subject to changes after Sumac
re_path(r'^embed/(?P<view_name>[\w\-]+)/$', views.embed_block_view),
])),
]
50 changes: 48 additions & 2 deletions openedx/core/djangoapps/xblock/rest_api/views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
"""
Views that implement a RESTful API for interacting with XBlocks.
"""
import itertools
import json

from common.djangoapps.util.json_request import JsonResponse
from corsheaders.signals import check_request_enabled
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db.transaction import atomic
from django.http import Http404
from django.shortcuts import render
from django.utils.translation import gettext as _
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.decorators.csrf import csrf_exempt
Expand All @@ -21,6 +25,7 @@

from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import UsageKey
import openedx.core.djangoapps.site_configuration.helpers as configuration_helpers
from openedx.core.djangoapps.xblock.learning_context.manager import get_learning_context_impl
from openedx.core.lib.api.view_utils import view_auth_classes
from ..api import (
Expand Down Expand Up @@ -87,6 +92,47 @@ def render_block_view(request, usage_key_str, view_name):
return Response(response_data)


@api_view(['GET'])
@view_auth_classes(is_authenticated=False)
@permission_classes((permissions.AllowAny, )) # Permissions are handled at a lower level, by the learning context
@xframe_options_exempt
def embed_block_view(request, usage_key_str, view_name):
"""
Render the given XBlock in an <iframe>
Unstable - may change after Sumac
"""
try:
usage_key = UsageKey.from_string(usage_key_str)
except InvalidKeyError as e:
raise NotFound(invalid_not_found_fmt.format(usage_key=usage_key_str)) from e

try:
block = load_block(usage_key, request.user)
except NoSuchUsage as exc:
raise NotFound(f"{usage_key} not found") from exc

fragment = _render_block_view(block, view_name, request.user)
handler_urls = {
str(key): _get_handler_url(key, 'handler_name', request.user)
for key in itertools.chain([block.scope_ids.usage_id], getattr(block, 'children', []))
}
lms_root_url = configuration_helpers.get_value('LMS_ROOT_URL', settings.LMS_ROOT_URL)
context = {
'fragment': fragment,
'handler_urls_json': json.dumps(handler_urls),
'lms_root_url': lms_root_url,
'is_development': settings.DEBUG,
}
response = render(request, 'xblock_v2/xblock_iframe.html', context, content_type='text/html')

# Only allow this iframe be embedded if the parent is in the CORS_ORIGIN_WHITELIST
cors_origin_whitelist = configuration_helpers.get_value('CORS_ORIGIN_WHITELIST', settings.CORS_ORIGIN_WHITELIST)
response["Content-Security-Policy"] = f"frame-ancestors 'self' {' '.join(cors_origin_whitelist)};"

return response


@api_view(['GET'])
@view_auth_classes(is_authenticated=False)
def get_handler_url(request, usage_key_str, handler_name):
Expand Down Expand Up @@ -185,8 +231,8 @@ class BlockFieldsView(APIView):
View to get/edit the field values of an XBlock as JSON (in the v2 runtime)
This class mimics the functionality of xblock_handler in block.py (for v1 xblocks), but for v2 xblocks.
However, it only implements the exact subset of functionality needed to support the v2 editors (from
the frontend-lib-content-components project). As such, it only supports GET and POST, and only the
However, it only implements the exact subset of functionality needed to support the v2 editors (in
the Course Authoring MFE). As such, it only supports GET and POST, and only the
POSTing of data/metadata fields.
"""

Expand Down

0 comments on commit bb6ac5a

Please sign in to comment.