francois / mephisto forked from halorgium/mephisto

A refactored Mephisto that has multiple spam detection engines.

mephisto / app / models / comment.rb
100644 127 lines (106 sloc) 4.648 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
require 'uri'
 
class Comment < Content
  validates_presence_of :author, :author_ip, :article_id, :body
  validates_format_of :author_email, :with => Format::EMAIL
  before_validation :clean_up_author_email
  before_validation :clean_up_author_url
  after_validation_on_create :snag_article_attributes
  before_create :check_comment_expiration
  before_create :sanitize_attributes
  before_save :update_counter_cache
  before_destroy :decrement_counter_cache
  belongs_to :article
  has_one :event, :dependent => :destroy
  before_create :check_if_previewing
 
  attr_accessible :article, :article_id, :user_id, :user, :excerpt, :body, :author, :author_url, :author_email, :author_ip, :updater_id, :updater, :comment_age, :user_agent, :referrer, :preview
  attr_accessor :preview
  class Previewing < StandardError; end
 
  # If the view sends the "preview" accessor, we raise this
  # error so the controller can simply rescue
  def check_if_previewing
    raise Comment::Previewing if preview
  end
  
  def self.find_all_by_section(section, options = {})
    find :all, options.update(:conditions => ['contents.approved = ? and assigned_sections.section_id = ?', true, section.id],
      :select => 'contents.*', :joins => 'INNER JOIN assigned_sections ON assigned_sections.article_id = contents.article_id',
      :order => 'contents.created_at DESC')
  end
 
  def to_liquid
    CommentDrop.new self
  end
  
  def approved=(value)
    @old_approved ||= approved? ? :true : :false
    write_attribute :approved, value
  end
 
  def clean_up_author_email
    if value = read_attribute(:author_email) then
      write_attribute :author_email, value.strip
    end
  end
 
  def clean_up_author_url
    if value = read_attribute(:author_url) then
      value.strip!
      value = 'http://' + value unless value.blank? || value[0..0] == '/' || URI::parse(value).scheme
      write_attribute :author_url, value
    end
  rescue URI::InvalidURIError
    write_attribute :author_url, nil
  end
 
  def article_referenced_cache_key
    "[#{article_id}:Article]"
  end
 
  def check_approval(site, request)
    self.approved = site.approve_comments?
    if valid_comment_system?(site)
      akismet = Akismet.new(site.akismet_key, site.akismet_url)
      self.approved = !akismet.comment_check(comment_spam_options(site, request))
      logger.info "Checking Akismet (#{site.akismet_key}) for new comment on Article #{article_id}. #{approved? ? 'Approved' : 'Blocked'}"
      logger.warn "Odd Akismet Response: #{akismet.last_response.inspect}" unless Akismet.normal_responses.include?(akismet.last_response)
    end
  end
 
  def mark_as_spam(site, request)
    mark_comment :spam, site, request
  end
  
  def mark_as_ham(site, request)
    mark_comment :ham, site, request
  end
 
  protected
    def sanitize_attributes
      [:author, :author_url, :author_email, :author_ip, :user_agent, :referrer].each do |a|
        self.send("#{a}=", CGI::escapeHTML(self.send(a).to_s))
      end
    end
 
    def snag_article_attributes
      self.filter ||= article.site.filter
      [:site, :title, :published_at, :permalink].each { |a| self.send("#{a}=", article.send(a)) }
    end
 
    def check_comment_expiration
      raise Article::CommentNotAllowed, "#{article.status} does not allow comments" unless article.accept_comments?
    end
 
    def update_counter_cache
      Article.increment_counter 'comments_count', article_id if approved? && @old_approved == :false
      Article.decrement_counter 'comments_count', article_id if !approved? && @old_approved == :true
    end
    
    def decrement_counter_cache
      Article.decrement_counter 'comments_count', article_id if approved?
    end
    
    def valid_comment_system?(site)
      [:akismet_key, :akismet_url].all? { |attr| !site.send(attr).blank? }
    end
    
    def comment_spam_options(site, request)
      {:user_ip => author_ip,
       :user_agent => user_agent,
       :referrer => referrer,
       :permalink => "http://#{request.host_with_port}#{site.permalink_for(self)}",
       :comment_author => author,
       :comment_author_email => author_email,
       :comment_author_url => author_url,
       :comment_content => body}
    end
    
    def mark_comment(comment_type, site, request)
      if valid_comment_system?(site)
        response = Akismet.new(site.akismet_key, site.akismet_url).send("submit_#{comment_type}", comment_spam_options(site, request))
        logger.info "Calling Akismet (#{site.akismet_key}) for #{comment_type} comment on Article #{article_id}. #{response}"
      end
    end
end