Merge branch 'master' into attribute-searching
This commit is contained in:
@@ -127,6 +127,10 @@ class ApplicationRecord < ActiveRecord::Base
|
||||
ensure
|
||||
connection.execute("SET STATEMENT_TIMEOUT = #{CurrentUser.user.try(:statement_timeout) || 3_000}") unless Rails.env == "test"
|
||||
end
|
||||
|
||||
def update!(*args)
|
||||
all.each { |record| record.update!(*args) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -6,11 +6,15 @@ class Ban < ApplicationRecord
|
||||
after_destroy :create_unban_mod_action
|
||||
belongs_to :user
|
||||
belongs_to :banner, :class_name => "User"
|
||||
|
||||
validates_presence_of :reason, :duration
|
||||
validate :user, :validate_user_is_bannable, on: :create
|
||||
|
||||
scope :unexpired, -> { where("bans.expires_at > ?", Time.now) }
|
||||
scope :expired, -> { where("bans.expires_at <= ?", Time.now) }
|
||||
|
||||
attr_reader :duration
|
||||
|
||||
def self.is_banned?(user)
|
||||
exists?(["user_id = ? AND expires_at > ?", user.id, Time.now])
|
||||
end
|
||||
@@ -48,6 +52,10 @@ class Ban < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def validate_user_is_bannable
|
||||
self.errors[:user] << "is already banned" if user.is_banned?
|
||||
end
|
||||
|
||||
def update_user_on_create
|
||||
user.update!(is_banned: true)
|
||||
end
|
||||
@@ -69,8 +77,6 @@ class Ban < ApplicationRecord
|
||||
@duration = dur
|
||||
end
|
||||
|
||||
attr_reader :duration
|
||||
|
||||
def humanized_duration
|
||||
ApplicationController.helpers.distance_of_time_in_words(created_at, expires_at)
|
||||
end
|
||||
|
||||
@@ -7,7 +7,8 @@ class Post < ApplicationRecord
|
||||
class TimeoutError < StandardError; end
|
||||
|
||||
# Tags to copy when copying notes.
|
||||
NOTE_COPY_TAGS = %w[translated partially_translated check_translation translation_request reverse_translation]
|
||||
NOTE_COPY_TAGS = %w[translated partially_translated check_translation translation_request reverse_translation
|
||||
annotated partially_annotated check_annotation annotation_request]
|
||||
|
||||
deletable
|
||||
|
||||
@@ -61,8 +62,10 @@ class Post < ApplicationRecord
|
||||
scope :pending, -> { where(is_pending: true) }
|
||||
scope :flagged, -> { where(is_flagged: true) }
|
||||
scope :banned, -> { where(is_banned: true) }
|
||||
scope :active, -> { where(is_pending: false, is_deleted: false, is_flagged: false) }
|
||||
scope :pending_or_flagged, -> { pending.or(flagged) }
|
||||
scope :active, -> { where(is_pending: false, is_deleted: false, is_flagged: false).where.not(id: PostAppeal.pending) }
|
||||
scope :appealed, -> { deleted.where(id: PostAppeal.pending.select(:post_id)) }
|
||||
scope :in_modqueue, -> { pending.or(flagged).or(appealed) }
|
||||
scope :expired, -> { pending.where("posts.created_at < ?", Danbooru.config.moderation_period.ago) }
|
||||
|
||||
scope :unflagged, -> { where(is_flagged: false) }
|
||||
scope :has_notes, -> { where.not(last_noted_at: nil) }
|
||||
@@ -237,9 +240,9 @@ class Post < ApplicationRecord
|
||||
|
||||
def large_image_width
|
||||
if has_large?
|
||||
[Danbooru.config.large_image_width, image_width].min
|
||||
[Danbooru.config.large_image_width, image_width.to_i].min
|
||||
else
|
||||
image_width
|
||||
image_width.to_i
|
||||
end
|
||||
end
|
||||
|
||||
@@ -269,6 +272,7 @@ class Post < ApplicationRecord
|
||||
end
|
||||
|
||||
def resize_percentage
|
||||
return 100 if image_width.to_i == 0
|
||||
100 * large_image_width.to_f / image_width.to_f
|
||||
end
|
||||
|
||||
@@ -279,12 +283,28 @@ class Post < ApplicationRecord
|
||||
end
|
||||
|
||||
module ApprovalMethods
|
||||
def in_modqueue?
|
||||
is_pending? || is_flagged? || is_appealed?
|
||||
end
|
||||
|
||||
def is_active?
|
||||
!is_deleted? && !in_modqueue?
|
||||
end
|
||||
|
||||
def is_appealed?
|
||||
is_deleted? && appeals.any?(&:pending?)
|
||||
end
|
||||
|
||||
def is_appealable?
|
||||
is_deleted? && !is_appealed?
|
||||
end
|
||||
|
||||
def is_approvable?(user = CurrentUser.user)
|
||||
!is_status_locked? && (is_pending? || is_flagged? || is_deleted?) && uploader != user
|
||||
!is_status_locked? && !is_active? && uploader != user
|
||||
end
|
||||
|
||||
def flag!(reason, is_deletion: false)
|
||||
flag = flags.create(reason: reason, is_resolved: false, is_deletion: is_deletion, creator: CurrentUser.user)
|
||||
flag = flags.create(reason: reason, is_deletion: is_deletion, creator: CurrentUser.user)
|
||||
|
||||
if flag.errors.any?
|
||||
raise PostFlag::Error.new(flag.errors.full_messages.join("; "))
|
||||
@@ -375,12 +395,6 @@ class Post < ApplicationRecord
|
||||
def update_tag_post_counts
|
||||
decrement_tags = tag_array_was - tag_array
|
||||
|
||||
decrement_tags_except_requests = decrement_tags.reject {|tag| tag == "tagme" || tag.end_with?("_request")}
|
||||
if !decrement_tags_except_requests.empty? && !CurrentUser.is_builder? && CurrentUser.created_at > 1.week.ago
|
||||
self.errors.add(:updater_id, "must have an account at least 1 week old to remove tags")
|
||||
return false
|
||||
end
|
||||
|
||||
increment_tags = tag_array - tag_array_was
|
||||
if increment_tags.any?
|
||||
Tag.increment_post_counts(increment_tags)
|
||||
@@ -398,24 +412,16 @@ class Post < ApplicationRecord
|
||||
set_tag_count(category, self.send("tag_count_#{category}") + 1)
|
||||
end
|
||||
|
||||
def set_tag_counts(disable_cache = true)
|
||||
def set_tag_counts
|
||||
self.tag_count = 0
|
||||
TagCategory.categories.each {|x| set_tag_count(x, 0)}
|
||||
categories = Tag.categories_for(tag_array, :disable_caching => disable_cache)
|
||||
categories = Tag.categories_for(tag_array, disable_caching: true)
|
||||
categories.each_value do |category|
|
||||
self.tag_count += 1
|
||||
inc_tag_count(TagCategory.reverse_mapping[category])
|
||||
end
|
||||
end
|
||||
|
||||
def fix_post_counts(post)
|
||||
post.set_tag_counts(false)
|
||||
if post.changes_saved?
|
||||
args = Hash[TagCategory.categories.map {|x| ["tag_count_#{x}", post.send("tag_count_#{x}")]}].update(:tag_count => post.tag_count)
|
||||
post.update_columns(args)
|
||||
end
|
||||
end
|
||||
|
||||
def merge_old_changes
|
||||
reset_tag_array_cache
|
||||
@removed_tags = []
|
||||
@@ -932,14 +938,7 @@ class Post < ApplicationRecord
|
||||
end
|
||||
|
||||
def update_children_on_destroy
|
||||
return unless children.present?
|
||||
|
||||
eldest = children[0]
|
||||
siblings = children[1..-1]
|
||||
|
||||
eldest.update(parent_id: nil)
|
||||
Post.where(id: siblings).find_each { |p| p.update(parent_id: eldest.id) }
|
||||
# Post.where(id: siblings).update(parent_id: eldest.id) # XXX rails 5
|
||||
children.update(parent: nil)
|
||||
end
|
||||
|
||||
def update_parent_on_save
|
||||
@@ -949,7 +948,7 @@ class Post < ApplicationRecord
|
||||
Post.find(parent_id_before_last_save).update_has_children_flag if parent_id_before_last_save.present?
|
||||
end
|
||||
|
||||
def give_favorites_to_parent(options = {})
|
||||
def give_favorites_to_parent
|
||||
return if parent.nil?
|
||||
|
||||
transaction do
|
||||
@@ -959,9 +958,7 @@ class Post < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
unless options[:without_mod_action]
|
||||
ModAction.log("moved favorites from post ##{id} to post ##{parent.id}", :post_move_favorites)
|
||||
end
|
||||
ModAction.log("moved favorites from post ##{id} to post ##{parent.id}", :post_move_favorites)
|
||||
end
|
||||
|
||||
def has_visible_children?
|
||||
@@ -985,9 +982,8 @@ class Post < ApplicationRecord
|
||||
|
||||
transaction do
|
||||
Post.without_timeout do
|
||||
ModAction.log("permanently deleted post ##{id}", :post_permanent_delete)
|
||||
ModAction.log("permanently deleted post ##{id} (md5=#{md5})", :post_permanent_delete)
|
||||
|
||||
give_favorites_to_parent
|
||||
update_children_on_destroy
|
||||
decrement_tag_post_counts
|
||||
remove_from_all_pools
|
||||
@@ -1009,29 +1005,22 @@ class Post < ApplicationRecord
|
||||
ModAction.log("unbanned post ##{id}", :post_unban)
|
||||
end
|
||||
|
||||
def delete!(reason, options = {})
|
||||
if is_status_locked?
|
||||
self.errors.add(:is_status_locked, "; cannot delete post")
|
||||
return false
|
||||
end
|
||||
def delete!(reason, move_favorites: false, user: CurrentUser.user)
|
||||
transaction do
|
||||
automated = (user == User.system)
|
||||
|
||||
Post.transaction do
|
||||
flag!(reason, is_deletion: true)
|
||||
flags.pending.update!(status: :succeeded)
|
||||
appeals.pending.update!(status: :rejected)
|
||||
|
||||
update(
|
||||
is_deleted: true,
|
||||
is_pending: false,
|
||||
is_flagged: false,
|
||||
is_banned: is_banned || options[:ban] || has_tag?("banned_artist")
|
||||
)
|
||||
flags.create!(reason: reason, is_deletion: true, creator: user, status: :succeeded)
|
||||
update!(is_deleted: true, is_pending: false, is_flagged: false)
|
||||
|
||||
# XXX This must happen *after* the `is_deleted` flag is set to true (issue #3419).
|
||||
give_favorites_to_parent(options) if options[:move_favorites]
|
||||
give_favorites_to_parent if move_favorites
|
||||
|
||||
is_automatic = (reason == "Unapproved in three days")
|
||||
uploader.upload_limit.update_limit!(self, incremental: is_automatic)
|
||||
uploader.upload_limit.update_limit!(self, incremental: automated)
|
||||
|
||||
unless options[:without_mod_action]
|
||||
unless automated
|
||||
ModAction.log("deleted post ##{id}, reason: #{reason}", :post_delete)
|
||||
end
|
||||
end
|
||||
@@ -1213,8 +1202,6 @@ class Post < ApplicationRecord
|
||||
def with_flag_stats
|
||||
relation = left_outer_joins(:flags).group(:id).select("posts.*")
|
||||
relation = relation.select("COUNT(post_flags.id) AS flag_count")
|
||||
relation = relation.select("COUNT(post_flags.id) FILTER (WHERE post_flags.is_resolved = TRUE) AS resolved_flag_count")
|
||||
relation = relation.select("COUNT(post_flags.id) FILTER (WHERE post_flags.is_resolved = FALSE) AS unresolved_flag_count")
|
||||
relation
|
||||
end
|
||||
|
||||
@@ -1256,6 +1243,14 @@ class Post < ApplicationRecord
|
||||
relation
|
||||
end
|
||||
|
||||
def with_queued_at
|
||||
relation = group(:id)
|
||||
relation = relation.left_outer_joins(:flags, :appeals)
|
||||
relation = relation.select("posts.*")
|
||||
relation = relation.select(Arel.sql("MAX(GREATEST(posts.created_at, post_flags.created_at, post_appeals.created_at)) AS queued_at"))
|
||||
relation
|
||||
end
|
||||
|
||||
def with_stats(tables)
|
||||
return all if tables.empty?
|
||||
|
||||
|
||||
@@ -1,57 +1,38 @@
|
||||
class PostAppeal < ApplicationRecord
|
||||
class Error < StandardError; end
|
||||
|
||||
MAX_APPEALS_PER_DAY = 1
|
||||
|
||||
belongs_to :creator, :class_name => "User"
|
||||
belongs_to :post
|
||||
validates_presence_of :reason
|
||||
validates :reason, presence: true, length: { in: 1..140 }
|
||||
validate :validate_post_is_inactive
|
||||
validate :validate_creator_is_not_limited
|
||||
validates_uniqueness_of :creator_id, :scope => :post_id, :message => "have already appealed this post"
|
||||
|
||||
scope :resolved, -> { where(post: Post.undeleted.unflagged) }
|
||||
scope :unresolved, -> { where(post: Post.deleted.or(Post.flagged)) }
|
||||
scope :recent, -> { where("post_appeals.created_at >= ?", 1.day.ago) }
|
||||
validates :reason, length: { maximum: 140 }
|
||||
validate :validate_post_is_appealable, on: :create
|
||||
validate :validate_creator_is_not_limited, on: :create
|
||||
validates :creator, uniqueness: { scope: :post, message: "have already appealed this post" }, on: :create
|
||||
|
||||
enum status: {
|
||||
pending: 0,
|
||||
succeeded: 1,
|
||||
rejected: 2
|
||||
}
|
||||
|
||||
scope :expired, -> { pending.where("post_appeals.created_at < ?", Danbooru.config.moderation_period.ago) }
|
||||
|
||||
module SearchMethods
|
||||
def search(params)
|
||||
q = super
|
||||
q = q.search_attributes(params, :reason)
|
||||
q = q.search_attributes(params, :reason, :status)
|
||||
q = q.text_attribute_matches(:reason, params[:reason_matches])
|
||||
|
||||
q = q.resolved if params[:is_resolved].to_s.truthy?
|
||||
q = q.unresolved if params[:is_resolved].to_s.falsy?
|
||||
|
||||
q.apply_default_order(params)
|
||||
end
|
||||
end
|
||||
|
||||
extend SearchMethods
|
||||
|
||||
def resolved?
|
||||
post.present? && !post.is_deleted? && !post.is_flagged?
|
||||
end
|
||||
|
||||
def is_resolved
|
||||
resolved?
|
||||
end
|
||||
|
||||
def validate_creator_is_not_limited
|
||||
if appeal_count_for_creator >= MAX_APPEALS_PER_DAY
|
||||
errors[:creator] << "can appeal at most #{MAX_APPEALS_PER_DAY} post a day"
|
||||
end
|
||||
errors[:creator] << "have reached your appeal limit" if creator.is_appeal_limited?
|
||||
end
|
||||
|
||||
def validate_post_is_inactive
|
||||
if resolved?
|
||||
errors[:post] << "is active"
|
||||
end
|
||||
end
|
||||
|
||||
def appeal_count_for_creator
|
||||
creator.post_appeals.recent.count
|
||||
def validate_post_is_appealable
|
||||
errors[:post] << "cannot be appealed" if post.is_status_locked? || !post.is_appealable?
|
||||
end
|
||||
|
||||
def self.searchable_includes
|
||||
|
||||
@@ -12,7 +12,7 @@ class PostApproval < ApplicationRecord
|
||||
errors.add(:post, "is locked and cannot be approved")
|
||||
end
|
||||
|
||||
if post.status == "active"
|
||||
if post.is_active?
|
||||
errors.add(:post, "is already active and cannot be approved")
|
||||
end
|
||||
|
||||
@@ -28,7 +28,9 @@ class PostApproval < ApplicationRecord
|
||||
def approve_post
|
||||
is_undeletion = post.is_deleted
|
||||
|
||||
post.flags.each(&:resolve!)
|
||||
post.flags.pending.update!(status: :rejected)
|
||||
post.appeals.pending.update!(status: :succeeded)
|
||||
|
||||
post.update(approver: user, is_flagged: false, is_pending: false, is_deleted: false)
|
||||
ModAction.log("undeleted post ##{post_id}", :post_undelete) if is_undeletion
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class PostDisapproval < ApplicationRecord
|
||||
end
|
||||
|
||||
def validate_disapproval
|
||||
if post.status == "active"
|
||||
if post.is_active?
|
||||
errors[:post] << "is already active and cannot be disapproved"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,10 +30,6 @@ class PostEvent
|
||||
event.try(:reason) || ""
|
||||
end
|
||||
|
||||
def is_resolved
|
||||
event.try(:is_resolved) || false
|
||||
end
|
||||
|
||||
def creator_id
|
||||
event.try(:creator_id) || event.try(:user_id)
|
||||
end
|
||||
@@ -42,6 +38,18 @@ class PostEvent
|
||||
event.try(:creator) || event.try(:user)
|
||||
end
|
||||
|
||||
def status
|
||||
if event.is_a?(PostApproval)
|
||||
"approved"
|
||||
elsif (event.is_a?(PostAppeal) && event.succeeded?) || (event.is_a?(PostFlag) && event.rejected?)
|
||||
"approved"
|
||||
elsif (event.is_a?(PostAppeal) && event.rejected?) || (event.is_a?(PostFlag) && event.succeeded?)
|
||||
"deleted"
|
||||
else
|
||||
"pending"
|
||||
end
|
||||
end
|
||||
|
||||
def is_creator_visible?(user = CurrentUser.user)
|
||||
case event
|
||||
when PostAppeal, PostApproval
|
||||
@@ -57,7 +65,7 @@ class PostEvent
|
||||
"creator_id": nil,
|
||||
"created_at": nil,
|
||||
"reason": nil,
|
||||
"is_resolved": nil,
|
||||
"status": nil,
|
||||
"type": nil
|
||||
}
|
||||
end
|
||||
|
||||
@@ -6,24 +6,26 @@ class PostFlag < ApplicationRecord
|
||||
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"
|
||||
validate :validate_post, on: :create
|
||||
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
|
||||
|
||||
enum status: {
|
||||
pending: 0,
|
||||
succeeded: 1,
|
||||
rejected: 2
|
||||
}
|
||||
|
||||
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) }
|
||||
scope :in_cooldown, -> { by_users.where("created_at >= ?", Danbooru.config.moderation_period.ago) }
|
||||
scope :expired, -> { pending.where("post_flags.created_at < ?", Danbooru.config.moderation_period.ago) }
|
||||
scope :active, -> { pending.or(rejected.in_cooldown) }
|
||||
|
||||
module SearchMethods
|
||||
def creator_matches(creator, searcher)
|
||||
@@ -56,7 +58,7 @@ class PostFlag < ApplicationRecord
|
||||
def search(params)
|
||||
q = super
|
||||
|
||||
q = q.search_attributes(params, :is_resolved, :reason)
|
||||
q = q.search_attributes(params, :reason, :status)
|
||||
q = q.text_attribute_matches(:reason, params[:reason_matches])
|
||||
|
||||
if params[:creator_id].present?
|
||||
@@ -93,36 +95,18 @@ class PostFlag < ApplicationRecord
|
||||
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
|
||||
errors[:creator] << "have reached your flag limit" if creator.is_flag_limited? && !is_deletion
|
||||
end
|
||||
|
||||
def validate_post
|
||||
errors[:post] << "is pending and cannot be flagged" if post.is_pending? && !is_deletion
|
||||
errors[:post] << "is deleted and cannot be flagged" if post.is_deleted? && !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
|
||||
flag = post.flags.in_cooldown.last
|
||||
if !is_deletion && flag.present?
|
||||
errors[:post] << "cannot be flagged more than once every #{Danbooru.config.moderation_period.inspect} (last flagged: #{flag.created_at.to_s(:long)})"
|
||||
end
|
||||
end
|
||||
|
||||
def uploader_id
|
||||
|
||||
@@ -15,12 +15,6 @@ class PostVersion < ApplicationRecord
|
||||
|
||||
establish_connection database_url if enabled?
|
||||
|
||||
def self.check_for_retry(msg)
|
||||
if msg =~ /can't get socket descriptor/ && msg =~ /post_versions/
|
||||
connection.reconnect!
|
||||
end
|
||||
end
|
||||
|
||||
module SearchMethods
|
||||
def changed_tags_include(tag)
|
||||
where_array_includes_all(:added_tags, [tag]).or(where_array_includes_all(:removed_tags, [tag]))
|
||||
@@ -32,6 +26,10 @@ class PostVersion < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def changed_tags_include_any(tags)
|
||||
where_array_includes_any(:added_tags, tags).or(where_array_includes_any(:removed_tags, tags))
|
||||
end
|
||||
|
||||
def tag_matches(string)
|
||||
tag = string.match(/\S+/)[0]
|
||||
return all if tag.nil?
|
||||
@@ -47,6 +45,14 @@ class PostVersion < ApplicationRecord
|
||||
q = q.changed_tags_include_all(params[:changed_tags].scan(/[^[:space:]]+/))
|
||||
end
|
||||
|
||||
if params[:all_changed_tags]
|
||||
q = q.changed_tags_include_all(params[:all_changed_tags].scan(/[^[:space:]]+/))
|
||||
end
|
||||
|
||||
if params[:any_changed_tags]
|
||||
q = q.changed_tags_include_any(params[:any_changed_tags].scan(/[^[:space:]]+/))
|
||||
end
|
||||
|
||||
if params[:tag_matches]
|
||||
q = q.tag_matches(params[:tag_matches])
|
||||
end
|
||||
|
||||
@@ -11,8 +11,8 @@ class Tag < ApplicationRecord
|
||||
validates :name, tag_name: true, on: :name
|
||||
validates_inclusion_of :category, in: TagCategory.category_ids
|
||||
|
||||
before_save :update_category_cache, if: :category_changed?
|
||||
before_save :update_category_post_counts, if: :category_changed?
|
||||
after_save :update_category_cache, if: :saved_change_to_category?
|
||||
after_save :update_category_post_counts, if: :saved_change_to_category?
|
||||
|
||||
scope :empty, -> { where("tags.post_count <= 0") }
|
||||
scope :nonempty, -> { where("tags.post_count > 0") }
|
||||
@@ -163,12 +163,10 @@ class Tag < ApplicationRecord
|
||||
end
|
||||
|
||||
def update_category_post_counts
|
||||
Post.with_timeout(30_000, nil, :tags => name) do
|
||||
Post.raw_tag_match(name).where("true /* Tag#update_category_post_counts */").find_each do |post|
|
||||
post.reload
|
||||
post.set_tag_counts(false)
|
||||
args = TagCategory.categories.map {|x| ["tag_count_#{x}", post.send("tag_count_#{x}")]}.to_h.update(:tag_count => post.tag_count)
|
||||
Post.where(:id => post.id).update_all(args)
|
||||
Post.with_timeout(30_000) do
|
||||
Post.raw_tag_match(name).find_each do |post|
|
||||
post.set_tag_counts
|
||||
post.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -265,6 +265,10 @@ class User < ApplicationRecord
|
||||
name.match?(/\Auser_[0-9]+~*\z/)
|
||||
end
|
||||
|
||||
def is_restricted?
|
||||
requires_verification? && !is_verified?
|
||||
end
|
||||
|
||||
def is_anonymous?
|
||||
level == Levels::ANONYMOUS
|
||||
end
|
||||
@@ -343,6 +347,26 @@ class User < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def is_appeal_limited?
|
||||
return false if can_upload_free?
|
||||
upload_limit.free_upload_slots < UploadLimit::APPEAL_COST
|
||||
end
|
||||
|
||||
def is_flag_limited?
|
||||
return false if has_unlimited_flags?
|
||||
post_flags.active.count >= 5
|
||||
end
|
||||
|
||||
# Flags are unlimited if you're an approver or you have at least 30 flags
|
||||
# in the last 3 months and have a 70% flag success rate.
|
||||
def has_unlimited_flags?
|
||||
return true if can_approve_posts?
|
||||
|
||||
recent_flags = post_flags.where("created_at >= ?", 3.months.ago)
|
||||
flag_ratio = recent_flags.succeeded.count / recent_flags.count.to_f
|
||||
recent_flags.count >= 30 && flag_ratio >= 0.70
|
||||
end
|
||||
|
||||
def upload_limit
|
||||
@upload_limit ||= UploadLimit.new(self)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user