discourse/spec/lib/new_post_manager_spec.rb
Osama Sayegh e40c4bb7f9
FIX: Validate category tag restrictions before sending new topics to review (#16292)
Tags (and tag groups) can be configured so that they can only be used in specific categories and (optionally) restrict topics in these categories to be able to add/use only these tags. These restrictions work as expected when a topic is created without going through the review queue; however, if the topic has to be reviewed by a moderator then these restrictions currently aren't checked before the topic is sent to the review queue, but they're checked later when a moderator tries to approve the topic. This is because if a user manages to submit a topic that doesn't meet the restrictions, moderators won't be able to approve and it'll be stuck in the review queue.

This PR prevents topics that don't meet the tags requirements from being sent to the review queue and shows the poster an error message that indicates which tags that cannot be used.

Internal ticket: t60562.
2022-03-28 21:25:26 +03:00

793 lines
24 KiB
Ruby

# frozen_string_literal: true
require 'new_post_manager'
describe NewPostManager do
fab!(:user) { Fabricate(:user) }
fab!(:topic) { Fabricate(:topic) }
context "default action" do
it "creates the post by default" do
manager = NewPostManager.new(user, raw: 'this is a new post', topic_id: topic.id)
result = manager.perform
expect(result.action).to eq(:create_post)
expect(result).to be_success
expect(result.post).to be_present
expect(result.post).to be_a(Post)
end
end
context "default action" do
fab!(:other_user) { Fabricate(:user) }
it "doesn't enqueue private messages" do
SiteSetting.approve_unless_trust_level = 4
manager = NewPostManager.new(user,
raw: 'this is a new post',
title: 'this is a new title',
archetype: Archetype.private_message,
target_usernames: other_user.username)
result = manager.perform
expect(result.action).to eq(:create_post)
expect(result).to be_success
expect(result.post).to be_present
expect(result.post.topic.private_message?).to eq(true)
expect(result.post).to be_a(Post)
# It doesn't enqueue replies to the private message either
manager = NewPostManager.new(user,
raw: 'this is a new reply',
topic_id: result.post.topic_id)
result = manager.perform
expect(result.action).to eq(:create_post)
expect(result).to be_success
expect(result.post).to be_present
expect(result.post.topic.private_message?).to eq(true)
expect(result.post).to be_a(Post)
end
it "doesn't enqueue topic if it doesn't meet the tag restrictions of the category" do
tag1 = Fabricate(:tag)
tag2 = Fabricate(:tag)
tag3 = Fabricate(:tag)
tag_group = Fabricate(:tag_group, tags: [tag2])
category = Fabricate(:category, tags: [tag1], tag_groups: [tag_group])
category.custom_fields[Category::REQUIRE_TOPIC_APPROVAL] = true
category.save!
manager = NewPostManager.new(
user,
raw: 'this is a new post',
title: 'this is a new post',
category: category.id,
tags: [tag1.name, tag3.name]
)
result = manager.perform
expect(result.success?).to eq(false)
expect(result.reviewable.persisted?).to eq(false)
expect(result.errors.full_messages).to contain_exactly(
I18n.t(
"tags.forbidden.category_does_not_allow_tags",
count: 1,
category: category.name,
tags: tag3.name
)
)
manager = NewPostManager.new(
user,
raw: 'this is a new post',
title: 'this is a new post',
category: category.id,
tags: [tag2.name, tag3.name]
)
result = manager.perform
expect(result.success?).to eq(false)
expect(result.reviewable.persisted?).to eq(false)
expect(result.errors.full_messages).to contain_exactly(
I18n.t(
"tags.forbidden.category_does_not_allow_tags",
count: 1,
category: category.name,
tags: tag3.name
)
)
end
end
context "default handler" do
let(:manager) { NewPostManager.new(user, raw: 'this is new post content', topic_id: topic.id) }
context 'with the settings zeroed out' do
before do
SiteSetting.approve_post_count = 0
SiteSetting.approve_unless_trust_level = 0
end
it "doesn't return a result action" do
result = NewPostManager.default_handler(manager)
expect(NewPostManager.queue_enabled?).to eq(false)
expect(result).to eq(nil)
end
end
context 'basic post/topic count restrictions' do
before do
SiteSetting.approve_post_count = 1
end
it "works with a correct `user_stat.post_count`" do
result = NewPostManager.default_handler(manager)
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:post_count)
manager.user.user_stat.update(post_count: 1)
result = NewPostManager.default_handler(manager)
expect(result).to eq(nil)
end
it "works with a correct `user_stat.topic_count`" do
result = NewPostManager.default_handler(manager)
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:post_count)
manager.user.user_stat.update(topic_count: 1)
result = NewPostManager.default_handler(manager)
expect(result).to eq(nil)
end
end
context 'with a high approval post count and TL0' do
before do
SiteSetting.approve_post_count = 100
topic.user.trust_level = 0
end
it "will return an enqueue result" do
result = NewPostManager.default_handler(manager)
expect(NewPostManager.queue_enabled?).to eq(true)
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:post_count)
end
end
context 'with a high approval post count and TL1' do
before do
SiteSetting.approve_post_count = 100
topic.user.trust_level = 1
end
it "will return an enqueue result" do
result = NewPostManager.default_handler(manager)
expect(NewPostManager.queue_enabled?).to eq(true)
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:post_count)
end
end
context 'with a high approval post count, but TL2' do
before do
SiteSetting.approve_post_count = 100
user.update!(trust_level: 2)
end
it "will return an enqueue result" do
result = NewPostManager.default_handler(manager)
expect(result).to be_nil
end
end
context 'with a high approval post count and secure category' do
it 'does not create topic' do
SiteSetting.approve_post_count = 100
user = Fabricate(:user)
category_group = Fabricate(:category_group, permission_type: 2)
Fabricate(:group_user, group: category_group.group, user_id: user.id)
manager = NewPostManager.new(
user,
raw: 'this is a new topic',
title: "Let's start a new topic!",
category: category_group.category_id
)
expect(manager.perform.errors["base"][0]).to eq(I18n.t("js.errors.reasons.forbidden"))
end
end
context 'with a high trust level setting' do
before do
SiteSetting.approve_unless_trust_level = 4
end
it "will return an enqueue result" do
result = NewPostManager.default_handler(manager)
expect(NewPostManager.queue_enabled?).to eq(true)
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:trust_level)
end
end
context "with uncategorized disabled, and approval" do
before do
SiteSetting.allow_uncategorized_topics = false
SiteSetting.approve_unless_trust_level = 4
end
it "will return an enqueue result" do
npm = NewPostManager.new(
Fabricate(:user),
title: 'this is a new topic title',
raw: "this is the raw content",
category: Fabricate(:category).id
)
result = NewPostManager.default_handler(npm)
expect(NewPostManager.queue_enabled?).to eq(true)
expect(result.action).to eq(:enqueued)
expect(result.errors).to be_blank
end
end
context 'with staged moderation setting enabled' do
before do
SiteSetting.approve_unless_staged = true
user.update!(staged: true)
end
it "will return an enqueue result" do
result = NewPostManager.default_handler(manager)
expect(NewPostManager.queue_enabled?).to eq(true)
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:staged)
end
end
context 'with a high trust level setting for new topics but post responds to existing topic' do
before do
SiteSetting.approve_new_topics_unless_trust_level = 4
end
it "doesn't return a result action" do
result = NewPostManager.default_handler(manager)
expect(result).to eq(nil)
end
end
context 'with a fast typer' do
before do
user.update!(trust_level: 0)
end
it "adds the silence reason in the system locale" do
manager = build_manager_with('this is new post content')
I18n.with_locale(:fr) do # Simulate french user
result = NewPostManager.default_handler(manager)
end
expect(user.silenced?).to eq(true)
expect(user.silence_reason).to eq(I18n.t("user.new_user_typed_too_fast", locale: :en))
end
it 'runs the watched words check before checking if the user is a fast typer' do
Fabricate(:watched_word, word: "darn", action: WatchedWord.actions[:require_approval])
manager = build_manager_with('this is darn new post content')
result = NewPostManager.default_handler(manager)
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:watched_word)
end
def build_manager_with(raw)
NewPostManager.new(user, raw: raw, topic_id: topic.id, first_post_checks: true)
end
end
context 'with media' do
let(:manager_opts) do
{
raw: 'this is new post content', topic_id: topic.id, first_post_checks: false,
image_sizes: {
"http://localhost:3000/uploads/default/original/1X/652fc9667040b1b89dc4d9b061a823ddb3c0cef0.jpeg" => {
"width" => "500", "height" => "500"
}
}
}
end
before do
user.update!(trust_level: 0)
end
it 'queues the post for review because if it contains embedded media.' do
SiteSetting.review_media_unless_trust_level = 1
manager = NewPostManager.new(user, manager_opts)
result = NewPostManager.default_handler(manager)
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:contains_media)
end
it 'does not enqueue the post if the poster is a trusted user' do
SiteSetting.review_media_unless_trust_level = 0
manager = NewPostManager.new(user, manager_opts)
result = NewPostManager.default_handler(manager)
expect(result).to be_nil
end
end
end
context "new topic handler" do
let(:manager) { NewPostManager.new(user, raw: 'this is new topic content', title: 'new topic title') }
context 'with a high trust level setting for new topics' do
before do
SiteSetting.approve_new_topics_unless_trust_level = 4
end
it "will return an enqueue result" do
result = NewPostManager.default_handler(manager)
expect(NewPostManager.queue_enabled?).to eq(true)
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:new_topics_unless_trust_level)
end
end
end
context "extensibility priority" do
after do
NewPostManager.clear_handlers!
end
let(:default_handler) { NewPostManager.method(:default_handler) }
it "adds in order by default" do
handler = -> { nil }
NewPostManager.add_handler(&handler)
expect(NewPostManager.handlers).to eq([handler])
end
it "can be added in high priority" do
a = -> { nil }
b = -> { nil }
c = -> { nil }
NewPostManager.add_handler(100, &a)
NewPostManager.add_handler(50, &b)
NewPostManager.add_handler(101, &c)
expect(NewPostManager.handlers).to eq([c, a, b])
end
end
context "extensibility" do
before do
@counter = 0
@counter_handler = lambda do |manager|
result = nil
if manager.args[:raw] == 'this post increases counter'
@counter += 1
result = NewPostResult.new(:counter, true)
end
result
end
@queue_handler = -> (manager) { manager.args[:raw] =~ /queue me/ ? manager.enqueue('default') : nil }
NewPostManager.add_handler(&@counter_handler)
NewPostManager.add_handler(&@queue_handler)
end
after do
NewPostManager.clear_handlers!
end
it "has a queue enabled" do
expect(NewPostManager.queue_enabled?).to eq(true)
end
it "calls custom handlers" do
manager = NewPostManager.new(user, raw: 'this post increases counter', topic_id: topic.id)
result = manager.perform
expect(result.action).to eq(:counter)
expect(result).to be_success
expect(result.post).to be_blank
expect(@counter).to be(1)
expect(Reviewable.list_for(Discourse.system_user).count).to be(0)
end
it "calls custom enqueuing handlers" do
SiteSetting.tagging_enabled = true
SiteSetting.min_trust_to_create_tag = 0
SiteSetting.min_trust_level_to_tag_topics = 0
manager = NewPostManager.new(
topic.user,
raw: 'to the handler I say enqueue me!',
title: 'this is the title of the queued post',
tags: ['hello', 'world'],
category: topic.category_id
)
result = manager.perform
reviewable = result.reviewable
expect(reviewable).to be_present
expect(reviewable.payload['title']).to eq('this is the title of the queued post')
expect(reviewable.reviewable_scores).to be_present
expect(reviewable.force_review).to eq(true)
expect(reviewable.reviewable_by_moderator?).to eq(true)
expect(reviewable.category).to be_present
expect(reviewable.payload['tags']).to eq(['hello', 'world'])
expect(result.action).to eq(:enqueued)
expect(result).to be_success
expect(result.pending_count).to eq(1)
expect(result.post).to be_blank
expect(Reviewable.list_for(Discourse.system_user).count).to eq(1)
expect(@counter).to be(0)
reviewable.perform(Discourse.system_user, :approve_post)
manager = NewPostManager.new(
topic.user,
raw: 'another post by this user queue me',
topic_id: topic.id
)
result = manager.perform
reviewable = result.reviewable
expect(reviewable.topic).to be_present
expect(reviewable.category).to be_present
expect(result.pending_count).to eq(1)
end
it "if nothing returns a result it creates a post" do
manager = NewPostManager.new(user, raw: 'this is a new post', topic_id: topic.id)
result = manager.perform
expect(result.action).to eq(:create_post)
expect(result).to be_success
expect(result.post).to be_present
expect(@counter).to be(0)
end
end
context "user needs approval?" do
let :user do
user = Fabricate.build(:user, trust_level: 0)
user_stat = UserStat.new(post_count: 0)
user.user_stat = user_stat
user
end
it "handles post_needs_approval? correctly" do
u = user
default = NewPostManager.new(u, {})
expect(NewPostManager.post_needs_approval?(default)).to eq(:skip)
with_check = NewPostManager.new(u, first_post_checks: true)
expect(NewPostManager.post_needs_approval?(with_check)).to eq(:fast_typer)
u.user_stat.post_count = 1
with_check_and_post = NewPostManager.new(u, first_post_checks: true)
expect(NewPostManager.post_needs_approval?(with_check_and_post)).to eq(:skip)
u.user_stat.post_count = 0
u.trust_level = 1
with_check_tl1 = NewPostManager.new(u, first_post_checks: true)
expect(NewPostManager.post_needs_approval?(with_check_tl1)).to eq(:skip)
end
end
context 'when posting in the category requires approval' do
let!(:user) { Fabricate(:user) }
let!(:review_group) { Fabricate(:group) }
let!(:category) { Fabricate(:category, reviewable_by_group_id: review_group.id) }
context 'when new topics require approval' do
before do
SiteSetting.tagging_enabled = true
category.custom_fields[Category::REQUIRE_TOPIC_APPROVAL] = true
category.save
end
it 'enqueues new topics' do
manager = NewPostManager.new(
user,
raw: 'this is a new topic',
title: "Let's start a new topic!",
category: category.id
)
result = manager.perform
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:category)
end
it 'does not enqueue the topic when the poster is a category group moderator' do
SiteSetting.enable_category_group_moderation = true
review_group.users << user
manager = NewPostManager.new(
user,
raw: 'this is a new topic',
title: "Let's start a new topic!",
category: category.id
)
result = manager.perform
expect(result.action).to eq(:create_post)
expect(result).to be_success
end
context "when the category has tagging rules" do
context "when there is a minimum number of tags required for the category" do
before do
category.update(minimum_required_tags: 1)
end
it "errors when there are no tags provided" do
manager = NewPostManager.new(
user,
raw: 'this is a new topic',
title: "Let's start a new topic!",
category: category.id
)
result = manager.perform
expect(result.action).to eq(:enqueued)
expect(result.errors.full_messages).to include(I18n.t("tags.minimum_required_tags", count: category.minimum_required_tags))
end
it "enqueues the topic if there are tags provided" do
tag = Fabricate(:tag)
manager = NewPostManager.new(
user,
raw: 'this is a new topic',
title: "Let's start a new topic!",
category: category.id,
tags: tag.name
)
result = manager.perform
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:category)
end
end
context "when there is a minimum number of tags required from a certain tag group for the category" do
let(:tag_group) { Fabricate(:tag_group) }
let(:tag) { Fabricate(:tag) }
before do
TagGroupMembership.create(tag: tag, tag_group: tag_group)
category.update(min_tags_from_required_group: 1, required_tag_group_id: tag_group.id)
end
it "errors when there are no tags from the group provided" do
manager = NewPostManager.new(
user,
raw: 'this is a new topic',
title: "Let's start a new topic!",
category: category.id
)
result = manager.perform
expect(result.action).to eq(:enqueued)
expect(result.errors.full_messages).to include(
I18n.t(
"tags.required_tags_from_group",
count: category.min_tags_from_required_group,
tag_group_name: category.required_tag_group.name,
tags: tag.name
)
)
end
it "enqueues the topic if there are tags provided" do
manager = NewPostManager.new(
user,
raw: 'this is a new topic',
title: "Let's start a new topic!",
category: category.id,
tags: [tag.name]
)
result = manager.perform
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:category)
end
end
end
end
context 'when new posts require approval' do
let!(:topic) { Fabricate(:topic, category: category) }
before do
category.custom_fields[Category::REQUIRE_REPLY_APPROVAL] = true
category.save
end
it 'enqueues new posts' do
manager = NewPostManager.new(user, raw: 'this is a new post', topic_id: topic.id)
result = manager.perform
expect(result.action).to eq(:enqueued)
expect(result.reason).to eq(:category)
end
it "doesn't blow up with invalid topic_id" do
expect do
manager = NewPostManager.new(
user,
raw: 'this is a new topic',
topic_id: 97546
)
expect(manager.perform.action).to eq(:create_post)
end.not_to raise_error
end
it 'does not enqueue the post when the poster is a category group moderator' do
SiteSetting.enable_category_group_moderation = true
review_group.users << user
manager = NewPostManager.new(
user,
raw: 'this is a new post',
topic_id: topic.id
)
result = manager.perform
expect(result.action).to eq(:create_post)
expect(result).to be_success
end
end
end
context "via email" do
let(:manager) do
NewPostManager.new(
topic.user,
raw: 'this is emailed content',
topic_id: topic.id,
via_email: true,
raw_email: 'raw email contents'
)
end
before do
SiteSetting.approve_post_count = 100
topic.user.trust_level = 0
end
it "will store via_email and raw_email in the enqueued post" do
result = manager.perform
expect(result.action).to eq(:enqueued)
expect(result.reviewable).to be_present
expect(result.reviewable.payload['via_email']).to eq(true)
expect(result.reviewable.payload['raw_email']).to eq('raw email contents')
post = result.reviewable.perform(Discourse.system_user, :approve_post).created_post
expect(post.via_email).to eq(true)
expect(post.raw_email).to eq("raw email contents")
end
end
context "via email with a spam failure" do
let(:user) { Fabricate(:user) }
let(:admin) { Fabricate(:admin) }
it "silences users if its their first post" do
manager = NewPostManager.new(
user,
raw: 'this is emailed content',
via_email: true,
raw_email: 'raw email contents',
email_spam: true,
first_post_checks: true
)
result = manager.perform
expect(result.action).to eq(:enqueued)
expect(user.silenced?).to be(true)
end
it "doesn't silence or enqueue exempt users" do
manager = NewPostManager.new(
admin,
raw: 'this is emailed content',
via_email: true,
raw_email: 'raw email contents',
email_spam: true,
first_post_checks: true
)
result = manager.perform
expect(result.action).to eq(:create_post)
expect(admin.silenced?).to be(false)
end
end
context "via email with an authentication results failure" do
let(:user) { Fabricate(:user) }
let(:admin) { Fabricate(:admin) }
it "doesn't silence users" do
manager = NewPostManager.new(
user,
raw: 'this is emailed content',
via_email: true,
raw_email: 'raw email contents',
email_auth_res_action: :enqueue,
first_post_checks: true
)
result = manager.perform
expect(result.action).to eq(:enqueued)
expect(user.silenced?).to be(false)
end
it "still enqueues exempt users" do
manager = NewPostManager.new(
admin,
raw: 'this is emailed content',
via_email: true,
raw_email: 'raw email contents',
email_auth_res_action: :enqueue
)
result = manager.perform
expect(result.action).to eq(:enqueued)
expect(user.silenced?).to be(false)
end
end
context "private message via email" do
it "doesn't enqueue authentication results failure" do
manager = NewPostManager.new(
topic.user,
raw: 'this is emailed content',
archetype: Archetype.private_message,
via_email: true,
raw_email: 'raw email contents',
email_auth_res_action: :enqueue
)
result = manager.perform
expect(result.action).to eq(:create_post)
end
it "doesn't enqueue spam failure" do
manager = NewPostManager.new(
topic.user,
raw: 'this is emailed content',
archetype: Archetype.private_message,
via_email: true,
raw_email: 'raw email contents',
email_spam: true
)
result = manager.perform
expect(result.action).to eq(:create_post)
end
end
end