helpyio/helpy

View on GitHub
app/models/topic.rb

Summary

Maintainability
A
3 hrs
Test Coverage
# == Schema Information
#
# Table name: topics
#
#  id               :integer          not null, primary key
#  forum_id         :integer
#  user_id          :integer
#  user_name        :string
#  name             :string
#  posts_count      :integer          default(0), not null
#  waiting_on       :string           default("admin"), not null
#  last_post_date   :datetime
#  closed_date      :datetime
#  last_post_id     :integer
#  current_status   :string           default("new"), not null
#  private          :boolean          default(FALSE)
#  assigned_user_id :integer
#  cheatsheet       :boolean          default(FALSE)
#  points           :integer          default(0)
#  post_cache       :text
#  created_at       :datetime         not null
#  updated_at       :datetime         not null
#  locale           :string
#  doc_id           :integer          default(0)
#  channel          :string           default("email")
#  kind             :string           default("ticket")
#  priority         :integer          default(1)
#

class Topic < ActiveRecord::Base

  include SentenceCase
  include Hashid::Rails
  
  belongs_to :forum, counter_cache: true, touch: true
  belongs_to :user, counter_cache: true, touch: true
  belongs_to :doc, counter_cache: true, touch: true
  belongs_to :assigned_user, class_name: 'User'

  has_many :posts, dependent: :delete_all
  accepts_nested_attributes_for :posts

  has_many :votes, :as => :voteable
  has_attachments  :screenshots, accept: [:jpg, :png, :gif, :pdf, :txt, :rtf, :doc, :docx, :ppt, :pptx, :xls, :xlsx, :zip]

  include PgSearch::Model
  multisearchable :against => [:id, :name, :post_cache],
                  :if => :public?

  pg_search_scope :admin_search,
                  against: [:id, :name, :user_name, :current_status, :post_cache],
                  associated_against: {
                    teams: [:name]
                  }

  # various scopes
  scope :recent, -> { order('created_at DESC').limit(8) }
  scope :open, -> { where(current_status: "open") }
  scope :unread, -> { where("assigned_user_id = ? OR current_status = ?", nil, "new").where.not(current_status: 'closed') }
  scope :pending, -> { where(current_status: "pending") }
  scope :mine, -> (user) { where(assigned_user_id: user) }
  scope :closed, -> { where(current_status: "closed") }
  scope :spam, -> { where(current_status: "spam")}
  scope :trash, -> { where(current_status: "trash")}
  scope :assigned, -> { where.not(assigned_user_id: nil) }

  scope :chronologic, -> { order('updated_at DESC') }
  scope :reverse, -> { order('updated_at ASC') }
  scope :by_popularity, -> { order('points DESC') }
  scope :active, -> { where(current_status: %w(new open pending)) }
  scope :undeleted, -> { where.not(current_status: 'trash') }
  scope :front, -> { limit(6) }
  scope :for_doc, -> { where("doc_id= ?", doc)}
  scope :external, -> { where.not(kind: 'internal') }

  # provided both public and private instead of one method, for code readability
  scope :isprivate, -> { where.not(current_status: 'spam').where(private: true)}
  scope :ispublic, -> { where.not(current_status: 'spam').where(private: false)}

  # may want to get rid of this filter:
  # before_save :check_for_private
  before_create :add_locale
  before_create :reject_blacklisted_email_addresses

  before_save :cache_user_name
  acts_as_taggable_on :tags, :teams

  validates :name, presence: true, length: { maximum: 255 }
  # validates :user_id, presence: true

  enum priority: { low: 0, normal: 1, high: 2, very_high: 3 }

  def to_param
    "#{id}-#{name.parameterize}"
  end

  def email_subject
    "##{self.id} | #{self.name}"
  end

  def assigned?
    self.assigned_user_id.present?
  end

  def open?
    self.current_status == "open"
  end

  def open
    self.current_status = "pending"
    self.save
  end

  def reopen(user_id = 2)
    self.posts.create(body: I18n.t(:reopen_message, user_name: User.find(user_id).name), kind: 'note', user_id: user_id)
    self.current_status = "open"
    self.save
  end

  def self.bulk_reopen(post_attributes)
    Post.bulk_insert values: post_attributes
    self.update_all(current_status: 'open')
  end

  def close(user_id = 2)
    self.posts.create(body: I18n.t(:closed_message, user_name: User.find(user_id).name), kind: 'note', user_id: user_id)
    self.current_status = "closed"
    self.closed_date = Time.current
    self.save
  end

  def self.bulk_close(post_attributes)
    Post.bulk_insert values: post_attributes
    self.update_all(current_status: 'closed', closed_date: Time.current)
  end

  def trash(user_id = 2)
    self.posts.create(body: I18n.t(:trash_message, user_name: User.find(user_id).name), kind: 'note', user_id: user_id)
    self.current_status = "trash"
    self.closed_date = Time.current
    self.forum_id = 2
    self.private = true
    self.assigned_user_id = nil
    self.save
  end

  def self.bulk_trash(post_attributes)
    Post.bulk_insert values: post_attributes
    self.update_all(current_status: 'trash', forum_id: 2, private: true, assigned_user_id: nil, closed_date: Time.current)
  end

  def assign(user_id=2, assigned_to)
    self.posts.create(body: I18n.t(:assigned_message, assigned_to: User.find(assigned_to).name), kind: 'note', user_id: user_id)
    self.assigned_user_id = assigned_to
    # self.current_status = 'pending'
    self.save
  end

  def self.bulk_agent_assign(post_attributes, assigned_to)
    Post.bulk_insert values: post_attributes
    #self.update_all(assigned_user_id: assigned_to, current_status: 'pending')
    self.update_all(assigned_user_id: assigned_to)
  end

  def self.bulk_group_assign(post_attributes, assigned_group)
    Post.bulk_insert values: post_attributes
    all.each do |t|
      t.team_list = assigned_group
      t.save
    end
  end

  # DEPRECATED updates the last post date, called when a post is made
  def self.last_post
    Topic.post(:first, :order => 'updated_at DESC')
  end

  #Callback method to check and see if this topic is in a private forum
  def check_for_private
    #association is not working
    f = Forum.find(self.forum_id)
    self.private = true if f.private?
  end

  def public?
    # Note: We assume forum_ids 1,2,3 are seed data
    forum_id >= 3 && !private?
  end

  def create_topic_with_user(params, current_user, post)
    self.user = current_user ? current_user : User.find_by_email(params[:topic][:user][:email])

    unless self.user #User not found, lets build it
      self.build_user(params[:topic].require(:user).permit(:email, :name)).signup_guest
    end
    post.user = self.user
    self.user.persisted? && self.save && post.save
  end

  def create_topic_with_webhook_user(params)
    self.user = User.find_by_email(params['customer']['emailAddress'])
    unless self.user #User not found, lets craete it from olark params
      @token, enc = Devise.token_generator.generate(User, :reset_password_token)

      @user = self.build_user
      @user.reset_password_token = enc
      @user.reset_password_sent_at = Time.now.utc

      @user.name = params['customer']['fullName']
      @user.login = params['customer']['emailAddress'].split("@")[0]
      @user.email = params['customer']['emailAddress']
      # @user.home_phone = params[:topic][:user][:home_phone]
      @user.password = User.create_password
      @user.save
    end
    self.user.persisted? && self.save
  end

  def self.create_comment_thread(doc_id, user_id)
    @doc = Doc.find(doc_id)
    @user = User.find(user_id)
    Topic.create!(
      name: "Discussion on #{@doc.title}",
      private: false,
      forum_id: Forum.for_docs.first.id,
      user_id: @user.id,
      doc_id: @doc.id
    )
  end

  def self.merge_topics(topic_ids, user_id=2)

    @merge_topics = Topic.where(id: topic_ids)
    @topic = @merge_topics.first.dup
    @topic.name = "MERGED: #{@merge_topics.first.name}"
    topics_merged = ""

    if @topic.save
      @merge_topics.each_with_index do |t, i|

        if i == 0
          @topic.posts << t.posts
        else
          @topic.posts << t.posts.where.not(kind: 'first').all
        end
        topics_merged << "#{t.name}\n"
      end

      @topic.posts.create(
          body: "#{topics_merged} were merged to create this discussion",
          kind: "note",
          user_id: user_id,
      )

      @merge_topics.each do |t|
        t.trash
      end

      return @topic
    end
  end

  def from_email_address
    system_from_email = %("#{AppSettings['settings.site_name']}" <#{AppSettings['email.admin_email']}>)
    return system_from_email if self.team_list.blank?

    team = ActsAsTaggableOn::Tag.where('lower(name) = ?', self.team_list.first.downcase).first
    if team.email_address.present?
      %("#{team.email_name}" <#{team.email_address}>)
    else
      system_from_email
    end
  end

  def posts_in_last_minute
    self.posts.where(created_at: Time.now-1.minutes..Time.now, kind: 'reply').count
  end

  private

  # Send any tickets created by a blacklisted email to spam
  def reject_blacklisted_email_addresses
    if AppSettings['email.email_blacklist'].split(",").any? { |s| self.user.email.downcase.include?(s.downcase) }
       self.current_status = "spam"
    end
  end  

  def cache_user_name
    return if self.user.nil?
    if self.user.name.present?
      self.user_name = self.user.name
    else
      "NA"
    end
  end

  def add_locale
    self.locale = I18n.locale
  end

end