Commit 4b0c7f1f71a783ac43113244044bf77a240b3d4e

Authored by Victor Costa
2 parents 64e78c09 657be12f

Merge branch 'next' into AI2888-context_block

Conflicts:
	test/unit/article_test.rb
Showing 316 changed files with 55715 additions and 48751 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 316 files displayed.

@@ -111,6 +111,7 @@ Diego Martinez <diegoamc90@gmail.com> @@ -111,6 +111,7 @@ Diego Martinez <diegoamc90@gmail.com>
111 Diego Martinez <diego@diego-K55A.(none)> 111 Diego Martinez <diego@diego-K55A.(none)>
112 Diego + Renan <renanteruoc@gmail.com> 112 Diego + Renan <renanteruoc@gmail.com>
113 Fernanda Lopes <nanda.listas+psl@gmail.com> 113 Fernanda Lopes <nanda.listas+psl@gmail.com>
  114 +Francisco Marcelo A. Lima Júnior <francisco.lima-junior@serpro.gov.br>
114 Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)> 115 Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)>
115 Grazieno Pellegrino <grazieno@gmail.com> 116 Grazieno Pellegrino <grazieno@gmail.com>
116 Isaac Canan <isaac@intelletto.com.br> 117 Isaac Canan <isaac@intelletto.com.br>
@@ -189,12 +190,14 @@ Renan Teruo + Diego Araujo &lt;renanteruoc@gmail.com&gt; @@ -189,12 +190,14 @@ Renan Teruo + Diego Araujo &lt;renanteruoc@gmail.com&gt;
189 Renan Teruo + Diego Araújo <renanteruoc@gmail.com> 190 Renan Teruo + Diego Araújo <renanteruoc@gmail.com>
190 Renan Teruo + Paulo Meirelles <renanteruoc@gmail.com> 191 Renan Teruo + Paulo Meirelles <renanteruoc@gmail.com>
191 Renan Teruo + Rafael Manzo <renanteruoc@gmail.com> 192 Renan Teruo + Rafael Manzo <renanteruoc@gmail.com>
  193 +Rodrigo Souto <diguliu@gmail.com>
192 Rodrigo Souto <rodrigo@colivre.coop.br> 194 Rodrigo Souto <rodrigo@colivre.coop.br>
193 Ronny Kursawe <kursawe.ronny@googlemail.com> 195 Ronny Kursawe <kursawe.ronny@googlemail.com>
194 root <root@debian.sdr.serpro> 196 root <root@debian.sdr.serpro>
195 Samuel R. C. Vale <srcvale@holoscopio.com> 197 Samuel R. C. Vale <srcvale@holoscopio.com>
196 Valessio Brito <valessio@gmail.com> 198 Valessio Brito <valessio@gmail.com>
197 vfcosta <vfcosta@gmail.com> 199 vfcosta <vfcosta@gmail.com>
  200 +Vinicius Cubas Brand <viniciuscb@gmail.com>
198 Visita <visita@debian.(none)> 201 Visita <visita@debian.(none)>
199 Yann Lugrin <yann.lugrin@liquid-concept.ch> 202 Yann Lugrin <yann.lugrin@liquid-concept.ch>
200 203
@@ -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/application_controller.rb
@@ -174,8 +174,6 @@ class ApplicationController &lt; ActionController::Base @@ -174,8 +174,6 @@ class ApplicationController &lt; ActionController::Base
174 end 174 end
175 175
176 def find_by_contents(asset, scope, query, paginate_options={:page => 1}, options={}) 176 def find_by_contents(asset, scope, query, paginate_options={:page => 1}, options={})
177 - scope = scope.send(options[:filter]) if options[:filter]  
178 -  
179 @plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) || 177 @plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) ||
180 fallback_find_by_contents(asset, scope, query, paginate_options, options) 178 fallback_find_by_contents(asset, scope, query, paginate_options, options)
181 end 179 end
@@ -183,8 +181,9 @@ class ApplicationController &lt; ActionController::Base @@ -183,8 +181,9 @@ class ApplicationController &lt; ActionController::Base
183 private 181 private
184 182
185 def fallback_find_by_contents(asset, scope, query, paginate_options, options) 183 def fallback_find_by_contents(asset, scope, query, paginate_options, options)
186 - return {:results => scope.paginate(paginate_options)} if query.blank?  
187 - {:results => scope.like_search(query).paginate(paginate_options)} 184 + scope = scope.like_search(query) unless query.blank?
  185 + scope = scope.send(options[:filter]) unless options[:filter].blank?
  186 + {:results => scope.paginate(paginate_options)}
188 end 187 end
189 188
190 end 189 end
app/controllers/my_profile/cms_controller.rb
@@ -73,6 +73,7 @@ class CmsController &lt; MyProfileController @@ -73,6 +73,7 @@ class CmsController &lt; MyProfileController
73 refuse_blocks 73 refuse_blocks
74 record_coming 74 record_coming
75 if request.post? 75 if request.post?
  76 + @article.image = nil if params[:remove_image] == 'true'
76 @article.last_changed_by = user 77 @article.last_changed_by = user
77 if @article.update_attributes(params[:article]) 78 if @article.update_attributes(params[:article])
78 if !continue 79 if !continue
@@ -144,10 +145,15 @@ class CmsController &lt; MyProfileController @@ -144,10 +145,15 @@ class CmsController &lt; MyProfileController
144 145
145 post_only :set_home_page 146 post_only :set_home_page
146 def set_home_page 147 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 148 + article = params[:id].nil? ? nil : profile.articles.find(params[:id])
  149 + profile.update_attribute(:home_page, article)
  150 +
  151 + if article.nil?
  152 + session[:notice] = _('Homepage reseted.')
  153 + else
  154 + session[:notice] = _('"%s" configured as homepage.') % article.name
  155 + end
  156 +
151 redirect_to (request.referer || profile.url) 157 redirect_to (request.referer || profile.url)
152 end 158 end
153 159
@@ -267,7 +273,10 @@ class CmsController &lt; MyProfileController @@ -267,7 +273,10 @@ class CmsController &lt; MyProfileController
267 @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url) 273 @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url)
268 @task = SuggestArticle.new(params[:task]) 274 @task = SuggestArticle.new(params[:task])
269 if request.post? 275 if request.post?
270 - @task.target = profile 276 + @task.target = profile
  277 + @task.ip_address = request.remote_ip
  278 + @task.user_agent = request.user_agent
  279 + @task.referrer = request.referrer
271 if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save 280 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.') 281 session[:notice] = _('Thanks for your suggestion. The community administrators were notified.')
273 redirect_to @back_to 282 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,8 +53,11 @@ class ContentViewerController &lt; ApplicationController @@ -53,8 +53,11 @@ 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 + if @page.download? params[:view]
57 headers['Content-Type'] = @page.mime_type 59 headers['Content-Type'] = @page.mime_type
  60 + headers.merge! @page.download_headers
58 data = @page.data 61 data = @page.data
59 62
60 # TODO test the condition 63 # TODO test the condition
@@ -70,7 +73,7 @@ class ContentViewerController &lt; ApplicationController @@ -70,7 +73,7 @@ class ContentViewerController &lt; ApplicationController
70 73
71 #FIXME see a better way to do this. It's not need to pass this variable anymore 74 #FIXME see a better way to do this. It's not need to pass this variable anymore
72 @comment = Comment.new 75 @comment = Comment.new
73 - 76 +
74 if @page.has_posts? 77 if @page.has_posts?
75 posts = if params[:year] and params[:month] 78 posts = if params[:year] and params[:month]
76 filter_date = DateTime.parse("#{params[:year]}-#{params[:month]}-01") 79 filter_date = DateTime.parse("#{params[:year]}-#{params[:month]}-01")
app/controllers/public/events_controller.rb
1 class EventsController < PublicController 1 class EventsController < PublicController
2 2
3 needs_profile 3 needs_profile
4 - no_design_blocks  
5 4
6 def events 5 def events
7 - @selected_day = nil  
8 - @events_of_the_day = []  
9 - date = build_date(params[:year], params[:month], params[:day]) 6 + @events = []
  7 + @date = build_date(params[:year], params[:month], params[:day])
10 8
11 - if params[:day] || !params[:year] && !params[:month]  
12 - @selected_day = date  
13 - @events_of_the_day = profile.events.by_day(@selected_day) 9 + if !params[:year] && !params[:month] && !params[:day]
  10 + @events = profile.events.next_events_from_month(@date)
14 end 11 end
15 12
16 - events = profile.events.by_range((date - 1.month).at_beginning_of_month..(date + 1.month).at_end_of_month) 13 + if params[:year] || params[:month]
  14 + @events = profile.events.by_month(@date)
  15 + end
  16 +
  17 + events_in_range = profile.events.by_range((@date - 1.month).at_beginning_of_month .. (@date + 1.month).at_end_of_month)
17 18
18 - @calendar = populate_calendar(date, events)  
19 - @previous_calendar = populate_calendar(date - 1.month, events)  
20 - @next_calendar = populate_calendar(date + 1.month, events) 19 + @calendar = populate_calendar(@date, events_in_range)
21 end 20 end
22 21
23 def events_by_day 22 def events_by_day
24 - @selected_day = build_date(params[:year], params[:month], params[:day])  
25 - @events_of_the_day = profile.events.by_day(@selected_day)  
26 - render :partial => 'events_by_day' 23 + @date = build_date(params[:year], params[:month], params[:day])
  24 + @events = profile.events.by_day(@date)
  25 + render :partial => 'events'
27 end 26 end
28 27
29 protected 28 protected
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/controllers/public/search_controller.rb
@@ -93,25 +93,27 @@ class SearchController &lt; PublicController @@ -93,25 +93,27 @@ class SearchController &lt; PublicController
93 year = (params[:year] ? params[:year].to_i : Date.today.year) 93 year = (params[:year] ? params[:year].to_i : Date.today.year)
94 month = (params[:month] ? params[:month].to_i : Date.today.month) 94 month = (params[:month] ? params[:month].to_i : Date.today.month)
95 day = (params[:day] ? params[:day].to_i : Date.today.day) 95 day = (params[:day] ? params[:day].to_i : Date.today.day)
96 - date = build_date(params[:year], params[:month], params[:day])  
97 - date_range = (date - 1.month).at_beginning_of_month..(date + 1.month).at_end_of_month 96 + @date = build_date(year, month, day)
  97 + date_range = (@date - 1.month).at_beginning_of_month..(@date + 1.month).at_end_of_month
98 98
99 - @selected_day = nil  
100 - @events_of_the_day = [] 99 + @events = []
101 if params[:day] || !params[:year] && !params[:month] 100 if params[:day] || !params[:year] && !params[:month]
102 - @selected_day = date  
103 - @events_of_the_day = @category ?  
104 - environment.events.by_day(@selected_day).in_category(Category.find(@category_id)) :  
105 - environment.events.by_day(@selected_day) 101 + @events = @category ?
  102 + environment.events.by_day(@date).in_category(Category.find(@category_id)) :
  103 + environment.events.by_day(@date)
  104 + end
  105 +
  106 + if params[:year] || params[:month]
  107 + @events = @category ?
  108 + environment.events.by_month(@date).in_category(Category.find(@category_id)) :
  109 + environment.events.by_month(@date)
106 end 110 end
107 111
108 @scope = date_range && params[:action] == 'events' ? environment.events.by_range(date_range) : environment.events 112 @scope = date_range && params[:action] == 'events' ? environment.events.by_range(date_range) : environment.events
109 full_text_search 113 full_text_search
110 114
111 events = @searches[@asset][:results] 115 events = @searches[@asset][:results]
112 - @calendar = populate_calendar(date, events)  
113 - @previous_calendar = populate_calendar(date - 1.month, events)  
114 - @next_calendar = populate_calendar(date + 1.month, events) 116 + @calendar = populate_calendar(@date, events)
115 end 117 end
116 118
117 # keep old URLs workings 119 # keep old URLs workings
@@ -136,9 +138,9 @@ class SearchController &lt; PublicController @@ -136,9 +138,9 @@ class SearchController &lt; PublicController
136 end 138 end
137 139
138 def events_by_day 140 def events_by_day
139 - @selected_day = build_date(params[:year], params[:month], params[:day])  
140 - @events_of_the_day = environment.events.by_day(@selected_day)  
141 - render :partial => 'events/events_by_day' 141 + @date = build_date(params[:year], params[:month], params[:day])
  142 + @events = environment.events.by_day(@date)
  143 + render :partial => 'events/events'
142 end 144 end
143 145
144 ####################################################### 146 #######################################################
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 = {})
@@ -727,8 +730,15 @@ module ApplicationHelper @@ -727,8 +730,15 @@ module ApplicationHelper
727 end 730 end
728 731
729 def rolename_for(profile, resource) 732 def rolename_for(profile, resource)
730 - role = profile.role_assignments.find_by_resource_id(resource.id).role  
731 - content_tag('span', role.name, :style => "color: #{role_color(role, resource.environment.id)}") 733 + roles = profile.role_assignments.
  734 + where(:resource_id => resource.id).
  735 + sort_by{ |role_assignment| role_assignment.role_id }.
  736 + map(&:role)
  737 + names = []
  738 + roles.each do |role|
  739 + names << content_tag('span', role.name, :style => "color: #{role_color(role, resource.environment.id)}")
  740 + end
  741 + names.join(', ')
732 end 742 end
733 743
734 def role_color(role, env_id) 744 def role_color(role, env_id)
@@ -1113,15 +1123,34 @@ module ApplicationHelper @@ -1113,15 +1123,34 @@ module ApplicationHelper
1113 result 1123 result
1114 end 1124 end
1115 1125
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]) 1126 + def manage_link(list, kind)
  1127 + if list.present?
  1128 + link_to_all = nil
  1129 + if list.count > 5
  1130 + list = list.first(5)
  1131 + link_to_all = link_to(content_tag('strong', _('See all')), :controller => 'memberships', :profile => current_user.login)
  1132 + end
  1133 + link = list.map do |element|
  1134 + 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])
  1135 + end
  1136 + if link_to_all
  1137 + link << link_to_all
1120 end 1138 end
1121 - render :partial => 'shared/manage_enterprises', :locals => {:enterprises_link => enterprises_link} 1139 + render :partial => "shared/manage_link", :locals => {:link => link, :kind => kind.to_s}
1122 end 1140 end
1123 end 1141 end
1124 1142
  1143 + def manage_enterprises
  1144 + return unless user && user.environment.enabled?(:display_my_enterprises_on_user_menu)
  1145 + manage_link(user.enterprises, :enterprises)
  1146 + end
  1147 +
  1148 + def manage_communities
  1149 + return unless user && user.environment.enabled?(:display_my_communities_on_user_menu)
  1150 + administered_communities = user.communities.more_popular.select {|c| c.admins.include? user}
  1151 + manage_link(administered_communities, :communities)
  1152 + end
  1153 +
1125 def usermenu_logged_in 1154 def usermenu_logged_in
1126 pending_tasks_count = '' 1155 pending_tasks_count = ''
1127 count = user ? Task.to(user).pending.count : -1 1156 count = user ? Task.to(user).pending.count : -1
@@ -1133,6 +1162,7 @@ module ApplicationHelper @@ -1133,6 +1162,7 @@ module ApplicationHelper
1133 render_environment_features(:usermenu) + 1162 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') + 1163 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 + 1164 manage_enterprises.to_s +
  1165 + 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")) + 1166 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 + 1167 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")) 1168 link_to('<i class="icon-menu-logout"></i><strong>' + _('Logout') + '</strong>', { :controller => 'account', :action => 'logout'} , :id => "logout", :title => _("Leave the system"))
@@ -1401,8 +1431,8 @@ module ApplicationHelper @@ -1401,8 +1431,8 @@ module ApplicationHelper
1401 end 1431 end
1402 1432
1403 def filter_html(html, source) 1433 def filter_html(html, source)
1404 - if @plugins && source.has_macro?  
1405 - html = convert_macro(html, source) 1434 + if @plugins && source && source.has_macro?
  1435 + html = convert_macro(html, source) unless @plugins.enabled_macros.blank?
1406 #TODO This parse should be done through the macro infra, but since there 1436 #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. 1437 # are old things that do not support it we are keeping this hot spot.
1408 html = @plugins.pipeline(:parse_content, html, source).first 1438 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/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/dates_helper.rb
@@ -35,6 +35,18 @@ module DatesHelper @@ -35,6 +35,18 @@ module DatesHelper
35 end 35 end
36 end 36 end
37 37
  38 + def show_date_month(date, use_numbers = false, year=true)
  39 + if date && use_numbers
  40 + date_format = year ? _('%{month}/%{year}') : _('%{month}/%{day}')
  41 + date_format % { :month => date.month, :year => date.year }
  42 + elsif date
  43 + date_format = year ? _('%{month_name}, %{year}') : _('%{month_name}')
  44 + date_format % { :month_name => month_name(date.month), :year => date.year }
  45 + else
  46 + ''
  47 + end
  48 + end
  49 +
38 # formats a datetime for displaying. 50 # formats a datetime for displaying.
39 def show_time(time) 51 def show_time(time)
40 if time 52 if time
@@ -98,7 +110,11 @@ module DatesHelper @@ -98,7 +110,11 @@ module DatesHelper
98 elsif opts[:previous] 110 elsif opts[:previous]
99 date = date << 1 111 date = date << 1
100 end 112 end
101 - _('%{month} %{year}') % { :year => date.year, :month => month_name(date.month.to_i) } 113 + if opts[:only_month]
  114 + _('%{month}') % {:month => month_name(date.month.to_i) }
  115 + else
  116 + _('%{month} %{year}') % { :year => date.year, :month => month_name(date.month.to_i) }
  117 + end
102 end 118 end
103 119
104 def build_date(year, month, day = 1) 120 def build_date(year, month, day = 1)
@@ -123,7 +139,7 @@ module DatesHelper @@ -123,7 +139,7 @@ module DatesHelper
123 previous_month_date = date - 1.month 139 previous_month_date = date - 1.month
124 140
125 label ||= show_month(previous_month_date.year, previous_month_date.month) 141 label ||= show_month(previous_month_date.year, previous_month_date.month)
126 - link_to label, :year => previous_month_date.year, :month => previous_month_date.month 142 + button(:back, label, {:year => previous_month_date.year, :month => previous_month_date.month})
127 end 143 end
128 144
129 def link_to_next_month(year, month, label = nil) 145 def link_to_next_month(year, month, label = nil)
@@ -131,7 +147,7 @@ module DatesHelper @@ -131,7 +147,7 @@ module DatesHelper
131 next_month_date = date + 1.month 147 next_month_date = date + 1.month
132 148
133 label ||= show_month(next_month_date.year, next_month_date.month) 149 label ||= show_month(next_month_date.year, next_month_date.month)
134 - link_to label, :year => next_month_date.year, :month => next_month_date.month 150 + button(:next, label, {:year => next_month_date.year, :month => next_month_date.month})
135 end 151 end
136 152
137 def pick_date(object, method, options = {}, html_options = {}) 153 def pick_date(object, method, options = {}, html_options = {})
app/helpers/events_helper.rb
1 module EventsHelper 1 module EventsHelper
2 2
3 def list_events(date, events) 3 def list_events(date, events)
4 - return content_tag('em', _("Select a day on the left to display it's events here"), :class => 'select-a-day') unless date  
5 - title = _('Events for %s') % show_date(date) 4 + title = _('Events for %s') % show_date_month(date)
6 content_tag('h2', title) + 5 content_tag('h2', title) +
7 content_tag('div', 6 content_tag('div',
8 (events.any? ? 7 (events.any? ?
9 content_tag('table', events.select { |item| item.display_to?(user) }.map {|item| display_event_in_listing(item)}.join('')) : 8 content_tag('table', events.select { |item| item.display_to?(user) }.map {|item| display_event_in_listing(item)}.join('')) :
10 - content_tag('em', _('No events for this date'), :class => 'no-events') 9 + content_tag('em', _('No events for this month'), :class => 'no-events')
11 ), :id => 'agenda-items' 10 ), :id => 'agenda-items'
12 ) 11 )
13 end 12 end
14 13
15 def display_event_in_listing(article) 14 def display_event_in_listing(article)
16 - content_tag(  
17 - 'tr',  
18 - content_tag('td', link_to(article.name, article.url, :class => icon_for_article(article))),  
19 - :class => 'agenda-item' 15 +
  16 + content_tag( 'tr',
  17 + content_tag('td',
  18 + content_tag('div', show_date(article.start_date) + ( article.end_date.nil? ? '' : (_(" to ") + show_date(article.end_date))),:class => 'event-date' ) +
  19 + content_tag('div',link_to(article.name,article.url),:class => 'event-title') +
  20 + content_tag('div',(article.address.nil? or article.address == '') ? '' : (_('Place: ') + article.address),:class => 'event-place')
  21 + )
20 ) 22 )
21 end 23 end
22 24
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
@@ -188,7 +194,7 @@ class Article &lt; ActiveRecord::Base @@ -188,7 +194,7 @@ class Article &lt; ActiveRecord::Base
188 pending_categorizations.clear 194 pending_categorizations.clear
189 end 195 end
190 196
191 - acts_as_taggable 197 + acts_as_taggable
192 N_('Tag list') 198 N_('Tag list')
193 199
194 acts_as_filesystem 200 acts_as_filesystem
@@ -268,7 +274,7 @@ class Article &lt; ActiveRecord::Base @@ -268,7 +274,7 @@ class Article &lt; ActiveRecord::Base
268 end 274 end
269 275
270 # returns the data of the article. Must be overriden in each subclass to 276 # returns the data of the article. Must be overriden in each subclass to
271 - # provide the correct content for the article. 277 + # provide the correct content for the article.
272 def data 278 def data
273 body 279 body
274 end 280 end
@@ -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)
@@ -360,6 +370,16 @@ class Article &lt; ActiveRecord::Base @@ -360,6 +370,16 @@ class Article &lt; ActiveRecord::Base
360 false 370 false
361 end 371 end
362 372
  373 + def download? view = nil
  374 + (self.uploaded_file? and not self.image?) or
  375 + (self.image? and view.blank?) or
  376 + (not self.uploaded_file? and self.mime_type != 'text/html')
  377 + end
  378 +
  379 + def download_headers
  380 + {}
  381 + end
  382 +
363 named_scope :native_translations, :conditions => { :translation_of_id => nil } 383 named_scope :native_translations, :conditions => { :translation_of_id => nil }
364 384
365 def translatable? 385 def translatable?
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
@@ -142,4 +142,15 @@ class Block &lt; ActiveRecord::Base @@ -142,4 +142,15 @@ class Block &lt; ActiveRecord::Base
142 false 142 false
143 end 143 end
144 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 +
145 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
@@ -7,13 +7,13 @@ class Category &lt; ActiveRecord::Base @@ -7,13 +7,13 @@ class Category &lt; ActiveRecord::Base
7 :slug => 1, 7 :slug => 1,
8 } 8 }
9 9
10 - validates_exclusion_of :slug, :in => [ 'index' ], :message => N_('%{fn} cannot be like that.').fix_i18n 10 + validates_exclusion_of :slug, :in => [ 'index' ], :message => N_('{fn} cannot be like that.').fix_i18n
11 validates_presence_of :name, :environment_id 11 validates_presence_of :name, :environment_id
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 ]  
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 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
17 17
18 # Finds all top level categories for a given environment. 18 # Finds all top level categories for a given environment.
19 named_scope :top_level_for, lambda { |environment| 19 named_scope :top_level_for, lambda { |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? })
@@ -29,7 +27,7 @@ class Comment &lt; ActiveRecord::Base @@ -29,7 +27,7 @@ class Comment &lt; ActiveRecord::Base
29 validates_presence_of :author_id, :if => (lambda { |rec| rec.name.blank? && rec.email.blank? }) 27 validates_presence_of :author_id, :if => (lambda { |rec| rec.name.blank? && rec.email.blank? })
30 validates_each :name do |rec,attribute,value| 28 validates_each :name do |rec,attribute,value|
31 if rec.author_id && (!rec.name.blank? || !rec.email.blank?) 29 if rec.author_id && (!rec.name.blank? || !rec.email.blank?)
32 - rec.errors.add(:name, _('%{fn} can only be informed for unauthenticated authors').fix_i18n) 30 + rec.errors.add(:name, _('{fn} can only be informed for unauthenticated authors').fix_i18n)
33 end 31 end
34 end 32 end
35 33
@@ -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/create_enterprise.rb
@@ -40,12 +40,12 @@ class CreateEnterprise &lt; Task @@ -40,12 +40,12 @@ class CreateEnterprise &lt; Task
40 40
41 if self.region && self.target 41 if self.region && self.target
42 unless self.region.validators.include?(self.target) || self.target_type == "Environment" 42 unless self.region.validators.include?(self.target) || self.target_type == "Environment"
43 - self.errors.add(:target, _('%{fn} is not a validator for the chosen region').fix_i18n) 43 + self.errors.add(:target, _('{fn} is not a validator for the chosen region').fix_i18n)
44 end 44 end
45 end 45 end
46 46
47 if self.status != Task::Status::CANCELLED && self.identifier && Profile.exists?(:identifier => self.identifier) 47 if self.status != Task::Status::CANCELLED && self.identifier && Profile.exists?(:identifier => self.identifier)
48 - self.errors.add(:identifier, _('%{fn} is already being as identifier by another enterprise, organization or person.').fix_i18n) 48 + self.errors.add(:identifier, _('{fn} is already being as identifier by another enterprise, organization or person.').fix_i18n)
49 end 49 end
50 end 50 end
51 51
app/models/domain.rb
@@ -10,14 +10,14 @@ class Domain &lt; ActiveRecord::Base @@ -10,14 +10,14 @@ class Domain &lt; ActiveRecord::Base
10 10
11 # <tt>name</tt> must be sequences of alphanumeric characters (a to z, 11 # <tt>name</tt> must be sequences of alphanumeric characters (a to z,
12 # 0 to 9), plus '_' or '-', separated by dots. Letters must be lowercase. 12 # 0 to 9), plus '_' or '-', separated by dots. Letters must be lowercase.
13 - validates_format_of :name, :with => /^([a-z0-9_-]+\.)+[a-z0-9_-]+$/, :message => N_('%{fn} must be composed of sequences of lowercase letters (a to z), numbers (0 to 9), "_" and "-", separated by dots.').fix_i18n 13 + validates_format_of :name, :with => /^([a-z0-9_-]+\.)+[a-z0-9_-]+$/, :message => N_('{fn} must be composed of sequences of lowercase letters (a to z), numbers (0 to 9), "_" and "-", separated by dots.').fix_i18n
14 14
15 # checks validations that could not be expressed using Rails' predefined 15 # checks validations that could not be expressed using Rails' predefined
16 # validations. In particular: 16 # validations. In particular:
17 # * <tt>name</tt> must not start with 'www.' 17 # * <tt>name</tt> must not start with 'www.'
18 def validate 18 def validate
19 if self.name =~ /^www\./ 19 if self.name =~ /^www\./
20 - self.errors.add(:name, _('%{fn} must not start with www.').fix_i18n) 20 + self.errors.add(:name, _('{fn} must not start with www.').fix_i18n)
21 end 21 end
22 end 22 end
23 23
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
@@ -168,7 +170,7 @@ class Environment &lt; ActiveRecord::Base @@ -168,7 +170,7 @@ class Environment &lt; ActiveRecord::Base
168 170
169 # One Environment can be reached by many domains 171 # One Environment can be reached by many domains
170 has_many :domains, :as => :owner 172 has_many :domains, :as => :owner
171 - has_many :profiles 173 + has_many :profiles, :dependent => :destroy
172 174
173 has_many :organizations 175 has_many :organizations
174 has_many :enterprises 176 has_many :enterprises
@@ -783,13 +785,6 @@ class Environment &lt; ActiveRecord::Base @@ -783,13 +785,6 @@ class Environment &lt; ActiveRecord::Base
783 self.save! 785 self.save!
784 end 786 end
785 787
786 - after_destroy :destroy_templates  
787 - def destroy_templates  
788 - [enterprise_template, inactive_enterprise_template, community_template, person_template].compact.each do |template|  
789 - template.destroy  
790 - end  
791 - end  
792 -  
793 after_create :create_default_licenses 788 after_create :create_default_licenses
794 def create_default_licenses 789 def create_default_licenses
795 License.create!(:name => 'CC (by)', :url => 'http://creativecommons.org/licenses/by/3.0/legalcode', :environment => self) 790 License.create!(:name => 'CC (by)', :url => 'http://creativecommons.org/licenses/by/3.0/legalcode', :environment => self)
app/models/event.rb
@@ -25,12 +25,30 @@ class Event &lt; Article @@ -25,12 +25,30 @@ class Event &lt; Article
25 25
26 validates_each :start_date do |event,field,value| 26 validates_each :start_date do |event,field,value|
27 if event.end_date && event.start_date && event.start_date > event.end_date 27 if event.end_date && event.start_date && event.start_date > event.end_date
28 - event.errors.add(:start_date, _('%{fn} cannot come before end date.').fix_i18n) 28 + event.errors.add(:start_date, _('{fn} cannot come before end date.').fix_i18n)
29 end 29 end
30 end 30 end
31 31
32 named_scope :by_day, lambda { |date| 32 named_scope :by_day, lambda { |date|
33 - {:conditions => ['start_date = :date AND end_date IS NULL OR (start_date <= :date AND end_date >= :date)', {:date => date}]} 33 + { :conditions => ['start_date = :date AND end_date IS NULL OR (start_date <= :date AND end_date >= :date)', {:date => date}],
  34 + :order => 'start_date ASC'
  35 + }
  36 + }
  37 +
  38 + named_scope :next_events_from_month, lambda { |date|
  39 + date_temp = date.strftime("%Y-%m-%d")
  40 + { :conditions => ["start_date >= ?","#{date_temp}"],
  41 + :limit => 10,
  42 + :order => 'start_date ASC'
  43 + }
  44 + }
  45 +
  46 + named_scope :by_month, lambda { |date|
  47 + date_temp = date.strftime("%Y-%m")
  48 + { :conditions => ["EXTRACT(YEAR FROM start_date) = ? AND EXTRACT(MONTH FROM start_date) = ?",date.year,date.month],
  49 + :limit => 10,
  50 + :order => 'start_date ASC'
  51 + }
34 } 52 }
35 53
36 include WhiteListFilter 54 include WhiteListFilter
@@ -105,7 +123,7 @@ class Event &lt; Article @@ -105,7 +123,7 @@ class Event &lt; Article
105 123
106 # TODO: some good soul, please clean this ugly hack: 124 # TODO: some good soul, please clean this ugly hack:
107 if self.body 125 if self.body
108 - html.div('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', :class => 'event-description') 126 + html.div('_____XXXX_DESCRIPTION_GOES_HERE_XXXX_____', :class => 'event-description')
109 end 127 end
110 } 128 }
111 129
app/models/image.rb
@@ -17,7 +17,7 @@ class Image &lt; ActiveRecord::Base @@ -17,7 +17,7 @@ class Image &lt; ActiveRecord::Base
17 :icon => '20x20!' }, 17 :icon => '20x20!' },
18 :max_size => 5.megabytes # remember to update validate message below 18 :max_size => 5.megabytes # remember to update validate message below
19 19
20 - validates_attachment :size => N_("%{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n 20 + validates_attachment :size => N_("{fn} of uploaded file was larger than the maximum size of 5.0 MB").fix_i18n
21 21
22 delay_attachment_fu_thumbnails 22 delay_attachment_fu_thumbnails
23 23
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/person.rb
@@ -240,7 +240,7 @@ class Person &lt; Profile @@ -240,7 +240,7 @@ class Person &lt; Profile
240 240
241 validates_each :email, :on => :update do |record,attr,value| 241 validates_each :email, :on => :update do |record,attr,value|
242 if User.find(:first, :conditions => ['email = ? and id != ? and environment_id = ?', value, record.user.id, record.environment.id]) 242 if User.find(:first, :conditions => ['email = ? and id != ? and environment_id = ?', value, record.user.id, record.environment.id])
243 - record.errors.add(attr, _('%{fn} is already used by other user').fix_i18n) 243 + record.errors.add(attr, _('{fn} is already used by other user').fix_i18n)
244 end 244 end
245 end 245 end
246 246
app/models/profile.rb
@@ -193,7 +193,7 @@ class Profile &lt; ActiveRecord::Base @@ -193,7 +193,7 @@ class Profile &lt; ActiveRecord::Base
193 193
194 has_many :tasks, :dependent => :destroy, :as => 'target' 194 has_many :tasks, :dependent => :destroy, :as => 'target'
195 195
196 - has_many :events, :source => 'articles', :class_name => 'Event', :order => 'name' 196 + has_many :events, :source => 'articles', :class_name => 'Event', :order => 'start_date'
197 197
198 def find_in_all_tasks(task_id) 198 def find_in_all_tasks(task_id)
199 begin 199 begin
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
@@ -86,45 +112,38 @@ class UploadedFile &lt; Article @@ -86,45 +112,38 @@ class UploadedFile &lt; Article
86 self.name = self.filename 112 self.name = self.filename
87 end 113 end
88 114
  115 + def download_headers
  116 + {
  117 + 'Content-Disposition' => "attachment; filename=\"#{self.filename}\"",
  118 + }
  119 + end
  120 +
89 def data 121 def data
90 File.read(self.full_filename) 122 File.read(self.full_filename)
91 end 123 end
92 124
93 def to_html(options = {}) 125 def to_html(options = {})
  126 + warn = ('='*80) + "\n" +
  127 + 'The method `UploadedFile#to_html()` is deprecated. ' +
  128 + 'You must to encapsulate UploadedFile with `FilePresenter.for()`.' +
  129 + "\n" + ('='*80)
  130 + raise NoMethodError, warn if ENV['RAILS_ENV'] == 'test'
  131 + Rails.logger.warn warn if Rails.logger
  132 + puts warn if ENV['RAILS_ENV'] == 'development'
94 article = self 133 article = self
95 if image? 134 if image?
96 lambda do 135 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 - 136 + image_tag(article.public_filename(:display),
  137 + :class => article.css_class_name,
  138 + :style => 'max-width: 100%') +
  139 + content_tag('div', article.abstract, :class => 'uploaded-file-description')
123 end 140 end
124 else 141 else
125 lambda do 142 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') 143 + content_tag('div',
  144 + link_to(article.name, article.url),
  145 + :class => article.css_class_name) +
  146 + content_tag('div', article.abstract, :class => 'uploaded-file-description')
128 end 147 end
129 end 148 end
130 end 149 end
app/models/user.rb
@@ -114,7 +114,7 @@ class User &lt; ActiveRecord::Base @@ -114,7 +114,7 @@ class User &lt; ActiveRecord::Base
114 before_save :encrypt_password 114 before_save :encrypt_password
115 validates_format_of :email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda {|user| !user.email.blank?}) 115 validates_format_of :email, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda {|user| !user.email.blank?})
116 116
117 - validates_inclusion_of :terms_accepted, :in => [ '1' ], :if => lambda { |u| ! u.terms_of_use.blank? }, :message => N_('%{fn} must be checked in order to signup.').fix_i18n 117 + validates_inclusion_of :terms_accepted, :in => [ '1' ], :if => lambda { |u| ! u.terms_of_use.blank? }, :message => N_('{fn} must be checked in order to signup.').fix_i18n
118 118
119 # Authenticates a user by their login name or email and unencrypted password. Returns the user or nil. 119 # Authenticates a user by their login name or email and unencrypted password. Returns the user or nil.
120 def self.authenticate(login, password, environment = nil) 120 def self.authenticate(login, password, environment = nil)
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/catalog/index.rhtml
@@ -92,7 +92,7 @@ @@ -92,7 +92,7 @@
92 <% end %> 92 <% end %>
93 <% product.price_details.each do |i| %> 93 <% product.price_details.each do |i| %>
94 <div class="search-product-input-dots-to-price"> 94 <div class="search-product-input-dots-to-price">
95 - <div class="search-product-input-name"><%= i.production_cost.name %></div> 95 + <div class="search-product-input-name"><%= i.name %></div>
96 <%= price_span i.price, :class => 'search-product-input-price' %> 96 <%= price_span i.price, :class => 'search-product-input-price' %>
97 </div> 97 </div>
98 <% end %> 98 <% end %>
app/views/cms/_blog.rhtml
@@ -54,6 +54,15 @@ @@ -54,6 +54,15 @@
54 54
55 <%= labelled_form_field(_('Description:'), text_area(:article, :body, :rows => 10)) %> 55 <%= labelled_form_field(_('Description:'), text_area(:article, :body, :rows => 10)) %>
56 56
  57 +<% f.fields_for :image_builder, @article.image do |i| %>
  58 + <%= file_field_or_thumbnail(_('Cover image:'), @article.image, i)%>
  59 + <%= _("Max size: %s (.jpg, .gif, .png)")% Image.max_size.to_humanreadable %>
  60 +<% end %>
  61 +
  62 +<% unless @article.image.nil? %>
  63 + <%= labelled_check_box(_('Remove cover image'),'remove_image',true,false)%>
  64 +<% end %>
  65 +
57 <%= labelled_form_field(_('How to display posts:'), f.select(:visualization_format, [ [ _('Full post'), 'full'], [ _('First paragraph'), 'short'] ])) %> 66 <%= labelled_form_field(_('How to display posts:'), f.select(:visualization_format, [ [ _('Full post'), 'full'], [ _('First paragraph'), 'short'] ])) %>
58 67
59 <%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, Blog.posts_per_page_options)) %> 68 <%= labelled_form_field(_('Posts per page:'), f.select(:posts_per_page, Blog.posts_per_page_options)) %>
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/content_viewer/_article_toolbar.rhtml
@@ -52,6 +52,9 @@ @@ -52,6 +52,9 @@
52 52
53 </div> 53 </div>
54 <div id="article-header"> 54 <div id="article-header">
  55 + <% if @page.blog? and !@page.image.nil? %>
  56 + <div class="blog-cover"><%= image_tag(@page.image.public_filename())%></div>
  57 + <% end %>
55 <%= link_to(image_tag('icons-mime/rss-feed.png'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %> 58 <%= link_to(image_tag('icons-mime/rss-feed.png'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %>
56 <%= article_title(@page, :no_link => true) %> 59 <%= article_title(@page, :no_link => true) %>
57 <%= article_translations(@page) %> 60 <%= article_translations(@page) %>
app/views/events/_agenda.rhtml
@@ -3,22 +3,14 @@ @@ -3,22 +3,14 @@
3 <table class='noborder current-month'> 3 <table class='noborder current-month'>
4 <caption> 4 <caption>
5 <h2><%= show_month(params[:year], params[:month]) %></h2> 5 <h2><%= show_month(params[:year], params[:month]) %></h2>
6 - <%= link_to_previous_month(params[:year], params[:month], '&laquo; %s' % _('previous')) %>  
7 - <%= link_to_next_month(params[:year], params[:month], '%s &raquo;' % _('next')) %> 6 + <%= link_to_previous_month(params[:year], params[:month], show_month(params[:year], params[:month], :previous => true, :only_month => true)) %>
  7 + <%= link_to_next_month(params[:year], params[:month], show_month(params[:year], params[:month], :next => true, :only_month => true)) %>
8 </caption> 8 </caption>
9 <%= render :partial => 'events/month', :locals => {:calendar => @calendar, :abbreviated => true} %> 9 <%= render :partial => 'events/month', :locals => {:calendar => @calendar, :abbreviated => true} %>
10 </table> 10 </table>
11 - <table class='noborder previous-month'>  
12 - <caption><h3><%= link_to_previous_month(params[:year], params[:month], show_month(params[:year], params[:month], :previous => true)) %></h3></caption>  
13 - <%= render :partial => 'events/month', :locals => {:calendar => @previous_calendar, :abbreviated => true} %>  
14 - </table>  
15 - <table class='noborder next-month'>  
16 - <caption><h3><%= link_to_next_month(params[:year], params[:month], show_month(params[:year], params[:month], :next => true)) %></h3></caption>  
17 - <%= render :partial => 'events/month', :locals => {:calendar => @next_calendar, :abbreviated => true} %>  
18 - </table>  
19 <br clear='both'/> 11 <br clear='both'/>
20 </div> 12 </div>
21 <div id='events-of-the-day'> 13 <div id='events-of-the-day'>
22 - <%= render :partial => 'events/events_by_day' %> 14 + <%= render :partial => 'events/events' %>
23 </div> 15 </div>
24 </div> 16 </div>
app/views/events/_events.rhtml 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +<%= list_events(@date, @events) %>
0 \ No newline at end of file 2 \ No newline at end of file
app/views/events/_events_by_day.rhtml
@@ -1 +0,0 @@ @@ -1 +0,0 @@
1 -<%= list_events(@selected_day, @events_of_the_day) %>  
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/_person_profile.rhtml
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 </tr> 4 </tr>
5 <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %> 5 <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %>
6 <%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%> 6 <%= display_field(_('Date of birth:'), profile, :birth_date) { |date| show_date(date) }%>
7 - <%= display_field(_('Location:'), profile, :location, true) %> 7 + <%= display_field _('Location:'), profile, :location %>
8 8
9 <%= display_field(_('Type:'), profile, :privacy_setting, true) %> 9 <%= display_field(_('Type:'), profile, :privacy_setting, true) %>
10 10
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/_list_groups.html.erb
@@ -12,7 +12,9 @@ @@ -12,7 +12,9 @@
12 <%= _('Members: %s') % group.members_count.to_s %> <br/> 12 <%= _('Members: %s') % group.members_count.to_s %> <br/>
13 <%= _('Created at: %s') % show_date(group.created_at) unless group.enterprise? %> <br/> 13 <%= _('Created at: %s') % show_date(group.created_at) unless group.enterprise? %> <br/>
14 <% button_bar do %> 14 <% button_bar do %>
15 - <%= button 'menu-ctrl-panel', _('Control panel of this group'), group.admin_url %> 15 + <% if user.has_permission?(:edit_profile, group) %>
  16 + <%= button 'menu-ctrl-panel', _('Control panel of this group'), group.admin_url %>
  17 + <% end %>
16 <%= button 'menu-logout', _('Leave community'), group.leave_url(true), :class => 'leave-community' %> 18 <%= button 'menu-logout', _('Leave community'), group.leave_url(true), :class => 'leave-community' %>
17 <% if (group.community? && user.has_permission?(:destroy_profile, group)) %> 19 <% if (group.community? && user.has_permission?(:destroy_profile, group)) %>
18 <%= button 'delete', _('Remove'), { :controller => 'profile_editor', :action => 'destroy_profile', :profile => group.identifier } %> 20 <%= button 'delete', _('Remove'), { :controller => 'profile_editor', :action => 'destroy_profile', :profile => group.identifier } %>
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/database.yml.pgsql
@@ -12,6 +12,7 @@ development: @@ -12,6 +12,7 @@ development:
12 adapter: postgresql 12 adapter: postgresql
13 encoding: unicode 13 encoding: unicode
14 database: noosfero_development 14 database: noosfero_development
  15 + template: template0
15 username: noosfero 16 username: noosfero
16 password: 17 password:
17 18
@@ -37,6 +38,7 @@ test: &amp;TEST @@ -37,6 +38,7 @@ test: &amp;TEST
37 adapter: postgresql 38 adapter: postgresql
38 encoding: unicode 39 encoding: unicode
39 database: noosfero_test 40 database: noosfero_test
  41 + template: template0
40 username: noosfero 42 username: noosfero
41 password: 43 password:
42 44
@@ -44,6 +46,7 @@ production: @@ -44,6 +46,7 @@ production:
44 adapter: postgresql 46 adapter: postgresql
45 encoding: unicode 47 encoding: unicode
46 database: noosfero_production 48 database: noosfero_production
  49 + template: template0
47 username: noosfero 50 username: noosfero
48 password: 51 password:
49 52
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
config/routes.rb
@@ -55,6 +55,7 @@ ActionController::Routing::Routes.draw do |map| @@ -55,6 +55,7 @@ ActionController::Routing::Routes.draw do |map|
55 55
56 # events 56 # events
57 map.events 'profile/:profile/events_by_day', :controller => 'events', :action => 'events_by_day', :profile => /#{Noosfero.identifier_format}/ 57 map.events 'profile/:profile/events_by_day', :controller => 'events', :action => 'events_by_day', :profile => /#{Noosfero.identifier_format}/
  58 + map.events 'profile/:profile/events_by_month', :controller => 'events', :action => 'events_by_month', :profile => /#{Noosfero.identifier_format}/
58 map.events 'profile/:profile/events/:year/:month/:day', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :day => /\d*/, :profile => /#{Noosfero.identifier_format}/ 59 map.events 'profile/:profile/events/:year/:month/:day', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :day => /\d*/, :profile => /#{Noosfero.identifier_format}/
59 map.events 'profile/:profile/events/:year/:month', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :profile => /#{Noosfero.identifier_format}/ 60 map.events 'profile/:profile/events/:year/:month', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :profile => /#{Noosfero.identifier_format}/
60 map.events 'profile/:profile/events', :controller => 'events', :action => 'events', :profile => /#{Noosfero.identifier_format}/ 61 map.events 'profile/:profile/events', :controller => 'events', :action => 'events', :profile => /#{Noosfero.identifier_format}/
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
db/migrate/20131121162641_remove_orphan_profiles.rb 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +class RemoveOrphanProfiles < ActiveRecord::Migration
  2 + def self.up
  3 + profiles = Profile.joins('LEFT JOIN environments ON profiles.environment_id=environments.id').where('environments.id IS NULL')
  4 + profiles.map(&:destroy)
  5 + end
  6 +
  7 + def self.down
  8 + say 'This migration can not be reverted.'
  9 + end
  10 +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"
debian/changelog
  1 +noosfero (0.45.0) unstable; urgency=low
  2 +
  3 + * New features release
  4 +
  5 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Tue, 10 Dec 2013 16:04:33 -0300
  6 +
  7 +noosfero (0.45.0~rc20131202153818) squeeze-test; urgency=low
  8 +
  9 + * 0.45.0 RC2
  10 +
  11 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Mon, 02 Dec 2013 15:38:28 -0300
  12 +
  13 +noosfero (0.44.6) unstable; urgency=low
  14 +
  15 + * 0.44.5 Addendum
  16 +
  17 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Wed, 27 Nov 2013 16:45:02 -0300
  18 +
  19 +noosfero (0.44.5) unstable; urgency=low
  20 +
  21 + * Bugfix release
  22 +
  23 + -- Rodrigo Souto <rodrigo@colivre.coop.br> Tue, 26 Nov 2013 17:39:39 -0300
  24 +
1 noosfero (0.44.4) unstable; urgency=low 25 noosfero (0.44.4) unstable; urgency=low
2 26
3 * Bugfix release 27 * Bugfix release
debian/control
@@ -31,13 +31,11 @@ Depends: @@ -31,13 +31,11 @@ Depends:
31 libgettext-rails-ruby1.8, 31 libgettext-rails-ruby1.8,
32 libsqlite3-ruby, 32 libsqlite3-ruby,
33 libpgsql-ruby, 33 libpgsql-ruby,
34 - libmysql-ruby,  
35 librmagick-ruby, 34 librmagick-ruby,
36 libredcloth-ruby, 35 libredcloth-ruby,
37 libwill-paginate-ruby (>= 2.3.12-1~), 36 libwill-paginate-ruby (>= 2.3.12-1~),
38 iso-codes, 37 iso-codes,
39 libfeedparser-ruby, 38 libfeedparser-ruby,
40 - openjdk-6-jre,  
41 libdaemons-ruby, 39 libdaemons-ruby,
42 rcov, 40 rcov,
43 thin, 41 thin,
features/blog.feature
@@ -15,6 +15,7 @@ Feature: blog @@ -15,6 +15,7 @@ Feature: blog
15 And I follow "Create blog" 15 And I follow "Create blog"
16 Then I should see "My Blog" 16 Then I should see "My Blog"
17 When I fill in "Title" with "My Blog" 17 When I fill in "Title" with "My Blog"
  18 + And I fill in "Address" with "my-blog"
18 And I press "Save" 19 And I press "Save"
19 And I go to joaosilva's control panel 20 And I go to joaosilva's control panel
20 Then I should see "Configure blog" 21 Then I should see "Configure blog"
@@ -24,6 +25,7 @@ Feature: blog @@ -24,6 +25,7 @@ Feature: blog
24 And I follow "Create blog" 25 And I follow "Create blog"
25 Then I should see "My Blog" 26 Then I should see "My Blog"
26 When I fill in "Title" with "My Blog" 27 When I fill in "Title" with "My Blog"
  28 + And I fill in "Address" with "my-blog"
27 And I press "Save" 29 And I press "Save"
28 Then I should be on /joaosilva/my-blog 30 Then I should be on /joaosilva/my-blog
29 31
@@ -33,6 +35,7 @@ Feature: blog @@ -33,6 +35,7 @@ Feature: blog
33 And I follow "New content" 35 And I follow "New content"
34 When I follow "Blog" 36 When I follow "Blog"
35 And I fill in "Title" with "Blog from cms" 37 And I fill in "Title" with "Blog from cms"
  38 + And I fill in "Address" with "blog-from-cms"
36 And I press "Save" 39 And I press "Save"
37 Then I should be on /joaosilva/blog-from-cms 40 Then I should be on /joaosilva/blog-from-cms
38 41
@@ -42,12 +45,14 @@ Feature: blog @@ -42,12 +45,14 @@ Feature: blog
42 And I follow "New content" 45 And I follow "New content"
43 And I follow "Blog" 46 And I follow "Blog"
44 And I fill in "Title" with "Blog One" 47 And I fill in "Title" with "Blog One"
  48 + And I fill in "Address" with "blog-one"
45 And I press "Save" 49 And I press "Save"
46 Then I go to joaosilva's control panel 50 Then I go to joaosilva's control panel
47 And I follow "Manage Content" 51 And I follow "Manage Content"
48 And I follow "New content" 52 And I follow "New content"
49 And I follow "Blog" 53 And I follow "Blog"
50 And I fill in "Title" with "Blog Two" 54 And I fill in "Title" with "Blog Two"
  55 + And I fill in "Address" with "blog-two"
51 And I press "Save" 56 And I press "Save"
52 Then I should not see "error" 57 Then I should not see "error"
53 And I should be on /joaosilva/blog-two 58 And I should be on /joaosilva/blog-two
@@ -109,3 +114,24 @@ Feature: blog @@ -109,3 +114,24 @@ Feature: blog
109 And I follow "New content" 114 And I follow "New content"
110 When I follow "Blog" 115 When I follow "Blog"
111 Then I should see "Tag list" 116 Then I should see "Tag list"
  117 +
  118 + Scenario: do not display the "clear cover image" when there is no uploaded image
  119 + Given the following blogs
  120 + | owner | name |
  121 + | joaosilva | My Blog |
  122 + And I go to joaosilva's control panel
  123 + And I follow "Configure blog"
  124 + Then I should not see "Delete cover image"
  125 +
  126 + # the step for attaching a file on the input only works with capybara 1.1.2, but it requires rails 1.9.3
  127 + @selenium-fixme
  128 + Scenario: display cover image after uploading an image as the blog cover
  129 + Given the following blogs
  130 + | owner | name |
  131 + | joaosilva | My Blog |
  132 + And I go to joaosilva's control panel
  133 + And I follow "Configure blog"
  134 + And I attach the file "public/images/rails.png" to "Uploaded data"
  135 + And I press "Save"
  136 + When I am on /joaosilva/my-blog
  137 + Then there should be a div with class "blog-cover"
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/events.feature
@@ -12,43 +12,38 @@ Feature: events @@ -12,43 +12,38 @@ Feature: events
12 12
13 Scenario: go to next month 13 Scenario: go to next month
14 Given I am on /profile/josesilva/events/2009/10 14 Given I am on /profile/josesilva/events/2009/10
15 - When I follow "next »" 15 + When I follow "November"
16 Then I should see "November 2009" within ".current-month" 16 Then I should see "November 2009" within ".current-month"
17 17
18 Scenario: go to next month in global agenda 18 Scenario: go to next month in global agenda
19 Given I am on /assets/events?year=2009&month=11 19 Given I am on /assets/events?year=2009&month=11
20 - When I follow "next »" 20 + When I follow "December"
21 Then I should see "December 2009" within ".current-month" 21 Then I should see "December 2009" within ".current-month"
22 22
23 Scenario: go to previous month 23 Scenario: go to previous month
24 Given I am on /profile/josesilva/events/2009/10 24 Given I am on /profile/josesilva/events/2009/10
25 - When I follow "« previous" 25 + When I follow "September"
26 Then I should see "September 2009" within ".current-month" 26 Then I should see "September 2009" within ".current-month"
27 27
28 Scenario: go to previous month in global agenda 28 Scenario: go to previous month in global agenda
29 Given I am on /assets/events?year=2009&month=11 29 Given I am on /assets/events?year=2009&month=11
30 - When I follow "« previous" 30 + When I follow "October"
31 Then I should see "October 2009" within ".current-month" 31 Then I should see "October 2009" within ".current-month"
32 32
33 Scenario: go to next month by clicking in month name 33 Scenario: go to next month by clicking in month name
34 Given I am on /profile/josesilva/events/2009/10 34 Given I am on /profile/josesilva/events/2009/10
35 - When I follow "November 2009" 35 + When I follow "November"
36 Then I should see "November 2009" within ".current-month" 36 Then I should see "November 2009" within ".current-month"
37 37
38 Scenario: go to previous month by clicking in month name 38 Scenario: go to previous month by clicking in month name
39 Given I am on /profile/josesilva/events/2009/10 39 Given I am on /profile/josesilva/events/2009/10
40 - When I follow "September 2009" 40 + When I follow "September"
41 Then I should see "September 2009" within ".current-month" 41 Then I should see "September 2009" within ".current-month"
42 42
43 - Scenario: go to specific day  
44 - Given I am on the homepage  
45 - When I am on /profile/josesilva/events/2009/01/20  
46 - Then I should see "Events for January 20, 2009"  
47 -  
48 Scenario: go to specific day in global agenda 43 Scenario: go to specific day in global agenda
49 Given I am on the homepage 44 Given I am on the homepage
50 When I am on /assets/events?year=2009&month=11&day=12 45 When I am on /assets/events?year=2009&month=11&day=12
51 - Then I should see "Events for November 12, 2009" 46 + Then I should see "Events for November, 2009"
52 47
53 Scenario: list events for specific day 48 Scenario: list events for specific day
54 Given I am on /profile/josesilva/events/2009/10 49 Given I am on /profile/josesilva/events/2009/10
@@ -64,7 +59,7 @@ Feature: events @@ -64,7 +59,7 @@ Feature: events
64 | owner | name | start_date | 59 | owner | name | start_date |
65 | josesilva | WikiSym 2009 | 2009-10-25 | 60 | josesilva | WikiSym 2009 | 2009-10-25 |
66 When I am on /profile/josesilva/events/2009/10/20 61 When I am on /profile/josesilva/events/2009/10/20
67 - Then I should not see "WikiSym 2009" 62 + Then I should see "WikiSym 2009"
68 63
69 Scenario: list event between a range 64 Scenario: list event between a range
70 Given I am on /profile/josesilva/events/2009/10 65 Given I am on /profile/josesilva/events/2009/10
@@ -96,20 +91,11 @@ Feature: events @@ -96,20 +91,11 @@ Feature: events
96 Then I should see "Another Conference" 91 Then I should see "Another Conference"
97 And I should see "Manuel Birthday" 92 And I should see "Manuel Birthday"
98 93
99 - Scenario: ask for a day when no inform complete date  
100 - When I am on /profile/josesilva/events/2009/5  
101 - Then I should see "Select a day on the left to display it's events here"  
102 -  
103 - Scenario: ask for a day when no inform complete date in global agenda  
104 - When I am on /assets/events?year=2009&month=5  
105 - Then I should see "Select a day on the left to display it's events here"  
106 - 94 + @selenium
107 Scenario: provide links to days with events 95 Scenario: provide links to days with events
108 Given I am on /profile/josesilva/events/2009/10 96 Given I am on /profile/josesilva/events/2009/10
109 Then I should see "24" link 97 Then I should see "24" link
110 - When I follow "next »"  
111 - Then I should see "24" link  
112 - When I follow "next »" 98 + When I follow "November"
113 Then I should not see "24" link 99 Then I should not see "24" link
114 100
115 Scenario: provide links to all days between start and end date 101 Scenario: provide links to all days between start and end date
@@ -128,7 +114,7 @@ Feature: events @@ -128,7 +114,7 @@ Feature: events
128 @selenium 114 @selenium
129 Scenario: show events when i follow a specific day 115 Scenario: show events when i follow a specific day
130 Given I am on /profile/josesilva/events/2009/10 116 Given I am on /profile/josesilva/events/2009/10
131 - And I should not see "Another Conference" 117 + And I should see "Another Conference"
132 When I follow "24" 118 When I follow "24"
133 Then I should see "Another Conference" 119 Then I should see "Another Conference"
134 120
@@ -138,7 +124,7 @@ Feature: events @@ -138,7 +124,7 @@ Feature: events
138 | owner | name | start_date | end_date | 124 | owner | name | start_date | end_date |
139 | josesilva | YAPC::Brasil 2010 | 2010-10-30 | 2010-11-01 | 125 | josesilva | YAPC::Brasil 2010 | 2010-10-30 | 2010-11-01 |
140 And I am on /profile/josesilva/events/2010/10 126 And I am on /profile/josesilva/events/2010/10
141 - And I should not see "YAPC::Brasil 2010" 127 + And I should see "YAPC::Brasil 2010"
142 When I follow "31" 128 When I follow "31"
143 Then I should see "YAPC::Brasil 2010" 129 Then I should see "YAPC::Brasil 2010"
144 130
@@ -150,10 +136,6 @@ Feature: events @@ -150,10 +136,6 @@ Feature: events
150 When I follow "Back to josesilva" 136 When I follow "Back to josesilva"
151 Then I should be on josesilva's homepage 137 Then I should be on josesilva's homepage
152 138
153 - Scenario: warn when there is no events  
154 - When I am on /profile/josesilva/events/2020/12/1  
155 - Then I should see "No events for this date"  
156 -  
157 Scenario: provide button to create new event 139 Scenario: provide button to create new event
158 Given I am logged in as "josesilva" 140 Given I am logged in as "josesilva"
159 When I am on /profile/josesilva/events/2020/12/1 141 When I am on /profile/josesilva/events/2020/12/1
@@ -230,3 +212,35 @@ Feature: events @@ -230,3 +212,35 @@ Feature: events
230 Then I should see "Leaded event" 212 Then I should see "Leaded event"
231 And I should see "This is the abstract." 213 And I should see "This is the abstract."
232 And I should not see "This is the real text." 214 And I should not see "This is the real text."
  215 +
  216 + Scenario: show range date of event
  217 + Given I am on /profile/josesilva/events/2009/10
  218 + And the following events
  219 + | owner | name | start_date | end_date |
  220 + | josesilva | WikiSym 2009 | 2009-10-25 | 2009-10-27 |
  221 + When I am on /profile/josesilva/events/2009/10/26
  222 + Then I should see "October 25, 2009 to October 27, 2009"
  223 +
  224 + Scenario: show place of the event
  225 + Given I am on /profile/josesilva/events/2009/10
  226 + And the following events
  227 + | owner | name | start_date | end_date | address |
  228 + | josesilva | WikiSym 2009 | 2009-10-25 | 2009-10-27 | Earth Planet |
  229 + When I am on /profile/josesilva/events/2009/10/26
  230 + Then I should see "Place: Earth Planet"
  231 +
  232 + Scenario: show event name as link
  233 + Given the following events
  234 + | owner | name | start_date |
  235 + | josesilva | Unpublished event | 2009-10-25 |
  236 + And I am logged in as "josesilva"
  237 + When I am on /profile/josesilva/events/2009/10/25
  238 + Then I should see "Unpublished event" link
  239 +
  240 + Scenario: go to event page
  241 + Given the following events
  242 + | owner | name | start_date |
  243 + | josesilva | Oktoberfest | 2009-10-25 |
  244 + Given I am on /profile/josesilva/events/2009/10
  245 + When I follow "Oktoberfest"
  246 + Then I should see "Oktoberfest"
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"