Skip to content

Commit 0deab07

Browse files
authored
Merge branch 'master' into pwnage101/ENT-11510
2 parents 7c9264c + 027eeec commit 0deab07

23 files changed

Lines changed: 1108 additions & 113 deletions

File tree

cms/djangoapps/contentstore/rest_api/v1/serializers/course_waffle_flags.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class CourseWaffleFlagsSerializer(serializers.Serializer):
2121
use_new_import_page = serializers.SerializerMethodField()
2222
use_new_export_page = serializers.SerializerMethodField()
2323
use_new_files_uploads_page = serializers.SerializerMethodField()
24+
use_new_pdf_editor = serializers.SerializerMethodField()
2425
use_new_video_uploads_page = serializers.SerializerMethodField()
2526
use_new_course_outline_page = serializers.SerializerMethodField()
2627
use_new_unit_page = serializers.SerializerMethodField()
@@ -120,6 +121,12 @@ def get_use_new_files_uploads_page(self, obj):
120121
"""
121122
return True
122123

124+
def get_use_new_pdf_editor(self, obj):
125+
"""
126+
Method to get the use_new_pdf_editor switch
127+
"""
128+
return toggles.use_new_pdf_editor()
129+
123130
def get_use_new_video_uploads_page(self, obj):
124131
"""
125132
Method to get the use_new_video_uploads_page switch.

cms/djangoapps/contentstore/rest_api/v1/views/tests/test_course_waffle_flags.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class CourseWaffleFlagsViewTest(CourseTestCase):
3535
"use_new_unit_page": True,
3636
"use_new_updates_page": True,
3737
"use_new_video_uploads_page": False,
38+
"use_new_pdf_editor": True,
3839
"use_react_markdown_editor": False,
3940
"use_video_gallery_flow": False,
4041
"enable_course_optimizer_check_prev_run_links": False,

cms/djangoapps/contentstore/toggles.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,23 @@ def use_new_video_editor(course_key):
105105
return not LEGACY_STUDIO_VIDEO_EDITOR.is_enabled(course_key)
106106

107107

108+
# .. toggle_name: legacy_studio.pdf_editor
109+
# .. toggle_implementation: WaffleFlag
110+
# .. toggle_default: False
111+
# .. toggle_description: Use the PDF XBlock's studio_view instead of the new React-based editor. You may wish to do
112+
# this if you run a custom PDF block.
113+
# .. toggle_use_cases: opt_out
114+
# .. toggle_creation_date: 2026-03-10
115+
LEGACY_STUDIO_PDF_EDITOR = WaffleFlag('legacy_studio.pdf_editor', __name__)
116+
117+
118+
def use_new_pdf_editor():
119+
"""
120+
Returns a boolean = true if new video editor is enabled
121+
"""
122+
return not LEGACY_STUDIO_PDF_EDITOR.is_enabled()
123+
124+
108125
# .. toggle_name: new_core_editors.use_video_gallery_flow
109126
# .. toggle_implementation: WaffleFlag
110127
# .. toggle_default: False

cms/static/js/views/pages/container.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -491,14 +491,14 @@ function($, _, Backbone, gettext, BasePage,
491491
if ($target.closest('button, a, input, label, .actions-list').length) {
492492
return;
493493
}
494-
494+
495495
var $wrapper = $target.closest('.studio-xblock-wrapper');
496496

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

500500
$wrapper.toggleClass('is-selected');
501-
501+
502502
if (this.options.isIframeEmbed) {
503503
const contentId = this.findXBlockElement(event.target).data('locator');
504504
this.postMessageToParent({
@@ -539,11 +539,13 @@ function($, _, Backbone, gettext, BasePage,
539539
const primaryHeader = $(event.target).closest('.xblock-header-primary, .nav-actions');
540540

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

544545
if((blockType === 'html')
545546
|| (useNewVideoEditor === 'True' && blockType === 'video')
546547
|| (blockType === 'problem')
548+
|| (useNewPdfEditor === 'True' && blockType === 'pdf')
547549
) {
548550
var destinationUrl = primaryHeader.attr('authoring_MFE_base_url')
549551
+ '/' + blockType

cms/templates/container.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from django.utils.translation import gettext as _
1414

1515
from cms.djangoapps.contentstore.helpers import xblock_studio_url, xblock_type_display_name
16-
from cms.djangoapps.contentstore.toggles import use_new_video_editor, use_video_gallery_flow
16+
from cms.djangoapps.contentstore.toggles import use_new_pdf_editor, use_new_video_editor, use_video_gallery_flow
1717
from cms.djangoapps.contentstore.utils import get_editor_page_base_url
1818
from openedx.core.djangolib.js_utils import (
1919
dump_js_escaped_json, js_escaped_string
@@ -112,6 +112,7 @@
112112

113113
<%
114114
use_new_editor_video = use_new_video_editor(xblock_locator.course_key)
115+
use_new_editor_pdf = use_new_pdf_editor()
115116
use_new_video_gallery_flow = use_video_gallery_flow()
116117
%>
117118

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

168169
<nav class="nav-actions" aria-label="${_('Page Actions')}"
169170
use-new-editor-video = ${use_new_editor_video}
171+
use-new-editor-pdf = ${use_new_editor_pdf}
170172
use-video-gallery-flow = ${use_new_video_gallery_flow}
171173
authoring_MFE_base_url = ${get_editor_page_base_url(xblock_locator.course_key)}
172174
data-block-type = ${xblock.scope_ids.block_type}

cms/templates/studio_xblock_wrapper.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88
from openedx.core.djangolib.js_utils import (
99
dump_js_escaped_json, js_escaped_string
1010
)
11-
from cms.djangoapps.contentstore.toggles import use_new_video_editor, use_video_gallery_flow
11+
from cms.djangoapps.contentstore.toggles import use_new_pdf_editor, use_new_video_editor, use_video_gallery_flow
1212
from cms.lib.xblock.upstream_sync import UpstreamLink
1313
from openedx.core.djangoapps.content_tagging.toggles import is_tagging_feature_disabled
1414
%>
1515
<%
1616
use_new_editor_video = use_new_video_editor(xblock.context_key)
17+
use_new_editor_pdf = use_new_pdf_editor()
1718
use_new_video_gallery_flow = use_video_gallery_flow()
1819
use_tagging = not is_tagging_feature_disabled()
1920
xblock_url = xblock_studio_url(xblock)
@@ -83,6 +84,7 @@
8384
% endif
8485
"
8586
use-new-editor-video = ${use_new_editor_video}
87+
use-new-editor-pdf = ${use_new_editor_pdf}
8688
use-video-gallery-flow = ${use_new_video_gallery_flow}
8789
authoring_MFE_base_url = ${get_editor_page_base_url(xblock.location.course_key)}
8890
data-block-type = ${xblock.scope_ids.block_type}

lms/djangoapps/course_blocks/transformers/library_content.py

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
"""
2-
Content Library Transformer.
2+
Item Bank Transformer.
3+
4+
Transformers for handling item bank blocks (library_content, itembank, etc.)
5+
that use ItemBankMixin for randomized content selection.
36
"""
47

58

69
import json
710
import logging
811

912
from eventtracking import tracker
13+
from xblock.core import XBlock
1014

1115
from common.djangoapps.track import contexts
1216
from lms.djangoapps.courseware.models import StudentModule
1317
from openedx.core.djangoapps.content.block_structure.transformer import (
1418
BlockStructureTransformer,
1519
FilteringTransformerMixin,
1620
)
17-
from xmodule.library_content_block import LegacyLibraryContentBlock # lint-amnesty, pylint: disable=wrong-import-order
21+
from xmodule.item_bank_block import ItemBankMixin # lint-amnesty, pylint: disable=wrong-import-order
1822
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
1923

2024
from ..utils import get_student_module_as_dict
@@ -25,10 +29,13 @@
2529
class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransformer):
2630
"""
2731
A transformer that manipulates the block structure by removing all
28-
blocks within a library_content block to which a user should not
29-
have access.
32+
blocks within item bank blocks (library_content, itembank, etc.)
33+
to which a user should not have access.
34+
35+
This transformer works with any XBlock that inherits from ItemBankMixin,
36+
filtering children based on the selection logic defined by each block type.
3037
31-
Staff users are not to be exempted from library content pathways.
38+
Staff users are not to be exempted from item bank pathways.
3239
"""
3340
WRITE_VERSION = 1
3441
READ_VERSION = 1
@@ -61,10 +68,10 @@ def summarize_block(usage_key):
6168
"original_usage_version": str(orig_version) if orig_version else None,
6269
}
6370

64-
# For each block check if block is library_content.
65-
# If library_content add children array to content_library_children field
71+
# For each block check if block uses ItemBankMixin (e.g., library_content, itembank).
72+
# If so add block analytics summary for each of its children.
6673
for block_key in block_structure.topological_traversal(
67-
filter_func=lambda block_key: block_key.block_type == 'library_content',
74+
filter_func=lambda block_key: issubclass(XBlock.load_class(block_key.block_type), ItemBankMixin),
6875
yield_descendants_of_unyielded=True,
6976
):
7077
xblock = block_structure.get_xblock(block_key)
@@ -76,7 +83,9 @@ def transform_block_filters(self, usage_info, block_structure):
7683
all_library_children = set()
7784
all_selected_children = set()
7885
for block_key in block_structure:
79-
if block_key.block_type != 'library_content':
86+
block_class = XBlock.load_class(block_key.block_type)
87+
88+
if block_class is None or not issubclass(block_class, ItemBankMixin):
8089
continue
8190
library_children = block_structure.get_children(block_key)
8291
if library_children:
@@ -98,7 +107,12 @@ def transform_block_filters(self, usage_info, block_structure):
98107

99108
# Update selected
100109
previous_count = len(selected)
101-
block_keys = LegacyLibraryContentBlock.make_selection(selected, library_children, max_count)
110+
# Get the cached block class to call make_selection
111+
block_class = XBlock.load_class(block_key.block_type)
112+
if block_class is None:
113+
logger.error('Failed to load block class for %s', block_key)
114+
continue
115+
block_keys = block_class.make_selection(selected, library_children, max_count)
102116
selected = block_keys['selected']
103117

104118
# Save back any changes
@@ -128,7 +142,7 @@ def check_child_removal(block_key):
128142
"""
129143
Return True if selected block should be removed.
130144
131-
Block is removed if it is part of library_content, but has
145+
Block is removed if it is a child of an item bank block, but has
132146
not been selected for current user.
133147
"""
134148
if block_key not in all_library_children:
@@ -156,6 +170,12 @@ def format_block_keys(keys):
156170
json_result.append(info)
157171
return json_result
158172

173+
# Get the cached block class to call publish_selected_children_events
174+
block_class = XBlock.load_class(location.block_type)
175+
if block_class is None:
176+
logger.error('Failed to load block class for publishing events: %s', location)
177+
return
178+
159179
def publish_event(event_name, result, **kwargs):
160180
"""
161181
Helper function to publish an event for analytics purposes
@@ -170,11 +190,12 @@ def publish_event(event_name, result, **kwargs):
170190
context = contexts.course_context_from_course_id(location.course_key)
171191
if user_id:
172192
context['user_id'] = user_id
173-
full_event_name = f"edx.librarycontentblock.content.{event_name}"
193+
event_prefix = block_class.get_selected_event_prefix()
194+
full_event_name = f"{event_prefix}.{event_name}"
174195
with tracker.get_tracker().context(full_event_name, context):
175196
tracker.emit(full_event_name, event_data)
176197

177-
LegacyLibraryContentBlock.publish_selected_children_events(
198+
block_class.publish_selected_children_events(
178199
block_keys,
179200
format_block_keys,
180201
publish_event,
@@ -184,12 +205,14 @@ def publish_event(event_name, result, **kwargs):
184205
class ContentLibraryOrderTransformer(BlockStructureTransformer):
185206
"""
186207
A transformer that manipulates the block structure by modifying the order of the
187-
selected blocks within a library_content block to match the order of the selections
188-
made by the ContentLibraryTransformer or the corresponding XBlock. So this transformer
189-
requires the selections for the randomized content block to be already
190-
made either by the ContentLibraryTransformer or the XBlock.
208+
selected blocks within item bank blocks (library_content, itembank, etc.)
209+
to match the order of the selections made by the ContentLibraryTransformer or the
210+
corresponding XBlock. This transformer requires the selections for the item bank block
211+
to be already made either by the ContentLibraryTransformer or the XBlock.
212+
213+
This transformer works with any XBlock that inherits from ItemBankMixin.
191214
192-
Staff users are *not* exempted from library content pathways.
215+
Staff users are *not* exempted from item bank pathways.
193216
"""
194217
WRITE_VERSION = 1
195218
READ_VERSION = 1
@@ -217,7 +240,8 @@ def transform(self, usage_info, block_structure):
217240
to match the order of the selections made and stored in the XBlock 'selected' field.
218241
"""
219242
for block_key in block_structure:
220-
if block_key.block_type != 'library_content':
243+
block_class = XBlock.load_class(block_key.block_type)
244+
if block_class is None or not issubclass(block_class, ItemBankMixin):
221245
continue
222246

223247
library_children = block_structure.get_children(block_key)
@@ -228,7 +252,7 @@ def transform(self, usage_info, block_structure):
228252
current_selected_blocks = {item[1] for item in state_dict.get('selected', [])}
229253

230254
# As the selections should have already been made by the ContentLibraryTransformer,
231-
# the current children of the library_content block should be the same as the stored
255+
# the current children of the item bank block should be the same as the stored
232256
# selections. If they aren't, some other transformer that ran before this transformer
233257
# has modified those blocks (for example, content gating may have affected this). So do not
234258
# transform the order in that case.

0 commit comments

Comments
 (0)