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.

AUTHORS
... ... @@ -111,6 +111,7 @@ Diego Martinez <diegoamc90@gmail.com>
111 111 Diego Martinez <diego@diego-K55A.(none)>
112 112 Diego + Renan <renanteruoc@gmail.com>
113 113 Fernanda Lopes <nanda.listas+psl@gmail.com>
  114 +Francisco Marcelo A. Lima Júnior <francisco.lima-junior@serpro.gov.br>
114 115 Francisco Marcelo de Araujo Lima Junior <79350259591@serpro-1457614.(none)>
115 116 Grazieno Pellegrino <grazieno@gmail.com>
116 117 Isaac Canan <isaac@intelletto.com.br>
... ... @@ -189,12 +190,14 @@ Renan Teruo + Diego Araujo &lt;renanteruoc@gmail.com&gt;
189 190 Renan Teruo + Diego Araújo <renanteruoc@gmail.com>
190 191 Renan Teruo + Paulo Meirelles <renanteruoc@gmail.com>
191 192 Renan Teruo + Rafael Manzo <renanteruoc@gmail.com>
  193 +Rodrigo Souto <diguliu@gmail.com>
192 194 Rodrigo Souto <rodrigo@colivre.coop.br>
193 195 Ronny Kursawe <kursawe.ronny@googlemail.com>
194 196 root <root@debian.sdr.serpro>
195 197 Samuel R. C. Vale <srcvale@holoscopio.com>
196 198 Valessio Brito <valessio@gmail.com>
197 199 vfcosta <vfcosta@gmail.com>
  200 +Vinicius Cubas Brand <viniciuscb@gmail.com>
198 201 Visita <visita@debian.(none)>
199 202 Yann Lugrin <yann.lugrin@liquid-concept.ch>
200 203  
... ...
COPYRIGHT
... ... @@ -4,8 +4,9 @@ Copyright (c) 2007-2009,
4 4 Cáritas Brasileira <http://www.caritasbrasileira.org/>
5 5 Copyright (c) 2007-2009,
6 6 Ynternet.org Foundation <http://www.ynternet.org/>
7   -Copyright (c) 2008-2009,
  7 +Copyright (c) 2008-2013,
8 8 Colivre <http://www.colivre.coop.br/>
  9 +Copyright (c) the Noosfero contributors. See AUTHORS
9 10  
10 11 This program is free software: you can redistribute it and/or modify
11 12 it under the terms of the GNU Affero General Public License as published by
... ...
HACKING
... ... @@ -52,3 +52,12 @@ If you write such script for your own OS, *please* share it with us at the
52 52 development mailing list so that we can include it in the official repository.
53 53 This way other people using the same OS will have to put less effort to develop
54 54 Noosfero.
  55 +
  56 +== Submitting your changes back
  57 +
  58 +For now please read:
  59 +
  60 +- Coding conventions
  61 + http://noosfero.org/Development/CodingConventions
  62 +- Patch guidelines
  63 + http://noosfero.org/Development/PatchGuidelines
... ...
HACKING.rails235
... ... @@ -1,13 +0,0 @@
1   -This is a draft of how to create a environment to Rails 2.3.5 to Noosfero
2   -development.
3   -
4   -Install dependencies:
5   -
6   -gem install rails -v 2.3.5
7   -gem install i18n
8   -gem install will_paginate -v 2.3.12
9   -gem install cucumber
10   -
11   -Creating initial environment:
12   -
13   -rake db:schema:load
INSTALL.chat
... ... @@ -6,7 +6,7 @@ To configure XMPP/BOSH in Noosfero you need:
6 6 * SystemTimer - http://ph7spot.com/musings/system-timer
7 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 11 # apt-get install librestclient-ruby pidgin-data ruby1.8-dev
12 12 # gem install SystemTimer
... ...
README
1   -noosfero - a web-based social platform
  1 +Noosfero - a web-based social platform
2 2 ======================================
3 3  
4   -:: About the project
  4 +http://www.noosfero.org/
5 5  
6   -Homepage: http://www.noosfero.org/
  6 +Documentation
  7 +-------------
7 8  
8   -:: Authors and copyright
  9 +The following documentation is available:
9 10  
10   -Authors: see file AUTHORS
11   -Copyright information: see file COPYRIGHT
12   -Full license text; see file COPYING
  11 +File Purpose
  12 +~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  13 +INSTALL install instructions
  14 +INSTALL.awstats install instructions - access statistics service
  15 +INSTALL.chat install instructions - chat service
  16 +INSTALL.email install instructions - email service
  17 +INSTALL.multitenancy install instructions - multiple sites
  18 +INSTALL.varnish install instructions - varnish HTTP caching (recommended)
  19 +HACKING development instruction
  20 +RELEASING instructions for doing releases
  21 +doc/noosfero/* user documentation (available through the app itself)
  22 +
  23 +
  24 +Authors and copyright
  25 +---------------------
  26 +
  27 +Authorship and copyright information is available in the files listed below.
  28 +
  29 +File Purpose
  30 +~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  31 +AUTHORS list of authors (updated at each release)
  32 +COPYRIGHT Copyright statement for the project
  33 +COPYING Full text of the project license
... ...
app/controllers/application_controller.rb
... ... @@ -174,8 +174,6 @@ class ApplicationController &lt; ActionController::Base
174 174 end
175 175  
176 176 def find_by_contents(asset, scope, query, paginate_options={:page => 1}, options={})
177   - scope = scope.send(options[:filter]) if options[:filter]
178   -
179 177 @plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) ||
180 178 fallback_find_by_contents(asset, scope, query, paginate_options, options)
181 179 end
... ... @@ -183,8 +181,9 @@ class ApplicationController &lt; ActionController::Base
183 181 private
184 182  
185 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 187 end
189 188  
190 189 end
... ...
app/controllers/my_profile/cms_controller.rb
... ... @@ -73,6 +73,7 @@ class CmsController &lt; MyProfileController
73 73 refuse_blocks
74 74 record_coming
75 75 if request.post?
  76 + @article.image = nil if params[:remove_image] == 'true'
76 77 @article.last_changed_by = user
77 78 if @article.update_attributes(params[:article])
78 79 if !continue
... ... @@ -144,10 +145,15 @@ class CmsController &lt; MyProfileController
144 145  
145 146 post_only :set_home_page
146 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 157 redirect_to (request.referer || profile.url)
152 158 end
153 159  
... ... @@ -267,7 +273,10 @@ class CmsController &lt; MyProfileController
267 273 @back_to = params[:back_to] || request.referer || url_for(profile.public_profile_url)
268 274 @task = SuggestArticle.new(params[:task])
269 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 280 if verify_recaptcha(:model => @task, :message => _('Please type the words correctly')) && @task.save
272 281 session[:notice] = _('Thanks for your suggestion. The community administrators were notified.')
273 282 redirect_to @back_to
... ...
app/controllers/my_profile/memberships_controller.rb
... ... @@ -9,9 +9,10 @@ class MembershipsController &lt; MyProfileController
9 9 def new_community
10 10 @community = Community.new(params[:community])
11 11 @community.environment = environment
  12 + @back_to = params[:back_to] || url_for(:action => 'index')
12 13 if request.post? && @community.valid?
13 14 @community = Community.create_after_moderation(user, {:environment => environment}.merge(params[:community]))
14   - redirect_to :action => 'index'
  15 + redirect_to @back_to
15 16 return
16 17 end
17 18 end
... ...
app/controllers/my_profile/profile_editor_controller.rb
... ... @@ -4,7 +4,7 @@ class ProfileEditorController &lt; MyProfileController
4 4 protect 'destroy_profile', :profile, :only => [:destroy_profile]
5 5  
6 6 def index
7   - @pending_tasks = Task.to(profile).pending.select{|i| user.has_permission?(i.permission, profile)}
  7 + @pending_tasks = Task.to(profile).pending.without_spam.select{|i| user.has_permission?(i.permission, profile)}
8 8 end
9 9  
10 10 helper :profile
... ...
app/controllers/my_profile/spam_controller.rb
... ... @@ -14,9 +14,15 @@ class SpamController &lt; MyProfileController
14 14 if params[:remove_comment]
15 15 profile.comments_received.find(params[:remove_comment]).destroy
16 16 end
  17 + if params[:remove_task]
  18 + Task.to(profile).find_by_id(params[:remove_task]).destroy
  19 + end
17 20 if params[:mark_comment_as_ham]
18 21 profile.comments_received.find(params[:mark_comment_as_ham]).ham!
19 22 end
  23 + if params[:mark_task_as_ham] && (t = Task.to(profile).find_by_id(params[:mark_task_as_ham]))
  24 + t.ham!
  25 + end
20 26 if request.xhr?
21 27 json_response(true)
22 28 else
... ... @@ -28,7 +34,8 @@ class SpamController &lt; MyProfileController
28 34 return
29 35 end
30 36  
31   - @spam = profile.comments_received.spam.paginate({:page => params[:page]})
  37 + @comment_spam = profile.comments_received.spam.paginate({:page => params[:comments_page]})
  38 + @task_spam = Task.to(profile).spam.paginate({:page => params[:tasks_page]})
32 39 end
33 40  
34 41 protected
... ...
app/controllers/my_profile/tasks_controller.rb
... ... @@ -4,12 +4,12 @@ class TasksController &lt; MyProfileController
4 4  
5 5 def index
6 6 @filter = params[:filter_type].blank? ? nil : params[:filter_type]
7   - @tasks = Task.to(profile).pending.of(@filter).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page])
  7 + @tasks = Task.to(profile).without_spam.pending.of(@filter).order_by('created_at', 'asc').paginate(:per_page => Task.per_page, :page => params[:page])
8 8 @failed = params ? params[:failed] : {}
9 9 end
10 10  
11 11 def processed
12   - @tasks = Task.to(profile).closed.sort_by(&:created_at)
  12 + @tasks = Task.to(profile).without_spam.closed.sort_by(&:created_at)
13 13 end
14 14  
15 15 VALID_DECISIONS = [ 'finish', 'cancel', 'skip' ]
... ... @@ -57,7 +57,7 @@ class TasksController &lt; MyProfileController
57 57 end
58 58  
59 59 def list_requested
60   - @tasks = Task.find_all_by_requestor_id(profile.id)
  60 + @tasks = Task.without_spam.find_all_by_requestor_id(profile.id)
61 61 end
62 62  
63 63 def ticket_details
... ...
app/controllers/public/content_viewer_controller.rb
... ... @@ -53,8 +53,11 @@ class ContentViewerController &lt; ApplicationController
53 53 # At this point the page will be showed
54 54 @page.hit
55 55  
56   - unless @page.mime_type == 'text/html' || (@page.image? && params[:view])
  56 + @page = FilePresenter.for @page
  57 +
  58 + if @page.download? params[:view]
57 59 headers['Content-Type'] = @page.mime_type
  60 + headers.merge! @page.download_headers
58 61 data = @page.data
59 62  
60 63 # TODO test the condition
... ... @@ -70,7 +73,7 @@ class ContentViewerController &lt; ApplicationController
70 73  
71 74 #FIXME see a better way to do this. It's not need to pass this variable anymore
72 75 @comment = Comment.new
73   -
  76 +
74 77 if @page.has_posts?
75 78 posts = if params[:year] and params[:month]
76 79 filter_date = DateTime.parse("#{params[:year]}-#{params[:month]}-01")
... ...
app/controllers/public/events_controller.rb
1 1 class EventsController < PublicController
2 2  
3 3 needs_profile
4   - no_design_blocks
5 4  
6 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 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 20 end
22 21  
23 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 26 end
28 27  
29 28 protected
... ...
app/controllers/public/profile_controller.rb
... ... @@ -206,10 +206,50 @@ class ProfileController &lt; PublicController
206 206 end
207 207  
208 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 210 render :partial => 'profile_network_activities', :locals => {:network_activities => @activities}
211 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 253 def remove_scrap
214 254 begin
215 255 scrap = current_user.person.scraps(params[:scrap_id])
... ... @@ -343,6 +383,7 @@ class ProfileController &lt; PublicController
343 383 end
344 384 end
345 385  
  386 +
346 387 protected
347 388  
348 389 def check_access_to_profile
... ... @@ -393,4 +434,5 @@ class ProfileController &lt; PublicController
393 434 def relations_to_include
394 435 [:image, :domains, :preferred_domain, :environment]
395 436 end
  437 +
396 438 end
... ...
app/controllers/public/search_controller.rb
... ... @@ -93,25 +93,27 @@ class SearchController &lt; PublicController
93 93 year = (params[:year] ? params[:year].to_i : Date.today.year)
94 94 month = (params[:month] ? params[:month].to_i : Date.today.month)
95 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 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 110 end
107 111  
108 112 @scope = date_range && params[:action] == 'events' ? environment.events.by_range(date_range) : environment.events
109 113 full_text_search
110 114  
111 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 117 end
116 118  
117 119 # keep old URLs workings
... ... @@ -136,9 +138,9 @@ class SearchController &lt; PublicController
136 138 end
137 139  
138 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 144 end
143 145  
144 146 #######################################################
... ...
app/helpers/application_helper.rb
... ... @@ -558,6 +558,9 @@ module ApplicationHelper
558 558 # displays a link to the profile homepage with its image (as generated by
559 559 # #profile_image) and its name below it.
560 560 def profile_image_link( profile, size=:portrait, tag='li', extra_info = nil )
  561 + if content = @plugins.dispatch_first(:profile_image_link, profile, size, tag, extra_info)
  562 + return instance_eval(&content)
  563 + end
561 564 name = profile.short_name
562 565 if profile.person?
563 566 url = url_for(profile.check_friendship_url)
... ... @@ -574,16 +577,16 @@ module ApplicationHelper
574 577 extra_info = extra_info.nil? ? '' : content_tag( 'span', extra_info, :class => 'extra_info' )
575 578 links = links_for_balloon(profile)
576 579 content_tag('div', content_tag(tag,
577   - (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") +
578   - link_to(
579   - content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +
580   - content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) +
581   - extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ),
582   - profile.url,
583   - :class => 'profile_link url',
584   - :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name,
585   - :title => profile.name ),
586   - :class => 'vcard'), :class => 'common-profile-list-block')
  580 + (environment.enabled?(:show_balloon_with_profile_links_when_clicked) ? link_to( content_tag( 'span', _('Profile links')), '#', :onclick => "toggleSubmenu(this, '#{profile.short_name}', #{links.to_json}); return false", :class => "menu-submenu-trigger #{trigger_class}", :url => url) : "") +
  581 + link_to(
  582 + content_tag( 'span', profile_image( profile, size ), :class => 'profile-image' ) +
  583 + content_tag( 'span', h(name), :class => ( profile.class == Person ? 'fn' : 'org' ) ) +
  584 + extra_info + profile_sex_icon( profile ) + profile_cat_icons( profile ),
  585 + profile.url,
  586 + :class => 'profile_link url',
  587 + :help => _('Click on this icon to go to the <b>%s</b>\'s home page') % profile.name,
  588 + :title => profile.name ),
  589 + :class => 'vcard'), :class => 'common-profile-list-block')
587 590 end
588 591  
589 592 def gravatar_url_for(email, options = {})
... ... @@ -727,8 +730,15 @@ module ApplicationHelper
727 730 end
728 731  
729 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 742 end
733 743  
734 744 def role_color(role, env_id)
... ... @@ -1113,15 +1123,34 @@ module ApplicationHelper
1113 1123 result
1114 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 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 1140 end
1123 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 1154 def usermenu_logged_in
1126 1155 pending_tasks_count = ''
1127 1156 count = user ? Task.to(user).pending.count : -1
... ... @@ -1133,6 +1162,7 @@ module ApplicationHelper
1133 1162 render_environment_features(:usermenu) +
1134 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 1164 manage_enterprises.to_s +
  1165 + manage_communities.to_s +
1136 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 1167 pending_tasks_count +
1138 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 1431 end
1402 1432  
1403 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 1436 #TODO This parse should be done through the macro infra, but since there
1407 1437 # are old things that do not support it we are keeping this hot spot.
1408 1438 html = @plugins.pipeline(:parse_content, html, source).first
... ...
app/helpers/blog_helper.rb
... ... @@ -42,7 +42,7 @@ module BlogHelper
42 42  
43 43 def display_post(article, format = 'full')
44 44 no_comments = (format == 'full') ? false : true
45   - html = send("display_#{format}_format", article).html_safe
  45 + html = send("display_#{format}_format", FilePresenter.for(article)).html_safe
46 46  
47 47 article_title(article, :no_comments => no_comments) + html
48 48 end
... ...
app/helpers/categories_helper.rb
... ... @@ -3,10 +3,21 @@ module CategoriesHelper
3 3  
4 4 COLORS = [
5 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 23 TYPES = [
... ...
app/helpers/cms_helper.rb
... ... @@ -33,7 +33,7 @@ module CmsHelper
33 33 link_to article_name, {:action => 'view', :id => article.id}, :class => icon_for_article(article)
34 34 else
35 35 if article.image?
36   - image_tag(icon_for_article(article)) + link_to(article_name, article.url)
  36 + image_tag(icon_for_article(article)) + link_to(article_name, article.url)
37 37 else
38 38 link_to article_name, article.url, :class => icon_for_article(article)
39 39 end
... ...
app/helpers/content_viewer_helper.rb
... ... @@ -17,7 +17,7 @@ module ContentViewerHelper
17 17 title = article.display_title if article.kind_of?(UploadedFile) && article.image?
18 18 title = article.title if title.blank?
19 19 title = content_tag('h1', h(title), :class => 'title')
20   - if article.belongs_to_blog?
  20 + if article.belongs_to_blog? || article.belongs_to_forum?
21 21 unless args[:no_link]
22 22 title = content_tag('h1', link_to(article.name, article.url), :class => 'title')
23 23 end
... ...
app/helpers/dates_helper.rb
... ... @@ -35,6 +35,18 @@ module DatesHelper
35 35 end
36 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 50 # formats a datetime for displaying.
39 51 def show_time(time)
40 52 if time
... ... @@ -98,7 +110,11 @@ module DatesHelper
98 110 elsif opts[:previous]
99 111 date = date << 1
100 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 118 end
103 119  
104 120 def build_date(year, month, day = 1)
... ... @@ -123,7 +139,7 @@ module DatesHelper
123 139 previous_month_date = date - 1.month
124 140  
125 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 143 end
128 144  
129 145 def link_to_next_month(year, month, label = nil)
... ... @@ -131,7 +147,7 @@ module DatesHelper
131 147 next_month_date = date + 1.month
132 148  
133 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 151 end
136 152  
137 153 def pick_date(object, method, options = {}, html_options = {})
... ...
app/helpers/events_helper.rb
1 1 module EventsHelper
2 2  
3 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 5 content_tag('h2', title) +
7 6 content_tag('div',
8 7 (events.any? ?
9 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 10 ), :id => 'agenda-items'
12 11 )
13 12 end
14 13  
15 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 23 end
22 24  
... ...
app/helpers/folder_helper.rb
... ... @@ -21,6 +21,7 @@ module FolderHelper
21 21 end
22 22  
23 23 def display_article_in_listing(article, recursive = false, level = 0)
  24 + article = FilePresenter.for article
24 25 article_link = if article.image?
25 26 link_to('&nbsp;' * (level * 4) + image_tag(icon_for_article(article)) + short_filename(article.name), article.url.merge(:view => true))
26 27 else
... ... @@ -40,12 +41,15 @@ module FolderHelper
40 41 end
41 42  
42 43 def icon_for_article(article)
43   - icon = article.class.icon_name(article)
  44 + article = FilePresenter.for article
  45 + icon = article.respond_to?(:icon_name) ?
  46 + article.icon_name :
  47 + article.class.icon_name(article)
44 48 if (icon =~ /\//)
45 49 icon
46 50 else
47   - klasses = 'icon icon-' + icon
48   - if article.kind_of?(UploadedFile)
  51 + klasses = 'icon ' + [icon].flatten.map{|name| 'icon-'+name}.join(' ')
  52 + if article.kind_of?(UploadedFile) || article.kind_of?(FilePresenter)
49 53 klasses += ' icon-upload-file'
50 54 end
51 55 klasses
... ...
app/helpers/sweeper_helper.rb
... ... @@ -44,4 +44,30 @@ module SweeperHelper
44 44 def expire_profile_index(profile)
45 45 expire_timeout_fragment(profile.relationships_cache_key)
46 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 73 end
... ...
app/models/article.rb
... ... @@ -2,6 +2,8 @@ require &#39;hpricot&#39;
2 2  
3 3 class Article < ActiveRecord::Base
4 4  
  5 + acts_as_having_image
  6 +
5 7 SEARCHABLE_FIELDS = {
6 8 :name => 10,
7 9 :abstract => 3,
... ... @@ -154,8 +156,12 @@ class Article &lt; ActiveRecord::Base
154 156 end
155 157 end
156 158  
  159 + def css_class_list
  160 + [self.class.name.underscore.dasherize]
  161 + end
  162 +
157 163 def css_class_name
158   - self.class.name.underscore.dasherize
  164 + [css_class_list].flatten.compact.join(' ')
159 165 end
160 166  
161 167 def pending_categorizations
... ... @@ -188,7 +194,7 @@ class Article &lt; ActiveRecord::Base
188 194 pending_categorizations.clear
189 195 end
190 196  
191   - acts_as_taggable
  197 + acts_as_taggable
192 198 N_('Tag list')
193 199  
194 200 acts_as_filesystem
... ... @@ -268,7 +274,7 @@ class Article &lt; ActiveRecord::Base
268 274 end
269 275  
270 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 278 def data
273 279 body
274 280 end
... ... @@ -310,6 +316,10 @@ class Article &lt; ActiveRecord::Base
310 316 def belongs_to_blog?
311 317 self.parent and self.parent.blog?
312 318 end
  319 +
  320 + def belongs_to_forum?
  321 + self.parent and self.parent.forum?
  322 + end
313 323  
314 324 def info_from_last_update
315 325 last_comment = comments.last
... ... @@ -325,7 +335,7 @@ class Article &lt; ActiveRecord::Base
325 335 end
326 336  
327 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 339 end
330 340  
331 341 def comment_url_structure(comment, action = :edit)
... ... @@ -360,6 +370,16 @@ class Article &lt; ActiveRecord::Base
360 370 false
361 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 383 named_scope :native_translations, :conditions => { :translation_of_id => nil }
364 384  
365 385 def translatable?
... ...
app/models/article_block.rb
... ... @@ -12,7 +12,7 @@ class ArticleBlock &lt; Block
12 12 block = self
13 13 lambda do
14 14 block_title(block.title) +
15   - (block.article ? article_to_html(block.article,
  15 + (block.article ? article_to_html(FilePresenter.for(block.article),
16 16 :gallery_view => false,
17 17 :inside_block => block, # For Blogs and folders
18 18 :format => block.visualization_format # For Articles and contents
... ... @@ -23,7 +23,7 @@ class ArticleBlock &lt; Block
23 23 def article_id
24 24 self.settings[:article_id]
25 25 end
26   -
  26 +
27 27 def article_id= value
28 28 self.settings[:article_id] = value.blank? ? nil : value.to_i
29 29 end
... ... @@ -63,4 +63,9 @@ class ArticleBlock &lt; Block
63 63 end
64 64  
65 65 settings_items :visualization_format, :type => :string, :default => 'short'
  66 +
  67 + def self.expire_on
  68 + { :profile => [:article], :environment => [:article] }
  69 + end
  70 +
66 71 end
... ...
app/models/block.rb
... ... @@ -142,4 +142,15 @@ class Block &lt; ActiveRecord::Base
142 142 false
143 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 156 end
... ...
app/models/blog_archives_block.rb
... ... @@ -45,4 +45,7 @@ class BlogArchivesBlock &lt; Block
45 45 content_tag('div', link_to(_('Subscribe RSS Feed'), owner_blog.feed.url), :class => 'subscribe-feed')
46 46 end
47 47  
  48 + def self.expire_on
  49 + { :profile => [:article], :environment => [:article] }
  50 + end
48 51 end
... ...
app/models/categories_block.rb
... ... @@ -35,4 +35,7 @@ class CategoriesBlock &lt; Block
35 35 end
36 36 end
37 37  
  38 + def self.expire_on
  39 + { :profile => [], :environment => [:category] }
  40 + end
38 41 end
... ...
app/models/category.rb
... ... @@ -7,13 +7,13 @@ class Category &lt; ActiveRecord::Base
7 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 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 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 18 # Finds all top level categories for a given environment.
19 19 named_scope :top_level_for, lambda { |environment|
... ...
app/models/comment.rb
... ... @@ -16,9 +16,7 @@ class Comment &lt; ActiveRecord::Base
16 16 has_many :children, :class_name => 'Comment', :foreign_key => 'reply_of_id', :dependent => :destroy
17 17 belongs_to :reply_of, :class_name => 'Comment', :foreign_key => 'reply_of_id'
18 18  
19   - named_scope :without_spam, :conditions => ['spam IS NULL OR spam = ?', false]
20 19 named_scope :without_reply, :conditions => ['reply_of_id IS NULL']
21   - named_scope :spam, :conditions => ['spam = ?', true]
22 20  
23 21 # unauthenticated authors:
24 22 validates_presence_of :name, :if => (lambda { |record| !record.email.blank? })
... ... @@ -29,7 +27,7 @@ class Comment &lt; ActiveRecord::Base
29 27 validates_presence_of :author_id, :if => (lambda { |rec| rec.name.blank? && rec.email.blank? })
30 28 validates_each :name do |rec,attribute,value|
31 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 31 end
34 32 end
35 33  
... ... @@ -108,6 +106,17 @@ class Comment &lt; ActiveRecord::Base
108 106  
109 107 include Noosfero::Plugin::HotSpot
110 108  
  109 + include Spammable
  110 +
  111 + def after_spam!
  112 + SpammerLogger.log(ip_address, self)
  113 + Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_spam))
  114 + end
  115 +
  116 + def after_ham!
  117 + Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_ham))
  118 + end
  119 +
111 120 def verify_and_notify
112 121 check_for_spam
113 122 unless spam?
... ... @@ -115,10 +124,6 @@ class Comment &lt; ActiveRecord::Base
115 124 end
116 125 end
117 126  
118   - def check_for_spam
119   - plugins.dispatch(:check_comment_for_spam, self)
120   - end
121   -
122 127 def notify_by_mail
123 128 if source.kind_of?(Article) && article.notify_comments?
124 129 if !notification_emails.empty?
... ... @@ -205,37 +210,6 @@ class Comment &lt; ActiveRecord::Base
205 210 @rejected = true
206 211 end
207 212  
208   - def spam?
209   - !spam.nil? && spam
210   - end
211   -
212   - def ham?
213   - !spam.nil? && !spam
214   - end
215   -
216   - def spam!
217   - self.spam = true
218   - self.save!
219   - SpammerLogger.log(ip_address, self)
220   - Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_spam))
221   - self
222   - end
223   -
224   - def ham!
225   - self.spam = false
226   - self.save!
227   - Delayed::Job.enqueue(CommentHandler.new(self.id, :marked_as_ham))
228   - self
229   - end
230   -
231   - def marked_as_spam
232   - plugins.dispatch(:comment_marked_as_spam, self)
233   - end
234   -
235   - def marked_as_ham
236   - plugins.dispatch(:comment_marked_as_ham, self)
237   - end
238   -
239 213 def need_moderation?
240 214 article.moderate_comments? && (author.nil? || article.author != author)
241 215 end
... ...
app/models/create_enterprise.rb
... ... @@ -40,12 +40,12 @@ class CreateEnterprise &lt; Task
40 40  
41 41 if self.region && self.target
42 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 44 end
45 45 end
46 46  
47 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 49 end
50 50 end
51 51  
... ...
app/models/domain.rb
... ... @@ -10,14 +10,14 @@ class Domain &lt; ActiveRecord::Base
10 10  
11 11 # <tt>name</tt> must be sequences of alphanumeric characters (a to z,
12 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 15 # checks validations that could not be expressed using Rails' predefined
16 16 # validations. In particular:
17 17 # * <tt>name</tt> must not start with 'www.'
18 18 def validate
19 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 21 end
22 22 end
23 23  
... ...
app/models/environment.rb
... ... @@ -127,7 +127,9 @@ class Environment &lt; ActiveRecord::Base
127 127 'captcha_for_logged_users' => _('Ask captcha when a logged user comments too'),
128 128 'skip_new_user_email_confirmation' => _('Skip e-mail confirmation for new users'),
129 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 134 end
133 135  
... ... @@ -168,7 +170,7 @@ class Environment &lt; ActiveRecord::Base
168 170  
169 171 # One Environment can be reached by many domains
170 172 has_many :domains, :as => :owner
171   - has_many :profiles
  173 + has_many :profiles, :dependent => :destroy
172 174  
173 175 has_many :organizations
174 176 has_many :enterprises
... ... @@ -783,13 +785,6 @@ class Environment &lt; ActiveRecord::Base
783 785 self.save!
784 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 788 after_create :create_default_licenses
794 789 def create_default_licenses
795 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 25  
26 26 validates_each :start_date do |event,field,value|
27 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 29 end
30 30 end
31 31  
32 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 54 include WhiteListFilter
... ... @@ -105,7 +123,7 @@ class Event &lt; Article
105 123  
106 124 # TODO: some good soul, please clean this ugly hack:
107 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 127 end
110 128 }
111 129  
... ...
app/models/image.rb
... ... @@ -17,7 +17,7 @@ class Image &lt; ActiveRecord::Base
17 17 :icon => '20x20!' },
18 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 22 delay_attachment_fu_thumbnails
23 23  
... ...
app/models/link_list_block.rb
... ... @@ -33,6 +33,12 @@ class LinkListBlock &lt; Block
33 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 42 settings_items :links, Array, :default => []
37 43  
38 44 before_save do |block|
... ... @@ -57,7 +63,7 @@ class LinkListBlock &lt; Block
57 63 def link_html(link)
58 64 klass = 'icon-' + link[:icon] if link[:icon]
59 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 68 end
63 69  
... ...
app/models/person.rb
... ... @@ -240,7 +240,7 @@ class Person &lt; Profile
240 240  
241 241 validates_each :email, :on => :update do |record,attr,value|
242 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 244 end
245 245 end
246 246  
... ...
app/models/profile.rb
... ... @@ -193,7 +193,7 @@ class Profile &lt; ActiveRecord::Base
193 193  
194 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 198 def find_in_all_tasks(task_id)
199 199 begin
... ...
app/models/recent_documents_block.rb
... ... @@ -30,4 +30,7 @@ class RecentDocumentsBlock &lt; Block
30 30 end
31 31 end
32 32  
  33 + def self.expire_on
  34 + { :profile => [:article], :environment => [:article] }
  35 + end
33 36 end
... ...
app/models/spammer_logger.rb
... ... @@ -6,6 +6,8 @@ class SpammerLogger &lt; Logger
6 6 if object
7 7 if object.kind_of?(Comment)
8 8 @logger << "[#{Time.now.strftime('%F %T %z')}] Comment-id: #{object.id} IP: #{spammer_ip}\n"
  9 + elsif object.kind_of?(SuggestArticle)
  10 + @logger << "[#{Time.now.strftime('%F %T %z')}] SuggestArticle-id: #{object.id} IP: #{spammer_ip}\n"
9 11 end
10 12 else
11 13 @logger << "[#{Time.now.strftime('%F %T %z')}] IP: #{spammer_ip}\n"
... ...
app/models/suggest_article.rb
... ... @@ -11,6 +11,17 @@ class SuggestArticle &lt; Task
11 11 settings_items :source, :type => String
12 12 settings_items :source_name, :type => String
13 13 settings_items :highlighted, :type => :boolean, :default => false
  14 + settings_items :ip_address, :type => String
  15 + settings_items :user_agent, :type => String
  16 + settings_items :referrer, :type => String
  17 +
  18 + after_create :schedule_spam_checking
  19 +
  20 + def schedule_spam_checking
  21 + self.delay.check_for_spam
  22 + end
  23 +
  24 + include Noosfero::Plugin::HotSpot
14 25  
15 26 def sender
16 27 "#{name} (#{email})"
... ... @@ -61,4 +72,12 @@ class SuggestArticle &lt; Task
61 72 _('You need to login on %{system} in order to approve or reject this article.') % { :system => target.environment.name }
62 73 end
63 74  
  75 + def after_spam!
  76 + SpammerLogger.log(ip_address, self)
  77 + self.delay.marked_as_spam
  78 + end
  79 +
  80 + def after_ham!
  81 + self.delay.marked_as_ham
  82 + end
64 83 end
... ...
app/models/tags_block.rb
... ... @@ -59,4 +59,8 @@ class TagsBlock &lt; Block
59 59 15.minutes
60 60 end
61 61  
  62 + def self.expire_on
  63 + { :profile => [:article], :environment => [:article] }
  64 + end
  65 +
62 66 end
... ...
app/models/task.rb
... ... @@ -235,6 +235,8 @@ class Task &lt; ActiveRecord::Base
235 235 end
236 236 end
237 237  
  238 + include Spammable
  239 +
238 240 protected
239 241  
240 242 # This method must be overrided in subclasses, and its implementation must do
... ... @@ -275,6 +277,7 @@ class Task &lt; ActiveRecord::Base
275 277 named_scope :of, lambda { |type| conditions = type ? "type LIKE '#{type}'" : "1=1"; {:conditions => [conditions]} }
276 278 named_scope :order_by, lambda { |attribute, ord| {:order => "#{attribute} #{ord}"} }
277 279  
  280 +
278 281 named_scope :to, lambda { |profile|
279 282 environment_condition = nil
280 283 if profile.person?
... ...
app/models/uploaded_file.rb
... ... @@ -41,7 +41,25 @@ class UploadedFile &lt; Article
41 41 end
42 42  
43 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 63 end
46 64  
47 65 # FIXME need to define min/max file size
... ... @@ -52,20 +70,28 @@ class UploadedFile &lt; Article
52 70 has_attachment :storage => :file_system,
53 71 :thumbnails => { :icon => [24,24], :thumb => '130x130>', :slideshow => '320x240>', :display => '640X480>' },
54 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 77 delay_attachment_fu_thumbnails
60 78  
61 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 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 93 end
  94 + 'upload-file'
69 95 end
70 96  
71 97 def mime_type
... ... @@ -86,45 +112,38 @@ class UploadedFile &lt; Article
86 112 self.name = self.filename
87 113 end
88 114  
  115 + def download_headers
  116 + {
  117 + 'Content-Disposition' => "attachment; filename=\"#{self.filename}\"",
  118 + }
  119 + end
  120 +
89 121 def data
90 122 File.read(self.full_filename)
91 123 end
92 124  
93 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 133 article = self
95 134 if image?
96 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 140 end
124 141 else
125 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 147 end
129 148 end
130 149 end
... ...
app/models/user.rb
... ... @@ -114,7 +114,7 @@ class User &lt; ActiveRecord::Base
114 114 before_save :encrypt_password
115 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 119 # Authenticates a user by their login name or email and unencrypted password. Returns the user or nil.
120 120 def self.authenticate(login, password, environment = nil)
... ...
app/presenters/generic.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +# Made to encapsulate any UploadedFile
  2 +class FilePresenter::Generic < FilePresenter
  3 + # if returns low priority, because it is generic.
  4 + def self.accepts?(f)
  5 + 1 if f.is_a? UploadedFile
  6 + end
  7 +end
... ...
app/presenters/image.rb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +class FilePresenter::Image < FilePresenter
  2 + def self.accepts?(f)
  3 + return nil unless f.respond_to? :image?
  4 + f.image? ? 10 : nil
  5 + end
  6 +
  7 + def icon_name
  8 + public_filename :icon
  9 + end
  10 +
  11 + def short_description
  12 + _('Image (%s)') % content_type.split('/')[1].upcase
  13 + end
  14 +end
... ...
app/sweepers/article_sweeper.rb
... ... @@ -16,15 +16,15 @@ class ArticleSweeper &lt; ActiveRecord::Observer
16 16 end
17 17 end
18 18  
  19 +
19 20 protected
20 21  
21 22 def expire_caches(article)
  23 + expire_blocks_cache(article.profile, [:article])
  24 +
22 25 return if !article.environment
  26 +
23 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 28 env = article.profile.environment
29 29 if env && (env.portal_community == article.profile)
30 30 article.environment.locales.keys.each do |locale|
... ...
app/sweepers/category_sweeper.rb
... ... @@ -3,7 +3,13 @@ class CategorySweeper &lt; ActiveRecord::Observer
3 3 include SweeperHelper
4 4  
5 5 def after_save(category)
  6 + expire_blocks_cache(category.environment, [:category])
  7 +
  8 + # Needed for environments with application layout
6 9 expire_fragment(category.environment.id.to_s + "_categories_menu")
7 10 end
8 11  
  12 + def after_destroy(category)
  13 + expire_blocks_cache(category.environment, [:category])
  14 + end
9 15 end
... ...
app/views/box_organizer/_link_list_block.rhtml
1 1 <strong><%= _('Links') %></strong>
2 2 <div id='edit-link-list-block' style='width:450px'>
3 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 5 <% for link in @block.links do %>
6 6 <tr>
7 7 <td>
... ... @@ -9,6 +9,9 @@
9 9 </td>
10 10 <td><%= text_field_tag 'block[links][][name]', link[:name], :class => 'link-name', :maxlength => 20 %></td>
11 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 15 </tr>
13 16 <% end %>
14 17 </table>
... ... @@ -18,7 +21,9 @@
18 21 page.insert_html :bottom, 'links', content_tag('tr',
19 22 content_tag('td', icon_selector('ok')) +
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 28 javascript_tag("$('edit-link-list-block').scrollTop = $('edit-link-list-block').scrollHeight")
24 29 end %>
... ...
app/views/catalog/index.rhtml
... ... @@ -92,7 +92,7 @@
92 92 <% end %>
93 93 <% product.price_details.each do |i| %>
94 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 96 <%= price_span i.price, :class => 'search-product-input-price' %>
97 97 </div>
98 98 <% end %>
... ...
app/views/cms/_blog.rhtml
... ... @@ -54,6 +54,15 @@
54 54  
55 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 66 <%= labelled_form_field(_('How to display posts:'), f.select(:visualization_format, [ [ _('Full post'), 'full'], [ _('First paragraph'), 'short'] ])) %>
58 67  
59 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 2 <%= _('Content management') %>
3 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 17 <% button_bar(:style => 'margin-bottom: 1em;') do %>
6 18 <% parent_id = ((@article && @article.allow_children?) ? @article : nil) %>
7 19  
... ... @@ -40,20 +52,27 @@
40 52 </tr>
41 53 <% end %>
42 54  
43   - <% @articles.each do |article| %>
  55 + <% @articles.each do |article| article = FilePresenter.for article %>
44 56 <tr title="<%= article.title%>" >
45   - <td>
  57 + <td class="article-name">
46 58 <%= link_to_article(article) %>
47 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 65 </td>
51 66 <td class="article-controls">
52 67 <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit) %>
53 68 <%= button_without_text :eyes, _('Public view'), article.view_url %>
54 69 <%= display_spread_button(profile, article) unless article.folder? || remove_content_button(:spread)%>
55 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 76 <% end %>
58 77 <%= display_delete_button(article) if !remove_content_button(:delete) %>
59 78 </td>
... ...
app/views/content_viewer/_article_toolbar.rhtml
... ... @@ -52,6 +52,9 @@
52 52  
53 53 </div>
54 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 58 <%= link_to(image_tag('icons-mime/rss-feed.png'), @page.feed.url, :class => 'blog-feed-link') if @page.has_posts? && @page.feed %>
56 59 <%= article_title(@page, :no_link => true) %>
57 60 <%= article_translations(@page) %>
... ...
app/views/events/_agenda.rhtml
... ... @@ -3,22 +3,14 @@
3 3 <table class='noborder current-month'>
4 4 <caption>
5 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 8 </caption>
9 9 <%= render :partial => 'events/month', :locals => {:calendar => @calendar, :abbreviated => true} %>
10 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 11 <br clear='both'/>
20 12 </div>
21 13 <div id='events-of-the-day'>
22   - <%= render :partial => 'events/events_by_day' %>
  14 + <%= render :partial => 'events/events' %>
23 15 </div>
24 16 </div>
... ...
app/views/events/_events.rhtml 0 → 100644
... ... @@ -0,0 +1 @@
  1 +<%= list_events(@date, @events) %>
0 2 \ No newline at end of file
... ...
app/views/events/_events_by_day.rhtml
... ... @@ -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 @@
  1 +<span class="download-link">
  2 + <span>Download</span>
  3 + <strong><%= link_to generic.filename, generic.public_filename %></strong>
  4 +</span>
  5 +
  6 +<div class="uploaded-file-description <%= 'empty' if generic.abstract.blank? %>">
  7 + <%= generic.abstract %>
  8 +</div>
  9 +
... ...
app/views/file_presenter/_image.html.erb 0 → 100644
... ... @@ -0,0 +1,36 @@
  1 +<% if image.gallery? && options[:gallery_view] %>
  2 +<%
  3 + images = image.parent.images
  4 + current_index = images.index(image.encapsulated_file)
  5 + total_of_images = images.count
  6 + link_to_previous = if current_index >= 1
  7 + link_to(_('&laquo; Previous'), images[current_index - 1].view_url, :class => 'previous')
  8 + else
  9 + content_tag('span', _('&laquo; Previous'), :class => 'previous')
  10 + end
  11 +
  12 + link_to_next = if current_index < total_of_images - 1
  13 + link_to(_('Next &raquo;'), images[current_index + 1].view_url, :class => 'next')
  14 + else
  15 + content_tag('span', _('Next &raquo;'), :class => 'next')
  16 + end
  17 +%>
  18 +
  19 +<div class="gallery-navigation">
  20 + <%= link_to_previous %>
  21 + <span class="total-of-images">
  22 + <%= _('image %d of %d') % [current_index + 1, total_of_images] %>
  23 + </span>
  24 + <%= link_to_next %>
  25 +</div>
  26 +
  27 +<% end %>
  28 +
  29 +<%# image_tag(article.public_filename(:display), :class => article.css_class_name, :style => 'max-width: 100%') %>
  30 +
  31 +<img src="<%=image.public_filename(:display)%>" class="<%=image.css_class_name%>">
  32 +
  33 +<div class="uploaded-file-description <%= 'empty' if image.abstract.blank? %>">
  34 + <%= image.abstract %>
  35 +</div>
  36 +
... ...
app/views/memberships/new_community.rhtml
... ... @@ -46,9 +46,11 @@
46 46  
47 47 <%= template_options(Community, 'community')%>
48 48  
  49 + <%= hidden_field_tag('back_to', @back_to) %>
  50 +
49 51 <% button_bar do %>
50 52 <%= submit_button(:save, _('Create')) %>
51   - <%= button(:cancel, _('Cancel'), :action => 'index') %>
  53 + <%= button(:cancel, _('Cancel'), @back_to ) %>
52 54 <% end %>
53 55  
54 56 <% end %>
... ...
app/views/profile/_more_comments.rhtml 0 → 100644
... ... @@ -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 @@
  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 4 </tr>
5 5 <%= display_field(_('Sex:'), profile, :sex) { |gender| { 'male' => _('Male'), 'female' => _('Female') }[gender] } %>
6 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 9 <%= display_field(_('Type:'), profile, :privacy_setting, true) %>
10 10  
... ...
app/views/profile/_profile_activities_list.rhtml
... ... @@ -4,7 +4,7 @@
4 4 <% if activity.kind_of?(ActionTracker::Record) %>
5 5 <%= render :partial => 'profile_activity', :locals => { :activity => activity, :tab_action => 'wall' } if activity.visible? %>
6 6 <% else %>
7   - <%= render :partial => 'profile_scrap', :locals => {:scrap => activity } %>
  7 + <%= render :partial => 'profile_scraps', :locals => { :activity => activity, :scrap => activity } %>
8 8 <% end %>
9 9 <% end %>
10 10 <% end %>
... ...
app/views/profile/_profile_comments.rhtml
1 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 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 9 </div>
  10 +</div>
7 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 12 <%= render :partial => 'profile_comment_form', :locals => { :activity => activity, :tab_action => tab_action } %>
... ...
app/views/profile/_profile_scrap.rhtml
... ... @@ -16,13 +16,7 @@
16 16 </div>
17 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 20 <% scrap.replies.map do |reply| %>
27 21 <%= render :partial => 'profile_scrap', :locals => {:scrap => reply} %>
28 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 12 <%= _('Members: %s') % group.members_count.to_s %> <br/>
13 13 <%= _('Created at: %s') % show_date(group.created_at) unless group.enterprise? %> <br/>
14 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 18 <%= button 'menu-logout', _('Leave community'), group.leave_url(true), :class => 'leave-community' %>
17 19 <% if (group.community? && user.has_permission?(:destroy_profile, group)) %>
18 20 <%= button 'delete', _('Remove'), { :controller => 'profile_editor', :action => 'destroy_profile', :profile => group.identifier } %>
... ...
app/views/shared/_manage_enterprises.rhtml
... ... @@ -1,8 +0,0 @@
1   -<div id='manage-enterprises'>
2   - <a href="#" id='manage-enterprises-link' class='simplemenu-trigger' title='<%= _('Manage enterprises') %>'><i class="icon-menu-enterprise"></i><strong><%= ui_icon('ui-icon-triangle-1-s') + _('My enterprises') %></strong></a>
3   - <ul class='simplemenu-submenu'>
4   - <% enterprises_link.each do |link| %>
5   - <li class='simplemenu-item'><%= link %></li>
6   - <% end %>
7   - </ul>
8   -</div>
app/views/shared/_manage_link.html.erb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +<div id=<%= "manage-#{kind}" %> class="manage-groups">
  2 + <a href="#" id=<%= "manage-#{kind}-link" %> class="simplemenu-trigger" title="<%= _('Manage %s') % kind %>"><i class=<%= "icon-menu-#{kind.singularize}" %>></i><strong><%= ui_icon('ui-icon-triangle-1-s') + _('My %s') % kind %></strong></a>
  3 + <ul class="simplemenu-submenu">
  4 + <% link.each do |link| %>
  5 + <li class="simplemenu-item"><%= link %></li>
  6 + <% end %>
  7 + </ul>
  8 +</div>
... ...
app/views/spam/_comment_spam.rhtml 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %>
  2 +<div id='article'>
  3 + <div class="comments" id="comments_list">
  4 + <ul class="article-comments-list">
  5 + <%= render :partial => 'comment/comment', :collection => @comment_spam %>
  6 + </ul>
  7 + </div>
  8 +</div>
  9 +
  10 +<%= pagination_links @comment_spam, :param_name => :comments_page %>
  11 +
... ...
app/views/spam/_suggest_article.html.erb 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +<% render :layout => 'task', :locals => { :task => task } do %>
  2 + <% content_for :extra_buttons do %>
  3 + <%= button_to_function('down', _('Show details'), "toggleDetails(this, '#{_('Hide details')}', '#{_('Show details')}')" ) %>
  4 + <% end %>
  5 +
  6 + <% content_for :extra_content do %>
  7 + <ul class="suggest-article-details" style="display: none">
  8 + <li><strong><%=_('Sent by')%></strong>: <%=task.name%> </li>
  9 + <li><strong><%=_('Email')%></strong>: <%=task.email%> </li>
  10 + <li><strong><%=_('Source')%></strong>: <%=task.source_name%> </li>
  11 + <li><strong><%=_('Source URL')%></strong>: <%=task.source%> </li>
  12 + <li><strong><%=_('Folder')%></strong>: <%=(a = Article.find_by_id(task.article_parent_id))?a.name : '<em>' + s_('Folder|none') + '</em>'%> </li>
  13 + <li><strong><%=_('Lead')%></strong>: <%=task.article_abstract.blank? ? '<em>' + s_('Abstract|empty') + '</em>' : task.article_abstract%> </li>
  14 + <li><strong><%=_('Body')%></strong>:
  15 + <div class='suggest-article-body'>
  16 + <%= task.article_body %>
  17 + </div>
  18 + </li>
  19 + </ul>
  20 + <% end %>
  21 +<% end %>
... ...
app/views/spam/_task.rhtml 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +<div class="task_box" id="task-<%= task.id %>">
  2 + <%= render :partial => 'tasks/task_icon', :locals => {:task => task} %>
  3 + <%= render :partial => 'tasks/task_title', :locals => {:task => task} %>
  4 + <div class="task-information">
  5 + <%= task_information(task) %>
  6 + </div>
  7 +
  8 + <%= yield %> <% # ??? %>
  9 +
  10 + <% button_bar do %>
  11 + <%= button_to_function('new', _('Mark as NOT SPAM'), 'removeTaskBox(this, %s, "%s", "")' % [url_for(:mark_task_as_ham => task.id).to_json, "task-#{task.id}"]) %>
  12 + <%= yield :extra_buttons %>
  13 + <%= button_to_function('delete', _('Remove'), 'removeTaskBox(this, %s, "%s", %s)' % [url_for(:profile => params[:profile], :remove_task => task.id).to_json, "task-#{task.id}", _('Are you sure you want to remove this article suggestion?').to_json]) %>
  14 +
  15 + <% end %>
  16 +
  17 + <%= yield :extra_content %>
  18 +</div>
... ...
app/views/spam/_task_spam.rhtml 0 → 100644
... ... @@ -0,0 +1,4 @@
  1 +<% @task_spam.each do |t| %>
  2 + <%= render :partial => partial_for_class(t.class), :locals => { :task => t } %>
  3 +<% end %>
  4 +<%= pagination_links @task_spam, :param_name => :tasks_page %>
... ...
app/views/spam/index.rhtml
  1 +<%= stylesheet('tasks') %>
  2 +
1 3 <h1><%= _('Manage SPAM') %></h1>
2 4  
  5 +<% no_tabs = @comment_spam.blank? && @task_spam.blank? %>
  6 +
  7 +<%= _('There are no spams to review.') if no_tabs %>
  8 +
3 9 <% button_bar do %>
4 10 <%= button :back, _('Back to control panel'), :controller => :profile_editor %>
5 11 <% end %>
6 12  
7 13 <%# FIXME should not need to replicate the article structure like this to be able to use the same formatting as the comments listing %>
8   -<div id='article'>
9   - <div class="comments" id="comments_list">
10   - <ul class="article-comments-list">
11   - <%= render :partial => 'comment/comment', :collection => @spam %>
12   - </ul>
13   - </div>
14   -</div>
15 14  
16   -<%= pagination_links @spam %>
  15 +<% tabs = [] %>
  16 +<% tabs << {:title => _('Comment Spam'), :id => 'comment-spam',
  17 + :content => (render :partial => 'comment_spam')} if @comment_spam.present? %>
  18 +<% tabs << {:title => _('Task Spam'), :id => 'task-spam',
  19 + :content => (render :partial => 'task_spam') } if @task_spam.present? %>
  20 +<%= render_tabs(tabs) %>
17 21  
18   -<% button_bar do %>
19   - <%= button :back, _('Back to control panel'), :controller => :profile_editor %>
  22 +<% unless no_tabs %>
  23 + <% button_bar do %>
  24 + <%= button :back, _('Back to control panel'), :controller => :profile_editor %>
  25 + <% end %>
20 26 <% end %>
  27 +
  28 +<%= javascript_include_tag 'spam' %>
... ...
app/views/tasks/_task.rhtml
1 1 <div class="task_box" id="task-<%= task.id %>">
2 2  
3   - <div class="task_icon">
4   - <%
5   - icon_info = task.icon
6   - if icon_info[:type] == :profile_image
7   - icon = profile_image(icon_info[:profile], :minor)
8   - elsif icon_info[:type] == :defined_image
9   - icon = "<img src='#{icon_info[:src]}' alt='#{icon_info[:name]}' />"
10   - end
11   - %>
12   - <%=
13   - if icon_info[:url]
14   - link_to(icon, icon_info[:url])
15   - else
16   - icon
17   - end
18   - %>
19   -
20   - </div>
  3 + <%= render :partial => 'task_icon', :locals => {:task => task} %>
21 4  
22 5 <div class="task_decisions">
23 6 <%=
... ... @@ -39,9 +22,7 @@
39 22 %>
40 23 </div><!-- class="task_decisions" -->
41 24  
42   - <strong class="task_title">
43   - <%= task.title %>
44   - </strong>
  25 + <%= render :partial => 'task_title', :locals => {:task => task} %>
45 26  
46 27 <div class="task_information">
47 28 <%= task_information(task) %>
... ...
app/views/tasks/_task_icon.rhtml 0 → 100644
... ... @@ -0,0 +1,16 @@
  1 +<%
  2 + icon_info = task.icon
  3 + if icon_info[:type] == :profile_image
  4 + icon = profile_image(icon_info[:profile], :minor)
  5 + elsif icon_info[:type] == :defined_image
  6 + icon = "<img src='#{icon_info[:src]}' alt='#{icon_info[:name]}' />"
  7 + end
  8 +
  9 + if icon_info[:url]
  10 + icon = link_to(icon, icon_info[:url])
  11 + end
  12 +%>
  13 +
  14 +<div class="task_icon">
  15 + <%= icon %>
  16 +</div>
... ...
app/views/tasks/_task_title.rhtml 0 → 100644
... ... @@ -0,0 +1,3 @@
  1 +<strong class="task_title">
  2 + <%= task.title %>
  3 +</strong>
... ...
config/database.yml.pgsql
... ... @@ -12,6 +12,7 @@ development:
12 12 adapter: postgresql
13 13 encoding: unicode
14 14 database: noosfero_development
  15 + template: template0
15 16 username: noosfero
16 17 password:
17 18  
... ... @@ -37,6 +38,7 @@ test: &amp;TEST
37 38 adapter: postgresql
38 39 encoding: unicode
39 40 database: noosfero_test
  41 + template: template0
40 42 username: noosfero
41 43 password:
42 44  
... ... @@ -44,6 +46,7 @@ production:
44 46 adapter: postgresql
45 47 encoding: unicode
46 48 database: noosfero_production
  49 + template: template0
47 50 username: noosfero
48 51 password:
49 52  
... ...
config/initializers/plugins.rb
... ... @@ -4,4 +4,5 @@ require &#39;noosfero/plugin/manager&#39;
4 4 require 'noosfero/plugin/active_record'
5 5 require 'noosfero/plugin/mailer_base'
6 6 require 'noosfero/plugin/settings'
  7 +require 'noosfero/plugin/spammable'
7 8 Noosfero::Plugin.init_system if $NOOSFERO_LOAD_PLUGINS
... ...
config/noosfero.yml.dist
... ... @@ -8,6 +8,7 @@ development:
8 8 gravatar: wavatar
9 9 googlemaps_initial_zoom: 4
10 10 exception_recipients: [admin@example.com]
  11 + max_upload_size: 5MB
11 12  
12 13 test:
13 14  
... ...
config/routes.rb
... ... @@ -55,6 +55,7 @@ ActionController::Routing::Routes.draw do |map|
55 55  
56 56 # events
57 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 59 map.events 'profile/:profile/events/:year/:month/:day', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :day => /\d*/, :profile => /#{Noosfero.identifier_format}/
59 60 map.events 'profile/:profile/events/:year/:month', :controller => 'events', :action => 'events', :year => /\d*/, :month => /\d*/, :profile => /#{Noosfero.identifier_format}/
60 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 @@
  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 @@
  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 @@
  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 @@
  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 @@
  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
... ...
db/schema.rb
... ... @@ -9,7 +9,7 @@
9 9 #
10 10 # It's strongly recommended to check this file into your version control system.
11 11  
12   -ActiveRecord::Schema.define(:version => 20130711213046) do
  12 +ActiveRecord::Schema.define(:version => 20131116165327) do
13 13  
14 14 create_table "abuse_reports", :force => true do |t|
15 15 t.integer "reporter_id"
... ... @@ -86,6 +86,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do
86 86 t.string "language"
87 87 t.string "source_name"
88 88 t.integer "license_id"
  89 + t.integer "image_id"
  90 + t.integer "position"
89 91 end
90 92  
91 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 131 t.string "language"
130 132 t.string "source_name"
131 133 t.integer "license_id"
  134 + t.integer "image_id"
  135 + t.integer "position"
132 136 end
133 137  
134 138 add_index "articles", ["name"], :name => "index_articles_on_name"
... ... @@ -547,8 +551,11 @@ ActiveRecord::Schema.define(:version =&gt; 20130711213046) do
547 551 t.datetime "created_at"
548 552 t.string "target_type"
549 553 t.integer "image_id"
  554 + t.boolean "spam", :default => false
550 555 end
551 556  
  557 + add_index "tasks", ["spam"], :name => "index_tasks_on_spam"
  558 +
552 559 create_table "thumbnails", :force => true do |t|
553 560 t.integer "size"
554 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 25 noosfero (0.44.4) unstable; urgency=low
2 26  
3 27 * Bugfix release
... ...
debian/control
... ... @@ -31,13 +31,11 @@ Depends:
31 31 libgettext-rails-ruby1.8,
32 32 libsqlite3-ruby,
33 33 libpgsql-ruby,
34   - libmysql-ruby,
35 34 librmagick-ruby,
36 35 libredcloth-ruby,
37 36 libwill-paginate-ruby (>= 2.3.12-1~),
38 37 iso-codes,
39 38 libfeedparser-ruby,
40   - openjdk-6-jre,
41 39 libdaemons-ruby,
42 40 rcov,
43 41 thin,
... ...
features/blog.feature
... ... @@ -15,6 +15,7 @@ Feature: blog
15 15 And I follow "Create blog"
16 16 Then I should see "My Blog"
17 17 When I fill in "Title" with "My Blog"
  18 + And I fill in "Address" with "my-blog"
18 19 And I press "Save"
19 20 And I go to joaosilva's control panel
20 21 Then I should see "Configure blog"
... ... @@ -24,6 +25,7 @@ Feature: blog
24 25 And I follow "Create blog"
25 26 Then I should see "My Blog"
26 27 When I fill in "Title" with "My Blog"
  28 + And I fill in "Address" with "my-blog"
27 29 And I press "Save"
28 30 Then I should be on /joaosilva/my-blog
29 31  
... ... @@ -33,6 +35,7 @@ Feature: blog
33 35 And I follow "New content"
34 36 When I follow "Blog"
35 37 And I fill in "Title" with "Blog from cms"
  38 + And I fill in "Address" with "blog-from-cms"
36 39 And I press "Save"
37 40 Then I should be on /joaosilva/blog-from-cms
38 41  
... ... @@ -42,12 +45,14 @@ Feature: blog
42 45 And I follow "New content"
43 46 And I follow "Blog"
44 47 And I fill in "Title" with "Blog One"
  48 + And I fill in "Address" with "blog-one"
45 49 And I press "Save"
46 50 Then I go to joaosilva's control panel
47 51 And I follow "Manage Content"
48 52 And I follow "New content"
49 53 And I follow "Blog"
50 54 And I fill in "Title" with "Blog Two"
  55 + And I fill in "Address" with "blog-two"
51 56 And I press "Save"
52 57 Then I should not see "error"
53 58 And I should be on /joaosilva/blog-two
... ... @@ -109,3 +114,24 @@ Feature: blog
109 114 And I follow "New content"
110 115 When I follow "Blog"
111 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 9 And the following enterprises
10 10 | identifier | owner | name | enabled |
11 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 13 And the following product_categories
14 14 | name |
15 15 | categ1 |
... ...
features/browse_enterprises.feature
... ... @@ -6,7 +6,7 @@ Background:
6 6 Given the following enterprises
7 7 | identifier | name |
8 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 10 And feature "show_balloon_with_profile_links_when_clicked" is enabled on environment
11 11  
12 12 Scenario: show all enterprises
... ...
features/enterprise_homepage.feature
... ... @@ -21,6 +21,7 @@ Feature: enterprise homepage
21 21 And the following product
22 22 | name | category | owner |
23 23 | Natural Handmade | soap | mayhem |
  24 + And feature "products_for_enterprises" is enabled on environment
24 25  
25 26  
26 27 Scenario: display profile info
... ...
features/events.feature
... ... @@ -12,43 +12,38 @@ Feature: events
12 12  
13 13 Scenario: go to next month
14 14 Given I am on /profile/josesilva/events/2009/10
15   - When I follow "next »"
  15 + When I follow "November"
16 16 Then I should see "November 2009" within ".current-month"
17 17  
18 18 Scenario: go to next month in global agenda
19 19 Given I am on /assets/events?year=2009&month=11
20   - When I follow "next »"
  20 + When I follow "December"
21 21 Then I should see "December 2009" within ".current-month"
22 22  
23 23 Scenario: go to previous month
24 24 Given I am on /profile/josesilva/events/2009/10
25   - When I follow "« previous"
  25 + When I follow "September"
26 26 Then I should see "September 2009" within ".current-month"
27 27  
28 28 Scenario: go to previous month in global agenda
29 29 Given I am on /assets/events?year=2009&month=11
30   - When I follow "« previous"
  30 + When I follow "October"
31 31 Then I should see "October 2009" within ".current-month"
32 32  
33 33 Scenario: go to next month by clicking in month name
34 34 Given I am on /profile/josesilva/events/2009/10
35   - When I follow "November 2009"
  35 + When I follow "November"
36 36 Then I should see "November 2009" within ".current-month"
37 37  
38 38 Scenario: go to previous month by clicking in month name
39 39 Given I am on /profile/josesilva/events/2009/10
40   - When I follow "September 2009"
  40 + When I follow "September"
41 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 43 Scenario: go to specific day in global agenda
49 44 Given I am on the homepage
50 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 48 Scenario: list events for specific day
54 49 Given I am on /profile/josesilva/events/2009/10
... ... @@ -64,7 +59,7 @@ Feature: events
64 59 | owner | name | start_date |
65 60 | josesilva | WikiSym 2009 | 2009-10-25 |
66 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 64 Scenario: list event between a range
70 65 Given I am on /profile/josesilva/events/2009/10
... ... @@ -96,20 +91,11 @@ Feature: events
96 91 Then I should see "Another Conference"
97 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 95 Scenario: provide links to days with events
108 96 Given I am on /profile/josesilva/events/2009/10
109 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 99 Then I should not see "24" link
114 100  
115 101 Scenario: provide links to all days between start and end date
... ... @@ -128,7 +114,7 @@ Feature: events
128 114 @selenium
129 115 Scenario: show events when i follow a specific day
130 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 118 When I follow "24"
133 119 Then I should see "Another Conference"
134 120  
... ... @@ -138,7 +124,7 @@ Feature: events
138 124 | owner | name | start_date | end_date |
139 125 | josesilva | YAPC::Brasil 2010 | 2010-10-30 | 2010-11-01 |
140 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 128 When I follow "31"
143 129 Then I should see "YAPC::Brasil 2010"
144 130  
... ... @@ -150,10 +136,6 @@ Feature: events
150 136 When I follow "Back to josesilva"
151 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 139 Scenario: provide button to create new event
158 140 Given I am logged in as "josesilva"
159 141 When I am on /profile/josesilva/events/2020/12/1
... ... @@ -230,3 +212,35 @@ Feature: events
230 212 Then I should see "Leaded event"
231 213 And I should see "This is the abstract."
232 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 19 And the following product
20 20 | owner | category | name |
21 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 23 And the following units
24 24 | singular | plural |
25 25 | Meter | Meters |
... ...
features/manage_product_price_details.feature
... ... @@ -19,7 +19,7 @@ Feature: manage product price details
19 19 And the following product
20 20 | owner | category | name | price |
21 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 23 And the following inputs
24 24 | product | category | price_per_unit | amount_used |
25 25 | Abbey Road | Rock | 10.0 | 2 |
... ...
features/manage_products.feature
... ... @@ -9,7 +9,7 @@ Feature: manage products
9 9 And the following enterprises
10 10 | identifier | owner | name | enabled |
11 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 14 Scenario: display "create new product" button
15 15 Given I am logged in as "joaosilva"
... ...