Commit ac862b7a by Adam Palay

only load tabs if user is enrolled in course (TNL-286)

add tests for static tabs
parent 1b06edb6
...@@ -57,7 +57,7 @@ class CourseTab(object): # pylint: disable=incomplete-protocol ...@@ -57,7 +57,7 @@ class CourseTab(object): # pylint: disable=incomplete-protocol
self.link_func = link_func self.link_func = link_func
def can_display(self, course, settings, is_user_authenticated, is_user_staff): # pylint: disable=unused-argument def can_display(self, course, settings, is_user_authenticated, is_user_staff, is_user_enrolled): # pylint: disable=unused-argument
""" """
Determines whether the tab should be displayed in the UI for the given course and a particular user. Determines whether the tab should be displayed in the UI for the given course and a particular user.
This method is to be overridden by subclasses when applicable. The base class implementation This method is to be overridden by subclasses when applicable. The base class implementation
...@@ -78,6 +78,8 @@ class CourseTab(object): # pylint: disable=incomplete-protocol ...@@ -78,6 +78,8 @@ class CourseTab(object): # pylint: disable=incomplete-protocol
is_user_staff: Indicates whether the user has staff access to the course. If the tab is of is_user_staff: Indicates whether the user has staff access to the course. If the tab is of
type StaffTab and this value is False, then can_display will return False. type StaffTab and this value is False, then can_display will return False.
is_user_enrolled: Indicates whether the user is enrolled in the course
Returns: Returns:
A boolean value to indicate whether this instance of the tab should be displayed to a A boolean value to indicate whether this instance of the tab should be displayed to a
given user for the given course. given user for the given course.
...@@ -212,7 +214,7 @@ class AuthenticatedCourseTab(CourseTab): ...@@ -212,7 +214,7 @@ class AuthenticatedCourseTab(CourseTab):
""" """
Abstract class for tabs that can be accessed by only authenticated users. Abstract class for tabs that can be accessed by only authenticated users.
""" """
def can_display(self, course, settings, is_user_authenticated, is_user_staff): def can_display(self, course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
return is_user_authenticated return is_user_authenticated
...@@ -220,9 +222,17 @@ class StaffTab(AuthenticatedCourseTab): ...@@ -220,9 +222,17 @@ class StaffTab(AuthenticatedCourseTab):
""" """
Abstract class for tabs that can be accessed by only users with staff access. Abstract class for tabs that can be accessed by only users with staff access.
""" """
def can_display(self, course, settings, is_user_authenticated, is_user_staff): # pylint: disable=unused-argument def can_display(self, course, settings, is_user_authenticated, is_user_staff, is_user_enrolled): # pylint: disable=unused-argument
return is_user_staff return is_user_staff
class EnrolledOrStaffTab(CourseTab):
"""
Abstract class for tabs that can be accessed by only users with staff access
or users enrolled in the course.
"""
def can_display(self, course, settings, is_user_authenticated, is_user_staff, is_user_enrolled): # pylint: disable=unused-argument
return is_user_authenticated and (is_user_staff or is_user_enrolled)
class HideableTab(CourseTab): class HideableTab(CourseTab):
""" """
...@@ -262,7 +272,7 @@ class HideableTab(CourseTab): ...@@ -262,7 +272,7 @@ class HideableTab(CourseTab):
return self.is_hidden == other.get('is_hidden', False) return self.is_hidden == other.get('is_hidden', False)
class CoursewareTab(CourseTab): class CoursewareTab(EnrolledOrStaffTab):
""" """
A tab containing the course content. A tab containing the course content.
""" """
...@@ -300,7 +310,7 @@ class CourseInfoTab(CourseTab): ...@@ -300,7 +310,7 @@ class CourseInfoTab(CourseTab):
return super(CourseInfoTab, cls).validate(tab_dict, raise_error) and need_name(tab_dict, raise_error) return super(CourseInfoTab, cls).validate(tab_dict, raise_error) and need_name(tab_dict, raise_error)
class ProgressTab(AuthenticatedCourseTab): class ProgressTab(EnrolledOrStaffTab):
""" """
A tab containing information about the authenticated user's progress. A tab containing information about the authenticated user's progress.
""" """
...@@ -315,8 +325,11 @@ class ProgressTab(AuthenticatedCourseTab): ...@@ -315,8 +325,11 @@ class ProgressTab(AuthenticatedCourseTab):
link_func=link_reverse_func(self.type), link_func=link_reverse_func(self.type),
) )
def can_display(self, course, settings, is_user_authenticated, is_user_staff): def can_display(self, course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
return not course.hide_progress_tab super_can_display = super(ProgressTab, self).can_display(
course, settings, is_user_authenticated, is_user_staff, is_user_enrolled
)
return super_can_display and not course.hide_progress_tab
@classmethod @classmethod
def validate(cls, tab_dict, raise_error=True): def validate(cls, tab_dict, raise_error=True):
...@@ -339,15 +352,17 @@ class WikiTab(HideableTab): ...@@ -339,15 +352,17 @@ class WikiTab(HideableTab):
tab_dict=tab_dict, tab_dict=tab_dict,
) )
def can_display(self, course, settings, is_user_authenticated, is_user_staff): def can_display(self, course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
return settings.WIKI_ENABLED return settings.WIKI_ENABLED and (
course.allow_public_wiki_access or is_user_enrolled or is_user_staff
)
@classmethod @classmethod
def validate(cls, tab_dict, raise_error=True): def validate(cls, tab_dict, raise_error=True):
return super(WikiTab, cls).validate(tab_dict, raise_error) and need_name(tab_dict, raise_error) return super(WikiTab, cls).validate(tab_dict, raise_error) and need_name(tab_dict, raise_error)
class DiscussionTab(CourseTab): class DiscussionTab(EnrolledOrStaffTab):
""" """
A tab only for the new Berkeley discussion forums. A tab only for the new Berkeley discussion forums.
""" """
...@@ -362,8 +377,11 @@ class DiscussionTab(CourseTab): ...@@ -362,8 +377,11 @@ class DiscussionTab(CourseTab):
link_func=link_reverse_func('django_comment_client.forum.views.forum_form_discussion'), link_func=link_reverse_func('django_comment_client.forum.views.forum_form_discussion'),
) )
def can_display(self, course, settings, is_user_authenticated, is_user_staff): def can_display(self, course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
return settings.FEATURES.get('ENABLE_DISCUSSION_SERVICE') super_can_display = super(DiscussionTab, self).can_display(
course, settings, is_user_authenticated, is_user_staff, is_user_enrolled
)
return settings.FEATURES.get('ENABLE_DISCUSSION_SERVICE') and super_can_display
@classmethod @classmethod
def validate(cls, tab_dict, raise_error=True): def validate(cls, tab_dict, raise_error=True):
...@@ -529,7 +547,7 @@ class TextbookTabs(TextbookTabsBase): ...@@ -529,7 +547,7 @@ class TextbookTabs(TextbookTabsBase):
tab_id=self.type, tab_id=self.type,
) )
def can_display(self, course, settings, is_user_authenticated, is_user_staff): def can_display(self, course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
return settings.FEATURES.get('ENABLE_TEXTBOOK') return settings.FEATURES.get('ENABLE_TEXTBOOK')
def items(self, course): def items(self, course):
...@@ -642,7 +660,7 @@ class SyllabusTab(CourseTab): ...@@ -642,7 +660,7 @@ class SyllabusTab(CourseTab):
""" """
type = 'syllabus' type = 'syllabus'
def can_display(self, course, settings, is_user_authenticated, is_user_staff): def can_display(self, course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
return hasattr(course, 'syllabus_present') and course.syllabus_present return hasattr(course, 'syllabus_present') and course.syllabus_present
def __init__(self, tab_dict=None): # pylint: disable=unused-argument def __init__(self, tab_dict=None): # pylint: disable=unused-argument
...@@ -660,7 +678,7 @@ class NotesTab(AuthenticatedCourseTab): ...@@ -660,7 +678,7 @@ class NotesTab(AuthenticatedCourseTab):
""" """
type = 'notes' type = 'notes'
def can_display(self, course, settings, is_user_authenticated, is_user_staff): def can_display(self, course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
return settings.FEATURES.get('ENABLE_STUDENT_NOTES') return settings.FEATURES.get('ENABLE_STUDENT_NOTES')
def __init__(self, tab_dict=None): def __init__(self, tab_dict=None):
...@@ -773,6 +791,7 @@ class CourseTabList(List): ...@@ -773,6 +791,7 @@ class CourseTabList(List):
settings, settings,
is_user_authenticated=True, is_user_authenticated=True,
is_user_staff=True, is_user_staff=True,
is_user_enrolled=False
): ):
""" """
Generator method for iterating through all tabs that can be displayed for the given course and Generator method for iterating through all tabs that can be displayed for the given course and
...@@ -780,7 +799,7 @@ class CourseTabList(List): ...@@ -780,7 +799,7 @@ class CourseTabList(List):
""" """
for tab in course.tabs: for tab in course.tabs:
if tab.can_display( if tab.can_display(
course, settings, is_user_authenticated, is_user_staff course, settings, is_user_authenticated, is_user_staff, is_user_enrolled
) and (not tab.is_hideable or not tab.is_hidden): ) and (not tab.is_hideable or not tab.is_hidden):
if tab.is_collection: if tab.is_collection:
for item in tab.items(course): for item in tab.items(course):
...@@ -788,7 +807,7 @@ class CourseTabList(List): ...@@ -788,7 +807,7 @@ class CourseTabList(List):
else: else:
yield tab yield tab
instructor_tab = InstructorTab() instructor_tab = InstructorTab()
if instructor_tab.can_display(course, settings, is_user_authenticated, is_user_staff): if instructor_tab.can_display(course, settings, is_user_authenticated, is_user_staff, is_user_enrolled):
yield instructor_tab yield instructor_tab
@staticmethod @staticmethod
...@@ -801,7 +820,7 @@ class CourseTabList(List): ...@@ -801,7 +820,7 @@ class CourseTabList(List):
with the provided settings. with the provided settings.
""" """
for tab in course.tabs: for tab in course.tabs:
if tab.can_display(course, settings, is_user_authenticated=True, is_user_staff=True): if tab.can_display(course, settings, is_user_authenticated=True, is_user_staff=True, is_user_enrolled=True):
if tab.is_collection and not len(list(tab.items(course))): if tab.is_collection and not len(list(tab.items(course))):
# do not yield collections that have no items # do not yield collections that have no items
continue continue
......
...@@ -90,22 +90,42 @@ class TabTestCase(unittest.TestCase): ...@@ -90,22 +90,42 @@ class TabTestCase(unittest.TestCase):
deserialized_tab = tab.from_json(serialized_tab) deserialized_tab = tab.from_json(serialized_tab)
self.assertEquals(serialized_tab, deserialized_tab) self.assertEquals(serialized_tab, deserialized_tab)
def check_can_display_results(self, tab, expected_value=True, for_authenticated_users_only=False, for_staff_only=False): def check_can_display_results(
self,
tab,
expected_value=True,
for_authenticated_users_only=False,
for_staff_only=False,
for_enrolled_users_only=False
):
"""Checks can display results for various users""" """Checks can display results for various users"""
if for_staff_only: if for_staff_only:
self.assertEquals( self.assertEquals(
expected_value, expected_value,
tab.can_display(self.course, self.settings, is_user_authenticated=False, is_user_staff=True) tab.can_display(
self.course, self.settings, is_user_authenticated=True, is_user_staff=True, is_user_enrolled=True
)
) )
if for_authenticated_users_only: if for_authenticated_users_only:
self.assertEquals( self.assertEquals(
expected_value, expected_value,
tab.can_display(self.course, self.settings, is_user_authenticated=True, is_user_staff=False) tab.can_display(
self.course, self.settings, is_user_authenticated=True, is_user_staff=False, is_user_enrolled=False
) )
if not for_staff_only and not for_authenticated_users_only: )
if not for_staff_only and not for_authenticated_users_only and not for_enrolled_users_only:
self.assertEquals( self.assertEquals(
expected_value, expected_value,
tab.can_display(self.course, self.settings, is_user_authenticated=False, is_user_staff=False) tab.can_display(
self.course, self.settings, is_user_authenticated=False, is_user_staff=False, is_user_enrolled=False
)
)
if for_enrolled_users_only:
self.assertEquals(
expected_value,
tab.can_display(
self.course, self.settings, is_user_authenticated=True, is_user_staff=False, is_user_enrolled=True
)
) )
def check_get_and_set_methods(self, tab): def check_get_and_set_methods(self, tab):
...@@ -147,11 +167,15 @@ class ProgressTestCase(TabTestCase): ...@@ -147,11 +167,15 @@ class ProgressTestCase(TabTestCase):
self.course.hide_progress_tab = False self.course.hide_progress_tab = False
tab = self.check_progress_tab() tab = self.check_progress_tab()
self.check_can_display_results(tab, for_authenticated_users_only=True) self.check_can_display_results(
tab, for_staff_only=True, for_enrolled_users_only=True
)
self.course.hide_progress_tab = True self.course.hide_progress_tab = True
self.check_progress_tab() self.check_progress_tab()
self.check_can_display_results(tab, for_authenticated_users_only=True, expected_value=False) self.check_can_display_results(
tab, for_staff_only=True, for_enrolled_users_only=True, expected_value=False
)
class WikiTestCase(TabTestCase): class WikiTestCase(TabTestCase):
...@@ -167,13 +191,25 @@ class WikiTestCase(TabTestCase): ...@@ -167,13 +191,25 @@ class WikiTestCase(TabTestCase):
invalid_dict_tab=self.fake_dict_tab, invalid_dict_tab=self.fake_dict_tab,
) )
def test_wiki_enabled(self): def test_wiki_enabled_and_public(self):
"""Test wiki tab when Enabled setting is True""" """
Test wiki tab when Enabled setting is True and the wiki is open to
the public.
"""
self.settings.WIKI_ENABLED = True self.settings.WIKI_ENABLED = True
self.course.allow_public_wiki_access = True
tab = self.check_wiki_tab() tab = self.check_wiki_tab()
self.check_can_display_results(tab) self.check_can_display_results(tab)
def test_wiki_enabled_and_not_public(self):
"""
Test wiki when it is enabled but not open to the public
"""
self.settings.WIKI_ENABLED = True
self.course.allow_public_wiki_access = False
tab = self.check_wiki_tab()
self.check_can_display_results(tab, for_enrolled_users_only=True, for_staff_only=True)
def test_wiki_enabled_false(self): def test_wiki_enabled_false(self):
"""Test wiki tab when Enabled setting is False""" """Test wiki tab when Enabled setting is False"""
...@@ -611,7 +647,14 @@ class DiscussionLinkTestCase(TabTestCase): ...@@ -611,7 +647,14 @@ class DiscussionLinkTestCase(TabTestCase):
return "default_discussion_link" return "default_discussion_link"
return reverse_discussion_link return reverse_discussion_link
def check_discussion(self, tab_list, expected_discussion_link, expected_can_display_value, discussion_link_in_course=""): def check_discussion(
self, tab_list,
expected_discussion_link,
expected_can_display_value,
discussion_link_in_course="",
is_staff=True,
is_enrolled=True,
):
"""Helper function to verify whether the discussion tab exists and can be displayed""" """Helper function to verify whether the discussion tab exists and can be displayed"""
self.course.tabs = tab_list self.course.tabs = tab_list
self.course.discussion_link = discussion_link_in_course self.course.discussion_link = discussion_link_in_course
...@@ -619,7 +662,7 @@ class DiscussionLinkTestCase(TabTestCase): ...@@ -619,7 +662,7 @@ class DiscussionLinkTestCase(TabTestCase):
self.assertEquals( self.assertEquals(
( (
discussion is not None and discussion is not None and
discussion.can_display(self.course, self.settings, True, True) and discussion.can_display(self.course, self.settings, True, is_staff, is_enrolled) and
(discussion.link_func(self.course, self._reverse(self.course)) == expected_discussion_link) (discussion.link_func(self.course, self._reverse(self.course)) == expected_discussion_link)
), ),
expected_can_display_value expected_can_display_value
...@@ -662,3 +705,25 @@ class DiscussionLinkTestCase(TabTestCase): ...@@ -662,3 +705,25 @@ class DiscussionLinkTestCase(TabTestCase):
expected_discussion_link=not None, expected_discussion_link=not None,
expected_can_display_value=False, expected_can_display_value=False,
) )
def test_tabs_enrolled_or_staff(self):
self.settings.FEATURES['ENABLE_DISCUSSION_SERVICE'] = True
for is_enrolled, is_staff in [(True, False), (False, True)]:
self.check_discussion(
tab_list=self.tabs_with_discussion,
expected_discussion_link="default_discussion_link",
expected_can_display_value=True,
is_enrolled=is_enrolled,
is_staff=is_staff
)
def test_tabs_not_enrolled_or_staff(self):
self.settings.FEATURES['ENABLE_DISCUSSION_SERVICE'] = True
is_enrolled = is_staff = False
self.check_discussion(
tab_list=self.tabs_with_discussion,
expected_discussion_link="default_discussion_link",
expected_can_display_value=False,
is_enrolled=is_enrolled,
is_staff=is_staff
)
...@@ -95,4 +95,3 @@ class StaticTabDateTestCaseXML(LoginEnrollmentTestCase, ModuleStoreTestCase): ...@@ -95,4 +95,3 @@ class StaticTabDateTestCaseXML(LoginEnrollmentTestCase, ModuleStoreTestCase):
resp = self.client.get(url) resp = self.client.get(url)
self.assertEqual(resp.status_code, 200) self.assertEqual(resp.status_code, 200)
self.assertIn(self.xml_data, resp.content) self.assertIn(self.xml_data, resp.content)
...@@ -18,12 +18,16 @@ def url_class(is_active): ...@@ -18,12 +18,16 @@ def url_class(is_active):
<%! from django.core.urlresolvers import reverse %> <%! from django.core.urlresolvers import reverse %>
<%! from django.utils.translation import ugettext as _ %> <%! from django.utils.translation import ugettext as _ %>
<%! from courseware.views import notification_image_for_tab %> <%! from courseware.views import notification_image_for_tab %>
<%! from student.models import CourseEnrollment%>
<%
user_is_enrolled = user.is_authenticated() and CourseEnrollment.is_enrolled(user, course.id)
%>
% if disable_tabs is UNDEFINED or not disable_tabs: % if disable_tabs is UNDEFINED or not disable_tabs:
<nav class="${active_page} course-material"> <nav class="${active_page} course-material">
<div class="inner-wrapper"> <div class="inner-wrapper">
<ol class="course-tabs"> <ol class="course-tabs">
% for tab in CourseTabList.iterate_displayable(course, settings, user.is_authenticated(), has_access(user, 'staff', course, course.id)): % for tab in CourseTabList.iterate_displayable(course, settings, user.is_authenticated(), has_access(user, 'staff', course, course.id), user_is_enrolled):
<% <%
tab_is_active = (tab.tab_id == active_page) or (tab.tab_id == default_tab) tab_is_active = (tab.tab_id == active_page) or (tab.tab_id == default_tab)
tab_image = notification_image_for_tab(tab, user, course) tab_image = notification_image_for_tab(tab, user, course)
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
<% <%
discussion_tab = CourseTabList.get_discussion(course) if course else None discussion_tab = CourseTabList.get_discussion(course) if course else None
discussion_link = discussion_tab.link_func(course, reverse) if (discussion_tab and discussion_tab.can_display(course, settings, True, True)) else None discussion_link = discussion_tab.link_func(course, reverse) if (discussion_tab and discussion_tab.can_display(course, settings, True, True, True)) else None
%> %>
% if discussion_link: % if discussion_link:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment