Commit d29ca9e2cc8fff7951cb1be4787575334704a3d1

Authored by Rodrigo Souto
2 parents 0972e95b c456fe22

Merge branch 'master' into chat

Conflicts:
	public/stylesheets/application.css
Showing 359 changed files with 15972 additions and 1016 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 359 files displayed.

1 source "https://rubygems.org" 1 source "https://rubygems.org"
2 -gem 'rails', '~> 3.2.19' 2 +gem 'rails', '~> 3.2.21'
3 gem 'minitest', '~> 3.2.0' 3 gem 'minitest', '~> 3.2.0'
4 gem 'fast_gettext', '~> 0.6.8' 4 gem 'fast_gettext', '~> 0.6.8'
5 gem 'acts-as-taggable-on', '~> 3.0.2' 5 gem 'acts-as-taggable-on', '~> 3.0.2'
@@ -21,6 +21,8 @@ gem 'exception_notification', '~> 4.0.1' @@ -21,6 +21,8 @@ gem 'exception_notification', '~> 4.0.1'
21 gem 'gettext', '~> 2.2.1', :require => false, :group => :development 21 gem 'gettext', '~> 2.2.1', :require => false, :group => :development
22 gem 'locale', '~> 2.0.5' 22 gem 'locale', '~> 2.0.5'
23 23
  24 +gem 'whenever', :require => false
  25 +
24 # FIXME list here all actual dependencies (i.e. the ones in debian/control), 26 # FIXME list here all actual dependencies (i.e. the ones in debian/control),
25 # with their GEM names (not the Debian package names) 27 # with their GEM names (not the Debian package names)
26 28
MIGRATION_ISSUES
@@ -1,41 +0,0 @@ @@ -1,41 +0,0 @@
1 -* ruby-get-text incmopatible with rails3. Maybe we can use it's gem  
2 -  
3 -* all js code is inside miscellaneous.js. Would be nice to refactor this  
4 -  
5 -* rails 2 uses prototype instead of jquery  
6 -  
7 -* config/environment.rb maybe still have some code that should be on the initializers  
8 -  
9 -* initializers session_store.rb inflections.rb... don't exist  
10 -  
11 -* rails gems version have to be forced on Gemfile or it will use incompatible pre3vious versions (3.1.3)  
12 -  
13 -* Sweepers are now natively supported on Rails 3. Would be nice to refactor it  
14 -  
15 -* On Rails 3 it is no more possible to add allowed tags to avoid scape. The html_safe initializer is an option.  
16 -  
17 -* error when call sqlite_extensiosn  
18 -  
19 -* error related to action_tracker  
20 -  
21 -* check FIXME's in script/quick-start  
22 -  
23 -* check FIXME's in Gemfile  
24 -  
25 -* Check the FIXME in config/routes.rb  
26 -  
27 -* rewrite conditional routing. See FIXME in lib/route_if.rb and re-implement using the Rails 3 mechanism - http://guides.rubyonrails.org/routing.html#advanced-constraints  
28 -  
29 -* check FIXME's in config/environment.rb  
30 -  
31 -* xss_terminate sucks. We should replace it with the builtin mechanism in Rails 3  
32 -  
33 -* instance_eval on Ruby 1.9 yields self, so lambdas that are passed to instance_eval and do not accept exactly 1 argument will blow up. See http://www.ruby-forum.com/topic/213313 ... search for instance_eval and fix where necessary. In special, most of the blocks still need fixing.  
34 -  
35 -* all instances of <% *_form_for ... %> must be changed to <%= instead of <%  
36 -  
37 -* all ActiveRecord models have to declare explicitly which attributes must be allowed for mass assignment with attr_accessible.  
38 -  
39 -* check if we need to update config/locales/*  
40 -  
41 -* check observe_field and labelled_form_for in app/helpers/application_helper.rb  
app/controllers/admin/region_validators_controller.rb
@@ -33,7 +33,7 @@ class RegionValidatorsController &lt; AdminController @@ -33,7 +33,7 @@ class RegionValidatorsController &lt; AdminController
33 def load_region_and_search 33 def load_region_and_search
34 @region = environment.regions.find(params[:id]) 34 @region = environment.regions.find(params[:id])
35 if params[:search] 35 if params[:search]
36 - @search = find_by_contents(:organizations, Organization, params[:search])[:results].reject {|item| @region.validator_ids.include?(item.id) } 36 + @search = find_by_contents(:organizations, environment, Organization, params[:search])[:results].reject {|item| @region.validator_ids.include?(item.id) }
37 end 37 end
38 end 38 end
39 39
app/controllers/admin/users_controller.rb
@@ -18,7 +18,7 @@ class UsersController &lt; AdminController @@ -18,7 +18,7 @@ class UsersController &lt; AdminController
18 end 18 end
19 scope = scope.order('name ASC') 19 scope = scope.order('name ASC')
20 @q = params[:q] 20 @q = params[:q]
21 - @collection = find_by_contents(:people, scope, @q, {:per_page => per_page, :page => params[:npage]})[:results] 21 + @collection = find_by_contents(:people, environment, scope, @q, {:per_page => per_page, :page => params[:npage]})[:results]
22 end 22 end
23 23
24 def set_admin_role 24 def set_admin_role
app/controllers/application_controller.rb
@@ -183,21 +183,19 @@ class ApplicationController &lt; ActionController::Base @@ -183,21 +183,19 @@ class ApplicationController &lt; ActionController::Base
183 end 183 end
184 end 184 end
185 185
186 - def find_by_contents(asset, scope, query, paginate_options={:page => 1}, options={})  
187 - plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options) ||  
188 - fallback_find_by_contents(asset, scope, query, paginate_options, options)  
189 - end 186 + include SearchTermHelper
190 187
191 - private 188 + def find_by_contents(asset, context, scope, query, paginate_options={:page => 1}, options={})
  189 + search = plugins.dispatch_first(:find_by_contents, asset, scope, query, paginate_options, options)
  190 + register_search_term(query, scope.count, search[:results].count, context, asset)
  191 + search
  192 + end
192 193
193 - def fallback_find_by_contents(asset, scope, query, paginate_options, options)  
194 - scope = scope.like_search(query) unless query.blank?  
195 - scope = scope.send(options[:filter]) unless options[:filter].blank?  
196 - {:results => scope.paginate(paginate_options)} 194 + def find_suggestions(query, context, asset, options={})
  195 + plugins.dispatch_first(:find_suggestions, query, context, asset, options)
197 end 196 end
198 197
199 def private_environment? 198 def private_environment?
200 @environment.enabled?(:restrict_to_members) 199 @environment.enabled?(:restrict_to_members)
201 end 200 end
202 -  
203 end 201 end
app/controllers/my_profile/cms_controller.rb
@@ -23,6 +23,9 @@ class CmsController &lt; MyProfileController @@ -23,6 +23,9 @@ class CmsController &lt; MyProfileController
23 end 23 end
24 24
25 before_filter :login_required, :except => [:suggest_an_article] 25 before_filter :login_required, :except => [:suggest_an_article]
  26 + before_filter :load_recent_files, :only => [:new, :edit]
  27 +
  28 + helper_method :file_types
26 29
27 protect_if :only => :upload_files do |c, user, profile| 30 protect_if :only => :upload_files do |c, user, profile|
28 article_id = c.params[:parent_id] 31 article_id = c.params[:parent_id]
@@ -30,7 +33,7 @@ class CmsController &lt; MyProfileController @@ -30,7 +33,7 @@ class CmsController &lt; MyProfileController
30 (user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile))) 33 (user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile)))
31 end 34 end
32 35
33 - protect_if :except => [:suggest_an_article, :set_home_page, :edit, :destroy, :publish, :upload_files, :new] do |c, user, profile| 36 + protect_if :except => [:suggest_an_article, :set_home_page, :edit, :destroy, :publish, :publish_on_portal_community, :publish_on_communities, :search_communities_to_publish, :upload_files, :new] do |c, user, profile|
34 user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile)) 37 user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile))
35 end 38 end
36 39
@@ -40,7 +43,7 @@ class CmsController &lt; MyProfileController @@ -40,7 +43,7 @@ class CmsController &lt; MyProfileController
40 (user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile))) 43 (user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile)))
41 end 44 end
42 45
43 - protect_if :only => [:destroy, :publish] do |c, user, profile| 46 + protect_if :only => :destroy do |c, user, profile|
44 profile.articles.find(c.params[:id]).allow_post_content?(user) 47 profile.articles.find(c.params[:id]).allow_post_content?(user)
45 end 48 end
46 49
@@ -117,7 +120,7 @@ class CmsController &lt; MyProfileController @@ -117,7 +120,7 @@ class CmsController &lt; MyProfileController
117 @success_back_to = params[:success_back_to] 120 @success_back_to = params[:success_back_to]
118 # user must choose an article type first 121 # user must choose an article type first
119 122
120 - @parent = profile.articles.find(params[:parent_id]) if params && params[:parent_id] 123 + @parent = profile.articles.find(params[:parent_id]) if params && params[:parent_id].present?
121 record_coming 124 record_coming
122 @type = params[:type] 125 @type = params[:type]
123 if @type.blank? 126 if @type.blank?
@@ -163,7 +166,10 @@ class CmsController &lt; MyProfileController @@ -163,7 +166,10 @@ class CmsController &lt; MyProfileController
163 if continue 166 if continue
164 redirect_to :action => 'edit', :id => @article 167 redirect_to :action => 'edit', :id => @article
165 else 168 else
166 - success_redirect 169 + respond_to do |format|
  170 + format.html { success_redirect }
  171 + format.json { render :text => {:id => @article.id, :full_name => profile.identifier + '/' + @article.full_name}.to_json }
  172 + end
167 end 173 end
168 return 174 return
169 end 175 end
@@ -256,28 +262,53 @@ class CmsController &lt; MyProfileController @@ -256,28 +262,53 @@ class CmsController &lt; MyProfileController
256 render :template => 'shared/update_categories', :locals => { :category => @current_category, :object_name => 'article' } 262 render :template => 'shared/update_categories', :locals => { :category => @current_category, :object_name => 'article' }
257 end 263 end
258 264
  265 + def search_communities_to_publish
  266 + render :text => find_by_contents(:profiles, environment, user.memberships, params['q'], {:page => 1}, {:fields => ['name']})[:results].map {|community| {:id => community.id, :name => community.name} }.to_json
  267 + end
  268 +
259 def publish 269 def publish
260 @article = profile.articles.find(params[:id]) 270 @article = profile.articles.find(params[:id])
261 record_coming 271 record_coming
262 - @groups = profile.memberships - [profile]  
263 - @marked_groups = []  
264 - groups_ids = profile.memberships.map{|m|m.id.to_s}  
265 - @marked_groups = params[:marked_groups].map do |key, item|  
266 - if groups_ids.include?(item[:group_id])  
267 - item.merge :group => Profile.find(item.delete(:group_id)) 272 + @failed = {}
  273 + if request.post?
  274 + article_name = params[:name]
  275 + task = ApproveArticle.create!(:article => @article, :name => article_name, :target => user, :requestor => user)
  276 + begin
  277 + task.finish
  278 + rescue Exception => ex
  279 + @failed[ex.message] ? @failed[ex.message] << @article.name : @failed[ex.message] = [@article.name]
  280 + task.cancel
  281 + end
  282 + if @failed.blank?
  283 + session[:notice] = _("Your publish request was sent successfully")
  284 + if @back_to
  285 + redirect_to @back_to
  286 + else
  287 + redirect_to @article.view_url
  288 + end
268 end 289 end
269 - end.compact unless params[:marked_groups].nil? 290 + end
  291 + end
  292 +
  293 + def publish_on_communities
270 if request.post? 294 if request.post?
  295 + @back_to = params[:back_to]
  296 + @article = profile.articles.find(params[:id])
271 @failed = {} 297 @failed = {}
  298 + article_name = params[:name]
  299 + params_marked = (params['q'] || '').split(',').select { |marked| user.memberships.map(&:id).include? marked.to_i }
  300 + @marked_groups = Profile.find(params_marked)
272 if @marked_groups.empty? 301 if @marked_groups.empty?
  302 + redirect_to @back_to
273 return session[:notice] = _("Select some group to publish your article") 303 return session[:notice] = _("Select some group to publish your article")
274 end 304 end
275 @marked_groups.each do |item| 305 @marked_groups.each do |item|
276 - task = ApproveArticle.create!(:article => @article, :name => item[:name], :target => item[:group], :requestor => profile) 306 + task = ApproveArticle.create!(:article => @article, :name => article_name, :target => item, :requestor => user)
277 begin 307 begin
278 - task.finish unless item[:group].moderated_articles? 308 + task.finish unless item.moderated_articles?
279 rescue Exception => ex 309 rescue Exception => ex
280 - @failed[ex.message] ? @failed[ex.message] << item[:group].name : @failed[ex.message] = [item[:group].name] 310 + @failed[ex.message] ? @failed[ex.message] << item.name : @failed[ex.message] = [item.name]
  311 + task.cancel
281 end 312 end
282 end 313 end
283 if @failed.blank? 314 if @failed.blank?
@@ -287,23 +318,27 @@ class CmsController &lt; MyProfileController @@ -287,23 +318,27 @@ class CmsController &lt; MyProfileController
287 else 318 else
288 redirect_to @article.view_url 319 redirect_to @article.view_url
289 end 320 end
  321 + else
  322 + session[:notice] = _("Some of your publish requests couldn't be sent.")
  323 + render :action => 'publish'
290 end 324 end
291 end 325 end
292 end 326 end
293 327
294 def publish_on_portal_community 328 def publish_on_portal_community
295 - @article = profile.articles.find(params[:id])  
296 if request.post? 329 if request.post?
297 - if environment.portal_community 330 + @article = profile.articles.find(params[:id])
  331 + if environment.portal_enabled
298 task = ApproveArticle.create!(:article => @article, :name => params[:name], :target => environment.portal_community, :requestor => user) 332 task = ApproveArticle.create!(:article => @article, :name => params[:name], :target => environment.portal_community, :requestor => user)
299 begin 333 begin
300 task.finish unless environment.portal_community.moderated_articles? 334 task.finish unless environment.portal_community.moderated_articles?
301 - flash[:notice] = _("Your publish request was sent successfully") 335 + session[:notice] = _("Your publish request was sent successfully")
302 rescue 336 rescue
303 - flash[:error] = _("Your publish request couldn't be sent.") 337 + session[:notice] = _("Your publish request couldn't be sent.")
  338 + task.cancel
304 end 339 end
305 else 340 else
306 - flash[:notice] = _("There is no portal community to publish your article.") 341 + session[:notice] = _("There is no portal community to publish your article.")
307 end 342 end
308 343
309 if @back_to 344 if @back_to
@@ -331,7 +366,7 @@ class CmsController &lt; MyProfileController @@ -331,7 +366,7 @@ class CmsController &lt; MyProfileController
331 366
332 def search 367 def search
333 query = params[:q] 368 query = params[:q]
334 - results = find_by_contents(:uploaded_files, profile.files.published, query)[:results] 369 + results = find_by_contents(:uploaded_files, profile, profile.files.published, query)[:results]
335 render :text => article_list_to_json(results), :content_type => 'application/json' 370 render :text => article_list_to_json(results), :content_type => 'application/json'
336 end 371 end
337 372
@@ -342,15 +377,26 @@ class CmsController &lt; MyProfileController @@ -342,15 +377,26 @@ class CmsController &lt; MyProfileController
342 end 377 end
343 378
344 def media_upload 379 def media_upload
345 - files_uploaded = []  
346 parent = check_parent(params[:parent_id]) 380 parent = check_parent(params[:parent_id])
347 - files = [:file1,:file2, :file3].map { |f| params[f] }.compact  
348 if request.post? 381 if request.post?
349 - files.each do |file|  
350 - files_uploaded << UploadedFile.create(:uploaded_data => file, :profile => profile, :parent => parent) unless file == '' 382 + begin
  383 + @file = UploadedFile.create!(:uploaded_data => params[:file], :profile => profile, :parent => parent) unless params[:file] == ''
  384 + @file = FilePresenter.for(@file)
  385 + rescue Exception => exception
  386 + render :text => exception.to_s, :status => :bad_request
351 end 387 end
352 end 388 end
353 - render :text => article_list_to_json(files_uploaded), :content_type => 'text/plain' 389 + end
  390 +
  391 + def published_media_items
  392 + load_recent_files(params[:parent_id], params[:q])
  393 + render :partial => 'published_media_items'
  394 + end
  395 +
  396 + def view_all_media
  397 + paginate_options = {:page => params[:page].blank? ? 1 : params[:page] }
  398 + @key = params[:key].to_sym
  399 + load_recent_files(params[:parent_id], params[:q], paginate_options)
354 end 400 end
355 401
356 protected 402 protected
@@ -445,4 +491,36 @@ class CmsController &lt; MyProfileController @@ -445,4 +491,36 @@ class CmsController &lt; MyProfileController
445 end 491 end
446 end 492 end
447 493
  494 + def file_types
  495 + {:images => _('Images'), :generics => _('Files')}
  496 + end
  497 +
  498 + def load_recent_files(parent_id = nil, q = nil, paginate_options = {:page => 1, :per_page => 6})
  499 + #TODO Since we only have special support for images, I'm limiting myself to
  500 + # consider generic files as non-images. In the future, with more supported
  501 + # file types we'll need to have a smart way to fetch from the database
  502 + # scopes of each supported type as well as the non-supported types as a
  503 + # whole.
  504 + @recent_files = {}
  505 +
  506 + parent = parent_id.present? ? profile.articles.find(parent_id) : nil
  507 + if parent.present?
  508 + files = parent.children.files
  509 + else
  510 + files = profile.files
  511 + end
  512 +
  513 + files = files.reorder('created_at DESC')
  514 + images = files.images
  515 + generics = files.no_images
  516 +
  517 + if q.present?
  518 + @recent_files[:images] = find_by_contents(:images, profile, images, q, paginate_options)[:results]
  519 + @recent_files[:generics] = find_by_contents(:generics, profile, generics, q, paginate_options)[:results]
  520 + else
  521 + @recent_files[:images] = images.paginate(paginate_options)
  522 + @recent_files[:generics] = generics.paginate(paginate_options)
  523 + end
  524 + end
  525 +
448 end 526 end
app/controllers/my_profile/friends_controller.rb
@@ -3,6 +3,7 @@ class FriendsController &lt; MyProfileController @@ -3,6 +3,7 @@ class FriendsController &lt; MyProfileController
3 protect 'manage_friends', :profile 3 protect 'manage_friends', :profile
4 4
5 def index 5 def index
  6 + @suggestions = profile.profile_suggestions.of_person.enabled.includes(:suggestion).limit(per_page)
6 if is_cache_expired?(profile.manage_friends_cache_key(params)) 7 if is_cache_expired?(profile.manage_friends_cache_key(params))
7 @friends = profile.friends.paginate(:per_page => per_page, :page => params[:npage]) 8 @friends = profile.friends.paginate(:per_page => per_page, :page => params[:npage])
8 end 9 end
@@ -16,6 +17,30 @@ class FriendsController &lt; MyProfileController @@ -16,6 +17,30 @@ class FriendsController &lt; MyProfileController
16 end 17 end
17 end 18 end
18 19
  20 + def suggest
  21 + @suggestions = profile.profile_suggestions.of_person.enabled.includes(:suggestion).limit(per_page)
  22 + end
  23 +
  24 + def remove_suggestion
  25 + @person = profile.suggested_people.find_by_identifier(params[:id])
  26 + redirect_to :action => 'suggest' unless @person
  27 + if @person && request.post?
  28 + profile.remove_suggestion(@person)
  29 + @suggestions = profile.profile_suggestions.of_person.enabled.includes(:suggestion).limit(per_page)
  30 + render :partial => 'shared/profile_suggestions_list', :locals => { :suggestions => @suggestions, :collection => :friends_suggestions, :per_page => params[:per_page] || per_page }
  31 + end
  32 + end
  33 +
  34 + def connections
  35 + @suggestion = profile.profile_suggestions.of_person.enabled.find_by_suggestion_id(params[:id])
  36 + if @suggestion
  37 + @tags = @suggestion.tag_connections
  38 + @profiles = @suggestion.profile_connections
  39 + else
  40 + redirect_to :action => 'suggest'
  41 + end
  42 + end
  43 +
19 protected 44 protected
20 45
21 class << self 46 class << self
app/controllers/my_profile/memberships_controller.rb
@@ -20,12 +20,54 @@ class MembershipsController &lt; MyProfileController @@ -20,12 +20,54 @@ class MembershipsController &lt; MyProfileController
20 @community.environment = environment 20 @community.environment = environment
21 @back_to = params[:back_to] || url_for(:action => 'index') 21 @back_to = params[:back_to] || url_for(:action => 'index')
22 if request.post? && @community.valid? 22 if request.post? && @community.valid?
23 - @community = Community.create_after_moderation(user, params[:community].merge({:environment => environment}))  
24 - if @community.new_record? 23 + begin
  24 + # Community was created
  25 + @community = Community.create_after_moderation(user, params[:community].merge({:environment => environment}))
  26 + @community.reload
  27 + redirect_to :action => 'welcome', :community_id => @community.id, :back_to => @back_to
  28 + rescue ActiveRecord::RecordNotFound
  29 + # Community pending approval
25 session[:notice] = _('Your new community creation request will be evaluated by an administrator. You will be notified.') 30 session[:notice] = _('Your new community creation request will be evaluated by an administrator. You will be notified.')
  31 + redirect_to @back_to
26 end 32 end
27 - redirect_to @back_to  
28 return 33 return
29 end 34 end
30 end 35 end
  36 +
  37 + def welcome
  38 + @community = Community.find(params[:community_id])
  39 + @back_to = params[:back_to]
  40 + end
  41 +
  42 + def suggest
  43 + @suggestions = profile.profile_suggestions.of_community.enabled.includes(:suggestion).limit(per_page)
  44 + end
  45 +
  46 + def remove_suggestion
  47 + @community = profile.suggested_communities.find_by_identifier(params[:id])
  48 + custom_per_page = params[:per_page] || per_page
  49 + redirect_to :action => 'suggest' unless @community
  50 + if @community && request.post?
  51 + profile.remove_suggestion(@community)
  52 + @suggestions = profile.profile_suggestions.of_community.enabled.includes(:suggestion).limit(custom_per_page)
  53 + render :partial => 'shared/profile_suggestions_list', :locals => { :suggestions => @suggestions, :collection => :communities_suggestions, :per_page => custom_per_page}
  54 + end
  55 + end
  56 +
  57 + def connections
  58 + @suggestion = profile.profile_suggestions.of_community.enabled.find_by_suggestion_id(params[:id])
  59 + if @suggestion
  60 + @tags = @suggestion.tag_connections
  61 + @profiles = @suggestion.profile_connections
  62 + else
  63 + redirect_to :action => 'suggest'
  64 + end
  65 + end
  66 +
  67 + protected
  68 +
  69 + def per_page
  70 + 12
  71 + end
  72 +
31 end 73 end
app/controllers/my_profile/profile_editor_controller.rb
@@ -3,6 +3,10 @@ class ProfileEditorController &lt; MyProfileController @@ -3,6 +3,10 @@ class ProfileEditorController &lt; MyProfileController
3 protect 'edit_profile', :profile, :except => [:destroy_profile] 3 protect 'edit_profile', :profile, :except => [:destroy_profile]
4 protect 'destroy_profile', :profile, :only => [:destroy_profile] 4 protect 'destroy_profile', :profile, :only => [:destroy_profile]
5 5
  6 + before_filter :access_welcome_page, :only => [:welcome_page]
  7 + before_filter :back_to
  8 + helper_method :has_welcome_page
  9 +
6 def index 10 def index
7 @pending_tasks = Task.to(profile).pending.without_spam.select{|i| user.has_permission?(i.permission, profile)} 11 @pending_tasks = Task.to(profile).pending.without_spam.select{|i| user.has_permission?(i.permission, profile)}
8 end 12 end
@@ -85,6 +89,21 @@ class ProfileEditorController &lt; MyProfileController @@ -85,6 +89,21 @@ class ProfileEditorController &lt; MyProfileController
85 end 89 end
86 end 90 end
87 91
  92 + def welcome_page
  93 + @welcome_page = profile.welcome_page || TinyMceArticle.new(:name => 'Welcome Page', :profile => profile, :published => false)
  94 + if request.post?
  95 + begin
  96 + @welcome_page.update_attributes!(params[:welcome_page])
  97 + profile.welcome_page = @welcome_page
  98 + profile.save!
  99 + session[:notice] = _('Welcome page saved successfully.')
  100 + redirect_to :action => 'index'
  101 + rescue Exception => exception
  102 + session[:notice] = _('Welcome page could not be saved.')
  103 + end
  104 + end
  105 + end
  106 +
88 def deactivate_profile 107 def deactivate_profile
89 if environment.admins.include?(current_person) 108 if environment.admins.include?(current_person)
90 profile = environment.profiles.find(params[:id]) 109 profile = environment.profiles.find(params[:id])
@@ -116,9 +135,24 @@ class ProfileEditorController &lt; MyProfileController @@ -116,9 +135,24 @@ class ProfileEditorController &lt; MyProfileController
116 protected 135 protected
117 136
118 def redirect_to_previous_location 137 def redirect_to_previous_location
119 - back = request.referer  
120 - back = "/" if back.nil? 138 + redirect_to @back_to
  139 + end
121 140
122 - redirect_to back 141 + #TODO Consider using this as a general controller feature to be available on every action.
  142 + def back_to
  143 + @back_to = params[:back_to] || request.referer || "/"
123 end 144 end
  145 +
  146 + private
  147 +
  148 + def has_welcome_page
  149 + profile.is_template
  150 + end
  151 +
  152 + def access_welcome_page
  153 + unless has_welcome_page
  154 + render_access_denied
  155 + end
  156 + end
  157 +
124 end 158 end
app/controllers/public/account_controller.rb
@@ -82,10 +82,12 @@ class AccountController &lt; ApplicationController @@ -82,10 +82,12 @@ class AccountController &lt; ApplicationController
82 if @plugins.dispatch(:allow_user_registration).include?(false) 82 if @plugins.dispatch(:allow_user_registration).include?(false)
83 redirect_back_or_default(:controller => 'home') 83 redirect_back_or_default(:controller => 'home')
84 session[:notice] = _("This environment doesn't allow user registration.") 84 session[:notice] = _("This environment doesn't allow user registration.")
  85 + return
85 end 86 end
86 87
87 store_location(request.referer) unless params[:return_to] or session[:return_to] 88 store_location(request.referer) unless params[:return_to] or session[:return_to]
88 89
  90 + # Tranforming to boolean
89 @block_bot = !!session[:may_be_a_bot] 91 @block_bot = !!session[:may_be_a_bot]
90 @invitation_code = params[:invitation_code] 92 @invitation_code = params[:invitation_code]
91 begin 93 begin
@@ -129,8 +131,8 @@ class AccountController &lt; ApplicationController @@ -129,8 +131,8 @@ class AccountController &lt; ApplicationController
129 check_join_in_community(@user) 131 check_join_in_community(@user)
130 go_to_signup_initial_page 132 go_to_signup_initial_page
131 else 133 else
  134 + redirect_to :controller => :home, :action => :welcome, :template_id => (@user.person.template && @user.person.template.id)
132 session[:notice] = _('Thanks for registering!') 135 session[:notice] = _('Thanks for registering!')
133 - @register_pending = true  
134 end 136 end
135 end 137 end
136 end 138 end
@@ -461,6 +463,8 @@ class AccountController &lt; ApplicationController @@ -461,6 +463,8 @@ class AccountController &lt; ApplicationController
461 redirect_to user.url 463 redirect_to user.url
462 when 'user_control_panel' 464 when 'user_control_panel'
463 redirect_to user.admin_url 465 redirect_to user.admin_url
  466 + when 'welcome_page'
  467 + redirect_to :controller => :home, :action => :welcome, :template_id => (user.template && user.template.id)
464 else 468 else
465 redirect_back_or_default(default) 469 redirect_back_or_default(default)
466 end 470 end
app/controllers/public/home_controller.rb
@@ -18,4 +18,10 @@ class HomeController &lt; PublicController @@ -18,4 +18,10 @@ class HomeController &lt; PublicController
18 @no_design_blocks = true 18 @no_design_blocks = true
19 end 19 end
20 20
  21 + def welcome
  22 + @no_design_blocks = true
  23 + @display_confirmation_tips = !user.present? && !environment.enabled?(:skip_new_user_email_confirmation)
  24 + @person_template = user && user.template || params[:template_id] && Person.find(params[:template_id])
  25 + end
  26 +
21 end 27 end
app/controllers/public/invite_controller.rb
@@ -4,8 +4,15 @@ class InviteController &lt; PublicController @@ -4,8 +4,15 @@ class InviteController &lt; PublicController
4 before_filter :login_required 4 before_filter :login_required
5 before_filter :check_permissions_to_invite 5 before_filter :check_permissions_to_invite
6 6
7 - def select_address_book 7 + def invite_friends
8 @import_from = params[:import_from] || "manual" 8 @import_from = params[:import_from] || "manual"
  9 + @mail_template = params[:mail_template] || environment.invitation_mail_template(profile)
  10 +
  11 + labels = Profile::SEARCHABLE_FIELDS.except(:nickname).merge(User::SEARCHABLE_FIELDS).map { |name,info| info[:label].downcase }
  12 + last = labels.pop
  13 + label = labels.join(', ')
  14 + @search_fields = "#{label} #{_('or')} #{last}"
  15 +
9 if request.post? 16 if request.post?
10 contact_list = ContactList.create 17 contact_list = ContactList.create
11 Delayed::Job.enqueue GetEmailContactsJob.new(@import_from, params[:login], params[:password], contact_list.id) if @import_from != 'manual' 18 Delayed::Job.enqueue GetEmailContactsJob.new(@import_from, params[:login], params[:password], contact_list.id) if @import_from != 'manual'
@@ -22,7 +29,7 @@ class InviteController &lt; PublicController @@ -22,7 +29,7 @@ class InviteController &lt; PublicController
22 webmail_import_addresses = params[:webmail_import_addresses] 29 webmail_import_addresses = params[:webmail_import_addresses]
23 contacts_to_invite = Invitation.join_contacts(manual_import_addresses, webmail_import_addresses) 30 contacts_to_invite = Invitation.join_contacts(manual_import_addresses, webmail_import_addresses)
24 if !contacts_to_invite.empty? 31 if !contacts_to_invite.empty?
25 - Delayed::Job.enqueue InvitationJob.new(current_user.person.id, contacts_to_invite, params[:mail_template], profile.id, @contact_list.id, locale) 32 + Delayed::Job.enqueue InvitationJob.new(user.id, contacts_to_invite, params[:mail_template], profile.id, @contact_list.id, locale)
26 session[:notice] = _('Your invitations are being sent.') 33 session[:notice] = _('Your invitations are being sent.')
27 if profile.person? 34 if profile.person?
28 redirect_to :controller => 'profile', :action => 'friends' 35 redirect_to :controller => 'profile', :action => 'friends'
@@ -52,16 +59,36 @@ class InviteController &lt; PublicController @@ -52,16 +59,36 @@ class InviteController &lt; PublicController
52 def cancel_fetching_emails 59 def cancel_fetching_emails
53 contact_list = ContactList.find(params[:contact_list]) 60 contact_list = ContactList.find(params[:contact_list])
54 contact_list.destroy 61 contact_list.destroy
55 - redirect_to :action => 'select_address_book' 62 + redirect_to :action => 'invite_friends'
  63 + end
  64 +
  65 + def invite_registered_friend
  66 + contacts_to_invite = params['q'].split(',')
  67 + if !contacts_to_invite.empty? && request.post?
  68 + Delayed::Job.enqueue InvitationJob.new(user.id, contacts_to_invite, '', profile.id, nil, locale)
  69 + session[:notice] = _('Your invitations are being sent.')
  70 + if profile.person?
  71 + redirect_to :controller => 'profile', :action => 'friends'
  72 + else
  73 + redirect_to :controller => 'profile', :action => 'members'
  74 + end
  75 + else
  76 + redirect_to :action => 'invite_friends'
  77 + session[:notice] = _('Please enter a valid profile.')
  78 + end
  79 + end
  80 +
  81 + def search
  82 + scope = profile.invite_friends_only ? user.friends : environment.people
  83 + scope = scope.not_members_of(profile) if profile.organization?
  84 + scope = scope.not_friends_of(profile) if profile.person?
  85 + results = find_by_contents(:people, environment, scope, params['q'], {:page => 1}, {:joins => :user})[:results]
  86 + render :text => prepare_to_token_input(results).to_json
56 end 87 end
57 88
58 protected 89 protected
59 90
60 def check_permissions_to_invite 91 def check_permissions_to_invite
61 - if profile.person? and !current_user.person.has_permission?(:manage_friends, profile) or  
62 - profile.community? and !current_user.person.has_permission?(:invite_members, profile)  
63 - render_access_denied  
64 - end 92 + render_access_denied if !profile.allow_invitation_from?(user)
65 end 93 end
66 -  
67 end 94 end
app/controllers/public/profile_search_controller.rb
@@ -9,9 +9,12 @@ class ProfileSearchController &lt; PublicController @@ -9,9 +9,12 @@ class ProfileSearchController &lt; PublicController
9 @q = params[:q] 9 @q = params[:q]
10 unless @q.blank? 10 unless @q.blank?
11 if params[:where] == 'environment' 11 if params[:where] == 'environment'
12 - redirect_to :controller => 'search', :query => @q 12 + # user is using global search, redirects to the search controller with
  13 + # the query
  14 + search_path = url_for(:controller => 'search', :query => @q)
  15 + request.xhr? ? render(:js => "window.location.href = #{search_path.to_json}") : redirect_to(search_path)
13 else 16 else
14 - @results = find_by_contents(:articles, profile.articles.published, @q, {:per_page => 10, :page => params[:page]})[:results] 17 + @results = find_by_contents(:articles, profile, profile.articles.published, @q, {:per_page => 10, :page => params[:page]})[:results]
15 end 18 end
16 end 19 end
17 end 20 end
app/controllers/public/search_controller.rb
@@ -4,11 +4,11 @@ class SearchController &lt; PublicController @@ -4,11 +4,11 @@ class SearchController &lt; PublicController
4 include SearchHelper 4 include SearchHelper
5 include ActionView::Helpers::NumberHelper 5 include ActionView::Helpers::NumberHelper
6 6
7 - before_filter :redirect_asset_param, :except => :assets  
8 - before_filter :load_category  
9 - before_filter :load_search_assets  
10 - before_filter :load_query  
11 - before_filter :load_filter 7 + before_filter :redirect_asset_param, :except => [:assets, :suggestions]
  8 + before_filter :load_category, :except => :suggestions
  9 + before_filter :load_search_assets, :except => :suggestions
  10 + before_filter :load_query, :except => :suggestions
  11 + before_filter :load_order, :except => :suggestions
12 12
13 # Backwards compatibility with old URLs 13 # Backwards compatibility with old URLs
14 def redirect_asset_param 14 def redirect_asset_param
@@ -20,7 +20,7 @@ class SearchController &lt; PublicController @@ -20,7 +20,7 @@ class SearchController &lt; PublicController
20 20
21 def index 21 def index
22 @searches = {} 22 @searches = {}
23 - @order = [] 23 + @assets = []
24 @names = {} 24 @names = {}
25 @results_only = true 25 @results_only = true
26 26
@@ -28,7 +28,7 @@ class SearchController &lt; PublicController @@ -28,7 +28,7 @@ class SearchController &lt; PublicController
28 load_query 28 load_query
29 @asset = key 29 @asset = key
30 send(key) 30 send(key)
31 - @order << key 31 + @assets << key
32 @names[key] = _(description) 32 @names[key] = _(description)
33 end 33 end
34 @asset = nil 34 @asset = nil
@@ -42,7 +42,7 @@ class SearchController &lt; PublicController @@ -42,7 +42,7 @@ class SearchController &lt; PublicController
42 # view the summary of one category 42 # view the summary of one category
43 def category_index 43 def category_index
44 @searches = {} 44 @searches = {}
45 - @order = [] 45 + @assets = []
46 @names = {} 46 @names = {}
47 limit = MULTIPLE_SEARCH_LIMIT 47 limit = MULTIPLE_SEARCH_LIMIT
48 [ 48 [
@@ -53,7 +53,7 @@ class SearchController &lt; PublicController @@ -53,7 +53,7 @@ class SearchController &lt; PublicController
53 [ :communities, _('Communities'), :recent_communities ], 53 [ :communities, _('Communities'), :recent_communities ],
54 [ :articles, _('Contents'), :recent_articles ] 54 [ :articles, _('Contents'), :recent_articles ]
55 ].each do |asset, name, filter| 55 ].each do |asset, name, filter|
56 - @order << asset 56 + @assets << asset
57 @searches[asset]= {:results => @category.send(filter, limit)} 57 @searches[asset]= {:results => @category.send(filter, limit)}
58 raise "No total_entries for: #{asset}" unless @searches[asset][:results].respond_to?(:total_entries) 58 raise "No total_entries for: #{asset}" unless @searches[asset][:results].respond_to?(:total_entries)
59 @names[asset] = name 59 @names[asset] = name
@@ -147,12 +147,16 @@ class SearchController &lt; PublicController @@ -147,12 +147,16 @@ class SearchController &lt; PublicController
147 render :partial => 'events/events' 147 render :partial => 'events/events'
148 end 148 end
149 149
  150 + def suggestions
  151 + render :text => find_suggestions(normalize_term(params[:term]), environment, params[:asset]).to_json
  152 + end
  153 +
150 ####################################################### 154 #######################################################
151 protected 155 protected
152 156
153 def load_query 157 def load_query
154 @asset = (params[:asset] || params[:action]).to_sym 158 @asset = (params[:asset] || params[:action]).to_sym
155 - @order ||= [@asset] 159 + @assets ||= [@asset]
156 @searches ||= {} 160 @searches ||= {}
157 161
158 @query = params[:query] || '' 162 @query = params[:query] || ''
@@ -173,13 +177,22 @@ class SearchController &lt; PublicController @@ -173,13 +177,22 @@ class SearchController &lt; PublicController
173 end 177 end
174 end 178 end
175 179
  180 + AVAILABLE_SEARCHES = ActiveSupport::OrderedHash[
  181 + :articles, _('Contents'),
  182 + :people, _('People'),
  183 + :communities, _('Communities'),
  184 + :enterprises, _('Enterprises'),
  185 + :products, _('Products and Services'),
  186 + :events, _('Events'),
  187 + ]
  188 +
176 def load_search_assets 189 def load_search_assets
177 - if SEARCHES.keys.include?(params[:action].to_sym) && environment.enabled?("disable_asset_#{params[:action]}") 190 + if AVAILABLE_SEARCHES.keys.include?(params[:action].to_sym) && environment.enabled?("disable_asset_#{params[:action]}")
178 render_not_found 191 render_not_found
179 return 192 return
180 end 193 end
181 194
182 - @enabled_searches = SEARCHES.select {|key, name| environment.disabled?("disable_asset_#{key}") } 195 + @enabled_searches = AVAILABLE_SEARCHES.select {|key, name| environment.disabled?("disable_asset_#{key}") }
183 @searching = {} 196 @searching = {}
184 @titles = {} 197 @titles = {}
185 @enabled_searches.each do |key, name| 198 @enabled_searches.each do |key, name|
@@ -189,11 +202,11 @@ class SearchController &lt; PublicController @@ -189,11 +202,11 @@ class SearchController &lt; PublicController
189 @names = @titles if @names.nil? 202 @names = @titles if @names.nil?
190 end 203 end
191 204
192 - def load_filter  
193 - @filter = 'more_recent'  
194 - if SEARCHES.keys.include?(@asset.to_sym)  
195 - available_filters = asset_class(@asset)::SEARCH_FILTERS  
196 - @filter = params[:filter] if available_filters.include?(params[:filter]) 205 + def load_order
  206 + @order = 'more_recent'
  207 + if AVAILABLE_SEARCHES.keys.include?(@asset.to_sym)
  208 + available_orders = asset_class(@asset)::SEARCH_FILTERS[:order]
  209 + @order = params[:order] if available_orders.include?(params[:order])
197 end 210 end
198 end 211 end
199 212
@@ -217,7 +230,7 @@ class SearchController &lt; PublicController @@ -217,7 +230,7 @@ class SearchController &lt; PublicController
217 end 230 end
218 231
219 def full_text_search 232 def full_text_search
220 - @searches[@asset] = find_by_contents(@asset, @scope, @query, paginate_options, {:category => @category, :filter => @filter}) 233 + @searches[@asset] = find_by_contents(@asset, environment, @scope, @query, paginate_options, {:category => @category, :filter => @order})
221 end 234 end
222 235
223 private 236 private
@@ -232,4 +245,14 @@ class SearchController &lt; PublicController @@ -232,4 +245,14 @@ class SearchController &lt; PublicController
232 20 245 20
233 end 246 end
234 247
  248 + def available_assets
  249 + assets = ActiveSupport::OrderedHash[
  250 + :articles, _('Contents'),
  251 + :enterprises, _('Enterprises'),
  252 + :people, _('People'),
  253 + :communities, _('Communities'),
  254 + :products, _('Products and Services'),
  255 + ]
  256 + end
  257 +
235 end 258 end
app/helpers/application_helper.rb
@@ -1307,8 +1307,19 @@ module ApplicationHelper @@ -1307,8 +1307,19 @@ module ApplicationHelper
1307 end 1307 end
1308 end 1308 end
1309 1309
1310 - def remove_content_button(action)  
1311 - @plugins.dispatch("content_remove_#{action.to_s}", @page).include?(true) 1310 + def content_remove_spread(content)
  1311 + !content.public? || content.folder? || (profile == user && user.communities.blank? && !environment.portal_enabled)
  1312 + end
  1313 +
  1314 + def remove_content_button(action, content)
  1315 + method_name = "content_remove_#{action.to_s}"
  1316 + plugin_condition = @plugins.dispatch(method_name, content).include?(true)
  1317 + begin
  1318 + core_condition = self.send(method_name, content)
  1319 + rescue NoMethodError
  1320 + core_condition = false
  1321 + end
  1322 + core_condition || plugin_condition
1312 end 1323 end
1313 1324
1314 def template_options(kind, field_name) 1325 def template_options(kind, field_name)
@@ -1411,6 +1422,43 @@ module ApplicationHelper @@ -1411,6 +1422,43 @@ module ApplicationHelper
1411 content_tag('ul', article.versions.map {|v| link_to("r#{v.version}", @page.url.merge(:version => v.version))}) 1422 content_tag('ul', article.versions.map {|v| link_to("r#{v.version}", @page.url.merge(:version => v.version))})
1412 end 1423 end
1413 1424
  1425 + def search_input_with_suggestions(name, asset, default, options = {})
  1426 + text_field_tag name, default, options.merge({:class => 'search-input-with-suggestions', 'data-asset' => asset})
  1427 + end
  1428 +
  1429 + def profile_suggestion_profile_connections(suggestion)
  1430 + profiles = suggestion.profile_connections.first(4).map do |profile|
  1431 + link_to(profile_image(profile, :icon, :title => profile.name), profile.url, :class => 'profile-suggestion-connection-icon')
  1432 + end
  1433 +
  1434 + controller_target = suggestion.suggestion_type == 'Person' ? :friends : :memberships
  1435 + profiles << link_to("<big> +#{suggestion.profile_connections.count - 4}</big>", :controller => controller_target, :action => :connections, :id => suggestion.suggestion_id) if suggestion.profile_connections.count > 4
  1436 +
  1437 + if profiles.present?
  1438 + content_tag(:div, profiles.join , :class => 'profile-connections')
  1439 + else
  1440 + ''
  1441 + end
  1442 + end
  1443 +
  1444 + def profile_suggestion_tag_connections(suggestion)
  1445 + tags = suggestion.tag_connections.first(4).map do |tag|
  1446 + tag.name + ', '
  1447 + end
  1448 + last_tag = tags.pop
  1449 + tags << last_tag.strip.chop if last_tag.present?
  1450 + title = tags.join
  1451 +
  1452 + controller_target = suggestion.suggestion_type == 'Person' ? :friends : :memberships
  1453 + tags << ' ' + link_to('...', {:controller => controller_target, :action => :connections, :id => suggestion.suggestion_id}, :class => 'more-tag-connections', :title => _('See all connections')) if suggestion.tag_connections.count > 4
  1454 +
  1455 + if tags.present?
  1456 + content_tag(:div, tags.join, :class => 'tag-connections', :title => title)
  1457 + else
  1458 + ''
  1459 + end
  1460 + end
  1461 +
1414 def labelled_colorpicker_field(human_name, object_name, method, options = {}) 1462 def labelled_colorpicker_field(human_name, object_name, method, options = {})
1415 options[:id] ||= 'text-field-' + FormsHelper.next_id_number 1463 options[:id] ||= 'text-field-' + FormsHelper.next_id_number
1416 content_tag('label', human_name, :for => options[:id], :class => 'formlabel') + 1464 content_tag('label', human_name, :for => options[:id], :class => 'formlabel') +
app/helpers/cms_helper.rb
@@ -40,12 +40,8 @@ module CmsHelper @@ -40,12 +40,8 @@ module CmsHelper
40 end 40 end
41 end 41 end
42 42
43 - def display_spread_button(profile, article)  
44 - if profile.person?  
45 - expirable_button article, :spread, _('Spread this'), :action => 'publish', :id => article.id  
46 - elsif profile.community? && environment.portal_community  
47 - expirable_button article, :spread, _('Spread this'), :action => 'publish_on_portal_community', :id => article.id  
48 - end 43 + def display_spread_button(article)
  44 + expirable_button article, :spread, _('Spread this'), {:action => 'publish', :id => article.id}, {:class => 'colorbox'}
49 end 45 end
50 46
51 def display_delete_button(article) 47 def display_delete_button(article)
app/helpers/forms_helper.rb
@@ -265,7 +265,7 @@ module FormsHelper @@ -265,7 +265,7 @@ module FormsHelper
265 ) 265 )
266 end 266 end
267 267
268 - def select_profile_folder(label_text, field_id, profile, default_value='', html_options = {}, js_options = {}, find_options = {}) 268 + def select_profile_folder(label_text, field_id, profile, default_value='', html_options = {}, js_options = {}, find_options = {}, extra_options = {})
269 if find_options.empty? 269 if find_options.empty?
270 folders = profile.folders 270 folders = profile.folders
271 else 271 else
@@ -276,7 +276,7 @@ module FormsHelper @@ -276,7 +276,7 @@ module FormsHelper
276 select_tag( 276 select_tag(
277 field_id, 277 field_id,
278 options_for_select( 278 options_for_select(
279 - [[profile.identifier, '']] + 279 + [[(extra_options[:root_label] || profile.identifier), '']] +
280 folders.collect {|f| [ profile.identifier + '/' + f.full_name, f.id.to_s ] }, 280 folders.collect {|f| [ profile.identifier + '/' + f.full_name, f.id.to_s ] },
281 default_value.to_s 281 default_value.to_s
282 ), 282 ),
app/helpers/layout_helper.rb
@@ -48,6 +48,7 @@ module LayoutHelper @@ -48,6 +48,7 @@ module LayoutHelper
48 'thickbox', 48 'thickbox',
49 'lightbox', 49 'lightbox',
50 'colorbox', 50 'colorbox',
  51 + 'selectordie',
51 'inputosaurus', 52 'inputosaurus',
52 'chat', 53 'chat',
53 pngfix_stylesheet_path, 54 pngfix_stylesheet_path,
app/helpers/search_helper.rb
@@ -5,20 +5,23 @@ module SearchHelper @@ -5,20 +5,23 @@ module SearchHelper
5 BLOCKS_SEARCH_LIMIT = 24 5 BLOCKS_SEARCH_LIMIT = 24
6 MULTIPLE_SEARCH_LIMIT = 8 6 MULTIPLE_SEARCH_LIMIT = 8
7 7
8 - SEARCHES = ActiveSupport::OrderedHash[  
9 - :articles, _('Contents'),  
10 - :enterprises, _('Enterprises'),  
11 - :people, _('People'),  
12 - :communities, _('Communities'),  
13 - :products, _('Products and Services'),  
14 - :events, _('Events'),  
15 - ] 8 + FILTERS_TRANSLATIONS = {
  9 + :order => _('Order'),
  10 + :display => _('Display')
  11 + }
16 12
17 - FILTER_TRANSLATION = {  
18 - 'more_popular' => _('More popular'),  
19 - 'more_active' => _('More active'),  
20 - 'more_recent' => _('More recent'),  
21 - 'more_comments' => _('More comments') 13 + FILTERS_OPTIONS_TRANSLATION = {
  14 + :order => {
  15 + 'more_popular' => _('More popular'),
  16 + 'more_active' => _('More active'),
  17 + 'more_recent' => _('More recent'),
  18 + 'more_comments' => _('More comments')
  19 + },
  20 + :display => {
  21 + 'map' => _('Map'),
  22 + 'full' => _('Full'),
  23 + 'compact' => _('Compact')
  24 + }
22 } 25 }
23 26
24 COMMON_PROFILE_LIST_BLOCK = [ 27 COMMON_PROFILE_LIST_BLOCK = [
@@ -56,7 +59,7 @@ module SearchHelper @@ -56,7 +59,7 @@ module SearchHelper
56 end 59 end
57 60
58 def display?(asset, mode) 61 def display?(asset, mode)
59 - defined?(asset_class(asset)::SEARCH_DISPLAYS) && asset_class(asset)::SEARCH_DISPLAYS.include?(mode.to_s) 62 + defined?(asset_class(asset)::SEARCH_FILTERS[:display]) && asset_class(asset)::SEARCH_FILTERS[:display].include?(mode.to_s)
60 end 63 end
61 64
62 def display_results(searches=nil, asset=nil) 65 def display_results(searches=nil, asset=nil)
@@ -93,6 +96,16 @@ module SearchHelper @@ -93,6 +96,16 @@ module SearchHelper
93 end 96 end
94 end 97 end
95 98
  99 + def select_filter(name, options, default = nil)
  100 + if options.size <= 1
  101 + return
  102 + else
  103 + options = options.map {|option| [FILTERS_OPTIONS_TRANSLATION[name][option], option]}
  104 + options = options_for_select(options, :selected => (params[name] || default))
  105 + select_tag(name, options)
  106 + end
  107 + end
  108 +
96 def display_selector(asset, display, float = 'right') 109 def display_selector(asset, display, float = 'right')
97 display = nil if display.blank? 110 display = nil if display.blank?
98 display ||= asset_class(asset).default_search_display 111 display ||= asset_class(asset).default_search_display
@@ -107,36 +120,32 @@ module SearchHelper @@ -107,36 +120,32 @@ module SearchHelper
107 end 120 end
108 end 121 end
109 122
110 - def filter_selector(asset, filter, float = 'right') 123 + def filters(asset)
  124 + return if !asset
111 klass = asset_class(asset) 125 klass = asset_class(asset)
112 - if klass::SEARCH_FILTERS.count > 1  
113 - options = options_for_select(klass::SEARCH_FILTERS.map {|f| [FILTER_TRANSLATION[f], f]}, filter)  
114 - url_params = url_for(params.merge(:filter => 'FILTER'))  
115 - onchange = "document.location.href = '#{url_params}'.replace('FILTER', this.value)"  
116 - select_field = select_tag(:filter, options, :onchange => onchange)  
117 - content_tag('div',  
118 - content_tag('strong', _('Filter')) + ': ' + select_field,  
119 - :class => "search-customize-options"  
120 - )  
121 - end 126 + content_tag('div', klass::SEARCH_FILTERS.map do |name, options|
  127 + default = klass.respond_to?("default_search_#{name}") ? klass.send("default_search_#{name}".to_s) : nil
  128 + select_filter(name, options, default)
  129 + end.join("\n"), :id => 'search-filters')
  130 + end
  131 +
  132 + def assets_menu(selected)
  133 + assets = @enabled_searches.keys
  134 + # Events is a search asset but do not have a good interface for
  135 + #TODO searching. When this is solved we may add it back again to the assets
  136 + # menu.
  137 + assets.delete(:events)
  138 + content_tag('ul',
  139 + assets.map do |asset|
  140 + options = {}
  141 + options.merge!(:class => 'selected') if selected.to_s == asset.to_s
  142 + content_tag('li', asset_link(asset), options)
  143 + end.join("\n"),
  144 + :id => 'assets-menu')
122 end 145 end
123 146
124 - def filter_title(asset, filter)  
125 - {  
126 - 'articles_more_recent' => _('More recent contents from network'),  
127 - 'articles_more_popular' => _('More viewed contents from network'),  
128 - 'articles_more_comments' => _('Most commented contents from network'),  
129 - 'people_more_recent' => _('More recent people from network'),  
130 - 'people_more_active' => _('More active people from network'),  
131 - 'people_more_popular' => _('More popular people from network'),  
132 - 'communities_more_recent' => _('More recent communities from network'),  
133 - 'communities_more_active' => _('More active communities from network'),  
134 - 'communities_more_popular' => _('More popular communities from network'),  
135 - 'enterprises_more_recent' => _('More recent enterprises from network'),  
136 - 'enterprises_more_active' => _('More active enterprises from network'),  
137 - 'enterprises_more_popular' => _('More popular enterprises from network'),  
138 - 'products_more_recent' => _('Highlights'),  
139 - }[asset.to_s + '_' + filter].to_s 147 + def asset_link(asset)
  148 + link_to(@enabled_searches[asset], "/search/#{asset}")
140 end 149 end
141 150
142 end 151 end
app/helpers/search_term_helper.rb 0 → 100644
@@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
  1 +module SearchTermHelper
  2 + def register_search_term(term, total, indexed, context, asset='all')
  3 + normalized_term = normalize_term(term)
  4 + if normalized_term.present?
  5 + search_term = SearchTerm.find_or_create(normalized_term, context, asset)
  6 + SearchTermOccurrence.create!(:search_term => search_term, :total => total, :indexed => indexed)
  7 + end
  8 + end
  9 +
  10 + #FIXME For some reason the job is created but nothing is ran.
  11 + #handle_asynchronously :register_search_term
  12 +
  13 + #TODO Think smarter criteria to normalize search terms properly
  14 + def normalize_term(search_term)
  15 + search_term ||= ''
  16 + search_term.downcase
  17 + end
  18 +end
app/helpers/token_helper.rb
@@ -12,6 +12,7 @@ module TokenHelper @@ -12,6 +12,7 @@ module TokenHelper
12 options[:search_delay] ||= 1000 12 options[:search_delay] ||= 1000
13 options[:prevent_duplicates] ||= true 13 options[:prevent_duplicates] ||= true
14 options[:backspace_delete_item] ||= false 14 options[:backspace_delete_item] ||= false
  15 + options[:zindex] ||= 999
15 options[:focus] ||= false 16 options[:focus] ||= false
16 options[:avoid_enter] ||= true 17 options[:avoid_enter] ||= true
17 options[:on_result] ||= 'null' 18 options[:on_result] ||= 'null'
@@ -31,6 +32,7 @@ module TokenHelper @@ -31,6 +32,7 @@ module TokenHelper
31 searchDelay: #{options[:search_delay].to_json}, 32 searchDelay: #{options[:search_delay].to_json},
32 preventDuplicates: #{options[:prevent_duplicates].to_json}, 33 preventDuplicates: #{options[:prevent_duplicates].to_json},
33 backspaceDeleteItem: #{options[:backspace_delete_item].to_json}, 34 backspaceDeleteItem: #{options[:backspace_delete_item].to_json},
  35 + zindex: #{options[:zindex].to_json},
34 queryParam: #{options[:query_param].to_json}, 36 queryParam: #{options[:query_param].to_json},
35 tokenLimit: #{options[:token_limit].to_json}, 37 tokenLimit: #{options[:token_limit].to_json},
36 onResult: #{options[:on_result]}, 38 onResult: #{options[:on_result]},
app/mailers/user_mailer.rb
@@ -41,6 +41,23 @@ class UserMailer &lt; ActionMailer::Base @@ -41,6 +41,23 @@ class UserMailer &lt; ActionMailer::Base
41 ) 41 )
42 end 42 end
43 43
  44 + def profiles_suggestions_email(user)
  45 + @recipient = user.name
  46 + @environment = user.environment.name
  47 + @url = user.environment.top_url
  48 + @people_suggestions_url = user.people_suggestions_url
  49 + @people_suggestions = user.suggested_people.sample(3)
  50 + @communities_suggestions_url = user.communities_suggestions_url
  51 + @communities_suggestions = user.suggested_communities.sample(3)
  52 +
  53 + mail(
  54 + content_type: 'text/html',
  55 + to: user.email,
  56 + from: "#{user.environment.name} <#{user.environment.contact_email}>",
  57 + subject: _("[%s] What about grow up your network?") % user.environment.name
  58 + )
  59 + end
  60 +
44 class Job < Struct.new(:user, :method) 61 class Job < Struct.new(:user, :method)
45 def perform 62 def perform
46 UserMailer.send(method, user).deliver 63 UserMailer.send(method, user).deliver
app/models/add_friend.rb
@@ -14,6 +14,11 @@ class AddFriend &lt; Task @@ -14,6 +14,11 @@ class AddFriend &lt; Task
14 alias :friend :target 14 alias :friend :target
15 alias :friend= :target= 15 alias :friend= :target=
16 16
  17 + after_create do |task|
  18 + TaskMailer.invitation_notification(task).deliver unless task.friend
  19 + remove_from_suggestion_list(task)
  20 + end
  21 +
17 def perform 22 def perform
18 target.add_friend(requestor, group_for_friend) 23 target.add_friend(requestor, group_for_friend)
19 requestor.add_friend(target, group_for_person) 24 requestor.add_friend(target, group_for_person)
@@ -48,4 +53,8 @@ class AddFriend &lt; Task @@ -48,4 +53,8 @@ class AddFriend &lt; Task
48 {:type => :profile_image, :profile => requestor, :url => requestor.url} 53 {:type => :profile_image, :profile => requestor, :url => requestor.url}
49 end 54 end
50 55
  56 + def remove_from_suggestion_list(task)
  57 + suggestion = task.requestor.profile_suggestions.find_by_suggestion_id task.target.id
  58 + suggestion.disable if suggestion
  59 + end
51 end 60 end
app/models/add_member.rb
@@ -10,6 +10,10 @@ class AddMember &lt; Task @@ -10,6 +10,10 @@ class AddMember &lt; Task
10 10
11 settings_items :roles 11 settings_items :roles
12 12
  13 + after_create do |task|
  14 + remove_from_suggestion_list(task)
  15 + end
  16 +
13 def perform 17 def perform
14 if !self.roles or (self.roles.uniq.compact.length == 1 and self.roles.uniq.compact.first.to_i.zero?) 18 if !self.roles or (self.roles.uniq.compact.length == 1 and self.roles.uniq.compact.first.to_i.zero?)
15 self.roles = [Profile::Roles.member(organization.environment.id).id] 19 self.roles = [Profile::Roles.member(organization.environment.id).id]
@@ -46,4 +50,9 @@ class AddMember &lt; Task @@ -46,4 +50,9 @@ class AddMember &lt; Task
46 _('You will need login to %{system} in order to accept or reject %{requestor} as a member of %{organization}.') % { :system => target.environment.name, :requestor => requestor.name, :organization => organization.name } 50 _('You will need login to %{system} in order to accept or reject %{requestor} as a member of %{organization}.') % { :system => target.environment.name, :requestor => requestor.name, :organization => organization.name }
47 end 51 end
48 52
  53 + def remove_from_suggestion_list(task)
  54 + suggestion = task.requestor.profile_suggestions.find_by_suggestion_id task.target.id
  55 + suggestion.disable if suggestion
  56 + end
  57 +
49 end 58 end
app/models/article.rb
@@ -14,20 +14,17 @@ class Article &lt; ActiveRecord::Base @@ -14,20 +14,17 @@ class Article &lt; ActiveRecord::Base
14 acts_as_having_image 14 acts_as_having_image
15 15
16 SEARCHABLE_FIELDS = { 16 SEARCHABLE_FIELDS = {
17 - :name => 10,  
18 - :abstract => 3,  
19 - :body => 2,  
20 - :slug => 1,  
21 - :filename => 1, 17 + :name => {:label => _('Name'), :weight => 10},
  18 + :abstract => {:label => _('Abstract'), :weight => 3},
  19 + :body => {:label => _('Content'), :weight => 2},
  20 + :slug => {:label => _('Slug'), :weight => 1},
  21 + :filename => {:label => _('Filename'), :weight => 1},
22 } 22 }
23 23
24 - SEARCH_FILTERS = %w[  
25 - more_recent  
26 - more_popular  
27 - more_comments  
28 - ]  
29 -  
30 - SEARCH_DISPLAYS = %w[full] 24 + SEARCH_FILTERS = {
  25 + :order => %w[more_recent more_popular more_comments],
  26 + :display => %w[full]
  27 + }
31 28
32 def self.default_search_display 29 def self.default_search_display
33 'full' 30 'full'
@@ -477,7 +474,9 @@ class Article &lt; ActiveRecord::Base @@ -477,7 +474,9 @@ class Article &lt; ActiveRecord::Base
477 scope :no_folders, lambda {|profile|{:conditions => ['articles.type NOT IN (?)', profile.folder_types]}} 474 scope :no_folders, lambda {|profile|{:conditions => ['articles.type NOT IN (?)', profile.folder_types]}}
478 scope :galleries, :conditions => [ "articles.type IN ('Gallery')" ] 475 scope :galleries, :conditions => [ "articles.type IN ('Gallery')" ]
479 scope :images, :conditions => { :is_image => true } 476 scope :images, :conditions => { :is_image => true }
  477 + scope :no_images, :conditions => { :is_image => false }
480 scope :text_articles, :conditions => [ 'articles.type IN (?)', text_article_types ] 478 scope :text_articles, :conditions => [ 'articles.type IN (?)', text_article_types ]
  479 + scope :files, :conditions => { :type => 'UploadedFile' }
481 scope :with_types, lambda { |types| { :conditions => [ 'articles.type IN (?)', types ] } } 480 scope :with_types, lambda { |types| { :conditions => [ 'articles.type IN (?)', types ] } }
482 481
483 scope :more_popular, :order => 'hits DESC' 482 scope :more_popular, :order => 'hits DESC'
@@ -528,7 +527,10 @@ class Article &lt; ActiveRecord::Base @@ -528,7 +527,10 @@ class Article &lt; ActiveRecord::Base
528 end 527 end
529 528
530 alias :allow_delete? :allow_post_content? 529 alias :allow_delete? :allow_post_content?
531 - alias :allow_spread? :allow_post_content? 530 +
  531 + def allow_spread?(user = nil)
  532 + user && public?
  533 + end
532 534
533 def allow_create?(user) 535 def allow_create?(user)
534 allow_post_content?(user) || allow_publish_content?(user) 536 allow_post_content?(user) || allow_publish_content?(user)
app/models/category.rb
@@ -3,10 +3,10 @@ class Category &lt; ActiveRecord::Base @@ -3,10 +3,10 @@ class Category &lt; ActiveRecord::Base
3 attr_accessible :name, :parent_id, :display_color, :display_in_menu, :image_builder, :environment, :parent 3 attr_accessible :name, :parent_id, :display_color, :display_in_menu, :image_builder, :environment, :parent
4 4
5 SEARCHABLE_FIELDS = { 5 SEARCHABLE_FIELDS = {
6 - :name => 10,  
7 - :acronym => 5,  
8 - :abbreviation => 5,  
9 - :slug => 1, 6 + :name => {:label => _('Name'), :weight => 10},
  7 + :acronym => {:label => _('Acronym'), :weight => 5},
  8 + :abbreviation => {:label => _('Abbreviation'), :weight => 5},
  9 + :slug => {:label => _('Slug'), :weight => 1},
10 } 10 }
11 11
12 validates_exclusion_of :slug, :in => [ 'index' ], :message => N_('{fn} cannot be like that.').fix_i18n 12 validates_exclusion_of :slug, :in => [ 'index' ], :message => N_('{fn} cannot be like that.').fix_i18n
app/models/certifier.rb
@@ -3,9 +3,9 @@ class Certifier &lt; ActiveRecord::Base @@ -3,9 +3,9 @@ class Certifier &lt; ActiveRecord::Base
3 attr_accessible :name, :environment 3 attr_accessible :name, :environment
4 4
5 SEARCHABLE_FIELDS = { 5 SEARCHABLE_FIELDS = {
6 - :name => 10,  
7 - :description => 3,  
8 - :link => 1, 6 + :name => {:label => _('Name'), :weight => 10},
  7 + :description => {:label => _('Description'), :weight => 3},
  8 + :link => {:label => _('Link'), :weight => 1},
9 } 9 }
10 10
11 belongs_to :environment 11 belongs_to :environment
app/models/comment.rb
1 class Comment < ActiveRecord::Base 1 class Comment < ActiveRecord::Base
2 2
3 SEARCHABLE_FIELDS = { 3 SEARCHABLE_FIELDS = {
4 - :title => 10,  
5 - :name => 4,  
6 - :body => 2, 4 + :title => {:label => _('Title'), :weight => 10},
  5 + :name => {:label => _('Name'), :weight => 4},
  6 + :body => {:label => _('Content'), :weight => 2},
7 } 7 }
8 8
9 attr_accessible :body, :author, :name, :email, :title, :reply_of_id, :source 9 attr_accessible :body, :author, :name, :email, :title, :reply_of_id, :source
app/models/communities_block.rb
@@ -14,19 +14,17 @@ class CommunitiesBlock &lt; ProfileListBlock @@ -14,19 +14,17 @@ class CommunitiesBlock &lt; ProfileListBlock
14 _('This block displays the communities in which the user is a member.') 14 _('This block displays the communities in which the user is a member.')
15 end 15 end
16 16
  17 + def suggestions
  18 + return nil unless owner.kind_of?(Profile)
  19 + owner.profile_suggestions.of_community.enabled.limit(3).includes(:suggestion)
  20 + end
  21 +
17 def footer 22 def footer
18 owner = self.owner 23 owner = self.owner
19 - case owner  
20 - when Profile  
21 - lambda do |context|  
22 - link_to s_('communities|View all'), :profile => owner.identifier, :controller => 'profile', :action => 'communities'  
23 - end  
24 - when Environment  
25 - lambda do |context|  
26 - link_to s_('communities|View all'), :controller => 'search', :action => 'communities'  
27 - end  
28 - else  
29 - '' 24 + suggestions = self.suggestions
  25 + return '' unless owner.kind_of?(Profile) || owner.kind_of?(Environment)
  26 + proc do
  27 + render :file => 'blocks/communities', :locals => { :owner => owner, :suggestions => suggestions }
30 end 28 end
31 end 29 end
32 30
app/models/create_enterprise.rb
@@ -73,7 +73,13 @@ class CreateEnterprise &lt; Task @@ -73,7 +73,13 @@ class CreateEnterprise &lt; Task
73 73
74 # sets the associated region for the enterprise creation 74 # sets the associated region for the enterprise creation
75 def region=(value) 75 def region=(value)
76 - raise ArgumentError.new("Region expected, but got #{value.class}") unless value.kind_of?(Region) 76 + unless value.kind_of?(Region)
  77 + begin
  78 + value = Region.find(value)
  79 + rescue
  80 + raise ArgumentError.new("Could not find any region with the id #{value}")
  81 + end
  82 + end
77 83
78 @region = value 84 @region = value
79 self.region_id = value.id 85 self.region_id = value.id
app/models/enterprise.rb
@@ -4,7 +4,10 @@ class Enterprise &lt; Organization @@ -4,7 +4,10 @@ class Enterprise &lt; Organization
4 4
5 attr_accessible :business_name, :address_reference, :district, :tag_list, :organization_website, :historic_and_current_context, :activities_short_description, :products_per_catalog_page 5 attr_accessible :business_name, :address_reference, :district, :tag_list, :organization_website, :historic_and_current_context, :activities_short_description, :products_per_catalog_page
6 6
7 - SEARCH_DISPLAYS += %w[map full] 7 + SEARCH_FILTERS = {
  8 + :order => %w[more_recent more_popular more_active],
  9 + :display => %w[compact full map]
  10 + }
8 11
9 def self.type_name 12 def self.type_name
10 _('Enterprise') 13 _('Enterprise')
app/models/environment.rb
@@ -10,6 +10,7 @@ class Environment &lt; ActiveRecord::Base @@ -10,6 +10,7 @@ class Environment &lt; ActiveRecord::Base
10 self.partial_updates = false 10 self.partial_updates = false
11 11
12 has_many :tasks, :dependent => :destroy, :as => 'target' 12 has_many :tasks, :dependent => :destroy, :as => 'target'
  13 + has_many :search_terms, :as => :context
13 14
14 IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/ 15 IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/
15 16
@@ -85,7 +86,9 @@ class Environment &lt; ActiveRecord::Base @@ -85,7 +86,9 @@ class Environment &lt; ActiveRecord::Base
85 end 86 end
86 87
87 def admins 88 def admins
88 - Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', Environment::Roles.admin(self).id]) 89 + admin_role = Environment::Roles.admin(self)
  90 + return [] if admin_role.blank?
  91 + Person.members_of(self).all(:conditions => ['role_assignments.role_id = ?', admin_role.id])
89 end 92 end
90 93
91 # returns the available features for a Environment, in the form of a 94 # returns the available features for a Environment, in the form of a
@@ -155,7 +158,8 @@ class Environment &lt; ActiveRecord::Base @@ -155,7 +158,8 @@ class Environment &lt; ActiveRecord::Base
155 'site_homepage' => _('Redirects the user to the environment homepage.'), 158 'site_homepage' => _('Redirects the user to the environment homepage.'),
156 'user_profile_page' => _('Redirects the user to his profile page.'), 159 'user_profile_page' => _('Redirects the user to his profile page.'),
157 'user_homepage' => _('Redirects the user to his homepage.'), 160 'user_homepage' => _('Redirects the user to his homepage.'),
158 - 'user_control_panel' => _('Redirects the user to his control panel.') 161 + 'user_control_panel' => _('Redirects the user to his control panel.'),
  162 + 'welcome_page' => _('Redirects the user to the environment welcome page.')
159 } 163 }
160 end 164 end
161 validates_inclusion_of :redirection_after_signup, :in => Environment.signup_redirection_options.keys, :allow_nil => true 165 validates_inclusion_of :redirection_after_signup, :in => Environment.signup_redirection_options.keys, :allow_nil => true
@@ -824,6 +828,10 @@ class Environment &lt; ActiveRecord::Base @@ -824,6 +828,10 @@ class Environment &lt; ActiveRecord::Base
824 "home-page-news/#{cache_key}-#{language}" 828 "home-page-news/#{cache_key}-#{language}"
825 end 829 end
826 830
  831 + def portal_enabled
  832 + portal_community && enabled?('use_portal_community')
  833 + end
  834 +
827 def notification_emails 835 def notification_emails
828 [contact_email].select(&:present?) + admins.map(&:email) 836 [contact_email].select(&:present?) + admins.map(&:email)
829 end 837 end
app/models/invitation.rb
@@ -51,7 +51,10 @@ class Invitation &lt; Task @@ -51,7 +51,10 @@ class Invitation &lt; Task
51 next if contact_to_invite == _("Firstname Lastname <friend@email.com>") 51 next if contact_to_invite == _("Firstname Lastname <friend@email.com>")
52 52
53 contact_to_invite.strip! 53 contact_to_invite.strip!
54 - if match = contact_to_invite.match(/(.*)<(.*)>/) and match[2].match(Noosfero::Constants::EMAIL_FORMAT) 54 + find_by_profile_id = false
  55 + if contact_to_invite.match(/^\d*$/)
  56 + find_by_profile_id = true
  57 + elsif match = contact_to_invite.match(/(.*)<(.*)>/) and match[2].match(Noosfero::Constants::EMAIL_FORMAT)
55 friend_name = match[1].strip 58 friend_name = match[1].strip
56 friend_email = match[2] 59 friend_email = match[2]
57 elsif match = contact_to_invite.strip.match(Noosfero::Constants::EMAIL_FORMAT) 60 elsif match = contact_to_invite.strip.match(Noosfero::Constants::EMAIL_FORMAT)
@@ -61,11 +64,15 @@ class Invitation &lt; Task @@ -61,11 +64,15 @@ class Invitation &lt; Task
61 next 64 next
62 end 65 end
63 66
64 - user = User.find_by_email(friend_email) 67 + begin
  68 + user = find_by_profile_id ? Person.find_by_id(contact_to_invite).user : User.find_by_email(friend_email)
  69 + rescue
  70 + user = nil
  71 + end
65 72
66 - task_args = if user.nil? 73 + task_args = if user.nil? && !find_by_profile_id
67 {:person => person, :friend_name => friend_name, :friend_email => friend_email, :message => message} 74 {:person => person, :friend_name => friend_name, :friend_email => friend_email, :message => message}
68 - else 75 + elsif user.present? && !(user.person.is_a_friend?(person) && profile.person?)
69 {:person => person, :target => user.person} 76 {:person => person, :target => user.person}
70 end 77 end
71 78
app/models/invite_friend.rb
1 class InviteFriend < Invitation 1 class InviteFriend < Invitation
2 2
3 settings_items :group_for_person, :group_for_friend 3 settings_items :group_for_person, :group_for_friend
  4 + before_create :check_for_invitation_existence
4 5
5 def perform 6 def perform
6 person.add_friend(friend, group_for_person) 7 person.add_friend(friend, group_for_person)
@@ -41,4 +42,11 @@ class InviteFriend &lt; Invitation @@ -41,4 +42,11 @@ class InviteFriend &lt; Invitation
41 ].join("\n\n") 42 ].join("\n\n")
42 end 43 end
43 44
  45 + private
  46 + def check_for_invitation_existence
  47 + if friend
  48 + friend.tasks.pending.of("InviteFriend").find(:all, :conditions => {:requestor_id => person.id, :target_id => friend.id}).blank?
  49 + end
  50 + end
  51 +
44 end 52 end
app/models/invite_member.rb
@@ -2,6 +2,7 @@ class InviteMember &lt; Invitation @@ -2,6 +2,7 @@ class InviteMember &lt; Invitation
2 2
3 settings_items :community_id, :type => :integer 3 settings_items :community_id, :type => :integer
4 validates_presence_of :community_id 4 validates_presence_of :community_id
  5 + before_create :check_for_invitation_existence
5 6
6 def community 7 def community
7 Community.find(community_id) 8 Community.find(community_id)
@@ -39,6 +40,14 @@ class InviteMember &lt; Invitation @@ -39,6 +40,14 @@ class InviteMember &lt; Invitation
39 _('%{requestor} invited you to join %{community}.') % {:requestor => requestor.name, :community => community.name} 40 _('%{requestor} invited you to join %{community}.') % {:requestor => requestor.name, :community => community.name}
40 end 41 end
41 42
  43 + def target_notification_message
  44 + if friend
  45 + _('%{requestor} is inviting you to join "%{community}" on %{system}.') % { :system => target.environment.name, :requestor => requestor.name, :community => community.name }
  46 + else
  47 + super
  48 + end
  49 + end
  50 +
42 def expanded_message 51 def expanded_message
43 super.gsub /<community>/, community.name 52 super.gsub /<community>/, community.name
44 end 53 end
@@ -53,4 +62,11 @@ class InviteMember &lt; Invitation @@ -53,4 +62,11 @@ class InviteMember &lt; Invitation
53 ].join("\n\n") 62 ].join("\n\n")
54 end 63 end
55 64
  65 + private
  66 + def check_for_invitation_existence
  67 + if friend
  68 + friend.tasks.pending.of("InviteMember").find(:all, :conditions => {:requestor_id => person.id}).select { |t| t.data[:community_id] == community_id }.blank?
  69 + end
  70 + end
  71 +
56 end 72 end
app/models/license.rb
@@ -3,8 +3,8 @@ class License &lt; ActiveRecord::Base @@ -3,8 +3,8 @@ class License &lt; ActiveRecord::Base
3 attr_accessible :name, :url 3 attr_accessible :name, :url
4 4
5 SEARCHABLE_FIELDS = { 5 SEARCHABLE_FIELDS = {
6 - :name => 10,  
7 - :url => 5, 6 + :name => {:label => _('Name'), :weight => 10},
  7 + :url => {:label => _('URL'), :weight => 5},
8 } 8 }
9 9
10 belongs_to :environment 10 belongs_to :environment
app/models/national_region.rb
1 class NationalRegion < ActiveRecord::Base 1 class NationalRegion < ActiveRecord::Base
2 2
3 SEARCHABLE_FIELDS = { 3 SEARCHABLE_FIELDS = {
4 - :name => 1,  
5 - :national_region_code => 1, 4 + :name => {:label => _('Name'), :weight => 1},
  5 + :national_region_code => {:label => _('Region Code'), :weight => 1},
6 } 6 }
7 7
8 def self.search_city(city_name, like = false, state = nil) 8 def self.search_city(city_name, like = false, state = nil)
app/models/organization.rb
@@ -3,10 +3,11 @@ class Organization &lt; Profile @@ -3,10 +3,11 @@ class Organization &lt; Profile
3 3
4 attr_accessible :moderated_articles, :foundation_year, :contact_person, :acronym, :legal_form, :economic_activity, :management_information, :cnpj, :display_name, :enable_contact_us 4 attr_accessible :moderated_articles, :foundation_year, :contact_person, :acronym, :legal_form, :economic_activity, :management_information, :cnpj, :display_name, :enable_contact_us
5 5
6 - SEARCH_FILTERS += %w[  
7 - more_popular  
8 - more_active  
9 - ] 6 + SEARCH_FILTERS = {
  7 + :order => %w[more_recent more_popular more_active],
  8 + :display => %w[compact]
  9 + }
  10 +
10 11
11 settings_items :closed, :type => :boolean, :default => false 12 settings_items :closed, :type => :boolean, :default => false
12 def closed? 13 def closed?
@@ -176,4 +177,8 @@ class Organization &lt; Profile @@ -176,4 +177,8 @@ class Organization &lt; Profile
176 self.visible = false 177 self.visible = false
177 save! 178 save!
178 end 179 end
  180 +
  181 + def allow_invitation_from?(person)
  182 + (followed_by?(person) && self.allow_members_to_invite) || person.has_permission?('invite-members', self)
  183 + end
179 end 184 end
app/models/person.rb
@@ -3,10 +3,11 @@ class Person &lt; Profile @@ -3,10 +3,11 @@ class Person &lt; Profile
3 3
4 attr_accessible :organization, :contact_information, :sex, :birth_date, :cell_phone, :comercial_phone, :jabber_id, :personal_website, :nationality, :address_reference, :district, :schooling, :schooling_status, :formation, :custom_formation, :area_of_study, :custom_area_of_study, :professional_activity, :organization_website 4 attr_accessible :organization, :contact_information, :sex, :birth_date, :cell_phone, :comercial_phone, :jabber_id, :personal_website, :nationality, :address_reference, :district, :schooling, :schooling_status, :formation, :custom_formation, :area_of_study, :custom_area_of_study, :professional_activity, :organization_website
5 5
6 - SEARCH_FILTERS += %w[  
7 - more_popular  
8 - more_active  
9 - ] 6 + SEARCH_FILTERS = {
  7 + :order => %w[more_recent more_popular more_active],
  8 + :display => %w[compact]
  9 + }
  10 +
10 11
11 def self.type_name 12 def self.type_name
12 _('Person') 13 _('Person')
@@ -21,16 +22,34 @@ class Person &lt; Profile @@ -21,16 +22,34 @@ class Person &lt; Profile
21 { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => [conditions] } 22 { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => [conditions] }
22 } 23 }
23 24
  25 + scope :not_members_of, lambda { |resources|
  26 + resources = [resources] if !resources.kind_of?(Array)
  27 + conditions = resources.map {|resource| "role_assignments.resource_type = '#{resource.class.base_class.name}' AND role_assignments.resource_id = #{resource.id || -1}"}.join(' OR ')
  28 + { :select => 'DISTINCT profiles.*', :conditions => ['"profiles"."id" NOT IN (SELECT DISTINCT profiles.id FROM "profiles" INNER JOIN "role_assignments" ON "role_assignments"."accessor_id" = "profiles"."id" AND "role_assignments"."accessor_type" = (\'Profile\') WHERE "profiles"."type" IN (\'Person\') AND (%s))' % conditions] }
  29 + }
  30 +
24 scope :by_role, lambda { |roles| 31 scope :by_role, lambda { |roles|
25 roles = [roles] unless roles.kind_of?(Array) 32 roles = [roles] unless roles.kind_of?(Array)
26 { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.role_id IN (?)', 33 { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.role_id IN (?)',
27 roles] } 34 roles] }
28 } 35 }
29 36
30 - def has_permission_with_plugins?(permission, profile)  
31 - permissions = [has_permission_without_plugins?(permission, profile)] 37 + scope :not_friends_of, lambda { |resources|
  38 + resources = Array(resources)
  39 + { :select => 'DISTINCT profiles.*', :conditions => ['"profiles"."id" NOT IN (SELECT DISTINCT profiles.id FROM "profiles" INNER JOIN "friendships" ON "friendships"."person_id" = "profiles"."id" WHERE "friendships"."friend_id" IN (%s))' % resources.map(&:id)] }
  40 + }
  41 +
  42 + def has_permission_with_admin?(permission, resource)
  43 + return true if resource.blank? || resource.admins.include?(self)
  44 + return true if resource.kind_of?(Profile) && resource.environment.admins.include?(self)
  45 + has_permission_without_admin?(permission, resource)
  46 + end
  47 + alias_method_chain :has_permission?, :admin
  48 +
  49 + def has_permission_with_plugins?(permission, resource)
  50 + permissions = [has_permission_without_plugins?(permission, resource)]
32 permissions += plugins.map do |plugin| 51 permissions += plugins.map do |plugin|
33 - plugin.has_permission?(self, permission, profile) 52 + plugin.has_permission?(self, permission, resource)
34 end 53 end
35 permissions.include?(true) 54 permissions.include?(true)
36 end 55 end
@@ -45,9 +64,9 @@ roles] } @@ -45,9 +64,9 @@ roles] }
45 ScopeTool.union *scopes 64 ScopeTool.union *scopes
46 end 65 end
47 66
48 - def memberships_by_role(role)  
49 - memberships.where('role_assignments.role_id = ?', role.id)  
50 - end 67 + def memberships_by_role(role)
  68 + memberships.where('role_assignments.role_id = ?', role.id)
  69 + end
51 70
52 has_many :friendships, :dependent => :destroy 71 has_many :friendships, :dependent => :destroy
53 has_many :friends, :class_name => 'Person', :through => :friendships 72 has_many :friends, :class_name => 'Person', :through => :friendships
@@ -65,6 +84,10 @@ roles] } @@ -65,6 +84,10 @@ roles] }
65 has_and_belongs_to_many :acepted_forums, :class_name => 'Forum', :join_table => 'terms_forum_people' 84 has_and_belongs_to_many :acepted_forums, :class_name => 'Forum', :join_table => 'terms_forum_people'
66 has_and_belongs_to_many :articles_with_access, :class_name => 'Article', :join_table => 'article_privacy_exceptions' 85 has_and_belongs_to_many :articles_with_access, :class_name => 'Article', :join_table => 'article_privacy_exceptions'
67 86
  87 + has_many :profile_suggestions, :foreign_key => :person_id, :order => 'score DESC', :dependent => :destroy
  88 + has_many :suggested_people, :through => :profile_suggestions, :source => :suggestion, :conditions => ['profile_suggestions.suggestion_type = ? AND profile_suggestions.enabled = ?', 'Person', true]
  89 + has_many :suggested_communities, :through => :profile_suggestions, :source => :suggestion, :conditions => ['profile_suggestions.suggestion_type = ? AND profile_suggestions.enabled = ?', 'Community', true]
  90 +
68 scope :more_popular, :order => 'friends_count DESC' 91 scope :more_popular, :order => 'friends_count DESC'
69 92
70 scope :abusers, :joins => :abuse_complaints, :conditions => ['tasks.status = 3'], :select => 'DISTINCT profiles.*' 93 scope :abusers, :joins => :abuse_complaints, :conditions => ['tasks.status = 3'], :select => 'DISTINCT profiles.*'
@@ -118,12 +141,12 @@ roles] } @@ -118,12 +141,12 @@ roles] }
118 end 141 end
119 142
120 def add_friend(friend, group = nil) 143 def add_friend(friend, group = nil)
121 - unless self.is_a_friend?(friend) 144 + unless self.is_a_friend?(friend)
122 friendship = self.friendships.build 145 friendship = self.friendships.build
123 friendship.friend = friend 146 friendship.friend = friend
124 friendship.group = group 147 friendship.group = group
125 friendship.save 148 friendship.save
126 - end 149 + end
127 end 150 end
128 151
129 def already_request_friendship?(person) 152 def already_request_friendship?(person)
@@ -497,6 +520,15 @@ roles] } @@ -497,6 +520,15 @@ roles] }
497 person.notifier.reschedule_next_notification_mail 520 person.notifier.reschedule_next_notification_mail
498 end 521 end
499 522
  523 + def remove_suggestion(profile)
  524 + suggestion = profile_suggestions.find_by_suggestion_id profile.id
  525 + suggestion.disable if suggestion
  526 + end
  527 +
  528 + def allow_invitation_from?(person)
  529 + person.has_permission?(:manage_friends, self)
  530 + end
  531 +
500 protected 532 protected
501 533
502 def followed_by?(profile) 534 def followed_by?(profile)
app/models/product.rb
1 class Product < ActiveRecord::Base 1 class Product < ActiveRecord::Base
2 2
3 SEARCHABLE_FIELDS = { 3 SEARCHABLE_FIELDS = {
4 - :name => 10,  
5 - :description => 1, 4 + :name => {:label => _('Name'), :weight => 10},
  5 + :description => {:label => _('Description'), :weight => 1},
6 } 6 }
7 7
8 - SEARCH_FILTERS = %w[  
9 - more_recent  
10 - ]  
11 -  
12 - SEARCH_DISPLAYS = %w[map full] 8 + SEARCH_FILTERS = {
  9 + :order => %w[more_recent],
  10 + :display => %w[full map]
  11 + }
13 12
14 attr_accessible :name, :product_category, :highlighted, :price, :enterprise, :image_builder, :description, :available, :qualifiers, :unit_id, :discount, :inputs, :qualifiers_list 13 attr_accessible :name, :product_category, :highlighted, :price, :enterprise, :image_builder, :description, :available, :qualifiers, :unit_id, :discount, :inputs, :qualifiers_list
15 14
app/models/profile.rb
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 # which by default is the one returned by Environment:default. 3 # which by default is the one returned by Environment:default.
4 class Profile < ActiveRecord::Base 4 class Profile < ActiveRecord::Base
5 5
6 - attr_accessible :name, :identifier, :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :environment, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time, :redirection_after_login 6 + attr_accessible :name, :identifier, :public_profile, :nickname, :custom_footer, :custom_header, :address, :zip_code, :contact_phone, :image_builder, :description, :closed, :template_id, :environment, :lat, :lng, :is_template, :fields_privacy, :preferred_domain_id, :category_ids, :country, :city, :state, :national_region_code, :email, :contact_email, :redirect_l10n, :notification_time, :redirection_after_login, :email_suggestions, :allow_members_to_invite, :invite_friends_only
7 7
8 # use for internationalizable human type names in search facets 8 # use for internationalizable human type names in search facets
9 # reimplement on subclasses 9 # reimplement on subclasses
@@ -12,16 +12,15 @@ class Profile &lt; ActiveRecord::Base @@ -12,16 +12,15 @@ class Profile &lt; ActiveRecord::Base
12 end 12 end
13 13
14 SEARCHABLE_FIELDS = { 14 SEARCHABLE_FIELDS = {
15 - :name => 10,  
16 - :identifier => 5,  
17 - :nickname => 2, 15 + :name => {:label => _('Name'), :weight => 10},
  16 + :identifier => {:label => _('Username'), :weight => 5},
  17 + :nickname => {:label => _('Nickname'), :weight => 2},
18 } 18 }
19 19
20 - SEARCH_FILTERS = %w[  
21 - more_recent  
22 - ]  
23 -  
24 - SEARCH_DISPLAYS = %w[compact] 20 + SEARCH_FILTERS = {
  21 + :order => %w[more_recent],
  22 + :display => %w[compact]
  23 + }
25 24
26 def self.default_search_display 25 def self.default_search_display
27 'compact' 26 'compact'
@@ -140,6 +139,17 @@ class Profile &lt; ActiveRecord::Base @@ -140,6 +139,17 @@ class Profile &lt; ActiveRecord::Base
140 139
141 has_many :comments_received, :class_name => 'Comment', :through => :articles, :source => :comments 140 has_many :comments_received, :class_name => 'Comment', :through => :articles, :source => :comments
142 141
  142 + # Although this should be a has_one relation, there are no non-silly names for
  143 + # a foreign key on article to reference the template to which it is
  144 + # welcome_page... =P
  145 + belongs_to :welcome_page, :class_name => 'Article', :dependent => :destroy
  146 +
  147 + def welcome_page_content
  148 + welcome_page && welcome_page.published ? welcome_page.body : nil
  149 + end
  150 +
  151 + has_many :search_terms, :as => :context
  152 +
143 def scraps(scrap=nil) 153 def scraps(scrap=nil)
144 scrap = scrap.is_a?(Scrap) ? scrap.id : scrap 154 scrap = scrap.is_a?(Scrap) ? scrap.id : scrap
145 scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap) 155 scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap)
@@ -155,6 +165,7 @@ class Profile &lt; ActiveRecord::Base @@ -155,6 +165,7 @@ class Profile &lt; ActiveRecord::Base
155 settings_items :public_content, :type => :boolean, :default => true 165 settings_items :public_content, :type => :boolean, :default => true
156 settings_items :description 166 settings_items :description
157 settings_items :fields_privacy, :type => :hash, :default => {} 167 settings_items :fields_privacy, :type => :hash, :default => {}
  168 + settings_items :email_suggestions, :type => :boolean, :default => false
158 169
159 validates_length_of :description, :maximum => 550, :allow_nil => true 170 validates_length_of :description, :maximum => 550, :allow_nil => true
160 171
@@ -219,6 +230,8 @@ class Profile &lt; ActiveRecord::Base @@ -219,6 +230,8 @@ class Profile &lt; ActiveRecord::Base
219 230
220 has_many :abuse_complaints, :foreign_key => 'requestor_id', :dependent => :destroy 231 has_many :abuse_complaints, :foreign_key => 'requestor_id', :dependent => :destroy
221 232
  233 + has_many :profile_suggestions, :foreign_key => :suggestion_id, :dependent => :destroy
  234 +
222 def top_level_categorization 235 def top_level_categorization
223 ret = {} 236 ret = {}
224 self.profile_categorizations.each do |c| 237 self.profile_categorizations.each do |c|
@@ -514,6 +527,14 @@ class Profile &lt; ActiveRecord::Base @@ -514,6 +527,14 @@ class Profile &lt; ActiveRecord::Base
514 generate_url(:profile => identifier, :controller => 'profile', :action => 'index') 527 generate_url(:profile => identifier, :controller => 'profile', :action => 'index')
515 end 528 end
516 529
  530 + def people_suggestions_url
  531 + generate_url(:profile => identifier, :controller => 'friends', :action => 'suggest')
  532 + end
  533 +
  534 + def communities_suggestions_url
  535 + generate_url(:profile => identifier, :controller => 'memberships', :action => 'suggest')
  536 + end
  537 +
517 def generate_url(options) 538 def generate_url(options)
518 url_options.merge(options) 539 url_options.merge(options)
519 end 540 end
@@ -603,7 +624,7 @@ private :generate_url, :url_options @@ -603,7 +624,7 @@ private :generate_url, :url_options
603 end 624 end
604 625
605 def copy_article_tree(article, parent=nil) 626 def copy_article_tree(article, parent=nil)
606 - return if article.is_a?(RssFeed) 627 + return if !copy_article?(article)
607 original_article = self.articles.find_by_name(article.name) 628 original_article = self.articles.find_by_name(article.name)
608 if original_article 629 if original_article
609 num = 2 630 num = 2
@@ -623,6 +644,11 @@ private :generate_url, :url_options @@ -623,6 +644,11 @@ private :generate_url, :url_options
623 end 644 end
624 end 645 end
625 646
  647 + def copy_article?(article)
  648 + !article.is_a?(RssFeed) &&
  649 + !(is_template && article.slug=='welcome-page')
  650 + end
  651 +
626 # Adds a person as member of this Profile. 652 # Adds a person as member of this Profile.
627 def add_member(person) 653 def add_member(person)
628 if self.has_members? 654 if self.has_members?
@@ -632,6 +658,8 @@ private :generate_url, :url_options @@ -632,6 +658,8 @@ private :generate_url, :url_options
632 self.affiliate(person, Profile::Roles.admin(environment.id)) if members.count == 0 658 self.affiliate(person, Profile::Roles.admin(environment.id)) if members.count == 0
633 self.affiliate(person, Profile::Roles.member(environment.id)) 659 self.affiliate(person, Profile::Roles.member(environment.id))
634 end 660 end
  661 + person.tasks.pending.of("InviteMember").select { |t| t.data[:community_id] == self.id }.each { |invite| invite.cancel }
  662 + remove_from_suggestion_list person
635 else 663 else
636 raise _("%s can't have members") % self.class.name 664 raise _("%s can't have members") % self.class.name
637 end 665 end
@@ -769,7 +797,10 @@ private :generate_url, :url_options @@ -769,7 +797,10 @@ private :generate_url, :url_options
769 end 797 end
770 798
771 def admins 799 def admins
772 - self.members_by_role(Profile::Roles.admin(environment.id)) 800 + return [] if environment.blank?
  801 + admin_role = Profile::Roles.admin(environment.id)
  802 + return [] if admin_role.blank?
  803 + self.members_by_role(admin_role)
773 end 804 end
774 805
775 def enable_contact? 806 def enable_contact?
@@ -967,4 +998,14 @@ private :generate_url, :url_options @@ -967,4 +998,14 @@ private :generate_url, :url_options
967 def preferred_login_redirection 998 def preferred_login_redirection
968 redirection_after_login.blank? ? environment.redirection_after_login : redirection_after_login 999 redirection_after_login.blank? ? environment.redirection_after_login : redirection_after_login
969 end 1000 end
  1001 +
  1002 + def remove_from_suggestion_list(person)
  1003 + suggestion = person.profile_suggestions.find_by_suggestion_id self.id
  1004 + suggestion.disable if suggestion
  1005 + end
  1006 +
  1007 + def allow_invitation_from(person)
  1008 + false
  1009 + end
  1010 +
970 end 1011 end
app/models/profile_suggestion.rb 0 → 100644
@@ -0,0 +1,290 @@ @@ -0,0 +1,290 @@
  1 +class ProfileSuggestion < ActiveRecord::Base
  2 + belongs_to :person
  3 + belongs_to :suggestion, :class_name => 'Profile', :foreign_key => :suggestion_id
  4 +
  5 + attr_accessible :person, :suggestion, :suggestion_type, :categories, :enabled
  6 +
  7 + has_many :suggestion_connections, :foreign_key => 'suggestion_id'
  8 + has_many :profile_connections, :through => :suggestion_connections, :source => :connection, :source_type => 'Profile'
  9 + has_many :tag_connections, :through => :suggestion_connections, :source => :connection, :source_type => 'ActsAsTaggableOn::Tag'
  10 +
  11 + before_create do |profile_suggestion|
  12 + profile_suggestion.suggestion_type = self.suggestion.class.to_s
  13 + end
  14 +
  15 + after_destroy do |profile_suggestion|
  16 + self.class.generate_profile_suggestions(profile_suggestion.person)
  17 + end
  18 +
  19 + acts_as_having_settings :field => :categories
  20 +
  21 + validate :must_be_a_valid_category, :on => :create
  22 + def must_be_a_valid_category
  23 + if categories.keys.map { |cat| self.respond_to?(cat)}.include?(false)
  24 + errors.add(:categories, 'Category must be valid')
  25 + end
  26 + end
  27 +
  28 + validates_uniqueness_of :suggestion_id, :scope => [ :person_id ]
  29 + scope :of_person, :conditions => { :suggestion_type => 'Person' }
  30 + scope :of_community, :conditions => { :suggestion_type => 'Community' }
  31 + scope :enabled, :conditions => { :enabled => true }
  32 +
  33 + # {:category_type => ['category-icon', 'category-label']}
  34 + CATEGORIES = {
  35 + :people_with_common_friends => ['menu-people', _('Friends in common')],
  36 + :people_with_common_communities => ['menu-community',_('Communities in common')],
  37 + :people_with_common_tags => ['edit', _('Tags in common')],
  38 + :communities_with_common_friends => ['menu-people', _('Friends in common')],
  39 + :communities_with_common_tags => ['edit', _('Tags in common')]
  40 + }
  41 +
  42 + def category_icon(category)
  43 + 'icon-' + ProfileSuggestion::CATEGORIES[category][0]
  44 + end
  45 +
  46 + def category_label(category)
  47 + ProfileSuggestion::CATEGORIES[category][1]
  48 + end
  49 +
  50 + RULES = {
  51 + :people_with_common_communities => {
  52 + :threshold => 2, :weight => 1, :connection => 'Profile'
  53 + },
  54 + :people_with_common_friends => {
  55 + :threshold => 2, :weight => 1, :connection => 'Profile'
  56 + },
  57 + :people_with_common_tags => {
  58 + :threshold => 2, :weight => 1, :connection => 'ActsAsTaggableOn::Tag'
  59 + },
  60 + :communities_with_common_friends => {
  61 + :threshold => 2, :weight => 1, :connection => 'Profile'
  62 + },
  63 + :communities_with_common_tags => {
  64 + :threshold => 2, :weight => 1, :connection => 'ActsAsTaggableOn::Tag'
  65 + }
  66 + }
  67 +
  68 + RULES.keys.each do |rule|
  69 + settings_items rule
  70 + attr_accessible rule
  71 + end
  72 +
  73 + # Number of suggestions by rule
  74 + N_SUGGESTIONS = 30
  75 +
  76 + # Minimum number of suggestions
  77 + MIN_LIMIT = 10
  78 +
  79 + def self.profile_id(rule)
  80 + "#{rule}_profile_id"
  81 + end
  82 +
  83 + def self.connections(rule)
  84 + "#{rule}_connections"
  85 + end
  86 +
  87 + def self.counter(rule)
  88 + "#{rule}_count"
  89 + end
  90 +
  91 + # If you are about to rewrite the following sql queries, think twice. After
  92 + # that make sure that whatever you are writing to replace it should be faster
  93 + # than how it is now. Yes, sqls are ugly but are fast! And fast is what we
  94 + # need here.
  95 + #
  96 + # The logic behind this code is to produce a table somewhat like this:
  97 + # profile_id | rule1_count | rule1_connections | rule2_count | rule2_connections | ... | score |
  98 + # 12 | 2 | {32,54} | 3 | {8,22,27} | ... | 13 |
  99 + # 13 | 4 | {3,12,32,54} | 2 | {11,24} | ... | 15 |
  100 + # 14 | | | 2 | {44,56} | ... | 17 |
  101 + # ...
  102 + # ...
  103 + #
  104 + # This table has the suggested profile id and the count and connections of
  105 + # each rule that made this profile be suggested. Each suggestion has a score
  106 + # associated based on the rules' counts and rules' weights.
  107 + #
  108 + # From this table, we can sort suggestions by the score and save a small
  109 + # amount of them in the database. At this moment we also register the
  110 + # connections of each suggestion.
  111 +
  112 + def self.calculate_suggestions(person)
  113 + suggested_profiles = all_suggestions(person)
  114 + return if suggested_profiles.nil?
  115 +
  116 + already_suggested_profiles = person.profile_suggestions.map(&:suggestion_id).join(',')
  117 + suggested_profiles = suggested_profiles.where("profiles.id NOT IN (#{already_suggested_profiles})") if already_suggested_profiles.present?
  118 + #TODO suggested_profiles = suggested_profiles.order('score DESC')
  119 + suggested_profiles = suggested_profiles.limit(N_SUGGESTIONS)
  120 + return if suggested_profiles.blank?
  121 +
  122 + suggested_profiles.each do |suggested_profile|
  123 + suggestion = person.profile_suggestions.find_or_initialize_by_suggestion_id(suggested_profile.id)
  124 + RULES.each do |rule, options|
  125 + begin
  126 + value = suggested_profile.send("#{rule}_count").to_i
  127 + rescue NoMethodError
  128 + next
  129 + end
  130 + connections = suggested_profile.send("#{rule}_connections")
  131 + if connections.present?
  132 + connections = connections[1..-2].split(',')
  133 + else
  134 + connections = []
  135 + end
  136 + suggestion.send("#{rule}=", value)
  137 + connections.each do |connection_id|
  138 + next if SuggestionConnection.where(:suggestion_id => suggestion.id, :connection_id => connection_id, :connection_type => options[:connection]).present?
  139 + SuggestionConnection.create!(:suggestion => suggestion, :connection_id => connection_id, :connection_type => options[:connection])
  140 + end
  141 + suggestion.score += value * options[:weight]
  142 + end
  143 + suggestion.save!
  144 + end
  145 + end
  146 +
  147 + def self.people_with_common_friends(person)
  148 + person_friends = person.friends.map(&:id)
  149 + rule = "people_with_common_friends"
  150 + return if person_friends.blank?
  151 + "SELECT person_id as #{profile_id(rule)},
  152 + array_agg(friend_id) as #{connections(rule)},
  153 + count(person_id) as #{counter(rule)}
  154 + FROM friendships WHERE friend_id IN (#{person_friends.join(',')})
  155 + AND person_id NOT IN (#{(person_friends << person.id).join(',')})
  156 + GROUP BY person_id"
  157 + end
  158 +
  159 + def self.people_with_common_communities(person)
  160 + person_communities = person.communities.map(&:id)
  161 + rule = "people_with_common_communities"
  162 + return if person_communities.blank?
  163 + "SELECT common_members.accessor_id as #{profile_id(rule)},
  164 + array_agg(common_members.resource_id) as #{connections(rule)},
  165 + count(common_members.accessor_id) as #{counter(rule)}
  166 + FROM
  167 + (SELECT DISTINCT accessor_id, resource_id FROM
  168 + role_assignments WHERE role_assignments.resource_id IN (#{person_communities.join(',')}) AND
  169 + role_assignments.accessor_id != #{person.id} AND role_assignments.resource_type = 'Profile' AND
  170 + role_assignments.accessor_type = 'Profile') AS common_members
  171 + GROUP BY common_members.accessor_id"
  172 + end
  173 +
  174 + def self.people_with_common_tags(person)
  175 + profile_tags = person.articles.select('tags.id').joins(:tags).map(&:id)
  176 + rule = "people_with_common_tags"
  177 + return if profile_tags.blank?
  178 + "SELECT results.profiles_id as #{profile_id(rule)},
  179 + array_agg(results.tags_id) as #{connections(rule)},
  180 + count(results.profiles_id) as #{counter(rule)}
  181 + FROM (
  182 + SELECT DISTINCT tags.id as tags_id, profiles.id as profiles_id FROM profiles
  183 + INNER JOIN articles ON articles.profile_id = profiles.id
  184 + INNER JOIN taggings ON taggings.taggable_id = articles.id AND taggings.context = ('tags') AND taggings.taggable_type = 'Article'
  185 + INNER JOIN tags ON tags.id = taggings.tag_id
  186 + WHERE (tags.id in (#{profile_tags.join(',')}) AND profiles.id != #{person.id})) AS results
  187 + GROUP BY results.profiles_id"
  188 + end
  189 +
  190 + def self.communities_with_common_friends(person)
  191 + person_friends = person.friends.map(&:id)
  192 + rule = "communities_with_common_friends"
  193 + return if person_friends.blank?
  194 + "SELECT common_communities.resource_id as #{profile_id(rule)},
  195 + array_agg(common_communities.accessor_id) as #{connections(rule)},
  196 + count(common_communities.resource_id) as #{counter(rule)}
  197 + FROM
  198 + (SELECT DISTINCT accessor_id, resource_id FROM
  199 + role_assignments WHERE role_assignments.accessor_id IN (#{person_friends.join(',')}) AND
  200 + role_assignments.accessor_id != #{person.id} AND role_assignments.resource_type = 'Profile' AND
  201 + role_assignments.accessor_type = 'Profile') AS common_communities
  202 + GROUP BY common_communities.resource_id"
  203 + end
  204 +
  205 + def self.communities_with_common_tags(person)
  206 + profile_tags = person.articles.select('tags.id').joins(:tags).map(&:id)
  207 + rule = "communities_with_common_tags"
  208 + return if profile_tags.blank?
  209 + "SELECT results.profiles_id as #{profile_id(rule)},
  210 + array_agg(results.tags_id) as #{connections(rule)},
  211 + count(results.profiles_id) as #{counter(rule)}
  212 + FROM
  213 + (SELECT DISTINCT tags.id as tags_id, profiles.id AS profiles_id FROM profiles
  214 + INNER JOIN articles ON articles.profile_id = profiles.id
  215 + INNER JOIN taggings ON taggings.taggable_id = articles.id AND taggings.context = ('tags') AND taggings.taggable_type = 'Article'
  216 + INNER JOIN tags ON tags.id = taggings.tag_id
  217 + WHERE (tags.id IN (#{profile_tags.join(',')}) AND profiles.id != #{person.id})) AS results
  218 + GROUP BY results.profiles_id"
  219 + end
  220 +
  221 + def self.all_suggestions(person)
  222 + select_string = ["profiles.*"]
  223 + suggestions_join = []
  224 + where_string = []
  225 + valid_rules = []
  226 + previous_rule = nil
  227 + join_column = nil
  228 + RULES.each do |rule, options|
  229 + rule_select = self.send(rule, person)
  230 + next if !rule_select.present?
  231 +
  232 + valid_rules << rule
  233 + select_string << "suggestions.#{counter(rule)} as #{counter(rule)}, suggestions.#{connections(rule)} as #{connections(rule)}"
  234 + where_string << "#{counter(rule)} >= #{options[:threshold]}"
  235 + rule_select = "
  236 + (SELECT profiles.id as #{profile_id(rule)},
  237 + #{rule}_sub.#{counter(rule)} as #{counter(rule)},
  238 + #{rule}_sub.#{connections(rule)} as #{connections(rule)}
  239 + FROM profiles
  240 + LEFT OUTER JOIN (#{rule_select}) as #{rule}_sub
  241 + ON profiles.id = #{rule}_sub.#{profile_id(rule)}) AS #{rule}"
  242 +
  243 + if previous_rule.nil?
  244 + result = rule_select
  245 + else
  246 + result = "INNER JOIN #{rule_select}
  247 + ON #{previous_rule}.#{profile_id(previous_rule)} = #{rule}.#{profile_id(rule)}"
  248 + end
  249 + previous_rule = rule
  250 + suggestions_join << result
  251 + end
  252 +
  253 + return if valid_rules.blank?
  254 +
  255 + select_string = select_string.compact.join(',')
  256 + join_string = "INNER JOIN (SELECT * FROM #{suggestions_join.compact.join(' ')}) AS suggestions ON profiles.id = suggestions.#{profile_id(valid_rules.first)}"
  257 + where_string = where_string.compact.join(' OR ')
  258 +
  259 + person.environment.profiles.
  260 + select(select_string).
  261 + joins(join_string).
  262 + where(where_string)
  263 + end
  264 +
  265 + def disable
  266 + self.enabled = false
  267 + self.save!
  268 + self.class.generate_profile_suggestions(self.person)
  269 + end
  270 +
  271 + def self.generate_all_profile_suggestions
  272 + Delayed::Job.enqueue(ProfileSuggestion::GenerateAllJob.new) unless ProfileSuggestion::GenerateAllJob.exists?
  273 + end
  274 +
  275 + def self.generate_profile_suggestions(person, force = false)
  276 + return if person.profile_suggestions.enabled.count >= MIN_LIMIT && !force
  277 + Delayed::Job.enqueue ProfileSuggestionsJob.new(person.id) unless ProfileSuggestionsJob.exists?(person.id)
  278 + end
  279 +
  280 + class GenerateAllJob
  281 + def self.exists?
  282 + Delayed::Job.by_handler("--- !ruby/object:ProfileSuggestion::GenerateAllJob {}\n").count > 0
  283 + end
  284 +
  285 + def perform
  286 + Person.find_each {|person| ProfileSuggestion.generate_profile_suggestions(person) }
  287 + end
  288 + end
  289 +
  290 +end
app/models/qualifier.rb
@@ -3,7 +3,7 @@ class Qualifier &lt; ActiveRecord::Base @@ -3,7 +3,7 @@ class Qualifier &lt; ActiveRecord::Base
3 attr_accessible :name, :environment 3 attr_accessible :name, :environment
4 4
5 SEARCHABLE_FIELDS = { 5 SEARCHABLE_FIELDS = {
6 - :name => 1, 6 + :name => {:label => _('Name'), :weight => 1},
7 } 7 }
8 8
9 belongs_to :environment 9 belongs_to :environment
app/models/scrap.rb
@@ -3,7 +3,7 @@ class Scrap &lt; ActiveRecord::Base @@ -3,7 +3,7 @@ class Scrap &lt; ActiveRecord::Base
3 attr_accessible :content, :sender_id, :receiver_id, :scrap_id 3 attr_accessible :content, :sender_id, :receiver_id, :scrap_id
4 4
5 SEARCHABLE_FIELDS = { 5 SEARCHABLE_FIELDS = {
6 - :content => 1, 6 + :content => {:label => _('Content'), :weight => 1},
7 } 7 }
8 validates_presence_of :content 8 validates_presence_of :content
9 validates_presence_of :sender_id, :receiver_id 9 validates_presence_of :sender_id, :receiver_id
app/models/search_term.rb 0 → 100644
@@ -0,0 +1,63 @@ @@ -0,0 +1,63 @@
  1 +class SearchTerm < ActiveRecord::Base
  2 + validates_presence_of :term, :context
  3 + validates_uniqueness_of :term, :scope => [:context_id, :context_type, :asset]
  4 +
  5 + belongs_to :context, :polymorphic => true
  6 + has_many :occurrences, :class_name => 'SearchTermOccurrence'
  7 +
  8 + attr_accessible :term, :context, :asset
  9 +
  10 + def self.calculate_scores
  11 + os = occurrences_scores
  12 + find_each { |search_term| search_term.calculate_score(os) }
  13 + end
  14 +
  15 + def self.find_or_create(term, context, asset='all')
  16 + context.search_terms.where(:term => term, :asset => asset).first || context.search_terms.create!(:term => term, :asset=> asset)
  17 + end
  18 +
  19 + # Fast way of getting the occurrences score for each search_term. Ugly but fast!
  20 + #
  21 + # Each occurrence of a search_term has a score that is smaller the older the
  22 + # occurrence happened. We subtract the amount of time between now and the
  23 + # moment it happened from the total time any occurrence is valid to happen. E.g.:
  24 + # The expiration time is 100 days and an occurrence happened 3 days ago.
  25 + # Therefore the score is 97. Them we sum every score to get the total score
  26 + # for a search term.
  27 + def self.occurrences_scores
  28 + ActiveSupport::OrderedHash[*ActiveRecord::Base.connection.execute(
  29 + joins(:occurrences).
  30 + select("search_terms.id, sum(#{SearchTermOccurrence::EXPIRATION_TIME.to_i} - extract(epoch from (now() - search_term_occurrences.created_at))) as value").
  31 + where("search_term_occurrences.created_at > ?", DateTime.now - SearchTermOccurrence::EXPIRATION_TIME).
  32 + group("search_terms.id").
  33 + order('value DESC').
  34 + to_sql
  35 + ).map {|result| [result['id'].to_i, result['value'].to_i]}.flatten]
  36 + end
  37 +
  38 + def calculate_occurrence(occurrences_scores)
  39 + max_score = occurrences_scores.first[1]
  40 + (occurrences_scores[id]/max_score.to_f)*100
  41 + end
  42 +
  43 + def calculate_relevance(valid_occurrences)
  44 + indexed = valid_occurrences.last.indexed.to_f
  45 + return 0 if indexed == 0
  46 + total = valid_occurrences.last.total.to_f
  47 + (1 - indexed/total)*100
  48 + end
  49 +
  50 + def calculate_score(occurrences_scores)
  51 + valid_occurrences = occurrences.valid
  52 + if valid_occurrences.present?
  53 + # These scores vary from 1~100
  54 + self.occurrence_score = calculate_occurrence(occurrences_scores)
  55 + self.relevance_score = calculate_relevance(valid_occurrences)
  56 + else
  57 + self.occurrence_score = 0
  58 + self.relevance_score = 0
  59 + end
  60 + self.score = (occurrence_score * relevance_score)/100.0
  61 + self.save!
  62 + end
  63 +end
app/models/search_term_occurrence.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +class SearchTermOccurrence < ActiveRecord::Base
  2 + belongs_to :search_term
  3 + validates_presence_of :search_term
  4 + attr_accessible :search_term, :created_at, :total, :indexed
  5 +
  6 + EXPIRATION_TIME = 1.year
  7 +
  8 + scope :valid, :conditions => ["search_term_occurrences.created_at > ?", DateTime.now - EXPIRATION_TIME]
  9 +end
app/models/suggestion_connection.rb 0 → 100644
@@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
  1 +class SuggestionConnection < ActiveRecord::Base
  2 + attr_accessible :suggestion, :connection_type, :connection_id
  3 +
  4 + belongs_to :suggestion, :class_name => 'ProfileSuggestion', :foreign_key => 'suggestion_id'
  5 + belongs_to :connection, :polymorphic => true
  6 +end
app/models/user.rb
@@ -11,6 +11,10 @@ class User &lt; ActiveRecord::Base @@ -11,6 +11,10 @@ class User &lt; ActiveRecord::Base
11 N_('Password confirmation') 11 N_('Password confirmation')
12 N_('Terms accepted') 12 N_('Terms accepted')
13 13
  14 + SEARCHABLE_FIELDS = {
  15 + :email => {:label => _('Email'), :weight => 5},
  16 + }
  17 +
14 def self.[](login) 18 def self.[](login)
15 self.find_by_login(login) 19 self.find_by_login(login)
16 end 20 end
app/views/account/signup.html.erb
1 -<% if @register_pending %>  
2 - <div id='thanks-for-signing'>  
3 - <% if environment.has_custom_welcome_screen? %>  
4 - <%= environment.settings[:signup_welcome_screen_body].html_safe %>  
5 - <% elsif environment.enabled?('admin_must_approve_new_users')%>  
6 - <h1><%= _("Welcome to %s!") % environment.name %></h1>  
7 - <h3><%= _("Thanks for signing up, we're thrilled to have you on our social network!") %></h3>  
8 - <p><%= _("Firstly, some tips for getting started:") %></p>  
9 - <% unless environment.enabled?('skip_new_user_email_confirmation') %>  
10 - <h4><%= _("Confirm your account and wait for admin approvement!") %></h4>  
11 - <p><%= _("You should receive a welcome email from us shortly. Please take a second to follow the link within to confirm your account.") %></p>  
12 - <p><%= _("You won't appear as %s until your account is confirmed and approved.") % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p>  
13 - <% else %>  
14 - <h4><%= _("Wait for admin approvement!") %></h4>  
15 - <p><%= _("The administrators will evaluate your signup request for approvement.") %></p>  
16 - <p><%= _("You won't appear as %s until your account is approved.") % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p>  
17 - <% end %>  
18 - <h4><%= _("What to do next?") %></h4>  
19 - <p><%= _("%s. Upload an avatar and let your friends find you easily :)") % link_to(_('Customize your profile'), {:controller => 'doc', :section => 'user', :topic => 'editing-person-info'}, :target => '_blank') %></p>  
20 - <p><%= _("Learn the guidelines. Read the %s for more details on how to use this social network!") % link_to(_('Documentation'), {:controller => 'doc'}, :target => '_blank') %></p>  
21 - <p><%= _("%s your Gmail, Yahoo and Hotmail contacts!") % link_to(_('Invite and find'), {:controller => 'doc', :section => 'user', :topic => 'invite-contacts'}, :target => '_blank') %></p>  
22 - <p><%= _("Start exploring and have fun!") %></p>  
23 - <% else %>  
24 - <h1><%= _("Welcome to %s!") % environment.name %></h1>  
25 - <h3><%= _("Thanks for signing up, we're thrilled to have you on our social network!") %></h3>  
26 - <p><%= _("Firstly, some tips for getting started:") %></p>  
27 - <h4><%= _("Confirm your account!") %></h4>  
28 - <p><%= _("You should receive a welcome email from us shortly. Please take a second to follow the link within to confirm your account.") %></p>  
29 - <p><%= _("You won't appear as %s until your account is confirmed.") % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p>  
30 - <h4><%= _("What to do next?") %></h4>  
31 - <p><%= _("%s. Upload an avatar and let your friends find you easily :)") % link_to(_('Customize your profile'), {:controller => 'doc', :section => 'user', :topic => 'editing-person-info'}, :target => '_blank') %></p>  
32 - <p><%= _("Learn the guidelines. Read the %s for more details on how to use this social network!") % link_to(_('Documentation'), {:controller => 'doc'}, :target => '_blank') %></p>  
33 - <p><%= _("%s your Gmail, Yahoo and Hotmail contacts!") % link_to(_('Invite and find'), {:controller => 'doc', :section => 'user', :topic => 'invite-contacts'}, :target => '_blank') %></p>  
34 - <p><%= _("Start exploring and have fun!") %></p>  
35 - <% end %>  
36 - </div>  
37 -<% else %>  
38 - <h1><%= _('Sign up for %s!') % environment.name %></h1>  
39 - <%= render :partial => 'signup_form' %>  
40 -<% end %> 1 +<h1><%= _('Sign up for %s!') % environment.name %></h1>
  2 +<%= render :partial => 'signup_form' %>
app/views/admin_panel/_signup_welcome_screen.html.erb
1 <div class='description'> 1 <div class='description'>
2 - <%= _('This text will be showed as a welcome message to users after signup') %><br/><br/> 2 + <%= _('If you enable this feature on the "Features" section of the Administration Panel, this text will be shown as a welcome message to users after signup.') %>
3 </div> 3 </div>
4 -  
5 <%= labelled_form_field(_('Body'), text_area(:environment, :signup_welcome_screen_body, :cols => 40, :style => 'width: 100%', :class => 'mceEditor')) %> 4 <%= labelled_form_field(_('Body'), text_area(:environment, :signup_welcome_screen_body, :cols => 40, :style => 'width: 100%', :class => 'mceEditor')) %>
  5 +
  6 +<div class='description'>
  7 + <%= _('If this content is left blank, the following page will be displayed to the user:') %>
  8 +</div>
  9 +
  10 +<%= render :file => 'home/welcome', :locals => {:default_message => true} %>
app/views/admin_panel/site_info.html.erb
@@ -10,9 +10,12 @@ @@ -10,9 +10,12 @@
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', 13 + <% #TODO I renamed the labels of signup-welcome-text and signup-welcome-page
  14 + # so texts more meaningful but I'm not rewriting every variable or reference to
  15 + # this. I leave this task to whoever thinks this is too annoying.%>
  16 + <% tabs << {:title => _('Signup welcome email'), :id => 'signup-welcome-text',
14 :content => (render :partial => 'signup_welcome_text', :locals => {:f => f})} %> 17 :content => (render :partial => 'signup_welcome_text', :locals => {:f => f})} %>
15 - <% tabs << {:title => _('Signup welcome message'), :id => 'signup-welcome-message', 18 + <% tabs << {:title => _('Signup welcome page'), :id => 'signup-welcome-message',
16 :content => (render :partial => 'signup_welcome_screen', :locals => {:f => f}) }%> 19 :content => (render :partial => 'signup_welcome_screen', :locals => {:f => f}) }%>
17 <%= render_tabs(tabs) %> 20 <%= render_tabs(tabs) %>
18 <% button_bar do %> 21 <% button_bar do %>
app/views/blocks/communities.html.erb 0 → 100644
@@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
  1 +<% if owner.kind_of?(Profile) %>
  2 + <%= link_to s_('communities|View all'), {:profile => owner.identifier, :controller => 'profile', :action => 'communities'}, :class => 'view-all' %>
  3 +<% elsif owner.kind_of?(Environment) %>
  4 + <%= link_to s_('communities|View all'), {:controller => 'search', :action => 'communities'}, :class => 'view-all' %>
  5 +<% end %>
  6 +
  7 +<% if user && user == profile && suggestions && !suggestions.empty? %>
  8 + <div class='suggestions-block common-profile-list-block'>
  9 + <h4 class='block-subtitle'><%= _('Some suggestions for you') %></h4>
  10 + <div class='profiles-suggestions'>
  11 + <%= render :partial => 'shared/profile_suggestions_list', :locals => { :suggestions => suggestions, :collection => :communities_suggestions, :per_page => 3 } %>
  12 + </div>
  13 + <div class='more-suggestions'>
  14 + <%= link_to _('See all suggestions'), profile.communities_suggestions_url %>
  15 + </div>
  16 + </div>
  17 +<% end %>
app/views/blocks/my_network.html.erb
1 <%= block_title(title) %> 1 <%= block_title(title) %>
2 2
3 -<%= render_profile_actions owner.class %>  
4 -  
5 <ul> 3 <ul>
6 <li><%= link_to(_('Homepage'), owner.url, :class => 'url') %></li> 4 <li><%= link_to(_('Homepage'), owner.url, :class => 'url') %></li>
7 <li><%= link_to(_('View profile'), owner.public_profile_url) %></li> 5 <li><%= link_to(_('View profile'), owner.public_profile_url) %></li>
@@ -11,5 +9,5 @@ @@ -11,5 +9,5 @@
11 </ul> 9 </ul>
12 10
13 <div class="my-network-actions"> 11 <div class="my-network-actions">
14 - <%= render 'blocks/profile_info_actions/' + owner.class.name.underscore %> 12 + <%= render_profile_actions owner.class %>
15 </div> 13 </div>
app/views/cms/_drag_and_drop_note.html.erb
1 <p> 1 <p>
2 <em> 2 <em>
3 <%= _('Drag images to add them to the text.') %> 3 <%= _('Drag images to add them to the text.') %>
4 - <%= _('Drag file names to the text to add links.') %> 4 + <%= _('Click on file names to add links to the text.') %>
5 </em> 5 </em>
6 </p> 6 </p>
app/views/cms/_media_new_folder.html.erb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +<%= form_tag({:action => 'new', :profile => profile.identifier}, :id => 'new-folder-dialog', :title => _('Create new folder'), :style => 'display: none;') do %>
  2 + <%= select_profile_folder(
  3 + _('Choose parent folder:'),
  4 + :parent_id, profile, default_folder, {}, {},
  5 + "type='Folder' or type='Gallery'")
  6 + %>
  7 +
  8 + <%= labelled_radio_button _('Gallery'), :folder_type, 'Gallery', true %>
  9 + <%= labelled_radio_button _('Folder'), :folder_type, 'Folder', false %>
  10 +
  11 + <%= labelled_form_field _('Name:'), text_field_tag(:new_folder, nil, 'data-url' => url_for({:action => 'new', :profile => profile.identifier})) %>
  12 + <% button_bar do %>
  13 + <%= submit_button(:newfolder, _('Create')) %>
  14 + <% end %>
  15 +<% end %>
app/views/cms/_published_media_items.html.erb 0 → 100644
@@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
  1 +<% file_types.each do |key, header| %>
  2 + <% display = @recent_files[key].present? ? '' : 'none' %>
  3 + <div class='<%= key.to_s %>' style='display: <%= display%>;'>
  4 + <div class='section-title'>
  5 + <h3><%= header %></h3>
  6 + <% if @recent_files[key].total_pages > 1 %>
  7 + <%= link_to(_('View all'), {:controller => 'cms', :action => 'view_all_media', :profile => profile.identifier, :key => key}, :class => 'view-all colorbox', 'data-key' => key) %>
  8 + <% end %>
  9 + </div>
  10 + <%= render :partial => "cms/media_panel/list_published_media_items", :locals => { key: key, show_pagination_links: false } %>
  11 + </div>
  12 +<% end %>
app/views/cms/_text_editor_sidebar.html.erb
  1 +<% default_folder = content_id_to_str default_folder_for_image_upload(profile) %>
  2 +
1 <div class='text-editor-sidebar'> 3 <div class='text-editor-sidebar'>
2 <span class='button-add' data-value='<%= _('Add to the text') %>'></span> 4 <span class='button-add' data-value='<%= _('Add to the text') %>'></span>
3 <span class='button-zoom' data-value='<%= _('Zoom in') %>'></span> 5 <span class='button-zoom' data-value='<%= _('Zoom in') %>'></span>
4 <span class='button-close' data-value='<%= _('Close') %>'></span> 6 <span class='button-close' data-value='<%= _('Close') %>'></span>
5 7
  8 + <div class='header'><strong><%= _('Insert media') %></strong><%= button('vertical-toggle', _('Show/Hide'), '#') %></div>
  9 +
6 <%= render(:partial => 'textile_quick_reference') if @article.is_a?(TextileArticle) %> 10 <%= render(:partial => 'textile_quick_reference') if @article.is_a?(TextileArticle) %>
7 <div class='text-editor-sidebar-box' id='media-upload-box'> 11 <div class='text-editor-sidebar-box' id='media-upload-box'>
8 - <div class='header'><strong><%= _('Insert media') %></strong></div>  
9 <div id='media-upload-form'> 12 <div id='media-upload-form'>
10 <%= form_tag({ :action => 'media_upload' }, :multipart => true) do %> 13 <%= form_tag({ :action => 'media_upload' }, :multipart => true) do %>
11 <div class='formfield'> 14 <div class='formfield'>
12 - <% default_folder = content_id_to_str default_folder_for_image_upload(profile) %>  
13 <%= select_profile_folder( 15 <%= select_profile_folder(
14 _('Choose folder to upload files:'), 16 _('Choose folder to upload files:'),
15 :parent_id, profile, default_folder, {}, {}, 17 :parent_id, profile, default_folder, {}, {},
16 "type='Folder' or type='Gallery'" 18 "type='Folder' or type='Gallery'"
17 ) %> 19 ) %>
18 </div> 20 </div>
19 - <p><%= file_field_tag('file1') %></p>  
20 - <p><%= file_field_tag('file2') %></p>  
21 - <p><%= file_field_tag('file3') %></p>  
22 - <% button_bar do %>  
23 - <%= submit_button(:save, _('Upload')) %>  
24 - <% end %> 21 + <%= button(:newfolder, _('New folder'), '#', :id => 'new-folder-button') %>
  22 + <p><%= file_field_tag('file', :multiple => true) %></p>
25 <% end %> 23 <% end %>
26 </div> 24 </div>
27 - <div id='media-upload-results' style='display: none'>  
28 - <%= render :partial => 'drag_and_drop_note' %>  
29 - <div class='items'>  
30 - </div>  
31 - <p><%= link_to(_('Upload more files ...'), '#', :id => 'media-upload-more-files')%></p> 25 + <div class='hide-and-show-uploads'>
  26 + <%= link_to(_('Hide all uploads'), nil, :id => 'hide-uploads', :style => 'display: none;', 'data-bootstraped' => false) %>
  27 + <%= link_to(_('Show all uploads'), nil, :id => 'show-uploads', :style => 'display: none;') %>
32 </div> 28 </div>
33 </div> 29 </div>
34 - <div id='media-search-box' class='text-editor-sidebar-box'>  
35 - <div class='header'><strong><%= _('Media search') %></strong></div>  
36 - <p>  
37 - <%= form_tag({ :action => 'search' }) do %>  
38 - <span class='formfield'>  
39 - <input name='q' type='text' id='media-search-query' style='width: 250px;'/>  
40 - </span>  
41 - <%= submit_button :search, _('Search'), :id => 'media-search-button' %>  
42 - <% end %>  
43 - </p>  
44 - <div id='media-search-results' style='display: none'>  
45 - <%= render :partial => 'drag_and_drop_note' %>  
46 - <div class='items'>  
47 - </div> 30 +
  31 + <div id='published-media' class='text-editor-sidebar-box' data-url='<%= url_for({:controller => 'cms', :action => 'published_media_items', :profile => profile.identifier}) %>'>
  32 + <%= select_profile_folder(nil, :parent_id, profile, 'recent-media', {}, {},
  33 + "type='Folder' or type='Gallery'", {:root_label => _('Recent media')}) %>
  34 + <%= labelled_form_field _('Search'), text_field_tag('q') %>
  35 + <%= render :partial => 'drag_and_drop_note' %>
  36 + <div class='items'>
  37 + <%= render :partial => 'published_media_items' %>
48 </div> 38 </div>
49 </div> 39 </div>
50 </div> 40 </div>
51 41
  42 +<script id="template-upload" type="text/x-tmpl">
  43 + <div id="file-{%= o.id %}" class="upload" title="{%= o.name %}">
  44 + <div class="file-name">{%=o.name%}</div>
  45 + <div class="percentage"></div>
  46 + <div class="progress"><div class="bar" style="width: 0%;"></div></div>
  47 + </div>
  48 +</script>
52 49
  50 +<%= render :partial => 'media_new_folder', :locals => {:default_folder => default_folder} %>
  51 +<%= javascript_include_tag 'jquery.fileupload.js', 'tmpl.js', 'media-panel.js' %>
app/views/cms/_textile_quick_reference.html.erb
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 <pre># <%= _('first item') %> 15 <pre># <%= _('first item') %>
16 # <%= _('second item') %></pre> 16 # <%= _('second item') %></pre>
17 <p><%= h(_('For code, use HTML tags <pre> and <code>, and indent the code inside them:')) %> 17 <p><%= h(_('For code, use HTML tags <pre> and <code>, and indent the code inside them:')) %>
  18 + </p>
18 <pre> 19 <pre>
19 &lt;pre&gt; 20 &lt;pre&gt;
20 &lt;code&gt; 21 &lt;code&gt;
app/views/cms/edit.html.erb
1 <%= error_messages_for 'article' %> 1 <%= error_messages_for 'article' %>
2 2
3 -<div class='<%= (environment.enabled?('media_panel') ? 'with_media_panel' : 'no_media_panel') %>'> 3 +<% show_media_panel = environment.enabled?('media_panel') && [TinyMceArticle, TextileArticle, Event, EnterpriseHomepage].any?{|klass| @article.kind_of?(klass)} %>
  4 +
  5 +<div class='<%= (show_media_panel ? 'with_media_panel' : 'no_media_panel') %>'>
4 <%= labelled_form_for 'article', :html => { :multipart => true, :class => @type } do |f| %> 6 <%= labelled_form_for 'article', :html => { :multipart => true, :class => @type } do |f| %>
5 7
6 <%= hidden_field_tag("type", @type) if @type %> 8 <%= hidden_field_tag("type", @type) if @type %>
@@ -66,7 +68,7 @@ @@ -66,7 +68,7 @@
66 <% end %> 68 <% end %>
67 </div> 69 </div>
68 70
69 -<% if environment.enabled?('media_panel') && [TinyMceArticle, TextileArticle, Event, EnterpriseHomepage].any?{|klass| @article.kind_of?(klass)} %> 71 +<% if show_media_panel %>
70 <%= render :partial => 'text_editor_sidebar' %> 72 <%= render :partial => 'text_editor_sidebar' %>
71 <% end %> 73 <% end %>
72 74
app/views/cms/media_panel/_generic.html.erb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +<div class="item file <%= icon_for_article(@file) %>" data-item="div">
  2 + <div>
  3 + <a class="add-to-text" href="<%= url_for(@file.url) %>" title="<%= @file.title %>"><%= @file.title %></a>
  4 + </div>
  5 +</div>
app/views/cms/media_panel/_image.html.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<div class="item image" data-item="span" title="<%= @file.name %>">
  2 + <span>
  3 + <img src="<%= @file.public_filename(:uploaded) %>"/>
  4 + </span>
  5 + <div class="controls image-controls">
  6 + <a class="button icon-add add-to-text" href="#"><span><%= _('Add to the text') %></span></a>
  7 + <a class="button icon-zoom zoom" href="#" title="<%= _('Zoom in') %>"><span><%= _('Zoom in') %></span></a>
  8 + </div>
  9 +</div>
app/views/cms/media_panel/_list_published_media_items.html.erb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +<% @recent_files[key].each do |file| %>
  2 + <% @file = file %>
  3 + <%= render :partial => "cms/media_panel/#{key.to_s.singularize}" %>
  4 +<% end %>
  5 +<%= pagination_links @recent_files[key] if show_pagination_links %>
app/views/cms/media_upload.js.erb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +loadPublishedMedia();
app/views/cms/publish.html.erb
1 -<h1><%= _('Select the groups where you want to publish your article') %></h1> 1 +<div class="select-publish-target">
  2 +
  3 +<h2><%= _('Where do you want to publish this article?') %></h2>
2 4
3 <% if !@failed.blank? %> 5 <% if !@failed.blank? %>
4 <div class="errorExplanation" id="errorExplanation"> 6 <div class="errorExplanation" id="errorExplanation">
@@ -14,15 +16,54 @@ @@ -14,15 +16,54 @@
14 </div> 16 </div>
15 <% end %> 17 <% end %>
16 18
17 -<%= form_tag do%>  
18 - <%= hidden_field_tag :back_to, @back_to %>  
19 - <% @groups.each do |group| %>  
20 - <%= labelled_check_box group.name, "marked_groups[#{group.id}][group_id]", group.id, @marked_groups.include?(group) %><br />  
21 - <%= labelled_text_field _('Title') + ': ', "marked_groups[#{group.id}][name]", @article.name, :style => 'width: 100%' %>  
22 - <hr /> 19 +<ul class='publish-targets'>
  20 + <% if profile != user %>
  21 + <li onmouseover="javascript: jQuery(this).addClass('mouseover')" onmouseout="jQuery(this).removeClass('mouseover')">
  22 + <strong><%= _("Publish this article on your profile") %></strong>
  23 + <div class='description'><%= _('You can publish this article on your profile where your friends and followers will see.') %></div>
  24 + <%= form_tag do %>
  25 + <%= hidden_field_tag :back_to, @back_to %>
  26 + <%= labelled_form_field _('Title'), text_field_tag('name', @article.name) %>
  27 +
  28 + <% button_bar do %>
  29 + <%= submit_button 'spread', _('Spread this') %>
  30 + <% end %>
  31 + <% end %>
  32 + </li>
23 <% end %> 33 <% end %>
24 34
25 - <% button_bar do %>  
26 - <%= submit_button 'spread', _('Spread this'), :cancel => @back_to %> 35 + <% if user.communities.present? %>
  36 + <li onmouseover="javascript: jQuery(this).addClass('mouseover')" onmouseout="jQuery(this).removeClass('mouseover')">
  37 + <strong><%= _("Publish this article on communities you are part of") %></strong>
  38 + <div class='description'><%= _('You can submit this article to one or more communities you are a member of, just search for the community below.') %></div>
  39 + <%= form_tag :action => 'publish_on_communities', :id => @article.id do %>
  40 + <%= hidden_field_tag :back_to, @back_to %>
  41 + <% search_action = url_for(:action => 'search_communities_to_publish') %>
  42 + <%= token_input_field_tag(:q, 'search-communities-to-publish', search_action, { :hint_text => _('Type in a search for your community'), :zindex => 10000, :focus => false }) %>
  43 + <%= labelled_form_field _('Title'), text_field_tag('name', @article.name) %>
  44 + <% button_bar do %>
  45 + <%= submit_button 'spread', _('Spread this') %>
  46 + <% end %>
  47 + <% end %>
  48 + </li>
27 <% end %> 49 <% end %>
28 -<% end %> 50 +
  51 +
  52 + <% if environment.portal_enabled %>
  53 + <li onmouseover="javascript: jQuery(this).addClass('mouseover')" onmouseout="jQuery(this).removeClass('mouseover')">
  54 + <strong><%= _("Publish your article on portal community") %></strong>
  55 + <div class='description'><%= _('You can suggest this article to the portal community, where it can show up on the homepage.') %></div>
  56 +
  57 + <%= form_tag :action => 'publish_on_portal_community', :id => @article.id do %>
  58 + <%= hidden_field_tag :back_to, @back_to %>
  59 + <%= labelled_form_field _('Title'), text_field_tag('name', @article.name) %>
  60 +
  61 + <% button_bar do %>
  62 + <%= submit_button 'spread', _('Spread this') %>
  63 + <% end %>
  64 + <% end %>
  65 + </li>
  66 + <% end %>
  67 +</ul>
  68 +
  69 +</div>
app/views/cms/view.html.erb
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <%= _('Content management') %> 2 <%= _('Content management') %>
3 </h1> 3 </h1>
4 4
5 -<% if user.can_change_homepage? && !remove_content_button(:home) %> 5 +<% if user.can_change_homepage? && !remove_content_button(:home, profile.home_page) %>
6 <div class="cms-homepage"> 6 <div class="cms-homepage">
7 <%= _('Profile homepage:') %> 7 <%= _('Profile homepage:') %>
8 <% if profile.home_page %> 8 <% if profile.home_page %>
@@ -66,17 +66,17 @@ @@ -66,17 +66,17 @@
66 <%= short_description %> 66 <%= short_description %>
67 </td> 67 </td>
68 <td class="article-controls"> 68 <td class="article-controls">
69 - <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit) %> 69 + <%= expirable_button article, :edit, _('Edit'), {:action => 'edit', :id => article.id} if !remove_content_button(:edit, article) %>
70 <%= button_without_text :eyes, _('Public view'), article.view_url %> 70 <%= button_without_text :eyes, _('Public view'), article.view_url %>
71 - <%= display_spread_button(profile, article) unless article.folder? || remove_content_button(:spread)%>  
72 - <% if user.can_change_homepage? && !remove_content_button(:home) %> 71 + <%= display_spread_button(article) unless remove_content_button(:spread, article) %>
  72 + <% if user.can_change_homepage? && !remove_content_button(:home, article) %>
73 <% if profile.home_page != article %> 73 <% if profile.home_page != article %>
74 <%= expirable_button article, :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %> 74 <%= expirable_button article, :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %>
75 <% else %> 75 <% else %>
76 <%= button_without_text(:'home-not', _('Reset homepage'), { :action => 'set_home_page', :id => nil }, :method => :post) %> 76 <%= button_without_text(:'home-not', _('Reset homepage'), { :action => 'set_home_page', :id => nil }, :method => :post) %>
77 <% end %> 77 <% end %>
78 <% end %> 78 <% end %>
79 - <%= display_delete_button(article) if !remove_content_button(:delete) %> 79 + <%= display_delete_button(article) if !remove_content_button(:delete, article) %>
80 </td> 80 </td>
81 </tr> 81 </tr>
82 <% end %> 82 <% end %>
app/views/cms/view_all_media.html.erb 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +<h1><%= file_types[@key] %></h1>
  2 +
  3 +<div class='view-all-media view-all-<%= @key %>'>
  4 + <%= render :partial => "cms/media_panel/list_published_media_items", :locals => { key: @key, show_pagination_links: true } %>
  5 +</div>
  6 +
  7 +<%= javascript_include_tag 'media-panel.js' %>
app/views/cms/view_all_media.js.erb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +jQuery('.view-all-media').html('<%= escape_javascript(render :partial => "cms/media_panel/list_published_media_items", :locals => { key: @key, show_pagination_links: true }) %>');
app/views/content_viewer/_article_toolbar.html.erb
@@ -2,46 +2,40 @@ @@ -2,46 +2,40 @@
2 <div id="article-actions"> 2 <div id="article-actions">
3 3
4 4
5 - <% if @page.allow_edit?(user) && !remove_content_button(:edit) %> 5 + <% if @page.allow_edit?(user) && !remove_content_button(:edit, @page) %>
6 <% content = content_tag('span', label_for_edit_article(@page)) %> 6 <% content = content_tag('span', label_for_edit_article(@page)) %>
7 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }) %> 7 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }) %>
8 <%= expirable_button @page, :edit, content, url %> 8 <%= expirable_button @page, :edit, content, url %>
9 <% end %> 9 <% end %>
10 10
11 - <% if @page != profile.home_page && !@page.has_posts? && @page.allow_delete?(user) && !remove_content_button(:delete)%> 11 + <% if @page != profile.home_page && !@page.has_posts? && @page.allow_delete?(user) && !remove_content_button(:delete, @page)%>
12 <% content = content_tag( 'span', _('Delete') ) %> 12 <% content = content_tag( 'span', _('Delete') ) %>
13 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'destroy', :id => @page.id}) %> 13 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'destroy', :id => @page.id}) %>
14 <% options = {:method => :post, :confirm => delete_article_message(@page)} %> 14 <% options = {:method => :post, :confirm => delete_article_message(@page)} %>
15 <%= expirable_button @page, :delete, content, url, options %> 15 <%= expirable_button @page, :delete, content, url, options %>
16 <% end %> 16 <% end %>
17 17
18 - <% if !@page.folder? && @page.allow_spread?(user) && !remove_content_button(:spread) %>  
19 - <% content = content_tag( 'span', _('Spread this') ) %>  
20 - <% url = nil %>  
21 - <% if profile.kind_of?(Person) %>  
22 - <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'publish', :id => @page.id }) %>  
23 - <% elsif profile.kind_of?(Community) && environment.portal_community %>  
24 - <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'publish_on_portal_community', :id => @page.id }) %>  
25 - <% end %>  
26 - <%= expirable_button @page, :spread, content, url if url %> 18 + <% if @page.allow_spread?(user) && !remove_content_button(:spread, @page) %>
  19 + <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'publish', :id => @page.id }) %>
  20 + <%= expirable_button @page, :spread, content_tag( 'span', _('Spread this') ), url, {:class => 'colorbox'} if url %>
27 <% end %> 21 <% end %>
28 22
29 <% if !@page.gallery? && (@page.allow_create?(user) || (@page.parent && @page.parent.allow_create?(user))) %> 23 <% if !@page.gallery? && (@page.allow_create?(user) || (@page.parent && @page.parent.allow_create?(user))) %>
30 - <% if @page.translatable? && !@page.native_translation.language.blank? && !remove_content_button(:locale) %> 24 + <% if @page.translatable? && !@page.native_translation.language.blank? && !remove_content_button(:locale, @page) %>
31 <% content = _('Add translation') %> 25 <% content = _('Add translation') %>
32 <% parent_id = (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)) %> 26 <% parent_id = (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)) %>
33 <% url = profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => parent_id, :type => @page.type, :article => { :translation_of_id => @page.native_translation.id })%> 27 <% url = profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => parent_id, :type => @page.type, :article => { :translation_of_id => @page.native_translation.id })%>
34 <%= expirable_button @page, :locale, content, url %> 28 <%= expirable_button @page, :locale, content, url %>
35 <% end %> 29 <% end %>
36 30
37 - <%= colorbox_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)))) unless remove_content_button(:new) %> 31 + <%= colorbox_button(:new, label_for_new_article(@page), profile.admin_url.merge(:controller => 'cms', :action => 'new', :parent_id => (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)))) unless remove_content_button(:new, @page) %>
38 <% end %> 32 <% end %>
39 33
40 <% if @page.accept_uploads? && @page.allow_create?(user) %> 34 <% if @page.accept_uploads? && @page.allow_create?(user) %>
41 - <%= button('upload-file', _('Upload files'), profile.admin_url.merge(:controller => 'cms', :action => 'upload_files', :parent_id => (@page.folder? ? @page : @page.parent))) unless remove_content_button(:upload)%> 35 + <%= button('upload-file', _('Upload files'), profile.admin_url.merge(:controller => 'cms', :action => 'upload_files', :parent_id => (@page.folder? ? @page : @page.parent))) unless remove_content_button(:upload, @page)%>
42 <% end %> 36 <% end %>
43 37
44 - <% if !@page.allow_create?(user) && profile.community? && (@page.blog? || @page.parent && @page.parent.blog?) && !remove_content_button(:suggest) %> 38 + <% if !@page.allow_create?(user) && profile.community? && (@page.blog? || @page.parent && @page.parent.blog?) && !remove_content_button(:suggest, @page) %>
45 <% content = content_tag( 'span', _('Suggest an article') ) %> 39 <% content = content_tag( 'span', _('Suggest an article') ) %>
46 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'suggest_an_article'}) %> 40 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'suggest_an_article'}) %>
47 <% options = {:id => 'suggest-article-link'} %> 41 <% options = {:id => 'suggest-article-link'} %>
app/views/content_viewer/versioned_article.html.erb
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 <div id="article-actions"> 7 <div id="article-actions">
8 <%= button(:clock, _('All versions'), {:controller => 'content_viewer', :profile => profile.identifier, :action => 'article_versions'}, :id => 'article-versions-link') %> 8 <%= button(:clock, _('All versions'), {:controller => 'content_viewer', :profile => profile.identifier, :action => 'article_versions'}, :id => 'article-versions-link') %>
9 9
10 - <% if @page.allow_edit?(user) && !remove_content_button(:undo) %> 10 + <% if @page.allow_edit?(user) && !remove_content_button(:undo, @page) %>
11 <% content = content_tag('span', _('Revert to this version')) %> 11 <% content = content_tag('span', _('Revert to this version')) %>
12 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id, :version => @version }) %> 12 <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id, :version => @version }) %>
13 <%= expirable_button @page, :undo, content, url, :id => 'article-revert-version-link' %> 13 <%= expirable_button @page, :undo, content, url, :id => 'article-revert-version-link' %>
app/views/enterprise_registration/creation.html.erb
1 <h1><%= _('Enterprise registration completed') %></h1> 1 <h1><%= _('Enterprise registration completed') %></h1>
  2 +<p><%= _("Your enterprise (%s) was successfully registered.") % @enterprise.name %></p>
  3 +<p><%= link_to _('You can manage your enterprise now.'), @enterprise.admin_url %></p>
2 4
3 -<p>  
4 -<%= _("Your enterprise (%s) was successfully registered.") % @enterprise.name %>  
5 -</p> 5 +<%= render :partial => 'shared/template_welcome_page', :locals => {:template => @enterprise.template, :header => _("What can I do with a %s?")} %>
  6 +
  7 +<% button_bar do %>
  8 + <%= button :back, _('Back'), {:controller => 'memberships', :action => 'index', :profile => user.identifier} %>
  9 +<% end %>
6 10
7 -<p>  
8 -<%= link_to _('You can manage your enterprise now.'), @enterprise.admin_url %>  
9 -</p>  
app/views/friends/_profile_list.html.erb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +<ul class="profile-list">
  2 + <% profiles.each do |profile| %>
  3 + <li>
  4 + <%= link_to_profile profile_image(profile) + '<br/>' + profile.short_name,
  5 + profile.identifier, :class => 'profile-link' %>
  6 + <div class="controll">
  7 + <%= button_without_text :remove, content_tag('span',_('remove')),
  8 + { :action => 'remove', :id => profile.id },
  9 + :title => _('remove') %>
  10 + <%= button_without_text 'menu-mail', content_tag('span',_('contact')),
  11 + profile.url.merge(:controller => 'contact', :action => 'new', :profile => profile.identifier),
  12 + :title => _('contact') %>
  13 + </div><!-- end class="controll" -->
  14 + </li>
  15 + <% end %>
  16 +</ul>
app/views/friends/connections.html.erb 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +<h1><%= _("Connections with %s") % @suggestion.suggestion.name %></h1>
  2 +
  3 +<% button_bar do %>
  4 + <%= button(:back, _('Go to friends list'), :controller => 'friends') %>
  5 +<% end %>
  6 +
  7 +<%= render :partial => 'shared/profile_connections', :locals => { :suggestion => @suggestion, :tags => @tags, :profiles => @profiles } %>
app/views/friends/index.html.erb
@@ -10,46 +10,28 @@ @@ -10,46 +10,28 @@
10 <%= link_to _('Do you want to see other people in this environment?'), :controller => 'search', :action => 'assets', :asset => 'people' %> 10 <%= link_to _('Do you want to see other people in this environment?'), :controller => 'search', :action => 'assets', :asset => 'people' %>
11 </em> 11 </em>
12 </p> 12 </p>
13 - <% else %>  
14 - <% button_bar do %>  
15 - <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %>  
16 - <%= button(:search, _('Find people'), :controller => 'search', :action => 'assets', :asset => 'people') %>  
17 - <% unless @plugins.dispatch(:remove_invite_friends_button).include?(true) %>  
18 - <%= button(:search, _('Invite people from my e-mail contacts'), :controller => 'invite', :action => 'select_address_book') %>  
19 - <% end %>  
20 - <% end %>  
21 <% end %> 13 <% end %>
22 14
23 - <ul class="profile-list">  
24 - <% @friends.each do |friend| %>  
25 - <li>  
26 - <%= link_to_profile profile_image(friend) + '<br/>' + friend.short_name,  
27 - friend.identifier, :class => 'profile-link' %>  
28 - <div class="controll">  
29 - <%= link_to content_tag('span',_('remove')),  
30 - { :action => 'remove', :id => friend.id },  
31 - :class => 'button icon-remove',  
32 - :title => _('remove') %>  
33 - <%= link_to content_tag('span',_('contact')),  
34 - friend.url.merge(:controller => 'contact', :action => 'new', :profile => friend.identifier),  
35 - :class => 'button icon-menu-mail',  
36 - :title => _('contact') %>  
37 - </div><!-- end class="controll" -->  
38 - </li>  
39 - <% end %>  
40 - </ul>  
41 - <div id='pagination-friends'>  
42 - <%= pagination_links @friends, :param_name => 'npage' %>  
43 - </div>  
44 -  
45 <% button_bar do %> 15 <% button_bar do %>
46 <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %> 16 <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %>
47 <%= button(:search, _('Find people'), :controller => 'search', :action => 'assets', :asset => 'people') %> 17 <%= button(:search, _('Find people'), :controller => 'search', :action => 'assets', :asset => 'people') %>
48 <% unless @plugins.dispatch(:remove_invite_friends_button).include?(true) %> 18 <% unless @plugins.dispatch(:remove_invite_friends_button).include?(true) %>
49 - <%= button(:search, _('Invite people from my e-mail contacts'), :controller => 'invite', :action => 'select_address_book') %> 19 + <%= button(:person, _('Invite people'), :controller => 'invite', :action => 'invite_friends') %>
50 <% end %> 20 <% end %>
51 <% end %> 21 <% end %>
  22 +
  23 + <%= render :partial => 'profile_list', :locals => { :profiles => @friends } %>
  24 +
  25 + <br style="clear:both" />
  26 + <%= pagination_links @friends, :param_name => 'npage' %>
52 <% end %> 27 <% end %>
53 28
54 -</div><!-- end id="manage_friends" --> 29 +<% unless @suggestions.empty? %>
  30 + <br style="clear:both" />
  31 + <h2><%= _("Friends suggestions") %></h2>
  32 + <div class="profiles-suggestions">
  33 + <%= render :partial => 'shared/profile_suggestions_list', :locals => { :suggestions => @suggestions, :collection => :friends_suggestions, :per_page => 12 } %>
  34 + </div>
  35 +<% end %>
55 36
  37 +</div><!-- end id="manage_friends" -->
app/views/friends/remove_suggestion.html.erb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +<div id="remove_suggestion">
  2 + <h1><%= _('Removing suggestion for friend: %s') % @person.name %></h1>
  3 +
  4 + <%= render :partial => 'shared/remove_suggestion', :locals => { :suggestion => @person } %>
  5 +</div>
app/views/friends/suggest.html.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<h1><%= _("Friends suggestions for %s") % profile.name %></h1>
  2 +
  3 +<% button_bar do %>
  4 + <%= button(:back, _('Go to friends list'), :controller => 'friends') %>
  5 +<% end %>
  6 +
  7 +<div class="profiles-suggestions">
  8 + <%= render :partial => 'shared/profile_suggestions_list', :locals => { :suggestions => @suggestions, :collection => :friends_suggestions, :per_page => 12 } %>
  9 +</div>
app/views/home/welcome.html.erb 0 → 100644
@@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
  1 +<% default_message = defined?(default_message) ? default_message : false %>
  2 +
  3 +<div id='thanks-for-signing'>
  4 + <h1><%= _("Welcome to %s!") % environment.name %></h1>
  5 + <% if environment.has_custom_welcome_screen? && !default_message %>
  6 + <%= environment.settings[:signup_welcome_screen_body].html_safe %>
  7 + <% else %>
  8 + <h3><%= _("Thanks for signing up, we're thrilled to have you on our social network!") %></h3>
  9 + <% if @display_confirmation_tips %>
  10 + <p><%= _("Firstly, some tips for getting started:") %></p>
  11 + <h4><%= _("Confirm your account!") %></h4>
  12 + <p><%= _("You should receive a welcome email from us shortly. Please take a second to follow the link within to confirm your account.") %></p>
  13 + <p><%= _("You won't appear as %s until your account is confirmed.") % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p>
  14 + <% else %>
  15 + <h4><%= _("Wait for admin approvement!") %></h4>
  16 + <p><%= _("The administrators will evaluate your signup request for approvement.") %></p>
  17 + <p><%= _("You won't appear as %s until your account is approved.") % link_to(_('user'), {:controller => :search, :action => :people, :filter => 'more_recent'}, :target => '_blank') %></p>
  18 + <% end %>
  19 + <h4><%= _("What to do next?") %></h4>
  20 + <p><%= _("Access your %s and see your face on the network!") %
  21 + (user.present? ? link_to(_('Profile'), {:controller => 'profile', :profile => user.identifier}, :target => '_blank') : 'Profile') %>
  22 + <%= _("You can also explore your %s to customize your profile. Here are some %s on what you can do there.") %
  23 + [user.present? ? link_to(_('Control Panel'), {:controller => 'profile_editor', :profile => user.identifier}, :target => '_blank') : 'Control Panel',
  24 + link_to(_('tips'), {:controller => 'doc', :action => 'topic', :section => 'user', :topic => 'editing-person-info'}, :target => '_blank')] %></p>
  25 + <p><%= _("%s your Gmail, Yahoo and Hotmail contacts!") % link_to(_('Invite and find'), {:controller => 'doc', :action => 'topic', :section => 'user', :topic => 'invite-contacts'}, :target => '_blank') %></p>
  26 + <p><%= _("Learn the guidelines. Read the %s for more details on how to use this social network!") % link_to(_('Documentation'), {:controller => 'doc'}, :target => '_blank') %></p>
  27 + <p><%= _("Start exploring and have fun!") %></p>
  28 + <% end %>
  29 + <%= render :partial => 'shared/template_welcome_page', :locals => {:template => @person_template, :header => _("What can I do as a %s?")} %>
  30 +</div>
app/views/invite/_personalize_invitation_mail.html.erb 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  1 +<br/>
  2 +
  3 +<%= link_to ('Personalize invitation mail'), nil, :onclick => "jQuery('#invitation-mail_template').show(); return false;" %>
  4 +
  5 +<div id='invitation-mail_template' style='display:none'>
  6 + <%= h _("Now enter an invitation message. You must keep the <url> code in your invitation text. When your friends receive the invitation e-mail, <url> will be replaced by a link that they need to click to activate their account. <user> and <friend> codes will be replaced by your name and friend name, but they are optional.") %>
  7 + <%= labelled_form_field(_('Invitation text:'), text_area_tag(:mail_template, mail_template, :cols => 72, :rows => 8)) %>
  8 +</div>
app/views/invite/_select_address_book.html.erb 0 → 100644
@@ -0,0 +1,41 @@ @@ -0,0 +1,41 @@
  1 +<% header ||='h2' %>
  2 +<<%= header %>><%= _('Step 1 of 2: Select address book') %></<%= header %>>
  3 +
  4 +<%= form_tag do %>
  5 +
  6 + <%= [
  7 + radio_button_tag(:import_from, "manual", @import_from == "manual", :onclick => 'hide_invite_friend_login_password()') + content_tag('label', _('Manually (empty field)'), :for => "import_from_manual"),
  8 + radio_button_tag(:import_from, "gmail", @import_from == "gmail", :onclick => 'show_invite_friend_login_password(this.value)') + content_tag('label', 'Gmail', :for => 'import_from_gmail'),
  9 + radio_button_tag(:import_from, "yahoo", @import_from == "yahoo", :onclick => 'show_invite_friend_login_password(this.value)') + content_tag('label', 'Yahoo', :for => "import_from_yahoo"),
  10 + radio_button_tag(:import_from, "hotmail", @import_from == "hotmail", :onclick => 'show_invite_friend_login_password(this.value)') + content_tag('label', 'Hotmail', :for => "import_from_hotmail")
  11 + ].join("\n<br/>\n") %>
  12 +
  13 + <script type="text/javascript">
  14 + function hide_invite_friend_login_password() {
  15 + $('invite-friends-login-password').hide();
  16 + }
  17 + function show_invite_friend_login_password(option) {
  18 + if (option == 'hotmail') {
  19 + $('hotmail_username_tip').show();
  20 + } else {
  21 + $('hotmail_username_tip').hide();
  22 + }
  23 + $('invite-friends-login-password').show();
  24 + $('login').focus();
  25 + }
  26 + </script>
  27 + <div id='invite-friends-login-password' <%= "style='display: none;'" if (@import_from == 'manual') %>>
  28 + <div id='hotmail_username_tip'>
  29 + <%= ui_icon('ui-icon-alert') %>
  30 + <%= _('Please type your username in the format yourname@example.com') %>
  31 + </div>
  32 +
  33 + <%= labelled_form_field(_("Username") + ":", text_field_tag(:login, @login)) %>
  34 + <%= labelled_form_field(_("Password") + ":", password_field_tag(:password)) %>
  35 + </div>
  36 +
  37 + <% button_bar do %>
  38 + <%= submit_button(:forward, _("Next")) %>
  39 + <% end %>
  40 + <p><%= _("We won't store your password or contact anyone without your permission.") %></p>
  41 +<% end %>
app/views/invite/invite_friends.html.erb 0 → 100644
@@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
  1 +<% if profile.person? %>
  2 + <h1><%= _('Ask for friendship') %></h1>
  3 + <% description = _('You can search for user profiles and ask them to become your friends.') %>
  4 +<% else %>
  5 + <h1><%= _('Invite people to join') %></h1>
  6 + <% description = _('You can search for user profiles and invite them to join this group.') %>
  7 +<% end %>
  8 +
  9 +<h3>
  10 + <%= _("Choose person by:") %>
  11 +</h3>
  12 +
  13 +<p>
  14 + <%= labelled_radio_button _("Name"), :invite_friend_by, 1, true, :id => "invite_friend_by_name", :class => "invite_friend_by" %>
  15 + <%= labelled_radio_button _("Email"), :invite_friend_by, 2, false, :id => "invite_friend_by_email", :class => "invite_friend_by" %>
  16 +</p>
  17 +
  18 +<div class='invite_by_name'>
  19 + <p><%= description %></p>
  20 + <%= form_tag :action => 'invite_registered_friend' do %>
  21 + <% search_action = url_for(:action => 'search') %>
  22 + <%= token_input_field_tag(
  23 + :q, 'search-people', search_action,
  24 + { :hint_text => _('Type in the person\'s %{search_fields}') % {:search_fields => @search_fields},
  25 + :focus => false }) %>
  26 +
  27 + <% button_bar do %>
  28 + <%= submit_button('save', _('Invite'))%>
  29 + <%= button('cancel', _('Cancel'), profile.url)%>
  30 + <% end %>
  31 + <% end %>
  32 +</div>
  33 +
  34 +<div class='invite_by_email' style="display: none;">
  35 + <h2><%= _('Invite people from my e-mail contacts') %></h2>
  36 + <% header = 'h3' %>
  37 +
  38 +<%= render :partial => 'invite/select_address_book', :locals => {:header => header} %>
  39 +</div>
  40 +
  41 +<div id="loadingScreen"></div>
  42 +<%= javascript_include_tag 'invite' %>
app/views/invite/select_address_book.html.erb
@@ -1,51 +0,0 @@ @@ -1,51 +0,0 @@
1 -<% if profile.person? %>  
2 - <h1><%= _('Invite your friends') %></h1>  
3 -<% else %>  
4 - <h1><%= _('Invite your friends to join %s') % profile.name %></h1>  
5 -<% end %>  
6 -  
7 -<h2><%= _('Step 1 of 2: Select address book') %></h2>  
8 -  
9 -<%= form_tag do %>  
10 -  
11 - <%= [  
12 - radio_button_tag(:import_from, "manual", @import_from == "manual", :onclick => 'hide_invite_friend_login_password()') + content_tag('label', _('Manually (empty field)'), :for => "import_from_manual"),  
13 - radio_button_tag(:import_from, "gmail", @import_from == "gmail", :onclick => 'show_invite_friend_login_password(this.value)') + content_tag('label', 'Gmail', :for => 'import_from_gmail'),  
14 - radio_button_tag(:import_from, "yahoo", @import_from == "yahoo", :onclick => 'show_invite_friend_login_password(this.value)') + content_tag('label', 'Yahoo', :for => "import_from_yahoo"),  
15 - radio_button_tag(:import_from, "hotmail", @import_from == "hotmail", :onclick => 'show_invite_friend_login_password(this.value)') + content_tag('label', 'Hotmail', :for => "import_from_hotmail")  
16 - ].join("\n<br/>\n") %>  
17 -  
18 - <script type="text/javascript">  
19 - function hide_invite_friend_login_password() {  
20 - $('invite-friends-login-password').hide();  
21 - }  
22 - function show_invite_friend_login_password(option) {  
23 - if (option == 'hotmail') {  
24 - $('hotmail_username_tip').show();  
25 - } else {  
26 - $('hotmail_username_tip').hide();  
27 - }  
28 - $('invite-friends-login-password').show();  
29 - $('login').focus();  
30 - }  
31 - </script>  
32 - <div id='invite-friends-login-password' <%= "style='display: none;'" if (@import_from == 'manual') %>>  
33 - <div id='hotmail_username_tip'>  
34 - <%= ui_icon('ui-icon-alert') %>  
35 - <%= _('Please type your username in the format yourname@example.com') %>  
36 - </div>  
37 -  
38 - <%= labelled_form_field(_("Username") + ":", text_field_tag(:login, @login)) %>  
39 - <%= labelled_form_field(_("Password") + ":", password_field_tag(:password)) %>  
40 - </div>  
41 -  
42 - <% button_bar do %>  
43 - <%= submit_button(:forward, _("Next")) %>  
44 - <% end %>  
45 - <p><%= _("We won't store your password or contact anyone without your permission.") %></p>  
46 -<% end %>  
47 -  
48 -<div id="loadingScreen"></div>  
49 -  
50 -  
51 -  
app/views/invite/select_friends.html.erb
1 <%= render :partial => 'invite/dialog_wait_loading', :locals => {:contact_list => @contact_list.id } if @import_from != 'manual' %> 1 <%= render :partial => 'invite/dialog_wait_loading', :locals => {:contact_list => @contact_list.id } if @import_from != 'manual' %>
2 2
3 <% if profile.person? %> 3 <% if profile.person? %>
4 - <h1><%= _('Invite your friends') %></h1> 4 + <h1><%= _('Invite people') %></h1>
5 <% else %> 5 <% else %>
6 - <h1><%= _('Invite your friends to join %s') % profile.name %></h1> 6 + <h1><%= _('Invite people to join') %></h1>
7 <% end %> 7 <% end %>
8 8
9 9
10 -<h2><%= _('Step 2 of 2: Selecting Friends') %></h2> 10 +<h2><%= _('Step 2 of 2: Selecting People') %></h2>
11 11
12 -<%= button(:back, _('Back'), { :action => 'select_address_book' }, :id => 'invitation_back_button') %> 12 +<%= button(:back, _('Back'), { :action => 'invite_friends' }, :id => 'invitation_back_button') %>
13 13
14 <p> 14 <p>
15 -<%= _('Indicate which friends you want to invite.') %> 15 +<%= _('Indicate which people you want to invite.') %>
16 </p> 16 </p>
17 17
18 <%= form_tag do %> 18 <%= form_tag do %>
@@ -30,16 +30,9 @@ @@ -30,16 +30,9 @@
30 </div> 30 </div>
31 <% end -%> 31 <% end -%>
32 32
33 - <br/>  
34 -  
35 - <%= link_to ('Personalize invitation mail'), nil, :onclick => "jQuery('#invitation-mail_template').show(); return false;" %>  
36 -  
37 - <div id='invitation-mail_template' style='display:none'>  
38 - <%= h _("Now enter an invitation message. You must keep the <url> code in your invitation text. When your friends receive the invitation e-mail, <url> will be replaced by a link that they need to click to activate their account. <user> and <friend> codes will be replaced by your name and friend name, but they are optional.") %>  
39 - <%= labelled_form_field(_('Invitation text:'), text_area_tag(:mail_template, @mail_template, :cols => 72, :rows => 8)) %>  
40 - </div> 33 + <%= render :partial => 'invite/personalize_invitation_mail', :locals => {:mail_template => @mail_template } %>
41 34
42 <% button_bar do %> 35 <% button_bar do %>
43 - <%= submit_button(:ok, _("Invite my friends!")) %> 36 + <%= submit_button(:ok, _("Invite!")) %>
44 <% end %> 37 <% end %>
45 <% end %> 38 <% end %>
app/views/layouts/_javascript.html.erb
@@ -2,8 +2,8 @@ @@ -2,8 +2,8 @@
2 'jquery-2.1.1.min', 'jquery-migrate-1.2.1', 2 'jquery-2.1.1.min', 'jquery-migrate-1.2.1',
3 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox', 3 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox',
4 'jquery-ui-1.10.4/js/jquery-ui-1.10.4.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate', 4 'jquery-ui-1.10.4/js/jquery-ui-1.10.4.min', 'jquery.scrollTo', 'jquery.form.js', 'jquery-validation/jquery.validate',
5 -'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput',  
6 -'add-and-join', 'report-abuse', 'catalog', 'manage-products', 'autogrow', 5 +'jquery.cookie', 'jquery.ba-bbq.min.js', 'reflection', 'jquery.tokeninput', 'jquery.typewatch', 'jquery.textchange',
  6 +'add-and-join', 'report-abuse', 'catalog', 'manage-products', 'autogrow', 'select-or-die/_src/selectordie',
7 'jquery-timepicker-addon/dist/jquery-ui-timepicker-addon', 'application.js', 'rails.js', 'inputosaurus.js', :cache => 'cache/application' %> 7 'jquery-timepicker-addon/dist/jquery-ui-timepicker-addon', 'application.js', 'rails.js', 'inputosaurus.js', :cache => 'cache/application' %>
8 8
9 <% language = FastGettext.locale %> 9 <% language = FastGettext.locale %>
app/views/layouts/_user.html.erb
@@ -10,16 +10,17 @@ @@ -10,16 +10,17 @@
10 <%= _("<span class='login'>%s</span>") % thickbox_inline_popup_link('<i class="icon-menu-login"></i><strong>' + _('Login') + '</strong>', login_url, 'inlineLoginBox', :id => 'link_login') %> 10 <%= _("<span class='login'>%s</span>") % thickbox_inline_popup_link('<i class="icon-menu-login"></i><strong>' + _('Login') + '</strong>', login_url, 'inlineLoginBox', :id => 'link_login') %>
11 <%= @plugins.dispatch(:alternative_authentication_link).collect { |content| instance_exec(&content) }.join("") %> 11 <%= @plugins.dispatch(:alternative_authentication_link).collect { |content| instance_exec(&content) }.join("") %>
12 12
13 - <div id='inlineLoginBox' style='display: none;'>  
14 - <%= render :file => 'account/login', :locals => { :is_thickbox => true } %>  
15 - </div> 13 + <div id='inlineLoginBox' style='display: none;'>
  14 + <%= render :file => 'account/login', :locals => { :is_thickbox => true } %>
  15 + </div>
16 16
17 - <% unless @plugins.dispatch(:allow_user_registration).include?(false) %>  
18 - <%= _("<span class='or'>or</span> <span class='signup'>%s</span>") % link_to('<strong>' + _('Sign up') + '</strong>', :controller => 'account', :action => 'signup')%>  
19 - <% end %>  
20 - </span> 17 + <% unless @plugins.dispatch(:allow_user_registration).include?(false) %>
  18 + <%= _("<span class='or'>or</span> <span class='signup'>%s</span>") % link_to('<strong>' + _('Sign up') + '</strong>', :controller => 'account', :action => 'signup')%>
  19 + <% end %>
  20 +
  21 + </span>
21 <% end %> 22 <% end %>
22 - <form action="/search" id="top-search" class="search_form clean" method="get"> 23 + <form action="/search/articles" id="top-search" class="search_form clean" method="get">
23 <input name="query" size="15" title="<%=_('Search...')%>" onfocus="this.form.className='focused';" onblur="this.form.className=''" /> 24 <input name="query" size="15" title="<%=_('Search...')%>" onfocus="this.form.className='focused';" onblur="this.form.className=''" />
24 <div><%=_('Press <strong>Enter</strong> to send the search query.')%></div> 25 <div><%=_('Press <strong>Enter</strong> to send the search query.')%></div>
25 <%= javascript_tag 'jQuery("#user form input").hint();' %> 26 <%= javascript_tag 'jQuery("#user form input").hint();' %>
app/views/memberships/connections.html.erb 0 → 100644
@@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
  1 +<h1><%= _("Connections with %s") % @suggestion.suggestion.name %></h1>
  2 +
  3 +<% button_bar do %>
  4 + <%= button(:back, _('Go to groups list'), :controller => 'memberships') %>
  5 +<% end %>
  6 +
  7 +<%= render :partial => 'shared/profile_connections', :locals => { :suggestion => @suggestion, :tags => @tags, :profiles => @profiles } %>
app/views/memberships/index.html.erb
@@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
16 <%= labelled_select(_('Filter')+': ', :filter_type, :first, :last, @filter, type_collection, :id => 'memberships_filter')%> 16 <%= labelled_select(_('Filter')+': ', :filter_type, :first, :last, @filter, type_collection, :id => 'memberships_filter')%>
17 </p> 17 </p>
18 18
  19 +<p><%= link_to _('See some suggestions of communities...'), :action => 'suggest' %></p>
  20 +
19 <% if @memberships.empty? %> 21 <% if @memberships.empty? %>
20 <p> 22 <p>
21 <em><%= _('No groups to list') %></em> 23 <em><%= _('No groups to list') %></em>
app/views/memberships/remove_suggestion.html.erb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +<div id="remove_suggestion">
  2 + <h1><%= _('Removing suggestion for community: %s') % @community.name %></h1>
  3 +
  4 + <%= render :partial => 'shared/remove_suggestion', :locals => { :suggestion => @community } %>
  5 +</div>
app/views/memberships/suggest.html.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<h1><%= _("Communities suggestions for %s") % profile.name %></h1>
  2 +
  3 +<% button_bar do %>
  4 + <%= button(:back, _('Go to groups list'), :controller => 'memberships') %>
  5 +<% end %>
  6 +
  7 +<div class="profiles-suggestions">
  8 + <%= render :partial => 'shared/profile_suggestions_list', :locals => { :suggestions => @suggestions, :collection => :communities_suggestions, :per_page => 12 } %>
  9 +</div>
app/views/memberships/welcome.html.erb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +<h1><%= _('Community created') %></h1>
  2 +<p><%= _("Your community (%s) was successfully created.") % @community.name %></p>
  3 +<p><%= link_to _('You can manage your community now.'), @community.admin_url %></p>
  4 +
  5 +<%= render :partial => 'shared/template_welcome_page', :locals => {:template => @community.template, :header => _("What can I do with a %s?")} %>
  6 +
  7 +<% button_bar do %>
  8 + <%= button :back, _('Back'), @back_to %>
  9 +<% end %>
app/views/profile/_upload_image.html.erb
@@ -10,5 +10,5 @@ @@ -10,5 +10,5 @@
10 </div> 10 </div>
11 </div> 11 </div>
12 </div> 12 </div>
13 -<div title='<%= activity.target.class.short_description %>' class='profile-activity-icon icon-new icon-newgallery'></div> 13 +<div title='<%#= activity.target.class.short_description %>' class='profile-activity-icon icon-new icon-newgallery'></div>
14 <br/> 14 <br/>
app/views/profile/friends.html.erb
@@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
18 <%= button :back, _('Go back'), { :controller => 'profile' } %> 18 <%= button :back, _('Go back'), { :controller => 'profile' } %>
19 <% if user == profile %> 19 <% if user == profile %>
20 <%= button :edit, _('Manage my friends'), :controller => 'friends', :action => 'index', :profile => profile.identifier %> 20 <%= button :edit, _('Manage my friends'), :controller => 'friends', :action => 'index', :profile => profile.identifier %>
21 - <%= button(:search, _('Invite people from my e-mail contacts'), :controller => 'invite', :action => 'select_address_book') %> 21 + <%= button(:person, _('Invite people'), :controller => 'invite', :action => 'invite_friends') %>
22 <% end %> 22 <% end %>
23 <% end %> 23 <% end %>
24 24
app/views/profile/members.html.erb
@@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
18 <%= button :back, _('Go back'), { :controller => 'profile' } %> 18 <%= button :back, _('Go back'), { :controller => 'profile' } %>
19 <% if profile.community? and user %> 19 <% if profile.community? and user %>
20 <% if user.has_permission?(:invite_members, profile) %> 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' %> 21 + <%= button :person, _('Invite people to join'), :controller => 'invite', :action => 'invite_friends' %>
22 <% end %> 22 <% end %>
23 <% if user.has_permission?(:send_mail_to_members, profile) %> 23 <% if user.has_permission?(:send_mail_to_members, profile) %>
24 <%= button :send, _('Send e-mail to members'), :controller => 'profile', :action => 'send_mail' %> 24 <%= button :send, _('Send e-mail to members'), :controller => 'profile', :action => 'send_mail' %>
app/views/profile_editor/_moderation.html.erb
1 <h2><%= _('Moderation options') %></h2> 1 <h2><%= _('Moderation options') %></h2>
2 <% if profile.community? %> 2 <% if profile.community? %>
3 <div style='margin-bottom: 1em'> 3 <div style='margin-bottom: 1em'>
  4 + <h4><%= _('Invitation moderation:')%></h4>
  5 + </div>
  6 + <div style='margin-bottom: 0.5em'>
  7 + <%= check_box(:profile_data, :allow_members_to_invite, :style => 'float: left') %>
  8 + <div style='margin-left: 30px'>
  9 + <%= _('Allow all members to send invitation (Default: only administrator)') %>
  10 + </div>
  11 + <br>
  12 + <div class = 'invite_friends_only' >
  13 + <%= check_box(:profile_data, :invite_friends_only, :style => 'float: left') %>
  14 + <div style='margin-left: 30px'>
  15 + <%= _('Allow members to invite only friends (Default: all users)') %>
  16 + </div>
  17 + </div>
  18 + </div>
  19 + <br>
  20 +
  21 + <div style='margin-bottom: 1em'>
4 <%= _('New members must be approved:')%> 22 <%= _('New members must be approved:')%>
5 </div> 23 </div>
6 <div style='margin-bottom: 0.5em'> 24 <div style='margin-bottom: 0.5em'>
@@ -33,3 +51,5 @@ @@ -33,3 +51,5 @@
33 <%= _('<strong>After</strong> being published in this group (a moderator can always remove publicated articles later).') %> 51 <%= _('<strong>After</strong> being published in this group (a moderator can always remove publicated articles later).') %>
34 </div> 52 </div>
35 </div> 53 </div>
  54 +
  55 +<%= javascript_include_tag('invite') %>
app/views/profile_editor/edit.html.erb
@@ -52,6 +52,12 @@ @@ -52,6 +52,12 @@
52 'profile_data[redirect_l10n]', true, @profile.redirect_l10n 52 'profile_data[redirect_l10n]', true, @profile.redirect_l10n
53 )%> 53 )%>
54 54
  55 + <h2><%= _('Suggestions') %></h2>
  56 + <%= labelled_check_box(
  57 + _('Send me relationship suggestions by email'),
  58 + 'profile_data[email_suggestions]', true, @profile.email_suggestions
  59 + )%>
  60 +
55 <%= 61 <%=
56 @plugins.dispatch(:profile_editor_extras).map do |content| 62 @plugins.dispatch(:profile_editor_extras).map do |content|
57 content.kind_of?(Proc) ? self.instance_exec(&content) : content 63 content.kind_of?(Proc) ? self.instance_exec(&content) : content
app/views/profile_editor/index.html.erb
@@ -68,6 +68,8 @@ @@ -68,6 +68,8 @@
68 68
69 <%= control_panel_button(_('Manage SPAM'), 'manage-spam', :controller => 'spam', :action => 'index') %> 69 <%= control_panel_button(_('Manage SPAM'), 'manage-spam', :controller => 'spam', :action => 'index') %>
70 70
  71 + <%= control_panel_button(_('Edit welcome page'), 'welcome-page', :action => 'welcome_page') if has_welcome_page %>
  72 +
71 <% @plugins.dispatch(:control_panel_buttons).each do |button| %> 73 <% @plugins.dispatch(:control_panel_buttons).each do |button| %>
72 <%= control_panel_button(button[:title], button[:icon], button[:url]) %> 74 <%= control_panel_button(button[:title], button[:icon], button[:url]) %>
73 <% end %> 75 <% end %>
app/views/profile_editor/welcome_page.html.erb 0 → 100644
@@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
  1 +<h1><%= _('Edit welcome page') %></h1>
  2 +
  3 +<%= labelled_form_for :welcome_page do |f| %>
  4 + <%= error_messages_for :welcome_page %>
  5 +
  6 + <%= f.check_box(:published) %>
  7 + <div class='explanation'>
  8 + <%= _('Your welcome page will only be displayed if this options is selected.') %>
  9 + </div>
  10 +
  11 + <%= f.text_area(:body, :cols => 40, :style => 'width: 100%', :class => 'mceEditor') %>
  12 + <div class='explanation'>
  13 + <%= _('This page will be displayed to the user after his signup with this template.') %>
  14 + </div>
  15 +
  16 + <% button_bar do%>
  17 + <%= submit_button('save', _('Save'), :cancel => @back_to) %>
  18 + <% end %>
  19 +<% end %>
  20 +
  21 +<%= render :file => 'shared/tiny_mce' %>
app/views/profile_members/_index_buttons.html.erb
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <%= button :back, _('Back'), :controller => 'profile_editor' %> 2 <%= button :back, _('Back'), :controller => 'profile_editor' %>
3 <%= button :add, _('Add members'), :action => 'add_members' if profile.enterprise? %> 3 <%= button :add, _('Add members'), :action => 'add_members' if profile.enterprise? %>
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 :person, _('Invite people to join'), :controller => 'invite', :action => 'invite_friends' %>
6 <% end %> 6 <% end %>
7 <% if profile.community? and user.has_permission?(:send_mail_to_members, profile) %> 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' %> 8 <%= button :send, _('Send e-mail to members'), :controller => 'profile', :action => 'send_mail' %>
app/views/profile_search/_results-list.html.erb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +<div id='search-content'>
  2 + <% if @results %>
  3 + <div class='results-found-message'>
  4 + <%= _("%s results found") % @results.total_entries %>
  5 + </div>
  6 +
  7 + <ul class='results-list'>
  8 + <% @results.sort_by { |r| r.is_image? ? 0 : 1}.each do |result| %>
  9 + <%= render :partial => partial_for_class(result.class), :locals => { :article => result } %>
  10 + <% end %>
  11 + </ul>
  12 +
  13 + <%= pagination_links @results %>
  14 + <% end %>
  15 +</div>
app/views/profile_search/index.html.erb
@@ -3,17 +3,7 @@ @@ -3,17 +3,7 @@
3 3
4 <%= render :partial => 'shared/profile_search_form' %> 4 <%= render :partial => 'shared/profile_search_form' %>
5 5
6 - <% if @results %>  
7 - <div class='results-found-message'>  
8 - <%= _("%s results found") % @results.total_entries %>  
9 - </div> 6 + <%= render :partial => 'results-list' %>
10 7
11 - <ul class='results-list'>  
12 - <% @results.sort_by { |r| r.is_image? ? 0 : 1}.each do |result| %>  
13 - <%= render :partial => partial_for_class(result.class), :locals => { :article => result } %>  
14 - <% end %>  
15 - </ul>  
16 -  
17 - <%= pagination_links @results %>  
18 - <% end %>  
19 </div> 8 </div>
  9 +<%= javascript_include_tag 'search' %>
app/views/profile_search/index.js.erb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +jQuery('#search-content').html('<%= escape_javascript(render :partial => "results-list") %>');
app/views/search/_compact_profile.html.erb
1 -<% filter_label = profile.send(@filter + '_label') %>  
2 -<% filter_label += show_date(profile.created_at) if @filter == 'more_recent' %> 1 +<% filter_label = profile.send(@order + '_label') %>
  2 +<% filter_label += show_date(profile.created_at) if @order == 'more_recent' %>
3 <li class="search-profile-item"> 3 <li class="search-profile-item">
4 <%= profile_image_link profile, :portrait, 'div', filter_label %> 4 <%= profile_image_link profile, :portrait, 'div', filter_label %>
5 </li> 5 </li>