Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class CourseWaffleFlagsSerializer(serializers.Serializer):
use_new_import_page = serializers.SerializerMethodField()
use_new_export_page = serializers.SerializerMethodField()
use_new_files_uploads_page = serializers.SerializerMethodField()
use_new_pdf_editor = serializers.SerializerMethodField()
use_new_video_uploads_page = serializers.SerializerMethodField()
use_new_course_outline_page = serializers.SerializerMethodField()
use_new_unit_page = serializers.SerializerMethodField()
Expand Down Expand Up @@ -120,6 +121,12 @@ def get_use_new_files_uploads_page(self, obj):
"""
return True

def get_use_new_pdf_editor(self, obj):
"""
Method to get the use_new_pdf_editor switch
"""
return toggles.use_new_pdf_editor()

def get_use_new_video_uploads_page(self, obj):
"""
Method to get the use_new_video_uploads_page switch.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class CourseWaffleFlagsViewTest(CourseTestCase):
"use_new_unit_page": True,
"use_new_updates_page": True,
"use_new_video_uploads_page": False,
"use_new_pdf_editor": True,
"use_react_markdown_editor": False,
"use_video_gallery_flow": False,
"enable_course_optimizer_check_prev_run_links": False,
Expand Down
17 changes: 17 additions & 0 deletions cms/djangoapps/contentstore/toggles.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,23 @@ def use_new_video_editor(course_key):
return not LEGACY_STUDIO_VIDEO_EDITOR.is_enabled(course_key)


# .. toggle_name: legacy_studio.pdf_editor
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: Use the PDF XBlock's studio_view instead of the new React-based editor. You may wish to do
# this if you run a custom PDF block.
# .. toggle_use_cases: opt_out
# .. toggle_creation_date: 2026-03-10
LEGACY_STUDIO_PDF_EDITOR = WaffleFlag('legacy_studio.pdf_editor', __name__)


def use_new_pdf_editor():
"""
Returns a boolean = true if new video editor is enabled
"""
return not LEGACY_STUDIO_PDF_EDITOR.is_enabled()


# .. toggle_name: new_core_editors.use_video_gallery_flow
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
Expand Down
8 changes: 5 additions & 3 deletions cms/static/js/views/pages/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -491,14 +491,14 @@ function($, _, Backbone, gettext, BasePage,
if ($target.closest('button, a, input, label, .actions-list').length) {
return;
}

var $wrapper = $target.closest('.studio-xblock-wrapper');

// Deselect all other xblocks
this.$('.studio-xblock-wrapper.is-selected').not($wrapper).removeClass('is-selected');

$wrapper.toggleClass('is-selected');

if (this.options.isIframeEmbed) {
const contentId = this.findXBlockElement(event.target).data('locator');
this.postMessageToParent({
Expand Down Expand Up @@ -539,11 +539,13 @@ function($, _, Backbone, gettext, BasePage,
const primaryHeader = $(event.target).closest('.xblock-header-primary, .nav-actions');

var useNewVideoEditor = primaryHeader.attr('use-new-editor-video'),
blockType = primaryHeader.attr('data-block-type');
blockType = primaryHeader.attr('data-block-type'),
useNewPdfEditor = primaryHeader.attr('use-new-editor-pdf');

if((blockType === 'html')
|| (useNewVideoEditor === 'True' && blockType === 'video')
|| (blockType === 'problem')
|| (useNewPdfEditor === 'True' && blockType === 'pdf')
) {
var destinationUrl = primaryHeader.attr('authoring_MFE_base_url')
+ '/' + blockType
Expand Down
4 changes: 3 additions & 1 deletion cms/templates/container.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from django.utils.translation import gettext as _

from cms.djangoapps.contentstore.helpers import xblock_studio_url, xblock_type_display_name
from cms.djangoapps.contentstore.toggles import use_new_video_editor, use_video_gallery_flow
from cms.djangoapps.contentstore.toggles import use_new_pdf_editor, use_new_video_editor, use_video_gallery_flow
from cms.djangoapps.contentstore.utils import get_editor_page_base_url
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
Expand Down Expand Up @@ -112,6 +112,7 @@

<%
use_new_editor_video = use_new_video_editor(xblock_locator.course_key)
use_new_editor_pdf = use_new_pdf_editor()
use_new_video_gallery_flow = use_video_gallery_flow()
%>

Expand Down Expand Up @@ -167,6 +168,7 @@ <h1 class="page-header-title xblock-field-value incontext-editor-value"><span cl

<nav class="nav-actions" aria-label="${_('Page Actions')}"
use-new-editor-video = ${use_new_editor_video}
use-new-editor-pdf = ${use_new_editor_pdf}
use-video-gallery-flow = ${use_new_video_gallery_flow}
authoring_MFE_base_url = ${get_editor_page_base_url(xblock_locator.course_key)}
data-block-type = ${xblock.scope_ids.block_type}
Expand Down
4 changes: 3 additions & 1 deletion cms/templates/studio_xblock_wrapper.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
)
from cms.djangoapps.contentstore.toggles import use_new_video_editor, use_video_gallery_flow
from cms.djangoapps.contentstore.toggles import use_new_pdf_editor, use_new_video_editor, use_video_gallery_flow
from cms.lib.xblock.upstream_sync import UpstreamLink
from openedx.core.djangoapps.content_tagging.toggles import is_tagging_feature_disabled
%>
<%
use_new_editor_video = use_new_video_editor(xblock.context_key)
use_new_editor_pdf = use_new_pdf_editor()
use_new_video_gallery_flow = use_video_gallery_flow()
use_tagging = not is_tagging_feature_disabled()
xblock_url = xblock_studio_url(xblock)
Expand Down Expand Up @@ -83,6 +84,7 @@
% endif
"
use-new-editor-video = ${use_new_editor_video}
use-new-editor-pdf = ${use_new_editor_pdf}
use-video-gallery-flow = ${use_new_video_gallery_flow}
authoring_MFE_base_url = ${get_editor_page_base_url(xblock.location.course_key)}
data-block-type = ${xblock.scope_ids.block_type}
Expand Down
9 changes: 9 additions & 0 deletions openedx/core/djangoapps/content_libraries/rest_api/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""
import edx_api_doc_tools as apidocs
from django.core.exceptions import ObjectDoesNotExist
from django.conf import settings
from django.db.transaction import non_atomic_requests
from django.http import Http404, HttpResponse, StreamingHttpResponse
from django.urls import reverse
Expand All @@ -18,6 +19,7 @@
from rest_framework.response import Response
from rest_framework.views import APIView

import openedx.core.djangoapps.site_configuration.helpers as configuration_helpers
from openedx.core.djangoapps.content_libraries import api, permissions
from openedx.core.djangoapps.content_libraries.rest_api import serializers
from openedx.core.djangoapps.xblock import api as xblock_api
Expand Down Expand Up @@ -435,6 +437,13 @@ def get_component_version_asset(request, component_version_uuid, asset_path):
# not needed there (the reverse-proxy would have direct access to the file).
headers['Content-Length'] = media.size

# Some assets, such as PDFs, need to be embedded in an iFrame in the MFE
# studio. Permit this, so long as the file is in the cors_origin_whitelist.
cors_origin_whitelist = configuration_helpers.get_value(
'CORS_ORIGIN_WHITELIST', getattr(settings, 'CORS_ORIGIN_WHITELIST', []),
)
headers["Content-Security-Policy"] = f"frame-ancestors 'self' {' '.join(cors_origin_whitelist)};"

if request.method == "HEAD":
return HttpResponse(headers=headers)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"""
from uuid import UUID

from django.test.utils import override_settings
from opaque_keys.edx.keys import UsageKey

from common.djangoapps.student.tests.factories import UserFactory
Expand Down Expand Up @@ -117,6 +118,7 @@ def check_download():
check_download()


@override_settings(CORS_ORIGIN_WHITELIST=["https://example.com/", "https://example2.com/"])
@skip_unless_cms
class ContentLibrariesComponentVersionAssetTest(ContentLibrariesRestApiTest):
"""
Expand Down Expand Up @@ -146,6 +148,11 @@ def test_good_responses(self):
f"/library_assets/component_versions/{self.draft_component_version.uuid}/static/test.svg"
)
assert good_head_response.headers == get_response.headers
assert "Content-Security-Policy" in get_response.headers
assert get_response.headers["Content-Security-Policy"] == (
"frame-ancestors 'self' https://example.com/ https://example2.com/;"
)


def test_missing(self):
"""Test asset requests that should 404."""
Expand Down
Loading