From b6576d9473c42f5597d685098f870c946800df1c Mon Sep 17 00:00:00 2001 From: Bianca Nenciu Date: Wed, 14 Nov 2018 16:48:16 +0200 Subject: [PATCH] FEATURE: Add new setting to force user edit last post. (#6571) --- config/locales/server.en.yml | 5 +++ config/site_settings.yml | 2 + lib/validators/post_validator.rb | 27 +++++++++++++ .../validators/post_validator_spec.rb | 38 +++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 7613a3861a5..7910228739c 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -218,6 +218,10 @@ en: one: "We're sorry, but new users are temporarily limited to 1 reply in the same topic." other: "We're sorry, but new users are temporarily limited to %{count} replies in the same topic." + max_consecutive_replies: + one: "No more than 1 reply is allowed. Please edit your previous reply instead, or wait for someone to reply to you." + other: "No more than %{count} consecutive replies are allowed. Please edit your previous reply instead, or wait for someone to reply to you." + embed: start_discussion: "Start Discussion" continue: "Continue Discussion" @@ -1536,6 +1540,7 @@ en: title_min_entropy: "The minimum entropy (unique characters, non-english count for more) required for a topic title." body_min_entropy: "The minimum entropy (unique characters, non-english count for more) required for a post body." allow_uppercase_posts: "Allow all caps in a topic title or a post body." + max_consecutive_replies: "Force users to edit their last post instead of replying" title_fancy_entities: "Convert common ASCII characters to fancy HTML entities in topic titles, ala SmartyPants https://daringfireball.net/projects/smartypants/" diff --git a/config/site_settings.yml b/config/site_settings.yml index aab7b5c0fe0..21ce90e2eeb 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -594,6 +594,8 @@ posting: default: false locale_default: ja: true + max_consecutive_replies: + default: 0 title_prettify: default: true locale_default: diff --git a/lib/validators/post_validator.rb b/lib/validators/post_validator.rb index f8c58ec2e64..750c3ac5899 100644 --- a/lib/validators/post_validator.rb +++ b/lib/validators/post_validator.rb @@ -17,6 +17,7 @@ class Validators::PostValidator < ActiveModel::Validator max_attachments_validator(record) can_post_links_validator(record) unique_post_validator(record) + force_edit_last_validator(record) end def presence(post) @@ -141,6 +142,32 @@ class Validators::PostValidator < ActiveModel::Validator end end + def force_edit_last_validator(post) + return if SiteSetting.max_consecutive_replies == 0 || post.id || post.acting_user&.staff? || private_message?(post) + + topic = post.topic + + last_posts_count = DB.query_single(<<~SQL, topic_id: post.topic_id, user_id: post.acting_user.id, max_replies: SiteSetting.max_consecutive_replies).first + SELECT COUNT(*) + FROM ( + SELECT user_id + FROM posts + WHERE deleted_at IS NULL + AND NOT hidden + AND topic_id = :topic_id + ORDER BY post_number DESC + LIMIT :max_replies + ) c + WHERE c.user_id = :user_id + SQL + return if last_posts_count < SiteSetting.max_consecutive_replies + + guardian = Guardian.new(post.acting_user) + if guardian.can_edit?(topic.posts.last) + post.errors.add(:base, I18n.t(:max_consecutive_replies, count: SiteSetting.max_consecutive_replies)) + end + end + private def acting_user_is_trusted?(post, level = 1) diff --git a/spec/components/validators/post_validator_spec.rb b/spec/components/validators/post_validator_spec.rb index 61c530cd8cb..cb33aa81468 100644 --- a/spec/components/validators/post_validator_spec.rb +++ b/spec/components/validators/post_validator_spec.rb @@ -219,6 +219,43 @@ describe Validators::PostValidator do end end + context "force_edit_last_validator" do + + let(:user) { Fabricate(:user) } + let(:other_user) { Fabricate(:user) } + let(:topic) { Fabricate(:topic) } + + before do + SiteSetting.max_consecutive_replies = 2 + end + + it "should not allow posting more than 2 consecutive replies" do + 1.upto(3).each do |i| + post = Post.new(user: user, topic: topic, raw: "post number #{i}") + validator.force_edit_last_validator(post) + expect(post.errors.count).to eq(i > SiteSetting.max_consecutive_replies ? 1 : 0) + post.save + end + end + + it "should always allow editing" do + post = Fabricate(:post, user: user, topic: topic) + post = Fabricate(:post, user: user, topic: topic) + + revisor = PostRevisor.new(post) + revisor.revise!(post.user, raw: 'hello world123456789') + end + + it "should allow posting more than 2 replies" do + 3.times do + post = Fabricate(:post, user: user, topic: topic) + Fabricate(:post, user: other_user, topic: topic) + validator.force_edit_last_validator(post) + expect(post.errors.count).to eq(0) + end + end + end + shared_examples "almost no validations" do it "skips most validations" do validator.expects(:stripped_length).never @@ -229,6 +266,7 @@ describe Validators::PostValidator do validator.expects(:max_attachments_validator).never validator.expects(:newuser_links_validator).never validator.expects(:unique_post_validator).never + validator.expects(:force_edit_last_validator).never validator.validate(post) end end