fix: if pages and resources view is disabled, show all pages in studio (#30550)

In a previous PR #28686, the ability to see and enable/disable wiki and progress tabs was removed from studio along with the ability to re-order non-static tabs. The ability to toggle the Wiki tab was moved to the pages and resources section of the course authoring MFE. If that MFE is unavailable this means there is no way to show/hide the Wiki. This reverts some of the old changes if the pages and resources view is disabled.
This commit is contained in:
Kshitij Sobti
2022-06-28 16:19:32 +00:00
committed by GitHub
parent ffbac0dc94
commit 8169aa99da
7 changed files with 103 additions and 60 deletions

View File

@@ -43,15 +43,11 @@ class TabsAPITests(CourseTestCase):
)
# add a static tab to the course, for code coverage
# add 4 static tabs to the course, for code coverage
self.test_tabs = []
for i in range(1, 5):
tab = ItemFactory.create(
parent_location=self.course.location,
category="static_tab",
display_name=f"Static_{i}"
)
self.test_tabs.append(tab)
self.test_tab = ItemFactory.create(
parent_location=self.course.location,
category="static_tab",
display_name="Static_1",
)
self.reload_course()
def check_invalid_response(self, resp):
@@ -129,27 +125,6 @@ class TabsAPITests(CourseTestCase):
new_tab_ids = [tab.tab_id for tab in self.course.tabs]
assert new_tab_ids == reordered_tab_ids
def test_reorder_tabs_invalid_list(self):
"""
Test re-ordering of tabs with invalid tab list.
Not all tabs can be rearranged. Here we are trying to swap the first
two tabs, which is disallowed since the first tab is the "Course" tab
which is immovable.
"""
orig_tab_ids = [tab.tab_id for tab in self.course.tabs]
tab_ids = list(orig_tab_ids)
# reorder the first two tabs
tab_ids[0], tab_ids[1] = tab_ids[1], tab_ids[0]
# post the request
resp = self.make_reorder_tabs_request([{"tab_id": tab_id} for tab_id in tab_ids])
assert resp.status_code == 400
error = self.check_invalid_response(resp)
assert "error" in error
def test_reorder_tabs_invalid_tab_ids(self):
"""
Test re-ordering of tabs with invalid tab.

View File

@@ -13,7 +13,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError
from common.djangoapps.student.auth import has_studio_read_access, has_studio_write_access
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, verify_course_exists, view_auth_classes
from ..serializers import CourseTabSerializer, CourseTabUpdateSerializer, TabIDLocatorSerializer
from ....views.tabs import edit_tab_handler, get_course_static_tabs, reorder_tabs_handler
from ....views.tabs import edit_tab_handler, get_course_tabs, reorder_tabs_handler
@view_auth_classes(is_authenticated=True)
@@ -34,7 +34,7 @@ class CourseTabListView(DeveloperErrorViewMixin, APIView):
@verify_course_exists()
def get(self, request: Request, course_id: str) -> Response:
"""
Get a list of all the static tabs in a course including hidden tabs.
Get a list of all the tabs in a course including hidden tabs.
**Example Request**
@@ -82,7 +82,7 @@ class CourseTabListView(DeveloperErrorViewMixin, APIView):
self.permission_denied(request)
course_module = modulestore().get_course(course_key)
tabs_to_render = get_course_static_tabs(course_module, request.user)
tabs_to_render = get_course_tabs(course_module, request.user)
return Response(CourseTabSerializer(tabs_to_render, many=True).data)

View File

@@ -19,7 +19,7 @@ from xmodule.tabs import CourseTab, CourseTabList, InvalidTabsException, StaticT
from common.djangoapps.edxmako.shortcuts import render_to_response
from common.djangoapps.student.auth import has_course_author_access
from common.djangoapps.util.json_request import JsonResponse, JsonResponseBadRequest, expect_json
from ..utils import get_lms_link_for_item
from ..utils import get_lms_link_for_item, get_pages_and_resources_url
__all__ = ["tabs_handler", "update_tabs_handler"]
@@ -61,10 +61,10 @@ def tabs_handler(request, course_key_string):
return JsonResponse()
elif request.method == "GET": # assume html
# get all tabs from the tabs list and select only static tabs (a.k.a. user-created tabs)
# get all tabs from the tabs list: static tabs (a.k.a. user-created tabs) and built-in tabs
# present in the same order they are displayed in LMS
tabs_to_render = list(get_course_static_tabs(course_item, request.user))
tabs_to_render = list(get_course_tabs(course_item, request.user))
return render_to_response(
"edit-tabs.html",
@@ -78,9 +78,9 @@ def tabs_handler(request, course_key_string):
return HttpResponseNotFound()
def get_course_static_tabs(course_item: CourseBlock, user: User) -> Iterable[CourseTab]:
def get_course_tabs(course_item: CourseBlock, user: User) -> Iterable[CourseTab]:
"""
Yields all the static tabs in a course including hidden tabs.
Yields all the course tabs in a course including hidden tabs.
Args:
course_item (CourseBlock): The course object from which to get the tabs
@@ -90,12 +90,14 @@ def get_course_static_tabs(course_item: CourseBlock, user: User) -> Iterable[Cou
Iterable[CourseTab]: An iterable containing course tab objects from the
course
"""
pages_and_resources_mfe_enabled = bool(get_pages_and_resources_url(course_item.id))
for tab in CourseTabList.iterate_displayable(course_item, user=user, inline_collections=False, include_hidden=True):
if isinstance(tab, StaticTab):
# static tab needs its locator information to render itself as an xmodule
static_tab_loc = course_item.id.make_usage_key("static_tab", tab.url_slug)
tab.locator = static_tab_loc
# If the course apps MFE is set up and pages and resources is enabled, then only show static tabs
if isinstance(tab, StaticTab) or not pages_and_resources_mfe_enabled:
yield tab
@@ -119,7 +121,7 @@ def update_tabs_handler(course_item: CourseBlock, tabs_data: Dict, user: User) -
def reorder_tabs_handler(course_item, tabs_data, user):
"""
Helper function for handling reorder of static tabs request
Helper function for handling reorder of tabs request
"""
# Static tabs are identified by locators (a UsageKey) instead of a tab id like
@@ -152,9 +154,8 @@ def create_new_list(tab_locators, old_tab_list):
tab = get_tab_by_tab_id_locator(old_tab_list, tab_locator)
if tab is None:
raise ValidationError({"error": f"Tab with id_locator '{tab_locator}' does not exist."})
if not isinstance(tab, StaticTab):
raise ValidationError({"error": f"Cannot reorder tabs of type '{tab.type}'"})
new_tab_list.append(tab)
if isinstance(tab, StaticTab):
new_tab_list.append(tab)
# the old_tab_list may contain additional tabs that were not rendered in the UI because of
# global or course settings. so add those to the end of the list.

View File

@@ -1,5 +1,6 @@
""" Tests for tab functions (just primitive). """
import json
import random
@@ -26,15 +27,12 @@ class TabsPageTests(CourseTestCase):
# Set the URL for tests
self.url = reverse_course_url('tabs_handler', self.course.id)
# add 4 static tabs to the course, for code coverage
self.test_tabs = []
for i in range(1, 5):
tab = ItemFactory.create(
parent_location=self.course.location,
category="static_tab",
display_name=f"Static_{i}"
)
self.test_tabs.append(tab)
# add a static tab to the course, for code coverage
self.test_tab = ItemFactory.create(
parent_location=self.course.location,
category="static_tab",
display_name="Static_1"
)
self.reload_course()
def check_invalid_tab_id_response(self, resp):
@@ -95,7 +93,7 @@ class TabsPageTests(CourseTestCase):
# Remove one tab randomly. This shouldn't delete the tab.
tabs_data.pop()
# post the request with the reordered static tabs only
# post the request
resp = self.client.ajax_post(
self.url,
data={
@@ -167,7 +165,7 @@ class TabsPageTests(CourseTestCase):
"""
Verify that the static tab renders itself with the correct HTML
"""
preview_url = f'/xblock/{self.test_tabs[0].location}/{STUDENT_VIEW}'
preview_url = f'/xblock/{self.test_tab.location}/{STUDENT_VIEW}'
resp = self.client.get(preview_url, HTTP_ACCEPT='application/json')
assert resp.status_code == 200

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -59,7 +59,7 @@
<h1 class="page-header">
<small class="subtitle">${_("Content")}</small>
## Translators: Custom Pages refer to the tabs that appear in the top navigation of each course.
<span class="sr"> > </span>${_("Custom pages")}
<span class="sr"> > </span>${_("Pages")}
</h1>
% endif
@@ -101,8 +101,59 @@
css_class = css_class + " is-fixed"
%>
<li class="component ${css_class}" data-locator="${tab.locator}" data-tab-id="${tab.tab_id}"></li>
% if isinstance(tab, StaticTab):
<li class="component ${css_class}" data-locator="${tab.locator}" data-tab-id="${tab.tab_id}"></li>
% else:
<li class="course-nav-item ${css_class}" data-tab-id="${tab.tab_id}">
<div class="course-nav-item-header">
% if tab.is_collection:
<h3 class="title-sub">${_(tab.name)}</h3>
<ul class="course-nav-item-children">
% for item in tab.items(context_course):
<li class="course-nav-item-child title">
${_(item.name)}
</li>
% endfor
</ul>
% else:
<h3 class="title">${_(tab.name)}</h3>
% endif
</div>
<div class="course-nav-item-actions wrapper-actions-list">
<ul class="actions-list">
% if tab.is_hideable:
<li class="action-item action-visible">
<label><span class="sr">${_("Show this page")}</span></label>
% if tab.is_hidden:
<input type="checkbox" class="toggle-checkbox" data-tooltip="${_('Show/hide page')}" checked />
% else:
<input type="checkbox" class="toggle-checkbox" data-tooltip="${_('Show/hide page')}" />
% endif
<div class="action-button"><span class="icon fa fa-eye" aria-hidden="true"></span><span class="icon fa fa-eye-slash"></span></div>
</li>
% endif
</ul>
</div>
% if tab.is_movable:
<div class="drag-handle action" data-tooltip="${_('Drag to reorder')}">
<span class="sr">${_("Drag to reorder")}</span>
</div>
% else:
<div class="drag-handle is-fixed" data-tooltip="${_('This page cannot be reordered')}">
<span class="sr">${_("This page cannot be reordered")}</span>
</div>
% endif
</li>
% endif
% endfor
<li class="new-component-item"></li>
@@ -116,6 +167,22 @@
</div>
</article>
% if pages_and_resources_mfe_enabled:
<aside class="content-supplementary" role="complementary">
<div class="bit">
<h3 class="title-3">${_("What are pages?")}</h3>
<p>${_("Pages are listed horizontally at the top of your course. Default pages (Home, Course, Discussion, Wiki, and Progress) are followed by textbooks and custom pages that you create.")}</p>
</div>
<div class="bit">
<h3 class="title-3">${_("Custom pages")}</h3>
<p>${_("You can create and edit custom pages to provide students with additional course content. For example, you can create pages for the grading policy, course slides, and a course calendar. ")} </p>
</div>
<div class="bit">
<h3 class="title-3">${_("How do pages look to students in my course?")}</h3>
<p>${_("Students see the default and custom pages at the top of your course and use these links to navigate.")} <br /> <a rel="modal" href="#preview-lms-staticpages">${_("See an example")}</a></p>
</div>
</aside>
% else:
<aside class="content-supplementary" role="complementary">
<div class="bit">
<h3 class="title-3">${_("What are custom pages?")}</h3>
@@ -126,15 +193,16 @@
<p>${_("Custom pages are listed horizontally at the top of your course after default pages.")} <br /> <a rel="modal" href="#preview-lms-staticpages">${_("See an example")}</a></p>
</div>
</aside>
% endif
</section>
</div>
<div class="content-modal" id="preview-lms-staticpages">
<h3 class="title">${_("Custom pages in your course")}</h3>
<h3 class="title">${_("Pages in Your Course")}</h3>
<figure>
<img src="${static.url("images/preview-lms-staticpage.png")}" alt="${_('Preview of Pages in your course')}" />
<figcaption class="description">${_("Custom pages are listed horizontally at the top of your course after default pages and textbooks. In the above example, custom pages for \"Course Schedule\" and \"Supplements\" have been added to a course.")}</figcaption>
<img src="${static.url("images/preview-lms-staticpages.png")}" alt="${_('Preview of Pages in your course')}" />
<figcaption class="description">${_("Pages appear in your course's top navigation bar. The default pages (Home, Course, Discussion, Wiki, and Progress) are followed by textbooks and custom pages.")}</figcaption>
</figure>
<a href="#" rel="view" class="action action-modal-close">

View File

@@ -53,7 +53,7 @@ class CourseTab(metaclass=ABCMeta):
priority = None
# Class property that specifies whether the tab can be moved within a course's list of tabs
is_movable = True
is_movable = False
# Class property that specifies whether the tab is a collection of other tabs
is_collection = False
@@ -301,6 +301,7 @@ class StaticTab(CourseTab):
"""
type = 'static_tab'
is_default = False # A static tab is never added to a course by default
is_movable = True
allow_multiple = True
priority = 100