From ef0699c4d861bd85d66a49cbc70ef92d9ec6008e Mon Sep 17 00:00:00 2001 From: Matthew Piatetsky Date: Wed, 26 Oct 2016 14:02:59 -0400 Subject: [PATCH] Add hidden flag and return courses hidden in LMS when include_hidden is passed --- lms/djangoapps/course_api/forms.py | 2 ++ lms/djangoapps/course_api/serializers.py | 11 +++++++++++ lms/djangoapps/course_api/tests/test_forms.py | 10 +++++++--- lms/djangoapps/course_api/tests/test_serializers.py | 1 + lms/djangoapps/course_api/views.py | 4 ++++ lms/djangoapps/courseware/courses.py | 11 +++++++++-- 6 files changed, 34 insertions(+), 5 deletions(-) diff --git a/lms/djangoapps/course_api/forms.py b/lms/djangoapps/course_api/forms.py index 1767cc7422..e271bf48a9 100644 --- a/lms/djangoapps/course_api/forms.py +++ b/lms/djangoapps/course_api/forms.py @@ -54,8 +54,10 @@ class CourseListGetForm(UsernameValidatorMixin, Form): filter_type = namedtuple('filter_type', ['param_name', 'field_name']) supported_filters = [ filter_type(param_name='mobile', field_name='mobile_available'), + filter_type(param_name='include_hidden', field_name='include_hidden'), ] mobile = ExtendedNullBooleanField(required=False) + include_hidden = ExtendedNullBooleanField(required=False) def clean(self): """ diff --git a/lms/djangoapps/course_api/serializers.py b/lms/djangoapps/course_api/serializers.py index bcd5806fb3..531b8b41b3 100644 --- a/lms/djangoapps/course_api/serializers.py +++ b/lms/djangoapps/course_api/serializers.py @@ -72,10 +72,21 @@ class CourseSerializer(serializers.Serializer): # pylint: disable=abstract-meth start_type = serializers.CharField() pacing = serializers.CharField() mobile_available = serializers.BooleanField() + hidden = serializers.SerializerMethodField() # 'course_id' is a deprecated field, please use 'id' instead. course_id = serializers.CharField(source='id', read_only=True) + def get_hidden(self, course_overview): + """ + Get the representation for SerializerMethodField `hidden` + Represents whether course is hidden in LMS + """ + catalog_visibility = course_overview.catalog_visibility + if catalog_visibility in ['about', 'none']: + return True + return False + def get_blocks_url(self, course_overview): """ Get the representation for SerializerMethodField `blocks_url` diff --git a/lms/djangoapps/course_api/tests/test_forms.py b/lms/djangoapps/course_api/tests/test_forms.py index 5ea6d2343a..744f8a9082 100644 --- a/lms/djangoapps/course_api/tests/test_forms.py +++ b/lms/djangoapps/course_api/tests/test_forms.py @@ -5,7 +5,7 @@ Tests for Course API forms. import ddt from django.contrib.auth.models import AnonymousUser from django.http import QueryDict -from itertools import product +from itertools import product, chain from urllib import urlencode from openedx.core.djangoapps.util.test_forms import FormTestMixin @@ -66,6 +66,7 @@ class TestCourseListGetForm(FormTestMixin, UsernameTestMixin, SharedModuleStoreT 'org': '', 'mobile': None, 'filter_': None, + 'include_hidden': None, } def test_basic(self): @@ -78,10 +79,13 @@ class TestCourseListGetForm(FormTestMixin, UsernameTestMixin, SharedModuleStoreT self.assert_valid(self.cleaned_data) @ddt.data( - *product( + *chain(product( [('mobile', 'mobile_available')], [(True, True), (False, False), ('1', True), ('0', False), (None, None)], - ) + ), product( + [('include_hidden', 'include_hidden')], + [(True, True), (False, False), ('1', True), ('0', False), (None, None)], + )) ) @ddt.unpack def test_filter(self, param_field_name, param_field_value): diff --git a/lms/djangoapps/course_api/tests/test_serializers.py b/lms/djangoapps/course_api/tests/test_serializers.py index 50373d32fc..ea14383068 100644 --- a/lms/djangoapps/course_api/tests/test_serializers.py +++ b/lms/djangoapps/course_api/tests/test_serializers.py @@ -68,6 +68,7 @@ class TestCourseSerializer(CourseApiFactoryMixin, ModuleStoreTestCase): 'effort': u'6 hours', 'pacing': 'instructor', 'mobile_available': False, + 'hidden': False, # 'course_id' is a deprecated field, please use 'id' instead. 'course_id': u'edX/toy/2012_Fall', diff --git a/lms/djangoapps/course_api/views.py b/lms/djangoapps/course_api/views.py index 33aea96e29..28189537cc 100644 --- a/lms/djangoapps/course_api/views.py +++ b/lms/djangoapps/course_api/views.py @@ -152,6 +152,10 @@ class CourseListView(DeveloperErrorViewMixin, ListAPIView): If specified, only visible `CourseOverview` objects that are designated as mobile_available are returned. + include_hidden (optional): + If specified, courses that are hidden in the LMS, will be returned + with a hidden flag set to true. + **Returns** * 200 on success, with a list of course discovery objects as returned diff --git a/lms/djangoapps/courseware/courses.py b/lms/djangoapps/courseware/courses.py index da6e88153c..7feb4ac4ca 100644 --- a/lms/djangoapps/courseware/courses.py +++ b/lms/djangoapps/courseware/courses.py @@ -376,6 +376,7 @@ def get_courses(user, org=None, filter_=None): Returns a list of courses available, sorted by course.number and optionally filtered by org code (case-insensitive). """ + include_hidden = filter_.pop('include_hidden', False) if filter_ else False courses = branding.get_visible_courses(org=org, filter_=filter_) permission_name = configuration_helpers.get_value( @@ -383,8 +384,14 @@ def get_courses(user, org=None, filter_=None): settings.COURSE_CATALOG_VISIBILITY_PERMISSION ) - courses = [c for c in courses if has_access(user, permission_name, c)] - + # see_in_catalog is the permission that checks the catalog visibility setting. + # include_hidden refers to showing courses that are normally not visible due to this setting. + # We don't want to show courses that are hidden for other reasons, which is why we don't + # use the override for other permissions. + if permission_name is 'see_in_catalog': + courses = [c for c in courses if include_hidden or has_access(user, permission_name, c)] + else: + courses = [c for c in courses if has_access(user, permission_name, c)] return courses