2015-03-25 14:24:34 -04:00
|
|
|
class QueuedPost < ActiveRecord::Base
|
|
|
|
|
2018-12-03 22:48:13 -05:00
|
|
|
class InvalidStateTransition < StandardError; end
|
2015-03-25 14:24:34 -04:00
|
|
|
|
|
|
|
belongs_to :user
|
|
|
|
belongs_to :topic
|
|
|
|
belongs_to :approved_by, class_name: "User"
|
|
|
|
belongs_to :rejected_by, class_name: "User"
|
|
|
|
|
2018-07-25 13:54:43 -04:00
|
|
|
after_commit :trigger_queued_post_event, on: :create
|
|
|
|
|
2015-04-24 15:25:47 -04:00
|
|
|
def create_pending_action
|
|
|
|
UserAction.log_action!(action_type: UserAction::PENDING,
|
|
|
|
user_id: user_id,
|
|
|
|
acting_user_id: user_id,
|
|
|
|
target_topic_id: topic_id,
|
|
|
|
queued_post_id: id)
|
|
|
|
end
|
|
|
|
|
2018-07-25 13:54:43 -04:00
|
|
|
def trigger_queued_post_event
|
2018-07-26 00:59:38 -04:00
|
|
|
DiscourseEvent.trigger(:queued_post_created, self)
|
2018-07-25 13:54:43 -04:00
|
|
|
true
|
|
|
|
end
|
|
|
|
|
2015-03-25 14:24:34 -04:00
|
|
|
def self.states
|
|
|
|
@states ||= Enum.new(:new, :approved, :rejected)
|
|
|
|
end
|
|
|
|
|
2015-04-20 17:19:05 -04:00
|
|
|
# By default queues are hidden from moderators
|
|
|
|
def self.visible_queues
|
|
|
|
@visible_queues ||= Set.new(['default'])
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.visible
|
|
|
|
where(queue: visible_queues.to_a)
|
|
|
|
end
|
|
|
|
|
2015-04-24 15:44:59 -04:00
|
|
|
def self.new_posts
|
2015-04-27 11:56:07 -04:00
|
|
|
where(state: states[:new])
|
2015-04-24 15:44:59 -04:00
|
|
|
end
|
|
|
|
|
2015-04-10 16:29:13 -04:00
|
|
|
def self.new_count
|
2015-04-27 11:56:07 -04:00
|
|
|
new_posts.visible.count
|
2015-04-20 17:19:05 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def visible?
|
|
|
|
QueuedPost.visible_queues.include?(queue)
|
2015-04-10 16:29:13 -04:00
|
|
|
end
|
|
|
|
|
2015-04-10 17:00:50 -04:00
|
|
|
def self.broadcast_new!
|
2015-04-10 16:29:13 -04:00
|
|
|
msg = { post_queue_new_count: QueuedPost.new_count }
|
2015-05-03 22:21:00 -04:00
|
|
|
MessageBus.publish('/queue_counts', msg, user_ids: User.staff.pluck(:id))
|
2015-04-10 16:29:13 -04:00
|
|
|
end
|
|
|
|
|
2015-03-25 14:24:34 -04:00
|
|
|
def reject!(rejected_by)
|
|
|
|
change_to!(:rejected, rejected_by)
|
2018-06-01 10:12:31 -04:00
|
|
|
StaffActionLogger.new(rejected_by).log_post_rejected(self)
|
2015-04-27 11:56:07 -04:00
|
|
|
DiscourseEvent.trigger(:rejected_post, self)
|
2015-03-25 14:24:34 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def create_options
|
2017-07-27 21:20:09 -04:00
|
|
|
opts = { raw: raw }
|
2015-04-15 14:32:10 -04:00
|
|
|
opts.merge!(post_options.symbolize_keys)
|
2015-03-25 14:24:34 -04:00
|
|
|
|
2015-03-26 16:57:50 -04:00
|
|
|
opts[:cooking_options].symbolize_keys! if opts[:cooking_options]
|
2015-03-25 14:24:34 -04:00
|
|
|
opts[:topic_id] = topic_id if topic_id
|
|
|
|
opts
|
|
|
|
end
|
|
|
|
|
|
|
|
def approve!(approved_by)
|
|
|
|
created_post = nil
|
2016-08-23 16:07:38 -04:00
|
|
|
|
2018-01-03 04:24:01 -05:00
|
|
|
creator = PostCreator.new(user, create_options.merge(
|
|
|
|
skip_validations: true,
|
|
|
|
skip_jobs: true,
|
|
|
|
skip_events: true
|
|
|
|
))
|
|
|
|
|
2015-03-25 14:24:34 -04:00
|
|
|
QueuedPost.transaction do
|
|
|
|
change_to!(:approved, approved_by)
|
|
|
|
|
2017-11-10 12:18:08 -05:00
|
|
|
UserSilencer.unsilence(user, approved_by) if user.silenced?
|
2015-08-04 20:49:59 -04:00
|
|
|
|
2015-03-25 14:24:34 -04:00
|
|
|
created_post = creator.create
|
2015-08-03 22:56:20 -04:00
|
|
|
|
2015-08-04 20:47:38 -04:00
|
|
|
unless created_post && creator.errors.blank?
|
2016-06-24 04:36:27 -04:00
|
|
|
raise StandardError.new(creator.errors.full_messages.join(" "))
|
2018-04-23 01:48:53 -04:00
|
|
|
else
|
|
|
|
# Log post approval
|
|
|
|
StaffActionLogger.new(approved_by).log_post_approved(created_post)
|
2015-08-04 20:47:38 -04:00
|
|
|
end
|
2015-03-25 14:24:34 -04:00
|
|
|
end
|
2015-04-27 11:56:07 -04:00
|
|
|
|
2019-02-13 23:42:40 -05:00
|
|
|
if create_options[:tags].present? &&
|
|
|
|
created_post.post_number == 1 &&
|
|
|
|
created_post.topic.tags.blank?
|
|
|
|
DiscourseTagging.tag_topic_by_names(created_post.topic, Guardian.new(approved_by), create_options[:tags])
|
|
|
|
end
|
|
|
|
|
2016-08-23 16:07:38 -04:00
|
|
|
# Do sidekiq work outside of the transaction
|
|
|
|
creator.enqueue_jobs
|
2018-01-03 04:24:01 -05:00
|
|
|
creator.trigger_after_events
|
2016-08-23 16:07:38 -04:00
|
|
|
|
2017-01-26 00:29:43 -05:00
|
|
|
DiscourseEvent.trigger(:approved_post, self, created_post)
|
2015-03-25 14:24:34 -04:00
|
|
|
created_post
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
2015-03-26 16:57:50 -04:00
|
|
|
|
2018-06-07 01:28:18 -04:00
|
|
|
def change_to!(state, changed_by)
|
|
|
|
state_val = QueuedPost.states[state]
|
2015-03-25 14:24:34 -04:00
|
|
|
|
2018-06-07 01:28:18 -04:00
|
|
|
updates = { state: state_val,
|
|
|
|
"#{state}_by_id" => changed_by.id,
|
|
|
|
"#{state}_at" => Time.now }
|
2015-03-25 14:24:34 -04:00
|
|
|
|
2018-06-07 01:28:18 -04:00
|
|
|
# We use an update with `row_count` trick here to avoid stampeding requests to
|
|
|
|
# update the same row simultaneously. Only one state change should go through and
|
|
|
|
# we can use the DB to enforce this
|
|
|
|
row_count = QueuedPost.where('id = ? AND state <> ?', id, state_val).update_all(updates)
|
|
|
|
raise InvalidStateTransition.new if row_count == 0
|
2015-03-25 14:24:34 -04:00
|
|
|
|
2018-06-07 01:28:18 -04:00
|
|
|
if [:rejected, :approved].include?(state)
|
|
|
|
UserAction.where(queued_post_id: id).destroy_all
|
|
|
|
end
|
2015-04-24 15:25:47 -04:00
|
|
|
|
2018-06-07 01:28:18 -04:00
|
|
|
# Update the record in memory too, and clear the dirty flag
|
|
|
|
updates.each { |k, v| send("#{k}=", v) }
|
|
|
|
changes_applied
|
2015-04-10 16:29:13 -04:00
|
|
|
|
2018-06-07 01:28:18 -04:00
|
|
|
QueuedPost.broadcast_new! if visible?
|
|
|
|
end
|
2015-03-25 14:24:34 -04:00
|
|
|
|
|
|
|
end
|
2015-09-17 20:41:10 -04:00
|
|
|
|
|
|
|
# == Schema Information
|
|
|
|
#
|
|
|
|
# Table name: queued_posts
|
|
|
|
#
|
|
|
|
# id :integer not null, primary key
|
2019-01-11 14:29:56 -05:00
|
|
|
# queue :string not null
|
2015-09-17 20:41:10 -04:00
|
|
|
# state :integer not null
|
|
|
|
# user_id :integer not null
|
|
|
|
# raw :text not null
|
|
|
|
# post_options :json not null
|
|
|
|
# topic_id :integer
|
|
|
|
# approved_by_id :integer
|
|
|
|
# approved_at :datetime
|
|
|
|
# rejected_by_id :integer
|
|
|
|
# rejected_at :datetime
|
2019-01-11 14:29:56 -05:00
|
|
|
# created_at :datetime not null
|
|
|
|
# updated_at :datetime not null
|
2015-09-17 20:41:10 -04:00
|
|
|
#
|
|
|
|
# Indexes
|
|
|
|
#
|
|
|
|
# by_queue_status (queue,state,created_at)
|
|
|
|
# by_queue_status_topic (topic_id,queue,state,created_at)
|
|
|
|
#
|