Add repsonse pagination to inline discussions
This commit is contained in:
@@ -0,0 +1,89 @@
|
|||||||
|
describe "DiscussionThreadInlineView", ->
|
||||||
|
beforeEach ->
|
||||||
|
setFixtures(
|
||||||
|
"""
|
||||||
|
<script type="text/template" id="_inline_thread">
|
||||||
|
<article class="discussion-article">
|
||||||
|
<div class="non-cohorted-indicator"/>
|
||||||
|
<div class="post-extended-content">
|
||||||
|
<div class="response-count"/>
|
||||||
|
<ol class="responses"/>
|
||||||
|
<div class="response-pagination"/>
|
||||||
|
</div>
|
||||||
|
<div class="post-tools">
|
||||||
|
<a href="javascript:void(0)" class="expand-post">Expand</a>
|
||||||
|
<a href="javascript:void(0)" class="collapse-post">Collapse</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</script>
|
||||||
|
<script type="text/template" id="_inline_thread_cohorted">
|
||||||
|
<article class="discussion-article">
|
||||||
|
<div class="cohorted-indicator"/>
|
||||||
|
<div class="post-extended-content">
|
||||||
|
<div class="response-count"/>
|
||||||
|
<ol class="responses"/>
|
||||||
|
<div class="response-pagination"/>
|
||||||
|
</div>
|
||||||
|
<div class="post-tools">
|
||||||
|
<a href="javascript:void(0)" class="expand-post">Expand</a>
|
||||||
|
<a href="javascript:void(0)" class="collapse-post">Collapse</a>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</script>
|
||||||
|
<div class="thread-fixture"/>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
@threadData = {
|
||||||
|
id: "dummy",
|
||||||
|
body: "dummy body",
|
||||||
|
abuse_flaggers: [],
|
||||||
|
votes: {up_count: "42"}
|
||||||
|
}
|
||||||
|
@thread = new Thread(@threadData)
|
||||||
|
@view = new DiscussionThreadInlineView({ model: @thread })
|
||||||
|
@view.setElement($(".thread-fixture"))
|
||||||
|
spyOn($, "ajax")
|
||||||
|
# Avoid unnecessary boilerplate
|
||||||
|
spyOn(@view.showView, "render")
|
||||||
|
spyOn(@view, "makeWmdEditor")
|
||||||
|
spyOn(DiscussionThreadView.prototype, "renderResponse")
|
||||||
|
|
||||||
|
assertContentVisible = (view, selector, visible) ->
|
||||||
|
content = view.$el.find(selector)
|
||||||
|
expect(content.length).toEqual(1)
|
||||||
|
expect(content.is(":visible")).toEqual(visible)
|
||||||
|
|
||||||
|
assertExpandedContentVisible = (view, expanded) ->
|
||||||
|
expect(view.$el.hasClass("expanded")).toEqual(expanded)
|
||||||
|
assertContentVisible(view, ".post-extended-content", expanded)
|
||||||
|
assertContentVisible(view, ".expand-post", not expanded)
|
||||||
|
assertContentVisible(view, ".collapse-post", expanded)
|
||||||
|
|
||||||
|
describe "render", ->
|
||||||
|
it "uses the cohorted template if cohorted", ->
|
||||||
|
@view.model.set({group_id: 1})
|
||||||
|
@view.render()
|
||||||
|
expect(@view.$el.find(".cohorted-indicator").length).toEqual(1)
|
||||||
|
|
||||||
|
it "uses the non-cohorted template if not cohorted", ->
|
||||||
|
@view.render()
|
||||||
|
expect(@view.$el.find(".non-cohorted-indicator").length).toEqual(1)
|
||||||
|
|
||||||
|
it "shows content that should be visible when collapsed", ->
|
||||||
|
@view.render()
|
||||||
|
assertExpandedContentVisible(@view, false)
|
||||||
|
|
||||||
|
it "does not render any responses by default", ->
|
||||||
|
@view.render()
|
||||||
|
expect($.ajax).not.toHaveBeenCalled()
|
||||||
|
expect(@view.$el.find(".responses li").length).toEqual(0)
|
||||||
|
|
||||||
|
describe "expand/collapse", ->
|
||||||
|
it "shows/hides appropriate content", ->
|
||||||
|
DiscussionViewSpecHelper.setNextResponseContent({resp_total: 0, children: []})
|
||||||
|
@view.render()
|
||||||
|
@view.expandPost()
|
||||||
|
assertExpandedContentVisible(@view, true)
|
||||||
|
@view.collapsePost()
|
||||||
|
assertExpandedContentVisible(@view, false)
|
||||||
@@ -27,16 +27,8 @@ describe "DiscussionThreadView", ->
|
|||||||
spyOn(DiscussionThreadView.prototype, "renderResponse")
|
spyOn(DiscussionThreadView.prototype, "renderResponse")
|
||||||
|
|
||||||
describe "response count and pagination", ->
|
describe "response count and pagination", ->
|
||||||
|
|
||||||
setNextResponseContent = (content) ->
|
|
||||||
$.ajax.andCallFake(
|
|
||||||
(params) =>
|
|
||||||
params.success({"content": content})
|
|
||||||
{always: ->}
|
|
||||||
)
|
|
||||||
|
|
||||||
renderWithContent = (view, content) ->
|
renderWithContent = (view, content) ->
|
||||||
setNextResponseContent(content)
|
DiscussionViewSpecHelper.setNextResponseContent(content)
|
||||||
view.render()
|
view.render()
|
||||||
jasmine.Clock.tick(100)
|
jasmine.Clock.tick(100)
|
||||||
|
|
||||||
@@ -73,16 +65,16 @@ describe "DiscussionThreadView", ->
|
|||||||
assertRenderedCorrectly(@view, "5 responses", "Showing first response", "Load all responses")
|
assertRenderedCorrectly(@view, "5 responses", "Showing first response", "Load all responses")
|
||||||
|
|
||||||
it "correctly re-render when all threads have loaded", ->
|
it "correctly re-render when all threads have loaded", ->
|
||||||
setNextResponseContent({resp_total: 5, children: [{}, {}, {}, {}]})
|
DiscussionViewSpecHelper.setNextResponseContent({resp_total: 5, children: [{}, {}, {}, {}]})
|
||||||
@view.$el.find(".load-response-button").click()
|
@view.$el.find(".load-response-button").click()
|
||||||
assertRenderedCorrectly(@view, "5 responses", "Showing all responses", null)
|
assertRenderedCorrectly(@view, "5 responses", "Showing all responses", null)
|
||||||
|
|
||||||
it "correctly re-render when one page remains", ->
|
it "correctly re-render when one page remains", ->
|
||||||
setNextResponseContent({resp_total: 42, children: [{}, {}]})
|
DiscussionViewSpecHelper.setNextResponseContent({resp_total: 42, children: [{}, {}]})
|
||||||
@view.$el.find(".load-response-button").click()
|
@view.$el.find(".load-response-button").click()
|
||||||
assertRenderedCorrectly(@view, "42 responses", "Showing first 3 responses", "Load all responses")
|
assertRenderedCorrectly(@view, "42 responses", "Showing first 3 responses", "Load all responses")
|
||||||
|
|
||||||
it "correctly re-render when multiple pages remain", ->
|
it "correctly re-render when multiple pages remain", ->
|
||||||
setNextResponseContent({resp_total: 111, children: [{}, {}]})
|
DiscussionViewSpecHelper.setNextResponseContent({resp_total: 111, children: [{}, {}]})
|
||||||
@view.$el.find(".load-response-button").click()
|
@view.$el.find(".load-response-button").click()
|
||||||
assertRenderedCorrectly(@view, "111 responses", "Showing first 3 responses", "Load next 100 responses")
|
assertRenderedCorrectly(@view, "111 responses", "Showing first 3 responses", "Load next 100 responses")
|
||||||
|
|||||||
@@ -110,6 +110,12 @@ class @DiscussionViewSpecHelper
|
|||||||
button.trigger($.Event("keydown", {which: 32}))
|
button.trigger($.Event("keydown", {which: 32}))
|
||||||
expect(spy).toHaveBeenCalled()
|
expect(spy).toHaveBeenCalled()
|
||||||
|
|
||||||
|
|
||||||
@checkVoteButtonEvents = (view) ->
|
@checkVoteButtonEvents = (view) ->
|
||||||
@checkButtonEvents(view, "toggleVote", ".vote-btn")
|
@checkButtonEvents(view, "toggleVote", ".vote-btn")
|
||||||
|
|
||||||
|
@setNextResponseContent = (content) ->
|
||||||
|
$.ajax.andCallFake(
|
||||||
|
(params) =>
|
||||||
|
params.success({"content": content})
|
||||||
|
{always: ->}
|
||||||
|
)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ if Backbone?
|
|||||||
|
|
||||||
render: ->
|
render: ->
|
||||||
@$el.html(@renderTemplate())
|
@$el.html(@renderTemplate())
|
||||||
|
@initLocal()
|
||||||
@delegateEvents()
|
@delegateEvents()
|
||||||
|
|
||||||
@renderShowView()
|
@renderShowView()
|
||||||
@@ -33,10 +34,7 @@ if Backbone?
|
|||||||
@responses.on("add", @renderResponse)
|
@responses.on("add", @renderResponse)
|
||||||
# Without a delay, jQuery doesn't add the loading extension defined in
|
# Without a delay, jQuery doesn't add the loading extension defined in
|
||||||
# utils.coffee before safeAjax is invoked, which results in an error
|
# utils.coffee before safeAjax is invoked, which results in an error
|
||||||
setTimeout(
|
setTimeout((=> @loadInitialResponses()), 100)
|
||||||
=> @loadResponses(INITIAL_RESPONSE_PAGE_SIZE, @$el.find(".responses"), true),
|
|
||||||
100
|
|
||||||
)
|
|
||||||
@
|
@
|
||||||
|
|
||||||
cleanup: ->
|
cleanup: ->
|
||||||
@@ -71,6 +69,9 @@ if Backbone?
|
|||||||
gettext("We had some trouble loading more responses. Please try again.")
|
gettext("We had some trouble loading more responses. Please try again.")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
loadInitialResponses: () ->
|
||||||
|
@loadResponses(INITIAL_RESPONSE_PAGE_SIZE, @$el.find(".responses"), true)
|
||||||
|
|
||||||
renderResponseCountAndPagination: (responseTotal) =>
|
renderResponseCountAndPagination: (responseTotal) =>
|
||||||
@$el.find(".response-count").html(
|
@$el.find(".response-count").html(
|
||||||
interpolate(
|
interpolate(
|
||||||
@@ -226,6 +227,9 @@ if Backbone?
|
|||||||
renderEditView: () ->
|
renderEditView: () ->
|
||||||
@renderSubView(@editView)
|
@renderSubView(@editView)
|
||||||
|
|
||||||
|
getShowViewClass: () ->
|
||||||
|
return DiscussionThreadShowView
|
||||||
|
|
||||||
createShowView: () ->
|
createShowView: () ->
|
||||||
|
|
||||||
if @editView?
|
if @editView?
|
||||||
@@ -233,7 +237,8 @@ if Backbone?
|
|||||||
@editView.$el.empty()
|
@editView.$el.empty()
|
||||||
@editView = null
|
@editView = null
|
||||||
|
|
||||||
@showView = new DiscussionThreadShowView(model: @model)
|
showViewClass = @getShowViewClass()
|
||||||
|
@showView = new showViewClass(model: @model)
|
||||||
@showView.bind "thread:_delete", @_delete
|
@showView.bind "thread:_delete", @_delete
|
||||||
@showView.bind "thread:edit", @edit
|
@showView.bind "thread:edit", @edit
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ if Backbone?
|
|||||||
@$local = @$el
|
@$local = @$el
|
||||||
@$delegateElement = @$local
|
@$delegateElement = @$local
|
||||||
|
|
||||||
render: ->
|
renderTemplate: () ->
|
||||||
if @model.has('group_id')
|
if @model.has('group_id')
|
||||||
@template = DiscussionUtil.getTemplate("_inline_thread_cohorted")
|
@template = DiscussionUtil.getTemplate("_inline_thread_cohorted")
|
||||||
else
|
else
|
||||||
@@ -25,107 +25,43 @@ if Backbone?
|
|||||||
if not @model.has('abbreviatedBody')
|
if not @model.has('abbreviatedBody')
|
||||||
@abbreviateBody()
|
@abbreviateBody()
|
||||||
params = @model.toJSON()
|
params = @model.toJSON()
|
||||||
@$el.html(Mustache.render(@template, params))
|
Mustache.render(@template, params)
|
||||||
#@createShowView()
|
|
||||||
|
|
||||||
@initLocal()
|
render: () ->
|
||||||
@delegateEvents()
|
super()
|
||||||
@renderShowView()
|
|
||||||
@renderAttrs()
|
|
||||||
|
|
||||||
@$("span.timeago").timeago()
|
|
||||||
@$el.find('.post-extended-content').hide()
|
@$el.find('.post-extended-content').hide()
|
||||||
|
@$el.find('.collapse-post').hide()
|
||||||
|
|
||||||
|
getShowViewClass: () ->
|
||||||
|
return DiscussionThreadInlineShowView
|
||||||
|
|
||||||
|
loadInitialResponses: () ->
|
||||||
if @expanded
|
if @expanded
|
||||||
@makeWmdEditor "reply-body"
|
super()
|
||||||
@renderAddResponseButton()
|
|
||||||
@renderResponses()
|
|
||||||
@
|
|
||||||
createShowView: () ->
|
|
||||||
|
|
||||||
if @editView?
|
|
||||||
@editView.undelegateEvents()
|
|
||||||
@editView.$el.empty()
|
|
||||||
@editView = null
|
|
||||||
@showView = new DiscussionThreadInlineShowView(model: @model)
|
|
||||||
@showView.bind "thread:_delete", @_delete
|
|
||||||
@showView.bind "thread:edit", @edit
|
|
||||||
|
|
||||||
renderResponses: ->
|
|
||||||
#TODO: threadview
|
|
||||||
DiscussionUtil.safeAjax
|
|
||||||
url: "/courses/#{$$course_id}/discussion/forum/#{@model.get('commentable_id')}/threads/#{@model.id}"
|
|
||||||
$loading: @$el
|
|
||||||
success: (data, textStatus, xhr) =>
|
|
||||||
# @$el.find(".loading").remove()
|
|
||||||
Content.loadContentInfos(data['annotated_content_info'])
|
|
||||||
comments = new Comments(data['content']['children'])
|
|
||||||
comments.each @renderResponse
|
|
||||||
@trigger "thread:responses:rendered"
|
|
||||||
@$('.loading').remove()
|
|
||||||
|
|
||||||
|
|
||||||
toggleClosed: (event) ->
|
|
||||||
#TODO: showview
|
|
||||||
$elem = $(event.target)
|
|
||||||
url = @model.urlFor('close')
|
|
||||||
closed = @model.get('closed')
|
|
||||||
data = { closed: not closed }
|
|
||||||
DiscussionUtil.safeAjax
|
|
||||||
$elem: $elem
|
|
||||||
url: url
|
|
||||||
data: data
|
|
||||||
type: "POST"
|
|
||||||
success: (response, textStatus) =>
|
|
||||||
@model.set('closed', not closed)
|
|
||||||
@model.set('ability', response.ability)
|
|
||||||
|
|
||||||
toggleEndorse: (event) ->
|
|
||||||
#TODO: showview
|
|
||||||
$elem = $(event.target)
|
|
||||||
url = @model.urlFor('endorse')
|
|
||||||
endorsed = @model.get('endorsed')
|
|
||||||
data = { endorsed: not endorsed }
|
|
||||||
DiscussionUtil.safeAjax
|
|
||||||
$elem: $elem
|
|
||||||
url: url
|
|
||||||
data: data
|
|
||||||
type: "POST"
|
|
||||||
success: (response, textStatus) =>
|
|
||||||
@model.set('endorsed', not endorsed)
|
|
||||||
|
|
||||||
abbreviateBody: ->
|
abbreviateBody: ->
|
||||||
abbreviated = DiscussionUtil.abbreviateString @model.get('body'), 140
|
abbreviated = DiscussionUtil.abbreviateString @model.get('body'), 140
|
||||||
@model.set('abbreviatedBody', abbreviated)
|
@model.set('abbreviatedBody', abbreviated)
|
||||||
|
|
||||||
expandPost: (event) =>
|
expandPost: (event) =>
|
||||||
@expanded = true
|
|
||||||
@$el.addClass('expanded')
|
@$el.addClass('expanded')
|
||||||
@$el.find('.post-body').html(@model.get('body'))
|
|
||||||
@showView.convertMath()
|
|
||||||
@$el.find('.expand-post').css('display', 'none')
|
@$el.find('.expand-post').css('display', 'none')
|
||||||
@$el.find('.collapse-post').css('display', 'block')
|
@$el.find('.collapse-post').css('display', 'block')
|
||||||
@$el.find('.post-extended-content').show()
|
@$el.find('.post-extended-content').show()
|
||||||
@makeWmdEditor "reply-body"
|
if not @expanded
|
||||||
@renderAttrs()
|
@expanded = true
|
||||||
if @$el.find('.loading').length
|
@loadInitialResponses()
|
||||||
@renderAddResponseButton()
|
|
||||||
@renderResponses()
|
|
||||||
|
|
||||||
collapsePost: (event) ->
|
collapsePost: (event) ->
|
||||||
curScroll = $(window).scrollTop()
|
curScroll = $(window).scrollTop()
|
||||||
postTop = @$el.offset().top
|
postTop = @$el.offset().top
|
||||||
if postTop < curScroll
|
if postTop < curScroll
|
||||||
$('html, body').animate({scrollTop: postTop})
|
$('html, body').animate({scrollTop: postTop})
|
||||||
@expanded = false
|
|
||||||
@$el.removeClass('expanded')
|
@$el.removeClass('expanded')
|
||||||
@$el.find('.post-body').html(@model.get('abbreviatedBody'))
|
@$el.find('.expand-post').css('display', 'block')
|
||||||
@showView.convertMath()
|
|
||||||
@$el.find('.collapse-post').css('display', 'none')
|
@$el.find('.collapse-post').css('display', 'none')
|
||||||
@$el.find('.post-extended-content').hide()
|
@$el.find('.post-extended-content').hide()
|
||||||
@$el.find('.expand-post').css('display', 'block')
|
|
||||||
|
|
||||||
createEditView: () ->
|
createEditView: () ->
|
||||||
super()
|
super()
|
||||||
@editView.bind "thread:update", @expandPost
|
|
||||||
@editView.bind "thread:update", @abbreviateBody
|
@editView.bind "thread:update", @abbreviateBody
|
||||||
@editView.bind "thread:cancel_edit", @expandPost
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ lib_paths:
|
|||||||
- js/vendor/jquery.min.js
|
- js/vendor/jquery.min.js
|
||||||
- js/vendor/jasmine-jquery.js
|
- js/vendor/jasmine-jquery.js
|
||||||
- js/vendor/jasmine-imagediff.js
|
- js/vendor/jasmine-imagediff.js
|
||||||
|
- js/vendor/mustache.js
|
||||||
- js/vendor/underscore-min.js
|
- js/vendor/underscore-min.js
|
||||||
- js/vendor/backbone-min.js
|
- js/vendor/backbone-min.js
|
||||||
- js/vendor/jquery.timeago.js
|
- js/vendor/jquery.timeago.js
|
||||||
|
|||||||
@@ -2069,10 +2069,6 @@ body.discussion {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
|
|
||||||
&.collapse-post {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
color: $link-color;
|
color: $link-color;
|
||||||
margin-right: ($baseline*0.25);
|
margin-right: ($baseline*0.25);
|
||||||
|
|||||||
@@ -3,23 +3,25 @@
|
|||||||
<article class="discussion-article" data-id="{{id}}">
|
<article class="discussion-article" data-id="{{id}}">
|
||||||
<div class="thread-wrapper">
|
<div class="thread-wrapper">
|
||||||
<div class="thread-content-wrapper"></div>
|
<div class="thread-content-wrapper"></div>
|
||||||
<div class="add-response post-extended-content">
|
<div class="post-extended-content">
|
||||||
<button class="button add-response-btn">
|
<div class="response-count"/>
|
||||||
<i class="icon icon-reply"></i>
|
<div class="add-response">
|
||||||
<span class="add-response-btn-text">${_('Add A Response')}</span>
|
<button class="button add-response-btn">
|
||||||
</button>
|
<i class="icon icon-reply"></i>
|
||||||
|
<span class="add-response-btn-text">${_('Add A Response')}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ol class="responses"/>
|
||||||
|
<div class="response-pagination"/>
|
||||||
|
<form class="local 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>
|
||||||
<ol class="responses post-extended-content">
|
|
||||||
<li class="loading"><div class="loading-animation"><span class="sr">${_("Loading content")}</span></div></li>
|
|
||||||
</ol>
|
|
||||||
<form class="local discussion-reply-new post-extended-content" 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="local post-tools">
|
<div class="local post-tools">
|
||||||
|
|||||||
@@ -4,23 +4,25 @@
|
|||||||
<div class="thread-wrapper">
|
<div class="thread-wrapper">
|
||||||
<div class="group-visibility-label">{{group_string}}</div>
|
<div class="group-visibility-label">{{group_string}}</div>
|
||||||
<div class="thread-content-wrapper"></div>
|
<div class="thread-content-wrapper"></div>
|
||||||
<div class="add-response post-extended-content">
|
<div class="post-extended-content">
|
||||||
<button class="button add-response-btn">
|
<div class="response-count"/>
|
||||||
<i class="icon icon-reply"></i>
|
<div class="add-response">
|
||||||
<span class="add-response-btn-text">${_('Add A Response')}</span>
|
<button class="button add-response-btn">
|
||||||
</button>
|
<i class="icon icon-reply"></i>
|
||||||
|
<span class="add-response-btn-text">${_('Add A Response')}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ol class="responses"/>
|
||||||
|
<div class="response-pagination"/>
|
||||||
|
<form class="local 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>
|
||||||
<ol class="responses post-extended-content">
|
|
||||||
<li class="loading"><div class="loading-animation"><span class="sr">${_("Loading content")}</span></div></li>
|
|
||||||
</ol>
|
|
||||||
<form class="local discussion-reply-new post-extended-content" 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="local post-tools">
|
<div class="local post-tools">
|
||||||
|
|||||||
Reference in New Issue
Block a user