feat!: Drop the legacy studio updates page. (#37544)

Remove the legacy studio updates page and its related artifacts. They
have been replaced by API and a new UI in the authoring MFE.

This cleanup is a part of https://github.com/openedx/edx-platform/issues/36108

BREAKING CHANGE: The 'legacy_studio.updates' waffle flag will no longer
be respected. The system will behave as if the flag is set to false
permanently.
This commit is contained in:
Feanil Patel
2025-11-20 18:00:13 -05:00
committed by GitHub
parent 5d0d60d426
commit d929cdb7fa
16 changed files with 23 additions and 514 deletions

View File

@@ -80,10 +80,15 @@ class CourseWaffleFlagsSerializer(serializers.Serializer):
def get_use_new_updates_page(self, obj):
"""
Method to get the use_new_updates_page switch
Method to indicate if we should use the new updates_page
This used to be based on a waffle flag but the flag is being removed so we
default it to true for now until we can remove the need for it from the consumers
of this serializer and the related APIs.
See https://github.com/openedx/edx-platform/issues/37497
"""
course_key = self.get_course_key()
return toggles.use_new_updates_page(course_key)
return True
def get_use_new_import_page(self, obj):
"""

View File

@@ -1498,8 +1498,6 @@ class ContentStoreTest(ContentStoreTestCase):
test_get_html('export_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_TEAM, True):
test_get_html('course_team_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_UPDATES, True):
test_get_html('course_info_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_CUSTOM_PAGES, True):
test_get_html('tabs_handler')
with override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True):
@@ -1510,6 +1508,16 @@ class ContentStoreTest(ContentStoreTestCase):
test_get_html('advanced_settings_handler')
test_get_json('textbooks_list_handler')
# Test that studio updates load
course_updates_url = reverse(
'course_info_update_handler',
kwargs={
'course_key_string': str(course_key),
}
)
resp = self.client.get(course_updates_url)
assert resp.status_code == 200
# go look at the Edit page
unit_key = course_key.make_usage_key('vertical', 'test_vertical')
with override_waffle_flag(toggles.LEGACY_STUDIO_UNIT_EDITOR, True):

View File

@@ -167,7 +167,6 @@ class CourseAdvanceSettingViewTest(CourseTestCase, MilestonesTestCaseMixin):
@override_waffle_flag(toggles.LEGACY_STUDIO_IMPORT, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_EXPORT, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_COURSE_TEAM, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_UPDATES, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_CUSTOM_PAGES, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_SCHEDULE_DETAILS, True)
@override_waffle_flag(toggles.LEGACY_STUDIO_GRADING, True)
@@ -185,7 +184,6 @@ class CourseAdvanceSettingViewTest(CourseTestCase, MilestonesTestCaseMixin):
'import_handler',
'export_handler',
'course_team_handler',
'course_info_handler',
'tabs_handler',
'settings_handler',
'grading_handler',

View File

@@ -256,25 +256,6 @@ def use_new_grading_page(course_key):
return not LEGACY_STUDIO_GRADING.is_enabled(course_key)
# .. toggle_name: legacy_studio.updates
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False
# .. toggle_description: Temporarily fall back to the old Studio Course Updates page.
# .. toggle_use_cases: temporary
# .. toggle_creation_date: 2025-03-14
# .. 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_UPDATES = CourseWaffleFlag('legacy_studio.updates', __name__)
def use_new_updates_page(course_key):
"""
Returns a boolean if new studio updates mfe is enabled
"""
return not LEGACY_STUDIO_UPDATES.is_enabled(course_key)
# .. toggle_name: legacy_studio.import
# .. toggle_implementation: WaffleFlag
# .. toggle_default: False

View File

@@ -51,7 +51,6 @@ from cms.djangoapps.contentstore.toggles import (
use_new_import_page,
use_new_schedule_details_page,
use_new_unit_page,
use_new_updates_page,
use_new_video_uploads_page,
)
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
@@ -362,11 +361,10 @@ def get_updates_url(course_locator) -> str:
Gets course authoring microfrontend URL for updates page view.
"""
updates_url = None
if use_new_updates_page(course_locator):
mfe_base_url = get_course_authoring_url(course_locator)
course_mfe_url = f'{mfe_base_url}/course/{course_locator}/course_info'
if mfe_base_url:
updates_url = course_mfe_url
mfe_base_url = get_course_authoring_url(course_locator)
course_mfe_url = f'{mfe_base_url}/course/{course_locator}/course_info'
if mfe_base_url:
updates_url = course_mfe_url
return updates_url

View File

@@ -71,7 +71,6 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_
from openedx.core.djangolib.js_utils import dump_js_escaped_json
from openedx.core.lib.course_tabs import CourseTabPluginManager
from organizations.models import Organization
from xmodule.contentstore.content import StaticContent # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.course_block import CourseBlock, CourseFields # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.error_block import ErrorBlock # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore import EdxJSONEncoder # lint-amnesty, pylint: disable=wrong-import-order
@@ -90,7 +89,6 @@ from ..courseware_index import CoursewareSearchIndexer, SearchIndexingError
from ..tasks import rerun_course as rerun_course_task
from ..toggles import (
default_enable_flexible_peer_openassessments,
use_new_updates_page,
use_new_advanced_settings_page,
use_new_grading_page,
use_new_group_configurations_page,
@@ -1064,24 +1062,7 @@ def course_info_handler(request, course_key_string):
except InvalidKeyError:
raise Http404 # lint-amnesty, pylint: disable=raise-missing-from
with modulestore().bulk_operations(course_key):
course_block = get_course_and_check_access(course_key, request.user)
if not course_block:
raise Http404
if use_new_updates_page(course_key):
return redirect(get_updates_url(course_key))
if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
return render_to_response(
'course_info.html',
{
'context_course': course_block,
'updates_url': reverse_course_url('course_info_update_handler', course_key),
'handouts_locator': course_key.make_usage_key('course_info', 'handouts'),
'base_asset_url': StaticContent.get_base_url_path_for_course_assets(course_block.id),
}
)
else:
return HttpResponseBadRequest("Only supports html requests")
return redirect(get_updates_url(course_key))
@login_required

View File

@@ -5,9 +5,7 @@ unit tests for course_info views and models.
import json
from opaque_keys.edx.keys import UsageKey
from edx_toggles.toggles.testutils import override_waffle_flag
from cms.djangoapps.contentstore import toggles
from cms.djangoapps.contentstore.tests.test_course_settings import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url, reverse_usage_url
from openedx.core.lib.xblock_utils import get_course_update_items
@@ -23,7 +21,6 @@ class CourseUpdateTest(CourseTestCase): # lint-amnesty, pylint: disable=missing
return reverse_course_url('course_info_update_handler', course_key, kwargs=kwargs)
# The do all and end all of unit test cases.
@override_waffle_flag(toggles.LEGACY_STUDIO_UPDATES, True)
def test_course_update(self):
"""Go through each interface and ensure it works."""
def get_response(content, date):
@@ -40,11 +37,6 @@ class CourseUpdateTest(CourseTestCase): # lint-amnesty, pylint: disable=missing
return json.loads(resp.content.decode('utf-8'))
resp = self.client.get_html(
reverse_course_url('course_info_handler', self.course.id)
)
self.assertContains(resp, 'Course Updates', status_code=200)
init_content = '<iframe width="560" height="315" src="http://www.youtube.com/embed/RocY-Jd93XU" frameborder="0">' # lint-amnesty, pylint: disable=line-too-long
content = init_content + '</iframe>'
payload = get_response(content, 'January 8, 2013')

View File

@@ -20,7 +20,6 @@
'js/factories/asset_index',
'js/factories/base',
'js/factories/course_create_rerun',
'js/factories/course_info',
'js/factories/export',
'js/factories/group_configurations',
'js/certificates/factories/certificates_page_factory',

View File

@@ -235,7 +235,6 @@
'js/spec/models/settings_course_grader_spec',
'js/spec/models/settings_grading_spec',
'js/spec/models/upload_spec',
'js/spec/views/course_info_spec',
'js/spec/views/metadata_edit_spec',
'js/spec/views/upload_spec',
'js/spec/video/transcripts/message_manager_spec',

View File

@@ -1,12 +0,0 @@
define(['backbone', 'js/models/course_update'], function(Backbone, CourseUpdateModel) {
/*
The intitializer of this collection must set id to the update's location.url and courseLocation to the course's location. Must pass the
collection of updates as [{ date : "month day", content : "html"}]
*/
var CourseUpdateCollection = Backbone.Collection.extend({
// instantiator must set url
model: CourseUpdateModel
});
return CourseUpdateCollection;
});

View File

@@ -1,26 +0,0 @@
define([
'jquery', 'js/collections/course_update', 'js/models/module_info',
'js/models/course_info', 'js/views/course_info_edit'
], function($, CourseUpdateCollection, ModuleInfoModel, CourseInfoModel, CourseInfoEditView) {
'use strict';
return function(updatesUrl, handoutsLocator, baseAssetUrl) {
var course_updates = new CourseUpdateCollection(),
course_handouts, editor;
course_updates.url = updatesUrl;
course_updates.fetch({reset: true});
course_handouts = new ModuleInfoModel({
id: handoutsLocator
});
editor = new CourseInfoEditView({
el: $('.main-wrapper'),
model: new CourseInfoModel({
updates: course_updates,
base_asset_url: baseAssetUrl,
handouts: course_handouts
})
});
editor.render();
};
});

View File

@@ -1,13 +0,0 @@
define(['backbone'], function(Backbone) {
// single per course holds the updates and handouts
var CourseInfo = Backbone.Model.extend({
// This model class is not suited for restful operations and is considered just a server side initialized container
url: '',
defaults: {
updates: null, // UpdateCollection
handouts: null // HandoutCollection
}
});
return CourseInfo;
});

View File

@@ -1,283 +0,0 @@
define(["js/views/course_info_handout", "js/views/course_info_update", "js/models/module_info",
"js/collections/course_update", "edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers"],
(CourseInfoHandoutsView, CourseInfoUpdateView, ModuleInfo, CourseUpdateCollection, AjaxHelpers) =>
describe("Course Updates and Handouts", function() {
const courseInfoPage = `\
<div class="course-info-wrapper">
<div class="main-column window">
<article class="course-updates" id="course-update-view">
<ol class="update-list" id="course-update-list"></ol>
</article>
</div>
<div class="sidebar window course-handouts" id="course-handouts-view"></div>
</div>
<div class="modal-cover"></div>\
`;
beforeEach(function() {
window.analytics = jasmine.createSpyObj('analytics', ['track']);
window.course_location_analytics = jasmine.createSpy();
});
afterEach(function() {
delete window.analytics;
delete window.course_location_analytics;
});
describe("Course Updates", function() {
const courseInfoTemplate = readFixtures('course_info_update.underscore');
beforeEach(function() {
let cancelEditingUpdate;
setFixtures($("<script>", {id: "course_info_update-tpl", type: "text/template"}).text(courseInfoTemplate));
appendSetFixtures(courseInfoPage);
this.collection = new CourseUpdateCollection();
this.collection.url = 'course_info_update/';
this.courseInfoEdit = new CourseInfoUpdateView({
el: $('.course-updates'),
collection: this.collection,
base_asset_url : 'base-asset-url/'
});
this.courseInfoEdit.render();
this.event = {
preventDefault() { return 'no op'; }
};
this.createNewUpdate = function(text) {
// Edit button is not in the template under test (it is in parent HTML).
// Therefore call onNew directly.
this.courseInfoEdit.onNew(this.event);
spyOn(this.courseInfoEdit.$codeMirror, 'getValue').and.returnValue(text);
return this.courseInfoEdit.$el.find('.save-button').click();
};
this.cancelNewCourseInfo = function(useCancelButton) {
this.courseInfoEdit.onNew(this.event);
spyOn(this.courseInfoEdit.$modalCover, 'hide').and.callThrough();
spyOn(this.courseInfoEdit.$codeMirror, 'getValue').and.returnValue('unsaved changes');
const model = this.collection.at(0);
spyOn(model, "save").and.callThrough();
cancelEditingUpdate(this.courseInfoEdit, this.courseInfoEdit.$modalCover, useCancelButton);
expect(this.courseInfoEdit.$modalCover.hide).toHaveBeenCalled();
expect(model.save).not.toHaveBeenCalled();
const previewContents = this.courseInfoEdit.$el.find('.update-contents').html();
expect(previewContents).not.toEqual('unsaved changes');
};
this.doNotCloseNewCourseInfo = function() {
this.courseInfoEdit.onNew(this.event);
spyOn(this.courseInfoEdit.$modalCover, 'hide').and.callThrough();
spyOn(this.courseInfoEdit.$codeMirror, 'getValue').and.returnValue('unsaved changes');
const model = this.collection.at(0);
spyOn(model, "save").and.callThrough();
cancelEditingUpdate(this.courseInfoEdit, this.courseInfoEdit.$modalCover, false);
expect(model.save).not.toHaveBeenCalled();
expect(this.courseInfoEdit.$modalCover.hide).not.toHaveBeenCalled();
};
this.cancelExistingCourseInfo = function(useCancelButton) {
this.createNewUpdate('existing update');
this.courseInfoEdit.$el.find('.edit-button').click();
spyOn(this.courseInfoEdit.$modalCover, 'hide').and.callThrough();
spyOn(this.courseInfoEdit.$codeMirror, 'getValue').and.returnValue('modification');
const model = this.collection.at(0);
spyOn(model, "save").and.callThrough();
model.id = "saved_to_server";
cancelEditingUpdate(this.courseInfoEdit, this.courseInfoEdit.$modalCover, useCancelButton);
expect(this.courseInfoEdit.$modalCover.hide).toHaveBeenCalled();
expect(model.save).not.toHaveBeenCalled();
const previewContents = this.courseInfoEdit.$el.find('.update-contents').html();
expect(previewContents).toEqual('existing update');
};
this.testInvalidDateValue = function(value) {
this.courseInfoEdit.onNew(this.event);
expect(this.courseInfoEdit.$el.find('.save-button').hasClass("is-disabled")).toEqual(false);
this.courseInfoEdit.$el.find('input.date').val(value).trigger("change");
expect(this.courseInfoEdit.$el.find('.save-button').hasClass("is-disabled")).toEqual(true);
this.courseInfoEdit.$el.find('input.date').val("01/01/16").trigger("change");
expect(this.courseInfoEdit.$el.find('.save-button').hasClass("is-disabled")).toEqual(false);
};
return cancelEditingUpdate = function(update, modalCover, useCancelButton) {
if (useCancelButton) {
return update.$el.find('.cancel-button').click();
} else {
return modalCover.click();
}
};
});
it("does send expected data on save", function() {
const requests = AjaxHelpers["requests"](this);
// Create a new update, verifying that the model is created
// in the collection and save is called.
expect(this.collection.isEmpty()).toBeTruthy();
this.courseInfoEdit.onNew(this.event);
expect(this.collection.length).toEqual(1);
const model = this.collection.at(0);
spyOn(model, "save").and.callThrough();
spyOn(this.courseInfoEdit.$codeMirror, 'getValue').and.returnValue('/static/image.jpg');
// Click the "Save button."
this.courseInfoEdit.$el.find('.save-button').click();
expect(model.save).toHaveBeenCalled();
const requestSent = JSON.parse(requests[requests.length - 1].requestBody);
// Verify the link is not rewritten when saved.
expect(requestSent.content).toEqual('/static/image.jpg');
// Verify that analytics are sent
expect(window.analytics.track).toHaveBeenCalled();
});
it("does rewrite links for preview", function() {
// Create a new update.
this.createNewUpdate('/static/image.jpg');
// Verify the link is rewritten for preview purposes.
const previewContents = this.courseInfoEdit.$el.find('.update-contents').html();
expect(previewContents).toEqual('base-asset-url/image.jpg');
});
it("shows static links in edit mode", function() {
this.createNewUpdate('/static/image.jpg');
// Click edit and verify CodeMirror contents.
this.courseInfoEdit.$el.find('.edit-button').click();
expect(this.courseInfoEdit.$codeMirror.getValue()).toEqual('/static/image.jpg');
});
it("removes newly created course info on cancel", function() {
this.cancelNewCourseInfo(true);
});
it("do not close new course info on click outside modal", function() {
this.doNotCloseNewCourseInfo();
});
it("does not remove existing course info on cancel", function() {
this.cancelExistingCourseInfo(true);
});
it("does not remove existing course info on click outside modal", function() {
this.cancelExistingCourseInfo(false);
});
it("does not allow updates to be saved with an invalid date", function() {
this.testInvalidDateValue("Marchtober 40, 2048");
});
it("does not allow updates to be saved with a blank date", function() {
this.testInvalidDateValue("");
});
});
describe("Course Handouts", function() {
const handoutsTemplate = readFixtures('course_info_handouts.underscore');
beforeEach(function() {
setFixtures($("<script>", {id: "course_info_handouts-tpl", type: "text/template"}).text(handoutsTemplate));
appendSetFixtures(courseInfoPage);
this.model = new ModuleInfo({
id: 'handouts-id',
data: '/static/fromServer.jpg'
});
this.handoutsEdit = new CourseInfoHandoutsView({
el: $('#course-handouts-view'),
model: this.model,
base_asset_url: 'base-asset-url/'
});
this.handoutsEdit.render();
});
it("saves <ol></ol> when content left empty", function() {
const requests = AjaxHelpers["requests"](this);
// Enter empty string in the handouts section, verifying that the model
// is saved with '<ol></ol>' instead of the empty string
this.handoutsEdit.$el.find('.edit-button').click();
spyOn(this.handoutsEdit.$codeMirror, 'getValue').and.returnValue('');
spyOn(this.model, "save").and.callThrough();
this.handoutsEdit.$el.find('.save-button').click();
expect(this.model.save).toHaveBeenCalled();
const contentSaved = JSON.parse(requests[requests.length - 1].requestBody).data;
expect(contentSaved).toEqual('<ol></ol>');
});
it("does not rewrite links on save", function() {
const requests = AjaxHelpers["requests"](this);
// Enter something in the handouts section, verifying that the model is saved
// when "Save" is clicked.
this.handoutsEdit.$el.find('.edit-button').click();
spyOn(this.handoutsEdit.$codeMirror, 'getValue').and.returnValue('/static/image.jpg');
spyOn(this.model, "save").and.callThrough();
this.handoutsEdit.$el.find('.save-button').click();
expect(this.model.save).toHaveBeenCalled();
const contentSaved = JSON.parse(requests[requests.length - 1].requestBody).data;
expect(contentSaved).toEqual('/static/image.jpg');
});
it("does rewrite links in initial content", function() {
expect(this.handoutsEdit.$preview.html().trim()).toBe('base-asset-url/fromServer.jpg');
});
it("does rewrite links after edit", function() {
// Edit handouts and save.
this.handoutsEdit.$el.find('.edit-button').click();
spyOn(this.handoutsEdit.$codeMirror, 'getValue').and.returnValue('/static/image.jpg');
this.handoutsEdit.$el.find('.save-button').click();
// Verify preview text.
expect(this.handoutsEdit.$preview.html().trim()).toBe('base-asset-url/image.jpg');
});
it("shows static links in edit mode", function() {
// Click edit and verify CodeMirror contents.
this.handoutsEdit.$el.find('.edit-button').click();
expect(this.handoutsEdit.$codeMirror.getValue().trim()).toEqual('/static/fromServer.jpg');
});
it("can open course handouts with bad html on edit", function() {
// Enter some bad html in handouts section, verifying that the
// model/handoutform opens when "Edit" is clicked
this.model = new ModuleInfo({
id: 'handouts-id',
data: '<p><a href="[URL OF FILE]>[LINK TEXT]</a></p>'
});
this.handoutsEdit = new CourseInfoHandoutsView({
el: $('#course-handouts-view'),
model: this.model,
base_asset_url: 'base-asset-url/'
});
this.handoutsEdit.render();
expect($('.edit-handouts-form').is(':hidden')).toEqual(true);
this.handoutsEdit.$el.find('.edit-button').click();
expect(this.handoutsEdit.$codeMirror.getValue()).toEqual('<p><a href="[URL OF FILE]>[LINK TEXT]</a></p>');
expect($('.edit-handouts-form').is(':hidden')).toEqual(false);
});
});
})
);

View File

@@ -1,32 +0,0 @@
define(['js/views/baseview', 'js/views/course_info_update', 'js/views/course_info_handout'],
function(BaseView, CourseInfoUpdateView, CourseInfoHandoutView) {
/* this view should own everything on the page which has controls effecting its operation
generate other views for the individual editors.
The render here adds views for each update/handout by delegating to their collections but does not
generate any html for the surrounding page.
*/
var CourseInfoEdit = BaseView.extend({
// takes CMS.Models.CourseInfo as model
tagName: 'div',
render: function() {
// instantiate the ClassInfoUpdateView and delegate the proper dom to it
// eslint-disable-next-line no-new
new CourseInfoUpdateView({
el: $('body.updates'),
collection: this.model.get('updates'),
base_asset_url: this.model.get('base_asset_url')
});
// eslint-disable-next-line no-new
new CourseInfoHandoutView({
el: this.$('#course-handouts-view'),
model: this.model.get('handouts'),
base_asset_url: this.model.get('base_asset_url')
});
return this;
}
});
return CourseInfoEdit;
}); // end define()

View File

@@ -1,78 +0,0 @@
<%page expression_filter="h"/>
<%inherit file="base.html" />
<%def name="online_help_token()"><% return "updates" %></%def>
<%namespace name='static' file='static_content.html'/>
<%!
from django.utils.translation import gettext as _
from openedx.core.djangolib.js_utils import (
dump_js_escaped_json, js_escaped_string
)
%>
## TODO decode course # from context_course into title.
<%block name="title">${_("Course Updates")}</%block>
<%block name="bodyclass">is-signedin course course-info updates view-updates</%block>
<%block name="header_extras">
<link rel="stylesheet" type="text/css" href="${static.url('js/vendor/timepicker/jquery.timepicker.css')}" />
% for template_name in ["course_info_update", "course_info_handouts"]:
<script type="text/template" id="${template_name}-tpl">
<%static:include path="js/${template_name}.underscore" />
</script>
% endfor
</%block>
<%block name="requirejs">
require(["js/factories/course_info"], function(CourseInfoFactory) {
CourseInfoFactory(
"${updates_url | n, js_escaped_string}",
"${handouts_locator | n, js_escaped_string}",
"${base_asset_url | n, js_escaped_string}"
);
});
</%block>
<%block name="content">
<div class="wrapper-mast wrapper">
<header class="mast has-actions has-subtitle">
<h1 class="page-header">
<small class="subtitle">${_("Content")}</small>
<span class="sr">&gt; </span>${_("Course Updates")}
</h1>
<nav class="nav-actions" aria-label="${_('Page Actions')}">
<h3 class="sr">${_('Page Actions')}</h3>
<ul>
<li class="nav-item">
<a href="#" class=" button new-button new-update-button" disabled><span class="icon fa fa-plus" aria-hidden="true"></span> ${_('New Update')}</a>
</li>
</ul>
</nav>
</header>
</div>
<div class="wrapper-content wrapper">
<section class="content">
<div class="introduction">
<p class="copy">${_('Use course updates to notify students of important dates or exams, highlight particular discussions in the forums, announce schedule changes, and respond to student questions. You add or edit updates in HTML.')}</p>
</div>
</section>
</div>
<div class="main-wrapper">
<div class="inner-wrapper">
<div class="course-info-wrapper"
% if getattr(context_course, 'language'):
lang="${context_course.language}"
% endif
>
<div class="main-column window">
<article class="course-updates" id="course-update-view">
<ol class="update-list" id="course-update-list"></ol>
</article>
</div>
<div class="sidebar course-handouts" id="course-handouts-view"></div>
</div>
</div>
</div>
</%block>

View File

@@ -46,7 +46,6 @@
certificates_url = reverse('certificates_list_handler', kwargs={'course_key_string': str(course_key)})
checklists_url = reverse('checklists_handler', kwargs={'course_key_string': str(course_key)})
pages_and_resources_mfe_enabled = ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND.is_enabled(context_course.id)
updates_mfe_enabled = toggles.use_new_updates_page(context_course.id)
video_upload_mfe_enabled = toggles.use_new_video_uploads_page(context_course.id)
schedule_details_mfe_enabled = toggles.use_new_schedule_details_page(context_course.id)
grading_mfe_enabled = toggles.use_new_grading_page(context_course.id)
@@ -83,16 +82,9 @@
<a href="${get_course_libraries_url(course_key)}">${_("Libraries")}</a>
</li>
% endif
% if not updates_mfe_enabled:
<li class="nav-item nav-course-courseware-updates">
<a href="${course_info_url}">${_("Updates")}</a>
</li>
% endif
% if updates_mfe_enabled:
<li class="nav-item nav-course-courseware-updates">
<a href="${get_updates_url(course_key)}">${_("Updates")}</a>
</li>
% endif
% if not pages_and_resources_mfe_enabled:
<li class="nav-item nav-course-courseware-pages">
<a href="${tabs_url}">${_("Pages")}</a>