forked from e621ng/e621ng
192 lines
5.2 KiB
Ruby
192 lines
5.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class PostFlag < ApplicationRecord
|
|
class Error < Exception;
|
|
end
|
|
|
|
COOLDOWN_PERIOD = 1.days
|
|
MAPPED_REASONS = Danbooru.config.flag_reasons.map { |i| [i[:name], i[:reason]] }.to_h
|
|
|
|
belongs_to_creator :class_name => "User"
|
|
user_status_counter :post_flag_count
|
|
belongs_to :post
|
|
validate :validate_creator_is_not_limited, on: :create
|
|
validate :validate_post
|
|
validate :validate_reason
|
|
validate :update_reason, on: :create
|
|
validates :reason, presence: true
|
|
before_save :update_post
|
|
after_create :create_post_event
|
|
after_commit :index_post
|
|
|
|
scope :by_users, -> { where.not(creator: User.system) }
|
|
scope :by_system, -> { where(creator: User.system) }
|
|
scope :in_cooldown, -> { by_users.where("created_at >= ?", COOLDOWN_PERIOD.ago) }
|
|
|
|
attr_accessor :parent_id, :reason_name, :force_flag
|
|
|
|
module SearchMethods
|
|
def post_tags_match(query)
|
|
where(post_id: Post.tag_match_sql(query))
|
|
end
|
|
|
|
def resolved
|
|
where("is_resolved = ?", true)
|
|
end
|
|
|
|
def unresolved
|
|
where("is_resolved = ?", false)
|
|
end
|
|
|
|
def for_creator(user_id)
|
|
where("creator_id = ?", user_id)
|
|
end
|
|
|
|
def search(params)
|
|
q = super
|
|
|
|
q = q.attribute_matches(:reason, params[:reason_matches])
|
|
q = q.attribute_matches(:is_resolved, params[:is_resolved])
|
|
|
|
q = q.where_user(:creator_id, :creator, params) do |condition, user_ids|
|
|
condition.where.not(creator_id: user_ids.reject { |user_id| CurrentUser.can_view_flagger?(user_id) })
|
|
end
|
|
|
|
if params[:post_id].present?
|
|
q = q.where(post_id: params[:post_id].split(",").map(&:to_i))
|
|
end
|
|
|
|
if params[:post_tags_match].present?
|
|
q = q.post_tags_match(params[:post_tags_match])
|
|
end
|
|
|
|
if params[:ip_addr].present?
|
|
q = q.where("creator_ip_addr <<= ?", params[:ip_addr])
|
|
end
|
|
|
|
case params[:type]
|
|
when "flag"
|
|
q = q.where(is_deletion: false)
|
|
when "deletion"
|
|
q = q.where(is_deletion: true)
|
|
end
|
|
|
|
q.apply_basic_order(params)
|
|
end
|
|
end
|
|
|
|
module ApiMethods
|
|
def hidden_attributes
|
|
list = super
|
|
unless CurrentUser.can_view_flagger_on_post?(self)
|
|
list += [:creator_id]
|
|
end
|
|
super + list
|
|
end
|
|
|
|
def method_attributes
|
|
super + [:type]
|
|
end
|
|
end
|
|
|
|
extend SearchMethods
|
|
include ApiMethods
|
|
|
|
def type
|
|
return :deletion if is_deletion
|
|
:flag
|
|
end
|
|
|
|
def update_post
|
|
post.update_column(:is_flagged, true) unless post.is_flagged?
|
|
end
|
|
|
|
def index_post
|
|
post.update_index
|
|
end
|
|
|
|
def validate_creator_is_not_limited
|
|
return if is_deletion
|
|
|
|
if creator.no_flagging?
|
|
errors.add(:creator, "cannot flag posts")
|
|
end
|
|
|
|
return if creator.is_janitor?
|
|
|
|
allowed = creator.can_post_flag_with_reason
|
|
if allowed != true
|
|
errors.add(:creator, User.throttle_reason(allowed))
|
|
return false
|
|
end
|
|
|
|
flag = post.flags.in_cooldown.last
|
|
if flag.present?
|
|
errors.add(:post, "cannot be flagged more than once every #{COOLDOWN_PERIOD.inspect} (last flagged: #{flag.created_at.to_fs(:long)})")
|
|
end
|
|
end
|
|
|
|
def validate_post
|
|
errors.add(:post, "is locked and cannot be flagged") if post.is_status_locked? && !(creator.is_admin? || force_flag)
|
|
errors.add(:post, "is deleted") if post.is_deleted?
|
|
end
|
|
|
|
def validate_reason
|
|
case reason_name
|
|
when 'deletion'
|
|
# You're probably looking at this line as you get this validation failure
|
|
errors.add(:reason, "is not one of the available choices") unless is_deletion
|
|
when 'inferior'
|
|
unless parent_post.present?
|
|
errors.add(:parent_id, "must exist")
|
|
return false
|
|
end
|
|
errors.add(:parent_id, "cannot be set to the post being flagged") if parent_post.id == post.id
|
|
when 'uploading_guidelines'
|
|
errors.add(:reason, "cannot be used. The post is grandfathered") unless post.flaggable_for_guidelines?
|
|
else
|
|
errors.add(:reason, "is not one of the available choices") unless MAPPED_REASONS.key?(reason_name)
|
|
end
|
|
end
|
|
|
|
def update_reason
|
|
case reason_name
|
|
when 'deletion'
|
|
# NOP
|
|
when 'inferior'
|
|
return unless parent_post
|
|
old_parent_id = post.parent_id
|
|
post.update_column(:parent_id, parent_post.id)
|
|
# Fix handling when parent/child is currently inverted. See #258
|
|
if parent_post.parent_id == post.id
|
|
parent_post.update_column(:parent_id, nil)
|
|
post.update_has_children_flag
|
|
end
|
|
# Update parent flags on parent post
|
|
parent_post.update_has_children_flag
|
|
# Update parent flags on old parent post, if it exists
|
|
Post.find(old_parent_id).update_has_children_flag if old_parent_id && parent_post.id != old_parent_id
|
|
self.reason = "Inferior version/duplicate of post ##{parent_post.id}"
|
|
else
|
|
self.reason = MAPPED_REASONS[reason_name]
|
|
end
|
|
end
|
|
|
|
def resolve!
|
|
update_column(:is_resolved, true)
|
|
end
|
|
|
|
def parent_post
|
|
@parent_post ||= begin
|
|
Post.where('id = ?', parent_id).first
|
|
rescue
|
|
nil
|
|
end
|
|
end
|
|
|
|
def create_post_event
|
|
# Deletions also create flags, but they create a deletion event instead
|
|
PostEvent.add(post.id, CurrentUser.user, :flag_created, { reason: reason }) unless is_deletion
|
|
end
|
|
end
|