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 101 end
102 102 end
103 103  
  104 + include Noosfero::Plugin::HotSpot
  105 +
104 106 def init_noosfero_plugins
105   - @plugins = Noosfero::Plugin::Manager.new(self)
106   - @plugins.each do |plugin|
  107 + plugins.each do |plugin|
107 108 prepend_view_path(plugin.class.view_path)
108 109 end
109 110 init_noosfero_plugins_controller_filters
... ... @@ -112,7 +113,7 @@ class ApplicationController < ActionController::Base
112 113 # This is a generic method that initialize any possible filter defined by a
113 114 # plugin to the current controller being initialized.
114 115 def init_noosfero_plugins_controller_filters
115   - @plugins.each do |plugin|
  116 + plugins.each do |plugin|
116 117 plugin.send(self.class.name.underscore + '_filters').each do |plugin_filter|
117 118 self.class.send(plugin_filter[:type], plugin.class.name.underscore + '_' + plugin_filter[:method_name], (plugin_filter[:options] || {}))
118 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 @@
  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 75 @comment = Comment.new
76 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 86 end
81 87  
82 88 if @page.has_posts?
... ... @@ -107,8 +113,9 @@ class ContentViewerController &lt; ApplicationController
107 113 end
108 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 119 if params[:slideshow]
113 120 render :action => 'slideshow', :layout => 'slideshow'
114 121 end
... ... @@ -120,10 +127,11 @@ class ContentViewerController &lt; ApplicationController
120 127 @comment.author = user if logged_in?
121 128 @comment.article = @page
122 129 @comment.ip_address = request.remote_ip
  130 + @comment.user_agent = request.user_agent
  131 + @comment.referrer = request.referrer
123 132 plugins_filter_comment(@comment)
124 133 return if @comment.rejected?
125 134 if (pass_without_comment_captcha? || verify_recaptcha(:model => @comment, :message => _('Please type the words correctly'))) && @comment.save
126   - plugins_comment_saved(@comment)
127 135 @page.touch
128 136 @comment = nil # clear the comment form
129 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 146 end
139 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 149 def pass_without_comment_captcha?
148 150 logged_in? && !environment.enabled?('captcha_for_logged_users')
149 151 end
... ... @@ -153,9 +155,24 @@ class ContentViewerController &lt; ApplicationController
153 155 @comment = @page.comments.find(params[:remove_comment])
154 156 if (user == @comment.author || user == @page.profile || user.has_permission?(:moderate_comments, @page.profile))
155 157 @comment.destroy
156   - session[:notice] = _('Comment succesfully deleted')
157 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 176 end
160 177  
161 178 def per_page
... ...
app/helpers/content_viewer_helper.rb
... ... @@ -4,11 +4,11 @@ module ContentViewerHelper
4 4 include ForumHelper
5 5  
6 6 def number_of_comments(article)
7   - n = article.comments.size
  7 + n = article.comments.without_spam.count
8 8 if n == 0
9 9 _('No comments yet')
10 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 12 end
13 13 end
14 14  
... ...
app/models/comment.rb
... ... @@ -10,6 +10,9 @@ class Comment &lt; ActiveRecord::Base
10 10 has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy
11 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 16 # unauthenticated authors:
14 17 validates_presence_of :name, :if => (lambda { |record| !record.email.blank? })
15 18 validates_presence_of :email, :if => (lambda { |record| !record.name.blank? })
... ... @@ -85,7 +88,28 @@ class Comment &lt; ActiveRecord::Base
85 88 end
86 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 113 def notify_by_mail
90 114 if source.kind_of?(Article) && article.notify_comments?
91 115 if !article.profile.notification_emails.empty?
... ... @@ -123,10 +147,14 @@ class Comment &lt; ActiveRecord::Base
123 147 def self.as_thread
124 148 result = {}
125 149 root = []
126   - all.each do |c|
  150 + order(:id).each do |c|
127 151 c.replies = []
128 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 158 end
131 159 root
132 160 end
... ... @@ -183,4 +211,34 @@ class Comment &lt; ActiveRecord::Base
183 211 @rejected = true
184 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 244 end
... ...
app/models/comment_handler.rb 0 → 100644
... ... @@ -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 22 super
23 23 end
24 24  
25   - acts_as_having_hotspots
26   -
27 25 named_scope :members_of, lambda { |resources|
28 26 resources = [resources] if !resources.kind_of?(Array)
29 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 30  
33 31 def has_permission_with_plugins?(permission, profile)
34 32 permissions = [has_permission_without_plugins?(permission, profile)]
35   - permissions += enabled_plugins.map do |plugin|
  33 + permissions += plugins.map do |plugin|
36 34 plugin.has_permission?(self, permission, profile)
37 35 end
38 36 permissions.include?(true)
... ...
app/models/profile.rb
... ... @@ -60,7 +60,8 @@ class Profile &lt; ActiveRecord::Base
60 60 }
61 61  
62 62 acts_as_accessible
63   - acts_as_having_hotspots
  63 +
  64 + include Noosfero::Plugin::HotSpot
64 65  
65 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 67 #FIXME: these will work only if the subclass is already loaded
... ... @@ -69,7 +70,7 @@ class Profile &lt; ActiveRecord::Base
69 70 named_scope :templates, :conditions => {:is_template => true}
70 71  
71 72 def members
72   - scopes = dispatch_scopes(:organization_members, self)
  73 + scopes = plugins.dispatch_scopes(:organization_members, self)
73 74 scopes << Person.members_of(self)
74 75 scopes.size == 1 ? scopes.first : Person.or_scope(scopes)
75 76 end
... ... @@ -113,6 +114,8 @@ class Profile &lt; ActiveRecord::Base
113 114 has_many :scraps_received, :class_name => 'Scrap', :foreign_key => :receiver_id, :order => "updated_at DESC", :dependent => :destroy
114 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 119 # FIXME ugly workaround
117 120 def self.human_attribute_name(attrib)
118 121 _(self.superclass.human_attribute_name(attrib))
... ... @@ -255,7 +258,7 @@ class Profile &lt; ActiveRecord::Base
255 258 self.categories(true)
256 259 self.solr_save
257 260 end
258   - self.categories(reload)
  261 + self.categories(reload)
259 262 end
260 263  
261 264 def category_ids=(ids)
... ...
app/views/admin_panel/index.rhtml
1 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 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 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 31 <tr><td><%= link_to link[:title], link[:url] %></td></tr>
21 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 42 </table>
... ...
app/views/content_viewer/_comment.rhtml
1 1 <li id="<%= comment.anchor %>" class="article-comment">
2 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 6 <% if comment.author %>
7 7 <%= link_to image_tag(profile_icon(comment.author, :minor)) +
... ... @@ -29,17 +29,12 @@
29 29 <% end %>
30 30  
31 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 33 <div class="comment-details">
39 34 <div class="comment-created-at">
40 35 <%= show_time(comment.created_at) %>
41 36 </div>
42   - <h4><%= comment.title %></h4>
  37 + <h4><%= comment.title.blank? && '&nbsp;' || comment.title %></h4>
43 38 <div class="comment-text">
44 39 <p/>
45 40 <%= txt2html comment.body %>
... ... @@ -57,18 +52,37 @@
57 52 </script>
58 53 <% end %>
59 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 74 "var f = add_comment_reply_form(this, %s); f.find('input[name=comment[title]], textarea').val(''); return false" % comment.id,
62 75 :class => 'comment-footer comment-footer-link comment-footer-hide',
63 76 :id => 'comment-reply-to-' + comment.id.to_s
64   - %>
  77 + %>
  78 + <% end %>
65 79 </div>
66 80  
67 81 <% end %>
68 82  
69 83 </div>
70 84  
71   - <% unless comment.replies.blank? %>
  85 + <% unless comment.replies.blank? || comment.spam? %>
72 86 <ul class="comment-replies">
73 87 <% comment.replies.each do |reply| %>
74 88 <%= render :partial => 'comment', :locals => { :comment => reply } %>
... ...
app/views/content_viewer/view_page.rhtml
... ... @@ -99,12 +99,6 @@
99 99  
100 100 <% if @page.accept_comments? %>
101 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 102 <% end %>
109 103 </div><!-- end class="comments" -->
110 104  
... ...
app/views/plugins/index.rhtml
1 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 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 28 <div>
23 29 <% button_bar do %>
... ...
app/views/profile_editor/index.rhtml
... ... @@ -66,6 +66,8 @@
66 66  
67 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 71 <% @plugins.dispatch(:control_panel_buttons).each do |button| %>
70 72 <%= control_panel_button(button[:title], button[:icon], button[:url]) %>
71 73 <% end %>
... ...
app/views/spam/index.rhtml 0 → 100644
... ... @@ -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 1 require 'noosfero/plugin'
2   -require 'noosfero/plugin/acts_as_having_hotspots'
  2 +require 'noosfero/plugin/hot_spot'
3 3 require 'noosfero/plugin/manager'
4   -require 'noosfero/plugin/context'
5 4 require 'noosfero/plugin/active_record'
6 5 require 'noosfero/plugin/mailer_base'
7 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 @@
  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
... ...
db/schema.rb
... ... @@ -9,7 +9,7 @@
9 9 #
10 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 14 create_table "abuse_reports", :force => true do |t|
15 15 t.integer "reporter_id"
... ... @@ -213,6 +213,8 @@ ActiveRecord::Schema.define(:version =&gt; 20120818030329) do
213 213 t.string "ip_address"
214 214 t.boolean "spam"
215 215 t.string "source_type"
  216 + t.string "user_agent"
  217 + t.string "referrer"
216 218 end
217 219  
218 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 7 noosfero (0.38.2) unstable; urgency=low
2 8  
3 9 * Bugfixes release
... ...
features/edit_environment_templates.feature
... ... @@ -8,7 +8,7 @@ Feature: edit environment templates
8 8 Scenario: See links to edit all templates
9 9 Given I am logged in as admin
10 10 When I follow "Administration"
11   - And I follow "Edit Templates"
  11 + And I follow "Profile templates"
12 12 Then I should see "Person template" link
13 13 And I should see "Community template" link
14 14 And I should see "Enterprise template" link
... ... @@ -17,28 +17,28 @@ Feature: edit environment templates
17 17 Scenario: Go to control panel of person template
18 18 Given I am logged in as admin
19 19 When I follow "Administration"
20   - And I follow "Edit Templates"
  20 + And I follow "Profile templates"
21 21 And I follow "Person template"
22 22 Then I should be on Person template's control panel
23 23  
24 24 Scenario: Go to control panel of enterprise template
25 25 Given I am logged in as admin
26 26 When I follow "Administration"
27   - And I follow "Edit Templates"
  27 + And I follow "Profile templates"
28 28 And I follow "Enterprise template"
29 29 Then I should be on Enterprise template's control panel
30 30  
31 31 Scenario: Go to control panel of inactive enterprise template
32 32 Given I am logged in as admin
33 33 When I follow "Administration"
34   - And I follow "Edit Templates"
  34 + And I follow "Profile templates"
35 35 And I follow "Inactive enterprise template"
36 36 Then I should be on Inactive Enterprise template's control panel
37 37  
38 38 Scenario: Go to control panel of community template
39 39 Given I am logged in as admin
40 40 When I follow "Administration"
41   - And I follow "Edit Templates"
  41 + And I follow "Profile templates"
42 42 And I follow "Community template"
43 43 Then I should be on Community template's control panel
44 44  
... ... @@ -46,7 +46,7 @@ Feature: edit environment templates
46 46 Given that the default environment have no Inactive Enterprise template
47 47 And I am logged in as admin
48 48 When I follow "Administration"
49   - And I follow "Edit Templates"
  49 + And I follow "Profile templates"
50 50 Then I should see "Person template" link
51 51 And I should see "Community template" link
52 52 And I should see "Enterprise template" link
... ...
features/environment_name.feature
... ... @@ -6,7 +6,7 @@ Feature: setting environment name
6 6 Scenario: setting environment name through administration panel
7 7 Given I am logged in as admin
8 8 When I follow "Administration"
9   - And I follow "Edit environment settings"
  9 + And I follow "Environment settings"
10 10 And I fill in "Site name" with "My environment"
11 11 And I press "Save"
12 12 Then I should see "My environment" within "title"
... ...
features/export_users.feature
... ... @@ -10,14 +10,14 @@ Feature: export users
10 10 Scenario: Export users as XML
11 11 Given I am logged in as admin
12 12 When I follow "Administration"
13   - And I follow "Manage users"
  13 + And I follow "Users"
14 14 And I follow "[XML]"
15 15 Then I should see "ultraje"
16 16  
17 17 Scenario: Export users as CSV
18 18 Given I am logged in as admin
19 19 When I follow "Administration"
20   - And I follow "Manage users"
  20 + And I follow "Users"
21 21 And I follow "[CSV]"
22 22 Then I should see "name;email"
23 23 And I should see "ultraje"
... ...
features/manage_categories.feature
... ... @@ -14,7 +14,7 @@ Feature: manage categories
14 14 | Development | services |
15 15 And I am logged in as admin
16 16 And I am on the environment control panel
17   - And I follow "Manage categories"
  17 + And I follow "Categories"
18 18  
19 19 Scenario: load only top level categories
20 20 Then I should see "Products"
... ...
features/plugins.feature
... ... @@ -49,7 +49,7 @@ Feature: plugins
49 49 When I go to the profile
50 50 Then I should see "Test plugin tab"
51 51 And I go to the environment control panel
52   - And I follow "Enable/disable plugins"
  52 + And I follow "Plugins"
53 53 And I uncheck "Test plugin"
54 54 And I press "Save changes"
55 55 When I go to the Control panel
... ...
features/roles.feature
... ... @@ -5,26 +5,26 @@ Feature: manage roles
5 5 Scenario: create new role
6 6 Given I am logged in as admin
7 7 And I go to the environment control panel
8   - And I follow "Manage User roles"
  8 + And I follow "User roles"
9 9 Then I should not see "My new role"
10 10 And I follow "Create a new role"
11 11 And I fill in "Name" with "My new role"
12 12 And I check "Publish content"
13 13 And I press "Create role"
14 14 And I go to the environment control panel
15   - And I follow "Manage User roles"
  15 + And I follow "User roles"
16 16 Then I should see "My new role"
17 17  
18 18 Scenario: edit a role
19 19 Given I am logged in as admin
20 20 And I go to the environment control panel
21   - And I follow "Manage User roles"
  21 + And I follow "User roles"
22 22 Then I should not see "My new role"
23 23 And I follow "Profile Administrator"
24 24 And I follow "Edit"
25 25 And I fill in "Name" with "My new role"
26 26 And I press "Save changes"
27 27 And I go to the environment control panel
28   - And I follow "Manage User roles"
  28 + And I follow "User roles"
29 29 Then I should see "My new role"
30 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 18 Scenario: Send e-mail to members
19 19 Given I am logged in as admin
20 20 When I follow "Administration"
21   - And I follow "Manage users"
  21 + And I follow "Users"
22 22 And I follow "Send e-mail to users"
23 23 And I fill in "Subject" with "Hello, user!"
24 24 And I fill in "body" with "We have some news"
... ... @@ -28,7 +28,7 @@ Feature: send emails to environment members users
28 28 Scenario: Not send e-mail to members if subject is blank
29 29 Given I am logged in as admin
30 30 When I follow "Administration"
31   - And I follow "Manage users"
  31 + And I follow "Users"
32 32 And I follow "Send e-mail to users"
33 33 And I fill in "body" with "We have some news"
34 34 When I press "Send"
... ... @@ -37,7 +37,7 @@ Feature: send emails to environment members users
37 37 Scenario: Not send e-mail to members if body is blank
38 38 Given I am logged in as admin
39 39 When I follow "Administration"
40   - And I follow "Manage users"
  40 + And I follow "Users"
41 41 And I follow "Send e-mail to users"
42 42 And I fill in "Subject" with "Hello, user!"
43 43 When I press "Send"
... ... @@ -46,7 +46,7 @@ Feature: send emails to environment members users
46 46 Scenario: Cancel creation of mailing
47 47 Given I am logged in as admin
48 48 When I follow "Administration"
49   - And I follow "Manage users"
  49 + And I follow "Users"
50 50 And I follow "Send e-mail to users"
51 51 Then I should be on /admin/users/send_mail
52 52 When I follow "Cancel e-mail"
... ...
lib/needs_profile.rb
... ... @@ -14,12 +14,12 @@ module NeedsProfile
14 14 profile || environment # prefers profile, but defaults to environment
15 15 end
16 16  
17   - protected
18   -
19 17 def profile
20 18 @profile
21 19 end
22 20  
  21 + protected
  22 +
23 23 def load_profile
24 24 @profile ||= environment.profiles.find_by_identifier(params[:profile])
25 25 if @profile
... ...
lib/noosfero.rb
... ... @@ -2,7 +2,7 @@ require &#39;fast_gettext&#39;
2 2  
3 3 module Noosfero
4 4 PROJECT = 'noosfero'
5   - VERSION = '0.38.2'
  5 + VERSION = '0.39.0~1'
6 6  
7 7 def self.pattern_for_controllers_in_directory(dir)
8 8 disjunction = controllers_in_directory(dir).join('|')
... ...
lib/noosfero/plugin.rb
... ... @@ -15,14 +15,29 @@ class Noosfero::Plugin
15 15 Dir.glob(File.join(Rails.root, 'config', 'plugins', '*')).select do |entry|
16 16 File.directory?(entry)
17 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 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 41 end
27 42 end
28 43  
... ... @@ -226,16 +241,53 @@ class Noosfero::Plugin
226 241 # example:
227 242 #
228 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 247 # end
231 248 #
232 249 def filter_comment(comment)
233 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 291 end
240 292  
241 293 # -> Adds fields to the signup form
... ...
lib/noosfero/plugin/acts_as_having_hotspots.rb
... ... @@ -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   -# 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 @@
  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 1 class Noosfero::Plugin::Manager
2 2  
3   - extend ActsAsHavingHotspots::ClassMethods
4   - acts_as_having_hotspots
5   -
  3 + attr_reader :environment
6 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 11 delegate :each, :to => :enabled_plugins
10 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 37 end
19 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 4 end
  5 +disabled_plugins = all_plugins - enabled_plugins
13 6  
14 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 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 67 end
52   - task plugin_name => dependencies
53 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 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 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 92 end
74   - Rake::Task['test:noosfero_plugins:rollback_temp_enable_plugins'].invoke
75 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 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 105 end
100 106  
  107 + task :rollback_enable_all_plugins do
  108 + sh './script/noosfero-plugins', 'disable', *disabled_plugins
  109 + end
101 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 114 end
106   -
... ...
lib/tasks/release.rake
... ... @@ -95,6 +95,9 @@ EOF
95 95 sh "cd #{target} && dpkg-buildpackage -us -uc -b"
96 96 end
97 97  
  98 + desc "Build Debian packages (shorcut)"
  99 + task :deb => :debian_packages
  100 +
98 101 desc 'Test Debian package'
99 102 task 'debian:test' => :debian_packages do
100 103 Dir.chdir 'pkg' do
... ...
plugins/anti_spam/controllers/anti_spam_plugin_admin_controller.rb 0 → 100644
... ... @@ -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 @@
  1 +require 'rakismet'
... ...
plugins/anti_spam/lib/anti_spam_plugin.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  1 +require 'savon'
  2 +require 'googlecharts'
... ...
plugins/mezuro/lib/mezuro_plugin.rb
1   -require 'savon'
2 1 require 'yaml'
3 2  
4 3 Savon.configure do |config|
... ...
plugins/mezuro/lib/mezuro_plugin/helpers/content_viewer_helper.rb
1   -require 'googlecharts'
2   -
3 1 class MezuroPlugin::Helpers::ContentViewerHelper
4 2 def self.format_grade(grade)
5 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 35 controller = mock()
36 36 controller.stubs(:logged_in?).returns(boolean)
37 37 controller.stubs(:profile).returns(Profile.new)
38   - Noosfero::Plugin::Context.new(controller)
  38 + controller
39 39 end
40 40  
41 41 end
... ...
plugins/stoa/test/functional/profile_editor_controller.rb
... ... @@ -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 @@
  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 @@
  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 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 713 function original_image_dimensions(src) {
687 714 var img = new Image();
688 715 img.src = src;
... ...
public/stylesheets/application.css
... ... @@ -1069,15 +1069,6 @@ a.comment-picture {
1069 1069 top: 9px;
1070 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 1072 .msie7 .article-comments-list .comment-balloon {
1082 1073 margin-top: -15px;
1083 1074 }
... ... @@ -1271,6 +1262,11 @@ a.comment-picture {
1271 1262 }
1272 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 1270 .post_comment_box {
1275 1271 text-align: center;
1276 1272 padding: 0px 15px 5px 15px;
... ... @@ -3854,6 +3850,9 @@ h1#agenda-title {
3854 3850 .controller-profile_editor .msie6 a.control-panel-edit-location {
3855 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 3856 /* ==> public/stylesheets/controller_profile_members.css <== */
3858 3857 .controller-profile_members .no-boxes {
3859 3858 margin: 30px
... ...
script/install-dependencies/debian-squeeze.sh
1 1 # needed to run noosfero
2 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 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 6 # needed for development
6 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 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 21 # operation defaults
22 22 quiet=false
23 23 needs_migrate=false
  24 +load_paths="$NOOSFERO_DIR/lib:$(echo $NOOSFERO_DIR/vendor/plugins/*/lib | tr ' ' :)"
  25 +
24 26  
25 27 _list() {
26 28 for plugin in $available_plugins; do
... ... @@ -72,11 +74,26 @@ _enable(){
72 74 if [ -h "$target" ]; then
73 75 _say "$plugin already enabled"
74 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 97 fi
81 98 }
82 99  
... ...
script/production
... ... @@ -34,8 +34,6 @@ do_start() {
34 34 }
35 35  
36 36 do_stop() {
37   - rake -s solr:stop
38   -
39 37 # During Debian upgrades, it is possible that rails is not available (e.g.
40 38 # Lenny -> Squeeze), so the programs below might fail. If they do, we fall
41 39 # back to stopping the daemons by manually reading their PID files, killing
... ... @@ -46,6 +44,8 @@ do_stop() {
46 44  
47 45 environments_loop stop ||
48 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 51 stop_via_pid_file() {
... ...
script/quick-start
... ... @@ -21,6 +21,22 @@ run() {
21 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 40 force_install=false
25 41 if test "$1" = '--force-install'; then
26 42 force_install=true
... ... @@ -28,9 +44,15 @@ fi
28 44 if gem which system_timer >/dev/null 2>&1 && which xvfb-run >/dev/null 2>&1 && test "$force_install" = 'false'; then
29 45 say "Assuming dependencies are already installed. Pass --force-install to force their installation"
30 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 56 fi
35 57 system=$(echo $(lsb_release -sic) | awk '{print(tolower($1) "-" tolower($2))}')
36 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 92  
93 93 login_as 'testuser'
94 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 96 end
97 97  
98 98 should 'display remove comment button with param view when image' do
... ... @@ -106,8 +106,9 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
106 106  
107 107 login_as 'testuser'
108 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 113 should 'not add unneeded params for remove comment button' do
113 114 profile = create_user('testuser').person
... ... @@ -117,8 +118,8 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
117 118 comment.save!
118 119  
119 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 123 end
123 124  
124 125 should 'be able to remove comment' do
... ... @@ -1374,12 +1375,16 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1374 1375 assert_not_nil assigns(:comment)
1375 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 1379 page = profile.articles.create!(:name => 'myarticle', :body => 'the body of the text')
1379 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 1383 post :view_page, :profile => profile.identifier, :page => [ 'myarticle' ], :comment => { :title => 'title', :body => 'body', :name => "Spammer", :email => 'damn@spammer.com' }, :confirm => 'true'
1381 1384 comment = Comment.last
1382 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 1388 end
1384 1389  
1385 1390 should 'not save a comment if a plugin rejects it' do
... ... @@ -1395,25 +1400,6 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1395 1400 end
1396 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 1403 should 'remove email from article followers when unfollow' do
1418 1404 profile = create_user('testuser').person
1419 1405 follower_email = 'john@doe.br'
... ... @@ -1426,4 +1412,24 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1426 1412 assert_not_includes Article.find(article.id).followers, follower_email
1427 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 1435 end
... ...
test/functional/spam_controller_test.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 14  
15 15 should 'deliver mail after make an article comment' do
16 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 18 end
19 19 end
20 20  
21 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 23 sent = ActionMailer::Base.deliveries.first
24 24 assert_equal [@profile.email], sent.to
25 25 end
26 26  
27 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 29 sent = ActionMailer::Base.deliveries.first
30 30 assert_match /user_comment_test/, sent.body
31 31 end
32 32  
33 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 35 sent = ActionMailer::Base.deliveries.first
36 36 assert_match /flatline/, sent.body
37 37 assert_match /flatline@invalid.com/, sent.body
... ... @@ -40,18 +40,18 @@ class CommentNotifierTest &lt; ActiveSupport::TestCase
40 40 should 'not deliver mail if notify comments is false' do
41 41 @article.update_attribute(:notify_comments, false)
42 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 44 end
45 45 end
46 46  
47 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 49 sent = ActionMailer::Base.deliveries.first
50 50 assert_match /comment title/, sent.body
51 51 end
52 52  
53 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 55 sent = ActionMailer::Base.deliveries.first
56 56 assert_match /comment body/, sent.body
57 57 end
... ... @@ -61,7 +61,7 @@ class CommentNotifierTest &lt; ActiveSupport::TestCase
61 61 assert_equal [], community.notification_emails
62 62 article = fast_create(Article, :name => 'Article test', :profile_id => community.id, :notify_comments => true)
63 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 65 end
66 66 end
67 67  
... ... @@ -70,24 +70,29 @@ class CommentNotifierTest &lt; ActiveSupport::TestCase
70 70 follower = create_user('follower').person
71 71 @article.followers += [follower.email]
72 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 74 assert_includes ActionMailer::Base.deliveries.map(&:bcc).flatten, follower.email
75 75 end
76 76  
77 77 should "not deliver follower's mail about new comment to comment's author" do
78 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 80 assert_not_includes ActionMailer::Base.deliveries.map(&:bcc).flatten, follower.email
81 81 end
82 82  
83 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 98 end
... ...
test/unit/comment_test.rb
... ... @@ -398,6 +398,9 @@ class CommentTest &lt; ActiveSupport::TestCase
398 398 end
399 399  
400 400 should 'update article activity when add a comment' do
  401 + now = Time.now
  402 + Time.stubs(:now).returns(now)
  403 +
401 404 profile = create_user('testuser').person
402 405 article = create(TinyMceArticle, :profile => profile)
403 406  
... ... @@ -422,4 +425,142 @@ class CommentTest &lt; ActiveSupport::TestCase
422 425 assert_not_nil article.activity
423 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 566 end
... ...
test/unit/content_viewer_helper_test.rb
... ... @@ -61,9 +61,9 @@ class ContentViewerHelperTest &lt; ActiveSupport::TestCase
61 61 end
62 62  
63 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 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 67 result = link_to_comments(article)
68 68 assert_match /One comment/, result
69 69 end
... ...
test/unit/person_test.rb
... ... @@ -3,10 +3,6 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 3 class PersonTest < ActiveSupport::TestCase
4 4 fixtures :profiles, :users, :environments
5 5  
6   - def teardown
7   - Thread.current[:enabled_plugins] = nil
8   - end
9   -
10 6 def test_person_must_come_form_the_cration_of_an_user
11 7 p = Person.new(:environment => Environment.default, :name => 'John', :identifier => 'john')
12 8 assert !p.valid?
... ...
test/unit/plugin_hot_spot_test.rb 0 → 100644
... ... @@ -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 8 @controller.stubs(:profile).returns()
9 9 @controller.stubs(:request).returns()
10 10 @controller.stubs(:response).returns()
11   - @controller.stubs(:environment).returns(@environment)
12 11 @controller.stubs(:params).returns()
  12 + @manager = Noosfero::Plugin::Manager.new(@environment, @controller)
13 13 end
14 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 22 should 'return the intersection between environment\'s enabled plugins and system available plugins' do
17 23 class Plugin1 < Noosfero::Plugin; end;
... ... @@ -20,7 +26,6 @@ class PluginManagerTest &lt; ActiveSupport::TestCase
20 26 class Plugin4 < Noosfero::Plugin; end;
21 27 environment.stubs(:enabled_plugins).returns([Plugin1.to_s, Plugin2.to_s, Plugin4.to_s])
22 28 Noosfero::Plugin.stubs(:all).returns([Plugin1.to_s, Plugin3.to_s, Plugin4.to_s])
23   - manager = Noosfero::Plugin::Manager.new(@controller)
24 29 plugins = manager.enabled_plugins.map { |instance| instance.class.to_s }
25 30 assert_equal [Plugin1.to_s, Plugin4.to_s], plugins
26 31 end
... ... @@ -49,7 +54,6 @@ class PluginManagerTest &lt; ActiveSupport::TestCase
49 54  
50 55 p1 = Plugin1.new
51 56 p2 = Plugin2.new
52   - manager = Noosfero::Plugin::Manager.new(@controller)
53 57  
54 58 assert_equal [p1.random_event, p2.random_event], manager.dispatch(:random_event)
55 59 end
... ...
test/unit/profile_test.rb
... ... @@ -3,10 +3,6 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 3 class ProfileTest < ActiveSupport::TestCase
4 4 fixtures :profiles, :environments, :users, :roles, :domains
5 5  
6   - def teardown
7   - Thread.current[:enabled_plugins] = nil
8   - end
9   -
10 6 def test_identifier_validation
11 7 p = Profile.new
12 8 p.valid?
... ... @@ -1834,16 +1830,6 @@ class ProfileTest &lt; ActiveSupport::TestCase
1834 1830 end
1835 1831  
1836 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 1833 class Plugin1 < Noosfero::Plugin
1848 1834 def organization_members(profile)
1849 1835 Person.members_of(Community.find_by_identifier('community1'))
... ... @@ -1855,8 +1841,18 @@ class ProfileTest &lt; ActiveSupport::TestCase
1855 1841 Person.members_of(Community.find_by_identifier('community2'))
1856 1842 end
1857 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 1857 assert_includes original_community.members, original_member
1862 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 20  
21 21 tmpdir = [ '/var/tmp', '/tmp' ].find { |d| File.exists?(d) }
22 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 34 sh "echo \"#{SOLR_MD5SUM} #{SOLR_FILENAME}\" | md5sum -c -" do |ok, res|
26 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 1 ActiveRecord::Base.establish_connection(
5 2 :adapter => "mysql",
6 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 1 ActiveRecord::Base.establish_connection(
5 2 :adapter => "sqlite3",
6 3 :encoding => "utf8",
... ...
vendor/plugins/rakismet/CHANGELOG 0 → 100644
... ... @@ -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 @@
  1 +source "http://rubygems.org"
  2 +
  3 +gemspec
... ...
vendor/plugins/rakismet/MIT-LICENSE 0 → 100644
... ... @@ -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 @@
  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 @@
  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 10 \ No newline at end of file
... ...
vendor/plugins/rakismet/lib/rakismet.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 @@
  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 @@
  1 +module Rakismet
  2 + VERSION = "1.2.1"
  3 +end
... ...
vendor/plugins/rakismet/rakismet.gemspec 0 → 100644
... ... @@ -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 @@
  1 +--color
... ...
vendor/plugins/rakismet/spec/models/block_params_spec.rb 0 → 100644
... ... @@ -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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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 35 \ No newline at end of file
... ...