Commit 12d4acaed8f73427008977442e70c744d034f576

Authored by Leandro Nunes dos Santos
2 parents 6e1fdc22 4eb56579

Merge branch 'next_colivre' into ActionItem2871

Showing 236 changed files with 5968 additions and 662 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 236 files displayed.

@@ -4,8 +4,9 @@ Copyright (c) 2007-2009, @@ -4,8 +4,9 @@ Copyright (c) 2007-2009,
4 Cáritas Brasileira <http://www.caritasbrasileira.org/> 4 Cáritas Brasileira <http://www.caritasbrasileira.org/>
5 Copyright (c) 2007-2009, 5 Copyright (c) 2007-2009,
6 Ynternet.org Foundation <http://www.ynternet.org/> 6 Ynternet.org Foundation <http://www.ynternet.org/>
7 -Copyright (c) 2008-2009, 7 +Copyright (c) 2008-2013,
8 Colivre <http://www.colivre.coop.br/> 8 Colivre <http://www.colivre.coop.br/>
  9 +Copyright (c) the Noosfero contributors. See AUTHORS
9 10
10 This program is free software: you can redistribute it and/or modify 11 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU Affero General Public License as published by 12 it under the terms of the GNU Affero General Public License as published by
@@ -52,3 +52,12 @@ If you write such script for your own OS, *please* share it with us at the @@ -52,3 +52,12 @@ If you write such script for your own OS, *please* share it with us at the
52 development mailing list so that we can include it in the official repository. 52 development mailing list so that we can include it in the official repository.
53 This way other people using the same OS will have to put less effort to develop 53 This way other people using the same OS will have to put less effort to develop
54 Noosfero. 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,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  
@@ -6,7 +6,7 @@ To configure XMPP/BOSH in Noosfero you need: @@ -6,7 +6,7 @@ To configure XMPP/BOSH in Noosfero you need:
6 * SystemTimer - http://ph7spot.com/musings/system-timer 6 * SystemTimer - http://ph7spot.com/musings/system-timer
7 * Pidgin data files - http://www.pidgin.im/ 7 * Pidgin data files - http://www.pidgin.im/
8 8
9 -If you use Debian Wheezy: 9 +If you use Debian 6.0 (squeeze):
10 10
11 # apt-get install librestclient-ruby pidgin-data ruby1.8-dev 11 # apt-get install librestclient-ruby pidgin-data ruby1.8-dev
12 # gem install SystemTimer 12 # gem install SystemTimer
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
@@ -144,10 +144,15 @@ class CmsController &lt; MyProfileController @@ -144,10 +144,15 @@ class CmsController &lt; MyProfileController
144 144
145 post_only :set_home_page 145 post_only :set_home_page
146 def set_home_page 146 def set_home_page
147 - @article = profile.articles.find(params[:id])  
148 - profile.home_page = @article  
149 - profile.save(false)  
150 - session[:notice] = _('"%s" configured as home page.') % @article.name 147 + article = params[:id].nil? ? nil : profile.articles.find(params[:id])
  148 + profile.update_attribute(:home_page, article)
  149 +
  150 + if article.nil?
  151 + session[:notice] = _('Homepage reseted.')
  152 + else
  153 + session[:notice] = _('"%s" configured as homepage.') % article.name
  154 + end
  155 +
151 redirect_to (request.referer || profile.url) 156 redirect_to (request.referer || profile.url)
152 end 157 end
153 158
@@ -267,7 +272,10 @@ class CmsController &lt; MyProfileController @@ -267,7 +272,10 @@ class CmsController &lt; MyProfileController
267 @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url) 272 @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url)
268 @task = SuggestArticle.new(params[:task]) 273 @task = SuggestArticle.new(params[:task])
269 if request.post? 274 if request.post?
270 - @task.target = profile 275 + @task.target = profile
  276 + @task.ip_address = request.remote_ip
  277 + @task.user_agent = request.user_agent
  278 + @task.referrer = request.referrer
271 if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save 279 if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save
272 session[:notice] = _('Thanks for your suggestion. The community administrators were notified.') 280 session[:notice] = _('Thanks for your suggestion. The community administrators were notified.')
273 redirect_to @back_to 281 redirect_to @back_to
app/controllers/my_profile/memberships_controller.rb
@@ -9,9 +9,10 @@ class MembershipsController &lt; MyProfileController @@ -9,9 +9,10 @@ class MembershipsController &lt; MyProfileController
9 def new_community 9 def new_community
10 @community = Community.new(params[:community]) 10 @community = Community.new(params[:community])
11 @community.environment = environment 11 @community.environment = environment
  12 + @back_to = params[:back_to] || url_for(:action => 'index')
12 if request.post? && @community.valid? 13 if request.post? && @community.valid?
13 @community = Community.create_after_moderation(user, {:environment => environment}.merge(params[:community])) 14 @community = Community.create_after_moderation(user, {:environment => environment}.merge(params[:community]))
14 - redirect_to :action => 'index' 15 + redirect_to @back_to
15 return 16 return
16 end 17 end
17 end 18 end
app/controllers/my_profile/profile_editor_controller.rb
@@ -4,7 +4,7 @@ class ProfileEditorController &lt; MyProfileController @@ -4,7 +4,7 @@ class ProfileEditorController &lt; MyProfileController
4 protect 'destroy_profile', :profile, :only => [:destroy_profile] 4 protect 'destroy_profile', :profile, :only => [:destroy_profile]
5 5
6 def index 6 def index
7 - @pending_tasks = Task.to(profile).pending.select{|i| user.has_permission?(i.permission, profile)} 7 + @pending_tasks = Task.to(profile).pending.without_spam.select{|i| user.has_permission?(i.permission, profile)}
8 end 8 end
9 9
10 helper :profile 10 helper :profile
app/controllers/my_profile/spam_controller.rb
@@ -14,9 +14,15 @@ class SpamController &lt; MyProfileController @@ -14,9 +14,15 @@ class SpamController &lt; MyProfileController
14 if params[:remove_comment] 14 if params[:remove_comment]
15 profile.comments_received.find(params[:remove_comment]).destroy 15 profile.comments_received.find(params[:remove_comment]).destroy
16 end 16 end
  17 + if params[:remove_task]
  18 + Task.to(profile).find_by_id(params[:remove_task]).destroy
  19 + end
17 if params[:mark_comment_as_ham] 20 if params[:mark_comment_as_ham]
18 profile.comments_received.find(params[:mark_comment_as_ham]).ham! 21 profile.comments_received.find(params[:mark_comment_as_ham]).ham!
19 end 22 end
  23 + if params[:mark_task_as_ham] && (t = Task.to(profile).find_by_id(params[:mark_task_as_ham]))
  24 + t.ham!
  25 + end
20 if request.xhr? 26 if request.xhr?
21 json_response(true) 27 json_response(true)
22 else 28 else
@@ -28,7 +34,8 @@ class SpamController &lt; MyProfileController @@ -28,7 +34,8 @@ class SpamController &lt; MyProfileController
28 return 34 return
29 end 35 end
30 36
31 - @spam = profile.comments_received.spam.paginate({:page => params[:page]}) 37 + @comment_spam = profile.comments_received.spam.paginate({:page => params[:comments_page]})
  38 + @task_spam = Task.to(profile).spam.paginate({:page => params[:tasks_page]})
32 end 39 end
33 40
34 protected 41 protected
app/controllers/my_profile/tasks_controller.rb
@@ -4,12 +4,12 @@ class TasksController &lt; MyProfileController @@ -4,12 +4,12 @@ class TasksController &lt; MyProfileController
4 4
5 def index 5 def index
6 @filter = params[:filter_type].blank? ? nil : params[:filter_type] 6 @filter = params[:filter_type].blank? ? nil : params[:filter_type]
7 - @tasks = Task.to(profile).pending.of(@filter).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page]) 7 + @tasks = Task.to(profile).without_spam.pending.of(@filter).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page])
8 @failed = params ? params[:failed] : {} 8 @failed = params ? params[:failed] : {}
9 end 9 end
10 10
11 def processed 11 def processed
12 - @tasks = Task.to(profile).closed.sort_by(&:created_at) 12 + @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at)
13 end 13 end
14 14
15 VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ] 15 VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ]
@@ -57,7 +57,7 @@ class TasksController &lt; MyProfileController @@ -57,7 +57,7 @@ class TasksController &lt; MyProfileController
57 end 57 end
58 58
59 def list_requested 59 def list_requested
60 - @tasks = Task.find_all_by_requestor_id(profile.id) 60 + @tasks = Task.without_spam.find_all_by_requestor_id(profile.id)
61 end 61 end
62 62
63 def ticket_details 63 def ticket_details
app/controllers/public/content_viewer_controller.rb
@@ -53,7 +53,9 @@ class ContentViewerController &lt; ApplicationController @@ -53,7 +53,9 @@ class ContentViewerController &lt; ApplicationController
53 # At this point the page will be showed 53 # At this point the page will be showed
54 @page.hit 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 headers['Content-Type'] = @page.mime_type 59 headers['Content-Type'] = @page.mime_type
58 data = @page.data 60 data = @page.data
59 61
app/controllers/public/profile_controller.rb
@@ -206,10 +206,50 @@ class ProfileController &lt; PublicController @@ -206,10 +206,50 @@ class ProfileController &lt; PublicController
206 end 206 end
207 207
208 def view_more_network_activities 208 def view_more_network_activities
209 - @activities = @profile.tracked_notifications.paginate(:per_page => 10, :page => params[:page]) 209 + @activities = @profile.tracked_notifications.paginate(:per_page => 10, :page => params[:page])
210 render :partial => 'profile_network_activities', :locals => {:network_activities => @activities} 210 render :partial => 'profile_network_activities', :locals => {:network_activities => @activities}
211 end 211 end
212 212
  213 + def more_comments
  214 + activity = ActionTracker::Record.find(:first, :conditions => {:id => params[:activity], :user_id => @profile})
  215 + comments_count = activity.comments.count
  216 + comment_page = (params[:comment_page] || 1).to_i
  217 + comments_per_page = 5
  218 + no_more_pages = comments_count <= comment_page * comments_per_page
  219 +
  220 + render :update do |page|
  221 + page.insert_html :bottom, 'profile-wall-activities-comments-'+params[:activity],
  222 + :partial => 'comment', :collection => activity.comments.paginate(:per_page => comments_per_page, :page => comment_page)
  223 +
  224 + if no_more_pages
  225 + page.remove 'profile-wall-activities-comments-more-'+params[:activity]
  226 + else
  227 + page.replace_html 'profile-wall-activities-comments-more-'+params[:activity],
  228 + :partial => 'more_comments', :locals => {:activity => activity, :comment_page => comment_page}
  229 + end
  230 + end
  231 + end
  232 +
  233 + def more_replies
  234 + activity = Scrap.find(:first, :conditions => {:id => params[:activity], :receiver_id => @profile, :scrap_id => nil})
  235 + comments_count = activity.replies.count
  236 + comment_page = (params[:comment_page] || 1).to_i
  237 + comments_per_page = 5
  238 + no_more_pages = comments_count <= comment_page * comments_per_page
  239 +
  240 + render :update do |page|
  241 + page.insert_html :bottom, 'profile-wall-activities-comments-'+params[:activity],
  242 + :partial => 'profile_scrap', :collection => activity.replies.paginate(:per_page => comments_per_page, :page => comment_page), :as => :scrap
  243 +
  244 + if no_more_pages
  245 + page.remove 'profile-wall-activities-comments-more-'+params[:activity]
  246 + else
  247 + page.replace_html 'profile-wall-activities-comments-more-'+params[:activity],
  248 + :partial => 'more_replies', :locals => {:activity => activity, :comment_page => comment_page}
  249 + end
  250 + end
  251 + end
  252 +
213 def remove_scrap 253 def remove_scrap
214 begin 254 begin
215 scrap = current_user.person.scraps(params[:scrap_id]) 255 scrap = current_user.person.scraps(params[:scrap_id])
@@ -343,6 +383,7 @@ class ProfileController &lt; PublicController @@ -343,6 +383,7 @@ class ProfileController &lt; PublicController
343 end 383 end
344 end 384 end
345 385
  386 +
346 protected 387 protected
347 388
348 def check_access_to_profile 389 def check_access_to_profile
@@ -393,4 +434,5 @@ class ProfileController &lt; PublicController @@ -393,4 +434,5 @@ class ProfileController &lt; PublicController
393 def relations_to_include 434 def relations_to_include
394 [:image, :domains, :preferred_domain, :environment] 435 [:image, :domains, :preferred_domain, :environment]
395 end 436 end
  437 +
396 end 438 end
app/helpers/application_helper.rb
@@ -558,6 +558,9 @@ module ApplicationHelper @@ -558,6 +558,9 @@ module ApplicationHelper
558 # displays a link to the profile homepage with its image (as generated by 558 # displays a link to the profile homepage with its image (as generated by
559 # #profile_image) and its name below it. 559 # #profile_image) and its name below it.
560 def profile_image_link( profile, size=:portrait, tag='li', extra_info = nil ) 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 name = profile.short_name 564 name = profile.short_name
562 if profile.person? 565 if profile.person?
563 url = url_for(profile.check_friendship_url) 566 url = url_for(profile.check_friendship_url)
@@ -574,16 +577,16 @@ module ApplicationHelper @@ -574,16 +577,16 @@ module ApplicationHelper
574 extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' ) 577 extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' )
575 links = links_for_balloon(profile) 578 links = links_for_balloon(profile)
576 content_tag('div', content_tag(tag, 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 end 590 end
588 591
589 def gravatar_url_for(email, options = {}) 592 def gravatar_url_for(email, options = {})
@@ -1113,15 +1116,34 @@ module ApplicationHelper @@ -1113,15 +1116,34 @@ module ApplicationHelper
1113 result 1116 result
1114 end 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 end 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 end 1133 end
1123 end 1134 end
1124 1135
  1136 + def manage_enterprises
  1137 + return unless user && user.environment.enabled?(:display_my_enterprises_on_user_menu)
  1138 + manage_link(user.enterprises, :enterprises)
  1139 + end
  1140 +
  1141 + def manage_communities
  1142 + return unless user && user.environment.enabled?(:display_my_communities_on_user_menu)
  1143 + administered_communities = user.communities.more_popular.select {|c| c.admins.include? user}
  1144 + manage_link(administered_communities, :communities)
  1145 + end
  1146 +
1125 def usermenu_logged_in 1147 def usermenu_logged_in
1126 pending_tasks_count = '' 1148 pending_tasks_count = ''
1127 count = user ? Task.to(user).pending.count : -1 1149 count = user ? Task.to(user).pending.count : -1
@@ -1133,6 +1155,7 @@ module ApplicationHelper @@ -1133,6 +1155,7 @@ module ApplicationHelper
1133 render_environment_features(:usermenu) + 1155 render_environment_features(:usermenu) +
1134 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') + 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 manage_enterprises.to_s + 1157 manage_enterprises.to_s +
  1158 + manage_communities.to_s +
1136 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")) + 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 pending_tasks_count + 1160 pending_tasks_count +
1138 link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system")) 1161 link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system"))
@@ -1401,8 +1424,8 @@ module ApplicationHelper @@ -1401,8 +1424,8 @@ module ApplicationHelper
1401 end 1424 end
1402 1425
1403 def filter_html(html, source) 1426 def filter_html(html, source)
1404 - if @plugins  
1405 - html = convert_macro(html, source) 1427 + if @plugins && source && source.has_macro?
  1428 + html = convert_macro(html, source) unless @plugins.enabled_macros.blank?
1406 #TODO This parse should be done through the macro infra, but since there 1429 #TODO This parse should be done through the macro infra, but since there
1407 # are old things that do not support it we are keeping this hot spot. 1430 # are old things that do not support it we are keeping this hot spot.
1408 html = @plugins.pipeline(:parse_content, html, source).first 1431 html = @plugins.pipeline(:parse_content, html, source).first
app/helpers/blog_helper.rb
@@ -42,7 +42,7 @@ module BlogHelper @@ -42,7 +42,7 @@ module BlogHelper
42 42
43 def display_post(article, format = 'full') 43 def display_post(article, format = 'full')
44 no_comments = (format == 'full') ? false : true 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 article_title(article, :no_comments => no_comments) + html 47 article_title(article, :no_comments => no_comments) + html
48 end 48 end
app/helpers/boxes_helper.rb
@@ -100,9 +100,7 @@ module BoxesHelper @@ -100,9 +100,7 @@ module BoxesHelper
100 options[:title] = _("This block is invisible. Your visitors will not see it.") 100 options[:title] = _("This block is invisible. Your visitors will not see it.")
101 end 101 end
102 102
103 - if @controller.send(:content_editor?)  
104 - result = filter_html(result, block)  
105 - end 103 + result = filter_html(result, block)
106 104
107 box_decorator.block_target(block.box, block) + 105 box_decorator.block_target(block.box, block) +
108 content_tag('div', 106 content_tag('div',
app/helpers/categories_helper.rb
@@ -3,10 +3,21 @@ module CategoriesHelper @@ -3,10 +3,21 @@ module CategoriesHelper
3 3
4 COLORS = [ 4 COLORS = [
5 [ N_('Do not display at the menu'), nil ], 5 [ N_('Do not display at the menu'), nil ],
6 - [ N_('Orange'), 1 ],  
7 - [ N_('Green'), 2 ],  
8 - [ N_('Purple'), 3 ],  
9 - [ N_('Red'), 4 ], 6 + [ N_('Orange'), 1],
  7 + [ N_('Green'), 2],
  8 + [ N_('Purple'), 3],
  9 + [ N_('Red'), 4],
  10 + [ N_('Dark Green'), 5],
  11 + [ N_('Blue Oil'), 6],
  12 + [ N_('Blue'), 7],
  13 + [ N_('Brown'), 8],
  14 + [ N_('Light Green'), 9],
  15 + [ N_('Light Blue'), 10],
  16 + [ N_('Dark Blue'), 11],
  17 + [ N_('Blue Pool'), 12],
  18 + [ N_('Beige'), 13],
  19 + [ N_('Yellow'), 14],
  20 + [ N_('Light Brown'), 15]
10 ] 21 ]
11 22
12 TYPES = [ 23 TYPES = [
app/helpers/cms_helper.rb
@@ -33,7 +33,7 @@ module CmsHelper @@ -33,7 +33,7 @@ module CmsHelper
33 link_to article_name, {:action => 'view', :id => article.id}, :class => icon_for_article(article) 33 link_to article_name, {:action => 'view', :id => article.id}, :class => icon_for_article(article)
34 else 34 else
35 if article.image? 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 else 37 else
38 link_to article_name, article.url, :class => icon_for_article(article) 38 link_to article_name, article.url, :class => icon_for_article(article)
39 end 39 end
app/helpers/content_viewer_helper.rb
@@ -17,7 +17,7 @@ module ContentViewerHelper @@ -17,7 +17,7 @@ module ContentViewerHelper
17 title = article.display_title if article.kind_of?(UploadedFile) && article.image? 17 title = article.display_title if article.kind_of?(UploadedFile) && article.image?
18 title = article.title if title.blank? 18 title = article.title if title.blank?
19 title = content_tag('h1', h(title), :class => 'title') 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 unless args[:no_link] 21 unless args[:no_link]
22 title = content_tag('h1', link_to(article.name, article.url), :class => 'title') 22 title = content_tag('h1', link_to(article.name, article.url), :class => 'title')
23 end 23 end
app/helpers/folder_helper.rb
@@ -21,6 +21,7 @@ module FolderHelper @@ -21,6 +21,7 @@ module FolderHelper
21 end 21 end
22 22
23 def display_article_in_listing(article, recursive = false, level = 0) 23 def display_article_in_listing(article, recursive = false, level = 0)
  24 + article = FilePresenter.for article
24 article_link = if article.image? 25 article_link = if article.image?
25 link_to('&nbsp;' * (level * 4) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true)) 26 link_to('&nbsp;' * (level * 4) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true))
26 else 27 else
@@ -40,12 +41,15 @@ module FolderHelper @@ -40,12 +41,15 @@ module FolderHelper
40 end 41 end
41 42
42 def icon_for_article(article) 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 if (icon =~ /\//) 48 if (icon =~ /\//)
45 icon 49 icon
46 else 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 klasses += ' icon-upload-file' 53 klasses += ' icon-upload-file'
50 end 54 end
51 klasses 55 klasses
app/helpers/sweeper_helper.rb
@@ -44,4 +44,30 @@ module SweeperHelper @@ -44,4 +44,30 @@ module SweeperHelper
44 def expire_profile_index(profile) 44 def expire_profile_index(profile)
45 expire_timeout_fragment(profile.relationships_cache_key) 45 expire_timeout_fragment(profile.relationships_cache_key)
46 end 46 end
  47 +
  48 + def expire_blocks_cache(context, causes)
  49 + if context.kind_of?(Profile)
  50 + profile = context
  51 + environment = profile.environment
  52 + else
  53 + environment = context
  54 + profile = nil
  55 + end
  56 +
  57 + blocks_to_expire = []
  58 + if profile
  59 + profile.blocks.each {|block|
  60 + conditions = block.class.expire_on
  61 + blocks_to_expire << block unless (conditions[:profile] & causes).empty?
  62 + }
  63 + end
  64 + environment.blocks.each {|block|
  65 + conditions = block.class.expire_on
  66 + blocks_to_expire << block unless (conditions[:environment] & causes).empty?
  67 + }
  68 +
  69 + blocks_to_expire.uniq!
  70 + BlockSweeper.expire_blocks(blocks_to_expire)
  71 + end
  72 +
47 end 73 end
app/models/article.rb
@@ -2,6 +2,8 @@ require &#39;hpricot&#39; @@ -2,6 +2,8 @@ require &#39;hpricot&#39;
2 2
3 class Article < ActiveRecord::Base 3 class Article < ActiveRecord::Base
4 4
  5 + acts_as_having_image
  6 +
5 SEARCHABLE_FIELDS = { 7 SEARCHABLE_FIELDS = {
6 :name => 10, 8 :name => 10,
7 :abstract => 3, 9 :abstract => 3,
@@ -154,8 +156,12 @@ class Article &lt; ActiveRecord::Base @@ -154,8 +156,12 @@ class Article &lt; ActiveRecord::Base
154 end 156 end
155 end 157 end
156 158
  159 + def css_class_list
  160 + [self.class.name.underscore.dasherize]
  161 + end
  162 +
157 def css_class_name 163 def css_class_name
158 - self.class.name.underscore.dasherize 164 + [css_class_list].flatten.compact.join(' ')
159 end 165 end
160 166
161 def pending_categorizations 167 def pending_categorizations
@@ -310,6 +316,10 @@ class Article &lt; ActiveRecord::Base @@ -310,6 +316,10 @@ class Article &lt; ActiveRecord::Base
310 def belongs_to_blog? 316 def belongs_to_blog?
311 self.parent and self.parent.blog? 317 self.parent and self.parent.blog?
312 end 318 end
  319 +
  320 + def belongs_to_forum?
  321 + self.parent and self.parent.forum?
  322 + end
313 323
314 def info_from_last_update 324 def info_from_last_update
315 last_comment = comments.last 325 last_comment = comments.last
@@ -325,7 +335,7 @@ class Article &lt; ActiveRecord::Base @@ -325,7 +335,7 @@ class Article &lt; ActiveRecord::Base
325 end 335 end
326 336
327 def view_url 337 def view_url
328 - @view_url ||= image? ? url.merge(:view => true) : url 338 + @view_url ||= is_a?(UploadedFile) ? url.merge(:view => true) : url
329 end 339 end
330 340
331 def comment_url_structure(comment, action = :edit) 341 def comment_url_structure(comment, action = :edit)
@@ -673,6 +683,10 @@ class Article &lt; ActiveRecord::Base @@ -673,6 +683,10 @@ class Article &lt; ActiveRecord::Base
673 683
674 delegate :region, :region_id, :environment, :environment_id, :to => :profile, :allow_nil => true 684 delegate :region, :region_id, :environment, :environment_id, :to => :profile, :allow_nil => true
675 685
  686 + def has_macro?
  687 + true
  688 + end
  689 +
676 private 690 private
677 691
678 def sanitize_tag_list 692 def sanitize_tag_list
app/models/article_block.rb
@@ -12,7 +12,7 @@ class ArticleBlock &lt; Block @@ -12,7 +12,7 @@ class ArticleBlock &lt; Block
12 block = self 12 block = self
13 lambda do 13 lambda do
14 block_title(block.title) + 14 block_title(block.title) +
15 - (block.article ? article_to_html(block.article, 15 + (block.article ? article_to_html(FilePresenter.for(block.article),
16 :gallery_view => false, 16 :gallery_view => false,
17 :inside_block => block, # For Blogs and folders 17 :inside_block => block, # For Blogs and folders
18 :format => block.visualization_format # For Articles and contents 18 :format => block.visualization_format # For Articles and contents
@@ -23,7 +23,7 @@ class ArticleBlock &lt; Block @@ -23,7 +23,7 @@ class ArticleBlock &lt; Block
23 def article_id 23 def article_id
24 self.settings[:article_id] 24 self.settings[:article_id]
25 end 25 end
26 - 26 +
27 def article_id= value 27 def article_id= value
28 self.settings[:article_id] = value.blank? ? nil : value.to_i 28 self.settings[:article_id] = value.blank? ? nil : value.to_i
29 end 29 end
@@ -63,4 +63,9 @@ class ArticleBlock &lt; Block @@ -63,4 +63,9 @@ class ArticleBlock &lt; Block
63 end 63 end
64 64
65 settings_items :visualization_format, :type => :string, :default => 'short' 65 settings_items :visualization_format, :type => :string, :default => 'short'
  66 +
  67 + def self.expire_on
  68 + { :profile => [:article], :environment => [:article] }
  69 + end
  70 +
66 end 71 end
app/models/block.rb
@@ -138,4 +138,19 @@ class Block &lt; ActiveRecord::Base @@ -138,4 +138,19 @@ class Block &lt; ActiveRecord::Base
138 4.hours 138 4.hours
139 end 139 end
140 140
  141 + def has_macro?
  142 + false
  143 + end
  144 +
  145 + # Override in your subclasses.
  146 + # Define which events and context should cause the block cache to expire
  147 + # Possible events are: :article, :profile, :friendship, :category
  148 + # Possible contexts are: :profile, :environment
  149 + def self.expire_on
  150 + {
  151 + :profile => [],
  152 + :environment => []
  153 + }
  154 + end
  155 +
141 end 156 end
app/models/blog_archives_block.rb
@@ -45,4 +45,7 @@ class BlogArchivesBlock &lt; Block @@ -45,4 +45,7 @@ class BlogArchivesBlock &lt; Block
45 content_tag('div', link_to(_('Subscribe RSS Feed'), owner_blog.feed.url), :class => 'subscribe-feed') 45 content_tag('div', link_to(_('Subscribe RSS Feed'), owner_blog.feed.url), :class => 'subscribe-feed')
46 end 46 end
47 47
  48 + def self.expire_on
  49 + { :profile => [:article], :environment => [:article] }
  50 + end
48 end 51 end
app/models/categories_block.rb
@@ -35,4 +35,7 @@ class CategoriesBlock &lt; Block @@ -35,4 +35,7 @@ class CategoriesBlock &lt; Block
35 end 35 end
36 end 36 end
37 37
  38 + def self.expire_on
  39 + { :profile => [], :environment => [:category] }
  40 + end
38 end 41 end
app/models/category.rb
@@ -12,7 +12,7 @@ class Category &lt; ActiveRecord::Base @@ -12,7 +12,7 @@ class Category &lt; ActiveRecord::Base
12 validates_uniqueness_of :slug,:scope => [ :environment_id, :parent_id ], :message => N_('%{fn} is already being used by another category.').fix_i18n 12 validates_uniqueness_of :slug,:scope => [ :environment_id, :parent_id ], :message => N_('%{fn} is already being used by another category.').fix_i18n
13 belongs_to :environment 13 belongs_to :environment
14 14
15 - validates_inclusion_of :display_color, :in => [ 1, 2, 3, 4, nil ] 15 + validates_inclusion_of :display_color, :in => 1..15, :allow_nil => true
16 validates_uniqueness_of :display_color, :scope => :environment_id, :if => (lambda { |cat| ! cat.display_color.nil? }), :message => N_('%{fn} was already assigned to another category.').fix_i18n 16 validates_uniqueness_of :display_color, :scope => :environment_id, :if => (lambda { |cat| ! cat.display_color.nil? }), :message => N_('%{fn} was already assigned to another category.').fix_i18n
17 17
18 # Finds all top level categories for a given environment. 18 # Finds all top level categories for a given environment.
app/models/comment.rb
@@ -16,9 +16,7 @@ class Comment &lt; ActiveRecord::Base @@ -16,9 +16,7 @@ class Comment &lt; ActiveRecord::Base
16 has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy 16 has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy
17 belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id' 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 named_scope :without_reply, :conditions => ['reply_of_id IS NULL'] 19 named_scope :without_reply, :conditions => ['reply_of_id IS NULL']
21 - named_scope :spam, :conditions => ['spam = ?', true]  
22 20
23 # unauthenticated authors: 21 # unauthenticated authors:
24 validates_presence_of :name, :if => (lambda { |record| !record.email.blank? }) 22 validates_presence_of :name, :if => (lambda { |record| !record.email.blank? })
@@ -108,6 +106,17 @@ class Comment &lt; ActiveRecord::Base @@ -108,6 +106,17 @@ class Comment &lt; ActiveRecord::Base
108 106
109 include Noosfero::Plugin::HotSpot 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 def verify_and_notify 120 def verify_and_notify
112 check_for_spam 121 check_for_spam
113 unless spam? 122 unless spam?
@@ -115,10 +124,6 @@ class Comment &lt; ActiveRecord::Base @@ -115,10 +124,6 @@ class Comment &lt; ActiveRecord::Base
115 end 124 end
116 end 125 end
117 126
118 - def check_for_spam  
119 - plugins.dispatch(:check_comment_for_spam, self)  
120 - end  
121 -  
122 def notify_by_mail 127 def notify_by_mail
123 if source.kind_of?(Article) && article.notify_comments? 128 if source.kind_of?(Article) && article.notify_comments?
124 if !notification_emails.empty? 129 if !notification_emails.empty?
@@ -205,37 +210,6 @@ class Comment &lt; ActiveRecord::Base @@ -205,37 +210,6 @@ class Comment &lt; ActiveRecord::Base
205 @rejected = true 210 @rejected = true
206 end 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 def need_moderation? 213 def need_moderation?
240 article.moderate_comments? && (author.nil? || article.author != author) 214 article.moderate_comments? && (author.nil? || article.author != author)
241 end 215 end
app/models/environment.rb
@@ -127,7 +127,9 @@ class Environment &lt; ActiveRecord::Base @@ -127,7 +127,9 @@ class Environment &lt; ActiveRecord::Base
127 'captcha_for_logged_users' => _('Ask captcha when a logged user comments too'), 127 'captcha_for_logged_users' => _('Ask captcha when a logged user comments too'),
128 'skip_new_user_email_confirmation' => _('Skip e-mail confirmation for new users'), 128 'skip_new_user_email_confirmation' => _('Skip e-mail confirmation for new users'),
129 'send_welcome_email_to_new_users' => _('Send welcome e-mail to new users'), 129 'send_welcome_email_to_new_users' => _('Send welcome e-mail to new users'),
130 - 'allow_change_of_redirection_after_login' => _('Allow users to set the page to redirect after login') 130 + 'allow_change_of_redirection_after_login' => _('Allow users to set the page to redirect after login'),
  131 + 'display_my_communities_on_user_menu' => _('Display on menu the list of communities the user can manage'),
  132 + 'display_my_enterprises_on_user_menu' => _('Display on menu the list of enterprises the user can manage')
131 } 133 }
132 end 134 end
133 135
app/models/link_list_block.rb
@@ -33,6 +33,12 @@ class LinkListBlock &lt; Block @@ -33,6 +33,12 @@ class LinkListBlock &lt; Block
33 ['chat', N_('Chat')] 33 ['chat', N_('Chat')]
34 ] 34 ]
35 35
  36 + TARGET_OPTIONS = [
  37 + [N_('Same page'), '_self'],
  38 + [N_('New tab'), '_blank'],
  39 + [N_('New window'), '_new'],
  40 + ]
  41 +
36 settings_items :links, Array, :default => [] 42 settings_items :links, Array, :default => []
37 43
38 before_save do |block| 44 before_save do |block|
@@ -57,7 +63,7 @@ class LinkListBlock &lt; Block @@ -57,7 +63,7 @@ class LinkListBlock &lt; Block
57 def link_html(link) 63 def link_html(link)
58 klass = 'icon-' + link[:icon] if link[:icon] 64 klass = 'icon-' + link[:icon] if link[:icon]
59 sanitize_link( 65 sanitize_link(
60 - link_to(link[:name], expand_address(link[:address]), :class => klass) 66 + link_to(link[:name], expand_address(link[:address]), :target => link[:target], :class => klass)
61 ) 67 )
62 end 68 end
63 69
app/models/raw_html_block.rb
@@ -10,4 +10,7 @@ class RawHTMLBlock &lt; Block @@ -10,4 +10,7 @@ class RawHTMLBlock &lt; Block
10 (title.blank? ? '' : block_title(title)).html_safe + html.to_s.html_safe 10 (title.blank? ? '' : block_title(title)).html_safe + html.to_s.html_safe
11 end 11 end
12 12
  13 + def has_macro?
  14 + true
  15 + end
13 end 16 end
app/models/recent_documents_block.rb
@@ -30,4 +30,7 @@ class RecentDocumentsBlock &lt; Block @@ -30,4 +30,7 @@ class RecentDocumentsBlock &lt; Block
30 end 30 end
31 end 31 end
32 32
  33 + def self.expire_on
  34 + { :profile => [:article], :environment => [:article] }
  35 + end
33 end 36 end
app/models/spammer_logger.rb
@@ -6,6 +6,8 @@ class SpammerLogger &lt; Logger @@ -6,6 +6,8 @@ class SpammerLogger &lt; Logger
6 if object 6 if object
7 if object.kind_of?(Comment) 7 if object.kind_of?(Comment)
8 @logger << "[#{Time.now.strftime('%F %T %z')}] Comment-id: #{object.id} IP: #{spammer_ip}\n" 8 @logger << "[#{Time.now.strftime('%F %T %z')}] Comment-id: #{object.id} IP: #{spammer_ip}\n"
  9 + elsif object.kind_of?(SuggestArticle)
  10 + @logger << "[#{Time.now.strftime('%F %T %z')}] SuggestArticle-id: #{object.id} IP: #{spammer_ip}\n"
9 end 11 end
10 else 12 else
11 @logger << "[#{Time.now.strftime('%F %T %z')}] IP: #{spammer_ip}\n" 13 @logger << "[#{Time.now.strftime('%F %T %z')}] IP: #{spammer_ip}\n"
app/models/suggest_article.rb
@@ -11,6 +11,17 @@ class SuggestArticle &lt; Task @@ -11,6 +11,17 @@ class SuggestArticle &lt; Task
11 settings_items :source, :type => String 11 settings_items :source, :type => String
12 settings_items :source_name, :type => String 12 settings_items :source_name, :type => String
13 settings_items :highlighted, :type => :boolean, :default => false 13 settings_items :highlighted, :type => :boolean, :default => false
  14 + settings_items :ip_address, :type => String
  15 + settings_items :user_agent, :type => String
  16 + settings_items :referrer, :type => String
  17 +
  18 + after_create :schedule_spam_checking
  19 +
  20 + def schedule_spam_checking
  21 + self.delay.check_for_spam
  22 + end
  23 +
  24 + include Noosfero::Plugin::HotSpot
14 25
15 def sender 26 def sender
16 "#{name} (#{email})" 27 "#{name} (#{email})"
@@ -61,4 +72,12 @@ class SuggestArticle &lt; Task @@ -61,4 +72,12 @@ class SuggestArticle &lt; Task
61 _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name } 72 _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name }
62 end 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 end 83 end
app/models/tags_block.rb
@@ -59,4 +59,8 @@ class TagsBlock &lt; Block @@ -59,4 +59,8 @@ class TagsBlock &lt; Block
59 15.minutes 59 15.minutes
60 end 60 end
61 61
  62 + def self.expire_on
  63 + { :profile => [:article], :environment => [:article] }
  64 + end
  65 +
62 end 66 end
app/models/task.rb
@@ -235,6 +235,8 @@ class Task &lt; ActiveRecord::Base @@ -235,6 +235,8 @@ class Task &lt; ActiveRecord::Base
235 end 235 end
236 end 236 end
237 237
  238 + include Spammable
  239 +
238 protected 240 protected
239 241
240 # This method must be overrided in subclasses, and its implementation must do 242 # This method must be overrided in subclasses, and its implementation must do
@@ -275,6 +277,7 @@ class Task &lt; ActiveRecord::Base @@ -275,6 +277,7 @@ class Task &lt; ActiveRecord::Base
275 named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} } 277 named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} }
276 named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} } 278 named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} }
277 279
  280 +
278 named_scope :to, lambda { |profile| 281 named_scope :to, lambda { |profile|
279 environment_condition = nil 282 environment_condition = nil
280 if profile.person? 283 if profile.person?
app/models/uploaded_file.rb
@@ -41,7 +41,25 @@ class UploadedFile &lt; Article @@ -41,7 +41,25 @@ class UploadedFile &lt; Article
41 end 41 end
42 42
43 def self.max_size 43 def self.max_size
44 - UploadedFile.attachment_options[:max_size] 44 + default = 5.megabytes
  45 +
  46 + multipliers = {
  47 + :KB => :kilobytes,
  48 + :MB => :megabytes,
  49 + :GB => :gigabytes,
  50 + :TB => :terabytes,
  51 + }
  52 + max_upload_size = NOOSFERO_CONF['max_upload_size']
  53 +
  54 + if max_upload_size =~ /^(\d+(\.\d+)?)\s*(KB|MB|GB|TB)?$/
  55 + number = $1.to_f
  56 + unit = $3 || :MB
  57 + multiplier = multipliers[unit.to_sym]
  58 +
  59 + number.send(multiplier).to_i
  60 + else
  61 + default
  62 + end
45 end 63 end
46 64
47 # FIXME need to define min/max file size 65 # FIXME need to define min/max file size
@@ -52,20 +70,28 @@ class UploadedFile &lt; Article @@ -52,20 +70,28 @@ class UploadedFile &lt; Article
52 has_attachment :storage => :file_system, 70 has_attachment :storage => :file_system,
53 :thumbnails => { :icon => [24,24], :thumb => '130x130>', :slideshow => '320x240>', :display => '640X480>' }, 71 :thumbnails => { :icon => [24,24], :thumb => '130x130>', :slideshow => '320x240>', :display => '640X480>' },
54 :thumbnail_class => Thumbnail, 72 :thumbnail_class => Thumbnail,
55 - :max_size => 5.megabytes # remember to update validate message below 73 + :max_size => self.max_size
56 74
57 - validates_attachment :size => N_("%{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n 75 + validates_attachment :size => N_("%{fn} of uploaded file was larger than the maximum size of %{size}").sub('%{size}', self.max_size.to_humanreadable).fix_i18n
58 76
59 delay_attachment_fu_thumbnails 77 delay_attachment_fu_thumbnails
60 78
61 postgresql_attachment_fu 79 postgresql_attachment_fu
62 80
  81 + # Use this method only to get the generic icon for this kind of content.
  82 + # If you want the specific icon for a file type or the iconified version
  83 + # of an image, use FilePresenter.for(uploaded_file).icon_name
63 def self.icon_name(article = nil) 84 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' 85 + unless article.nil?
  86 + warn = ('='*80) + "\n" +
  87 + 'The method `UploadedFile.icon_name(obj)` is deprecated. ' +
  88 + 'You must to encapsulate UploadedFile with `FilePresenter.for()`.' +
  89 + "\n" + ('='*80)
  90 + raise NoMethodError, warn if ENV['RAILS_ENV'] == 'test'
  91 + Rails.logger.warn warn if Rails.logger
  92 + puts warn if ENV['RAILS_ENV'] == 'development'
68 end 93 end
  94 + 'upload-file'
69 end 95 end
70 96
71 def mime_type 97 def mime_type
@@ -91,40 +117,27 @@ class UploadedFile &lt; Article @@ -91,40 +117,27 @@ class UploadedFile &lt; Article
91 end 117 end
92 118
93 def to_html(options = {}) 119 def to_html(options = {})
  120 + warn = ('='*80) + "\n" +
  121 + 'The method `UploadedFile#to_html()` is deprecated. ' +
  122 + 'You must to encapsulate UploadedFile with `FilePresenter.for()`.' +
  123 + "\n" + ('='*80)
  124 + raise NoMethodError, warn if ENV['RAILS_ENV'] == 'test'
  125 + Rails.logger.warn warn if Rails.logger
  126 + puts warn if ENV['RAILS_ENV'] == 'development'
94 article = self 127 article = self
95 if image? 128 if image?
96 lambda do 129 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 - 130 + image_tag(article.public_filename(:display),
  131 + :class => article.css_class_name,
  132 + :style => 'max-width: 100%') +
  133 + content_tag('div', article.abstract, :class => 'uploaded-file-description')
123 end 134 end
124 else 135 else
125 lambda do 136 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') 137 + content_tag('div',
  138 + link_to(article.name, article.url),
  139 + :class => article.css_class_name) +
  140 + content_tag('div', article.abstract, :class => 'uploaded-file-description')
128 end 141 end
129 end 142 end
130 end 143 end
app/presenters/generic.rb 0 → 100644
@@ -0,0 +1,7 @@ @@ -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 @@ @@ -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/sweepers/article_sweeper.rb
@@ -16,15 +16,15 @@ class ArticleSweeper &lt; ActiveRecord::Observer @@ -16,15 +16,15 @@ class ArticleSweeper &lt; ActiveRecord::Observer
16 end 16 end
17 end 17 end
18 18
  19 +
19 protected 20 protected
20 21
21 def expire_caches(article) 22 def expire_caches(article)
  23 + expire_blocks_cache(article.profile, [:article])
  24 +
22 return if !article.environment 25 return if !article.environment
  26 +
23 article.hierarchy(true).each { |a| a.touch if a != article } 27 article.hierarchy(true).each { |a| a.touch if a != article }
24 - blocks = article.profile.blocks  
25 - blocks += article.profile.environment.blocks if article.profile.environment  
26 - blocks = blocks.select{|b|[RecentDocumentsBlock, BlogArchivesBlock].any?{|c| b.kind_of?(c)}}  
27 - BlockSweeper.expire_blocks(blocks)  
28 env = article.profile.environment 28 env = article.profile.environment
29 if env && (env.portal_community == article.profile) 29 if env && (env.portal_community == article.profile)
30 article.environment.locales.keys.each do |locale| 30 article.environment.locales.keys.each do |locale|
app/sweepers/category_sweeper.rb
@@ -3,7 +3,13 @@ class CategorySweeper &lt; ActiveRecord::Observer @@ -3,7 +3,13 @@ class CategorySweeper &lt; ActiveRecord::Observer
3 include SweeperHelper 3 include SweeperHelper
4 4
5 def after_save(category) 5 def after_save(category)
  6 + expire_blocks_cache(category.environment, [:category])
  7 +
  8 + # Needed for environments with application layout
6 expire_fragment(category.environment.id.to_s + "_categories_menu") 9 expire_fragment(category.environment.id.to_s + "_categories_menu")
7 end 10 end
8 11
  12 + def after_destroy(category)
  13 + expire_blocks_cache(category.environment, [:category])
  14 + end
9 end 15 end
app/views/box_organizer/_link_list_block.rhtml
1 <strong><%= _('Links') %></strong> 1 <strong><%= _('Links') %></strong>
2 <div id='edit-link-list-block' style='width:450px'> 2 <div id='edit-link-list-block' style='width:450px'>
3 <table id='links' class='noborder'> 3 <table id='links' class='noborder'>
4 - <tr><th><%= _('Icon') %></th><th><%= _('Name') %></th><th><%= _('Address') %></th></tr> 4 + <tr><th><%= _('Icon') %></th><th><%= _('Name') %></th><th><%= _('Address') %></th><th><%= _('Target') %></th></tr>
5 <% for link in @block.links do %> 5 <% for link in @block.links do %>
6 <tr> 6 <tr>
7 <td> 7 <td>
@@ -9,6 +9,9 @@ @@ -9,6 +9,9 @@
9 </td> 9 </td>
10 <td><%= text_field_tag 'block[links][][name]', link[:name], :class => 'link-name', :maxlength => 20 %></td> 10 <td><%= text_field_tag 'block[links][][name]', link[:name], :class => 'link-name', :maxlength => 20 %></td>
11 <td class='cel-address'><%= text_field_tag 'block[links][][address]', link[:address], :class => 'link-address' %></td> 11 <td class='cel-address'><%= text_field_tag 'block[links][][address]', link[:address], :class => 'link-address' %></td>
  12 + <td>
  13 + <%= select_tag('block[links][][target]', options_for_select(LinkListBlock::TARGET_OPTIONS, link[:target])) %>
  14 + </td>
12 </tr> 15 </tr>
13 <% end %> 16 <% end %>
14 </table> 17 </table>
@@ -18,7 +21,9 @@ @@ -18,7 +21,9 @@
18 page.insert_html :bottom, 'links', content_tag('tr', 21 page.insert_html :bottom, 'links', content_tag('tr',
19 content_tag('td', icon_selector('ok')) + 22 content_tag('td', icon_selector('ok')) +
20 content_tag('td', text_field_tag('block[links][][name]', '', :maxlength => 20)) + 23 content_tag('td', text_field_tag('block[links][][name]', '', :maxlength => 20)) +
21 - content_tag('td', text_field_tag('block[links][][address]', nil, :class => 'cel-address')) 24 + content_tag('td', text_field_tag('block[links][][address]', nil, :class => 'link-address'), :class => 'cel-address') +
  25 + content_tag('td', select_tag('block[links][][target]',
  26 +options_for_select(LinkListBlock::TARGET_OPTIONS, link[:target])))
22 ) + 27 ) +
23 javascript_tag("$('edit-link-list-block').scrollTop = $('edit-link-list-block').scrollHeight") 28 javascript_tag("$('edit-link-list-block').scrollTop = $('edit-link-list-block').scrollHeight")
24 end %> 29 end %>
app/views/cms/view.rhtml
@@ -2,6 +2,18 @@ @@ -2,6 +2,18 @@
2 <%= _('Content management') %> 2 <%= _('Content management') %>
3 </h1> 3 </h1>
4 4
  5 +<% if !environment.enabled?('cant_change_homepage') && !remove_content_button(:home) %>
  6 + <div class="cms-homepage">
  7 + <%= _('Profile homepage:') %>
  8 + <% if profile.home_page %>
  9 + <%= link_to_article(profile.home_page) %>
  10 + <%= button_without_text(:'home-not', _('Reset homepage'), { :action => 'set_home_page', :id => nil }, :method => :post) %>
  11 + <% else %>
  12 + <span class="cms-homepage-default"><%= _('Profile Information') %></span>
  13 + <% end %>
  14 + </div>
  15 +<% end %>
  16 +
5 <% button_bar(:style => 'margin-bottom: 1em;') do %> 17 <% button_bar(:style => 'margin-bottom: 1em;') do %>
6 <% parent_id = ((@article && @article.allow_children?) ? @article : nil) %> 18 <% parent_id = ((@article && @article.allow_children?) ? @article : nil) %>
7 19
@@ -40,20 +52,27 @@ @@ -40,20 +52,27 @@
40 </tr> 52 </tr>
41 <% end %> 53 <% end %>
42 54
43 - <% @articles.each do |article| %> 55 + <% @articles.each do |article| article = FilePresenter.for article %>
44 <tr title="<%= article.title%>" > 56 <tr title="<%= article.title%>" >
45 - <td> 57 + <td class="article-name">
46 <%= link_to_article(article) %> 58 <%= link_to_article(article) %>
47 </td> 59 </td>
48 - <td>  
49 - <%= article.class.short_description %> 60 + <% short_description = article.respond_to?(:short_description) ?
  61 + article.short_description :
  62 + article.class.short_description %>
  63 + <td class="article-mime" title=<%= short_description.to_json %>>
  64 + <%= short_description %>
50 </td> 65 </td>
51 <td class="article-controls"> 66 <td class="article-controls">
52 <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit) %> 67 <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit) %>
53 <%= button_without_text :eyes, _('Public view'), article.view_url %> 68 <%= button_without_text :eyes, _('Public view'), article.view_url %>
54 <%= display_spread_button(profile, article) unless article.folder? || remove_content_button(:spread)%> 69 <%= display_spread_button(profile, article) unless article.folder? || remove_content_button(:spread)%>
55 <% if !environment.enabled?('cant_change_homepage') && !remove_content_button(:home) %> 70 <% if !environment.enabled?('cant_change_homepage') && !remove_content_button(:home) %>
56 - <%= expirable_button article, :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %> 71 + <% if profile.home_page != article %>
  72 + <%= expirable_button article, :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %>
  73 + <% else %>
  74 + <%= button_without_text(:'home-not', _('Reset homepage'), { :action => 'set_home_page', :id => nil }, :method => :post) %>
  75 + <% end %>
57 <% end %> 76 <% end %>
58 <%= display_delete_button(article) if !remove_content_button(:delete) %> 77 <%= display_delete_button(article) if !remove_content_button(:delete) %>
59 </td> 78 </td>
app/views/file_presenter/_generic.html.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -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 @@ @@ -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/memberships/new_community.rhtml
@@ -46,9 +46,11 @@ @@ -46,9 +46,11 @@
46 46
47 <%= template_options(Community, 'community')%> 47 <%= template_options(Community, 'community')%>
48 48
  49 + <%= hidden_field_tag('back_to', @back_to) %>
  50 +
49 <% button_bar do %> 51 <% button_bar do %>
50 <%= submit_button(:save, _('Create')) %> 52 <%= submit_button(:save, _('Create')) %>
51 - <%= button(:cancel, _('Cancel'), :action => 'index') %> 53 + <%= button(:cancel, _('Cancel'), @back_to ) %>
52 <% end %> 54 <% end %>
53 55
54 <% end %> 56 <% end %>
app/views/profile/_more_comments.rhtml 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +<div class='view-more-comments'>
  2 + <%= link_to(_('More'), :profile => @profile.identifier, :controller => 'profile', :action => 'more_comments', :activity => activity, :comment_page => (comment_page + 1)) %>
  3 +</div>
app/views/profile/_more_replies.rhtml 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +<div class='view-more-replies'>
  2 + <%= link_to(_('More'), :profile => @profile.identifier, :controller => 'profile', :action => 'more_replies', :activity => activity, :comment_page => (comment_page + 1)) %>
  3 +</div>
app/views/profile/_profile_activities_list.rhtml
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 <% if activity.kind_of?(ActionTracker::Record) %> 4 <% if activity.kind_of?(ActionTracker::Record) %>
5 <%= render :partial => 'profile_activity', :locals => { :activity => activity, :tab_action => 'wall' } if activity.visible? %> 5 <%= render :partial => 'profile_activity', :locals => { :activity => activity, :tab_action => 'wall' } if activity.visible? %>
6 <% else %> 6 <% else %>
7 - <%= render :partial => 'profile_scrap', :locals => {:scrap => activity } %> 7 + <%= render :partial => 'profile_scraps', :locals => { :activity => activity, :scrap => activity } %>
8 <% end %> 8 <% end %>
9 <% end %> 9 <% end %>
10 <% end %> 10 <% end %>
app/views/profile/_profile_comments.rhtml
1 <hr /> 1 <hr />
2 2
3 -<% if activity.comments_count > 2 %> 3 +<ul id="profile-wall-activities-comments-<%= activity.id %>" class="profile-wall-activities-comments" >
  4 +</ul>
  5 +<% if activity.comments_count > 0 %>
  6 +<div id="profile-wall-activities-comments-more-<%= activity.id %>" class="profile-wall-activities-comments" >
4 <div class='view-all-comments icon-chat'> 7 <div class='view-all-comments icon-chat'>
5 - <%= link_to(_("View all %s comments") % activity.comments_count, '#') %> 8 + <%= link_to(n_('View comment', "View all %s comments", activity.comments_count) % activity.comments_count, :profile => profile.identifier, :controller => 'profile', :action => 'more_comments', :activity => activity, :comment_page => (1)) %>
6 </div> 9 </div>
  10 +</div>
7 <% end %> 11 <% end %>
8 -  
9 -<ul class="profile-wall-activities-comments" style="<%= 'display:none;' if (activity.comments_count > 2) %>" >  
10 - <%= render :partial => 'comment', :collection => activity.comments_as_thread %>  
11 -</ul>  
12 -  
13 <%= render :partial => 'profile_comment_form', :locals => { :activity => activity, :tab_action => tab_action } %> 12 <%= render :partial => 'profile_comment_form', :locals => { :activity => activity, :tab_action => tab_action } %>
app/views/profile/_profile_scrap.rhtml
@@ -16,13 +16,7 @@ @@ -16,13 +16,7 @@
16 </div> 16 </div>
17 </div> 17 </div>
18 18
19 - <% if scrap.replies.count > 2 %>  
20 - <div class='view-all-comments icon-chat'>  
21 - <%= link_to(_("View all %s comments") % scrap.replies.count, '#') %>  
22 - </div>  
23 - <% end %>  
24 -  
25 - <ul class="profile-wall-activities-comments scrap-replies" style="width: auto; <%= 'display:none;' if (scrap.replies.count > 2) %>" > 19 + <ul class="profile-wall-activities-comments scrap-replies" style="width: auto;" >
26 <% scrap.replies.map do |reply| %> 20 <% scrap.replies.map do |reply| %>
27 <%= render :partial => 'profile_scrap', :locals => {:scrap => reply} %> 21 <%= render :partial => 'profile_scrap', :locals => {:scrap => reply} %>
28 <% end %> 22 <% end %>
app/views/profile/_profile_scraps.rhtml
1 -NÃO DEVE APARECER 1 +<li class='profile-activity-item' id='profile-activity-item-<%= scrap.id %>'>
  2 + <div class='profile-activity-image'>
  3 + <%= link_to(profile_image(scrap.sender, :minor), scrap.sender.url) %>
  4 + </div>
  5 + <div class='profile-activity-description'>
  6 + <p class='profile-activity-sender'><%= link_to scrap.sender.name, scrap.sender.url %></p>
  7 + <p class='profile-activity-text'><%= txt2html scrap.content %></p>
  8 + <p class='profile-activity-time'><%= time_ago_as_sentence(scrap.created_at) %></p>
  9 + <div class='profile-wall-actions'>
  10 + <% if logged_in? && current_person.follows?(scrap.sender) %>
  11 + <span class='profile-activity-send-reply'>
  12 + <%= link_to_function s_('profile|Comment'), "hide_and_show(['#profile-wall-message-response-#{scrap.id}'],['#profile-wall-reply-#{scrap.id}', '#profile-wall-reply-form-#{scrap.id}']);$('reply_content_#{scrap.id}').value='';$('reply_content_#{scrap.id}').focus();return false", :class => "profile-send-reply" %>
  13 + </span>
  14 + <% end %>
  15 + <%= link_to_function(_('Remove'), 'remove_item_wall(this, %s, %s, %s); return false ;' % ["'.profile-activity-item'", url_for(:profile => params[:profile], :action => :remove_scrap, :scrap_id => scrap.id, :view => params[:view]).to_json, _('Are you sure you want to remove this scrap and all its replies?').to_json]) if logged_in? && user.can_control_scrap?(scrap) %>
  16 + </div>
  17 + </div>
  18 +
  19 +
  20 + <ul id="profile-wall-activities-comments-<%= activity.id %>" class="profile-wall-activities-comments scrap-replies" style="width: auto;" >
  21 + </ul>
  22 +
  23 + <% if scrap.replies.count > 0 %>
  24 + <div id="profile-wall-activities-comments-more-<%= activity.id %>" class="profile-wall-activities-comments">
  25 + <div class='view-all-comments icon-chat'>
  26 + <%= link_to(n_('View comment', "View all %s comments", scrap.replies.count) % scrap.replies.count, :profile => profile.identifier, :controller => 'profile', :action => 'more_replies', :activity => activity, :comment_page => (1)) %>
  27 + </div>
  28 + </div>
  29 + <% end %>
  30 + <%= render :partial => 'profile_scrap_reply_form', :locals => { :scrap => scrap } %>
  31 + <hr />
  32 +</li>
app/views/shared/_manage_enterprises.rhtml
@@ -1,8 +0,0 @@ @@ -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 @@ @@ -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 @@ @@ -0,0 +1,11 @@
  1 +<%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %>
  2 +<div id='article'>
  3 + <div class="comments" id="comments_list">
  4 + <ul class="article-comments-list">
  5 + <%= render :partial => 'comment/comment', :collection => @comment_spam %>
  6 + </ul>
  7 + </div>
  8 +</div>
  9 +
  10 +<%= pagination_links @comment_spam, :param_name => :comments_page %>
  11 +
app/views/spam/_suggest_article.html.erb 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +<% render :layout => 'task', :locals => { :task => task } do %>
  2 + <% content_for :extra_buttons do %>
  3 + <%= button_to_function('down', _('Show details'), "toggleDetails(this, '#{_('Hide details')}', '#{_('Show details')}')" ) %>
  4 + <% end %>
  5 +
  6 + <% content_for :extra_content do %>
  7 + <ul class="suggest-article-details" style="display: none">
  8 + <li><strong><%=_('Sent by')%></strong>: <%=task.name%> </li>
  9 + <li><strong><%=_('Email')%></strong>: <%=task.email%> </li>
  10 + <li><strong><%=_('Source')%></strong>: <%=task.source_name%> </li>
  11 + <li><strong><%=_('Source URL')%></strong>: <%=task.source%> </li>
  12 + <li><strong><%=_('Folder')%></strong>: <%=(a = Article.find_by_id(task.article_parent_id))?a.name : '<em>' + s_('Folder|none') + '</em>'%> </li>
  13 + <li><strong><%=_('Lead')%></strong>: <%=task.article_abstract.blank? ? '<em>' + s_('Abstract|empty') + '</em>' : task.article_abstract%> </li>
  14 + <li><strong><%=_('Body')%></strong>:
  15 + <div class='suggest-article-body'>
  16 + <%= task.article_body %>
  17 + </div>
  18 + </li>
  19 + </ul>
  20 + <% end %>
  21 +<% end %>
app/views/spam/_task.rhtml 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +<div class="task_box" id="task-<%= task.id %>">
  2 + <%= render :partial => 'tasks/task_icon', :locals => {:task => task} %>
  3 + <%= render :partial => 'tasks/task_title', :locals => {:task => task} %>
  4 + <div class="task-information">
  5 + <%= task_information(task) %>
  6 + </div>
  7 +
  8 + <%= yield %> <% # ??? %>
  9 +
  10 + <% button_bar do %>
  11 + <%= button_to_function('new', _('Mark as NOT SPAM'), 'removeTaskBox(this, %s, "%s", "")' % [url_for(:mark_task_as_ham => task.id).to_json, "task-#{task.id}"]) %>
  12 + <%= yield :extra_buttons %>
  13 + <%= button_to_function('delete', _('Remove'), 'removeTaskBox(this, %s, "%s", %s)' % [url_for(:profile => params[:profile], :remove_task => task.id).to_json, "task-#{task.id}", _('Are you sure you want to remove this article suggestion?').to_json]) %>
  14 +
  15 + <% end %>
  16 +
  17 + <%= yield :extra_content %>
  18 +</div>
app/views/spam/_task_spam.rhtml 0 → 100644
@@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
  1 +<% @task_spam.each do |t| %>
  2 + <%= render :partial => partial_for_class(t.class), :locals => { :task => t } %>
  3 +<% end %>
  4 +<%= pagination_links @task_spam, :param_name => :tasks_page %>
app/views/spam/index.rhtml
  1 +<%= stylesheet('tasks') %>
  2 +
1 <h1><%= _('Manage SPAM') %></h1> 3 <h1><%= _('Manage SPAM') %></h1>
2 4
  5 +<% no_tabs = @comment_spam.blank? && @task_spam.blank? %>
  6 +
  7 +<%= _('There are no spams to review.') if no_tabs %>
  8 +
3 <% button_bar do %> 9 <% button_bar do %>
4 <%= button :back, _('Back to control panel'), :controller => :profile_editor %> 10 <%= button :back, _('Back to control panel'), :controller => :profile_editor %>
5 <% end %> 11 <% end %>
6 12
7 <%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %> 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 <% end %> 26 <% end %>
  27 +
  28 +<%= javascript_include_tag 'spam' %>
app/views/tasks/_task.rhtml
1 <div class="task_box" id="task-<%= task.id %>"> 1 <div class="task_box" id="task-<%= task.id %>">
2 2
3 - <div class="task_icon">  
4 - <%  
5 - icon_info = task.icon  
6 - if icon_info[:type] == :profile_image  
7 - icon = profile_image(icon_info[:profile], :minor)  
8 - elsif icon_info[:type] == :defined_image  
9 - icon = "<img src='#{icon_info[:src]}' alt='#{icon_info[:name]}' />"  
10 - end  
11 - %>  
12 - <%=  
13 - if icon_info[:url]  
14 - link_to(icon, icon_info[:url])  
15 - else  
16 - icon  
17 - end  
18 - %>  
19 -  
20 - </div> 3 + <%= render :partial => 'task_icon', :locals => {:task => task} %>
21 4
22 <div class="task_decisions"> 5 <div class="task_decisions">
23 <%= 6 <%=
@@ -39,9 +22,7 @@ @@ -39,9 +22,7 @@
39 %> 22 %>
40 </div><!-- class="task_decisions" --> 23 </div><!-- class="task_decisions" -->
41 24
42 - <strong class="task_title">  
43 - <%= task.title %>  
44 - </strong> 25 + <%= render :partial => 'task_title', :locals => {:task => task} %>
45 26
46 <div class="task_information"> 27 <div class="task_information">
47 <%= task_information(task) %> 28 <%= task_information(task) %>
app/views/tasks/_task_icon.rhtml 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +<%
  2 + icon_info = task.icon
  3 + if icon_info[:type] == :profile_image
  4 + icon = profile_image(icon_info[:profile], :minor)
  5 + elsif icon_info[:type] == :defined_image
  6 + icon = "<img src='#{icon_info[:src]}' alt='#{icon_info[:name]}' />"
  7 + end
  8 +
  9 + if icon_info[:url]
  10 + icon = link_to(icon, icon_info[:url])
  11 + end
  12 +%>
  13 +
  14 +<div class="task_icon">
  15 + <%= icon %>
  16 +</div>
app/views/tasks/_task_title.rhtml 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +<strong class="task_title">
  2 + <%= task.title %>
  3 +</strong>
config/initializers/plugins.rb
@@ -4,4 +4,5 @@ require &#39;noosfero/plugin/manager&#39; @@ -4,4 +4,5 @@ require &#39;noosfero/plugin/manager&#39;
4 require 'noosfero/plugin/active_record' 4 require 'noosfero/plugin/active_record'
5 require 'noosfero/plugin/mailer_base' 5 require 'noosfero/plugin/mailer_base'
6 require 'noosfero/plugin/settings' 6 require 'noosfero/plugin/settings'
  7 +require 'noosfero/plugin/spammable'
7 Noosfero::Plugin.init_system if $NOOSFERO_LOAD_PLUGINS 8 Noosfero::Plugin.init_system if $NOOSFERO_LOAD_PLUGINS
config/noosfero.yml.dist
@@ -8,6 +8,7 @@ development: @@ -8,6 +8,7 @@ development:
8 gravatar: wavatar 8 gravatar: wavatar
9 googlemaps_initial_zoom: 4 9 googlemaps_initial_zoom: 4
10 exception_recipients: [admin@example.com] 10 exception_recipients: [admin@example.com]
  11 + max_upload_size: 5MB
11 12
12 test: 13 test:
13 14
db/migrate/20131011164400_add_spam_to_task.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class AddSpamToTask < ActiveRecord::Migration
  2 + def self.up
  3 + change_table :tasks do |t|
  4 + t.boolean :spam, :default => false
  5 + end
  6 + Task.update_all ["spam = ?", false]
  7 + add_index :tasks, [:spam]
  8 + end
  9 +
  10 + def self.down
  11 + remove_column :tasks, :spam
  12 + end
  13 +end
db/migrate/20131011172930_add_image_to_article.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class AddImageToArticle < ActiveRecord::Migration
  2 +
  3 + def self.up
  4 + add_column :articles, :image_id, :integer
  5 + add_column :article_versions, :image_id, :integer
  6 + end
  7 +
  8 + def self.down
  9 + remove_column :articles, :image_id
  10 + remove_column :article_versions, :image_id
  11 + end
  12 +
  13 +end
db/migrate/20131015161830_add_position_to_article.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class AddPositionToArticle < ActiveRecord::Migration
  2 +
  3 + def self.up
  4 + add_column :articles, :position, :integer
  5 + add_column :article_versions, :position, :integer
  6 + end
  7 +
  8 + def self.down
  9 + remove_column :articles, :position
  10 + remove_column :article_versions, :position
  11 + end
  12 +
  13 +end
db/migrate/20131116165327_enable_enterprises_list_on_user_menu.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +class EnableEnterprisesListOnUserMenu < ActiveRecord::Migration
  2 + def self.up
  3 + # The enterprises were always listed on user menu.
  4 + # As now it is configured by admin, the running environments should not need to enable it
  5 + select_all("select id from environments").each do |environment|
  6 + env = Environment.find(environment['id'])
  7 + env.enable(:display_my_enterprises_on_user_menu)
  8 + end
  9 + end
  10 +
  11 + def self.down
  12 + #nothing to be done
  13 + end
  14 +end
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 # 9 #
10 # It's strongly recommended to check this file into your version control system. 10 # It's strongly recommended to check this file into your version control system.
11 11
12 -ActiveRecord::Schema.define(:version => 20130711213046) do 12 +ActiveRecord::Schema.define(:version => 20131116165327) do
13 13
14 create_table "abuse_reports", :force => true do |t| 14 create_table "abuse_reports", :force => true do |t|
15 t.integer "reporter_id" 15 t.integer "reporter_id"
@@ -86,6 +86,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do @@ -86,6 +86,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do
86 t.string "language" 86 t.string "language"
87 t.string "source_name" 87 t.string "source_name"
88 t.integer "license_id" 88 t.integer "license_id"
  89 + t.integer "image_id"
  90 + t.integer "position"
89 end 91 end
90 92
91 add_index "article_versions", ["article_id"], :name => "index_article_versions_on_article_id" 93 add_index "article_versions", ["article_id"], :name => "index_article_versions_on_article_id"
@@ -129,6 +131,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do @@ -129,6 +131,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do
129 t.string "language" 131 t.string "language"
130 t.string "source_name" 132 t.string "source_name"
131 t.integer "license_id" 133 t.integer "license_id"
  134 + t.integer "image_id"
  135 + t.integer "position"
132 end 136 end
133 137
134 add_index "articles", ["name"], :name => "index_articles_on_name" 138 add_index "articles", ["name"], :name => "index_articles_on_name"
@@ -547,8 +551,11 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do @@ -547,8 +551,11 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do
547 t.datetime "created_at" 551 t.datetime "created_at"
548 t.string "target_type" 552 t.string "target_type"
549 t.integer "image_id" 553 t.integer "image_id"
  554 + t.boolean "spam", :default => false
550 end 555 end
551 556
  557 + add_index "tasks", ["spam"], :name => "index_tasks_on_spam"
  558 +
552 create_table "thumbnails", :force => true do |t| 559 create_table "thumbnails", :force => true do |t|
553 t.integer "size" 560 t.integer "size"
554 t.string "content_type" 561 t.string "content_type"
features/browse_catalogs.feature
@@ -9,7 +9,7 @@ Feature: browse catalogs @@ -9,7 +9,7 @@ Feature: browse catalogs
9 And the following enterprises 9 And the following enterprises
10 | identifier | owner | name | enabled | 10 | identifier | owner | name | enabled |
11 | artebonito | joaosilva | Associação de Artesanato de Bonito | true | 11 | artebonito | joaosilva | Associação de Artesanato de Bonito | true |
12 - And feature "disable_products_for_enterprises" is disabled on environment 12 + And feature "products_for_enterprises" is enabled on environment
13 And the following product_categories 13 And the following product_categories
14 | name | 14 | name |
15 | categ1 | 15 | categ1 |
features/browse_enterprises.feature
@@ -6,7 +6,7 @@ Background: @@ -6,7 +6,7 @@ Background:
6 Given the following enterprises 6 Given the following enterprises
7 | identifier | name | 7 | identifier | name |
8 | shop1 | Shoes Shop | 8 | shop1 | Shoes Shop |
9 - And feature "disable_products_for_enterprises" is disabled on environment 9 + And feature "products_for_enterprises" is enabled on environment
10 And feature "show_balloon_with_profile_links_when_clicked" is enabled on environment 10 And feature "show_balloon_with_profile_links_when_clicked" is enabled on environment
11 11
12 Scenario: show all enterprises 12 Scenario: show all enterprises
features/enterprise_homepage.feature
@@ -21,6 +21,7 @@ Feature: enterprise homepage @@ -21,6 +21,7 @@ Feature: enterprise homepage
21 And the following product 21 And the following product
22 | name | category | owner | 22 | name | category | owner |
23 | Natural Handmade | soap | mayhem | 23 | Natural Handmade | soap | mayhem |
  24 + And feature "products_for_enterprises" is enabled on environment
24 25
25 26
26 Scenario: display profile info 27 Scenario: display profile info
features/manage_inputs.feature
@@ -19,7 +19,7 @@ Feature: manage inputs @@ -19,7 +19,7 @@ Feature: manage inputs
19 And the following product 19 And the following product
20 | owner | category | name | 20 | owner | category | name |
21 | redemoinho | rock | Abbey Road | 21 | redemoinho | rock | Abbey Road |
22 - And feature "disable_products_for_enterprises" is disabled on environment 22 + And feature "products_for_enterprises" is enabled on environment
23 And the following units 23 And the following units
24 | singular | plural | 24 | singular | plural |
25 | Meter | Meters | 25 | Meter | Meters |
features/manage_product_price_details.feature
@@ -19,7 +19,7 @@ Feature: manage product price details @@ -19,7 +19,7 @@ Feature: manage product price details
19 And the following product 19 And the following product
20 | owner | category | name | price | 20 | owner | category | name | price |
21 | redemoinho | rock | Abbey Road | 80.0 | 21 | redemoinho | rock | Abbey Road | 80.0 |
22 - And feature "disable_products_for_enterprises" is disabled on environment 22 + And feature "products_for_enterprises" is enabled on environment
23 And the following inputs 23 And the following inputs
24 | product | category | price_per_unit | amount_used | 24 | product | category | price_per_unit | amount_used |
25 | Abbey Road | Rock | 10.0 | 2 | 25 | Abbey Road | Rock | 10.0 | 2 |
features/manage_products.feature
@@ -9,7 +9,7 @@ Feature: manage products @@ -9,7 +9,7 @@ Feature: manage products
9 And the following enterprises 9 And the following enterprises
10 | identifier | owner | name | enabled | 10 | identifier | owner | name | enabled |
11 | redemoinho | joaosilva | Rede Moinho | true | 11 | redemoinho | joaosilva | Rede Moinho | true |
12 - And feature "disable_products_for_enterprises" is disabled on environment 12 + And feature "products_for_enterprises" is enabled on environment
13 13
14 Scenario: display "create new product" button 14 Scenario: display "create new product" button
15 Given I am logged in as "joaosilva" 15 Given I am logged in as "joaosilva"
lib/feed_updater.rb
@@ -20,14 +20,20 @@ end @@ -20,14 +20,20 @@ end
20 class FeedUpdater 20 class FeedUpdater
21 21
22 class ExceptionNotification < ActionMailer::Base 22 class ExceptionNotification < ActionMailer::Base
23 - def mail error 23 + def mail container, error
24 environment = Environment.default 24 environment = Environment.default
25 25
26 recipients NOOSFERO_CONF['exception_recipients'] 26 recipients NOOSFERO_CONF['exception_recipients']
27 from environment.contact_email 27 from environment.contact_email
28 reply_to environment.contact_email 28 reply_to environment.contact_email
29 subject "[#{environment.name}] Feed-updater: #{error.message}" 29 subject "[#{environment.name}] Feed-updater: #{error.message}"
30 - body render(:text => error.backtrace.join("\n")) 30 + body render(:text => "
  31 +Container:
  32 +#{container.inspect}
  33 +
  34 +Backtrace:
  35 +#{error.backtrace.join("\n")}
  36 + ")
31 end 37 end
32 end 38 end
33 39
@@ -88,11 +94,13 @@ class FeedUpdater @@ -88,11 +94,13 @@ class FeedUpdater
88 if !running 94 if !running
89 break 95 break
90 end 96 end
91 - feed_handler.process(container) 97 + begin
  98 + feed_handler.process(container)
  99 + rescue Exception => e
  100 + FeedUpdater::ExceptionNotification.deliver_mail container, e if NOOSFERO_CONF['exception_recipients'].present?
  101 + end
92 end 102 end
93 end 103 end
94 - rescue Exception => e  
95 - FeedUpdater::ExceptionNotification.deliver_mail e if NOOSFERO_CONF['exception_recipients'].present?  
96 end 104 end
97 end 105 end
98 106
lib/file_presenter.rb 0 → 100644
@@ -0,0 +1,107 @@ @@ -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,6 +155,7 @@ class Noosfero::Plugin
155 155
156 # Here the developer may specify the events to which the plugins can 156 # Here the developer may specify the events to which the plugins can
157 # register and must return true or false. The default value must be false. 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 # -> If true, noosfero will include plugin_dir/public/style.css into 160 # -> If true, noosfero will include plugin_dir/public/style.css into
160 # application 161 # application
@@ -162,10 +163,6 @@ class Noosfero::Plugin @@ -162,10 +163,6 @@ class Noosfero::Plugin
162 false 163 false
163 end 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 # -> Adds buttons to the control panel 166 # -> Adds buttons to the control panel
170 # returns = { :title => title, :icon => icon, :url => url } 167 # returns = { :title => title, :icon => icon, :url => url }
171 # title = name that will be displayed. 168 # title = name that will be displayed.
@@ -175,6 +172,13 @@ class Noosfero::Plugin @@ -175,6 +172,13 @@ class Noosfero::Plugin
175 nil 172 nil
176 end 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 # -> Adds tabs to the profile 182 # -> Adds tabs to the profile
179 # returns = { :title => title, :id => id, :content => content, :start => start } 183 # returns = { :title => title, :id => id, :content => content, :start => start }
180 # title = name that will be displayed. 184 # title = name that will be displayed.
@@ -304,45 +308,16 @@ class Noosfero::Plugin @@ -304,45 +308,16 @@ class Noosfero::Plugin
304 scope 308 scope
305 end 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 end 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 end 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 end 321 end
347 322
348 # Adds extra actions for comments 323 # Adds extra actions for comments
lib/noosfero/plugin/manager.rb
@@ -34,18 +34,20 @@ class Noosfero::Plugin::Manager @@ -34,18 +34,20 @@ class Noosfero::Plugin::Manager
34 alias :dispatch_scopes :dispatch_without_flatten 34 alias :dispatch_scopes :dispatch_without_flatten
35 35
36 def dispatch_first(event, *args) 36 def dispatch_first(event, *args)
37 - result = nil 37 + default = Noosfero::Plugin.new.send(event, *args)
  38 + result = default
38 each do |plugin| 39 each do |plugin|
39 result = plugin.send(event, *args) 40 result = plugin.send(event, *args)
40 - break if result.present? 41 + break if result != default
41 end 42 end
42 result 43 result
43 end 44 end
44 45
45 def fetch_first_plugin(event, *args) 46 def fetch_first_plugin(event, *args)
  47 + default = Noosfero::Plugin.new.send(event, *args)
46 result = nil 48 result = nil
47 each do |plugin| 49 each do |plugin|
48 - if plugin.send(event, *args) 50 + if plugin.send(event, *args) != default
49 result = plugin.class 51 result = plugin.class
50 break 52 break
51 end 53 end
lib/noosfero/plugin/spammable.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -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,51 @@ @@ -0,0 +1,51 @@
  1 +module Spammable
  2 + def self.included(recipient)
  3 + #TODO This line crashes the migration which includes the spam attribute to
  4 + # Task... =P
  5 + # No fail-safe until someone find out how to use this without crashing
  6 + # the migration process
  7 + #raise "This model (#{recipient.to_s}) should have a spam attribute!" if !recipient.new.respond_to?('spam=')
  8 + recipient.extend(ClassMethods)
  9 + end
  10 +
  11 + module ClassMethods
  12 + def self.extended (base)
  13 + if base.respond_to?(:named_scope)
  14 + base.class_eval do
  15 + named_scope :without_spam, :conditions => ['spam IS NULL OR spam = ?', false]
  16 + named_scope :spam, :conditions => ['spam = ?', true]
  17 + end
  18 + end
  19 + end
  20 + end
  21 +
  22 + def spam?
  23 + !spam.nil? && spam
  24 + end
  25 +
  26 + def ham?
  27 + !spam.nil? && !spam
  28 + end
  29 +
  30 + def spam!
  31 + before_spam!
  32 + self.spam = true
  33 + self.save!
  34 + after_spam!
  35 + self
  36 + end
  37 +
  38 + def ham!
  39 + before_ham!
  40 + self.spam = false
  41 + self.save!
  42 + after_ham!
  43 + self
  44 + end
  45 +
  46 + def after_spam!; end
  47 + def before_spam!; end
  48 +
  49 + def after_ham!; end
  50 + def before_ham!; end
  51 +end
plugins/anti_spam/lib/anti_spam_plugin.rb
@@ -5,38 +5,37 @@ class AntiSpamPlugin &lt; Noosfero::Plugin @@ -5,38 +5,37 @@ class AntiSpamPlugin &lt; Noosfero::Plugin
5 end 5 end
6 6
7 def self.plugin_description 7 def self.plugin_description
8 - _("Checks comments against a spam checking service compatible with the Akismet API") 8 + _("Tests comments and suggested articles against a spam checking service compatible with the Akismet API")
9 end 9 end
10 10
11 def self.host_default_setting 11 def self.host_default_setting
12 'api.antispam.typepad.com' 12 'api.antispam.typepad.com'
13 end 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 end 19 end
20 end 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 end 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 end 28 end
29 29
30 protected 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 Rakismet.host = settings.host 35 Rakismet.host = settings.host
36 Rakismet.key = settings.api_key 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 submission.send(op) 39 submission.send(op)
41 end 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 end 9 end
plugins/anti_spam/lib/anti_spam_plugin/suggest_article_wrapper.rb 0 → 100644
@@ -0,0 +1,10 @@ @@ -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 @@ @@ -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 require 'test_helper' 1 require 'test_helper'
2 2
3 -class AntiSpamPluginCommentWrapperTest < ActiveSupport::TestCase 3 +class AntiSpamPlugin::CommentWrapperTest < ActiveSupport::TestCase
4 4
5 def setup 5 def setup
6 @comment = Comment.new( 6 @comment = Comment.new(
@@ -15,10 +15,6 @@ class AntiSpamPluginCommentWrapperTest &lt; ActiveSupport::TestCase @@ -15,10 +15,6 @@ class AntiSpamPluginCommentWrapperTest &lt; ActiveSupport::TestCase
15 @wrapper = AntiSpamPlugin::CommentWrapper.new(@comment) 15 @wrapper = AntiSpamPlugin::CommentWrapper.new(@comment)
16 end 16 end
17 17
18 - should 'use Rakismet::Model' do  
19 - assert_includes @wrapper.class.included_modules, Rakismet::Model  
20 - end  
21 -  
22 should 'get contents' do 18 should 'get contents' do
23 assert_equal @comment.body, @wrapper.content 19 assert_equal @comment.body, @wrapper.content
24 end 20 end
plugins/anti_spam/test/unit/anti_spam_plugin/suggest_article_wrapper_test.rb 0 → 100644
@@ -0,0 +1,41 @@ @@ -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 @@ @@ -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,35 +2,36 @@ require &#39;test_helper&#39;
2 2
3 class AntiSpamPluginTest < ActiveSupport::TestCase 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 @plugin = AntiSpamPlugin.new 15 @plugin = AntiSpamPlugin.new
15 - @plugin.context = @comment  
16 end 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 end 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 end 31 end
35 32
  33 + should 'report comment ham' do
  34 + spammable.expects(:ham!)
  35 + @plugin.marked_as_ham(spammable)
  36 + end
36 end 37 end
plugins/comment_group/controllers/profile/comment_group_plugin_profile_controller.rb
1 class CommentGroupPluginProfileController < ProfileController 1 class CommentGroupPluginProfileController < ProfileController
2 - append_view_path File.join(File.dirname(__FILE__) + '/../views') 2 + append_view_path File.join(File.dirname(__FILE__) + '/../../views')
3 3
4 def view_comments 4 def view_comments
5 - article_id = params[:article_id]  
6 - group_id = params[:group_id] 5 + @article_id = params[:article_id]
  6 + @group_id = params[:group_id]
7 7
8 - article = profile.articles.find(article_id)  
9 - comments = article.group_comments.without_spam.in_group(group_id)  
10 - render :update do |page|  
11 - page.replace_html "comments_list_group_#{group_id}", :partial => 'comment/comment.rhtml', :collection => comments  
12 - page.replace_html "comment-count-#{group_id}", comments.count  
13 - end 8 + article = profile.articles.find(@article_id)
  9 + @group_comment_page = (params[:group_comment_page] || 1).to_i
  10 +
  11 + @comments = article.comments.without_spam.in_group(@group_id)
  12 + @comments_count = @comments.count
  13 + @comments = @comments.without_reply.paginate(:per_page => per_page, :page => @group_comment_page )
  14 +
  15 + @no_more_pages = @comments_count <= @group_comment_page * per_page
  16 + end
  17 +
  18 + def per_page
  19 + 3
14 end 20 end
15 21
16 end 22 end
plugins/comment_group/lib/comment_group_plugin.rb
@@ -24,6 +24,11 @@ class CommentGroupPlugin &lt; Noosfero::Plugin @@ -24,6 +24,11 @@ class CommentGroupPlugin &lt; Noosfero::Plugin
24 'comment_group_macro.js' 24 'comment_group_macro.js'
25 end 25 end
26 26
  27 + def stylesheet?
  28 + true
  29 + end
  30 +
  31 +
27 end 32 end
28 33
29 require_dependency 'comment_group_plugin/macros/allow_comment' 34 require_dependency 'comment_group_plugin/macros/allow_comment'
plugins/comment_group/lib/comment_group_plugin/macros/allow_comment.rb
@@ -11,7 +11,6 @@ class CommentGroupPlugin::AllowComment &lt; Noosfero::Plugin::Macro @@ -11,7 +11,6 @@ class CommentGroupPlugin::AllowComment &lt; Noosfero::Plugin::Macro
11 :css_files => 'comment_group.css' } 11 :css_files => 'comment_group.css' }
12 end 12 end
13 13
14 - #FIXME Make this test  
15 def parse(params, inner_html, source) 14 def parse(params, inner_html, source)
16 group_id = params[:group_id].to_i 15 group_id = params[:group_id].to_i
17 article = source 16 article = source
plugins/comment_group/lib/ext/article.rb
@@ -2,13 +2,10 @@ require_dependency &#39;article&#39; @@ -2,13 +2,10 @@ require_dependency &#39;article&#39;
2 2
3 class Article 3 class Article
4 4
5 - #FIXME make this test  
6 has_many :group_comments, :class_name => 'Comment', :foreign_key => 'source_id', :dependent => :destroy, :order => 'created_at asc', :conditions => [ 'group_id IS NOT NULL'] 5 has_many :group_comments, :class_name => 'Comment', :foreign_key => 'source_id', :dependent => :destroy, :order => 'created_at asc', :conditions => [ 'group_id IS NOT NULL']
7 6
8 - #FIXME make this test  
9 validate :not_empty_group_comments_removed 7 validate :not_empty_group_comments_removed
10 8
11 - #FIXME make this test  
12 def not_empty_group_comments_removed 9 def not_empty_group_comments_removed
13 if body 10 if body
14 groups_with_comments = group_comments.collect {|comment| comment.group_id}.uniq 11 groups_with_comments = group_comments.collect {|comment| comment.group_id}.uniq
plugins/comment_group/public/style.css 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +div.article-comments-list-more{
  2 + width: 100%;
  3 + height: 30px;
  4 + text-align: center;
  5 + font-size: 20px;
  6 + margin-bottom: 5px;
  7 +}
plugins/comment_group/test/functional/comment_group_plugin_profile_controller_test.rb
1 -require File.dirname(__FILE__) + '/../../../../test/test_helper' 1 +require File.dirname(__FILE__) + '/../test_helper'
2 require File.dirname(__FILE__) + '/../../controllers/profile/comment_group_plugin_profile_controller' 2 require File.dirname(__FILE__) + '/../../controllers/profile/comment_group_plugin_profile_controller'
3 3
4 # Re-raise errors caught by the controller. 4 # Re-raise errors caught by the controller.
@@ -21,18 +21,52 @@ class CommentGroupPluginProfileControllerTest &lt; ActionController::TestCase @@ -21,18 +21,52 @@ class CommentGroupPluginProfileControllerTest &lt; ActionController::TestCase
21 should 'be able to show group comments' do 21 should 'be able to show group comments' do
22 comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0) 22 comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0)
23 xhr :get, :view_comments, :profile => @profile.identifier, :article_id => article.id, :group_id => 0 23 xhr :get, :view_comments, :profile => @profile.identifier, :article_id => article.id, :group_id => 0
24 - assert_template 'comment/_comment.rhtml' 24 + assert_template 'comment_group_plugin_profile/view_comments.rjs'
25 assert_match /comments_list_group_0/, @response.body 25 assert_match /comments_list_group_0/, @response.body
26 assert_match /\"comment-count-0\", \"1\"/, @response.body 26 assert_match /\"comment-count-0\", \"1\"/, @response.body
27 end 27 end
28 28
29 should 'do not show global comments' do 29 should 'do not show global comments' do
30 - comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'global comment', :body => 'global', :group_id => nil)  
31 - comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0) 30 + fast_create(Comment, :source_id => article, :author_id => profile, :title => 'global comment', :body => 'global', :group_id => nil)
  31 + fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0)
32 xhr :get, :view_comments, :profile => @profile.identifier, :article_id => article.id, :group_id => 0 32 xhr :get, :view_comments, :profile => @profile.identifier, :article_id => article.id, :group_id => 0
33 - assert_template 'comment/_comment.rhtml' 33 + assert_template 'comment_group_plugin_profile/view_comments.rjs'
34 assert_match /comments_list_group_0/, @response.body 34 assert_match /comments_list_group_0/, @response.body
35 assert_match /\"comment-count-0\", \"1\"/, @response.body 35 assert_match /\"comment-count-0\", \"1\"/, @response.body
36 end 36 end
37 37
  38 + should 'show first page comments only' do
  39 + comment1 = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'secondpage', :group_id => 0)
  40 + comment2 = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'firstpage 1', :group_id => 0)
  41 + comment3 = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'firstpage 2', :group_id => 0)
  42 + comment4 = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'firstpage 3', :group_id => 0)
  43 + xhr :get, :view_comments, :profile => @profile.identifier, :article_id => article.id, :group_id => 0
  44 + assert_match /firstpage 1/, @response.body
  45 + assert_match /firstpage 2/, @response.body
  46 + assert_match /firstpage 3/, @response.body
  47 + assert_no_match /secondpage/, @response.body
  48 + end
  49 +
  50 + should 'show link to display more comments' do
  51 + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0)
  52 + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0)
  53 + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0)
  54 + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'secondpage', :body => 'secondpage', :group_id => 0)
  55 + xhr :get, :view_comments, :profile => @profile.identifier, :article_id => article.id, :group_id => 0
  56 + assert_match /group_comment_page=2/, @response.body
  57 + end
  58 +
  59 + should 'do not show link to display more comments if do not have more pages' do
  60 + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0)
  61 + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0)
  62 + comment = fast_create(Comment, :source_id => article, :author_id => profile, :title => 'a comment', :body => 'lalala', :group_id => 0)
  63 + xhr :get, :view_comments, :profile => @profile.identifier, :article_id => article.id, :group_id => 0
  64 + assert_no_match /group_comment_page/, @response.body
  65 + end
  66 +
  67 + should 'do not show link to display more comments if do not have any comments' do
  68 + xhr :get, :view_comments, :profile => @profile.identifier, :article_id => article.id, :group_id => 0
  69 + assert_no_match /group_comment_page/, @response.body
  70 + end
  71 +
38 end 72 end
plugins/comment_group/test/functional/comment_group_plugin_public_controller_test.rb
1 -require File.dirname(__FILE__) + '/../../../../test/test_helper' 1 +require File.dirname(__FILE__) + '/../test_helper'
2 require File.dirname(__FILE__) + '/../../controllers/public/comment_group_plugin_public_controller' 2 require File.dirname(__FILE__) + '/../../controllers/public/comment_group_plugin_public_controller'
3 3
4 # Re-raise errors caught by the controller. 4 # Re-raise errors caught by the controller.
plugins/comment_group/test/functional/content_viewer_controller_test.rb 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class ContentViewerController
  4 + append_view_path File.join(File.dirname(__FILE__) + '/../../views')
  5 + def rescue_action(e)
  6 + raise e
  7 + end
  8 +end
  9 +
  10 +class ContentViewerControllerTest < ActionController::TestCase
  11 +
  12 + def setup
  13 + @profile = fast_create(Community)
  14 + @page = fast_create(Article, :profile_id => @profile.id, :body => "<div class=\"macro\" data-macro-group_id=\"1\" data-macro='comment_group_plugin/allow_comment' ></div>")
  15 + @environment = Environment.default
  16 + @environment.enable_plugin(CommentGroupPlugin)
  17 + end
  18 +
  19 + attr_reader :page
  20 +
  21 + should 'parse article body and render comment group view' do
  22 + comment1 = fast_create(Comment, :group_id => 1, :source_id => page.id)
  23 + get :view_page, @page.url
  24 + assert_tag 'div', :attributes => {:class => 'comment_group_1'}
  25 + assert_tag 'div', :attributes => {:id => 'comments_group_count_1'}
  26 + end
  27 +
  28 +end
plugins/comment_group/test/test_helper.rb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +require File.dirname(__FILE__) + '/../../../test/test_helper'
plugins/comment_group/test/unit/allow_comment_test.rb 0 → 100644
@@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class AllowCommentTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + @macro = CommentGroupPlugin::AllowComment.new
  7 + end
  8 +
  9 + attr_reader :macro
  10 +
  11 + should 'have a configuration' do
  12 + assert CommentGroupPlugin::AllowComment.configuration
  13 + end
  14 +
  15 + should 'parse contents to include comment group view' do
  16 + profile = fast_create(Community)
  17 + article = fast_create(Article, :profile_id => profile.id)
  18 + comment = fast_create(Comment, :group_id => 1, :source_id => article.id)
  19 + inner_html = 'inner'
  20 + content = macro.parse({:group_id => comment.group_id}, inner_html, article)
  21 +
  22 + expects(:render).with({:partial => 'plugins/comment_group/views/comment_group.rhtml', :locals => {:group_id => comment.group_id, :article_id => article.id, :inner_html => inner_html, :count => 1, :profile_identifier => profile.identifier} })
  23 + instance_eval(&content)
  24 + end
  25 +
  26 +end
plugins/comment_group/test/unit/article_test.rb 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class ArticleTest < ActiveSupport::TestCase
  4 +
  5 + def setup
  6 + profile = fast_create(Community)
  7 + @article = fast_create(Article, :profile_id => profile.id)
  8 + end
  9 +
  10 + attr_reader :article
  11 +
  12 + should 'return group comments from article' do
  13 + comment1 = fast_create(Comment, :group_id => 1, :source_id => article.id)
  14 + comment2 = fast_create(Comment, :group_id => nil, :source_id => article.id)
  15 + assert_equal [comment1], article.group_comments
  16 + end
  17 +
  18 + should 'do not allow a exclusion of a group comment macro if this group has comments' do
  19 + article.update_attribute(:body, "<div class=\"macro\" data-macro-group_id=2></div>")
  20 + comment1 = fast_create(Comment, :group_id => 1, :source_id => article.id)
  21 + assert !article.save
  22 + assert_equal 'Not empty group comment cannot be removed', article.errors[:base]
  23 + end
  24 +
  25 + should 'allow save if comment group macro is not removed for group with comments' do
  26 + article.update_attribute(:body, "<div class=\"macro\" data-macro-group_id=1></div>")
  27 + comment1 = fast_create(Comment, :group_id => 1, :source_id => article.id)
  28 + assert article.save
  29 + end
  30 +
  31 +end
plugins/comment_group/test/unit/comment_group_plugin_test.rb
1 -require File.dirname(__FILE__) + '/../../../../test/test_helper' 1 +require File.dirname(__FILE__) + '/../test_helper'
2 2
3 class CommentGroupPluginTest < ActiveSupport::TestCase 3 class CommentGroupPluginTest < ActiveSupport::TestCase
4 4
5 - include Noosfero::Plugin::HotSpot  
6 -  
7 def setup 5 def setup
8 @environment = Environment.default 6 @environment = Environment.default
  7 + @plugin = CommentGroupPlugin.new
  8 + end
  9 +
  10 + attr_reader :environment, :plugin
  11 +
  12 + should 'have a name' do
  13 + assert_not_equal Noosfero::Plugin.plugin_name, CommentGroupPlugin::plugin_name
  14 + end
  15 +
  16 + should 'describe yourself' do
  17 + assert_not_equal Noosfero::Plugin.plugin_description, CommentGroupPlugin::plugin_description
  18 + end
  19 +
  20 + should 'have a js file' do
  21 + assert !plugin.js_files.blank?
9 end 22 end
10 23
11 - attr_reader :environment 24 + should 'have stylesheet' do
  25 + assert plugin.stylesheet?
  26 + end
  27 +
  28 + should 'have extra contents for comment form' do
  29 + comment = fast_create(Comment, :group_id => 1)
  30 + content = plugin.comment_form_extra_contents({:comment => comment})
  31 + expects(:hidden_field_tag).with('comment[group_id]', comment.group_id).once
  32 + instance_eval(&content)
  33 + end
  34 +
  35 + should 'do not have extra contents for comments without group' do
  36 + comment = fast_create(Comment, :group_id => nil)
  37 + content = plugin.comment_form_extra_contents({:comment => comment})
  38 + assert_equal nil, instance_eval(&content)
  39 + end
  40 +
  41 + should 'call without_group for scope passed as parameter to unavailable_comments' do
  42 + article = fast_create(Article)
  43 + article.expects(:without_group).once
  44 + plugin.unavailable_comments(article)
  45 + end
12 46
13 #FIXME Obsolete test 47 #FIXME Obsolete test
14 # 48 #