Commit 34d3a283087086d38b318c8e5021ba60e301a1e2
Exists in
master
and in
22 other branches
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.
Gemfile
... | ... | @@ -21,6 +21,8 @@ gem 'exception_notification', '~> 4.0.1' |
21 | 21 | gem 'gettext', '~> 2.2.1', :require => false, :group => :development |
22 | 22 | gem 'locale', '~> 2.0.5' |
23 | 23 | |
24 | +gem 'whenever', :require => false | |
25 | + | |
24 | 26 | # FIXME list here all actual dependencies (i.e. the ones in debian/control), |
25 | 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 | 33 | def load_region_and_search |
34 | 34 | @region = environment.regions.find(params[:id]) |
35 | 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 | 37 | end |
38 | 38 | end |
39 | 39 | ... | ... |
app/controllers/admin/users_controller.rb
... | ... | @@ -18,7 +18,7 @@ class UsersController < AdminController |
18 | 18 | end |
19 | 19 | scope = scope.order('name ASC') |
20 | 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 | 22 | end |
23 | 23 | |
24 | 24 | def set_admin_role | ... | ... |
app/controllers/application_controller.rb
... | ... | @@ -183,21 +183,19 @@ class ApplicationController < ActionController::Base |
183 | 183 | end |
184 | 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 | 196 | end |
198 | 197 | |
199 | 198 | def private_environment? |
200 | 199 | @environment.enabled?(:restrict_to_members) |
201 | 200 | end |
202 | - | |
203 | 201 | end | ... | ... |
app/controllers/my_profile/cms_controller.rb
... | ... | @@ -23,6 +23,9 @@ class CmsController < MyProfileController |
23 | 23 | end |
24 | 24 | |
25 | 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 | 30 | protect_if :only => :upload_files do |c, user, profile| |
28 | 31 | article_id = c.params[:parent_id] |
... | ... | @@ -30,7 +33,7 @@ class CmsController < MyProfileController |
30 | 33 | (user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile))) |
31 | 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 | 37 | user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile)) |
35 | 38 | end |
36 | 39 | |
... | ... | @@ -40,7 +43,7 @@ class CmsController < MyProfileController |
40 | 43 | (user && (user.has_permission?('post_content', profile) || user.has_permission?('publish_content', profile))) |
41 | 44 | end |
42 | 45 | |
43 | - protect_if :only => [:destroy, :publish] do |c, user, profile| | |
46 | + protect_if :only => :destroy do |c, user, profile| | |
44 | 47 | profile.articles.find(c.params[:id]).allow_post_content?(user) |
45 | 48 | end |
46 | 49 | |
... | ... | @@ -117,7 +120,7 @@ class CmsController < MyProfileController |
117 | 120 | @success_back_to = params[:success_back_to] |
118 | 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 | 124 | record_coming |
122 | 125 | @type = params[:type] |
123 | 126 | if @type.blank? |
... | ... | @@ -163,7 +166,10 @@ class CmsController < MyProfileController |
163 | 166 | if continue |
164 | 167 | redirect_to :action => 'edit', :id => @article |
165 | 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 | 173 | end |
168 | 174 | return |
169 | 175 | end |
... | ... | @@ -256,28 +262,53 @@ class CmsController < MyProfileController |
256 | 262 | render :template => 'shared/update_categories', :locals => { :category => @current_category, :object_name => 'article' } |
257 | 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 | 269 | def publish |
260 | 270 | @article = profile.articles.find(params[:id]) |
261 | 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 | 289 | end |
269 | - end.compact unless params[:marked_groups].nil? | |
290 | + end | |
291 | + end | |
292 | + | |
293 | + def publish_on_communities | |
270 | 294 | if request.post? |
295 | + @back_to = params[:back_to] | |
296 | + @article = profile.articles.find(params[:id]) | |
271 | 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 | 301 | if @marked_groups.empty? |
302 | + redirect_to @back_to | |
273 | 303 | return session[:notice] = _("Select some group to publish your article") |
274 | 304 | end |
275 | 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 | 307 | begin |
278 | - task.finish unless item[:group].moderated_articles? | |
308 | + task.finish unless item.moderated_articles? | |
279 | 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 | 312 | end |
282 | 313 | end |
283 | 314 | if @failed.blank? |
... | ... | @@ -287,23 +318,27 @@ class CmsController < MyProfileController |
287 | 318 | else |
288 | 319 | redirect_to @article.view_url |
289 | 320 | end |
321 | + else | |
322 | + session[:notice] = _("Some of your publish requests couldn't be sent.") | |
323 | + render :action => 'publish' | |
290 | 324 | end |
291 | 325 | end |
292 | 326 | end |
293 | 327 | |
294 | 328 | def publish_on_portal_community |
295 | - @article = profile.articles.find(params[:id]) | |
296 | 329 | if request.post? |
297 | - if environment.portal_community | |
330 | + @article = profile.articles.find(params[:id]) | |
331 | + if environment.portal_enabled | |
298 | 332 | task = ApproveArticle.create!(:article => @article, :name => params[:name], :target => environment.portal_community, :requestor => user) |
299 | 333 | begin |
300 | 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 | 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 | 339 | end |
305 | 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 | 342 | end |
308 | 343 | |
309 | 344 | if @back_to |
... | ... | @@ -331,7 +366,7 @@ class CmsController < MyProfileController |
331 | 366 | |
332 | 367 | def search |
333 | 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 | 370 | render :text => article_list_to_json(results), :content_type => 'application/json' |
336 | 371 | end |
337 | 372 | |
... | ... | @@ -342,15 +377,26 @@ class CmsController < MyProfileController |
342 | 377 | end |
343 | 378 | |
344 | 379 | def media_upload |
345 | - files_uploaded = [] | |
346 | 380 | parent = check_parent(params[:parent_id]) |
347 | - files = [:file1,:file2, :file3].map { |f| params[f] }.compact | |
348 | 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 | 387 | end |
352 | 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 | 400 | end |
355 | 401 | |
356 | 402 | protected |
... | ... | @@ -445,4 +491,36 @@ class CmsController < MyProfileController |
445 | 491 | end |
446 | 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 | 526 | end | ... | ... |
app/controllers/my_profile/friends_controller.rb
... | ... | @@ -3,6 +3,7 @@ class FriendsController < MyProfileController |
3 | 3 | protect 'manage_friends', :profile |
4 | 4 | |
5 | 5 | def index |
6 | + @suggestions = profile.profile_suggestions.of_person.enabled.includes(:suggestion).limit(per_page) | |
6 | 7 | if is_cache_expired?(profile.manage_friends_cache_key(params)) |
7 | 8 | @friends = profile.friends.paginate(:per_page => per_page, :page => params[:npage]) |
8 | 9 | end |
... | ... | @@ -16,6 +17,30 @@ class FriendsController < MyProfileController |
16 | 17 | end |
17 | 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 | 44 | protected |
20 | 45 | |
21 | 46 | class << self | ... | ... |
app/controllers/my_profile/memberships_controller.rb
... | ... | @@ -20,12 +20,54 @@ class MembershipsController < MyProfileController |
20 | 20 | @community.environment = environment |
21 | 21 | @back_to = params[:back_to] || url_for(:action => 'index') |
22 | 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 | 30 | session[:notice] = _('Your new community creation request will be evaluated by an administrator. You will be notified.') |
31 | + redirect_to @back_to | |
26 | 32 | end |
27 | - redirect_to @back_to | |
28 | 33 | return |
29 | 34 | end |
30 | 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 | 73 | end | ... | ... |
app/controllers/my_profile/profile_editor_controller.rb
... | ... | @@ -3,6 +3,10 @@ class ProfileEditorController < MyProfileController |
3 | 3 | protect 'edit_profile', :profile, :except => [:destroy_profile] |
4 | 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 | 10 | def index |
7 | 11 | @pending_tasks = Task.to(profile).pending.without_spam.select{|i| user.has_permission?(i.permission, profile)} |
8 | 12 | end |
... | ... | @@ -85,6 +89,21 @@ class ProfileEditorController < MyProfileController |
85 | 89 | end |
86 | 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 | 107 | def deactivate_profile |
89 | 108 | if environment.admins.include?(current_person) |
90 | 109 | profile = environment.profiles.find(params[:id]) |
... | ... | @@ -116,9 +135,24 @@ class ProfileEditorController < MyProfileController |
116 | 135 | protected |
117 | 136 | |
118 | 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 | 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 | 158 | end | ... | ... |
app/controllers/public/account_controller.rb
... | ... | @@ -82,10 +82,12 @@ class AccountController < ApplicationController |
82 | 82 | if @plugins.dispatch(:allow_user_registration).include?(false) |
83 | 83 | redirect_back_or_default(:controller => 'home') |
84 | 84 | session[:notice] = _("This environment doesn't allow user registration.") |
85 | + return | |
85 | 86 | end |
86 | 87 | |
87 | 88 | store_location(request.referer) unless params[:return_to] or session[:return_to] |
88 | 89 | |
90 | + # Tranforming to boolean | |
89 | 91 | @block_bot = !!session[:may_be_a_bot] |
90 | 92 | @invitation_code = params[:invitation_code] |
91 | 93 | begin |
... | ... | @@ -129,8 +131,8 @@ class AccountController < ApplicationController |
129 | 131 | check_join_in_community(@user) |
130 | 132 | go_to_signup_initial_page |
131 | 133 | else |
134 | + redirect_to :controller => :home, :action => :welcome, :template_id => (@user.person.template && @user.person.template.id) | |
132 | 135 | session[:notice] = _('Thanks for registering!') |
133 | - @register_pending = true | |
134 | 136 | end |
135 | 137 | end |
136 | 138 | end |
... | ... | @@ -461,6 +463,8 @@ class AccountController < ApplicationController |
461 | 463 | redirect_to user.url |
462 | 464 | when 'user_control_panel' |
463 | 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 | 468 | else |
465 | 469 | redirect_back_or_default(default) |
466 | 470 | end | ... | ... |
app/controllers/public/home_controller.rb
... | ... | @@ -18,4 +18,10 @@ class HomeController < PublicController |
18 | 18 | @no_design_blocks = true |
19 | 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 | 27 | end | ... | ... |
app/controllers/public/invite_controller.rb
... | ... | @@ -4,8 +4,15 @@ class InviteController < PublicController |
4 | 4 | before_filter :login_required |
5 | 5 | before_filter :check_permissions_to_invite |
6 | 6 | |
7 | - def select_address_book | |
7 | + def invite_friends | |
8 | 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 | 16 | if request.post? |
10 | 17 | contact_list = ContactList.create |
11 | 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 < PublicController |
22 | 29 | webmail_import_addresses = params[:webmail_import_addresses] |
23 | 30 | contacts_to_invite = Invitation.join_contacts(manual_import_addresses, webmail_import_addresses) |
24 | 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 | 33 | session[:notice] = _('Your invitations are being sent.') |
27 | 34 | if profile.person? |
28 | 35 | redirect_to :controller => 'profile', :action => 'friends' |
... | ... | @@ -52,16 +59,36 @@ class InviteController < PublicController |
52 | 59 | def cancel_fetching_emails |
53 | 60 | contact_list = ContactList.find(params[:contact_list]) |
54 | 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 | 87 | end |
57 | 88 | |
58 | 89 | protected |
59 | 90 | |
60 | 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 | 93 | end |
66 | - | |
67 | 94 | end | ... | ... |
app/controllers/public/profile_search_controller.rb
... | ... | @@ -9,9 +9,12 @@ class ProfileSearchController < PublicController |
9 | 9 | @q = params[:q] |
10 | 10 | unless @q.blank? |
11 | 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 | 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 | 18 | end |
16 | 19 | end |
17 | 20 | end | ... | ... |
app/controllers/public/search_controller.rb
... | ... | @@ -4,11 +4,11 @@ class SearchController < PublicController |
4 | 4 | include SearchHelper |
5 | 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 | 13 | # Backwards compatibility with old URLs |
14 | 14 | def redirect_asset_param |
... | ... | @@ -20,7 +20,7 @@ class SearchController < PublicController |
20 | 20 | |
21 | 21 | def index |
22 | 22 | @searches = {} |
23 | - @order = [] | |
23 | + @assets = [] | |
24 | 24 | @names = {} |
25 | 25 | @results_only = true |
26 | 26 | |
... | ... | @@ -28,7 +28,7 @@ class SearchController < PublicController |
28 | 28 | load_query |
29 | 29 | @asset = key |
30 | 30 | send(key) |
31 | - @order << key | |
31 | + @assets << key | |
32 | 32 | @names[key] = _(description) |
33 | 33 | end |
34 | 34 | @asset = nil |
... | ... | @@ -42,7 +42,7 @@ class SearchController < PublicController |
42 | 42 | # view the summary of one category |
43 | 43 | def category_index |
44 | 44 | @searches = {} |
45 | - @order = [] | |
45 | + @assets = [] | |
46 | 46 | @names = {} |
47 | 47 | limit = MULTIPLE_SEARCH_LIMIT |
48 | 48 | [ |
... | ... | @@ -53,7 +53,7 @@ class SearchController < PublicController |
53 | 53 | [ :communities, _('Communities'), :recent_communities ], |
54 | 54 | [ :articles, _('Contents'), :recent_articles ] |
55 | 55 | ].each do |asset, name, filter| |
56 | - @order << asset | |
56 | + @assets << asset | |
57 | 57 | @searches[asset]= {:results => @category.send(filter, limit)} |
58 | 58 | raise "No total_entries for: #{asset}" unless @searches[asset][:results].respond_to?(:total_entries) |
59 | 59 | @names[asset] = name |
... | ... | @@ -147,12 +147,16 @@ class SearchController < PublicController |
147 | 147 | render :partial => 'events/events' |
148 | 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 | 155 | protected |
152 | 156 | |
153 | 157 | def load_query |
154 | 158 | @asset = (params[:asset] || params[:action]).to_sym |
155 | - @order ||= [@asset] | |
159 | + @assets ||= [@asset] | |
156 | 160 | @searches ||= {} |
157 | 161 | |
158 | 162 | @query = params[:query] || '' |
... | ... | @@ -173,13 +177,22 @@ class SearchController < PublicController |
173 | 177 | end |
174 | 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 | 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 | 191 | render_not_found |
179 | 192 | return |
180 | 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 | 196 | @searching = {} |
184 | 197 | @titles = {} |
185 | 198 | @enabled_searches.each do |key, name| |
... | ... | @@ -189,11 +202,11 @@ class SearchController < PublicController |
189 | 202 | @names = @titles if @names.nil? |
190 | 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 | 210 | end |
198 | 211 | end |
199 | 212 | |
... | ... | @@ -217,7 +230,7 @@ class SearchController < PublicController |
217 | 230 | end |
218 | 231 | |
219 | 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 | 234 | end |
222 | 235 | |
223 | 236 | private |
... | ... | @@ -232,4 +245,14 @@ class SearchController < PublicController |
232 | 245 | 20 |
233 | 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 | 258 | end | ... | ... |
app/helpers/application_helper.rb
... | ... | @@ -1307,8 +1307,19 @@ module ApplicationHelper |
1307 | 1307 | end |
1308 | 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 | 1323 | end |
1313 | 1324 | |
1314 | 1325 | def template_options(kind, field_name) |
... | ... | @@ -1411,6 +1422,43 @@ module ApplicationHelper |
1411 | 1422 | content_tag('ul', article.versions.map {|v| link_to("r#{v.version}", @page.url.merge(:version => v.version))}) |
1412 | 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 | 1462 | def labelled_colorpicker_field(human_name, object_name, method, options = {}) |
1415 | 1463 | options[:id] ||= 'text-field-' + FormsHelper.next_id_number |
1416 | 1464 | content_tag('label', human_name, :for => options[:id], :class => 'formlabel') + | ... | ... |
app/helpers/cms_helper.rb
... | ... | @@ -40,12 +40,8 @@ module CmsHelper |
40 | 40 | end |
41 | 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 | 45 | end |
50 | 46 | |
51 | 47 | def display_delete_button(article) | ... | ... |
app/helpers/forms_helper.rb
... | ... | @@ -265,7 +265,7 @@ module FormsHelper |
265 | 265 | ) |
266 | 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 | 269 | if find_options.empty? |
270 | 270 | folders = profile.folders |
271 | 271 | else |
... | ... | @@ -276,7 +276,7 @@ module FormsHelper |
276 | 276 | select_tag( |
277 | 277 | field_id, |
278 | 278 | options_for_select( |
279 | - [[profile.identifier, '']] + | |
279 | + [[(extra_options[:root_label] || profile.identifier), '']] + | |
280 | 280 | folders.collect {|f| [ profile.identifier + '/' + f.full_name, f.id.to_s ] }, |
281 | 281 | default_value.to_s |
282 | 282 | ), | ... | ... |
app/helpers/layout_helper.rb
app/helpers/search_helper.rb
... | ... | @@ -5,20 +5,23 @@ module SearchHelper |
5 | 5 | BLOCKS_SEARCH_LIMIT = 24 |
6 | 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 | 27 | COMMON_PROFILE_LIST_BLOCK = [ |
... | ... | @@ -56,7 +59,7 @@ module SearchHelper |
56 | 59 | end |
57 | 60 | |
58 | 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 | 63 | end |
61 | 64 | |
62 | 65 | def display_results(searches=nil, asset=nil) |
... | ... | @@ -93,6 +96,16 @@ module SearchHelper |
93 | 96 | end |
94 | 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 | 109 | def display_selector(asset, display, float = 'right') |
97 | 110 | display = nil if display.blank? |
98 | 111 | display ||= asset_class(asset).default_search_display |
... | ... | @@ -107,36 +120,32 @@ module SearchHelper |
107 | 120 | end |
108 | 121 | end |
109 | 122 | |
110 | - def filter_selector(asset, filter, float = 'right') | |
123 | + def filters(asset) | |
124 | + return if !asset | |
111 | 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 | 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 | 149 | end |
141 | 150 | |
142 | 151 | end | ... | ... |
... | ... | @@ -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 | 12 | options[:search_delay] ||= 1000 |
13 | 13 | options[:prevent_duplicates] ||= true |
14 | 14 | options[:backspace_delete_item] ||= false |
15 | + options[:zindex] ||= 999 | |
15 | 16 | options[:focus] ||= false |
16 | 17 | options[:avoid_enter] ||= true |
17 | 18 | options[:on_result] ||= 'null' |
... | ... | @@ -31,6 +32,7 @@ module TokenHelper |
31 | 32 | searchDelay: #{options[:search_delay].to_json}, |
32 | 33 | preventDuplicates: #{options[:prevent_duplicates].to_json}, |
33 | 34 | backspaceDeleteItem: #{options[:backspace_delete_item].to_json}, |
35 | + zindex: #{options[:zindex].to_json}, | |
34 | 36 | queryParam: #{options[:query_param].to_json}, |
35 | 37 | tokenLimit: #{options[:token_limit].to_json}, |
36 | 38 | onResult: #{options[:on_result]}, | ... | ... |
app/mailers/user_mailer.rb
... | ... | @@ -41,6 +41,23 @@ class UserMailer < ActionMailer::Base |
41 | 41 | ) |
42 | 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 | 61 | class Job < Struct.new(:user, :method) |
45 | 62 | def perform |
46 | 63 | UserMailer.send(method, user).deliver | ... | ... |
app/models/add_friend.rb
... | ... | @@ -14,6 +14,11 @@ class AddFriend < Task |
14 | 14 | alias :friend :target |
15 | 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 | 22 | def perform |
18 | 23 | target.add_friend(requestor, group_for_friend) |
19 | 24 | requestor.add_friend(target, group_for_person) |
... | ... | @@ -48,4 +53,8 @@ class AddFriend < Task |
48 | 53 | {:type => :profile_image, :profile => requestor, :url => requestor.url} |
49 | 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 | 60 | end | ... | ... |
app/models/add_member.rb
... | ... | @@ -10,6 +10,10 @@ class AddMember < Task |
10 | 10 | |
11 | 11 | settings_items :roles |
12 | 12 | |
13 | + after_create do |task| | |
14 | + remove_from_suggestion_list(task) | |
15 | + end | |
16 | + | |
13 | 17 | def perform |
14 | 18 | if !self.roles or (self.roles.uniq.compact.length == 1 and self.roles.uniq.compact.first.to_i.zero?) |
15 | 19 | self.roles = [Profile::Roles.member(organization.environment.id).id] |
... | ... | @@ -46,4 +50,9 @@ class AddMember < Task |
46 | 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 | 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 | 58 | end | ... | ... |
app/models/article.rb
... | ... | @@ -14,20 +14,17 @@ class Article < ActiveRecord::Base |
14 | 14 | acts_as_having_image |
15 | 15 | |
16 | 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 | 29 | def self.default_search_display |
33 | 30 | 'full' |
... | ... | @@ -477,7 +474,9 @@ class Article < ActiveRecord::Base |
477 | 474 | scope :no_folders, lambda {|profile|{:conditions => ['articles.type NOT IN (?)', profile.folder_types]}} |
478 | 475 | scope :galleries, :conditions => [ "articles.type IN ('Gallery')" ] |
479 | 476 | scope :images, :conditions => { :is_image => true } |
477 | + scope :no_images, :conditions => { :is_image => false } | |
480 | 478 | scope :text_articles, :conditions => [ 'articles.type IN (?)', text_article_types ] |
479 | + scope :files, :conditions => { :type => 'UploadedFile' } | |
481 | 480 | scope :with_types, lambda { |types| { :conditions => [ 'articles.type IN (?)', types ] } } |
482 | 481 | |
483 | 482 | scope :more_popular, :order => 'hits DESC' |
... | ... | @@ -528,7 +527,10 @@ class Article < ActiveRecord::Base |
528 | 527 | end |
529 | 528 | |
530 | 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 | 535 | def allow_create?(user) |
534 | 536 | allow_post_content?(user) || allow_publish_content?(user) | ... | ... |
app/models/category.rb
... | ... | @@ -3,10 +3,10 @@ class Category < ActiveRecord::Base |
3 | 3 | attr_accessible :name, :parent_id, :display_color, :display_in_menu, :image_builder, :environment, :parent |
4 | 4 | |
5 | 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 | 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 < ActiveRecord::Base |
3 | 3 | attr_accessible :name, :environment |
4 | 4 | |
5 | 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 | 11 | belongs_to :environment | ... | ... |
app/models/comment.rb
1 | 1 | class Comment < ActiveRecord::Base |
2 | 2 | |
3 | 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 | 9 | attr_accessible :body, :author, :name, :email, :title, :reply_of_id, :source | ... | ... |
app/models/communities_block.rb
... | ... | @@ -14,19 +14,17 @@ class CommunitiesBlock < ProfileListBlock |
14 | 14 | _('This block displays the communities in which the user is a member.') |
15 | 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 | 22 | def footer |
18 | 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 | 28 | end |
31 | 29 | end |
32 | 30 | ... | ... |
app/models/create_enterprise.rb
... | ... | @@ -73,7 +73,13 @@ class CreateEnterprise < Task |
73 | 73 | |
74 | 74 | # sets the associated region for the enterprise creation |
75 | 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 | 84 | @region = value |
79 | 85 | self.region_id = value.id | ... | ... |
app/models/enterprise.rb
... | ... | @@ -4,7 +4,10 @@ class Enterprise < Organization |
4 | 4 | |
5 | 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 | 12 | def self.type_name |
10 | 13 | _('Enterprise') | ... | ... |
app/models/environment.rb
... | ... | @@ -10,6 +10,7 @@ class Environment < ActiveRecord::Base |
10 | 10 | self.partial_updates = false |
11 | 11 | |
12 | 12 | has_many :tasks, :dependent => :destroy, :as => 'target' |
13 | + has_many :search_terms, :as => :context | |
13 | 14 | |
14 | 15 | IDENTIFY_SCRIPTS = /(php[0-9s]?|[sp]htm[l]?|pl|py|cgi|rb)/ |
15 | 16 | |
... | ... | @@ -85,7 +86,9 @@ class Environment < ActiveRecord::Base |
85 | 86 | end |
86 | 87 | |
87 | 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 | 92 | end |
90 | 93 | |
91 | 94 | # returns the available features for a Environment, in the form of a |
... | ... | @@ -155,7 +158,8 @@ class Environment < ActiveRecord::Base |
155 | 158 | 'site_homepage' => _('Redirects the user to the environment homepage.'), |
156 | 159 | 'user_profile_page' => _('Redirects the user to his profile page.'), |
157 | 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 | 164 | end |
161 | 165 | validates_inclusion_of :redirection_after_signup, :in => Environment.signup_redirection_options.keys, :allow_nil => true |
... | ... | @@ -824,6 +828,10 @@ class Environment < ActiveRecord::Base |
824 | 828 | "home-page-news/#{cache_key}-#{language}" |
825 | 829 | end |
826 | 830 | |
831 | + def portal_enabled | |
832 | + portal_community && enabled?('use_portal_community') | |
833 | + end | |
834 | + | |
827 | 835 | def notification_emails |
828 | 836 | [contact_email].select(&:present?) + admins.map(&:email) |
829 | 837 | end | ... | ... |
app/models/invitation.rb
... | ... | @@ -51,7 +51,10 @@ class Invitation < Task |
51 | 51 | next if contact_to_invite == _("Firstname Lastname <friend@email.com>") |
52 | 52 | |
53 | 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 | 58 | friend_name = match[1].strip |
56 | 59 | friend_email = match[2] |
57 | 60 | elsif match = contact_to_invite.strip.match(Noosfero::Constants::EMAIL_FORMAT) |
... | ... | @@ -61,11 +64,15 @@ class Invitation < Task |
61 | 64 | next |
62 | 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 | 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 | 76 | {:person => person, :target => user.person} |
70 | 77 | end |
71 | 78 | ... | ... |
app/models/invite_friend.rb
1 | 1 | class InviteFriend < Invitation |
2 | 2 | |
3 | 3 | settings_items :group_for_person, :group_for_friend |
4 | + before_create :check_for_invitation_existence | |
4 | 5 | |
5 | 6 | def perform |
6 | 7 | person.add_friend(friend, group_for_person) |
... | ... | @@ -41,4 +42,11 @@ class InviteFriend < Invitation |
41 | 42 | ].join("\n\n") |
42 | 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 | 52 | end | ... | ... |
app/models/invite_member.rb
... | ... | @@ -2,6 +2,7 @@ class InviteMember < Invitation |
2 | 2 | |
3 | 3 | settings_items :community_id, :type => :integer |
4 | 4 | validates_presence_of :community_id |
5 | + before_create :check_for_invitation_existence | |
5 | 6 | |
6 | 7 | def community |
7 | 8 | Community.find(community_id) |
... | ... | @@ -39,6 +40,14 @@ class InviteMember < Invitation |
39 | 40 | _('%{requestor} invited you to join %{community}.') % {:requestor => requestor.name, :community => community.name} |
40 | 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 | 51 | def expanded_message |
43 | 52 | super.gsub /<community>/, community.name |
44 | 53 | end |
... | ... | @@ -53,4 +62,11 @@ class InviteMember < Invitation |
53 | 62 | ].join("\n\n") |
54 | 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 | 72 | end | ... | ... |
app/models/license.rb
... | ... | @@ -3,8 +3,8 @@ class License < ActiveRecord::Base |
3 | 3 | attr_accessible :name, :url |
4 | 4 | |
5 | 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 | 10 | belongs_to :environment | ... | ... |
app/models/national_region.rb
1 | 1 | class NationalRegion < ActiveRecord::Base |
2 | 2 | |
3 | 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 | 8 | def self.search_city(city_name, like = false, state = nil) | ... | ... |
app/models/organization.rb
... | ... | @@ -3,10 +3,11 @@ class Organization < Profile |
3 | 3 | |
4 | 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 | 12 | settings_items :closed, :type => :boolean, :default => false |
12 | 13 | def closed? |
... | ... | @@ -176,4 +177,8 @@ class Organization < Profile |
176 | 177 | self.visible = false |
177 | 178 | save! |
178 | 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 | 184 | end | ... | ... |
app/models/person.rb
... | ... | @@ -3,10 +3,11 @@ class Person < Profile |
3 | 3 | |
4 | 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 | 12 | def self.type_name |
12 | 13 | _('Person') |
... | ... | @@ -21,16 +22,34 @@ class Person < Profile |
21 | 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 | 31 | scope :by_role, lambda { |roles| |
25 | 32 | roles = [roles] unless roles.kind_of?(Array) |
26 | 33 | { :select => 'DISTINCT profiles.*', :joins => :role_assignments, :conditions => ['role_assignments.role_id IN (?)', |
27 | 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 | 51 | permissions += plugins.map do |plugin| |
33 | - plugin.has_permission?(self, permission, profile) | |
52 | + plugin.has_permission?(self, permission, resource) | |
34 | 53 | end |
35 | 54 | permissions.include?(true) |
36 | 55 | end |
... | ... | @@ -65,6 +84,10 @@ roles] } |
65 | 84 | has_and_belongs_to_many :acepted_forums, :class_name => 'Forum', :join_table => 'terms_forum_people' |
66 | 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 | 91 | scope :more_popular, :order => 'friends_count DESC' |
69 | 92 | |
70 | 93 | scope :abusers, :joins => :abuse_complaints, :conditions => ['tasks.status = 3'], :select => 'DISTINCT profiles.*' |
... | ... | @@ -497,6 +520,15 @@ roles] } |
497 | 520 | person.notifier.reschedule_next_notification_mail |
498 | 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 | 532 | protected |
501 | 533 | |
502 | 534 | def followed_by?(profile) | ... | ... |
app/models/product.rb
1 | 1 | class Product < ActiveRecord::Base |
2 | 2 | |
3 | 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 | 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 | 3 | # which by default is the one returned by Environment:default. |
4 | 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 | 8 | # use for internationalizable human type names in search facets |
9 | 9 | # reimplement on subclasses |
... | ... | @@ -12,16 +12,15 @@ class Profile < ActiveRecord::Base |
12 | 12 | end |
13 | 13 | |
14 | 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 | 25 | def self.default_search_display |
27 | 26 | 'compact' |
... | ... | @@ -140,6 +139,17 @@ class Profile < ActiveRecord::Base |
140 | 139 | |
141 | 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 | 153 | def scraps(scrap=nil) |
144 | 154 | scrap = scrap.is_a?(Scrap) ? scrap.id : scrap |
145 | 155 | scrap.nil? ? Scrap.all_scraps(self) : Scrap.all_scraps(self).find(scrap) |
... | ... | @@ -155,6 +165,7 @@ class Profile < ActiveRecord::Base |
155 | 165 | settings_items :public_content, :type => :boolean, :default => true |
156 | 166 | settings_items :description |
157 | 167 | settings_items :fields_privacy, :type => :hash, :default => {} |
168 | + settings_items :email_suggestions, :type => :boolean, :default => false | |
158 | 169 | |
159 | 170 | validates_length_of :description, :maximum => 550, :allow_nil => true |
160 | 171 | |
... | ... | @@ -219,6 +230,8 @@ class Profile < ActiveRecord::Base |
219 | 230 | |
220 | 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 | 235 | def top_level_categorization |
223 | 236 | ret = {} |
224 | 237 | self.profile_categorizations.each do |c| |
... | ... | @@ -514,6 +527,14 @@ class Profile < ActiveRecord::Base |
514 | 527 | generate_url(:profile => identifier, :controller => 'profile', :action => 'index') |
515 | 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 | 538 | def generate_url(options) |
518 | 539 | url_options.merge(options) |
519 | 540 | end |
... | ... | @@ -603,7 +624,7 @@ private :generate_url, :url_options |
603 | 624 | end |
604 | 625 | |
605 | 626 | def copy_article_tree(article, parent=nil) |
606 | - return if article.is_a?(RssFeed) | |
627 | + return if !copy_article?(article) | |
607 | 628 | original_article = self.articles.find_by_name(article.name) |
608 | 629 | if original_article |
609 | 630 | num = 2 |
... | ... | @@ -623,6 +644,11 @@ private :generate_url, :url_options |
623 | 644 | end |
624 | 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 | 652 | # Adds a person as member of this Profile. |
627 | 653 | def add_member(person) |
628 | 654 | if self.has_members? |
... | ... | @@ -632,6 +658,8 @@ private :generate_url, :url_options |
632 | 658 | self.affiliate(person, Profile::Roles.admin(environment.id)) if members.count == 0 |
633 | 659 | self.affiliate(person, Profile::Roles.member(environment.id)) |
634 | 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 | 663 | else |
636 | 664 | raise _("%s can't have members") % self.class.name |
637 | 665 | end |
... | ... | @@ -769,7 +797,10 @@ private :generate_url, :url_options |
769 | 797 | end |
770 | 798 | |
771 | 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 | 804 | end |
774 | 805 | |
775 | 806 | def enable_contact? |
... | ... | @@ -967,4 +998,14 @@ private :generate_url, :url_options |
967 | 998 | def preferred_login_redirection |
968 | 999 | redirection_after_login.blank? ? environment.redirection_after_login : redirection_after_login |
969 | 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 | 1011 | end | ... | ... |
... | ... | @@ -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
app/models/scrap.rb
... | ... | @@ -3,7 +3,7 @@ class Scrap < ActiveRecord::Base |
3 | 3 | attr_accessible :content, :sender_id, :receiver_id, :scrap_id |
4 | 4 | |
5 | 5 | SEARCHABLE_FIELDS = { |
6 | - :content => 1, | |
6 | + :content => {:label => _('Content'), :weight => 1}, | |
7 | 7 | } |
8 | 8 | validates_presence_of :content |
9 | 9 | validates_presence_of :sender_id, :receiver_id | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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 | ... | ... |
... | ... | @@ -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
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 | 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 | 3 | </div> |
4 | - | |
5 | 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 | 10 | :content => (render :partial => 'site_info', :locals => {:f => f})} %> |
11 | 11 | <% tabs << {:title => _('Terms of use'), :id => 'terms-of-use', |
12 | 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 | 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 | 19 | :content => (render :partial => 'signup_welcome_screen', :locals => {:f => f}) }%> |
17 | 20 | <%= render_tabs(tabs) %> |
18 | 21 | <% button_bar do %> | ... | ... |
... | ... | @@ -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
... | ... | @@ -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 %> | ... | ... |
... | ... | @@ -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 | 3 | <div class='text-editor-sidebar'> |
2 | 4 | <span class='button-add' data-value='<%= _('Add to the text') %>'></span> |
3 | 5 | <span class='button-zoom' data-value='<%= _('Zoom in') %>'></span> |
4 | 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 | 10 | <%= render(:partial => 'textile_quick_reference') if @article.is_a?(TextileArticle) %> |
7 | 11 | <div class='text-editor-sidebar-box' id='media-upload-box'> |
8 | - <div class='header'><strong><%= _('Insert media') %></strong></div> | |
9 | 12 | <div id='media-upload-form'> |
10 | 13 | <%= form_tag({ :action => 'media_upload' }, :multipart => true) do %> |
11 | 14 | <div class='formfield'> |
12 | - <% default_folder = content_id_to_str default_folder_for_image_upload(profile) %> | |
13 | 15 | <%= select_profile_folder( |
14 | 16 | _('Choose folder to upload files:'), |
15 | 17 | :parent_id, profile, default_folder, {}, {}, |
16 | 18 | "type='Folder' or type='Gallery'" |
17 | 19 | ) %> |
18 | 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 | 23 | <% end %> |
26 | 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 | 28 | </div> |
33 | 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 | 38 | </div> |
49 | 39 | </div> |
50 | 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
app/views/cms/edit.html.erb
1 | 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 | 6 | <%= labelled_form_for 'article', :html => { :multipart => true, :class => @type } do |f| %> |
5 | 7 | |
6 | 8 | <%= hidden_field_tag("type", @type) if @type %> |
... | ... | @@ -66,7 +68,7 @@ |
66 | 68 | <% end %> |
67 | 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 | 72 | <%= render :partial => 'text_editor_sidebar' %> |
71 | 73 | <% end %> |
72 | 74 | ... | ... |
... | ... | @@ -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 @@ |
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 | 5 | <% if !@failed.blank? %> |
4 | 6 | <div class="errorExplanation" id="errorExplanation"> |
... | ... | @@ -14,15 +16,54 @@ |
14 | 16 | </div> |
15 | 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 | 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 | 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 | 2 | <%= _('Content management') %> |
3 | 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 | 6 | <div class="cms-homepage"> |
7 | 7 | <%= _('Profile homepage:') %> |
8 | 8 | <% if profile.home_page %> |
... | ... | @@ -66,17 +66,17 @@ |
66 | 66 | <%= short_description %> |
67 | 67 | </td> |
68 | 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 | 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 | 73 | <% if profile.home_page != article %> |
74 | 74 | <%= expirable_button article, :home, _('Use as homepage'), { :action => 'set_home_page', :id => article.id }, :method => :post %> |
75 | 75 | <% else %> |
76 | 76 | <%= button_without_text(:'home-not', _('Reset homepage'), { :action => 'set_home_page', :id => nil }, :method => :post) %> |
77 | 77 | <% end %> |
78 | 78 | <% end %> |
79 | - <%= display_delete_button(article) if !remove_content_button(:delete) %> | |
79 | + <%= display_delete_button(article) if !remove_content_button(:delete, article) %> | |
80 | 80 | </td> |
81 | 81 | </tr> |
82 | 82 | <% end %> | ... | ... |
... | ... | @@ -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' %> | ... | ... |
... | ... | @@ -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 | 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 | 6 | <% content = content_tag('span', label_for_edit_article(@page)) %> |
7 | 7 | <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id }) %> |
8 | 8 | <%= expirable_button @page, :edit, content, url %> |
9 | 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 | 12 | <% content = content_tag( 'span', _('Delete') ) %> |
13 | 13 | <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'destroy', :id => @page.id}) %> |
14 | 14 | <% options = {:method => :post, :confirm => delete_article_message(@page)} %> |
15 | 15 | <%= expirable_button @page, :delete, content, url, options %> |
16 | 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 | 21 | <% end %> |
28 | 22 | |
29 | 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 | 25 | <% content = _('Add translation') %> |
32 | 26 | <% parent_id = (@page.folder? ? @page : (@page.parent.nil? ? nil : @page.parent)) %> |
33 | 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 | 28 | <%= expirable_button @page, :locale, content, url %> |
35 | 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 | 32 | <% end %> |
39 | 33 | |
40 | 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 | 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 | 39 | <% content = content_tag( 'span', _('Suggest an article') ) %> |
46 | 40 | <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'suggest_an_article'}) %> |
47 | 41 | <% options = {:id => 'suggest-article-link'} %> | ... | ... |
app/views/content_viewer/versioned_article.html.erb
... | ... | @@ -7,7 +7,7 @@ |
7 | 7 | <div id="article-actions"> |
8 | 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 | 11 | <% content = content_tag('span', _('Revert to this version')) %> |
12 | 12 | <% url = profile.admin_url.merge({ :controller => 'cms', :action => 'edit', :id => @page.id, :version => @version }) %> |
13 | 13 | <%= expirable_button @page, :undo, content, url, :id => 'article-revert-version-link' %> | ... | ... |
app/views/enterprise_registration/creation.html.erb
1 | 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> | ... | ... |
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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 | 10 | <%= link_to _('Do you want to see other people in this environment?'), :controller => 'search', :action => 'assets', :asset => 'people' %> |
11 | 11 | </em> |
12 | 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 | 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 | 15 | <% button_bar do %> |
46 | 16 | <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %> |
47 | 17 | <%= button(:search, _('Find people'), :controller => 'search', :action => 'assets', :asset => 'people') %> |
48 | 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 | 20 | <% end %> |
51 | 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 | 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" --> | ... | ... |
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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 %> | ... | ... |
... | ... | @@ -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 | -<% 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 | 1 | <%= render :partial => 'invite/dialog_wait_loading', :locals => {:contact_list => @contact_list.id } if @import_from != 'manual' %> |
2 | 2 | |
3 | 3 | <% if profile.person? %> |
4 | - <h1><%= _('Invite your friends') %></h1> | |
4 | + <h1><%= _('Invite people') %></h1> | |
5 | 5 | <% else %> |
6 | - <h1><%= _('Invite your friends to join %s') % profile.name %></h1> | |
6 | + <h1><%= _('Invite people to join') %></h1> | |
7 | 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 | 14 | <p> |
15 | -<%= _('Indicate which friends you want to invite.') %> | |
15 | +<%= _('Indicate which people you want to invite.') %> | |
16 | 16 | </p> |
17 | 17 | |
18 | 18 | <%= form_tag do %> |
... | ... | @@ -30,16 +30,9 @@ |
30 | 30 | </div> |
31 | 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 | 35 | <% button_bar do %> |
43 | - <%= submit_button(:ok, _("Invite my friends!")) %> | |
36 | + <%= submit_button(:ok, _("Invite!")) %> | |
44 | 37 | <% end %> |
45 | 38 | <% end %> | ... | ... |
app/views/layouts/_javascript.html.erb
... | ... | @@ -2,8 +2,8 @@ |
2 | 2 | 'jquery-2.1.1.min', 'jquery-migrate-1.2.1', |
3 | 3 | 'jquery.noconflict.js', 'jquery.cycle.all.min.js', 'thickbox.js', 'lightbox', 'colorbox', |
4 | 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 | 7 | 'jquery-timepicker-addon/dist/jquery-ui-timepicker-addon', 'application.js', 'rails.js', 'inputosaurus.js', :cache => 'cache/application' %> |
8 | 8 | |
9 | 9 | <% language = FastGettext.locale %> | ... | ... |
app/views/layouts/_user.html.erb
... | ... | @@ -10,16 +10,17 @@ |
10 | 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 | 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 | 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 | 24 | <input name="query" size="15" title="<%=_('Search...')%>" onfocus="this.form.className='focused';" onblur="this.form.className=''" /> |
24 | 25 | <div><%=_('Press <strong>Enter</strong> to send the search query.')%></div> |
25 | 26 | <%= javascript_tag 'jQuery("#user form input").hint();' %> | ... | ... |
... | ... | @@ -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 | 16 | <%= labelled_select(_('Filter')+': ', :filter_type, :first, :last, @filter, type_collection, :id => 'memberships_filter')%> |
17 | 17 | </p> |
18 | 18 | |
19 | +<p><%= link_to _('See some suggestions of communities...'), :action => 'suggest' %></p> | |
20 | + | |
19 | 21 | <% if @memberships.empty? %> |
20 | 22 | <p> |
21 | 23 | <em><%= _('No groups to list') %></em> | ... | ... |
... | ... | @@ -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> | ... | ... |
... | ... | @@ -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 | 10 | </div> |
11 | 11 | </div> |
12 | 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 | 14 | <br/> | ... | ... |
app/views/profile/friends.html.erb
... | ... | @@ -18,7 +18,7 @@ |
18 | 18 | <%= button :back, _('Go back'), { :controller => 'profile' } %> |
19 | 19 | <% if user == profile %> |
20 | 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 | 22 | <% end %> |
23 | 23 | <% end %> |
24 | 24 | ... | ... |
app/views/profile/members.html.erb
... | ... | @@ -18,7 +18,7 @@ |
18 | 18 | <%= button :back, _('Go back'), { :controller => 'profile' } %> |
19 | 19 | <% if profile.community? and user %> |
20 | 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 | 22 | <% end %> |
23 | 23 | <% if user.has_permission?(:send_mail_to_members, profile) %> |
24 | 24 | <%= button :send, _('Send e-mail to members'), :controller => 'profile', :action => 'send_mail' %> | ... | ... |
app/views/profile_editor/_moderation.html.erb
1 | 1 | <h2><%= _('Moderation options') %></h2> |
2 | 2 | <% if profile.community? %> |
3 | 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 | 22 | <%= _('New members must be approved:')%> |
5 | 23 | </div> |
6 | 24 | <div style='margin-bottom: 0.5em'> |
... | ... | @@ -33,3 +51,5 @@ |
33 | 51 | <%= _('<strong>After</strong> being published in this group (a moderator can always remove publicated articles later).') %> |
34 | 52 | </div> |
35 | 53 | </div> |
54 | + | |
55 | +<%= javascript_include_tag('invite') %> | ... | ... |
app/views/profile_editor/edit.html.erb
... | ... | @@ -52,6 +52,12 @@ |
52 | 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 | 62 | @plugins.dispatch(:profile_editor_extras).map do |content| |
57 | 63 | content.kind_of?(Proc) ? self.instance_exec(&content) : content | ... | ... |
app/views/profile_editor/index.html.erb
... | ... | @@ -68,6 +68,8 @@ |
68 | 68 | |
69 | 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 | 73 | <% @plugins.dispatch(:control_panel_buttons).each do |button| %> |
72 | 74 | <%= control_panel_button(button[:title], button[:icon], button[:url]) %> |
73 | 75 | <% end %> | ... | ... |
... | ... | @@ -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 | 2 | <%= button :back, _('Back'), :controller => 'profile_editor' %> |
3 | 3 | <%= button :add, _('Add members'), :action => 'add_members' if profile.enterprise? %> |
4 | 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 | 6 | <% end %> |
7 | 7 | <% if profile.community? and user.has_permission?(:send_mail_to_members, profile) %> |
8 | 8 | <%= button :send, _('Send e-mail to members'), :controller => 'profile', :action => 'send_mail' %> | ... | ... |
... | ... | @@ -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 | 3 | |
4 | 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 | 8 | </div> |
9 | +<%= javascript_include_tag 'search' %> | ... | ... |
... | ... | @@ -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 | 3 | <li class="search-profile-item"> |
4 | 4 | <%= profile_image_link profile, :portrait, 'div', filter_label %> |
5 | 5 | </li> | ... | ... |
app/views/search/_display_results.html.erb
1 | 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 | 3 | <% search = @searches[name] %> |
4 | 4 | |
5 | 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 | 2 | <div class="search-enterprise-item"> |
3 | 3 | <div class="search-enterprise-item-column-left"> |
4 | 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 | 6 | </div> |
7 | 7 | <div class="search-enterprise-item-column-right"> |
8 | 8 | <%= link_to_homepage(enterprise.name, enterprise.identifier, :class => "search-result-title") %> | ... | ... |