feat: add support for enabling/disabling the wiki app (#28889)

Currently the wiki app can't be enabled or configured. This change allows enabling/disabling the wiki app which effectively hides/shows the wiki tab.
This commit is contained in:
Kshitij Sobti
2021-10-07 11:34:03 +05:30
committed by GitHub
parent 5d8acc5b41
commit 3e05e0f49b
11 changed files with 181 additions and 87 deletions

View File

@@ -184,8 +184,8 @@ class TabsAPITests(CourseTestCase):
"""
Test that toggling the visibility via the API works.
"""
self.check_toggle_tab_visibility("wiki", True)
self.check_toggle_tab_visibility("wiki", False)
self.check_toggle_tab_visibility("wiki", True)
def test_toggle_tab_visibility_fail(self):
"""

View File

@@ -159,8 +159,8 @@ class TabsPageTests(CourseTestCase):
def test_toggle_tab_visibility(self):
"""Test toggling of tab visibility"""
self.check_toggle_tab_visiblity('wiki', True)
self.check_toggle_tab_visiblity('wiki', False)
self.check_toggle_tab_visiblity('wiki', True)
def test_toggle_invalid_tab_visibility(self):
"""Test toggling visibility of an invalid tab"""

View File

@@ -41,8 +41,8 @@ class CourseHomeMetadataTests(BaseCourseHomeTests):
response = self.client.get(self.url)
assert response.status_code == 200
assert not response.data.get('is_staff')
# 'Course', 'Wiki', 'Progress' tabs
assert len(response.data.get('tabs', [])) == 4
# 'Course', and 'Progress' tabs
assert len(response.data.get('tabs', [])) == 3
@ddt.data(True, False)
def test_get_authenticated_not_enrolled(self, has_previously_enrolled):
@@ -61,8 +61,8 @@ class CourseHomeMetadataTests(BaseCourseHomeTests):
assert response.status_code == 200
assert response.data['is_staff']
# This differs for a staff user because they also receive the Instructor tab
# 'Course', 'Wiki', 'Progress', and 'Instructor' tabs
assert len(response.data.get('tabs', [])) == 5
# 'Course', 'Progress', and 'Instructor' tabs
assert len(response.data.get('tabs', [])) == 4
def test_get_masqueraded_user(self):
CourseEnrollment.enroll(self.user, self.course.id, CourseMode.VERIFIED)

View File

@@ -1,67 +0,0 @@
"""Module with the course app configuration for the Wiki."""
from typing import Dict, Optional, TYPE_CHECKING
from django.conf import settings
from django.utils.translation import ugettext_noop as _
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.course_apps.plugins import CourseApp
# Import the User model only for type checking since importing it at runtime
# will prevent the app from starting since the model is imported before
# Django's machinery is ready.
if TYPE_CHECKING:
from django.contrib.auth import get_user_model
User = get_user_model()
WIKI_ENABLED = settings.WIKI_ENABLED
class WikiCourseApp(CourseApp):
"""
Course app for the Wiki.
"""
app_id = "wiki"
name = _("Wiki")
description = _("Enable learners to access, and collaborate on course-related information.")
documentation_links = {
"learn_more_configuration": settings.WIKI_HELP_URL,
}
@classmethod
def is_available(cls, course_key: CourseKey) -> bool: # pylint: disable=unused-argument
"""
Returns if the app is available for the course.
The wiki is available for all courses or none of them depending on the a Django setting.
"""
return WIKI_ENABLED
@classmethod
def is_enabled(cls, course_key: CourseKey) -> bool: # pylint: disable=unused-argument
"""
Returns if the wiki is available for the course.
The wiki currently cannot be enabled or disabled on a per-course basis.
"""
return WIKI_ENABLED
@classmethod
def set_enabled(cls, course_key: CourseKey, enabled: bool, user: 'User') -> bool:
"""
The wiki cannot be enabled or disabled.
"""
# Currently, you cannot enable/disable wiki via the API
raise ValueError("Wiki cannot be enabled/disabled vis this API.")
@classmethod
def get_allowed_operations(cls, course_key: CourseKey, user: Optional['User'] = None) -> Dict[str, bool]:
"""
Returns the operations you can perform on the wiki.
"""
return {
# The wiki cannot be enabled/disabled via the API yet.
"enable": False,
"configure": True,
}

View File

@@ -0,0 +1,82 @@
"""Module with the course app configuration for the Wiki."""
from typing import Dict, Optional, TYPE_CHECKING
from django.conf import settings
from django.utils.translation import ugettext_noop as _
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview, CourseOverviewTab
from openedx.core.djangoapps.course_apps.plugins import CourseApp
from openedx.core.lib.courses import get_course_by_id
from xmodule.modulestore.django import modulestore
from xmodule.tabs import CourseTab, CourseTabList
# Import the User model only for type checking since importing it at runtime
# will prevent the app from starting since the model is imported before
# Django's machinery is ready.
if TYPE_CHECKING:
from django.contrib.auth import get_user_model
User = get_user_model()
WIKI_ENABLED = settings.WIKI_ENABLED
class WikiCourseApp(CourseApp):
"""
Course app for the Wiki.
"""
app_id = "wiki"
name = _("Wiki")
description = _("Enable learners to access, and collaborate on course-related information.")
documentation_links = {
"learn_more_configuration": settings.WIKI_HELP_URL,
}
@classmethod
def is_available(cls, course_key: CourseKey) -> bool: # pylint: disable=unused-argument
"""
Returns if the app is available for the course.
The wiki is available for all courses or none of them depending on the Django setting.
"""
return WIKI_ENABLED
@classmethod
def is_enabled(cls, course_key: CourseKey) -> bool: # pylint: disable=unused-argument
"""
Returns if the wiki is enabled for the course.
"""
try:
wiki_tab = CourseOverview.get_from_id(course_key).tab_set.get(tab_id='wiki')
return not wiki_tab.is_hidden
except CourseOverviewTab.DoesNotExist:
return False
@classmethod
def set_enabled(cls, course_key: CourseKey, enabled: bool, user: 'User') -> bool:
"""
Enabled/disables the wiki tab in the course.
"""
course = get_course_by_id(course_key)
wiki_tab = CourseTabList.get_tab_by_id(course.tabs, 'wiki')
if wiki_tab is None:
if not enabled:
return False
# If the course doesn't already have the wiki tab, add it.
wiki_tab = CourseTab.load("wiki")
course.tabs.append(wiki_tab)
wiki_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]:
"""
Returns the operations you can perform on the wiki.
"""
return {
"enable": True,
"configure": True,
}

View File

@@ -21,6 +21,17 @@ class WikiTab(EnrolledTab):
is_hideable = True
is_default = False
def __init__(self, tab_dict):
# Default to hidden
super().__init__({"is_hidden": True, **tab_dict})
def to_json(self):
json_val = super().to_json()
# Persist that the tab is *not* hidden
if not self.is_hidden:
json_val.update({"is_hidden": False})
return json_val
@classmethod
def is_enabled(cls, course, user=None):
"""

View File

@@ -4,10 +4,9 @@ Tests for wiki views.
from django.conf import settings
from django.test.client import RequestFactory
from lms.djangoapps.courseware.tabs import get_course_tab_list
from common.djangoapps.student.tests.factories import AdminFactory, UserFactory
from lms.djangoapps.courseware.tabs import get_course_tab_list
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -21,19 +20,28 @@ class WikiTabTestCase(ModuleStoreTestCase):
self.instructor = AdminFactory.create()
self.user = UserFactory()
def _enable_wiki_tab(self):
"""
Enables the wiki tab globally and unhides it for the course.
"""
settings.WIKI_ENABLED = True
# Enable the wiki tab for these tests
for tab in self.course.tabs:
if tab.type == 'wiki':
tab.is_hidden = False
self.course.save()
def get_wiki_tab(self, user, course):
"""Returns true if the "Wiki" tab is shown."""
request = RequestFactory().request()
"""Returns wiki tab if it is shown."""
all_tabs = get_course_tab_list(user, course)
wiki_tabs = [tab for tab in all_tabs if tab.name == 'Wiki']
return wiki_tabs[0] if len(wiki_tabs) == 1 else None
return next((tab for tab in all_tabs if tab.type == 'wiki'), None)
def test_wiki_enabled_and_public(self):
"""
Test wiki tab when Enabled setting is True and the wiki is open to
the public.
"""
settings.WIKI_ENABLED = True
self._enable_wiki_tab()
self.course.allow_public_wiki_access = True
assert self.get_wiki_tab(self.user, self.course) is not None
@@ -41,20 +49,19 @@ class WikiTabTestCase(ModuleStoreTestCase):
"""
Test wiki when it is enabled but not open to the public
"""
settings.WIKI_ENABLED = True
self._enable_wiki_tab()
self.course.allow_public_wiki_access = False
assert self.get_wiki_tab(self.user, self.course) is None
assert self.get_wiki_tab(self.instructor, self.course) is not None
def test_wiki_enabled_false(self):
"""Test wiki tab when Enabled setting is False"""
settings.WIKI_ENABLED = False
assert self.get_wiki_tab(self.user, self.course) is None
assert self.get_wiki_tab(self.instructor, self.course) is None
def test_wiki_visibility(self):
"""Test toggling of visibility of wiki tab"""
settings.WIKI_ENABLED = True
self._enable_wiki_tab()
self.course.allow_public_wiki_access = True
wiki_tab = self.get_wiki_tab(self.user, self.course)
assert wiki_tab is not None
@@ -63,3 +70,9 @@ class WikiTabTestCase(ModuleStoreTestCase):
assert wiki_tab['is_hidden']
wiki_tab['is_hidden'] = False
assert not wiki_tab.is_hidden
def test_wiki_hidden_by_default(self):
"""
Test that the wiki tab is hidden by default
"""
assert self.get_wiki_tab(self.user, self.course) is None

View File

@@ -426,7 +426,7 @@ class EntranceExamsTabsTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, Mi
self.client.logout()
self.login(self.email, self.password)
course_tab_list = get_course_tab_list(self.user, self.course)
assert len(course_tab_list) == 5
assert len(course_tab_list) == 4
def test_course_tabs_list_for_staff_members(self):
"""
@@ -438,7 +438,7 @@ class EntranceExamsTabsTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, Mi
staff_user = StaffFactory(course_key=self.course.id)
self.client.login(username=staff_user.username, password='test')
course_tab_list = get_course_tab_list(staff_user, self.course)
assert len(course_tab_list) == 5
assert len(course_tab_list) == 4
class TextBookCourseViewsTestCase(LoginEnrollmentTestCase, SharedModuleStoreTestCase):

View File

@@ -0,0 +1,55 @@
"""
Tests for wiki course app.
"""
from common.djangoapps.student.tests.factories import AdminFactory, UserFactory
from lms.djangoapps.course_wiki.plugins.course_app import WikiCourseApp
from openedx.core.djangolib.testing.utils import skip_unless_cms
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@skip_unless_cms
class WikiCourseAppTestCase(ModuleStoreTestCase):
"""Test cases for Wiki CourseApp."""
def setUp(self):
super().setUp()
self.course = CourseFactory.create()
self.instructor = AdminFactory.create()
self.user = UserFactory()
def get_wiki_tab(self, course_key):
"""
Reload the course and fetch the wiki tab if present.
"""
course = self.store.get_course(course_key)
return next((tab for tab in course.tabs if tab.type == 'wiki'), None)
def test_app_disabled_by_default(self):
"""
Test that the wiki tab is disabled by default.
"""
assert not WikiCourseApp.is_enabled(self.course.id)
def test_app_enabling(self):
"""
Test that enabling and disable the app enabled/disables the tab.
"""
WikiCourseApp.set_enabled(self.course.id, True, self.instructor)
wiki_tab = self.get_wiki_tab(self.course.id)
assert not wiki_tab.is_hidden
WikiCourseApp.set_enabled(self.course.id, False, self.instructor)
wiki_tab = self.get_wiki_tab(self.course.id)
assert wiki_tab.is_hidden
def test_app_adds_wiki(self):
"""
Test that enabling the app for a course that doesn't have the wiki tab
adds the wiki tab.
"""
self.course.tabs = [tab for tab in self.course.tabs if tab.type != 'wiki']
self.store.update_item(self.course, self.instructor.id)
assert self.get_wiki_tab(self.course.id) is None
WikiCourseApp.set_enabled(self.course.id, True, self.instructor)
assert self.get_wiki_tab(self.course.id) is not None

View File

@@ -155,7 +155,7 @@ class CourseApiTestViews(BaseCoursewareTests, MasqueradeMixin):
enrollment = response.data['enrollment']
assert enrollment_mode == enrollment['mode']
assert enrollment['is_active']
assert len(response.data['tabs']) == 6
assert len(response.data['tabs']) == 5
found = False
for tab in response.data['tabs']:
if tab['type'] == 'external_link':

View File

@@ -46,7 +46,7 @@ setup(
"progress = lms.djangoapps.courseware.plugins:ProgressCourseApp",
"teams = lms.djangoapps.teams.plugins:TeamsCourseApp",
"textbooks = lms.djangoapps.courseware.plugins:TextbooksCourseApp",
"wiki = lms.djangoapps.course_wiki.plugins:WikiCourseApp",
"wiki = lms.djangoapps.course_wiki.plugins.course_app:WikiCourseApp",
"custom_pages = lms.djangoapps.courseware.plugins:CustomPagesCourseApp",
],
"openedx.course_tool": [