Commit 45b484729bb6aa46e4902e0deadb2df9b480eaa1

Authored by Rodrigo Souto
2 parents 70890398 c90b8e63

Merge commit 'refs/merge-requests/248' of git://gitorious.org/noosfero/noosfero …

…into merge-requests/248

Conflicts:
	app/models/person.rb
	app/views/catalog/index.rhtml
	public/stylesheets/application.css
	test/functional/catalog_controller_test.rb
Showing 223 changed files with 74020 additions and 62087 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 223 files displayed.

app/controllers/admin/admin_panel_controller.rb
@@ -8,6 +8,9 @@ class AdminPanelController < AdminController @@ -8,6 +8,9 @@ class AdminPanelController < AdminController
8 8
9 def site_info 9 def site_info
10 if request.post? 10 if request.post?
  11 + if params[:environment][:languages]
  12 + params[:environment][:languages] = params[:environment][:languages].map {|lang, value| lang if value=='true'}.compact
  13 + end
11 if @environment.update_attributes(params[:environment]) 14 if @environment.update_attributes(params[:environment])
12 session[:notice] = _('Environment settings updated') 15 session[:notice] = _('Environment settings updated')
13 redirect_to :action => 'index' 16 redirect_to :action => 'index'
app/controllers/application_controller.rb
@@ -42,9 +42,9 @@ class ApplicationController < ActionController::Base @@ -42,9 +42,9 @@ class ApplicationController < ActionController::Base
42 42
43 before_filter :set_locale 43 before_filter :set_locale
44 def set_locale 44 def set_locale
45 - FastGettext.available_locales = Noosfero.available_locales  
46 - FastGettext.default_locale = Noosfero.default_locale  
47 - FastGettext.locale = (params[:lang] || session[:lang] || Noosfero.default_locale || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en') 45 + FastGettext.available_locales = environment.available_locales
  46 + FastGettext.default_locale = environment.default_locale
  47 + FastGettext.locale = (params[:lang] || session[:lang] || environment.default_locale || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en')
48 I18n.locale = FastGettext.locale 48 I18n.locale = FastGettext.locale
49 if params[:lang] 49 if params[:lang]
50 session[:lang] = params[:lang] 50 session[:lang] = params[:lang]
app/controllers/box_organizer_controller.rb
@@ -68,7 +68,8 @@ class BoxOrganizerController < ApplicationController @@ -68,7 +68,8 @@ class BoxOrganizerController < ApplicationController
68 raise ArgumentError.new("Type %s is not allowed. Go away." % type) 68 raise ArgumentError.new("Type %s is not allowed. Go away." % type)
69 end 69 end
70 else 70 else
71 - @block_types = available_blocks 71 + @center_block_types = Box.acceptable_center_blocks & available_blocks
  72 + @side_block_types = Box.acceptable_side_blocks & available_blocks
72 @boxes = boxes_holder.boxes 73 @boxes = boxes_holder.boxes
73 render :action => 'add_block', :layout => false 74 render :action => 'add_block', :layout => false
74 end 75 end
app/controllers/my_profile/cms_controller.rb
@@ -116,11 +116,11 @@ class CmsController < MyProfileController @@ -116,11 +116,11 @@ class CmsController < MyProfileController
116 @parent_id = parent.id 116 @parent_id = parent.id
117 end 117 end
118 118
119 - translations if @article.translatable?  
120 -  
121 @article.profile = profile 119 @article.profile = profile
122 @article.last_changed_by = user 120 @article.last_changed_by = user
123 121
  122 + translations if @article.translatable?
  123 +
124 continue = params[:continue] 124 continue = params[:continue]
125 if request.post? 125 if request.post?
126 if @article.save 126 if @article.save
@@ -149,7 +149,6 @@ class CmsController < MyProfileController @@ -149,7 +149,6 @@ class CmsController < MyProfileController
149 @uploaded_files = [] 149 @uploaded_files = []
150 @article = @parent = check_parent(params[:parent_id]) 150 @article = @parent = check_parent(params[:parent_id])
151 @target = @parent ? ('/%s/%s' % [profile.identifier, @parent.full_name]) : '/%s' % profile.identifier 151 @target = @parent ? ('/%s/%s' % [profile.identifier, @parent.full_name]) : '/%s' % profile.identifier
152 - @folders = Folder.find(:all, :conditions => { :profile_id => profile })  
153 if @article 152 if @article
154 record_coming 153 record_coming
155 end 154 end
@@ -345,7 +344,7 @@ class CmsController < MyProfileController @@ -345,7 +344,7 @@ class CmsController < MyProfileController
345 end 344 end
346 345
347 def translations 346 def translations
348 - @locales = Noosfero.locales.invert.reject { |name, lang| !@article.possible_translations.include?(lang) } 347 + @locales = environment.locales.invert.reject { |name, lang| !@article.possible_translations.include?(lang) }
349 @selected_locale = @article.language || FastGettext.locale 348 @selected_locale = @article.language || FastGettext.locale
350 end 349 end
351 350
app/controllers/my_profile/profile_design_controller.rb
@@ -5,7 +5,7 @@ class ProfileDesignController < BoxOrganizerController @@ -5,7 +5,7 @@ class ProfileDesignController < BoxOrganizerController
5 protect 'edit_profile_design', :profile 5 protect 'edit_profile_design', :profile
6 6
7 def available_blocks 7 def available_blocks
8 - blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock ] 8 + blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock, HighlightsBlock ]
9 9
10 # blocks exclusive for organizations 10 # blocks exclusive for organizations
11 if profile.has_members? 11 if profile.has_members?
app/controllers/my_profile/profile_editor_controller.rb
@@ -14,6 +14,7 @@ class ProfileEditorController < MyProfileController @@ -14,6 +14,7 @@ class ProfileEditorController < MyProfileController
14 @profile_data = profile 14 @profile_data = profile
15 @possible_domains = profile.possible_domains 15 @possible_domains = profile.possible_domains
16 if request.post? 16 if request.post?
  17 + params[:profile_data][:fields_privacy] ||= {} if profile.person? && params[:profile_data].is_a?(Hash)
17 begin 18 begin
18 Profile.transaction do 19 Profile.transaction do
19 Image.transaction do 20 Image.transaction do
app/controllers/my_profile/profile_members_controller.rb
@@ -156,18 +156,4 @@ class ProfileMembersController < MyProfileController @@ -156,18 +156,4 @@ class ProfileMembersController < MyProfileController
156 end 156 end
157 end 157 end
158 158
159 - def send_mail  
160 - @mailing = profile.mailings.build(params[:mailing])  
161 - if request.post?  
162 - @mailing.locale = locale  
163 - @mailing.person = user  
164 - if @mailing.save  
165 - session[:notice] = _('The e-mails are being sent')  
166 - redirect_to :action => 'index'  
167 - else  
168 - session[:notice] = _('Could not create the e-mail')  
169 - end  
170 - end  
171 - end  
172 -  
173 end 159 end
app/controllers/public/account_controller.rb
@@ -25,11 +25,13 @@ class AccountController < ApplicationController @@ -25,11 +25,13 @@ class AccountController < ApplicationController
25 25
26 # action to perform login to the application 26 # action to perform login to the application
27 def login 27 def login
28 - @user = User.new  
29 - @person = @user.build_person  
30 store_location(request.referer) unless session[:return_to] 28 store_location(request.referer) unless session[:return_to]
31 return unless request.post? 29 return unless request.post?
32 - self.current_user = User.authenticate(params[:user][:login], params[:user][:password], environment) if params[:user] 30 +
  31 + self.current_user = plugins_alternative_authentication
  32 +
  33 + self.current_user ||= User.authenticate(params[:user][:login], params[:user][:password], environment) if params[:user]
  34 +
33 if logged_in? 35 if logged_in?
34 if params[:remember_me] == "1" 36 if params[:remember_me] == "1"
35 self.current_user.remember_me 37 self.current_user.remember_me
@@ -41,7 +43,6 @@ class AccountController < ApplicationController @@ -41,7 +43,6 @@ class AccountController < ApplicationController
41 end 43 end
42 else 44 else
43 session[:notice] = _('Incorrect username or password') if redirect? 45 session[:notice] = _('Incorrect username or password') if redirect?
44 - redirect_to :back if redirect?  
45 end 46 end
46 end 47 end
47 48
@@ -56,6 +57,11 @@ class AccountController < ApplicationController @@ -56,6 +57,11 @@ class AccountController < ApplicationController
56 57
57 # action to register an user to the application 58 # action to register an user to the application
58 def signup 59 def signup
  60 + if @plugins.dispatch(:allow_user_registration).include?(false)
  61 + redirect_back_or_default(:controller => 'home')
  62 + session[:notice] = _("This environment doesn't allow user registration.")
  63 + end
  64 +
59 @invitation_code = params[:invitation_code] 65 @invitation_code = params[:invitation_code]
60 begin 66 begin
61 if params[:user] 67 if params[:user]
@@ -125,6 +131,10 @@ class AccountController < ApplicationController @@ -125,6 +131,10 @@ class AccountController < ApplicationController
125 # 131 #
126 # Posts back. 132 # Posts back.
127 def forgot_password 133 def forgot_password
  134 + if @plugins.dispatch(:allow_password_recovery).include?(false)
  135 + redirect_back_or_default(:controller => 'home')
  136 + session[:notice] = _("This environment doesn't allow password recovery.")
  137 + end
128 @change_password = ChangePassword.new(params[:change_password]) 138 @change_password = ChangePassword.new(params[:change_password])
129 139
130 if request.post? 140 if request.post?
@@ -303,10 +313,27 @@ class AccountController < ApplicationController @@ -303,10 +313,27 @@ class AccountController < ApplicationController
303 end 313 end
304 314
305 def go_to_initial_page 315 def go_to_initial_page
306 - if environment == current_user.environment  
307 - redirect_back_or_default(user.admin_url) 316 + if environment.enabled?('allow_change_of_redirection_after_login')
  317 + case user.preferred_login_redirection
  318 + when 'keep_on_same_page'
  319 + redirect_back_or_default(user.admin_url)
  320 + when 'site_homepage'
  321 + redirect_to :controller => :home
  322 + when 'user_profile_page'
  323 + redirect_to user.public_profile_url
  324 + when 'user_homepage'
  325 + redirect_to user.url
  326 + when 'user_control_panel'
  327 + redirect_to user.admin_url
  328 + else
  329 + redirect_back_or_default(user.admin_url)
  330 + end
308 else 331 else
309 - redirect_back_or_default(:controller => 'home') 332 + if environment == current_user.environment
  333 + redirect_back_or_default(user.admin_url)
  334 + else
  335 + redirect_back_or_default(:controller => 'home')
  336 + end
310 end 337 end
311 end 338 end
312 339
@@ -316,4 +343,13 @@ class AccountController < ApplicationController @@ -316,4 +343,13 @@ class AccountController < ApplicationController
316 end 343 end
317 end 344 end
318 345
  346 + def plugins_alternative_authentication
  347 + user = nil
  348 + @plugins.each do |plugin|
  349 + user = plugin.alternative_authentication
  350 + break unless user.nil?
  351 + end
  352 + user
  353 + end
  354 +
319 end 355 end
app/controllers/public/not_found_controller.rb
@@ -2,4 +2,8 @@ class NotFoundController < ApplicationController @@ -2,4 +2,8 @@ class NotFoundController < ApplicationController
2 def index 2 def index
3 render_not_found 3 render_not_found
4 end 4 end
  5 +
  6 + def nothing
  7 + render :nothing => true, :status => 404
  8 + end
5 end 9 end
app/controllers/public/profile_controller.rb
@@ -2,11 +2,13 @@ class ProfileController < PublicController @@ -2,11 +2,13 @@ class ProfileController < PublicController
2 2
3 needs_profile 3 needs_profile
4 before_filter :check_access_to_profile, :except => [:join, :join_not_logged, :index, :add] 4 before_filter :check_access_to_profile, :except => [:join, :join_not_logged, :index, :add]
5 - before_filter :store_location, :only => [:join, :join_not_logged, :report_abuse]  
6 - before_filter :login_required, :only => [:add, :join, :join_not_logged, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_activities, :view_more_network_activities, :report_abuse, :register_report, :leave_comment_on_activity] 5 + before_filter :store_location, :only => [:join, :join_not_logged, :report_abuse, :send_mail]
  6 + before_filter :login_required, :only => [:add, :join, :join_not_logged, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_activities, :view_more_network_activities, :report_abuse, :register_report, :leave_comment_on_activity, :send_mail]
7 7
8 helper TagsHelper 8 helper TagsHelper
9 9
  10 + protect 'send_mail_to_members', :profile, :only => [:send_mail]
  11 +
10 def index 12 def index
11 @network_activities = !@profile.is_a?(Person) ? @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) : [] 13 @network_activities = !@profile.is_a?(Person) ? @profile.tracked_notifications.visible.paginate(:per_page => 15, :page => params[:page]) : []
12 if logged_in? && current_person.follows?(@profile) 14 if logged_in? && current_person.follows?(@profile)
@@ -49,36 +51,36 @@ class ProfileController < PublicController @@ -49,36 +51,36 @@ class ProfileController < PublicController
49 51
50 def communities 52 def communities
51 if is_cache_expired?(profile.communities_cache_key(params)) 53 if is_cache_expired?(profile.communities_cache_key(params))
52 - @communities = profile.communities.paginate(:per_page => per_page, :page => params[:npage]) 54 + @communities = profile.communities.includes(relations_to_include).paginate(:per_page => per_page, :page => params[:npage])
53 end 55 end
54 end 56 end
55 57
56 def enterprises 58 def enterprises
57 - @enterprises = profile.enterprises 59 + @enterprises = profile.enterprises.includes(relations_to_include)
58 end 60 end
59 61
60 def friends 62 def friends
61 if is_cache_expired?(profile.friends_cache_key(params)) 63 if is_cache_expired?(profile.friends_cache_key(params))
62 - @friends = profile.friends.paginate(:per_page => per_page, :page => params[:npage]) 64 + @friends = profile.friends.includes(relations_to_include).paginate(:per_page => per_page, :page => params[:npage])
63 end 65 end
64 end 66 end
65 67
66 def members 68 def members
67 if is_cache_expired?(profile.members_cache_key(params)) 69 if is_cache_expired?(profile.members_cache_key(params))
68 - @members = profile.members.paginate(:per_page => members_per_page, :page => params[:npage]) 70 + @members = profile.members.includes(relations_to_include).paginate(:per_page => members_per_page, :page => params[:npage])
69 end 71 end
70 end 72 end
71 73
72 def fans 74 def fans
73 - @fans = profile.fans 75 + @fans = profile.fans.includes(relations_to_include)
74 end 76 end
75 77
76 def favorite_enterprises 78 def favorite_enterprises
77 - @favorite_enterprises = profile.favorite_enterprises 79 + @favorite_enterprises = profile.favorite_enterprises.includes(relations_to_include)
78 end 80 end
79 81
80 def sitemap 82 def sitemap
81 - @articles = profile.top_level_articles 83 + @articles = profile.top_level_articles.includes([:profile, :parent])
82 end 84 end
83 85
84 def join 86 def join
@@ -327,6 +329,20 @@ class ProfileController < PublicController @@ -327,6 +329,20 @@ class ProfileController < PublicController
327 end 329 end
328 end 330 end
329 331
  332 + def send_mail
  333 + @mailing = profile.mailings.build(params[:mailing])
  334 + if request.post?
  335 + @mailing.locale = locale
  336 + @mailing.person = user
  337 + if @mailing.save
  338 + session[:notice] = _('The e-mails are being sent')
  339 + redirect_to_previous_location
  340 + else
  341 + session[:notice] = _('Could not create the e-mail')
  342 + end
  343 + end
  344 + end
  345 +
330 protected 346 protected
331 347
332 def check_access_to_profile 348 def check_access_to_profile
@@ -378,4 +394,8 @@ class ProfileController < PublicController @@ -378,4 +394,8 @@ class ProfileController < PublicController
378 @can_edit_profile ||= user && user.has_permission?('edit_profile', profile) 394 @can_edit_profile ||= user && user.has_permission?('edit_profile', profile)
379 end 395 end
380 helper_method :can_edit_profile 396 helper_method :can_edit_profile
  397 +
  398 + def relations_to_include
  399 + [:image, :domains, :preferred_domain, :environment]
  400 + end
381 end 401 end
app/controllers/public/search_controller.rb
@@ -46,7 +46,7 @@ class SearchController < PublicController @@ -46,7 +46,7 @@ class SearchController < PublicController
46 if !@empty_query 46 if !@empty_query
47 full_text_search ['public:true'] 47 full_text_search ['public:true']
48 else 48 else
49 - @results[@asset] = @environment.people.visible.send(@filter).paginate(paginate_options) 49 + @results[@asset] = visible_profiles(Person).send(@filter).paginate(paginate_options)
50 end 50 end
51 end 51 end
52 52
@@ -76,7 +76,7 @@ class SearchController < PublicController @@ -76,7 +76,7 @@ class SearchController < PublicController
76 full_text_search ['public:true'] 76 full_text_search ['public:true']
77 else 77 else
78 @filter_title = _('Enterprises from network') 78 @filter_title = _('Enterprises from network')
79 - @results[@asset] = @environment.enterprises.visible.paginate(paginate_options) 79 + @results[@asset] = visible_profiles(Enterprise, [{:products => :product_category}]).paginate(paginate_options)
80 end 80 end
81 end 81 end
82 82
@@ -84,7 +84,7 @@ class SearchController < PublicController @@ -84,7 +84,7 @@ class SearchController < PublicController
84 if !@empty_query 84 if !@empty_query
85 full_text_search ['public:true'] 85 full_text_search ['public:true']
86 else 86 else
87 - @results[@asset] = @environment.communities.visible.send(@filter).paginate(paginate_options) 87 + @results[@asset] = visible_profiles(Community).send(@filter).paginate(paginate_options)
88 end 88 end
89 end 89 end
90 90
@@ -310,4 +310,12 @@ class SearchController < PublicController @@ -310,4 +310,12 @@ class SearchController < PublicController
310 @all_facets = ret[:all_facets] 310 @all_facets = ret[:all_facets]
311 end 311 end
312 312
  313 + private
  314 +
  315 + def visible_profiles(klass, *extra_relations)
  316 + relations = [:image, :domains, :environment, :preferred_domain]
  317 + relations += extra_relations
  318 + @environment.send(klass.name.underscore.pluralize).visible.includes(relations)
  319 + end
  320 +
313 end 321 end
app/helpers/application_helper.rb
@@ -265,9 +265,9 @@ module ApplicationHelper @@ -265,9 +265,9 @@ module ApplicationHelper
265 265
266 VIEW_EXTENSIONS = %w[.rhtml .html.erb] 266 VIEW_EXTENSIONS = %w[.rhtml .html.erb]
267 267
268 - def partial_for_class_in_view_path(klass, view_path) 268 + def partial_for_class_in_view_path(klass, view_path, suffix = nil)
269 return nil if klass.nil? 269 return nil if klass.nil?
270 - name = klass.name.underscore 270 + name = [klass.name.underscore, suffix].compact.map(&:to_s).join('_')
271 271
272 search_name = String.new(name) 272 search_name = String.new(name)
273 if search_name.include?("/") 273 if search_name.include?("/")
@@ -282,31 +282,20 @@ module ApplicationHelper @@ -282,31 +282,20 @@ module ApplicationHelper
282 return name if File.exists?(File.join(path)) 282 return name if File.exists?(File.join(path))
283 end 283 end
284 284
285 - partial_for_class_in_view_path(klass.superclass, view_path) 285 + partial_for_class_in_view_path(klass.superclass, view_path, suffix)
286 end 286 end
287 287
288 - def partial_for_class(klass) 288 + def partial_for_class(klass, suffix=nil)
289 raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil? 289 raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil?
290 name = klass.name.underscore 290 name = klass.name.underscore
291 @controller.view_paths.each do |view_path| 291 @controller.view_paths.each do |view_path|
292 - partial = partial_for_class_in_view_path(klass, view_path) 292 + partial = partial_for_class_in_view_path(klass, view_path, suffix)
293 return partial if partial 293 return partial if partial
294 end 294 end
295 295
296 raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' 296 raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?'
297 end 297 end
298 298
299 - def partial_for_task_class(klass, action)  
300 - raise ArgumentError, 'No partial for object. Is there a partial for any class in the inheritance hierarchy?' if klass.nil?  
301 -  
302 - name = "#{klass.name.underscore}_#{action.to_s}"  
303 - VIEW_EXTENSIONS.each do |ext|  
304 - return name if File.exists?(File.join(RAILS_ROOT, 'app', 'views', params[:controller], '_'+name+ext))  
305 - end  
306 -  
307 - partial_for_task_class(klass.superclass, action)  
308 - end  
309 -  
310 def view_for_profile_actions(klass) 299 def view_for_profile_actions(klass)
311 raise ArgumentError, 'No profile actions view for this class.' if klass.nil? 300 raise ArgumentError, 'No profile actions view for this class.' if klass.nil?
312 301
@@ -664,19 +653,6 @@ module ApplicationHelper @@ -664,19 +653,6 @@ module ApplicationHelper
664 content_tag('div', result) 653 content_tag('div', result)
665 end 654 end
666 655
667 - def select_folder(label, object, method, collection, html_options = {}, js_options = {})  
668 - root = profile ? profile.identifier : _("root")  
669 - labelled_form_field(label, select(object, method,  
670 - collection.map {|f| [ root + '/' + f.full_name, f.id ]},  
671 - {:include_blank => root}, html_options.merge(js_options)))  
672 - end  
673 -  
674 - def select_profile_folder(label, object, method, profile, html_options = {}, js_options = {})  
675 - labelled_form_field(label, select(object, method,  
676 - profile.folders.map {|f| [ profile.identifier + '/' + f.full_name, f.id ]},  
677 - {:include_blank => profile.identifier}, html_options.merge(js_options)))  
678 - end  
679 -  
680 def theme_option(opt = nil) 656 def theme_option(opt = nil)
681 conf = RAILS_ROOT.to_s() + 657 conf = RAILS_ROOT.to_s() +
682 '/public' + theme_path + 658 '/public' + theme_path +
@@ -877,7 +853,7 @@ module ApplicationHelper @@ -877,7 +853,7 @@ module ApplicationHelper
877 end 853 end
878 else 854 else
879 if profile.active_fields.include?(name) 855 if profile.active_fields.include?(name)
880 - result = field_html 856 + result = content_tag('div', field_html + profile_field_privacy_selector(profile, name), :class => 'field-with-privacy-selector')
881 end 857 end
882 end 858 end
883 859
@@ -892,6 +868,11 @@ module ApplicationHelper @@ -892,6 +868,11 @@ module ApplicationHelper
892 result 868 result
893 end 869 end
894 870
  871 + def profile_field_privacy_selector(profile, name)
  872 + return '' unless profile.public?
  873 + content_tag('div', labelled_check_box(_('Public'), 'profile_data[fields_privacy]['+name+']', 'public', profile.public_fields.include?(name)), :class => 'field-privacy-selector')
  874 + end
  875 +
895 def template_stylesheet_path 876 def template_stylesheet_path
896 if profile.nil? 877 if profile.nil?
897 "/designs/templates/#{environment.layout_template}/stylesheets/style.css" 878 "/designs/templates/#{environment.layout_template}/stylesheets/style.css"
app/helpers/article_helper.rb
@@ -2,14 +2,19 @@ module ArticleHelper @@ -2,14 +2,19 @@ module ArticleHelper
2 2
3 def custom_options_for_article(article) 3 def custom_options_for_article(article)
4 @article = article 4 @article = article
  5 + content_tag('h4', _('Visibility')) +
  6 + content_tag('div',
  7 + content_tag('div',
  8 + radio_button(:article, :published, true) +
  9 + content_tag('label', _('Public (visible to other people)'), :for => 'article_published_true')
  10 + ) +
  11 + content_tag('div',
  12 + radio_button(:article, :published, false) +
  13 + content_tag('label', _('Private'), :for => 'article_published_false')
  14 + )
  15 + ) +
5 content_tag('h4', _('Options')) + 16 content_tag('h4', _('Options')) +
6 content_tag('div', 17 content_tag('div',
7 - content_tag(  
8 - 'div',  
9 - check_box(:article, :published) +  
10 - content_tag('label', _('This article must be published (visible to other people)'), :for => 'article_published')  
11 - ) +  
12 -  
13 (article.profile.has_members? ? 18 (article.profile.has_members? ?
14 content_tag( 19 content_tag(
15 'div', 20 'div',
app/helpers/boxes_helper.rb
@@ -66,7 +66,7 @@ module BoxesHelper @@ -66,7 +66,7 @@ module BoxesHelper
66 66
67 def display_box_content(box, main_content) 67 def display_box_content(box, main_content)
68 context = { :article => @page, :request_path => request.path, :locale => locale } 68 context = { :article => @page, :request_path => request.path, :locale => locale }
69 - box_decorator.select_blocks(box.blocks, context).map { |item| display_block(item, main_content) }.join("\n") + box_decorator.block_target(box) 69 + box_decorator.select_blocks(box.blocks.includes(:box), context).map { |item| display_block(item, main_content) }.join("\n") + box_decorator.block_target(box)
70 end 70 end
71 71
72 def select_blocks(arr, context) 72 def select_blocks(arr, context)
@@ -162,9 +162,6 @@ module BoxesHelper @@ -162,9 +162,6 @@ module BoxesHelper
162 # 162 #
163 # +box+ is always needed 163 # +box+ is always needed
164 def block_target(box, block = nil) 164 def block_target(box, block = nil)
165 - # FIXME hardcoded  
166 - return '' if box.position == 1  
167 -  
168 id = 165 id =
169 if block.nil? 166 if block.nil?
170 "end-of-box-#{box.id}" 167 "end-of-box-#{box.id}"
@@ -172,14 +169,11 @@ module BoxesHelper @@ -172,14 +169,11 @@ module BoxesHelper
172 "before-block-#{block.id}" 169 "before-block-#{block.id}"
173 end 170 end
174 171
175 - content_tag('div', ' ', :id => id, :class => 'block-target' ) + drop_receiving_element(id, :url => { :action => 'move_block', :target => id }, :accept => 'block', :hoverclass => 'block-target-hover') 172 + content_tag('div', ' ', :id => id, :class => 'block-target' ) + drop_receiving_element(id, :url => { :action => 'move_block', :target => id }, :accept => box.acceptable_blocks, :hoverclass => 'block-target-hover')
176 end 173 end
177 174
178 # makes the given block draggable so it can be moved away. 175 # makes the given block draggable so it can be moved away.
179 def block_handle(block) 176 def block_handle(block)
180 - # FIXME hardcoded  
181 - return '' if block.box.position == 1  
182 -  
183 draggable_element("block-#{block.id}", :revert => true) 177 draggable_element("block-#{block.id}", :revert => true)
184 end 178 end
185 179
@@ -211,7 +205,7 @@ module BoxesHelper @@ -211,7 +205,7 @@ module BoxesHelper
211 end 205 end
212 206
213 if block.editable? 207 if block.editable?
214 - buttons << lightbox_icon_button(:edit, _('Edit'), { :action => 'edit', :id => block.id }) 208 + buttons << colorbox_icon_button(:edit, _('Edit'), { :action => 'edit', :id => block.id })
215 end 209 end
216 210
217 if !block.main? 211 if !block.main?
app/helpers/colorbox_helper.rb
@@ -8,6 +8,10 @@ module ColorboxHelper @@ -8,6 +8,10 @@ module ColorboxHelper
8 button(type, label, url, colorbox_options(options)) 8 button(type, label, url, colorbox_options(options))
9 end 9 end
10 10
  11 + def colorbox_icon_button(type, label, url, options = {})
  12 + icon_button(type, label, url, colorbox_options(options))
  13 + end
  14 +
11 # options must be an HTML options hash as passed to link_to etc. 15 # options must be an HTML options hash as passed to link_to etc.
12 # 16 #
13 # returns a new hash with colorbox class added. Keeps existing classes. 17 # returns a new hash with colorbox class added. Keeps existing classes.
app/helpers/content_viewer_helper.rb
@@ -42,7 +42,7 @@ module ContentViewerHelper @@ -42,7 +42,7 @@ module ContentViewerHelper
42 def article_translations(article) 42 def article_translations(article)
43 unless article.native_translation.translations.empty? 43 unless article.native_translation.translations.empty?
44 links = (article.native_translation.translations + [article.native_translation]).map do |translation| 44 links = (article.native_translation.translations + [article.native_translation]).map do |translation|
45 - { Noosfero.locales[translation.language] => { :href => url_for(translation.url) } } 45 + { article.environment.locales[translation.language] => { :href => url_for(translation.url) } }
46 end 46 end
47 content_tag(:div, link_to(_('Translations'), '#', 47 content_tag(:div, link_to(_('Translations'), '#',
48 :onclick => "toggleSubmenu(this, '#{_('Translations')}', #{links.to_json}); return false", 48 :onclick => "toggleSubmenu(this, '#{_('Translations')}', #{links.to_json}); return false",
app/helpers/display_helper.rb
@@ -8,6 +8,14 @@ module DisplayHelper @@ -8,6 +8,14 @@ module DisplayHelper
8 opts 8 opts
9 end 9 end
10 10
  11 + def themed_path(file)
  12 + if File.exists?(File.join(Rails.root, 'public', theme_path, file))
  13 + File.join(theme_path, file)
  14 + else
  15 + file
  16 + end
  17 + end
  18 +
11 def image_link_to_product(product, opts={}) 19 def image_link_to_product(product, opts={})
12 return _('No product') unless product 20 return _('No product') unless product
13 target = product_path(product) 21 target = product_path(product)
app/helpers/folder_helper.rb
@@ -58,18 +58,18 @@ module FolderHelper @@ -58,18 +58,18 @@ module FolderHelper
58 58
59 def custom_options_for_article(article) 59 def custom_options_for_article(article)
60 @article = article 60 @article = article
61 - content_tag('h4', _('Options')) + 61 + content_tag('h4', _('Visibility')) +
  62 + content_tag('div',
  63 + content_tag('div',
  64 + radio_button(:article, :published, true) +
  65 + content_tag('label', _('Public (visible to other people)'), :for => 'article_published_true')
  66 + ) +
  67 + content_tag('div',
  68 + radio_button(:article, :published, false) +
  69 + content_tag('label', _('Private'), :for => 'article_published_false')
  70 + )
  71 + ) +
62 content_tag('div', 72 content_tag('div',
63 - content_tag(  
64 - 'div',  
65 - check_box(:article, :published) +  
66 - content_tag('label', _('This article must be published (visible to other people)'), :for => 'article_published')  
67 - ) + (article.can_display_hits? ?  
68 - content_tag(  
69 - 'div',  
70 - check_box(:article, :display_hits) +  
71 - content_tag('label', _('I want this article to display the number of hits it received'), :for => 'article_display_hits')  
72 - ) : '') +  
73 hidden_field_tag('article[accept_comments]', 0) 73 hidden_field_tag('article[accept_comments]', 0)
74 ) 74 )
75 end 75 end
app/helpers/forms_helper.rb
@@ -123,6 +123,170 @@ module FormsHelper @@ -123,6 +123,170 @@ module FormsHelper
123 options_for_select.join("\n") 123 options_for_select.join("\n")
124 end 124 end
125 125
  126 + def balanced_table(items, per_row=3)
  127 + items = items.map {|item| content_tag('td', item, :style => 'border: none; background: transparent;')}
  128 + rows = []
  129 + row = []
  130 + counter = 0
  131 + items.each do |item|
  132 + counter += 1
  133 + row << item
  134 + if counter % per_row == 0
  135 + rows << content_tag('tr', row.join("\n"))
  136 + counter = 0
  137 + row = []
  138 + end
  139 + end
  140 + rows << content_tag('tr', row.join("\n"))
  141 +
  142 + content_tag('table',rows.join("\n"))
  143 + end
  144 +
  145 + def date_field(name, value, format = '%Y-%m-%d', datepicker_options = {}, html_options = {})
  146 + datepicker_options[:disabled] ||= false
  147 + datepicker_options[:alt_field] ||= ''
  148 + datepicker_options[:alt_format] ||= ''
  149 + datepicker_options[:append_text] ||= ''
  150 + datepicker_options[:auto_size] ||= false
  151 + datepicker_options[:button_image] ||= ''
  152 + datepicker_options[:button_image_only] ||= false
  153 + datepicker_options[:button_text] ||= '...'
  154 + datepicker_options[:calculate_week] ||= 'jQuery.datepicker.iso8601Week'
  155 + datepicker_options[:change_month] ||= false
  156 + datepicker_options[:change_year] ||= false
  157 + datepicker_options[:close_text] ||= _('Done')
  158 + datepicker_options[:constrain_input] ||= true
  159 + datepicker_options[:current_text] ||= _('Today')
  160 + datepicker_options[:date_format] ||= 'mm/dd/yy'
  161 + datepicker_options[:day_names] ||= [_('Sunday'), _('Monday'), _('Tuesday'), _('Wednesday'), _('Thursday'), _('Friday'), _('Saturday')]
  162 + datepicker_options[:day_names_min] ||= [_('Su'), _('Mo'), _('Tu'), _('We'), _('Th'), _('Fr'), _('Sa')]
  163 + datepicker_options[:day_names_short] ||= [_('Sun'), _('Mon'), _('Tue'), _('Wed'), _('Thu'), _('Fri'), _('Sat')]
  164 + datepicker_options[:default_date] ||= nil
  165 + datepicker_options[:duration] ||= 'normal'
  166 + datepicker_options[:first_day] ||= 0
  167 + datepicker_options[:goto_current] ||= false
  168 + datepicker_options[:hide_if_no_prev_next] ||= false
  169 + datepicker_options[:is_rtl] ||= false
  170 + datepicker_options[:max_date] ||= nil
  171 + datepicker_options[:min_date] ||= nil
  172 + datepicker_options[:month_names] ||= [_('January'), _('February'), _('March'), _('April'), _('May'), _('June'), _('July'), _('August'), _('September'), _('October'), _('November'), _('December')]
  173 + datepicker_options[:month_names_short] ||= [_('Jan'), _('Feb'), _('Mar'), _('Apr'), _('May'), _('Jun'), _('Jul'), _('Aug'), _('Sep'), _('Oct'), _('Nov'), _('Dec')]
  174 + datepicker_options[:navigation_as_date_format] ||= false
  175 + datepicker_options[:next_text] ||= _('Next')
  176 + datepicker_options[:number_of_months] ||= 1
  177 + datepicker_options[:prev_text] ||= _('Prev')
  178 + datepicker_options[:select_other_months] ||= false
  179 + datepicker_options[:short_year_cutoff] ||= '+10'
  180 + datepicker_options[:show_button_panel] ||= false
  181 + datepicker_options[:show_current_at_pos] ||= 0
  182 + datepicker_options[:show_month_after_year] ||= false
  183 + datepicker_options[:show_on] ||= 'focus'
  184 + datepicker_options[:show_options] ||= {}
  185 + datepicker_options[:show_other_months] ||= false
  186 + datepicker_options[:show_week] ||= false
  187 + datepicker_options[:step_months] ||= 1
  188 + datepicker_options[:week_header] ||= _('Wk')
  189 + datepicker_options[:year_range] ||= 'c-10:c+10'
  190 + datepicker_options[:year_suffix] ||= ''
  191 +
  192 + element_id = html_options[:id] || 'datepicker-date'
  193 + value = value.strftime(format) if value.present?
  194 + method = datepicker_options[:time] ? 'datetimepicker' : 'datepicker'
  195 + result = text_field_tag(name, value, html_options)
  196 + result +=
  197 + "
  198 + <script type='text/javascript'>
  199 + jQuery('##{element_id}').#{method}({
  200 + disabled: #{datepicker_options[:disabled].to_json},
  201 + altField: #{datepicker_options[:alt_field].to_json},
  202 + altFormat: #{datepicker_options[:alt_format].to_json},
  203 + appendText: #{datepicker_options[:append_text].to_json},
  204 + autoSize: #{datepicker_options[:auto_size].to_json},
  205 + buttonImage: #{datepicker_options[:button_image].to_json},
  206 + buttonImageOnly: #{datepicker_options[:button_image_only].to_json},
  207 + buttonText: #{datepicker_options[:button_text].to_json},
  208 + calculateWeek: #{datepicker_options[:calculate_week].to_json},
  209 + changeMonth: #{datepicker_options[:change_month].to_json},
  210 + changeYear: #{datepicker_options[:change_year].to_json},
  211 + closeText: #{datepicker_options[:close_text].to_json},
  212 + constrainInput: #{datepicker_options[:constrain_input].to_json},
  213 + currentText: #{datepicker_options[:current_text].to_json},
  214 + dateFormat: #{datepicker_options[:date_format].to_json},
  215 + dayNames: #{datepicker_options[:day_names].to_json},
  216 + dayNamesMin: #{datepicker_options[:day_names_min].to_json},
  217 + dayNamesShort: #{datepicker_options[:day_names_short].to_json},
  218 + defaultDate: #{datepicker_options[:default_date].to_json},
  219 + duration: #{datepicker_options[:duration].to_json},
  220 + firstDay: #{datepicker_options[:first_day].to_json},
  221 + gotoCurrent: #{datepicker_options[:goto_current].to_json},
  222 + hideIfNoPrevNext: #{datepicker_options[:hide_if_no_prev_next].to_json},
  223 + isRTL: #{datepicker_options[:is_rtl].to_json},
  224 + maxDate: #{datepicker_options[:max_date].to_json},
  225 + minDate: #{datepicker_options[:min_date].to_json},
  226 + monthNames: #{datepicker_options[:month_names].to_json},
  227 + monthNamesShort: #{datepicker_options[:month_names_short].to_json},
  228 + navigationAsDateFormat: #{datepicker_options[:navigation_as_date_format].to_json},
  229 + nextText: #{datepicker_options[:next_text].to_json},
  230 + numberOfMonths: #{datepicker_options[:number_of_months].to_json},
  231 + prevText: #{datepicker_options[:prev_text].to_json},
  232 + selectOtherMonths: #{datepicker_options[:select_other_months].to_json},
  233 + shortYearCutoff: #{datepicker_options[:short_year_cutoff].to_json},
  234 + showButtonPanel: #{datepicker_options[:show_button_panel].to_json},
  235 + showCurrentAtPos: #{datepicker_options[:show_current_at_pos].to_json},
  236 + showMonthAfterYear: #{datepicker_options[:show_month_after_year].to_json},
  237 + showOn: #{datepicker_options[:show_on].to_json},
  238 + showOptions: #{datepicker_options[:show_options].to_json},
  239 + showOtherMonths: #{datepicker_options[:show_other_months].to_json},
  240 + showWeek: #{datepicker_options[:show_week].to_json},
  241 + stepMonths: #{datepicker_options[:step_months].to_json},
  242 + weekHeader: #{datepicker_options[:week_header].to_json},
  243 + yearRange: #{datepicker_options[:year_range].to_json},
  244 + yearSuffix: #{datepicker_options[:year_suffix].to_json}
  245 + })
  246 + </script>
  247 + "
  248 + result
  249 + end
  250 +
  251 + def date_range_field(from_name, to_name, from_value, to_value, format = '%Y-%m-%d', datepicker_options = {}, html_options = {})
  252 + from_id = html_options[:from_id] || 'datepicker-from-date'
  253 + to_id = html_options[:to_id] || 'datepicker-to-date'
  254 + return _('From') +' '+ date_field(from_name, from_value, format, datepicker_options, html_options.merge({:id => from_id})) +
  255 + ' ' + _('until') +' '+ date_field(to_name, to_value, format, datepicker_options, html_options.merge({:id => to_id}))
  256 + end
  257 +
  258 + def select_folder(label_text, field_id, collection, default_value=nil, html_options = {}, js_options = {})
  259 + root = profile ? profile.identifier : _("root")
  260 + labelled_form_field(
  261 + label_text,
  262 + select_tag(
  263 + field_id,
  264 + options_for_select(
  265 + [[root, '']] +
  266 + collection.collect {|f| [ root + '/' + f.full_name, f.id ] },
  267 + default_value
  268 + ),
  269 + html_options.merge(js_options)
  270 + )
  271 + )
  272 + end
  273 +
  274 + def select_profile_folder(label_text, field_id, profile, default_value='', html_options = {}, js_options = {})
  275 + result = labelled_form_field(
  276 + label_text,
  277 + select_tag(
  278 + field_id,
  279 + options_for_select(
  280 + [[profile.identifier, '']] +
  281 + profile.folders.collect {|f| [ profile.identifier + '/' + f.full_name, f.id ] },
  282 + default_value
  283 + ),
  284 + html_options.merge(js_options)
  285 + )
  286 + )
  287 + return result
  288 + end
  289 +
126 protected 290 protected
127 def self.next_id_number 291 def self.next_id_number
128 if defined? @@id_num 292 if defined? @@id_num
app/helpers/language_helper.rb
@@ -13,18 +13,19 @@ module LanguageHelper @@ -13,18 +13,19 @@ module LanguageHelper
13 13
14 alias :calendar_date_select_language :tinymce_language 14 alias :calendar_date_select_language :tinymce_language
15 15
16 - def language_chooser(options = {}) 16 + def language_chooser(environment, options = {})
  17 + return if environment.locales.size < 2
17 current = language 18 current = language
18 separator = options[:separator] || ' &mdash; ' 19 separator = options[:separator] || ' &mdash; '
19 20
20 if options[:element] == 'dropdown' 21 if options[:element] == 'dropdown'
21 select_tag('lang', 22 select_tag('lang',
22 - options_for_select(Noosfero.locales.map{|code,name| [name, code]}, current), 23 + options_for_select(environment.locales.map{|code,name| [name, code]}, current),
23 :onchange => "document.location.href= #{url_for(params.merge(:lang => 'LANGUAGE')).inspect}.replace(/LANGUAGE/, this.value) ;", 24 :onchange => "document.location.href= #{url_for(params.merge(:lang => 'LANGUAGE')).inspect}.replace(/LANGUAGE/, this.value) ;",
24 :help => _('The language you choose here is the language used for options, buttons, etc. It does not affect the language of the content created by other users.') 25 :help => _('The language you choose here is the language used for options, buttons, etc. It does not affect the language of the content created by other users.')
25 ) 26 )
26 else 27 else
27 - languages = Noosfero.locales.map do |code,name| 28 + languages = environment.locales.map do |code,name|
28 if code == current 29 if code == current
29 content_tag('strong', name) 30 content_tag('strong', name)
30 else 31 else
app/helpers/profile_editor_helper.rb
@@ -145,4 +145,12 @@ module ProfileEditorHelper @@ -145,4 +145,12 @@ module ProfileEditorHelper
145 link_to title, url, :class => 'control-panel-%s' % icon 145 link_to title, url, :class => 'control-panel-%s' % icon
146 end 146 end
147 147
  148 + def unchangeable_privacy_field(profile)
  149 + if profile.public?
  150 + labelled_check_box(_('Public'), '', '', true, :disabled => true, :title => _('This field must be public'), :class => 'disabled')
  151 + else
  152 + ''
  153 + end
  154 + end
  155 +
148 end 156 end
app/helpers/profile_helper.rb
1 module ProfileHelper 1 module ProfileHelper
2 2
3 def display_field(title, profile, field, force = false) 3 def display_field(title, profile, field, force = false)
4 - if !force && !profile.active_fields.include?(field.to_s) 4 + if (!force && !profile.active_fields.include?(field.to_s)) ||
  5 + (profile.active_fields.include?(field.to_s) && !profile.public_fields.include?(field.to_s) && (!user || (user != profile && !user.is_a_friend?(profile))))
5 return '' 6 return ''
6 end 7 end
7 value = profile.send(field) 8 value = profile.send(field)
@@ -15,4 +16,26 @@ module ProfileHelper @@ -15,4 +16,26 @@ module ProfileHelper
15 end 16 end
16 end 17 end
17 18
  19 + def display_contact(profile)
  20 + address = display_field(_('Address:'), profile, :address)
  21 + zip = display_field(_('ZIP code:'), profile, :zip_code)
  22 + phone = display_field(_('Contact phone:'), profile, :contact_phone)
  23 + email = display_field(_('e-Mail:'), profile, :email) { |email| link_to_email(email) }
  24 + if address.blank? && zip.blank? && phone.blank? && email.blank?
  25 + ''
  26 + else
  27 + content_tag('tr', content_tag('th', _('Contact'), { :colspan => 2 })) + address + zip + phone + email
  28 + end
  29 + end
  30 +
  31 + def display_work_info(profile)
  32 + organization = display_field(_('Organization:'), profile, :organization)
  33 + organization_site = display_field(_('Organization website:'), profile, :organization_website) { |url| link_to(url, url) }
  34 + if organization.blank? && organization_site.blank?
  35 + ''
  36 + else
  37 + content_tag('tr', content_tag('th', _('Work'), { :colspan => 2 })) + organization + organization_site
  38 + end
  39 + end
  40 +
18 end 41 end
app/models/article.rb
@@ -79,6 +79,25 @@ class Article &lt; ActiveRecord::Base @@ -79,6 +79,25 @@ class Article &lt; ActiveRecord::Base
79 validate :native_translation_must_have_language 79 validate :native_translation_must_have_language
80 validate :translation_must_have_language 80 validate :translation_must_have_language
81 81
  82 + validate :no_self_reference
  83 + validate :no_cyclical_reference, :if => 'parent_id.present?'
  84 +
  85 + def no_self_reference
  86 + errors.add(:parent_id, _('self-reference is not allowed.')) if id && parent_id == id
  87 + end
  88 +
  89 + def no_cyclical_reference
  90 + current_parent = Article.find(parent_id)
  91 + while current_parent
  92 + if current_parent == self
  93 + errors.add(:parent_id, _('cyclical reference is not allowed.'))
  94 + break
  95 + end
  96 + current_parent = current_parent.parent
  97 + end
  98 + end
  99 +
  100 +
82 def is_trackable? 101 def is_trackable?
83 self.published? && self.notifiable? && self.advertise? && self.profile.public_profile 102 self.published? && self.notifiable? && self.advertise? && self.profile.public_profile
84 end 103 end
@@ -154,6 +173,12 @@ class Article &lt; ActiveRecord::Base @@ -154,6 +173,12 @@ class Article &lt; ActiveRecord::Base
154 article.advertise = true 173 article.advertise = true
155 end 174 end
156 175
  176 + before_save do |article|
  177 + article.parent = article.parent_id ? Article.find(article.parent_id) : nil
  178 + parent_path = article.parent ? article.parent.path : nil
  179 + article.path = [parent_path, article.slug].compact.join('/')
  180 + end
  181 +
157 # retrieves all articles belonging to the given +profile+ that are not 182 # retrieves all articles belonging to the given +profile+ that are not
158 # sub-articles of any other article. 183 # sub-articles of any other article.
159 named_scope :top_level_for, lambda { |profile| 184 named_scope :top_level_for, lambda { |profile|
@@ -179,37 +204,23 @@ class Article &lt; ActiveRecord::Base @@ -179,37 +204,23 @@ class Article &lt; ActiveRecord::Base
179 end 204 end
180 205
181 named_scope :more_popular, :order => 'hits DESC' 206 named_scope :more_popular, :order => 'hits DESC'
182 -  
183 - # retrieves the latest +limit+ articles, sorted from the most recent to the  
184 - # oldest.  
185 - #  
186 - # Only includes articles where advertise == true  
187 - def self.recent(limit = nil, extra_conditions = {})  
188 - # FIXME this method is a horrible hack  
189 - options = { :page => 1, :per_page => limit,  
190 - :conditions => [  
191 - "advertise = ? AND  
192 - published = ? AND  
193 - profiles.visible = ? AND  
194 - profiles.public_profile = ? AND  
195 - ((articles.type != ? and articles.type != ? and articles.type != ?) OR articles.type is NULL)", true, true, true, true, 'UploadedFile', 'RssFeed', 'Blog'  
196 - ],  
197 - :include => 'profile',  
198 - :order => 'articles.published_at desc, articles.id desc'  
199 - }  
200 - if ( scoped_methods && scoped_methods.last &&  
201 - scoped_methods.last[:find] &&  
202 - scoped_methods.last[:find][:joins] &&  
203 - scoped_methods.last[:find][:joins].index('profiles') )  
204 - options.delete(:include)  
205 - end  
206 - if extra_conditions == {}  
207 - self.paginate(options)  
208 - else  
209 - with_scope :find => {:conditions => extra_conditions} do  
210 - self.paginate(options)  
211 - end 207 + named_scope :relevant_as_recent, :conditions => ["(articles.type != 'UploadedFile' and articles.type != 'RssFeed' and articles.type != 'Blog') OR articles.type is NULL"]
  208 +
  209 + def self.recent(limit = nil, extra_conditions = {}, pagination = true)
  210 + result = scoped({:conditions => extra_conditions}).
  211 + public.
  212 + relevant_as_recent.
  213 + limit(limit).
  214 + order(['articles.published_at desc', 'articles.id desc'])
  215 +
  216 + if !( scoped_methods && scoped_methods.last &&
  217 + scoped_methods.last[:find] &&
  218 + scoped_methods.last[:find][:joins] &&
  219 + scoped_methods.last[:find][:joins].index('profiles') )
  220 + result = result.includes(:profile)
212 end 221 end
  222 +
  223 + pagination ? result.paginate({:page => 1, :per_page => limit}) : result
213 end 224 end
214 225
215 # produces the HTML code that is to be displayed as this article's contents. 226 # produces the HTML code that is to be displayed as this article's contents.
@@ -325,14 +336,14 @@ class Article &lt; ActiveRecord::Base @@ -325,14 +336,14 @@ class Article &lt; ActiveRecord::Base
325 end 336 end
326 337
327 def possible_translations 338 def possible_translations
328 - possibilities = Noosfero.locales.keys - self.native_translation.translations(:select => :language).map(&:language) - [self.native_translation.language] 339 + possibilities = environment.locales.keys - self.native_translation.translations(:select => :language).map(&:language) - [self.native_translation.language]
329 possibilities << self.language unless self.language_changed? 340 possibilities << self.language unless self.language_changed?
330 possibilities 341 possibilities
331 end 342 end
332 343
333 def known_language 344 def known_language
334 unless self.language.blank? 345 unless self.language.blank?
335 - errors.add(:language, N_('Language not supported by Noosfero')) unless Noosfero.locales.key?(self.language) 346 + errors.add(:language, N_('Language not supported by the environment.')) unless environment.locales.key?(self.language)
336 end 347 end
337 end 348 end
338 349
@@ -665,7 +676,7 @@ class Article &lt; ActiveRecord::Base @@ -665,7 +676,7 @@ class Article &lt; ActiveRecord::Base
665 self.categories.collect(&:name) 676 self.categories.collect(&:name)
666 end 677 end
667 678
668 - delegate :region, :region_id, :environment, :environment_id, :to => :profile 679 + delegate :region, :region_id, :environment, :environment_id, :to => :profile, :allow_nil => true
669 def name_sortable # give a different name for solr 680 def name_sortable # give a different name for solr
670 name 681 name
671 end 682 end
app/models/block.rb
@@ -7,6 +7,8 @@ class Block &lt; ActiveRecord::Base @@ -7,6 +7,8 @@ class Block &lt; ActiveRecord::Base
7 # Block-specific stuff 7 # Block-specific stuff
8 include BlockHelper 8 include BlockHelper
9 9
  10 + delegate :environment, :to => :box, :allow_nil => true
  11 +
10 acts_as_list :scope => :box 12 acts_as_list :scope => :box
11 belongs_to :box 13 belongs_to :box
12 14
app/models/box.rb
@@ -2,4 +2,80 @@ class Box &lt; ActiveRecord::Base @@ -2,4 +2,80 @@ class Box &lt; ActiveRecord::Base
2 belongs_to :owner, :polymorphic => true 2 belongs_to :owner, :polymorphic => true
3 acts_as_list :scope => 'owner_id = #{owner_id} and owner_type = \'#{owner_type}\'' 3 acts_as_list :scope => 'owner_id = #{owner_id} and owner_type = \'#{owner_type}\''
4 has_many :blocks, :dependent => :destroy, :order => 'position' 4 has_many :blocks, :dependent => :destroy, :order => 'position'
  5 +
  6 + def environment
  7 + owner ? (owner.kind_of?(Environment) ? owner : owner.environment) : nil
  8 + end
  9 +
  10 + def acceptable_blocks
  11 + to_css_class_name central? ? Box.acceptable_center_blocks : Box.acceptable_side_blocks
  12 + end
  13 +
  14 + def central?
  15 + position == 1
  16 + end
  17 +
  18 + def self.acceptable_center_blocks
  19 + [ ArticleBlock,
  20 + BlogArchivesBlock,
  21 + CategoriesBlock,
  22 + CommunitiesBlock,
  23 + EnterprisesBlock,
  24 + EnvironmentStatisticsBlock,
  25 + FansBlock,
  26 + FavoriteEnterprisesBlock,
  27 + FeedReaderBlock,
  28 + FriendsBlock,
  29 + HighlightsBlock,
  30 + LinkListBlock,
  31 + LoginBlock,
  32 + MainBlock,
  33 + MembersBlock,
  34 + MyNetworkBlock,
  35 + PeopleBlock,
  36 + ProfileImageBlock,
  37 + RawHTMLBlock,
  38 + RecentDocumentsBlock,
  39 + SellersSearchBlock,
  40 + TagsBlock ]
  41 + end
  42 +
  43 + def self.acceptable_side_blocks
  44 + [ ArticleBlock,
  45 + BlogArchivesBlock,
  46 + CategoriesBlock,
  47 + CommunitiesBlock,
  48 + DisabledEnterpriseMessageBlock,
  49 + EnterprisesBlock,
  50 + EnvironmentStatisticsBlock,
  51 + FansBlock,
  52 + FavoriteEnterprisesBlock,
  53 + FeaturedProductsBlock,
  54 + FeedReaderBlock,
  55 + FriendsBlock,
  56 + HighlightsBlock,
  57 + LinkListBlock,
  58 + LocationBlock,
  59 + LoginBlock,
  60 + MembersBlock,
  61 + MyNetworkBlock,
  62 + PeopleBlock,
  63 + ProductsBlock,
  64 + ProfileImageBlock,
  65 + ProfileInfoBlock,
  66 + ProfileSearchBlock,
  67 + RawHTMLBlock,
  68 + RecentDocumentsBlock,
  69 + SellersSearchBlock,
  70 + SlideshowBlock,
  71 + TagsBlock
  72 + ]
  73 + end
  74 +
  75 + private
  76 +
  77 + def to_css_class_name(blocks)
  78 + blocks.map{ |block| block.to_s.underscore.tr('_', '-') }
  79 + end
  80 +
5 end 81 end
app/models/change_password.rb
@@ -7,7 +7,7 @@ class ChangePassword &lt; Task @@ -7,7 +7,7 @@ class ChangePassword &lt; Task
7 when :login: 7 when :login:
8 _('Username') 8 _('Username')
9 when :email 9 when :email
10 - _('e-Mail') 10 + _('e-mail')
11 when :password 11 when :password
12 _('Password') 12 _('Password')
13 when :password_confirmation 13 when :password_confirmation
@@ -20,9 +20,7 @@ class ChangePassword &lt; Task @@ -20,9 +20,7 @@ class ChangePassword &lt; Task
20 ################################################### 20 ###################################################
21 # validations for creating a ChangePassword task 21 # validations for creating a ChangePassword task
22 22
23 - validates_presence_of :login, :email, :environment_id, :on => :create  
24 -  
25 - validates_presence_of :requestor_id 23 + validates_presence_of :login, :email, :environment_id, :on => :create, :message => _('must be filled in')
26 24
27 validates_format_of :email, :on => :create, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda { |obj| !obj.email.blank? }) 25 validates_format_of :email, :on => :create, :with => Noosfero::Constants::EMAIL_FORMAT, :if => (lambda { |obj| !obj.email.blank? })
28 26
@@ -30,10 +28,10 @@ class ChangePassword &lt; Task @@ -30,10 +28,10 @@ class ChangePassword &lt; Task
30 unless data.login.blank? || data.email.blank? 28 unless data.login.blank? || data.email.blank?
31 user = User.find_by_login_and_environment_id(data.login, data.environment_id) 29 user = User.find_by_login_and_environment_id(data.login, data.environment_id)
32 if user.nil? 30 if user.nil?
33 - data.errors.add(:login, _('%{fn} is not a valid username.').fix_i18n) 31 + data.errors.add(:login, _('is invalid or user does not exists.'))
34 else 32 else
35 if user.email != data.email 33 if user.email != data.email
36 - data.errors.add(:email) 34 + data.errors.add(:email, _('does not match the username you filled in'))
37 end 35 end
38 end 36 end
39 end 37 end
app/models/enterprise.rb
@@ -17,7 +17,7 @@ class Enterprise &lt; Organization @@ -17,7 +17,7 @@ class Enterprise &lt; Organization
17 after_save_reindex [:products], :with => :delayed_job 17 after_save_reindex [:products], :with => :delayed_job
18 extra_data_for_index :product_categories 18 extra_data_for_index :product_categories
19 def product_categories 19 def product_categories
20 - products.map{|p| p.category_full_name}.compact 20 + products.includes(:product_category).map{|p| p.category_full_name}.compact
21 end 21 end
22 22
23 N_('Organization website'); N_('Historic and current context'); N_('Activities short description'); N_('City'); N_('State'); N_('Country'); N_('ZIP code') 23 N_('Organization website'); N_('Historic and current context'); N_('Activities short description'); N_('City'); N_('State'); N_('Country'); N_('ZIP code')
app/models/environment.rb
@@ -123,10 +123,23 @@ class Environment &lt; ActiveRecord::Base @@ -123,10 +123,23 @@ class Environment &lt; ActiveRecord::Base
123 'xmpp_chat' => _('XMPP/Jabber based chat'), 123 'xmpp_chat' => _('XMPP/Jabber based chat'),
124 'show_zoom_button_on_article_images' => _('Show a zoom link on all article images'), 124 'show_zoom_button_on_article_images' => _('Show a zoom link on all article images'),
125 'captcha_for_logged_users' => _('Ask captcha when a logged user comments too'), 125 'captcha_for_logged_users' => _('Ask captcha when a logged user comments too'),
126 - 'skip_new_user_email_confirmation' => _('Skip e-mail confirmation for new users') 126 + 'skip_new_user_email_confirmation' => _('Skip e-mail confirmation for new users'),
  127 + 'send_welcome_email_to_new_users' => _('Send welcome e-mail to new users'),
  128 + 'allow_change_of_redirection_after_login' => _('Allow users to set the page to redirect after login')
127 } 129 }
128 end 130 end
129 131
  132 + def self.login_redirection_options
  133 + {
  134 + 'keep_on_same_page' => _('Stays on the same page the user was before login.'),
  135 + 'site_homepage' => _('Redirects the user to the environment homepage.'),
  136 + 'user_profile_page' => _('Redirects the user to his profile page.'),
  137 + 'user_homepage' => _('Redirects the user to his homepage.'),
  138 + 'user_control_panel' => _('Redirects the user to his control panel.')
  139 + }
  140 + end
  141 + validates_inclusion_of :redirection_after_login, :in => Environment.login_redirection_options.keys, :allow_nil => true
  142 +
130 # ################################################# 143 # #################################################
131 # Relationships and applied behaviour 144 # Relationships and applied behaviour
132 # ################################################# 145 # #################################################
@@ -530,6 +543,31 @@ class Environment &lt; ActiveRecord::Base @@ -530,6 +543,31 @@ class Environment &lt; ActiveRecord::Base
530 signup_fields 543 signup_fields
531 end 544 end
532 545
  546 + serialize :signup_welcome_text, Hash
  547 + def signup_welcome_text
  548 + self[:signup_welcome_text] ||= {}
  549 + end
  550 +
  551 + def signup_welcome_text_subject
  552 + self.signup_welcome_text[:subject]
  553 + end
  554 +
  555 + def signup_welcome_text_subject=(subject)
  556 + self.signup_welcome_text[:subject] = subject
  557 + end
  558 +
  559 + def signup_welcome_text_body
  560 + self.signup_welcome_text[:body]
  561 + end
  562 +
  563 + def signup_welcome_text_body=(body)
  564 + self.signup_welcome_text[:body] = body
  565 + end
  566 +
  567 + def has_signup_welcome_text?
  568 + signup_welcome_text && !signup_welcome_text_body.blank?
  569 + end
  570 +
533 # ################################################# 571 # #################################################
534 # Validations 572 # Validations
535 # ################################################# 573 # #################################################
@@ -591,8 +629,8 @@ class Environment &lt; ActiveRecord::Base @@ -591,8 +629,8 @@ class Environment &lt; ActiveRecord::Base
591 end 629 end
592 630
593 has_many :articles, :through => :profiles 631 has_many :articles, :through => :profiles
594 - def recent_documents(limit = 10)  
595 - self.articles.recent(limit) 632 + def recent_documents(limit = 10, options = {}, pagination = true)
  633 + self.articles.recent(limit, options, pagination)
596 end 634 end
597 635
598 has_many :events, :through => :profiles, :source => :articles, :class_name => 'Event' 636 has_many :events, :through => :profiles, :source => :articles, :class_name => 'Event'
@@ -766,4 +804,53 @@ class Environment &lt; ActiveRecord::Base @@ -766,4 +804,53 @@ class Environment &lt; ActiveRecord::Base
766 def image_galleries 804 def image_galleries
767 portal_community ? portal_community.image_galleries : [] 805 portal_community ? portal_community.image_galleries : []
768 end 806 end
  807 +
  808 + serialize :languages
  809 +
  810 + before_validation do |environment|
  811 + environment.default_language = nil if environment.default_language.blank?
  812 + end
  813 +
  814 + validate :default_language_available
  815 + validate :languages_available
  816 +
  817 + def locales
  818 + if languages.present?
  819 + languages.inject({}) {|r, l| r.merge({l => Noosfero.locales[l]})}
  820 + else
  821 + Noosfero.locales
  822 + end
  823 + end
  824 +
  825 + def default_locale
  826 + default_language || Noosfero.default_locale
  827 + end
  828 +
  829 + def available_locales
  830 + locales_list = locales.keys
  831 + # move English to the beginning
  832 + if locales_list.include?('en')
  833 + locales_list = ['en'] + (locales_list - ['en']).sort
  834 + end
  835 + locales_list
  836 + end
  837 +
  838 + private
  839 +
  840 + def default_language_available
  841 + if default_language.present? && !available_locales.include?(default_language)
  842 + errors.add(:default_language, _('is not available.'))
  843 + end
  844 + end
  845 +
  846 + def languages_available
  847 + if languages.present?
  848 + languages.each do |language|
  849 + if !Noosfero.available_locales.include?(language)
  850 + errors.add(:languages, _('have unsupported languages.'))
  851 + break
  852 + end
  853 + end
  854 + end
  855 + end
769 end 856 end
app/models/external_feed.rb
@@ -19,9 +19,15 @@ class ExternalFeed &lt; ActiveRecord::Base @@ -19,9 +19,15 @@ class ExternalFeed &lt; ActiveRecord::Base
19 article.valid? 19 article.valid?
20 end 20 end
21 21
  22 + def address=(new_address)
  23 + self.fetched_at = nil unless address == new_address
  24 + super(new_address)
  25 + end
  26 +
22 def clear 27 def clear
23 # do nothing 28 # do nothing
24 end 29 end
  30 +
25 def finish_fetch 31 def finish_fetch
26 if self.only_once && self.update_errors.zero? 32 if self.only_once && self.update_errors.zero?
27 self.enabled = false 33 self.enabled = false
app/models/person.rb
@@ -71,10 +71,7 @@ class Person &lt; Profile @@ -71,10 +71,7 @@ class Person &lt; Profile
71 Friendship.find(:all, :conditions => { :friend_id => person.id}).each { |friendship| friendship.destroy } 71 Friendship.find(:all, :conditions => { :friend_id => person.id}).each { |friendship| friendship.destroy }
72 end 72 end
73 73
74 - after_destroy :destroy_user  
75 - def destroy_user  
76 - self.user.destroy if self.user  
77 - end 74 + belongs_to :user, :dependent => :delete
78 75
79 def can_control_scrap?(scrap) 76 def can_control_scrap?(scrap)
80 begin 77 begin
@@ -183,7 +180,8 @@ class Person &lt; Profile @@ -183,7 +180,8 @@ class Person &lt; Profile
183 include MaybeAddHttp 180 include MaybeAddHttp
184 181
185 def active_fields 182 def active_fields
186 - environment ? environment.active_person_fields : [] 183 + fields = environment ? environment.active_person_fields : []
  184 + fields << 'email'
187 end 185 end
188 186
189 def required_fields 187 def required_fields
@@ -253,7 +251,7 @@ class Person &lt; Profile @@ -253,7 +251,7 @@ class Person &lt; Profile
253 251
254 def is_admin?(environment = nil) 252 def is_admin?(environment = nil)
255 environment ||= self.environment 253 environment ||= self.environment
256 - role_assignments.select { |ra| ra.resource == environment }.map{|ra|ra.role.permissions}.any? do |ps| 254 + role_assignments.includes([:role, :resource]).select { |ra| ra.resource == environment }.map{|ra|ra.role.permissions}.any? do |ps|
257 ps.any? do |p| 255 ps.any? do |p|
258 ActiveRecord::Base::PERMISSIONS['Environment'].keys.include?(p) 256 ActiveRecord::Base::PERMISSIONS['Environment'].keys.include?(p)
259 end 257 end
@@ -461,6 +459,10 @@ class Person &lt; Profile @@ -461,6 +459,10 @@ class Person &lt; Profile
461 Scrap.find_by_sql("SELECT id, updated_at, '#{Scrap.to_s}' AS klass FROM #{Scrap.table_name} WHERE scraps.receiver_id = #{self.id} AND scraps.scrap_id IS NULL UNION SELECT id, updated_at, '#{ActionTracker::Record.to_s}' AS klass FROM #{ActionTracker::Record.table_name} WHERE action_tracker.user_id = #{self.id} and action_tracker.verb != 'leave_scrap_to_self' and action_tracker.verb != 'add_member_in_community' ORDER BY updated_at DESC") 459 Scrap.find_by_sql("SELECT id, updated_at, '#{Scrap.to_s}' AS klass FROM #{Scrap.table_name} WHERE scraps.receiver_id = #{self.id} AND scraps.scrap_id IS NULL UNION SELECT id, updated_at, '#{ActionTracker::Record.to_s}' AS klass FROM #{ActionTracker::Record.table_name} WHERE action_tracker.user_id = #{self.id} and action_tracker.verb != 'leave_scrap_to_self' and action_tracker.verb != 'add_member_in_community' ORDER BY updated_at DESC")
462 end 460 end
463 461
  462 + def public_fields
  463 + self.fields_privacy.nil? ? self.active_fields : self.fields_privacy.reject{ |k, v| v != 'public' }.keys.map(&:to_s)
  464 + end
  465 +
464 protected 466 protected
465 467
466 def followed_by?(profile) 468 def followed_by?(profile)
app/models/profile.rb
@@ -57,6 +57,7 @@ class Profile &lt; ActiveRecord::Base @@ -57,6 +57,7 @@ class Profile &lt; ActiveRecord::Base
57 'view_private_content' => N_('View private content'), 57 'view_private_content' => N_('View private content'),
58 'publish_content' => N_('Publish content'), 58 'publish_content' => N_('Publish content'),
59 'invite_members' => N_('Invite members'), 59 'invite_members' => N_('Invite members'),
  60 + 'send_mail_to_members' => N_('Send e-Mail to members'),
60 } 61 }
61 62
62 acts_as_accessible 63 acts_as_accessible
@@ -147,6 +148,7 @@ class Profile &lt; ActiveRecord::Base @@ -147,6 +148,7 @@ class Profile &lt; ActiveRecord::Base
147 settings_items :redirect_l10n, :type => :boolean, :default => false 148 settings_items :redirect_l10n, :type => :boolean, :default => false
148 settings_items :public_content, :type => :boolean, :default => true 149 settings_items :public_content, :type => :boolean, :default => true
149 settings_items :description 150 settings_items :description
  151 + settings_items :fields_privacy, :type => :hash, :default => {}
150 152
151 validates_length_of :description, :maximum => 550, :allow_nil => true 153 validates_length_of :description, :maximum => 550, :allow_nil => true
152 154
@@ -402,8 +404,8 @@ class Profile &lt; ActiveRecord::Base @@ -402,8 +404,8 @@ class Profile &lt; ActiveRecord::Base
402 # 404 #
403 # +limit+ is the maximum number of documents to be returned. It defaults to 405 # +limit+ is the maximum number of documents to be returned. It defaults to
404 # 10. 406 # 10.
405 - def recent_documents(limit = 10, options = {})  
406 - self.articles.recent(limit, options) 407 + def recent_documents(limit = 10, options = {}, pagination = true)
  408 + self.articles.recent(limit, options, pagination)
407 end 409 end
408 410
409 def last_articles(limit = 10, options = {}) 411 def last_articles(limit = 10, options = {})
@@ -879,6 +881,15 @@ private :generate_url, :url_options @@ -879,6 +881,15 @@ private :generate_url, :url_options
879 [] 881 []
880 end 882 end
881 883
  884 + # field => privacy (e.g.: "address" => "public")
  885 + def fields_privacy
  886 + self.data[:fields_privacy]
  887 + end
  888 +
  889 + def public_fields
  890 + self.active_fields
  891 + end
  892 +
882 private 893 private
883 def self.f_categories_label_proc(environment) 894 def self.f_categories_label_proc(environment)
884 ids = environment.top_level_category_as_facet_ids 895 ids = environment.top_level_category_as_facet_ids
@@ -975,4 +986,8 @@ private :generate_url, :url_options @@ -975,4 +986,8 @@ private :generate_url, :url_options
975 end 986 end
976 end 987 end
977 988
  989 + validates_inclusion_of :redirection_after_login, :in => Environment.login_redirection_options.keys, :allow_nil => true
  990 + def preferred_login_redirection
  991 + redirection_after_login.blank? ? environment.redirection_after_login : redirection_after_login
  992 + end
978 end 993 end
app/models/profile_list_block.rb
@@ -14,12 +14,13 @@ class ProfileListBlock &lt; Block @@ -14,12 +14,13 @@ class ProfileListBlock &lt; Block
14 14
15 def profile_list 15 def profile_list
16 result = nil 16 result = nil
  17 + visible_profiles = profiles.visible.includes([:image,:domains,:preferred_domain,:environment])
17 if !prioritize_profiles_with_image 18 if !prioritize_profiles_with_image
18 - result = profiles.visible.all(:limit => limit, :order => 'updated_at DESC').sort_by{ rand }  
19 - elsif profiles.visible.with_image.count >= limit  
20 - result = profiles.visible.with_image.all(:limit => limit * 5, :order => 'updated_at DESC').sort_by{ rand } 19 + result = visible_profiles.all(:limit => limit, :order => 'updated_at DESC').sort_by{ rand }
  20 + elsif visible_profiles.with_image.count >= limit
  21 + result = visible_profiles.with_image.all(:limit => limit * 5, :order => 'updated_at DESC').sort_by{ rand }
21 else 22 else
22 - result = profiles.visible.with_image.sort_by{ rand } + profiles.visible.without_image.all(:limit => limit * 5, :order => 'updated_at DESC').sort_by{ rand } 23 + result = visible_profiles.with_image.sort_by{ rand } + visible_profiles.without_image.all(:limit => limit * 5, :order => 'updated_at DESC').sort_by{ rand }
23 end 24 end
24 result.slice(0..limit-1) 25 result.slice(0..limit-1)
25 end 26 end
app/models/recent_documents_block.rb
@@ -16,11 +16,9 @@ class RecentDocumentsBlock &lt; Block @@ -16,11 +16,9 @@ class RecentDocumentsBlock &lt; Block
16 16
17 include ActionController::UrlWriter 17 include ActionController::UrlWriter
18 def content(args={}) 18 def content(args={})
19 - docs = self.limit.nil? ? owner.recent_documents : owner.recent_documents(self.limit)  
20 - 19 + docs = self.limit.nil? ? owner.recent_documents(nil, {}, false) : owner.recent_documents(self.limit, {}, false)
21 block_title(title) + 20 block_title(title) +
22 content_tag('ul', docs.map {|item| content_tag('li', link_to(h(item.title), item.url))}.join("\n")) 21 content_tag('ul', docs.map {|item| content_tag('li', link_to(h(item.title), item.url))}.join("\n"))
23 -  
24 end 22 end
25 23
26 def footer 24 def footer
app/models/task.rb
@@ -31,7 +31,7 @@ class Task &lt; ActiveRecord::Base @@ -31,7 +31,7 @@ class Task &lt; ActiveRecord::Base
31 end 31 end
32 end 32 end
33 33
34 - belongs_to :requestor, :class_name => 'Person', :foreign_key => :requestor_id 34 + belongs_to :requestor, :class_name => 'Profile', :foreign_key => :requestor_id
35 belongs_to :target, :foreign_key => :target_id, :polymorphic => true 35 belongs_to :target, :foreign_key => :target_id, :polymorphic => true
36 36
37 validates_uniqueness_of :code, :on => :create 37 validates_uniqueness_of :code, :on => :create
app/models/user.rb
@@ -30,7 +30,7 @@ class User &lt; ActiveRecord::Base @@ -30,7 +30,7 @@ class User &lt; ActiveRecord::Base
30 30
31 after_create do |user| 31 after_create do |user|
32 user.person ||= Person.new 32 user.person ||= Person.new
33 - user.person.attributes = user.person_data.merge(:identifier => user.login, :user_id => user.id, :environment_id => user.environment_id) 33 + user.person.attributes = user.person_data.merge(:identifier => user.login, :user => user, :environment_id => user.environment_id)
34 user.person.name ||= user.login 34 user.person.name ||= user.login
35 user.person.visible = false unless user.activated? 35 user.person.visible = false unless user.activated?
36 user.person.save! 36 user.person.save!
@@ -73,6 +73,18 @@ class User &lt; ActiveRecord::Base @@ -73,6 +73,18 @@ class User &lt; ActiveRecord::Base
73 :environment => user.environment.name, 73 :environment => user.environment.name,
74 :url => user.environment.top_url 74 :url => user.environment.top_url
75 end 75 end
  76 +
  77 + def signup_welcome_email(user)
  78 + email_body = user.environment.signup_welcome_text_body.gsub('{user_name}', user.name)
  79 + email_subject = user.environment.signup_welcome_text_subject
  80 +
  81 + content_type 'text/html'
  82 + recipients user.email
  83 +
  84 + from "#{user.environment.name} <#{user.environment.contact_email}>"
  85 + subject email_subject.blank? ? _("Welcome to environment %s") % [user.environment.name] : email_subject
  86 + body email_body
  87 + end
76 end 88 end
77 89
78 def signup! 90 def signup!
@@ -88,13 +100,13 @@ class User &lt; ActiveRecord::Base @@ -88,13 +100,13 @@ class User &lt; ActiveRecord::Base
88 attr_protected :activated_at 100 attr_protected :activated_at
89 101
90 # Virtual attribute for the unencrypted password 102 # Virtual attribute for the unencrypted password
91 - attr_accessor :password 103 + attr_accessor :password, :name
92 104
93 validates_presence_of :login, :email 105 validates_presence_of :login, :email
94 validates_format_of :login, :with => Profile::IDENTIFIER_FORMAT, :if => (lambda {|user| !user.login.blank?}) 106 validates_format_of :login, :with => Profile::IDENTIFIER_FORMAT, :if => (lambda {|user| !user.login.blank?})
95 validates_presence_of :password, :if => :password_required? 107 validates_presence_of :password, :if => :password_required?
96 - validates_presence_of :password_confirmation, :if => :password_required?, :if => (lambda {|user| !user.password.blank?})  
97 - validates_length_of :password, :within => 4..40, :if => :password_required?, :if => (lambda {|user| !user.password.blank?}) 108 + validates_presence_of :password_confirmation, :if => :password_required?
  109 + validates_length_of :password, :within => 4..40, :if => :password_required?
98 validates_confirmation_of :password, :if => :password_required? 110 validates_confirmation_of :password, :if => :password_required?
99 validates_length_of :login, :within => 2..40, :if => (lambda {|user| !user.login.blank?}) 111 validates_length_of :login, :within => 2..40, :if => (lambda {|user| !user.login.blank?})
100 validates_length_of :email, :within => 3..100, :if => (lambda {|user| !user.email.blank?}) 112 validates_length_of :email, :within => 3..100, :if => (lambda {|user| !user.email.blank?})
@@ -117,7 +129,17 @@ class User &lt; ActiveRecord::Base @@ -117,7 +129,17 @@ class User &lt; ActiveRecord::Base
117 self.activated_at = Time.now.utc 129 self.activated_at = Time.now.utc
118 self.activation_code = nil 130 self.activation_code = nil
119 self.person.visible = true 131 self.person.visible = true
120 - self.person.save! && self.save! 132 + begin
  133 + self.person.save! && self.save!
  134 + rescue Exception => exception
  135 + logger.error(exception.to_s)
  136 + false
  137 + else
  138 + if environment.enabled?('send_welcome_email_to_new_users') && environment.has_signup_welcome_text?
  139 + User::Mailer.delay.deliver_signup_welcome_email(self)
  140 + end
  141 + true
  142 + end
121 end 143 end
122 144
123 def activated? 145 def activated?
@@ -228,7 +250,12 @@ class User &lt; ActiveRecord::Base @@ -228,7 +250,12 @@ class User &lt; ActiveRecord::Base
228 end 250 end
229 251
230 def name 252 def name
231 - person ? person.name : login 253 + name = (self[:name] || login)
  254 + person.nil? ? name : (person.name || name)
  255 + end
  256 +
  257 + def name= name
  258 + self[:name] = name
232 end 259 end
233 260
234 def enable_email! 261 def enable_email!
@@ -274,6 +301,11 @@ class User &lt; ActiveRecord::Base @@ -274,6 +301,11 @@ class User &lt; ActiveRecord::Base
274 15 # in minutes 301 15 # in minutes
275 end 302 end
276 303
  304 +
  305 + def not_require_password!
  306 + @is_password_required = false
  307 + end
  308 +
277 protected 309 protected
278 # before filter 310 # before filter
279 def encrypt_password 311 def encrypt_password
@@ -282,9 +314,13 @@ class User &lt; ActiveRecord::Base @@ -282,9 +314,13 @@ class User &lt; ActiveRecord::Base
282 self.password_type ||= User.system_encryption_method.to_s 314 self.password_type ||= User.system_encryption_method.to_s
283 self.crypted_password = encrypt(password) 315 self.crypted_password = encrypt(password)
284 end 316 end
285 - 317 +
286 def password_required? 318 def password_required?
287 - crypted_password.blank? || !password.blank? 319 + (crypted_password.blank? || !password.blank?) && is_password_required?
  320 + end
  321 +
  322 + def is_password_required?
  323 + @is_password_required.nil? ? true : @is_password_required
288 end 324 end
289 325
290 def make_activation_code 326 def make_activation_code
app/sweepers/article_sweeper.rb
@@ -10,17 +10,24 @@ class ArticleSweeper &lt; ActiveRecord::Observer @@ -10,17 +10,24 @@ class ArticleSweeper &lt; ActiveRecord::Observer
10 expire_caches(article) 10 expire_caches(article)
11 end 11 end
12 12
  13 + def before_update(article)
  14 + if article.parent_id_change
  15 + Article.find(article.parent_id_was).touch if article.parent_id_was
  16 + end
  17 + end
  18 +
13 protected 19 protected
14 20
15 def expire_caches(article) 21 def expire_caches(article)
16 - article.hierarchy.each { |a| a.touch if a != article } 22 + return if !article.environment
  23 + article.hierarchy(true).each { |a| a.touch if a != article }
17 blocks = article.profile.blocks 24 blocks = article.profile.blocks
18 blocks += article.profile.environment.blocks if article.profile.environment 25 blocks += article.profile.environment.blocks if article.profile.environment
19 blocks = blocks.select{|b|[RecentDocumentsBlock, BlogArchivesBlock].any?{|c| b.kind_of?(c)}} 26 blocks = blocks.select{|b|[RecentDocumentsBlock, BlogArchivesBlock].any?{|c| b.kind_of?(c)}}
20 BlockSweeper.expire_blocks(blocks) 27 BlockSweeper.expire_blocks(blocks)
21 env = article.profile.environment 28 env = article.profile.environment
22 if env && (env.portal_community == article.profile) 29 if env && (env.portal_community == article.profile)
23 - Noosfero.locales.keys.each do |locale| 30 + article.environment.locales.keys.each do |locale|
24 expire_fragment(env.portal_news_cache_key(locale)) 31 expire_fragment(env.portal_news_cache_key(locale))
25 end 32 end
26 end 33 end
app/sweepers/block_sweeper.rb
@@ -7,10 +7,11 @@ class BlockSweeper &lt; ActiveRecord::Observer @@ -7,10 +7,11 @@ class BlockSweeper &lt; ActiveRecord::Observer
7 7
8 # Expire block's all languages cache 8 # Expire block's all languages cache
9 def expire_block(block) 9 def expire_block(block)
  10 + return if !block.environment
10 regex = '-[a-z]*$' 11 regex = '-[a-z]*$'
11 clean_ck = block.cache_key.gsub(/#{regex}/,'') 12 clean_ck = block.cache_key.gsub(/#{regex}/,'')
12 13
13 - Noosfero.locales.keys.each do |locale| 14 + block.environment.locales.keys.each do |locale|
14 expire_timeout_fragment("#{clean_ck}-#{locale}") 15 expire_timeout_fragment("#{clean_ck}-#{locale}")
15 end 16 end
16 end 17 end
app/views/account/forgot_password.rhtml
1 <h1><%= _('Forgot your password?') %></h1> 1 <h1><%= _('Forgot your password?') %></h1>
2 2
3 -<%= error_messages_for :change_password %> 3 +<%= error_messages_for :change_password, :header_message => _('Instructions to password recovery could not be sent'), :message => nil %>
4 4
5 <% labelled_form_for :change_password, @change_password, :url => { :action => 'forgot_password' } do |f| %> 5 <% labelled_form_for :change_password, @change_password, :url => { :action => 'forgot_password' } do |f| %>
6 6
app/views/account/login.rhtml
@@ -13,6 +13,8 @@ @@ -13,6 +13,8 @@
13 13
14 <%= f.password_field :password %> 14 <%= f.password_field :password %>
15 15
  16 + <%= @plugins.dispatch(:login_extra_contents).collect { |content| instance_eval(&content) }.join("") %>
  17 +
16 <% button_bar do %> 18 <% button_bar do %>
17 <%= submit_button( 'login', _('Log in') )%> 19 <%= submit_button( 'login', _('Log in') )%>
18 <% if is_thickbox %> 20 <% if is_thickbox %>
@@ -23,8 +25,13 @@ @@ -23,8 +25,13 @@
23 <% end %> 25 <% end %>
24 26
25 <% button_bar do %> 27 <% button_bar do %>
26 - <%= button :add, _("New user"), :controller => 'account', :action => 'signup' %>  
27 - <%= button :help, _("I forgot my password!"), :controller => 'account', :action => 'forgot_password' %> 28 + <% unless @plugins.dispatch(:allow_user_registration).include?(false) %>
  29 + <%= button :add, _("New user"), :controller => 'account', :action => 'signup' %>
  30 + <% end %>
  31 +
  32 + <% unless @plugins.dispatch(:allow_password_recovery).include?(false) %>
  33 + <%= button :help, _("I forgot my password!"), :controller => 'account', :action => 'forgot_password' %>
  34 + <% end %>
28 <% end %> 35 <% end %>
29 36
30 </div><!-- end class="login-box" --> 37 </div><!-- end class="login-box" -->
app/views/account/login_block.rhtml
@@ -9,25 +9,30 @@ @@ -9,25 +9,30 @@
9 @user ||= User.new 9 @user ||= User.new
10 %> 10 %>
11 11
12 - <% labelled_form_for :user, @user,  
13 - :url => login_url do |f| %> 12 + <% labelled_form_for :user, @user, :url => login_url do |f| %>
14 13
15 - <%= f.text_field :login, :onchange => 'this.value = convToValidLogin( this.value )' %> 14 + <%= f.text_field :login, :onchange => 'this.value = convToValidLogin( this.value )' %>
16 15
17 - <%= f.password_field :password %> 16 + <%= f.password_field :password %>
  17 +
  18 + <%= @plugins.dispatch(:login_extra_contents).collect { |content| instance_eval(&content) }.join("") %>
18 19
19 <% button_bar do %> 20 <% button_bar do %>
20 <%= submit_button( 'login', _('Log in') )%> 21 <%= submit_button( 'login', _('Log in') )%>
21 - <%= link_to content_tag( 'span', _('New user') ),  
22 - { :controller => 'account', :action => 'signup' },  
23 - :class => 'button with-text icon-add' %> 22 + <% unless @plugins.dispatch(:allow_user_registration).include?(false) %>
  23 + <%= link_to content_tag( 'span', _('New user') ),
  24 + { :controller => 'account', :action => 'signup' },
  25 + :class => 'button with-text icon-add' %>
  26 + <% end %>
24 <% end %> 27 <% end %>
25 28
26 <% end %> 29 <% end %>
27 30
28 - <p class="forgot-passwd">  
29 - <%= link_to _("I forgot my password!"), :controller => 'account', :action => 'forgot_password' %>  
30 - </p> 31 + <% unless @plugins.dispatch(:allow_password_recovery).include?(false) %>
  32 + <p class="forgot-passwd">
  33 + <%= link_to _("I forgot my password!"), :controller => 'account', :action => 'forgot_password' %>
  34 + </p>
  35 + <% end %>
31 36
32 </div> 37 </div>
33 38
app/views/admin_panel/_signup_welcome_text.rhtml 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +<div class='description'>
  2 + <%= _('This text will be sent to new users if the feature "Send welcome e-mail to new users" is enabled on environment.') %><br/><br/>
  3 + <%= _('Including %s on body, it will be replaced by the real name of the e-mail recipient.') % content_tag('code', '{user_name}') %>
  4 +</div>
  5 +
  6 +<%= labelled_form_field(_('Subject'), text_field(:environment, :signup_welcome_text_subject, :style => 'width:100%')) %>
  7 +<%= labelled_form_field(_('Body'), text_area(:environment, :signup_welcome_text_body, :cols => 40, :style => 'width: 100%', :class => 'mceEditor')) %>
app/views/admin_panel/_site_info.rhtml
1 <%= required labelled_form_field(_('Site name'), text_field(:environment, :name)) %> 1 <%= required labelled_form_field(_('Site name'), text_field(:environment, :name)) %>
  2 +<%= labelled_form_field(_('Contact email'), text_field(:environment, :contact_email)) %>
  3 +<% themes_options = Theme.system_themes.map {|theme| [theme.name, theme.id] }.sort %>
  4 +<%= labelled_form_field(_('Theme'), select(:environment, :theme, options_for_select(themes_options, environment.theme))) %>
2 <%= required f.text_field(:reports_lower_bound, :size => 3) %> 5 <%= required f.text_field(:reports_lower_bound, :size => 3) %>
  6 +<%= labelled_form_field(_('Default language'), select(:environment, :default_language, environment.locales.invert, { :selected => environment.default_locale, :include_blank => true })) %>
  7 +<%= label_tag :languages, _('Available languages') %>
  8 +<br />
  9 +
  10 +<%
  11 + fields = Noosfero.locales.map do |value, name|
  12 + labelled_check_box(name, "environment[languages][#{value}]", true, environment.available_locales.include?(value))
  13 + end
  14 +%>
  15 +<%= balanced_table(fields)%>
  16 +
  17 +<br />
3 <%= labelled_form_field _('Homepage content'), text_area(:environment, :description, :cols => 40, :style => 'width: 90%', :class => 'mceEditor') %> 18 <%= labelled_form_field _('Homepage content'), text_area(:environment, :description, :cols => 40, :style => 'width: 90%', :class => 'mceEditor') %>
app/views/admin_panel/site_info.rhtml
@@ -10,6 +10,8 @@ @@ -10,6 +10,8 @@
10 :content => (render :partial => 'site_info', :locals => {:f => f})} %> 10 :content => (render :partial => 'site_info', :locals => {:f => f})} %>
11 <% tabs << {:title => _('Terms of use'), :id => 'terms-of-use', 11 <% tabs << {:title => _('Terms of use'), :id => 'terms-of-use',
12 :content => (render :partial => 'terms_of_use', :locals => {:f => f})} %> 12 :content => (render :partial => 'terms_of_use', :locals => {:f => f})} %>
  13 + <% tabs << {:title => _('Signup welcome text'), :id => 'signup-welcome-text',
  14 + :content => (render :partial => 'signup_welcome_text', :locals => {:f => f})} %>
13 <%= render_tabs(tabs) %> 15 <%= render_tabs(tabs) %>
14 <% button_bar do %> 16 <% button_bar do %>
15 <%= submit_button(:save, _('Save'), :cancel => {:action => 'index'}) %> 17 <%= submit_button(:save, _('Save'), :cancel => {:action => 'index'}) %>
app/views/blocks/profile_info_actions/community.rhtml
@@ -32,7 +32,7 @@ @@ -32,7 +32,7 @@
32 { :profile => profile.identifier, 32 { :profile => profile.identifier,
33 :controller => 'contact', 33 :controller => 'contact',
34 :action => 'new' }, 34 :action => 'new' },
35 - :class => 'button with-text icon-menu-mail' %> 35 + {:class => 'button with-text icon-menu-mail', :title => _('Send an e-mail to the administrators')} %>
36 </li> 36 </li>
37 <% end %> 37 <% end %>
38 38
app/views/box_organizer/_block_types.rhtml 0 → 100644
@@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
  1 +<% block_types.in_groups_of(2) do |block1, block2| %>
  2 + <div style='float: left; width: 48%; padding-top: 2px;'>
  3 + <%= labelled_radio_button(block1.description, :type, block1.name) %>
  4 + </div>
  5 + <% if block2 %>
  6 + <div style='float: left; width: 48%; padding-top: 2px;'>
  7 + <%= labelled_radio_button(block2.description, :type, block2.name) %>
  8 + </div>
  9 + <% end %>
  10 +<% end %>
app/views/box_organizer/_highlights_block.rhtml
1 <strong><%= _('Highlights') %></strong> 1 <strong><%= _('Highlights') %></strong>
2 -<div id='edit-highlights-block'> 2 +<div id='edit-highlights-block' style='width:450px'>
3 <table id='highlights' class='noborder'> 3 <table id='highlights' class='noborder'>
4 <tr><th><%= _('Image') %></th><th><%= _('Address') %></th><th><%= _('Position') %></th><th><%= _('Title') %></th></tr> 4 <tr><th><%= _('Image') %></th><th><%= _('Address') %></th><th><%= _('Position') %></th><th><%= _('Title') %></th></tr>
5 <% for image in @block.images do %> 5 <% for image in @block.images do %>
app/views/box_organizer/_link_list_block.rhtml
1 <strong><%= _('Links') %></strong> 1 <strong><%= _('Links') %></strong>
2 -<div id='edit-link-list-block'> 2 +<div id='edit-link-list-block' style='width:450px'>
3 <table id='links' class='noborder'> 3 <table id='links' class='noborder'>
4 <tr><th><%= _('Icon') %></th><th><%= _('Name') %></th><th><%= _('Address') %></th></tr> 4 <tr><th><%= _('Icon') %></th><th><%= _('Name') %></th><th><%= _('Address') %></th></tr>
5 <% for link in @block.links do %> 5 <% for link in @block.links do %>
app/views/box_organizer/_raw_html_block.rhtml
1 -<%= labelled_form_field(_('HTML code'), text_area(:block, :html, :style => 'width: 100%', :rows => 15)) %> 1 +<%= labelled_form_field(_('HTML code'), text_area(:block, :html, :style => 'width: 90%', :rows => 15)) %>
app/views/box_organizer/add_block.rhtml
1 -<% form_tag do %>  
2 -  
3 - <p><%= _('In what area do you want to put your new block?') %></p>  
4 -  
5 - <%# FIXME hardcoded stuff %>  
6 - <%= select_tag('box_id', options_for_select(@boxes.select { |item| item.position != 1 }.map {|item| [ _("Area %d") % item.position, item.id]})) %>  
7 -  
8 - <p><%= _('Select the type of block you want to add to your page.') %></p>  
9 -  
10 - <% @block_types.in_groups_of(2) do |block1, block2| %>  
11 - <div style='float: left; width: 48%; padding-top: 2px;'>  
12 - <%= radio_button_tag('type', block1.name) %>  
13 - <%= label_tag "type_#{block1.name.downcase}", block1.description %> 1 +<div style='height:350px'>
  2 + <% form_tag do %>
  3 +
  4 + <p><%= _('In what area do you want to put your new block?') %></p>
  5 +
  6 + <% @boxes.each do |box| %>
  7 + <%= labelled_radio_button(_("Area %d") % box.position, :box_id, box.id, box.central?, { :class => 'box-position', 'data-position' => box.position }) %>
  8 + <% end %>
  9 +
  10 + <script type="text/javascript">
  11 + (function ($) {
  12 + $(document).ready(function () {
  13 + $(".box-position").live('change', function () {
  14 + if ($(this).attr('data-position') == '1') {
  15 + $('#center-block-types').show();
  16 + $('#side-block-types').hide();
  17 + } else {
  18 + $('#center-block-types').hide();
  19 + $('#side-block-types').show();
  20 + };
  21 + });
  22 + })})(jQuery);
  23 + </script>
  24 +
  25 + <p><%= _('Select the type of block you want to add to your page.') %></p>
  26 +
  27 + <div id='center-block-types'>
  28 + <%= render :partial => 'block_types', :locals => { :block_types => @center_block_types } %>
14 </div> 29 </div>
15 - <% if block2 %>  
16 - <div style='float: left; width: 48%; padding-top: 2px;'>  
17 - <%= radio_button_tag('type', block2.name) %>  
18 - <%= label_tag "type_#{block2.name.downcase}", block2.description %>  
19 - </div> 30 +
  31 + <div id='side-block-types' style='display:none'>
  32 + <%= render :partial => 'block_types', :locals => { :block_types => @side_block_types } %>
  33 + </div>
  34 +
  35 + <br style='clear: both'/>
  36 +
  37 + <% button_bar do %>
  38 + <%= submit_button(:add, _("Add")) %>
  39 + <%= colorbox_close_button(_('Close')) %>
20 <% end %> 40 <% end %>
21 - <% end %>  
22 - <br style='clear: both'/>  
23 -  
24 - <% button_bar do %>  
25 - <%= submit_button(:add, _("Add")) %>  
26 - <%= lightbox_close_button(_('Close')) %>  
27 - <% end %>  
28 41
29 -<% end %> 42 + <% end %>
  43 +</div>
app/views/box_organizer/edit.rhtml
1 -<h2><%= _('Editing block') %></h2> 1 +<div style='width: 500px;'>
  2 + <h2><%= _('Editing block') %></h2>
2 3
3 -<% form_tag(:action => 'save', :id => @block.id) do %> 4 + <% form_tag(:action => 'save', :id => @block.id) do %>
4 5
5 - <%= labelled_form_field(_('Custom title for this block: '), text_field(:block, :title, :maxlength => 20)) %> 6 + <%= labelled_form_field(_('Custom title for this block: '), text_field(:block, :title, :maxlength => 20)) %>
6 7
7 - <%= render :partial => partial_for_class(@block.class) %> 8 + <%= render :partial => partial_for_class(@block.class) %>
8 9
9 - <%= labelled_form_field _('Display this block:'), '' %>  
10 - <div style='margin-left: 10px'>  
11 - <%= radio_button(:block, :display, 'always') %>  
12 - <%= label_tag('block_display_always', _('In all pages')) %>  
13 - <br/>  
14 - <%= radio_button(:block, :display, 'home_page_only') %>  
15 - <%= label_tag('block_display_home_page_only', _('Only in the homepage')) %>  
16 - <br/>  
17 - <%= radio_button(:block, :display, 'except_home_page') %>  
18 - <%= label_tag('block_display_except_home_page', _('In all pages, except in the homepage')) %>  
19 - <br/>  
20 - <%= radio_button(:block, :display, 'never') %>  
21 - <%= label_tag('block_display_never', _("Don't display")) %>  
22 - </div> 10 + <%= labelled_form_field _('Display this block:'), '' %>
  11 + <div style='margin-left: 10px'>
  12 + <%= radio_button(:block, :display, 'always') %>
  13 + <%= label_tag('block_display_always', _('In all pages')) %>
  14 + <br/>
  15 + <%= radio_button(:block, :display, 'home_page_only') %>
  16 + <%= label_tag('block_display_home_page_only', _('Only in the homepage')) %>
  17 + <br/>
  18 + <%= radio_button(:block, :display, 'except_home_page') %>
  19 + <%= label_tag('block_display_except_home_page', _('In all pages, except in the homepage')) %>
  20 + <br/>
  21 + <%= radio_button(:block, :display, 'never') %>
  22 + <%= label_tag('block_display_never', _("Don't display")) %>
  23 + </div>
23 24
24 - <%= labelled_form_field(_('Show for:'), select(:block, :language, [ [ _('all languages'), 'all']] + Noosfero.locales.map {|key, value| [value, key]} )) %> 25 + <%= labelled_form_field(_('Show for:'), select(:block, :language, [ [ _('all languages'), 'all']] + environment.locales.map {|key, value| [value, key]} )) %>
25 26
26 - <% button_bar do %>  
27 - <%= submit_button(:save, _('Save')) %>  
28 - <%= lightbox_close_button(_('Cancel')) %>  
29 - <% end %> 27 + <% button_bar do %>
  28 + <%= submit_button(:save, _('Save')) %>
  29 + <%= colorbox_close_button(_('Cancel')) %>
  30 + <% end %>
30 31
31 -<% end %> 32 + <% end %>
  33 +</div>
app/views/box_organizer/index.rhtml
1 <h1><%= _('Editing sideboxes')%></h1> 1 <h1><%= _('Editing sideboxes')%></h1>
2 2
3 <% button_bar do %> 3 <% button_bar do %>
4 - <%= lightbox_button('add', _('Add a block'), { :action => 'add_block' }) %> 4 + <%= colorbox_button('add', _('Add a block'), { :action => 'add_block' }) %>
5 <%= button(:back, _('Back to control panel'), :controller => (profile.nil? ? 'admin_panel': 'profile_editor')) %> 5 <%= button(:back, _('Back to control panel'), :controller => (profile.nil? ? 'admin_panel': 'profile_editor')) %>
6 <% end %> 6 <% end %>
app/views/catalog/index.rhtml
@@ -30,6 +30,9 @@ @@ -30,6 +30,9 @@
30 <li class="product <%= status.join(' ') %>"> 30 <li class="product <%= status.join(' ') %>">
31 <ul> 31 <ul>
32 <li class="product-image-link"> 32 <li class="product-image-link">
  33 + <% if product.highlighted? %>
  34 + <%= link_to image_tag(themed_path('/images/star.png'), :class => 'star', :alt => _('Highlighted product')), product_path(product) %>
  35 + <% end %>
33 <% if product.image %> 36 <% if product.image %>
34 <div class="zoomable-image"> 37 <div class="zoomable-image">
35 <%= link_to_product product, :class => 'product-big', :style => "background-image: url(#{product.default_image(:big)})" %> 38 <%= link_to_product product, :class => 'product-big', :style => "background-image: url(#{product.default_image(:big)})" %>
app/views/cms/_blog.rhtml
@@ -62,7 +62,7 @@ @@ -62,7 +62,7 @@
62 62
63 <% f.fields_for 'feed', @article.feed do |feed| %> 63 <% f.fields_for 'feed', @article.feed do |feed| %>
64 <%= labelled_form_field(_('Limit of posts in RSS Feed'), feed.select(:limit, [5, 10, 20, 50])) %> 64 <%= labelled_form_field(_('Limit of posts in RSS Feed'), feed.select(:limit, [5, 10, 20, 50])) %>
65 - <%= labelled_form_field(_('Include in RSS Feed only posts from language:'), feed.select(:language, [[_('All'), nil ]] + Noosfero.locales.map { |k,v| [v, k]})) %> 65 + <%= labelled_form_field(_('Include in RSS Feed only posts from language:'), feed.select(:language, [[_('All'), nil ]] + environment.locales.map { |k,v| [v, k]})) %>
66 <% end %> 66 <% end %>
67 67
68 <% f.fields_for 'external_feed_builder', @article.external_feed do |efeed| %> 68 <% f.fields_for 'external_feed_builder', @article.external_feed do |efeed| %>
app/views/cms/_general_fields.html.erb
  1 +<%= select_profile_folder(_('Parent folder:'), 'article[parent_id]', profile, @article.parent_id) %>
1 <%= labelled_form_field(_('License'), select(:article, :license_id, options_for_select_with_title([[_('None'), nil]] + profile.environment.licenses.map {|license| [license.name, license.id]}, @article.license ? @article.license.id : nil))) %> 2 <%= labelled_form_field(_('License'), select(:article, :license_id, options_for_select_with_title([[_('None'), nil]] + profile.environment.licenses.map {|license| [license.name, license.id]}, @article.license ? @article.license.id : nil))) %>
app/views/cms/_rss_feed.rhtml
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 6
7 <%= required labelled_form_field(_('Limit of articles'), text_field(:article, :limit)) %> 7 <%= required labelled_form_field(_('Limit of articles'), text_field(:article, :limit)) %>
8 8
9 -<%= labelled_form_field(_('Include in RSS Feed only posts from language:'), f.select(:language, [[_('All'), nil ]] + Noosfero.locales.map { |k,v| [v, k]})) %> 9 +<%= labelled_form_field(_('Include in RSS Feed only posts from language:'), f.select(:language, [[_('All'), nil ]] + environment.locales.map { |k,v| [v, k]})) %>
10 10
11 <%= labelled_form_field(_('Use as item description:'), select(:article, :feed_item_description, [ [ _('Article abstract'), 'abstract'], [ _('Article body'), 'body']])) %> 11 <%= labelled_form_field(_('Use as item description:'), select(:article, :feed_item_description, [ [ _('Article abstract'), 'abstract'], [ _('Article body'), 'body']])) %>
12 12
app/views/cms/_text_editor_sidebar.rhtml
@@ -9,8 +9,7 @@ @@ -9,8 +9,7 @@
9 <div id='media-upload-form'> 9 <div id='media-upload-form'>
10 <% form_tag({ :action => 'media_upload' }, :multipart => true) do %> 10 <% form_tag({ :action => 'media_upload' }, :multipart => true) do %>
11 <div class='formfield'> 11 <div class='formfield'>
12 - <%# TODO duplicated from partial upload_file_form %>  
13 - <%= labelled_form_field(_('Choose folder to upload files:'), select_tag('parent_id', options_for_select([[profile.identifier, '']] + profile.folders.collect {|f| [ profile.identifier + '/' + f.full_name, f.id ] }))) %> 12 + <%= select_profile_folder(_('Choose folder to upload files:'), :parent_id, profile) %>
14 </div> 13 </div>
15 <p><%= file_field_tag('file1') %></p> 14 <p><%= file_field_tag('file1') %></p>
16 <p><%= file_field_tag('file2') %></p> 15 <p><%= file_field_tag('file2') %></p>
app/views/cms/_upload_file_form.rhtml
1 <% if @parent %> 1 <% if @parent %>
2 <%= hidden_field_tag('parent_id', @parent.id) %> 2 <%= hidden_field_tag('parent_id', @parent.id) %>
3 <% else %> 3 <% else %>
4 - <%= labelled_form_field(_('Choose folder to upload files:'), select_tag('parent_id', options_for_select([[profile.identifier, '']] + @folders.collect {|f| [ profile.identifier + '/' + f.full_name, f.id ] }))) %> 4 + <%= select_profile_folder(_('Choose folder to upload files:'), :parent_id, profile) %>
5 <% end %> 5 <% end %>
6 6
7 <div id='uploaded_files'> 7 <div id='uploaded_files'>
app/views/cms/_uploaded_file.rhtml
1 <%= labelled_form_field(_('Title'), text_field(:article, :title, :maxlength => 60)) %> 1 <%= labelled_form_field(_('Title'), text_field(:article, :title, :maxlength => 60)) %>
  2 +
  3 +<%= render :partial => 'general_fields' %>
  4 +
2 <%= labelled_form_field(_('Description'), text_area(:article, :abstract, :rows => 3, :cols => 64)) %> 5 <%= labelled_form_field(_('Description'), text_area(:article, :abstract, :rows => 3, :cols => 64)) %>
3 <% if @article.image? %> 6 <% if @article.image? %>
4 <%= f.text_field(:external_link, :size => 64) %> 7 <%= f.text_field(:external_link, :size => 64) %>
app/views/contact/new.rhtml
1 -<h1><%= _('Send an e-mail to %s') % profile.name %></h1> 1 +<% if profile.person? %>
  2 + <h1><%= _('Send an e-mail to %s') % profile.name %></h1>
  3 +<% else %>
  4 + <h1><%= _('Send an e-mail to administrators') %></h1>
2 5
3 -<%= error_messages_for 'contact' %> 6 + <div class='tooltip'><%= _("The e-mail will be sent to the administrators of '%s'") % profile.name %></div>
  7 +<% end %>
4 8
  9 +<%= error_messages_for 'contact' %>
5 10
6 <% labelled_form_for :contact, @contact do |f| %> 11 <% labelled_form_for :contact, @contact do |f| %>
7 <%= hidden_field_tag(:confirm, 'false') %> 12 <%= hidden_field_tag(:confirm, 'false') %>
app/views/features/index.rhtml
@@ -26,17 +26,12 @@ Check all the features you want to enable for your environment, uncheck all the @@ -26,17 +26,12 @@ Check all the features you want to enable for your environment, uncheck all the
26 26
27 <h2><%= _('Configure features') %></h2> 27 <h2><%= _('Configure features') %></h2>
28 28
29 -<table>  
30 - <tr>  
31 - <th><%= _('Option') %></th>  
32 - <th><%= _('Choice') %></th>  
33 - </tr>  
34 - <tr>  
35 - <td><%= _('Organization Approval Method') %></td>  
36 - <td><%= select_organization_approval_method('environment', 'organization_approval_method') %></td>  
37 - </tr>  
38 -</table>  
39 - 29 +<h3><%= _('Page to redirect after login') %></h3>
  30 + <%= select 'environment', 'redirection_after_login', Environment.login_redirection_options.map{|key,value|[value,key]} %>
  31 +<hr/>
  32 +<h3><%= _('Organization Approval Method') %></h3>
  33 + <%= select_organization_approval_method('environment', 'organization_approval_method') %>
  34 +<hr/>
40 35
41 <div> 36 <div>
42 <% button_bar do %> 37 <% button_bar do %>
app/views/layouts/_javascript.rhtml
@@ -2,7 +2,8 @@ @@ -2,7 +2,8 @@
2 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox', 2 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox',
3 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate', 3 'jquery-ui-1.8.2.custom.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate',
4 'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput', 4 'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput',
5 -'add-and-join', 'report-abuse', 'catalog', 'manage-products', :cache => 'cache-general' %> 5 +'add-and-join', 'report-abuse', 'catalog', 'manage-products',
  6 +'jquery-ui-timepicker-addon', :cache => 'cache-general' %>
6 7
7 <% language = FastGettext.locale %> 8 <% language = FastGettext.locale %>
8 <% %w{messages methods}.each do |type| %> 9 <% %w{messages methods}.each do |type| %>
app/views/layouts/application-ng.rhtml
@@ -56,10 +56,18 @@ @@ -56,10 +56,18 @@
56 <%= usermenu_logged_in %> 56 <%= usermenu_logged_in %>
57 </span> 57 </span>
58 <span class='not-logged-in' style='display: none'> 58 <span class='not-logged-in' style='display: none'>
59 - <%= _("<span class='login'>%s</span> <span class='or'>or</span> <span class='signup'>%s</span>") % [thickbox_inline_popup_link('<i class="icon-menu-login"></i><strong>' + _('Login') + '</strong>', login_url, 'inlineLoginBox', :id => 'link_login'), link_to('<strong>' + _('Sign up') + '</strong>', :controller => 'account', :action => 'signup') ] %> 59 +
  60 + <%= _("<span class='login'>%s</span>") % thickbox_inline_popup_link('<i class="icon-menu-login"></i><strong>' + _('Login') + '</strong>', login_url, 'inlineLoginBox', :id => 'link_login') %>
  61 + <%= @plugins.dispatch(:alternative_authentication_link).collect { |content| instance_eval(&content) }.join("") %>
  62 +
60 <div id='inlineLoginBox' style='display: none;'> 63 <div id='inlineLoginBox' style='display: none;'>
61 <%= render :file => 'account/login', :locals => { :is_thickbox => true } %> 64 <%= render :file => 'account/login', :locals => { :is_thickbox => true } %>
62 </div> 65 </div>
  66 +
  67 + <% unless @plugins.dispatch(:allow_user_registration).include?(false) %>
  68 + <%= _("<span class='or'>or</span> <span class='signup'>%s</span>") % link_to('<strong>' + _('Sign up') + '</strong>', :controller => 'account', :action => 'signup')%>
  69 + <% end %>
  70 +
63 </span> 71 </span>
64 <form action="/search" class="search_form" method="get" class="clean"> 72 <form action="/search" class="search_form" method="get" class="clean">
65 <input name="query" size="15" title="<%=_('Search...')%>" onfocus="this.form.className='focused';" onblur="this.form.className=''" /> 73 <input name="query" size="15" title="<%=_('Search...')%>" onfocus="this.form.className='focused';" onblur="this.form.className=''" />
app/views/layouts/application.rhtml
@@ -80,7 +80,7 @@ @@ -80,7 +80,7 @@
80 </div><!-- id='navigation_bar' --> 80 </div><!-- id='navigation_bar' -->
81 81
82 <div id="language-selector"> 82 <div id="language-selector">
83 - <%= language_chooser(:element => 'dropdown') %> 83 + <%= language_chooser(environment, :element => 'dropdown') %>
84 </div> 84 </div>
85 85
86 <div id="user_box"> 86 <div id="user_box">
app/views/profile/_common.rhtml
1 -  
2 -<script type="text/javascript">  
3 - jQuery( function() {  
4 - var parent_selector = '.profile-wall-description, .profile-activity-description, .profile-network-description';  
5 - var child_selector = '.icon-delete, .icon-reply';  
6 - jQuery(parent_selector).live('mouseover', function () { jQuery(this).find(child_selector).css('visibility', 'visible'); });  
7 - jQuery(parent_selector).live('mouseout', function () { jQuery(this).find(child_selector).css('visibility', 'hidden'); });  
8 - });  
9 -</script>  
10 -  
11 <% unless @action %> 1 <% unless @action %>
12 <% cache_timeout(profile.cache_key + '-profile-general-info', 4.hours) do %> 2 <% cache_timeout(profile.cache_key + '-profile-general-info', 4.hours) do %>
13 <tr> 3 <tr>
app/views/profile/_person_profile.rhtml
@@ -13,31 +13,16 @@ @@ -13,31 +13,16 @@
13 <td><%= show_date(profile.created_at) %></td> 13 <td><%= show_date(profile.created_at) %></td>
14 </tr> 14 </tr>
15 15
16 - <% if profile == user || profile.friends.include?(user) %>  
17 - <tr>  
18 - <th colspan='2'><%= _('Contact')%></th>  
19 - </tr>  
20 - <%= display_field(_('Address:'), profile, :address) %>  
21 - <%= display_field(_('ZIP code:'), profile, :zip_code) %>  
22 - <%= display_field(_('Contact phone:'), profile, :contact_phone) %>  
23 - <%= display_field(_('e-Mail:'), profile, :email, true) { |email| link_to_email(email) } %>  
24 - <% end %> 16 + <%= display_contact profile %>
25 17
26 <% cache_timeout(profile.relationships_cache_key, 4.hours) do %> 18 <% cache_timeout(profile.relationships_cache_key, 4.hours) do %>
27 - <% if !(profile.organization.blank? && profile.organization_website.blank?) && (profile.active_fields.include?('organization') || profile.active_fields.include?('organization_website')) %>  
28 - <tr>  
29 - <th colspan='2'><%= _('Work')%></th>  
30 - </tr>  
31 - <% end %>  
32 - <%= display_field(_('Organization:'), profile, :organization) %>  
33 - <%= display_field(_('Organization website:'), profile, :organization_website) { |url| link_to(url, url) }%>  
34 - 19 + <%= display_work_info profile %>
35 20
36 <% if !environment.enabled?('disable_asset_enterprises') && !profile.enterprises.empty? %> 21 <% if !environment.enabled?('disable_asset_enterprises') && !profile.enterprises.empty? %>
37 <tr> 22 <tr>
38 <th colspan='2'><%= __('Enterprises') %></th> 23 <th colspan='2'><%= __('Enterprises') %></th>
39 </tr> 24 </tr>
40 - <% profile.enterprises.each do |item| %> 25 + <% profile.enterprises.includes(:environment,:domains, :preferred_domain).each do |item| %>
41 <tr> 26 <tr>
42 <td></td> 27 <td></td>
43 <td><%= button 'menu-enterprise', item.name, item.url %></td> 28 <td><%= button 'menu-enterprise', item.name, item.url %></td>
@@ -59,6 +44,6 @@ @@ -59,6 +44,6 @@
59 44
60 <%= render :partial => 'common' %> 45 <%= render :partial => 'common' %>
61 46
62 - </table>  
63 -<% end %> 47 + <% end %>
  48 +</table>
64 49
app/views/profile/members.rhtml
@@ -16,8 +16,13 @@ @@ -16,8 +16,13 @@
16 16
17 <% button_bar do %> 17 <% button_bar do %>
18 <%= button :back, _('Go back'), { :controller => 'profile' } %> 18 <%= button :back, _('Go back'), { :controller => 'profile' } %>
19 - <% if profile.community? and user and user.has_permission?(:invite_members, profile) %>  
20 - <%= button :search, _('Invite your friends to join %s') % profile.name, :controller => 'invite', :action => 'select_address_book' %> 19 + <% if profile.community? and user %>
  20 + <% if user.has_permission?(:invite_members, profile) %>
  21 + <%= button :search, _('Invite your friends to join %s') % profile.name, :controller => 'invite', :action => 'select_address_book' %>
  22 + <% end %>
  23 + <% if user.has_permission?(:send_mail_to_members, profile) %>
  24 + <%= button :send, _('Send e-mail to members'), :controller => 'profile', :action => 'send_mail' %>
  25 + <% end %>
21 <% end %> 26 <% end %>
22 <% end %> 27 <% end %>
23 28
app/views/profile/send_mail.rhtml 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +<h1><%= h profile.short_name(50) %></h1>
  2 +
  3 +<h2><%= _('Send e-mail to members') %></h2>
  4 +
  5 +<%= error_messages_for :mailing %>
  6 +
  7 +<%= render :file => 'shared/tiny_mce' %>
  8 +
  9 +<% form_for :mailing, :url => {:action => 'send_mail'}, :html => {:id => 'mailing-form'} do |f| %>
  10 + <%= labelled_form_field(_('Subject:'), f.text_field(:subject)) %>
  11 + <%= labelled_form_field(_('Body:'), f.text_area(:body, :class => 'mceEditor')) %>
  12 + <%= submit_button(:send, _('Send')) %>
  13 + <%= button :cancel, _('Cancel e-mail'), :back %>
  14 +<% end %>
app/views/profile_editor/_person.rhtml
@@ -2,9 +2,19 @@ @@ -2,9 +2,19 @@
2 2
3 <%= required_fields_message %> 3 <%= required_fields_message %>
4 4
5 - <%= required f.text_field(:name) %> 5 + <div class="field-with-privacy-selector">
  6 + <%= required f.text_field(:name) %>
  7 + <div class="field-privacy-selector">
  8 + <%= unchangeable_privacy_field @profile %>
  9 + </div>
  10 + </div>
6 11
7 - <%= required f.text_field(:email) %> 12 + <div class="field-with-privacy-selector">
  13 + <%= required f.text_field(:email) %>
  14 + <div class="field-privacy-selector">
  15 + <%= profile_field_privacy_selector @profile, 'email' %>
  16 + </div>
  17 + </div>
8 18
9 <%= @plugins.dispatch(:profile_info_extra_contents).collect { |content| instance_eval(&content) }.join("") %> 19 <%= @plugins.dispatch(:profile_info_extra_contents).collect { |content| instance_eval(&content) }.join("") %>
10 20
app/views/profile_editor/_person_form.rhtml
@@ -61,11 +61,3 @@ @@ -61,11 +61,3 @@
61 <%= optional_field(@person, 'professional_activity', f.text_field(:professional_activity, :rel => _('Professional activity'))) %> 61 <%= optional_field(@person, 'professional_activity', f.text_field(:professional_activity, :rel => _('Professional activity'))) %>
62 <%= optional_field(@person, 'organization', f.text_field(:organization, :rel => _('Organization'))) %> 62 <%= optional_field(@person, 'organization', f.text_field(:organization, :rel => _('Organization'))) %>
63 <%= optional_field(@person, 'organization_website', f.text_field(:organization_website, :rel => _('Organization website'))) %> 63 <%= optional_field(@person, 'organization_website', f.text_field(:organization_website, :rel => _('Organization website'))) %>
64 -  
65 -<% optional_field(@person, 'image') do %>  
66 - <div id="profile_choose_picture">  
67 - <% f.fields_for :image_builder, @person.image do |i| %>  
68 - <%= file_field_or_thumbnail(_('Image:'), @person.image, i) %><span class="person_image_maxsize"><%= _("Max size: %s (.jpg, .gif, .png)")% Image.max_size.to_humanreadable %></span>  
69 - <% end %>  
70 - </div>  
71 -<% end %>  
app/views/profile_editor/edit.rhtml
@@ -12,73 +12,37 @@ @@ -12,73 +12,37 @@
12 12
13 <%= render :partial => partial_for_class(@profile.class), :locals => { :f => f } %> 13 <%= render :partial => partial_for_class(@profile.class), :locals => { :f => f } %>
14 14
15 - <% unless @profile.person? && @environment.active_person_fields.include?('image') %>  
16 - <div id="profile_change_picture"> 15 + <div id="profile_change_picture_title">
17 <h2><%= _('Change picture') %></h2> 16 <h2><%= _('Change picture') %></h2>
  17 + <span><%= unchangeable_privacy_field @profile %></span>
  18 + </div>
  19 + <div id="profile_change_picture">
18 <% f.fields_for :image_builder, @profile.image do |i| %> 20 <% f.fields_for :image_builder, @profile.image do |i| %>
19 <%= file_field_or_thumbnail(_('Image:'), @profile.image, i) %><%= _("Max size: %s (.jpg, .gif, .png)")% Image.max_size.to_humanreadable %> 21 <%= file_field_or_thumbnail(_('Image:'), @profile.image, i) %><%= _("Max size: %s (.jpg, .gif, .png)")% Image.max_size.to_humanreadable %>
20 <% end %> 22 <% end %>
21 </div> 23 </div>
22 - <% end %>  
23 24
24 <h2><%= _('Privacy options') %></h2> 25 <h2><%= _('Privacy options') %></h2>
25 26
26 <% if profile.person? %> 27 <% if profile.person? %>
27 - <table>  
28 - <tr>  
29 - <th style='text-align: right;'>  
30 - <%= _('This profile is:') %>  
31 - </th>  
32 - <th>  
33 - <%= radio_button 'profile_data', 'public_profile', 'true' %>  
34 - <label for="profile_data_public_profile_true"><u><%= _('Public') %></u></label>  
35 - </th>  
36 - <th style='padding: 2px 10px 2px 2px;'>  
37 - <%= radio_button 'profile_data', 'public_profile', 'false' %>  
38 - <label for="profile_data_public_profile_false"><u><%= _('Private') %></u></label>  
39 - </th>  
40 - </tr>  
41 - <tr>  
42 - <td> <%= _('Activate Intranet access (restricted area only for me)') %> </td><td><%= _('Yes') %></td><td><%= _('Yes') %></td>  
43 - </tr>  
44 - <tr>  
45 - <td> <%= _('Include my contact in directory of people') %> </td><td><%= _('Yes') %></td><td><%= _('Yes') %></td>  
46 - </tr>  
47 - <tr>  
48 - <td> <%= _('Show my contents to all internet users') %> </td><td><%= _('Yes') %></td><td><%= _('No') %></td>  
49 - </tr>  
50 - <tr>  
51 - <td> <%= _('Show my contents to my friends (person)') %> </td><td><%= _('Yes') %></td><td><%= _('Yes') %></td>  
52 - </tr>  
53 - </table> 28 + <div>
  29 + <%= labelled_radio_button _('Public &mdash; show my contents to all internet users'), 'profile_data[public_profile]', true, @profile.public_profile? %>
  30 + </div>
  31 + <div>
  32 + <%= labelled_radio_button _('Private &mdash; show my contents only to friends'), 'profile_data[public_profile]', false, !@profile.public_profile? %>
  33 + </div>
54 <% else %> 34 <% else %>
55 - <table>  
56 - <tr>  
57 - <th style='text-align: right;'>  
58 - <%= _('This profile is:') %>  
59 - </th>  
60 - <th>  
61 - <%= radio_button 'profile_data', 'public_profile', 'true' %>  
62 - <label for="profile_data_public_profile_true"><u><%= _('Public') %></u></label>  
63 - </th>  
64 - <th style='padding: 2px 10px 2px 2px;'>  
65 - <%= radio_button 'profile_data', 'public_profile', 'false' %>  
66 - <label for="profile_data_public_profile_false"><u><%= _('Private') %></u></label>  
67 - </th>  
68 - </tr>  
69 - <tr>  
70 - <td> <%= _('Activate Intranet access (restricted area only for members)') %> </td><td><%= _('Yes') %></td><td><%= _('Yes') %></td>  
71 - </tr>  
72 - <tr>  
73 - <td> <%= _('Include this group directory of groups') %> </td><td><%= _('Yes') %></td><td><%= _('Yes') %></td>  
74 - </tr>  
75 - <tr>  
76 - <td> <%= _('Show content of this group to all internet users') %> </td><td><%= _('Yes') %></td><td><%= _('No') %></td>  
77 - </tr>  
78 - <tr>  
79 - <td> <%= _('Show content of this group to members') %> </td><td><%= _('Yes') %></td><td><%= _('Yes') %></td>  
80 - </tr>  
81 - </table> 35 + <div>
  36 + <%= labelled_radio_button _('Public &mdash; show content of this group to all internet users'), 'profile_data[public_profile]', true, @profile.public_profile? %>
  37 + </div>
  38 + <div>
  39 + <%= labelled_radio_button _('Private &mdash; show content of this group only to members'), 'profile_data[public_profile]', false, !@profile.public_profile? %>
  40 + </div>
  41 + <% end %>
  42 +
  43 + <% if environment.enabled?('allow_change_of_redirection_after_login') %>
  44 + <h2><%= _('Page to redirect after login') %></h2>
  45 + <%= select 'profile_data', 'redirection_after_login', Environment.login_redirection_options.map{|key,value|[value,key]}, { :selected => @profile.preferred_login_redirection} %>
82 <% end %> 46 <% end %>
83 47
84 <h2><%= _('Translations') %></h2> 48 <h2><%= _('Translations') %></h2>
app/views/profile_members/_index_buttons.rhtml
@@ -4,7 +4,9 @@ @@ -4,7 +4,9 @@
4 <% if profile.community? and user.has_permission?(:invite_members, profile) %> 4 <% if profile.community? and user.has_permission?(:invite_members, profile) %>
5 <%= button :search, _('Invite your friends to join %s') % profile.short_name, :controller => 'invite', :action => 'select_address_book' %> 5 <%= button :search, _('Invite your friends to join %s') % profile.short_name, :controller => 'invite', :action => 'select_address_book' %>
6 <% end %> 6 <% end %>
7 - <%= button :send, _('Send e-mail to members'), :action => 'send_mail' %> 7 + <% if profile.community? and user.has_permission?(:send_mail_to_members, profile) %>
  8 + <%= button :send, _('Send e-mail to members'), :controller => 'profile', :action => 'send_mail' %>
  9 + <% end %>
8 <% @plugins.dispatch(:manage_members_extra_buttons).each do |plugin_button| %> 10 <% @plugins.dispatch(:manage_members_extra_buttons).each do |plugin_button| %>
9 <%= button plugin_button[:icon], plugin_button[:title], plugin_button[:url] %> 11 <%= button plugin_button[:icon], plugin_button[:title], plugin_button[:url] %>
10 <% end %> 12 <% end %>
app/views/profile_members/send_mail.rhtml
@@ -1,14 +0,0 @@ @@ -1,14 +0,0 @@
1 -<h1><%= h profile.short_name(50) %></h1>  
2 -  
3 -<h2><%= _('Send e-mail to members') %></h2>  
4 -  
5 -<%= error_messages_for :mailing %>  
6 -  
7 -<%= render :file => 'shared/tiny_mce' %>  
8 -  
9 -<% form_for :mailing, :url => {:action => 'send_mail'}, :html => {:id => 'mailing-form'} do |f| %>  
10 - <%= labelled_form_field(_('Subject:'), f.text_field(:subject)) %>  
11 - <%= labelled_form_field(_('Body:'), f.text_area(:body, :class => 'mceEditor')) %>  
12 - <%= submit_button(:send, _('Send')) %>  
13 - <%= button :cancel, _('Cancel e-mail'), :action => 'index' %>  
14 -<% end %>  
app/views/search/_product.rhtml
1 <% extra_content = @plugins.dispatch(:asset_product_extras, product, product.enterprise).collect { |content| instance_eval(&content) } %> 1 <% extra_content = @plugins.dispatch(:asset_product_extras, product, product.enterprise).collect { |content| instance_eval(&content) } %>
2 <% extra_properties = @plugins.dispatch(:asset_product_properties, product)%> 2 <% extra_properties = @plugins.dispatch(:asset_product_properties, product)%>
3 3
4 -<li class="search-product-item"> 4 +<li class="search-product-item <%= 'highlighted' if product.highlighted? %>">
5 5
6 <div class="search-product-item-first-column"> 6 <div class="search-product-item-first-column">
7 <%= render :partial => 'search/image', :object => product %> 7 <%= render :partial => 'search/image', :object => product %>
app/views/tasks/_approve_article_accept_details.rhtml
1 <%= render :file => 'shared/tiny_mce' %> 1 <%= render :file => 'shared/tiny_mce' %>
2 2
3 <%= labelled_form_field(_('Name for publishing'), f.text_field(:name)) %> 3 <%= labelled_form_field(_('Name for publishing'), f.text_field(:name)) %>
4 -<%= select_profile_folder(_('Select the folder where the article must be published'), "tasks[#{task.id}][task]", 'article_parent_id', task.target) %> 4 +<%= select_profile_folder(_('Select the folder where the article must be published'), "tasks[#{task.id}][task][article_parent_id]", task.target) %>
5 <%= labelled_form_field(_('Highlight this article'), f.check_box(:highlighted)) %> 5 <%= labelled_form_field(_('Highlight this article'), f.check_box(:highlighted)) %>
6 6
7 <% tiny = task.article && task.article.tiny_mce? ? {:tiny_mce => true} : {} %> 7 <% tiny = task.article && task.article.tiny_mce? ? {:tiny_mce => true} : {} %>
app/views/tasks/_suggest_article_accept_details.rhtml
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 <%= labelled_form_field(_('Source'), f.text_field(:source_name)) %> 6 <%= labelled_form_field(_('Source'), f.text_field(:source_name)) %>
7 <%= labelled_form_field(_("Source URL"), f.text_field(:source)) %> 7 <%= labelled_form_field(_("Source URL"), f.text_field(:source)) %>
8 8
9 -<%= select_profile_folder(_('Select the folder where the article must be published'), "tasks[#{task.id}][task]", 'article_parent_id', task.target) %> 9 +<%= select_profile_folder(_('Select the folder where the article must be published'), "tasks[#{task.id}][task][article_parent_id]", task.target) %>
10 <%= labelled_form_field(_('Highlight this article'), f.check_box(:highlighted)) %> 10 <%= labelled_form_field(_('Highlight this article'), f.check_box(:highlighted)) %>
11 11
12 <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true, :f => f, :abstract_method => 'article_abstract', :body_method => 'article_body', :lead_id => task.id} %> 12 <%= render :partial => 'shared/lead_and_body', :locals => {:tiny_mce => true, :f => f, :abstract_method => 'article_abstract', :body_method => 'article_body', :lead_id => task.id} %>
app/views/tasks/_task.rhtml
@@ -50,13 +50,13 @@ @@ -50,13 +50,13 @@
50 <% fields_for "tasks[#{task.id}][task]", task do |f| %> 50 <% fields_for "tasks[#{task.id}][task]", task do |f| %>
51 <% if task.accept_details %> 51 <% if task.accept_details %>
52 <div id="on-accept-information-<%=task.id%>" style="display: none"> 52 <div id="on-accept-information-<%=task.id%>" style="display: none">
53 - <%= render :partial => partial_for_task_class(task.class, :accept_details), :locals => {:task => task, :f => f} %> 53 + <%= render :partial => partial_for_class(task.class, :accept_details), :locals => {:task => task, :f => f} %>
54 </div> 54 </div>
55 <% end %> 55 <% end %>
56 56
57 <% if task.reject_details %> 57 <% if task.reject_details %>
58 <div id="on-reject-information-<%=task.id%>" style="display: none"> 58 <div id="on-reject-information-<%=task.id%>" style="display: none">
59 - <%= render :partial => partial_for_task_class(task.class, :reject_details), :locals => {:task => task, :f => f} %> 59 + <%= render :partial => partial_for_class(task.class, :reject_details), :locals => {:task => task, :f => f} %>
60 </div> 60 </div>
61 <% end %> 61 <% end %>
62 <% end %> 62 <% end %>
app/views/user/mailer/signup_welcome_email.rhtml 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<!DOCTYPE html>
  2 +<html>
  3 + <head>
  4 + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  5 + </head>
  6 + <body>
  7 + <p><%= word_wrap @body %></p>
  8 + </body>
  9 +</html>
config/routes.rb
@@ -19,16 +19,17 @@ ActionController::Routing::Routes.draw do |map| @@ -19,16 +19,17 @@ ActionController::Routing::Routes.draw do |map|
19 19
20 # -- just remember to delete public/index.html. 20 # -- just remember to delete public/index.html.
21 # You can have the root of your site routed by hooking up '' 21 # You can have the root of your site routed by hooking up ''
  22 + map.root :controller => "home", :conditions => { :if => lambda { |env| !Domain.hosting_profile_at(env[:host]) } }
22 map.connect '', :controller => "home", :conditions => { :if => lambda { |env| !Domain.hosting_profile_at(env[:host]) } } 23 map.connect '', :controller => "home", :conditions => { :if => lambda { |env| !Domain.hosting_profile_at(env[:host]) } }
23 map.home 'site/:action', :controller => 'home' 24 map.home 'site/:action', :controller => 'home'
24 25
25 - map.connect 'images/*stuff', :controller => 'not_found', :action => 'index'  
26 - map.connect 'stylesheets/*stuff', :controller => 'not_found', :action => 'index'  
27 - map.connect 'designs/*stuff', :controller => 'not_found', :action => 'index'  
28 - map.connect 'articles/*stuff', :controller => 'not_found', :action => 'index'  
29 - map.connect 'javascripts/*stuff', :controller => 'not_found', :action => 'index'  
30 - map.connect 'thumbnails/*stuff', :controller => 'not_found', :action => 'index'  
31 - map.connect 'user_themes/*stuff', :controller => 'not_found', :action => 'index' 26 + map.connect 'images/*stuff', :controller => 'not_found', :action => 'nothing'
  27 + map.connect 'stylesheets/*stuff', :controller => 'not_found', :action => 'nothing'
  28 + map.connect 'designs/*stuff', :controller => 'not_found', :action => 'nothing'
  29 + map.connect 'articles/*stuff', :controller => 'not_found', :action => 'nothing'
  30 + map.connect 'javascripts/*stuff', :controller => 'not_found', :action => 'nothing'
  31 + map.connect 'thumbnails/*stuff', :controller => 'not_found', :action => 'nothing'
  32 + map.connect 'user_themes/*stuff', :controller => 'not_found', :action => 'nothing'
32 33
33 # online documentation 34 # online documentation
34 map.doc 'doc', :controller => 'doc', :action => 'index' 35 map.doc 'doc', :controller => 'doc', :action => 'index'
db/migrate/20120823215007_add_languages_and_default_language_to_environment.rb 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +class AddLanguagesAndDefaultLanguageToEnvironment < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :environments, :languages, :string
  4 + add_column :environments, :default_language, :string
  5 + end
  6 +
  7 + def self.down
  8 + remove_column :environments, :languages
  9 + remove_column :environments, :default_language
  10 + end
  11 +end
db/migrate/20120824165019_add_permission_for_send_mail_to_members_to_admin_and_moderator_roles.rb 0 → 100644
@@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
  1 +class AddPermissionForSendMailToMembersToAdminAndModeratorRoles < ActiveRecord::Migration
  2 + def self.up
  3 + Environment.all.map(&:id).each do |id|
  4 + role = Profile::Roles.admin(id)
  5 + role.permissions += ['send_mail_to_members']
  6 + role.save!
  7 + role = Profile::Roles.moderator(id)
  8 + role.permissions += ['send_mail_to_members']
  9 + role.save!
  10 + end
  11 + end
  12 +
  13 + def self.down
  14 + Environment.all.map(&:id).each do |id|
  15 + role = Profile::Roles.admin(id)
  16 + role.permissions -= ['send_mail_to_members']
  17 + role.save!
  18 + role = Profile::Roles.moderator(id)
  19 + role.permissions -= ['send_mail_to_members']
  20 + role.save!
  21 + end
  22 + end
  23 +end
db/migrate/20120824183534_add_redirection_after_login_to_environment.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class AddRedirectionAfterLoginToEnvironment < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :environments, :redirection_after_login, :string, :default => 'keep_on_same_page'
  4 + end
  5 +
  6 + def self.down
  7 + remove_column :environments, :redirection_after_login
  8 + end
  9 +end
db/migrate/20120824184046_add_redirection_after_login_to_profiles.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class AddRedirectionAfterLoginToProfiles < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :profiles, :redirection_after_login, :string
  4 + end
  5 +
  6 + def self.down
  7 + remove_column :profiles, :redirection_after_login
  8 + end
  9 +end
db/migrate/20121008185303_add_signup_welcome_text.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class AddSignupWelcomeText < ActiveRecord::Migration
  2 + def self.up
  3 + add_column :environments, :signup_welcome_text, :text
  4 + end
  5 +
  6 + def self.down
  7 + remove_column :environments, :signup_welcome_text
  8 + end
  9 +end
@@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
9 # 9 #
10 # It's strongly recommended to check this file into your version control system. 10 # It's strongly recommended to check this file into your version control system.
11 11
12 -ActiveRecord::Schema.define(:version => 20120825185219) do 12 +ActiveRecord::Schema.define(:version => 20121008185303) do
13 13
14 create_table "abuse_reports", :force => true do |t| 14 create_table "abuse_reports", :force => true do |t|
15 t.integer "reporter_id" 15 t.integer "reporter_id"
@@ -261,6 +261,10 @@ ActiveRecord::Schema.define(:version =&gt; 20120825185219) do @@ -261,6 +261,10 @@ ActiveRecord::Schema.define(:version =&gt; 20120825185219) do
261 t.datetime "created_at" 261 t.datetime "created_at"
262 t.datetime "updated_at" 262 t.datetime "updated_at"
263 t.integer "reports_lower_bound", :default => 0, :null => false 263 t.integer "reports_lower_bound", :default => 0, :null => false
  264 + t.string "redirection_after_login", :default => "keep_on_same_page"
  265 + t.text "signup_welcome_text"
  266 + t.string "languages"
  267 + t.string "default_language"
264 end 268 end
265 269
266 create_table "external_feeds", :force => true do |t| 270 create_table "external_feeds", :force => true do |t|
@@ -438,6 +442,7 @@ ActiveRecord::Schema.define(:version =&gt; 20120825185219) do @@ -438,6 +442,7 @@ ActiveRecord::Schema.define(:version =&gt; 20120825185219) do
438 t.string "national_region_code" 442 t.string "national_region_code"
439 t.boolean "is_template", :default => false 443 t.boolean "is_template", :default => false
440 t.integer "template_id" 444 t.integer "template_id"
  445 + t.string "redirection_after_login"
441 end 446 end
442 447
443 add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id" 448 add_index "profiles", ["environment_id"], :name => "index_profiles_on_environment_id"
debian/changelog
@@ -4,6 +4,12 @@ noosfero (0.39.0~1) UNRELEASED; urgency=low @@ -4,6 +4,12 @@ noosfero (0.39.0~1) UNRELEASED; urgency=low
4 4
5 -- Antonio Terceiro <terceiro@debian.org> Thu, 30 Aug 2012 14:55:10 -0300 5 -- Antonio Terceiro <terceiro@debian.org> Thu, 30 Aug 2012 14:55:10 -0300
6 6
  7 +noosfero (0.38.3) unstable; urgency=low
  8 +
  9 + * Bugfixes release
  10 +
  11 + -- Daniela Soares Feitosa <daniela@colivre.coop.br> Wed, 07 Nov 2012 20:25:51 -0200
  12 +
7 noosfero (0.38.2) unstable; urgency=low 13 noosfero (0.38.2) unstable; urgency=low
8 14
9 * Bugfixes release 15 * Bugfixes release
features/login.feature
@@ -9,7 +9,8 @@ Feature: login @@ -9,7 +9,8 @@ Feature: login
9 | joaosilva | Joao Silva | 9 | joaosilva | Joao Silva |
10 10
11 Scenario: login from portal homepage 11 Scenario: login from portal homepage
12 - Given I am not logged in 12 + Given feature "allow_change_of_redirection_after_login" is disabled on environment
  13 + And I am not logged in
13 And I go to the homepage 14 And I go to the homepage
14 And I fill in the following: 15 And I fill in the following:
15 | Username | joaosilva | 16 | Username | joaosilva |
@@ -19,7 +20,8 @@ Feature: login @@ -19,7 +20,8 @@ Feature: login
19 And I should be logged in as "joaosilva" 20 And I should be logged in as "joaosilva"
20 21
21 Scenario: login from some profile page 22 Scenario: login from some profile page
22 - Given I am not logged in 23 + Given feature "allow_change_of_redirection_after_login" is disabled on environment
  24 + And I am not logged in
23 And the following users 25 And the following users
24 | login | name | 26 | login | name |
25 | mariasilva | Maria Silva | 27 | mariasilva | Maria Silva |
@@ -35,7 +37,8 @@ Feature: login @@ -35,7 +37,8 @@ Feature: login
35 Then I should be on Maria Silva's homepage 37 Then I should be on Maria Silva's homepage
36 38
37 Scenario: view my control panel 39 Scenario: view my control panel
38 - Given I am not logged in 40 + Given feature "allow_change_of_redirection_after_login" is disabled on environment
  41 + And I am not logged in
39 And I go to Joao Silva's control panel 42 And I go to Joao Silva's control panel
40 And I should be on login page 43 And I should be on login page
41 And I fill in the following: 44 And I fill in the following:
@@ -48,3 +51,146 @@ Feature: login @@ -48,3 +51,146 @@ Feature: login
48 Given I am logged in as "joaosilva" 51 Given I am logged in as "joaosilva"
49 And I go to login page 52 And I go to login page
50 Then I should be on Joao Silva's control panel 53 Then I should be on Joao Silva's control panel
  54 +
  55 + Scenario: stay on the same page after login if this is the environment default
  56 + Given feature "allow_change_of_redirection_after_login" is enabled on environment
  57 + And I am not logged in
  58 + And the environment is configured to stay on the same page after login
  59 + And the following users
  60 + | login | name |
  61 + | mariasilva | Maria Silva |
  62 + And the following articles
  63 + | owner | name | homepage |
  64 + | mariasilva | my home page | true |
  65 + And I go to Maria Silva's homepage
  66 + And I follow "Login"
  67 + And I fill in the following:
  68 + | Username | joaosilva |
  69 + | Password | 123456 |
  70 + When I press "Log in"
  71 + Then I should be on Maria Silva's homepage
  72 +
  73 + Scenario: go to site homepage if this is the environment default
  74 + Given feature "allow_change_of_redirection_after_login" is enabled on environment
  75 + And I am not logged in
  76 + And the environment is configured to redirect to site homepage after login
  77 + And I go to Joao Silva's homepage
  78 + And I follow "Login"
  79 + And I fill in the following:
  80 + | Username | joaosilva |
  81 + | Password | 123456 |
  82 + When I press "Log in"
  83 + Then I should be on the homepage
  84 +
  85 + Scenario: go to user profile after login if this is the environment default
  86 + Given feature "allow_change_of_redirection_after_login" is enabled on environment
  87 + And I am not logged in
  88 + And the environment is configured to redirect to user profile page after login
  89 + And I go to the homepage
  90 + And I follow "Login"
  91 + And I fill in the following:
  92 + | Username | joaosilva |
  93 + | Password | 123456 |
  94 + When I press "Log in"
  95 + Then I should be on Joao Silva's profile
  96 +
  97 + Scenario: go to profile homepage after login if this is the environment default
  98 + Given the following articles
  99 + | owner | name | body | homepage |
  100 + | joaosilva | Sample Article | This is an article | true |
  101 + And feature "allow_change_of_redirection_after_login" is enabled on environment
  102 + And I am not logged in
  103 + And the environment is configured to redirect to profile homepage after login
  104 + And I go to the homepage
  105 + And I follow "Login"
  106 + And I fill in the following:
  107 + | Username | joaosilva |
  108 + | Password | 123456 |
  109 + When I press "Log in"
  110 + Then I should be on Joao Silva's homepage
  111 +
  112 + Scenario: go to profile control panel after login if this is the environment default
  113 + Given feature "allow_change_of_redirection_after_login" is enabled on environment
  114 + And I am not logged in
  115 + And the environment is configured to redirect to profile control panel after login
  116 + And I go to the homepage
  117 + And I follow "Login"
  118 + And I fill in the following:
  119 + | Username | joaosilva |
  120 + | Password | 123456 |
  121 + When I press "Log in"
  122 + Then I should be on Joao Silva's control panel
  123 +
  124 + Scenario: stay on the same page after login if this is the profile default
  125 + Given feature "allow_change_of_redirection_after_login" is enabled on environment
  126 + And I am not logged in
  127 + And the environment is configured to redirect to site homepage after login
  128 + And the profile joaosilva is configured to stay on the same page after login
  129 + And the following users
  130 + | login | name |
  131 + | mariasilva | Maria Silva |
  132 + And the following articles
  133 + | owner | name | homepage |
  134 + | mariasilva | my home page | true |
  135 + And I go to Maria Silva's homepage
  136 + And I follow "Login"
  137 + And I fill in the following:
  138 + | Username | joaosilva |
  139 + | Password | 123456 |
  140 + When I press "Log in"
  141 + Then I should be on Maria Silva's homepage
  142 +
  143 + Scenario: go to site homepage if this is the profile default
  144 + Given feature "allow_change_of_redirection_after_login" is enabled on environment
  145 + And I am not logged in
  146 + And the environment is configured to stay on the same page after login
  147 + And the profile joaosilva is configured to redirect to site homepage after login
  148 + And I go to Joao Silva's homepage
  149 + And I follow "Login"
  150 + And I fill in the following:
  151 + | Username | joaosilva |
  152 + | Password | 123456 |
  153 + When I press "Log in"
  154 + Then I should be on the homepage
  155 +
  156 + Scenario: go to user profile after login if this is the profile default
  157 + Given feature "allow_change_of_redirection_after_login" is enabled on environment
  158 + And I am not logged in
  159 + And the environment is configured to stay on the same page after login
  160 + And the profile joaosilva is configured to redirect to user profile page after login
  161 + And I go to the homepage
  162 + And I follow "Login"
  163 + And I fill in the following:
  164 + | Username | joaosilva |
  165 + | Password | 123456 |
  166 + When I press "Log in"
  167 + Then I should be on Joao Silva's profile
  168 +
  169 + Scenario: go to profile homepage after login if this is the profile default
  170 + Given the following articles
  171 + | owner | name | body | homepage |
  172 + | joaosilva | Sample Article | This is an article | true |
  173 + And feature "allow_change_of_redirection_after_login" is enabled on environment
  174 + And I am not logged in
  175 + And the environment is configured to stay on the same page after login
  176 + And the profile joaosilva is configured to redirect to profile homepage after login
  177 + And I go to the homepage
  178 + And I follow "Login"
  179 + And I fill in the following:
  180 + | Username | joaosilva |
  181 + | Password | 123456 |
  182 + When I press "Log in"
  183 + Then I should be on Joao Silva's homepage
  184 +
  185 + Scenario: go to profile control panel after login if this is the profile default
  186 + Given feature "allow_change_of_redirection_after_login" is enabled on environment
  187 + And I am not logged in
  188 + And the environment is configured to stay on the same page after login
  189 + And the profile joaosilva is configured to redirect to profile control panel after login
  190 + And I go to the homepage
  191 + And I follow "Login"
  192 + And I fill in the following:
  193 + | Username | joaosilva |
  194 + | Password | 123456 |
  195 + When I press "Log in"
  196 + Then I should be on Joao Silva's control panel
features/send_email_to_organization_members.feature
1 Feature: send emails to organization members 1 Feature: send emails to organization members
2 - As a organization administrator 2 + As a organization administrator or moderator
3 I want to send email to all members 3 I want to send email to all members
4 4
5 Background: 5 Background:
6 Given the following users 6 Given the following users
7 | login | name | 7 | login | name |
8 | joaosilva | Joao Silva | 8 | joaosilva | Joao Silva |
  9 + | jose | Jose Silva |
  10 + | manoel | Manoel Silva |
9 And the following communities 11 And the following communities
10 | identifier | name | 12 | identifier | name |
11 | sample-community | Sample Community | 13 | sample-community | Sample Community |
12 And "Joao Silva" is admin of "Sample Community" 14 And "Joao Silva" is admin of "Sample Community"
  15 + And "Jose Silva" is moderator of "Sample Community"
  16 + And "Manoel Silva" is a member of "Sample Community"
13 17
14 Scenario: Cant access if not logged in 18 Scenario: Cant access if not logged in
15 Given I am not logged in 19 Given I am not logged in
16 - When I go to /myprofile/sample-community/profile_members/send_mail 20 + When I go to /profile/sample-community/send_mail
17 Then I should be on login page 21 Then I should be on login page
18 22
19 Scenario: Cant access as normal user 23 Scenario: Cant access as normal user
@@ -21,7 +25,7 @@ Feature: send emails to organization members @@ -21,7 +25,7 @@ Feature: send emails to organization members
21 | login | 25 | login |
22 | josesilva | 26 | josesilva |
23 And I am logged in as "josesilva" 27 And I am logged in as "josesilva"
24 - When I go to /myprofile/sample-community/profile_members/send_mail 28 + When I go to /profile/sample-community/send_mail
25 Then I should see "Access denied" 29 Then I should see "Access denied"
26 30
27 Scenario: Send e-mail to members 31 Scenario: Send e-mail to members
@@ -39,7 +43,7 @@ Feature: send emails to organization members @@ -39,7 +43,7 @@ Feature: send emails to organization members
39 And I follow "Send e-mail to members" 43 And I follow "Send e-mail to members"
40 And I fill in "body" with "We have some news" 44 And I fill in "body" with "We have some news"
41 When I press "Send" 45 When I press "Send"
42 - Then I should be on /myprofile/sample-community/profile_members/send_mail 46 + Then I should be on /profile/sample-community/send_mail
43 47
44 Scenario: Not send e-mail to members if body is blank 48 Scenario: Not send e-mail to members if body is blank
45 Given I am logged in as "joaosilva" 49 Given I am logged in as "joaosilva"
@@ -47,7 +51,7 @@ Feature: send emails to organization members @@ -47,7 +51,7 @@ Feature: send emails to organization members
47 And I follow "Send e-mail to members" 51 And I follow "Send e-mail to members"
48 And I fill in "Subject" with "Hello, user!" 52 And I fill in "Subject" with "Hello, user!"
49 When I press "Send" 53 When I press "Send"
50 - Then I should be on /myprofile/sample-community/profile_members/send_mail 54 + Then I should be on /profile/sample-community/send_mail
51 55
52 Scenario: Cancel creation of mailing 56 Scenario: Cancel creation of mailing
53 Given I am logged in as "joaosilva" 57 Given I am logged in as "joaosilva"
@@ -55,3 +59,34 @@ Feature: send emails to organization members @@ -55,3 +59,34 @@ Feature: send emails to organization members
55 And I follow "Send e-mail to members" 59 And I follow "Send e-mail to members"
56 When I follow "Cancel e-mail" 60 When I follow "Cancel e-mail"
57 Then I should be on Sample Community's members management 61 Then I should be on Sample Community's members management
  62 +
  63 + Scenario: Cant access if has no send_mail_to_members permission
  64 + Given I am logged in as "manoel"
  65 + When I go to /profile/sample-community/send_mail
  66 + Then I should see "Access denied"
  67 +
  68 + Scenario: Show button "Send e-Mail to members" of community to an moderator
  69 + Given I am logged in as "jose"
  70 + When I go to Sample Community's members page
  71 + Then I should see "Send e-mail to members" link
  72 +
  73 + Scenario: Not show button "Send e-Mail to members" if user has no right permission
  74 + Given I am logged in as "manoel"
  75 + When I go to Sample Community's members page
  76 + Then I should not see "Send e-mail to members" link
  77 +
  78 + Scenario: Redirect back to profile members page after send mail
  79 + Given I am logged in as "jose"
  80 + When I go to Sample Community's members page
  81 + And I follow "Send e-mail to members"
  82 + And I fill in "Subject" with "Hello, member!"
  83 + And I fill in "body" with "We have some news"
  84 + When I press "Send"
  85 + Then I should be on Sample Community's members page
  86 +
  87 + Scenario: Back to profile members page after cancel creation of mailing
  88 + Given I am logged in as "jose"
  89 + And I go to Sample Community's members page
  90 + And I follow "Send e-mail to members"
  91 + When I follow "Cancel e-mail"
  92 + Then I should be on Sample Community's members page
features/step_definitions/noosfero_steps.rb
@@ -355,6 +355,12 @@ Given /^&quot;(.+)&quot; is admin of &quot;(.+)&quot;$/ do |person, organization| @@ -355,6 +355,12 @@ Given /^&quot;(.+)&quot; is admin of &quot;(.+)&quot;$/ do |person, organization|
355 org.add_admin(user) 355 org.add_admin(user)
356 end 356 end
357 357
  358 +Given /^"(.+)" is moderator of "(.+)"$/ do |person, organization|
  359 + org = Profile.find_by_name(organization)
  360 + user = Profile.find_by_name(person)
  361 + org.add_moderator(user)
  362 +end
  363 +
358 Then /^"(.+)" should be admin of "(.+)"$/ do |person, organization| 364 Then /^"(.+)" should be admin of "(.+)"$/ do |person, organization|
359 org = Organization.find_by_name(organization) 365 org = Organization.find_by_name(organization)
360 user = Person.find_by_name(person) 366 user = Person.find_by_name(person)
@@ -706,3 +712,39 @@ When /^I make a AJAX request to (.*)$/ do |page| @@ -706,3 +712,39 @@ When /^I make a AJAX request to (.*)$/ do |page|
706 header 'X-Requested-With', 'XMLHttpRequest' 712 header 'X-Requested-With', 'XMLHttpRequest'
707 visit(path_to(page)) 713 visit(path_to(page))
708 end 714 end
  715 +
  716 +Given /^the environment is configured to (.*) after login$/ do |option|
  717 + redirection = case option
  718 + when 'stay on the same page'
  719 + 'keep_on_same_page'
  720 + when 'redirect to site homepage'
  721 + 'site_homepage'
  722 + when 'redirect to user profile page'
  723 + 'user_profile_page'
  724 + when 'redirect to profile homepage'
  725 + 'user_homepage'
  726 + when 'redirect to profile control panel'
  727 + 'user_control_panel'
  728 + end
  729 + environment = Environment.default
  730 + environment.redirection_after_login = redirection
  731 + environment.save
  732 +end
  733 +
  734 +Given /^the profile (.*) is configured to (.*) after login$/ do |profile, option|
  735 + redirection = case option
  736 + when 'stay on the same page'
  737 + 'keep_on_same_page'
  738 + when 'redirect to site homepage'
  739 + 'site_homepage'
  740 + when 'redirect to user profile page'
  741 + 'user_profile_page'
  742 + when 'redirect to profile homepage'
  743 + 'user_homepage'
  744 + when 'redirect to profile control panel'
  745 + 'user_control_panel'
  746 + end
  747 + profile = Profile.find_by_identifier(profile)
  748 + profile.redirection_after_login = redirection
  749 + profile.save
  750 +end
features/support/paths.rb
@@ -108,6 +108,9 @@ module NavigationHelpers @@ -108,6 +108,9 @@ module NavigationHelpers
108 when /the user data path/ 108 when /the user data path/
109 '/account/user_data' 109 '/account/user_data'
110 110
  111 + when /^(.+)'s members page/
  112 + '/profile/%s/members' % Profile.find_by_name($1).identifier
  113 +
111 # Add more mappings here. 114 # Add more mappings here.
112 # Here is a more fancy example: 115 # Here is a more fancy example:
113 # 116 #
lib/acts_as_having_boxes.rb
@@ -18,7 +18,7 @@ module ActsAsHavingBoxes @@ -18,7 +18,7 @@ module ActsAsHavingBoxes
18 @blocks = nil 18 @blocks = nil
19 end 19 end
20 if @blocks.nil? 20 if @blocks.nil?
21 - @blocks = boxes.inject([]) do |acc,obj| 21 + @blocks = boxes.includes(:blocks).inject([]) do |acc,obj|
22 acc.concat(obj.blocks) 22 acc.concat(obj.blocks)
23 end 23 end
24 @blocks.send(:extend, BlockArray) 24 @blocks.send(:extend, BlockArray)
lib/noosfero/plugin.rb
@@ -320,6 +320,37 @@ class Noosfero::Plugin @@ -320,6 +320,37 @@ class Noosfero::Plugin
320 nil 320 nil
321 end 321 end
322 322
  323 + # -> Add an alternative authentication method.
  324 + # Your plugin have to make the access control and return the logged user.
  325 + # returns = User
  326 + def alternative_authentication
  327 + nil
  328 + end
  329 +
  330 + # -> Adds adicional link to make the user authentication
  331 + # returns = lambda block that creates html code
  332 + def alternative_authentication_link
  333 + nil
  334 + end
  335 +
  336 + # -> Allow or not user registration
  337 + # returns = boolean
  338 + def allow_user_registration
  339 + true
  340 + end
  341 +
  342 + # -> Allow or not password recovery by users
  343 + # returns = boolean
  344 + def allow_password_recovery
  345 + true
  346 + end
  347 +
  348 + # -> Adds fields to the login form
  349 + # returns = lambda block that creates html code
  350 + def login_extra_contents
  351 + nil
  352 + end
  353 +
323 def method_missing(method, *args, &block) 354 def method_missing(method, *args, &block)
324 # This is a generic hotspot for all controllers on Noosfero. 355 # This is a generic hotspot for all controllers on Noosfero.
325 # If any plugin wants to define filters to run on any controller, the name of 356 # If any plugin wants to define filters to run on any controller, the name of
plugins/custom_forms/controllers/custom_forms_plugin_myprofile_controller.rb 0 → 100644
@@ -0,0 +1,147 @@ @@ -0,0 +1,147 @@
  1 +class CustomFormsPluginMyprofileController < MyProfileController
  2 +
  3 + protect 'post_content', :profile
  4 + def index
  5 + @forms = CustomFormsPlugin::Form.from(profile)
  6 + end
  7 +
  8 + def create
  9 + @form = CustomFormsPlugin::Form.new(:profile => profile)
  10 + @fields = []
  11 + @empty_field = CustomFormsPlugin::Field.new
  12 + if request.post?
  13 + begin
  14 + @form.update_attributes!(params[:form])
  15 + params[:fields] = format_kind(params[:fields])
  16 + params[:fields] = format_choices(params[:fields])
  17 + params[:fields] = set_form_id(params[:fields], @form.id)
  18 + create_fields(new_fields(params))
  19 + session['notice'] = _('Form created')
  20 + redirect_to :action => 'index'
  21 + rescue Exception => exception
  22 + logger.error(exception.to_s)
  23 + session['notice'] = _('Form could not be created')
  24 + end
  25 + end
  26 + end
  27 +
  28 + def edit
  29 + @form = CustomFormsPlugin::Form.find(params[:id])
  30 + @fields = @form.fields
  31 + @empty_field = CustomFormsPlugin::TextField.new
  32 + if request.post?
  33 + begin
  34 + @form.update_attributes!(params[:form])
  35 + params[:fields] = format_kind(params[:fields])
  36 + params[:fields] = format_choices(params[:fields])
  37 + remove_fields(params, @form)
  38 + create_fields(new_fields(params))
  39 + update_fields(edited_fields(params))
  40 + session['notice'] = _('Form updated')
  41 + redirect_to :action => 'index'
  42 + rescue Exception => exception
  43 + logger.error(exception.to_s)
  44 + session['notice'] = _('Form could not be updated')
  45 + end
  46 + end
  47 + end
  48 +
  49 + def remove
  50 + @form = CustomFormsPlugin::Form.find(params[:id])
  51 + begin
  52 + @form.destroy
  53 + session[:notice] = _('Form removed')
  54 + rescue
  55 + session[:notice] = _('Form could not be removed')
  56 + end
  57 + redirect_to :action => 'index'
  58 + end
  59 +
  60 + def submissions
  61 + @form = CustomFormsPlugin::Form.find(params[:id])
  62 + @submissions = @form.submissions
  63 + end
  64 +
  65 + def show_submission
  66 + @submission = CustomFormsPlugin::Submission.find(params[:id])
  67 + @form = @submission.form
  68 + end
  69 +
  70 + private
  71 +
  72 + def new_fields(params)
  73 + result = params[:fields].map {|id, hash| hash.has_key?(:real_id) ? nil : hash}.compact
  74 + result.delete_if {|field| field[:name].blank?}
  75 + result
  76 + end
  77 +
  78 + def edited_fields(params)
  79 + params[:fields].map {|id, hash| hash.has_key?(:real_id) ? hash : nil}.compact
  80 + end
  81 +
  82 + def create_fields(fields)
  83 + fields.each do |field|
  84 + case field[:type]
  85 + when 'text_field'
  86 + CustomFormsPlugin::TextField.create!(field)
  87 + when 'select_field'
  88 + CustomFormsPlugin::SelectField.create!(field)
  89 + else
  90 + CustomFormsPlugin::Field.create!(field)
  91 + end
  92 + end
  93 + end
  94 +
  95 + def update_fields(fields)
  96 + fields.each do |field_attrs|
  97 + field = CustomFormsPlugin::Field.find(field_attrs.delete(:real_id))
  98 + field.attributes = field_attrs
  99 + field.save! if field.changed?
  100 + end
  101 + end
  102 +
  103 + def format_kind(fields)
  104 + fields.each do |id, field|
  105 + next if field[:kind].blank?
  106 + kind = field.delete(:kind)
  107 + case kind
  108 + when 'radio'
  109 + field[:list] = false
  110 + field[:multiple] = false
  111 + when 'check_box'
  112 + field[:list] = false
  113 + field[:multiple] = true
  114 + when 'select'
  115 + field[:list] = true
  116 + field[:multiple] = false
  117 + when 'multiple_select'
  118 + field[:list] = true
  119 + field[:multiple] = true
  120 + end
  121 + end
  122 + fields
  123 + end
  124 +
  125 + def format_choices(fields)
  126 + fields.each do |id, field|
  127 + next if !field.has_key?(:choices)
  128 + field[:choices] = field[:choices].map {|key, value| value}.inject({}) do |result, choice|
  129 + hash = (choice[:name].blank? || choice[:value].blank?) ? {} : {choice[:name] => choice[:value]}
  130 + result.merge!(hash)
  131 + end
  132 + end
  133 + fields
  134 + end
  135 +
  136 + def remove_fields(params, form)
  137 + present_fields = params[:fields].map{|id, value| value}.collect {|field| field[:real_id]}.compact
  138 + form.fields.each {|field| field.destroy if !present_fields.include?(field.id.to_s) }
  139 + end
  140 +
  141 + def set_form_id(fields, form_id)
  142 + fields.each do |id, field|
  143 + field[:form_id] = form_id
  144 + end
  145 + fields
  146 + end
  147 +end
plugins/custom_forms/controllers/custom_forms_plugin_profile_controller.rb 0 → 100644
@@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
  1 +class CustomFormsPluginProfileController < ProfileController
  2 +
  3 + before_filter :has_access, :show
  4 +
  5 + def show
  6 + @form = CustomFormsPlugin::Form.find(params[:id])
  7 + if user
  8 + @submission ||= CustomFormsPlugin::Submission.find_by_form_id_and_profile_id(@form.id,user.id)
  9 + @submission ||= CustomFormsPlugin::Submission.new(:form_id => @form.id, :profile_id => user.id)
  10 + else
  11 + @submission ||= CustomFormsPlugin::Submission.new(:form_id => @form.id)
  12 + end
  13 + if request.post?
  14 + begin
  15 + extend(CustomFormsPlugin::Helper)
  16 + answers = build_answers(params[:submission], @form)
  17 + failed_answers = answers.select {|answer| !answer.valid? }
  18 + if failed_answers.empty?
  19 + if !user
  20 + @submission.author_name = params[:author_name]
  21 + @submission.author_email = params[:author_email]
  22 + end
  23 + @submission.save!
  24 + answers.map {|answer| answer.submission = @submission; answer.save!}
  25 + else
  26 + @submission.valid?
  27 + failed_answers.each do |answer|
  28 + @submission.errors.add(answer.field.name.to_sym, answer.errors[answer.field.slug.to_sym])
  29 + end
  30 + end
  31 + session[:notice] = _('Submission saved')
  32 + redirect_to :action => 'show'
  33 + rescue
  34 + session[:notice] = _('Submission could not be saved')
  35 + end
  36 + end
  37 + end
  38 +
  39 + private
  40 +
  41 + def has_access
  42 + form = CustomFormsPlugin::Form.find(params[:id])
  43 + render_access_denied if !form.accessible_to(user)
  44 + end
  45 +end
plugins/custom_forms/db/migrate/20120727162444_create_custom_forms_plugin_forms.rb 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +class CreateCustomFormsPluginForms < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :custom_forms_plugin_forms do |t|
  4 + t.string :name
  5 + t.string :slug
  6 + t.text :description
  7 + t.references :profile
  8 + t.datetime :begining
  9 + t.datetime :ending
  10 + t.boolean :report_submissions, :default => false
  11 + t.boolean :on_membership, :default => false
  12 + t.string :access
  13 + t.timestamps
  14 + end
  15 + end
  16 +
  17 + def self.down
  18 + drop_table :custom_forms_plugin_forms
  19 + end
  20 +end
plugins/custom_forms/db/migrate/20120727174506_create_custom_forms_plugin_fields.rb 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +class CreateCustomFormsPluginFields < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :custom_forms_plugin_fields do |t|
  4 + t.string :name
  5 + t.string :slug
  6 + t.string :type
  7 + t.string :default_value
  8 + t.string :choices
  9 + t.float :minimum
  10 + t.float :maximum
  11 + t.references :form
  12 + t.boolean :mandatory, :default => false
  13 + t.boolean :multiple
  14 + t.boolean :list
  15 + end
  16 + end
  17 +
  18 + def self.down
  19 + drop_table :custom_forms_plugin_fields
  20 + end
  21 +end
plugins/custom_forms/db/migrate/20120727175250_create_custom_forms_plugin_answers.rb 0 → 100644
@@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
  1 +class CreateCustomFormsPluginAnswers < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :custom_forms_plugin_answers do |t|
  4 + t.text :value
  5 + t.references :field
  6 + t.references :submission
  7 + end
  8 + end
  9 +
  10 + def self.down
  11 + drop_table :custom_forms_plugin_answers
  12 + end
  13 +end
plugins/custom_forms/db/migrate/20120727180512_create_custom_forms_plugin_submissions.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +class CreateCustomFormsPluginSubmissions < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :custom_forms_plugin_submissions do |t|
  4 + t.string :author_name
  5 + t.string :author_email
  6 + t.references :profile
  7 + t.references :form
  8 + t.timestamps
  9 + end
  10 + end
  11 +
  12 + def self.down
  13 + drop_table :custom_forms_plugin_submissions
  14 + end
  15 +end
plugins/custom_forms/lib/custom_forms_plugin.rb 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +require 'ext/role_assignment_trigger'
  2 +
  3 +class CustomFormsPlugin < Noosfero::Plugin
  4 +
  5 + def self.plugin_name
  6 + "Custom Forms"
  7 + end
  8 +
  9 + def self.plugin_description
  10 + _("Enables the creation of forms.")
  11 + end
  12 +
  13 + def stylesheet?
  14 + true
  15 + end
  16 +
  17 + def control_panel_buttons
  18 + {:title => _('Manage Forms'), :icon => 'custom-forms', :url => {:controller => 'custom_forms_plugin_myprofile'}}
  19 + end
  20 +
  21 +end
plugins/custom_forms/lib/custom_forms_plugin/answer.rb 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +class CustomFormsPlugin::Answer < Noosfero::Plugin::ActiveRecord
  2 + belongs_to :field, :class_name => 'CustomFormsPlugin::Field'
  3 + belongs_to :submission, :class_name => 'CustomFormsPlugin::Submission'
  4 +
  5 + validates_presence_of :field
  6 + validate :value_mandatory, :if => 'field.present?'
  7 +
  8 + def value_mandatory
  9 + if field.mandatory && value.blank?
  10 + errors.add(field.slug.to_sym, _("is mandatory.").fix_i18n)
  11 + end
  12 + end
  13 +end
  14 +
plugins/custom_forms/lib/custom_forms_plugin/field.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +class CustomFormsPlugin::Field < ActiveRecord::Base
  2 + set_table_name :custom_forms_plugin_fields
  3 +
  4 + validates_presence_of :form, :name
  5 + validates_uniqueness_of :slug, :scope => :form_id
  6 +
  7 + belongs_to :form, :class_name => 'CustomFormsPlugin::Form', :dependent => :destroy
  8 + has_many :answers, :class_name => 'CustomFormsPlugin::Answer'
  9 +
  10 + serialize :choices, Hash
  11 +
  12 + before_validation do |field|
  13 + field.slug = field.name.to_slug if field.name.present?
  14 + end
  15 +end
  16 +