class ForumPost < ApplicationRecord LATEST_CACHE_KEY = 'forum_posts_latest_v4' belongs_to :creator, class_name: "User", foreign_key: :creator_id belongs_to :updater, class_name: "User", foreign_key: :last_updated_by after_create :initialize_last_updated_by after_create :clear_cache before_validation :validate_title validates_length_of :body, minimum: 1, message: "You need to enter a body" module LockMethods module ClassMethods def lock!(id) # Run raw SQL to skip the lock check execute_sql("UPDATE forum_posts SET is_locked = TRUE WHERE id = ?", id) end def unlock!(id) # Run raw SQL to skip the lock check execute_sql("UPDATE forum_posts SET is_locked = FALSE WHERE id = ?", id) end end def self.included(m) m.extend(ClassMethods) m.before_validation :validate_lock end def validate_lock if root.is_locked? errors.add(:base, "Thread is locked") throw :abort end true end end module StickyMethods module ClassMethods def stick!(id) # Run raw SQL to skip the lock check execute_sql("UPDATE forum_posts SET is_sticky = TRUE WHERE id = ?", id) end def unstick!(id) # Run raw SQL to skip the lock check execute_sql("UPDATE forum_posts SET is_sticky = FALSE WHERE id = ?", id) end end def self.included(m) m.extend(ClassMethods) end end module ParentMethods def self.included(m) m.after_create :update_parent_on_create m.before_destroy :update_parent_on_destroy m.has_many :children, lambda { order "id" }, class_name: "ForumPost", foreign_key: :parent_id m.belongs_to :parent, class_name: "ForumPost", foreign_key: :parent_id end def update_parent_on_destroy unless is_parent? p = parent p.update(response_count: p.response_count - 1) end end def update_parent_on_create unless is_parent? p = parent p.update(updated_at: updated_at, response_count: p.response_count + 1, last_updated_by: creator_id) end end def is_parent? parent_id.nil? end def root if is_parent? self else parent end end def root_id if is_parent? id else parent_id end end end module ApiMethods def api_attributes { body: body, creator: author, creator_id: creator_id, id: id, parent_id: parent_id, title: title, updated_at: updated_at, pages: pages } end def as_json(*params) api_attributes.as_json(*params) end def to_xml(options = {}) api_attributes.to_xml(options.reverse_merge(root: "forum_post")) end end include LockMethods include StickyMethods include ParentMethods include ApiMethods def self.updated?(user) conds = [] conds += [ "creator_id <> %d" % [ user.id ] ] unless user.is_anonymous? newest_topic = ForumPost.where(conds).order(id: :desc).select(:created_at).take return false if newest_topic.nil? newest_topic.created_at > user.last_forum_topic_read_at end def validate_title if is_parent? if title.blank? errors.add :title, "missing" throw :abort end if title !~ /\S/ errors.add :title, "missing" throw :abort end end true end def initialize_last_updated_by if is_parent? update(last_updated_by: creator_id) end end def clear_cache Rails.cache.delete "forum_posts" Rails.cache.delete LATEST_CACHE_KEY end def last_updater updater.name rescue CONFIG["default_guest_name"] end def author creator.name end def pages ((response_count == 0 ? 1 : response_count) / 30.0).ceil end def self.latest where(parent_id: nil).reorder(updated_at: :desc).paginate(page: 1, per_page: 10) end end