diff --git a/cms/djangoapps/contentstore/config/waffle.py b/cms/djangoapps/contentstore/config/waffle.py index 86d0151ae4..e9c1bd90cd 100644 --- a/cms/djangoapps/contentstore/config/waffle.py +++ b/cms/djangoapps/contentstore/config/waffle.py @@ -67,19 +67,3 @@ REDIRECT_TO_LIBRARY_AUTHORING_MICROFRONTEND = LegacyWaffleFlag( flag_name='library_authoring_mfe', module_name=__name__, ) - - -# .. toggle_name: studio.pages_and_resources_mfe -# .. toggle_implementation: CourseWaffleFlag -# .. toggle_default: False -# .. toggle_description: Waffle flag to link existing studio views to the new Pages and Resources experience. -# .. toggle_use_cases: temporary, open_edx -# .. toggle_creation_date: 2021-05-24 -# .. toggle_target_removal_date: 2021-12-31 -# .. toggle_warnings: Also set settings.COURSE_AUTHORING_MICROFRONTEND_URL. -# .. toggle_tickets: None -ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND = CourseWaffleFlag( - waffle_namespace=waffle_flags(), - flag_name='pages_and_resources_mfe', - module_name=__name__, -) diff --git a/cms/djangoapps/contentstore/tests/test_course_settings.py b/cms/djangoapps/contentstore/tests/test_course_settings.py index d0a81795ce..aac15a55b4 100644 --- a/cms/djangoapps/contentstore/tests/test_course_settings.py +++ b/cms/djangoapps/contentstore/tests/test_course_settings.py @@ -34,6 +34,10 @@ from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRol from common.djangoapps.student.tests.factories import UserFactory from common.djangoapps.util import milestones_helpers from common.djangoapps.xblock_django.models import XBlockStudioConfigurationFlag +from openedx.core.djangoapps.discussions.config.waffle import ( + ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND, + OVERRIDE_DISCUSSION_LEGACY_SETTINGS_FLAG +) from openedx.core.djangoapps.models.course_details import CourseDetails from xmodule.fields import Date from xmodule.modulestore import ModuleStoreEnum @@ -93,6 +97,7 @@ class CourseSettingsEncoderTest(CourseTestCase): self.assertEqual(jsondetails['string'], 'string') +@ddt.ddt class CourseAdvanceSettingViewTest(CourseTestCase, MilestonesTestCaseMixin): """ Tests for AdvanceSettings View. @@ -119,6 +124,27 @@ class CourseAdvanceSettingViewTest(CourseTestCase, MilestonesTestCaseMixin): self.assertEqual(settings_fields["display_name"], "Mobile Course Available") self.assertEqual(settings_fields["deprecated"], True) + @ddt.data( + (False, False, True), + (True, False, False), + (True, True, True), + (False, True, True) + ) + @ddt.unpack + def test_discussion_fields_available(self, is_pages_and_resources_enabled, + is_legacy_discussion_setting_enabled, fields_visible): + """ + Test to check the availability of discussion related fields when relevant flags are enabled + """ + + with override_waffle_flag(ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND, is_pages_and_resources_enabled): + with override_waffle_flag(OVERRIDE_DISCUSSION_LEGACY_SETTINGS_FLAG, is_legacy_discussion_setting_enabled): + response = self.client.get_html(self.course_setting_url).content.decode('utf-8') + self.assertEqual('allow_anonymous' in response, fields_visible) + self.assertEqual('allow_anonymous_to_peers' in response, fields_visible) + self.assertEqual('discussion_blackouts' in response, fields_visible) + self.assertEqual('discussion_topics' in response, fields_visible) + @ddt.ddt class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin): diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index 703f7dc653..226d496e74 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -14,11 +14,11 @@ from opaque_keys.edx.keys import CourseKey, UsageKey from opaque_keys.edx.locator import LibraryLocator from pytz import UTC -from cms.djangoapps.contentstore.config.waffle import ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND from cms.djangoapps.contentstore.toggles import exam_setting_view_enabled from common.djangoapps.student import auth from common.djangoapps.student.models import CourseEnrollment from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole +from openedx.core.djangoapps.discussions.config.waffle import ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND from openedx.core.djangoapps.django_comment_common.models import assign_default_role from openedx.core.djangoapps.django_comment_common.utils import seed_permissions_roles from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers diff --git a/cms/djangoapps/models/settings/course_metadata.py b/cms/djangoapps/models/settings/course_metadata.py index f00ca35287..0dbf338218 100644 --- a/cms/djangoapps/models/settings/course_metadata.py +++ b/cms/djangoapps/models/settings/course_metadata.py @@ -14,6 +14,7 @@ from xblock.fields import Scope from cms.djangoapps.contentstore import toggles from common.djangoapps.xblock_django.models import XBlockStudioConfigurationFlag +from openedx.core.djangoapps.discussions.config.waffle_utils import legacy_discussion_experience_enabled from openedx.core.lib.teams_config import TeamsetType from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG from xmodule.modulestore.django import modulestore @@ -141,6 +142,12 @@ class CourseMetadata: if not settings.PROCTORING_BACKENDS or settings.PROCTORING_BACKENDS.get('proctortrack') is None: exclude_list.append('proctoring_escalation_email') + if not legacy_discussion_experience_enabled(course_key): + exclude_list.append('discussion_blackouts') + exclude_list.append('allow_anonymous') + exclude_list.append('allow_anonymous_to_peers') + exclude_list.append('discussion_topics') + return exclude_list @classmethod diff --git a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py index 5a58599dd6..90a35f1d6b 100644 --- a/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py +++ b/lms/djangoapps/instructor/tests/views/test_instructor_dashboard.py @@ -2,7 +2,6 @@ Unit tests for instructor_dashboard.py. """ - import datetime import re from unittest.mock import patch @@ -30,6 +29,11 @@ from lms.djangoapps.courseware.tests.helpers import LoginEnrollmentTestCase from lms.djangoapps.grades.config.waffle import WRITABLE_GRADEBOOK, waffle_flags from lms.djangoapps.instructor.toggles import DATA_DOWNLOAD_V2 from lms.djangoapps.instructor.views.gradebook_api import calculate_page_info +from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted +from openedx.core.djangoapps.discussions.config.waffle import ( + ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND, + OVERRIDE_DISCUSSION_LEGACY_SETTINGS_FLAG +) from openedx.core.djangoapps.site_configuration.models import SiteConfiguration from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, ModuleStoreTestCase @@ -104,6 +108,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT """ Verify that the instructor tab appears for staff only. """ + def has_instructor_tab(user, course): """Returns true if the "Instructor" tab is shown.""" tabs = get_course_tab_list(user, course) @@ -135,6 +140,62 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT ) assert has_instructor_tab(org_researcher, self.course) + @ddt.data( + ('staff', False, False, True), + ('staff', True, False, False), + ('staff', True, True, True), + ('staff', False, True, True), + ('instructor', False, False, True), + ('instructor', True, False, False), + ('instructor', True, True, True), + ('instructor', False, True, True) + ) + @ddt.unpack + def test_discussion_tab_for_course_staff_role(self, access_role, is_pages_and_resources_enabled, + is_legacy_discussion_setting_enabled, is_discussion_tab_available): + """ + Verify that the Discussion tab is available for course for course staff role. + """ + discussion_section = ('
') + + with override_waffle_flag(ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND, is_pages_and_resources_enabled): + with override_waffle_flag(OVERRIDE_DISCUSSION_LEGACY_SETTINGS_FLAG, is_legacy_discussion_setting_enabled): + user = UserFactory.create() + CourseAccessRoleFactory( + course_id=self.course.id, + user=user, + role=access_role, + org=self.course.id.org + ) + set_course_cohorted(self.course.id, True) + self.client.login(username=self.user.username, password='test') + response = self.client.get(self.url).content.decode('utf-8') + self.assertEqual(discussion_section in response, is_discussion_tab_available) + + @ddt.data( + (False, False, True), + (True, False, False), + (True, True, True), + (False, True, True), + ) + @ddt.unpack + def test_discussion_tab_for_global_user(self, is_pages_and_resources_enabled, + is_legacy_discussion_setting_enabled, is_discussion_tab_available): + """ + Verify that the Discussion tab is available for course for global user. + """ + discussion_section = ('') + + with override_waffle_flag(ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND, is_pages_and_resources_enabled): + with override_waffle_flag(OVERRIDE_DISCUSSION_LEGACY_SETTINGS_FLAG, is_legacy_discussion_setting_enabled): + user = UserFactory.create(is_staff=True) + set_course_cohorted(self.course.id, True) + self.client.login(username=user.username, password='test') + response = self.client.get(self.url).content.decode('utf-8') + self.assertEqual(discussion_section in response, is_discussion_tab_available) + @ddt.data( ('staff', False, False), ('instructor', False, False), @@ -154,7 +215,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT download_section = '' if waffle_status: - download_section = '' user = UserFactory.create(is_staff=access_role == 'global_staff') CourseAccessRoleFactory( @@ -438,7 +499,7 @@ class TestInstructorDashboard(ModuleStoreTestCase, LoginEnrollmentTestCase, XssT ) @ddt.unpack def test_ccx_coaches_option_on_admin_list_management_instructor( - self, ccx_feature_flag, enable_ccx, expected_result + self, ccx_feature_flag, enable_ccx, expected_result ): """ Test whether the "CCX Coaches" option is visible or hidden depending on the value of course.enable_ccx. diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py index 99ecd484a8..474ead4712 100644 --- a/lms/djangoapps/instructor/views/instructor_dashboard.py +++ b/lms/djangoapps/instructor/views/instructor_dashboard.py @@ -52,6 +52,7 @@ from lms.djangoapps.courseware.module_render import get_module_by_usage_id from lms.djangoapps.discussion.django_comment_client.utils import available_division_schemes, has_forum_access from lms.djangoapps.grades.api import is_writable_gradebook_enabled from openedx.core.djangoapps.course_groups.cohorts import DEFAULT_COHORT_NAME, get_course_cohorts, is_course_cohorted +from openedx.core.djangoapps.discussions.config.waffle_utils import legacy_discussion_experience_enabled from openedx.core.djangoapps.django_comment_common.models import FORUM_ROLE_ADMINISTRATOR, CourseDiscussionSettings from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.verified_track_content.models import VerifiedTrackCohortedCourse @@ -133,13 +134,17 @@ def instructor_dashboard_2(request, course_id): # lint-amnesty, pylint: disable sections = [] if access['staff']: - sections.extend([ + sections_content = [ _section_course_info(course, access), _section_membership(course, access), _section_cohort_management(course, access), - _section_discussions_management(course, access), _section_student_admin(course, access), - ]) + ] + + if legacy_discussion_experience_enabled(course_key): + sections_content.append(_section_discussions_management(course, access)) + sections.extend(sections_content) + if access['data_researcher']: sections.append(_section_data_download(course, access)) diff --git a/openedx/core/djangoapps/discussions/config/__init__.py b/openedx/core/djangoapps/discussions/config/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/openedx/core/djangoapps/discussions/config/waffle.py b/openedx/core/djangoapps/discussions/config/waffle.py new file mode 100644 index 0000000000..b642baabca --- /dev/null +++ b/openedx/core/djangoapps/discussions/config/waffle.py @@ -0,0 +1,42 @@ +""" +This module contains various configuration settings via +waffle switches for the discussions app. +""" + +from edx_toggles.toggles import LegacyWaffleFlagNamespace + +from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag + + +WAFFLE_NAMESPACE = LegacyWaffleFlagNamespace(name='discussions') + +# .. toggle_name: discussions.override_discussion_legacy_settings +# .. toggle_implementation: CourseWaffleFlag +# .. toggle_default: False +# .. toggle_description: Waffle flag to override visibility of discussion settings for legacy experience. +# .. toggle_use_cases: temporary, open_edx +# .. toggle_creation_date: 2021-06-15 +# .. toggle_target_removal_date: 2021-12-31 +# .. toggle_warnings: When the flag is ON, the discussion settings will be available on legacy experience. +# .. toggle_tickets: TNL-8389 +OVERRIDE_DISCUSSION_LEGACY_SETTINGS_FLAG = CourseWaffleFlag( + waffle_namespace=WAFFLE_NAMESPACE, + flag_name='override_discussion_legacy_settings', + module_name=__name__, +) + + +# .. toggle_name: discussions.pages_and_resources_mfe +# .. toggle_implementation: CourseWaffleFlag +# .. toggle_default: False +# .. toggle_description: Waffle flag to enable new Pages and Resources experience for course. +# .. toggle_use_cases: temporary, open_edx +# .. toggle_creation_date: 2021-05-24 +# .. toggle_target_removal_date: 2021-12-31 +# .. toggle_warnings: When the flag is ON, the new experience for Pages and Resources will be enabled. +# .. toggle_tickets: TNL-7791 +ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND = CourseWaffleFlag( + waffle_namespace=WAFFLE_NAMESPACE, + flag_name='pages_and_resources_mfe', + module_name=__name__, +) diff --git a/openedx/core/djangoapps/discussions/config/waffle_utils.py b/openedx/core/djangoapps/discussions/config/waffle_utils.py new file mode 100644 index 0000000000..d44b7af5ad --- /dev/null +++ b/openedx/core/djangoapps/discussions/config/waffle_utils.py @@ -0,0 +1,16 @@ +""" +Utils methods for Discussion app waffle flags. +""" + +from openedx.core.djangoapps.discussions.config.waffle import ( + ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND, + OVERRIDE_DISCUSSION_LEGACY_SETTINGS_FLAG +) + + +def legacy_discussion_experience_enabled(course_key): + """ + Checks for relevant flags and returns a boolean whether to show legacy discussion settings or not + """ + return bool(OVERRIDE_DISCUSSION_LEGACY_SETTINGS_FLAG.is_enabled(course_key) or + not ENABLE_PAGES_AND_RESOURCES_MICROFRONTEND.is_enabled(course_key))