FEATURE: Allow admins to reply without topic bump
This commit is contained in:
parent
6ec92d5845
commit
ef4b9f98c1
|
@ -53,7 +53,8 @@ function loadDraft(store, opts) {
|
||||||
composerTime: draft.composerTime,
|
composerTime: draft.composerTime,
|
||||||
typingTime: draft.typingTime,
|
typingTime: draft.typingTime,
|
||||||
whisper: draft.whisper,
|
whisper: draft.whisper,
|
||||||
tags: draft.tags
|
tags: draft.tags,
|
||||||
|
noBump: draft.noBump
|
||||||
});
|
});
|
||||||
return composer;
|
return composer;
|
||||||
}
|
}
|
||||||
|
@ -194,6 +195,13 @@ export default Ember.Controller.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@computed("model.noBump")
|
||||||
|
topicBumpText(noBump) {
|
||||||
|
if (noBump) {
|
||||||
|
return I18n.t("composer.no_topic_bump");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
isStaffUser() {
|
isStaffUser() {
|
||||||
const currentUser = this.currentUser;
|
const currentUser = this.currentUser;
|
||||||
|
|
|
@ -41,7 +41,8 @@ const CLOSED = "closed",
|
||||||
composer_open_duration_msecs: "composerTime",
|
composer_open_duration_msecs: "composerTime",
|
||||||
tags: "tags",
|
tags: "tags",
|
||||||
featured_link: "featuredLink",
|
featured_link: "featuredLink",
|
||||||
shared_draft: "sharedDraft"
|
shared_draft: "sharedDraft",
|
||||||
|
no_bump: "noBump"
|
||||||
},
|
},
|
||||||
_edit_topic_serializer = {
|
_edit_topic_serializer = {
|
||||||
title: "topic.title",
|
title: "topic.title",
|
||||||
|
@ -71,6 +72,7 @@ const SAVE_ICONS = {
|
||||||
const Composer = RestModel.extend({
|
const Composer = RestModel.extend({
|
||||||
_categoryId: null,
|
_categoryId: null,
|
||||||
unlistTopic: false,
|
unlistTopic: false,
|
||||||
|
noBump: false,
|
||||||
|
|
||||||
archetypes: function() {
|
archetypes: function() {
|
||||||
return this.site.get("archetypes");
|
return this.site.get("archetypes");
|
||||||
|
@ -608,7 +610,8 @@ const Composer = RestModel.extend({
|
||||||
composerTotalOpened: opts.composerTime,
|
composerTotalOpened: opts.composerTime,
|
||||||
typingTime: opts.typingTime,
|
typingTime: opts.typingTime,
|
||||||
whisper: opts.whisper,
|
whisper: opts.whisper,
|
||||||
tags: opts.tags
|
tags: opts.tags,
|
||||||
|
noBump: opts.noBump
|
||||||
});
|
});
|
||||||
|
|
||||||
if (opts.post) {
|
if (opts.post) {
|
||||||
|
@ -714,7 +717,8 @@ const Composer = RestModel.extend({
|
||||||
typingTime: 0,
|
typingTime: 0,
|
||||||
composerOpened: null,
|
composerOpened: null,
|
||||||
composerTotalOpened: 0,
|
composerTotalOpened: 0,
|
||||||
featuredLink: null
|
featuredLink: null,
|
||||||
|
noBump: false
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -964,7 +968,8 @@ const Composer = RestModel.extend({
|
||||||
usernames: this.get("targetUsernames"),
|
usernames: this.get("targetUsernames"),
|
||||||
composerTime: this.get("composerTime"),
|
composerTime: this.get("composerTime"),
|
||||||
typingTime: this.get("typingTime"),
|
typingTime: this.get("typingTime"),
|
||||||
tags: this.get("tags")
|
tags: this.get("tags"),
|
||||||
|
noBump: this.get("noBump")
|
||||||
};
|
};
|
||||||
|
|
||||||
this.set("draftStatus", I18n.t("composer.saving_draft_tip"));
|
this.set("draftStatus", I18n.t("composer.saving_draft_tip"));
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
{{#if whisperOrUnlistTopicText}}
|
{{#if whisperOrUnlistTopicText}}
|
||||||
<span class='whisper'>({{whisperOrUnlistTopicText}})</span>
|
<span class='whisper'>({{whisperOrUnlistTopicText}})</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#if topicBumpText}}
|
||||||
|
<span class="no-bump">{{topicBumpText}}</span>
|
||||||
|
{{/if}}
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
{{#if canEdit}}
|
{{#if canEdit}}
|
||||||
|
@ -120,6 +123,11 @@
|
||||||
{{d-icon "eye-slash"}}
|
{{d-icon "eye-slash"}}
|
||||||
</span>
|
</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#if topicBumpText}}
|
||||||
|
<span class="no-bump">
|
||||||
|
{{d-icon "anchor"}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -192,6 +192,17 @@ export default DropdownSelectBoxComponent.extend({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentUser = Discourse.User.current();
|
||||||
|
|
||||||
|
if (action === REPLY && currentUser && currentUser.get("staff")) {
|
||||||
|
items.push({
|
||||||
|
name: I18n.t("composer.composer_actions.toggle_topic_bump.label"),
|
||||||
|
description: I18n.t("composer.composer_actions.toggle_topic_bump.desc"),
|
||||||
|
icon: "anchor",
|
||||||
|
id: "toggle_topic_bump"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -234,6 +245,10 @@ export default DropdownSelectBoxComponent.extend({
|
||||||
model.toggleProperty("whisper");
|
model.toggleProperty("whisper");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
toggleTopicBumpSelected(options, model) {
|
||||||
|
model.toggleProperty("noBump");
|
||||||
|
},
|
||||||
|
|
||||||
replyToTopicSelected(options) {
|
replyToTopicSelected(options) {
|
||||||
options.action = REPLY;
|
options.action = REPLY;
|
||||||
options.topic = _topicSnapshot;
|
options.topic = _topicSnapshot;
|
||||||
|
|
|
@ -153,6 +153,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.whisper,
|
.whisper,
|
||||||
|
.no-bump,
|
||||||
.display-edit-reason {
|
.display-edit-reason {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
|
@ -653,6 +653,11 @@ class PostsController < ApplicationController
|
||||||
result[:is_warning] = false
|
result[:is_warning] = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if params[:no_bump] == "true"
|
||||||
|
raise Discourse::InvalidParameters.new(:no_bump) unless guardian.can_skip_bump?
|
||||||
|
result[:no_bump] = true
|
||||||
|
end
|
||||||
|
|
||||||
if params[:shared_draft] == 'true'
|
if params[:shared_draft] == 'true'
|
||||||
raise Discourse::InvalidParameters.new(:shared_draft) unless guardian.can_create_shared_draft?
|
raise Discourse::InvalidParameters.new(:shared_draft) unless guardian.can_create_shared_draft?
|
||||||
|
|
||||||
|
|
|
@ -1318,6 +1318,7 @@ en:
|
||||||
whisper: "whisper"
|
whisper: "whisper"
|
||||||
unlist: "unlisted"
|
unlist: "unlisted"
|
||||||
blockquote_text: "Blockquote"
|
blockquote_text: "Blockquote"
|
||||||
|
no_topic_bump: "(no bump)"
|
||||||
|
|
||||||
add_warning: "This is an official warning."
|
add_warning: "This is an official warning."
|
||||||
toggle_whisper: "Toggle Whisper"
|
toggle_whisper: "Toggle Whisper"
|
||||||
|
@ -1437,6 +1438,9 @@ en:
|
||||||
shared_draft:
|
shared_draft:
|
||||||
label: "Shared Draft"
|
label: "Shared Draft"
|
||||||
desc: "Draft a topic that will only be visible to staff"
|
desc: "Draft a topic that will only be visible to staff"
|
||||||
|
toggle_topic_bump:
|
||||||
|
label: "Toggle topic bump"
|
||||||
|
desc: "Reply without changing the topic's bump date"
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
tooltip:
|
tooltip:
|
||||||
|
|
|
@ -255,4 +255,8 @@ module PostGuardian
|
||||||
def can_unhide?(post)
|
def can_unhide?(post)
|
||||||
post.try(:hidden) && is_staff?
|
post.try(:hidden) && is_staff?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_skip_bump?
|
||||||
|
is_staff?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1055,6 +1055,67 @@ describe PostsController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "topic bump" do
|
||||||
|
shared_examples "it works" do
|
||||||
|
let(:original_bumped_at) { 1.day.ago }
|
||||||
|
let!(:topic) { Fabricate(:topic, bumped_at: original_bumped_at) }
|
||||||
|
|
||||||
|
it "should be able to skip topic bumping" do
|
||||||
|
post "/posts.json", params: {
|
||||||
|
raw: 'this is the test content',
|
||||||
|
topic_id: topic.id,
|
||||||
|
no_bump: true
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(topic.reload.bumped_at).to be_within_one_second_of(original_bumped_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should be able to post with topic bumping" do
|
||||||
|
post "/posts.json", params: {
|
||||||
|
raw: 'this is the test content',
|
||||||
|
topic_id: topic.id
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
expect(topic.reload.bumped_at).to eq(topic.posts.last.created_at)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "admins" do
|
||||||
|
before do
|
||||||
|
sign_in(Fabricate(:admin))
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples "it works"
|
||||||
|
end
|
||||||
|
|
||||||
|
context "moderators" do
|
||||||
|
before do
|
||||||
|
sign_in(Fabricate(:moderator))
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples "it works"
|
||||||
|
end
|
||||||
|
|
||||||
|
context "users" do
|
||||||
|
let(:topic) { Fabricate(:topic) }
|
||||||
|
|
||||||
|
[:user, :trust_level_4].each do |user|
|
||||||
|
it "will raise an error for #{user}" do
|
||||||
|
sign_in(Fabricate(user))
|
||||||
|
post "/posts.json", params: {
|
||||||
|
raw: 'this is the test content',
|
||||||
|
topic_id: topic.id,
|
||||||
|
no_bump: true
|
||||||
|
}
|
||||||
|
expect(response.status).to eq(400)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#revisions' do
|
describe '#revisions' do
|
||||||
|
@ -1524,5 +1585,4 @@ describe PostsController do
|
||||||
expect(public_post).not_to be_locked
|
expect(public_post).not_to be_locked
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { acceptance } from "helpers/qunit-helpers";
|
import { acceptance, replaceCurrentUser } from "helpers/qunit-helpers";
|
||||||
import { _clearSnapshots } from "select-kit/components/composer-actions";
|
import { _clearSnapshots } from "select-kit/components/composer-actions";
|
||||||
|
|
||||||
acceptance("Composer Actions", {
|
acceptance("Composer Actions", {
|
||||||
|
@ -25,7 +25,8 @@ QUnit.test("replying to post", async assert => {
|
||||||
);
|
);
|
||||||
assert.equal(composerActions.rowByIndex(2).value(), "reply_to_topic");
|
assert.equal(composerActions.rowByIndex(2).value(), "reply_to_topic");
|
||||||
assert.equal(composerActions.rowByIndex(3).value(), "toggle_whisper");
|
assert.equal(composerActions.rowByIndex(3).value(), "toggle_whisper");
|
||||||
assert.equal(composerActions.rowByIndex(4).value(), undefined);
|
assert.equal(composerActions.rowByIndex(4).value(), "toggle_topic_bump");
|
||||||
|
assert.equal(composerActions.rowByIndex(5).value(), undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test("replying to post - reply_as_private_message", async assert => {
|
QUnit.test("replying to post - reply_as_private_message", async assert => {
|
||||||
|
@ -179,7 +180,8 @@ QUnit.test("interactions", async assert => {
|
||||||
"reply_as_private_message"
|
"reply_as_private_message"
|
||||||
);
|
);
|
||||||
assert.equal(composerActions.rowByIndex(3).value(), "toggle_whisper");
|
assert.equal(composerActions.rowByIndex(3).value(), "toggle_whisper");
|
||||||
assert.equal(composerActions.rows().length, 4);
|
assert.equal(composerActions.rowByIndex(4).value(), "toggle_topic_bump");
|
||||||
|
assert.equal(composerActions.rows().length, 5);
|
||||||
|
|
||||||
await composerActions.selectRowByValue("reply_to_post");
|
await composerActions.selectRowByValue("reply_to_post");
|
||||||
await composerActions.expand();
|
await composerActions.expand();
|
||||||
|
@ -199,7 +201,8 @@ QUnit.test("interactions", async assert => {
|
||||||
);
|
);
|
||||||
assert.equal(composerActions.rowByIndex(2).value(), "reply_to_topic");
|
assert.equal(composerActions.rowByIndex(2).value(), "reply_to_topic");
|
||||||
assert.equal(composerActions.rowByIndex(3).value(), "toggle_whisper");
|
assert.equal(composerActions.rowByIndex(3).value(), "toggle_whisper");
|
||||||
assert.equal(composerActions.rows().length, 4);
|
assert.equal(composerActions.rowByIndex(4).value(), "toggle_topic_bump");
|
||||||
|
assert.equal(composerActions.rows().length, 5);
|
||||||
|
|
||||||
await composerActions.selectRowByValue("reply_as_new_topic");
|
await composerActions.selectRowByValue("reply_as_new_topic");
|
||||||
await composerActions.expand();
|
await composerActions.expand();
|
||||||
|
@ -243,3 +246,62 @@ QUnit.test("interactions", async assert => {
|
||||||
assert.equal(composerActions.rowByIndex(2).value(), "reply_to_topic");
|
assert.equal(composerActions.rowByIndex(2).value(), "reply_to_topic");
|
||||||
assert.equal(composerActions.rows().length, 3);
|
assert.equal(composerActions.rows().length, 3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
QUnit.test("replying to post - toggle_topic_bump", async assert => {
|
||||||
|
const composerActions = selectKit(".composer-actions");
|
||||||
|
|
||||||
|
await visit("/t/internationalization-localization/280");
|
||||||
|
await click("article#post_3 button.reply");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
find(".composer-fields .no-bump").length === 0,
|
||||||
|
"no-bump text is not visible"
|
||||||
|
);
|
||||||
|
|
||||||
|
await composerActions.expand();
|
||||||
|
await composerActions.selectRowByValue("toggle_topic_bump");
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
find(".composer-fields .no-bump").text(),
|
||||||
|
I18n.t("composer.no_topic_bump"),
|
||||||
|
"no-bump text is visible"
|
||||||
|
);
|
||||||
|
|
||||||
|
await composerActions.expand();
|
||||||
|
await composerActions.selectRowByValue("toggle_topic_bump");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
find(".composer-fields .no-bump").length === 0,
|
||||||
|
"no-bump text is not visible"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("replying to post as staff", async assert => {
|
||||||
|
const composerActions = selectKit(".composer-actions");
|
||||||
|
|
||||||
|
replaceCurrentUser({ staff: true, admin: false });
|
||||||
|
await visit("/t/internationalization-localization/280");
|
||||||
|
await click("article#post_3 button.reply");
|
||||||
|
await composerActions.expand();
|
||||||
|
|
||||||
|
assert.equal(composerActions.rows().length, 5);
|
||||||
|
assert.equal(composerActions.rowByIndex(4).value(), "toggle_topic_bump");
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("replying to post as regular user", async assert => {
|
||||||
|
const composerActions = selectKit(".composer-actions");
|
||||||
|
|
||||||
|
replaceCurrentUser({ staff: false, admin: false });
|
||||||
|
await visit("/t/internationalization-localization/280");
|
||||||
|
await click("article#post_3 button.reply");
|
||||||
|
await composerActions.expand();
|
||||||
|
|
||||||
|
assert.equal(composerActions.rows().length, 3);
|
||||||
|
Array.from(composerActions.rows()).forEach(row => {
|
||||||
|
assert.notEqual(
|
||||||
|
row.value,
|
||||||
|
"toggle_topic_bump",
|
||||||
|
"toggle button is not visible"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue