Files
danbooru/app/models/post_flag.rb
evazion eacb4d4df3 models: factor out api_attributes to policies.
Refactor models so that we define attribute API permissions in policy
files instead of directly in models.

This is cleaner because a) permissions are better handled by policies
and b) which attributes are visible to the API is an API-level concern
that models shouldn't have to care about.

This fixes an issue with not being able to precompile CSS/JS assets
unless the database was up and running. This was a problem when building
Docker images because we don't have a database at build time. We needed
the database because `api_attributes` was a class-level macro in some
places, which meant it ran at boot time, but this triggered a database
call because api_attributes used database introspection to get the list
of allowed API attributes.
2020-06-08 18:38:02 -05:00

136 lines
3.9 KiB
Ruby

class PostFlag < ApplicationRecord
class Error < StandardError; end
module Reasons
UNAPPROVED = "Unapproved in three days"
REJECTED = "Unapproved in three days after returning to moderation queue%"
end
COOLDOWN_PERIOD = 3.days
belongs_to :creator, class_name: "User"
belongs_to :post
validates :reason, presence: true, length: { in: 1..140 }
validate :validate_creator_is_not_limited, on: :create
validate :validate_post
validates_uniqueness_of :creator_id, :scope => :post_id, :on => :create, :unless => :is_deletion, :message => "have already flagged this post"
before_save :update_post
attr_accessor :is_deletion
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) }
scope :resolved, -> { where(is_resolved: true) }
scope :unresolved, -> { where(is_resolved: false) }
scope :recent, -> { where("post_flags.created_at >= ?", 1.day.ago) }
scope :old, -> { where("post_flags.created_at <= ?", 3.days.ago) }
module SearchMethods
def creator_matches(creator, searcher)
return none if creator.nil?
policy = Pundit.policy!([searcher, nil], PostFlag.new(creator: creator))
if policy.can_view_flagger?
where(creator: creator).where.not(post: searcher.posts)
else
none
end
end
def category_matches(category)
case category
when "normal"
where("reason NOT IN (?) AND reason NOT LIKE ?", [Reasons::UNAPPROVED], Reasons::REJECTED)
when "unapproved"
where(reason: Reasons::UNAPPROVED)
when "rejected"
where("reason LIKE ?", Reasons::REJECTED)
when "deleted"
where("reason = ? OR reason LIKE ?", Reasons::UNAPPROVED, Reasons::REJECTED)
else
none
end
end
def search(params)
q = super
q = q.search_attributes(params, :post, :is_resolved, :reason)
q = q.text_attribute_matches(:reason, params[:reason_matches])
if params[:creator_id].present?
flagger = User.find(params[:creator_id])
q = q.creator_matches(flagger, CurrentUser.user)
elsif params[:creator_name].present?
flagger = User.find_by_name(params[:creator_name])
q = q.creator_matches(flagger, CurrentUser.user)
end
if params[:category]
q = q.category_matches(params[:category])
end
q.apply_default_order(params)
end
end
extend SearchMethods
def category
case reason
when Reasons::UNAPPROVED
:unapproved
when /#{Reasons::REJECTED.gsub("%", ".*")}/
:rejected
else
:normal
end
end
def update_post
post.update_column(:is_flagged, true) unless post.is_flagged?
end
def validate_creator_is_not_limited
return if is_deletion
if creator.can_approve_posts?
# do nothing
elsif creator.created_at > 1.week.ago
errors[:creator] << "cannot flag within the first week of sign up"
elsif creator.is_gold? && flag_count_for_creator >= 10
errors[:creator] << "can flag 10 posts a day"
elsif !creator.is_gold? && flag_count_for_creator >= 1
errors[:creator] << "can flag 1 post a day"
end
flag = post.flags.in_cooldown.last
if flag.present?
errors[:post] << "cannot be flagged more than once every #{COOLDOWN_PERIOD.inspect} (last flagged: #{flag.created_at.to_s(:long)})"
end
end
def validate_post
errors[:post] << "is pending and cannot be flagged" if post.is_pending? && !is_deletion
errors[:post] << "is locked and cannot be flagged" if post.is_status_locked?
errors[:post] << "is deleted" if post.is_deleted?
end
def resolve!
update_column(:is_resolved, true)
end
def flag_count_for_creator
creator.post_flags.recent.count
end
def uploader_id
post.uploader_id
end
def self.available_includes
[:post]
end
end