This is to avoid specifying permissions in multiple disjoint places and because most API users shouldn't need these fields.
231 lines
6.7 KiB
Ruby
231 lines
6.7 KiB
Ruby
class ForumPost < ApplicationRecord
|
|
include Mentionable
|
|
|
|
attr_readonly :topic_id
|
|
belongs_to :creator, class_name: "User"
|
|
belongs_to_updater
|
|
belongs_to :topic, :class_name => "ForumTopic"
|
|
has_many :dtext_links, as: :model, dependent: :destroy
|
|
has_many :moderation_reports, as: :model
|
|
has_many :votes, class_name: "ForumPostVote"
|
|
has_one :tag_alias
|
|
has_one :tag_implication
|
|
has_one :bulk_update_request
|
|
|
|
before_validation :initialize_is_deleted, :on => :create
|
|
before_save :update_dtext_links, if: :dtext_links_changed?
|
|
before_create :autoreport_spam
|
|
after_create :update_topic_updated_at_on_create
|
|
after_update :update_topic_updated_at_on_update_for_original_posts
|
|
after_destroy :update_topic_updated_at_on_destroy
|
|
validates_presence_of :body
|
|
validate :validate_topic_is_unlocked
|
|
validate :topic_is_not_restricted, :on => :create
|
|
before_destroy :validate_topic_is_unlocked
|
|
after_save :delete_topic_if_original_post
|
|
after_update(:if => ->(rec) {rec.updater_id != rec.creator_id}) do |rec|
|
|
ModAction.log("#{CurrentUser.name} updated forum ##{rec.id}", :forum_post_update)
|
|
end
|
|
after_destroy(:if => ->(rec) {rec.updater_id != rec.creator_id}) do |rec|
|
|
ModAction.log("#{CurrentUser.name} deleted forum ##{rec.id}", :forum_post_delete)
|
|
end
|
|
mentionable(
|
|
:message_field => :body,
|
|
:title => ->(user_name) {%{#{creator.name} mentioned you in topic ##{topic_id} (#{topic.title})}},
|
|
:body => ->(user_name) {%{@#{creator.name} mentioned you in topic ##{topic_id} ("#{topic.title}":[/forum_topics/#{topic_id}?page=#{forum_topic_page}]):\n\n[quote]\n#{DText.extract_mention(body, "@" + user_name)}\n[/quote]\n}}
|
|
)
|
|
|
|
module SearchMethods
|
|
def topic_title_matches(title)
|
|
where(topic_id: ForumTopic.search(title_matches: title).select(:id))
|
|
end
|
|
|
|
def active
|
|
where("forum_posts.is_deleted = false")
|
|
end
|
|
|
|
def permitted
|
|
where(topic_id: ForumTopic.permitted)
|
|
end
|
|
|
|
def search(params)
|
|
q = super
|
|
q = q.permitted
|
|
q = q.search_attributes(params, :creator, :updater, :topic_id, :is_deleted, :body)
|
|
q = q.text_attribute_matches(:body, params[:body_matches], index_column: :text_index)
|
|
|
|
if params[:linked_to].present?
|
|
q = q.where(id: DtextLink.forum_post.wiki_link.where(link_target: params[:linked_to]).select(:model_id))
|
|
end
|
|
|
|
if params[:topic_title_matches].present?
|
|
q = q.topic_title_matches(params[:topic_title_matches])
|
|
end
|
|
|
|
if params[:topic_category_id].present?
|
|
q = q.where(topic_id: ForumTopic.where(category_id: params[:topic_category_id]))
|
|
end
|
|
|
|
q.apply_default_order(params)
|
|
end
|
|
end
|
|
|
|
module ApiMethods
|
|
def html_data_attributes
|
|
super + [[:topic, :is_deleted?]]
|
|
end
|
|
end
|
|
|
|
extend SearchMethods
|
|
include ApiMethods
|
|
|
|
def self.new_reply(params)
|
|
if params[:topic_id]
|
|
new(:topic_id => params[:topic_id])
|
|
elsif params[:post_id]
|
|
forum_post = ForumPost.find(params[:post_id])
|
|
forum_post.build_response
|
|
else
|
|
new
|
|
end
|
|
end
|
|
|
|
def reportable_by?(user)
|
|
visible?(user) && creator_id != user.id && !creator.is_moderator?
|
|
end
|
|
|
|
def votable?
|
|
bulk_update_request.present? && bulk_update_request.is_pending?
|
|
end
|
|
|
|
def voted?(user, score)
|
|
votes.where(creator_id: user.id, score: score).exists?
|
|
end
|
|
|
|
def autoreport_spam
|
|
if SpamDetector.new(self, user_ip: CurrentUser.ip_addr).spam?
|
|
moderation_reports << ModerationReport.new(creator: User.system, reason: "Spam.")
|
|
end
|
|
end
|
|
|
|
def validate_topic_is_unlocked
|
|
return if creator.is_moderator?
|
|
return if topic.nil?
|
|
|
|
if topic.is_locked?
|
|
errors[:topic] << "is locked"
|
|
throw :abort
|
|
end
|
|
end
|
|
|
|
def topic_is_not_restricted
|
|
if topic && !topic.visible?(creator)
|
|
errors[:topic] << "is restricted"
|
|
end
|
|
end
|
|
|
|
def editable_by?(user)
|
|
(creator_id == user.id || user.is_moderator?) && visible?(user)
|
|
end
|
|
|
|
def visible?(user, show_deleted_posts = false)
|
|
user.is_moderator? || (topic.visible?(user) && (show_deleted_posts || !is_deleted?))
|
|
end
|
|
|
|
def update_topic_updated_at_on_create
|
|
if topic
|
|
# need to do this to bypass the topic's original post from getting touched
|
|
ForumTopic.where(:id => topic.id).update_all(["updater_id = ?, response_count = response_count + 1, updated_at = ?", creator.id, Time.now])
|
|
topic.response_count += 1
|
|
end
|
|
end
|
|
|
|
def update_topic_updated_at_on_update_for_original_posts
|
|
if is_original_post?
|
|
topic.touch
|
|
end
|
|
end
|
|
|
|
def delete!
|
|
update(is_deleted: true)
|
|
update_topic_updated_at_on_delete
|
|
end
|
|
|
|
def undelete!
|
|
update(is_deleted: false)
|
|
update_topic_updated_at_on_undelete
|
|
end
|
|
|
|
def dtext_links_changed?
|
|
body_changed? && DText.dtext_links_differ?(body, body_was)
|
|
end
|
|
|
|
def update_dtext_links
|
|
self.dtext_links = DtextLink.new_from_dtext(body)
|
|
end
|
|
|
|
def update_topic_updated_at_on_delete
|
|
max = ForumPost.where(:topic_id => topic.id, :is_deleted => false).order("updated_at desc").first
|
|
if max
|
|
ForumTopic.where(:id => topic.id).update_all(["updated_at = ?, updater_id = ?", max.updated_at, max.updater_id])
|
|
end
|
|
end
|
|
|
|
def update_topic_updated_at_on_undelete
|
|
if topic
|
|
ForumTopic.where(:id => topic.id).update_all(["updater_id = ?, updated_at = ?", CurrentUser.id, Time.now])
|
|
end
|
|
end
|
|
|
|
def update_topic_updated_at_on_destroy
|
|
max = ForumPost.where(:topic_id => topic.id, :is_deleted => false).order("updated_at desc").first
|
|
if max
|
|
ForumTopic.where(:id => topic.id).update_all(["response_count = response_count - 1, updated_at = ?, updater_id = ?", max.updated_at, max.updater_id])
|
|
else
|
|
ForumTopic.where(:id => topic.id).update_all("response_count = response_count - 1")
|
|
end
|
|
|
|
topic.response_count -= 1
|
|
end
|
|
|
|
def initialize_is_deleted
|
|
self.is_deleted = false if is_deleted.nil?
|
|
end
|
|
|
|
def quoted_response
|
|
DText.quote(body, creator.name)
|
|
end
|
|
|
|
def forum_topic_page
|
|
(ForumPost.where("topic_id = ? and created_at <= ?", topic_id, created_at).count / Danbooru.config.posts_per_page.to_f).ceil
|
|
end
|
|
|
|
def is_original_post?(original_post_id = nil)
|
|
if original_post_id
|
|
return id == original_post_id
|
|
else
|
|
ForumPost.exists?(["id = ? and id = (select _.id from forum_posts _ where _.topic_id = ? order by _.id asc limit 1)", id, topic_id])
|
|
end
|
|
end
|
|
|
|
def delete_topic_if_original_post
|
|
if is_deleted? && is_original_post?
|
|
topic.update_attribute(:is_deleted, true)
|
|
end
|
|
end
|
|
|
|
def build_response
|
|
dup.tap do |x|
|
|
x.body = x.quoted_response
|
|
end
|
|
end
|
|
|
|
def dtext_shortlink
|
|
"forum ##{id}"
|
|
end
|
|
|
|
def self.available_includes
|
|
[:creator, :updater, :topic, :dtext_links, :votes, :tag_alias, :tag_implication, :bulk_update_request]
|
|
end
|
|
end
|