Merge pull request #6503 from edx/pull-out-lms-underscore-templates

Pull discussion underscore templates out into individual files
This commit is contained in:
Andy Armstrong
2015-07-28 22:33:34 -04:00
41 changed files with 708 additions and 1166 deletions

View File

@@ -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'

View File

@@ -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>
""")

View File

@@ -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", ->

View File

@@ -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)
)

View File

@@ -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()

View File

@@ -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")

View File

@@ -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

View File

@@ -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())

View File

@@ -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: ->

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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,") %>&nbsp;</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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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') %>
<% } %>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>