mirror of
https://github.com/discourse/discourse.git
synced 2026-08-08 17:53:55 +08:00
This is an effort to make our existing review queue actions more consistently follow the pattern of question/response. For example, instead of the existing state of: <img width="250" alt="image" src="https://github.com/user-attachments/assets/7dba3937-b25a-4cf1-9a03-16b2ddb3f3f0" /> This PR updates things to "Approve this post?" <img width="500" alt="image" src="https://github.com/user-attachments/assets/1f3a693e-63f7-43d5-beba-8179cc52a63d" /> This is to gain more consistency around all flag types. Currently we have a mix of generic "Moderator Actions" and question based actions like "Is there something wrong with this post?" Here's a series of screenshots covering different flag types covered here: <img width="299" alt="image" src="https://github.com/user-attachments/assets/4910531f-2bed-4c5d-8b0a-76c5166f3db0" /> <img width="601" alt="image" src="https://github.com/user-attachments/assets/b98d7067-ada2-4b26-89a2-7d25073f3680" /> <img width="619" alt="image" src="https://github.com/user-attachments/assets/332b6f0c-6893-4aef-b30c-41bf297f53f7" /> <img width="287" alt="image" src="https://github.com/user-attachments/assets/39bee5ee-6486-4342-b39d-29aa6e0b0b2a" /> <img width="562" alt="image" src="https://github.com/user-attachments/assets/57f5ccb9-4353-4ecb-b478-5e6c324f1d38" /> <img width="505" alt="image" src="https://github.com/user-attachments/assets/33967f1c-7b63-440c-9595-0e05d2c52a78" /> <img width="328" alt="image" src="https://github.com/user-attachments/assets/c36dec5d-adbf-4c61-86b4-b074070c75cf" /> <img width="448" alt="image" src="https://github.com/user-attachments/assets/959d1355-6568-4a5a-bc8a-d61b278d6a67" /> <img width="528" alt="image" src="https://github.com/user-attachments/assets/6098a098-a5c3-496a-9ec3-67839067692e" /> <img width="454" alt="image" src="https://github.com/user-attachments/assets/723b584e-74ff-4828-b92c-ae5361bc757c" /> <img width="429" alt="image" src="https://github.com/user-attachments/assets/9a3e8f53-f62e-4df2-b3a1-668373e03d53" /> <img width="532" alt="image" src="https://github.com/user-attachments/assets/e4aed6e8-bb33-4d17-a863-cb3d94bcbf00" /> <img width="390" alt="image" src="https://github.com/user-attachments/assets/54a4af77-b16b-4add-925f-f648caf09432" /> <img width="651" alt="image" src="https://github.com/user-attachments/assets/edb10140-69e7-4795-813f-b51875d104cf" /> <img width="406" alt="image" src="https://github.com/user-attachments/assets/9f4dbcee-e0a7-4404-9e22-04630286e2e9" /> <img width="375" alt="image" src="https://github.com/user-attachments/assets/3901215f-a752-4ad0-94e7-8924d069302f" /> <img width="538" alt="image" src="https://github.com/user-attachments/assets/f3d2c3cc-bc64-4445-a6fc-7c550077cb31" /> <img width="404" alt="image" src="https://github.com/user-attachments/assets/8ca1b7a9-a4bc-48d4-9c0e-0dd162367398" /> <img width="421" alt="image" src="https://github.com/user-attachments/assets/bffcb595-0581-4526-943b-04e6792b62f1" /> <img width="529" alt="image" src="https://github.com/user-attachments/assets/10e4381f-f662-49bd-9604-79ca8a75dca2" /> <img width="363" alt="image" src="https://github.com/user-attachments/assets/64276d2b-4c47-4f1d-aabc-b9125a8026ef" /> <img width="527" alt="image" src="https://github.com/user-attachments/assets/9533e22f-fe29-456c-a10b-dfda28319199" /> <img width="393" alt="image" src="https://github.com/user-attachments/assets/d9479c85-0c44-4742-9680-68a79ebaf4d4" /> <img width="321" alt="image" src="https://github.com/user-attachments/assets/33cedd6d-3a40-43f2-bde2-0903567d6c40" /> <img width="465" alt="image" src="https://github.com/user-attachments/assets/cb73016a-1e37-4273-b5f2-0834405b349f" /> <img width="531" alt="image" src="https://github.com/user-attachments/assets/b01f3ab8-ce4b-448d-9c4f-a6a8149486a6" />
179 lines
6 KiB
Ruby
Vendored
179 lines
6 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
class ReviewablePost < Reviewable
|
|
include ReviewableActionBuilder
|
|
|
|
def self.action_aliases
|
|
{ reject_and_silence: :reject_and_suspend }
|
|
end
|
|
|
|
def self.queue_for_review_if_possible(post, created_or_edited_by)
|
|
return unless SiteSetting.review_every_post
|
|
return if post.post_type != Post.types[:regular] || post.topic.private_message?
|
|
return if Reviewable.pending.where(target: post).exists?
|
|
if created_or_edited_by.bot? || created_or_edited_by.staff? ||
|
|
created_or_edited_by.has_trust_level?(TrustLevel[4])
|
|
return
|
|
end
|
|
queue_for_review(post)
|
|
end
|
|
|
|
def self.queue_for_review(post)
|
|
system_user = Discourse.system_user
|
|
|
|
needs_review!(
|
|
target: post,
|
|
topic: post.topic,
|
|
created_by: system_user,
|
|
reviewable_by_moderator: true,
|
|
potential_spam: false,
|
|
).tap do |reviewable|
|
|
reviewable.add_score(system_user, ReviewableScore.types[:needs_approval], force_review: true)
|
|
end
|
|
end
|
|
|
|
def build_actions(actions, guardian, args)
|
|
return unless pending?
|
|
super
|
|
end
|
|
|
|
# TODO (reviewable-refresh): Remove this method when fully migrated to new UI
|
|
def build_legacy_combined_actions(actions, guardian, args)
|
|
if post.trashed? && guardian.can_recover_post?(post)
|
|
build_action(actions, :approve_and_restore, icon: "check")
|
|
elsif post.hidden?
|
|
build_action(actions, :approve_and_unhide, icon: "check")
|
|
else
|
|
build_action(actions, :approve, icon: "check")
|
|
end
|
|
|
|
reject =
|
|
actions.add_bundle(
|
|
"#{id}-reject-post",
|
|
icon: "xmark",
|
|
label: "reviewables.actions.reject_post_bundle.title",
|
|
)
|
|
|
|
can_penalize = guardian.can_suspend?(target_created_by)
|
|
|
|
if post.trashed?
|
|
if can_penalize
|
|
build_action(actions, :reject_and_keep_deleted, icon: "trash-can", bundle: reject)
|
|
else
|
|
actions.add(:reject_and_keep_deleted, bundle: reject) do |a|
|
|
a.icon = "trash-can"
|
|
a.label = "reviewables.actions.reject_and_keep_deleted_standalone.title"
|
|
end
|
|
end
|
|
elsif guardian.can_delete_post_or_topic?(post)
|
|
if can_penalize
|
|
build_action(actions, :reject_and_delete, icon: "trash-can", bundle: reject)
|
|
else
|
|
actions.add(:reject_and_delete, bundle: reject) do |a|
|
|
a.icon = "trash-can"
|
|
a.label = "reviewables.actions.reject_and_delete_standalone.title"
|
|
end
|
|
end
|
|
end
|
|
|
|
if can_penalize
|
|
build_action(
|
|
actions,
|
|
:reject_and_suspend,
|
|
icon: "ban",
|
|
bundle: reject,
|
|
client_action: "suspend",
|
|
)
|
|
build_action(
|
|
actions,
|
|
:reject_and_silence,
|
|
icon: "microphone-slash",
|
|
bundle: reject,
|
|
client_action: "silence",
|
|
)
|
|
end
|
|
end
|
|
|
|
# TODO (reviewable-refresh): Merge this method into build_actions when fully migrated to new UI
|
|
def build_new_separated_actions
|
|
build_post_actions_bundle
|
|
build_user_actions_bundle
|
|
end
|
|
|
|
# TODO (reviewable-refresh): Remove combined actions below when fully migrated to new UI
|
|
def perform_approve(performed_by, _args)
|
|
create_result(:success, :approved, [created_by_id], false)
|
|
end
|
|
|
|
def perform_reject_and_keep_deleted(performed_by, _args)
|
|
create_result(:success, :rejected, [created_by_id], false)
|
|
end
|
|
|
|
def perform_approve_and_restore(performed_by, _args)
|
|
PostDestroyer.new(performed_by, post).recover
|
|
|
|
create_result(:success, :approved, [created_by_id], false)
|
|
end
|
|
|
|
def perform_approve_and_unhide(performed_by, _args)
|
|
post.acting_user = performed_by
|
|
post.unhide!
|
|
|
|
create_result(:success, :approved, [created_by_id], false)
|
|
end
|
|
|
|
def perform_reject_and_delete(performed_by, _args)
|
|
PostDestroyer.new(performed_by, post, reviewable: self).destroy
|
|
|
|
create_result(:success, :rejected, [created_by_id], false)
|
|
end
|
|
|
|
def perform_reject_and_suspend(performed_by, _args)
|
|
create_result(:success, :rejected, [created_by_id], false)
|
|
end
|
|
# TODO (reviewable-refresh): Remove combined actions above when fully migrated to new UI
|
|
|
|
private
|
|
|
|
def post
|
|
@post ||= (target || Post.with_deleted.find_by(id: target_id))
|
|
end
|
|
end
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: reviewables
|
|
#
|
|
# id :bigint not null, primary key
|
|
# type :string not null
|
|
# type_source :string default("unknown"), not null
|
|
# status :integer default("pending"), not null
|
|
# created_by_id :integer not null
|
|
# reviewable_by_moderator :boolean default(FALSE), not null
|
|
# category_id :integer
|
|
# topic_id :integer
|
|
# score :float default(0.0), not null
|
|
# potential_spam :boolean default(FALSE), not null
|
|
# target_id :integer
|
|
# target_type :string
|
|
# target_created_by_id :integer
|
|
# payload :json
|
|
# version :integer default(0), not null
|
|
# latest_score :datetime
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
# force_review :boolean default(FALSE), not null
|
|
# reject_reason :text
|
|
# potentially_illegal :boolean default(FALSE)
|
|
#
|
|
# Indexes
|
|
#
|
|
# idx_reviewables_score_desc_created_at_desc (score,created_at)
|
|
# index_reviewables_on_reviewable_by_group_id (reviewable_by_group_id)
|
|
# index_reviewables_on_status_and_created_at (status,created_at)
|
|
# index_reviewables_on_status_and_score (status,score)
|
|
# index_reviewables_on_status_and_type (status,type)
|
|
# index_reviewables_on_target_id_where_post_type_eq_post (target_id) WHERE ((target_type)::text = 'Post'::text)
|
|
# index_reviewables_on_topic_id_and_status_and_created_by_id (topic_id,status,created_by_id)
|
|
# index_reviewables_on_type_and_target_id (type,target_id) UNIQUE
|
|
#
|