Commit 14c2d322aff60f054b3f2d672286081e35496609

Authored by Leandro Nunes dos Santos
2 parents ba4f52d8 06f5295f

Merge branch 'master_colivre' into ActionItem2823_merge

Showing 120 changed files with 1333 additions and 482 deletions   Show diff stats
COPYRIGHT
... ... @@ -4,8 +4,9 @@ Copyright (c) 2007-2009,
4 4 Cáritas Brasileira <http://www.caritasbrasileira.org/>
5 5 Copyright (c) 2007-2009,
6 6 Ynternet.org Foundation <http://www.ynternet.org/>
7   -Copyright (c) 2008-2009,
  7 +Copyright (c) 2008-2013,
8 8 Colivre <http://www.colivre.coop.br/>
  9 +Copyright (c) the Noosfero contributors. See AUTHORS
9 10  
10 11 This program is free software: you can redistribute it and/or modify
11 12 it under the terms of the GNU Affero General Public License as published by
... ...
HACKING
... ... @@ -52,3 +52,12 @@ If you write such script for your own OS, *please* share it with us at the
52 52 development mailing list so that we can include it in the official repository.
53 53 This way other people using the same OS will have to put less effort to develop
54 54 Noosfero.
  55 +
  56 +== Submitting your changes back
  57 +
  58 +For now please read:
  59 +
  60 +- Coding conventions
  61 + http://noosfero.org/Development/CodingConventions
  62 +- Patch guidelines
  63 + http://noosfero.org/Development/PatchGuidelines
... ...
HACKING.rails235
... ... @@ -1,13 +0,0 @@
1   -This is a draft of how to create a environment to Rails 2.3.5 to Noosfero
2   -development.
3   -
4   -Install dependencies:
5   -
6   -gem install rails -v 2.3.5
7   -gem install i18n
8   -gem install will_paginate -v 2.3.12
9   -gem install cucumber
10   -
11   -Creating initial environment:
12   -
13   -rake db:schema:load
README
1   -noosfero - a web-based social platform
  1 +Noosfero - a web-based social platform
2 2 ======================================
3 3  
4   -:: About the project
  4 +http://www.noosfero.org/
5 5  
6   -Homepage: http://www.noosfero.org/
  6 +Documentation
  7 +-------------
7 8  
8   -:: Authors and copyright
  9 +The following documentation is available:
9 10  
10   -Authors: see file AUTHORS
11   -Copyright information: see file COPYRIGHT
12   -Full license text; see file COPYING
  11 +File Purpose
  12 +~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  13 +INSTALL install instructions
  14 +INSTALL.awstats install instructions - access statistics service
  15 +INSTALL.chat install instructions - chat service
  16 +INSTALL.email install instructions - email service
  17 +INSTALL.multitenancy install instructions - multiple sites
  18 +INSTALL.varnish install instructions - varnish HTTP caching (recommended)
  19 +HACKING development instruction
  20 +RELEASING instructions for doing releases
  21 +doc/noosfero/* user documentation (available through the app itself)
  22 +
  23 +
  24 +Authors and copyright
  25 +---------------------
  26 +
  27 +Authorship and copyright information is available in the files listed below.
  28 +
  29 +File Purpose
  30 +~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  31 +AUTHORS list of authors (updated at each release)
  32 +COPYRIGHT Copyright statement for the project
  33 +COPYING Full text of the project license
... ...
app/controllers/my_profile/cms_controller.rb
... ... @@ -267,7 +267,10 @@ class CmsController &lt; MyProfileController
267 267 @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url)
268 268 @task = SuggestArticle.new(params[:task])
269 269 if request.post?
270   - @task.target = profile
  270 + @task.target = profile
  271 + @task.ip_address = request.remote_ip
  272 + @task.user_agent = request.user_agent
  273 + @task.referrer = request.referrer
271 274 if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save
272 275 session[:notice] = _('Thanks for your suggestion. The community administrators were notified.')
273 276 redirect_to @back_to
... ...
app/controllers/my_profile/profile_editor_controller.rb
... ... @@ -4,7 +4,7 @@ class ProfileEditorController &lt; MyProfileController
4 4 protect 'destroy_profile', :profile, :only => [:destroy_profile]
5 5  
6 6 def index
7   - @pending_tasks = Task.to(profile).pending.select{|i| user.has_permission?(i.permission, profile)}
  7 + @pending_tasks = Task.to(profile).pending.without_spam.select{|i| user.has_permission?(i.permission, profile)}
8 8 end
9 9  
10 10 helper :profile
... ...
app/controllers/my_profile/spam_controller.rb
... ... @@ -14,9 +14,15 @@ class SpamController &lt; MyProfileController
14 14 if params[:remove_comment]
15 15 profile.comments_received.find(params[:remove_comment]).destroy
16 16 end
  17 + if params[:remove_task]
  18 + Task.to(profile).find_by_id(params[:remove_task]).destroy
  19 + end
17 20 if params[:mark_comment_as_ham]
18 21 profile.comments_received.find(params[:mark_comment_as_ham]).ham!
19 22 end
  23 + if params[:mark_task_as_ham] && (t = Task.to(profile).find_by_id(params[:mark_task_as_ham]))
  24 + t.ham!
  25 + end
20 26 if request.xhr?
21 27 json_response(true)
22 28 else
... ... @@ -28,7 +34,8 @@ class SpamController &lt; MyProfileController
28 34 return
29 35 end
30 36  
31   - @spam = profile.comments_received.spam.paginate({:page => params[:page]})
  37 + @comment_spam = profile.comments_received.spam.paginate({:page => params[:comments_page]})
  38 + @task_spam = Task.to(profile).spam.paginate({:page => params[:tasks_page]})
32 39 end
33 40  
34 41 protected
... ...
app/controllers/my_profile/tasks_controller.rb
... ... @@ -4,12 +4,12 @@ class TasksController &lt; MyProfileController
4 4  
5 5 def index
6 6 @filter = params[:filter_type].blank? ? nil : params[:filter_type]
7   - @tasks = Task.to(profile).pending.of(@filter).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page])
  7 + @tasks = Task.to(profile).without_spam.pending.of(@filter).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page])
8 8 @failed = params ? params[:failed] : {}
9 9 end
10 10  
11 11 def processed
12   - @tasks = Task.to(profile).closed.sort_by(&:created_at)
  12 + @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at)
13 13 end
14 14  
15 15 VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ]
... ... @@ -57,7 +57,7 @@ class TasksController &lt; MyProfileController
57 57 end
58 58  
59 59 def list_requested
60   - @tasks = Task.find_all_by_requestor_id(profile.id)
  60 + @tasks = Task.without_spam.find_all_by_requestor_id(profile.id)
61 61 end
62 62  
63 63 def ticket_details
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -53,7 +53,9 @@ class ContentViewerController &lt; ApplicationController
53 53 # At this point the page will be showed
54 54 @page.hit
55 55  
56   - unless @page.mime_type == 'text/html' || (@page.image? && params[:view])
  56 + @page = FilePresenter.for @page
  57 +
  58 + unless @page.mime_type == 'text/html' || params[:view]
57 59 headers['Content-Type'] = @page.mime_type
58 60 data = @page.data
59 61  
... ...
app/helpers/application_helper.rb
... ... @@ -558,6 +558,9 @@ module ApplicationHelper
558 558 # displays a link to the profile homepage with its image (as generated by
559 559 # #profile_image) and its name below it.
560 560 def profile_image_link( profile, size=:portrait, tag='li', extra_info = nil )
  561 + if content = @plugins.dispatch_first(:profile_image_link, profile, size, tag, extra_info)
  562 + return instance_eval(&content)
  563 + end
561 564 name = profile.short_name
562 565 if profile.person?
563 566 url = url_for(profile.check_friendship_url)
... ... @@ -574,16 +577,16 @@ module ApplicationHelper
574 577 extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' )
575 578 links = links_for_balloon(profile)
576 579 content_tag('div', content_tag(tag,
577   - (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") +
578   - link_to(
579   - content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +
580   - content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) +
581   - extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ),
582   - profile.url,
583   - :class => 'profile_link url',
584   - :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name,
585   - :title => profile.name ),
586   - :class => 'vcard'), :class => 'common-profile-list-block')
  580 + (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") +
  581 + link_to(
  582 + content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +
  583 + content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) +
  584 + extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ),
  585 + profile.url,
  586 + :class => 'profile_link url',
  587 + :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name,
  588 + :title => profile.name ),
  589 + :class => 'vcard'), :class => 'common-profile-list-block')
587 590 end
588 591  
589 592 def gravatar_url_for(email, options = {})
... ... @@ -1113,15 +1116,34 @@ module ApplicationHelper
1113 1116 result
1114 1117 end
1115 1118  
1116   - def manage_enterprises
1117   - if user && !user.enterprises.empty?
1118   - enterprises_link = user.enterprises.map do |enterprise|
1119   - link_to(content_tag('strong', [_('<span>Manage</span> %s') % enterprise.short_name(25)]), @environment.top_url + "/myprofile/#{enterprise.identifier}", :class => "icon-menu-"+enterprise.class.identification.underscore, :title => [_('Manage %s') % enterprise.short_name])
  1119 + def manage_link(list, kind)
  1120 + if list.present?
  1121 + link_to_all = nil
  1122 + if list.count > 5
  1123 + list = list.first(5)
  1124 + link_to_all = link_to(content_tag('strong', _('See all')), :controller => 'memberships', :profile => current_user.login)
  1125 + end
  1126 + link = list.map do |element|
  1127 + link_to(content_tag('strong', [_('<span>Manage</span> %s') % element.short_name(25)]), @environment.top_url + "/myprofile/#{element.identifier}", :class => "icon-menu-"+element.class.identification.underscore, :title => [_('Manage %s') % element.short_name])
1120 1128 end
1121   - render :partial => 'shared/manage_enterprises', :locals => {:enterprises_link => enterprises_link}
  1129 + if link_to_all
  1130 + link << link_to_all
  1131 + end
  1132 + render :partial => "shared/manage_link", :locals => {:link => link, :kind => kind.to_s}
1122 1133 end
1123 1134 end
1124 1135  
  1136 + def manage_enterprises
  1137 + return if not user
  1138 + manage_link(user.enterprises, :enterprises)
  1139 + end
  1140 +
  1141 + def manage_communities
  1142 + return if not user
  1143 + administered_communities = user.communities.more_popular.select {|c| c.admins.include? user}
  1144 + manage_link(administered_communities, :communities)
  1145 + end
  1146 +
1125 1147 def usermenu_logged_in
1126 1148 pending_tasks_count = ''
1127 1149 count = user ? Task.to(user).pending.count : -1
... ... @@ -1133,6 +1155,7 @@ module ApplicationHelper
1133 1155 render_environment_features(:usermenu) +
1134 1156 link_to('<i class="icon-menu-admin"></i><strong>' + _('Administration') + '</strong>', @environment.top_url + '/admin', :id => "controlpanel", :title => _("Configure the environment"), :class => 'admin-link', :style => 'display: none') +
1135 1157 manage_enterprises.to_s +
  1158 + manage_communities.to_s +
1136 1159 link_to('<i class="icon-menu-ctrl-panel"></i><strong>' + _('Control panel') + '</strong>', @environment.top_url + '/myprofile/{login}', :id => "controlpanel", :title => _("Configure your personal account and content")) +
1137 1160 pending_tasks_count +
1138 1161 link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system"))
... ...
app/helpers/blog_helper.rb
... ... @@ -42,7 +42,7 @@ module BlogHelper
42 42  
43 43 def display_post(article, format = 'full')
44 44 no_comments = (format == 'full') ? false : true
45   - html = send("display_#{format}_format", article).html_safe
  45 + html = send("display_#{format}_format", FilePresenter.for(article)).html_safe
46 46  
47 47 article_title(article, :no_comments => no_comments) + html
48 48 end
... ...
app/helpers/cms_helper.rb
... ... @@ -33,7 +33,7 @@ module CmsHelper
33 33 link_to article_name, {:action => 'view', :id => article.id}, :class => icon_for_article(article)
34 34 else
35 35 if article.image?
36   - image_tag(icon_for_article(article)) + link_to(article_name, article.url)
  36 + image_tag(icon_for_article(article)) + link_to(article_name, article.url)
37 37 else
38 38 link_to article_name, article.url, :class => icon_for_article(article)
39 39 end
... ...
app/helpers/content_viewer_helper.rb
... ... @@ -17,7 +17,7 @@ module ContentViewerHelper
17 17 title = article.display_title if article.kind_of?(UploadedFile) && article.image?
18 18 title = article.title if title.blank?
19 19 title = content_tag('h1', h(title), :class => 'title')
20   - if article.belongs_to_blog?
  20 + if article.belongs_to_blog? || article.belongs_to_forum?
21 21 unless args[:no_link]
22 22 title = content_tag('h1', link_to(article.name, article.url), :class => 'title')
23 23 end
... ...
app/helpers/folder_helper.rb
... ... @@ -21,6 +21,7 @@ module FolderHelper
21 21 end
22 22  
23 23 def display_article_in_listing(article, recursive = false, level = 0)
  24 + article = FilePresenter.for article
24 25 article_link = if article.image?
25 26 link_to('&nbsp;' * (level * 4) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true))
26 27 else
... ... @@ -40,12 +41,15 @@ module FolderHelper
40 41 end
41 42  
42 43 def icon_for_article(article)
43   - icon = article.class.icon_name(article)
  44 + article = FilePresenter.for article
  45 + icon = article.respond_to?(:icon_name) ?
  46 + article.icon_name :
  47 + article.class.icon_name(article)
44 48 if (icon =~ /\//)
45 49 icon
46 50 else
47   - klasses = 'icon icon-' + icon
48   - if article.kind_of?(UploadedFile)
  51 + klasses = 'icon ' + [icon].flatten.map{|name| 'icon-'+name}.join(' ')
  52 + if article.kind_of?(UploadedFile) || article.kind_of?(FilePresenter)
49 53 klasses += ' icon-upload-file'
50 54 end
51 55 klasses
... ...
app/models/article.rb
... ... @@ -154,8 +154,12 @@ class Article &lt; ActiveRecord::Base
154 154 end
155 155 end
156 156  
  157 + def css_class_list
  158 + [self.class.name.underscore.dasherize]
  159 + end
  160 +
157 161 def css_class_name
158   - self.class.name.underscore.dasherize
  162 + [css_class_list].flatten.compact.join(' ')
159 163 end
160 164  
161 165 def pending_categorizations
... ... @@ -310,6 +314,10 @@ class Article &lt; ActiveRecord::Base
310 314 def belongs_to_blog?
311 315 self.parent and self.parent.blog?
312 316 end
  317 +
  318 + def belongs_to_forum?
  319 + self.parent and self.parent.forum?
  320 + end
313 321  
314 322 def info_from_last_update
315 323 last_comment = comments.last
... ... @@ -325,7 +333,7 @@ class Article &lt; ActiveRecord::Base
325 333 end
326 334  
327 335 def view_url
328   - @view_url ||= image? ? url.merge(:view => true) : url
  336 + @view_url ||= is_a?(UploadedFile) ? url.merge(:view => true) : url
329 337 end
330 338  
331 339 def comment_url_structure(comment, action = :edit)
... ...
app/models/article_block.rb
... ... @@ -12,7 +12,7 @@ class ArticleBlock &lt; Block
12 12 block = self
13 13 lambda do
14 14 block_title(block.title) +
15   - (block.article ? article_to_html(block.article,
  15 + (block.article ? article_to_html(FilePresenter.for(block.article),
16 16 :gallery_view => false,
17 17 :inside_block => block, # For Blogs and folders
18 18 :format => block.visualization_format # For Articles and contents
... ...
app/models/comment.rb
... ... @@ -16,9 +16,7 @@ class Comment &lt; ActiveRecord::Base
16 16 has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy
17 17 belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id'
18 18  
19   - named_scope :without_spam, :conditions => ['spam IS NULL OR spam = ?', false]
20 19 named_scope :without_reply, :conditions => ['reply_of_id IS NULL']
21   - named_scope :spam, :conditions => ['spam = ?', true]
22 20  
23 21 # unauthenticated authors:
24 22 validates_presence_of :name, :if => (lambda { |record| !record.email.blank? })
... ... @@ -108,6 +106,17 @@ class Comment &lt; ActiveRecord::Base
108 106  
109 107 include Noosfero::Plugin::HotSpot
110 108  
  109 + include Spammable
  110 +
  111 + def after_spam!
  112 + SpammerLogger.log(ip_address, self)
  113 + Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_spam))
  114 + end
  115 +
  116 + def after_ham!
  117 + Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_ham))
  118 + end
  119 +
111 120 def verify_and_notify
112 121 check_for_spam
113 122 unless spam?
... ... @@ -115,10 +124,6 @@ class Comment &lt; ActiveRecord::Base
115 124 end
116 125 end
117 126  
118   - def check_for_spam
119   - plugins.dispatch(:check_comment_for_spam, self)
120   - end
121   -
122 127 def notify_by_mail
123 128 if source.kind_of?(Article) && article.notify_comments?
124 129 if !notification_emails.empty?
... ... @@ -205,37 +210,6 @@ class Comment &lt; ActiveRecord::Base
205 210 @rejected = true
206 211 end
207 212  
208   - def spam?
209   - !spam.nil? && spam
210   - end
211   -
212   - def ham?
213   - !spam.nil? && !spam
214   - end
215   -
216   - def spam!
217   - self.spam = true
218   - self.save!
219   - SpammerLogger.log(ip_address, self)
220   - Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_spam))
221   - self
222   - end
223   -
224   - def ham!
225   - self.spam = false
226   - self.save!
227   - Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_ham))
228   - self
229   - end
230   -
231   - def marked_as_spam
232   - plugins.dispatch(:comment_marked_as_spam, self)
233   - end
234   -
235   - def marked_as_ham
236   - plugins.dispatch(:comment_marked_as_ham, self)
237   - end
238   -
239 213 def need_moderation?
240 214 article.moderate_comments? && (author.nil? || article.author != author)
241 215 end
... ...
app/models/spammer_logger.rb
... ... @@ -6,6 +6,8 @@ class SpammerLogger &lt; Logger
6 6 if object
7 7 if object.kind_of?(Comment)
8 8 @logger << "[#{Time.now.strftime('%F %T %z')}] Comment-id: #{object.id} IP: #{spammer_ip}\n"
  9 + elsif object.kind_of?(SuggestArticle)
  10 + @logger << "[#{Time.now.strftime('%F %T %z')}] SuggestArticle-id: #{object.id} IP: #{spammer_ip}\n"
9 11 end
10 12 else
11 13 @logger << "[#{Time.now.strftime('%F %T %z')}] IP: #{spammer_ip}\n"
... ...
app/models/suggest_article.rb
... ... @@ -11,6 +11,17 @@ class SuggestArticle &lt; Task
11 11 settings_items :source, :type => String
12 12 settings_items :source_name, :type => String
13 13 settings_items :highlighted, :type => :boolean, :default => false
  14 + settings_items :ip_address, :type => String
  15 + settings_items :user_agent, :type => String
  16 + settings_items :referrer, :type => String
  17 +
  18 + after_create :schedule_spam_checking
  19 +
  20 + def schedule_spam_checking
  21 + self.delay.check_for_spam
  22 + end
  23 +
  24 + include Noosfero::Plugin::HotSpot
14 25  
15 26 def sender
16 27 "#{name} (#{email})"
... ... @@ -61,4 +72,12 @@ class SuggestArticle &lt; Task
61 72 _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name }
62 73 end
63 74  
  75 + def after_spam!
  76 + SpammerLogger.log(ip_address, self)
  77 + self.delay.marked_as_spam
  78 + end
  79 +
  80 + def after_ham!
  81 + self.delay.marked_as_ham
  82 + end
64 83 end
... ...
app/models/task.rb
... ... @@ -235,6 +235,8 @@ class Task &lt; ActiveRecord::Base
235 235 end
236 236 end
237 237  
  238 + include Spammable
  239 +
238 240 protected
239 241  
240 242 # This method must be overrided in subclasses, and its implementation must do
... ... @@ -275,6 +277,7 @@ class Task &lt; ActiveRecord::Base
275 277 named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} }
276 278 named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} }
277 279  
  280 +
278 281 named_scope :to, lambda { |profile|
279 282 environment_condition = nil
280 283 if profile.person?
... ...
app/models/uploaded_file.rb
... ... @@ -60,12 +60,20 @@ class UploadedFile &lt; Article
60 60  
61 61 postgresql_attachment_fu
62 62  
  63 + # Use this method only to get the generic icon for this kind of content.
  64 + # If you want the specific icon for a file type or the iconified version
  65 + # of an image, use FilePresenter.for(uploaded_file).icon_name
63 66 def self.icon_name(article = nil)
64   - if article
65   - article.image? ? article.public_filename(:icon) : (article.mime_type ? article.mime_type.gsub(/[\/+.]/, '-') : 'upload-file')
66   - else
67   - 'upload-file'
  67 + unless article.nil?
  68 + warn = ('='*80) + "\n" +
  69 + 'The method `UploadedFile.icon_name(obj)` is deprecated. ' +
  70 + 'You must to encapsulate UploadedFile with `FilePresenter.for()`.' +
  71 + "\n" + ('='*80)
  72 + raise NoMethodError, warn if ENV['RAILS_ENV'] == 'test'
  73 + Rails.logger.warn warn if Rails.logger
  74 + puts warn if ENV['RAILS_ENV'] == 'development'
68 75 end
  76 + 'upload-file'
69 77 end
70 78  
71 79 def mime_type
... ... @@ -91,40 +99,27 @@ class UploadedFile &lt; Article
91 99 end
92 100  
93 101 def to_html(options = {})
  102 + warn = ('='*80) + "\n" +
  103 + 'The method `UploadedFile#to_html()` is deprecated. ' +
  104 + 'You must to encapsulate UploadedFile with `FilePresenter.for()`.' +
  105 + "\n" + ('='*80)
  106 + raise NoMethodError, warn if ENV['RAILS_ENV'] == 'test'
  107 + Rails.logger.warn warn if Rails.logger
  108 + puts warn if ENV['RAILS_ENV'] == 'development'
94 109 article = self
95 110 if image?
96 111 lambda do
97   - if article.gallery? && options[:gallery_view]
98   - images = article.parent.images
99   - current_index = images.index(article)
100   - total_of_images = images.count
101   -
102   - link_to_previous = if current_index >= 1
103   - link_to(_('&laquo; Previous'), images[current_index - 1].view_url, :class => 'left')
104   - else
105   - content_tag('span', _('&laquo; Previous'), :class => 'left')
106   - end
107   -
108   - link_to_next = if current_index < total_of_images - 1
109   - link_to(_('Next &raquo;'), images[current_index + 1].view_url, :class => 'right')
110   - else
111   - content_tag('span', _('Next &raquo;'), :class => 'right')
112   - end
113   -
114   - content_tag(
115   - 'div',
116   - link_to_previous + (content_tag('span', _('image %d of %d'), :class => 'total-of-images') % [current_index + 1, total_of_images]).html_safe + link_to_next,
117   - :class => 'gallery-navigation'
118   - )
119   - end.to_s +
120   - image_tag(article.public_filename(:display), :class => article.css_class_name, :style => 'max-width: 100%') +
121   - content_tag('p', article.abstract, :class => 'uploaded-file-description')
122   -
  112 + image_tag(article.public_filename(:display),
  113 + :class => article.css_class_name,
  114 + :style => 'max-width: 100%') +
  115 + content_tag('div', article.abstract, :class => 'uploaded-file-description')
123 116 end
124 117 else
125 118 lambda do
126   - content_tag('ul', content_tag('li', link_to(article.name, article.url, :class => article.css_class_name))) +
127   - content_tag('p', article.abstract, :class => 'uploaded-file-description')
  119 + content_tag('div',
  120 + link_to(article.name, article.url),
  121 + :class => article.css_class_name) +
  122 + content_tag('div', article.abstract, :class => 'uploaded-file-description')
128 123 end
129 124 end
130 125 end
... ...
app/presenters/generic.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +# Made to encapsulate any UploadedFile
  2 +class FilePresenter::Generic < FilePresenter
  3 + # if returns low priority, because it is generic.
  4 + def self.accepts?(f)
  5 + 1 if f.is_a? UploadedFile
  6 + end
  7 +end
... ...
app/presenters/image.rb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +class FilePresenter::Image < FilePresenter
  2 + def self.accepts?(f)
  3 + return nil unless f.respond_to? :image?
  4 + f.image? ? 10 : nil
  5 + end
  6 +
  7 + def icon_name
  8 + public_filename :icon
  9 + end
  10 +
  11 + def short_description
  12 + _('Image (%s)') % content_type.split('/')[1].upcase
  13 + end
  14 +end
... ...
app/views/cms/view.rhtml
... ... @@ -40,13 +40,16 @@
40 40 </tr>
41 41 <% end %>
42 42  
43   - <% @articles.each do |article| %>
  43 + <% @articles.each do |article| article = FilePresenter.for article %>
44 44 <tr title="<%= article.title%>" >
45   - <td>
  45 + <td class="article-name">
46 46 <%= link_to_article(article) %>
47 47 </td>
48   - <td>
49   - <%= article.class.short_description %>
  48 + <% short_description = article.respond_to?(:short_description) ?
  49 + article.short_description :
  50 + article.class.short_description %>
  51 + <td class="article-mime" title=<%= short_description.to_json %>>
  52 + <%= short_description %>
50 53 </td>
51 54 <td class="article-controls">
52 55 <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit) %>
... ...
app/views/file_presenter/_generic.html.erb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +<span class="download-link">
  2 + <span>Download</span>
  3 + <strong><%= link_to generic.filename, generic.public_filename %></strong>
  4 +</span>
  5 +
  6 +<div class="uploaded-file-description <%= 'empty' if generic.abstract.blank? %>">
  7 + <%= generic.abstract %>
  8 +</div>
  9 +
... ...
app/views/file_presenter/_image.html.erb 0 → 100644
... ... @@ -0,0 +1,36 @@
  1 +<% if image.gallery? && options[:gallery_view] %>
  2 +<%
  3 + images = image.parent.images
  4 + current_index = images.index(image.encapsulated_file)
  5 + total_of_images = images.count
  6 + link_to_previous = if current_index >= 1
  7 + link_to(_('&laquo; Previous'), images[current_index - 1].view_url, :class => 'previous')
  8 + else
  9 + content_tag('span', _('&laquo; Previous'), :class => 'previous')
  10 + end
  11 +
  12 + link_to_next = if current_index < total_of_images - 1
  13 + link_to(_('Next &raquo;'), images[current_index + 1].view_url, :class => 'next')
  14 + else
  15 + content_tag('span', _('Next &raquo;'), :class => 'next')
  16 + end
  17 +%>
  18 +
  19 +<div class="gallery-navigation">
  20 + <%= link_to_previous %>
  21 + <span class="total-of-images">
  22 + <%= _('image %d of %d') % [current_index + 1, total_of_images] %>
  23 + </span>
  24 + <%= link_to_next %>
  25 +</div>
  26 +
  27 +<% end %>
  28 +
  29 +<%# image_tag(article.public_filename(:display), :class => article.css_class_name, :style => 'max-width: 100%') %>
  30 +
  31 +<img src="<%=image.public_filename(:display)%>" class="<%=image.css_class_name%>">
  32 +
  33 +<div class="uploaded-file-description <%= 'empty' if image.abstract.blank? %>">
  34 + <%= image.abstract %>
  35 +</div>
  36 +
... ...
app/views/shared/_manage_enterprises.rhtml
... ... @@ -1,8 +0,0 @@
1   -<div id='manage-enterprises'>
2   - <a href="#" id='manage-enterprises-link' class='simplemenu-trigger' title='<%= _('Manage enterprises') %>'><i class="icon-menu-enterprise"></i><strong><%= ui_icon('ui-icon-triangle-1-s') + _('My enterprises') %></strong></a>
3   - <ul class='simplemenu-submenu'>
4   - <% enterprises_link.each do |link| %>
5   - <li class='simplemenu-item'><%= link %></li>
6   - <% end %>
7   - </ul>
8   -</div>
app/views/shared/_manage_link.html.erb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<div id=<%= "manage-#{kind}" %> class="manage-groups">
  2 + <a href="#" id=<%= "manage-#{kind}-link" %> class="simplemenu-trigger" title="<%= _('Manage %s') % kind %>"><i class=<%= "icon-menu-#{kind.singularize}" %>></i><strong><%= ui_icon('ui-icon-triangle-1-s') + _('My %s') % kind %></strong></a>
  3 + <ul class="simplemenu-submenu">
  4 + <% link.each do |link| %>
  5 + <li class="simplemenu-item"><%= link %></li>
  6 + <% end %>
  7 + </ul>
  8 +</div>
... ...
app/views/spam/_comment_spam.rhtml 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %>
  2 +<div id='article'>
  3 + <div class="comments" id="comments_list">
  4 + <ul class="article-comments-list">
  5 + <%= render :partial => 'comment/comment', :collection => @comment_spam %>
  6 + </ul>
  7 + </div>
  8 +</div>
  9 +
  10 +<%= pagination_links @comment_spam, :param_name => :comments_page %>
  11 +
... ...
app/views/spam/_suggest_article.html.erb 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +<% render :layout => 'task', :locals => { :task => task } do %>
  2 + <% content_for :extra_buttons do %>
  3 + <%= button_to_function('down', _('Show details'), "toggleDetails(this, '#{_('Hide details')}', '#{_('Show details')}')" ) %>
  4 + <% end %>
  5 +
  6 + <% content_for :extra_content do %>
  7 + <ul class="suggest-article-details" style="display: none">
  8 + <li><strong><%=_('Sent by')%></strong>: <%=task.name%> </li>
  9 + <li><strong><%=_('Email')%></strong>: <%=task.email%> </li>
  10 + <li><strong><%=_('Source')%></strong>: <%=task.source_name%> </li>
  11 + <li><strong><%=_('Source URL')%></strong>: <%=task.source%> </li>
  12 + <li><strong><%=_('Folder')%></strong>: <%=(a = Article.find_by_id(task.article_parent_id))?a.name : '<em>' + s_('Folder|none') + '</em>'%> </li>
  13 + <li><strong><%=_('Lead')%></strong>: <%=task.article_abstract.blank? ? '<em>' + s_('Abstract|empty') + '</em>' : task.article_abstract%> </li>
  14 + <li><strong><%=_('Body')%></strong>:
  15 + <div class='suggest-article-body'>
  16 + <%= task.article_body %>
  17 + </div>
  18 + </li>
  19 + </ul>
  20 + <% end %>
  21 +<% end %>
... ...
app/views/spam/_task.rhtml 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +<div class="task_box" id="task-<%= task.id %>">
  2 + <%= render :partial => 'tasks/task_icon', :locals => {:task => task} %>
  3 + <%= render :partial => 'tasks/task_title', :locals => {:task => task} %>
  4 + <div class="task-information">
  5 + <%= task_information(task) %>
  6 + </div>
  7 +
  8 + <%= yield %> <% # ??? %>
  9 +
  10 + <% button_bar do %>
  11 + <%= button_to_function('new', _('Mark as NOT SPAM'), 'removeTaskBox(this, %s, "%s", "")' % [url_for(:mark_task_as_ham => task.id).to_json, "task-#{task.id}"]) %>
  12 + <%= yield :extra_buttons %>
  13 + <%= button_to_function('delete', _('Remove'), 'removeTaskBox(this, %s, "%s", %s)' % [url_for(:profile => params[:profile], :remove_task => task.id).to_json, "task-#{task.id}", _('Are you sure you want to remove this article suggestion?').to_json]) %>
  14 +
  15 + <% end %>
  16 +
  17 + <%= yield :extra_content %>
  18 +</div>
... ...
app/views/spam/_task_spam.rhtml 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +<% @task_spam.each do |t| %>
  2 + <%= render :partial => partial_for_class(t.class), :locals => { :task => t } %>
  3 +<% end %>
  4 +<%= pagination_links @task_spam, :param_name => :tasks_page %>
... ...
app/views/spam/index.rhtml
  1 +<%= stylesheet('tasks') %>
  2 +
1 3 <h1><%= _('Manage SPAM') %></h1>
2 4  
  5 +<% no_tabs = @comment_spam.blank? && @task_spam.blank? %>
  6 +
  7 +<%= _('There are no spams to review.') if no_tabs %>
  8 +
3 9 <% button_bar do %>
4 10 <%= button :back, _('Back to control panel'), :controller => :profile_editor %>
5 11 <% end %>
6 12  
7 13 <%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %>
8   -<div id='article'>
9   - <div class="comments" id="comments_list">
10   - <ul class="article-comments-list">
11   - <%= render :partial => 'comment/comment', :collection => @spam %>
12   - </ul>
13   - </div>
14   -</div>
15 14  
16   -<%= pagination_links @spam %>
  15 +<% tabs = [] %>
  16 +<% tabs << {:title => _('Comment Spam'), :id => 'comment-spam',
  17 + :content => (render :partial => 'comment_spam')} if @comment_spam.present? %>
  18 +<% tabs << {:title => _('Task Spam'), :id => 'task-spam',
  19 + :content => (render :partial => 'task_spam') } if @task_spam.present? %>
  20 +<%= render_tabs(tabs) %>
17 21  
18   -<% button_bar do %>
19   - <%= button :back, _('Back to control panel'), :controller => :profile_editor %>
  22 +<% unless no_tabs %>
  23 + <% button_bar do %>
  24 + <%= button :back, _('Back to control panel'), :controller => :profile_editor %>
  25 + <% end %>
20 26 <% end %>
  27 +
  28 +<%= javascript_include_tag 'spam' %>
... ...
app/views/tasks/_task.rhtml
1 1 <div class="task_box" id="task-<%= task.id %>">
2 2  
3   - <div class="task_icon">
4   - <%
5   - icon_info = task.icon
6   - if icon_info[:type] == :profile_image
7   - icon = profile_image(icon_info[:profile], :minor)
8   - elsif icon_info[:type] == :defined_image
9   - icon = "<img src='#{icon_info[:src]}' alt='#{icon_info[:name]}' />"
10   - end
11   - %>
12   - <%=
13   - if icon_info[:url]
14   - link_to(icon, icon_info[:url])
15   - else
16   - icon
17   - end
18   - %>
19   -
20   - </div>
  3 + <%= render :partial => 'task_icon', :locals => {:task => task} %>
21 4  
22 5 <div class="task_decisions">
23 6 <%=
... ... @@ -39,9 +22,7 @@
39 22 %>
40 23 </div><!-- class="task_decisions" -->
41 24  
42   - <strong class="task_title">
43   - <%= task.title %>
44   - </strong>
  25 + <%= render :partial => 'task_title', :locals => {:task => task} %>
45 26  
46 27 <div class="task_information">
47 28 <%= task_information(task) %>
... ...
app/views/tasks/_task_icon.rhtml 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +<%
  2 + icon_info = task.icon
  3 + if icon_info[:type] == :profile_image
  4 + icon = profile_image(icon_info[:profile], :minor)
  5 + elsif icon_info[:type] == :defined_image
  6 + icon = "<img src='#{icon_info[:src]}' alt='#{icon_info[:name]}' />"
  7 + end
  8 +
  9 + if icon_info[:url]
  10 + icon = link_to(icon, icon_info[:url])
  11 + end
  12 +%>
  13 +
  14 +<div class="task_icon">
  15 + <%= icon %>
  16 +</div>
... ...
app/views/tasks/_task_title.rhtml 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<strong class="task_title">
  2 + <%= task.title %>
  3 +</strong>
... ...
config/initializers/plugins.rb
... ... @@ -4,4 +4,5 @@ require &#39;noosfero/plugin/manager&#39;
4 4 require 'noosfero/plugin/active_record'
5 5 require 'noosfero/plugin/mailer_base'
6 6 require 'noosfero/plugin/settings'
  7 +require 'noosfero/plugin/spammable'
7 8 Noosfero::Plugin.init_system if $NOOSFERO_LOAD_PLUGINS
... ...
db/migrate/20131011164400_add_spam_to_task.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +class AddSpamToTask < ActiveRecord::Migration
  2 + def self.up
  3 + change_table :tasks do |t|
  4 + t.boolean :spam, :default => false
  5 + end
  6 + Task.update_all ["spam = ?", false]
  7 + add_index :tasks, [:spam]
  8 + end
  9 +
  10 + def self.down
  11 + remove_column :tasks, :spam
  12 + end
  13 +end
... ...
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 => 20130711213046) do
  12 +ActiveRecord::Schema.define(:version => 20131011164400) do
13 13  
14 14 create_table "abuse_reports", :force => true do |t|
15 15 t.integer "reporter_id"
... ... @@ -547,8 +547,11 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do
547 547 t.datetime "created_at"
548 548 t.string "target_type"
549 549 t.integer "image_id"
  550 + t.boolean "spam", :default => false
550 551 end
551 552  
  553 + add_index "tasks", ["spam"], :name => "index_tasks_on_spam"
  554 +
552 555 create_table "thumbnails", :force => true do |t|
553 556 t.integer "size"
554 557 t.string "content_type"
... ...
lib/file_presenter.rb 0 → 100644
... ... @@ -0,0 +1,107 @@
  1 +# All file presenters must extends `FilePresenter` not only to ensure the
  2 +# same interface, but also to make `FilePresenter.for(file)` to work.
  3 +class FilePresenter
  4 +
  5 + # Will return a encapsulated `UploadedFile` or the same object if no
  6 + # one accepts it. That behave allow to give any model to this class,
  7 + # like a Article and have no trouble with that.
  8 + def self.for(f)
  9 + return f if f.is_a? FilePresenter
  10 + klass = FilePresenter.subclasses.sort_by {|class_name|
  11 + class_name.constantize.accepts?(f) || 0
  12 + }.last.constantize
  13 + klass.accepts?(f) ? klass.new(f) : f
  14 + end
  15 +
  16 + def initialize(f)
  17 + @file = f
  18 + end
  19 +
  20 + # Allows to use the original `UploadedFile` reference.
  21 + def encapsulated_file
  22 + @file
  23 + end
  24 +
  25 + def id
  26 + @file.id
  27 + end
  28 +
  29 + def reload
  30 + @file.reload
  31 + self
  32 + end
  33 +
  34 + # This method must be overridden in subclasses.
  35 + #
  36 + # If the class accepts the file, return a number that represents the
  37 + # priority the class should be given to handle that file. Higher numbers
  38 + # mean higher priority.
  39 + #
  40 + # If the class does not accept the file, return false.
  41 + def self.accepts?(f)
  42 + nil
  43 + end
  44 +
  45 + def short_description
  46 + _("File (%s)") % content_type.sub(/^application\//, '').sub(/^x-/, '').sub(/^image\//, '')
  47 + end
  48 +
  49 + # Define the css classes to style the page fragment with the file related
  50 + # content. If you want other classes to identify this area to your
  51 + # customized presenter, so do this:
  52 + # def css_class_list
  53 + # [super, 'myclass'].flatten
  54 + # end
  55 + def css_class_list
  56 + [ @file.css_class_list,
  57 + 'file-' + self.class.to_s.split(/:+/).map(&:underscore)[1..-1].join('-'),
  58 + 'content-type_' + self.content_type.split('/')[0],
  59 + 'content-type_' + self.content_type.gsub(/[^a-z0-9]/i,'-')
  60 + ].flatten
  61 + end
  62 +
  63 + # Enable file presenter to customize the css classes on view_page.rhtml
  64 + # You may not overwrite this method on your customized presenter.
  65 + def css_class_name
  66 + [css_class_list].flatten.compact.join(' ')
  67 + end
  68 +
  69 + # The generic icon class-name or the specific file path.
  70 + # You may replace this method on your custom FilePresenter.
  71 + # See the current used icons class-names in public/designs/icons/tango/style.css
  72 + def icon_name
  73 + if mime_type
  74 + [ mime_type.split('/')[0], mime_type.gsub(/[^a-z0-9]/i, '-') ]
  75 + else
  76 + 'upload-file'
  77 + end
  78 + end
  79 +
  80 + # Automatic render `file_presenter/<custom>.html.erb` to display your
  81 + # custom presenter html content.
  82 + # You may not overwrite this method on your customized presenter.
  83 + # A variable with the same presenter name will be created to refer
  84 + # to the file object.
  85 + # Example:
  86 + # The `FilePresenter::Image` render `file_presenter/image.html.erb`
  87 + # inside the `file_presenter/image.html.erb` you can access the
  88 + # required `FilePresenter::Image` instance in the `image` variable.
  89 + def to_html(options = {})
  90 + file = self
  91 + lambda do
  92 + render :partial => file.class.to_s.underscore,
  93 + :locals => { :options => options },
  94 + :object => file
  95 + end
  96 + end
  97 +
  98 + # That makes the presenter to works like any other `UploadedFile` instance.
  99 + def method_missing(m, *args)
  100 + @file.send(m, *args)
  101 + end
  102 +end
  103 +
  104 +# Preload FilePresenters to allow `FilePresenter.for()` to work
  105 +Dir.glob(File.join('app', 'presenters', '*.rb')) do |file|
  106 + load file
  107 +end
... ...
lib/noosfero/plugin.rb
... ... @@ -155,6 +155,7 @@ class Noosfero::Plugin
155 155  
156 156 # Here the developer may specify the events to which the plugins can
157 157 # register and must return true or false. The default value must be false.
  158 + # Must also explicitly define its returning variables.
158 159  
159 160 # -> If true, noosfero will include plugin_dir/public/style.css into
160 161 # application
... ... @@ -162,10 +163,6 @@ class Noosfero::Plugin
162 163 false
163 164 end
164 165  
165   - # Here the developer should specify the events to which the plugins can
166   - # register to. Must be explicitly defined its returning
167   - # variables.
168   -
169 166 # -> Adds buttons to the control panel
170 167 # returns = { :title => title, :icon => icon, :url => url }
171 168 # title = name that will be displayed.
... ... @@ -175,6 +172,13 @@ class Noosfero::Plugin
175 172 nil
176 173 end
177 174  
  175 + # -> Customize profile block design and behavior
  176 + # (overwrites profile_image_link function)
  177 + # returns = lambda block that creates html code.
  178 + def profile_image_link(profile, size, tag, extra_info)
  179 + nil
  180 + end
  181 +
178 182 # -> Adds tabs to the profile
179 183 # returns = { :title => title, :id => id, :content => content, :start => start }
180 184 # title = name that will be displayed.
... ... @@ -304,45 +308,16 @@ class Noosfero::Plugin
304 308 scope
305 309 end
306 310  
307   - # This method is called by the CommentHandler background job before sending
308   - # the notification email. If the comment is marked as spam (i.e. by calling
309   - # <tt>comment.spam!</tt>), then the notification email will *not* be sent.
310   - #
311   - # example:
312   - #
313   - # def check_comment_for_spam(comment)
314   - # if anti_spam_service.is_spam?(comment)
315   - # comment.spam!
316   - # end
317   - # end
318   - #
319   - def check_comment_for_spam(comment)
  311 + # -> Allows plugins to check weather object is a spam
  312 + def check_for_spam(object)
320 313 end
321 314  
322   - # This method is called when the user manually marks a comment as SPAM. A
323   - # plugin implementing this method should train its spam detection mechanism
324   - # by submitting this comment as a confirmed spam.
325   - #
326   - # example:
327   - #
328   - # def comment_marked_as_spam(comment)
329   - # anti_spam_service.train_with_spam(comment)
330   - # end
331   - #
332   - def comment_marked_as_spam(comment)
  315 + # -> Allows plugins to know when an object is marked as a spam
  316 + def marked_as_spam(object)
333 317 end
334 318  
335   - # This method is called when the user manually marks a comment a NOT SPAM. A
336   - # plugin implementing this method should train its spam detection mechanism
337   - # by submitting this coimment as a confirmed ham.
338   - #
339   - # example:
340   - #
341   - # def comment_marked_as_ham(comment)
342   - # anti_spam_service.train_with_ham(comment)
343   - # end
344   - #
345   - def comment_marked_as_ham(comment)
  319 + # -> Allows plugins to know when an object is marked as a ham
  320 + def marked_as_ham(object)
346 321 end
347 322  
348 323 # Adds extra actions for comments
... ...
lib/noosfero/plugin/manager.rb
... ... @@ -34,18 +34,20 @@ class Noosfero::Plugin::Manager
34 34 alias :dispatch_scopes :dispatch_without_flatten
35 35  
36 36 def dispatch_first(event, *args)
37   - result = nil
  37 + default = Noosfero::Plugin.new.send(event, *args)
  38 + result = default
38 39 each do |plugin|
39 40 result = plugin.send(event, *args)
40   - break if result.present?
  41 + break if result != default
41 42 end
42 43 result
43 44 end
44 45  
45 46 def fetch_first_plugin(event, *args)
  47 + default = Noosfero::Plugin.new.send(event, *args)
46 48 result = nil
47 49 each do |plugin|
48   - if plugin.send(event, *args)
  50 + if plugin.send(event, *args) != default
49 51 result = plugin.class
50 52 break
51 53 end
... ...
lib/noosfero/plugin/spammable.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +Spammable.module_eval do
  2 + def marked_as_spam
  3 + plugins.dispatch(:marked_as_spam, self)
  4 + end
  5 +
  6 + def marked_as_ham
  7 + plugins.dispatch(:marked_as_ham, self)
  8 + end
  9 +
  10 + def check_for_spam
  11 + plugins.dispatch(:check_for_spam, self)
  12 + end
  13 +end
... ...
lib/spammable.rb 0 → 100644
... ... @@ -0,0 +1,47 @@
  1 +module Spammable
  2 + def self.included(recipient)
  3 + raise "This model should have a spam attribute!" if !recipient.new.respond_to?('spam=')
  4 + recipient.extend(ClassMethods)
  5 + end
  6 +
  7 + module ClassMethods
  8 + def self.extended (base)
  9 + if base.respond_to?(:named_scope)
  10 + base.class_eval do
  11 + named_scope :without_spam, :conditions => ['spam IS NULL OR spam = ?', false]
  12 + named_scope :spam, :conditions => ['spam = ?', true]
  13 + end
  14 + end
  15 + end
  16 + end
  17 +
  18 + def spam?
  19 + !spam.nil? && spam
  20 + end
  21 +
  22 + def ham?
  23 + !spam.nil? && !spam
  24 + end
  25 +
  26 + def spam!
  27 + before_spam!
  28 + self.spam = true
  29 + self.save!
  30 + after_spam!
  31 + self
  32 + end
  33 +
  34 + def ham!
  35 + before_ham!
  36 + self.spam = false
  37 + self.save!
  38 + after_ham!
  39 + self
  40 + end
  41 +
  42 + def after_spam!; end
  43 + def before_spam!; end
  44 +
  45 + def after_ham!; end
  46 + def before_ham!; end
  47 +end
... ...
plugins/anti_spam/lib/anti_spam_plugin.rb
... ... @@ -5,38 +5,37 @@ class AntiSpamPlugin &lt; Noosfero::Plugin
5 5 end
6 6  
7 7 def self.plugin_description
8   - _("Checks comments against a spam checking service compatible with the Akismet API")
  8 + _("Tests comments and suggested articles against a spam checking service compatible with the Akismet API")
9 9 end
10 10  
11 11 def self.host_default_setting
12 12 'api.antispam.typepad.com'
13 13 end
14 14  
15   - def check_comment_for_spam(comment)
16   - if rakismet_call(comment, :spam?)
17   - comment.spam = true
18   - comment.save!
  15 + def check_for_spam(object)
  16 + if rakismet_call AntiSpamPlugin::Wrapper.wrap(object), object.environment, :spam?
  17 + object.spam = true
  18 + object.save!
19 19 end
20 20 end
21 21  
22   - def comment_marked_as_spam(comment)
23   - rakismet_call(comment, :spam!)
  22 + def marked_as_spam(object)
  23 + rakismet_call AntiSpamPlugin::Wrapper.wrap(object), object.environment, :spam!
24 24 end
25 25  
26   - def comment_marked_as_ham(comment)
27   - rakismet_call(comment, :ham!)
  26 + def marked_as_ham(object)
  27 + rakismet_call AntiSpamPlugin::Wrapper.wrap(object), object.environment, :ham!
28 28 end
29 29  
30 30 protected
31 31  
32   - def rakismet_call(comment, op)
33   - settings = Noosfero::Plugin::Settings.new(comment.environment, self.class)
  32 + def rakismet_call(submission, environment, op)
  33 + settings = Noosfero::Plugin::Settings.new(environment, self.class)
34 34  
35 35 Rakismet.host = settings.host
36 36 Rakismet.key = settings.api_key
37   - Rakismet.url = comment.environment.top_url
  37 + Rakismet.url = environment.top_url
38 38  
39   - submission = AntiSpamPlugin::CommentWrapper.new(comment)
40 39 submission.send(op)
41 40 end
42 41  
... ...
plugins/anti_spam/lib/anti_spam_plugin/comment_wrapper.rb
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   -
  1 +class AntiSpamPlugin::CommentWrapper < AntiSpamPlugin::Wrapper
  2 + alias_attribute :author, :author_name
  3 + alias_attribute :user_ip, :ip_address
  4 + alias_attribute :content, :body
  5 +
  6 + def self.wraps?(object)
  7 + object.kind_of?(Comment)
  8 + end
11 9 end
... ...
plugins/anti_spam/lib/anti_spam_plugin/suggest_article_wrapper.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +class AntiSpamPlugin::SuggestArticleWrapper < AntiSpamPlugin::Wrapper
  2 + alias_attribute :author, :name
  3 + alias_attribute :author_email, :email
  4 + alias_attribute :user_ip, :ip_address
  5 + alias_attribute :content, :article_body
  6 +
  7 + def self.wraps?(object)
  8 + object.kind_of?(SuggestArticle)
  9 + end
  10 +end
... ...
plugins/anti_spam/lib/anti_spam_plugin/wrapper.rb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +class AntiSpamPlugin::Wrapper < SimpleDelegator
  2 + include Rakismet::Model
  3 +
  4 + @@wrappers = []
  5 +
  6 + def self.wrap(object)
  7 + wrapper = @@wrappers.find { |wrapper| wrapper.wraps?(object) }
  8 + wrapper ? wrapper.new(object) : object
  9 + end
  10 +
  11 + def self.wraps?(object)
  12 + false
  13 + end
  14 +
  15 + def self.inherited(child)
  16 + @@wrappers << child
  17 + end
  18 +end
... ...
plugins/anti_spam/test/unit/anti_spam_plugin/comment_wrapper_test.rb
1 1 require 'test_helper'
2 2  
3   -class AntiSpamPluginCommentWrapperTest < ActiveSupport::TestCase
  3 +class AntiSpamPlugin::CommentWrapperTest < ActiveSupport::TestCase
4 4  
5 5 def setup
6 6 @comment = Comment.new(
... ... @@ -15,10 +15,6 @@ class AntiSpamPluginCommentWrapperTest &lt; ActiveSupport::TestCase
15 15 @wrapper = AntiSpamPlugin::CommentWrapper.new(@comment)
16 16 end
17 17  
18   - should 'use Rakismet::Model' do
19   - assert_includes @wrapper.class.included_modules, Rakismet::Model
20   - end
21   -
22 18 should 'get contents' do
23 19 assert_equal @comment.body, @wrapper.content
24 20 end
... ...
plugins/anti_spam/test/unit/anti_spam_plugin/suggest_article_wrapper_test.rb 0 → 100644
... ... @@ -0,0 +1,41 @@
  1 +require 'test_helper'
  2 +
  3 +class AntiSpamPlugin::SuggestArticleWrapperTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @suggest_article = SuggestArticle.new(
  7 + :article_body => 'comment body',
  8 + :name => 'author',
  9 + :email => 'foo@example.com',
  10 + :ip_address => '1.2.3.4',
  11 + :user_agent => 'Some Good Browser (I hope)',
  12 + :referrer => 'http://noosfero.org/'
  13 + )
  14 + @wrapper = AntiSpamPlugin::SuggestArticleWrapper.new(@suggest_article)
  15 + end
  16 +
  17 + should 'get contents' do
  18 + assert_equal @suggest_article.article_body, @wrapper.content
  19 + end
  20 +
  21 + should 'get author name' do
  22 + assert_equal @suggest_article.name, @wrapper.author
  23 + end
  24 +
  25 + should 'get author email' do
  26 + assert_equal @suggest_article.email, @wrapper.author_email
  27 + end
  28 +
  29 + should 'get IP address' do
  30 + assert_equal @suggest_article.ip_address, @wrapper.user_ip
  31 + end
  32 +
  33 + should 'get User-Agent' do
  34 + assert_equal @suggest_article.user_agent, @wrapper.user_agent
  35 + end
  36 +
  37 + should 'get get Referrer' do
  38 + assert_equal @suggest_article.referrer, @wrapper.referrer
  39 + end
  40 +
  41 +end
... ...
plugins/anti_spam/test/unit/anti_spam_plugin/wrapper_test.rb 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +require 'test_helper'
  2 +require 'anti_spam_plugin/wrapper'
  3 +
  4 +class AntiSpamPluginWrapperTest < ActiveSupport::TestCase
  5 + should 'use Rakismet::Model' do
  6 + wrapped = AntiSpamPlugin::Wrapper.new(mock)
  7 + assert_includes wrapped.class.included_modules, Rakismet::Model
  8 + end
  9 +
  10 + should 'wrap object according to wraps? method' do
  11 + class EvenWrapper < AntiSpamPlugin::Wrapper
  12 + def self.wraps?(object)
  13 + object % 2 == 0
  14 + end
  15 + end
  16 + class OddWrapper < AntiSpamPlugin::Wrapper
  17 + def self.wraps?(object)
  18 + object % 2 != 0
  19 + end
  20 + end
  21 +
  22 + assert AntiSpamPlugin::Wrapper.wrap(5).kind_of?(OddWrapper)
  23 + assert AntiSpamPlugin::Wrapper.wrap(6).kind_of?(EvenWrapper)
  24 + end
  25 +end
... ...
plugins/anti_spam/test/unit/anti_spam_plugin_test.rb
... ... @@ -2,35 +2,36 @@ require &#39;test_helper&#39;
2 2  
3 3 class AntiSpamPluginTest < ActiveSupport::TestCase
4 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')
  5 + class SpammableContent
  6 + attr_accessor :spam
  7 + include Spammable
9 8  
10   - @settings = Noosfero::Plugin::Settings.new(@comment.environment, AntiSpamPlugin)
11   - @settings.api_key = 'b8b80ddb8084062d0c9119c945ce3bc3'
12   - @settings.save!
  9 + def save!; end
  10 + def environment; Environment.default; end
  11 + end
13 12  
  13 + def setup
  14 + @spammable = SpammableContent.new
14 15 @plugin = AntiSpamPlugin.new
15   - @plugin.context = @comment
16 16 end
17 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!)
  18 + attr_accessor :spammable
21 19  
22   - @plugin.check_comment_for_spam(@comment)
23   - assert @comment.spam
24   - end
  20 + should 'check for spam and mark as spam if server says it is spam' do
  21 + spammable.expects(:spam?).returns(true)
  22 + spammable.expects(:save!)
25 23  
26   - should 'report spam' do
27   - AntiSpamPlugin::CommentWrapper.any_instance.expects(:spam!)
28   - @plugin.comment_marked_as_spam(@comment)
  24 + @plugin.check_for_spam(spammable)
  25 + assert spammable.spam
29 26 end
30 27  
31   - should 'report ham' do
32   - AntiSpamPlugin::CommentWrapper.any_instance.expects(:ham!)
33   - @plugin.comment_marked_as_ham(@comment)
  28 + should 'report comment spam' do
  29 + spammable.expects(:spam!)
  30 + @plugin.marked_as_spam(spammable)
34 31 end
35 32  
  33 + should 'report comment ham' do
  34 + spammable.expects(:ham!)
  35 + @plugin.marked_as_ham(spammable)
  36 + end
36 37 end
... ...
plugins/html5_video/lib/file_presenter/video.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +class FilePresenter::Video < FilePresenter
  2 + def self.accepts?(f)
  3 + return nil if !f.respond_to?(:content_type) || f.content_type.nil?
  4 + ( f.content_type[0..4] == 'video' ) ? 10 : nil
  5 + end
  6 +
  7 + def short_description
  8 + _('Video (%s)') % content_type.split('/')[1].upcase
  9 + end
  10 +end
... ...
plugins/html5_video/lib/html5_video_plugin.rb 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +class Html5VideoPlugin < Noosfero::Plugin
  2 +
  3 + FilePresenter::Video
  4 +
  5 + def self.plugin_name
  6 + "HTML5 Video"
  7 + end
  8 +
  9 + def self.plugin_description
  10 + _("A plugin to enable the video suport, with auto conversion for the web.")
  11 + end
  12 +
  13 +end
... ...
plugins/html5_video/test/functional/content_viewer_controler_test.rb 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +require File.dirname(__FILE__) + '/../../../../test/test_helper'
  2 +require 'content_viewer_controller'
  3 +
  4 +class ContentViewerController
  5 + # Re-raise errors caught by the controller.
  6 + def rescue_action(e) raise e end
  7 + append_view_path File.join(File.dirname(__FILE__) + '/../../views')
  8 +end
  9 +
  10 +class ContentViewerControllerTest < ActionController::TestCase
  11 +
  12 + all_fixtures
  13 +
  14 + def setup
  15 + @controller = ContentViewerController.new
  16 + @request = ActionController::TestRequest.new
  17 + @response = ActionController::TestResponse.new
  18 +
  19 + @profile = create_user('testinguser').person
  20 + @environment = @profile.environment
  21 + end
  22 + attr_reader :profile, :environment
  23 +
  24 + should 'add html5 video tag to the page of file type video' do
  25 + file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/test.txt', 'video/ogg'), :profile => profile)
  26 + get :view_page, file.url.merge(:view=>:true)
  27 + assert_select '#article video'
  28 + end
  29 +
  30 +end
... ...
plugins/html5_video/views/file_presenter/_video.html.erb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<video class="video-js vjs-default-skin" controls poster="video.jpg" preload="auto" data-setup="{}">
  2 + <source type="video/ogg" src="<%= video.public_filename %>"/>
  3 +</video>
  4 +
  5 +<div class="uploaded-file-description <%= 'empty' if video.abstract.blank? %>">
  6 + <%= video.abstract %>
  7 +</div>
  8 +
... ...
public/designs/icons/tango/ie6.css
... ... @@ -1,52 +0,0 @@
1   -.msie6 .icon-edit { background-image: url(ie6/Tango/16x16/apps/text-editor.gif) }
2   -.msie6 .icon-home { background-image: url(ie6/Tango/16x16/actions/go-home.gif) }
3   -.msie6 .icon-new,
4   -.msie6 .icon-suggest { background-image: url(ie6/Tango/16x16/actions/filenew.gif) }
5   -.msie6 .icon-close { background-image: url(ie6/Tango/16x16/actions/gtk-cancel.gif) }
6   -.msie6 .icon-newfolder { background-image: url(ie6/Tango/16x16/actions/folder-new.gif) }
7   -.msie6 .icon-save { background-image: url(ie6/Tango/16x16/actions/filesave.gif) }
8   -.msie6 .icon-send { background-image: url(ie6/Tango/16x16/actions/stock_mail-forward.gif) }
9   -.msie6 .icon-cancel { background-image: url(ie6/Tango/16x16/actions/gtk-cancel.gif) }
10   -.msie6 .icon-person { background-image: url(ie6/Tango/16x16/apps/system-config-users.gif) }
11   -.msie6 .icon-product { background-image: url(ie6/Tango/16x16/mimetypes/package.gif) }
12   -.msie6 .icon-delete { background-image: url(ie6/Tango/16x16/places/user-trash.gif) }
13   -.msie6 .icon-back { background-image: url(ie6/Tango/16x16/actions/back.gif) }
14   -.msie6 .icon-next { background-image: url(ie6/Tango/16x16/actions/go-next.gif) }
15   -.msie6 .icon-add { background-image: url(ie6/Tango/16x16/actions/add.gif) }
16   -.msie6 .icon-more { background-image: url(ie6/Tango/16x16/actions/add.gif) }
17   -.msie6 .icon-up { background-image: url(ie6/Tango/16x16/actions/go-up.gif) }
18   -.msie6 .icon-down { background-image: url(ie6/Tango/16x16/actions/go-down.gif) }
19   -.msie6 .icon-left { background-image: url(ie6/Tango/16x16/actions/go-previous.gif) }
20   -.msie6 .icon-right { background-image: url(ie6/Tango/16x16/actions/go-next.gif) }
21   -.msie6 .icon-up-disabled { background-image: url(ie6/Tango/16x16/actions/go-up.gif); opacity: 0.25; filter:alpha(opacity=25); }
22   -.msie6 .icon-down-disabled { background-image: url(ie6/Tango/16x16/actions/go-down.gif); opacity: 0.25; filter:alpha(opacity=25); }
23   -.msie6 .icon-left-disabled { background-image: url(ie6/Tango/16x16/actions/go-previous.gif); opacity: 0.25; filter:alpha(opacity=25); }
24   -.msie6 .icon-right-disabled { background-image: url(ie6/Tango/16x16/actions/go-next.gif); opacity: 0.25; filter:alpha(opacity=25); }
25   -.msie6 .icon-up-red { background-image: url(ie6/mod/16x16/actions/go-up-red.gif) }
26   -.msie6 .icon-forward { background-image: url(ie6/Tango/16x16/actions/go-next.gif) }
27   -.msie6 .icon-search { background-image: url(ie6/Tango/16x16/actions/search.gif) }
28   -.msie6 .icon-ok { background-image: url(ie6/Tango/16x16/actions/media-playback-start.gif) }
29   -.msie6 .icon-login { background-image: url(ie6/mod/16x16/actions/log-in.gif) }
30   -.msie6 .icon-help { background-image: url(ie6/Tango/16x16/apps/gnome-help.gif) }
31   -.msie6 .icon-firefox { background-image: url(firefox-24x24.gif) }
32   -.msie6 .icon-help32on { background-image: url(ie6/Tango/32x32/apps/gnome-help.gif) }
33   -.msie6 .icon-help32off { background-image: url(ie6/mod/32x32/apps/gnome-help-red.gif) }
34   -.msie6 .icon-spread { background-image: url(ie6/mod/16x16/actions/spread.gif) }
35   -.msie6 .icon-todo { background-image: url(ie6/Tango/16x16/actions/stock_paste.gif) }
36   -.msie6 .icon-eyes { background-image: url(ie6/Tango/16x16/actions/gtk-print-preview.gif) }
37   -.msie6 .icon-menu-home { background-image: url(ie6/Tango/16x16/actions/go-home.gif) }
38   -.msie6 .icon-menu-product { background-image: url(ie6/Tango/16x16/mimetypes/package.gif) }
39   -.msie6 .icon-menu-enterprise { background-image: url(ie6/Tango/16x16/actions/go-home.gif) }
40   -.msie6 .icon-menu-community { background-image: url(ie6/Tango/16x16/apps/system-config-users.gif) }
41   -.msie6 .icon-menu-ctrl-panel { background-image: url(ie6/Tango/16x16/categories/preferences-desktop.gif) }
42   -.msie6 .icon-menu-admin { background-image: url(ie6/Tango/16x16/categories/preferences-system.gif) }
43   -.msie6 .icon-menu-my-groups { background-image: url(ie6/Tango/16x16/apps/system-config-users.gif) }
44   -.msie6 .icon-menu-login { background-image: url(ie6/mod/16x16/actions/log-in.gif) }
45   -.msie6 .icon-menu-logout { background-image: url(ie6/Tango/16x16/actions/exit.gif) }
46   -.msie6 .icon-menu-search { background-image: url(ie6/Tango/16x16/actions/search.gif) }
47   -.msie6 .icon-menu-events { background-image: url(ie6/Tango/16x16/mimetypes/stock_calendar.gif) }
48   -.msie6 .icon-menu-articles { background-image: url(ie6/Tango/16x16/apps/text-editor.gif) }
49   -.msie6 .icon-menu-people { background-image: url(ie6/mod/16x16/apps/user.gif) }
50   -.msie6 .icon-menu-mail { background-image: url(ie6/Tango/16x16/apps/email.gif) }
51   -.msie6 .icon-upload-file { background-image: url(ie6/Tango/16x16/actions/filesave.gif) }
52   -.msie6 .icon-slideshow { background-image: url(ie6/Tango/16x16/mimetypes/x-office-presentation.gif) }
public/designs/icons/tango/ie6/Tango/16x16/actions/add.gif

192 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/back.gif

570 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/exit.gif

1.03 KB

public/designs/icons/tango/ie6/Tango/16x16/actions/filenew.gif

354 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/filesave.gif

750 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/folder-new.gif

599 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/go-down.gif

568 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/go-home.gif

587 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/go-next.gif

570 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/go-previous.gif

570 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/go-up.gif

572 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/gtk-cancel.gif

1022 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/gtk-print-preview.gif

1022 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/media-playback-start.gif

314 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/search.gif

601 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/stock_mail-forward.gif

601 Bytes

public/designs/icons/tango/ie6/Tango/16x16/actions/stock_paste.gif

595 Bytes

public/designs/icons/tango/ie6/Tango/16x16/apps/email.gif

347 Bytes

public/designs/icons/tango/ie6/Tango/16x16/apps/gnome-help.gif

1.01 KB

public/designs/icons/tango/ie6/Tango/16x16/apps/system-config-users.gif

1.05 KB

public/designs/icons/tango/ie6/Tango/16x16/apps/text-editor.gif

363 Bytes

public/designs/icons/tango/ie6/Tango/16x16/categories/preferences-desktop.gif

357 Bytes

public/designs/icons/tango/ie6/Tango/16x16/categories/preferences-system.gif

333 Bytes

public/designs/icons/tango/ie6/Tango/16x16/mimetypes/package.gif

573 Bytes

public/designs/icons/tango/ie6/Tango/16x16/mimetypes/stock_calendar.gif

382 Bytes

public/designs/icons/tango/ie6/Tango/16x16/mimetypes/x-office-presentation.gif

559 Bytes

public/designs/icons/tango/ie6/Tango/16x16/places/user-home.gif

1 KB

public/designs/icons/tango/ie6/Tango/16x16/places/user-trash.gif

1009 Bytes

public/designs/icons/tango/ie6/Tango/32x32/apps/gnome-help.gif

1.03 KB

public/designs/icons/tango/ie6/mod/16x16/actions/go-up-red.gif

361 Bytes

public/designs/icons/tango/ie6/mod/16x16/actions/log-in.gif

246 Bytes

public/designs/icons/tango/ie6/mod/16x16/actions/log-out.gif

241 Bytes

public/designs/icons/tango/ie6/mod/16x16/actions/password.gif

546 Bytes

public/designs/icons/tango/ie6/mod/16x16/actions/spread.gif

532 Bytes

public/designs/icons/tango/ie6/mod/16x16/apps/user.gif

620 Bytes

public/designs/icons/tango/ie6/mod/32x32/apps/gnome-help-red.gif

969 Bytes

public/designs/icons/tango/style.css
1   -@import url(ie6.css);
2   -
3 1 /******************SMALL ICONS********************/
4 2 .icon-edit { background-image: url(Tango/16x16/apps/text-editor.png) }
5   -.icon-home { background-image: url(Tango/16x16/actions/go-home.png) }
  3 +.icon-home { background-image: url(Tango/16x16/actions/go-home.png) }
6 4 .icon-new,
7   -.icon-suggest { background-image: url(Tango/16x16/actions/filenew.png) }
8   -.icon-close { background-image: url(Tango/16x16/actions/gtk-cancel.png) }
  5 +.icon-suggest { background-image: url(Tango/16x16/actions/filenew.png) }
  6 +.icon-close { background-image: url(Tango/16x16/actions/gtk-cancel.png) }
9 7 .icon-newfolder { background-image: url(Tango/16x16/actions/folder-new.png) }
10 8 .icon-folder { background-image: url(Tango/16x16/places/folder.png) }
11   -.icon-parent-folder { background-image: url(Tango/16x16/places/folder_home.png) }
  9 +.icon-parent-folder { background-image: url(Tango/16x16/places/folder_home.png) }
12 10 .icon-newblog { background-image: url(mod/16x16/apps/text-editor.png) }
13 11 .icon-blog { background-image: url(mod/16x16/apps/text-editor.png) }
14   -/*.icon-open { background-image: url(folder-open.gif) } UNUSED*/
15   -/*.icon-cms { background-image: url(abiword_48.png) } UNUSED*/
16   -.icon-save { background-image: url(Tango/16x16/actions/filesave.png) }
17   -.icon-send { background-image: url(Tango/16x16/actions/stock_mail-forward.png) }
18   -.icon-cancel { background-image: url(Tango/16x16/actions/gtk-cancel.png) }
19   -.icon-person { background-image: url(Tango/16x16/apps/system-config-users.png) }
20   -.icon-product { background-image: url(Tango/16x16/mimetypes/package.png) }
  12 +/*.icon-open { background-image: url(folder-open.gif) } UNUSED*/
  13 +/*.icon-cms { background-image: url(abiword_48.png) } UNUSED*/
  14 +.icon-save { background-image: url(Tango/16x16/actions/filesave.png) }
  15 +.icon-send { background-image: url(Tango/16x16/actions/stock_mail-forward.png) }
  16 +.icon-cancel { background-image: url(Tango/16x16/actions/gtk-cancel.png) }
  17 +.icon-person { background-image: url(Tango/16x16/apps/system-config-users.png) }
  18 +.icon-product { background-image: url(Tango/16x16/mimetypes/package.png) }
21 19 .icon-delete { background-image: url(Tango/16x16/places/user-trash.png) }
22   -/*.icon-find { background-image: url(noosfero-find.png) } UNUSED*/
23   -.icon-back { background-image: url(Tango/16x16/actions/back.png) }
24   -.icon-next { background-image: url(Tango/16x16/actions/go-next.png) }
25   -.icon-add { background-image: url(Tango/16x16/actions/add.png) }
26   -.icon-remove { background-image: url(Tango/16x16/actions/gtk-remove.png) }
27   -.icon-more { background-image: url(Tango/16x16/actions/add.png) }
28   -.icon-up { background-image: url(Tango/16x16/actions/go-up.png) }
29   -.icon-down { background-image: url(Tango/16x16/actions/go-down.png) }
30   -.icon-left { background-image: url(Tango/16x16/actions/go-previous.png) }
31   -.icon-right { background-image: url(Tango/16x16/actions/go-next.png) }
32   -.icon-up-disabled { background-image: url(Tango/16x16/actions/go-up.png); opacity: 0.25; filter:alpha(opacity=25); }
33   -.icon-down-disabled { background-image: url(Tango/16x16/actions/go-down.png); opacity: 0.25; filter:alpha(opacity=25); }
34   -.icon-left-disabled { background-image: url(Tango/16x16/actions/go-previous.png); opacity: 0.25; filter:alpha(opacity=25); }
35   -.icon-right-disabled { background-image: url(Tango/16x16/actions/go-next.png); opacity: 0.25; filter:alpha(opacity=25); }
36   -.icon-up-red { background-image: url(mod/16x16/actions/go-up-red.png) }
37   -.icon-forward { background-image: url(Tango/16x16/actions/go-next.png) }
38   -.icon-search { background-image: url(Tango/16x16/actions/search.png) }
39   -.icon-ok { background-image: url(Tango/16x16/actions/media-playback-start.png) }
40   -.icon-login { background-image: url(mod/16x16/actions/log-in.png) }
41   -.icon-help { background-image: url(Tango/16x16/apps/gnome-help.png) }
42   -.icon-firefox { background-image: url(firefox-24x24.gif) }
43   -.icon-help32on { background-image: url(Tango/32x32/apps/gnome-help.png) }
44   -.icon-help32off { background-image: url(mod/32x32/apps/gnome-help-red.png) }
  20 +/*.icon-find { background-image: url(noosfero-find.png) } UNUSED*/
  21 +.icon-back { background-image: url(Tango/16x16/actions/back.png) }
  22 +.icon-next { background-image: url(Tango/16x16/actions/go-next.png) }
  23 +.icon-add { background-image: url(Tango/16x16/actions/add.png) }
  24 +.icon-remove { background-image: url(Tango/16x16/actions/gtk-remove.png) }
  25 +.icon-more { background-image: url(Tango/16x16/actions/add.png) }
  26 +.icon-up { background-image: url(Tango/16x16/actions/go-up.png) }
  27 +.icon-down { background-image: url(Tango/16x16/actions/go-down.png) }
  28 +.icon-left { background-image: url(Tango/16x16/actions/go-previous.png) }
  29 +.icon-right { background-image: url(Tango/16x16/actions/go-next.png) }
  30 +.icon-up-disabled { background-image: url(Tango/16x16/actions/go-up.png); opacity: 0.25; filter:alpha(opacity=25); }
  31 +.icon-down-disabled { background-image: url(Tango/16x16/actions/go-down.png); opacity: 0.25; filter:alpha(opacity=25); }
  32 +.icon-left-disabled { background-image: url(Tango/16x16/actions/go-previous.png); opacity: 0.25; filter:alpha(opacity=25); }
  33 +.icon-right-disabled { background-image: url(Tango/16x16/actions/go-next.png); opacity: 0.25; filter:alpha(opacity=25); }
  34 +.icon-up-red { background-image: url(mod/16x16/actions/go-up-red.png) }
  35 +.icon-forward { background-image: url(Tango/16x16/actions/go-next.png) }
  36 +.icon-search { background-image: url(Tango/16x16/actions/search.png) }
  37 +.icon-ok { background-image: url(Tango/16x16/actions/media-playback-start.png) }
  38 +.icon-login { background-image: url(mod/16x16/actions/log-in.png) }
  39 +.icon-help { background-image: url(Tango/16x16/apps/gnome-help.png) }
  40 +.icon-firefox { background-image: url(firefox-24x24.gif) }
  41 +.icon-help32on { background-image: url(Tango/32x32/apps/gnome-help.png) }
  42 +.icon-help32off { background-image: url(mod/32x32/apps/gnome-help-red.png) }
45 43 .icon-spread { background-image: url(mod/16x16/actions/spread.png) }
46   -.icon-todo { background-image: url(Tango/16x16/actions/stock_paste.png) }
47   -.icon-eyes { background-image: url(Tango/16x16/actions/gtk-print-preview.png) }
48   -/*.icon-menu- { background-image: url(menu-without-ico-HC.gif) }*/
49   -.icon-menu-home { background-image: url(Tango/16x16/actions/go-home.png) }
50   -/*.icon-menu-blog { background-image: url(blog-HC.gif) } UNUSED*/
51   -/*.icon-menu-album { background-image: url(album-HC.gif) } UNUSED*/
52   -.icon-menu-product { background-image: url(Tango/16x16/mimetypes/package.png) }
53   -.icon-menu-enterprise { background-image: url(Tango/16x16/actions/go-home.png) }
54   -.icon-menu-community { background-image: url(Tango/16x16/apps/system-config-users.png) }
55   -/*.icon-menu-edit { background-image: url(edit-HC.gif) } UNUSED */
56   -.icon-menu-ctrl-panel { background-image: url(Tango/16x16/categories/preferences-desktop.png) }
57   -.icon-menu-admin { background-image: url(Tango/16x16/categories/preferences-system.png) }
58   -.icon-menu-my-groups { background-image: url(Tango/16x16/apps/system-config-users.png) }
59   -.icon-menu-login { background-image: url(mod/16x16/actions/log-in.png) }
60   -.icon-menu-logout { background-image: url(mod/16x16/actions/log-out.png) }
61   -.icon-menu-search { background-image: url(Tango/16x16/actions/search.png) }
62   -/*.icon-menu-ed-design { background-image: url(edit-design-HC.gif) } UNUSED */
63   -.icon-menu-events { background-image: url(Tango/16x16/mimetypes/stock_calendar.png) }
64   -.icon-event { background-image: url(Tango/16x16/mimetypes/stock_calendar.png) }
65   -.icon-newevent { background-image: url(Tango/16x16/mimetypes/stock_calendar.png) }
66   -.icon-menu-articles { background-image: url(Tango/16x16/apps/text-editor.png) }
67   -/*.icon-menu-comments { background-image: url(blog-HC.gif) } UNUSED */
68   -.icon-menu-people { background-image: url(mod/16x16/apps/user.png) }
69   -.icon-menu-mail { background-image: url(Tango/16x16/apps/email.png) }
70   -.icon-upload-file { background-image: url(Tango/16x16/actions/filesave.png) }
71   -.icon-newupload-file { background-image: url(Tango/16x16/actions/filesave.png) }
72   -.icon-slideshow { background-image: url(Tango/16x16/mimetypes/x-office-presentation.png) }
  44 +.icon-todo { background-image: url(Tango/16x16/actions/stock_paste.png) }
  45 +.icon-eyes { background-image: url(Tango/16x16/actions/find.png) }
  46 +/*.icon-menu- { background-image: url(menu-without-ico-HC.gif) }*/
  47 +.icon-menu-home { background-image: url(Tango/16x16/actions/go-home.png) }
  48 +/*.icon-menu-blog { background-image: url(blog-HC.gif) } UNUSED*/
  49 +/*.icon-menu-album { background-image: url(album-HC.gif) } UNUSED*/
  50 +.icon-menu-product { background-image: url(Tango/16x16/mimetypes/package.png) }
  51 +.icon-menu-enterprise { background-image: url(Tango/16x16/actions/go-home.png) }
  52 +.icon-menu-community { background-image: url(Tango/16x16/apps/system-config-users.png) }
  53 +/*.icon-menu-edit { background-image: url(edit-HC.gif) } UNUSED */
  54 +.icon-menu-ctrl-panel { background-image: url(Tango/16x16/categories/preferences-desktop.png) }
  55 +.icon-menu-admin { background-image: url(Tango/16x16/categories/preferences-system.png) }
  56 +.icon-menu-my-groups { background-image: url(Tango/16x16/apps/system-config-users.png) }
  57 +.icon-menu-login { background-image: url(mod/16x16/actions/log-in.png) }
  58 +.icon-menu-logout { background-image: url(mod/16x16/actions/log-out.png) }
  59 +.icon-menu-search { background-image: url(Tango/16x16/actions/search.png) }
  60 +/*.icon-menu-ed-design { background-image: url(edit-design-HC.gif) } UNUSED */
  61 +.icon-menu-events { background-image: url(Tango/16x16/mimetypes/stock_calendar.png) }
  62 +.icon-event { background-image: url(Tango/16x16/mimetypes/stock_calendar.png) }
  63 +.icon-newevent { background-image: url(Tango/16x16/mimetypes/stock_calendar.png) }
  64 +.icon-menu-articles { background-image: url(Tango/16x16/apps/text-editor.png) }
  65 +/*.icon-menu-comments { background-image: url(blog-HC.gif) } UNUSED */
  66 +.icon-menu-people { background-image: url(mod/16x16/apps/user.png) }
  67 +.icon-menu-mail { background-image: url(Tango/16x16/apps/email.png) }
  68 +.icon-upload-file { background-image: url(Tango/16x16/actions/filesave.png) }
  69 +.icon-newupload-file { background-image: url(Tango/16x16/actions/filesave.png) }
  70 +.icon-slideshow { background-image: url(Tango/16x16/mimetypes/x-office-presentation.png) }
73 71 .icon-photos { background-image: url(Tango/16x16/devices/camera-photo.png) }
74 72  
75   -.icon-text-html { background-image: url(Tango/16x16/mimetypes/text-html.png) }
  73 +.icon-text-html { background-image: url(Tango/16x16/mimetypes/text-html.png) }
76 74 .icon-text-plain { background-image: url(Tango/16x16/mimetypes/text-x-generic.png) }
77   -.icon-image-svg-xml { background-image: url(Tango/16x16/mimetypes/image-x-generic.png) }
  75 +.icon-image-svg-xml { background-image: url(Tango/16x16/mimetypes/image-x-generic.png) }
78 76 .icon-application-octet-stream { background-image: url(Tango/16x16/mimetypes/binary.png) }
79   -.icon-application-x-gzip { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-x-gzip.png) }
80   -.icon-application-postscript { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-postscript.png) }
81   -.icon-application-pdf { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-pdf.png) }
82   -.icon-application-ogg { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-ogg.png) }
83   -.icon-video-mpeg { background-image: url(Tango/16x16/mimetypes/video-x-generic.png) }
84   -.icon-application-vnd-oasis-opendocument-text { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-vnd.oasis.opendocument.text.png) }
  77 +.icon-application-x-gzip { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-x-gzip.png) }
  78 +.icon-application-postscript { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-postscript.png) }
  79 +.icon-application-pdf { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-pdf.png) }
  80 +.icon-application-ogg { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-ogg.png) }
  81 +.icon-video, .icon-video-mpeg { background-image: url(Tango/16x16/mimetypes/video-x-generic.png) }
  82 +.icon-application-vnd-oasis-opendocument-text { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-vnd.oasis.opendocument.text.png) }
85 83 .icon-application-vnd-oasis-opendocument-spreadsheet { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-vnd.oasis.opendocument.spreadsheet.png) }
86   -.icon-application-vnd-oasis-opendocument-presentation { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-vnd.oasis.opendocument.presentation.png) }
  84 +.icon-application-vnd-oasis-opendocument-presentation { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-vnd.oasis.opendocument.presentation.png) }
87 85  
88   -.icon-media-pause { background-image: url(Tango/16x16/actions/media-playback-pause.png) }
89   -.icon-media-play { background-image: url(Tango/16x16/actions/media-playback-start.png) }
90   -.icon-media-prev { background-image: url(Tango/16x16/actions/media-skip-backward.png) }
91   -.icon-media-next { background-image: url(Tango/16x16/actions/media-skip-forward.png) }
  86 +.icon-media-pause { background-image: url(Tango/16x16/actions/media-playback-pause.png) }
  87 +.icon-media-play { background-image: url(Tango/16x16/actions/media-playback-start.png) }
  88 +.icon-media-prev { background-image: url(Tango/16x16/actions/media-skip-backward.png) }
  89 +.icon-media-next { background-image: url(Tango/16x16/actions/media-skip-forward.png) }
92 90 .icon-lock { background-image: url(Tango/16x16/actions/lock.png) }
93   -.icon-chat { background-image: url(Tango/16x16/apps/internet-group-chat.png); background-repeat: no-repeat }
94   -.icon-reply { background-image: url(Tango/16x16/actions/mail-reply-sender.png) }
95   -.icon-newforum { background-image: url(Tango/16x16/apps/internet-group-chat.png) }
96   -.icon-forum { background-image: url(Tango/16x16/apps/system-users.png) }
97   -.icon-gallery { background-image: url(Tango/16x16/mimetypes/image-x-generic.png) }
  91 +.icon-chat { background-image: url(Tango/16x16/apps/internet-group-chat.png); background-repeat: no-repeat }
  92 +.icon-reply { background-image: url(Tango/16x16/actions/mail-reply-sender.png) }
  93 +.icon-newforum { background-image: url(Tango/16x16/apps/internet-group-chat.png) }
  94 +.icon-forum { background-image: url(Tango/16x16/apps/system-users.png) }
  95 +.icon-gallery { background-image: url(Tango/16x16/mimetypes/image-x-generic.png) }
98 96 .icon-newgallery { background-image: url(Tango/16x16/mimetypes/image-x-generic.png) }
99   -.icon-locale { background-image: url(Tango/16x16/apps/preferences-desktop-locale.png) }
100   -.icon-user-removed { background-image: url(Tango/16x16/actions/gtk-cancel.png) }
101   -.icon-user-unknown { background-image: url(Tango/16x16/status/dialog-error.png) }
102   -.icon-alert { background-image: url(Tango/16x16/status/dialog-warning.png) }
  97 +.icon-locale { background-image: url(Tango/16x16/apps/preferences-desktop-locale.png) }
  98 +.icon-user-removed { background-image: url(Tango/16x16/actions/gtk-cancel.png) }
  99 +.icon-user-unknown { background-image: url(Tango/16x16/status/dialog-error.png) }
  100 +.icon-alert { background-image: url(Tango/16x16/status/dialog-warning.png) }
103 101  
104 102 /******************LARGE ICONS********************/
105   -.image-gallery-item .folder { background-image: url(mod/96x96/places/folder.png) }
106   -.image-gallery-item .gallery { background-image: url(mod/96x96/mimetypes/image-x-generic.png) }
  103 +.image-gallery-item .folder { background-image: url(mod/96x96/places/folder.png) }
  104 +.image-gallery-item .gallery { background-image: url(mod/96x96/mimetypes/image-x-generic.png) }
... ...
public/designs/themes/base/style.css
... ... @@ -89,7 +89,7 @@ body, th, td, input {
89 89 color: #555753;
90 90 }
91 91  
92   -#controlpanel, #logout, #openchat, #manage-enterprises {
  92 +#controlpanel, #logout, #openchat, .manage-groups {
93 93 margin-left: 25px;
94 94 }
95 95  
... ... @@ -822,12 +822,6 @@ div#notice {
822 822 margin-bottom: 0px;
823 823 }
824 824  
825   -X.sep {
826   - background: url(imgs/blog-sep.png) 50% 50% repeat-x;
827   - padding: 10px 20px;
828   - margin: 10px -19px;
829   -}
830   -
831 825 /* ==> search-results.css <== */
832 826  
833 827  
... ... @@ -1019,6 +1013,46 @@ hr.pre-posts, hr.sep-posts {
1019 1013 background: transparent;
1020 1014 }
1021 1015  
  1016 +/************* uploaded file *****************/
  1017 +
  1018 +#article .gallery-navigation {
  1019 + padding: 10px 0;
  1020 +}
  1021 +
  1022 +#article .gallery-navigation .previous {
  1023 + margin-right: 10px;
  1024 +}
  1025 +
  1026 +#article .gallery-navigation .next {
  1027 + margin-left: 10px;
  1028 +}
  1029 +
  1030 +#article .gallery-navigation .total-of-images {
  1031 + font-weight: bold;
  1032 +}
  1033 +
  1034 +#article .uploaded-file-description {
  1035 + background: #f6f6f6;
  1036 + border-top: 1px solid #ccc;
  1037 + border-bottom: 1px solid #ccc;
  1038 + padding: 1em;
  1039 +}
  1040 +#article .uploaded-file-description.empty {
  1041 + display: none;
  1042 +}
  1043 +
  1044 +#article.file-generic .download-link {
  1045 + display: block;
  1046 + margin-bottom: 10px;
  1047 +}
  1048 +#article.file-generic .download-link span {
  1049 + font-size: 150%;
  1050 + padding-right: 5px;
  1051 +}
  1052 +#article.file-generic .download-link a {
  1053 + font-size: 180%;
  1054 + text-decoration: none;
  1055 +}
1022 1056  
1023 1057 /**************************** Comments *******************************/
1024 1058  
... ...
public/javascripts/application.js
... ... @@ -709,7 +709,7 @@ jQuery(function($) {
709 709 document.location.href = this.href;
710 710 })
711 711 }
712   - $('#manage-enterprises-link').live('click', function() {
  712 + $('.manage-groups > a').live('click', function() {
713 713 toggleMenu(this);
714 714 return false;
715 715 });
... ...
public/javascripts/spam.js 0 → 100644
... ... @@ -0,0 +1,28 @@
  1 +function removeTaskBox(button, url, task_box_id, msg) {
  2 + var $ = jQuery;
  3 + if (msg && !confirm(msg)) {
  4 + return;
  5 + }
  6 + button = $(button);
  7 + button.addClass('task-button-loading');
  8 + $.post(url, function (data) {
  9 + if (data.ok) {
  10 + $('#' + task_box_id).slideUp();
  11 + } else {
  12 + button.removeClass('task-button-loading');
  13 + button.addClass('task-button-failure');
  14 + }
  15 + });
  16 +}
  17 +
  18 +function toggleDetails(link, msg_hide, msg_show) {
  19 + var $ = jQuery;
  20 + $(link).toggleClass('icon-up icon-down');
  21 + details = $(link).closest('.task_box').find('.suggest-article-details');
  22 + if (details.css('display') == 'none') {
  23 + link.innerHTML = msg_hide;
  24 + } else {
  25 + link.innerHTML = msg_show;
  26 + }
  27 + details.slideToggle();
  28 +}
... ...
public/stylesheets/application.css
... ... @@ -882,6 +882,40 @@ code input {
882 882 .webkit #manage-enterprises .simplemenu-submenu {
883 883 top: 20px;
884 884 }
  885 +#manage-communities {
  886 + display: inline-block;
  887 + margin-right: 5px;
  888 + position: relative;
  889 +}
  890 +#manage-communities .ui-icon {
  891 + position: absolute;
  892 + top: 0;
  893 + right: -20px;
  894 +}
  895 +#manage-communities .simplemenu-submenu {
  896 + text-align: left;
  897 + left: -20px;
  898 + width: 200px;
  899 +}
  900 +#manage-communities .simplemenu-item {
  901 + padding: 5px 0;
  902 +}
  903 +#manage-communities .simplemenu-item a {
  904 + background-repeat: no-repeat;
  905 + padding-left: 20px;
  906 +}
  907 +#manage-communities .simplemenu-item a span {
  908 + display: none;
  909 +}
  910 +.msie8 #manage-communities-link { /* IE8 hack */
  911 + border: 0px solid;
  912 +}
  913 +.msie8 #manage-communities .simplemenu-submenu {
  914 + top: 16px;
  915 +}
  916 +.webkit #manage-communities .simplemenu-submenu {
  917 + top: 20px;
  918 +}
885 919 #article {
886 920 position: relative;
887 921 text-align: justify;
... ... @@ -1552,6 +1586,7 @@ a.button.disabled, input.disabled {
1552 1586 .map {
1553 1587 clear: both;
1554 1588 }
  1589 +
1555 1590 /***********************************************************
1556 1591 * style for blocks
1557 1592 ***********************************************************/
... ... @@ -3176,6 +3211,14 @@ table.cms-articles .icon:hover {
3176 3211 background: #eeeeec;
3177 3212 border: 1px solid #d3d7cf;
3178 3213 }
  3214 +
  3215 +.controller-cms .article-mime {
  3216 + max-width: 90px;
  3217 + overflow: hidden;
  3218 + text-overflow: ellipsis;
  3219 + white-space: nowrap;
  3220 +}
  3221 +
3179 3222 .controller-cms .article-controls {
3180 3223 white-space: nowrap;
3181 3224 }
... ... @@ -3446,26 +3489,7 @@ div#article-parent {
3446 3489 .article-body-uploaded-file {
3447 3490 text-align: center;
3448 3491 }
3449   -/************* uploaded file *****************/
3450 3492  
3451   -#article .gallery-navigation {
3452   - padding: 10px 0;
3453   -}
3454   -#article .gallery-navigation .left {
3455   - margin-right: 10px;
3456   -}
3457   -#article .gallery-navigation .right {
3458   - margin-left: 10px;
3459   -}
3460   -#article .gallery-navigation .total-of-images {
3461   - font-weight: bold;
3462   -}
3463   -#article .uploaded-file-description {
3464   - background: #f6f6f6;
3465   - border-top: 1px solid #ccc;
3466   - border-bottom: 1px solid #ccc;
3467   - padding: 1em;
3468   -}
3469 3493 /* ==> public/stylesheets/controller_events.css <== */
3470 3494 #agenda {
3471 3495 position: relative;
... ...
script/development
1 1 #!/bin/sh
2 2  
  3 +set -e
  4 +
3 5 export RAILS_ENV=development
4 6  
5 7 stop() {
... ... @@ -9,6 +11,7 @@ stop() {
9 11 }
10 12  
11 13 start() {
  14 + rake db:abort_if_pending_migrations
12 15 ./script/feed-updater start
13 16 ./script/delayed_job start
14 17 trap stop INT TERM
... ...
test/functional/cms_controller_test.rb
... ... @@ -940,8 +940,8 @@ class CmsControllerTest &lt; ActionController::TestCase
940 940 :article => {:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')}
941 941  
942 942 process_delayed_job_queue
943   - file = profile.articles.find_by_name('rails.png')
944   - assert File.exists?(file.class.icon_name(file))
  943 + file = FilePresenter.for profile.articles.find_by_name('rails.png')
  944 + assert File.exists?(file.icon_name)
945 945 file.destroy
946 946 end
947 947  
... ... @@ -951,8 +951,8 @@ class CmsControllerTest &lt; ActionController::TestCase
951 951 :article => {:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')}
952 952  
953 953 process_delayed_job_queue
954   - file = profile.articles.find_by_name('rails.png')
955   - assert File.exists?(file.class.icon_name(file))
  954 + file = FilePresenter.for profile.articles.find_by_name('rails.png')
  955 + assert File.exists?(file.icon_name)
956 956 file.destroy
957 957 end
958 958  
... ...
test/functional/content_viewer_controller_test.rb
... ... @@ -1271,4 +1271,12 @@ class ContentViewerControllerTest &lt; ActionController::TestCase
1271 1271 assert_tag :tag => 'strong', :content => /bold/
1272 1272 end
1273 1273  
  1274 + should 'display link to download of non-recognized file types on its page' do
  1275 + file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/test.txt', 'bin/unknown'), :profile => profile)
  1276 + get :view_page, file.url.merge(:view=>:true)
  1277 + assert_tag :tag => 'a',
  1278 + :content => file.filename,
  1279 + :attributes => { :href => file.public_filename }
  1280 + end
  1281 +
1274 1282 end
... ...
test/functional/profile_controller_test.rb
... ... @@ -96,7 +96,7 @@ class ProfileControllerTest &lt; ActionController::TestCase
96 96 end
97 97  
98 98 should 'not show enterprises link to enterprise' do
99   - ent = fast_create(Enterprise, :identifier => 'test_enterprise1', :name => 'Test enteprise1')
  99 + ent = fast_create(Enterprise, :identifier => 'test_enterprise1', :name => 'Test enterprise1')
100 100 get :index, :profile => ent.identifier
101 101 assert_no_tag :tag => 'a', :content => 'Enterprises', :attributes => { :href => /profile\/#{ent.identifier}\/enterprises$/ }
102 102 end
... ... @@ -560,7 +560,7 @@ class ProfileControllerTest &lt; ActionController::TestCase
560 560 assert_response 403
561 561 end
562 562  
563   - should 'allow environment admin to unblock enteprises' do
  563 + should 'allow environment admin to unblock enterprises' do
564 564 login_as(profile.identifier)
565 565 enterprise = fast_create(Enterprise)
566 566 enterprise.environment.add_admin(profile)
... ... @@ -881,7 +881,7 @@ class ProfileControllerTest &lt; ActionController::TestCase
881 881 assert_template 'index'
882 882 end
883 883  
884   - should 'the network activity be visible to uses not logged in on communities and enteprises' do
  884 + should 'the network activity be visible to uses not logged in on communities and enterprises' do
885 885 p1= Person.first
886 886 community = fast_create(Community)
887 887 p2= fast_create(Person)
... ... @@ -1549,6 +1549,63 @@ class ProfileControllerTest &lt; ActionController::TestCase
1549 1549 assert_tag :tag => 'td', :content => 'e-Mail:'
1550 1550 end
1551 1551  
  1552 + should 'build menu to the community panel' do
  1553 + u = create_user('other_other_ze').person
  1554 + u2 = create_user('guy_that_will_be_admin_of_all').person # because the first member of each community is an admin
  1555 + Environment.any_instance.stubs(:required_person_fields).returns([])
  1556 + u.data = { :email => 'test@test.com', :fields_privacy => { } }
  1557 + u.save!
  1558 + c1 = Community.create!(:name => 'community_1')
  1559 + c2 = Community.create!(:name => 'community_2')
  1560 + c3 = Community.create!(:name => 'community_3')
  1561 + c4 = Community.create!(:name => 'community_4')
  1562 +
  1563 + c1.add_admin(u2)
  1564 + c2.add_admin(u2)
  1565 + c3.add_admin(u2)
  1566 +
  1567 + c1.add_member(u)
  1568 + c2.add_member(u)
  1569 + c3.add_member(u)
  1570 + c1.add_admin(u)
  1571 + c2.add_admin(u)
  1572 +
  1573 + login_as(u.identifier)
  1574 +
  1575 + get :index
  1576 +
  1577 + assert_tag :tag => 'div', :attributes => {:id => 'manage-communities'}
  1578 + assert_select '#manage-communities li > a' do |links|
  1579 + assert_equal 2, links.length
  1580 + assert_match /community_1/, links.to_s
  1581 + assert_match /community_2/, links.to_s
  1582 + assert_no_match /community_3/, links.to_s
  1583 + assert_no_match /community_4/, links.to_s
  1584 + end
  1585 + end
  1586 +
  1587 + should 'build menu to the enterprise panel' do
  1588 + u = create_user('other_other_ze').person
  1589 + Environment.any_instance.stubs(:required_person_fields).returns([])
  1590 + u.data = { :email => 'test@test.com', :fields_privacy => { } }
  1591 + u.save!
  1592 + e1 = fast_create(Enterprise, :identifier => 'test_enterprise1', :name => 'Test enterprise1')
  1593 + e2 = fast_create(Enterprise, :identifier => 'test_enterprise2', :name => 'Test enterprise2')
  1594 +
  1595 + e1.add_member(u)
  1596 +
  1597 + login_as(u.identifier)
  1598 +
  1599 + get :index
  1600 +
  1601 + assert_tag :tag => 'div', :attributes => {:id => 'manage-enterprises'}
  1602 + assert_select '#manage-enterprises li > a' do |links|
  1603 + assert_equal 1, links.length
  1604 + assert_match /Test enterprise1/, links.to_s
  1605 + assert_no_match /Test enterprise_2/, links.to_s
  1606 + end
  1607 + end
  1608 +
1552 1609 should 'show enterprises field if enterprises are enabled on environment' do
1553 1610 person = fast_create(Person)
1554 1611 environment = person.environment
... ...
test/functional/spam_controller_test.rb
... ... @@ -4,37 +4,55 @@ class SpamControllerTest &lt; ActionController::TestCase
4 4  
5 5 def setup
6 6 @profile = create_user.person
7   - @article = fast_create(TextileArticle, :profile_id => @profile.id)
8   - @spam = fast_create(Comment, :source_id => @article.id, :spam => true, :name => 'foo', :email => 'foo@example.com')
9 7  
  8 + @community = fast_create(Community, :name => 'testcommunity')
  9 + @community.add_admin(@profile)
  10 + @article = fast_create(TextileArticle, :profile_id => @community.id)
  11 + @spam_comment = fast_create(Comment, :source_id => @article.id, :spam => true, :name => 'foo', :email => 'foo@example.com')
  12 +
  13 + @spam_suggest_article = SuggestArticle.create!(:name => 'spammer', :article_name => 'Spam article', :email => 'spammer@shady.place', :article_body => "Something you don't need", :target => @community, :spam => true)
10 14 login_as @profile.identifier
11 15 end
12 16  
13   - test "should only list spammy comments" do
  17 + test "should only list spammy comments and spammy suggest articles" do
14 18 ham = fast_create(Comment, :source_id => @article.id)
15 19  
16   - get :index, :profile => @profile.identifier
  20 + get :index, :profile => @community.identifier
17 21  
18   - assert_equivalent [@spam], assigns(:spam)
  22 + assert_equivalent [@spam_comment], assigns(:comment_spam)
  23 + assert_equivalent [@spam_suggest_article], assigns(:task_spam)
19 24 end
20 25  
21 26 test "should mark comments as ham" do
22   - post :index, :profile => @profile.identifier, :mark_comment_as_ham => @spam.id
  27 + post :index, :profile => @community.identifier, :mark_comment_as_ham => @spam_comment.id
  28 +
  29 + @spam_comment.reload
  30 + assert @spam_comment.ham?
  31 + end
  32 +
  33 + test "should mark suggest article as ham" do
  34 + post :index, :profile => @community.identifier, :mark_task_as_ham => @spam_suggest_article.id
23 35  
24   - @spam.reload
25   - assert @spam.ham?
  36 + @spam_suggest_article.reload
  37 + assert @spam_suggest_article.ham?
26 38 end
27 39  
28 40 test "should remove comments" do
29   - post :index, :profile => @profile.identifier, :remove_comment => @spam.id
  41 + post :index, :profile => @community.identifier, :remove_comment => @spam_comment.id
  42 +
  43 + assert !Comment.exists?(@spam_comment.id)
  44 + end
  45 +
  46 + test "should remove suggest articles" do
  47 + post :index, :profile => @community.identifier, :remove_task => @spam_suggest_article.id
30 48  
31   - assert !Comment.exists?(@spam.id)
  49 + assert !SuggestArticle.exists?(@spam_suggest_article.id)
32 50 end
33 51  
34 52 should 'properly render spam that have replies' do
35   - reply_spam = fast_create(Comment, :source_id => @article_id, :reply_of_id => @spam.id)
  53 + reply_spam = fast_create(Comment, :source_id => @article_id, :reply_of_id => @spam_comment.id)
36 54  
37   - get :index, :profile => @profile.identifier
  55 + get :index, :profile => @community.identifier
38 56 assert_response :success
39 57 end
40 58  
... ...
test/functional/tasks_controller_test.rb
... ... @@ -38,6 +38,17 @@ class TasksControllerTest &lt; ActionController::TestCase
38 38 assert_kind_of Array, assigns(:tasks)
39 39 end
40 40  
  41 + should 'list pending tasks without spam' do
  42 + requestor = fast_create(Person)
  43 + task_spam = Task.create!(:requestor => requestor, :target => profile, :spam => true)
  44 + task_ham = Task.create!(:requestor => requestor, :target => profile, :spam => false)
  45 +
  46 + get :index
  47 + assert_response :success
  48 + assert_includes assigns(:tasks), task_ham
  49 + assert_not_includes assigns(:tasks), task_spam
  50 + end
  51 +
41 52 should 'list processed tasks' do
42 53 get :processed
43 54  
... ... @@ -46,6 +57,17 @@ class TasksControllerTest &lt; ActionController::TestCase
46 57 assert_kind_of Array, assigns(:tasks)
47 58 end
48 59  
  60 + should 'list processed tasks without spam' do
  61 + requestor = fast_create(Person)
  62 + task_spam = Task.create!(:status => Task::Status::FINISHED, :requestor => requestor, :target => profile, :spam => true)
  63 + task_ham = Task.create!(:status => Task::Status::FINISHED, :requestor => requestor, :target => profile, :spam => false)
  64 +
  65 + get :processed
  66 + assert_response :success
  67 + assert_includes assigns(:tasks), task_ham
  68 + assert_not_includes assigns(:tasks), task_spam
  69 + end
  70 +
49 71 should 'be able to finish a task' do
50 72 t = profile.tasks.build; t.save!
51 73  
... ... @@ -140,6 +162,15 @@ class TasksControllerTest &lt; ActionController::TestCase
140 162 assert_includes assigns(:tasks), task
141 163 end
142 164  
  165 + should 'list tasks that this profile created without spam' do
  166 + task_spam = Ticket.create!(:name => 'test', :requestor => profile, :spam => true)
  167 + task_ham = Ticket.create!(:name => 'test', :requestor => profile, :spam => false)
  168 + get :list_requested, :profile => profile.identifier
  169 +
  170 + assert_includes assigns(:tasks), task_ham
  171 + assert_not_includes assigns(:tasks), task_spam
  172 + end
  173 +
143 174 should 'set target of ticket when creating it' do
144 175 f = create_user('friend').person
145 176 profile.add_friend f
... ...
test/unit/application_helper_test.rb
... ... @@ -592,7 +592,7 @@ class ApplicationHelperTest &lt; ActiveSupport::TestCase
592 592  
593 593 should 'include item in usermenu for environment enabled features' do
594 594 env = Environment.new
595   - env.enable('xmpp_chat')
  595 + env.enable('xmpp_chat', false)
596 596 stubs(:environment).returns(env)
597 597  
598 598 @controller = ApplicationController.new
... ...
test/unit/article_block_test.rb
... ... @@ -110,9 +110,12 @@ class ArticleBlockTest &lt; ActiveSupport::TestCase
110 110 block.article = image
111 111 block.save!
112 112  
113   - expects(:image_tag).with(image.public_filename(:display), :class => image.css_class_name, :style => 'max-width: 100%').returns('image')
114   -
115   - assert_match(/image/, instance_eval(&block.content))
  113 + assert_tag_in_string instance_eval(&block.content),
  114 + :tag => 'img',
  115 + :attributes => {
  116 + :src => image.public_filename(:display),
  117 + :class => /file-image/
  118 + }
116 119 end
117 120  
118 121 should 'not display gallery pages navigation in content' do
... ... @@ -123,8 +126,6 @@ class ArticleBlockTest &lt; ActiveSupport::TestCase
123 126 block.article = image
124 127 block.save!
125 128  
126   - expects(:image_tag).with(image.public_filename(:display), :class => image.css_class_name, :style => 'max-width: 100%').returns('image')
127   -
128 129 assert_no_match(/Previous/, instance_eval(&block.content))
129 130 end
130 131  
... ...
test/unit/article_test.rb
... ... @@ -1744,4 +1744,24 @@ class ArticleTest &lt; ActiveSupport::TestCase
1744 1744 assert_nil article.author_id
1745 1745 end
1746 1746  
  1747 + should 'identify if belongs to forum' do
  1748 + p = create_user('user_forum_test').person
  1749 + forum = fast_create(Forum, :name => 'Forum test', :profile_id => p.id)
  1750 + post = fast_create(TextileArticle, :name => 'First post', :profile_id => p.id, :parent_id => forum.id)
  1751 + assert post.belongs_to_forum?
  1752 + end
  1753 +
  1754 + should 'not belongs to forum' do
  1755 + p = create_user('user_forum_test').person
  1756 + blog = fast_create(Blog, :name => 'Not Forum', :profile_id => p.id)
  1757 + a = fast_create(TextileArticle, :name => 'Not forum post', :profile_id => p.id, :parent_id => blog.id)
  1758 + assert !a.belongs_to_forum?
  1759 + end
  1760 +
  1761 + should 'not belongs to forum if do not have a parent' do
  1762 + p = create_user('user_forum_test').person
  1763 + a = fast_create(TextileArticle, :name => 'Orphan post', :profile_id => p.id)
  1764 + assert !a.belongs_to_forum?
  1765 + end
  1766 +
1747 1767 end
... ...
test/unit/blog_helper_test.rb
... ... @@ -94,10 +94,10 @@ class BlogHelperTest &lt; ActiveSupport::TestCase
94 94 should 'display link to file if post is an uploaded_file' do
95 95 file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/test.txt', 'text/plain'), :profile => profile, :published => true, :parent => blog)
96 96  
97   - expects(:article_to_html).with(file).returns('TO HTML')
98   -
99 97 result = display_post(file)
100   - assert_tag_in_string result, :content => /TO HTML/
  98 + assert_tag_in_string result, :tag => 'a',
  99 + :attributes => { :href => file.public_filename },
  100 + :content => file.filename
101 101 end
102 102  
103 103 should 'display image if post is an image' do
... ...
test/unit/cms_helper_test.rb
... ... @@ -41,6 +41,7 @@ class CmsHelperTest &lt; ActiveSupport::TestCase
41 41 should 'display image and link if article is an image' do
42 42 profile = fast_create(Profile)
43 43 file = UploadedFile.create!(:profile => profile, :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
  44 + file = FilePresenter.for file
44 45 icon = icon_for_article(file)
45 46 expects(:image_tag).with(icon).returns('icon')
46 47  
... ...
test/unit/comment_test.rb
... ... @@ -447,7 +447,7 @@ class CommentTest &lt; ActiveSupport::TestCase
447 447 end
448 448  
449 449 class EverythingIsSpam < Noosfero::Plugin
450   - def check_comment_for_spam(comment)
  450 + def check_for_spam(comment)
451 451 comment.spam!
452 452 end
453 453 end
... ... @@ -469,11 +469,11 @@ class CommentTest &lt; ActiveSupport::TestCase
469 469 attr_accessor :marked_as_ham
470 470 end
471 471  
472   - def comment_marked_as_spam(c)
  472 + def marked_as_spam(c)
473 473 self.class.marked_as_spam = c
474 474 end
475 475  
476   - def comment_marked_as_ham(c)
  476 + def marked_as_ham(c)
477 477 self.class.marked_as_ham = c
478 478 end
479 479 end
... ...
test/unit/content_viewer_helper_test.rb
... ... @@ -18,8 +18,15 @@ class ContentViewerHelperTest &lt; ActiveSupport::TestCase
18 18 result = article_title(post)
19 19 assert_tag_in_string result, :tag => 'span', :content => show_date(post.published_at)
20 20 end
  21 +
  22 + should 'display published-at for forum posts' do
  23 + forum = fast_create(Forum, :name => 'Forum test', :profile_id => profile.id)
  24 + post = TextileArticle.create!(:name => 'post test', :profile => profile, :parent => forum)
  25 + result = article_title(post)
  26 + assert_tag_in_string result, :tag => 'span', :content => show_date(post.published_at)
  27 + end
21 28  
22   - should 'not display published-at for non-blog posts' do
  29 + should 'not display published-at for non-blog and non-forum posts' do
23 30 article = TextileArticle.create!(:name => 'article for test', :profile => profile)
24 31 result = article_title(article)
25 32 assert_no_match /<span class="date">#{show_date(article.published_at)}<\/span><span class="author">, by .*#{profile.identifier}/, result
... ...
test/unit/environment_statistics_block_test.rb
... ... @@ -85,7 +85,7 @@ class EnvironmentStatisticsBlockTest &lt; ActiveSupport::TestCase
85 85  
86 86 should 'not display enterprises if disabled' do
87 87 env = Environment.new
88   - env.enable('disable_asset_enterprises')
  88 + env.enable('disable_asset_enterprises', false)
89 89  
90 90 block = EnvironmentStatisticsBlock.new
91 91 block.stubs(:owner).returns(env)
... ...
test/unit/environment_test.rb
... ... @@ -35,21 +35,21 @@ class EnvironmentTest &lt; ActiveSupport::TestCase
35 35  
36 36 def test_features
37 37 v = Environment.new
38   - v.enable('feature1')
  38 + v.enable('feature1', false)
39 39 assert v.enabled?('feature1')
40   - v.disable('feature1')
  40 + v.disable('feature1', false)
41 41 assert !v.enabled?('feature1')
42 42 end
43 43  
44 44 def test_enabled_features
45 45 v = Environment.new
46   - v.enabled_features = [ 'feature1', 'feature2' ]
  46 + v.enable('feature1', false)
  47 + v.enable('feature2', false)
47 48 assert v.enabled?('feature1') && v.enabled?('feature2') && !v.enabled?('feature3')
48 49 end
49 50  
50 51 def test_enabled_features_no_features_enabled
51 52 v = Environment.new
52   - v.enabled_features = nil
53 53 assert !v.enabled?('feature1') && !v.enabled?('feature2') && !v.enabled?('feature3')
54 54 end
55 55  
... ... @@ -1075,9 +1075,9 @@ class EnvironmentTest &lt; ActiveSupport::TestCase
1075 1075  
1076 1076 should 'get enabled features' do
1077 1077 env = Environment.new
1078   - env.enable('feature1')
1079   - env.enable('feature2')
1080   - env.disable('feature3')
  1078 + env.enable('feature1', false)
  1079 + env.enable('feature2', false)
  1080 + env.disable('feature3', false)
1081 1081  
1082 1082 assert_includes env.enabled_features.keys, 'feature1'
1083 1083 assert_includes env.enabled_features.keys, 'feature2'
... ...
test/unit/file_presenter_test.rb 0 → 100644
... ... @@ -0,0 +1,61 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class FilePresenterTest < ActiveSupport::TestCase
  4 +
  5 + should 'notify about deprecated method UploadedFile.icon_name' do
  6 + profile = fast_create(Profile)
  7 + file = UploadedFile.create!(
  8 + :profile => profile,
  9 + :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')
  10 + )
  11 + assert_raise NoMethodError do
  12 + UploadedFile.icon_name file
  13 + end
  14 + ENV.stubs('[]').with('RAILS_ENV').returns('other')
  15 + Rails.logger.expects(:warn) # must warn on any other RAILS_ENV
  16 + stubs(:puts)
  17 + UploadedFile.icon_name file
  18 + end
  19 +
  20 + should 'notify about deprecated method UploadedFile#to_html' do
  21 + profile = fast_create(Profile)
  22 + file = UploadedFile.create!(
  23 + :profile => profile,
  24 + :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png')
  25 + )
  26 + assert_raise NoMethodError do
  27 + file.to_html
  28 + end
  29 + ENV.stubs('[]').with('RAILS_ENV').returns('other')
  30 + Rails.logger.expects(:warn) # must warn on any other RAILS_ENV
  31 + stubs(:puts)
  32 + file.to_html
  33 + end
  34 +
  35 + should 'return a thumbnail as icon for images ' do
  36 + f = UploadedFile.new
  37 + f.stubs(:image?).returns(true)
  38 + p = FilePresenter.for f
  39 + p.expects(:public_filename).with(:icon).returns('/path/to/file.xyz')
  40 + assert_equal '/path/to/file.xyz', p.icon_name
  41 + end
  42 +
  43 + should 'not crach when accepts? method receives a pure article' do
  44 + assert_nothing_raised do
  45 + FilePresenter.for Article.new
  46 + end
  47 + end
  48 +
  49 + should 'not crach when accepts? method receives a non-sense object' do
  50 + assert_nothing_raised do
  51 + FilePresenter.for nil
  52 + end
  53 + assert_nothing_raised do
  54 + FilePresenter.for({:key => 'value'})
  55 + end
  56 + assert_nothing_raised do
  57 + FilePresenter.for 'a string'
  58 + end
  59 + end
  60 +
  61 +end
... ...
test/unit/folder_helper_test.rb
... ... @@ -26,6 +26,7 @@ class FolderHelperTest &lt; ActiveSupport::TestCase
26 26 should 'display icon for images' do
27 27 profile = fast_create(Profile)
28 28 file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :profile => profile)
  29 + file = FilePresenter.for file
29 30 process_delayed_job_queue
30 31  
31 32 assert_match /rails_icon\.png/, icon_for_article(file.reload)
... ...
test/unit/plugin_manager_test.rb
... ... @@ -61,6 +61,10 @@ class PluginManagerTest &lt; ActiveSupport::TestCase
61 61 end
62 62  
63 63 should 'dispatch_first method returns the first plugin response if there is many plugins to responde the event' do
  64 + class Noosfero::Plugin
  65 + def random_event
  66 + end
  67 + end
64 68  
65 69 class Plugin1 < Noosfero::Plugin
66 70 def random_event
... ...
test/unit/profile_list_block_test.rb
... ... @@ -2,6 +2,8 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
2 2  
3 3 class ProfileListBlockTest < ActiveSupport::TestCase
4 4  
  5 + include ActionView::Helpers::TagHelper
  6 +
5 7 should 'describe itself' do
6 8 assert_not_equal Block.description, ProfileListBlock.description
7 9 end
... ...
test/unit/suggest_article_test.rb
... ... @@ -150,5 +150,79 @@ class SuggestArticleTest &lt; ActiveSupport::TestCase
150 150 assert_match(/#{task.name}.*suggested the publication of the article: #{task.subject}/, email.subject)
151 151 end
152 152  
  153 + class EverythingIsSpam < Noosfero::Plugin
  154 + def check_for_spam(object)
  155 + object.spam!
  156 + end
  157 + end
  158 +
  159 + should 'delegate spam detection to plugins' do
  160 + Environment.default.enable_plugin(EverythingIsSpam)
  161 +
  162 + t1 = build(SuggestArticle, :target => @profile, :article_name => 'suggested article', :name => 'johndoe', :email => 'johndoe@example.com')
  163 +
  164 + EverythingIsSpam.any_instance.expects(:check_for_spam)
  165 +
  166 + t1.check_for_spam
  167 + end
  168 +
  169 + class SpamNotification < Noosfero::Plugin
  170 + class << self
  171 + attr_accessor :marked_as_spam
  172 + attr_accessor :marked_as_ham
  173 + end
  174 +
  175 + def check_for_spam(c)
  176 + # do nothing
  177 + end
  178 +
  179 + def marked_as_spam(c)
  180 + self.class.marked_as_spam = c
  181 + end
  182 +
  183 + def marked_as_ham(c)
  184 + self.class.marked_as_ham = c
  185 + end
  186 + end
  187 +
  188 + should 'notify plugins of suggest_articles being marked as spam' do
  189 + Environment.default.enable_plugin(SpamNotification)
  190 +
  191 + t = SuggestArticle.create!(:target => @profile, :article_name => 'suggested article', :name => 'johndoe', :article_body => 'wanna feel my body? my body baaaby', :email => 'johndoe@example.com')
  192 +
  193 + t.spam!
  194 + process_delayed_job_queue
  195 +
  196 + assert_equal t, SpamNotification.marked_as_spam
  197 + end
  198 +
  199 + should 'notify plugins of suggest_articles being marked as ham' do
  200 + Environment.default.enable_plugin(SpamNotification)
  201 +
  202 + t = SuggestArticle.create!(:target => @profile, :article_name => 'suggested article', :name => 'johndoe', :article_body => 'wanna feel my body? my body baaaby', :email => 'johndoe@example.com')
  203 +
  204 + t.ham!
  205 + process_delayed_job_queue
  206 +
  207 + assert_equal t, SpamNotification.marked_as_ham
  208 + end
  209 +
  210 + should 'store User-Agent' do
  211 + t = SuggestArticle.new(:user_agent => 'foo')
  212 + assert_equal 'foo', t.user_agent
  213 + end
  214 +
  215 + should 'store referrer' do
  216 + t = SuggestArticle.new(:referrer => 'bar')
  217 + assert_equal 'bar', t.referrer
  218 + end
  219 +
  220 + should 'log spammer ip after marking comment as spam' do
  221 + t = SuggestArticle.create!(:target => @profile, :article_name => 'suggested article', :name => 'johndoe', :article_body => 'wanna feel my body? my body baaaby', :email => 'johndoe@example.com', :ip_address => '192.168.0.1')
  222 + t.spam!
  223 + log = File.open('log/test_spammers.log')
  224 + assert_match "SuggestArticle-id: #{t.id} IP: 192.168.0.1", log.read
  225 + SpammerLogger.clean_log
  226 + end
153 227  
154 228 end
... ...
test/unit/task_test.rb
... ... @@ -372,6 +372,55 @@ class TaskTest &lt; ActiveSupport::TestCase
372 372 assert_includes Task.closed, canceled
373 373 end
374 374  
  375 + should 'be ham by default' do # ham means not spam
  376 + assert_equal false, Task.create.spam
  377 + end
  378 +
  379 + should 'be able to mark tasks as spam/ham/unknown' do
  380 + t = Task.new
  381 + t.spam = true
  382 + assert t.spam?
  383 + assert !t.ham?
  384 +
  385 + t.spam = false
  386 + assert t.ham?
  387 + assert !t.spam?
  388 +
  389 + t.spam = nil
  390 + assert !t.spam?
  391 + assert !t.ham?
  392 + end
  393 +
  394 + should 'be able to select non-spam tasks' do
  395 + t1 = fast_create(Task)
  396 + t2 = fast_create(Task, :spam => false)
  397 + t3 = fast_create(Task, :spam => true)
  398 +
  399 + assert_equivalent [t1,t2], Task.without_spam
  400 + end
  401 +
  402 + should 'be able to select spam tasks' do
  403 + t1 = fast_create(Task)
  404 + t2 = fast_create(Task, :spam => false)
  405 + t3 = fast_create(Task, :spam => true)
  406 +
  407 + assert_equivalent [t3], Task.spam
  408 + end
  409 +
  410 + should 'be able to mark as spam' do
  411 + t1 = fast_create(Task)
  412 + t1.spam!
  413 + t1.reload
  414 + assert t1.spam?
  415 + end
  416 +
  417 + should 'be able to mark as ham' do
  418 + t1 = fast_create(Task)
  419 + t1.ham!
  420 + t1.reload
  421 + assert t1.ham?
  422 + end
  423 +
375 424 protected
376 425  
377 426 def sample_user
... ...
test/unit/uploaded_file_test.rb
... ... @@ -7,13 +7,6 @@ class UploadedFileTest &lt; ActiveSupport::TestCase
7 7 end
8 8 attr_reader :profile
9 9  
10   - should 'return a thumbnail as icon for images ' do
11   - f = UploadedFile.new
12   - f.expects(:image?).returns(true)
13   - f.expects(:public_filename).with(:icon).returns('/path/to/file.xyz')
14   - assert_equal '/path/to/file.xyz', UploadedFile.icon_name(f)
15   - end
16   -
17 10 should 'return a default icon for uploaded files' do
18 11 assert_equal 'upload-file', UploadedFile.icon_name
19 12 end
... ... @@ -113,8 +106,11 @@ class UploadedFileTest &lt; ActiveSupport::TestCase
113 106 p = create_user('test_user').person
114 107 file = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/test.txt', 'text/plain'), :profile => p)
115 108  
  109 + ENV.stubs('[]').with('RAILS_ENV').returns('other')
  110 + Rails.logger.expects(:warn) # warn about deprecatede usage of UploadedFile#to_html
  111 + stubs(:puts)
116 112 stubs(:content_tag).returns('link')
117   - expects(:link_to).with(file.name, file.url, :class => file.css_class_name)
  113 + expects(:link_to).with(file.name, file.url)
118 114  
119 115 instance_eval(&file.to_html)
120 116 end
... ... @@ -206,13 +202,6 @@ class UploadedFileTest &lt; ActiveSupport::TestCase
206 202 file.destroy
207 203 end
208 204  
209   - should 'return the default thumbnail image as icon for images ' do
210   - f = UploadedFile.new
211   - f.expects(:image?).returns(true)
212   - f.expects(:public_filename).with(:icon).returns('/path/to/file.xyz')
213   - assert_equal '/path/to/file.xyz', UploadedFile.icon_name(f)
214   - end
215   -
216 205 should 'store width and height after processing' do
217 206 file = UploadedFile.create!(:profile => @profile, :uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'))
218 207 file.create_thumbnails
... ... @@ -287,12 +276,6 @@ class UploadedFileTest &lt; ActiveSupport::TestCase
287 276 assert_equal '', f.lead
288 277 end
289 278  
290   - should 'survive when try to get icon_name from a file with mime_type nil' do
291   - f = UploadedFile.new
292   - f.expects(:mime_type).returns(nil)
293   - assert_equal 'upload-file', UploadedFile.icon_name(f)
294   - end
295   -
296 279 should 'upload to a folder with same name as the schema if database is postgresql' do
297 280 uses_postgresql 'image_schema_one'
298 281 file1 = UploadedFile.create!(:uploaded_data => fixture_file_upload('/files/rails.png', 'image/png'), :profile => @profile)
... ...