FEATURE: Allow admins to reply without topic bump

This commit is contained in:
Gerhard Schlager 2018-08-10 02:48:30 +02:00 committed by Sam
parent 6ec92d5845
commit ef4b9f98c1
10 changed files with 182 additions and 10 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -153,6 +153,7 @@
} }
.whisper, .whisper,
.no-bump,
.display-edit-reason { .display-edit-reason {
font-style: italic; font-style: italic;
} }

View File

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

View File

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

View File

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

View File

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

View File

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