diff --git a/app/assets/javascripts/discourse/templates/components/reviewable-score.hbs b/app/assets/javascripts/discourse/templates/components/reviewable-score.hbs index e19c9ceb65e..6454ffb5a40 100644 --- a/app/assets/javascripts/discourse/templates/components/reviewable-score.hbs +++ b/app/assets/javascripts/discourse/templates/components/reviewable-score.hbs @@ -19,7 +19,7 @@ -{{d-icon "angle-double-right"}} + {{d-icon "angle-double-right"}} @@ -38,12 +38,19 @@ {{#if rs.reviewed_by}} - - {{format-date rs.reviewed_at format="tiny"}} + {{format-date rs.reviewed_at format="tiny"}} {{/if}} - + +{{#if rs.reason}} + + +
{{{rs.reason}}}
+ + +{{/if}} + {{#if rs.reviewable_conversation}} diff --git a/app/assets/stylesheets/common/base/reviewables.scss b/app/assets/stylesheets/common/base/reviewables.scss index 88cf15bd888..0dbd1821576 100644 --- a/app/assets/stylesheets/common/base/reviewables.scss +++ b/app/assets/stylesheets/common/base/reviewables.scss @@ -296,6 +296,12 @@ } } +.reviewable-score-reason { + margin: 0.5em 0; + max-width: $topic-body-width; + margin-bottom: 0.5em; +} + .reviewable-conversation { margin: 0.5em 0; diff --git a/app/jobs/regular/create_user_reviewable.rb b/app/jobs/regular/create_user_reviewable.rb index 2546f7fe636..fb1824c8aa0 100644 --- a/app/jobs/regular/create_user_reviewable.rb +++ b/app/jobs/regular/create_user_reviewable.rb @@ -2,6 +2,12 @@ class Jobs::CreateUserReviewable < Jobs::Base def execute(args) raise Discourse::InvalidParameters unless args[:user_id].present? + reason = nil + reason ||= :must_approve_users if SiteSetting.must_approve_users? + reason ||= :invite_only if SiteSetting.invite_only? + + return unless reason + if user = User.find_by(id: args[:user_id]) return if user.approved? @@ -18,6 +24,7 @@ class Jobs::CreateUserReviewable < Jobs::Base reviewable.add_score( Discourse.system_user, ReviewableScore.types[:needs_approval], + reason: reason, force_review: true ) end diff --git a/app/models/reviewable.rb b/app/models/reviewable.rb index dfd73b8d3f0..5debd5ba4cf 100644 --- a/app/models/reviewable.rb +++ b/app/models/reviewable.rb @@ -115,6 +115,7 @@ class Reviewable < ActiveRecord::Base def add_score( user, reviewable_score_type, + reason: nil, created_at: nil, take_action: false, meta_topic_id: nil, @@ -130,7 +131,7 @@ class Reviewable < ActiveRecord::Base sub_total = SiteSetting.min_score_default_visibility end - rs = reviewable_scores.create!( + rs = reviewable_scores.new( user: user, status: ReviewableScore.statuses[:pending], reviewable_score_type: reviewable_score_type, @@ -139,6 +140,8 @@ class Reviewable < ActiveRecord::Base take_action_bonus: take_action_bonus, created_at: created_at || Time.zone.now ) + rs.reason = reason.to_s if reason + rs.save! update(score: self.score + rs.score, latest_score: rs.created_at) topic.update(reviewable_score: topic.reviewable_score + rs.score) if topic diff --git a/app/serializers/reviewable_score_serializer.rb b/app/serializers/reviewable_score_serializer.rb index c58568944cd..f5180d8f766 100644 --- a/app/serializers/reviewable_score_serializer.rb +++ b/app/serializers/reviewable_score_serializer.rb @@ -2,7 +2,7 @@ require_dependency 'reviewable_score_type_serializer' class ReviewableScoreSerializer < ApplicationSerializer - attributes :id, :score, :agree_stats, :status, :created_at, :reviewed_at + attributes :id, :score, :agree_stats, :status, :reason, :created_at, :reviewed_at has_one :user, serializer: BasicUserSerializer, root: 'users' has_one :score_type, serializer: ReviewableScoreTypeSerializer has_one :reviewable_conversation, serializer: ReviewableConversationSerializer @@ -16,4 +16,28 @@ class ReviewableScoreSerializer < ApplicationSerializer } end + def reason + return unless object.reason + + if text = I18n.t("reviewables.reasons.#{object.reason}", default: nil) + # Create a convenient link to any site settings if the user is staff + settings_url = "#{Discourse.base_uri}/admin/site_settings/category/all_results?filter=" + + text.gsub!(/`[a-z_]+`/) do |m| + if scope.is_staff? + setting = m[1..-2] + "#{setting.gsub('_', ' ')}" + else + m.gsub('_', ' ') + end + end + end + + text + end + + def include_reason? + reason.present? + end + end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 4d2f668aa23..7d7aefd3b8a 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -429,6 +429,7 @@ en: conversation: view_full: "view full conversation" scores: + score: "Score" date: "Date" type: "Type" status: "Status" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index f156782efbe..a859b85417a 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -4385,6 +4385,18 @@ en: reviewables: missing_version: "You must supply a version parameter" conflict: "There was an update conflict preventing you from doing that." + reasons: + post_count: "The poster has not met the `approve_post_count` requirements." + trust_level: "The poster has not met the `approve_unless_trust_level` requirement." + new_topics_unless_trust_level: "The poster has not met the `approve_new_topics_unless_trust_level` requirement." + fast_typer: "The poster typed in their post suspiciously fast. See: `min_first_post_typing_time`." + auto_silence_regexp: "The post matched the `auto_silence_first_post_regex` setting." + watched_word: "The post included a Watched Word." + staged: "The poster was staged and `approve_unless_staged` was set." + category: "The category of the post requires approval." + must_approve_users: "The user must be approved because `must_approve_users` is enabled." + invite_only: "The user must be approved because `invite_only` is enabled." + actions: agree: title: "Agree..." diff --git a/db/migrate/20190411144545_add_reason_to_reviewable_scores.rb b/db/migrate/20190411144545_add_reason_to_reviewable_scores.rb new file mode 100644 index 00000000000..a6105c6b271 --- /dev/null +++ b/db/migrate/20190411144545_add_reason_to_reviewable_scores.rb @@ -0,0 +1,5 @@ +class AddReasonToReviewableScores < ActiveRecord::Migration[5.2] + def change + add_column :reviewable_scores, :reason, :string + end +end diff --git a/lib/new_post_manager.rb b/lib/new_post_manager.rb index d0eca260147..6269d978c10 100644 --- a/lib/new_post_manager.rb +++ b/lib/new_post_manager.rb @@ -198,12 +198,9 @@ class NewPostManager def enqueue(reason = nil) result = NewPostResult.new(:enqueued) - payload = { raw: @args[:raw], tags: @args[:tags] } - payload[:reason] = reason.to_s if reason - reviewable = ReviewableQueuedPost.new( created_by: @user, - payload: payload, + payload: { raw: @args[:raw], tags: @args[:tags] }, topic_id: @args[:topic_id], reviewable_by_moderator: true ) @@ -225,6 +222,7 @@ class NewPostManager reviewable.add_score( Discourse.system_user, ReviewableScore.types[:needs_approval], + reason: reason, force_review: true ) else diff --git a/spec/jobs/create_user_reviewable_spec.rb b/spec/jobs/create_user_reviewable_spec.rb index ffb2c3b8922..acab20ba9af 100644 --- a/spec/jobs/create_user_reviewable_spec.rb +++ b/spec/jobs/create_user_reviewable_spec.rb @@ -6,14 +6,40 @@ describe Jobs::CreateUserReviewable do let(:user) { Fabricate(:user) } it "creates the reviewable" do + SiteSetting.must_approve_users = true described_class.new.execute(user_id: user.id) reviewable = Reviewable.find_by(target: user) expect(reviewable).to be_present expect(reviewable.pending?).to eq(true) - expect(reviewable.reviewable_scores).to be_present expect(reviewable.payload['username']).to eq(user.username) expect(reviewable.payload['name']).to eq(user.name) expect(reviewable.payload['email']).to eq(user.email) end + + describe "reasons" do + it "does nothing if there's no reason" do + described_class.new.execute(user_id: user.id) + expect(Reviewable.find_by(target: user)).to be_blank + end + + it "adds must_approve_users if enabled" do + SiteSetting.must_approve_users = true + described_class.new.execute(user_id: user.id) + reviewable = Reviewable.find_by(target: user) + score = reviewable.reviewable_scores.first + expect(score).to be_present + expect(score.reason).to eq('must_approve_users') + end + + it "adds invite_only if enabled" do + SiteSetting.invite_only = true + described_class.new.execute(user_id: user.id) + reviewable = Reviewable.find_by(target: user) + score = reviewable.reviewable_scores.first + expect(score).to be_present + expect(score.reason).to eq('invite_only') + end + end + end diff --git a/spec/models/reviewable_user_spec.rb b/spec/models/reviewable_user_spec.rb index fed75f86d71..1b914d826d3 100644 --- a/spec/models/reviewable_user_spec.rb +++ b/spec/models/reviewable_user_spec.rb @@ -38,6 +38,7 @@ RSpec.describe ReviewableUser, type: :model do context "when a user is deleted" do it "should reject the reviewable" do + SiteSetting.must_approve_users = true Jobs::CreateUserReviewable.new.execute(user_id: user.id) reviewable = Reviewable.find_by(target: user) expect(reviewable.pending?).to eq(true) diff --git a/spec/requests/posts_controller_spec.rb b/spec/requests/posts_controller_spec.rb index 17b2b71a317..7e5e65557c6 100644 --- a/spec/requests/posts_controller_spec.rb +++ b/spec/requests/posts_controller_spec.rb @@ -801,7 +801,7 @@ describe PostsController do expect(user).to be_silenced rp = ReviewableQueuedPost.find_by(created_by: user) - expect(rp.payload['reason']).to eq('fast_typer') + expect(rp.reviewable_scores.first.reason).to eq('fast_typer') mod = Fabricate(:moderator) rp.perform(mod, :approve) @@ -851,7 +851,8 @@ describe PostsController do expect(parsed["action"]).to eq("enqueued") reviewable = ReviewableQueuedPost.find_by(created_by: user) - expect(reviewable.payload['reason']).to eq('auto_silence_regex') + score = reviewable.reviewable_scores.first + expect(score.reason).to eq('auto_silence_regex') user.reload expect(user).to be_silenced diff --git a/spec/serializers/reviewable_user_serializer_spec.rb b/spec/serializers/reviewable_user_serializer_spec.rb index 88991ea19ae..2fa506494fd 100644 --- a/spec/serializers/reviewable_user_serializer_spec.rb +++ b/spec/serializers/reviewable_user_serializer_spec.rb @@ -6,6 +6,7 @@ describe ReviewableUserSerializer do let(:admin) { Fabricate(:admin) } it "includes the user fields for review" do + SiteSetting.must_approve_users = true Jobs::CreateUserReviewable.new.execute(user_id: user.id) reviewable = Reviewable.find_by(target: user)