diff --git a/common/lib/xmodule/xmodule/course_module.py b/common/lib/xmodule/xmodule/course_module.py index f8b287858c..3a829d37ca 100644 --- a/common/lib/xmodule/xmodule/course_module.py +++ b/common/lib/xmodule/xmodule/course_module.py @@ -24,6 +24,9 @@ _ = lambda text: text DEFAULT_START_DATE = datetime(2030, 1, 1, tzinfo=UTC()) +CATALOG_VISIBILITY_CATALOG_AND_ABOUT = "both" +CATALOG_VISIBILITY_ABOUT = "about" +CATALOG_VISIBILITY_NONE = "none" class StringOrDate(Date): def from_json(self, value): @@ -575,6 +578,17 @@ class CourseFields(object): deprecated=True ) + catalog_visibility = String( + display_name=_("Course Visibility In Catalog"), + help=_("Defines the access permissions for showing the course in the course catalog. This can be set to one of three values: 'both' (show in catalog and allow access to about page), 'about' (only allow access to about page), 'none' (do not show in catalog and do not allow access to an about page)."), + default=CATALOG_VISIBILITY_CATALOG_AND_ABOUT, + scope=Scope.settings, + values=[ + {"display_name": _("Both"), "value": CATALOG_VISIBILITY_CATALOG_AND_ABOUT}, + {"display_name": _("About"), "value": CATALOG_VISIBILITY_ABOUT}, + {"display_name": _("None"), "value": CATALOG_VISIBILITY_NONE}] + ) + class CourseDescriptor(CourseFields, SequenceDescriptor): module_class = SequenceModule diff --git a/lms/djangoapps/courseware/access.py b/lms/djangoapps/courseware/access.py index c29d76e533..93bcbf7c1f 100644 --- a/lms/djangoapps/courseware/access.py +++ b/lms/djangoapps/courseware/access.py @@ -8,7 +8,9 @@ import pytz from django.conf import settings from django.contrib.auth.models import AnonymousUser -from xmodule.course_module import CourseDescriptor +from xmodule.course_module import ( + CourseDescriptor, CATALOG_VISIBILITY_CATALOG_AND_ABOUT, + CATALOG_VISIBILITY_ABOUT) from xmodule.error_module import ErrorDescriptor from xmodule.x_module import XModule @@ -110,6 +112,8 @@ def _has_access_course_desc(user, action, course): ACCESS_REQUIRE_STAFF_FOR_COURSE, 'see_exists' -- can see that the course exists. 'staff' -- staff access to course. + 'see_in_catalog' -- user is able to see the course listed in the course catalog. + 'see_about_page' -- user is able to see the course about page. """ def can_load(): """ @@ -204,6 +208,29 @@ def _has_access_course_desc(user, action, course): return can_enroll() or can_load() + def can_see_in_catalog(): + """ + Implements the "can see course in catalog" logic if a course should be visible in the main course catalog + In this case we use the catalog_visibility property on the course descriptor + but also allow course staff to see this. + """ + return ( + course.catalog_visibility == CATALOG_VISIBILITY_CATALOG_AND_ABOUT or + _has_staff_access_to_descriptor(user, course, course.id) + ) + + def can_see_about_page(): + """ + Implements the "can see course about page" logic if a course about page should be visible + In this case we use the catalog_visibility property on the course descriptor + but also allow course staff to see this. + """ + return ( + course.catalog_visibility == CATALOG_VISIBILITY_CATALOG_AND_ABOUT or + course.catalog_visibility == CATALOG_VISIBILITY_ABOUT or + _has_staff_access_to_descriptor(user, course, course.id) + ) + checkers = { 'load': can_load, 'load_forum': can_load_forum, @@ -211,6 +238,8 @@ def _has_access_course_desc(user, action, course): 'see_exists': see_exists, 'staff': lambda: _has_staff_access_to_descriptor(user, course, course.id), 'instructor': lambda: _has_instructor_access_to_descriptor(user, course, course.id), + 'see_in_catalog': can_see_in_catalog, + 'see_about_page': can_see_about_page, } return _dispatch(checkers, action, user, course) diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py index 27ceb530f3..33b8548067 100644 --- a/lms/djangoapps/courseware/courses.py +++ b/lms/djangoapps/courseware/courses.py @@ -16,6 +16,7 @@ from xmodule.modulestore.exceptions import ItemNotFoundError from static_replace import replace_static_urls from xmodule.modulestore import ModuleStoreEnum from xmodule.x_module import STUDENT_VIEW +from microsite_configuration import microsite from courseware.access import has_access from courseware.model_data import FieldDataCache @@ -256,7 +257,7 @@ def get_course_info_section_module(request, course, section_key): log_if_not_found=False, wrap_xmodule_display=False, static_asset_path=course.static_asset_path - ) + ) def get_course_info_section(request, course, section_key): """ @@ -345,7 +346,13 @@ def get_courses(user, domain=None): Returns a list of courses available, sorted by course.number ''' courses = branding.get_visible_courses() - courses = [c for c in courses if has_access(user, 'see_exists', c)] + + permission_name = microsite.get_value( + 'COURSE_CATALOG_VISIBILITY_PERMISSION', + settings.COURSE_CATALOG_VISIBILITY_PERMISSION + ) + + courses = [c for c in courses if has_access(user, permission_name, c)] courses = sorted(courses, key=lambda course: course.number) diff --git a/lms/djangoapps/courseware/tests/test_about.py b/lms/djangoapps/courseware/tests/test_about.py index 164c5be340..bd4cb51013 100644 --- a/lms/djangoapps/courseware/tests/test_about.py +++ b/lms/djangoapps/courseware/tests/test_about.py @@ -15,6 +15,12 @@ from courseware.tests.modulestore_config import TEST_DATA_MONGO_MODULESTORE, TES from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory from opaque_keys.edx.locations import SlashSeparatedCourseKey from student.tests.factories import UserFactory, CourseEnrollmentAllowedFactory +from course_modes.models import CourseMode +from student.models import CourseEnrollment + +from shoppingcart.models import Order, PaidCourseRegistration + +from xmodule.course_module import CATALOG_VISIBILITY_ABOUT, CATALOG_VISIBILITY_NONE # HTML for registration button REG_STR = "