Skip to content

Commit

Permalink
Merge pull request #493 from ucsd-ets/develop
Browse files Browse the repository at this point in the history
Add settings to hide courses to non-ucsd students + Bug fixes
  • Loading branch information
Ali-Salman29 authored Dec 27, 2022
2 parents 4ce7f1f + a8e57d2 commit 251c590
Show file tree
Hide file tree
Showing 12 changed files with 84 additions and 8 deletions.
1 change: 1 addition & 0 deletions cms/djangoapps/contentstore/courseware_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ def index_about_information(cls, modulestore, course):
'course': course_run_key,
'content': {},
'image_url': course_image_url(course),
'only_allow_ucsd_students': course.only_allow_ucsd_students,
}

# load data for all of the 'about' modules for this course into a dictionary
Expand Down
11 changes: 10 additions & 1 deletion common/lib/xmodule/xmodule/course_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -1028,7 +1028,16 @@ class CourseFields(object):
),
scope=Scope.settings
)

only_allow_ucsd_students = Boolean(
display_name=_("Restrict course to UCSD students"),
help=_(
"Restrict course only to the ucsd students. "
"If the value is true, It only allow students logedin with ucsd emails. "
"Note that, the studnet's emails should contain @ucsd in their domain name. "
),
scope=Scope.settings,
default=False,
)

class CourseModule(CourseFields, SequenceModule): # pylint: disable=abstract-method
"""
Expand Down
11 changes: 11 additions & 0 deletions lms/djangoapps/courseware/courses.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from fs.errors import ResourceNotFound
from opaque_keys.edx.keys import UsageKey
from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG
from openedx.features.ucsd_features.access_utils import check_organization_access
from path import Path as path
from six import text_type

Expand Down Expand Up @@ -129,6 +130,7 @@ def get_course_with_access(user, action, course_key, depth=0, check_if_enrolled=
"""
course = get_course_by_id(course_key, depth)
check_course_access_with_redirect(course, user, action, check_if_enrolled, check_survey_complete, check_if_authenticated)
check_course_organization_access_with_redirect(course, user)
return course


Expand Down Expand Up @@ -196,6 +198,15 @@ def _check_nonstaff_access():
# This access_response will be ACCESS_GRANTED
return nonstaff_access_response

def check_course_organization_access_with_redirect(course, user):
"""
Check that the user has access to the organization courses
Only those users are allowed to see the course content that are either staff members or
are logedin with emails from certain organizations i.e ucsd.edu
"""
access_response = has_access(user, 'staff', course.id) or check_organization_access(user, course)
if not access_response:
raise CourseAccessRedirect(reverse('courses'), access_response)

def check_course_access_with_redirect(course, user, action, check_if_enrolled=False, check_survey_complete=True, check_if_authenticated=False):
"""
Expand Down
2 changes: 1 addition & 1 deletion lms/djangoapps/courseware/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def get_state_by_params(cls, course_id, module_state_keys, student_id=None):

@classmethod
def save_state(cls, student, course_id, module_state_key, defaults):
if not student.is_authenticated():
if not student.is_authenticated:
return
else:
cls.objects.update_or_create(
Expand Down
3 changes: 3 additions & 0 deletions lms/djangoapps/courseware/views/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML
from openedx.features.course_experience.waffle import waffle as course_experience_waffle
from openedx.features.enterprise_support.api import data_sharing_consent_required
from openedx.features.ucsd_features.utils import check_ucsd_email, get_course_id
from common.djangoapps.student.models import CourseEnrollment, UserTestGroup
from common.djangoapps.track import segment
from common.djangoapps.util.cache import cache, cache_if_anonymous
Expand Down Expand Up @@ -255,6 +256,7 @@ def courses(request):
Render "find courses" page. The course selection work is done in courseware.courses.
"""
courses_list = []
has_ucsd_courses_access = bool(has_access(request.user, 'staff', 'global')) or check_ucsd_email(request.user)
course_discovery_meanings = getattr(settings, 'COURSE_DISCOVERY_MEANINGS', {})
if not settings.FEATURES.get('ENABLE_COURSE_DISCOVERY'):
courses_list = get_courses(request.user)
Expand All @@ -272,6 +274,7 @@ def courses(request):
"courseware/courses.html",
{
'courses': courses_list,
'has_ucsd_courses_access': has_ucsd_courses_access,
'course_discovery_meanings': course_discovery_meanings,
'programs_list': programs_list,
}
Expand Down
4 changes: 2 additions & 2 deletions lms/static/js/discovery/discovery_factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
'js/discovery/views/search_form', 'js/discovery/views/courses_listing',
'js/discovery/views/filter_bar', 'js/discovery/views/refine_sidebar'],
function(Backbone, SearchState, Filters, SearchForm, CoursesListing, FilterBar, RefineSidebar) {
return function(meanings, searchQuery, userLanguage, userTimezone) {
return function(meanings, searchQuery, userLanguage, userTimezone, hasUCSDCoursesAccess) {
var dispatcher = _.extend({}, Backbone.Events);
var search = new SearchState();
var search = new SearchState(hasUCSDCoursesAccess);
var filters = new Filters();
var form = new SearchForm();
var filterBar = new FilterBar({collection: filters});
Expand Down
14 changes: 13 additions & 1 deletion lms/static/js/discovery/models/course_discovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,25 @@
latestCount: 0
},

initialize: function() {
initialize: function(hasUCSDCoursesAccess) {
this.hasUCSDCoursesAccess = hasUCSDCoursesAccess;
this.courseCards = new Backbone.Collection([], {model: CourseCard});
this.facetOptions = new Backbone.Collection([], {model: FacetOption});
},

ucsd_course_filter: function (course) {
if (course.data.only_allow_ucsd_students){
if (this.hasUCSDCoursesAccess) {
return true;
}
return false;
}
return true;
},

parse: function(response) {
var courses = response.results || [];
courses = courses.filter(course=>this.ucsd_course_filter(course));
var facets = response.facets || {};
this.courseCards.add(_.pluck(courses, 'data'));
this.set({
Expand Down
4 changes: 2 additions & 2 deletions lms/static/js/discovery/models/search_state.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
terms: {},
jqhxr: null,

initialize: function() {
this.discovery = new CourseDiscovery();
initialize: function(hasUCSDCoursesAccess) {
this.discovery = new CourseDiscovery(hasUCSDCoursesAccess);
this.listenTo(this.discovery, 'sync', this.onSync, this);
this.listenTo(this.discovery, 'error', this.onError, this);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,15 @@ def get_site_program_uuids(self, client, site):
def fetch_program_details(self, client, uuids):
programs = {}
failure = False
querystring = {
'exclude_utm': 1,
'marketable_enrollable_course_runs_with_archived': 1,
}
for uuid in uuids:
try:
cache_key = PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)
logger.info(u'Requesting details for program {uuid}.'.format(uuid=uuid))
program = client.programs(uuid).get(exclude_utm=1)
program = client.programs(uuid).get(**querystring)
# pathways get added in process_pathways
program['pathway_ids'] = []
programs[cache_key] = program
Expand Down
18 changes: 18 additions & 0 deletions openedx/features/ucsd_features/access_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
Simple utility functions for computing access.
"""
from lms.djangoapps.courseware.access_response import AccessResponse
from openedx.features.ucsd_features.utils import check_ucsd_email

ACCESS_GRANTED = AccessResponse(True)
ACCESS_DENIED = AccessResponse(False)

def check_organization_access(user, course):
"""
Checks if the urers belogs to ucsd organization
"""
if course.only_allow_ucsd_students:
if check_ucsd_email(user):
return ACCESS_GRANTED
return ACCESS_DENIED
return ACCESS_GRANTED
10 changes: 10 additions & 0 deletions openedx/features/ucsd_features/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,13 @@ def get_course_id(course):
a string course id in this format edX+CS123
"""
return f'{course.org}+{course.number}'.upper()

def check_ucsd_email(user):
"""
Checks if user email's domain is ucsd
"""
if user.is_authenticated and user.email:
email = user.email
domain = email[email.index('@') + 1 : ]
return domain.startswith('ucsd.')
return False
8 changes: 8 additions & 0 deletions openedx/features/ucsd_features/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from lms.djangoapps.courseware.access_utils import check_public_access
from lms.djangoapps.courseware.courses import (
can_self_enroll_in_course,
check_course_organization_access_with_redirect,
get_course_by_id,
get_course_with_access,
get_permission_for_course_about
)
Expand Down Expand Up @@ -50,9 +52,15 @@ def course_page(request, course_id):
raise Http404(u"Course key not found on discovery")

course_key = course.get('key')
course_canonical_key = course.get('canonical_course_run_key')
course_runs = course.get('course_runs')
course_runs_context = []
course_price = 0

if course_canonical_key:
module_course = get_course_by_id(CourseKey.from_string(course.get('canonical_course_run_key')))
module_course and check_course_organization_access_with_redirect(module_course, request.user)

# extract course run context for each course run
for course_run in course_runs:
# extract price from course runs
Expand Down

0 comments on commit 251c590

Please sign in to comment.