denny/ShinyCMS-ruby

View on GitHub
plugins/ShinyCMS/app/models/shinycms/discussion.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
90%
# frozen_string_literal: true

# ShinyCMS ~ https://shinycms.org
#
# Copyright 2009-2024 Denny de la Haye ~ https://denny.me
#
# ShinyCMS is free software; you can redistribute it and/or modify it under the terms of the GPL (version 2 or later)

module ShinyCMS
  # Model class for discussions (used to group comments)
  class Discussion < ApplicationRecord
    include ShinyCMS::CanHide
    include ShinyCMS::SoftDelete

    include ShinyCMS::ProvidesDemoSiteData

    # Assocations

    belongs_to :resource, inverse_of: :discussion, polymorphic: true

    has_many :comments, -> { not_spam.includes( [ :author ] ) }, inverse_of: :discussion, dependent: :destroy
    has_many :all_comments, inverse_of: :discussion, dependent: :destroy, class_name: 'Comment'
    # TODO: Get rid of .all_comments; only used to find the next .number when creating a new comment

    # Scopes

    scope :with_authors,  -> { includes( [ :author   ] ) }
    scope :with_comments, -> { includes( [ :comments ] ) }

    # Instance methods

    def notifiable?
      resource&.user&.email.present?
    end

    def notification_email
      return unless notifiable?

      resource.user.email
    end

    def lock
      update( locked: true )
    end

    def unlock
      update( locked: false )
    end

    def find_comment( number: )
      comment = comments.find_by( number: number )
      return comment if comment.present?

      raise ActiveRecord::RecordNotFound
    end

    def next_comment_number
      ( all_comments.maximum( :number ) || 0 ) + 1
    end

    def most_recent_comment
      comments.order( posted_at: :desc ).first
    end

    # Class methods

    # Trigger whichever notification emails are appropriate for a given comment
    def self.send_notifications( comment )
      parent_author, content_author, admin = email_addresses_to_notify( comment )

      mailer = DiscussionMailer.with( comment: comment )

      mailer.parent_comment_author_notification if parent_author.present?

      mailer.content_author_notification unless blank_or_already_emailed? content_author, [ parent_author ]

      mailer.comment_admin_notification unless blank_or_already_emailed? admin, [ parent_author, content_author ]
    end

    def self.email_addresses_to_notify( comment )
      [
        comment.parent&.notification_email,
        comment.discussion.notification_email,
        ShinyCMS::Setting.get( :all_comment_notifications_email )
      ]
    end

    def self.blank_or_already_emailed?( email, previous_emails )
      ( [ nil, '' ] + previous_emails ).include? email
    end

    def self.recently_active( days: 7, count: 10 )
      counts = Comment.visible.since( days.days.ago ).group( :discussion_id )
                      .order( 'count(id) desc' ).limit( count ).count

      discussions = where( id: counts.keys )

      [ discussions, counts ]
    end

    def self.my_demo_data_position
      10  # hopefully higher than any content types that might have discussions attached
    end
  end
end