Files
danbooru/app/models/forum_topic.rb
evazion b4ce2d83a6 models: remove belongs_to_creator macro.
The belongs_to_creator macro was used to initialize the creator_id field
to the CurrentUser. This made tests complicated because it meant you had
to create and set the current user every time you wanted to create an
object, when lead to the current user being set over and over again. It
also meant you had to constantly be aware of what the CurrentUser was in
many different contexts, which was often confusing. Setting creators
explicitly simplifies everything greatly.
2020-01-21 00:09:38 -06:00

190 lines
5.2 KiB
Ruby

class ForumTopic < ApplicationRecord
CATEGORIES = {
0 => "General",
1 => "Tags",
2 => "Bugs & Features"
}
MIN_LEVELS = {
None: 0,
Moderator: User::Levels::MODERATOR,
Admin: User::Levels::ADMIN
}
belongs_to :creator, class_name: "User"
belongs_to_updater
has_many :posts, -> {order("forum_posts.id asc")}, :class_name => "ForumPost", :foreign_key => "topic_id", :dependent => :destroy
has_many :moderation_reports, through: :posts
has_one :original_post, -> {order("forum_posts.id asc")}, class_name: "ForumPost", foreign_key: "topic_id", inverse_of: :topic
has_many :subscriptions, :class_name => "ForumSubscription"
before_validation :initialize_is_deleted, :on => :create
validates_presence_of :title
validates_associated :original_post
validates_inclusion_of :category_id, :in => CATEGORIES.keys
validates_inclusion_of :min_level, :in => MIN_LEVELS.values
validates :title, :length => {:maximum => 255}
accepts_nested_attributes_for :original_post
after_update :update_orignal_post
after_save(:if => ->(rec) {rec.is_locked? && rec.saved_change_to_is_locked?}) do |rec|
ModAction.log("locked forum topic ##{id} (title: #{title})", :forum_topic_lock)
end
module CategoryMethods
extend ActiveSupport::Concern
module ClassMethods
def categories
CATEGORIES.values
end
def reverse_category_mapping
@reverse_category_mapping ||= CATEGORIES.invert
end
end
def category_name
CATEGORIES[category_id]
end
end
module SearchMethods
def active
where(is_deleted: false)
end
def permitted
where("min_level <= ?", CurrentUser.level)
end
def sticky_first
order(is_sticky: :desc, updated_at: :desc)
end
def default_order
order(updated_at: :desc)
end
def search(params)
q = super
q = q.permitted
q = q.search_attributes(params, :creator, :updater, :is_sticky, :is_locked, :is_deleted, :category_id, :title, :response_count)
q = q.text_attribute_matches(:title, params[:title_matches], index_column: :text_index)
if params[:mod_only].present?
q = q.where("min_level >= ?", MIN_LEVELS[:Moderator])
end
case params[:order]
when "sticky"
q = q.sticky_first
else
q = q.apply_default_order(params)
end
unless params[:is_deleted].present?
q = q.active
end
q
end
end
module VisitMethods
def read_by?(user = nil)
user ||= CurrentUser.user
if user.last_forum_read_at && updated_at <= user.last_forum_read_at
return true
end
ForumTopicVisit.where("user_id = ? and forum_topic_id = ? and last_read_at >= ?", user.id, id, updated_at).exists?
end
def mark_as_read!(user = CurrentUser.user)
return if user.is_anonymous?
match = ForumTopicVisit.where(:user_id => user.id, :forum_topic_id => id).first
if match
match.update_attribute(:last_read_at, updated_at)
else
ForumTopicVisit.create(:user_id => user.id, :forum_topic_id => id, :last_read_at => updated_at)
end
has_unread_topics =
ForumTopic
.permitted
.active
.where("forum_topics.updated_at >= ?", user.last_forum_read_at)
.joins("left join forum_topic_visits on (forum_topic_visits.forum_topic_id = forum_topics.id and forum_topic_visits.user_id = #{user.id})")
.where("(forum_topic_visits.id is null or forum_topic_visits.last_read_at < forum_topics.updated_at)")
.exists?
unless has_unread_topics
user.update_attribute(:last_forum_read_at, Time.now)
ForumTopicVisit.prune!(user)
end
end
end
module SubscriptionMethods
def user_subscription(user)
subscriptions.where(:user_id => user.id).first
end
end
extend SearchMethods
include CategoryMethods
include VisitMethods
include SubscriptionMethods
def editable_by?(user)
(creator_id == user.id || user.is_moderator?) && visible?(user)
end
def visible?(user)
user.level >= min_level
end
def create_mod_action_for_delete
ModAction.log("deleted forum topic ##{id} (title: #{title})", :forum_topic_delete)
end
def create_mod_action_for_undelete
ModAction.log("undeleted forum topic ##{id} (title: #{title})", :forum_topic_undelete)
end
def initialize_is_deleted
self.is_deleted = false if is_deleted.nil?
end
def page_for(post_id)
(posts.where("id < ?", post_id).count / Danbooru.config.posts_per_page.to_f).ceil
end
def last_page
(response_count / Danbooru.config.posts_per_page.to_f).ceil
end
def merge(topic)
ForumPost.where(:id => self.posts.map(&:id)).update_all(:topic_id => topic.id)
topic.update(response_count: topic.response_count + self.posts.length, updater_id: CurrentUser.id)
self.update_columns(:response_count => 0, :is_deleted => true, :updater_id => CurrentUser.id)
end
def delete!
update(is_deleted: true)
end
def undelete!
update(is_deleted: false)
end
def update_orignal_post
original_post&.update_columns(:updater_id => updater.id, :updated_at => Time.now)
end
def viewable_moderation_reports
CurrentUser.is_moderator? ? moderation_reports.recent : []
end
end