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
4 changes: 3 additions & 1 deletion cms/djangoapps/contentstore/views/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -1309,7 +1309,9 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F

# Update with gating info
xblock_info.update(_get_gating_info(course, xblock))

if is_xblock_unit:
# if xblock is a Unit we add the discussion_enabled option
xblock_info['discussion_enabled'] = xblock.discussion_enabled
Comment thread
awaisdar001 marked this conversation as resolved.
Outdated
if xblock.category == 'sequential':
# Entrance exam subsection should be hidden. in_entrance_exam is
# inherited metadata, all children will have it.
Expand Down
117 changes: 117 additions & 0 deletions cms/djangoapps/contentstore/views/tests/test_discussion_enabled.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""
Test module to test the discussion enabled flag.
"""


import json

from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory

from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_usage_url


class TestDiscussionEnabled(CourseTestCase):
"""
Test discussion enabled flags functionality in a Unit.
"""
def setUp(self):
super().setUp()
self.course = self.get_test_course()
self.course_usage_key = self.course.id.make_usage_key("course", self.course.id.run)
self.non_staff_authed_user_client, _ = self.create_non_staff_authed_user_client()

def get_test_course(self):
"""
Create and return a test course
"""
self.course = CourseFactory(
org="SHIELD",
number="SH101",
name="Introduction to Avengers",
run="2020_T2",
modulestore=self.store
)
self.chapter = ItemFactory(
parent_location=self.course.location,
category="chapter",
display_name="What is SHIELD?",
modulestore=self.store
)
self.sequential = ItemFactory(
parent_location=self.chapter.location,
category="sequential",
display_name="HQ",
modulestore=self.store
)
self.vertical = ItemFactory(
parent_location=self.sequential.location,
category="vertical",
display_name="Triskelion",
modulestore=self.store
)
self.vertical_1 = ItemFactory(
parent_location=self.sequential.location,
category="vertical",
display_name="Helicarrier",
modulestore=self.store
)
self.course.save()
return self.course

def _get_discussion_enabled_status(self, usage_key, client=None):
"""
Issue a GET request to fetch value of discussion_enabled flag of xblock represented by param:usage_key
"""
client = client if client is not None else self.client
url = reverse_usage_url("xblock_handler", usage_key)
resp = client.get(url, HTTP_ACCEPT="application/json")
return resp

def get_discussion_enabled_status(self, xblock, client=None):
"""
Issue a GET request to fetch value of discussion_enabled flag of param:xblock's
"""
resp = self._get_discussion_enabled_status(xblock.location, client=client)
content = json.loads(resp.content.decode("utf-8"))
return content.get("discussion_enabled", None)

def set_discussion_enabled_status(self, xblock, value, client=None):
"""
Issue a POST request to update value of discussion_enabled flag of param:xblock's
"""
client = client if client is not None else self.client
xblock_location = xblock.location
url = reverse_usage_url("xblock_handler", xblock_location)
resp = client.post(
url,
HTTP_ACCEPT="application/json",
data=json.dumps({"metadata": {"discussion_enabled": value}}),
content_type="application/json",
)
return resp

def test_discussion_enabled_false_initially(self):
"""
Tests discussion_enabled flag is False initially for vertical
"""
self.assertFalse(self.get_discussion_enabled_status(self.vertical))
self.assertFalse(self.get_discussion_enabled_status(self.vertical_1))

def test_discussion_enabled_toggle(self):
"""
Tests discussion_enabled can be toggled.
"""
self.set_discussion_enabled_status(self.vertical, True)
self.assertTrue(self.get_discussion_enabled_status(self.vertical))
self.assertFalse(self.get_discussion_enabled_status(self.vertical_1))

def test_non_course_author_cannot_get_or_set_discussion_enabled_flag(self):
"""
Test non course author cannot get/set discussion_enabled flag
"""
resp = self._get_discussion_enabled_status(self.course_usage_key, self.non_staff_authed_user_client)
self.assertEqual(resp.status_code, 403)
# Set call to the API with non authorised user should raise a 403
resp = self.set_discussion_enabled_status(self.vertical, True, self.non_staff_authed_user_client)
self.assertEqual(resp.status_code, 403)
33 changes: 30 additions & 3 deletions common/lib/xmodule/xmodule/vertical_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,55 @@
import pytz
from lxml import etree
from web_fragments.fragment import Fragment

from xmodule.util.misc import is_xblock_an_assignment
from xblock.core import XBlock # lint-amnesty, pylint: disable=wrong-import-order
from xblock.fields import Boolean, Scope
from xmodule.mako_module import MakoTemplateBlockBase
from xmodule.progress import Progress
from xmodule.seq_module import SequenceFields
from xmodule.studio_editable import StudioEditableBlock
from xmodule.util.misc import is_xblock_an_assignment
from xmodule.util.xmodule_django import add_webpack_to_fragment
from xmodule.x_module import PUBLIC_VIEW, STUDENT_VIEW, XModuleFields
from xmodule.xml_module import XmlParserMixin

log = logging.getLogger(__name__)

# Make '_' a no-op so we can scrape strings. Using lambda instead of
# `django.utils.translation.ugettext_noop` because Django cannot be imported in this file
_ = lambda text: text

# HACK: This shouldn't be hard-coded to two types
# OBSOLETE: This obsoletes 'type'
CLASS_PRIORITY = ['video', 'problem']


class VerticalFields:
"""
A mixin to introduce fields in the Vertical Block.
"""

discussion_enabled = Boolean(
display_name=_("Enable in-context discussions for the Unit"),
help=_(
"Add discussion for the Unit."
),
default=False,
scope=Scope.settings,
)


@XBlock.needs('user', 'bookmarks')
@XBlock.wants('completion')
@XBlock.wants('call_to_action')
class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParserMixin, MakoTemplateBlockBase, XBlock):
class VerticalBlock(
SequenceFields,
VerticalFields,
XModuleFields,
StudioEditableBlock,
XmlParserMixin,
MakoTemplateBlockBase,
XBlock
):
"""
Layout XBlock for rendering subblocks vertically.
"""
Expand Down