|
1 | 1 | """
|
2 | 2 | Views that implement a RESTful API for interacting with XBlocks.
|
3 | 3 | """
|
| 4 | +import itertools |
| 5 | +import json |
4 | 6 |
|
5 | 7 | from common.djangoapps.util.json_request import JsonResponse
|
6 | 8 | from corsheaders.signals import check_request_enabled
|
| 9 | +from django.conf import settings |
7 | 10 | from django.contrib.auth import get_user_model
|
8 | 11 | from django.db.transaction import atomic
|
9 | 12 | from django.http import Http404
|
| 13 | +from django.shortcuts import render |
10 | 14 | from django.utils.translation import gettext as _
|
11 | 15 | from django.views.decorators.clickjacking import xframe_options_exempt
|
12 | 16 | from django.views.decorators.csrf import csrf_exempt
|
|
21 | 25 |
|
22 | 26 | from opaque_keys import InvalidKeyError
|
23 | 27 | from opaque_keys.edx.keys import UsageKey
|
| 28 | +import openedx.core.djangoapps.site_configuration.helpers as configuration_helpers |
24 | 29 | from openedx.core.djangoapps.xblock.learning_context.manager import get_learning_context_impl
|
25 | 30 | from openedx.core.lib.api.view_utils import view_auth_classes
|
26 | 31 | from ..api import (
|
@@ -87,6 +92,47 @@ def render_block_view(request, usage_key_str, view_name):
|
87 | 92 | return Response(response_data)
|
88 | 93 |
|
89 | 94 |
|
| 95 | +@api_view(['GET']) |
| 96 | +@view_auth_classes(is_authenticated=False) |
| 97 | +@permission_classes((permissions.AllowAny, )) # Permissions are handled at a lower level, by the learning context |
| 98 | +@xframe_options_exempt |
| 99 | +def embed_block_view(request, usage_key_str, view_name): |
| 100 | + """ |
| 101 | + Render the given XBlock in an <iframe> |
| 102 | +
|
| 103 | + Unstable - may change after Sumac |
| 104 | + """ |
| 105 | + try: |
| 106 | + usage_key = UsageKey.from_string(usage_key_str) |
| 107 | + except InvalidKeyError as e: |
| 108 | + raise NotFound(invalid_not_found_fmt.format(usage_key=usage_key_str)) from e |
| 109 | + |
| 110 | + try: |
| 111 | + block = load_block(usage_key, request.user) |
| 112 | + except NoSuchUsage as exc: |
| 113 | + raise NotFound(f"{usage_key} not found") from exc |
| 114 | + |
| 115 | + fragment = _render_block_view(block, view_name, request.user) |
| 116 | + handler_urls = { |
| 117 | + str(key): _get_handler_url(key, 'handler_name', request.user) |
| 118 | + for key in itertools.chain([block.scope_ids.usage_id], getattr(block, 'children', [])) |
| 119 | + } |
| 120 | + lms_root_url = configuration_helpers.get_value('LMS_ROOT_URL', settings.LMS_ROOT_URL) |
| 121 | + context = { |
| 122 | + 'fragment': fragment, |
| 123 | + 'handler_urls_json': json.dumps(handler_urls), |
| 124 | + 'lms_root_url': lms_root_url, |
| 125 | + 'is_development': settings.DEBUG, |
| 126 | + } |
| 127 | + response = render(request, 'xblock_v2/xblock_iframe.html', context, content_type='text/html') |
| 128 | + |
| 129 | + # Only allow this iframe be embedded if the parent is in the CORS_ORIGIN_WHITELIST |
| 130 | + cors_origin_whitelist = configuration_helpers.get_value('CORS_ORIGIN_WHITELIST', settings.CORS_ORIGIN_WHITELIST) |
| 131 | + response["Content-Security-Policy"] = f"frame-ancestors 'self' {' '.join(cors_origin_whitelist)};" |
| 132 | + |
| 133 | + return response |
| 134 | + |
| 135 | + |
90 | 136 | @api_view(['GET'])
|
91 | 137 | @view_auth_classes(is_authenticated=False)
|
92 | 138 | def get_handler_url(request, usage_key_str, handler_name):
|
@@ -185,8 +231,8 @@ class BlockFieldsView(APIView):
|
185 | 231 | View to get/edit the field values of an XBlock as JSON (in the v2 runtime)
|
186 | 232 |
|
187 | 233 | This class mimics the functionality of xblock_handler in block.py (for v1 xblocks), but for v2 xblocks.
|
188 |
| - However, it only implements the exact subset of functionality needed to support the v2 editors (from |
189 |
| - the frontend-lib-content-components project). As such, it only supports GET and POST, and only the |
| 234 | + However, it only implements the exact subset of functionality needed to support the v2 editors (in |
| 235 | + the Course Authoring MFE). As such, it only supports GET and POST, and only the |
190 | 236 | POSTing of data/metadata fields.
|
191 | 237 | """
|
192 | 238 |
|
|
0 commit comments