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
7 changes: 5 additions & 2 deletions common/lib/xmodule/xmodule/conditional_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,11 @@ def is_condition_satisfied(self):
# the descriptor of a required module to have a property but
# for the resulting module to be a (flavor of) ErrorModule.
# So just log and return false.
log.warn('Error in conditional module: \
required module {module} has no {module_attr}'.format(module=module, module_attr=attr_name))
if module is not None:
Copy link
Contributor

Choose a reason for hiding this comment

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

why could a module be None here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

get_module has been used in required_modules which may return None if requester does not have access to that module – see more.

Copy link
Contributor

Choose a reason for hiding this comment

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

can you just include a comment to that effect?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair enough, will do that.

# We do not want to log when module is None, and it is when requester
# does not have access to the requested required module.
log.warn('Error in conditional module: \
required module {module} has no {module_attr}'.format(module=module, module_attr=attr_name))
return False

attr = getattr(module, attr_name)
Expand Down
29 changes: 27 additions & 2 deletions common/lib/xmodule/xmodule/tests/test_conditional.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class ConditionalFactory(object):
to allow for testing.
"""
@staticmethod
def create(system, source_is_error_module=False):
def create(system, source_is_error_module=False, source_visible_to_staff_only=False):
"""
return a dict of modules: the conditional with a single source and a single child.
Keys are 'cond_module', 'source_module', and 'child_module'.
Expand All @@ -75,11 +75,13 @@ def create(system, source_is_error_module=False):
source_descriptor = Mock(name='source_descriptor')
source_descriptor.location = source_location

source_descriptor.visible_to_staff_only = source_visible_to_staff_only
source_descriptor.runtime = descriptor_system
source_descriptor.render = lambda view, context=None: descriptor_system.render(source_descriptor, view, context)

# construct other descriptors:
child_descriptor = Mock(name='child_descriptor')
child_descriptor.visible_to_staff_only = False
child_descriptor._xmodule.student_view.return_value.content = u'<p>This is a secret</p>'
child_descriptor.student_view = child_descriptor._xmodule.student_view
child_descriptor.displayable_items.return_value = [child_descriptor]
Expand All @@ -88,6 +90,12 @@ def create(system, source_is_error_module=False):
child_descriptor.render = lambda view, context=None: descriptor_system.render(child_descriptor, view, context)
child_descriptor.location = source_location.replace(category='html', name='child')

def visible_to_nonstaff_users(desc):
"""
Returns if the object is visible to nonstaff users.
"""
return not desc.visible_to_staff_only

def load_item(usage_id, for_parent=None): # pylint: disable=unused-argument
"""Test-only implementation of load_item that simply returns static xblocks."""
return {
Expand Down Expand Up @@ -115,8 +123,12 @@ def load_item(usage_id, for_parent=None): # pylint: disable=unused-argument
ScopeIds(None, None, cond_location, cond_location)
)
cond_descriptor.xmodule_runtime = system
system.get_module = lambda desc: desc
system.get_module = lambda desc: desc if visible_to_nonstaff_users(desc) else None
cond_descriptor.get_required_module_descriptors = Mock(return_value=[source_descriptor])
cond_descriptor.required_modules = [
system.get_module(descriptor)
for descriptor in cond_descriptor.get_required_module_descriptors()
]

# return dict:
return {'cond_module': cond_descriptor,
Expand Down Expand Up @@ -183,6 +195,19 @@ def test_error_as_source(self):
html = ajax['html']
self.assertFalse(any(['This is a secret' in item for item in html]))

@patch('xmodule.conditional_module.log')
def test_conditional_with_staff_only_source_module(self, mock_log):
modules = ConditionalFactory.create(
self.test_system,
source_visible_to_staff_only=True,
)
cond_module = modules['cond_module']
cond_module.save()
cond_module.is_attempted = "false"
cond_module.handle_ajax('', '')
self.assertFalse(mock_log.warn.called)
self.assertIn(None, cond_module.required_modules)


class ConditionalModuleXmlTest(unittest.TestCase):
"""
Expand Down
18 changes: 13 additions & 5 deletions lms/templates/conditional_module.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
<%

<%!
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
%>

<%
def _message(reqm, message):
return message.format(link="<a href={url}>{url_name}</a>".format(
url = reverse('jump_to', kwargs=dict(course_id=reqm.course_id.to_deprecated_string(),
location=reqm.location.to_deprecated_string())),
url_name = reqm.display_name_with_default_escaped))
%>
% if message:
% for reqm in module.required_modules:
<p class="conditional-message">${_message(reqm, message)}</p>
% endfor
% for reqm in module.required_modules:
% if reqm:
<p class="conditional-message">${_message(reqm, message)}</p>
% else:
<p class="conditional-message">
${_("You do not have access to this dependency module.")}
</p>
% endif
% endfor
% endif