From f99756ba049b0f5057e164bb6e82135baab37302 Mon Sep 17 00:00:00 2001 From: Jillian Date: Thu, 22 May 2025 09:47:31 +0930 Subject: [PATCH] fix: recurse through pasted block data to replace static paths (#36723) Fixes an error that is triggered when trying to copy/pasting Drag-and-drop block into Courses. When a block is pasted into a Course or Library, we perform a search/replace on block's data to replace any old static URLs with their new path. Ordinary blocks like HTML and Video have a simple string of data where static URLs may live, but DnDv2 blocks have a dict of data which can contain other dicts and lists of strings that need to be modified. Other XBlocks may have similarly complex structures, and so this fix will resolve them as well. This fixes the issue by recursing into the data structure to locate all the strings where replacements may need to be made. This fix helps Course Authors use content staging in their Courses. (cherry picked from commit 21399b48189a3766f2dd3e3347cab909e777a889) --- cms/djangoapps/contentstore/helpers.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/cms/djangoapps/contentstore/helpers.py b/cms/djangoapps/contentstore/helpers.py index f80e304fb791..ff2020afd89f 100644 --- a/cms/djangoapps/contentstore/helpers.py +++ b/cms/djangoapps/contentstore/helpers.py @@ -315,7 +315,8 @@ def _insert_static_files_into_downstream_xblock( if hasattr(downstream_xblock, "data") and substitutions: data_with_substitutions = downstream_xblock.data for old_static_ref, new_static_ref in substitutions.items(): - data_with_substitutions = data_with_substitutions.replace( + data_with_substitutions = _replace_strings( + data_with_substitutions, old_static_ref, new_static_ref, ) @@ -325,6 +326,26 @@ def _insert_static_files_into_downstream_xblock( return notices +def _replace_strings(obj: dict | list | str, old_str: str, new_str: str): + """ + Replacing any instances of the given `old_str` string with `new_str` in any strings found in the the given object. + + Returns the updated object. + """ + if isinstance(obj, dict): + for key, value in obj.items(): + obj[key] = _replace_strings(value, old_str, new_str) + + elif isinstance(obj, list): + for index, item in enumerate(obj): + obj[index] = _replace_strings(item, old_str, new_str) + + elif isinstance(obj, str): + return obj.replace(old_str, new_str) + + return obj + + def import_staged_content_from_user_clipboard(parent_key: UsageKey, request) -> tuple[XBlock | None, StaticFileNotices]: """ Import a block (along with its children and any required static assets) from