Commit 44627b4a898e59f5a5082404e65bc09a19b65a88

Authored by Lucas Melo
1 parent 688b3512

use anti_spam plugin in suggest_articles

ActionItem2691
app/controllers/my_profile/cms_controller.rb
@@ -267,7 +267,10 @@ class CmsController < MyProfileController @@ -267,7 +267,10 @@ class CmsController < MyProfileController
267 @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url) 267 @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url)
268 @task = SuggestArticle.new(params[:task]) 268 @task = SuggestArticle.new(params[:task])
269 if request.post? 269 if request.post?
270 - @task.target = profile 270 + @task.target = profile
  271 + @task.ip_address = request.remote_ip
  272 + @task.user_agent = request.user_agent
  273 + @task.referrer = request.referrer
271 if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save 274 if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save
272 session[:notice] = _('Thanks for your suggestion. The community administrators were notified.') 275 session[:notice] = _('Thanks for your suggestion. The community administrators were notified.')
273 redirect_to @back_to 276 redirect_to @back_to
app/controllers/my_profile/profile_editor_controller.rb
@@ -4,7 +4,7 @@ class ProfileEditorController < MyProfileController @@ -4,7 +4,7 @@ class ProfileEditorController < MyProfileController
4 protect 'destroy_profile', :profile, :only => [:destroy_profile] 4 protect 'destroy_profile', :profile, :only => [:destroy_profile]
5 5
6 def index 6 def index
7 - @pending_tasks = Task.to(profile).pending.select{|i| user.has_permission?(i.permission, profile)} 7 + @pending_tasks = Task.to(profile).pending.without_spam.select{|i| user.has_permission?(i.permission, profile)}
8 end 8 end
9 9
10 helper :profile 10 helper :profile
app/controllers/my_profile/spam_controller.rb
@@ -14,9 +14,15 @@ class SpamController < MyProfileController @@ -14,9 +14,15 @@ class SpamController < MyProfileController
14 if params[:remove_comment] 14 if params[:remove_comment]
15 profile.comments_received.find(params[:remove_comment]).destroy 15 profile.comments_received.find(params[:remove_comment]).destroy
16 end 16 end
  17 + if params[:remove_task]
  18 + Task.to(profile).find_by_id(params[:remove_task]).destroy
  19 + end
17 if params[:mark_comment_as_ham] 20 if params[:mark_comment_as_ham]
18 profile.comments_received.find(params[:mark_comment_as_ham]).ham! 21 profile.comments_received.find(params[:mark_comment_as_ham]).ham!
19 end 22 end
  23 + if params[:mark_task_as_ham] && (t = Task.to(profile).find_by_id(params[:mark_task_as_ham]))
  24 + t.ham!
  25 + end
20 if request.xhr? 26 if request.xhr?
21 json_response(true) 27 json_response(true)
22 else 28 else
@@ -28,7 +34,8 @@ class SpamController < MyProfileController @@ -28,7 +34,8 @@ class SpamController < MyProfileController
28 return 34 return
29 end 35 end
30 36
31 - @spam = profile.comments_received.spam.paginate({:page => params[:page]}) 37 + @comment_spam = profile.comments_received.spam.paginate({:page => params[:comments_page]})
  38 + @task_spam = Task.to(profile).spam.paginate({:page => params[:tasks_page]})
32 end 39 end
33 40
34 protected 41 protected
app/controllers/my_profile/tasks_controller.rb
@@ -4,12 +4,12 @@ class TasksController < MyProfileController @@ -4,12 +4,12 @@ class TasksController < MyProfileController
4 4
5 def index 5 def index
6 @filter = params[:filter_type].blank? ? nil : params[:filter_type] 6 @filter = params[:filter_type].blank? ? nil : params[:filter_type]
7 - @tasks = Task.to(profile).pending.of(@filter).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page]) 7 + @tasks = Task.to(profile).without_spam.pending.of(@filter).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page])
8 @failed = params ? params[:failed] : {} 8 @failed = params ? params[:failed] : {}
9 end 9 end
10 10
11 def processed 11 def processed
12 - @tasks = Task.to(profile).closed.sort_by(&:created_at) 12 + @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at)
13 end 13 end
14 14
15 VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ] 15 VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ]
@@ -57,7 +57,7 @@ class TasksController < MyProfileController @@ -57,7 +57,7 @@ class TasksController < MyProfileController
57 end 57 end
58 58
59 def list_requested 59 def list_requested
60 - @tasks = Task.find_all_by_requestor_id(profile.id) 60 + @tasks = Task.without_spam.find_all_by_requestor_id(profile.id)
61 end 61 end
62 62
63 def ticket_details 63 def ticket_details
app/models/spammer_logger.rb
@@ -6,6 +6,8 @@ class SpammerLogger < Logger @@ -6,6 +6,8 @@ class SpammerLogger < Logger
6 if object 6 if object
7 if object.kind_of?(Comment) 7 if object.kind_of?(Comment)
8 @logger << "[#{Time.now.strftime('%F %T %z')}] Comment-id: #{object.id} IP: #{spammer_ip}\n" 8 @logger << "[#{Time.now.strftime('%F %T %z')}] Comment-id: #{object.id} IP: #{spammer_ip}\n"
  9 + elsif object.kind_of?(SuggestArticle)
  10 + @logger << "[#{Time.now.strftime('%F %T %z')}] SuggestArticle-id: #{object.id} IP: #{spammer_ip}\n"
9 end 11 end
10 else 12 else
11 @logger << "[#{Time.now.strftime('%F %T %z')}] IP: #{spammer_ip}\n" 13 @logger << "[#{Time.now.strftime('%F %T %z')}] IP: #{spammer_ip}\n"
app/models/suggest_article.rb
@@ -11,6 +11,21 @@ class SuggestArticle &lt; Task @@ -11,6 +11,21 @@ class SuggestArticle &lt; Task
11 settings_items :source, :type => String 11 settings_items :source, :type => String
12 settings_items :source_name, :type => String 12 settings_items :source_name, :type => String
13 settings_items :highlighted, :type => :boolean, :default => false 13 settings_items :highlighted, :type => :boolean, :default => false
  14 + settings_items :ip_address, :type => String
  15 + settings_items :user_agent, :type => String
  16 + settings_items :referrer, :type => String
  17 +
  18 + after_create :schedule_spam_checking
  19 +
  20 + def schedule_spam_checking
  21 + self.delay.check_for_spam
  22 + end
  23 +
  24 + include Noosfero::Plugin::HotSpot
  25 +
  26 + def check_for_spam
  27 + plugins.dispatch(:check_suggest_article_for_spam, self)
  28 + end
14 29
15 def sender 30 def sender
16 "#{name} (#{email})" 31 "#{name} (#{email})"
@@ -61,4 +76,25 @@ class SuggestArticle &lt; Task @@ -61,4 +76,25 @@ class SuggestArticle &lt; Task
61 _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name } 76 _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name }
62 end 77 end
63 78
  79 + def spam!
  80 + super
  81 + SpammerLogger.log(ip_address, self)
  82 + self.delay.marked_as_spam
  83 + self
  84 + end
  85 +
  86 + def ham!
  87 + super
  88 + self.delay.marked_as_ham
  89 + self
  90 + end
  91 +
  92 + def marked_as_spam
  93 + plugins.dispatch(:suggest_article_marked_as_spam, self)
  94 + end
  95 +
  96 + def marked_as_ham
  97 + plugins.dispatch(:suggest_article_marked_as_ham, self)
  98 + end
  99 +
64 end 100 end
app/models/task.rb
@@ -235,6 +235,26 @@ class Task &lt; ActiveRecord::Base @@ -235,6 +235,26 @@ class Task &lt; ActiveRecord::Base
235 end 235 end
236 end 236 end
237 237
  238 + def spam?
  239 + !spam.nil? && spam
  240 + end
  241 +
  242 + def ham?
  243 + !spam.nil? && !spam
  244 + end
  245 +
  246 + def spam!
  247 + self.spam = true
  248 + self.save!
  249 + self
  250 + end
  251 +
  252 + def ham!
  253 + self.spam = false
  254 + self.save!
  255 + self
  256 + end
  257 +
238 protected 258 protected
239 259
240 # This method must be overrided in subclasses, and its implementation must do 260 # This method must be overrided in subclasses, and its implementation must do
@@ -274,6 +294,9 @@ class Task &lt; ActiveRecord::Base @@ -274,6 +294,9 @@ class Task &lt; ActiveRecord::Base
274 named_scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] } 294 named_scope :opened, :conditions => { :status => [Task::Status::ACTIVE, Task::Status::HIDDEN] }
275 named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } 295 named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} }
276 named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } 296 named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} }
  297 + named_scope :without_spam, :conditions => ['spam IS NULL OR spam = ?', false]
  298 + named_scope :spam, :conditions => ['spam = ?', true]
  299 +
277 300
278 named_scope :to, lambda { |profile| 301 named_scope :to, lambda { |profile|
279 environment_condition = nil 302 environment_condition = nil
app/views/spam/_comment_spam.rhtml 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +<%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %>
  2 +<div id='article'>
  3 + <div class="comments" id="comments_list">
  4 + <ul class="article-comments-list">
  5 + <%= render :partial => 'comment/comment', :collection => @comment_spam %>
  6 + </ul>
  7 + </div>
  8 +</div>
  9 +
  10 +<%= pagination_links @comment_spam, :param_name => :comments_page %>
  11 +
app/views/spam/_suggest_article.html.erb 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +<% render :layout => 'task', :locals => { :task => task } do %>
  2 + <% content_for :extra_buttons do %>
  3 + <%= button_to_function('down', _('Show details'), "toggleDetails(this, '#{_('Hide details')}', '#{_('Show details')}')" ) %>
  4 + <% end %>
  5 +
  6 + <% content_for :extra_content do %>
  7 + <ul class="suggest-article-details" style="display: none">
  8 + <li><strong><%=_('Sent by')%></strong>: <%=task.name%> </li>
  9 + <li><strong><%=_('Email')%></strong>: <%=task.email%> </li>
  10 + <li><strong><%=_('Source')%></strong>: <%=task.source_name%> </li>
  11 + <li><strong><%=_('Source URL')%></strong>: <%=task.source%> </li>
  12 + <li><strong><%=_('Folder')%></strong>: <%=(a = Article.find_by_id(task.article_parent_id))?a.name : '<em>' + s_('Folder|none') + '</em>'%> </li>
  13 + <li><strong><%=_('Lead')%></strong>: <%=task.article_abstract.blank? ? '<em>' + s_('Abstract|empty') + '</em>' : task.article_abstract%> </li>
  14 + <li><strong><%=_('Body')%></strong>:
  15 + <div class='suggest-article-body'>
  16 + <%= task.article_body %>
  17 + </div>
  18 + </li>
  19 + </ul>
  20 + <% end %>
  21 +<% end %>
app/views/spam/_task.rhtml 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +<div class="task_box" id="task-<%= task.id %>">
  2 + <%= render :partial => 'tasks/task_icon', :locals => {:task => task} %>
  3 + <%= render :partial => 'tasks/task_title', :locals => {:task => task} %>
  4 + <div class="task-information">
  5 + <%= task_information(task) %>
  6 + </div>
  7 +
  8 + <%= yield %> <% # ??? %>
  9 +
  10 + <% button_bar do %>
  11 + <%= button_to_function('new', _('Mark as NOT SPAM'), 'removeTaskBox(this, %s, "%s", "")' % [url_for(:mark_task_as_ham => task.id).to_json, "task-#{task.id}"]) %>
  12 + <%= yield :extra_buttons %>
  13 + <%= button_to_function('delete', _('Remove'), 'removeTaskBox(this, %s, "%s", %s)' % [url_for(:profile => params[:profile], :remove_task => task.id).to_json, "task-#{task.id}", _('Are you sure you want to remove this article suggestion?').to_json]) %>
  14 +
  15 + <% end %>
  16 +
  17 + <%= yield :extra_content %>
  18 +</div>
app/views/spam/_task_spam.rhtml 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +<% @task_spam.each do |t| %>
  2 + <%= render :partial => partial_for_class(t.class), :locals => { :task => t } %>
  3 +<% end %>
  4 +<%= pagination_links @task_spam, :param_name => :tasks_page %>
app/views/spam/index.rhtml
  1 +<%= stylesheet('tasks') %>
  2 +
1 <h1><%= _('Manage SPAM') %></h1> 3 <h1><%= _('Manage SPAM') %></h1>
2 4
3 <% button_bar do %> 5 <% button_bar do %>
@@ -5,16 +7,21 @@ @@ -5,16 +7,21 @@
5 <% end %> 7 <% end %>
6 8
7 <%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %> 9 <%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %>
8 -<div id='article'>  
9 - <div class="comments" id="comments_list">  
10 - <ul class="article-comments-list">  
11 - <%= render :partial => 'comment/comment', :collection => @spam %>  
12 - </ul>  
13 - </div>  
14 -</div>  
15 10
16 -<%= pagination_links @spam %> 11 +<% if @task_spam.length > 0 %>
  12 + <% tabs = [] %>
  13 + <% tabs << {:title => _('Comment Spam'), :id => 'comment-spam',
  14 + :content => (render :partial => 'comment_spam')} %>
  15 + <% tabs << {:title => _('Task Spam'), :id => 'task-spam',
  16 + :content => (render :partial => 'task_spam') } %>
  17 + <%= render_tabs(tabs) %>
  18 +<% else %>
  19 + <%= render :partial => 'comment_spam' %>
  20 +<% end %>
  21 +
17 22
18 <% button_bar do %> 23 <% button_bar do %>
19 <%= button :back, _('Back to control panel'), :controller => :profile_editor %> 24 <%= button :back, _('Back to control panel'), :controller => :profile_editor %>
20 <% end %> 25 <% end %>
  26 +
  27 +<%= javascript_include_tag 'spam' %>
app/views/tasks/_task.rhtml
1 <div class="task_box" id="task-<%= task.id %>"> 1 <div class="task_box" id="task-<%= task.id %>">
2 2
3 - <div class="task_icon">  
4 - <%  
5 - icon_info = task.icon  
6 - if icon_info[:type] == :profile_image  
7 - icon = profile_image(icon_info[:profile], :minor)  
8 - elsif icon_info[:type] == :defined_image  
9 - icon = "<img src='#{icon_info[:src]}' alt='#{icon_info[:name]}' />"  
10 - end  
11 - %>  
12 - <%=  
13 - if icon_info[:url]  
14 - link_to(icon, icon_info[:url])  
15 - else  
16 - icon  
17 - end  
18 - %>  
19 -  
20 - </div> 3 + <%= render :partial => 'task_icon', :locals => {:task => task} %>
21 4
22 <div class="task_decisions"> 5 <div class="task_decisions">
23 <%= 6 <%=
@@ -39,9 +22,7 @@ @@ -39,9 +22,7 @@
39 %> 22 %>
40 </div><!-- class="task_decisions" --> 23 </div><!-- class="task_decisions" -->
41 24
42 - <strong class="task_title">  
43 - <%= task.title %>  
44 - </strong> 25 + <%= render :partial => 'task_title', :locals => {:task => task} %>
45 26
46 <div class="task_information"> 27 <div class="task_information">
47 <%= task_information(task) %> 28 <%= task_information(task) %>
app/views/tasks/_task_icon.rhtml 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +<%
  2 + icon_info = task.icon
  3 + if icon_info[:type] == :profile_image
  4 + icon = profile_image(icon_info[:profile], :minor)
  5 + elsif icon_info[:type] == :defined_image
  6 + icon = "<img src='#{icon_info[:src]}' alt='#{icon_info[:name]}' />"
  7 + end
  8 +
  9 + if icon_info[:url]
  10 + icon = link_to(icon, icon_info[:url])
  11 + end
  12 +%>
  13 +
  14 +<div class="task_icon">
  15 + <%= icon %>
  16 +</div>
app/views/tasks/_task_title.rhtml 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +<strong class="task_title">
  2 + <%= task.title %>
  3 +</strong>
db/migrate/20131011164400_add_spam_to_task.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class AddSpamToTask < ActiveRecord::Migration
  2 + def self.up
  3 + change_table :tasks do |t|
  4 + t.boolean :spam, :default => false
  5 + end
  6 + Task.update_all ["spam = ?", false]
  7 + add_index :tasks, [:spam]
  8 + end
  9 +
  10 + def self.down
  11 + remove_column :tasks, :spam
  12 + end
  13 +end
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 # 9 #
10 # It's strongly recommended to check this file into your version control system. 10 # It's strongly recommended to check this file into your version control system.
11 11
12 -ActiveRecord::Schema.define(:version => 20130711213046) do 12 +ActiveRecord::Schema.define(:version => 20131011164400) do
13 13
14 create_table "abuse_reports", :force => true do |t| 14 create_table "abuse_reports", :force => true do |t|
15 t.integer "reporter_id" 15 t.integer "reporter_id"
@@ -233,6 +233,50 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do @@ -233,6 +233,50 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do
233 t.datetime "updated_at" 233 t.datetime "updated_at"
234 end 234 end
235 235
  236 + create_table "custom_forms_plugin_answers", :force => true do |t|
  237 + t.text "value"
  238 + t.integer "field_id"
  239 + t.integer "submission_id"
  240 + end
  241 +
  242 + create_table "custom_forms_plugin_fields", :force => true do |t|
  243 + t.string "name"
  244 + t.string "slug"
  245 + t.string "type"
  246 + t.string "default_value"
  247 + t.string "choices"
  248 + t.float "minimum"
  249 + t.float "maximum"
  250 + t.integer "form_id"
  251 + t.boolean "mandatory", :default => false
  252 + t.boolean "multiple"
  253 + t.boolean "list"
  254 + t.integer "position", :default => 0
  255 + end
  256 +
  257 + create_table "custom_forms_plugin_forms", :force => true do |t|
  258 + t.string "name"
  259 + t.string "slug"
  260 + t.text "description"
  261 + t.integer "profile_id"
  262 + t.datetime "begining"
  263 + t.datetime "ending"
  264 + t.boolean "report_submissions", :default => false
  265 + t.boolean "on_membership", :default => false
  266 + t.string "access"
  267 + t.datetime "created_at"
  268 + t.datetime "updated_at"
  269 + end
  270 +
  271 + create_table "custom_forms_plugin_submissions", :force => true do |t|
  272 + t.string "author_name"
  273 + t.string "author_email"
  274 + t.integer "profile_id"
  275 + t.integer "form_id"
  276 + t.datetime "created_at"
  277 + t.datetime "updated_at"
  278 + end
  279 +
236 create_table "delayed_jobs", :force => true do |t| 280 create_table "delayed_jobs", :force => true do |t|
237 t.integer "priority", :default => 0 281 t.integer "priority", :default => 0
238 t.integer "attempts", :default => 0 282 t.integer "attempts", :default => 0
@@ -547,8 +591,11 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do @@ -547,8 +591,11 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do
547 t.datetime "created_at" 591 t.datetime "created_at"
548 t.string "target_type" 592 t.string "target_type"
549 t.integer "image_id" 593 t.integer "image_id"
  594 + t.boolean "spam", :default => false
550 end 595 end
551 596
  597 + add_index "tasks", ["spam"], :name => "index_tasks_on_spam"
  598 +
552 create_table "thumbnails", :force => true do |t| 599 create_table "thumbnails", :force => true do |t|
553 t.integer "size" 600 t.integer "size"
554 t.string "content_type" 601 t.string "content_type"
plugins/anti_spam/lib/anti_spam_plugin.rb
@@ -5,7 +5,7 @@ class AntiSpamPlugin &lt; Noosfero::Plugin @@ -5,7 +5,7 @@ class AntiSpamPlugin &lt; Noosfero::Plugin
5 end 5 end
6 6
7 def self.plugin_description 7 def self.plugin_description
8 - _("Checks comments against a spam checking service compatible with the Akismet API") 8 + _("Tests comments and suggested articles against a spam checking service compatible with the Akismet API")
9 end 9 end
10 10
11 def self.host_default_setting 11 def self.host_default_setting
@@ -13,30 +13,44 @@ class AntiSpamPlugin &lt; Noosfero::Plugin @@ -13,30 +13,44 @@ class AntiSpamPlugin &lt; Noosfero::Plugin
13 end 13 end
14 14
15 def check_comment_for_spam(comment) 15 def check_comment_for_spam(comment)
16 - if rakismet_call(comment, :spam?) 16 + if rakismet_call AntiSpamPlugin::CommentWrapper.new(comment), comment.environment, :spam?
17 comment.spam = true 17 comment.spam = true
18 comment.save! 18 comment.save!
19 end 19 end
20 end 20 end
21 21
22 def comment_marked_as_spam(comment) 22 def comment_marked_as_spam(comment)
23 - rakismet_call(comment, :spam!) 23 + rakismet_call AntiSpamPlugin::CommentWrapper.new(comment), comment.environment, :spam!
24 end 24 end
25 25
26 def comment_marked_as_ham(comment) 26 def comment_marked_as_ham(comment)
27 - rakismet_call(comment, :ham!) 27 + rakismet_call AntiSpamPlugin::CommentWrapper.new(comment), comment.environment, :ham!
  28 + end
  29 +
  30 + def check_suggest_article_for_spam(suggest_article)
  31 + if rakismet_call AntiSpamPlugin::SuggestArticleWrapper.new(suggest_article), suggest_article.environment, :spam?
  32 + suggest_article.spam = true
  33 + suggest_article.save!
  34 + end
  35 + end
  36 +
  37 + def suggest_article_marked_as_spam(suggest_article)
  38 + rakismet_call AntiSpamPlugin::SuggestArticleWrapper.new(suggest_article), suggest_article.environment, :spam!
  39 + end
  40 +
  41 + def suggest_article_marked_as_ham(suggest_article)
  42 + rakismet_call AntiSpamPlugin::SuggestArticleWrapper.new(suggest_article), suggest_article.environment, :ham!
28 end 43 end
29 44
30 protected 45 protected
31 46
32 - def rakismet_call(comment, op)  
33 - settings = Noosfero::Plugin::Settings.new(comment.environment, self.class) 47 + def rakismet_call(submission, environment, op)
  48 + settings = Noosfero::Plugin::Settings.new(environment, self.class)
34 49
35 Rakismet.host = settings.host 50 Rakismet.host = settings.host
36 Rakismet.key = settings.api_key 51 Rakismet.key = settings.api_key
37 - Rakismet.url = comment.environment.top_url 52 + Rakismet.url = environment.top_url
38 53
39 - submission = AntiSpamPlugin::CommentWrapper.new(comment)  
40 submission.send(op) 54 submission.send(op)
41 end 55 end
42 56
plugins/anti_spam/lib/anti_spam_plugin/suggest_article_wrapper.rb 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +class AntiSpamPlugin::SuggestArticleWrapper < Struct.new(:suggest_article)
  2 +
  3 + delegate :name, :email, :article_body, :ip_address, :user_agent, :referrer, :to => :suggest_article
  4 +
  5 + include Rakismet::Model
  6 +
  7 + alias :author :name
  8 + alias :author_email :email
  9 + alias :user_ip :ip_address
  10 + alias :content :article_body
  11 +
  12 +end
plugins/anti_spam/test/unit/anti_spam_plugin/suggest_article_wrapper_test.rb 0 → 100644
@@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
  1 +require 'test_helper'
  2 +
  3 +class AntiSpamPluginCommentWrapperTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @suggest_article = SuggestArticle.new(
  7 + :article_body => 'comment body',
  8 + :name => 'author',
  9 + :email => 'foo@example.com',
  10 + :ip_address => '1.2.3.4',
  11 + :user_agent => 'Some Good Browser (I hope)',
  12 + :referrer => 'http://noosfero.org/'
  13 + )
  14 + @wrapper = AntiSpamPlugin::SuggestArticleWrapper.new(@suggest_article)
  15 + end
  16 +
  17 + should 'use Rakismet::Model' do
  18 + assert_includes @wrapper.class.included_modules, Rakismet::Model
  19 + end
  20 +
  21 + should 'get contents' do
  22 + assert_equal @suggest_article.article_body, @wrapper.content
  23 + end
  24 +
  25 + should 'get author name' do
  26 + assert_equal @suggest_article.name, @wrapper.author
  27 + end
  28 +
  29 + should 'get author email' do
  30 + assert_equal @suggest_article.email, @wrapper.author_email
  31 + end
  32 +
  33 + should 'get IP address' do
  34 + assert_equal @suggest_article.ip_address, @wrapper.user_ip
  35 + end
  36 +
  37 + should 'get User-Agent' do
  38 + assert_equal @suggest_article.user_agent, @wrapper.user_agent
  39 + end
  40 +
  41 + should 'get get Referrer' do
  42 + assert_equal @suggest_article.referrer, @wrapper.referrer
  43 + end
  44 +
  45 +end
plugins/anti_spam/test/unit/anti_spam_plugin_test.rb
@@ -7,6 +7,11 @@ class AntiSpamPluginTest &lt; ActiveSupport::TestCase @@ -7,6 +7,11 @@ class AntiSpamPluginTest &lt; ActiveSupport::TestCase
7 article = fast_create(TextileArticle, :profile_id => profile.id) 7 article = fast_create(TextileArticle, :profile_id => profile.id)
8 @comment = fast_create(Comment, :source_id => article.id, :source_type => 'Article') 8 @comment = fast_create(Comment, :source_id => article.id, :source_type => 'Article')
9 9
  10 +
  11 + @suggest_article = SuggestArticle.new(:target_id => profile.id, :target_type => 'Profile', :article_name => 'article', :article_body => 'lorem ipsum', :email => 'invalid@example.com', :name => 'article')
  12 +
  13 + @suggest_article.save!
  14 +
10 @settings = Noosfero::Plugin::Settings.new(@comment.environment, AntiSpamPlugin) 15 @settings = Noosfero::Plugin::Settings.new(@comment.environment, AntiSpamPlugin)
11 @settings.api_key = 'b8b80ddb8084062d0c9119c945ce3bc3' 16 @settings.api_key = 'b8b80ddb8084062d0c9119c945ce3bc3'
12 @settings.save! 17 @settings.save!
@@ -23,14 +28,32 @@ class AntiSpamPluginTest &lt; ActiveSupport::TestCase @@ -23,14 +28,32 @@ class AntiSpamPluginTest &lt; ActiveSupport::TestCase
23 assert @comment.spam 28 assert @comment.spam
24 end 29 end
25 30
26 - should 'report spam' do 31 + should 'report comment spam' do
27 AntiSpamPlugin::CommentWrapper.any_instance.expects(:spam!) 32 AntiSpamPlugin::CommentWrapper.any_instance.expects(:spam!)
28 @plugin.comment_marked_as_spam(@comment) 33 @plugin.comment_marked_as_spam(@comment)
29 end 34 end
30 35
31 - should 'report ham' do 36 + should 'report comment ham' do
32 AntiSpamPlugin::CommentWrapper.any_instance.expects(:ham!) 37 AntiSpamPlugin::CommentWrapper.any_instance.expects(:ham!)
33 @plugin.comment_marked_as_ham(@comment) 38 @plugin.comment_marked_as_ham(@comment)
34 end 39 end
35 40
  41 + should 'check for spam and mark suggest_article as spam if server says it is spam' do
  42 + AntiSpamPlugin::SuggestArticleWrapper.any_instance.expects(:spam?).returns(true)
  43 + @suggest_article.expects(:save!)
  44 +
  45 + @plugin.check_suggest_article_for_spam(@suggest_article)
  46 + assert @suggest_article.spam
  47 + end
  48 +
  49 + should 'report suggest_article spam' do
  50 + AntiSpamPlugin::SuggestArticleWrapper.any_instance.expects(:spam!)
  51 + @plugin.suggest_article_marked_as_spam(@suggest_article)
  52 + end
  53 +
  54 + should 'report suggest_article ham' do
  55 + AntiSpamPlugin::SuggestArticleWrapper.any_instance.expects(:ham!)
  56 + @plugin.suggest_article_marked_as_ham(@suggest_article)
  57 + end
  58 +
36 end 59 end
public/javascripts/spam.js 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +function removeTaskBox(button, url, task_box_id, msg) {
  2 + var $ = jQuery;
  3 + if (msg && !confirm(msg)) {
  4 + return;
  5 + }
  6 + button = $(button);
  7 + button.addClass('task-button-loading');
  8 + $.post(url, function (data) {
  9 + if (data.ok) {
  10 + $('#' + task_box_id).slideUp();
  11 + } else {
  12 + button.removeClass('task-button-loading');
  13 + button.addClass('task-button-failure');
  14 + }
  15 + });
  16 +}
  17 +
  18 +function toggleDetails(link, msg_hide, msg_show) {
  19 + var $ = jQuery;
  20 + $(link).toggleClass('icon-up icon-down');
  21 + details = $(link).closest('.task_box').find('.suggest-article-details');
  22 + if (details.css('display') == 'none') {
  23 + link.innerHTML = msg_hide;
  24 + } else {
  25 + link.innerHTML = msg_show;
  26 + }
  27 + details.slideToggle();
  28 +}
test/functional/spam_controller_test.rb
@@ -4,37 +4,55 @@ class SpamControllerTest &lt; ActionController::TestCase @@ -4,37 +4,55 @@ class SpamControllerTest &lt; ActionController::TestCase
4 4
5 def setup 5 def setup
6 @profile = create_user.person 6 @profile = create_user.person
7 - @article = fast_create(TextileArticle, :profile_id => @profile.id)  
8 - @spam = fast_create(Comment, :source_id => @article.id, :spam => true, :name => 'foo', :email => 'foo@example.com')  
9 7
  8 + @community = fast_create(Community, :name => 'testcommunity')
  9 + @community.add_admin(@profile)
  10 + @article = fast_create(TextileArticle, :profile_id => @community.id)
  11 + @spam_comment = fast_create(Comment, :source_id => @article.id, :spam => true, :name => 'foo', :email => 'foo@example.com')
  12 +
  13 + @spam_suggest_article = SuggestArticle.create!(:name => 'spammer', :article_name => 'Spam article', :email => 'spammer@shady.place', :article_body => "Something you don't need", :target => @community, :spam => true)
10 login_as @profile.identifier 14 login_as @profile.identifier
11 end 15 end
12 16
13 - test "should only list spammy comments" do 17 + test "should only list spammy comments and spammy suggest articles" do
14 ham = fast_create(Comment, :source_id => @article.id) 18 ham = fast_create(Comment, :source_id => @article.id)
15 19
16 - get :index, :profile => @profile.identifier 20 + get :index, :profile => @community.identifier
17 21
18 - assert_equivalent [@spam], assigns(:spam) 22 + assert_equivalent [@spam_comment], assigns(:comment_spam)
  23 + assert_equivalent [@spam_suggest_article], assigns(:task_spam)
19 end 24 end
20 25
21 test "should mark comments as ham" do 26 test "should mark comments as ham" do
22 - post :index, :profile => @profile.identifier, :mark_comment_as_ham => @spam.id 27 + post :index, :profile => @community.identifier, :mark_comment_as_ham => @spam_comment.id
  28 +
  29 + @spam_comment.reload
  30 + assert @spam_comment.ham?
  31 + end
  32 +
  33 + test "should mark suggest article as ham" do
  34 + post :index, :profile => @community.identifier, :mark_task_as_ham => @spam_suggest_article.id
23 35
24 - @spam.reload  
25 - assert @spam.ham? 36 + @spam_suggest_article.reload
  37 + assert @spam_suggest_article.ham?
26 end 38 end
27 39
28 test "should remove comments" do 40 test "should remove comments" do
29 - post :index, :profile => @profile.identifier, :remove_comment => @spam.id 41 + post :index, :profile => @community.identifier, :remove_comment => @spam_comment.id
  42 +
  43 + assert !Comment.exists?(@spam_comment.id)
  44 + end
  45 +
  46 + test "should remove suggest articles" do
  47 + post :index, :profile => @community.identifier, :remove_task => @spam_suggest_article.id
30 48
31 - assert !Comment.exists?(@spam.id) 49 + assert !SuggestArticle.exists?(@spam_suggest_article.id)
32 end 50 end
33 51
34 should 'properly render spam that have replies' do 52 should 'properly render spam that have replies' do
35 - reply_spam = fast_create(Comment, :source_id => @article_id, :reply_of_id => @spam.id) 53 + reply_spam = fast_create(Comment, :source_id => @article_id, :reply_of_id => @spam_comment.id)
36 54
37 - get :index, :profile => @profile.identifier 55 + get :index, :profile => @community.identifier
38 assert_response :success 56 assert_response :success
39 end 57 end
40 58
test/functional/tasks_controller_test.rb
@@ -38,6 +38,17 @@ class TasksControllerTest &lt; ActionController::TestCase @@ -38,6 +38,17 @@ class TasksControllerTest &lt; ActionController::TestCase
38 assert_kind_of Array, assigns(:tasks) 38 assert_kind_of Array, assigns(:tasks)
39 end 39 end
40 40
  41 + should 'list pending tasks without spam' do
  42 + requestor = fast_create(Person)
  43 + task_spam = Task.create!(:requestor => requestor, :target => profile, :spam => true)
  44 + task_ham = Task.create!(:requestor => requestor, :target => profile, :spam => false)
  45 +
  46 + get :index
  47 + assert_response :success
  48 + assert_includes assigns(:tasks), task_ham
  49 + assert_not_includes assigns(:tasks), task_spam
  50 + end
  51 +
41 should 'list processed tasks' do 52 should 'list processed tasks' do
42 get :processed 53 get :processed
43 54
@@ -46,6 +57,17 @@ class TasksControllerTest &lt; ActionController::TestCase @@ -46,6 +57,17 @@ class TasksControllerTest &lt; ActionController::TestCase
46 assert_kind_of Array, assigns(:tasks) 57 assert_kind_of Array, assigns(:tasks)
47 end 58 end
48 59
  60 + should 'list processed tasks without spam' do
  61 + requestor = fast_create(Person)
  62 + task_spam = Task.create!(:status => Task::Status::FINISHED, :requestor => requestor, :target => profile, :spam => true)
  63 + task_ham = Task.create!(:status => Task::Status::FINISHED, :requestor => requestor, :target => profile, :spam => false)
  64 +
  65 + get :processed
  66 + assert_response :success
  67 + assert_includes assigns(:tasks), task_ham
  68 + assert_not_includes assigns(:tasks), task_spam
  69 + end
  70 +
49 should 'be able to finish a task' do 71 should 'be able to finish a task' do
50 t = profile.tasks.build; t.save! 72 t = profile.tasks.build; t.save!
51 73
@@ -140,6 +162,15 @@ class TasksControllerTest &lt; ActionController::TestCase @@ -140,6 +162,15 @@ class TasksControllerTest &lt; ActionController::TestCase
140 assert_includes assigns(:tasks), task 162 assert_includes assigns(:tasks), task
141 end 163 end
142 164
  165 + should 'list tasks that this profile created without spam' do
  166 + task_spam = Ticket.create!(:name => 'test', :requestor => profile, :spam => true)
  167 + task_ham = Ticket.create!(:name => 'test', :requestor => profile, :spam => false)
  168 + get :list_requested, :profile => profile.identifier
  169 +
  170 + assert_includes assigns(:tasks), task_ham
  171 + assert_not_includes assigns(:tasks), task_spam
  172 + end
  173 +
143 should 'set target of ticket when creating it' do 174 should 'set target of ticket when creating it' do
144 f = create_user('friend').person 175 f = create_user('friend').person
145 profile.add_friend f 176 profile.add_friend f
test/unit/suggest_article_test.rb
@@ -150,5 +150,79 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase @@ -150,5 +150,79 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase
150 assert_match(/#{task.name}.*suggested the publication of the article: #{task.subject}/, email.subject) 150 assert_match(/#{task.name}.*suggested the publication of the article: #{task.subject}/, email.subject)
151 end 151 end
152 152
  153 + class EverythingIsSpam < Noosfero::Plugin
  154 + def check_suggest_article_for_spam(suggest_article)
  155 + suggest_article.spam!
  156 + end
  157 + end
  158 +
  159 + should 'delegate spam detection to plugins' do
  160 + Environment.default.enable_plugin(EverythingIsSpam)
  161 +
  162 + t1 = build(SuggestArticle, :target => @profile, :article_name => 'suggested article', :name => 'johndoe', :email => 'johndoe@example.com')
  163 +
  164 + EverythingIsSpam.any_instance.expects(:check_suggest_article_for_spam)
  165 +
  166 + t1.check_for_spam
  167 + end
  168 +
  169 + class SpamNotification < Noosfero::Plugin
  170 + class << self
  171 + attr_accessor :marked_as_spam
  172 + attr_accessor :marked_as_ham
  173 + end
  174 +
  175 + def check_suggest_article_for_spam(c)
  176 + # do nothing
  177 + end
  178 +
  179 + def suggest_article_marked_as_spam(c)
  180 + self.class.marked_as_spam = c
  181 + end
  182 +
  183 + def suggest_article_marked_as_ham(c)
  184 + self.class.marked_as_ham = c
  185 + end
  186 + end
  187 +
  188 + should 'notify plugins of suggest_articles being marked as spam' do
  189 + Environment.default.enable_plugin(SpamNotification)
  190 +
  191 + t = SuggestArticle.create!(:target => @profile, :article_name => 'suggested article', :name => 'johndoe', :article_body => 'wanna feel my body? my body baaaby', :email => 'johndoe@example.com')
  192 +
  193 + t.spam!
  194 + process_delayed_job_queue
  195 +
  196 + assert_equal t, SpamNotification.marked_as_spam
  197 + end
  198 +
  199 + should 'notify plugins of suggest_articles being marked as ham' do
  200 + Environment.default.enable_plugin(SpamNotification)
  201 +
  202 + t = SuggestArticle.create!(:target => @profile, :article_name => 'suggested article', :name => 'johndoe', :article_body => 'wanna feel my body? my body baaaby', :email => 'johndoe@example.com')
  203 +
  204 + t.ham!
  205 + process_delayed_job_queue
  206 +
  207 + assert_equal t, SpamNotification.marked_as_ham
  208 + end
  209 +
  210 + should 'store User-Agent' do
  211 + t = SuggestArticle.new(:user_agent => 'foo')
  212 + assert_equal 'foo', t.user_agent
  213 + end
  214 +
  215 + should 'store referrer' do
  216 + t = SuggestArticle.new(:referrer => 'bar')
  217 + assert_equal 'bar', t.referrer
  218 + end
  219 +
  220 + should 'log spammer ip after marking comment as spam' do
  221 + t = SuggestArticle.create!(:target => @profile, :article_name => 'suggested article', :name => 'johndoe', :article_body => 'wanna feel my body? my body baaaby', :email => 'johndoe@example.com', :ip_address => '192.168.0.1')
  222 + t.spam!
  223 + log = File.open('log/test_spammers.log')
  224 + assert_match "SuggestArticle-id: #{t.id} IP: 192.168.0.1", log.read
  225 + SpammerLogger.clean_log
  226 + end
153 227
154 end 228 end
test/unit/task_test.rb
@@ -372,6 +372,55 @@ class TaskTest &lt; ActiveSupport::TestCase @@ -372,6 +372,55 @@ class TaskTest &lt; ActiveSupport::TestCase
372 assert_includes Task.closed, canceled 372 assert_includes Task.closed, canceled
373 end 373 end
374 374
  375 + should 'be ham by default' do # ham means not spam
  376 + assert_equal false, Task.create.spam
  377 + end
  378 +
  379 + should 'be able to mark tasks as spam/ham/unknown' do
  380 + t = Task.new
  381 + t.spam = true
  382 + assert t.spam?
  383 + assert !t.ham?
  384 +
  385 + t.spam = false
  386 + assert t.ham?
  387 + assert !t.spam?
  388 +
  389 + t.spam = nil
  390 + assert !t.spam?
  391 + assert !t.ham?
  392 + end
  393 +
  394 + should 'be able to select non-spam tasks' do
  395 + t1 = fast_create(Task)
  396 + t2 = fast_create(Task, :spam => false)
  397 + t3 = fast_create(Task, :spam => true)
  398 +
  399 + assert_equivalent [t1,t2], Task.without_spam
  400 + end
  401 +
  402 + should 'be able to select spam tasks' do
  403 + t1 = fast_create(Task)
  404 + t2 = fast_create(Task, :spam => false)
  405 + t3 = fast_create(Task, :spam => true)
  406 +
  407 + assert_equivalent [t3], Task.spam
  408 + end
  409 +
  410 + should 'be able to mark as spam' do
  411 + t1 = fast_create(Task)
  412 + t1.spam!
  413 + t1.reload
  414 + assert t1.spam?
  415 + end
  416 +
  417 + should 'be able to mark as ham' do
  418 + t1 = fast_create(Task)
  419 + t1.ham!
  420 + t1.reload
  421 + assert t1.ham?
  422 + end
  423 +
375 protected 424 protected
376 425
377 def sample_user 426 def sample_user