feat: add optional API for hiding the dates tab (#37923)
This commit is contained in:
@@ -256,6 +256,15 @@ IN_CONTEXT_DISCUSSION_ENABLED_DEFAULT = True
|
||||
# .. toggle_tickets: 'https://openedx.atlassian.net/browse/VAN-622'
|
||||
ENABLE_COPPA_COMPLIANCE = False
|
||||
|
||||
# .. toggle_name: ENABLE_DATES_COURSE_APP
|
||||
# .. toggle_implementation: DjangoSetting
|
||||
# .. toggle_default: False
|
||||
# .. toggle_description: Controls whether the Dates course app is surfaced via the course apps API/UI.
|
||||
# .. toggle_use_cases: open_edx
|
||||
# .. toggle_creation_date: 2026-02-02
|
||||
# .. toggle_tickets: https://github.com/openedx/platform-roadmap/issues/392
|
||||
ENABLE_DATES_COURSE_APP = False
|
||||
|
||||
ENABLE_JASMINE = False
|
||||
|
||||
MARKETING_EMAILS_OPT_IN = False
|
||||
|
||||
@@ -19,6 +19,7 @@ from lms.djangoapps.courseware.context_processor import user_timezone_locale_pre
|
||||
from lms.djangoapps.courseware.courses import get_course_date_blocks
|
||||
from lms.djangoapps.courseware.date_summary import TodaysDate
|
||||
from lms.djangoapps.courseware.masquerade import setup_masquerade
|
||||
from lms.djangoapps.courseware.tabs import DatesTab
|
||||
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
|
||||
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
|
||||
|
||||
@@ -110,13 +111,19 @@ class DatesTabView(RetrieveAPIView):
|
||||
course_key=course_key,
|
||||
)
|
||||
|
||||
course_date_blocks = (
|
||||
[block for block in blocks if not isinstance(block, TodaysDate)]
|
||||
if DatesTab.is_enabled(course, request.user)
|
||||
else []
|
||||
)
|
||||
|
||||
# User locale settings
|
||||
user_timezone_locale = user_timezone_locale_prefs(request)
|
||||
user_timezone = user_timezone_locale['user_timezone']
|
||||
|
||||
data = {
|
||||
'has_ended': course.has_ended(),
|
||||
'course_date_blocks': [block for block in blocks if not isinstance(block, TodaysDate)],
|
||||
'course_date_blocks': course_date_blocks,
|
||||
'learner_is_full_access': learner_is_full_access,
|
||||
'user_timezone': user_timezone,
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ from lms.djangoapps.courseware.context_processor import user_timezone_locale_pre
|
||||
from lms.djangoapps.courseware.courses import get_course_date_blocks, get_course_info_section
|
||||
from lms.djangoapps.courseware.date_summary import TodaysDate
|
||||
from lms.djangoapps.courseware.masquerade import is_masquerading, setup_masquerade
|
||||
from lms.djangoapps.courseware.tabs import DatesTab
|
||||
from lms.djangoapps.courseware.toggles import courseware_disable_navigation_sidebar_blocks_caching
|
||||
from lms.djangoapps.courseware.views.views import get_cert_data
|
||||
from lms.djangoapps.grades.course_grade_factory import CourseGradeFactory
|
||||
@@ -249,7 +250,12 @@ class OutlineTabView(RetrieveAPIView):
|
||||
if show_enrolled:
|
||||
course_blocks = get_course_outline_block_tree(request, course_key_string, request.user)
|
||||
date_blocks = get_course_date_blocks(course, request.user, request, num_assignments=1)
|
||||
dates_widget['course_date_blocks'] = [block for block in date_blocks if not isinstance(block, TodaysDate)]
|
||||
course_date_blocks = (
|
||||
[block for block in date_blocks if not isinstance(block, TodaysDate)]
|
||||
if DatesTab.is_enabled(course, request.user)
|
||||
else []
|
||||
)
|
||||
dates_widget['course_date_blocks'] = course_date_blocks
|
||||
|
||||
handouts_html = get_course_info_section(request, request.user, course, 'handouts')
|
||||
welcome_message_html = get_current_update_for_user(request, course)
|
||||
|
||||
@@ -8,8 +8,10 @@ from django.utils.translation import gettext_noop as _
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.tabs import CourseTabList
|
||||
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview, CourseTab
|
||||
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.course_apps.plugins import CourseApp
|
||||
from openedx.core.lib.courses import get_course_by_id
|
||||
|
||||
@@ -65,6 +67,58 @@ class ProgressCourseApp(CourseApp):
|
||||
}
|
||||
|
||||
|
||||
class DatesCourseApp(CourseApp):
|
||||
"""Course app stub for course dates."""
|
||||
|
||||
app_id = "dates"
|
||||
name = _("Dates")
|
||||
description = _("Provide learners a summary of important course dates.")
|
||||
documentation_links = {
|
||||
"learn_more_configuration": getattr(settings, "DATES_HELP_URL", ""),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def is_available(cls, course_key: CourseKey) -> bool: # pylint: disable=unused-argument
|
||||
"""
|
||||
Dates app is available when explicitly enabled via settings.
|
||||
"""
|
||||
return settings.ENABLE_DATES_COURSE_APP
|
||||
|
||||
@classmethod
|
||||
def is_enabled(cls, course_key: CourseKey) -> bool:
|
||||
"""
|
||||
The dates course status is stored in the course block.
|
||||
"""
|
||||
course = get_course_by_id(course_key)
|
||||
dates_tab = CourseTabList.get_tab_by_id(course.tabs, 'dates')
|
||||
return bool(dates_tab and not dates_tab.is_hidden)
|
||||
|
||||
@classmethod
|
||||
def set_enabled(cls, course_key: CourseKey, enabled: bool, user: 'User') -> bool:
|
||||
"""
|
||||
The dates course enabled/disabled status is stored in the course block.
|
||||
"""
|
||||
course = get_course_by_id(course_key)
|
||||
dates_tab = CourseTabList.get_tab_by_id(course.tabs, 'dates')
|
||||
if enabled and dates_tab is None:
|
||||
dates_tab = CourseTab.load("dates")
|
||||
course.tabs.append(dates_tab)
|
||||
if dates_tab is not None:
|
||||
dates_tab.is_hidden = not enabled
|
||||
modulestore().update_item(course, user.id)
|
||||
return enabled
|
||||
|
||||
@classmethod
|
||||
def get_allowed_operations(cls, course_key: CourseKey, user: Optional[User] = None) -> Dict[str, bool]: # pylint: disable=unused-argument
|
||||
"""
|
||||
Returns the allowed operations for the app.
|
||||
"""
|
||||
return {
|
||||
"enable": True,
|
||||
"configure": True,
|
||||
}
|
||||
|
||||
|
||||
class TextbooksCourseApp(CourseApp):
|
||||
"""
|
||||
Course app config for textbooks app.
|
||||
|
||||
@@ -307,6 +307,7 @@ class DatesTab(EnrolledTab):
|
||||
title = gettext_noop("Dates")
|
||||
priority = 30
|
||||
view_name = "dates"
|
||||
is_hideable = True
|
||||
|
||||
def __init__(self, tab_dict):
|
||||
def link_func(course, _reverse_func):
|
||||
@@ -315,6 +316,13 @@ class DatesTab(EnrolledTab):
|
||||
tab_dict['link_func'] = link_func
|
||||
super().__init__(tab_dict)
|
||||
|
||||
@classmethod
|
||||
def is_enabled(cls, course, user=None):
|
||||
if not super().is_enabled(course, user=user):
|
||||
return False
|
||||
dates_tab = CourseTabList.get_tab_by_id(course.tabs, 'dates')
|
||||
return bool(dates_tab and not dates_tab.is_hidden)
|
||||
|
||||
|
||||
def get_course_tab_list(user, course):
|
||||
"""
|
||||
|
||||
@@ -870,6 +870,12 @@ class DatesTabTestCase(TabListTestCase):
|
||||
"""Test cases for making sure no persisted dates tab is surfaced"""
|
||||
user = self.create_mock_user()
|
||||
self.course.tabs = self.all_valid_tab_list
|
||||
|
||||
# Ensure hidden state from other tests does not affect this test's intent.
|
||||
dates_tab = xmodule_tabs.CourseTabList.get_tab_by_id(self.course.tabs, 'dates')
|
||||
assert dates_tab is not None
|
||||
dates_tab.is_hidden = False
|
||||
|
||||
self.course.save()
|
||||
|
||||
# Verify that there is a dates tab in the modulestore
|
||||
@@ -886,3 +892,21 @@ class DatesTabTestCase(TabListTestCase):
|
||||
if tab.type == 'dates':
|
||||
num_dates_tabs += 1
|
||||
assert num_dates_tabs == 1
|
||||
|
||||
@patch('common.djangoapps.student.models.course_enrollment.CourseEnrollment.is_enrolled')
|
||||
def test_dates_tab_respects_hide_flag(self, is_enrolled):
|
||||
"""Test that the dates tab respects the hide flag."""
|
||||
is_enrolled.return_value = True
|
||||
user = self.create_mock_user(is_staff=False, is_enrolled=True)
|
||||
self.course.tabs = self.all_valid_tab_list
|
||||
dates_tab = xmodule_tabs.CourseTabList.get_tab_by_id(self.course.tabs, 'dates')
|
||||
assert dates_tab is not None
|
||||
|
||||
dates_tab.is_hidden = False
|
||||
self.course.save()
|
||||
tabs = get_course_tab_list(user, self.course)
|
||||
assert any(tab.type == 'dates' for tab in tabs)
|
||||
|
||||
dates_tab.is_hidden = True
|
||||
tabs = get_course_tab_list(user, self.course)
|
||||
assert not any(tab.type == 'dates' for tab in tabs)
|
||||
|
||||
1
setup.py
1
setup.py
@@ -86,6 +86,7 @@ setup(
|
||||
"openedx.course_app": [
|
||||
"calculator = lms.djangoapps.courseware.plugins:CalculatorCourseApp",
|
||||
"custom_pages = lms.djangoapps.courseware.plugins:CustomPagesCourseApp",
|
||||
"dates = lms.djangoapps.courseware.plugins:DatesCourseApp",
|
||||
"discussion = openedx.core.djangoapps.discussions.plugins:DiscussionCourseApp",
|
||||
"edxnotes = lms.djangoapps.edxnotes.plugins:EdxNotesCourseApp",
|
||||
"live = openedx.core.djangoapps.course_live.plugins:LiveCourseApp",
|
||||
|
||||
Reference in New Issue
Block a user