Skip to content

feat: remove legacy Django unit editor - #38985

Merged
feanil merged 11 commits into
masterfrom
salman/remove-legacy-unit-editor
Nov 12, 2026
Merged

feanil merged 11 commits into
masterfrom
salman/remove-legacy-unit-editor

Conversation

@salman2013

@salman2013 salman2013 commented Aug 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Removes the LEGACY_STUDIO_UNIT_EDITOR waffle flag and all code paths behind it — the MFE-based unit editor (frontend-app-authoring) is now always active
  • Removes use_new_unit_page() from toggles.py — it had no remaining callers after container_handler was simplified
  • Simplifies container_handler to always redirect to the MFE unit editor
  • Simplifies get_unit_url() in utils.py — removed the use_new_unit_page() conditional, always builds the MFE URL
  • Hardcodes use_new_unit_page: true in the waffle flags serializer — the legacy toggle is gone but the API key is kept for now (see Future cleanup below)
  • Deletes container.html — the legacy full-page Django-template unit editor
  • Cleans up related legacy tests and JS specs

Future cleanup

References:

#35261 (DEPR)

Test plan

  • Visit a unit page in Studio — confirm redirect to MFE unit editor
  • Confirm xblock components load correctly inside the MFE unit page (via container_embed_handler iframes)
  • Confirm editing an xblock component opens the edit modal correctly (via xblock_edit_view)
  • Confirm adding/removing components on the unit page works

Testing Results

Browser Testing Verification Results

Method: Playwright (Chromium) against a running Tutor devstack, logged in as edx / Studio course course-v1:courseorg+cs104+2025_T7.

Passed

  • Login — authenticated successfully via LMS login form (shared SSO session also works when hitting Studio's /login directly, which redirects through /oauth2/authorize → LMS login → back to the MFE).
  • Unit page redirects to MFE unit editor — visiting /container/ in Studio correctly redirects to apps.local.openedx.io/.../authoring/course/.../container/..., confirming the simplified container_handler.
  • XBlock components load inside the MFE unit page — verified via the kept container_embed_handler iframe; Word Cloud and Survey/Poll blocks rendered correctly inside it.
  • Editing an XBlock opens the edit modal — clicking Edit loads /xblock/<usage_key>/action/edit in a new iframe and the modal renders, confirming xblock_edit_view works as intended.
  • Removing a component works end-to-end — full round trip verified: iframe → postMessage('deleteXBlock') → MFE confirmation dialog ("Delete this component?") → MFE calls the delete API → postMessage('completeXBlockDeleting') back to the iframe → component removed from the DOM (verified wrapper count dropped from 2 → 1).

Failed / Pre-existing (not caused by this PR)

  • "Add a new component" buttons unresponsive (Text, Library Content, Open Response, Advanced) — confirmed pre-existing via git diff master...HEAD showing zero changes to the relevant MFE-side code; already tracked separately.
  • xblockElement is empty or not defined console warning in edit_xblock.js — confirmed pre-existing (no diff from master in that file); belongs to an unrelated upstream-link feature.
  • Django Debug Toolbar overlay intercepting clicks — local devstack DEBUG artifact, not application code; required removing the #djDebug element in test scripts to interact with the page.

Local setup testing

Right now, the sandbox is not working, so these are the local machine testing results. I made sure the MFE unit page is working as expected in all scenarios after removing legacy code.
Screenshot 2026-08-24 at 3 37 41 PM
Screenshot 2026-08-24 at 3 37 53 PM
Screenshot 2026-08-24 at 3 38 04 PM

@openedx-webhooks openedx-webhooks added open-source-contribution PR author is not from Axim or 2U core contributor PR author is a Core Contributor (who may or may not have write access to this repo). labels Aug 13, 2026
@openedx-webhooks

openedx-webhooks commented Aug 13, 2026 •

Copy link
Copy Markdown

Thanks for the pull request, @salman2013!

This repository is currently maintained by @openedx/wg-maintenance-openedx-platform-oncall.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

Details
Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

The legacy unit editor (controlled by LEGACY_STUDIO_UNIT_EDITOR waffle
flag) has been fully removed. The MFE-based unit editor (frontend-app-
authoring) is now the only editor and is always active.

Removed:
- LEGACY_STUDIO_UNIT_EDITOR CourseWaffleFlag
- container_handler legacy Django-rendered path (now always redirects to MFE)
- xblock_edit_view and xblock_edit_handler URL (/xblock/<key>/action/edit)
- container.html and container_editor.html templates
- unit_outline_spec.js and related legacy tests

Kept (still used by MFE):
- container_embed_handler and /container_embed/<key> URL
- container_chromeless.html template (rendered for MFE iframes)
- use_new_unit_page serializer field (hardcoded True; MFE still reads it)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@salman2013
salman2013 force-pushed the salman/remove-legacy-unit-editor branch from 717835e to 2d87434 Compare August 13, 2026 13:00
@salman2013 salman2013 added the create-sandbox open-craft-grove should create a sandbox environment from this PR label Aug 13, 2026
salman2013 and others added 2 commits August 17, 2026 15:50
- Remove unused imports in block.py (xframe_options_exempt,
  render_to_response, _get_item_in_course)
- Remove unused libraries_v1_enabled import in component.py
- Remove unused ADVANCED_COMPONENT_TYPES import in test_contentstore.py
- Remove unused BeautifulSoup import in test_block.py
- Fix trailing newlines in test_block.py and test_container_page.py
- Remove deleted unit_outline_spec.js from Karma spec list in main.js

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The test_container_html was testing legacy Django-rendered HTML content
(breadcrumbs, section tags from the deleted container.html template).
Removed it since the legacy editor is gone and the split_test container
URL (/container/<key>) was legacy-only — the MFE does not use it.

Also reverts the get_parent_xblock fallback added to container_handler
as it was adding new functionality rather than just removing legacy code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@salman2013 salman2013 closed this Aug 18, 2026
@salman2013 salman2013 reopened this Aug 18, 2026
@github-project-automation github-project-automation Bot moved this from Needs Triage to Done in Contributions Aug 18, 2026
@Agrendalath Agrendalath added create-sandbox open-craft-grove should create a sandbox environment from this PR and removed create-sandbox open-craft-grove should create a sandbox environment from this PR labels Aug 19, 2026
@@ -158,10 +158,14 @@ def get_use_new_course_outline_page(self, obj):

def get_use_new_unit_page(self, obj):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use_new_unit_page is kept in the serializer response for now as a field returning True. Once the MFE confirms it no longer reads this key from the waffle flags API, the field and its method can be removed in a follow-up PR.

…toggles

- Restore xblock_edit_view, container_editor.html, and the xblock_edit_handler
  URL — the Authoring MFE still calls /xblock/<id>/action/edit to load the
  xblock edit modal in an iframe (getLegacyEditModalUrl in xblock-container-iframe)
- Remove use_new_unit_page() from toggles.py — it had no remaining callers
  after container_handler was simplified to always redirect to the MFE

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@salman2013
salman2013 marked this pull request as ready for review August 24, 2026 10:41
@salman2013
salman2013 requested a review from kdmccormick August 25, 2026 10:07
self.assertIn(expected_section_tag, html) # noqa: PT009
self.assertRegex(html, re.compile(expected_breadcrumbs, re.DOTALL)) # noqa: PT009

def test_public_container_preview_html(self):

@irfanuddinahmad irfanuddinahmad Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: container_handler used to fall through to rendering container.html for xblocks-with-children that aren't unit/library-content/split-test-child (what test_container_on_container_html, deleted). Now it raises Http404 unconditionally for that case.

xblock_studio_url() still generates /container/<key> links for such xblocks and surfaces them as studio_url via the REST API, so this path looks reachable in principle (e.g. a nested container xblock other than a unit/library_content/split_test child).

Is this confirmed dead in the current MFE flow? If so, a small test asserting the 404 (replacing the deleted one) would guard against an accidental regression later.

ADVANCED_COMPONENT_TYPES,
['Word cloud', 'Annotation', 'split_test'],
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: two consecutive blank lines left here after the deleted methods above — worth squashing to one.

def test_edit_unit(self):
"""Verifies rendering the editor in all the verticals in the given test course"""
self._check_verticals([self.vert_loc])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: same as above — two consecutive blank lines left after the deleted methods here too.

@salman2013
salman2013 requested a review from feanil October 8, 2026 12:49
@kdmccormick

Copy link
Copy Markdown
Member

Hey @salman2013 and @irfanuddinahmad , are you still working on this?

@feanil feanil left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kdmccormick I think the are ready for it to be more thoroughly reviewed. I looked through some of it and llm review found the follow reasonable issues but I still want to go through it from an approach perspective to make sure it makes sense.

Comment thread cms/djangoapps/contentstore/toggles.py Outdated
"""
return not LEGACY_STUDIO_UNIT_EDITOR.is_enabled(course_key)


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines +161 to +162

raise Http404

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On master a /container/<key> request for a block with children that isn't a unit, library content or a split test child renders the legacy container page. Now it is a 404, but Studio still hands out those URLs: the "Used in" links on the group configurations page point at /container/<split_test> (course_group_config.py), and the clipboard source_edit_url and the library component usage links point at /container/<parent> when the parent is a nested container. The MFE renders a container page for any block with children since openedx/frontend-app-authoring#3058, so redirect those blocks there too.

With this change on a scratch course, /container/ gives 302 for the unit, a split_test and a wrapper block and 404 for a problem. The current head gives 302/404/404/404 and master gives 302/200/200/200.

Suggested change
raise Http404
if xblock_has_own_studio_page(xblock):
return redirect(get_unit_url(course.id, xblock.location))
raise Http404

xblock_has_own_studio_page is in helpers.py next to the imports at the top of this file. Then add a test for the split test case here since test_container_on_container_html went away.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines +163 to +166
Always returns True. The legacy unit editor has been removed.
This method will be removed in a follow-up cleanup.

See https://github.com/openedx/edx-platform/issues/36275

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

frontend-app-authoring has never read this key (nothing in its history matches use_new_unit_page), so there is nothing to wait on from the MFE. Point this at the ticket that tracks dropping the hard-coded fields, like get_use_new_home_page above does.

Suggested change
Always returns True. The legacy unit editor has been removed.
This method will be removed in a follow-up cleanup.
See https://github.com/openedx/edx-platform/issues/36275
Always returns True. The legacy unit editor has been removed and the
Authoring MFE never read this key; it goes when the other hard-coded
fields in this serializer are dropped.
See https://github.com/openedx/openedx-platform/issues/37497

@override_waffle_flag(toggles.LEGACY_STUDIO_UNIT_EDITOR, True)
def test_edit_unit(self):
"""Verifies rendering the editor in all the verticals in the given test course"""
self._check_verticals([self.vert_loc])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the only caller of _check_verticals (line 985 in this file), so delete that too. Its 200 assertion is wrong now that the handler redirects. Not a blocker but while you're here, check_verticals in tests/utils.py has no callers either.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

'js/spec/views/assets_spec',
'js/spec/views/baseview_spec',
'js/spec/views/paged_container_spec',
'js/spec/views/unit_outline_spec',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You removed the spec for js/views/unit_outline but the view is still shipped, along with unit_outline_child.js and unit-outline.underscore, and container.js still mounts it on .wrapper-unit-overview, an element only container.html had. Delete the view and its pieces here too, otherwise keep the spec so the view stays covered until they go.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@feanil feanil left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found a couple more issues while doing a more thorough review. The approach and level of cleanup looks good, looks like some more JS code was missed which can be cleaned up and we need to add some tests back but I think this is pretty close to landing after the comments are addressed.

self.store.publish(self.released_public_vertical.location, self.user.id)
self.store.publish(self.vertical.location, self.user.id)

def test_container_html(self):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were the only tests rendering a full container page and container_embed_handler, which is what the MFE loads, has no test at all. Point get_page_html in tests/utils.py at container_embed_handler instead of deleting these, it has no other callers after this PR. Keep the section tag assertion for the unit, the split test and the nested wrapper and drop the breadcrumbs one since the chromeless page doesn't render them. I tried that on this branch and all three cases pass.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return render_to_response('container.html', container_handler_context)
else:
return HttpResponseBadRequest("Only supports HTML requests")
course, xblock, lms_link, preview_lms_link = _get_item_in_course(request, usage_key)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the LMS links aren't used anymore.

Suggested change
course, xblock, lms_link, preview_lms_link = _get_item_in_course(request, usage_key)
course, xblock, _, _ = _get_item_in_course(request, usage_key)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_UNIT_EDITOR = CourseWaffleFlag('legacy_studio.unit_editor', __name__)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link #35261 from the description and add a line to the operator notes for the release this lands in, since legacy_studio.unit_editor stops being read here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@feanil Could you please elaborate on this comment a bit more? I’m not sure I fully understand what you mean.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. #35261 is the DEPR ticket for the legacy unit editor, add it to the References list in the description so the ticket and the PR that closes it point at each other. Then add a line to the Willow operator notes page, https://openedx.atlassian.net/wiki/spaces/COMM/pages/6476169217/Willow+-+Operator+Dev+Notes, saying the legacy_studio.unit_editor waffle flag is removed and can be deleted from any site that still has it set.

While you're in the description, the "until the MFE confirms it no longer reads this key" lines are out of date, the MFE never read it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@salman2013 For next time, the wiki isn't restricted, any logged-in confluence account can edit that page, so there's nothing for me to grant. I've added the Willow note for this one myself so it doesn't hold the PR up.

- container_handler: redirect any xblock with its own Studio page
  (via xblock_has_own_studio_page) instead of only units/library-content/
  split-test-children, fixing a 404 for nested containers (e.g. split_test)
  that Studio still links to directly
- drop unused lms_link/preview_lms_link locals in container_handler
- repoint get_page_html at container_embed_handler (what the MFE actually
  loads) instead of container_handler, which now only redirects
- restore container page test coverage for a unit, a split_test, and a
  nested wrapper container via container_embed_handler
- add a split_test redirect test case to container_handler's redirect test
- remove dead test helpers _check_verticals/check_verticals (their only
  caller was already removed)
- remove a stray blank line left in toggles.py
- update get_use_new_unit_page docstring to reflect that the Authoring MFE
  never read this waffle flag key
- delete the dead unit_outline JS view, its child view, and template: their
  only mount point (.wrapper-unit-overview) existed solely in the already-
  removed legacy container.html

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@salman2013

salman2013 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor Author

@feanil I’ve addressed the review comments. I also added some testing verification results in the description under "Browser Testing Verification Results"

@salman2013
salman2013 requested a review from feanil October 21, 2026 07:09
CONTAINER_TEMPLATES is rendered into every container_embed_handler,
xblock_edit_view, and library page via get_container_handler_context.
It still listed "unit-outline" after the underlying unit-outline.underscore
template file was deleted, breaking those pages with
TemplateDoesNotExist: js/unit-outline.underscore.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

from cms.djangoapps.contentstore.helpers import get_parent_if_split_test, is_library_content, is_unit
from cms.djangoapps.contentstore.toggles import libraries_v2_enabled, use_new_unit_page
from cms.djangoapps.contentstore.helpers import xblock_has_own_studio_page

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
from cms.djangoapps.contentstore.helpers import xblock_has_own_studio_page
from cms.djangoapps.contentstore.helpers import get_parent_if_split_test, xblock_has_own_studio_page

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_UNIT_EDITOR = CourseWaffleFlag('legacy_studio.unit_editor', __name__)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. #35261 is the DEPR ticket for the legacy unit editor, add it to the References list in the description so the ticket and the PR that closes it point at each other. Then add a line to the Willow operator notes page, https://openedx.atlassian.net/wiki/spaces/COMM/pages/6476169217/Willow+-+Operator+Dev+Notes, saying the legacy_studio.unit_editor waffle flag is removed and can be deleted from any site that still has it set.

While you're in the description, the "until the MFE confirms it no longer reads this key" lines are out of date, the MFE never read it.

Restore the get_parent_if_split_test fallback in container_handler so
/container/<group vertical> redirects to the parent split test's MFE
page instead of returning a 404. Studio still links to these URLs from
the deprecated-blocks list and the import error page.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@salman2013
salman2013 requested a review from feanil October 28, 2026 11:08
# .. toggle_target_removal_date: 2025-09-14
# .. toggle_tickets: https://github.com/openedx/edx-platform/issues/36275
# .. toggle_warning: In Ulmo, this toggle will be removed. Only the new (React-based) experience will be available.
LEGACY_STUDIO_UNIT_EDITOR = CourseWaffleFlag('legacy_studio.unit_editor', __name__)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@salman2013 For next time, the wiki isn't restricted, any logged-in confluence account can edit that page, so there's nothing for me to grant. I've added the Willow note for this one myself so it doesn't hold the PR up.

@feanil
feanil enabled auto-merge (squash) November 12, 2026 20:25
@feanil
feanil merged commit 32e2f7d into master Nov 12, 2026
46 of 47 checks passed
@feanil
feanil deleted the salman/remove-legacy-unit-editor branch November 12, 2026 21:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core contributor PR author is a Core Contributor (who may or may not have write access to this repo). create-sandbox open-craft-grove should create a sandbox environment from this PR open-source-contribution PR author is not from Axim or 2U

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

6 participants