From c1a38a6dc746df2e3f12b861746a2c3b91d73137 Mon Sep 17 00:00:00 2001 From: David Baumgold Date: Thu, 8 Jan 2015 14:18:58 -0500 Subject: [PATCH] Pull discussion underscore templates out into individual files No need to go through Mako --- .../lib/xmodule/xmodule/discussion_module.py | 34 +- .../discussion/discussion_spec_helper.coffee | 580 +---------------- .../discussion_thread_list_view_spec.coffee | 2 +- .../view/discussion_thread_view_spec.coffee | 8 +- .../discussion/view/new_post_view_spec.coffee | 6 +- .../static/coffee/src/discussion/main.coffee | 1 + .../views/discussion_thread_list_view.coffee | 2 +- .../views/discussion_thread_view.coffee | 8 +- .../views/thread_response_view.coffee | 5 + .../discussion/discussion-home.underscore | 59 ++ .../discussion/forum-action-answer.underscore | 10 + .../discussion/forum-action-close.underscore | 12 + .../discussion/forum-action-delete.underscore | 6 + .../discussion/forum-action-edit.underscore | 6 + .../forum-action-endorse.underscore | 10 + .../discussion/forum-action-follow.underscore | 10 + .../discussion/forum-action-pin.underscore | 12 + .../discussion/forum-action-report.underscore | 12 + .../discussion/forum-action-vote.underscore | 18 + .../discussion/forum-actions.underscore | 16 + .../new-post-menu-category.underscore | 4 + .../discussion/new-post-menu-entry.underscore | 3 + .../templates/discussion/new-post.underscore | 52 ++ .../discussion/post-user-display.underscore | 10 + .../response-comment-edit.underscore | 9 + .../response-comment-show.underscore | 32 + .../discussion/search-alert.underscore | 9 + .../discussion/thread-edit.underscore | 12 + .../discussion/thread-list-item.underscore | 97 +++ .../thread-response-edit.underscore | 9 + .../thread-response-show.underscore | 59 ++ .../discussion/thread-response.underscore | 27 + .../discussion/thread-type.underscore | 23 + .../templates/discussion/thread.underscore | 38 ++ .../templates/discussion/topic.underscore | 19 + .../tests/test_discussion_module.py | 26 + .../django_comment_client/forum/views.py | 10 + .../discussion/_discussion_module.html | 5 +- .../discussion/_underscore_templates.html | 608 +----------------- lms/templates/discussion/index.html | 3 + lms/templates/discussion/user_profile.html | 2 +- 41 files changed, 708 insertions(+), 1166 deletions(-) create mode 100644 common/static/common/templates/discussion/discussion-home.underscore create mode 100644 common/static/common/templates/discussion/forum-action-answer.underscore create mode 100644 common/static/common/templates/discussion/forum-action-close.underscore create mode 100644 common/static/common/templates/discussion/forum-action-delete.underscore create mode 100644 common/static/common/templates/discussion/forum-action-edit.underscore create mode 100644 common/static/common/templates/discussion/forum-action-endorse.underscore create mode 100644 common/static/common/templates/discussion/forum-action-follow.underscore create mode 100644 common/static/common/templates/discussion/forum-action-pin.underscore create mode 100644 common/static/common/templates/discussion/forum-action-report.underscore create mode 100644 common/static/common/templates/discussion/forum-action-vote.underscore create mode 100644 common/static/common/templates/discussion/forum-actions.underscore create mode 100644 common/static/common/templates/discussion/new-post-menu-category.underscore create mode 100644 common/static/common/templates/discussion/new-post-menu-entry.underscore create mode 100644 common/static/common/templates/discussion/new-post.underscore create mode 100644 common/static/common/templates/discussion/post-user-display.underscore create mode 100644 common/static/common/templates/discussion/response-comment-edit.underscore create mode 100644 common/static/common/templates/discussion/response-comment-show.underscore create mode 100644 common/static/common/templates/discussion/search-alert.underscore create mode 100644 common/static/common/templates/discussion/thread-edit.underscore create mode 100644 common/static/common/templates/discussion/thread-list-item.underscore create mode 100644 common/static/common/templates/discussion/thread-response-edit.underscore create mode 100644 common/static/common/templates/discussion/thread-response-show.underscore create mode 100644 common/static/common/templates/discussion/thread-response.underscore create mode 100644 common/static/common/templates/discussion/thread-type.underscore create mode 100644 common/static/common/templates/discussion/thread.underscore create mode 100644 common/static/common/templates/discussion/topic.underscore create mode 100644 lms/djangoapps/courseware/tests/test_discussion_module.py diff --git a/common/lib/xmodule/xmodule/discussion_module.py b/common/lib/xmodule/xmodule/discussion_module.py index 8ae7f9ad93..89bd27a9c7 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 8d094c49d0..c969f7edd0 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($(' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -""") +
+
+ """) 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 b30efed581..c2dc5f78dd 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 6757d000f2..85fb7ad760 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 e6dd3013a9..6458c3cb3d 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("
  • Title can't be empty
  • ") - 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 67c15d1e34..5c663ffa21 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 9ac93798ec..a01ef013d2 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('' + gettext("Current conversation") + '') 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 ce893c38e7..f369cf6619 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 7418f80e28..a299738d6f 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 0000000000..d06a4205fc --- /dev/null +++ b/common/static/common/templates/discussion/discussion-home.underscore @@ -0,0 +1,59 @@ +
    +
    + <%- gettext("DISCUSSION HOME:") %> + <% if (window.courseName) { %> +

    <%- window.courseName %>

    + <% } %> +
    + + <% if (window.ENABLE_DISCUSSION_HOME_PANEL) { %> + + <%- interpolate(gettext("How to use %(platform_name)s discussions"), {platform_name: window.PLATFORM_NAME}, true) %> + + + + + + + + + + + + + + + + + + +
    <%- gettext("Find discussions") %> + + <%- gettext("Focus in on specific topics") %> + + + <%- gettext("Search for specific posts") %> + + + <%- gettext("Sort by date, vote, or comments") %> +
    <%- gettext("Engage with posts") %> + + <%- gettext("Upvote posts and good responses") %> + + + <%- gettext("Report Forum Misuse") %> + + + <%- gettext("Follow posts for updates") %> +
    <%- gettext('Receive updates') %> + + <%- gettext("Check this box to receive an email digest once a day notifying you about new, unread activity from posts you are following.") %> +
    + <% } %> +
    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 0000000000..6f10e860ed --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-answer.underscore @@ -0,0 +1,10 @@ +
  • + + <%- gettext("Mark as Answer") %> + + + +
  • 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 0000000000..61fd9af230 --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-close.underscore @@ -0,0 +1,12 @@ +
  • + + <%- gettext("Close") %> + + + + + +
  • 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 0000000000..facf308c61 --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-delete.underscore @@ -0,0 +1,6 @@ +
  • + + <%- gettext("Delete") %> + + +
  • 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 0000000000..1a67c97d90 --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-edit.underscore @@ -0,0 +1,6 @@ +
  • + + <%- gettext("Edit") %> + + +
  • 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 0000000000..98d3fd174b --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-endorse.underscore @@ -0,0 +1,10 @@ +
  • + + <%- gettext("Endorse") %> + + + +
  • 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 0000000000..c4adc7aa9f --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-follow.underscore @@ -0,0 +1,10 @@ +
  • + + <%- gettext("Follow") %> + + + +
  • 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 0000000000..a4b3117bcd --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-pin.underscore @@ -0,0 +1,12 @@ +
  • + + <%- gettext("Pin") %> + + + + + +
  • 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 0000000000..dfc52eaa7b --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-report.underscore @@ -0,0 +1,12 @@ +
  • + + <%- gettext("Report abuse") %> + + + + + +
  • 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 0000000000..b837236e21 --- /dev/null +++ b/common/static/common/templates/discussion/forum-action-vote.underscore @@ -0,0 +1,18 @@ +
  • + + + <% // Vote counts are populated by JS %> + <%- gettext("Vote for this post,") %>  + + + + + + +
  • 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 0000000000..9fd9714a3e --- /dev/null +++ b/common/static/common/templates/discussion/forum-actions.underscore @@ -0,0 +1,16 @@ + 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 0000000000..f10d07638f --- /dev/null +++ b/common/static/common/templates/discussion/new-post-menu-category.underscore @@ -0,0 +1,4 @@ + 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 0000000000..bad96e0078 --- /dev/null +++ b/common/static/common/templates/discussion/new-post-menu-entry.underscore @@ -0,0 +1,3 @@ + 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 0000000000..7f155841f7 --- /dev/null +++ b/common/static/common/templates/discussion/new-post.underscore @@ -0,0 +1,52 @@ +
    + +
    + <% if (cohort_options) { %> +
    +
    + <%- gettext("Discussion admins, moderators, and TAs can make their posts visible to all students or specify a single cohort.") %> +
    +
    + <% } %> +
    + + <%- gettext("Add a clear and descriptive title to encourage participation.") %> + +
    +
    +
    + + <% if (allow_anonymous) { %> + + <% } %> + <% if (allow_anonymous_to_peers) { %> + + <% } %> +
    +
    + + <%- gettext('Cancel') %> +
    +
    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 0000000000..515538fce3 --- /dev/null +++ b/common/static/common/templates/discussion/post-user-display.underscore @@ -0,0 +1,10 @@ +<% if (username) { %> +<%- username %> + <% if (is_community_ta) { %> + <%- gettext("Community TA") %> + <% } else if (is_staff) { %> + <%- gettext("Staff") %> + <% } %> +<% } 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 0000000000..5dff8ec978 --- /dev/null +++ b/common/static/common/templates/discussion/response-comment-edit.underscore @@ -0,0 +1,9 @@ +
    +

    <%- gettext("Editing comment") %>

    + +
    +
    <%- body %>
    +
    + "> + <%- gettext("Cancel") %> +
    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 0000000000..5f15338f1e --- /dev/null +++ b/common/static/common/templates/discussion/response-comment-show.underscore @@ -0,0 +1,32 @@ +
    +
    <%- body %>
    + <%= + _.template( + $('#forum-actions').html(), + { + contentId: cid, + contentType: 'comment', + primaryActions: [], + secondaryActions: ['edit', 'delete', 'report'] + } + ) + %> +

    + <% + var time_ago = interpolate( + '%(time)s', + {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 + ) %> +

    + +
    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 0000000000..e75fc65cfd --- /dev/null +++ b/common/static/common/templates/discussion/search-alert.underscore @@ -0,0 +1,9 @@ +
    +
    +

    <%= message %>

    +
    + +
    + +
    +
    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 0000000000..a3d83bea03 --- /dev/null +++ b/common/static/common/templates/discussion/thread-edit.underscore @@ -0,0 +1,12 @@ +

    <%- gettext("Editing post") %>

    + +
    +
    + + +
    +
    +
    <%- body %>
    +
    +"> +<%- gettext("Cancel") %> 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 0000000000..335085fbc3 --- /dev/null +++ b/common/static/common/templates/discussion/thread-list-item.underscore @@ -0,0 +1,97 @@ +
  • is-unread<% } %>"> + +
    + <% + 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"); + } + %> + <%= sr_text %> + +
    + <%- title %> + <% if(typeof(subscribed) === "undefined") { var subscribed = null; } %> + <% if(pinned || subscribed || staff_authored || community_ta_authored) { %> +
      + <% if (pinned) { %> + + <% } %> + <% if (subscribed) { %> + + <% } %> + <% if (staff_authored) { %> + + <% } %> + <% if (community_ta_authored) { %> + + <% } %> +
    + <% } %> +
    + <% + // 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'] + ); + %> + + +<%- interpolate(fmt, { + votes_count: votes['up_count'], + span_start: '', + span_end: '' + }, true) + %> + + + + <% + var fmt; + // Counts in data do not include the post itself, but the UI should + var data = { + 'span_sr_open': '', + 'span_close': '', + '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)); + %> + +
    +
    +
  • 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 0000000000..ce3f4c6535 --- /dev/null +++ b/common/static/common/templates/discussion/thread-response-edit.underscore @@ -0,0 +1,9 @@ +
    +

    <%- gettext("Editing response") %>

    + +
    +
    <%- body %>
    +
    + "> + <%- gettext("Cancel") %> +
    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 0000000000..d43fd2d8aa --- /dev/null +++ b/common/static/common/templates/discussion/thread-response-show.underscore @@ -0,0 +1,59 @@ +
    +
    + <%= author_display %> +

    + <%= created_at %> + <% 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( + '%(time)s', + {time: endorsement.time}, + true + ); + %> + <%= interpolate(fmt, {time_ago: time_ago, user: endorser_display}, true) %> + <% } %> +

    + +
    +
    + <%= + _.template( + $('#forum-actions').html(), + { + contentId: cid, + contentType: 'response', + primaryActions: ['vote', thread.get('thread_type') == 'question' ? 'answer' : 'endorse'], + secondaryActions: ['edit', 'delete', 'report'] + } + ) + %> +
    +
    + +
    <%- body %>
    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 0000000000..7e3aeffde2 --- /dev/null +++ b/common/static/common/templates/discussion/thread-response.underscore @@ -0,0 +1,27 @@ +
    + + <% + var fmts = ngettext( + "Show Comment (%(num_comments)s)", + "Show Comments (%(num_comments)s)", + comments.length + ); + print(interpolate(fmts, {num_comments: comments.length}, true)); + %> + + +
      +
    1. + <% if (create_sub_comment) { %> +
      +
        + +
        +
        + <%- gettext("Submit") %> +
        +
        + <% } %> +
      • +
      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 0000000000..4ba9fa4d70 --- /dev/null +++ b/common/static/common/templates/discussion/thread-type.underscore @@ -0,0 +1,23 @@ +
      +
      + + <% // Translators: This is the label for a control to select a forum post type %> + <%- gettext("Post type:") %> +
      <%- gettext("Post type:") %> + + + + +
      +
      + <%- gettext("Questions raise issues that need answers. Discussions share ideas and start conversations.") %> + +
      diff --git a/common/static/common/templates/discussion/thread.underscore b/common/static/common/templates/discussion/thread.underscore new file mode 100644 index 0000000000..3212105475 --- /dev/null +++ b/common/static/common/templates/discussion/thread.underscore @@ -0,0 +1,38 @@ +
      +
      +
      +
      +
      +
        +
        +
        +
        +
        +
        + +
        +
          +
          + + <% if (can_create_comment) { %> +
          +

          <%- gettext("Post a response:") %>

          +
            +
            +
            + <%- gettext("Submit") %> +
            +
            + <% } %> +
            +
          +
          + <%- gettext("Expand discussion") %> + <%- gettext("Collapse discussion") %> +
          +
          diff --git a/common/static/common/templates/discussion/topic.underscore b/common/static/common/templates/discussion/topic.underscore new file mode 100644 index 0000000000..b59fd8e771 --- /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 %> +
          + <%- gettext("Topic Area:") %>
          + + <%- gettext("Discussion topics; current selection is: ") %> + + + +
          + + +
          +
          +
          + <%- gettext("Add your post to a relevant topic to help others find it.") %> + 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 0000000000..d9eb826514 --- /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 aeb4a7ba75..fd41ea47e7 100644 --- a/lms/djangoapps/django_comment_client/forum/views.py +++ b/lms/djangoapps/django_comment_client/forum/views.py @@ -276,6 +276,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) @@ -375,6 +380,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 8789a7492d..aedf007343 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 %> -
          +
          ${_("Show Discussion")} - % if has_permission(user, 'create_thread', course.id): + % if can_create_thread: ${_("New Post")} % endif
          diff --git a/lms/templates/discussion/_underscore_templates.html b/lms/templates/discussion/_underscore_templates.html index cab9ef174c..35e35785d3 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. - -% 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']: % endfor - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<%def name="primaryAction(action_class, icon, sr_label, unchecked_label, checked_label)"> - - - -${primaryAction("endorse", "check", _("Endorse"), _("Endorse"), _("Unendorse"))} -${primaryAction("answer", "check", _("Mark as Answer"), _("Mark as Answer"), _("Unmark as Answer"))} -${primaryAction("follow", "star", _("Follow"), _("Follow"), _("Unfollow"))} - - - -<%def name="secondaryStateAction(action_class, icon, sr_label, unchecked_label, checked_label)"> - - - -${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)"> - - - -${secondaryAction("edit", "pencil", _("Edit"))} -${secondaryAction("delete", "remove", _("Delete"))} - - - - +% endfor diff --git a/lms/templates/discussion/index.html b/lms/templates/discussion/index.html index 9586ba3cef..53497a48a2 100644 --- a/lms/templates/discussion/index.html +++ b/lms/templates/discussion/index.html @@ -28,7 +28,10 @@ from django.core.urlresolvers import reverse
          -
          +