Skip to content
Merged
21 changes: 12 additions & 9 deletions lms/djangoapps/courseware/tests/test_discussion_xblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@
import uuid

from unittest import mock
from unittest.mock import patch
import ddt
from django.conf import settings
from django.test.utils import override_settings
from django.urls import reverse
from opaque_keys.edx.keys import CourseKey
from web_fragments.fragment import Fragment
from xblock.field_data import DictFieldData
from xmodule.discussion_block import DiscussionXBlock, loader
from xmodule.discussion_block import DiscussionXBlock
from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import BlockFactory, ToyCourseFactory
from xmodule.tests.helpers import mock_render_template

from lms.djangoapps.course_api.blocks.tests.helpers import deserialize_usage_key
from lms.djangoapps.courseware.block_render import get_block_for_descriptor
Expand Down Expand Up @@ -153,15 +155,15 @@ def get_rendered_template(self):
assert self.render_template.call_count == 1
return self.render_template.call_args_list[0][0][0]

def test_studio_view(self):
@patch('xblock.utils.resources.ResourceLoader.render_django_template', side_effect=mock_render_template)
def test_studio_view(self, mock_render_django_template):
"""
Test for the studio view.
"""
fragment = self.block.author_view()
assert isinstance(fragment, Fragment)
assert fragment.content == self.template_canary
self.render_template.assert_called_once_with(
'discussion/_discussion_inline_studio.html',
mock_render_django_template.assert_called_once_with(
'templates/discussion/_discussion_inline_studio.html',
{
'discussion_id': self.discussion_id,
'is_visible': True,
Expand Down Expand Up @@ -192,10 +194,11 @@ def test_student_perms_are_correct(self, permissions):
}

self.block.has_permission = lambda perm: permission_dict[perm]
with mock.patch.object(loader, 'render_template', mock.Mock):
with mock.patch('xmodule.discussion_block.render_to_string', return_value='') as mock_render:
self.block.student_view()

context = self.get_template_context()
# Get context from the mock call
assert mock_render.call_count == 1
context = mock_render.call_args_list[0][0][1]

for permission_name, expected_value in expected_permissions.items():
assert expected_value == context[permission_name]
Expand All @@ -204,7 +207,7 @@ def test_js_init(self):
"""
Test proper js init function is called.
"""
with mock.patch.object(loader, 'render_template', mock.Mock):
with mock.patch('xmodule.discussion_block.render_to_string', return_value=''):
fragment = self.block.student_view()
assert fragment.js_init_fn == 'DiscussionInlineBlock'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,78 +1,76 @@
## mako
{% load static i18n %}

<%namespace name='static' file='../static_content.html'/>

<%page expression_filter="h"/>

<%!
import json
from django.utils.translation import gettext as _
from django.template.defaultfilters import escapejs
from django.urls import reverse

from lms.djangoapps.discussion.django_comment_client.permissions import has_permission
from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_string
from openedx.core.djangolib.markup import HTML
%>

<section class="discussion discussion-board page-content-container" id="discussion-container"
data-course-id="${course.id}"
data-user-create-comment="${json.dumps(can_create_comment)}"
data-user-create-subcomment="${json.dumps(can_create_subcomment)}"
<section class="discussion discussion-board page-content-container"
id="discussion-container"
data-course-id="{{ course.id }}"
data-user-create-comment="{{ can_create_comment|yesno:'true,false' }}"
data-user-create-subcomment="{{ can_create_subcomment|yesno:'true,false' }}"
data-read-only="false"
data-sort-preference="${sort_preference}"
data-flag-moderator="${json.dumps(flag_moderator)}"
data-user-group-id="${user_group_id}">
data-sort-preference="{{ sort_preference }}"
data-flag-moderator="{{ flag_moderator|yesno:'true,false' }}"
data-user-group-id="{{ user_group_id }}">

<header class="page-header has-secondary">
## Breadcrumb navigation
{# Breadcrumb navigation #}
<div class="page-header-main">
<nav aria-label="${_('Discussions')}" class="sr-is-focusable" tabindex="-1">
<nav aria-label="{% trans 'Discussions' as tmsg %} {{tmsg|force_escape}}" class="sr-is-focusable" tabindex="-1">
<div class="has-breadcrumbs"></div>
</nav>
</div>

<div class="page-header-secondary">
## Add Post button
% if has_permission(user, 'create_thread', course.id):
{# Add Post button #}
{% if can_create_thread %}
<div class="forum-actions">
<button class="btn btn-outline-primary btn-small new-post-btn">${_("Add a Post")}</button>
<button class="btn btn-outline-primary btn-small new-post-btn">
{% trans "Add a Post" as tmsg %} {{tmsg|force_escape}}
</button>
</div>
% endif
## Search box
{% endif %}
{# Search box #}
<div class="forum-search"></div>
</div>
</header>
% if course_expiration_fragment:
${HTML(course_expiration_fragment.content)}
% endif

{% if course_expiration_fragment %}
{{ course_expiration_fragment.content|safe }}
{% endif %}

<div class="page-content"
% if getattr(course, 'language'):
lang="${course.language}"
% endif
{% if course.language %}
lang="{{ course.language }}"
{% endif %}
>
<div class="discussion-body">
<main id="main" class="discussion-column" aria-label="Content" tabindex="-1">
<article class="new-post-article is-hidden" style="display: none" tabindex="-1" aria-label="${_("New topic form")}"></article>
<article class="new-post-article is-hidden" style="display: none" tabindex="-1" aria-label="{% trans 'New topic form' as tmsg %} {{tmsg|force_escape}}">
</article>
<div class="forum-content"></div>
</main>
<aside class="forum-nav" role="complementary" aria-label="${_("Discussion thread list")}">
<%include file="_filter_dropdown.html" />
<aside class="forum-nav" role="complementary" aria-label="{% trans 'Discussion thread list' as tmsg %} {{tmsg|force_escape}}">
{% include "discussion/_filter_dropdown.html" %}
<div class="discussion-thread-list-container"></div>
</aside>
</div>
</div>
</section>

<%include file="_underscore_templates.html" />
<%include file="_thread_list_template.html" />

<%static:require_module_async module_name="js/commerce/track_ecommerce_events" class_name="TrackECommerceEvents">
{% include "discussion/_underscore_templates.html" %}
{% include "discussion/_thread_list_template.html" %}

var fbeLink = $("#FBE_banner");
<script>
require(["js/commerce/track_ecommerce_events"], function (TrackECommerceEvents) {

TrackECommerceEvents.trackUpsellClick(fbeLink, 'discussion_audit_access_expires', {
pageName: "discussion_tab",
linkType: "link",
linkCategory: "FBE_banner"
});
var fbeLink = $("#FBE_banner");

</%static:require_module_async>
TrackECommerceEvents.trackUpsellClick(
fbeLink,
'discussion_audit_access_expires',
{
pageName: "discussion_tab",
linkType: "link",
linkCategory: "FBE_banner"
}
);
});
</script>
3 changes: 2 additions & 1 deletion lms/djangoapps/discussion/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,8 @@ def _create_discussion_board_context(request, base_context, thread=None):
'is_commentable_divided': is_commentable_divided(course_key, discussion_id, course_discussion_settings),
# If the default topic id is None the front-end code will look for a topic that contains "General"
'discussion_default_topic_id': _get_discussion_default_topic_id(course),
'enable_daily_digest': is_forum_daily_digest_enabled()
'enable_daily_digest': is_forum_daily_digest_enabled(),
'PLATFORM_NAME': settings.PLATFORM_NAME
})
context.update(
get_experiment_user_metadata_context(
Expand Down
55 changes: 28 additions & 27 deletions lms/templates/discussion/_discussion_inline.html
Original file line number Diff line number Diff line change
@@ -1,44 +1,45 @@
<%page expression_filter="h"/>
{% load i18n %}

<%include file="_underscore_templates.html" />
<%include file="_thread_list_template.html" />
{% include "discussion/_underscore_templates.html" %}
{% include "discussion/_thread_list_template.html" %}

<%!
from django.utils.translation import gettext as _
from json import dumps as json_dumps
from openedx.core.djangolib.js_utils import js_escaped_string
%>

<div class="discussion-module" data-discussion-id="${discussion_id}"
data-user-create-comment="${json_dumps(can_create_comment)}"
data-user-create-subcomment="${json_dumps(can_create_subcomment)}"
data-read-only="${'false' if can_create_thread else 'true'}">
% if not user.is_authenticated:
<div class="discussion-module" data-discussion-id="{{ discussion_id }}"
data-user-create-comment="{{ can_create_comment|yesno:"true,false" }}"
data-user-create-subcomment="{{ can_create_subcomment|yesno:"true,false" }}"
data-read-only="{% if can_create_thread %}false{% else %}true{% endif %}">
{% if not user.is_authenticated %}
<div class="page-banner">
<div class="alert alert-warning" role="alert">
<span class="icon icon-alert fa fa fa-warning" aria-hidden="true"></span>
<div class="message-content">${login_msg}</div>
<span class="icon icon-alert fa fa-warning" aria-hidden="true"></span>
<div class="message-content">{{ login_msg }}</div>
</div>
</div>
<br>
% endif
{% endif %}

<div class="discussion-module-header">
<h3 class="hd hd-3 discussion-module-title">${_(display_name)}</h3>
<div class="inline-discussion-topic"><span class="inline-discussion-topic-title">${_("Topic:")}</span> ${discussion_category}
% if discussion_target:
/ ${discussion_target}
%endif
</div>
<h3 class="hd hd-3 discussion-module-title">
{% filter force_escape %}
{% blocktrans with title=display_name %}{{ title }}{% endblocktrans %}
{% endfilter %}
</h3>
<div class="inline-discussion-topic">
<span class="inline-discussion-topic-title">{% trans "Topic:" as tmsg %} {{tmsg|force_escape}}</span>
{{ discussion_category }}
{% if discussion_target %}
/ {{ discussion_target }}
{% endif %}
</div>
</div>
<button class="discussion-show btn"
data-discussion-id="${discussion_id}"
${"disabled=disabled" if not user.is_authenticated else ""}>
<span class="button-text">${_("Show Discussion")}</span>
data-discussion-id="{{ discussion_id }}"
{% if not user.is_authenticated %}disabled="disabled"{% endif %}>
<span class="button-text">{% trans "Show Discussion" as tmsg %} {{tmsg|force_escape}}</span>
</button>

</div>
<script type="text/javascript">
var $$course_id = "${course_id | n, js_escaped_string}";
var $$course_id = "{{ course_id|escapejs }}";

function DiscussionInlineBlock(runtime, element) {
'use strict';
Expand Down
19 changes: 10 additions & 9 deletions lms/templates/discussion/_discussion_inline_studio.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<%! from django.utils.translation import gettext as _ %>
<%page expression_filter="h"/>
{% load i18n %}

<div class="discussion-module" data-discussion-id="${discussion_id}">
<div class="discussion-module" data-discussion-id="{{ discussion_id }}">
<p>
<span class="discussion-preview">
<span class="icon fa fa-comment"/>
${_("To view live discussions, click Preview or View Live in Unit Settings.")}<br />
${_("Discussion ID: {discussion_id}").format(discussion_id=discussion_id)}
% if not is_visible:
<br /><b>${_('The discussion block is disabled for this course as it is not using a compatible discussion provider.')}</b>
% endif
<span class="icon fa fa-comment"></span>
{% trans "To view live discussions, click Preview or View Live in Unit Settings." as tmsg %} {{tmsg|force_escape}}<br />
{% filter force_escape %}
{% blocktrans with discussion_id=discussion_id %}Discussion ID: {{ discussion_id }}{% endblocktrans %}
{% endfilter %}
{% if not is_visible %}
<br /><b>{% trans "The discussion block is disabled for this course as it is not using a compatible discussion provider." as tmsg %} {{tmsg|force_escape}}</b>
{% endif %}
</span>
</p>
</div>
12 changes: 12 additions & 0 deletions lms/templates/discussion/_dropdown_category.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<li class="forum-nav-browse-menu-item"
id='{{ category_name|force_escape }}'
role="option">
<span class="forum-nav-browse-title">{{ category_name|force_escape }}</span>
<ul class="forum-nav-browse-submenu" role="presentation">
{% if topic_list %}
{% with new_topic_list=topic_list|add:", "|add:category_name %}
{% include "discussion/_dropdown_items.html" with category_map=category topic_list=new_topic_list %}
{% endwith %}
{% endif %}
</ul>
</li>
14 changes: 14 additions & 0 deletions lms/templates/discussion/_dropdown_entry.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<li class="forum-nav-browse-menu-item"
data-discussion-id='{{ entry.id|force_escape }}'
id='{{ entry.id|force_escape }}'
data-divided="{{ entry.is_divided|yesno:'true,false' }}"
role="option">
{% if entry_name %}
<span class="forum-nav-browse-title">
{% if topic_list %}
<span class="sr">{{ topic_list|force_escape }}, </span>
{% endif %}
{{ entry_name|force_escape }}
</span>
{% endif %}
</li>
15 changes: 15 additions & 0 deletions lms/templates/discussion/_dropdown_items.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% for child, c_type in map.children %}
{% if c_type == "entry" %}
{% for entry_key, entry_value in map.entries.items %}
{% if entry_key == child %}
{% include "discussion/_dropdown_entry.html" with entry_name=child entry=entry_value topic_list=topic_list %}
{% endif %}
{% endfor %}
{% else %}
{% for category_key, category_value in map.subcategories.items %}
{% if category_key == child %}
{% include "discussion/_dropdown_category.html" with category_name=child category=category_value topic_list=topic_list %}
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
Loading
Loading