eBooru/app/models/comment.rb
2024-11-10 20:22:40 -08:00

228 lines
7.7 KiB
Ruby

# frozen_string_literal: true
class Comment < ApplicationRecord
RECENT_COUNT = 6
include UserWarnable
simple_versioning
belongs_to_creator
belongs_to_updater
normalizes :body, with: ->(body) { body.gsub("\r\n", "\n") }
validate :validate_post_exists, on: :create
validate :validate_creator_is_not_limited, on: :create
validate :post_not_comment_locked, on: :create
validates :body, presence: { message: "has no content" }
validates :body, length: { minimum: 1, maximum: Danbooru.config.comment_max_size }
after_create :update_last_commented_at_on_create
after_update(if: ->(rec) { !rec.saved_change_to_is_hidden? && CurrentUser.id != rec.creator_id }) do |rec|
ModAction.log(:comment_update, { comment_id: rec.id, user_id: rec.creator_id })
end
after_destroy :update_last_commented_at_on_destroy
after_destroy do |rec|
ModAction.log(:comment_delete, { comment_id: rec.id, user_id: rec.creator_id })
end
after_save :update_last_commented_at_on_destroy, if: ->(rec) { rec.is_hidden? && rec.saved_change_to_is_hidden? }
after_save(if: ->(rec) { rec.saved_change_to_is_hidden? && CurrentUser.id != rec.creator_id }) do |rec|
action = rec.is_hidden? ? :comment_hide : :comment_unhide
ModAction.log(action, { comment_id: rec.id, user_id: rec.creator_id })
end
user_status_counter :comment_count
belongs_to :post, counter_cache: :comment_count
belongs_to :warning_user, class_name: "User", optional: true
has_many :votes, :class_name => "CommentVote", :dependent => :destroy
scope :deleted, -> { where(is_hidden: true) }
scope :undeleted, -> { where(is_hidden: false) }
scope :stickied, -> { where(is_sticky: true) }
module SearchMethods
def recent
reorder("comments.id desc").limit(RECENT_COUNT)
end
def hidden(user)
if user.is_moderator?
where("not(comments.score >= ? or comments.is_sticky = true)", user.comment_threshold)
elsif user.is_janitor?
where("not((comments.score >= ? or comments.is_sticky = true) and (comments.is_sticky = true or comments.is_hidden = false or comments.creator_id = ?))", user.comment_threshold, user.id)
else
where("not((comments.score >= ? or comments.is_sticky = true) and (comments.is_hidden = false or comments.creator_id = ?))", user.comment_threshold, user.id)
end
end
def visible(user)
q = where("comments.score >= ? or comments.is_sticky = true", user.comment_threshold)
unless user.is_moderator?
q = q.joins(:post).where("comments.is_sticky = true or posts.is_comment_disabled = false or comments.creator_id = ?", user.id)
if user.is_janitor?
q = q.where("comments.is_sticky = true or comments.is_hidden = false or comments.creator_id = ?", user.id)
else
q = q.where("comments.is_hidden = false or comments.creator_id = ?", user.id)
end
end
q
end
def post_tags_match(query)
where(post_id: Post.tag_match_sql(query).order(id: :desc).limit(300))
end
def for_creator(user_id)
user_id.present? ? where("creator_id = ?", user_id) : none
end
def search(params)
q = super.includes(:creator).includes(:updater).includes(:post)
q = q.attribute_matches(:body, params[:body_matches])
if params[:post_id].present?
q = q.where("post_id in (?)", params[:post_id].split(",").map(&:to_i))
end
if params[:post_tags_match].present?
q = q.post_tags_match(params[:post_tags_match])
end
with_resolved_user_ids(:post_note_updater, params) do |user_ids|
q = q.where(post_id: NoteVersion.select(:post_id).where(updater_id: user_ids))
end
q = q.where_user(:creator_id, :creator, params)
if params[:ip_addr].present?
q = q.where("creator_ip_addr <<= ?", params[:ip_addr])
end
q = q.attribute_matches(:is_hidden, params[:is_hidden])
q = q.attribute_matches(:is_sticky, params[:is_sticky])
q = q.attribute_matches(:do_not_bump_post, params[:do_not_bump_post])
case params[:order]
when "post_id", "post_id_desc"
q = q.order("comments.post_id DESC, comments.created_at DESC")
when "score", "score_desc"
q = q.order("comments.score DESC, comments.created_at DESC")
when "updated_at", "updated_at_desc"
q = q.order("comments.updated_at DESC")
else
# Force a better query plan
if %i[body_matches creator_name creator_id].any? { |key| params[key].present? }
q = q.order(created_at: :desc)
else
q = q.apply_basic_order(params)
end
end
q.where_user(:"posts.uploader_id", :poster, params) do |condition, _ids|
condition = condition.joins(:post)
# Force a better query plan by ordering by created_at
condition.reorder("comments.created_at desc")
end
end
end
extend SearchMethods
def validate_post_exists
errors.add(:post, "must exist") unless Post.exists?(post_id)
end
def validate_creator_is_not_limited
allowed = creator.can_comment_with_reason
if allowed != true
errors.add(:creator, User.throttle_reason(allowed))
return false
end
true
end
def post_not_comment_locked
return if CurrentUser.is_moderator?
post = Post.find_by(id: post_id)
return if post.blank?
errors.add(:base, "Post has comments locked") if post.is_comment_locked?
errors.add(:base, "Post has comments disabled") if post.is_comment_disabled?
end
def update_last_commented_at_on_create
post = Post.find(post_id)
return unless post
post.update_column(:last_commented_at, created_at)
if Comment.where("post_id = ?", post_id).count <= Danbooru.config.comment_threshold && !do_not_bump_post?
post.update_column(:last_comment_bumped_at, created_at)
end
post.update_index
true
end
def update_last_commented_at_on_destroy
post = Post.find(post_id)
return unless post
other_comments = Comment.where("post_id = ? and id <> ?", post_id, id).order("id DESC")
if other_comments.count == 0
post.update_columns(:last_commented_at => nil)
else
post.update_columns(:last_commented_at => other_comments.first.created_at)
end
other_comments = other_comments.where("do_not_bump_post = FALSE")
if other_comments.count == 0
post.update_columns(:last_comment_bumped_at => nil)
else
post.update_columns(:last_comment_bumped_at => other_comments.first.created_at)
end
post.update_index
true
end
def below_threshold?(user = CurrentUser.user)
score < user.comment_threshold
end
def can_reply?(user)
return false if is_sticky?
return false if (post.is_comment_locked? || post.is_comment_disabled?) && !user.is_moderator?
true
end
def editable_by?(user)
return true if user.is_admin?
return false if (post.is_comment_locked? || post.is_comment_disabled?) && !user.is_moderator?
return false if was_warned?
creator_id == user.id
end
def can_hide?(user)
return true if user.is_moderator?
return false if !visible_to?(user) || was_warned? || post&.is_comment_disabled?
user.id == creator_id
end
def visible_to?(user)
return true if user.is_moderator?
return false if !is_sticky? && (post&.is_comment_disabled? && creator_id != user.id)
return true if user.is_janitor? && is_sticky?
return true if is_hidden? == false
creator_id == user.id # Can always see your own comments, even if hidden.
end
def should_see?(user)
return user.show_hidden_comments? if creator_id == user.id && is_hidden?
visible_to?(user)
end
def method_attributes
super + [:creator_name, :updater_name]
end
def hide!
update(is_hidden: true)
end
def unhide!
update(is_hidden: false)
end
end