Commit cbc821f22fa498cebe584b77a0fc053a263856ca

Authored by Daniela Feitosa
2 parents f4012a36 d570dc44

Merge commit 'refs/merge-requests/216' of git://gitorious.org/noosfero/noosfero …

…into merge-requests/216

Conflicts:
	debian/changelog
	etc/noosfero/varnish-noosfero.vcl
	lib/noosfero.rb
Showing 94 changed files with 2774 additions and 409 deletions   Show diff stats
app/controllers/application_controller.rb
@@ -101,9 +101,10 @@ class ApplicationController < ActionController::Base @@ -101,9 +101,10 @@ class ApplicationController < ActionController::Base
101 end 101 end
102 end 102 end
103 103
  104 + include Noosfero::Plugin::HotSpot
  105 +
104 def init_noosfero_plugins 106 def init_noosfero_plugins
105 - @plugins = Noosfero::Plugin::Manager.new(self)  
106 - @plugins.each do |plugin| 107 + plugins.each do |plugin|
107 prepend_view_path(plugin.class.view_path) 108 prepend_view_path(plugin.class.view_path)
108 end 109 end
109 init_noosfero_plugins_controller_filters 110 init_noosfero_plugins_controller_filters
@@ -112,7 +113,7 @@ class ApplicationController < ActionController::Base @@ -112,7 +113,7 @@ class ApplicationController < ActionController::Base
112 # This is a generic method that initialize any possible filter defined by a 113 # This is a generic method that initialize any possible filter defined by a
113 # plugin to the current controller being initialized. 114 # plugin to the current controller being initialized.
114 def init_noosfero_plugins_controller_filters 115 def init_noosfero_plugins_controller_filters
115 - @plugins.each do |plugin| 116 + plugins.each do |plugin|
116 plugin.send(self.class.name.underscore + '_filters').each do |plugin_filter| 117 plugin.send(self.class.name.underscore + '_filters').each do |plugin_filter|
117 self.class.send(plugin_filter[:type], plugin.class.name.underscore + '_' + plugin_filter[:method_name], (plugin_filter[:options] || {})) 118 self.class.send(plugin_filter[:type], plugin.class.name.underscore + '_' + plugin_filter[:method_name], (plugin_filter[:options] || {}))
118 self.class.send(:define_method, plugin.class.name.underscore + '_' + plugin_filter[:method_name], plugin_filter[:block]) 119 self.class.send(:define_method, plugin.class.name.underscore + '_' + plugin_filter[:method_name], plugin_filter[:block])
app/controllers/my_profile/spam_controller.rb 0 → 100644
@@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
  1 +class SpamController < MyProfileController
  2 +
  3 + protect :moderate_comments, :profile
  4 +
  5 + def index
  6 + if request.post?
  7 + begin
  8 + # FIXME duplicated logic
  9 + #
  10 + # This logic more or less replicates what is already in
  11 + # ContentViewerController#view_page,
  12 + # ContentViewerController#remove_comment and
  13 + # ContentViewerController#mark_comment_as_spam
  14 + if params[:remove_comment]
  15 + profile.comments_received.find(params[:remove_comment]).destroy
  16 + end
  17 + if params[:mark_comment_as_ham]
  18 + profile.comments_received.find(params[:mark_comment_as_ham]).ham!
  19 + end
  20 + if request.xhr?
  21 + json_response(true)
  22 + else
  23 + redirect_to :action => :index
  24 + end
  25 + rescue
  26 + json_response(false)
  27 + end
  28 + return
  29 + end
  30 +
  31 + @spam = profile.comments_received.spam.paginate({:page => params[:page]})
  32 + end
  33 +
  34 + protected
  35 +
  36 + def json_response(status)
  37 + render :text => {'ok' => status }.to_json, :content_type => 'application/json'
  38 + end
  39 +
  40 +end
app/controllers/public/content_viewer_controller.rb
@@ -75,8 +75,14 @@ class ContentViewerController &lt; ApplicationController @@ -75,8 +75,14 @@ class ContentViewerController &lt; ApplicationController
75 @comment = Comment.new 75 @comment = Comment.new
76 end 76 end
77 77
78 - if request.post? && params[:remove_comment]  
79 - remove_comment 78 + if request.post?
  79 + if params[:remove_comment]
  80 + remove_comment
  81 + return
  82 + elsif params[:mark_comment_as_spam]
  83 + mark_comment_as_spam
  84 + return
  85 + end
80 end 86 end
81 87
82 if @page.has_posts? 88 if @page.has_posts?
@@ -107,8 +113,9 @@ class ContentViewerController &lt; ApplicationController @@ -107,8 +113,9 @@ class ContentViewerController &lt; ApplicationController
107 end 113 end
108 end 114 end
109 115
110 - @comments = @page.comments(true).as_thread  
111 - @comments_count = @page.comments.count 116 + comments = @page.comments.without_spam
  117 + @comments = comments.as_thread
  118 + @comments_count = comments.count
112 if params[:slideshow] 119 if params[:slideshow]
113 render :action => 'slideshow', :layout => 'slideshow' 120 render :action => 'slideshow', :layout => 'slideshow'
114 end 121 end
@@ -120,10 +127,11 @@ class ContentViewerController &lt; ApplicationController @@ -120,10 +127,11 @@ class ContentViewerController &lt; ApplicationController
120 @comment.author = user if logged_in? 127 @comment.author = user if logged_in?
121 @comment.article = @page 128 @comment.article = @page
122 @comment.ip_address = request.remote_ip 129 @comment.ip_address = request.remote_ip
  130 + @comment.user_agent = request.user_agent
  131 + @comment.referrer = request.referrer
123 plugins_filter_comment(@comment) 132 plugins_filter_comment(@comment)
124 return if @comment.rejected? 133 return if @comment.rejected?
125 if (pass_without_comment_captcha? || verify_recaptcha(:model => @comment, :message => _('Please type the words correctly'))) && @comment.save 134 if (pass_without_comment_captcha? || verify_recaptcha(:model => @comment, :message => _('Please type the words correctly'))) && @comment.save
126 - plugins_comment_saved(@comment)  
127 @page.touch 135 @page.touch
128 @comment = nil # clear the comment form 136 @comment = nil # clear the comment form
129 redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view] 137 redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view]
@@ -138,12 +146,6 @@ class ContentViewerController &lt; ApplicationController @@ -138,12 +146,6 @@ class ContentViewerController &lt; ApplicationController
138 end 146 end
139 end 147 end
140 148
141 - def plugins_comment_saved(comment)  
142 - @plugins.each do |plugin|  
143 - plugin.comment_saved(comment)  
144 - end  
145 - end  
146 -  
147 def pass_without_comment_captcha? 149 def pass_without_comment_captcha?
148 logged_in? && !environment.enabled?('captcha_for_logged_users') 150 logged_in? && !environment.enabled?('captcha_for_logged_users')
149 end 151 end
@@ -153,9 +155,24 @@ class ContentViewerController &lt; ApplicationController @@ -153,9 +155,24 @@ class ContentViewerController &lt; ApplicationController
153 @comment = @page.comments.find(params[:remove_comment]) 155 @comment = @page.comments.find(params[:remove_comment])
154 if (user == @comment.author || user == @page.profile || user.has_permission?(:moderate_comments, @page.profile)) 156 if (user == @comment.author || user == @page.profile || user.has_permission?(:moderate_comments, @page.profile))
155 @comment.destroy 157 @comment.destroy
156 - session[:notice] = _('Comment succesfully deleted')  
157 end 158 end
158 - redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view] 159 + finish_comment_handling
  160 + end
  161 +
  162 + def mark_comment_as_spam
  163 + @comment = @page.comments.find(params[:mark_comment_as_spam])
  164 + if logged_in? && (user == @page.profile || user.has_permission?(:moderate_comments, @page.profile))
  165 + @comment.spam!
  166 + end
  167 + finish_comment_handling
  168 + end
  169 +
  170 + def finish_comment_handling
  171 + if request.xhr?
  172 + render :text => {'ok' => true}.to_json, :content_type => 'application/json'
  173 + else
  174 + redirect_to :action => 'view_page', :profile => params[:profile], :page => @page.explode_path, :view => params[:view]
  175 + end
159 end 176 end
160 177
161 def per_page 178 def per_page
app/helpers/content_viewer_helper.rb
@@ -4,11 +4,11 @@ module ContentViewerHelper @@ -4,11 +4,11 @@ module ContentViewerHelper
4 include ForumHelper 4 include ForumHelper
5 5
6 def number_of_comments(article) 6 def number_of_comments(article)
7 - n = article.comments.size 7 + n = article.comments.without_spam.count
8 if n == 0 8 if n == 0
9 _('No comments yet') 9 _('No comments yet')
10 else 10 else
11 - n_('One comment', '%{comments} comments', n) % { :comments => n } 11 + n_('One comment', '<span class="comment-count">%{comments}</span> comments', n) % { :comments => n }
12 end 12 end
13 end 13 end
14 14
app/models/comment.rb
@@ -10,6 +10,9 @@ class Comment &lt; ActiveRecord::Base @@ -10,6 +10,9 @@ class Comment &lt; ActiveRecord::Base
10 has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy 10 has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy
11 belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id' 11 belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id'
12 12
  13 + named_scope :without_spam, :conditions => ['spam IS NULL OR spam = ?', false]
  14 + named_scope :spam, :conditions => ['spam = ?', true]
  15 +
13 # unauthenticated authors: 16 # unauthenticated authors:
14 validates_presence_of :name, :if => (lambda { |record| !record.email.blank? }) 17 validates_presence_of :name, :if => (lambda { |record| !record.email.blank? })
15 validates_presence_of :email, :if => (lambda { |record| !record.name.blank? }) 18 validates_presence_of :email, :if => (lambda { |record| !record.name.blank? })
@@ -85,7 +88,28 @@ class Comment &lt; ActiveRecord::Base @@ -85,7 +88,28 @@ class Comment &lt; ActiveRecord::Base
85 end 88 end
86 end 89 end
87 90
88 - after_create :notify_by_mail 91 + after_create :schedule_notification
  92 +
  93 + def schedule_notification
  94 + Delayed::Job.enqueue CommentHandler.new(self.id, :verify_and_notify)
  95 + end
  96 +
  97 + delegate :environment, :to => :profile
  98 + delegate :profile, :to => :source
  99 +
  100 + include Noosfero::Plugin::HotSpot
  101 +
  102 + def verify_and_notify
  103 + check_for_spam
  104 + unless spam?
  105 + notify_by_mail
  106 + end
  107 + end
  108 +
  109 + def check_for_spam
  110 + plugins.dispatch(:check_comment_for_spam, self)
  111 + end
  112 +
89 def notify_by_mail 113 def notify_by_mail
90 if source.kind_of?(Article) && article.notify_comments? 114 if source.kind_of?(Article) && article.notify_comments?
91 if !article.profile.notification_emails.empty? 115 if !article.profile.notification_emails.empty?
@@ -123,10 +147,14 @@ class Comment &lt; ActiveRecord::Base @@ -123,10 +147,14 @@ class Comment &lt; ActiveRecord::Base
123 def self.as_thread 147 def self.as_thread
124 result = {} 148 result = {}
125 root = [] 149 root = []
126 - all.each do |c| 150 + order(:id).each do |c|
127 c.replies = [] 151 c.replies = []
128 result[c.id] ||= c 152 result[c.id] ||= c
129 - c.reply_of_id.nil? ? root << c : result[c.reply_of_id].replies << c 153 + if result[c.reply_of_id]
  154 + result[c.reply_of_id].replies << c
  155 + else
  156 + root << c
  157 + end
130 end 158 end
131 root 159 root
132 end 160 end
@@ -183,4 +211,34 @@ class Comment &lt; ActiveRecord::Base @@ -183,4 +211,34 @@ class Comment &lt; ActiveRecord::Base
183 @rejected = true 211 @rejected = true
184 end 212 end
185 213
  214 + def spam?
  215 + !spam.nil? && spam
  216 + end
  217 +
  218 + def ham?
  219 + !spam.nil? && !spam
  220 + end
  221 +
  222 + def spam!
  223 + self.spam = true
  224 + self.save!
  225 + Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_spam))
  226 + self
  227 + end
  228 +
  229 + def ham!
  230 + self.spam = false
  231 + self.save!
  232 + Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_ham))
  233 + self
  234 + end
  235 +
  236 + def marked_as_spam
  237 + plugins.dispatch(:comment_marked_as_spam, self)
  238 + end
  239 +
  240 + def marked_as_ham
  241 + plugins.dispatch(:comment_marked_as_ham, self)
  242 + end
  243 +
186 end 244 end
app/models/comment_handler.rb 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +class CommentHandler < Struct.new(:comment_id, :method)
  2 +
  3 + def perform
  4 + comment = Comment.find(comment_id)
  5 + comment.send(method)
  6 + rescue ActiveRecord::RecordNotFound
  7 + # just ignore non-existing comments
  8 + end
  9 +
  10 +end
app/models/person.rb
@@ -22,8 +22,6 @@ class Person &lt; Profile @@ -22,8 +22,6 @@ class Person &lt; Profile
22 super 22 super
23 end 23 end
24 24
25 - acts_as_having_hotspots  
26 -  
27 named_scope :members_of, lambda { |resources| 25 named_scope :members_of, lambda { |resources|
28 resources = [resources] if !resources.kind_of?(Array) 26 resources = [resources] if !resources.kind_of?(Array)
29 conditions = resources.map {|resource| "role_assignments.resource_type = '#{resource.class.base_class.name}' AND role_assignments.resource_id = #{resource.id || -1}"}.join(' OR ') 27 conditions = resources.map {|resource| "role_assignments.resource_type = '#{resource.class.base_class.name}' AND role_assignments.resource_id = #{resource.id || -1}"}.join(' OR ')
@@ -32,7 +30,7 @@ class Person &lt; Profile @@ -32,7 +30,7 @@ class Person &lt; Profile
32 30
33 def has_permission_with_plugins?(permission, profile) 31 def has_permission_with_plugins?(permission, profile)
34 permissions = [has_permission_without_plugins?(permission, profile)] 32 permissions = [has_permission_without_plugins?(permission, profile)]
35 - permissions += enabled_plugins.map do |plugin| 33 + permissions += plugins.map do |plugin|
36 plugin.has_permission?(self, permission, profile) 34 plugin.has_permission?(self, permission, profile)
37 end 35 end
38 permissions.include?(true) 36 permissions.include?(true)
app/models/profile.rb
@@ -60,7 +60,8 @@ class Profile &lt; ActiveRecord::Base @@ -60,7 +60,8 @@ class Profile &lt; ActiveRecord::Base
60 } 60 }
61 61
62 acts_as_accessible 62 acts_as_accessible
63 - acts_as_having_hotspots 63 +
  64 + include Noosfero::Plugin::HotSpot
64 65
65 named_scope :memberships_of, lambda { |person| { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.accessor_type = ? AND role_assignments.accessor_id = ?', person.class.base_class.name, person.id ] } } 66 named_scope :memberships_of, lambda { |person| { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.accessor_type = ? AND role_assignments.accessor_id = ?', person.class.base_class.name, person.id ] } }
66 #FIXME: these will work only if the subclass is already loaded 67 #FIXME: these will work only if the subclass is already loaded
@@ -69,7 +70,7 @@ class Profile &lt; ActiveRecord::Base @@ -69,7 +70,7 @@ class Profile &lt; ActiveRecord::Base
69 named_scope :templates, :conditions => {:is_template => true} 70 named_scope :templates, :conditions => {:is_template => true}
70 71
71 def members 72 def members
72 - scopes = dispatch_scopes(:organization_members, self) 73 + scopes = plugins.dispatch_scopes(:organization_members, self)
73 scopes << Person.members_of(self) 74 scopes << Person.members_of(self)
74 scopes.size == 1 ? scopes.first : Person.or_scope(scopes) 75 scopes.size == 1 ? scopes.first : Person.or_scope(scopes)
75 end 76 end
@@ -113,6 +114,8 @@ class Profile &lt; ActiveRecord::Base @@ -113,6 +114,8 @@ class Profile &lt; ActiveRecord::Base
113 has_many :scraps_received, :class_name => 'Scrap', :foreign_key => :receiver_id, :order => "updated_at DESC", :dependent => :destroy 114 has_many :scraps_received, :class_name => 'Scrap', :foreign_key => :receiver_id, :order => "updated_at DESC", :dependent => :destroy
114 belongs_to :template, :class_name => 'Profile', :foreign_key => 'template_id' 115 belongs_to :template, :class_name => 'Profile', :foreign_key => 'template_id'
115 116
  117 + has_many :comments_received, :class_name => 'Comment', :through => :articles, :source => :comments
  118 +
116 # FIXME ugly workaround 119 # FIXME ugly workaround
117 def self.human_attribute_name(attrib) 120 def self.human_attribute_name(attrib)
118 _(self.superclass.human_attribute_name(attrib)) 121 _(self.superclass.human_attribute_name(attrib))
@@ -255,7 +258,7 @@ class Profile &lt; ActiveRecord::Base @@ -255,7 +258,7 @@ class Profile &lt; ActiveRecord::Base
255 self.categories(true) 258 self.categories(true)
256 self.solr_save 259 self.solr_save
257 end 260 end
258 - self.categories(reload) 261 + self.categories(reload)
259 end 262 end
260 263
261 def category_ids=(ids) 264 def category_ids=(ids)
app/views/admin_panel/index.rhtml
1 <h1><%= _('Administrator Panel') %></h1> 1 <h1><%= _('Administrator Panel') %></h1>
2 2
3 -<p><%= _('You, as an environment administrator, has the following options:')%></p> 3 +<h2><%= _('System settings') %></h2>
4 4
5 <table> 5 <table>
6 - <tr><td><%= link_to _('Edit environment settings'), :action => 'site_info' %></td></tr>  
7 - <tr><td><%= link_to __('Edit message for disabled enterprises'), :action => 'message_for_disabled_enterprise' %></td></tr>  
8 - <tr><td><%= link_to _('Enable/disable features'), :controller => 'features' %></td></tr>  
9 - <tr><td><%= link_to _('Enable/disable plugins'), :controller => 'plugins' %></td></tr>  
10 - <tr><td><%= link_to _('Edit sideboxes'), :controller => 'environment_design'%></td></tr>  
11 - <tr><td><%= link_to _('Manage Categories'), :controller => 'categories'%></td></tr>  
12 - <tr><td><%= link_to _('Manage User roles'), :controller => 'role' %></td></tr>  
13 - <tr><td><%= link_to _('Manage users'), :controller => 'users' %></td></tr>  
14 - <tr><td><%= link_to _('Manage Validators by region'), :controller => 'region_validators' %></td></tr>  
15 - <tr><td><%= link_to _('Edit Templates'), :controller => 'templates' %></td></tr>  
16 - <tr><td><%= link_to _('Manage Fields'), :controller => 'features', :action => 'manage_fields' %></td></tr>  
17 - <tr><td><%= link_to _('Set Portal'), :action => 'set_portal_community' %></td></tr> 6 + <tr><td><%= link_to _('Environment settings'), :action => 'site_info' %></td></tr>
  7 + <tr><td><%= link_to _('Features'), :controller => 'features' %></td></tr>
  8 + <tr><td><%= link_to _('Plugins'), :controller => 'plugins' %></td></tr>
  9 + <tr><td><%= link_to _('Sideboxes'), :controller => 'environment_design'%></td></tr>
  10 + <tr><td><%= link_to _('Homepage'), :action => 'set_portal_community' %></td></tr>
18 <tr><td><%= link_to _('Manage Licenses'), :controller =>'licenses' %></td></tr> 11 <tr><td><%= link_to _('Manage Licenses'), :controller =>'licenses' %></td></tr>
19 - <% @plugins.dispatch(:admin_panel_links).each do |link| %> 12 +</table>
  13 +
  14 +<h2><%= _('Profiles') %></h2>
  15 +
  16 +<table>
  17 + <tr><td><%= link_to _('User roles'), :controller => 'role' %></td></tr>
  18 + <tr><td><%= link_to _('Users'), :controller => 'users' %></td></tr>
  19 + <tr><td><%= link_to _('Profile templates'), :controller => 'templates' %></td></tr>
  20 + <tr><td><%= link_to _('Fields'), :controller => 'features', :action => 'manage_fields' %></td></tr>
  21 +</table>
  22 +
  23 +
  24 +<%
  25 + plugin_links = @plugins.dispatch(:admin_panel_links)
  26 +%>
  27 +<% unless plugin_links.empty? %>
  28 + <h2><%= _('Plugins') %></h2>
  29 + <table>
  30 + <% plugin_links.each do |link| %>
20 <tr><td><%= link_to link[:title], link[:url] %></td></tr> 31 <tr><td><%= link_to link[:title], link[:url] %></td></tr>
21 <% end %> 32 <% end %>
  33 + </table>
  34 +<% end %>
  35 +
  36 +<h2><%= _('Enterprise-related settings') %></h2>
  37 +
  38 +<table>
  39 + <tr><td><%= link_to __('Message for disabled enterprises'), :action => 'message_for_disabled_enterprise' %></td></tr>
  40 + <tr><td><%= link_to _('Validators by region'), :controller => 'region_validators' %></td></tr>
  41 + <tr><td><%= link_to _('Categories'), :controller => 'categories'%></td></tr>
22 </table> 42 </table>
app/views/content_viewer/_comment.rhtml
1 <li id="<%= comment.anchor %>" class="article-comment"> 1 <li id="<%= comment.anchor %>" class="article-comment">
2 <div class="article-comment-inner"> 2 <div class="article-comment-inner">
3 3
4 - <div class="comment-content comment-logged-<%= comment.author ? 'in' : 'out' %> <%= 'comment-from-owner' if ( comment.author && (@page.profile.name == comment.author.name) ) %>"> 4 + <div class="comment-content comment-logged-<%= comment.author ? 'in' : 'out' %> <%= 'comment-from-owner' if ( comment.author && (profile == comment.author) ) %>">
5 5
6 <% if comment.author %> 6 <% if comment.author %>
7 <%= link_to image_tag(profile_icon(comment.author, :minor)) + 7 <%= link_to image_tag(profile_icon(comment.author, :minor)) +
@@ -29,17 +29,12 @@ @@ -29,17 +29,12 @@
29 <% end %> 29 <% end %>
30 30
31 <% comment_balloon do %> 31 <% comment_balloon do %>
32 - <% if logged_in? && (user == @page.profile || user == comment.author || user.has_permission?(:moderate_comments, @page.profile)) %>  
33 - <% button_bar(:style => 'float: right; margin-top: 0px;') do %>  
34 - <%= icon_button(:delete, _('Remove this comment and all its replies'), { :profile => params[:profile], :remove_comment => comment.id, :view => params[:view] }, :method => :post, :confirm => _('Are you sure you want to remove this comment and all its replies?')) %>  
35 - <% end %>  
36 - <% end %>  
37 32
38 <div class="comment-details"> 33 <div class="comment-details">
39 <div class="comment-created-at"> 34 <div class="comment-created-at">
40 <%= show_time(comment.created_at) %> 35 <%= show_time(comment.created_at) %>
41 </div> 36 </div>
42 - <h4><%= comment.title %></h4> 37 + <h4><%= comment.title.blank? && '&nbsp;' || comment.title %></h4>
43 <div class="comment-text"> 38 <div class="comment-text">
44 <p/> 39 <p/>
45 <%= txt2html comment.body %> 40 <%= txt2html comment.body %>
@@ -57,18 +52,37 @@ @@ -57,18 +52,37 @@
57 </script> 52 </script>
58 <% end %> 53 <% end %>
59 <%= report_abuse(comment.author, :comment_link, comment) if comment.author %> 54 <%= report_abuse(comment.author, :comment_link, comment) if comment.author %>
60 - <%= link_to_function _('Reply'), 55 +
  56 + <% if comment.spam? %>
  57 + &nbsp;
  58 + <%= link_to_function(_('Mark as NOT SPAM'), 'remove_comment(this, %s); return false;' % url_for(:mark_comment_as_ham => comment.id).to_json, :class => 'comment-footer comment-footer-link comment-footer-hide') %>
  59 + <% else %>
  60 + <% if (logged_in? && (user == profile || user.has_permission?(:moderate_comments, profile))) %>
  61 + &nbsp;
  62 + <%= link_to_function(_('Mark as SPAM'), 'remove_comment(this, %s, %s); return false;' % [url_for(:mark_comment_as_spam => comment.id).to_json, _('Are you sure you want to mark this comment as SPAM?').to_json], :class => 'comment-footer comment-footer-link comment-footer-hide') %>
  63 + <% end %>
  64 + <% end %>
  65 +
  66 + <% if logged_in? && (user == profile || user == comment.author || user.has_permission?(:moderate_comments, profile)) %>
  67 + &nbsp;
  68 + <%= link_to_function(_('Remove'), 'remove_comment(this, %s, %s); return false ;' % [url_for(:profile => params[:profile], :remove_comment => comment.id, :view => params[:view]).to_json, _('Are you sure you want to remove this comment and all its replies?').to_json], :class => 'comment-footer comment-footer-link comment-footer-hide remove-children') %>
  69 + <% end %>
  70 +
  71 + <% unless comment.spam? %>
  72 + &nbsp;
  73 + <%= link_to_function _('Reply'),
61 "var f = add_comment_reply_form(this, %s); f.find('input[name=comment[title]], textarea').val(''); return false" % comment.id, 74 "var f = add_comment_reply_form(this, %s); f.find('input[name=comment[title]], textarea').val(''); return false" % comment.id,
62 :class => 'comment-footer comment-footer-link comment-footer-hide', 75 :class => 'comment-footer comment-footer-link comment-footer-hide',
63 :id => 'comment-reply-to-' + comment.id.to_s 76 :id => 'comment-reply-to-' + comment.id.to_s
64 - %> 77 + %>
  78 + <% end %>
65 </div> 79 </div>
66 80
67 <% end %> 81 <% end %>
68 82
69 </div> 83 </div>
70 84
71 - <% unless comment.replies.blank? %> 85 + <% unless comment.replies.blank? || comment.spam? %>
72 <ul class="comment-replies"> 86 <ul class="comment-replies">
73 <% comment.replies.each do |reply| %> 87 <% comment.replies.each do |reply| %>
74 <%= render :partial => 'comment', :locals => { :comment => reply } %> 88 <%= render :partial => 'comment', :locals => { :comment => reply } %>
app/views/content_viewer/view_page.rhtml
@@ -99,12 +99,6 @@ @@ -99,12 +99,6 @@
99 99
100 <% if @page.accept_comments? %> 100 <% if @page.accept_comments? %>
101 <div id="page-comment-form"><%= render :partial => 'comment_form' %></div> 101 <div id="page-comment-form"><%= render :partial => 'comment_form' %></div>
102 - <script type="text/javascript">  
103 - jQuery( function() {  
104 - jQuery('.article-comment').live('mouseover', function() { jQuery(this).find('.icon-delete:first').show(); });  
105 - jQuery('.article-comment').live('mouseout', function() { jQuery(this).find('.icon-delete').hide(); });  
106 - });  
107 - </script>  
108 <% end %> 102 <% end %>
109 </div><!-- end class="comments" --> 103 </div><!-- end class="comments" -->
110 104
app/views/plugins/index.rhtml
1 <h1><%= _('Manage plugins') %></h1> 1 <h1><%= _('Manage plugins') %></h1>
2 -<%= _('Here you can enable or disable any plugin of your environment.')%> 2 +
  3 +<p>
  4 +<%= _('Select which plugins you want to enable in your environment') %>
  5 +</p>
3 6
4 <% labelled_form_for(:environment, @environment, :url => {:action => 'update'}) do |f| %> 7 <% labelled_form_for(:environment, @environment, :url => {:action => 'update'}) do |f| %>
5 8
6 -<table>  
7 - <tr>  
8 - <th><%= _('Plugin') %></th>  
9 - <th><%= _('Description') %></th>  
10 - <th><%= _('Enabled?') %></th>  
11 - </tr>  
12 - <%= hidden_field_tag('environment[enabled_plugins][]', '') %>  
13 - <% @active_plugins.each do |plugin| %>  
14 - <tr>  
15 - <td><%= plugin.has_admin_url? ? link_to(plugin.plugin_name, plugin.admin_url) : plugin.plugin_name %></td>  
16 - <td><%= plugin.plugin_description %></td>  
17 - <td><%= check_box_tag "environment[enabled_plugins][]", plugin, @environment.enabled_plugins.include?(plugin.to_s), :id => plugin.plugin_name %></td>  
18 - </tr>  
19 - <% end %>  
20 -</table> 9 + <table>
  10 + <% @active_plugins.sort_by(&:plugin_name).each do |plugin| %>
  11 + <tr>
  12 + <td style='vertical-align: top'><%= check_box_tag "environment[enabled_plugins][]", plugin, @environment.enabled_plugins.include?(plugin.to_s), :id => plugin.plugin_name %></td>
  13 + <td>
  14 + <%= hidden_field_tag('environment[enabled_plugins][]', '') %>
  15 + <strong><%= plugin.plugin_name %></strong>
  16 + <br/>
  17 + <%= plugin.plugin_description %>
  18 + <% if plugin.has_admin_url? %>
  19 + <br/>
  20 + <br/>
  21 + <%= link_to(_('Configuration'), plugin.admin_url) %>
  22 + <% end %>
  23 + </td>
  24 + </tr>
  25 + <% end %>
  26 + </table>
21 27
22 <div> 28 <div>
23 <% button_bar do %> 29 <% button_bar do %>
app/views/profile_editor/index.rhtml
@@ -66,6 +66,8 @@ @@ -66,6 +66,8 @@
66 66
67 <%= control_panel_button(_('Manage my groups'), 'groups', :controller => 'memberships') if profile.person? %> 67 <%= control_panel_button(_('Manage my groups'), 'groups', :controller => 'memberships') if profile.person? %>
68 68
  69 + <%= control_panel_button(_('Manage SPAM'), 'manage-spam', :controller => 'spam', :action => 'index') %>
  70 +
69 <% @plugins.dispatch(:control_panel_buttons).each do |button| %> 71 <% @plugins.dispatch(:control_panel_buttons).each do |button| %>
70 <%= control_panel_button(button[:title], button[:icon], button[:url]) %> 72 <%= control_panel_button(button[:title], button[:icon], button[:url]) %>
71 <% end %> 73 <% end %>
app/views/spam/index.rhtml 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +<h1><%= _('Manage SPAM') %></h1>
  2 +
  3 +<% button_bar do %>
  4 + <%= button :back, _('Back to control panel'), :controller => :profile_editor %>
  5 +<% end %>
  6 +
  7 +<%# 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 => 'content_viewer/comment', :collection => @spam %>
  12 + </ul>
  13 + </div>
  14 +</div>
  15 +
  16 +<%= pagination_links @spam %>
  17 +
  18 +<% button_bar do %>
  19 + <%= button :back, _('Back to control panel'), :controller => :profile_editor %>
  20 +<% end %>
config/initializers/plugins.rb
1 require 'noosfero/plugin' 1 require 'noosfero/plugin'
2 -require 'noosfero/plugin/acts_as_having_hotspots' 2 +require 'noosfero/plugin/hot_spot'
3 require 'noosfero/plugin/manager' 3 require 'noosfero/plugin/manager'
4 -require 'noosfero/plugin/context'  
5 require 'noosfero/plugin/active_record' 4 require 'noosfero/plugin/active_record'
6 require 'noosfero/plugin/mailer_base' 5 require 'noosfero/plugin/mailer_base'
7 Noosfero::Plugin.init_system if $NOOSFERO_LOAD_PLUGINS 6 Noosfero::Plugin.init_system if $NOOSFERO_LOAD_PLUGINS
db/migrate/20120825185219_add_user_agent_and_referrer_to_comments.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +class AddUserAgentAndReferrerToComments < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :comments, :user_agent, :string
  4 + add_column :comments, :referrer, :string
  5 + end
  6 +
  7 + def self.down
  8 + remove_column :comments, :user_agent
  9 + remove_column :comments, :referrer
  10 + end
  11 +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 => 20120818030329) do 12 +ActiveRecord::Schema.define(:version => 20120825185219) 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"
@@ -213,6 +213,8 @@ ActiveRecord::Schema.define(:version =&gt; 20120818030329) do @@ -213,6 +213,8 @@ ActiveRecord::Schema.define(:version =&gt; 20120818030329) do
213 t.string "ip_address" 213 t.string "ip_address"
214 t.boolean "spam" 214 t.boolean "spam"
215 t.string "source_type" 215 t.string "source_type"
  216 + t.string "user_agent"
  217 + t.string "referrer"
216 end 218 end
217 219
218 create_table "contact_lists", :force => true do |t| 220 create_table "contact_lists", :force => true do |t|
debian/changelog
  1 +noosfero (0.39.0~1) UNRELEASED; urgency=low
  2 +
  3 + * Pre-release to test the antispam mechanism.
  4 +
  5 + -- Antonio Terceiro <terceiro@debian.org> Thu, 30 Aug 2012 14:55:10 -0300
  6 +
1 noosfero (0.38.2) unstable; urgency=low 7 noosfero (0.38.2) unstable; urgency=low
2 8
3 * Bugfixes release 9 * Bugfixes release
features/edit_environment_templates.feature
@@ -8,7 +8,7 @@ Feature: edit environment templates @@ -8,7 +8,7 @@ Feature: edit environment templates
8 Scenario: See links to edit all templates 8 Scenario: See links to edit all templates
9 Given I am logged in as admin 9 Given I am logged in as admin
10 When I follow "Administration" 10 When I follow "Administration"
11 - And I follow "Edit Templates" 11 + And I follow "Profile templates"
12 Then I should see "Person template" link 12 Then I should see "Person template" link
13 And I should see "Community template" link 13 And I should see "Community template" link
14 And I should see "Enterprise template" link 14 And I should see "Enterprise template" link
@@ -17,28 +17,28 @@ Feature: edit environment templates @@ -17,28 +17,28 @@ Feature: edit environment templates
17 Scenario: Go to control panel of person template 17 Scenario: Go to control panel of person template
18 Given I am logged in as admin 18 Given I am logged in as admin
19 When I follow "Administration" 19 When I follow "Administration"
20 - And I follow "Edit Templates" 20 + And I follow "Profile templates"
21 And I follow "Person template" 21 And I follow "Person template"
22 Then I should be on Person template's control panel 22 Then I should be on Person template's control panel
23 23
24 Scenario: Go to control panel of enterprise template 24 Scenario: Go to control panel of enterprise template
25 Given I am logged in as admin 25 Given I am logged in as admin
26 When I follow "Administration" 26 When I follow "Administration"
27 - And I follow "Edit Templates" 27 + And I follow "Profile templates"
28 And I follow "Enterprise template" 28 And I follow "Enterprise template"
29 Then I should be on Enterprise template's control panel 29 Then I should be on Enterprise template's control panel
30 30
31 Scenario: Go to control panel of inactive enterprise template 31 Scenario: Go to control panel of inactive enterprise template
32 Given I am logged in as admin 32 Given I am logged in as admin
33 When I follow "Administration" 33 When I follow "Administration"
34 - And I follow "Edit Templates" 34 + And I follow "Profile templates"
35 And I follow "Inactive enterprise template" 35 And I follow "Inactive enterprise template"
36 Then I should be on Inactive Enterprise template's control panel 36 Then I should be on Inactive Enterprise template's control panel
37 37
38 Scenario: Go to control panel of community template 38 Scenario: Go to control panel of community template
39 Given I am logged in as admin 39 Given I am logged in as admin
40 When I follow "Administration" 40 When I follow "Administration"
41 - And I follow "Edit Templates" 41 + And I follow "Profile templates"
42 And I follow "Community template" 42 And I follow "Community template"
43 Then I should be on Community template's control panel 43 Then I should be on Community template's control panel
44 44
@@ -46,7 +46,7 @@ Feature: edit environment templates @@ -46,7 +46,7 @@ Feature: edit environment templates
46 Given that the default environment have no Inactive Enterprise template 46 Given that the default environment have no Inactive Enterprise template
47 And I am logged in as admin 47 And I am logged in as admin
48 When I follow "Administration" 48 When I follow "Administration"
49 - And I follow "Edit Templates" 49 + And I follow "Profile templates"
50 Then I should see "Person template" link 50 Then I should see "Person template" link
51 And I should see "Community template" link 51 And I should see "Community template" link
52 And I should see "Enterprise template" link 52 And I should see "Enterprise template" link
features/environment_name.feature
@@ -6,7 +6,7 @@ Feature: setting environment name @@ -6,7 +6,7 @@ Feature: setting environment name
6 Scenario: setting environment name through administration panel 6 Scenario: setting environment name through administration panel
7 Given I am logged in as admin 7 Given I am logged in as admin
8 When I follow "Administration" 8 When I follow "Administration"
9 - And I follow "Edit environment settings" 9 + And I follow "Environment settings"
10 And I fill in "Site name" with "My environment" 10 And I fill in "Site name" with "My environment"
11 And I press "Save" 11 And I press "Save"
12 Then I should see "My environment" within "title" 12 Then I should see "My environment" within "title"
features/export_users.feature
@@ -10,14 +10,14 @@ Feature: export users @@ -10,14 +10,14 @@ Feature: export users
10 Scenario: Export users as XML 10 Scenario: Export users as XML
11 Given I am logged in as admin 11 Given I am logged in as admin
12 When I follow "Administration" 12 When I follow "Administration"
13 - And I follow "Manage users" 13 + And I follow "Users"
14 And I follow "[XML]" 14 And I follow "[XML]"
15 Then I should see "ultraje" 15 Then I should see "ultraje"
16 16
17 Scenario: Export users as CSV 17 Scenario: Export users as CSV
18 Given I am logged in as admin 18 Given I am logged in as admin
19 When I follow "Administration" 19 When I follow "Administration"
20 - And I follow "Manage users" 20 + And I follow "Users"
21 And I follow "[CSV]" 21 And I follow "[CSV]"
22 Then I should see "name;email" 22 Then I should see "name;email"
23 And I should see "ultraje" 23 And I should see "ultraje"
features/manage_categories.feature
@@ -14,7 +14,7 @@ Feature: manage categories @@ -14,7 +14,7 @@ Feature: manage categories
14 | Development | services | 14 | Development | services |
15 And I am logged in as admin 15 And I am logged in as admin
16 And I am on the environment control panel 16 And I am on the environment control panel
17 - And I follow "Manage categories" 17 + And I follow "Categories"
18 18
19 Scenario: load only top level categories 19 Scenario: load only top level categories
20 Then I should see "Products" 20 Then I should see "Products"
features/plugins.feature
@@ -49,7 +49,7 @@ Feature: plugins @@ -49,7 +49,7 @@ Feature: plugins
49 When I go to the profile 49 When I go to the profile
50 Then I should see "Test plugin tab" 50 Then I should see "Test plugin tab"
51 And I go to the environment control panel 51 And I go to the environment control panel
52 - And I follow "Enable/disable plugins" 52 + And I follow "Plugins"
53 And I uncheck "Test plugin" 53 And I uncheck "Test plugin"
54 And I press "Save changes" 54 And I press "Save changes"
55 When I go to the Control panel 55 When I go to the Control panel
features/roles.feature
@@ -5,26 +5,26 @@ Feature: manage roles @@ -5,26 +5,26 @@ Feature: manage roles
5 Scenario: create new role 5 Scenario: create new role
6 Given I am logged in as admin 6 Given I am logged in as admin
7 And I go to the environment control panel 7 And I go to the environment control panel
8 - And I follow "Manage User roles" 8 + And I follow "User roles"
9 Then I should not see "My new role" 9 Then I should not see "My new role"
10 And I follow "Create a new role" 10 And I follow "Create a new role"
11 And I fill in "Name" with "My new role" 11 And I fill in "Name" with "My new role"
12 And I check "Publish content" 12 And I check "Publish content"
13 And I press "Create role" 13 And I press "Create role"
14 And I go to the environment control panel 14 And I go to the environment control panel
15 - And I follow "Manage User roles" 15 + And I follow "User roles"
16 Then I should see "My new role" 16 Then I should see "My new role"
17 17
18 Scenario: edit a role 18 Scenario: edit a role
19 Given I am logged in as admin 19 Given I am logged in as admin
20 And I go to the environment control panel 20 And I go to the environment control panel
21 - And I follow "Manage User roles" 21 + And I follow "User roles"
22 Then I should not see "My new role" 22 Then I should not see "My new role"
23 And I follow "Profile Administrator" 23 And I follow "Profile Administrator"
24 And I follow "Edit" 24 And I follow "Edit"
25 And I fill in "Name" with "My new role" 25 And I fill in "Name" with "My new role"
26 And I press "Save changes" 26 And I press "Save changes"
27 And I go to the environment control panel 27 And I go to the environment control panel
28 - And I follow "Manage User roles" 28 + And I follow "User roles"
29 Then I should see "My new role" 29 Then I should see "My new role"
30 And I should not see "Profile Administrator" 30 And I should not see "Profile Administrator"
features/send_email_to_environment_members.feature
@@ -18,7 +18,7 @@ Feature: send emails to environment members users @@ -18,7 +18,7 @@ Feature: send emails to environment members users
18 Scenario: Send e-mail to members 18 Scenario: Send e-mail to members
19 Given I am logged in as admin 19 Given I am logged in as admin
20 When I follow "Administration" 20 When I follow "Administration"
21 - And I follow "Manage users" 21 + And I follow "Users"
22 And I follow "Send e-mail to users" 22 And I follow "Send e-mail to users"
23 And I fill in "Subject" with "Hello, user!" 23 And I fill in "Subject" with "Hello, user!"
24 And I fill in "body" with "We have some news" 24 And I fill in "body" with "We have some news"
@@ -28,7 +28,7 @@ Feature: send emails to environment members users @@ -28,7 +28,7 @@ Feature: send emails to environment members users
28 Scenario: Not send e-mail to members if subject is blank 28 Scenario: Not send e-mail to members if subject is blank
29 Given I am logged in as admin 29 Given I am logged in as admin
30 When I follow "Administration" 30 When I follow "Administration"
31 - And I follow "Manage users" 31 + And I follow "Users"
32 And I follow "Send e-mail to users" 32 And I follow "Send e-mail to users"
33 And I fill in "body" with "We have some news" 33 And I fill in "body" with "We have some news"
34 When I press "Send" 34 When I press "Send"
@@ -37,7 +37,7 @@ Feature: send emails to environment members users @@ -37,7 +37,7 @@ Feature: send emails to environment members users
37 Scenario: Not send e-mail to members if body is blank 37 Scenario: Not send e-mail to members if body is blank
38 Given I am logged in as admin 38 Given I am logged in as admin
39 When I follow "Administration" 39 When I follow "Administration"
40 - And I follow "Manage users" 40 + And I follow "Users"
41 And I follow "Send e-mail to users" 41 And I follow "Send e-mail to users"
42 And I fill in "Subject" with "Hello, user!" 42 And I fill in "Subject" with "Hello, user!"
43 When I press "Send" 43 When I press "Send"
@@ -46,7 +46,7 @@ Feature: send emails to environment members users @@ -46,7 +46,7 @@ Feature: send emails to environment members users
46 Scenario: Cancel creation of mailing 46 Scenario: Cancel creation of mailing
47 Given I am logged in as admin 47 Given I am logged in as admin
48 When I follow "Administration" 48 When I follow "Administration"
49 - And I follow "Manage users" 49 + And I follow "Users"
50 And I follow "Send e-mail to users" 50 And I follow "Send e-mail to users"
51 Then I should be on /admin/users/send_mail 51 Then I should be on /admin/users/send_mail
52 When I follow "Cancel e-mail" 52 When I follow "Cancel e-mail"
lib/needs_profile.rb
@@ -14,12 +14,12 @@ module NeedsProfile @@ -14,12 +14,12 @@ module NeedsProfile
14 profile || environment # prefers profile, but defaults to environment 14 profile || environment # prefers profile, but defaults to environment
15 end 15 end
16 16
17 - protected  
18 -  
19 def profile 17 def profile
20 @profile 18 @profile
21 end 19 end
22 20
  21 + protected
  22 +
23 def load_profile 23 def load_profile
24 @profile ||= environment.profiles.find_by_identifier(params[:profile]) 24 @profile ||= environment.profiles.find_by_identifier(params[:profile])
25 if @profile 25 if @profile
lib/noosfero.rb
@@ -2,7 +2,7 @@ require &#39;fast_gettext&#39; @@ -2,7 +2,7 @@ require &#39;fast_gettext&#39;
2 2
3 module Noosfero 3 module Noosfero
4 PROJECT = 'noosfero' 4 PROJECT = 'noosfero'
5 - VERSION = '0.38.2' 5 + VERSION = '0.39.0~1'
6 6
7 def self.pattern_for_controllers_in_directory(dir) 7 def self.pattern_for_controllers_in_directory(dir)
8 disjunction = controllers_in_directory(dir).join('|') 8 disjunction = controllers_in_directory(dir).join('|')
lib/noosfero/plugin.rb
@@ -15,14 +15,29 @@ class Noosfero::Plugin @@ -15,14 +15,29 @@ class Noosfero::Plugin
15 Dir.glob(File.join(Rails.root, 'config', 'plugins', '*')).select do |entry| 15 Dir.glob(File.join(Rails.root, 'config', 'plugins', '*')).select do |entry|
16 File.directory?(entry) 16 File.directory?(entry)
17 end.each do |dir| 17 end.each do |dir|
18 - Rails.configuration.controller_paths << File.join(dir, 'controllers')  
19 - ActiveSupport::Dependencies.load_paths << File.join(dir, 'controllers')  
20 - [ ActiveSupport::Dependencies.load_paths, $:].each do |path|  
21 - path << File.join(dir, 'models')  
22 - path << File.join(dir, 'lib') 18 + plugin_name = File.basename(dir)
  19 +
  20 + plugin_dependencies_ok = true
  21 + plugin_dependencies_file = File.join(dir, 'dependencies.rb')
  22 + if File.exists?(plugin_dependencies_file)
  23 + begin
  24 + require plugin_dependencies_file
  25 + rescue LoadError => ex
  26 + plugin_dependencies_ok = false
  27 + $stderr.puts "W: Noosfero plugin #{plugin_name} failed to load (#{ex})"
  28 + end
23 end 29 end
24 30
25 - klass(File.basename(dir)) 31 + if plugin_dependencies_ok
  32 + Rails.configuration.controller_paths << File.join(dir, 'controllers')
  33 + ActiveSupport::Dependencies.load_paths << File.join(dir, 'controllers')
  34 + [ ActiveSupport::Dependencies.load_paths, $:].each do |path|
  35 + path << File.join(dir, 'models')
  36 + path << File.join(dir, 'lib')
  37 + end
  38 +
  39 + klass(plugin_name)
  40 + end
26 end 41 end
27 end 42 end
28 43
@@ -226,16 +241,53 @@ class Noosfero::Plugin @@ -226,16 +241,53 @@ class Noosfero::Plugin
226 # example: 241 # example:
227 # 242 #
228 # def filter_comment(comment) 243 # def filter_comment(comment)
229 - # comment.reject! if anti_spam_service.is_spam?(comment) 244 + # if user_not_logged_in
  245 + # comment.reject!
  246 + # end
230 # end 247 # end
231 # 248 #
232 def filter_comment(comment) 249 def filter_comment(comment)
233 end 250 end
234 251
235 - # This method will be called just after a comment has been saved to the  
236 - # database, so that a plugin can perform some action on it. 252 + # This method is called by the CommentHandler background job before sending
  253 + # the notification email. If the comment is marked as spam (i.e. by calling
  254 + # <tt>comment.spam!</tt>), then the notification email will *not* be sent.
  255 + #
  256 + # example:
  257 + #
  258 + # def check_comment_for_spam(comment)
  259 + # if anti_spam_service.is_spam?(comment)
  260 + # comment.spam!
  261 + # end
  262 + # end
  263 + #
  264 + def check_comment_for_spam(comment)
  265 + end
  266 +
  267 + # This method is called when the user manually marks a comment as SPAM. A
  268 + # plugin implementing this method should train its spam detection mechanism
  269 + # by submitting this comment as a confirmed spam.
  270 + #
  271 + # example:
  272 + #
  273 + # def comment_marked_as_spam(comment)
  274 + # anti_spam_service.train_with_spam(comment)
  275 + # end
  276 + #
  277 + def comment_marked_as_spam(comment)
  278 + end
  279 +
  280 + # This method is called when the user manually marks a comment a NOT SPAM. A
  281 + # plugin implementing this method should train its spam detection mechanism
  282 + # by submitting this coimment as a confirmed ham.
  283 + #
  284 + # example:
  285 + #
  286 + # def comment_marked_as_ham(comment)
  287 + # anti_spam_service.train_with_ham(comment)
  288 + # end
237 # 289 #
238 - def comment_saved(comment) 290 + def comment_marked_as_ham(comment)
239 end 291 end
240 292
241 # -> Adds fields to the signup form 293 # -> Adds fields to the signup form
lib/noosfero/plugin/acts_as_having_hotspots.rb
@@ -1,44 +0,0 @@ @@ -1,44 +0,0 @@
1 -module ActsAsHavingHotspots  
2 - module ClassMethods  
3 - # Adding this feature to a class demands that it defines an instance method  
4 - # 'environment' that returns the environment associated with the instance.  
5 - def acts_as_having_hotspots  
6 - send :include, InstanceMethods  
7 - end  
8 -  
9 - module InstanceMethods  
10 - # Dispatches +event+ to each enabled plugin and collect the results.  
11 - #  
12 - # Returns an Array containing the objects returned by the event method in  
13 - # each plugin. This array is compacted (i.e. nils are removed) and flattened  
14 - # (i.e. elements of arrays are added to the resulting array). For example, if  
15 - # the enabled plugins return 1, 0, nil, and [1,2,3], then this method will  
16 - # return [1,0,1,2,3]  
17 - #  
18 - def dispatch(event, *args)  
19 - enabled_plugins.map { |plugin| plugin.send(event, *args) }.compact.flatten  
20 - end  
21 -  
22 - # Dispatch without flatten since scopes are executed if you run flatten on them  
23 - def dispatch_scopes(event, *args)  
24 - enabled_plugins.map { |plugin| plugin.send(event, *args) }.compact  
25 - end  
26 -  
27 - def enabled_plugins  
28 - Thread.current[:enabled_plugins] ||= (Noosfero::Plugin.all & environment.enabled_plugins).map do |plugin_name|  
29 - plugin = plugin_name.constantize.new  
30 - plugin.context = context  
31 - plugin  
32 - end  
33 - end  
34 -  
35 - if !method_defined?(:context)  
36 - define_method(:context) do  
37 - Noosfero::Plugin::Context.new  
38 - end  
39 - end  
40 - end  
41 - end  
42 -end  
43 -  
44 -ActiveRecord::Base.send(:extend, ActsAsHavingHotspots::ClassMethods)  
lib/noosfero/plugin/context.rb
@@ -1,15 +0,0 @@ @@ -1,15 +0,0 @@
1 -# This class defines the interface to important context information from the  
2 -# controller that can be accessed by plugins  
3 -class Noosfero::Plugin::Context  
4 -  
5 - def initialize(controller = ApplicationController.new)  
6 - @controller = controller  
7 - end  
8 -  
9 - delegate :profile, :request, :response, :environment, :params, :session, :user, :logged_in?, :to => :controller  
10 -  
11 - protected  
12 -  
13 - attr_reader :controller  
14 -  
15 -end  
lib/noosfero/plugin/hot_spot.rb 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +# This module must be included by classes that contain Noosfero plugin
  2 +# hotspots.
  3 +#
  4 +# Classes that include this module *must* provide a method called
  5 +# <tt>environment</tt> which returns an intance of Environment. This
  6 +# Environment will be used to determine which plugins are enabled and therefore
  7 +# which plugins should be instantiated.
  8 +module Noosfero::Plugin::HotSpot
  9 +
  10 + # Returns an instance of Noosfero::Plugin::Manager.
  11 + #
  12 + # This which is intantiated on the first call and just returned in subsequent
  13 + # calls.
  14 + def plugins
  15 + @plugins ||= Noosfero::Plugin::Manager.new(environment, self)
  16 + end
  17 +
  18 +end
lib/noosfero/plugin/manager.rb
1 class Noosfero::Plugin::Manager 1 class Noosfero::Plugin::Manager
2 2
3 - extend ActsAsHavingHotspots::ClassMethods  
4 - acts_as_having_hotspots  
5 - 3 + attr_reader :environment
6 attr_reader :context 4 attr_reader :context
7 5
8 - delegate :environment, :to => :context 6 + def initialize(environment, context)
  7 + @environment = environment
  8 + @context = context
  9 + end
  10 +
9 delegate :each, :to => :enabled_plugins 11 delegate :each, :to => :enabled_plugins
10 include Enumerable 12 include Enumerable
11 13
12 - def initialize(controller)  
13 - @context = Noosfero::Plugin::Context.new(controller)  
14 - Thread.current[:enabled_plugins] = (Noosfero::Plugin.all & environment.enabled_plugins).map do |plugin_name|  
15 - plugin = plugin_name.constantize.new  
16 - plugin.context = context  
17 - plugin 14 + # Dispatches +event+ to each enabled plugin and collect the results.
  15 + #
  16 + # Returns an Array containing the objects returned by the event method in
  17 + # each plugin. This array is compacted (i.e. nils are removed) and flattened
  18 + # (i.e. elements of arrays are added to the resulting array). For example, if
  19 + # the enabled plugins return 1, 0, nil, and [1,2,3], then this method will
  20 + # return [1,0,1,2,3]
  21 + #
  22 + def dispatch(event, *args)
  23 + dispatch_without_flatten(event, *args).flatten
  24 + end
  25 +
  26 + def dispatch_without_flatten(event, *args)
  27 + map { |plugin| plugin.send(event, *args) }.compact
  28 + end
  29 +
  30 + alias :dispatch_scopes :dispatch_without_flatten
  31 +
  32 + def enabled_plugins
  33 + @enabled_plugins ||= (Noosfero::Plugin.all & environment.enabled_plugins).map do |plugin|
  34 + p = plugin.constantize.new
  35 + p.context = context
  36 + p
18 end 37 end
19 end 38 end
20 39
lib/tasks/plugins_tests.rake
1 -@disabled_plugins = Dir.glob(File.join(Rails.root, 'plugins', '*')).map { |file| File.basename(file)} - Dir.glob(File.join(Rails.root, 'config', 'plugins', '*')).map { |file| File.basename(file)}  
2 -@disabled_plugins.delete('template')  
3 -  
4 -def define_task(test, plugins_folder='plugins', plugin = '*')  
5 - test_files = Dir.glob(File.join(Rails.root, plugins_folder, plugin, 'test', test[:folder], '**', '*_test.rb'))  
6 - desc 'Runs ' + (plugin != '*' ? plugin : 'plugins') + ' ' + test[:name] + ' tests'  
7 - Rake::TestTask.new(test[:name].to_sym => 'db:test:plugins:prepare') do |t|  
8 - t.libs << 'test'  
9 - t.test_files = test_files  
10 - t.verbose = true  
11 - end 1 +all_plugins = Dir.glob('plugins/*').map { |f| File.basename(f) } - ['template']
  2 +def enabled_plugins
  3 + Dir.glob('config/plugins/*').map { |f| File.basename(f) } - ['README']
12 end 4 end
  5 +disabled_plugins = all_plugins - enabled_plugins
13 6
14 task 'db:test:plugins:prepare' do 7 task 'db:test:plugins:prepare' do
15 - Rake::Task['db:test:prepare'].invoke  
16 - sh 'rake db:migrate RAILS_ENV=test SCHEMA=/dev/null' 8 + if Dir.glob('config/plugins/*/db/migrate/*.rb').empty?
  9 + puts "I: skipping database setup, enabled plugins have no migrations"
  10 + else
  11 + Rake::Task['db:test:prepare'].invoke
  12 + sh 'rake db:migrate RAILS_ENV=test SCHEMA=/dev/null'
  13 + end
17 end 14 end
18 15
19 -namespace :test do  
20 - namespace :noosfero_plugins do  
21 - tasks = [  
22 - {:name => :available, :folder => 'plugins'},  
23 - {:name => :enabled, :folder => File.join('config', 'plugins')}  
24 - ]  
25 - tests = [  
26 - {:name => 'units', :folder => 'unit'},  
27 - {:name => 'functionals', :folder => 'functional'},  
28 - {:name => 'integration', :folder => 'integration'}  
29 - ]  
30 -  
31 - tasks.each do |t|  
32 - namespace t[:name] do  
33 - tests.each do |test|  
34 - define_task(test, t[:folder])  
35 - end  
36 - end  
37 - end 16 +def plugin_name(plugin)
  17 + "#{plugin} plugin"
  18 +end
  19 +
  20 +def run_tests(name, files_glob)
  21 + files = Dir.glob(files_glob)
  22 + if files.empty?
  23 + puts "I: no tests to run (#{name})"
  24 + else
  25 + sh 'testrb', '-Itest', *files
  26 + end
  27 +end
38 28
39 - plugins = Dir.glob(File.join(Rails.root, 'plugins', '*')).map {|path| File.basename(path)} 29 +def run_cucumber(name, profile, files_glob)
  30 + files = Dir.glob(files_glob)
  31 + if files.empty?
  32 + puts "I: no tests to run #{name}"
  33 + else
  34 + sh 'xvfb-run', 'ruby', '-S', 'cucumber', '--profile', profile, '--format', ENV['CUCUMBER_FORMAT'] || 'progress' , *features
  35 + end
  36 +end
40 37
41 - plugins.each do |plugin_name|  
42 - namespace plugin_name do  
43 - tests.each do |test|  
44 - define_task(test, 'plugins', plugin_name)  
45 - end  
46 - end 38 +def plugin_test_task(name, plugin, files_glob)
  39 + desc "Run #{name} tests for #{plugin_name(plugin)}"
  40 + task name => 'db:test:plugins:prepare' do |t|
  41 + run_tests t.name, files_glob
  42 + end
  43 +end
  44 +
  45 +def plugin_cucumber_task(plugin, files_glob)
  46 + task :cucumber => 'db:test:plugins:prepare' do |t|
  47 + run_cucumber t.name, :default, files_glob
  48 + end
  49 +end
47 50
48 - dependencies = []  
49 - tests.each do |test|  
50 - dependencies << plugin_name+':'+test[:name] 51 +def plugin_selenium_task(plugin, files_glob)
  52 + task :selenium => 'db:test:plugins:prepare' do |t|
  53 + run_cucumber t.name, :selenium, files_glob
  54 + end
  55 +end
  56 +
  57 +def test_sequence_task(name, plugin, *tasks)
  58 + desc "Run all tests for #{plugin_name(plugin)}"
  59 + task name do
  60 + failed = []
  61 + tasks.each do |task|
  62 + begin
  63 + Rake::Task['test:noosfero_plugins:' + task.to_s].invoke
  64 + rescue Exception => ex
  65 + puts ex
  66 + failed << task
51 end 67 end
52 - task plugin_name => dependencies  
53 end 68 end
54 -  
55 - task :temp_enable_plugins do  
56 - system('./script/noosfero-plugins enableall') 69 + unless failed.empty?
  70 + fail 'Tests failed: ' + failed.join(', ')
57 end 71 end
  72 + end
  73 +end
58 74
59 - task :rollback_temp_enable_plugins do  
60 - @disabled_plugins.each { |plugin| system('./script/noosfero-plugins disable ' + plugin)} 75 +namespace :test do
  76 + namespace :noosfero_plugins do
  77 + all_plugins.each do |plugin|
  78 + namespace plugin do
  79 + plugin_test_task :units, plugin, "plugins/#{plugin}/test/unit/**/*.rb"
  80 + plugin_test_task :functionals, plugin, "plugins/#{plugin}/test/functional/**/*.rb"
  81 + plugin_test_task :integration, plugin, "plugins/#{plugin}/test/integration/**/*.rb"
  82 + plugin_cucumber_task plugin, "plugins/#{plugin}/features/**/*.feature"
  83 + plugin_selenium_task plugin, "plugins/#{plugin}/features/**/*.feature"
  84 + end
  85 +
  86 + test_sequence_task(plugin, plugin, "#{plugin}:units", "#{plugin}:functionals", "#{plugin}:integration", "#{plugin}:cucumber", "#{plugin}:selenium") # FIXME missing cucumber and selenium
61 end 87 end
62 88
63 - task :units => 'available:units'  
64 - task :functionals => 'available:functionals'  
65 - task :integration => 'available:integration'  
66 - task :available do  
67 - Rake::Task['test:noosfero_plugins:temp_enable_plugins'].invoke  
68 - begin  
69 - Rake::Task['test:noosfero_plugins:units'].invoke  
70 - Rake::Task['test:noosfero_plugins:functionals'].invoke  
71 - Rake::Task['test:noosfero_plugins:integration'].invoke  
72 - rescue 89 + { :units => :unit , :functionals => :functional , :integration => :integration }.each do |taskname,folder|
  90 + task taskname => 'db:test:plugins:prepare' do |t|
  91 + run_tests t.name, "plugins/{#{enabled_plugins.join(',')}}/test/#{folder}/**/*.rb"
73 end 92 end
74 - Rake::Task['test:noosfero_plugins:rollback_temp_enable_plugins'].invoke  
75 end 93 end
76 - task :enabled => ['enabled:units', 'enabled:functionals', 'enabled:integration']  
77 94
  95 + task :cucumber => 'db:test:plugins:prepare' do |t|
  96 + run_cucumber t.name, :default, "plugins/{#{enabled_plugins.join(',')}}/features/**/*.features"
  97 + end
78 98
79 - namespace :cucumber do  
80 - task :enabled do  
81 - features = Dir.glob('config/plugins/*/features/*.feature')  
82 - if features.empty?  
83 - puts "No acceptance tests for enabled plugins, skipping"  
84 - else  
85 - ruby '-S', 'cucumber', '--format', ENV['CUCUMBER_FORMAT'] || 'progress' , *features  
86 - end  
87 - end 99 + task :selenium => 'db:test:plugins:prepare' do |t|
  100 + run_cucumber t.name, :selenium, "plugins/{#{enabled_plugins.join(',')}}/features/**/*.features"
88 end 101 end
89 102
90 - namespace :selenium do  
91 - task :enabled do  
92 - features = Dir.glob('config/plugins/*/features/*.feature')  
93 - if features.empty?  
94 - puts "No acceptance tests for enabled plugins, skipping"  
95 - else  
96 - sh 'xvfb-run', 'ruby', '-S', 'cucumber', '--profile', 'selenium', '--format', ENV['CUCUMBER_FORMAT'] || 'progress' , *features  
97 - end  
98 - end 103 + task :temp_enable_all_plugins do
  104 + sh './script/noosfero-plugins', 'enableall'
99 end 105 end
100 106
  107 + task :rollback_enable_all_plugins do
  108 + sh './script/noosfero-plugins', 'disable', *disabled_plugins
  109 + end
101 end 110 end
102 111
103 - task :noosfero_plugins => %w[ noosfero_plugins:available noosfero_plugins:cucumber:enabled noosfero_plugins:selenium:enabled ] 112 + test_sequence_task(:noosfero_plugins, '*', :temp_enable_all_plugins, :units, :functionals, :integration, :cucumber, :selenium, :rollback_enable_all_plugins)
104 113
105 end 114 end
106 -  
lib/tasks/release.rake
@@ -95,6 +95,9 @@ EOF @@ -95,6 +95,9 @@ EOF
95 sh "cd #{target} && dpkg-buildpackage -us -uc -b" 95 sh "cd #{target} && dpkg-buildpackage -us -uc -b"
96 end 96 end
97 97
  98 + desc "Build Debian packages (shorcut)"
  99 + task :deb => :debian_packages
  100 +
98 desc 'Test Debian package' 101 desc 'Test Debian package'
99 task 'debian:test' => :debian_packages do 102 task 'debian:test' => :debian_packages do
100 Dir.chdir 'pkg' do 103 Dir.chdir 'pkg' do
plugins/anti_spam/controllers/anti_spam_plugin_admin_controller.rb 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +class AntiSpamPluginAdminController < AdminController
  2 + append_view_path File.join(File.dirname(__FILE__) + '/../views')
  3 +
  4 + def index
  5 + @settings = AntiSpamPlugin::Settings.new(environment, params[:settings])
  6 + if request.post?
  7 + @settings.save!
  8 + redirect_to :action => 'index'
  9 + end
  10 + end
  11 +
  12 +end
plugins/anti_spam/dependencies.rb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +require 'rakismet'
plugins/anti_spam/lib/anti_spam_plugin.rb 0 → 100644
@@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
  1 +class AntiSpamPlugin < Noosfero::Plugin
  2 +
  3 + def self.plugin_name
  4 + "AntiSpam"
  5 + end
  6 +
  7 + def self.plugin_description
  8 + _("Checks comments against a spam checking service compatible with the Akismet API")
  9 + end
  10 +
  11 + def check_comment_for_spam(comment)
  12 + if rakismet_call(comment, :spam?)
  13 + comment.spam = true
  14 + comment.save!
  15 + end
  16 + end
  17 +
  18 + def comment_marked_as_spam(comment)
  19 + rakismet_call(comment, :spam!)
  20 + end
  21 +
  22 + def comment_marked_as_ham(comment)
  23 + rakismet_call(comment, :ham!)
  24 + end
  25 +
  26 + protected
  27 +
  28 + def rakismet_call(comment, op)
  29 + settings = AntiSpamPlugin::Settings.new(comment.environment)
  30 +
  31 + Rakismet.host = settings.host
  32 + Rakismet.key = settings.api_key
  33 + Rakismet.url = comment.environment.top_url
  34 +
  35 + submission = AntiSpamPlugin::CommentWrapper.new(comment)
  36 + submission.send(op)
  37 + end
  38 +
  39 +end
plugins/anti_spam/lib/anti_spam_plugin/comment_wrapper.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +class AntiSpamPlugin::CommentWrapper < Struct.new(:comment)
  2 +
  3 + delegate :author_name, :author_email, :title, :body, :ip_address, :user_agent, :referrer, :to => :comment
  4 +
  5 + include Rakismet::Model
  6 +
  7 + alias :author :author_name
  8 + alias :user_ip :ip_address
  9 + alias :content :body
  10 +
  11 +end
plugins/anti_spam/lib/anti_spam_plugin/settings.rb 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +class AntiSpamPlugin::Settings
  2 +
  3 + def initialize(environment, attributes = nil)
  4 + @environment = environment
  5 + attributes ||= {}
  6 + attributes.each do |k,v|
  7 + self.send("#{k}=", v)
  8 + end
  9 + end
  10 +
  11 + def settings
  12 + @environment.settings[:anti_spam_plugin] ||= {}
  13 + end
  14 +
  15 + def host
  16 + settings[:host] ||= 'api.antispam.typepad.com'
  17 + end
  18 +
  19 + def host=(value)
  20 + settings[:host] = value
  21 + end
  22 +
  23 + def api_key
  24 + settings[:api_key]
  25 + end
  26 +
  27 + def api_key=(value)
  28 + settings[:api_key] = value
  29 + end
  30 +
  31 + def save!
  32 + @environment.save!
  33 + end
  34 +
  35 +end
plugins/anti_spam/lib/anti_spam_plugin/spaminator.rb 0 → 100644
@@ -0,0 +1,115 @@ @@ -0,0 +1,115 @@
  1 +require 'benchmark'
  2 +
  3 +class AntiSpamPlugin::Spaminator
  4 +
  5 + class << self
  6 + def run(environment)
  7 + instance = new(environment)
  8 + instance.run
  9 + end
  10 +
  11 + def benchmark(environment)
  12 + puts Benchmark.measure { run(environment) }
  13 + end
  14 + end
  15 +
  16 +
  17 + def initialize(environment)
  18 + @environment = environment
  19 + end
  20 +
  21 + def run
  22 + start_time = Time.now
  23 +
  24 + process_all_comments
  25 + process_all_people
  26 + process_people_without_network
  27 +
  28 + finish(start_time)
  29 + end
  30 +
  31 + protected
  32 +
  33 + def finish(start_time)
  34 + @environment.settings[:spaminator_last_run] = start_time
  35 + @environment.save!
  36 + end
  37 +
  38 + def conditions(table)
  39 + last_run = @environment.settings[:spaminator_last_run]
  40 + if last_run
  41 + ["profiles.environment_id = ? AND #{table}.created_at > ?", @environment.id, last_run]
  42 + else
  43 + [ "profiles.environment_id = ?", @environment.id]
  44 + end
  45 + end
  46 +
  47 + def process_all_comments
  48 + puts 'Processing comments ...'
  49 + i = 0
  50 + comments = Comment.joins("JOIN articles ON (comments.source_id = articles.id AND comments.source_type = 'Article') JOIN profiles ON (profiles.id = articles.profile_id)").where(conditions(:comments))
  51 + total = comments.count
  52 + comments.find_each do |comment|
  53 + puts "Comment #{i += 1}/#{total} (#{100*i/total}%)"
  54 + process_comment(comment)
  55 + end
  56 + end
  57 +
  58 + def process_all_people
  59 + puts 'Processing people ...'
  60 + i = 0
  61 + people = Person.where(conditions(:profiles))
  62 + total = people.count
  63 + people.find_each do |person|
  64 + puts "Person #{i += 1}/#{total} (#{100*i/total}%)"
  65 + process_person(person)
  66 + end
  67 + end
  68 +
  69 + def process_comment(comment)
  70 + comment.check_for_spam
  71 +
  72 + # TODO several comments with the same content:
  73 + # → disable author
  74 + # → mark all of them as spam
  75 +
  76 + # TODO check comments that contains URL's
  77 + end
  78 +
  79 + def process_person(person)
  80 + # person is author of more than 2 comments marked as spam
  81 + # → burn
  82 + #
  83 + number_of_spam_comments = Comment.spam.where(author_id => person.id).count
  84 + if number_of_spam_comments > 2
  85 + mark_as_spammer(person)
  86 + end
  87 + end
  88 +
  89 + def process_people_without_network
  90 + # people who signed up more than one month ago, have no friends and <= 1
  91 + # communities
  92 + #
  93 + # → burn
  94 + # → mark their comments as spam
  95 + #
  96 + Person.where(:environment_id => @environment.id).where(['created_at < ?', Time.now - 1.month]).find_each do |person|
  97 + # TODO progress indicator - see process_all_people above
  98 + number_of_friends = person.friends.count
  99 + number_of_communities = person.communities.count
  100 + if number_of_friends == 0 && number_of_communities <= 1
  101 + mark_as_spammer(person)
  102 + Comment.where(:author_id => person.id).find_each do |comment|
  103 + comment.spam!
  104 + end
  105 + end
  106 + end
  107 + end
  108 +
  109 + def mark_as_spammer(person)
  110 + # FIXME create an AbuseComplaint and finish instead of calling
  111 + # Person#disable directly
  112 + person.disable
  113 + end
  114 +
  115 +end
plugins/anti_spam/test/unit/anti_spam_plugin/comment_wrapper_test.rb 0 → 100644
@@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
  1 +require 'test_helper'
  2 +
  3 +class AntiSpamPluginCommentWrapperTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @comment = Comment.new(
  7 + :title => 'comment title',
  8 + :body => 'comment body',
  9 + :name => 'foo',
  10 + :email => 'foo@example.com',
  11 + :ip_address => '1.2.3.4',
  12 + :user_agent => 'Some Good Browser (I hope)',
  13 + :referrer => 'http://noosfero.org/'
  14 + )
  15 + @wrapper = AntiSpamPlugin::CommentWrapper.new(@comment)
  16 + end
  17 +
  18 + should 'use Rakismet::Model' do
  19 + assert_includes @wrapper.class.included_modules, Rakismet::Model
  20 + end
  21 +
  22 + should 'get contents' do
  23 + assert_equal @comment.body, @wrapper.content
  24 + end
  25 +
  26 + should 'get author name' do
  27 + assert_equal @comment.author_name, @wrapper.author
  28 + end
  29 +
  30 + should 'get author email' do
  31 + assert_equal @comment.author_email, @wrapper.author_email
  32 + end
  33 +
  34 + should 'get IP address' do
  35 + assert_equal @comment.ip_address, @wrapper.user_ip
  36 + end
  37 +
  38 + should 'get User-Agent' do
  39 + assert_equal @comment.user_agent, @wrapper.user_agent
  40 + end
  41 +
  42 + should 'get get Referrer' do
  43 + assert_equal @comment.referrer, @wrapper.referrer
  44 + end
  45 +
  46 +end
plugins/anti_spam/test/unit/anti_spam_plugin/settings_test.rb 0 → 100644
@@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
  1 +require 'test_helper'
  2 +
  3 +class AntiSpamSettingsTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @environment = Environment.new
  7 + @settings = AntiSpamPlugin::Settings.new(@environment)
  8 + end
  9 +
  10 + should 'store setttings in environment' do
  11 + @settings.host = 'foo.com'
  12 + @settings.api_key = '1234567890'
  13 + assert_equal 'foo.com', @environment.settings[:anti_spam_plugin][:host]
  14 + assert_equal '1234567890', @environment.settings[:anti_spam_plugin][:api_key]
  15 + assert_equal 'foo.com', @settings.host
  16 + assert_equal '1234567890', @settings.api_key
  17 + end
  18 +
  19 + should 'save environment on save' do
  20 + @environment.expects(:save!)
  21 + @settings.save!
  22 + end
  23 +
  24 + should 'use TypePad AntiSpam by default' do
  25 + assert_equal 'api.antispam.typepad.com', @settings.host
  26 + end
  27 +
  28 +
  29 +end
plugins/anti_spam/test/unit/anti_spam_plugin/spaminator_test.rb 0 → 100644
@@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
  1 +require 'test_helper'
  2 +
  3 +class AntiSpamPluginSpaminatorTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @environment = Environment.new
  7 + @environment.id = 99
  8 + @spaminator = AntiSpamPlugin::Spaminator.new(@environment)
  9 + @spaminator.stubs(:puts)
  10 + @now = Time.now
  11 + Time.stubs(:now).returns(@now)
  12 + end
  13 +
  14 + should 'search everything in the first run' do
  15 + assert_equal(['profiles.environment_id = ?',99], @spaminator.send(:conditions, nil))
  16 + end
  17 +
  18 + should 'search using recorded last date' do
  19 + @environment.settings[:spaminator_last_run] = @now
  20 + assert_equal(['profiles.environment_id = ? AND table.created_at > ?', 99, @now], @spaminator.send(:conditions, 'table'))
  21 + end
  22 +
  23 + should 'record time of last run in environment' do
  24 + @spaminator.expects(:process_all_comments)
  25 + @spaminator.expects(:process_all_people)
  26 + @environment.stubs(:save!)
  27 + @spaminator.run
  28 + assert_equal @now, @environment.settings[:spaminator_last_run]
  29 + end
  30 +
  31 + should 'find all comments' do
  32 + @spaminator.stubs(:process_comment)
  33 + @spaminator.send :process_all_comments
  34 + end
  35 +
  36 + should 'find all people' do
  37 + @spaminator.stubs(:process_person)
  38 + @spaminator.send :process_all_people
  39 + end
  40 +
  41 + should 'find all comments newer than a date' do
  42 + @environment.settings[:spaminator_last_run] = Time.now - 1.month
  43 + @spaminator.stubs(:process_comment)
  44 + @spaminator.send :process_all_comments
  45 + end
  46 +
  47 + should 'find all people newer than a date' do
  48 + @environment.settings[:spaminator_last_run] = Time.now - 1.month
  49 + @spaminator.stubs(:process_person)
  50 + @spaminator.send :process_all_people
  51 + end
  52 +
  53 +end
plugins/anti_spam/test/unit/anti_spam_plugin_test.rb 0 → 100644
@@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
  1 +require 'test_helper'
  2 +
  3 +class AntiSpamPluginTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + profile = fast_create(Profile)
  7 + article = fast_create(TextileArticle, :profile_id => profile.id)
  8 + @comment = fast_create(Comment, :source_id => article.id, :source_type => 'Article')
  9 +
  10 + @settings = AntiSpamPlugin::Settings.new(@comment.environment)
  11 + @settings.api_key = 'b8b80ddb8084062d0c9119c945ce3bc3'
  12 + @settings.save!
  13 +
  14 + @plugin = AntiSpamPlugin.new
  15 + @plugin.context = @comment
  16 + end
  17 +
  18 + should 'check for spam and mark comment as spam if server says it is spam' do
  19 + AntiSpamPlugin::CommentWrapper.any_instance.expects(:spam?).returns(true)
  20 + @comment.expects(:save!)
  21 +
  22 + @plugin.check_comment_for_spam(@comment)
  23 + assert @comment.spam
  24 + end
  25 +
  26 + should 'report spam' do
  27 + AntiSpamPlugin::CommentWrapper.any_instance.expects(:spam!)
  28 + @plugin.comment_marked_as_spam(@comment)
  29 + end
  30 +
  31 + should 'report ham' do
  32 + AntiSpamPlugin::CommentWrapper.any_instance.expects(:ham!)
  33 + @plugin.comment_marked_as_ham(@comment)
  34 + end
  35 +
  36 +end
plugins/anti_spam/views/anti_spam_plugin_admin/index.rhtml 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +<h1><%= _('AntiSpam settings')%></h1>
  2 +
  3 +<% form_for(:settings) do |f| %>
  4 +
  5 + <%= labelled_form_field _('Host'), f.text_field(:host) %>
  6 +
  7 + <%= labelled_form_field _('API key'), f.text_field(:api_key, :size => 40) %>
  8 +
  9 + <% button_bar do %>
  10 + <%= submit_button(:save, _('Save'), :cancel => {:controller => 'plugins', :action => 'index'}) %>
  11 + <% end %>
  12 +
  13 +<% end %>
  14 +
plugins/mezuro/dependencies.rb 0 → 100644
@@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
  1 +require 'savon'
  2 +require 'googlecharts'
plugins/mezuro/lib/mezuro_plugin.rb
1 -require 'savon'  
2 require 'yaml' 1 require 'yaml'
3 2
4 Savon.configure do |config| 3 Savon.configure do |config|
plugins/mezuro/lib/mezuro_plugin/helpers/content_viewer_helper.rb
1 -require 'googlecharts'  
2 -  
3 class MezuroPlugin::Helpers::ContentViewerHelper 1 class MezuroPlugin::Helpers::ContentViewerHelper
4 def self.format_grade(grade) 2 def self.format_grade(grade)
5 sprintf("%.2f", grade.to_f) 3 sprintf("%.2f", grade.to_f)
plugins/require_auth_to_comment/test/unit/require_auth_to_comment_plugin_test.rb
@@ -35,7 +35,7 @@ class RequireAuthToCommentPluginTest &lt; ActiveSupport::TestCase @@ -35,7 +35,7 @@ class RequireAuthToCommentPluginTest &lt; ActiveSupport::TestCase
35 controller = mock() 35 controller = mock()
36 controller.stubs(:logged_in?).returns(boolean) 36 controller.stubs(:logged_in?).returns(boolean)
37 controller.stubs(:profile).returns(Profile.new) 37 controller.stubs(:profile).returns(Profile.new)
38 - Noosfero::Plugin::Context.new(controller) 38 + controller
39 end 39 end
40 40
41 end 41 end
plugins/stoa/test/functional/profile_editor_controller.rb
@@ -1,55 +0,0 @@ @@ -1,55 +0,0 @@
1 -require File.dirname(__FILE__) + '/../../../../test/test_helper'  
2 -require File.dirname(__FILE__) + '/../../../../app/controllers/my_profile/profile_editor_controller'  
3 -  
4 -# Re-raise errors caught by the controller.  
5 -class ProfileEditorController; def rescue_action(e) raise e end; end  
6 -  
7 -class ProfileEditorTest < ActionController::TestCase  
8 -  
9 - SALT=YAML::load(File.open(StoaPlugin.root_path + '/config.yml'))['salt']  
10 -  
11 - def setup  
12 - @controller = ProfileEditorController.new  
13 - @request = ActionController::TestRequest.new  
14 - @response = ActionController::TestResponse.new  
15 - @person = User.create(:login => 'test_user', :email => 'test_user@example.com', :password => 'test', :password_confirmation => 'test').person  
16 - login_as(@person.identifier)  
17 - Environment.default.enable_plugin(StoaPlugin.name)  
18 - db = Tempfile.new('stoa-test')  
19 - ActiveRecord::Base.configurations['stoa'] = {:adapter => 'sqlite3', :database => db.path}  
20 - end  
21 -  
22 - attr_accessor :person  
23 -  
24 - should 'show usp_id field if person did not filled it' do  
25 - get :edit, :profile => person.identifier  
26 - assert_match /USP number/, @response.body  
27 - end  
28 -  
29 - should 'not show usp_id field if person already filled it' do  
30 - person.usp_id = 12345  
31 - person.save  
32 - get :edit, :profile => person.identifier  
33 - assert_no_match /USP number/, @response.body  
34 - end  
35 -  
36 - should 'not display field if profile is an organization' do  
37 - organization = fast_create(Organization)  
38 - get :edit, :profile => organization.identifier  
39 - assert_no_match /USP number/, @response.body  
40 - end  
41 -  
42 - should 'display error if usp_id does not match with supplied confirmation' do  
43 - StoaPlugin::UspUser.stubs(:matches?).returns(false)  
44 - post :edit, :profile => person.identifier, :profile_data => {:usp_id => 12345678}, :confirmation_field => 'cpf', :cpf => 99999999  
45 - assert assigns(:profile_data).errors.invalid?(:usp_id)  
46 - end  
47 -  
48 - should 'save usp_id if everyhtings is ok' do  
49 - StoaPlugin::UspUser.stubs(:matches?).returns(true)  
50 - post :edit, :profile => person.identifier, :profile_data => {:usp_id => 12345678}, :confirmation_field => 'cpf', :cpf => 99999999  
51 - person.reload  
52 - assert_equal '12345678', person.usp_id  
53 - end  
54 -  
55 -end  
plugins/stoa/test/functional/profile_editor_controller_test.rb 0 → 100644
@@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +require File.dirname(__FILE__) + '/../../../../app/controllers/my_profile/profile_editor_controller'
  3 +
  4 +# Re-raise errors caught by the controller.
  5 +class ProfileEditorController; def rescue_action(e) raise e end; end
  6 +
  7 +class StoaPluginProfileEditorControllerTest < ActionController::TestCase
  8 +
  9 + SALT=YAML::load(File.open(StoaPlugin.root_path + '/config.yml'))['salt']
  10 +
  11 + def setup
  12 + @controller = ProfileEditorController.new
  13 + @request = ActionController::TestRequest.new
  14 + @response = ActionController::TestResponse.new
  15 + @person = User.create(:login => 'test_user', :email => 'test_user@example.com', :password => 'test', :password_confirmation => 'test').person
  16 + login_as(@person.identifier)
  17 + Environment.default.enable_plugin(StoaPlugin.name)
  18 + db = Tempfile.new('stoa-test')
  19 + ActiveRecord::Base.configurations['stoa'] = {:adapter => 'sqlite3', :database => db.path}
  20 + end
  21 +
  22 + attr_accessor :person
  23 +
  24 + should 'show usp_id field if person did not filled it' do
  25 + get :edit, :profile => person.identifier
  26 + assert_match /USP number/, @response.body
  27 + end
  28 +
  29 + should 'not show usp_id field if person already filled it' do
  30 + person.usp_id = 12345
  31 + person.save
  32 + get :edit, :profile => person.identifier
  33 + assert_no_match /USP number/, @response.body
  34 + end
  35 +
  36 + should 'not display field if profile is an organization' do
  37 + organization = fast_create(Organization)
  38 + get :edit, :profile => organization.identifier
  39 + assert_no_match /USP number/, @response.body
  40 + end
  41 +
  42 + should 'display error if usp_id does not match with supplied confirmation' do
  43 + StoaPlugin::UspUser.stubs(:matches?).returns(false)
  44 + post :edit, :profile => person.identifier, :profile_data => {:usp_id => 12345678}, :confirmation_field => 'cpf', :cpf => 99999999
  45 + assert assigns(:profile_data).errors.invalid?(:usp_id)
  46 + end
  47 +
  48 + should 'save usp_id if everyhtings is ok' do
  49 + StoaPlugin::UspUser.stubs(:matches?).returns(true)
  50 + post :edit, :profile => person.identifier, :profile_data => {:usp_id => 12345678}, :confirmation_field => 'cpf', :cpf => 99999999
  51 + person.reload
  52 + assert_equal '12345678', person.usp_id
  53 + end
  54 +
  55 +end
public/images/control-panel/mail-mark-junk.png 0 → 100644

3.94 KB

public/images/control-panel/mail-mark-junk.svg 0 → 100644
@@ -0,0 +1,504 @@ @@ -0,0 +1,504 @@
  1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 +<!-- Created with Inkscape (http://www.inkscape.org/) -->
  3 +<svg
  4 + xmlns:dc="http://purl.org/dc/elements/1.1/"
  5 + xmlns:cc="http://creativecommons.org/ns#"
  6 + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  7 + xmlns:svg="http://www.w3.org/2000/svg"
  8 + xmlns="http://www.w3.org/2000/svg"
  9 + xmlns:xlink="http://www.w3.org/1999/xlink"
  10 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
  11 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
  12 + inkscape:export-ydpi="90.000000"
  13 + inkscape:export-xdpi="90.000000"
  14 + inkscape:export-filename="/home/jimmac/Desktop/wi-fi.png"
  15 + width="48px"
  16 + height="48px"
  17 + id="svg11300"
  18 + sodipodi:version="0.32"
  19 + inkscape:version="0.46"
  20 + sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/actions"
  21 + sodipodi:docname="mail-mark-junk.svg"
  22 + inkscape:output_extension="org.inkscape.output.svg.inkscape">
  23 + <defs
  24 + id="defs3">
  25 + <inkscape:perspective
  26 + sodipodi:type="inkscape:persp3d"
  27 + inkscape:vp_x="0 : 24 : 1"
  28 + inkscape:vp_y="0 : 1000 : 0"
  29 + inkscape:vp_z="48 : 24 : 1"
  30 + inkscape:persp3d-origin="24 : 16 : 1"
  31 + id="perspective72" />
  32 + <linearGradient
  33 + inkscape:collect="always"
  34 + id="linearGradient5166">
  35 + <stop
  36 + style="stop-color:white;stop-opacity:1;"
  37 + offset="0"
  38 + id="stop5168" />
  39 + <stop
  40 + style="stop-color:white;stop-opacity:0;"
  41 + offset="1"
  42 + id="stop5170" />
  43 + </linearGradient>
  44 + <linearGradient
  45 + id="linearGradient5196">
  46 + <stop
  47 + style="stop-color:#dfe2dc;stop-opacity:1;"
  48 + offset="0"
  49 + id="stop5198" />
  50 + <stop
  51 + style="stop-color:#86917a;stop-opacity:1;"
  52 + offset="1"
  53 + id="stop5200" />
  54 + </linearGradient>
  55 + <linearGradient
  56 + id="linearGradient5188">
  57 + <stop
  58 + style="stop-color:white;stop-opacity:1;"
  59 + offset="0"
  60 + id="stop5190" />
  61 + <stop
  62 + style="stop-color:#aeaea3;stop-opacity:1;"
  63 + offset="1"
  64 + id="stop5192" />
  65 + </linearGradient>
  66 + <linearGradient
  67 + inkscape:collect="always"
  68 + id="linearGradient5176">
  69 + <stop
  70 + style="stop-color:black;stop-opacity:1;"
  71 + offset="0"
  72 + id="stop5178" />
  73 + <stop
  74 + style="stop-color:black;stop-opacity:0;"
  75 + offset="1"
  76 + id="stop5180" />
  77 + </linearGradient>
  78 + <linearGradient
  79 + id="linearGradient5162">
  80 + <stop
  81 + style="stop-color:#babdb6;stop-opacity:1;"
  82 + offset="0"
  83 + id="stop5164" />
  84 + <stop
  85 + style="stop-color:white;stop-opacity:1;"
  86 + offset="1"
  87 + id="stop5166" />
  88 + </linearGradient>
  89 + <linearGradient
  90 + inkscape:collect="always"
  91 + id="linearGradient5150">
  92 + <stop
  93 + style="stop-color:black;stop-opacity:1;"
  94 + offset="0"
  95 + id="stop5152" />
  96 + <stop
  97 + style="stop-color:black;stop-opacity:0;"
  98 + offset="1"
  99 + id="stop5154" />
  100 + </linearGradient>
  101 + <linearGradient
  102 + id="linearGradient11520">
  103 + <stop
  104 + id="stop11522"
  105 + offset="0.0000000"
  106 + style="stop-color:#ffffff;stop-opacity:1.0000000;" />
  107 + <stop
  108 + id="stop11524"
  109 + offset="1.0000000"
  110 + style="stop-color:#dcdcdc;stop-opacity:1.0000000;" />
  111 + </linearGradient>
  112 + <linearGradient
  113 + id="linearGradient11508"
  114 + inkscape:collect="always">
  115 + <stop
  116 + id="stop11510"
  117 + offset="0"
  118 + style="stop-color:#000000;stop-opacity:1;" />
  119 + <stop
  120 + id="stop11512"
  121 + offset="1"
  122 + style="stop-color:#000000;stop-opacity:0;" />
  123 + </linearGradient>
  124 + <linearGradient
  125 + id="linearGradient11494"
  126 + inkscape:collect="always">
  127 + <stop
  128 + id="stop11496"
  129 + offset="0"
  130 + style="stop-color:#ef2929;stop-opacity:1;" />
  131 + <stop
  132 + id="stop11498"
  133 + offset="1"
  134 + style="stop-color:#ef2929;stop-opacity:0;" />
  135 + </linearGradient>
  136 + <linearGradient
  137 + id="linearGradient11415">
  138 + <stop
  139 + id="stop11417"
  140 + offset="0.0000000"
  141 + style="stop-color:#204a87;stop-opacity:0.0000000;" />
  142 + <stop
  143 + style="stop-color:#204a87;stop-opacity:1.0000000;"
  144 + offset="0.50000000"
  145 + id="stop11423" />
  146 + <stop
  147 + id="stop11419"
  148 + offset="1"
  149 + style="stop-color:#204a87;stop-opacity:0;" />
  150 + </linearGradient>
  151 + <linearGradient
  152 + id="linearGradient11399"
  153 + inkscape:collect="always">
  154 + <stop
  155 + id="stop11401"
  156 + offset="0"
  157 + style="stop-color:#000000;stop-opacity:1;" />
  158 + <stop
  159 + id="stop11403"
  160 + offset="1"
  161 + style="stop-color:#000000;stop-opacity:0;" />
  162 + </linearGradient>
  163 + <linearGradient
  164 + gradientTransform="translate(-60.28571,-0.285714)"
  165 + y2="34.462429"
  166 + x2="43.615788"
  167 + y1="3.7744560"
  168 + x1="15.828360"
  169 + gradientUnits="userSpaceOnUse"
  170 + id="linearGradient11425"
  171 + xlink:href="#linearGradient11415"
  172 + inkscape:collect="always" />
  173 + <linearGradient
  174 + gradientTransform="translate(-60.57143,0.000000)"
  175 + y2="39.033859"
  176 + x2="35.679932"
  177 + y1="9.3458843"
  178 + x1="9.6957054"
  179 + gradientUnits="userSpaceOnUse"
  180 + id="linearGradient11427"
  181 + xlink:href="#linearGradient11415"
  182 + inkscape:collect="always" />
  183 + <linearGradient
  184 + y2="33.462429"
  185 + x2="26.758644"
  186 + y1="19.774456"
  187 + x1="13.267134"
  188 + gradientTransform="translate(-60.85714,0.428571)"
  189 + gradientUnits="userSpaceOnUse"
  190 + id="linearGradient11439"
  191 + xlink:href="#linearGradient11415"
  192 + inkscape:collect="always" />
  193 + <radialGradient
  194 + r="8.5000000"
  195 + fy="39.142857"
  196 + fx="12.071428"
  197 + cy="39.142857"
  198 + cx="12.071428"
  199 + gradientTransform="matrix(1.000000,0.000000,0.000000,0.487395,0.000000,20.06483)"
  200 + gradientUnits="userSpaceOnUse"
  201 + id="radialGradient11441"
  202 + xlink:href="#linearGradient11399"
  203 + inkscape:collect="always" />
  204 + <radialGradient
  205 + gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
  206 + gradientUnits="userSpaceOnUse"
  207 + r="3.8335034"
  208 + fy="15.048258"
  209 + fx="27.577173"
  210 + cy="15.048258"
  211 + cx="27.577173"
  212 + id="radialGradient11500"
  213 + xlink:href="#linearGradient11494"
  214 + inkscape:collect="always" />
  215 + <radialGradient
  216 + r="3.8335034"
  217 + fy="16.049133"
  218 + fx="27.577173"
  219 + cy="16.049133"
  220 + cx="27.577173"
  221 + gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
  222 + gradientUnits="userSpaceOnUse"
  223 + id="radialGradient11504"
  224 + xlink:href="#linearGradient11494"
  225 + inkscape:collect="always" />
  226 + <radialGradient
  227 + gradientUnits="userSpaceOnUse"
  228 + gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,2.166583e-14,29.48178)"
  229 + r="6.5659914"
  230 + fy="44.565483"
  231 + fx="30.203562"
  232 + cy="44.565483"
  233 + cx="30.203562"
  234 + id="radialGradient11514"
  235 + xlink:href="#linearGradient11508"
  236 + inkscape:collect="always" />
  237 + <radialGradient
  238 + gradientTransform="matrix(1.995058,-1.651527e-32,0.000000,1.995058,-24.32488,-35.70087)"
  239 + gradientUnits="userSpaceOnUse"
  240 + r="20.530962"
  241 + fy="35.878170"
  242 + fx="24.445690"
  243 + cy="35.878170"
  244 + cx="24.445690"
  245 + id="radialGradient11526"
  246 + xlink:href="#linearGradient11520"
  247 + inkscape:collect="always" />
  248 + <radialGradient
  249 + r="6.5659914"
  250 + fy="44.565483"
  251 + fx="30.203562"
  252 + cy="44.565483"
  253 + cx="30.203562"
  254 + gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,3.185827e-15,29.48178)"
  255 + gradientUnits="userSpaceOnUse"
  256 + id="radialGradient11532"
  257 + xlink:href="#linearGradient11508"
  258 + inkscape:collect="always" />
  259 + <radialGradient
  260 + inkscape:collect="always"
  261 + xlink:href="#linearGradient11508"
  262 + id="radialGradient1348"
  263 + gradientUnits="userSpaceOnUse"
  264 + gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,-1.353344e-14,29.48178)"
  265 + cx="30.203562"
  266 + cy="44.565483"
  267 + fx="30.203562"
  268 + fy="44.565483"
  269 + r="6.5659914" />
  270 + <radialGradient
  271 + inkscape:collect="always"
  272 + xlink:href="#linearGradient11520"
  273 + id="radialGradient1350"
  274 + gradientUnits="userSpaceOnUse"
  275 + gradientTransform="matrix(1.995058,-1.651527e-32,0.000000,1.995058,-24.32488,-35.70087)"
  276 + cx="24.445690"
  277 + cy="35.878170"
  278 + fx="24.445690"
  279 + fy="35.878170"
  280 + r="20.530962" />
  281 + <radialGradient
  282 + inkscape:collect="always"
  283 + xlink:href="#linearGradient11494"
  284 + id="radialGradient1352"
  285 + gradientUnits="userSpaceOnUse"
  286 + gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
  287 + cx="27.577173"
  288 + cy="16.049133"
  289 + fx="27.577173"
  290 + fy="16.049133"
  291 + r="3.8335034" />
  292 + <radialGradient
  293 + inkscape:collect="always"
  294 + xlink:href="#linearGradient11494"
  295 + id="radialGradient1354"
  296 + gradientUnits="userSpaceOnUse"
  297 + gradientTransform="matrix(1.243453,2.106784e-16,-2.106784e-16,1.243453,-6.713754,-3.742847)"
  298 + cx="27.577173"
  299 + cy="15.048258"
  300 + fx="27.577173"
  301 + fy="15.048258"
  302 + r="3.8335034" />
  303 + <radialGradient
  304 + inkscape:collect="always"
  305 + xlink:href="#linearGradient11508"
  306 + id="radialGradient1356"
  307 + gradientUnits="userSpaceOnUse"
  308 + gradientTransform="matrix(1.000000,0.000000,0.000000,0.338462,2.220359e-14,29.48178)"
  309 + cx="30.203562"
  310 + cy="44.565483"
  311 + fx="30.203562"
  312 + fy="44.565483"
  313 + r="6.5659914" />
  314 + <radialGradient
  315 + inkscape:collect="always"
  316 + xlink:href="#linearGradient11520"
  317 + id="radialGradient1366"
  318 + gradientUnits="userSpaceOnUse"
  319 + gradientTransform="matrix(2.049266,-1.696401e-32,0.000000,2.049266,-25.65002,-37.31089)"
  320 + cx="24.445690"
  321 + cy="35.878170"
  322 + fx="24.445690"
  323 + fy="35.878170"
  324 + r="20.530962" />
  325 + <radialGradient
  326 + inkscape:collect="always"
  327 + xlink:href="#linearGradient5150"
  328 + id="radialGradient5156"
  329 + cx="24.837126"
  330 + cy="40.663769"
  331 + fx="24.837126"
  332 + fy="40.663769"
  333 + r="21.478369"
  334 + gradientTransform="matrix(1,0,0,0.325103,2.211772e-16,27.44386)"
  335 + gradientUnits="userSpaceOnUse" />
  336 + <linearGradient
  337 + inkscape:collect="always"
  338 + xlink:href="#linearGradient5162"
  339 + id="linearGradient5168"
  340 + x1="24.365993"
  341 + y1="20.246058"
  342 + x2="32.600704"
  343 + y2="28.554564"
  344 + gradientUnits="userSpaceOnUse" />
  345 + <linearGradient
  346 + inkscape:collect="always"
  347 + xlink:href="#linearGradient5162"
  348 + id="linearGradient5170"
  349 + gradientUnits="userSpaceOnUse"
  350 + x1="22.008699"
  351 + y1="36.509514"
  352 + x2="23.585091"
  353 + y2="14.412428" />
  354 + <linearGradient
  355 + inkscape:collect="always"
  356 + xlink:href="#linearGradient5176"
  357 + id="linearGradient5182"
  358 + x1="25.632622"
  359 + y1="10.611729"
  360 + x2="38.714096"
  361 + y2="18.389904"
  362 + gradientUnits="userSpaceOnUse" />
  363 + <radialGradient
  364 + inkscape:collect="always"
  365 + xlink:href="#linearGradient5188"
  366 + id="radialGradient5361"
  367 + gradientUnits="userSpaceOnUse"
  368 + gradientTransform="matrix(2.135667,1.912751e-16,-1.890308e-16,2.110607,-26.90176,-15.66914)"
  369 + cx="23.688078"
  370 + cy="14.210698"
  371 + fx="23.688078"
  372 + fy="14.210698"
  373 + r="22.597087" />
  374 + <radialGradient
  375 + inkscape:collect="always"
  376 + xlink:href="#linearGradient5196"
  377 + id="radialGradient5363"
  378 + gradientUnits="userSpaceOnUse"
  379 + gradientTransform="matrix(1.790269,1.339577e-16,-1.323859e-16,1.769263,-15.81394,-11.94997)"
  380 + cx="20.089987"
  381 + cy="10.853651"
  382 + fx="20.089987"
  383 + fy="10.853651"
  384 + r="22.597087" />
  385 + <linearGradient
  386 + inkscape:collect="always"
  387 + xlink:href="#linearGradient5166"
  388 + id="linearGradient5172"
  389 + x1="19.450956"
  390 + y1="14.463861"
  391 + x2="23.71875"
  392 + y2="48.404987"
  393 + gradientUnits="userSpaceOnUse" />
  394 + </defs>
  395 + <sodipodi:namedview
  396 + stroke="#ef2929"
  397 + fill="#eeeeec"
  398 + id="base"
  399 + pagecolor="#ffffff"
  400 + bordercolor="#666666"
  401 + borderopacity="0.25490196"
  402 + inkscape:pageopacity="0.0"
  403 + inkscape:pageshadow="2"
  404 + inkscape:zoom="1"
  405 + inkscape:cx="-91.650069"
  406 + inkscape:cy="-6.8095951"
  407 + inkscape:current-layer="layer1"
  408 + showgrid="false"
  409 + inkscape:grid-bbox="true"
  410 + inkscape:document-units="px"
  411 + inkscape:showpageshadow="false"
  412 + inkscape:window-width="872"
  413 + inkscape:window-height="688"
  414 + inkscape:window-x="441"
  415 + inkscape:window-y="160" />
  416 + <metadata
  417 + id="metadata4">
  418 + <rdf:RDF>
  419 + <cc:Work
  420 + rdf:about="">
  421 + <dc:format>image/svg+xml</dc:format>
  422 + <dc:type
  423 + rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
  424 + <dc:creator>
  425 + <cc:Agent>
  426 + <dc:title>Jakub Steiner</dc:title>
  427 + </cc:Agent>
  428 + </dc:creator>
  429 + <dc:source>http://jimmac.musichall.cz</dc:source>
  430 + <cc:license
  431 + rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
  432 + <dc:title>Mark mail as Junk</dc:title>
  433 + <dc:subject>
  434 + <rdf:Bag>
  435 + <rdf:li>mail</rdf:li>
  436 + <rdf:li>spam</rdf:li>
  437 + <rdf:li>junk</rdf:li>
  438 + </rdf:Bag>
  439 + </dc:subject>
  440 + </cc:Work>
  441 + <cc:License
  442 + rdf:about="http://creativecommons.org/licenses/publicdomain/">
  443 + <cc:permits
  444 + rdf:resource="http://creativecommons.org/ns#Reproduction" />
  445 + <cc:permits
  446 + rdf:resource="http://creativecommons.org/ns#Distribution" />
  447 + <cc:permits
  448 + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
  449 + </cc:License>
  450 + </rdf:RDF>
  451 + </metadata>
  452 + <g
  453 + id="layer1"
  454 + inkscape:label="Layer 1"
  455 + inkscape:groupmode="layer">
  456 + <path
  457 + sodipodi:type="arc"
  458 + style="opacity:0.3258427;color:black;fill:url(#radialGradient5156);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  459 + id="path4275"
  460 + sodipodi:cx="24.837126"
  461 + sodipodi:cy="40.663769"
  462 + sodipodi:rx="21.478369"
  463 + sodipodi:ry="6.9826794"
  464 + d="M 46.315495 40.663769 A 21.478369 6.9826794 0 1 1 3.358757,40.663769 A 21.478369 6.9826794 0 1 1 46.315495 40.663769 z"
  465 + transform="matrix(1.106996,0,0,1.106996,-3.364576,-5.411516)" />
  466 + <path
  467 + style="opacity:1;color:black;fill:url(#radialGradient5361);fill-opacity:1;fill-rule:evenodd;stroke:url(#radialGradient5363);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  468 + d="M 9.0156115,37.570175 L 7.2478445,40.398602 L 10.960155,41.989592 L 13.611806,39.868272 L 25.455844,40.752155 L 29.168155,45.701903 L 32.526912,40.221825 L 36.769553,42.519922 L 42.426407,41.812816 L 41.012193,38.807612 L 44.901281,34.918524 L 39.421203,28.73134 L 44.724504,29.438447 L 47.022601,27.317127 L 44.017397,27.847457 L 40.835417,22.367379 L 36.239223,21.306719 L 39.951533,20.069282 L 40.12831,16.887302 L 32.880465,10.523341 L 30.228815,2.0380592 L 18.208,5.5735931 L 15.202796,12.644661 L 14.142136,10.346564 L 11.136932,10.523341 L 11.136932,13.351768 L 7.6013979,9.2859037 L 2.8284271,14.412428 L 2.8284271,21.660272 L 11.136932,28.908117 L 5.833631,31.913321 L 6.0104076,34.918524 L 9.0156115,37.570175 z "
  469 + id="path4273" />
  470 + <path
  471 + style="opacity:0.76966292;color:black;fill:url(#linearGradient5170);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  472 + d="M 19.445437,22.720933 L 18.561553,29.26167 L 20.682873,32.620427 L 16.086679,34.034641 L 13.435029,39.337942 L 25.455844,40.221825 L 28.284271,44.287689 L 30.582368,38.100505 L 26.162951,35.272078 L 19.445437,37.216622 L 22.097087,33.681088 L 20.682873,27.493903 L 19.445437,22.720933 z "
  473 + id="path5158" />
  474 + <path
  475 + style="opacity:0.61797753;color:black;fill:url(#linearGradient5168);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  476 + d="M 20.506097,23.781593 L 24.925514,30.675884 L 32.173359,25.726136 L 36.592776,28.20101 L 40.12831,25.195806 L 43.31029,28.024233 L 40.305087,23.074486 L 33.410795,21.483496 L 24.748737,17.240855 C 24.748737,17.240855 12.374369,20.953166 13.081475,20.953166 C 13.788582,20.953166 23.334524,21.129942 23.334524,21.129942 L 24.925514,18.831845 L 29.168155,20.776389 L 20.506097,23.781593 z "
  477 + id="path5160" />
  478 + <path
  479 + style="opacity:1;color:black;fill:white;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  480 + d="M 29.521708,3.4522727 L 19.091883,6.9878066 L 17.67767,10.876894 L 23.688077,15.119535 L 29.344931,9.6394571 L 29.521708,3.4522727 z "
  481 + id="path5172" />
  482 + <path
  483 + style="opacity:0.13483146;color:black;fill:url(#linearGradient5182);fill-opacity:1.0;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  484 + d="M 29.698485,3.8058261 L 31.996582,11.230447 L 36.239223,14.942758 L 27.577165,12.998214 C 27.577165,12.998214 25.102291,15.826641 26.162951,15.826641 C 27.223611,15.826641 38.714096,17.240855 38.714096,17.240855 L 38.53732,19.538952 L 22.273864,16.533748 L 29.344931,9.9930105 L 29.698485,3.8058261 z "
  485 + id="path5174" />
  486 + <path
  487 + style="opacity:1;color:black;fill:white;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  488 + d="M 7.4246212,14.235651 L 6.5407377,23.074486 L 8.4852814,17.064078 L 13.611806,18.655069 L 14.849242,16.003418 L 21.036427,16.887302 L 22.45064,15.649865 L 13.435029,15.296311 L 12.551145,17.771185 L 8.6620581,15.826641 L 7.4246212,14.235651 z "
  489 + id="path5184" />
  490 + <path
  491 + style="opacity:1;color:black;fill:white;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  492 + d="M 22.45064,27.317127 L 25.455844,30.852661 L 32.350135,26.256466 L 36.592776,28.554563 L 43.487067,35.095301 L 35.885669,29.26167 L 32.173359,28.20101 L 24.925514,33.150758 L 22.45064,27.317127 z "
  493 + id="path5186" />
  494 + <path
  495 + sodipodi:type="inkscape:offset"
  496 + inkscape:radius="-0.83777463"
  497 + inkscape:original="M 30.21875 2.03125 L 18.21875 5.5625 L 15.1875 12.65625 L 14.15625 10.34375 L 11.125 10.53125 L 11.125 13.34375 L 7.59375 9.28125 L 2.84375 14.40625 L 2.84375 21.65625 L 11.125 28.90625 L 5.84375 31.90625 L 6 34.90625 L 9 37.5625 L 7.25 40.40625 L 10.96875 42 L 13.625 39.875 L 25.46875 40.75 L 29.15625 45.6875 L 32.53125 40.21875 L 36.78125 42.53125 L 42.4375 41.8125 L 41 38.8125 L 44.90625 34.90625 L 39.40625 28.71875 L 44.71875 29.4375 L 47.03125 27.3125 L 44.03125 27.84375 L 40.84375 22.375 L 36.25 21.3125 L 39.9375 20.0625 L 40.125 16.875 L 32.875 10.53125 L 30.21875 2.03125 z "
  498 + xlink:href="#path4273"
  499 + style="opacity:1;color:black;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient5172);stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible"
  500 + id="path5359"
  501 + inkscape:href="#path4273"
  502 + d="M 29.65625,3.09375 L 18.84375,6.25 L 15.96875,13 C 15.834262,13.30255 15.53422,13.497528 15.203125,13.497528 C 14.87203,13.497528 14.571988,13.30255 14.4375,13 L 13.65625,11.21875 L 11.96875,11.3125 L 11.96875,13.34375 C 11.973462,13.69495 11.758625,14.011813 11.430625,14.13743 C 11.102625,14.263047 10.731089,14.170753 10.5,13.90625 L 7.5625,10.53125 L 3.6875,14.71875 L 3.6875,21.28125 L 11.6875,28.28125 C 11.883657,28.462891 11.981357,28.727236 11.950479,28.992788 C 11.919601,29.25834 11.763852,29.493214 11.53125,29.625 L 6.71875,32.375 L 6.8125,34.5 L 9.5625,36.9375 C 9.8645501,37.207345 9.9303261,37.654622 9.71875,38 L 8.46875,40 L 10.84375,41.03125 L 13.09375,39.21875 C 13.260233,39.0823 13.472846,39.015159 13.6875,39.03125 L 25.53125,39.90625 C 25.769158,39.930835 25.985219,40.055923 26.125,40.25 L 29.09375,44.21875 L 31.8125,39.78125 C 32.041236,39.389926 32.539705,39.251463 32.9375,39.46875 L 36.96875,41.65625 L 41.1875,41.125 L 40.25,39.1875 C 40.087762,38.864671 40.150741,38.474201 40.40625,38.21875 L 43.75,34.875 L 38.78125,29.28125 C 38.529387,29.019826 38.474521,28.625949 38.645349,28.305645 C 38.816178,27.985341 39.17384,27.811478 39.53125,27.875 L 43.40625,28.40625 C 43.369413,28.35773 43.337977,28.305337 43.3125,28.25 L 40.3125,23.09375 L 36.0625,22.125 C 35.709163,22.034799 35.454745,21.726417 35.433331,21.362378 C 35.411917,20.998338 35.628425,20.662254 35.96875,20.53125 L 39.125,19.46875 L 39.25,17.21875 L 32.3125,11.15625 C 32.19648,11.05643 32.110019,10.926737 32.0625,10.78125 L 29.65625,3.09375 z " />
  503 + </g>
  504 +</svg>
public/javascripts/application.js
@@ -683,6 +683,33 @@ function add_comment_reply_form(button, comment_id) { @@ -683,6 +683,33 @@ function add_comment_reply_form(button, comment_id) {
683 return f; 683 return f;
684 } 684 }
685 685
  686 +function remove_comment(button, url, msg) {
  687 + var $ = jQuery;
  688 + var $button = $(button);
  689 + if (msg && !confirm(msg)) {
  690 + $button.removeClass('comment-button-loading');
  691 + return;
  692 + }
  693 + $button.addClass('comment-button-loading');
  694 + $.post(url, function(data) {
  695 + if (data.ok) {
  696 + var $comment = $button.closest('.article-comment');
  697 + var $replies = $comment.find('.comment-replies .article-comment');
  698 + $comment.slideUp();
  699 + var comments_removed = 1;
  700 + if ($button.hasClass('remove-children')) {
  701 + comments_removed = 1 + $replies.size();
  702 + } else {
  703 + $replies.appendTo('.article-comments-list');
  704 + }
  705 + $('.comment-count').each(function() {
  706 + var count = parseInt($(this).html());
  707 + $(this).html(count - comments_removed);
  708 + });
  709 + }
  710 + });
  711 +}
  712 +
686 function original_image_dimensions(src) { 713 function original_image_dimensions(src) {
687 var img = new Image(); 714 var img = new Image();
688 img.src = src; 715 img.src = src;
public/stylesheets/application.css
@@ -1069,15 +1069,6 @@ a.comment-picture { @@ -1069,15 +1069,6 @@ a.comment-picture {
1069 top: 9px; 1069 top: 9px;
1070 right: 8px; 1070 right: 8px;
1071 } 1071 }
1072 -#content .comment-balloon a.button.icon-delete {  
1073 - border: 0;  
1074 - padding-top: 0;  
1075 - padding-bottom: 0;  
1076 - background-color: transparent;  
1077 -}  
1078 -#content .comments .comment-balloon a.button.icon-delete {  
1079 - display: none;  
1080 -}  
1081 .msie7 .article-comments-list .comment-balloon { 1072 .msie7 .article-comments-list .comment-balloon {
1082 margin-top: -15px; 1073 margin-top: -15px;
1083 } 1074 }
@@ -1271,6 +1262,11 @@ a.comment-picture { @@ -1271,6 +1262,11 @@ a.comment-picture {
1271 } 1262 }
1272 /* * * Comment Box * * */ 1263 /* * * Comment Box * * */
1273 1264
  1265 +.comment-button-loading {
  1266 + padding-left: 20px;
  1267 + background: transparent url(../images/loading-small.gif) no-repeat left center;
  1268 +}
  1269 +
1274 .post_comment_box { 1270 .post_comment_box {
1275 text-align: center; 1271 text-align: center;
1276 padding: 0px 15px 5px 15px; 1272 padding: 0px 15px 5px 15px;
@@ -3854,6 +3850,9 @@ h1#agenda-title { @@ -3854,6 +3850,9 @@ h1#agenda-title {
3854 .controller-profile_editor .msie6 a.control-panel-edit-location { 3850 .controller-profile_editor .msie6 a.control-panel-edit-location {
3855 background-image: url(../images/control-panel/set-geolocation.gif) 3851 background-image: url(../images/control-panel/set-geolocation.gif)
3856 } 3852 }
  3853 +.controller-profile_editor a.control-panel-manage-spam {
  3854 + background-image: url(../images/control-panel/mail-mark-junk.png)
  3855 +}
3857 /* ==> public/stylesheets/controller_profile_members.css <== */ 3856 /* ==> public/stylesheets/controller_profile_members.css <== */
3858 .controller-profile_members .no-boxes { 3857 .controller-profile_members .no-boxes {
3859 margin: 30px 3858 margin: 30px
script/install-dependencies/debian-squeeze.sh
1 # needed to run noosfero 1 # needed to run noosfero
2 runtime_dependencies=$(sed -e '1,/^Depends:/d; /^Recommends:/,$ d; s/([^)]*)//g; s/,\s*/\n/g' debian/control | grep -v 'memcached\|debconf\|dbconfig-common\|postgresql\|misc:Depends\|adduser\|mail-transport-agent') 2 runtime_dependencies=$(sed -e '1,/^Depends:/d; /^Recommends:/,$ d; s/([^)]*)//g; s/,\s*/\n/g' debian/control | grep -v 'memcached\|debconf\|dbconfig-common\|postgresql\|misc:Depends\|adduser\|mail-transport-agent')
3 run sudo apt-get -y install $runtime_dependencies 3 run sudo apt-get -y install $runtime_dependencies
  4 +sudo apt-get -y install iceweasel || sudo apt-get -y install firefox
4 5
5 # needed for development 6 # needed for development
6 run sudo apt-get -y install libtidy-ruby libhpricot-ruby libmocha-ruby imagemagick po4a xvfb libxml2-dev libxslt-dev 7 run sudo apt-get -y install libtidy-ruby libhpricot-ruby libmocha-ruby imagemagick po4a xvfb libxml2-dev libxslt-dev
7 -gem which bundler >/dev/null 2>&1 || run gem install bundler  
8 -which bundle >/dev/null 2>&1 || export PATH="$(ruby -rubygems -e 'puts Gem.bindir'):$PATH" 8 +gem which bundler >/dev/null 2>&1 || gem_install bundler
  9 +setup_rubygems_path
9 run bundle install 10 run bundle install
script/noosfero-plugins
@@ -21,6 +21,8 @@ disabled_plugins=$(printf &quot;%s\n&quot; $available_plugins $enabled_plugins_dir | sort @@ -21,6 +21,8 @@ disabled_plugins=$(printf &quot;%s\n&quot; $available_plugins $enabled_plugins_dir | sort
21 # operation defaults 21 # operation defaults
22 quiet=false 22 quiet=false
23 needs_migrate=false 23 needs_migrate=false
  24 +load_paths="$NOOSFERO_DIR/lib:$(echo $NOOSFERO_DIR/vendor/plugins/*/lib | tr ' ' :)"
  25 +
24 26
25 _list() { 27 _list() {
26 for plugin in $available_plugins; do 28 for plugin in $available_plugins; do
@@ -72,11 +74,26 @@ _enable(){ @@ -72,11 +74,26 @@ _enable(){
72 if [ -h "$target" ]; then 74 if [ -h "$target" ]; then
73 _say "$plugin already enabled" 75 _say "$plugin already enabled"
74 else 76 else
75 - ln -s "$source" "$target"  
76 - plugins_public_dir="$NOOSFERO_DIR/public/plugins"  
77 - test -d "$target/public/" && ln -s "$target/public" "$plugins_public_dir/$plugin"  
78 - _say "$plugin enabled"  
79 - needs_migrate=true 77 + if [ ! -d "$source" ]; then
  78 + echo "E: $plugin plugin does not exist!"
  79 + return
  80 + fi
  81 + dependencies_ok=true
  82 + dependencies_file="$source/dependencies.rb"
  83 + if [ -e "$dependencies_file" ]; then
  84 + if ! ruby -I$load_paths -e "require '$dependencies_file'"; then
  85 + dependencies_ok=false
  86 + fi
  87 + fi
  88 + if [ "$dependencies_ok" = true ]; then
  89 + ln -s "$source" "$target"
  90 + plugins_public_dir="$NOOSFERO_DIR/public/plugins"
  91 + test -d "$target/public/" && ln -s "$target/public" "$plugins_public_dir/$plugin"
  92 + _say "$plugin enabled"
  93 + needs_migrate=true
  94 + else
  95 + echo "W: failed to load dependencies for $plugin; not enabling"
  96 + fi
80 fi 97 fi
81 } 98 }
82 99
script/production
@@ -34,8 +34,6 @@ do_start() { @@ -34,8 +34,6 @@ do_start() {
34 } 34 }
35 35
36 do_stop() { 36 do_stop() {
37 - rake -s solr:stop  
38 -  
39 # During Debian upgrades, it is possible that rails is not available (e.g. 37 # During Debian upgrades, it is possible that rails is not available (e.g.
40 # Lenny -> Squeeze), so the programs below might fail. If they do, we fall 38 # Lenny -> Squeeze), so the programs below might fail. If they do, we fall
41 # back to stopping the daemons by manually reading their PID files, killing 39 # back to stopping the daemons by manually reading their PID files, killing
@@ -46,6 +44,8 @@ do_stop() { @@ -46,6 +44,8 @@ do_stop() {
46 44
47 environments_loop stop || 45 environments_loop stop ||
48 stop_via_pid_file tmp/pids/delayed_job.pid tmp/pids/delayed_job.*.pid tmp/pids/feed-updater.*.pid 46 stop_via_pid_file tmp/pids/delayed_job.pid tmp/pids/delayed_job.*.pid tmp/pids/feed-updater.*.pid
  47 +
  48 + rake -s solr:stop || stop_via_pid_file tmp/pids/solr.*.pid
49 } 49 }
50 50
51 stop_via_pid_file() { 51 stop_via_pid_file() {
script/quick-start
@@ -21,6 +21,22 @@ run() { @@ -21,6 +21,22 @@ run() {
21 fi 21 fi
22 } 22 }
23 23
  24 +gem_install() {
  25 + if [ -w "$(ruby -rubygems -e 'puts Gem.dir')" ]; then
  26 + run gem install --no-ri --no-rdoc $@
  27 + else
  28 + run gem install --user-install --no-ri --no-rdoc $@
  29 + fi
  30 +}
  31 +
  32 +setup_rubygems_path() {
  33 + local dir="$(ruby -rubygems -e 'puts Gem.user_dir')/bin"
  34 + if [ -d "$dir" ]; then
  35 + export PATH="$dir:$PATH"
  36 + fi
  37 +}
  38 +
  39 +
24 force_install=false 40 force_install=false
25 if test "$1" = '--force-install'; then 41 if test "$1" = '--force-install'; then
26 force_install=true 42 force_install=true
@@ -28,9 +44,15 @@ fi @@ -28,9 +44,15 @@ fi
28 if gem which system_timer >/dev/null 2>&1 && which xvfb-run >/dev/null 2>&1 && test "$force_install" = 'false'; then 44 if gem which system_timer >/dev/null 2>&1 && which xvfb-run >/dev/null 2>&1 && test "$force_install" = 'false'; then
29 say "Assuming dependencies are already installed. Pass --force-install to force their installation" 45 say "Assuming dependencies are already installed. Pass --force-install to force their installation"
30 else 46 else
31 - if !which lsb_release >/dev/null 2>&1; then  
32 - complain "E: lsb_release not available! (Try installing the lsb-release package)"  
33 - exit 1 47 + if ! which lsb_release >/dev/null 2>&1; then
  48 + # special case Debian-based systems; in others people will have to install
  49 + # lsb-release by themselves
  50 + if which apt-get >/dev/null 2>&1; then
  51 + sudo apt-get -y install lsb-release
  52 + else
  53 + complain "E: lsb_release not available! (Try installing the lsb-release package)"
  54 + exit 1
  55 + fi
34 fi 56 fi
35 system=$(echo $(lsb_release -sic) | awk '{print(tolower($1) "-" tolower($2))}') 57 system=$(echo $(lsb_release -sic) | awk '{print(tolower($1) "-" tolower($2))}')
36 install_script="$(dirname $0)/install-dependencies/${system}.sh" 58 install_script="$(dirname $0)/install-dependencies/${system}.sh"
test/functional/content_viewer_controller_test.rb
@@ -92,7 +92,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -92,7 +92,7 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
92 92
93 login_as 'testuser' 93 login_as 'testuser'
94 get :view_page, :profile => 'testuser', :page => [ 'test' ] 94 get :view_page, :profile => 'testuser', :page => [ 'test' ]
95 - assert_tag :tag => 'a', :attributes => { :href => '/testuser/test?remove_comment=' + comment.id.to_s } 95 + assert_tag :tag => 'a', :attributes => { :onclick => %r(/testuser/test\?remove_comment=#{comment.id}.quot) }
96 end 96 end
97 97
98 should 'display remove comment button with param view when image' do 98 should 'display remove comment button with param view when image' do
@@ -106,8 +106,9 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -106,8 +106,9 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
106 106
107 login_as 'testuser' 107 login_as 'testuser'
108 get :view_page, :profile => 'testuser', :page => [ image.filename ], :view => true 108 get :view_page, :profile => 'testuser', :page => [ image.filename ], :view => true
109 - assert_tag :tag => 'a', :attributes => { :href => "/testuser/#{image.filename}?remove_comment=" + comment.id.to_s + '&amp;view=true'}  
110 - end 109 + assert_tag :tag => 'a', :attributes => { :onclick => %r(/testuser/#{image.filename}\?remove_comment=#{comment.id}.*amp;view=true.quot) }
  110 +end
  111 +
111 112
112 should 'not add unneeded params for remove comment button' do 113 should 'not add unneeded params for remove comment button' do
113 profile = create_user('testuser').person 114 profile = create_user('testuser').person
@@ -117,8 +118,8 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -117,8 +118,8 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
117 comment.save! 118 comment.save!
118 119
119 login_as 'testuser' 120 login_as 'testuser'
120 - get :view_page, :profile => 'testuser', :page => [ 'test' ], :random_param => 'bli' # <<<<<<<<<<<<<<<  
121 - assert_tag :tag => 'a', :attributes => { :href => '/testuser/test?remove_comment=' + comment.id.to_s } 121 + get :view_page, :profile => 'testuser', :page => [ 'test' ], :random_param => 'bli'
  122 + assert_tag :tag => 'a', :attributes => { :onclick => %r(/testuser/test\?remove_comment=#{comment.id.to_s}.quot) }
122 end 123 end
123 124
124 should 'be able to remove comment' do 125 should 'be able to remove comment' do
@@ -1374,12 +1375,16 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -1374,12 +1375,16 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1374 assert_not_nil assigns(:comment) 1375 assert_not_nil assigns(:comment)
1375 end 1376 end
1376 1377
1377 - should 'store IP address for comments' do 1378 + should 'store IP address, user agent and referrer for comments' do
1378 page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text') 1379 page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
1379 @request.stubs(:remote_ip).returns('33.44.55.66') 1380 @request.stubs(:remote_ip).returns('33.44.55.66')
  1381 + @request.stubs(:referrer).returns('http://example.com')
  1382 + @request.stubs(:user_agent).returns('MyBrowser')
1380 post :view_page, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :title => 'title', :body => 'body', :name => "Spammer", :email => 'damn@spammer.com' }, :confirm => 'true' 1383 post :view_page, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :title => 'title', :body => 'body', :name => "Spammer", :email => 'damn@spammer.com' }, :confirm => 'true'
1381 comment = Comment.last 1384 comment = Comment.last
1382 assert_equal '33.44.55.66', comment.ip_address 1385 assert_equal '33.44.55.66', comment.ip_address
  1386 + assert_equal 'MyBrowser', comment.user_agent
  1387 + assert_equal 'http://example.com', comment.referrer
1383 end 1388 end
1384 1389
1385 should 'not save a comment if a plugin rejects it' do 1390 should 'not save a comment if a plugin rejects it' do
@@ -1395,25 +1400,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -1395,25 +1400,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1395 end 1400 end
1396 end 1401 end
1397 1402
1398 - should 'notify plugins after a comment is saved' do  
1399 - class TestNotifyCommentPlugin < Noosfero::Plugin  
1400 - def comment_saved(c)  
1401 - @__saved = c.id  
1402 - @__title = c.title  
1403 - end  
1404 - attr_reader :__title  
1405 - attr_reader :__saved  
1406 - end  
1407 - plugin = TestNotifyCommentPlugin.new  
1408 - Noosfero::Plugin::Manager.any_instance.stubs(:enabled_plugins).returns([plugin])  
1409 - page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')  
1410 - post :view_page, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :title => 'the title of the comment', :body => 'body', :name => "Spammer", :email => 'damn@spammer.com' }, :confirm => 'true'  
1411 -  
1412 - assert_equal 'the title of the comment', plugin.__title  
1413 - assert plugin.__saved  
1414 -  
1415 - end  
1416 -  
1417 should 'remove email from article followers when unfollow' do 1403 should 'remove email from article followers when unfollow' do
1418 profile = create_user('testuser').person 1404 profile = create_user('testuser').person
1419 follower_email = 'john@doe.br' 1405 follower_email = 'john@doe.br'
@@ -1426,4 +1412,24 @@ class ContentViewerControllerTest &lt; ActionController::TestCase @@ -1426,4 +1412,24 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1426 assert_not_includes Article.find(article.id).followers, follower_email 1412 assert_not_includes Article.find(article.id).followers, follower_email
1427 end 1413 end
1428 1414
  1415 + should 'not display comments marked as spam' do
  1416 + article = fast_create(Article, :profile_id => profile.id)
  1417 + ham = fast_create(Comment, :source_id => article.id, :source_type => 'Article')
  1418 + spam = fast_create(Comment, :source_id => article.id, :source_type => 'Article', :spam => true)
  1419 +
  1420 + get 'view_page', :profile => profile.identifier, :page => article.path.split('/')
  1421 + assert_equal 1, assigns(:comments_count)
  1422 + end
  1423 +
  1424 + should 'be able to mark comments as spam' do
  1425 + login_as profile.identifier
  1426 + article = fast_create(Article, :profile_id => profile.id)
  1427 + spam = fast_create(Comment, :name => 'foo', :email => 'foo@example.com', :source_id => article.id, :source_type => 'Article')
  1428 +
  1429 + post 'view_page', :profile => profile.identifier, :page => article.path.split('/'), :mark_comment_as_spam => spam.id
  1430 +
  1431 + spam.reload
  1432 + assert spam.spam?
  1433 + end
  1434 +
1429 end 1435 end
test/functional/spam_controller_test.rb 0 → 100644
@@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class SpamControllerTest < ActionController::TestCase
  4 +
  5 + def setup
  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 +
  10 + login_as @profile.identifier
  11 + end
  12 +
  13 + test "should only list spammy comments" do
  14 + ham = fast_create(Comment, :source_id => @article.id)
  15 +
  16 + get :index, :profile => @profile.identifier
  17 +
  18 + assert_equivalent [@spam], assigns(:spam)
  19 + end
  20 +
  21 + test "should mark comments as ham" do
  22 + post :index, :profile => @profile.identifier, :mark_comment_as_ham => @spam.id
  23 +
  24 + @spam.reload
  25 + assert @spam.ham?
  26 + end
  27 +
  28 + test "should remove comments" do
  29 + post :index, :profile => @profile.identifier, :remove_comment => @spam.id
  30 +
  31 + assert !Comment.exists?(@spam.id)
  32 + end
  33 +
  34 + should 'properly render spam that have replies' do
  35 + reply_spam = fast_create(Comment, :source_id => @article_id, :reply_of_id => @spam.id)
  36 +
  37 + get :index, :profile => @profile.identifier
  38 + assert_response :success
  39 + end
  40 +
  41 +end
test/mocks/test/profile.rb 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +require File.expand_path(File.dirname(__FILE__) + "/../../../app/models/profile")
  2 +
  3 +class Profile
  4 + def inspect
  5 + "#{self.class.name}/#{id}/#{identifier}"
  6 + end
  7 +end
  8 +
test/unit/comment_handler_test.rb 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class CommentHandlerTest < ActiveSupport::TestCase
  4 +
  5 + should 'receive comment id' do
  6 + handler = CommentHandler.new(99)
  7 + assert_equal 99, handler.comment_id
  8 + end
  9 +
  10 + should 'not crash with unexisting comment' do
  11 + handler = CommentHandler.new(-1)
  12 + handler.perform
  13 + end
  14 +
  15 + should 'call Comment#whatever_method' do
  16 + handler = CommentHandler.new(-1, :whatever_method)
  17 + comment = Comment.new
  18 + Comment.stubs(:find).with(-1).returns(comment)
  19 + comment.expects(:whatever_method)
  20 +
  21 + handler.perform
  22 + end
  23 +
  24 +end
test/unit/comment_notifier_test.rb
@@ -14,24 +14,24 @@ class CommentNotifierTest &lt; ActiveSupport::TestCase @@ -14,24 +14,24 @@ class CommentNotifierTest &lt; ActiveSupport::TestCase
14 14
15 should 'deliver mail after make an article comment' do 15 should 'deliver mail after make an article comment' do
16 assert_difference ActionMailer::Base.deliveries, :size do 16 assert_difference ActionMailer::Base.deliveries, :size do
17 - Comment.create(:author => @profile, :title => 'test comment', :body => 'you suck!', :source => @article ) 17 + create_comment_and_notify(:author => @profile, :title => 'test comment', :body => 'you suck!', :source => @article )
18 end 18 end
19 end 19 end
20 20
21 should 'deliver mail to owner of article' do 21 should 'deliver mail to owner of article' do
22 - Comment.create(:author => @profile, :title => 'test comment', :body => 'you suck!', :source => @article ) 22 + create_comment_and_notify(:author => @profile, :title => 'test comment', :body => 'you suck!', :source => @article )
23 sent = ActionMailer::Base.deliveries.first 23 sent = ActionMailer::Base.deliveries.first
24 assert_equal [@profile.email], sent.to 24 assert_equal [@profile.email], sent.to
25 end 25 end
26 26
27 should 'display author name in delivered mail' do 27 should 'display author name in delivered mail' do
28 - Comment.create(:author => @profile, :title => 'test comment', :body => 'you suck!', :source => @article) 28 + create_comment_and_notify(:author => @profile, :title => 'test comment', :body => 'you suck!', :source => @article)
29 sent = ActionMailer::Base.deliveries.first 29 sent = ActionMailer::Base.deliveries.first
30 assert_match /user_comment_test/, sent.body 30 assert_match /user_comment_test/, sent.body
31 end 31 end
32 32
33 should 'display unauthenticated author name and email in delivered mail' do 33 should 'display unauthenticated author name and email in delivered mail' do
34 - Comment.create(:name => 'flatline', :email => 'flatline@invalid.com', :title => 'test comment', :body => 'you suck!', :source => @article ) 34 + create_comment_and_notify(:name => 'flatline', :email => 'flatline@invalid.com', :title => 'test comment', :body => 'you suck!', :source => @article )
35 sent = ActionMailer::Base.deliveries.first 35 sent = ActionMailer::Base.deliveries.first
36 assert_match /flatline/, sent.body 36 assert_match /flatline/, sent.body
37 assert_match /flatline@invalid.com/, sent.body 37 assert_match /flatline@invalid.com/, sent.body
@@ -40,18 +40,18 @@ class CommentNotifierTest &lt; ActiveSupport::TestCase @@ -40,18 +40,18 @@ class CommentNotifierTest &lt; ActiveSupport::TestCase
40 should 'not deliver mail if notify comments is false' do 40 should 'not deliver mail if notify comments is false' do
41 @article.update_attribute(:notify_comments, false) 41 @article.update_attribute(:notify_comments, false)
42 assert_no_difference ActionMailer::Base.deliveries, :size do 42 assert_no_difference ActionMailer::Base.deliveries, :size do
43 - @article.comments << Comment.new(:author => @profile, :title => 'test comment', :body => 'you suck!') 43 + create_comment_and_notify(:author => @profile, :title => 'test comment', :body => 'you suck!', :source => @article)
44 end 44 end
45 end 45 end
46 46
47 should 'include comment title in the e-mail' do 47 should 'include comment title in the e-mail' do
48 - Comment.create(:author => @profile, :title => 'comment title', :body => 'comment body', :source => @article) 48 + create_comment_and_notify(:author => @profile, :title => 'comment title', :body => 'comment body', :source => @article)
49 sent = ActionMailer::Base.deliveries.first 49 sent = ActionMailer::Base.deliveries.first
50 assert_match /comment title/, sent.body 50 assert_match /comment title/, sent.body
51 end 51 end
52 52
53 should 'include comment text in the e-mail' do 53 should 'include comment text in the e-mail' do
54 - Comment.create(:author => @profile, :title => 'comment title', :body => 'comment body', :source => @article) 54 + create_comment_and_notify(:author => @profile, :title => 'comment title', :body => 'comment body', :source => @article)
55 sent = ActionMailer::Base.deliveries.first 55 sent = ActionMailer::Base.deliveries.first
56 assert_match /comment body/, sent.body 56 assert_match /comment body/, sent.body
57 end 57 end
@@ -61,7 +61,7 @@ class CommentNotifierTest &lt; ActiveSupport::TestCase @@ -61,7 +61,7 @@ class CommentNotifierTest &lt; ActiveSupport::TestCase
61 assert_equal [], community.notification_emails 61 assert_equal [], community.notification_emails
62 article = fast_create(Article, :name => 'Article test', :profile_id => community.id, :notify_comments => true) 62 article = fast_create(Article, :name => 'Article test', :profile_id => community.id, :notify_comments => true)
63 assert_no_difference ActionMailer::Base.deliveries, :size do 63 assert_no_difference ActionMailer::Base.deliveries, :size do
64 - article.comments << Comment.new(:author => @profile, :title => 'test comment', :body => 'there is no addresses to send notification') 64 + create_comment_and_notify(:author => @profile, :title => 'test comment', :body => 'there is no addresses to send notification', :source => article)
65 end 65 end
66 end 66 end
67 67
@@ -70,24 +70,29 @@ class CommentNotifierTest &lt; ActiveSupport::TestCase @@ -70,24 +70,29 @@ class CommentNotifierTest &lt; ActiveSupport::TestCase
70 follower = create_user('follower').person 70 follower = create_user('follower').person
71 @article.followers += [follower.email] 71 @article.followers += [follower.email]
72 @article.save! 72 @article.save!
73 - @article.comments << Comment.new(:source => @article, :author => author, :title => 'comment title', :body => 'comment body') 73 + create_comment_and_notify(:source => @article, :author => author, :title => 'comment title', :body => 'comment body')
74 assert_includes ActionMailer::Base.deliveries.map(&:bcc).flatten, follower.email 74 assert_includes ActionMailer::Base.deliveries.map(&:bcc).flatten, follower.email
75 end 75 end
76 76
77 should "not deliver follower's mail about new comment to comment's author" do 77 should "not deliver follower's mail about new comment to comment's author" do
78 follower = create_user('follower').person 78 follower = create_user('follower').person
79 - @article.comments << Comment.new(:source => @article, :author => follower, :title => 'comment title', :body => 'comment body') 79 + create_comment_and_notify(:source => @article, :author => follower, :title => 'comment title', :body => 'comment body')
80 assert_not_includes ActionMailer::Base.deliveries.map(&:bcc).flatten, follower.email 80 assert_not_includes ActionMailer::Base.deliveries.map(&:bcc).flatten, follower.email
81 end 81 end
82 82
83 private 83 private
84 84
85 - def read_fixture(action)  
86 - IO.readlines("#{FIXTURES_PATH}/mail_sender/#{action}")  
87 - end 85 + def create_comment_and_notify(args)
  86 + Comment.create!(args)
  87 + process_delayed_job_queue
  88 + end
88 89
89 - def encode(subject)  
90 - quoted_printable(subject, CHARSET)  
91 - end 90 + def read_fixture(action)
  91 + IO.readlines("#{FIXTURES_PATH}/mail_sender/#{action}")
  92 + end
  93 +
  94 + def encode(subject)
  95 + quoted_printable(subject, CHARSET)
  96 + end
92 97
93 end 98 end
test/unit/comment_test.rb
@@ -398,6 +398,9 @@ class CommentTest &lt; ActiveSupport::TestCase @@ -398,6 +398,9 @@ class CommentTest &lt; ActiveSupport::TestCase
398 end 398 end
399 399
400 should 'update article activity when add a comment' do 400 should 'update article activity when add a comment' do
  401 + now = Time.now
  402 + Time.stubs(:now).returns(now)
  403 +
401 profile = create_user('testuser').person 404 profile = create_user('testuser').person
402 article = create(TinyMceArticle, :profile => profile) 405 article = create(TinyMceArticle, :profile => profile)
403 406
@@ -422,4 +425,142 @@ class CommentTest &lt; ActiveSupport::TestCase @@ -422,4 +425,142 @@ class CommentTest &lt; ActiveSupport::TestCase
422 assert_not_nil article.activity 425 assert_not_nil article.activity
423 end 426 end
424 427
  428 + should 'be able to mark comments as spam/ham/unknown' do
  429 + c = Comment.new
  430 + c.spam = true
  431 + assert c.spam?
  432 + assert !c.ham?
  433 +
  434 + c.spam = false
  435 + assert c.ham?
  436 + assert !c.spam?
  437 +
  438 + c.spam = nil
  439 + assert !c.spam?
  440 + assert !c.ham?
  441 + end
  442 +
  443 + should 'be able to select non-spam comments' do
  444 + c1 = fast_create(Comment)
  445 + c2 = fast_create(Comment, :spam => false)
  446 + c3 = fast_create(Comment, :spam => true)
  447 +
  448 + assert_equivalent [c1,c2], Comment.without_spam
  449 + end
  450 +
  451 + should 'be able to mark as spam atomically' do
  452 + c1 = create_comment
  453 + c1.spam!
  454 + c1.reload
  455 + assert c1.spam?
  456 + end
  457 +
  458 + should 'be able to select spammy comments' do
  459 + c1 = fast_create(Comment)
  460 + c2 = fast_create(Comment, :spam => false)
  461 + c3 = fast_create(Comment, :spam => true)
  462 +
  463 + assert_equivalent [c3], Comment.spam
  464 + end
  465 +
  466 + should 'be able to mark as ham atomically' do
  467 + c1 = create_comment
  468 + c1.ham!
  469 + c1.reload
  470 + assert c1.ham?
  471 + end
  472 +
  473 + should 'notify by email' do
  474 + c1 = create_comment
  475 + c1.expects(:notify_by_mail)
  476 + c1.verify_and_notify
  477 + end
  478 +
  479 + should 'not notify by email when comment is spam' do
  480 + c1 = create_comment(:spam => true)
  481 + c1.expects(:notify_by_mail).never
  482 + c1.verify_and_notify
  483 + end
  484 +
  485 + class EverythingIsSpam < Noosfero::Plugin
  486 + def check_comment_for_spam(comment)
  487 + comment.spam!
  488 + end
  489 + end
  490 +
  491 +
  492 + should 'delegate spam detection to plugins' do
  493 + Environment.default.enable_plugin(EverythingIsSpam)
  494 +
  495 + c1 = create_comment
  496 +
  497 + c1.expects(:notify_by_mail).never
  498 +
  499 + c1.verify_and_notify
  500 + end
  501 +
  502 + class SpamNotification < Noosfero::Plugin
  503 + class << self
  504 + attr_accessor :marked_as_spam
  505 + attr_accessor :marked_as_ham
  506 + end
  507 +
  508 + def comment_marked_as_spam(c)
  509 + self.class.marked_as_spam = c
  510 + end
  511 +
  512 + def comment_marked_as_ham(c)
  513 + self.class.marked_as_ham = c
  514 + end
  515 + end
  516 +
  517 + should 'notify plugins of comments being marked as spam' do
  518 + Environment.default.enable_plugin(SpamNotification)
  519 +
  520 + c = create_comment
  521 +
  522 + c.spam!
  523 + process_delayed_job_queue
  524 +
  525 + assert_equal c, SpamNotification.marked_as_spam
  526 + end
  527 +
  528 + should 'notify plugins of comments being marked as ham' do
  529 + Environment.default.enable_plugin(SpamNotification)
  530 +
  531 + c = create_comment
  532 +
  533 + c.ham!
  534 + process_delayed_job_queue
  535 +
  536 + assert_equal c, SpamNotification.marked_as_ham
  537 + end
  538 +
  539 + should 'ignore spam when constructing threads' do
  540 + original = create_comment
  541 + response = create_comment(:reply_of_id => original.id)
  542 + original.spam!
  543 +
  544 + assert_equivalent [response], Comment.without_spam.as_thread
  545 + end
  546 +
  547 +
  548 + should 'store User-Agent' do
  549 + c = Comment.new(:user_agent => 'foo')
  550 + assert_equal 'foo', c.user_agent
  551 + end
  552 +
  553 + should 'store referrer' do
  554 + c = Comment.new(:referrer => 'bar')
  555 + assert_equal 'bar', c.referrer
  556 + end
  557 +
  558 + private
  559 +
  560 + def create_comment(args = {})
  561 + owner = create_user('testuser').person
  562 + article = create(TextileArticle, :profile_id => owner.id)
  563 + create(Comment, { :name => 'foo', :email => 'foo@example.com', :source => article }.merge(args))
  564 + end
  565 +
425 end 566 end
test/unit/content_viewer_helper_test.rb
@@ -61,9 +61,9 @@ class ContentViewerHelperTest &lt; ActiveSupport::TestCase @@ -61,9 +61,9 @@ class ContentViewerHelperTest &lt; ActiveSupport::TestCase
61 end 61 end
62 62
63 should 'count total of comments from post' do 63 should 'count total of comments from post' do
64 - article = TextileArticle.new(:name => 'first post for test', :body => 'first post for test', :profile => profile) 64 + article = fast_create(TextileArticle)
65 article.stubs(:url).returns({}) 65 article.stubs(:url).returns({})
66 - article.stubs(:comments).returns([Comment.new(:author => profile, :title => 'test', :body => 'test')]) 66 + article.comments.create!(:author => profile, :title => 'test', :body => 'test')
67 result = link_to_comments(article) 67 result = link_to_comments(article)
68 assert_match /One comment/, result 68 assert_match /One comment/, result
69 end 69 end
test/unit/person_test.rb
@@ -3,10 +3,6 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39; @@ -3,10 +3,6 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 class PersonTest < ActiveSupport::TestCase 3 class PersonTest < ActiveSupport::TestCase
4 fixtures :profiles, :users, :environments 4 fixtures :profiles, :users, :environments
5 5
6 - def teardown  
7 - Thread.current[:enabled_plugins] = nil  
8 - end  
9 -  
10 def test_person_must_come_form_the_cration_of_an_user 6 def test_person_must_come_form_the_cration_of_an_user
11 p = Person.new(:environment => Environment.default, :name => 'John', :identifier => 'john') 7 p = Person.new(:environment => Environment.default, :name => 'John', :identifier => 'john')
12 assert !p.valid? 8 assert !p.valid?
test/unit/plugin_hot_spot_test.rb 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class PluginHotSpotTest < ActiveSupport::TestCase
  4 +
  5 + class Client
  6 + include Noosfero::Plugin::HotSpot
  7 + end
  8 +
  9 + def setup
  10 + @client = Client.new
  11 + @client.stubs(:environment).returns(Environment.new)
  12 + end
  13 +
  14 + should 'instantiate only once' do
  15 + assert_same @client.plugins, @client.plugins
  16 + end
  17 +
  18 +end
test/unit/plugin_manager_test.rb
@@ -8,10 +8,16 @@ class PluginManagerTest &lt; ActiveSupport::TestCase @@ -8,10 +8,16 @@ class PluginManagerTest &lt; ActiveSupport::TestCase
8 @controller.stubs(:profile).returns() 8 @controller.stubs(:profile).returns()
9 @controller.stubs(:request).returns() 9 @controller.stubs(:request).returns()
10 @controller.stubs(:response).returns() 10 @controller.stubs(:response).returns()
11 - @controller.stubs(:environment).returns(@environment)  
12 @controller.stubs(:params).returns() 11 @controller.stubs(:params).returns()
  12 + @manager = Noosfero::Plugin::Manager.new(@environment, @controller)
13 end 13 end
14 attr_reader :environment 14 attr_reader :environment
  15 + attr_reader :manager
  16 +
  17 + should 'give access to environment and context' do
  18 + assert_same @environment, @manager.environment
  19 + assert_same @controller, @manager.context
  20 + end
15 21
16 should 'return the intersection between environment\'s enabled plugins and system available plugins' do 22 should 'return the intersection between environment\'s enabled plugins and system available plugins' do
17 class Plugin1 < Noosfero::Plugin; end; 23 class Plugin1 < Noosfero::Plugin; end;
@@ -20,7 +26,6 @@ class PluginManagerTest &lt; ActiveSupport::TestCase @@ -20,7 +26,6 @@ class PluginManagerTest &lt; ActiveSupport::TestCase
20 class Plugin4 < Noosfero::Plugin; end; 26 class Plugin4 < Noosfero::Plugin; end;
21 environment.stubs(:enabled_plugins).returns([Plugin1.to_s, Plugin2.to_s, Plugin4.to_s]) 27 environment.stubs(:enabled_plugins).returns([Plugin1.to_s, Plugin2.to_s, Plugin4.to_s])
22 Noosfero::Plugin.stubs(:all).returns([Plugin1.to_s, Plugin3.to_s, Plugin4.to_s]) 28 Noosfero::Plugin.stubs(:all).returns([Plugin1.to_s, Plugin3.to_s, Plugin4.to_s])
23 - manager = Noosfero::Plugin::Manager.new(@controller)  
24 plugins = manager.enabled_plugins.map { |instance| instance.class.to_s } 29 plugins = manager.enabled_plugins.map { |instance| instance.class.to_s }
25 assert_equal [Plugin1.to_s, Plugin4.to_s], plugins 30 assert_equal [Plugin1.to_s, Plugin4.to_s], plugins
26 end 31 end
@@ -49,7 +54,6 @@ class PluginManagerTest &lt; ActiveSupport::TestCase @@ -49,7 +54,6 @@ class PluginManagerTest &lt; ActiveSupport::TestCase
49 54
50 p1 = Plugin1.new 55 p1 = Plugin1.new
51 p2 = Plugin2.new 56 p2 = Plugin2.new
52 - manager = Noosfero::Plugin::Manager.new(@controller)  
53 57
54 assert_equal [p1.random_event, p2.random_event], manager.dispatch(:random_event) 58 assert_equal [p1.random_event, p2.random_event], manager.dispatch(:random_event)
55 end 59 end
test/unit/profile_test.rb
@@ -3,10 +3,6 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39; @@ -3,10 +3,6 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 class ProfileTest < ActiveSupport::TestCase 3 class ProfileTest < ActiveSupport::TestCase
4 fixtures :profiles, :environments, :users, :roles, :domains 4 fixtures :profiles, :environments, :users, :roles, :domains
5 5
6 - def teardown  
7 - Thread.current[:enabled_plugins] = nil  
8 - end  
9 -  
10 def test_identifier_validation 6 def test_identifier_validation
11 p = Profile.new 7 p = Profile.new
12 p.valid? 8 p.valid?
@@ -1834,16 +1830,6 @@ class ProfileTest &lt; ActiveSupport::TestCase @@ -1834,16 +1830,6 @@ class ProfileTest &lt; ActiveSupport::TestCase
1834 end 1830 end
1835 1831
1836 should 'merge members of plugins to original members' do 1832 should 'merge members of plugins to original members' do
1837 - original_community = fast_create(Community)  
1838 - community1 = fast_create(Community, :identifier => 'community1')  
1839 - community2 = fast_create(Community, :identifier => 'community2')  
1840 - original_member = fast_create(Person)  
1841 - plugin1_member = fast_create(Person)  
1842 - plugin2_member = fast_create(Person)  
1843 - original_community.add_member(original_member)  
1844 - community1.add_member(plugin1_member)  
1845 - community2.add_member(plugin2_member)  
1846 -  
1847 class Plugin1 < Noosfero::Plugin 1833 class Plugin1 < Noosfero::Plugin
1848 def organization_members(profile) 1834 def organization_members(profile)
1849 Person.members_of(Community.find_by_identifier('community1')) 1835 Person.members_of(Community.find_by_identifier('community1'))
@@ -1855,8 +1841,18 @@ class ProfileTest &lt; ActiveSupport::TestCase @@ -1855,8 +1841,18 @@ class ProfileTest &lt; ActiveSupport::TestCase
1855 Person.members_of(Community.find_by_identifier('community2')) 1841 Person.members_of(Community.find_by_identifier('community2'))
1856 end 1842 end
1857 end 1843 end
  1844 + Environment.default.enable_plugin(Plugin1)
  1845 + Environment.default.enable_plugin(Plugin2)
1858 1846
1859 - original_community.stubs(:enabled_plugins).returns([Plugin1.new, Plugin2.new]) 1847 + original_community = fast_create(Community)
  1848 + community1 = fast_create(Community, :identifier => 'community1')
  1849 + community2 = fast_create(Community, :identifier => 'community2')
  1850 + original_member = fast_create(Person)
  1851 + plugin1_member = fast_create(Person)
  1852 + plugin2_member = fast_create(Person)
  1853 + original_community.add_member(original_member)
  1854 + community1.add_member(plugin1_member)
  1855 + community2.add_member(plugin2_member)
1860 1856
1861 assert_includes original_community.members, original_member 1857 assert_includes original_community.members, original_member
1862 assert_includes original_community.members, plugin1_member 1858 assert_includes original_community.members, plugin1_member
vendor/plugins/acts_as_solr_reloaded/lib/tasks/solr.rake
@@ -20,7 +20,16 @@ namespace :solr do @@ -20,7 +20,16 @@ namespace :solr do
20 20
21 tmpdir = [ '/var/tmp', '/tmp' ].find { |d| File.exists?(d) } 21 tmpdir = [ '/var/tmp', '/tmp' ].find { |d| File.exists?(d) }
22 Dir.chdir tmpdir do 22 Dir.chdir tmpdir do
23 - sh "wget -c #{SOLR_URL}" 23 + skip_download = false
  24 + if File.exists?(SOLR_FILENAME)
  25 + sh "echo \"#{SOLR_MD5SUM} #{SOLR_FILENAME}\" | md5sum -c -" do |ok, res|
  26 + skip_download = ok
  27 + end
  28 + end
  29 +
  30 + unless skip_download
  31 + sh "wget -c #{SOLR_URL}"
  32 + end
24 33
25 sh "echo \"#{SOLR_MD5SUM} #{SOLR_FILENAME}\" | md5sum -c -" do |ok, res| 34 sh "echo \"#{SOLR_MD5SUM} #{SOLR_FILENAME}\" | md5sum -c -" do |ok, res|
26 abort "MD5SUM do not match" if !ok 35 abort "MD5SUM do not match" if !ok
vendor/plugins/acts_as_solr_reloaded/test/db/connections/mysql/connection.rb
1 -require 'logger'  
2 -ActiveRecord::Base.logger = Logger.new("debug.log")  
3 -  
4 ActiveRecord::Base.establish_connection( 1 ActiveRecord::Base.establish_connection(
5 :adapter => "mysql", 2 :adapter => "mysql",
6 :username => "root", 3 :username => "root",
vendor/plugins/acts_as_solr_reloaded/test/db/connections/sqlite/connection.rb
1 -require 'logger'  
2 -ActiveRecord::Base.logger = Logger.new("debug.log")  
3 -  
4 ActiveRecord::Base.establish_connection( 1 ActiveRecord::Base.establish_connection(
5 :adapter => "sqlite3", 2 :adapter => "sqlite3",
6 :encoding => "utf8", 3 :encoding => "utf8",
vendor/plugins/rakismet/CHANGELOG 0 → 100644
@@ -0,0 +1,35 @@ @@ -0,0 +1,35 @@
  1 +* Clean up gemspec and load paths [Steven Harman]
  2 +* Add Akismet is_test param [Steven Harman]
  3 +* Add Akismet user_role attribute [Steven Harman]
  4 += 1.2.1
  5 +* Fix deprecated usage of HTTPResponse for Ruby 1.9.3 [Leonid Shevtsov]
  6 += 1.2.0
  7 +* Rakismet attribute mappings are now inheritable
  8 += 1.1.2
  9 +* Explicitly load version
  10 += 1.1.1
  11 +* Fix SafeBuffer error under Rails 3.0.8 and 3.0.9 [Brandon Ferguson]
  12 +* Readme cleanup [Zeke Sikelianos]
  13 +* Drop Jeweler in favor of Bundler's gem tasks
  14 += 1.1.0
  15 +* Add HTTP Proxy support [Francisco Trindade]
  16 += 1.0.1
  17 +* Fix hash access for Ruby 1.9 [Alex Crichton]
  18 += 1.0.0
  19 +* Update for Rails 3
  20 +* Remove filters and replace with middleware
  21 +* Remove initializers and replace with Railtie
  22 += 0.4.0
  23 +* Rakismet is no longer injected into ActiveRecord or ActionController
  24 +* API changes to support newly decoupled modules
  25 +* Use Jeweler to manage gemspec
  26 += 0.3.6
  27 +* Allow attributes to fall through to methods or AR attributes
  28 += 0.3.5
  29 +* Added gemspec and rails/init.rb so rakismet can work as a gem [Michael Air]
  30 +* Added generator template and manifest [Michael Air]
  31 += 0.3.0
  32 +* Abstract out Rakismet version string
  33 +* Set default Akismet Host
  34 +* Abstract out the Akismet host [Mike Burns]
  35 +* Started keeping a changelog :P
vendor/plugins/rakismet/Gemfile 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +source "http://rubygems.org"
  2 +
  3 +gemspec
vendor/plugins/rakismet/MIT-LICENSE 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +Copyright (c) 2008 Josh French
  2 +
  3 +Permission is hereby granted, free of charge, to any person obtaining
  4 +a copy of this software and associated documentation files (the
  5 +"Software"), to deal in the Software without restriction, including
  6 +without limitation the rights to use, copy, modify, merge, publish,
  7 +distribute, sublicense, and/or sell copies of the Software, and to
  8 +permit persons to whom the Software is furnished to do so, subject to
  9 +the following conditions:
  10 +
  11 +The above copyright notice and this permission notice shall be
  12 +included in all copies or substantial portions of the Software.
  13 +
  14 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
vendor/plugins/rakismet/README.md 0 → 100644
@@ -0,0 +1,229 @@ @@ -0,0 +1,229 @@
  1 +Rakismet
  2 +========
  3 +
  4 +**Akismet** (<http://akismet.com/>) is a collaborative spam filtering service.
  5 +**Rakismet** is easy Akismet integration with Rails and rack apps. TypePad's
  6 +AntiSpam service and generic Akismet endpoints are supported.
  7 +
  8 +Compatibility
  9 +=============
  10 +
  11 +**Rakismet >= 1.0.0** work with Rails 3 and other Rack-based frameworks.
  12 +
  13 +**Rakismet <= 0.4.2** is compatible with Rails 2.
  14 +
  15 +Getting Started
  16 +===============
  17 +
  18 +Once you've installed the Rakismet gem and added it to your application's Gemfile,
  19 +you'll need an API key. Head on over to http://akismet.com/signup/ and sign up
  20 +for a new username.
  21 +
  22 +Configure the Rakismet key and the URL of your application by setting the following
  23 +in application.rb:
  24 +
  25 +```ruby
  26 +config.rakismet.key = 'your wordpress key'
  27 +config.rakismet.url = 'http://yourdomain.com/'
  28 +```
  29 +
  30 +or an initializer, for example `config/initializers/rakismet.rb`:
  31 +
  32 +```ruby
  33 +YourApp::Application.config.rakismet.key = 'your wordpress key'
  34 +YourApp::Application.config.rakismet.url = 'http://yourdomain.com/'
  35 +```
  36 +
  37 +If you wish to use another Akismet-compatible API provider such as TypePad's
  38 +antispam service, you'll also need to set `config.rakismet.host` to your service
  39 +provider's endpoint.
  40 +
  41 +If you want to use a proxy to access akismet (i.e. your application is behind a
  42 +firewall), set the proxy_host and proxy_port option.
  43 +
  44 +```ruby
  45 +config.rakismet.proxy_host = 'http://yourdomain.com/'
  46 +config.rakismet.proxy_port = '8080'
  47 +```
  48 +
  49 +Checking For Spam
  50 +-----------------
  51 +
  52 +First, introduce Rakismet to your model:
  53 +
  54 +```ruby
  55 +class Comment
  56 + include Rakismet::Model
  57 +end
  58 +```
  59 +
  60 +With Rakismet mixed in to your model, you'll get three instance methods for interacting with
  61 +Akismet:
  62 +
  63 + * `spam?` submits the comment to Akismet and returns true if Akismet thinks the comment is spam, false if not.
  64 + * `ham!` resubmits a valid comment that Akismet erroneously marked as spam (marks it as a false positive.)
  65 + * `spam!` resubmits a spammy comment that Akismet missed (marks it as a false negative.)
  66 +
  67 +The `ham!` and `spam!` methods will change the value of `spam?` but their
  68 +primary purpose is to send feedback to Akismet. The service works best when you
  69 +help correct the rare mistake; please consider using these methods if you're
  70 +moderating comments or otherwise reviewing the Akismet responses.
  71 +
  72 +Configuring Your Model
  73 +----------------------
  74 +
  75 +Rakismet sends the following information to the spam-hungry robots at Akismet:
  76 +
  77 + author : name submitted with the comment
  78 + author_url : URL submitted with the comment
  79 + author_email : email submitted with the comment
  80 + comment_type : Defaults to comment but you can set it to trackback, pingback, or something more appropriate
  81 + content : the content submitted
  82 + permalink : the permanent URL for the entry the comment belongs to
  83 + user_ip : IP address used to submit this comment
  84 + user_agent : user agent string
  85 + referrer : referring URL (note the spelling)
  86 +
  87 +By default, Rakismet just looks for attributes or methods on your class that
  88 +match these names. You don't have to have accessors that match these exactly,
  89 +however. If yours differ, just tell Rakismet what to call them:
  90 +
  91 +```ruby
  92 +class Comment
  93 + include Rakismet::Model
  94 + attr_accessor :commenter_name, :commenter_email
  95 + rakismet_attrs :author => :commenter_name, :author_email => :commenter_email
  96 +end
  97 +```
  98 +
  99 +Or you can pass in a proc, to access associations:
  100 +
  101 +```ruby
  102 +class Comment < ActiveRecord::Base
  103 + include Rakismet::Model
  104 + belongs_to :author
  105 + rakismet_attrs :author => proc { author.name },
  106 + :author_email => proc { author.email }
  107 +end
  108 +```
  109 +
  110 +You can even hard-code specific fields:
  111 +
  112 +```ruby
  113 +class Trackback
  114 + include Rakismet::Model
  115 + rakismet_attrs :comment_type => "trackback"
  116 +end
  117 +```
  118 +
  119 +Optional Request Variables
  120 +--------------------------
  121 +
  122 +Akismet wants certain information about the request environment: remote IP, the
  123 +user agent string, and the HTTP referer when available. Normally, Rakismet
  124 +asks your model for these. Storing this information on your model allows you to
  125 +call the `spam?` method at a later time. For instance, maybe you're storing your
  126 +comments in an administrative queue or processing them with a background job.
  127 +
  128 +You don't need to have these three attributes on your model, however. If you
  129 +choose to omit them, Rakismet will instead look at the current request (if one
  130 +exists) and take the values from the request object instead.
  131 +
  132 +This means that if you are **not storing the request variables**, you must call
  133 +`spam?` from within the controller action that handles comment submissions. That
  134 +way the IP, user agent, and referer will belong to the person submitting the
  135 +comment. If you're not storing the request variables and you call `spam?` at a
  136 +later time, the request information will be missing or invalid and Akismet won't
  137 +be able to do its job properly.
  138 +
  139 +If you've decided to handle the request variables yourself, you can disable the
  140 +middleware responsible for tracking the request information by adding this to
  141 +your app initialization:
  142 +
  143 +```ruby
  144 +config.rakismet.use_middleware = false
  145 +```
  146 +
  147 +Testing
  148 +-------
  149 +
  150 +Rakismet can be configued to tell Akismet that it should operate in test mode -
  151 +so Akismet will not change its behavior based on any test API calls, meaning
  152 +they will have no training effect. That means your tests can be somewhat
  153 +repeatable in the sense that one test won't influence subsequent calls.
  154 +
  155 +You can configure Rakismet for test mode via application.rb:
  156 +
  157 +```ruby
  158 +config.rakismet.test = false # <- default
  159 +config.rakismet.test = true
  160 +```
  161 +
  162 +Or via an initializer:
  163 +
  164 +```ruby
  165 +YourApp::Application.config.rakismet.test = false # <- default
  166 +YourApp::Application.config.rakismet.test = true
  167 +```
  168 +
  169 +**NOTE**: When running in Rails, Rakismet will run in test mode when your Rails
  170 +environment is `test` or `development`, unless explictly configured otherwise.
  171 +Outside of Rails Rakismet defaults to test mode turned **off**.
  172 +
  173 +
  174 +Verifying Responses
  175 +-------------------
  176 +
  177 +If you want to see what's happening behind the scenes, after you call one of
  178 +`@comment.spam?`, `@comment.spam!` or `@comment.ham!` you can check
  179 +`@comment.akismet_response`.
  180 +
  181 +This will contain the last response from the Akismet server. In the case of
  182 +`spam?` it should be `true` or `false.` For `spam!` and `ham!` it should be
  183 +`Feedback received.` If Akismet returned an error instead (e.g. if you left out
  184 +some required information) this will contain the error message.
  185 +
  186 +FAQ
  187 +===
  188 +
  189 +Why does Akismet think all of my test data is spam?
  190 +---------------------------------------------------
  191 +
  192 +Akismet needs enough information to decide if your test data is spam or not.
  193 +Try to supply as much as possible, especially the author name and request
  194 +variables.
  195 +
  196 +How can I simulate a spam submission?
  197 +-------------------------------------
  198 +
  199 +Most people have the opposite problem, where Akismet doesn't think anything is
  200 +spam. The only guaranteed way to trigger a positive spam response is to set the
  201 +comment author to "viagra-test-123".
  202 +
  203 +If you've done this and `spam?` is still returning false, you're probably
  204 +missing the user IP or one of the key/url config variables. One way to check is
  205 +to call `@comment.akismet_response`. If you are missing a required field or
  206 +there was another error, this will hold the Akismet error message. If your comment
  207 +was processed normally, this value will simply be `true` or `false`.
  208 +
  209 +Can I use Rakismet with a different ORM or framework?
  210 +-----------------------------------------------------
  211 +
  212 +Sure. Rakismet doesn't care what your persistence layer is. It will work with
  213 +Datamapper, a NoSQL store, or whatever next month's DB flavor is.
  214 +
  215 +Rakismet also has no dependencies on Rails or any of its components, and only
  216 +uses a small Rack middleware object to do some of its magic. Depending on your
  217 +framework, you may have to modify this slightly and/or manually place it in your
  218 +stack.
  219 +
  220 +You'll also need to set a few config variables by hand. Instead of
  221 +`config.rakismet.key`, `config.rakismet.url`, and `config.rakismet.host`, set
  222 +these values directly with `Rakismet.key`, `Rakismet.url`, and `Rakismet.host`.
  223 +
  224 +---------------------------------------------------------------------------
  225 +
  226 +If you have any implementation or usage questions, don't hesitate to get in
  227 +touch: josh@vitamin-j.com.
  228 +
  229 +Copyright (c) 2008 Josh French, released under the MIT license
vendor/plugins/rakismet/Rakefile 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +require 'bundler'
  2 +Bundler::GemHelper.install_tasks
  3 +
  4 +require 'rspec/core/rake_task'
  5 +RSpec::Core::RakeTask.new do |spec|
  6 + spec.rspec_opts = ["--color", "--format progress"]
  7 +end
  8 +
  9 +task :default => :spec
0 \ No newline at end of file 10 \ No newline at end of file
vendor/plugins/rakismet/lib/rakismet.rb 0 → 100644
@@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
  1 +require 'net/http'
  2 +require 'uri'
  3 +require 'cgi'
  4 +require 'yaml'
  5 +
  6 +require 'rakismet/model'
  7 +require 'rakismet/middleware'
  8 +require 'rakismet/version'
  9 +
  10 +if defined?(Rails) && Rails::VERSION::STRING > '3.2.0'
  11 + require 'rakismet/railtie.rb'
  12 + $stderr.puts "W: on Rails 3, this vendored version of rakismet should be replaced by a proper dependency"
  13 +end
  14 +
  15 +module Rakismet
  16 + Request = Struct.new(:user_ip, :user_agent, :referrer)
  17 + Undefined = Class.new(NameError)
  18 +
  19 + class << self
  20 + attr_accessor :key, :url, :host, :proxy_host, :proxy_port, :test
  21 +
  22 + def request
  23 + @request ||= Request.new
  24 + end
  25 +
  26 + def set_request_vars(env)
  27 + request.user_ip, request.user_agent, request.referrer =
  28 + env['REMOTE_ADDR'], env['HTTP_USER_AGENT'], env['HTTP_REFERER']
  29 + end
  30 +
  31 + def clear_request
  32 + @request = Request.new
  33 + end
  34 +
  35 + def headers
  36 + @headers ||= begin
  37 + user_agent = "Rakismet/#{Rakismet::VERSION}"
  38 + user_agent = "Rails/#{Rails.version} | " + user_agent if defined?(Rails)
  39 + { 'User-Agent' => user_agent, 'Content-Type' => 'application/x-www-form-urlencoded' }
  40 + end
  41 + end
  42 +
  43 + def validate_key
  44 + validate_config
  45 + akismet = URI.parse(verify_url)
  46 + response = Net::HTTP::Proxy(proxy_host, proxy_port).start(akismet.host) do |http|
  47 + data = "key=#{Rakismet.key}&blog=#{Rakismet.url}"
  48 + http.post(akismet.path, data, Rakismet.headers)
  49 + end
  50 + @valid_key = (response.body == 'valid')
  51 + end
  52 +
  53 + def valid_key?
  54 + @valid_key == true
  55 + end
  56 +
  57 + def akismet_call(function, args={})
  58 + validate_config
  59 + args.merge!(:blog => Rakismet.url, :is_test => Rakismet.test_mode)
  60 + akismet = URI.parse(call_url(function))
  61 + response = Net::HTTP::Proxy(proxy_host, proxy_port).start(akismet.host) do |http|
  62 + params = args.map do |k,v|
  63 + param = v.class < String ? v.to_str : v.to_s # for ActiveSupport::SafeBuffer and Nil, respectively
  64 + "#{k}=#{CGI.escape(param)}"
  65 + end
  66 + http.post(akismet.path, params.join('&'), Rakismet.headers)
  67 + end
  68 + response.body
  69 + end
  70 +
  71 + protected
  72 +
  73 + def verify_url
  74 + "http://#{Rakismet.host}/1.1/verify-key"
  75 + end
  76 +
  77 + def call_url(function)
  78 + "http://#{Rakismet.key}.#{Rakismet.host}/1.1/#{function}"
  79 + end
  80 +
  81 + def validate_config
  82 + raise Undefined, "Rakismet.key is not defined" if Rakismet.key.nil? || Rakismet.key.empty?
  83 + raise Undefined, "Rakismet.url is not defined" if Rakismet.url.nil? || Rakismet.url.empty?
  84 + raise Undefined, "Rakismet.host is not defined" if Rakismet.host.nil? || Rakismet.host.empty?
  85 + end
  86 +
  87 + def test_mode
  88 + test ? 1 : 0
  89 + end
  90 + end
  91 +
  92 +end
vendor/plugins/rakismet/lib/rakismet/middleware.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +module Rakismet
  2 + class Middleware
  3 +
  4 + def initialize(app)
  5 + @app = app
  6 + end
  7 +
  8 + def call(env)
  9 + Rakismet.set_request_vars(env)
  10 + response = @app.call(env)
  11 + Rakismet.clear_request
  12 + response
  13 + end
  14 +
  15 + end
  16 +end
vendor/plugins/rakismet/lib/rakismet/model.rb 0 → 100644
@@ -0,0 +1,86 @@ @@ -0,0 +1,86 @@
  1 +module Rakismet
  2 + module Model
  3 +
  4 + def self.included(base)
  5 + base.class_eval do
  6 + attr_accessor :akismet_response
  7 + class << self; attr_accessor :akismet_attrs; end
  8 + extend ClassMethods
  9 + include InstanceMethods
  10 + self.rakismet_attrs
  11 + end
  12 + end
  13 +
  14 + module ClassMethods
  15 + def rakismet_attrs(args={})
  16 + self.akismet_attrs ||= {}
  17 + [:comment_type, :author, :author_url, :author_email, :content, :user_role].each do |field|
  18 + # clunky, but throwing around +type+ will break your heart
  19 + fieldname = field.to_s =~ %r(^comment_) ? field : "comment_#{field}".intern
  20 + self.akismet_attrs[fieldname] = args.delete(field) || field
  21 + end
  22 + [:user_ip, :user_agent, :referrer].each do |field|
  23 + self.akismet_attrs[field] = args.delete(field) || field
  24 + end
  25 + args.each_pair do |f,v|
  26 + self.akismet_attrs[f] = v
  27 + end
  28 + end
  29 +
  30 + def inherited(subclass)
  31 + super
  32 + subclass.rakismet_attrs akismet_attrs.dup
  33 + end
  34 + end
  35 +
  36 + module InstanceMethods
  37 + def spam?
  38 + if instance_variable_defined? :@_spam
  39 + @_spam
  40 + else
  41 + data = akismet_data
  42 + self.akismet_response = Rakismet.akismet_call('comment-check', data)
  43 + @_spam = self.akismet_response == 'true'
  44 + end
  45 + end
  46 +
  47 + def spam!
  48 + Rakismet.akismet_call('submit-spam', akismet_data)
  49 + @_spam = true
  50 + end
  51 +
  52 + def ham!
  53 + Rakismet.akismet_call('submit-ham', akismet_data)
  54 + @_spam = false
  55 + end
  56 +
  57 + private
  58 +
  59 + def akismet_data
  60 + akismet = self.class.akismet_attrs.keys.inject({}) do |data,attr|
  61 + mapped_field = self.class.akismet_attrs[attr]
  62 + data.merge attr => if mapped_field.is_a?(Proc)
  63 + instance_eval(&mapped_field)
  64 + elsif !mapped_field.nil? && respond_to?(mapped_field)
  65 + send(mapped_field)
  66 + elsif not [:comment_type, :author, :author_email,
  67 + :author_url, :content, :user_role,
  68 + :user_ip, :referrer,
  69 + :user_agent].include?(mapped_field)
  70 + # we've excluded any fields that appear to
  71 + # have their default unmapped values
  72 + mapped_field
  73 + elsif respond_to?(attr)
  74 + send(attr)
  75 + elsif Rakismet.request.respond_to?(attr)
  76 + Rakismet.request.send(attr)
  77 + end
  78 + end
  79 + akismet.delete_if { |k,v| v.nil? || v.empty? }
  80 + akismet[:comment_type] ||= 'comment'
  81 + akismet
  82 + end
  83 + end
  84 +
  85 + end
  86 +end
vendor/plugins/rakismet/lib/rakismet/railtie.rb 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 +require 'rails'
  2 +require 'rakismet'
  3 +
  4 +module Rakismet
  5 + class Railtie < Rails::Railtie
  6 +
  7 + config.rakismet = ActiveSupport::OrderedOptions.new
  8 + config.rakismet.host = 'rest.akismet.com'
  9 + config.rakismet.use_middleware = true
  10 +
  11 + initializer 'rakismet.setup', :after => :load_config_initializers do |app|
  12 + Rakismet.key = app.config.rakismet[:key]
  13 + Rakismet.url = app.config.rakismet[:url]
  14 + Rakismet.host = app.config.rakismet[:host]
  15 + Rakismet.proxy_host = app.config.rakismet[:proxy_host]
  16 + Rakismet.proxy_port = app.config.rakismet[:proxy_port]
  17 + Rakismet.test = app.config.rakismet.fetch(:test) { Rails.env.test? || Rails.env.development? }
  18 + app.middleware.use Rakismet::Middleware if app.config.rakismet.use_middleware
  19 + end
  20 +
  21 + end
  22 +end
vendor/plugins/rakismet/lib/rakismet/version.rb 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +module Rakismet
  2 + VERSION = "1.2.1"
  3 +end
vendor/plugins/rakismet/rakismet.gemspec 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +# -*- encoding: utf-8 -*-
  2 +require File.expand_path('../lib/rakismet/version', __FILE__)
  3 +
  4 +Gem::Specification.new do |s|
  5 + s.name = "rakismet"
  6 + s.version = Rakismet::VERSION
  7 + s.platform = Gem::Platform::RUBY
  8 + s.authors = ["Josh French"]
  9 + s.email = "josh@vitamin-j.com"
  10 + s.homepage = "http://github.com/joshfrench/rakismet"
  11 + s.summary = "Akismet and TypePad AntiSpam integration for Rails."
  12 + s.description = "Rakismet is the easiest way to integrate Akismet or TypePad's AntiSpam into your Rails app."
  13 + s.date = "2012-04-22"
  14 +
  15 + s.rubyforge_project = "rakismet"
  16 + s.add_development_dependency "rake"
  17 + s.add_development_dependency "rspec", "~> 2.11"
  18 +
  19 + s.files = `git ls-files`.split("\n")
  20 + s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
  21 + s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
  22 + s.require_paths = ["lib"]
  23 + s.extra_rdoc_files = ["README.md"]
  24 +end
  25 +
vendor/plugins/rakismet/spec/.rspec 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +--color
vendor/plugins/rakismet/spec/models/block_params_spec.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +require 'spec_helper'
  2 +
  3 +PROC = proc { author.reverse }
  4 +
  5 +class BlockAkismetModel
  6 + include Rakismet::Model
  7 + rakismet_attrs :author => PROC
  8 +end
  9 +
  10 +describe BlockAkismetModel do
  11 +
  12 + before do
  13 + @block = BlockAkismetModel.new
  14 + comment_attrs.each_pair { |k,v| @block.stub!(k).and_return(v) }
  15 + end
  16 +
  17 + it "should accept a block" do
  18 + BlockAkismetModel.akismet_attrs[:comment_author].should eql(PROC)
  19 + end
  20 +
  21 + it "should eval block with self = instance" do
  22 + data = @block.send(:akismet_data)
  23 + data[:comment_author].should eql(comment_attrs[:author].reverse)
  24 + end
  25 +end
vendor/plugins/rakismet/spec/models/custom_params_spec.rb 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +require 'spec_helper'
  2 +
  3 +MAPPED_PARAMS = { :comment_type => :type2, :author => :author2, :content => :content2,
  4 + :author_email => :author_email2, :author_url => :author_url2,
  5 + :user_role => :user_role2 }
  6 +
  7 +class CustomAkismetModel
  8 + include Rakismet::Model
  9 + rakismet_attrs MAPPED_PARAMS.dup
  10 +end
  11 +
  12 +
  13 +describe CustomAkismetModel do
  14 + it "should override default mappings" do
  15 + [:comment_type, :author, :author_url, :author_email, :content, :user_role].each do |field|
  16 + fieldname = field.to_s =~ %r(^comment_) ? field : "comment_#{field}".intern
  17 + CustomAkismetModel.akismet_attrs[fieldname].should eql(MAPPED_PARAMS[field])
  18 + end
  19 + end
  20 +end
vendor/plugins/rakismet/spec/models/extended_params_spec.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +require 'spec_helper'
  2 +
  3 +EXTRA = { :extra => :extra, :another => lambda { } }
  4 +
  5 +class ExtendedAkismetModel
  6 + include Rakismet::Model
  7 + rakismet_attrs EXTRA.dup
  8 +end
  9 +
  10 +describe ExtendedAkismetModel do
  11 + it "should include additional attributes" do
  12 + [:extra, :another].each do |field|
  13 + ExtendedAkismetModel.akismet_attrs[field].should eql(EXTRA[field])
  14 + end
  15 + end
  16 +end
vendor/plugins/rakismet/spec/models/rakismet_model_spec.rb 0 → 100644
@@ -0,0 +1,98 @@ @@ -0,0 +1,98 @@
  1 +require 'spec_helper'
  2 +
  3 +describe AkismetModel do
  4 +
  5 + before do
  6 + @model = AkismetModel.new
  7 + comment_attrs.each_pair { |k,v| @model.stub!(k).and_return(v) }
  8 + end
  9 +
  10 + it "should have default mappings" do
  11 + [:comment_type, :author, :author_email, :author_url, :content, :user_role].each do |field|
  12 + fieldname = field.to_s =~ %r(^comment_) ? field : "comment_#{field}".intern
  13 + AkismetModel.akismet_attrs[fieldname].should eql(field)
  14 + end
  15 + end
  16 +
  17 + it "should have request mappings" do
  18 + [:user_ip, :user_agent, :referrer].each do |field|
  19 + AkismetModel.akismet_attrs[field].should eql(field)
  20 + end
  21 + end
  22 +
  23 + it "should populate comment type" do
  24 + @model.send(:akismet_data)[:comment_type].should == comment_attrs[:comment_type]
  25 + end
  26 +
  27 + describe ".spam?" do
  28 +
  29 + it "should use request variables from Rakismet.request if absent in model" do
  30 + [:user_ip, :user_agent, :referrer].each do |field|
  31 + @model.should_not respond_to(:field)
  32 + end
  33 + Rakismet.stub!(:request).and_return(request)
  34 + Rakismet.should_receive(:akismet_call).
  35 + with('comment-check', akismet_attrs.merge(:user_ip => '127.0.0.1',
  36 + :user_agent => 'RSpec',
  37 + :referrer => 'http://test.host/referrer'))
  38 + @model.spam?
  39 + end
  40 +
  41 + it "should cache result of #spam?" do
  42 + Rakismet.should_receive(:akismet_call).once
  43 + @model.spam?
  44 + @model.spam?
  45 + end
  46 +
  47 + it "should be true if comment is spam" do
  48 + Rakismet.stub!(:akismet_call).and_return('true')
  49 + @model.should be_spam
  50 + end
  51 +
  52 + it "should be false if comment is not spam" do
  53 + Rakismet.stub!(:akismet_call).and_return('false')
  54 + @model.should_not be_spam
  55 + end
  56 +
  57 + it "should set akismet_response" do
  58 + Rakismet.stub!(:akismet_call).and_return('response')
  59 + @model.spam?
  60 + @model.akismet_response.should eql('response')
  61 + end
  62 +
  63 + it "should not throw an error if request vars are missing" do
  64 + Rakismet.stub!(:request).and_return(empty_request)
  65 + lambda { @model.spam? }.should_not raise_error(NoMethodError)
  66 + end
  67 + end
  68 +
  69 +
  70 + describe ".spam!" do
  71 + it "should call Base.akismet_call with submit-spam" do
  72 + Rakismet.should_receive(:akismet_call).with('submit-spam', akismet_attrs)
  73 + @model.spam!
  74 + end
  75 +
  76 + it "should mutate #spam?" do
  77 + Rakismet.stub!(:akismet_call)
  78 + @model.instance_variable_set(:@_spam, false)
  79 + @model.spam!
  80 + @model.should be_spam
  81 + end
  82 + end
  83 +
  84 + describe ".ham!" do
  85 + it "should call Base.akismet_call with submit-ham" do
  86 + Rakismet.should_receive(:akismet_call).with('submit-ham', akismet_attrs)
  87 + @model.ham!
  88 + end
  89 +
  90 + it "should mutate #spam?" do
  91 + Rakismet.stub!(:akismet_call)
  92 + @model.instance_variable_set(:@_spam, true)
  93 + @model.ham!
  94 + @model.should_not be_spam
  95 + end
  96 + end
  97 +
  98 +end
vendor/plugins/rakismet/spec/models/request_params_spec.rb 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +require 'spec_helper'
  2 +
  3 +class RequestParams
  4 + include Rakismet::Model
  5 + attr_accessor :user_ip, :user_agent, :referrer
  6 +end
  7 +
  8 +describe RequestParams do
  9 + before do
  10 + @model = RequestParams.new
  11 + attrs = comment_attrs(:user_ip => '192.168.0.1', :user_agent => 'Rakismet', :referrer => 'http://localhost/referrer')
  12 + attrs.each_pair { |k,v| @model.stub!(k).and_return(v) }
  13 + end
  14 +
  15 + it "should use local values even if Rakismet.request is populated" do
  16 + Rakismet.stub(:request).and_return(request)
  17 + Rakismet.should_receive(:akismet_call).
  18 + with('comment-check', akismet_attrs.merge(:user_ip => '192.168.0.1',
  19 + :user_agent => 'Rakismet',
  20 + :referrer => 'http://localhost/referrer'))
  21 + @model.spam?
  22 + end
  23 +end
vendor/plugins/rakismet/spec/models/subclass_spec.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +require 'spec_helper'
  2 +
  3 +class Subclass < AkismetModel
  4 +end
  5 +
  6 +describe Subclass do
  7 + it "should inherit parent's rakismet attrs" do
  8 + Subclass.akismet_attrs.should eql AkismetModel.akismet_attrs # key/value equality
  9 + end
  10 +
  11 + it "should get a new copy of parent's rakismet attrs" do
  12 + Subclass.akismet_attrs.should_not equal AkismetModel.akismet_attrs # object equality
  13 + end
  14 +end
vendor/plugins/rakismet/spec/rakismet_middleware_spec.rb 0 → 100644
@@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Rakismet::Middleware do
  4 +
  5 + let(:env) { { 'REMOTE_ADDR' => '127.0.0.1', 'HTTP_USER_AGENT' => 'RSpec', 'HTTP_REFERER' => 'http://test.host/referrer' } }
  6 + let(:app) { double(:app, :call => nil) }
  7 + let(:request) { double(:request).as_null_object }
  8 +
  9 + before do
  10 + @middleware = Rakismet::Middleware.new(app)
  11 + end
  12 +
  13 + it "should set set Rakismet.request variables" do
  14 + Rakismet.stub(:request).and_return(request)
  15 + request.should_receive(:user_ip=).with('127.0.0.1')
  16 + request.should_receive(:user_agent=).with('RSpec')
  17 + request.should_receive(:referrer=).with('http://test.host/referrer')
  18 + @middleware.call(env)
  19 + end
  20 +
  21 + it "should clear Rakismet.request after request is complete" do
  22 + @middleware.call(env)
  23 + Rakismet.request.user_ip.should be_nil
  24 + Rakismet.request.user_agent.should be_nil
  25 + Rakismet.request.referrer.should be_nil
  26 + end
  27 +end
vendor/plugins/rakismet/spec/rakismet_spec.rb 0 → 100644
@@ -0,0 +1,123 @@ @@ -0,0 +1,123 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Rakismet do
  4 +
  5 + def mock_response(body)
  6 + double(:response, :body => body)
  7 + end
  8 + let(:http) { double(:http, :post => mock_response('akismet response')) }
  9 +
  10 + before do
  11 + Rakismet.key = 'dummy-key'
  12 + Rakismet.url = 'test.localhost'
  13 + Rakismet.host = 'endpoint.localhost'
  14 + end
  15 +
  16 + describe "proxy host" do
  17 + it "should have proxy host and port as nil by default" do
  18 + Rakismet.proxy_host.should be_nil
  19 + Rakismet.proxy_port.should be_nil
  20 + end
  21 + end
  22 +
  23 + describe ".validate_config" do
  24 + it "should raise an error if key is not found" do
  25 + Rakismet.key = ''
  26 + lambda { Rakismet.send(:validate_config) }.should raise_error(Rakismet::Undefined)
  27 + end
  28 +
  29 + it "should raise an error if url is not found" do
  30 + Rakismet.url = ''
  31 + lambda { Rakismet.send(:validate_config) }.should raise_error(Rakismet::Undefined)
  32 + end
  33 +
  34 + it "should raise an error if host is not found" do
  35 + Rakismet.host = ''
  36 + lambda { Rakismet.send(:validate_config) }.should raise_error(Rakismet::Undefined)
  37 + end
  38 + end
  39 +
  40 + describe ".validate_key" do
  41 + before (:each) do
  42 + @proxy = mock(Net::HTTP)
  43 + Net::HTTP.stub!(:Proxy).and_return(@proxy)
  44 + end
  45 +
  46 + it "should use proxy host and port" do
  47 + Rakismet.proxy_host = 'proxy_host'
  48 + Rakismet.proxy_port = 'proxy_port'
  49 + @proxy.stub!(:start).and_return(mock_response('valid'))
  50 + Net::HTTP.should_receive(:Proxy).with('proxy_host', 'proxy_port').and_return(@proxy)
  51 + Rakismet.validate_key
  52 + end
  53 +
  54 + it "should set @@valid_key = true if key is valid" do
  55 + @proxy.stub!(:start).and_return(mock_response('valid'))
  56 + Rakismet.validate_key
  57 + Rakismet.valid_key?.should be_true
  58 + end
  59 +
  60 + it "should set @@valid_key = false if key is invalid" do
  61 + @proxy.stub!(:start).and_return(mock_response('invalid'))
  62 + Rakismet.validate_key
  63 + Rakismet.valid_key?.should be_false
  64 + end
  65 +
  66 + it "should build url with host" do
  67 + host = "api.antispam.typepad.com"
  68 + Rakismet.host = host
  69 + @proxy.should_receive(:start).with(host).and_yield(http)
  70 + Rakismet.validate_key
  71 + end
  72 + end
  73 +
  74 + describe ".akismet_call" do
  75 + before do
  76 + @proxy = mock(Net::HTTP)
  77 + Net::HTTP.stub!(:Proxy).and_return(@proxy)
  78 + @proxy.stub(:start).and_yield(http)
  79 + end
  80 +
  81 + it "should use proxy host and port" do
  82 + Rakismet.proxy_host = 'proxy_host'
  83 + Rakismet.proxy_port = 'proxy_port'
  84 + @proxy.stub!(:start).and_return(mock_response('valid'))
  85 + Net::HTTP.should_receive(:Proxy).with('proxy_host', 'proxy_port').and_return(@proxy)
  86 + Rakismet.send(:akismet_call, 'bogus-function')
  87 + end
  88 +
  89 + it "should build url with API key for the correct host" do
  90 + host = 'api.antispam.typepad.com'
  91 + Rakismet.host = host
  92 + @proxy.should_receive(:start).with("#{Rakismet.key}.#{host}")
  93 + Rakismet.send(:akismet_call, 'bogus-function')
  94 + end
  95 +
  96 + it "should post data to named function" do
  97 + http.should_receive(:post).with('/1.1/bogus-function', %r(foo=#{CGI.escape 'escape//this'}), Rakismet.headers)
  98 + Rakismet.send(:akismet_call, 'bogus-function', { :foo => 'escape//this' })
  99 + end
  100 +
  101 + it "should default to not being in test mode" do
  102 + http.should_receive(:post).with(anything, %r(is_test=0), anything)
  103 + Rakismet.send(:akismet_call, 'bogus-function')
  104 + end
  105 +
  106 + it "should be in test mode when configured" do
  107 + Rakismet.test = true
  108 + http.should_receive(:post).with(anything, %r(is_test=1), anything)
  109 + Rakismet.send(:akismet_call, 'bogus-function')
  110 + end
  111 +
  112 + it "should return response.body" do
  113 + Rakismet.send(:akismet_call, 'bogus-function').should eql('akismet response')
  114 + end
  115 +
  116 + it "should build query string when params are nil" do
  117 + lambda {
  118 + Rakismet.send(:akismet_call, 'bogus-function', { :nil_param => nil })
  119 + }.should_not raise_error(NoMethodError)
  120 + end
  121 + end
  122 +
  123 +end
vendor/plugins/rakismet/spec/spec_helper.rb 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +require File.expand_path "lib/rakismet"
  2 +require 'ostruct'
  3 +
  4 +RSpec.configure do |config|
  5 + config.mock_with :rspec
  6 +end
  7 +
  8 +class AkismetModel
  9 + include Rakismet::Model
  10 +end
  11 +
  12 +def comment_attrs(attrs={})
  13 + { :comment_type => 'test', :author => 'Rails test',
  14 + :author_email => 'test@test.host', :author_url => 'test.host',
  15 + :content => 'comment content', :blog => Rakismet.url }.merge(attrs)
  16 +end
  17 +
  18 +def akismet_attrs(attrs={})
  19 + { :comment_type => 'test', :comment_author_email => 'test@test.host',
  20 + :comment_author => 'Rails test', :comment_author_url => 'test.host',
  21 + :comment_content => 'comment content' }.merge(attrs)
  22 +end
  23 +
  24 +def request
  25 + OpenStruct.new(:user_ip => '127.0.0.1',
  26 + :user_agent => 'RSpec',
  27 + :referrer => 'http://test.host/referrer')
  28 +end
  29 +
  30 +def empty_request
  31 + OpenStruct.new(:user_ip => nil,
  32 + :user_agent => nil,
  33 + :referrer => nil)
  34 +end
0 \ No newline at end of file 35 \ No newline at end of file