Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions cms/djangoapps/models/settings/course_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from xblock_django.models import XBlockStudioConfigurationFlag
from xmodule.modulestore.django import modulestore

from openedx.features.course_experience import COURSE_ENABLE_UNENROLLED_ACCESS_FLAG


class CourseMetadata(object):
'''
Expand Down Expand Up @@ -63,7 +65,7 @@ class CourseMetadata(object):
]

@classmethod
def filtered_list(cls):
def filtered_list(cls, course_key=None):
"""
Filter fields based on feature flag, i.e. enabled, disabled.
"""
Expand Down Expand Up @@ -117,6 +119,10 @@ def filtered_list(cls):
if not XBlockStudioConfigurationFlag.is_enabled():
filtered_list.append('allow_unsupported_xblocks')

# Do not show "Course Visibility For Unenrolled Learners" in Studio Advanced Settings
# if the enable_anonymous_access flag is not enabled
if not COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(course_key=course_key):
filtered_list.append('course_visibility')
return filtered_list

@classmethod
Expand All @@ -128,7 +134,7 @@ def fetch(cls, descriptor):
result = {}
metadata = cls.fetch_all(descriptor)
for key, value in metadata.iteritems():
if key in cls.filtered_list():
if key in cls.filtered_list(descriptor.id):
continue
result[key] = value
return result
Expand Down Expand Up @@ -163,7 +169,7 @@ def update_from_json(cls, descriptor, jsondict, user, filter_tabs=True):

Ensures none of the fields are in the blacklist.
"""
filtered_list = cls.filtered_list()
filtered_list = cls.filtered_list(descriptor.id)
# Don't filter on the tab attribute if filter_tabs is False.
if not filter_tabs:
filtered_list.remove("tabs")
Expand Down Expand Up @@ -199,7 +205,7 @@ def validate_and_update_from_json(cls, descriptor, jsondict, user, filter_tabs=T
errors: list of error objects
result: the updated course metadata or None if error
"""
filtered_list = cls.filtered_list()
filtered_list = cls.filtered_list(descriptor.id)
if not filter_tabs:
filtered_list.remove("tabs")

Expand Down
19 changes: 19 additions & 0 deletions common/lib/xmodule/xmodule/course_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@

DEFAULT_MOBILE_AVAILABLE = getattr(settings, 'DEFAULT_MOBILE_AVAILABLE', False)

COURSE_VISIBILITY_PRIVATE = 'private'
COURSE_VISIBILITY_PUBLIC_OUTLINE = 'public_outline'
COURSE_VISIBILITY_PUBLIC = 'public'


class StringOrDate(Date):
def from_json(self, value):
Expand Down Expand Up @@ -814,6 +818,21 @@ class CourseFields(object):
scope=Scope.settings
)

course_visibility = String(
display_name=_("Course Visibility For Unenrolled Learners"),
help=_(
"Defines the access permissions for unenrolled learners. This can be set to one of three values: "
"'private' (default visibility, only allowed for enrolled students), 'public_outline' "
"(allow access to course outline) and 'public' (allow access to both outline and course content)."
),
default=COURSE_VISIBILITY_PRIVATE,
scope=Scope.settings,
values=[
{"display_name": _("private"), "value": COURSE_VISIBILITY_PRIVATE},
{"display_name": _("public_outline"), "value": COURSE_VISIBILITY_PUBLIC_OUTLINE},
{"display_name": _("public"), "value": COURSE_VISIBILITY_PUBLIC}]
)

"""
instructor_info dict structure:
{
Expand Down
7 changes: 7 additions & 0 deletions common/lib/xmodule/xmodule/html_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ def student_view(self, _context):
"""
return Fragment(self.get_html())

@XBlock.supports("multi_device")
def public_view(self, context):
"""
Returns a fragment that contains the html for the preview view
"""
return self.student_view(context)

def student_view_data(self, context=None): # pylint: disable=unused-argument
"""
Return a JSON representation of the student_view of this XBlock.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from openedx.core.djangolib.testing.utils import CacheIsolationMixin, CacheIsolationTestCase, FilteredQueryCountMixin
from openedx.core.lib.tempdir import mkdtemp_clean
from student.models import CourseEnrollment
from student.tests.factories import UserFactory
from student.tests.factories import AdminFactory, UserFactory
from xmodule.contentstore.django import _CONTENTSTORE
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import SignalHandler, clear_existing_modulestores, modulestore
Expand Down Expand Up @@ -343,6 +343,8 @@ def create_user_for_course(self, course, user_type=CourseUserType.ENROLLED):
# Set up the test user
if is_unenrolled_staff:
user = StaffFactory(course_key=course.id, password=self.TEST_PASSWORD)
elif user_type is CourseUserType.GLOBAL_STAFF:
user = AdminFactory(password=self.TEST_PASSWORD)
else:
user = UserFactory(password=self.TEST_PASSWORD)
self.client.login(username=user.username, password=self.TEST_PASSWORD)
Expand Down
32 changes: 23 additions & 9 deletions common/lib/xmodule/xmodule/seq_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from .fields import Date
from .mako_module import MakoModuleDescriptor
from .progress import Progress
from .x_module import STUDENT_VIEW, XModule
from .x_module import STUDENT_VIEW, PUBLIC_VIEW, XModule
from .xml_module import XmlDescriptor

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -257,7 +257,18 @@ def student_view(self, context):
banner_text, special_html = special_html_view
if special_html and not masquerading_as_specific_student:
return Fragment(special_html)
return self._student_view(context, prereq_met, prereq_meta_info, banner_text)
return self._student_or_public_view(context, prereq_met, prereq_meta_info, banner_text)

def public_view(self, context):
"""
Renders the preview view of the block in the LMS.
"""
prereq_met = True
prereq_meta_info = {}

if self._required_prereq():
prereq_met, prereq_meta_info = self._compute_is_prereq_met(True)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ormsbee I had to put this in during the rebase to match recent changes to the student_view: edx@6fbd861#diff-14daf51ec8decc308953f7a4e7e0a03f

return self._student_or_public_view(context or {}, prereq_met, prereq_meta_info, None, PUBLIC_VIEW)

def _special_exam_student_view(self):
"""
Expand Down Expand Up @@ -309,7 +320,7 @@ def is_user_authenticated(self, context):
# NOTE (CCB): We default to true to maintain the behavior in place prior to allowing anonymous access access.
return context.get('user_authenticated', True)

def _student_view(self, context, prereq_met, prereq_meta_info, banner_text=None):
def _student_or_public_view(self, context, prereq_met, prereq_meta_info, banner_text=None, view=STUDENT_VIEW):
"""
Returns the rendered student view of the content of this
sequential. If banner_text is given, it is added to the
Expand All @@ -319,11 +330,14 @@ def _student_view(self, context, prereq_met, prereq_meta_info, banner_text=None)
self._update_position(context, len(display_items))

if prereq_met and not self._is_gate_fulfilled():
banner_text = _('This section is a prerequisite. You must complete this section in order to unlock additional content.')
banner_text = _(
'This section is a prerequisite. You must complete this section in order to unlock additional content.'
)

fragment = Fragment()
items = self._render_student_view_for_items(context, display_items, fragment, view) if prereq_met else []
params = {
'items': self._render_student_view_for_items(context, display_items, fragment) if prereq_met else [],
'items': items,
'element_id': self.location.html_id(),
'item_id': text_type(self.location),
'position': self.position,
Expand All @@ -332,8 +346,8 @@ def _student_view(self, context, prereq_met, prereq_meta_info, banner_text=None)
'next_url': context.get('next_url'),
'prev_url': context.get('prev_url'),
'banner_text': banner_text,
'save_position': self.is_user_authenticated(context),
'show_completion': self.is_user_authenticated(context),
'save_position': view != PUBLIC_VIEW,
'show_completion': view != PUBLIC_VIEW,
'gated_content': self._get_gated_content_info(prereq_met, prereq_meta_info)
}
fragment.add_content(self.system.render_template("seq_module.html", params))
Expand Down Expand Up @@ -425,7 +439,7 @@ def _update_position(self, context, number_of_display_items):
elif self.position is None or self.position > number_of_display_items:
self.position = 1

def _render_student_view_for_items(self, context, display_items, fragment):
def _render_student_view_for_items(self, context, display_items, fragment, view=STUDENT_VIEW):
"""
Updates the given fragment with rendered student views of the given
display_items. Returns a list of dict objects with information about
Expand Down Expand Up @@ -463,7 +477,7 @@ def _render_student_view_for_items(self, context, display_items, fragment):
context['show_bookmark_button'] = show_bookmark_button
context['bookmarked'] = is_bookmarked

rendered_item = item.render(STUDENT_VIEW, context)
rendered_item = item.render(view, context)
fragment.add_fragment_resources(rendered_item)

iteminfo = {
Expand Down
11 changes: 10 additions & 1 deletion common/lib/xmodule/xmodule/tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,19 @@ class StubUserService(UserService):
"""
Stub UserService for testing the sequence module.
"""

def __init__(self, is_anonymous=False, **kwargs):
self.is_anonymous = is_anonymous
super(StubUserService, self).__init__(**kwargs)

def get_current_user(self):
"""
Implements abstract method for getting the current user.
"""
user = XBlockUser()
user.opt_attrs['edx-platform.username'] = 'bilbo'
if self.is_anonymous:
user.opt_attrs['edx-platform.username'] = 'anonymous'
user.opt_attrs['edx-platform.is_authenticated'] = False
else:
user.opt_attrs['edx-platform.username'] = 'bilbo'
return user
17 changes: 17 additions & 0 deletions common/lib/xmodule/xmodule/tests/test_html_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from xmodule.html_module import CourseInfoModule, HtmlDescriptor, HtmlModule

from . import get_test_descriptor_system, get_test_system
from ..x_module import PUBLIC_VIEW, STUDENT_VIEW


def instantiate_descriptor(**field_data):
Expand Down Expand Up @@ -81,6 +82,22 @@ def test_common_values(self, html):
module = HtmlModule(descriptor, module_system, field_data, Mock())
self.assertEqual(module.student_view_data(), dict(enabled=True, html=html))

@ddt.data(
STUDENT_VIEW,
PUBLIC_VIEW,
)
def test_student_preview_view(self, view):
"""
Ensure that student_view and public_view renders correctly.
"""
html = '<p>This is a test</p>'
descriptor = Mock()
field_data = DictFieldData({'data': html})
module_system = get_test_system()
module = HtmlModule(descriptor, module_system, field_data, Mock())
rendered = module_system.render(module, view, {}).content
self.assertIn(html, rendered)


class HtmlModuleSubstitutionTestCase(unittest.TestCase):
descriptor = Mock()
Expand Down
Loading