Commit 34d3a283087086d38b318c8e5021ba60e301a1e2

Authored by Rodrigo Souto
2 parents ef453b43 d2029dc6

Merge branch 'stoa' into 'master'

Stoa Network Features

This merge-request groups 6 features developed majorly by me, @danielafeitosa and @larissa funded by Stoa Network. These features were finished during the freezing time and only now we were able to update it to the current master. I'm sorry that we'll merge them all together in a single request, but we had to group them on a single branch do improve the efficiency of our work. Here are the features grouped here:
  - Media gallery complete refactoring
  - Welcome page improvements
  - Better interaction with other networks
  - Friends and communities suggestions
  - Invitation improvements
  - Search improvements

All 6 features were reviewed by at least one of us and since we are committers, I'm merging this right away. This merge-request was made more for information sake.

See merge request !451
Showing 354 changed files with 15890 additions and 880 deletions   Show diff stats

Too many changes.

To preserve performance only 100 of 354 files displayed.

@@ -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
app/controllers/admin/region_validators_controller.rb
@@ -33,7 +33,7 @@ class RegionValidatorsController < AdminController @@ -33,7 +33,7 @@ class RegionValidatorsController < 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 < AdminController @@ -18,7 +18,7 @@ class UsersController < 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 < ActionController::Base @@ -183,21 +183,19 @@ class ApplicationController < 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 < MyProfileController @@ -23,6 +23,9 @@ class CmsController < 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 < MyProfileController @@ -30,7 +33,7 @@ class CmsController < 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 < MyProfileController @@ -40,7 +43,7 @@ class CmsController < 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 < MyProfileController @@ -117,7 +120,7 @@ class CmsController < 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 < MyProfileController @@ -163,7 +166,10 @@ class CmsController < 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 < MyProfileController @@ -256,28 +262,53 @@ class CmsController < 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 pngfix_stylesheet_path, 53 pngfix_stylesheet_path,
53 ] + tokeninput_stylesheets 54 ] + tokeninput_stylesheets
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
@@ -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.*'
@@ -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/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>
app/views/search/_display_results.html.erb
1 <div id="search-results" class="<%= !multiple_search? ? 'only-one-result-box' : 'multiple-results-boxes' %>"> 1 <div id="search-results" class="<%= !multiple_search? ? 'only-one-result-box' : 'multiple-results-boxes' %>">
2 - <% @order.each do |name| %> 2 + <% @assets.each do |name| %>
3 <% search = @searches[name] %> 3 <% search = @searches[name] %>
4 4
5 <div class="search-results-<%= name %> search-results-box <%= "search-results-empty" if search[:results].blank? %>"> 5 <div class="search-results-<%= name %> search-results-box <%= "search-results-empty" if search[:results].blank? %>">
app/views/search/_full_enterprise.html.erb
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <div class="search-enterprise-item"> 2 <div class="search-enterprise-item">
3 <div class="search-enterprise-item-column-left"> 3 <div class="search-enterprise-item-column-left">
4 <%= profile_image_link enterprise, :portrait, 'div', 4 <%= profile_image_link enterprise, :portrait, 'div',
5 - @filter == 'more_recent' ? enterprise.send(@filter + '_label') + show_date(enterprise.created_at) : enterprise.send(@filter + '_label') %> 5 + @order == 'more_recent' ? enterprise.send(@order + '_label') + show_date(enterprise.created_at) : enterprise.send(@order + '_label') %>
6 </div> 6 </div>
7 <div class="search-enterprise-item-column-right"> 7 <div class="search-enterprise-item-column-right">
8 <%= link_to_homepage(enterprise.name, enterprise.identifier, :class => "search-result-title") %> 8 <%= link_to_homepage(enterprise.name, enterprise.identifier, :class => "search-result-title") %>