diff --git a/common/lib/xmodule/xmodule/discussion_module.py b/common/lib/xmodule/xmodule/discussion_module.py index 8ae7f9ad933bb821154d242f73b1b50b6cdd9b54..89bd27a9c72591106a300de8b45b0fef6f7c2551 100644 --- a/common/lib/xmodule/xmodule/discussion_module.py +++ b/common/lib/xmodule/xmodule/discussion_module.py @@ -1,5 +1,7 @@ from pkg_resources import resource_string +import json +from xblock.core import XBlock from xmodule.x_module import XModule from xmodule.raw_module import RawDescriptor from xmodule.editing_module import MetadataOnlyEditingDescriptor @@ -41,7 +43,20 @@ class DiscussionFields(object): sort_key = String(scope=Scope.settings) +def has_permission(user, permission, course_id): + """ + Copied from django_comment_client/permissions.py because I can't import + that file from here. It causes the xmodule_assets command to fail. + """ + return any(role.has_permission(permission) + for role in user.roles.filter(course_id=course_id)) + + +@XBlock.wants('user') class DiscussionModule(DiscussionFields, XModule): + """ + XModule for discussion forums. + """ js = { 'coffee': [ resource_string(__name__, 'js/src/discussion/display.coffee') @@ -53,9 +68,26 @@ class DiscussionModule(DiscussionFields, XModule): js_module_name = "InlineDiscussion" def get_html(self): + course = self.get_course() + user = None + user_service = self.runtime.service(self, 'user') + if user_service: + user = user_service._django_user # pylint: disable=protected-access + if user: + course_key = course.id # pylint: disable=no-member + can_create_comment = has_permission(user, "create_comment", course_key) + can_create_subcomment = has_permission(user, "create_sub_comment", course_key) + can_create_thread = has_permission(user, "create_thread", course_key) + else: + can_create_comment = False + can_create_subcomment = False + can_create_thread = False context = { 'discussion_id': self.discussion_id, - 'course': self.get_course(), + 'course': course, + 'can_create_comment': json.dumps(can_create_comment), + 'can_create_subcomment': json.dumps(can_create_subcomment), + 'can_create_thread': can_create_thread, } if getattr(self.system, 'is_author_mode', False): template = 'discussion/_discussion_module_studio.html' diff --git a/common/static/coffee/spec/discussion/discussion_spec_helper.coffee b/common/static/coffee/spec/discussion/discussion_spec_helper.coffee index 8d094c49d0d10977a08d0bbfbd80f161512ea448..c969f7edd061bd6f87bbc068dcc6fee3d8928b4d 100644 --- a/common/static/coffee/spec/discussion/discussion_spec_helper.coffee +++ b/common/static/coffee/spec/discussion/discussion_spec_helper.coffee @@ -37,560 +37,34 @@ class @DiscussionSpecHelper ) @setUnderscoreFixtures = -> - for templateName in ['thread-show'] + templateNames = [ + 'thread', 'thread-show', 'thread-edit', + 'thread-response', 'thread-response-show', 'thread-response-edit', + 'response-comment-show', 'response-comment-edit', + 'thread-list-item', 'discussion-home', 'search-alert', + 'new-post', 'thread-type', 'new-post-menu-entry', + 'new-post-menu-category', 'topic', 'post-user-display', + ] + templateNamesNoTrailingTemplate = [ + 'forum-action-endorse', 'forum-action-answer', 'forum-action-follow', + 'forum-action-vote', 'forum-action-report', 'forum-action-pin', + 'forum-action-close', 'forum-action-edit', 'forum-action-delete', + 'forum-actions', + ] + + for templateName in templateNames templateFixture = readFixtures('common/templates/discussion/' + templateName + '.underscore') appendSetFixtures($('<script>', { id: templateName + '-template', type: 'text/template' }) .text(templateFixture)) + for templateName in templateNamesNoTrailingTemplate + templateFixture = readFixtures('common/templates/discussion/' + templateName + '.underscore') + appendSetFixtures($('<script>', { id: templateName, type: 'text/template' }) + .text(templateFixture)) appendSetFixtures(""" -<div id="fixture-element"></div> - -<!-- -NOTE the html markup here comes from rendering lms/templates/discussion/_underscore_templates.html through a -browser and pasting the output. When that file changes, this one should be regenerated alongside it. ---> -<script aria-hidden="true" type="text/template" id="thread-template"> - <article class="discussion-article" data-id="<%- id %>"> - <div class="thread-wrapper"> - <div class="forum-thread-main-wrapper"> - <div class="thread-content-wrapper"></div> - <div class="post-extended-content"> - <ol class="responses js-marked-answer-list"></ol> - </div> - </div> - <div class="post-extended-content"> - <div class="response-count"/> - <div class="add-response"> - <button class="button add-response-btn"> - <i class="icon fa fa-reply"></i> - <span class="add-response-btn-text">Add A Response</span> - </button> - </div> - <ol class="responses js-response-list"/> - <div class="response-pagination"/> - <div class="post-status-closed bottom-post-status" style="display: none"> - This thread is closed. - </div> - <form class="discussion-reply-new" data-id="<%- id %>"> - <h4>Post a response:</h4> - <ul class="discussion-errors"></ul> - <div class="reply-body" data-id="<%- id %>"></div> - <div class="reply-post-control"> - <a class="discussion-submit-post control-button" href="#">Submit</a> - </div> - </form> - </div> - </div> - <div class="post-tools"> - <a href="javascript:void(0)" class="forum-thread-expand"><span class="icon fa fa-plus"/> Expand discussion</a> - <a href="javascript:void(0)" class="forum-thread-collapse"><span class="icon fa fa-minus"/> Collapse discussion</a> - </div> - </article> -</script> - -<script aria-hidden="true" type="text/template" id="thread-edit-template"> - <h1>Editing post</h1> - <ul class="edit-post-form-errors"></ul> - <div class="forum-edit-post-form-wrapper"></div> - <div class="form-row"> - <label class="sr" for="edit-post-title">Edit post title</label> - <input type="text" id="edit-post-title" class="edit-post-title" name="title" value="<%-title %>" placeholder="Title"> - </div> - <div class="form-row"> - <div class="edit-post-body" name="body"><%- body %></div> - </div> - <input type="submit" id="edit-post-submit" class="post-update" value="Update post"> - <a href="#" class="post-cancel">Cancel</a> -</script> - -<script aria-hidden="true" type="text/template" id="thread-response-template"> - <div class="discussion-response"></div> - <a href="#" class="action-show-comments"> - <%- interpolate('Show Comments (%(num_comments)s)', {num_comments: comments.length}, true) %> - <i class="icon fa fa-caret-down"></i> - </a> - <ol class="comments"> - <li class="new-comment"> - <form class="comment-form" data-id="<%- wmdId %>"> - <ul class="discussion-errors"></ul> - <label class="sr" for="add-new-comment">Add a comment</label> - <div class="comment-body" id="add-new-comment" data-id="<%- wmdId %>" - data-placeholder="Add a comment..."></div> - <div class="comment-post-control"> - <a class="discussion-submit-comment control-button" href="#">Submit</a> - </div> - </form> - </li> - </ol> -</script> - -<script aria-hidden="true" type="text/template" id="thread-response-show-template"> - <header> - <div class="response-header-content"> - <%= author_display %> - <p class="posted-details"> - <span class="timeago" title="<%= created_at %>"><%= created_at %></span> - - <% if (obj.endorsement) { %> - <%= - interpolate( - thread.get("thread_type") == "question" ? - (endorsement.username ? "marked as answer %(time_ago)s by %(user)s" : "marked as answer %(time_ago)s") : - (endorsement.username ? "endorsed %(time_ago)s by %(user)s" : "endorsed %(time_ago)s"), - { - 'time_ago': '<span class="timeago" title="' + endorsement.time + '">' + endorsement.time + '</span>', - 'user': endorser_display - }, - true - )%><% } %> - </p> - <div class="post-labels"> - <span class="post-label-reported"><i class="icon fa fa-flag"></i>Reported</span> - </div> - </div> - <div class="response-header-actions"> - <%= - _.template( - $('#forum-actions').html(), - { - contentId: cid, - contentType: 'response', - primaryActions: ['vote', thread.get('thread_type') == 'question' ? 'answer' : 'endorse'], - secondaryActions: ['edit', 'delete', 'report'] - } - ) - %> - </div> - </header> - - <div class="response-body"><%- body %></div> -</script> - -<script aria-hidden="true" type="text/template" id="thread-response-edit-template"> - <div class="edit-post-form"> - <h1>Editing response</h1> - <ul class="edit-post-form-errors"></ul> - <div class="form-row"> - <div class="edit-post-body" name="body" data-id="<%- id %>"><%- body %></div> - </div> - <input type="submit" id="edit-response-submit"class="post-update" value="Update response"> - <a href="#" class="post-cancel">Cancel</a> - </div> -</script> - -<script aria-hidden="true" type="text/template" id="response-comment-show-template"> - <div id="comment_<%- id %>"> - <div class="response-body"><%- body %></div> - <%= - _.template( - $('#forum-actions').html(), - { - contentId: cid, - contentType: 'comment', - primaryActions: [], - secondaryActions: ['edit', 'delete', 'report'] - } - ) - %> - - <p class="posted-details"> - <%= - interpolate( - 'posted %(time_ago)s by %(author)s', - {'time_ago': '<span class="timeago" title="' + created_at + '">' + created_at + '</span>', 'author': author_display}, - true - )%> - </p> - <div class="post-labels"> - <span class="post-label-reported"><i class="icon fa fa-flag"></i>Reported</span> - </div> - </div> -</script> - -<script aria-hidden="true" type="text/template" id="response-comment-edit-template"> - <div class="edit-post-form" id="comment_<%- id %>"> - <h1>Editing comment</h1> - <ul class="edit-comment-form-errors"></ul> - <div class="form-row"> - <div class="edit-comment-body" name="body" data-id="<%- id %>"><%- body %></div> - </div> - <input type="submit" id="edit-comment-submit" class="post-update" value="Update comment"> - <a href="#" class="post-cancel">Cancel</a> - </div> -</script> - -<script aria-hidden="true" type="text/template" id="thread-list-item-template"> - <li data-id="<%- id %>" class="forum-nav-thread<% if (typeof(read) != "undefined" && !read) { %> is-unread<% } %>"> - <a href="#" class="forum-nav-thread-link"> - <div class="forum-nav-thread-wrapper-0"> - <% - var icon_class, sr_text; - if (thread_type == "discussion") { - icon_class = "fa-comments"; - sr_text = "discussion"; - } else if (endorsed) { - icon_class = "fa-check"; - sr_text = "answered question"; - } else { - icon_class = "fa-question"; - sr_text = "unanswered question"; - } - %> - <span class="sr"><%= sr_text %></span> - <i class="icon fa <%= icon_class %>"></i> - </div><div class="forum-nav-thread-wrapper-1"> - <span class="forum-nav-thread-title"><%- title %></span> - - <% - var labels = ""; - if (pinned) { - labels += '<li class="post-label-pinned"><i class="icon fa fa-thumb-tack"></i>Pinned</li> '; - } - if (typeof(subscribed) != "undefined" && subscribed) { - labels += '<li class="post-label-following"><i class="icon fa fa-star"></i>Following</li> '; - } - if (staff_authored) { - labels += '<li class="post-label-by-staff"><i class="icon fa fa-user"></i>By: Staff</li> '; - } - if (community_ta_authored) { - labels += '<li class="post-label-by-community-ta"><i class="icon fa fa-user"></i>By: Community TA</li> '; - } - if (labels != "") { - print('<ul class="forum-nav-thread-labels">' + labels + '</ul>'); - } - %> - </div><div class="forum-nav-thread-wrapper-2"> - - <span class="forum-nav-thread-votes-count">+<%= - interpolate( - '%(votes_up_count)s%(span_sr_open)s votes %(span_close)s', - {'span_sr_open': '<span class="sr">', 'span_close': '</span>', 'votes_up_count': votes['up_count']}, - true - ) - %></span> - - <span class="forum-nav-thread-comments-count <% if (unread_comments_count > 0) { %>is-unread<% } %>"> - <% - var fmt; - // Counts in data do not include the post itself, but the UI should - var data = { - 'span_sr_open': '<span class="sr">', - 'span_close': '</span>', - 'unread_comments_count': unread_comments_count + (read ? 0 : 1), - 'comments_count': comments_count + 1 - }; - if (unread_comments_count > 0) { - fmt = '%(comments_count)s %(span_sr_open)scomments (%(unread_comments_count)s unread comments)%(span_close)s'; - } else { - fmt = '%(comments_count)s %(span_sr_open)scomments %(span_close)s'; - } - print(interpolate(fmt, data, true)); - %> - </span> - </div> - </a> - </li> -</script> - -<script aria-hidden="true" type="text/template" id="discussion-home"> - <div class="discussion-article blank-slate"> - <section class="home-header"> - <span class="label">DISCUSSION HOME:</span> - <h1 class="home-title">Cohort Course</h1> - </section> - - </div> -</script> - -<script aria-hidden="true" type="text/template" id="search-alert-template"> - <div class="search-alert" id="search-alert-<%- cid %>"> - <div class="search-alert-content"> - <p class="message"><%= message %></p> - </div> - - <div class="search-alert-controls"> - <a href="#" class="dismiss control control-dismiss"><i class="icon fa fa-remove"></i></a> - </div> - </div> -</script> - -<script aria-hidden="true" type="text/template" id="new-post-template"> - <form class="forum-new-post-form"> - <ul class="post-errors" style="display: none"></ul> - <div class="forum-new-post-form-wrapper"></div> - <% if (cohort_options) { %> - <div class="post-field group-selector-wrapper<% if (!is_commentable_cohorted) { %> disabled<% } %>"> - <label class="field-label"> - <span class="field-label-text"> - Visible To: - </span><select class="field-input js-group-select" name="group_id" <% if (!is_commentable_cohorted) { %>disabled<% } %>> - <option value="">All Groups</option> - <% _.each(cohort_options, function(opt) { %> - <option value="<%= opt.value %>" <% if (opt.selected) { %>selected<% } %>><%- opt.text %></option> - <% }); %> - </select> - </label><div class="field-help"> - Discussion admins, moderators, and TAs can make their posts visible to all students or specify a single cohort. - </div> - </div> - <% } %> - <div class="post-field"> - <label class="field-label"> - <span class="sr">Title:</span> - <input type="text" class="field-input js-post-title" name="title" placeholder="Title"> - </label><span class="field-help"> - Add a clear and descriptive title to encourage participation. - </span> - </div> - <div class="post-field js-post-body editor" name="body" data-placeholder="Enter your question or comment"></div> - <div class="post-options"> - <label class="post-option is-enabled"> - <input type="checkbox" name="follow" class="post-option-input js-follow" checked> - <i class="icon fa fa-star"></i>follow this post - </label> - <% if (allow_anonymous) { %> - <label class="post-option"> - <input type="checkbox" name="anonymous" class="post-option-input js-anon"> - post anonymously - </label> - <% } %> - <% if (allow_anonymous_to_peers) { %> - <label class="post-option"> - <input type="checkbox" name="anonymous_to_peers" class="post-option-input js-anon-peers"> - post anonymously to classmates - </label> - <% } %> - </div> - <div> - <input type="submit" class="submit" value="Add Post"> - <a href="#" class="cancel">Cancel</a> - </div> - </form> -</script> - -<script aria-hidden="true" type="text/template" id="thread-type-template"> - <div class="post-field"> - <div class="field-label"> - <span class="field-label-text"> - "Post type:" - </span><fieldset class="field-input"> - <input type="radio" name="<%= form_id %>-post-type" class="post-type-input" id="<%= form_id %>-post-type-question" value="question" checked> - <label for="<%= form_id %>-post-type-question" class="post-type-label"> - <i class="icon fa fa-question"></i> - "Question" - </label> - <input type="radio" name="<%= form_id %>-post-type" class="post-type-input" id="<%= form_id %>-post-type-discussion" value="discussion"> - <label for="<%= form_id %>-post-type-discussion" class="post-type-label"> - <i class="icon fa fa-comments"></i> - "Discussion" - </label> - </fieldset> - </div><span class="field-help"> - "Questions raise issues that need answers. Discussions share ideas and start conversations." - </span> - </div> -</script> - -<script aria-hidden="true" type="text/template" id="new-post-menu-entry-template"> - <li role="menuitem" class="topic-menu-item"> - <a href="#" class="topic-title" data-discussion-id="<%- id %>" data-cohorted="<%- is_cohorted %>"><%- text %></a> - </li> -</script> - -<script aria-hidden="true" type="text/template" id="new-post-menu-category-template"> - <li role="menuitem" class="topic-menu-item"> - <span class="topic-title"><%- text %></span> - <ul role="menu" class="topic-submenu"><%= entries %></ul> - </li> -</script> - -<script aria-hidden="true" type="text/template" id="topic-template"> - <div class="field-label"> - <span class="field-label-text">Topic Area:</span><div class="field-input post-topic"> - <a href="#" class="post-topic-button"> - <span class="sr">Discussion topics; current selection is: </span> - <span class="js-selected-topic"></span> - <span class="drop-arrow" aria-hidden="true">â–¾</span> - </a> - <div class="topic-menu-wrapper"> - <label class="topic-filter-label"> - <span class="sr">Filter topics</span> - <input type="text" class="topic-filter-input" placeholder="Filter topics"> - </label> - <ul class="topic-menu" role="menu"><%= topics_html %></ul> - </div> - </div> - </div><span class="field-help"> - Add your post to a relevant topic to help others find it. - </span> -</script> - - - - - <script type="text/template" id="forum-action-endorse"> - <li class="actions-item"> - <a href="javascript:void(0)" class="action-button action-endorse" role="checkbox" aria-checked="false"> - <span class="sr">Endorse</span> - <span class="action-label" aria-hidden="true"> - <span class="label-unchecked">Endorse</span> - <span class="label-checked">Unendorse</span> - </span> - <span class="action-icon"><i class="icon fa fa-check"></i></span> - </a> - </li> - </script> - - - <script type="text/template" id="forum-action-answer"> - <li class="actions-item"> - <a href="javascript:void(0)" class="action-button action-answer" role="checkbox" aria-checked="false"> - <span class="sr">Mark as Answer</span> - <span class="action-label" aria-hidden="true"> - <span class="label-unchecked">Mark as Answer</span> - <span class="label-checked">Unmark as Answer</span> - </span> - <span class="action-icon"><i class="icon fa fa-check"></i></span> - </a> - </li> - </script> - - - <script type="text/template" id="forum-action-follow"> - <li class="actions-item"> - <a href="javascript:void(0)" class="action-button action-follow" role="checkbox" aria-checked="false"> - <span class="sr">Follow</span> - <span class="action-label" aria-hidden="true"> - <span class="label-unchecked">Follow</span> - <span class="label-checked">Unfollow</span> - </span> - <span class="action-icon"><i class="icon fa fa-star"></i></span> - </a> - </li> - </script> - - -<script type="text/template" id="forum-action-vote"> - <li class="actions-item"> - <span aria-hidden="true" class="display-vote" style="display: none;"> - <span class="vote-count"></span> - </span> - <a href="#" class="action-button action-vote" role="checkbox" aria-checked="false"> - <span class="sr">Vote</span> - <span class="sr js-sr-vote-count"></span> - - <span class="action-label" aria-hidden="true"> - <span class="vote-count"></span> - </span> - - <span class="action-icon" aria-hidden="true"> - <i class="icon fa fa-plus"></i> - </span> - </a> - </li> -</script> - - - - - <script type="text/template" id="forum-action-report"> - <li class="actions-item"> - <a href="javascript:void(0)" class="action-list-item action-report" role="checkbox" aria-checked="false"> - <span class="sr">Report abuse</span> - <span class="action-label" aria-hidden="true"> - <span class="label-unchecked">Report</span> - <span class="label-checked">Unreport</span> - </span> - <span class="action-icon"> - <i class="icon fa fa-flag"></i> - </span> - </a> - </li> - </script> - - - <script type="text/template" id="forum-action-pin"> - <li class="actions-item"> - <a href="javascript:void(0)" class="action-list-item action-pin" role="checkbox" aria-checked="false"> - <span class="sr">Pin</span> - <span class="action-label" aria-hidden="true"> - <span class="label-unchecked">Pin</span> - <span class="label-checked">Unpin</span> - </span> - <span class="action-icon"> - <i class="icon fa fa-thumb-tack"></i> - </span> - </a> - </li> - </script> - - - <script type="text/template" id="forum-action-close"> - <li class="actions-item"> - <a href="javascript:void(0)" class="action-list-item action-close" role="checkbox" aria-checked="false"> - <span class="sr">Close</span> - <span class="action-label" aria-hidden="true"> - <span class="label-unchecked">Close</span> - <span class="label-checked">Open</span> - </span> - <span class="action-icon"> - <i class="icon fa fa-lock"></i> - </span> - </a> - </li> - </script> - - - - - - <script type="text/template" id="forum-action-edit"> - <li class="actions-item"> - <a href="javascript:void(0)" class="action-list-item action-edit" role="button"> - <span class="action-label">Edit</span> - <span class="action-icon"><i class="icon fa fa-pencil"></i></span> - </a> - </li> - </script> - - - <script type="text/template" id="forum-action-delete"> - <li class="actions-item"> - <a href="javascript:void(0)" class="action-list-item action-delete" role="button"> - <span class="action-label">Delete</span> - <span class="action-icon"><i class="icon fa fa-remove"></i></span> - </a> - </li> - </script> - - -<script type="text/template" id="forum-actions"> - <ul class="<%= contentType %>-actions-list"> - <% _.each(primaryActions, function(action) { print(_.template($('#forum-action-' + action).html(), {})) }) %> - <li class="actions-item is-visible"> - <div class="more-wrapper"> - <a href="javascript:void(0)" class="action-button action-more" role="button" aria-haspopup="true" aria-controls="action-menu-<%= contentId %>"> - <span class="action-label">More</span> - <span class="action-icon"><i class="icon fa fa-ellipsis-h"></i></span> - </a> - <div class="actions-dropdown" id="action-menu-<%= contentType %>" aria-expanded="false"> - <ul class="actions-dropdown-list"> - <% _.each(secondaryActions, function(action) { print(_.template($('#forum-action-' + action).html(), {})) }) %> - </ul> - </div> - </div> - </li> - </ul> -</script> - -<script aria-hidden="true" type="text/template" id="post-user-display-template"> - <% if (username) { %> - <a href="<%- user_url %>" class="username"><%- username %></a> - <% if (is_community_ta) { %> - <span class="user-label-community-ta">Community TA</span> - <% } else if (is_staff) { %> - <span class="user-label-staff">Staff</span> - <% } %> - <% } else { %> - anonymous - <% } %> -</script> -""") + <div id="fixture-element"></div> + <div id="discussion-container" + data-course-name="Fake Course" + data-user-create-comment="true" + data-user-create-subcomment="true" + ></div> + """) diff --git a/common/static/coffee/spec/discussion/view/discussion_thread_list_view_spec.coffee b/common/static/coffee/spec/discussion/view/discussion_thread_list_view_spec.coffee index b30efed581502083f1644c2e5f2e3bb50aea98bf..c2dc5f78ddfa596658136a818bdd89376eb51f9a 100644 --- a/common/static/coffee/spec/discussion/view/discussion_thread_list_view_spec.coffee +++ b/common/static/coffee/spec/discussion/view/discussion_thread_list_view_spec.coffee @@ -415,7 +415,7 @@ describe "DiscussionThreadListView", -> it "for answered question", -> renderSingleThreadWithProps({thread_type: "question", endorsed: true}) - expect($(".forum-nav-thread-wrapper-0 .icon")).toHaveClass("fa-check") + expect($(".forum-nav-thread-wrapper-0 .icon")).toHaveClass("fa-check-square-o") expect($(".forum-nav-thread-wrapper-0 .sr")).toHaveText("answered question") it "for unanswered question", -> diff --git a/common/static/coffee/spec/discussion/view/discussion_thread_view_spec.coffee b/common/static/coffee/spec/discussion/view/discussion_thread_view_spec.coffee index 6757d000f2c7e1861ff7a366416e8ba9712f27f1..85fb7ad760f036107135dfff2b68e1e9807864a7 100644 --- a/common/static/coffee/spec/discussion/view/discussion_thread_view_spec.coffee +++ b/common/static/coffee/spec/discussion/view/discussion_thread_view_spec.coffee @@ -80,16 +80,16 @@ describe "DiscussionThreadView", -> expect(view.$('.display-vote').is(":visible")).toBe(not originallyClosed) _.each(["tab", "inline"], (mode) => - it 'Test that in #{mode} mode when a closed thread is opened the comment form is displayed', -> + it "Test that in #{mode} mode when a closed thread is opened the comment form is displayed", -> checkCommentForm(true, mode) - it 'Test that in #{mode} mode when a open thread is closed the comment form is hidden', -> + it "Test that in #{mode} mode when a open thread is closed the comment form is hidden", -> checkCommentForm(false, mode) - it 'Test that in #{mode} mode when a closed thread is opened the vote button is displayed and vote count is hidden', -> + it "Test that in #{mode} mode when a closed thread is opened the vote button is displayed and vote count is hidden", -> checkVoteDisplay(true, mode) - it 'Test that in #{mode} mode when a open thread is closed the vote button is hidden and vote count is displayed', -> + it "Test that in #{mode} mode when a open thread is closed the vote button is hidden and vote count is displayed", -> checkVoteDisplay(false, mode) ) diff --git a/common/static/coffee/spec/discussion/view/new_post_view_spec.coffee b/common/static/coffee/spec/discussion/view/new_post_view_spec.coffee index e6dd3013a96babb90a0c9e4077c08b856d3db4e2..6458c3cb3d5554de2801554c2bd5f326745380f7 100644 --- a/common/static/coffee/spec/discussion/view/new_post_view_spec.coffee +++ b/common/static/coffee/spec/discussion/view/new_post_view_spec.coffee @@ -180,7 +180,7 @@ describe "NewPostView", -> eventSpy = jasmine.createSpy('eventSpy') view.listenTo(view, "newPost:cancel", eventSpy) view.$(".post-errors").html("<li class='post-error'>Title can't be empty</li>") - view.$("label[for$='post-type-discussion']").click() + view.$("label[for$='post-type-question']").click() view.$(".js-post-title").val("Test Title") view.$(".js-post-body textarea").val("Test body") view.$(".wmd-preview p").html("Test body") @@ -192,8 +192,8 @@ describe "NewPostView", -> view.$(".cancel").click() expect(eventSpy).toHaveBeenCalled() expect(view.$(".post-errors").html()).toEqual(""); - expect($("input[id$='post-type-question']")).toBeChecked() - expect($("input[id$='post-type-discussion']")).not.toBeChecked() + expect($("input[id$='post-type-discussion']")).toBeChecked() + expect($("input[id$='post-type-question']")).not.toBeChecked() expect(view.$(".js-post-title").val()).toEqual(""); expect(view.$(".js-post-body textarea").val()).toEqual(""); expect(view.$(".js-follow")).toBeChecked() diff --git a/common/static/coffee/src/discussion/main.coffee b/common/static/coffee/src/discussion/main.coffee index 67c15d1e3447ef983cdeacd38d4012cf251d0771..5c663ffa214673bb2221f8bfcafc7adca6c12d7d 100644 --- a/common/static/coffee/src/discussion/main.coffee +++ b/common/static/coffee/src/discussion/main.coffee @@ -5,6 +5,7 @@ if Backbone? DiscussionUtil.loadRolesFromContainer() element = $(elem) window.$$course_id = element.data("course-id") + window.courseName = element.data("course-name") user_info = element.data("user-info") sort_preference = element.data("sort-preference") threads = element.data("threads") diff --git a/common/static/coffee/src/discussion/views/discussion_thread_list_view.coffee b/common/static/coffee/src/discussion/views/discussion_thread_list_view.coffee index 9ac93798ecfc6da6805780a3c78b1ca7c1fa57d0..a01ef013d2e766a947b4451b2e15c4d78092b403 100644 --- a/common/static/coffee/src/discussion/views/discussion_thread_list_view.coffee +++ b/common/static/coffee/src/discussion/views/discussion_thread_list_view.coffee @@ -248,7 +248,7 @@ if Backbone? @$(".forum-nav-thread[data-id='#{thread_id}'] .forum-nav-thread-link").addClass("is-active").find(".forum-nav-thread-wrapper-1").prepend('<span class="sr">' + gettext("Current conversation") + '</span>') goHome: -> - @template = _.template($("#discussion-home").html()) + @template = _.template($("#discussion-home-template").html()) $(".forum-content").html(@template) $(".forum-nav-thread-list a").removeClass("is-active").find(".sr").remove() $("input.email-setting").bind "click", @updateEmailNotifications diff --git a/common/static/coffee/src/discussion/views/discussion_thread_view.coffee b/common/static/coffee/src/discussion/views/discussion_thread_view.coffee index ce893c38e7aad6a4ffccdee699d955dca23fddaf..f369cf6619603036b900b320f6dd2d8e7410cc44 100644 --- a/common/static/coffee/src/discussion/views/discussion_thread_view.coffee +++ b/common/static/coffee/src/discussion/views/discussion_thread_view.coffee @@ -50,7 +50,13 @@ if Backbone? renderTemplate: -> @template = _.template($("#thread-template").html()) - @template(@model.toJSON()) + templateData = @model.toJSON() + container = $("#discussion-container") + if !container.length + # inline discussion + container = $(".discussion-module") + templateData.can_create_comment = container.data("user-create-comment") + @template(templateData) render: -> @$el.html(@renderTemplate()) diff --git a/common/static/coffee/src/discussion/views/thread_response_view.coffee b/common/static/coffee/src/discussion/views/thread_response_view.coffee index 7418f80e28bc9fc55a7ead2a71173445b4432684..a299738d6f63959a188d877a05312377b7c55b63 100644 --- a/common/static/coffee/src/discussion/views/thread_response_view.coffee +++ b/common/static/coffee/src/discussion/views/thread_response_view.coffee @@ -19,6 +19,11 @@ if Backbone? templateData = @model.toJSON() templateData.wmdId = @model.id ? (new Date()).getTime() + container = $("#discussion-container") + if !container.length + # inline discussion + container = $(".discussion-module") + templateData.create_sub_comment = container.data("user-create-subcomment") @template(templateData) render: -> diff --git a/common/static/common/templates/discussion/discussion-home.underscore b/common/static/common/templates/discussion/discussion-home.underscore new file mode 100644 index 0000000000000000000000000000000000000000..d06a4205fcf21778849393867e0315b98ca4e75d --- /dev/null +++ b/common/static/common/templates/discussion/discussion-home.underscore @@ -0,0 +1,59 @@ +<div class="discussion-article blank-slate"> + <section class="home-header"> + <span class="label"><%- gettext("DISCUSSION HOME:") %></span> + <% if (window.courseName) { %> + <h1 class="home-title"><%- window.courseName %></h1> + <% } %> + </section> + + <% if (window.ENABLE_DISCUSSION_HOME_PANEL) { %> + <span class="label label-settings"> + <%- interpolate(gettext("How to use %(platform_name)s discussions"), {platform_name: window.PLATFORM_NAME}, true) %> + </span> + <table class="home-helpgrid"> + <tr class="helpgrid-row helpgrid-row-navigation"> + <td class="row-title"><%- gettext("Find discussions") %></td> + <td class="row-item"> + <i class="icon fa fa-reorder"></i> + <span class="row-description"><%- gettext("Focus in on specific topics") %></span> + </td> + <td class="row-item"> + <i class="icon fa fa-search"></i> + <span class="row-description"><%- gettext("Search for specific posts") %></span> + </td> + <td class="row-item"> + <i class="icon fa fa-sort"></i> + <span class="row-description"><%- gettext("Sort by date, vote, or comments") %></span> + </td> + </tr> + <tr class="helpgrid-row helpgrid-row-participation"> + <td class="row-title"><%- gettext("Engage with posts") %></td> + <td class="row-item"> + <i class="icon fa fa-plus"></i> + <span class="row-description"><%- gettext("Upvote posts and good responses") %></span> + </td> + <td class="row-item"> + <i class="icon fa fa-flag"></i> + <span class="row-description"><%- gettext("Report Forum Misuse") %></span> + </td> + <td class="row-item"> + <i class="icon fa fa-star"></i> + <span class="row-description"><%- gettext("Follow posts for updates") %></span> + </td> + </tr> + <tr class="helpgrid-row helpgrid-row-notification"> + <td class="row-title"><%- gettext('Receive updates') %></td> + <td class="row-item-full" colspan="3"> + <label for="email-setting-checkbox"> + <span class="sr"><%- gettext("Toggle Notifications Setting") %></span> + <span class="notification-checkbox"> + <input type="checkbox" id="email-setting-checkbox" class="email-setting" name="email-notification"/> + <i class="icon fa fa-envelope"></i> + </span> + </label> + <span class="row-description"><%- gettext("Check this box to receive an email digest once a day notifying you about new, unread activity from posts you are following.") %></span> + </td> + </tr> + </table> + <% } %> +</div> diff --git a/common/static/common/templates/discussion/forum-action-answer.underscore b/common/static/common/templates/discussion/forum-action-answer.underscore new file mode 100644 index 0000000000000000000000000000000000000000..6f10e860edfe1eae331a2736e12148c915181124 --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-answer.underscore @@ -0,0 +1,10 @@ +<li class="actions-item"> + <a href="javascript:void(0)" class="action-button action-answer" role="checkbox" aria-checked="false"> + <span class="sr"><%- gettext("Mark as Answer") %></span> + <span class="action-label" aria-hidden="true"> + <span class="label-unchecked"><%- gettext("Mark as Answer") %></span> + <span class="label-checked"><%- gettext("Unmark as Answer") %></span> + </span> + <span class="action-icon"><i class="icon fa fa-ok"></i></span> + </a> +</li> diff --git a/common/static/common/templates/discussion/forum-action-close.underscore b/common/static/common/templates/discussion/forum-action-close.underscore new file mode 100644 index 0000000000000000000000000000000000000000..61fd9af230b08e56feab10f1b1133df8917f8401 --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-close.underscore @@ -0,0 +1,12 @@ +<li class="actions-item"> + <a href="javascript:void(0)" class="action-list-item action-close" role="checkbox" aria-checked="false"> + <span class="sr"><%- gettext("Close") %></span> + <span class="action-label" aria-hidden="true"> + <span class="label-unchecked"><%- gettext("Close") %></span> + <span class="label-checked"><%- gettext("Open") %></span> + </span> + <span class="action-icon"> + <i class="icon fa fa-lock"></i> + </span> + </a> +</li> diff --git a/common/static/common/templates/discussion/forum-action-delete.underscore b/common/static/common/templates/discussion/forum-action-delete.underscore new file mode 100644 index 0000000000000000000000000000000000000000..facf308c611e4375377bb27b601f9da48a5eb43b --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-delete.underscore @@ -0,0 +1,6 @@ +<li class="actions-item"> + <a href="javascript:void(0)" class="action-list-item action-delete" role="button"> + <span class="action-label"><%- gettext("Delete") %></span> + <span class="action-icon"><i class="icon fa fa-remove"></i></span> + </a> +</li> diff --git a/common/static/common/templates/discussion/forum-action-edit.underscore b/common/static/common/templates/discussion/forum-action-edit.underscore new file mode 100644 index 0000000000000000000000000000000000000000..1a67c97d90e4c62a3c4090b1d2e2399fabe72b7a --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-edit.underscore @@ -0,0 +1,6 @@ +<li class="actions-item"> + <a href="javascript:void(0)" class="action-list-item action-edit" role="button"> + <span class="action-label"><%- gettext("Edit") %></span> + <span class="action-icon"><i class="icon fa fa-pencil"></i></span> + </a> +</li> diff --git a/common/static/common/templates/discussion/forum-action-endorse.underscore b/common/static/common/templates/discussion/forum-action-endorse.underscore new file mode 100644 index 0000000000000000000000000000000000000000..98d3fd174b07087d89e07445fda72c60c54f0eb9 --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-endorse.underscore @@ -0,0 +1,10 @@ +<li class="actions-item"> + <a href="javascript:void(0)" class="action-button action-endorse" role="checkbox" aria-checked="false"> + <span class="sr"><%- gettext("Endorse") %></span> + <span class="action-label" aria-hidden="true"> + <span class="label-unchecked"><%- gettext("Endorse") %></span> + <span class="label-checked"><%- gettext("Unendorse") %></span> + </span> + <span class="action-icon"><i class="icon fa fa-ok"></i></span> + </a> +</li> diff --git a/common/static/common/templates/discussion/forum-action-follow.underscore b/common/static/common/templates/discussion/forum-action-follow.underscore new file mode 100644 index 0000000000000000000000000000000000000000..c4adc7aa9f9d1273c0a8f58bde5185b6e64346ce --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-follow.underscore @@ -0,0 +1,10 @@ +<li class="actions-item"> + <a href="javascript:void(0)" class="action-button action-follow" role="checkbox" aria-checked="false"> + <span class="sr"><%- gettext("Follow") %></span> + <span class="action-label" aria-hidden="true"> + <span class="label-unchecked"><%- gettext("Follow") %></span> + <span class="label-checked"><%- gettext("Unfollow") %></span> + </span> + <span class="action-icon"><i class="icon fa fa-star"></i></span> + </a> +</li> diff --git a/common/static/common/templates/discussion/forum-action-pin.underscore b/common/static/common/templates/discussion/forum-action-pin.underscore new file mode 100644 index 0000000000000000000000000000000000000000..a4b3117bcd56c4f0a70dff4cff632f71b5292fc4 --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-pin.underscore @@ -0,0 +1,12 @@ +<li class="actions-item"> + <a href="javascript:void(0)" class="action-list-item action-pin" role="checkbox" aria-checked="false"> + <span class="sr"><%- gettext("Pin") %></span> + <span class="action-label" aria-hidden="true"> + <span class="label-unchecked"><%- gettext("Pin") %></span> + <span class="label-checked"><%- gettext("Unpin") %></span> + </span> + <span class="action-icon"> + <i class="icon fa fa-thumb-tack"></i> + </span> + </a> +</li> diff --git a/common/static/common/templates/discussion/forum-action-report.underscore b/common/static/common/templates/discussion/forum-action-report.underscore new file mode 100644 index 0000000000000000000000000000000000000000..dfc52eaa7b9b07c2b2fc3b06b912a64c246b97dd --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-report.underscore @@ -0,0 +1,12 @@ +<li class="actions-item"> + <a href="javascript:void(0)" class="action-list-item action-report" role="checkbox" aria-checked="false"> + <span class="sr"><%- gettext("Report abuse") %></span> + <span class="action-label" aria-hidden="true"> + <span class="label-unchecked"><%- gettext("Report") %></span> + <span class="label-checked"><%- gettext("Unreport") %></span> + </span> + <span class="action-icon"> + <i class="icon fa fa-flag"></i> + </span> + </a> +</li> diff --git a/common/static/common/templates/discussion/forum-action-vote.underscore b/common/static/common/templates/discussion/forum-action-vote.underscore new file mode 100644 index 0000000000000000000000000000000000000000..b837236e212b2bf6f511ae423406a96c812e7496 --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-vote.underscore @@ -0,0 +1,18 @@ +<li class="actions-item"> + <span aria-hidden="true" class="display-vote" > + <span class="vote-count"></span> + </span> + <a href="#" class="action-button action-vote" role="checkbox" aria-checked="false"> + <% // Vote counts are populated by JS %> + <span class="sr"><%- gettext("Vote for this post,") %> </span> + <span class="sr js-sr-vote-count"></span> + + <span class="action-label" aria-hidden="true"> + <span class="vote-count"></span> + </span> + + <span class="action-icon" aria-hidden="true"> + <i class="icon fa fa-plus"></i> + </span> + </a> +</li> diff --git a/common/static/common/templates/discussion/forum-actions.underscore b/common/static/common/templates/discussion/forum-actions.underscore new file mode 100644 index 0000000000000000000000000000000000000000..9fd9714a3e438efdcd89063c45715bbdb98dc9e9 --- /dev/null +++ b/common/static/common/templates/discussion/forum-actions.underscore @@ -0,0 +1,16 @@ +<ul class="<%= contentType %>-actions-list"> + <% _.each(primaryActions, function(action) { print(_.template($('#forum-action-' + action).html(), {})) }) %> + <li class="actions-item is-visible"> + <div class="more-wrapper"> + <a href="javascript:void(0)" class="action-button action-more" role="button" aria-haspopup="true" aria-controls="action-menu-<%= contentId %>"> + <span class="action-label"><%- gettext("More") %></span> + <span class="action-icon"><i class="icon fa fa-ellipsis-h"></i></span> + </a> + <div class="actions-dropdown" id="action-menu-<%= contentType %>" aria-expanded="false"> + <ul class="actions-dropdown-list"> + <% _.each(secondaryActions, function(action) { print(_.template($('#forum-action-' + action).html(), {})) }) %> + </ul> + </div> + </div> + </li> +</ul> diff --git a/common/static/common/templates/discussion/new-post-menu-category.underscore b/common/static/common/templates/discussion/new-post-menu-category.underscore new file mode 100644 index 0000000000000000000000000000000000000000..f10d07638f71c4039a081a3239e50383eab7f767 --- /dev/null +++ b/common/static/common/templates/discussion/new-post-menu-category.underscore @@ -0,0 +1,4 @@ +<li role="menuitem" class="topic-menu-item"> + <span class="topic-title"><%- text %></span> + <ul role="menu" class="topic-submenu"><%= entries %></ul> +</li> diff --git a/common/static/common/templates/discussion/new-post-menu-entry.underscore b/common/static/common/templates/discussion/new-post-menu-entry.underscore new file mode 100644 index 0000000000000000000000000000000000000000..bad96e0078f1c10b8f7588f071198289aa67fdc4 --- /dev/null +++ b/common/static/common/templates/discussion/new-post-menu-entry.underscore @@ -0,0 +1,3 @@ +<li role="menuitem" class="topic-menu-item"> + <a href="#" class="topic-title" data-discussion-id="<%- id %>" data-cohorted="<%- is_cohorted %>"><%- text %></a> +</li> diff --git a/common/static/common/templates/discussion/new-post.underscore b/common/static/common/templates/discussion/new-post.underscore new file mode 100644 index 0000000000000000000000000000000000000000..7f155841f7addaba97881099ddbf99f597a941f7 --- /dev/null +++ b/common/static/common/templates/discussion/new-post.underscore @@ -0,0 +1,52 @@ +<form class="forum-new-post-form"> + <ul class="post-errors" style="display: none"></ul> + <div class="forum-new-post-form-wrapper"></div> + <% if (cohort_options) { %> + <div class="post-field group-selector-wrapper <% if (!is_commentable_cohorted) { print('disabled'); } %>"> + <label class="field-label"> + <span class="field-label-text"> + <% //Translators: This labels the selector for which group of students can view a post %> + <%- gettext("Visible To:") %> + </span><select aria-describedby="field_help_visible_to" class="field-input js-group-select" name="group_id" <% if (!is_commentable_cohorted) { print("disabled"); } %>> + <option value=""><%- gettext("All Groups") %></option> + <% _.each(cohort_options, function(opt) { %> + <option value="<%= opt.value %>" <% if (opt.selected) { print("selected"); } %>><%- opt.text %></option> + <% }); %> + </select> + </label><div class="field-help" id="field_help_visible_to"> + <%- gettext("Discussion admins, moderators, and TAs can make their posts visible to all students or specify a single cohort.") %> + </div> + </div> + <% } %> + <div class="post-field"> + <label class="field-label"> + <span class="sr"><%- gettext("Title:") %></span> + <input aria-describedby="field_help_title" type="text" class="field-input js-post-title" name="title" placeholder="<%- gettext('Title') %>"> + </label><span class="field-help" id="field_help_title"> + <%- gettext("Add a clear and descriptive title to encourage participation.") %> + </span> + </div> + <div class="post-field js-post-body editor" name="body" data-placeholder="<%- gettext('Enter your question or comment') %>"></div> + <div class="post-options"> + <label class="post-option is-enabled"> + <input type="checkbox" name="follow" class="post-option-input js-follow" checked> + <i class="icon fa fa-star"></i><%- gettext("follow this post") %> + </label> + <% if (allow_anonymous) { %> + <label class="post-option"> + <input type="checkbox" name="anonymous" class="post-option-input js-anon"> + <%- gettext("post anonymously") %> + </label> + <% } %> + <% if (allow_anonymous_to_peers) { %> + <label class="post-option"> + <input type="checkbox" name="anonymous_to_peers" class="post-option-input js-anon-peers"> + <%- gettext("post anonymously to classmates") %> + </label> + <% } %> + </div> + <div> + <input type="submit" class="submit" value="<%- gettext('Add Post') %>"> + <a href="#" class="cancel"><%- gettext('Cancel') %></a> + </div> +</form> diff --git a/common/static/common/templates/discussion/post-user-display.underscore b/common/static/common/templates/discussion/post-user-display.underscore new file mode 100644 index 0000000000000000000000000000000000000000..515538fce3ac46196e7081871b26719c710b557d --- /dev/null +++ b/common/static/common/templates/discussion/post-user-display.underscore @@ -0,0 +1,10 @@ +<% if (username) { %> +<a href="<%- user_url %>" class="username"><%- username %></a> + <% if (is_community_ta) { %> + <span class="user-label-community-ta"><%- gettext("Community TA") %></span> + <% } else if (is_staff) { %> + <span class="user-label-staff"><%- gettext("Staff") %></span> + <% } %> +<% } else { %> + <%- gettext('anonymous') %> +<% } %> diff --git a/common/static/common/templates/discussion/response-comment-edit.underscore b/common/static/common/templates/discussion/response-comment-edit.underscore new file mode 100644 index 0000000000000000000000000000000000000000..5dff8ec97883c6780a60b8ab8dd163dadbe3a745 --- /dev/null +++ b/common/static/common/templates/discussion/response-comment-edit.underscore @@ -0,0 +1,9 @@ +<div class="edit-post-form" id="comment_<%- id %>"> + <h1><%- gettext("Editing comment") %></h1> + <ul class="edit-comment-form-errors"></ul> + <div class="form-row"> + <div class="edit-comment-body" name="body" data-id="<%- id %>"><%- body %></div> + </div> + <input type="submit" id="edit-comment-submit" class="post-update" value="<%- gettext("Update comment") %>"> + <a href="#" class="post-cancel"><%- gettext("Cancel") %></a> +</div> diff --git a/common/static/common/templates/discussion/response-comment-show.underscore b/common/static/common/templates/discussion/response-comment-show.underscore new file mode 100644 index 0000000000000000000000000000000000000000..5f15338f1e8a0c2ce47059ae16118add86a3765d --- /dev/null +++ b/common/static/common/templates/discussion/response-comment-show.underscore @@ -0,0 +1,32 @@ +<div id="comment_<%- id %>"> + <div class="response-body"><%- body %></div> + <%= + _.template( + $('#forum-actions').html(), + { + contentId: cid, + contentType: 'comment', + primaryActions: [], + secondaryActions: ['edit', 'delete', 'report'] + } + ) + %> + <p class="posted-details"> + <% + var time_ago = interpolate( + '<span class="timeago" title="%(time)s">%(time)s</span>', + {time: created_at}, + true + ); + %> + <%= interpolate( + // Translators: 'timeago' is a placeholder for a fuzzy, relative timestamp (see: https://github.com/rmm5t/jquery-timeago) + gettext("posted %(time_ago)s by %(author)s"), + {time_ago: time_ago, author: author_display}, + true + ) %> + </p> + <div class="post-labels"> + <span class="post-label-reported"><i class="icon fa fa-flag"></i><%- gettext("Reported") %></span> + </div> +</div> diff --git a/common/static/common/templates/discussion/search-alert.underscore b/common/static/common/templates/discussion/search-alert.underscore new file mode 100644 index 0000000000000000000000000000000000000000..e75fc65cfd5c55a3e55f7115c73b415d76ecf084 --- /dev/null +++ b/common/static/common/templates/discussion/search-alert.underscore @@ -0,0 +1,9 @@ +<div class="search-alert" id="search-alert-<%- cid %>"> + <div class="search-alert-content"> + <p class="message"><%= message %></p> + </div> + + <div class="search-alert-controls"> + <a href="#" class="dismiss control control-dismiss"><i class="icon fa fa-remove"></i></a> + </div> +</div> diff --git a/common/static/common/templates/discussion/thread-edit.underscore b/common/static/common/templates/discussion/thread-edit.underscore new file mode 100644 index 0000000000000000000000000000000000000000..a3d83bea03a6438c4b76cef50126bc599bb4dada --- /dev/null +++ b/common/static/common/templates/discussion/thread-edit.underscore @@ -0,0 +1,12 @@ +<h1><%- gettext("Editing post") %></h1> +<ul class="post-errors"></ul> +<div class="forum-edit-post-form-wrapper"></div> +<div class="form-row"> + <label class="sr" for="edit-post-title"><%- gettext("Edit post title") %></label> + <input type="text" id="edit-post-title" class="edit-post-title" name="title" value="<%-title %>" placeholder="<%- gettext('Title') %>"> +</div> +<div class="form-row"> + <div class="edit-post-body" name="body"><%- body %></div> +</div> +<input type="submit" id="edit-post-submit" class="post-update" value="<%- gettext("Update post") %>"> +<a href="#" class="post-cancel"><%- gettext("Cancel") %></a> diff --git a/common/static/common/templates/discussion/thread-list-item.underscore b/common/static/common/templates/discussion/thread-list-item.underscore new file mode 100644 index 0000000000000000000000000000000000000000..335085fbc32d11d78263cd696d3aed2be97da801 --- /dev/null +++ b/common/static/common/templates/discussion/thread-list-item.underscore @@ -0,0 +1,97 @@ +<li data-id="<%- id %>" class="forum-nav-thread<% if (typeof(read) != "undefined" && !read) { %> is-unread<% } %>"> + <a href="#" class="forum-nav-thread-link"> + <div class="forum-nav-thread-wrapper-0"> + <% + var icon_class, sr_text; + if (thread_type === "discussion") { + icon_class = "fa-comments"; + // Translators: This is a label for a Discussion forum thread + sr_text = gettext("discussion"); + } else if (endorsed) { + icon_class = "fa-check-square-o"; + // Translators: This is a label for a Question forum thread with a marked answer + sr_text = gettext("answered question"); + } else { + icon_class = "fa-question"; + // Translators: This is a label for a Question forum thread without a marked answer + sr_text = gettext("unanswered question"); + } + %> + <span class="sr"><%= sr_text %></span> + <i class="icon fa <%= icon_class %>"></i> + </div><div class="forum-nav-thread-wrapper-1"> + <span class="forum-nav-thread-title"><%- title %></span> + <% if(typeof(subscribed) === "undefined") { var subscribed = null; } %> + <% if(pinned || subscribed || staff_authored || community_ta_authored) { %> + <ul class="forum-nav-thread-labels"> + <% if (pinned) { %> + <li class="post-label-pinned"> + <i class="icon fa fa-thumb-tack"></i> + <% // Translators: This is a label for a forum thread that has been pinned %> + <%- gettext("Pinned") %> + </li> + <% } %> + <% if (subscribed) { %> + <li class="post-label-following"> + <i class="icon fa fa-star"></i> + <% // Translators: This is a label for a forum thread that the user is subscribed to %> + <%- gettext("Following") %> + </li> + <% } %> + <% if (staff_authored) { %> + <li class="post-label-by-staff"> + <i class="icon fa fa-user"></i> + <% // Translators: This is a label for a forum thread that was authored by a member of the course staff %> + <%- gettext("By: Staff") %> + </li> + <% } %> + <% if (community_ta_authored) { %> + <li class="post-label-by-community-ta"> + <i class="icon fa fa-user"></i> + <% // Translators: This is a label for a forum thread that was authored by a community TA %> + <%- gettext("By: Community TA") %> + </li> + <% } %> + </ul> + <% } %> + </div><div class="forum-nav-thread-wrapper-2"> + <% + // Translators: 'votes_count' is a numerical placeholder for a specific discussion thread; 'span_start' and 'span_end' placeholders refer to HTML markup. Please translate the word 'votes'. + var fmt = ngettext( + "%(votes_count)s%(span_start)s vote %(span_end)s", + "%(votes_count)s%(span_start)s votes %(span_end)s", + votes['up_count'] + ); + %> + <span class="forum-nav-thread-votes-count"> + +<%- interpolate(fmt, { + votes_count: votes['up_count'], + span_start: '<span class="sr">', + span_end: '</span>' + }, true) + %> + </span> + + <span class="forum-nav-thread-comments-count <% if (unread_comments_count > 0) { %>is-unread<% } %>"> + <% + var fmt; + // Counts in data do not include the post itself, but the UI should + var data = { + 'span_sr_open': '<span class="sr">', + 'span_close': '</span>', + 'unread_comments_count': unread_comments_count + (read ? 0 : 1), + 'comments_count': comments_count + 1 + }; + if (unread_comments_count > 0) { + // Translators: 'comments_count' and 'unread_comments_count' are numerical placeholders for a specific discussion thread; 'span_*' placeholders refer to HTML markup. Please translate the word 'comments'. + fmt = gettext('%(comments_count)s %(span_sr_open)scomments (%(unread_comments_count)s unread comments)%(span_close)s'); + } else { + // Translators: 'comments_count' is a numerical placeholder for a specific discussion thread; 'span_*' placeholders refer to HTML markup. Please translate the word 'comments'. + fmt = gettext('%(comments_count)s %(span_sr_open)scomments %(span_close)s'); + } + print(interpolate(fmt, data, true)); + %> + </span> + </div> + </a> +</li> diff --git a/common/static/common/templates/discussion/thread-response-edit.underscore b/common/static/common/templates/discussion/thread-response-edit.underscore new file mode 100644 index 0000000000000000000000000000000000000000..ce3f4c6535a8dbe752315f38afac66f94b9218a7 --- /dev/null +++ b/common/static/common/templates/discussion/thread-response-edit.underscore @@ -0,0 +1,9 @@ +<div class="edit-post-form"> + <h1><%- gettext("Editing response") %></h1> + <ul class="edit-post-form-errors"></ul> + <div class="form-row"> + <div class="edit-post-body" name="body" data-id="<%- id %>"><%- body %></div> + </div> + <input type="submit" id="edit-response-submit"class="post-update" value="<%- gettext("Update response") %>"> + <a href="#" class="post-cancel"><%- gettext("Cancel") %></a> +</div> diff --git a/common/static/common/templates/discussion/thread-response-show.underscore b/common/static/common/templates/discussion/thread-response-show.underscore new file mode 100644 index 0000000000000000000000000000000000000000..d43fd2d8aa6ebf045a66c2d94bd0ba911c49c571 --- /dev/null +++ b/common/static/common/templates/discussion/thread-response-show.underscore @@ -0,0 +1,59 @@ +<header> + <div class="response-header-content"> + <%= author_display %> + <p class="posted-details"> + <span class="timeago" title="<%= created_at %>"><%= created_at %></span> + <% if (obj.endorsement) { %> + - + <% + var fmt = null; + if (thread.get("thread_type") == "question") { + if (endorsement.username) { + // Translators: time_ago is a placeholder for a fuzzy, relative timestamp + // like "4 hours ago" or "about a month ago" + fmt = gettext("marked as answer %(time_ago)s by %(user)s"); + } else { + // Translators: time_ago is a placeholder for a fuzzy, relative timestamp + // like "4 hours ago" or "about a month ago" + fmt = gettext("marked as answer %(time_ago)s"); + } + } else { + if (endorsement.username) { + // Translators: time_ago is a placeholder for a fuzzy, relative timestamp + // like "4 hours ago" or "about a month ago" + fmt = gettext("endorsed %(time_ago)s by %(user)s"); + } else { + // Translators: time_ago is a placeholder for a fuzzy, relative timestamp + // like "4 hours ago" or "about a month ago" + fmt = gettext("endorsed %(time_ago)s"); + } + } + var time_ago = interpolate( + '<span class="timeago" title="%(time)s">%(time)s</span>', + {time: endorsement.time}, + true + ); + %> + <%= interpolate(fmt, {time_ago: time_ago, user: endorser_display}, true) %> + <% } %> + </p> + <div class="post-labels"> + <span class="post-label-reported"><i class="icon fa fa-flag"></i><%- gettext("Reported") %></span> + </div> + </div> + <div class="response-header-actions"> + <%= + _.template( + $('#forum-actions').html(), + { + contentId: cid, + contentType: 'response', + primaryActions: ['vote', thread.get('thread_type') == 'question' ? 'answer' : 'endorse'], + secondaryActions: ['edit', 'delete', 'report'] + } + ) + %> + </div> +</header> + +<div class="response-body"><%- body %></div> diff --git a/common/static/common/templates/discussion/thread-response.underscore b/common/static/common/templates/discussion/thread-response.underscore new file mode 100644 index 0000000000000000000000000000000000000000..7e3aeffde2c2c2304eea6a057d1b524475bedd2d --- /dev/null +++ b/common/static/common/templates/discussion/thread-response.underscore @@ -0,0 +1,27 @@ +<div class="discussion-response"></div> +<a href="#" class="action-show-comments"> + <% + var fmts = ngettext( + "Show Comment (%(num_comments)s)", + "Show Comments (%(num_comments)s)", + comments.length + ); + print(interpolate(fmts, {num_comments: comments.length}, true)); + %> + <i class="icon fa fa-caret-down"></i> +</a> +<ol class="comments"> + <li class="new-comment"> + <% if (create_sub_comment) { %> + <form class="comment-form" data-id="<%- wmdId %>"> + <ul class="discussion-errors"></ul> + <label class="sr" for="add-new-comment"><%- gettext("Add a comment") %></label> + <div class="comment-body" id="add-new-comment" data-id="<%- wmdId %>" + data-placeholder="<%- gettext('Add a comment') %>"></div> + <div class="comment-post-control"> + <a class="discussion-submit-comment control-button" href="#"><%- gettext("Submit") %></a> + </div> + </form> + <% } %> + </li> +</ol> diff --git a/common/static/common/templates/discussion/thread-type.underscore b/common/static/common/templates/discussion/thread-type.underscore new file mode 100644 index 0000000000000000000000000000000000000000..4ba9fa4d70a2fcb64f8c02292d92e8775468fa03 --- /dev/null +++ b/common/static/common/templates/discussion/thread-type.underscore @@ -0,0 +1,23 @@ +<div class="post-field"> + <div class="field-label"> + <span class="field-label-text"> + <% // Translators: This is the label for a control to select a forum post type %> + <%- gettext("Post type:") %> + </span><fieldset class="field-input"><legend class="sr"><%- gettext("Post type:") %></legend> + <input aria-describedby="field_help_post_type" type="radio" name="<%= form_id %>-post-type" class="post-type-input" id="<%= form_id %>-post-type-question" value="question"> + <label for="<%= form_id %>-post-type-question" class="post-type-label"> + <i class="icon fa fa-question"></i> + <% // Translators: This is a forum post type %> + <%- gettext("Question") %> + </label> + <input aria-describedby="field_help_post_type" type="radio" name="<%= form_id %>-post-type" class="post-type-input" id="<%= form_id %>-post-type-discussion" value="discussion" checked> + <label for="<%= form_id %>-post-type-discussion" class="post-type-label"> + <i class="icon fa fa-comments"></i> + <% // Translators: This is a forum post type %> + <%- gettext("Discussion") %> + </label> + </fieldset> + </div><span class="field-help" id="field_help_post_type"> + <%- gettext("Questions raise issues that need answers. Discussions share ideas and start conversations.") %> + </span> +</div> diff --git a/common/static/common/templates/discussion/thread.underscore b/common/static/common/templates/discussion/thread.underscore new file mode 100644 index 0000000000000000000000000000000000000000..3212105475007231bf012d9142f7f71f6acc5194 --- /dev/null +++ b/common/static/common/templates/discussion/thread.underscore @@ -0,0 +1,38 @@ +<article class="discussion-article" data-id="<%- id %>"> + <div class="thread-wrapper" tabindex="-1"> + <div class="forum-thread-main-wrapper"> + <div class="thread-content-wrapper"></div> + <div class="post-extended-content"> + <ol class="responses js-marked-answer-list"></ol> + </div> + </div> + <div class="post-extended-content"> + <div class="response-count"/> + <div class="add-response"> + <button class="button add-response-btn"> + <i class="icon fa fa-reply"></i> + <span class="add-response-btn-text"><%- gettext("Add a Response") %></span> + </button> + </div> + <ol class="responses js-response-list"/> + <div class="response-pagination"/> + <div class="post-status-closed bottom-post-status" style="display: none"> + <%- gettext("This thread is closed.") %> + </div> + <% if (can_create_comment) { %> + <form class="discussion-reply-new" data-id="<%- id %>"> + <h4><%- gettext("Post a response:") %></h4> + <ul class="discussion-errors"></ul> + <div class="reply-body" data-id="<%- id %>"></div> + <div class="reply-post-control"> + <a class="discussion-submit-post control-button" href="#"><%- gettext("Submit") %></a> + </div> + </form> + <% } %> + </div> + </div> + <div class="post-tools"> + <a href="javascript:void(0)" class="forum-thread-expand"><span class="icon fa fa-plus"/><%- gettext("Expand discussion") %></a> + <a href="javascript:void(0)" class="forum-thread-collapse"><span class="icon fa fa-minus"/><%- gettext("Collapse discussion") %></a> + </div> +</article> diff --git a/common/static/common/templates/discussion/topic.underscore b/common/static/common/templates/discussion/topic.underscore new file mode 100644 index 0000000000000000000000000000000000000000..b59fd8e7711397684a29e89c569aa7b5b21c46f6 --- /dev/null +++ b/common/static/common/templates/discussion/topic.underscore @@ -0,0 +1,19 @@ +<% // Using div here instead of label because we are using a non-native control %> +<div class="field-label"> + <span class="field-label-text"><%- gettext("Topic Area:") %></span><div class="field-input post-topic"> + <a href="#" class="post-topic-button"> + <span class="sr"><%- gettext("Discussion topics; current selection is: ") %></span> + <span class="js-selected-topic"></span> + <span class="drop-arrow" aria-hidden="true">â–¾</span> + </a> + <div class="topic-menu-wrapper"> + <label class="topic-filter-label"> + <span class="sr"><%- gettext("Filter topics") %></span> + <input aria-describedby="field_help_topic_area" type="text" class="topic-filter-input" placeholder="<%- gettext('Filter topics') %>"> + </label> + <ul class="topic-menu" role="menu"><%= topics_html %></ul> + </div> + </div> +</div><span class="field-help" id="field_help_topic_area"> + <%- gettext("Add your post to a relevant topic to help others find it.") %> +</span> diff --git a/lms/djangoapps/courseware/tests/test_discussion_module.py b/lms/djangoapps/courseware/tests/test_discussion_module.py new file mode 100644 index 0000000000000000000000000000000000000000..d9eb8265146e59d16472b8c7c6c747b52d88cc1a --- /dev/null +++ b/lms/djangoapps/courseware/tests/test_discussion_module.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +"""Test for Discussion Xmodule functional logic.""" +from mock import Mock +from . import BaseTestXmodule +from courseware.module_render import get_module_for_descriptor_internal + + +class DiscussionModuleTest(BaseTestXmodule): + """Logic tests for Discussion Xmodule.""" + CATEGORY = "discussion" + + def test_html_with_user(self): + discussion = get_module_for_descriptor_internal( + user=self.users[0], + descriptor=self.item_descriptor, + student_data=Mock(name='student_data'), + course_id=self.course.id, + track_function=Mock(name='track_function'), + xqueue_callback_url_prefix=Mock(name='xqueue_callback_url_prefix'), + request_token='request_token', + ) + + fragment = discussion.render('student_view') + html = fragment.content + self.assertIn('data-user-create-comment="false"', html) + self.assertIn('data-user-create-subcomment="false"', html) diff --git a/lms/djangoapps/django_comment_client/forum/views.py b/lms/djangoapps/django_comment_client/forum/views.py index 0b2e1249c77e2cc8d1e17774bebe1146267da291..1521b0d30375fbe37d0022ac2fed834004c95986 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -277,6 +277,11 @@ def forum_form_discussion(request, course_key): 'threads': _attr_safe_json(threads), 'thread_pages': query_params['num_pages'], 'user_info': _attr_safe_json(user_info), + 'can_create_comment': _attr_safe_json( + has_permission(request.user, "create_comment", course.id)), + 'can_create_subcomment': _attr_safe_json( + has_permission(request.user, "create_sub_comment", course.id)), + 'can_create_thread': has_permission(request.user, "create_thread", course.id), 'flag_moderator': bool( has_permission(request.user, 'openclose_thread', course.id) or has_access(request.user, 'staff', course) @@ -376,6 +381,11 @@ def single_thread(request, course_key, discussion_id, thread_id): 'csrf': csrf(request)['csrf_token'], 'init': '', # TODO: What is this? 'user_info': _attr_safe_json(user_info), + 'can_create_comment': _attr_safe_json( + has_permission(request.user, "create_comment", course.id)), + 'can_create_subcomment': _attr_safe_json( + has_permission(request.user, "create_sub_comment", course.id)), + 'can_create_thread': has_permission(request.user, "create_thread", course.id), 'annotated_content_info': _attr_safe_json(annotated_content_info), 'course': course, #'recent_active_threads': recent_active_threads, diff --git a/lms/templates/discussion/_discussion_module.html b/lms/templates/discussion/_discussion_module.html index 8789a7492da5507e9a6309194fe7e19c5024c7d6..aedf0073439ff47c1f31afc82af4b3932e0aefb0 100644 --- a/lms/templates/discussion/_discussion_module.html +++ b/lms/templates/discussion/_discussion_module.html @@ -1,12 +1,11 @@ <%include file="_underscore_templates.html" /> <%! from django.utils.translation import ugettext as _ -from django_comment_client.permissions import has_permission %> -<div class="discussion-module" data-discussion-id="${discussion_id | h}"> +<div class="discussion-module" data-discussion-id="${discussion_id | h}" data-user-create-comment="${can_create_comment}" data-user-create-subcomment="${can_create_subcomment}"> <a class="discussion-show control-button" href="javascript:void(0)" data-discussion-id="${discussion_id | h}" role="button"><span class="show-hide-discussion-icon"></span><span class="button-text">${_("Show Discussion")}</span></a> - % if has_permission(user, 'create_thread', course.id): + % if can_create_thread: <a href="#" class="new-post-btn" role="button"><span class="icon fa fa-edit new-post-icon"></span>${_("New Post")}</a> % endif </div> diff --git a/lms/templates/discussion/_underscore_templates.html b/lms/templates/discussion/_underscore_templates.html index cab9ef174cd630498f94102db25f8f6eadee4fba..35e35785d3697e4137195f4a91e49ac6919b0325 100644 --- a/lms/templates/discussion/_underscore_templates.html +++ b/lms/templates/discussion/_underscore_templates.html @@ -1,608 +1,20 @@ <%namespace name='static' file='../static_content.html'/> -<%! -from django.utils.translation import ugettext as _ -from django.template.defaultfilters import escapejs -from django_comment_client.permissions import has_permission -%> +<%! import json %> -## IMPORTANT: In order to keep js tests valid and relevant, please be sure to update the appropriate HTML in -## common/static/coffee/spec/discussion_spec_helper.coffee is changed and regenerated, whenever this one changes. -<script aria-hidden="true" type="text/template" id="thread-template"> - <article class="discussion-article" data-id="${'<%- id %>'}"> - <div class="thread-wrapper" tabindex="-1"> - <div class="forum-thread-main-wrapper"> - <div class="thread-content-wrapper"></div> - <div class="post-extended-content"> - <ol class="responses js-marked-answer-list"></ol> - </div> - </div> - <div class="post-extended-content"> - <div class="response-count"/> - <div class="add-response"> - <button class="button add-response-btn"> - <i class="icon fa fa-reply"></i> - <span class="add-response-btn-text">${_('Add A Response')}</span> - </button> - </div> - <ol class="responses js-response-list"/> - <div class="response-pagination"/> - <div class="post-status-closed bottom-post-status" style="display: none"> - ${_("This thread is closed.")} - </div> - % if course is UNDEFINED or has_permission(user, 'create_comment', course.id): - <form class="discussion-reply-new" data-id="${'<%- id %>'}"> - <h4>${_("Post a response:")}</h4> - <ul class="discussion-errors"></ul> - <div class="reply-body" data-id="${'<%- id %>'}"></div> - <div class="reply-post-control"> - <a class="discussion-submit-post control-button" href="#">${_("Submit")}</a> - </div> - </form> - % endif - </div> - </div> - <div class="post-tools"> - <a href="javascript:void(0)" class="forum-thread-expand"><span class="icon fa fa-plus"/> ${_("Expand discussion")}</a> - <a href="javascript:void(0)" class="forum-thread-collapse"><span class="icon fa fa-minus"/> ${_("Collapse discussion")}</a> - </div> - </article> +<script type="text/javascript"> +window.PLATFORM_NAME = ${json.dumps(settings.PLATFORM_NAME)}; +window.ENABLE_DISCUSSION_HOME_PANEL = ${json.dumps(settings.FEATURES.get('ENABLE_DISCUSSION_HOME_PANEL', False))}; </script> -% for template_name in ['thread-show']: +% for template_name in ['thread', 'thread-show', 'thread-edit', 'thread-response', 'thread-response-show', 'thread-response-edit', 'response-comment-show', 'response-comment-edit', 'thread-list-item', 'discussion-home', 'search-alert', 'new-post', 'thread-type', 'new-post-menu-entry', 'new-post-menu-category', 'topic', 'post-user-display']: <script aria-hidden="true" type="text/template" id="${template_name}-template"> <%static:include path="common/templates/discussion/${template_name}.underscore" /> </script> % endfor -<script aria-hidden="true" type="text/template" id="thread-edit-template"> - <h1>${_("Editing post")}</h1> - <ul class="post-errors"></ul> - <div class="forum-edit-post-form-wrapper"></div> - <div class="form-row"> - <label class="sr" for="edit-post-title">${_("Edit post title")}</label> - <input type="text" id="edit-post-title" class="edit-post-title" name="title" value="${"<%-title %>"}" placeholder="${_('Title') | h}"> - </div> - <div class="form-row"> - <div class="edit-post-body" name="body">${"<%- body %>"}</div> - </div> - <input type="submit" id="edit-post-submit" class="post-update" value="${_("Update post") | h}"> - <a href="#" class="post-cancel">${_("Cancel")}</a> -</script> - -<script aria-hidden="true" type="text/template" id="thread-response-template"> - <div class="discussion-response"></div> - <a href="#" class="action-show-comments"> - ${u"<%- interpolate('{}', {{num_comments: comments.length}}, true) %>".format(escapejs(_("Show Comments (%(num_comments)s)")))} - <i class="icon fa fa-caret-down"></i> - </a> - <ol class="comments"> - <li class="new-comment"> - % if course is UNDEFINED or has_permission(user, 'create_sub_comment', course.id): - <form class="comment-form" data-id="${'<%- wmdId %>'}"> - <ul class="discussion-errors"></ul> - <label class="sr" for="add-new-comment">${_("Add a comment")}</label> - <div class="comment-body" id="add-new-comment" data-id="${'<%- wmdId %>'}" - data-placeholder="${_('Add a comment') | h}"></div> - <div class="comment-post-control"> - <a class="discussion-submit-comment control-button" href="#">${_("Submit")}</a> - </div> - </form> - % endif - </li> - </ol> -</script> - -<script aria-hidden="true" type="text/template" id="thread-response-show-template"> - <header> - <div class="response-header-content"> - ${'<%= author_display %>'} - <p class="posted-details"> - <span class="timeago" title="${'<%= created_at %>'}">${'<%= created_at %>'}</span> - <% - js_block = u""" - interpolate( - thread.get("thread_type") == "question" ? - (endorsement.username ? "{question_user_fmt_str}" : "{question_anon_fmt_str}") : - (endorsement.username ? "{discussion_user_fmt_str}" : "{discussion_anon_fmt_str}"), - {{ - 'time_ago': '<span class="timeago" title="' + endorsement.time + '">' + endorsement.time + '</span>', - 'user': endorser_display - }}, - true - )""".format( - ## Translators: time_ago is a placeholder for a fuzzy, relative timestamp - ## like "4 hours ago" or "about a month ago" - question_user_fmt_str=escapejs(_("marked as answer %(time_ago)s by %(user)s")), - ## Translators: time_ago is a placeholder for a fuzzy, relative timestamp - ## like "4 hours ago" or "about a month ago" - question_anon_fmt_str=escapejs(_("marked as answer %(time_ago)s")), - ## Translators: time_ago is a placeholder for a fuzzy, relative timestamp - ## like "4 hours ago" or "about a month ago" - discussion_user_fmt_str=escapejs(_("endorsed %(time_ago)s by %(user)s")), - ## Translators: time_ago is a placeholder for a fuzzy, relative timestamp - ## like "4 hours ago" or "about a month ago" - discussion_anon_fmt_str=escapejs(_("endorsed %(time_ago)s")), - ) - %> - ${"<% if (obj.endorsement) { %> - <%="}${js_block}${"%><% } %>"} - </p> - <div class="post-labels"> - <span class="post-label-reported"><i class="icon fa fa-flag"></i>${_("Reported")}</span> - </div> - </div> - <div class="response-header-actions"> - ${"""<%= - _.template( - $('#forum-actions').html(), - { - contentId: cid, - contentType: 'response', - primaryActions: ['vote', thread.get('thread_type') == 'question' ? 'answer' : 'endorse'], - secondaryActions: ['edit', 'delete', 'report'] - } - ) - %>"""} - </div> - </header> - - <div class="response-body">${"<%- body %>"}</div> -</script> - -<script aria-hidden="true" type="text/template" id="thread-response-edit-template"> - <div class="edit-post-form"> - <h1>${_("Editing response")}</h1> - <ul class="edit-post-form-errors"></ul> - <div class="form-row"> - <div class="edit-post-body" name="body" data-id="${'<%- id %>'}">${"<%- body %>"}</div> - </div> - <input type="submit" id="edit-response-submit"class="post-update" value="${_("Update response") | h}"> - <a href="#" class="post-cancel">${_("Cancel")}</a> - </div> -</script> - -<script aria-hidden="true" type="text/template" id="response-comment-show-template"> - <div id="comment_${'<%- id %>'}"> - <div class="response-body">${'<%- body %>'}</div> - ${"""<%= - _.template( - $('#forum-actions').html(), - { - contentId: cid, - contentType: 'comment', - primaryActions: [], - secondaryActions: ['edit', 'delete', 'report'] - } - ) - %>"""} - <% - js_block = u""" - interpolate( - '{}', - {{'time_ago': '<span class=\"timeago\" title=\"' + created_at + '\">' + created_at + '</span>', 'author': author_display}}, - true - )""".format( - ## Translators: 'timeago' is a placeholder for a fuzzy, relative timestamp (see: https://github.com/rmm5t/jquery-timeago) - escapejs(_('posted %(time_ago)s by %(author)s')) - ) - %> - <p class="posted-details"> - ${'<%='}${js_block}${'%>'} - </p> - <div class="post-labels"> - <span class="post-label-reported"><i class="icon fa fa-flag"></i>${_("Reported")}</span> - </div> - </div> -</script> - -<script aria-hidden="true" type="text/template" id="response-comment-edit-template"> - <div class="edit-post-form" id="comment_${'<%- id %>'}"> - <h1>${_("Editing comment")}</h1> - <ul class="edit-comment-form-errors"></ul> - <div class="form-row"> - <div class="edit-comment-body" name="body" data-id="${'<%- id %>'}">${"<%- body %>"}</div> - </div> - <input type="submit" id="edit-comment-submit" class="post-update" value="${_("Update comment") | h}"> - <a href="#" class="post-cancel">${_("Cancel")}</a> - </div> -</script> - -<script aria-hidden="true" type="text/template" id="thread-list-item-template"> - <li data-id="${'<%- id %>'}" class="forum-nav-thread${'<% if (typeof(read) != "undefined" && !read) { %> is-unread<% } %>'}"> - <a href="#" class="forum-nav-thread-link"> - <div class="forum-nav-thread-wrapper-0"> - ${u"""<% - var icon_class, sr_text; - if (thread_type == "discussion") {{ - icon_class = "fa-comments"; - sr_text = "{discussion}"; - }} else if (endorsed) {{ - icon_class = "fa-check-square-o"; - sr_text = "{answered_question}"; - }} else {{ - icon_class = "fa-question"; - sr_text = "{unanswered_question}"; - }} - %>""".format( - ## Translators: This is a label for a Discussion forum thread - discussion=escapejs(_("discussion")), - ## Translators: This is a label for a Question forum thread with a marked answer - answered_question=escapejs(_("answered question")), - ## Translators: This is a label for a Question forum thread without a marked answer - unanswered_question=escapejs(_("unanswered question")) - )} - <span class="sr">${"<%= sr_text %>"}</span> - <i class="icon fa ${"<%= icon_class %>"}"></i> - </div><div class="forum-nav-thread-wrapper-1"> - <span class="forum-nav-thread-title">${"<%- title %>"}</span> - <% - js_block = u""" - var labels = ""; - if (pinned) {{ - labels += '<li class="post-label-pinned"><i class="icon fa fa-thumb-tack"></i>{pinned_text}</li> '; - }} - if (typeof(subscribed) != "undefined" && subscribed) {{ - labels += '<li class="post-label-following"><i class="icon fa fa-star"></i>{following_text}</li> '; - }} - if (staff_authored) {{ - labels += '<li class="post-label-by-staff"><i class="icon fa fa-user"></i>{staff_text}</li> '; - }} - if (community_ta_authored) {{ - labels += '<li class="post-label-by-community-ta"><i class="icon fa fa-user"></i>{community_ta_text}</li> '; - }} - if (labels != "") {{ - print('<ul class="forum-nav-thread-labels">' + labels + '</ul>'); - }} - """.format( - ## Translators: This is a label for a forum thread that has been pinned - pinned_text=escapejs(_("Pinned")), - ## Translators: This is a label for a forum thread that the user is subscribed to - following_text=escapejs(_("Following")), - ## Translators: This is a label for a forum thread that was authored by a member of the course staff - staff_text=escapejs(_("By: Staff")), - ## Translators: This is a label for a forum thread that was authored by a community TA - community_ta_text=escapejs(_("By: Community TA")) - ) - %> - ${"<%"}${js_block}${"%>"} - </div><div class="forum-nav-thread-wrapper-2"> - <% - js_block = u""" - interpolate( - '{}', - {{'span_sr_open': '<span class=\"sr\">', 'span_close': '</span>', 'votes_up_count': votes['up_count']}}, - true - ) - """.format( - ## Translators: 'votes_up_count' is a numerical placeholder for a specific discussion thread; 'span_*' placeholders refer to HTML markup. Please translate the word 'votes'. - escapejs( _('%(votes_up_count)s%(span_sr_open)s votes %(span_close)s')) - ) - %> - <span class="forum-nav-thread-votes-count">+${'<%='}${js_block}${'%>'}</span> - <% - js_block = u""" - var fmt; - // Counts in data do not include the post itself, but the UI should - var data = {{ - 'span_sr_open': '<span class=\"sr\">', - 'span_close': '</span>', - 'unread_comments_count': unread_comments_count + (read ? 0 : 1), - 'comments_count': comments_count + 1 - }}; - if (unread_comments_count > 0) {{ - fmt = '{markup_with_unread}'; - }} else {{ - fmt = '{markup_none_unread}'; - }} - print(interpolate(fmt, data, true)); - """.format( - ## Translators: 'comments_count' and 'unread_comments_count' are numerical placeholders for a specific discussion thread; 'span_*' placeholders refer to HTML markup. Please translate the word 'comments'. - markup_with_unread=escapejs(_('%(comments_count)s %(span_sr_open)scomments (%(unread_comments_count)s unread comments)%(span_close)s')), - ## Translators: 'comments_count' is a numerical placeholder for a specific discussion thread; 'span_*' placeholders refer to HTML markup. Please translate the word 'comments'. - markup_none_unread=escapejs(_('%(comments_count)s %(span_sr_open)scomments %(span_close)s')) - ) - %> - <span class="forum-nav-thread-comments-count ${'<% if (unread_comments_count > 0) { %>is-unread<% } %>'}"> - ${'<%'}${js_block}${'%>'} - </span> - </div> - </a> - </li> -</script> - -<script aria-hidden="true" type="text/template" id="discussion-home"> - <div class="discussion-article blank-slate"> - <section class="home-header"> - <span class="label">${_("DISCUSSION HOME:")}</span> - % if course and course.display_name_with_default: - <h1 class="home-title">${course.display_name_with_default}</h1> - % endif - </section> - - % if settings.FEATURES.get('ENABLE_DISCUSSION_HOME_PANEL'): - <span class="label label-settings"> - ${_("How to use {platform_name} discussions").format(platform_name=settings.PLATFORM_NAME)} - </span> - <table class="home-helpgrid"> - <tr class="helpgrid-row helpgrid-row-navigation"> - <td class="row-title">${_("Find discussions")}</td> - <td class="row-item"> - <i class="icon fa fa-reorder"></i> - <span class="row-description">${_("Focus in on specific topics")}</span> - </td> - <td class="row-item"> - <i class="icon fa fa-search"></i> - <span class="row-description">${_("Search for specific posts ")}</span> - </td> - <td class="row-item"> - <i class="icon fa fa-sort"></i> - <span class="row-description">${_("Sort by date, vote, or comments")}</span> - </td> - </tr> - <tr class="helpgrid-row helpgrid-row-participation"> - <td class="row-title">${_("Engage with posts")}</td> - <td class="row-item"> - <i class="icon fa fa-plus"></i> - <span class="row-description">${_("Upvote posts and good responses")}</span> - </td> - <td class="row-item"> - <i class="icon fa fa-flag"></i> - <span class="row-description">${_("Report Forum Misuse")}</span> - </td> - <td class="row-item"> - <i class="icon fa fa-star"></i> - <span class="row-description">${_("Follow posts for updates")}</span> - </td> - </tr> - <tr class="helpgrid-row helpgrid-row-notification"> - <td class="row-title">${_('Receive updates')}</td> - <td class="row-item-full" colspan="3"> - <label for="email-setting-checkbox"> - <span class="sr">${_("Toggle Notifications Setting")}</span> - <span class="notification-checkbox"> - <input type="checkbox" id="email-setting-checkbox" class="email-setting" name="email-notification"/> - <i class="icon fa fa-envelope"></i> - </span> - </label> - <span class="row-description">${_("Check this box to receive an email digest once a day notifying you about new, unread activity from posts you are following.")}</span> - </td> - </tr> - </table> - % endif - </div> -</script> - -<script aria-hidden="true" type="text/template" id="search-alert-template"> - <div class="search-alert" id="search-alert-${'<%- cid %>'}"> - <div class="search-alert-content"> - <p class="message">${'<%= message %>'}</p> - </div> - - <div class="search-alert-controls"> - <a href="#" class="dismiss control control-dismiss"><i class="icon fa fa-remove"></i></a> - </div> - </div> -</script> - -<script aria-hidden="true" type="text/template" id="new-post-template"> - <form class="forum-new-post-form"> - <ul class="post-errors" style="display: none"></ul> - <div class="forum-new-post-form-wrapper"></div> - ${'<% if (cohort_options) { %>'} - <div class="post-field group-selector-wrapper${'<% if (!is_commentable_cohorted) { %>'} disabled${'<% } %>'}" > - <label class="field-label"> - <span class="field-label-text"> - ## Translators: This labels the selector for which group of students can view a post - ${_("Visible To:")} - </span><select aria-describedby="field_help_visible_to" class="field-input js-group-select" name="group_id" ${'<% if (!is_commentable_cohorted) { %>'}disabled${'<% } %>'}> - <option value="">${_("All Groups")}</option> - ${'<% _.each(cohort_options, function(opt) { %>'} - <option value="${'<%= opt.value %>'}" ${'<% if (opt.selected) { %>selected<% } %>'}>${'<%- opt.text %>'}</option> - ${'<% }); %>'} - </select> - </label><div class="field-help" id="field_help_visible_to"> - ${_("Discussion admins, moderators, and TAs can make their posts visible to all students or specify a single cohort.")} - </div> - </div> - ${'<% } %>'} - <div class="post-field"> - <label class="field-label"> - <span class="sr">${_("Title:")}</span> - <input aria-describedby="field_help_title" type="text" class="field-input js-post-title" name="title" placeholder="${_('Title')}"> - </label><span class="field-help" id="field_help_title"> - ${_("Add a clear and descriptive title to encourage participation.")} - </span> - </div> - <div class="post-field js-post-body editor" name="body" data-placeholder="${_('Enter your question or comment')}"></div> - <div class="post-options"> - <label class="post-option is-enabled"> - <input type="checkbox" name="follow" class="post-option-input js-follow" checked> - <i class="icon fa fa-star"></i>${_("follow this post")} - </label> - ${'<% if (allow_anonymous) { %>'} - <label class="post-option"> - <input type="checkbox" name="anonymous" class="post-option-input js-anon"> - ${_("post anonymously")} - </label> - ${'<% } %>'} - ${'<% if (allow_anonymous_to_peers) { %>'} - <label class="post-option"> - <input type="checkbox" name="anonymous_to_peers" class="post-option-input js-anon-peers"> - ${_("post anonymously to classmates")} - </label> - ${'<% } %>'} - </div> - <div> - <input type="submit" class="submit" value="${_('Add Post')}"> - <a href="#" class="cancel">${_('Cancel')}</a> - </div> - </form> -</script> - -<script aria-hidden="true" type="text/template" id="thread-type-template"> - <div class="post-field"> - <div class="field-label"> - <span class="field-label-text"> - ## Translators: This is the label for a control to - ## select a forum post type - ${_("Post type:")} - </span><fieldset class="field-input"><legend class="sr">${_("Post type:")}</legend> - <input aria-describedby="field_help_post_type" type="radio" name="${"<%= form_id %>"}-post-type" class="post-type-input" id="${"<%= form_id %>"}-post-type-question" value="question"> - <label for="${"<%= form_id %>"}-post-type-question" class="post-type-label"> - <i class="icon fa fa-question"></i> - ## Translators: This is a forum post type - ${_("Question")} - </label> - <input aria-describedby="field_help_post_type" type="radio" name="${"<%= form_id %>"}-post-type" class="post-type-input" id="${"<%= form_id %>"}-post-type-discussion" value="discussion" checked> - <label for="${"<%= form_id %>"}-post-type-discussion" class="post-type-label"> - <i class="icon fa fa-comments"></i> - ## Translators: This is a forum post type - ${_("Discussion")} - </label> - </fieldset> - </div><span class="field-help" id="field_help_post_type"> - ${_("Questions raise issues that need answers. Discussions share ideas and start conversations.")} - </span> - </div> -</script> - -<script aria-hidden="true" type="text/template" id="new-post-menu-entry-template"> - <li role="menuitem" class="topic-menu-item"> - <a href="#" class="topic-title" data-discussion-id="${'<%- id %>'}" data-cohorted="${'<%- is_cohorted %>'}">${'<%- text %>'}</a> - </li> -</script> - -<script aria-hidden="true" type="text/template" id="new-post-menu-category-template"> - <li role="menuitem" class="topic-menu-item"> - <span class="topic-title">${'<%- text %>'}</span> - <ul role="menu" class="topic-submenu">${'<%= entries %>'}</ul> - </li> -</script> - -<script aria-hidden="true" type="text/template" id="topic-template"> - ## Using div here instead of label because we are using a non-native control - <div class="field-label"> - <span class="field-label-text">${_("Topic Area:")}</span><div class="field-input post-topic"> - <a href="#" class="post-topic-button"> - <span class="sr">${_("Discussion topics; current selection is: ")}</span> - <span class="js-selected-topic"></span> - <span class="drop-arrow" aria-hidden="true">â–¾</span> - </a> - <div class="topic-menu-wrapper"> - <label class="topic-filter-label"> - <span class="sr">${_("Filter topics")}</span> - <input aria-describedby="field_help_topic_area" type="text" class="topic-filter-input" placeholder="${_('Filter topics')}"> - </label> - <ul class="topic-menu" role="menu">${'<%= topics_html %>'}</ul> - </div> - </div> - </div><span class="field-help" id="field_help_topic_area"> - ${_("Add your post to a relevant topic to help others find it.")} - </span> -</script> - -<%def name="primaryAction(action_class, icon, sr_label, unchecked_label, checked_label)"> - <script type="text/template" id="forum-action-${action_class}"> - <li class="actions-item"> - <a href="javascript:void(0)" class="action-button action-${action_class}" role="checkbox" aria-checked="false"> - <span class="sr">${sr_label}</span> - <span class="action-label" aria-hidden="true"> - <span class="label-unchecked">${unchecked_label}</span> - <span class="label-checked">${checked_label}</span> - </span> - <span class="action-icon"><i class="icon fa fa-${icon}"></i></span> - </a> - </li> - </script> -</%def> - -${primaryAction("endorse", "check", _("Endorse"), _("Endorse"), _("Unendorse"))} -${primaryAction("answer", "check", _("Mark as Answer"), _("Mark as Answer"), _("Unmark as Answer"))} -${primaryAction("follow", "star", _("Follow"), _("Follow"), _("Unfollow"))} - -<script type="text/template" id="forum-action-vote"> - <li class="actions-item"> - <span aria-hidden="true" class="display-vote" > - <span class="vote-count"></span> - </span> - <a href="#" class="action-button action-vote" role="checkbox" aria-checked="false"> - ## Vote counts are populated by JS - <span class="sr">${_("Vote for this post,")} </span> - <span class="sr js-sr-vote-count"></span> - - <span class="action-label" aria-hidden="true"> - <span class="vote-count"></span> - </span> - - <span class="action-icon" aria-hidden="true"> - <i class="icon fa fa-plus"></i> - </span> - </a> - </li> -</script> - -<%def name="secondaryStateAction(action_class, icon, sr_label, unchecked_label, checked_label)"> - <script type="text/template" id="forum-action-${action_class}"> - <li class="actions-item"> - <a href="javascript:void(0)" class="action-list-item action-${action_class}" role="checkbox" aria-checked="false"> - <span class="sr">${sr_label}</span> - <span class="action-label" aria-hidden="true"> - <span class="label-unchecked">${unchecked_label}</span> - <span class="label-checked">${checked_label}</span> - </span> - <span class="action-icon"> - <i class="icon fa fa-${icon}"></i> - </span> - </a> - </li> - </script> -</%def> - -${secondaryStateAction("report", "flag", _("Report abuse"), _("Report"), _("Unreport"))} -${secondaryStateAction("pin", "thumb-tack", _("Pin"), _("Pin"), _("Unpin"))} -${secondaryStateAction("close", "lock", _("Close"), _("Close"), _("Open"))} - -<%def name="secondaryAction(action_class, icon, label)"> - <script type="text/template" id="forum-action-${action_class}"> - <li class="actions-item"> - <a href="javascript:void(0)" class="action-list-item action-${action_class}" role="button"> - <span class="action-label">${label}</span> - <span class="action-icon"><i class="icon fa fa-${icon}"></i></span> - </a> - </li> - </script> -</%def> - -${secondaryAction("edit", "pencil", _("Edit"))} -${secondaryAction("delete", "remove", _("Delete"))} - -<script type="text/template" id="forum-actions"> - <ul class="${"<%= contentType %>"}-actions-list"> - ${"<% _.each(primaryActions, function(action) { print(_.template($('#forum-action-' + action).html(), {})) }) %>"} - <li class="actions-item is-visible"> - <div class="more-wrapper"> - <a href="javascript:void(0)" class="action-button action-more" role="button" aria-haspopup="true" aria-controls="action-menu-${"<%= contentId %>"}"> - <span class="action-label">${_("More")}</span> - <span class="action-icon"><i class="icon fa fa-ellipsis-h"></i></span> - </a> - <div class="actions-dropdown" id="action-menu-${"<%= contentType %>"}" aria-expanded="false"> - <ul class="actions-dropdown-list"> - ${"<% _.each(secondaryActions, function(action) { print(_.template($('#forum-action-' + action).html(), {})) }) %>"} - </ul> - </div> - </div> - </li> - </ul> -</script> - -<script aria-hidden="true" type="text/template" id="post-user-display-template"> - ${"<% if (username) { %>"} - <a href="${'<%- user_url %>'}" class="username">${'<%- username %>'}</a> - ${"<% if (is_community_ta) { %>"} - <span class="user-label-community-ta">${_("Community TA")}</span> - ${"<% } else if (is_staff) { %>"} - <span class="user-label-staff">${_("Staff")}</span> - ${"<% } %>"} - ${"<% } else { %>"} - ${_('anonymous') | h} - ${"<% } %>"} +## same, but without trailing "-template" in script ID +% for template_name in ['forum-action-endorse', 'forum-action-answer', 'forum-action-follow', 'forum-action-vote', 'forum-action-report', 'forum-action-pin', 'forum-action-close', 'forum-action-edit', 'forum-action-delete', 'forum-actions']: +<script aria-hidden="true" type="text/template" id="${template_name}"> + <%static:include path="common/templates/discussion/${template_name}.underscore" /> </script> +% endfor diff --git a/lms/templates/discussion/index.html b/lms/templates/discussion/index.html index 9586ba3cef4c1c5b4759c0c3f1e8906b571ed1c6..53497a48a2f57c2ea98a042864abac0b4f778841 100644 --- a/lms/templates/discussion/index.html +++ b/lms/templates/discussion/index.html @@ -28,7 +28,10 @@ from django.core.urlresolvers import reverse <section class="discussion container" id="discussion-container" data-roles="${roles}" data-course-id="${course_id | h}" + data-course-name="${course.display_name_with_default}" data-user-info="${user_info}" + data-user-create-comment="${can_create_comment}" + data-user-create-subcomment="${can_create_subcomment}" data-threads="${threads}" data-thread-pages="${thread_pages}" data-content-info="${annotated_content_info}" diff --git a/lms/templates/discussion/user_profile.html b/lms/templates/discussion/user_profile.html index 4a22737194358a64d7c646d197609f1cae79cf43..eb4bd94ade898f87800fd539e64b1385b34ec38a 100644 --- a/lms/templates/discussion/user_profile.html +++ b/lms/templates/discussion/user_profile.html @@ -34,7 +34,7 @@ from django.template.defaultfilters import escapejs </nav> </section> - <section class="course-content container discussion-user-threads" data-course-id="${course.id | h}" data-threads="${threads}" data-user-info="${user_info}" data-page="${page}" data-num-pages="${num_pages}"/> + <section class="course-content container discussion-user-threads" data-course-id="${course.id | h}" data-course-name="${course.display_name_with_default}" data-threads="${threads}" data-user-info="${user_info}" data-page="${page}" data-num-pages="${num_pages}"/> </div> </section>