Commit 8b945809d1d522c39798b10d96ecca9eccd13328
Committed by
Larissa Reis
1 parent
fceeac68
Exists in
profile_api_improvements
and in
1 other branch
Profile followers feature
Signed-off-by: Alessandro Caetano <alessandro.caetanob@gmail.com> Signed-off-by: Artur Bersan de Faria <arturbersan@gmail.com> Signed-off-by: Gabriel Silva <gabriel93.silva@gmail.com> Signed-off-by: Marcos Ronaldo <marcos.rpj2@gmail.com> Signed-off-by: Matheus Miranda <matheusmirandalacerda@gmail.com> Signed-off-by: Sabryna Sousa <sabryna.sousa1323@gmail.com> Signed-off-by: Victor Matias Navarro <victor.matias.navarro@gmail.com> Signed-off-by: Vitor Barbosa <vitormga15@gmail.com>
Showing
58 changed files
with
1448 additions
and
92 deletions
Show diff stats
app/controllers/application_controller.rb
... | ... | @@ -115,6 +115,10 @@ class ApplicationController < ActionController::Base |
115 | 115 | |
116 | 116 | protected |
117 | 117 | |
118 | + def accept_only_post | |
119 | + return render_not_found if !request.post? | |
120 | + end | |
121 | + | |
118 | 122 | def verified_request? |
119 | 123 | super || form_authenticity_token == request.headers['X-XSRF-TOKEN'] |
120 | 124 | end | ... | ... |
... | ... | @@ -0,0 +1,58 @@ |
1 | +class CirclesController < MyProfileController | |
2 | + | |
3 | + before_action :accept_only_post, :only => [:create, :update, :destroy] | |
4 | + | |
5 | + def index | |
6 | + @circles = current_person.circles | |
7 | + end | |
8 | + | |
9 | + def new | |
10 | + @circle = Circle.new | |
11 | + end | |
12 | + | |
13 | + def create | |
14 | + @circle = Circle.new(params[:circle].merge({ :person => current_person })) | |
15 | + if @circle.save | |
16 | + redirect_to :action => 'index' | |
17 | + else | |
18 | + render :action => 'new' | |
19 | + end | |
20 | + end | |
21 | + | |
22 | + def xhr_create | |
23 | + if request.xhr? | |
24 | + circle = Circle.new(params[:circle].merge({:person => current_person })) | |
25 | + if circle.save | |
26 | + render :partial => "circle_checkbox", :locals => { :circle => circle }, | |
27 | + :status => 201 | |
28 | + else | |
29 | + render :text => _('The circle could not be saved'), :status => 400 | |
30 | + end | |
31 | + else | |
32 | + render_not_found | |
33 | + end | |
34 | + end | |
35 | + | |
36 | + def edit | |
37 | + @circle = Circle.find_by_id(params[:id]) | |
38 | + render_not_found if @circle.nil? | |
39 | + end | |
40 | + | |
41 | + def update | |
42 | + @circle = Circle.find_by_id(params[:id]) | |
43 | + return render_not_found if @circle.nil? | |
44 | + | |
45 | + if @circle.update(params[:circle]) | |
46 | + redirect_to :action => 'index' | |
47 | + else | |
48 | + render :action => 'edit' | |
49 | + end | |
50 | + end | |
51 | + | |
52 | + def destroy | |
53 | + @circle = Circle.find_by_id(params[:id]) | |
54 | + return render_not_found if @circle.nil? | |
55 | + @circle.destroy | |
56 | + redirect_to :action => 'index' | |
57 | + end | |
58 | +end | ... | ... |
... | ... | @@ -0,0 +1,43 @@ |
1 | +class FollowersController < MyProfileController | |
2 | + | |
3 | + before_action :only_for_person, :only => :index | |
4 | + before_action :accept_only_post, :only => [:update_category] | |
5 | + | |
6 | + def index | |
7 | + @followed_people = current_person.followed_profiles.order(:type) | |
8 | + @profile_types = {_('All profiles') => nil}.merge(Circle.profile_types).to_a | |
9 | + | |
10 | + if params['filter'].present? | |
11 | + @followed_people = @followed_people.where(:type => params['filter']) | |
12 | + @active_filter = params['filter'] | |
13 | + end | |
14 | + | |
15 | + @followed_people = @followed_people.paginate(:per_page => 15, :page => params[:npage]) | |
16 | + end | |
17 | + | |
18 | + def set_category_modal | |
19 | + profile = Profile.find(params[:followed_profile_id]) | |
20 | + circles = Circle.where(:person => current_person, :profile_type => profile.class.name) | |
21 | + render :partial => 'followers/edit_circles_modal', :locals => { :circles => circles, :profile => profile } | |
22 | + end | |
23 | + | |
24 | + def update_category | |
25 | + followed_profile = Profile.find_by(:id => params["followed_profile_id"]) | |
26 | + | |
27 | + selected_circles = params[:circles].map{|circle_name, circle_id| Circle.find_by(:id => circle_id)}.select{|c|not c.nil?} | |
28 | + | |
29 | + if followed_profile | |
30 | + current_person.update_profile_circles(followed_profile, selected_circles) | |
31 | + render :text => _("Circles of %s updated successfully") % followed_profile.name, :status => 200 | |
32 | + else | |
33 | + render :text => _("Error: No profile to follow."), :status => 400 | |
34 | + end | |
35 | + end | |
36 | + | |
37 | + protected | |
38 | + | |
39 | + def only_for_person | |
40 | + render_not_found unless profile.person? | |
41 | + end | |
42 | + | |
43 | +end | ... | ... |
app/controllers/public/content_viewer_controller.rb
... | ... | @@ -128,9 +128,9 @@ class ContentViewerController < ApplicationController |
128 | 128 | end |
129 | 129 | |
130 | 130 | unless @page.display_to?(user) |
131 | - if !profile.visible? || profile.secret? || (user && user.follows?(profile)) || user.blank? | |
131 | + if !profile.visible? || profile.secret? || (user && profile.in_social_circle?(user)) || user.blank? | |
132 | 132 | render_access_denied |
133 | - else #!profile.public? | |
133 | + else | |
134 | 134 | private_profile_partial_parameters |
135 | 135 | render :template => 'profile/_private_profile', :status => 403, :formats => [:html] |
136 | 136 | end | ... | ... |
app/controllers/public/profile_controller.rb
... | ... | @@ -3,7 +3,8 @@ class ProfileController < PublicController |
3 | 3 | needs_profile |
4 | 4 | before_filter :check_access_to_profile, :except => [:join, :join_not_logged, :index, :add] |
5 | 5 | before_filter :store_location, :only => [:join, :join_not_logged, :report_abuse, :send_mail] |
6 | - before_filter :login_required, :only => [:add, :join, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_activities, :view_more_network_activities, :report_abuse, :register_report, :leave_comment_on_activity, :send_mail] | |
6 | + before_filter :login_required, :only => [:add, :join, :leave, :unblock, :leave_scrap, :remove_scrap, :remove_activity, :view_more_activities, :view_more_network_activities, :report_abuse, :register_report, :leave_comment_on_activity, :send_mail, :follow, :unfollow] | |
7 | + before_filter :allow_followers?, :only => [:follow, :unfollow] | |
7 | 8 | |
8 | 9 | helper TagsHelper |
9 | 10 | helper ActionTrackerHelper |
... | ... | @@ -65,6 +66,14 @@ class ProfileController < PublicController |
65 | 66 | end |
66 | 67 | end |
67 | 68 | |
69 | + def following | |
70 | + @followed_people = profile.followed_profiles.paginate(:per_page => per_page, :page => params[:npage], :total_entries => profile.followed_profiles.count) | |
71 | + end | |
72 | + | |
73 | + def followed | |
74 | + @followed_by = profile.followers.paginate(:per_page => per_page, :page => params[:npage], :total_entries => profile.followers.count) | |
75 | + end | |
76 | + | |
68 | 77 | def members |
69 | 78 | if is_cache_expired?(profile.members_cache_key(params)) |
70 | 79 | sort = (params[:sort] == 'desc') ? params[:sort] : 'asc' |
... | ... | @@ -151,6 +160,37 @@ class ProfileController < PublicController |
151 | 160 | end |
152 | 161 | end |
153 | 162 | |
163 | + def follow | |
164 | + if request.post? | |
165 | + if profile.followed_by?(current_person) | |
166 | + render :text => _("You are already following %s.") % profile.name, :status => 400 | |
167 | + else | |
168 | + selected_circles = params[:circles].map{|circle_name, circle_id| Circle.find_by(:id => circle_id)}.select{|c|not c.nil?} | |
169 | + if selected_circles.present? | |
170 | + current_person.follow(profile, selected_circles) | |
171 | + render :text => _("You are now following %s") % profile.name, :status => 200 | |
172 | + else | |
173 | + render :text => _("Select at least one circle to follow %s.") % profile.name, :status => 400 | |
174 | + end | |
175 | + end | |
176 | + else | |
177 | + render_not_found | |
178 | + end | |
179 | + end | |
180 | + | |
181 | + def find_profile_circles | |
182 | + circles = Circle.where(:person => current_person, :profile_type => profile.class.name) | |
183 | + render :partial => 'blocks/profile_info_actions/circles', :locals => { :circles => circles, :profile_types => Circle.profile_types.to_a } | |
184 | + end | |
185 | + | |
186 | + def unfollow | |
187 | + if current_person.follows?(profile) | |
188 | + current_person.unfollow(profile) | |
189 | + end | |
190 | + redirect_url = params["redirect_to"] ? params["redirect_to"] : profile.url | |
191 | + redirect_to redirect_url | |
192 | + end | |
193 | + | |
154 | 194 | def check_friendship |
155 | 195 | unless logged_in? |
156 | 196 | render :text => '' |
... | ... | @@ -437,4 +477,8 @@ class ProfileController < PublicController |
437 | 477 | [:image, :domains, :preferred_domain, :environment] |
438 | 478 | end |
439 | 479 | |
480 | + def allow_followers? | |
481 | + render_not_found unless profile.allow_followers? | |
482 | + end | |
483 | + | |
440 | 484 | end | ... | ... |
app/helpers/action_tracker_helper.rb
... | ... | @@ -14,13 +14,23 @@ module ActionTrackerHelper |
14 | 14 | } |
15 | 15 | end |
16 | 16 | |
17 | + def new_follower_description ta | |
18 | + n_('has 1 new follower:<br />%{name}', 'has %{num} new followers:<br />%{name}', ta.get_follower_name.size).html_safe % { | |
19 | + num: ta.get_follower_name.size, | |
20 | + name: safe_join(ta.collect_group_with_index(:follower_name) do |n,i| | |
21 | + link_to image_tag(ta.get_follower_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/person-icon.png")), | |
22 | + ta.get_follower_url[i], title: n | |
23 | + end) | |
24 | + } | |
25 | + end | |
26 | + | |
17 | 27 | def join_community_description ta |
18 | 28 | n_('has joined 1 community:<br />%{name}', 'has joined %{num} communities:<br />%{name}', ta.get_resource_name.size).html_safe % { |
19 | 29 | num: ta.get_resource_name.size, |
20 | - name: ta.collect_group_with_index(:resource_name) do |n,i| | |
30 | + name: safe_join(ta.collect_group_with_index(:resource_name) do |n,i| | |
21 | 31 | link = link_to image_tag(ta.get_resource_profile_custom_icon[i] || default_or_themed_icon("/images/icons-app/community-icon.png")), |
22 | 32 | ta.get_resource_url[i], title: n |
23 | - end.join.html_safe | |
33 | + end) | |
24 | 34 | } |
25 | 35 | end |
26 | 36 | |
... | ... | @@ -68,9 +78,9 @@ module ActionTrackerHelper |
68 | 78 | end |
69 | 79 | |
70 | 80 | def favorite_enterprise_description ta |
71 | - _('favorited enterprise %{title}') % { | |
81 | + (_('favorited enterprise %{title}') % { | |
72 | 82 | title: link_to(truncate(ta.get_enterprise_name), ta.get_enterprise_url), |
73 | - } | |
83 | + }).html_safe | |
74 | 84 | end |
75 | 85 | |
76 | 86 | end | ... | ... |
app/helpers/profile_helper.rb
... | ... | @@ -11,7 +11,7 @@ module ProfileHelper |
11 | 11 | PERSON_CATEGORIES[:location] = [:address, :address_reference, :zip_code, :city, :state, :district, :country, :nationality] |
12 | 12 | PERSON_CATEGORIES[:work] = [:organization, :organization_website, :professional_activity] |
13 | 13 | PERSON_CATEGORIES[:study] = [:schooling, :formation, :area_of_study] |
14 | - PERSON_CATEGORIES[:network] = [:friends, :communities, :enterprises] | |
14 | + PERSON_CATEGORIES[:network] = [:friends, :followers, :followed_profiles, :communities, :enterprises] | |
15 | 15 | PERSON_CATEGORIES.merge!(COMMON_CATEGORIES) |
16 | 16 | |
17 | 17 | ORGANIZATION_CATEGORIES = {} |
... | ... | @@ -42,7 +42,8 @@ module ProfileHelper |
42 | 42 | :created_at => _('Profile created at'), |
43 | 43 | :members_count => _('Members'), |
44 | 44 | :privacy_setting => _('Privacy setting'), |
45 | - :article_tags => _('Tags') | |
45 | + :article_tags => _('Tags'), | |
46 | + :followed_profiles => _('Following') | |
46 | 47 | } |
47 | 48 | |
48 | 49 | EXCEPTION = { |
... | ... | @@ -144,6 +145,14 @@ module ProfileHelper |
144 | 145 | link_to(n_('One picture', '%{num} pictures', gallery.images.published.count) % { :num => gallery.images.published.count }, gallery.url) |
145 | 146 | end |
146 | 147 | |
148 | + def treat_followers(followers) | |
149 | + link_to(profile.followers.count, {:action=>"followed", :controller=>"profile", :profile=>"#{profile.identifier}"}) | |
150 | + end | |
151 | + | |
152 | + def treat_followed_profiles(followed_profiles) | |
153 | + link_to(profile.followed_profiles.count, {:action=>"following", :controller=>"profile", :profile=>"#{profile.identifier}"}) | |
154 | + end | |
155 | + | |
147 | 156 | def treat_events(events) |
148 | 157 | link_to events.published.count, :controller => 'events', :action => 'events' |
149 | 158 | end | ... | ... |
app/jobs/notify_activity_to_profiles_job.rb
... | ... | @@ -19,8 +19,8 @@ class NotifyActivityToProfilesJob < Struct.new(:tracked_action_id) |
19 | 19 | # Notify the user |
20 | 20 | ActionTrackerNotification.create(:profile_id => tracked_action.user.id, :action_tracker_id => tracked_action.id) |
21 | 21 | |
22 | - # Notify all friends | |
23 | - ActionTrackerNotification.connection.execute("insert into action_tracker_notifications(profile_id, action_tracker_id) select f.friend_id, #{tracked_action.id} from friendships as f where person_id=#{tracked_action.user.id} and f.friend_id not in (select atn.profile_id from action_tracker_notifications as atn where atn.action_tracker_id = #{tracked_action.id})") | |
22 | + # Notify all followers | |
23 | + ActionTrackerNotification.connection.execute("INSERT INTO action_tracker_notifications(profile_id, action_tracker_id) SELECT DISTINCT c.person_id, #{tracked_action.id} FROM profiles_circles AS p JOIN circles as c ON c.id = p.circle_id WHERE p.profile_id = #{tracked_action.user.id} AND (c.person_id NOT IN (SELECT atn.profile_id FROM action_tracker_notifications AS atn WHERE atn.action_tracker_id = #{tracked_action.id}))") | |
24 | 24 | |
25 | 25 | if tracked_action.user.is_a? Organization |
26 | 26 | ActionTrackerNotification.connection.execute "insert into action_tracker_notifications(profile_id, action_tracker_id) " + | ... | ... |
app/models/add_member.rb
... | ... | @@ -22,6 +22,7 @@ class AddMember < Task |
22 | 22 | self.roles = [Profile::Roles.member(organization.environment.id).id] |
23 | 23 | end |
24 | 24 | target.affiliate(requestor, self.roles.select{|r| !r.to_i.zero? }.map{|i| Role.find(i)}) |
25 | + person.follow(organization, Circle.find_or_create_by(:person => person, :name =>_('memberships'), :profile_type => 'Community')) | |
25 | 26 | end |
26 | 27 | |
27 | 28 | def title | ... | ... |
app/models/article.rb
... | ... | @@ -538,13 +538,13 @@ class Article < ApplicationRecord |
538 | 538 | |
539 | 539 | scope :display_filter, lambda {|user, profile| |
540 | 540 | return published if (user.nil? && profile && profile.public?) |
541 | - return [] if user.nil? || (profile && !profile.public? && !user.follows?(profile)) | |
541 | + return [] if user.nil? || (profile && !profile.public? && !profile.in_social_circle?(user)) | |
542 | 542 | where( |
543 | 543 | [ |
544 | 544 | "published = ? OR last_changed_by_id = ? OR profile_id = ? OR ? |
545 | 545 | OR (show_to_followers = ? AND ? AND profile_id IN (?))", true, user.id, user.id, |
546 | 546 | profile.nil? ? false : user.has_permission?(:view_private_content, profile), |
547 | - true, (profile.nil? ? true : user.follows?(profile)), ( profile.nil? ? (user.friends.select('profiles.id')) : [profile.id]) | |
547 | + true, (profile.nil? ? true : profile.in_social_circle?(user)), ( profile.nil? ? (user.friends.select('profiles.id')) : [profile.id]) | |
548 | 548 | ] |
549 | 549 | ) |
550 | 550 | } | ... | ... |
app/models/block.rb
... | ... | @@ -89,7 +89,7 @@ class Block < ApplicationRecord |
89 | 89 | end |
90 | 90 | |
91 | 91 | def display_to_user?(user) |
92 | - display_user == 'all' || (user.nil? && display_user == 'not_logged') || (user && display_user == 'logged') || (user && display_user == 'followers' && user.follows?(owner)) | |
92 | + display_user == 'all' || (user.nil? && display_user == 'not_logged') || (user && display_user == 'logged') || (user && display_user == 'followers' && owner.in_social_circle?(user)) | |
93 | 93 | end |
94 | 94 | |
95 | 95 | def display_always(context) | ... | ... |
... | ... | @@ -0,0 +1,37 @@ |
1 | +class Circle < ApplicationRecord | |
2 | + has_many :profile_followers | |
3 | + belongs_to :person | |
4 | + | |
5 | + attr_accessible :name, :person, :profile_type | |
6 | + | |
7 | + validates :name, presence: true | |
8 | + validates :person_id, presence: true | |
9 | + validates :profile_type, presence: true | |
10 | + validates :person_id, :uniqueness => {:scope => :name, :message => "can't add two circles with the same name"} | |
11 | + | |
12 | + validate :profile_type_must_be_in_list | |
13 | + | |
14 | + scope :by_owner, -> person{ | |
15 | + where(:person => person) | |
16 | + } | |
17 | + | |
18 | + scope :with_name, -> name{ | |
19 | + where(:name => name) | |
20 | + } | |
21 | + | |
22 | + def self.profile_types | |
23 | + { | |
24 | + _("Person") => Person.name, | |
25 | + _("Community") => Community.name, | |
26 | + _("Enterprise") => Enterprise.name | |
27 | + } | |
28 | + end | |
29 | + | |
30 | + def profile_type_must_be_in_list | |
31 | + valid_profile_types = Circle.profile_types.values | |
32 | + unless self.profile_type.in? valid_profile_types | |
33 | + self.errors.add(:profile_type, "invalid profile type") | |
34 | + end | |
35 | + end | |
36 | + | |
37 | +end | ... | ... |
app/models/enterprise.rb
app/models/favorite_enterprise_person.rb
... | ... | @@ -7,6 +7,10 @@ class FavoriteEnterprisePerson < ApplicationRecord |
7 | 7 | belongs_to :enterprise |
8 | 8 | belongs_to :person |
9 | 9 | |
10 | + after_create do |favorite| | |
11 | + favorite.person.follow(favorite.enterprise, Circle.find_or_create_by(:person => favorite.person, :name =>_('favorites'), :profile_type => 'Enterprise')) | |
12 | + end | |
13 | + | |
10 | 14 | protected |
11 | 15 | |
12 | 16 | def is_trackable? | ... | ... |
app/models/friendship.rb
... | ... | @@ -9,11 +9,15 @@ class Friendship < ApplicationRecord |
9 | 9 | after_create do |friendship| |
10 | 10 | Friendship.update_cache_counter(:friends_count, friendship.person, 1) |
11 | 11 | Friendship.update_cache_counter(:friends_count, friendship.friend, 1) |
12 | + friendship.person.follow(friendship.friend, Circle.find_or_create_by(:person => friendship.person, :name => (friendship.group.blank? ? 'friendships': friendship.group), :profile_type => 'Person')) | |
12 | 13 | end |
13 | 14 | |
14 | 15 | after_destroy do |friendship| |
15 | 16 | Friendship.update_cache_counter(:friends_count, friendship.person, -1) |
16 | 17 | Friendship.update_cache_counter(:friends_count, friendship.friend, -1) |
18 | + | |
19 | + circle = Circle.find_by(:person => friendship.person, :name => (friendship.group.blank? ? 'friendships': friendship.group) ) | |
20 | + friendship.person.remove_profile_from_circle(friendship.friend, circle) if circle | |
17 | 21 | end |
18 | 22 | |
19 | 23 | def self.remove_friendship(person1, person2) | ... | ... |
app/models/person.rb
... | ... | @@ -8,7 +8,6 @@ class Person < Profile |
8 | 8 | :display => %w[compact] |
9 | 9 | } |
10 | 10 | |
11 | - | |
12 | 11 | def self.type_name |
13 | 12 | _('Person') |
14 | 13 | end |
... | ... | @@ -93,6 +92,7 @@ class Person < Profile |
93 | 92 | has_many :following_articles, :class_name => 'Article', :through => :article_followers, :source => :article |
94 | 93 | has_many :friendships, :dependent => :destroy |
95 | 94 | has_many :friends, :class_name => 'Person', :through => :friendships |
95 | + has_many :circles | |
96 | 96 | |
97 | 97 | scope :online, -> { |
98 | 98 | joins(:user).where("users.chat_status != '' AND users.chat_status_at >= ?", DateTime.now - User.expires_chat_status_every.minutes) |
... | ... | @@ -200,6 +200,33 @@ class Person < Profile |
200 | 200 | end |
201 | 201 | end |
202 | 202 | |
203 | + def follow (profile, circles) | |
204 | + circles = [circles] unless circles.is_a?(Array) | |
205 | + circles.each do |new_circle| | |
206 | + ProfileFollower.create(profile: profile, circle: new_circle) | |
207 | + end | |
208 | + end | |
209 | + | |
210 | + def update_profile_circles (profile, new_circles) | |
211 | + profile_circles = ProfileFollower.with_profile(profile).with_follower(self).map(&:circle) | |
212 | + circles_to_add = new_circles - profile_circles | |
213 | + circles_to_remove = profile_circles - new_circles | |
214 | + circles_to_add.each do |new_circle| | |
215 | + ProfileFollower.create(profile: profile, circle: new_circle) | |
216 | + end | |
217 | + | |
218 | + ProfileFollower.where('circle_id IN (?) AND profile_id = ?', | |
219 | + circles_to_remove.map(&:id), profile.id).destroy_all | |
220 | + end | |
221 | + | |
222 | + def unfollow(profile) | |
223 | + ProfileFollower.with_follower(self).with_profile(profile).destroy_all | |
224 | + end | |
225 | + | |
226 | + def remove_profile_from_circle(profile, circle) | |
227 | + ProfileFollower.with_profile(profile).with_circle(circle).destroy_all | |
228 | + end | |
229 | + | |
203 | 230 | def already_request_friendship?(person) |
204 | 231 | person.tasks.where(requestor_id: self.id, type: 'AddFriend', status: Task::Status::ACTIVE).first |
205 | 232 | end |
... | ... | @@ -580,9 +607,12 @@ class Person < Profile |
580 | 607 | person.has_permission?(:manage_friends, self) |
581 | 608 | end |
582 | 609 | |
583 | - protected | |
610 | + def followed_profiles | |
611 | + Profile.followed_by self | |
612 | + end | |
584 | 613 | |
585 | - def followed_by?(profile) | |
586 | - self == profile || self.is_a_friend?(profile) | |
614 | + def in_social_circle?(person) | |
615 | + self.is_a_friend?(person) || super | |
587 | 616 | end |
617 | + | |
588 | 618 | end | ... | ... |
app/models/profile.rb
... | ... | @@ -5,7 +5,7 @@ class Profile < ApplicationRecord |
5 | 5 | |
6 | 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, |
7 | 7 | :redirection_after_login, :custom_url_redirection, |
8 | - :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification | |
8 | + :email_suggestions, :allow_members_to_invite, :invite_friends_only, :secret, :profile_admin_mail_notification, :allow_followers | |
9 | 9 | |
10 | 10 | # use for internationalizable human type names in search facets |
11 | 11 | # reimplement on subclasses |
... | ... | @@ -206,6 +206,23 @@ class Profile < ApplicationRecord |
206 | 206 | scope :more_active, -> { order 'activities_count DESC' } |
207 | 207 | scope :more_recent, -> { order "created_at DESC" } |
208 | 208 | |
209 | + scope :followed_by, -> person{ | |
210 | + distinct.select('profiles.*'). | |
211 | + joins('left join profiles_circles ON profiles_circles.profile_id = profiles.id'). | |
212 | + joins('left join circles ON circles.id = profiles_circles.circle_id'). | |
213 | + where('circles.person_id = ?', person.id) | |
214 | + } | |
215 | + | |
216 | + scope :in_circle, -> circle{ | |
217 | + distinct.select('profiles.*'). | |
218 | + joins('left join profiles_circles ON profiles_circles.profile_id = profiles.id'). | |
219 | + joins('left join circles ON circles.id = profiles_circles.circle_id'). | |
220 | + where('circles.id = ?', circle.id) | |
221 | + } | |
222 | + | |
223 | + settings_items :allow_followers, :type => :boolean, :default => true | |
224 | + alias_method :allow_followers?, :allow_followers | |
225 | + | |
209 | 226 | acts_as_trackable :dependent => :destroy |
210 | 227 | |
211 | 228 | has_many :profile_activities |
... | ... | @@ -218,6 +235,9 @@ class Profile < ApplicationRecord |
218 | 235 | |
219 | 236 | has_many :email_templates, :foreign_key => :owner_id |
220 | 237 | |
238 | + has_many :profile_followers | |
239 | + has_many :followers, :class_name => 'Person', :through => :profile_followers, :source => :person | |
240 | + | |
221 | 241 | # Although this should be a has_one relation, there are no non-silly names for |
222 | 242 | # a foreign key on article to reference the template to which it is |
223 | 243 | # welcome_page... =P |
... | ... | @@ -769,6 +789,7 @@ private :generate_url, :url_options |
769 | 789 | else |
770 | 790 | self.affiliate(person, Profile::Roles.admin(environment.id), attributes) if members.count == 0 |
771 | 791 | self.affiliate(person, Profile::Roles.member(environment.id), attributes) |
792 | + person.follow(self, Circle.find_or_create_by(:person => person, :name =>_('memberships'), :profile_type => 'Community')) | |
772 | 793 | end |
773 | 794 | person.tasks.pending.of("InviteMember").select { |t| t.data[:community_id] == self.id }.each { |invite| invite.cancel } |
774 | 795 | remove_from_suggestion_list person |
... | ... | @@ -1112,7 +1133,11 @@ private :generate_url, :url_options |
1112 | 1133 | end |
1113 | 1134 | |
1114 | 1135 | def followed_by?(person) |
1115 | - person.is_member_of?(self) | |
1136 | + (person == self) || (person.in? self.followers) | |
1137 | + end | |
1138 | + | |
1139 | + def in_social_circle?(person) | |
1140 | + (person == self) || (person.is_member_of?(self)) | |
1116 | 1141 | end |
1117 | 1142 | |
1118 | 1143 | def display_private_info_to?(user) |
... | ... | @@ -1153,4 +1178,8 @@ private :generate_url, :url_options |
1153 | 1178 | def allow_destroy?(person = nil) |
1154 | 1179 | person.kind_of?(Profile) && person.has_permission?('destroy_profile', self) |
1155 | 1180 | end |
1181 | + | |
1182 | + def in_circle?(circle, follower) | |
1183 | + ProfileFollower.with_follower(follower).with_circle(circle).with_profile(self).present? | |
1184 | + end | |
1156 | 1185 | end | ... | ... |
... | ... | @@ -0,0 +1,28 @@ |
1 | +class ProfileFollower < ApplicationRecord | |
2 | + self.table_name = :profiles_circles | |
3 | + track_actions :new_follower, :after_create, :keep_params => ["follower.name", "follower.url", "follower.profile_custom_icon"], :custom_user => :profile | |
4 | + | |
5 | + attr_accessible :profile, :circle | |
6 | + | |
7 | + belongs_to :profile | |
8 | + belongs_to :circle | |
9 | + | |
10 | + has_one :person, through: :circle | |
11 | + alias follower person | |
12 | + | |
13 | + validates_presence_of :profile_id, :circle_id | |
14 | + validates :profile_id, :uniqueness => {:scope => :circle_id, :message => "can't put a profile in the same circle twice"} | |
15 | + | |
16 | + scope :with_follower, -> person{ | |
17 | + joins(:circle).where('circles.person_id = ?', person.id) | |
18 | + } | |
19 | + | |
20 | + scope :with_profile, -> profile{ | |
21 | + where(:profile => profile) | |
22 | + } | |
23 | + | |
24 | + scope :with_circle, -> circle{ | |
25 | + where(:circle => circle) | |
26 | + } | |
27 | + | |
28 | +end | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +<div class="circles" id='circles-list'> | |
2 | + | |
3 | + <%= form_for :circles, :url => {:controller => 'profile', :action => 'follow'}, :html => {:id => "follow-circles-form"} do |f|%> | |
4 | + <%= render partial: "blocks/profile_info_actions/select_circles", :locals => {:circles => circles} %> | |
5 | + | |
6 | + <div id="circle-actions"> | |
7 | + <%= submit_button :ok, _("Follow") %> | |
8 | + <input type="button" value="<%= _("Cancel") %>" id="cancel-set-circle" class="button with-text icon-cancel"/> | |
9 | + </div> | |
10 | + <% end %> | |
11 | +</div> | ... | ... |
app/views/blocks/profile_info_actions/_common.html.erb
1 | 1 | <li><%= report_abuse(profile, :button) %></li> |
2 | +<%if logged_in? && (user != profile) && profile.allow_followers?%> | |
3 | +<li> | |
4 | + <% follow = user.follows?(profile) %> | |
5 | + <%= button(:unfollow, content_tag('span', _('Unfollow')), {:profile => profile.identifier, :controller => 'profile', :action => 'unfollow'}, :id => 'action-unfollow', :title => _("Unfollow"), :style => follow ? "" : "display: none;") %> | |
6 | + <%= button(:ok, content_tag('span', _('Follow')), {:profile => profile.identifier, :controller => 'profile', :action => 'find_profile_circles'}, :id => 'action-follow', :title => _("Follow"), :style => follow ? "display: none;" : "") %> | |
7 | + <div id="circles-container" style="display: none;"> | |
8 | + </div> | |
9 | +</li> | |
10 | +<%end%> | |
2 | 11 | <%= render_environment_features(:profile_actions) %> | ... | ... |
app/views/blocks/profile_info_actions/_select_circles.html.erb
0 → 100644
... | ... | @@ -0,0 +1,24 @@ |
1 | +<div class="circles" id='circles-list'> | |
2 | + <p><%= _("Select the circles for %s") % profile.name %></p> | |
3 | + <div id="circles-checkboxes"> | |
4 | + <% circles.each do |circle| %> | |
5 | + <div class="circle"> | |
6 | + <%= labelled_check_box circle.name, "circles[#{circle.name}]", circle.id, profile.in_circle?(circle, current_person) %> | |
7 | + </div> | |
8 | + <% end %> | |
9 | + </div> | |
10 | + | |
11 | + <a href="#" id="new-circle"> | |
12 | + <span><%= _("New Circle") %></span> | |
13 | + </a> | |
14 | + | |
15 | + <div id="new-circle-form" style="display: none;"> | |
16 | + <%= labelled_text_field _('Circle name') , 'circle[name]', "",:id => 'text-field-name-new-circle'%> | |
17 | + <%= hidden_field_tag('circle[profile_type]', profile.class.name) %> | |
18 | + | |
19 | + <%= button_bar do %> | |
20 | + <%= button(:save, _('Create'), {:profile => profile.identifier, :controller => 'circles', :action => 'xhr_create'}, :id => "new-circle-submit") %> | |
21 | + <%= button(:cancel, _('Cancel'), '#', :id => "new-circle-cancel") %> | |
22 | + <% end %> | |
23 | + </div> | |
24 | +</div> | ... | ... |
... | ... | @@ -0,0 +1,14 @@ |
1 | +<%= error_messages_for :circle %> | |
2 | + | |
3 | +<%= labelled_form_for :circle, :url => (mode == :edit) ? {:action => 'update', :id => circle} : {:action => 'create'} do |f| %> | |
4 | + | |
5 | + <%= required_fields_message %> | |
6 | + | |
7 | + <%= required f.text_field(:name) %> | |
8 | + | |
9 | + <%= required labelled_form_field _("Profile type"), f.select(:profile_type, Circle.profile_types.to_a) %> | |
10 | + | |
11 | + <%= button_bar do %> | |
12 | + <%= submit_button('save', (mode == :edit) ? _('Save changes') : _('Create circle'), :cancel => {:action => 'index'} ) %> | |
13 | + <% end %> | |
14 | +<% end %> | ... | ... |
... | ... | @@ -0,0 +1,30 @@ |
1 | +<h1><%= _('Manage circles') %></h1> | |
2 | + | |
3 | +<table> | |
4 | + <tr> | |
5 | + <th><%= _('Circle name') %></th> | |
6 | + <th><%= _('Profile type') %></th> | |
7 | + <th><%= _('Actions') %></th> | |
8 | + </tr> | |
9 | + <% @circles.each do |circle| %> | |
10 | + <tr> | |
11 | + <td> | |
12 | + <%= circle.name %> | |
13 | + </td> | |
14 | + <td> | |
15 | + <%= _(circle.profile_type) %> | |
16 | + </td> | |
17 | + <td> | |
18 | + <div style="text-align: center;"> | |
19 | + <%= button_without_text :edit, _('Edit'), :action => 'edit', :id => circle %> | |
20 | + <%= button_without_text :delete, _('Delete'), { :action => 'destroy', :id => circle }, { "data-method" => "POST" } %> | |
21 | + </div> | |
22 | + </td> | |
23 | + </tr> | |
24 | + <% end %> | |
25 | +</table> | |
26 | + | |
27 | +<%= button_bar do %> | |
28 | + <%= button :add, _('Create a new circle'), :action => 'new' %> | |
29 | + <%= button :back, _('Back to control panel'), :controller => 'profile_editor' %> | |
30 | +<% end %> | ... | ... |
... | ... | @@ -0,0 +1,14 @@ |
1 | +<div class="circles" id='circles-list'> | |
2 | + <%= form_for :circles, :url => {:controller => 'followers', :action => 'update_category'}, :html => {:id => "follow-circles-form"} do |f|%> | |
3 | + <%= render partial: "blocks/profile_info_actions/select_circles", :locals => {:circles => circles, :profile => profile} %> | |
4 | + | |
5 | + <%= hidden_field_tag('followed_profile_id', profile.id) %> | |
6 | + | |
7 | + <div id="circle-actions"> | |
8 | + <div id="actions-container"> | |
9 | + <%= submit_button('save', _('Save')) %> | |
10 | + <%= modal_close_button _("Cancel") %> | |
11 | + </div> | |
12 | + </div> | |
13 | + <% end %> | |
14 | +</div> | ... | ... |
... | ... | @@ -0,0 +1,16 @@ |
1 | +<ul class="profile-list"> | |
2 | + <% profiles.each do |profile| %> | |
3 | + <li> | |
4 | + <%= link_to_profile profile_image(profile) + tag('br') + profile.short_name, | |
5 | + profile.identifier, :class => 'profile-link' %> | |
6 | + <div class="controll"> | |
7 | + <%= button_without_text :remove, content_tag('span',_('unfollow')), | |
8 | + { :controller => "profile", :profile => profile.identifier , :action => 'unfollow', :redirect_to => url_for({:controller => "followers", :profile => user.identifier}) }, | |
9 | + :title => _('remove') %> | |
10 | + <%= modal_icon_button :edit, _('change category'), | |
11 | + url_for(:controller => 'followers', :action => 'set_category_modal', | |
12 | + :followed_profile_id => profile.id) %> | |
13 | + </div><!-- end class="controll" --> | |
14 | + </li> | |
15 | + <% end %> | |
16 | +</ul> | ... | ... |
... | ... | @@ -0,0 +1,27 @@ |
1 | +<div id="manage_followed people"> | |
2 | + | |
3 | +<h1><%= _("%s following") % profile.name %></h1> | |
4 | + | |
5 | +<% cache_timeout(profile.manage_friends_cache_key(params), 4.hours) do %> | |
6 | + <% if @followed_people.empty? %> | |
7 | + <p> | |
8 | + <em> | |
9 | + <%= _("You don't follow anybody yet.") %> | |
10 | + </em> | |
11 | + </p> | |
12 | + <% end %> | |
13 | + | |
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 | + <% end %> | |
18 | + | |
19 | + <%= labelled_select(_('Profile type')+': ', :filter_profile_type, :last, :first, @active_filter, @profile_types, :id => "profile-type-filter") %> | |
20 | + | |
21 | + <%= render :partial => 'profile_list', :locals => { :profiles => @followed_people } %> | |
22 | + | |
23 | + <br style="clear:both" /> | |
24 | + <%= pagination_links @followed_people, :param_name => 'npage' %> | |
25 | +<% end %> | |
26 | + | |
27 | +</div> | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +<%= render :partial => 'default_activity', :locals => { :activity => activity } %> | ... | ... |
... | ... | @@ -0,0 +1,18 @@ |
1 | +<% cache_timeout(profile.friends_cache_key(params), 4.hours) do %> | |
2 | + <ul class='profile-list'> | |
3 | + <% follow.each do |follower| %> | |
4 | + <%= profile_image_link(follower) %> | |
5 | + <% end%> | |
6 | + </ul> | |
7 | + | |
8 | + <div id='pagination-profiles'> | |
9 | + <%= pagination_links follow, :param_name => 'npage' %> | |
10 | + </div> | |
11 | +<% end %> | |
12 | + | |
13 | +<%= button_bar do %> | |
14 | + <%= button :back, _('Go back'), { :controller => 'profile' } %> | |
15 | + <% if user == profile %> | |
16 | + <%= button :edit, _('Manage followed people'), :controller => 'friends', :action => 'index', :profile => profile.identifier %> | |
17 | + <% end %> | |
18 | +<% end %> | ... | ... |
... | ... | @@ -0,0 +1 @@ |
1 | +<%= render :partial => 'default_activity', :locals => { :activity => activity, :tab_action => tab_action } %> | ... | ... |
app/views/profile_editor/edit.html.erb
... | ... | @@ -23,9 +23,11 @@ |
23 | 23 | </div> |
24 | 24 | |
25 | 25 | <h2><%= _('Privacy options') %></h2> |
26 | - | |
26 | + <div> | |
27 | + <%= labelled_check_box _("Allow other users to follow me"), 'profile_data[allow_followers]', true, @profile.allow_followers?, :class => "person-can-be-followed" %> | |
28 | + </div> | |
27 | 29 | <% if profile.person? %> |
28 | - <div> | |
30 | + <div id="profile_allow_follows"> | |
29 | 31 | <%= labelled_radio_button _('Public — show my contents to all internet users').html_safe, 'profile_data[public_profile]', true, @profile.public_profile? %> |
30 | 32 | </div> |
31 | 33 | <div> | ... | ... |
app/views/profile_editor/index.html.erb
... | ... | @@ -72,6 +72,11 @@ |
72 | 72 | |
73 | 73 | <%= control_panel_button(_('Email Templates'), 'email-templates', :controller => :profile_email_templates) if profile.organization? %> |
74 | 74 | |
75 | + <% if profile.person? %> | |
76 | + <%= control_panel_button(_('Manage followed profiles'), 'edit-profile', :controller => :followers) %> | |
77 | + <%= control_panel_button(_('Manage circles'), 'edit-profile-group', :controller => :circles) %> | |
78 | + <% end %> | |
79 | + | |
75 | 80 | <% @plugins.dispatch(:control_panel_buttons).each do |button| %> |
76 | 81 | <%= control_panel_button(button[:title], button[:icon], button[:url], button[:html_options]) %> |
77 | 82 | <% end %> | ... | ... |
config/initializers/action_tracker.rb
db/migrate/20160608123748_create_profile_followers_table.rb
0 → 100644
... | ... | @@ -0,0 +1,42 @@ |
1 | +class CreateProfileFollowersTable < ActiveRecord::Migration | |
2 | + def up | |
3 | + create_table :profiles_circles do |t| | |
4 | + t.column :profile_id, :integer | |
5 | + t.column :circle_id, :integer | |
6 | + t.timestamps | |
7 | + end | |
8 | + | |
9 | + create_table :circles do |t| | |
10 | + t.column :name, :string | |
11 | + t.belongs_to :person | |
12 | + t.column :profile_type, :string, :null => false | |
13 | + end | |
14 | + | |
15 | + add_foreign_key :profiles_circles, :circles, :on_delete => :nullify | |
16 | + | |
17 | + add_index :profiles_circles, [:profile_id, :circle_id], :name => "profiles_circles_composite_key_index", :unique => true | |
18 | + add_index :circles, [:person_id, :name], :name => "circles_composite_key_index", :unique => true | |
19 | + | |
20 | + #insert one category for each friend group a person has | |
21 | + execute("INSERT INTO circles(name, person_id, profile_type) SELECT DISTINCT (CASE WHEN (f.group IS NULL OR f.group = '') THEN 'friendships' ELSE f.group END), f.person_id, 'Person' FROM friendships as f") | |
22 | + #insert 'memberships' category if a person is in a community as a member, moderator or profile admin | |
23 | + execute("INSERT INTO circles(name, person_id, profile_type) SELECT DISTINCT 'memberships', ra.accessor_id, 'Community' FROM role_assignments as ra JOIN roles ON ra.role_id = roles.id WHERE roles.name IN ('Member','Moderator','Profile Administrator')") | |
24 | + #insert 'favorites' category if a person has any favorited enterprise | |
25 | + execute("INSERT INTO circles(name, person_id, profile_type) SELECT DISTINCT 'favorites', person_id, 'Enterprise' FROM favorite_enterprise_people") | |
26 | + | |
27 | + #insert a follower entry for each friend, with the category the same as the friendship group or equals 'friendships' | |
28 | + execute("INSERT INTO profiles_circles(profile_id, circle_id) SELECT DISTINCT f.friend_id, c.id FROM friendships as f JOIN circles as c ON f.person_id = c.person_id WHERE c.name = f.group OR c.name = 'friendships'") | |
29 | + #insert a follower entry for each favorited enterprise, with the category 'favorites' | |
30 | + execute("INSERT INTO profiles_circles(profile_id, circle_id) SELECT DISTINCT f.enterprise_id, c.id FROM favorite_enterprise_people AS f JOIN circles as c ON f.person_id = c.person_id WHERE c.name = 'favorites' ") | |
31 | + #insert a follower entry for each community a person participates as a member, moderator or admininstrator | |
32 | + execute("INSERT INTO profiles_circles(profile_id, circle_id) SELECT DISTINCT ra.resource_id, c.id FROM role_assignments as ra JOIN roles ON ra.role_id = roles.id JOIN circles as c ON ra.accessor_id = c.person_id WHERE roles.name IN ('Member','Moderator','Profile Administrator') AND c.name = 'memberships'") | |
33 | + end | |
34 | + | |
35 | + def down | |
36 | + remove_foreign_key :profiles_circles, :circles | |
37 | + remove_index :profiles_circles, :name => "profiles_circles_composite_key_index" | |
38 | + remove_index :circles, :name => "circles_composite_key_index" | |
39 | + drop_table :circles | |
40 | + drop_table :profiles_circles | |
41 | + end | |
42 | +end | ... | ... |
... | ... | @@ -0,0 +1,114 @@ |
1 | +Feature: follow profile | |
2 | + As a noosfero user | |
3 | + I want to follow a profile | |
4 | + So I can receive notifications from it | |
5 | + | |
6 | + Background: | |
7 | + Given the following community | |
8 | + | identifier | name | | |
9 | + | nightswatch | Nights Watch | | |
10 | + And the following users | |
11 | + | login | | |
12 | + | johnsnow | | |
13 | + And the user "johnsnow" has the following circles | |
14 | + | name | profile_type | | |
15 | + | Family | Person | | |
16 | + | Work | Community | | |
17 | + | Favorites | Community | | |
18 | + | |
19 | + @selenium | |
20 | + Scenario: Common noofero user follow a community | |
21 | + Given I am logged in as "johnsnow" | |
22 | + When I go to nightswatch's homepage | |
23 | + When I follow "Follow" | |
24 | + When I check "Work" | |
25 | + When I press "Follow" | |
26 | + And I wait 1 second | |
27 | + Then "johnsnow" should be a follower of "nightswatch" in circle "Work" | |
28 | + | |
29 | + @selenium | |
30 | + Scenario: Common noofero user follow a community in more than one circle | |
31 | + Given I am logged in as "johnsnow" | |
32 | + When I go to nightswatch's homepage | |
33 | + When I follow "Follow" | |
34 | + When I check "Work" | |
35 | + When I check "Favorites" | |
36 | + When I press "Follow" | |
37 | + And I wait 1 second | |
38 | + Then "johnsnow" should be a follower of "nightswatch" in circle "Work" | |
39 | + And "johnsnow" should be a follower of "nightswatch" in circle "Favorites" | |
40 | + | |
41 | + @selenium | |
42 | + Scenario: No see another profile type circle when following a community | |
43 | + Given I am logged in as "johnsnow" | |
44 | + When I go to nightswatch's homepage | |
45 | + When I follow "Follow" | |
46 | + Then I should not see "Family" | |
47 | + And I should see "Favorites" | |
48 | + And I should see "Work" | |
49 | + | |
50 | + @selenium | |
51 | + Scenario: Common noofero user follow a community then cancel the action | |
52 | + Given I am logged in as "johnsnow" | |
53 | + When I go to nightswatch's homepage | |
54 | + When I follow "Follow" | |
55 | + When I press "Cancel" | |
56 | + And I wait 1 second | |
57 | + Then I should not see "Family" | |
58 | + And I should not see "Favorites" | |
59 | + And I should not see "Work" | |
60 | + And I should not see "New Circle" | |
61 | + Then "johnsnow" should not be a follower of "nightswatch" | |
62 | + | |
63 | + @selenium | |
64 | + Scenario: Common noofero user cancel the circle creation action | |
65 | + Given I am logged in as "johnsnow" | |
66 | + When I go to nightswatch's homepage | |
67 | + When I follow "Follow" | |
68 | + When I follow "New Circle" | |
69 | + When I press "Cancel" | |
70 | + And I wait 1 second | |
71 | + Then I should not see "Circle name" | |
72 | + And I should not see "Create" | |
73 | + | |
74 | + @selenium | |
75 | + Scenario: Noosfero user see new circle option when following a community | |
76 | + Given I am logged in as "johnsnow" | |
77 | + When I go to nightswatch's homepage | |
78 | + When I follow "Follow" | |
79 | + Then I should see "New Circle" | |
80 | + | |
81 | + @selenium | |
82 | + Scenario: Common noofero user follow a community with a new circle | |
83 | + Given I am logged in as "johnsnow" | |
84 | + When I go to nightswatch's homepage | |
85 | + When I follow "Follow" | |
86 | + When I follow "New Circle" | |
87 | + And I fill in "text-field-name-new-circle" with "Winterfell" | |
88 | + When I follow "Create" | |
89 | + When I check "Winterfell" | |
90 | + When I press "Follow" | |
91 | + And I wait 1 second | |
92 | + Then "johnsnow" should be a follower of "nightswatch" in circle "Winterfell" | |
93 | + | |
94 | + @selenium | |
95 | + Scenario: Common noofero user create a new circle when following a community | |
96 | + Given I am logged in as "johnsnow" | |
97 | + When I go to nightswatch's homepage | |
98 | + When I follow "Follow" | |
99 | + When I follow "New Circle" | |
100 | + And I fill in "text-field-name-new-circle" with "Winterfell" | |
101 | + When I follow "Create" | |
102 | + And I wait 1 second | |
103 | + Then "johnsnow" should have the circle "Winterfell" with profile type "Community" | |
104 | + Then I should not see "Circle name" | |
105 | + Then I should not see "Create" | |
106 | + | |
107 | + @selenium | |
108 | + Scenario: Common noofero user unfollow a community | |
109 | + Given "johnsnow" is a follower of "nightswatch" in circle "Work" | |
110 | + And I am logged in as "johnsnow" | |
111 | + When I go to nightswatch's homepage | |
112 | + When I follow "Unfollow" | |
113 | + Then "johnsnow" should not be a follower of "nightswatch" | |
114 | + | ... | ... |
... | ... | @@ -0,0 +1,35 @@ |
1 | +Given /^the user "(.+)" has the following circles$/ do |user_name,table| | |
2 | + person = User.find_by(:login => user_name).person | |
3 | + table.hashes.each do |circle| | |
4 | + Circle.create!(:person => person, :name => circle[:name], :profile_type => circle[:profile_type]) | |
5 | + end | |
6 | +end | |
7 | + | |
8 | +Then /^"(.+)" should be a follower of "(.+)" in circle "(.+)"$/ do |person, profile, circle| | |
9 | + profile = Profile.find_by(identifier: profile) | |
10 | + followers = profile.followers | |
11 | + person = Person.find_by(identifier: person) | |
12 | + followers.should include(person) | |
13 | + | |
14 | + circle = Circle.find_by(:name => circle, :person => person) | |
15 | + ProfileFollower.find_by(:circle => circle, :profile => profile).should_not == nil | |
16 | +end | |
17 | + | |
18 | +Then /^"(.+)" should not be a follower of "(.+)"$/ do |person, profile| | |
19 | + profile = Profile.find_by(identifier: profile) | |
20 | + followers = profile.followers | |
21 | + person = Person.find_by(identifier: person) | |
22 | + followers.should_not include(person) | |
23 | +end | |
24 | + | |
25 | +Given /^"(.+)" is a follower of "(.+)" in circle "(.+)"$/ do |person, profile, circle| | |
26 | + profile = Profile.find_by(identifier: profile) | |
27 | + person = Person.find_by(identifier: person) | |
28 | + circle = Circle.find_by(:name => circle, :person => person) | |
29 | + ProfileFollower.create!(:circle => circle, :profile => profile) | |
30 | +end | |
31 | + | |
32 | +Then /^"(.+)" should have the circle "(.+)" with profile type "(.+)"$/ do |user_name, circle, profile_type| | |
33 | + person = User.find_by(:login => user_name).person | |
34 | + Circle.find_by(:name => circle, :person => person, :profile_type => profile_type).should_not == nil | |
35 | +end | ... | ... |
public/javascripts/application.js
... | ... | @@ -26,6 +26,8 @@ |
26 | 26 | *= require pagination.js |
27 | 27 | * views speficics |
28 | 28 | *= require add-and-join.js |
29 | +*= require followers.js | |
30 | +*= require manage-followers.js | |
29 | 31 | *= require report-abuse.js |
30 | 32 | *= require autogrow.js |
31 | 33 | *= require require_login.js |
... | ... | @@ -550,6 +552,11 @@ function loading_for_button(selector) { |
550 | 552 | jQuery(selector).css('cursor', 'progress'); |
551 | 553 | } |
552 | 554 | |
555 | +function hide_loading_for_button(selector) { | |
556 | + selector.css("cursor",""); | |
557 | + $(".small-loading").remove(); | |
558 | +} | |
559 | + | |
553 | 560 | function new_qualifier_row(selector, select_qualifiers, delete_button) { |
554 | 561 | index = jQuery(selector + ' tr').size() - 1; |
555 | 562 | jQuery(selector).append("<tr><td>" + select_qualifiers + "</td><td id='certifier-area-" + index + "'><select></select>" + delete_button + "</td></tr>"); | ... | ... |
... | ... | @@ -0,0 +1,81 @@ |
1 | +$("#action-follow").live("click", function() { | |
2 | + var button = $(this); | |
3 | + var url = button.attr("href"); | |
4 | + loading_for_button(button); | |
5 | + | |
6 | + $.post(url, function(data) { | |
7 | + button.fadeOut("fast", function() { | |
8 | + $("#circles-container").html(data); | |
9 | + $("#circles-container").fadeIn(); | |
10 | + }); | |
11 | + }).always(function() { | |
12 | + hide_loading_for_button(button); | |
13 | + }); | |
14 | + return false; | |
15 | +}); | |
16 | + | |
17 | +$("#cancel-set-circle").live("click", function() { | |
18 | + $("#circles-container").fadeOut("fast", function() { | |
19 | + $("#action-follow").fadeIn(); | |
20 | + }); | |
21 | + return false; | |
22 | +}); | |
23 | + | |
24 | +$("#new-circle").live("click", function() { | |
25 | + $(this).fadeOut(); | |
26 | + $("#circle-actions").fadeOut("fast", function() { | |
27 | + $("#new-circle-form").fadeIn(); | |
28 | + }); | |
29 | + return false; | |
30 | +}); | |
31 | + | |
32 | +$("#new-circle-cancel").live("click", function() { | |
33 | + $("#new-circle-form").fadeOut("fast", function() { | |
34 | + $("#circle-actions").fadeIn(); | |
35 | + $("#new-circle").fadeIn(); | |
36 | + $("#text-field-name-new-circle").val('') | |
37 | + }); | |
38 | + return false; | |
39 | +}); | |
40 | + | |
41 | +$('#follow-circles-form').live("submit", function() { | |
42 | + var valuesToSubmit = $(this).serialize(); | |
43 | + $.ajax({ | |
44 | + type: "POST", | |
45 | + url: $(this).attr('action'), | |
46 | + data: valuesToSubmit, | |
47 | + dataType: "JSON", | |
48 | + statusCode: { | |
49 | + 200: function(response){ | |
50 | + $("#circles-container").fadeOut(); | |
51 | + $("#action-unfollow").fadeIn(); | |
52 | + $.colorbox.close(); | |
53 | + display_notice(response.responseText); | |
54 | + }, | |
55 | + 400: function(response) { | |
56 | + display_notice(response.responseText); | |
57 | + } | |
58 | + } | |
59 | + }) | |
60 | + return false; | |
61 | +}); | |
62 | + | |
63 | +$("#new-circle-submit").live("click", function() { | |
64 | + $.ajax({ | |
65 | + method: 'POST', | |
66 | + url: $(this).attr("href"), | |
67 | + data: {'circle[name]': $("#text-field-name-new-circle").val(), | |
68 | + 'circle[profile_type]': $("#circle_profile_type").val()}, | |
69 | + success: function(response) { | |
70 | + $('#circles-checkboxes').append(response); | |
71 | + }, | |
72 | + error: function(response) { | |
73 | + display_notice(response.responseText); | |
74 | + }, | |
75 | + complete: function(response) { | |
76 | + $("#text-field-name-new-circle").val('') | |
77 | + $("#new-circle-cancel").trigger("click"); | |
78 | + } | |
79 | + }) | |
80 | + return false; | |
81 | +}); | ... | ... |
... | ... | @@ -0,0 +1,9 @@ |
1 | +$('#profile-type-filter').live('change', function() { | |
2 | + var filter_type = $(this).val(); | |
3 | + $(".profile-list").addClass("fetching"); | |
4 | + $.get(window.location.pathname, {filter: filter_type}, function(data) { | |
5 | + $(".main-content").html(data); | |
6 | + }).fail(function(data) { | |
7 | + $(".profile-list").removeClass("fetching"); | |
8 | + }); | |
9 | +}); | ... | ... |
public/stylesheets/blocks/profile-info.scss
... | ... | @@ -99,3 +99,44 @@ |
99 | 99 | margin: 0px 0px 5px 0px; |
100 | 100 | padding: 2px; |
101 | 101 | } |
102 | +#circles-container { | |
103 | + background-color: #eee; | |
104 | + padding: 5px; | |
105 | + display: flex; | |
106 | +} | |
107 | +#circles-container p { | |
108 | + font-size: 12px; | |
109 | + margin-bottom: 5px; | |
110 | +} | |
111 | +#circle-actions { | |
112 | + margin-top: 15px; | |
113 | +} | |
114 | +#new-category-field-actions-block { | |
115 | + float: left; | |
116 | + width: 80%; | |
117 | + margin-bottom: 10px; | |
118 | +} | |
119 | +#new-circle-form { | |
120 | + margin-top: 10px; | |
121 | +} | |
122 | +#new-circle-form input { | |
123 | + width: 90px; | |
124 | +} | |
125 | +#new-circle-form select { | |
126 | + margin-top: 2px; | |
127 | + width: 95px; | |
128 | +} | |
129 | +#new-circle-form label { | |
130 | + font-size: 10px; | |
131 | + margin-right: 5px; | |
132 | +} | |
133 | +#new-circle-form .button-bar { | |
134 | + padding-top: 0px; | |
135 | +} | |
136 | +#new-circle-form .button { | |
137 | + width: 60px; | |
138 | +} | |
139 | +#new-circle-form .button-bar .button { | |
140 | + width: 40px; | |
141 | + font-size: 10px; | |
142 | +} | ... | ... |
public/stylesheets/profile-activity.scss
... | ... | @@ -167,7 +167,9 @@ li.profile-activity-item.upload_image .activity-gallery-images-count-1 img { |
167 | 167 | |
168 | 168 | #profile-wall li.profile-activity-item.join_community .profile-activity-text a img, |
169 | 169 | #profile-wall li.profile-activity-item.new_friendship .profile-activity-text a img, |
170 | +#profile-wall li.profile-activity-item.new_follower .profile-activity-text a img, | |
170 | 171 | #profile-network li.profile-activity-item.join_community .profile-activity-text a img, |
172 | +#profile-network li.profile-activity-item.new_follower .profile-activity-text a img, | |
171 | 173 | #profile-network li.profile-activity-item.new_friendship .profile-activity-text a img { |
172 | 174 | margin: 5px 5px 0 0; |
173 | 175 | padding: 1px; | ... | ... |
public/stylesheets/profile-editor.scss
public/stylesheets/profile-list.scss
... | ... | @@ -23,6 +23,7 @@ |
23 | 23 | } |
24 | 24 | .controller-favorite_enterprises .profile-list a.profile-link, |
25 | 25 | .controller-friends .profile-list a.profile-link, |
26 | +.controller-followers .profile-list a.profile-link, | |
26 | 27 | .list-profile-connections .profile-list a.profile-link, |
27 | 28 | .profiles-suggestions .profile-list a.profile-link { |
28 | 29 | text-decoration: none; |
... | ... | @@ -32,11 +33,13 @@ |
32 | 33 | } |
33 | 34 | .controller-favorite_enterprises .profile-list a.profile-link:hover, |
34 | 35 | .controller-friends .profile-list a.profile-link:hover, |
36 | +.controller-followers .profile-list a.profile-link:hover, | |
35 | 37 | .profiles-suggestions .profile-list a.profile-link:hover { |
36 | 38 | color: #FFF; |
37 | 39 | } |
38 | 40 | .controller-favorite_enterprises .profile-list .profile_link span, |
39 | 41 | .controller-friends .profile-list .profile_link span, |
42 | +.controller-followers .profile-list .profile_link span, | |
40 | 43 | .box-1 .profiles-suggestions .profile-list .profile_link span { |
41 | 44 | width: 80px; |
42 | 45 | display: block; |
... | ... | @@ -44,12 +47,14 @@ |
44 | 47 | } |
45 | 48 | .controller-favorite_enterprises .profile-list, |
46 | 49 | .controller-friends .profile-list, |
50 | +.controller-followers .profile-list, | |
47 | 51 | .profiles-suggestions .profile-list { |
48 | 52 | position: relative; |
49 | 53 | } |
50 | 54 | |
51 | 55 | .controller-favorite_enterprises .profile-list .controll, |
52 | 56 | .controller-friends .profile-list .controll, |
57 | +.controller-followers .profile-list .controll, | |
53 | 58 | .profiles-suggestions .profile-list .controll { |
54 | 59 | position: absolute; |
55 | 60 | top: 7px; |
... | ... | @@ -57,17 +62,20 @@ |
57 | 62 | } |
58 | 63 | .controller-favorite_enterprises .profile-list .controll a, |
59 | 64 | .controller-friends .profile-list .controll a, |
65 | +.controller-followers .profile-list .controll a, | |
60 | 66 | .profiles-suggestions .profile-list .controll a { |
61 | 67 | display: block; |
62 | 68 | margin-bottom: 2px; |
63 | 69 | } |
64 | 70 | .controller-favorite_enterprises .msie6 .profile-list .controll a, |
65 | 71 | .controller-friends .msie6 .profile-list .controll a, |
72 | +.controller-folloed_people .msie6 .profile-list .controll a, | |
66 | 73 | .profiles-suggestions .msie6 .profile-list .controll a { |
67 | 74 | width: 0px; |
68 | 75 | } |
69 | 76 | .controller-favorite_enterprises .button-bar, |
70 | 77 | .controller-friends .button-bar, |
78 | +.controller-followers .button-bar, | |
71 | 79 | .profiles-suggestions .button-bar { |
72 | 80 | clear: both; |
73 | 81 | padding-top: 20px; |
... | ... | @@ -208,22 +216,35 @@ |
208 | 216 | font-size: 12px; |
209 | 217 | } |
210 | 218 | .action-profile-members .profile_link{ |
211 | - position: relative; | |
219 | + position: relative; | |
212 | 220 | } |
213 | 221 | .action-profile-members .profile_link span.new-profile:last-child{ |
214 | - position: absolute; | |
215 | - top: 3px; | |
216 | - right: 2px; | |
217 | - text-transform: uppercase; | |
218 | - color: #FFF; | |
219 | - font-size: 9px; | |
220 | - background: #66CC33; | |
221 | - padding: 2px; | |
222 | - display: block; | |
223 | - width: 35px; | |
224 | - font-weight: 700; | |
222 | + position: absolute; | |
223 | + top: 3px; | |
224 | + right: 2px; | |
225 | + text-transform: uppercase; | |
226 | + color: #FFF; | |
227 | + font-size: 9px; | |
228 | + background: #66CC33; | |
229 | + padding: 2px; | |
230 | + display: block; | |
231 | + width: 35px; | |
232 | + font-weight: 700; | |
225 | 233 | } |
226 | 234 | .action-profile-members .profile_link .fn{ |
227 | - font-style: normal; | |
228 | - color: #000; | |
235 | + font-style: normal; | |
236 | + color: #000; | |
237 | +} | |
238 | +.category-name { | |
239 | + margin-top: 0px; | |
240 | + margin-bottom: 0px; | |
241 | + font-style: italic; | |
242 | + color: #888a85; | |
243 | + text-align: center; | |
244 | +} | |
245 | +.set-category-modal { | |
246 | + width: 250px; | |
247 | +} | |
248 | +.set-category-modal #actions-container { | |
249 | + margin-top: 20px | |
229 | 250 | } | ... | ... |
public/stylesheets/profile.scss
... | ... | @@ -0,0 +1,129 @@ |
1 | +require_relative "../test_helper" | |
2 | +require 'circles_controller' | |
3 | + | |
4 | +class CirclesControllerTest < ActionController::TestCase | |
5 | + | |
6 | + def setup | |
7 | + @controller = CirclesController.new | |
8 | + @person = create_user('person').person | |
9 | + login_as(@person.identifier) | |
10 | + end | |
11 | + | |
12 | + should 'return all circles of a profile' do | |
13 | + circle1 = Circle.create!(:name => "circle1", :person => @person, :profile_type => 'Person') | |
14 | + circle2 = Circle.create!(:name => "circle2", :person => @person, :profile_type => 'Person') | |
15 | + get :index, :profile => @person.identifier | |
16 | + | |
17 | + assert_equivalent [circle1, circle2], assigns[:circles] | |
18 | + end | |
19 | + | |
20 | + should 'initialize an empty circle for creation' do | |
21 | + get :new, :profile => @person.identifier | |
22 | + assert_nil assigns[:circle].id | |
23 | + assert_nil assigns[:circle].name | |
24 | + end | |
25 | + | |
26 | + should 'create a new circle' do | |
27 | + assert_difference '@person.circles.count' do | |
28 | + post :create, :profile => @person.identifier, | |
29 | + :circle => { :name => 'circle' , :profile_type => Person.name} | |
30 | + end | |
31 | + assert_redirected_to :action => :index | |
32 | + end | |
33 | + | |
34 | + should 'not create a circle without a name' do | |
35 | + assert_no_difference '@person.circles.count' do | |
36 | + post :create, :profile => @person.identifier, :circle => { :name => nil } | |
37 | + end | |
38 | + assert_template :new | |
39 | + end | |
40 | + | |
41 | + should 'retrieve an existing circle when editing' do | |
42 | + circle = Circle.create!(:name => "circle", :person => @person, :profile_type => 'Person') | |
43 | + get :edit, :profile => @person.identifier, :id => circle.id | |
44 | + assert_equal circle.name, assigns[:circle].name | |
45 | + end | |
46 | + | |
47 | + should 'return 404 when editing a circle that does not exist' do | |
48 | + get :edit, :profile => @person.identifier, :id => "nope" | |
49 | + assert_response 404 | |
50 | + end | |
51 | + | |
52 | + should 'update an existing circle' do | |
53 | + circle = Circle.create!(:name => "circle", :person => @person, :profile_type => 'Person') | |
54 | + post :update, :profile => @person.identifier, :id => circle.id, | |
55 | + :circle => { :name => "new name" } | |
56 | + | |
57 | + circle.reload | |
58 | + assert_equal "new name", circle.name | |
59 | + assert_redirected_to :action => :index | |
60 | + end | |
61 | + | |
62 | + should 'not update an existing circle without a name' do | |
63 | + circle = Circle.create!(:name => "circle", :person => @person, :profile_type => 'Person') | |
64 | + post :update, :profile => @person.identifier, :id => circle.id, | |
65 | + :circle => { :name => nil } | |
66 | + | |
67 | + circle.reload | |
68 | + assert_equal "circle", circle.name | |
69 | + assert_template :edit | |
70 | + end | |
71 | + | |
72 | + should 'return 404 when updating a circle that does not exist' do | |
73 | + post :update, :profile => @person.identifier, :id => "nope", :name => "new name" | |
74 | + assert_response 404 | |
75 | + end | |
76 | + | |
77 | + should 'destroy an existing circle and update related profiles' do | |
78 | + circle = Circle.create!(:name => "circle", :person => @person, :profile_type => 'Person') | |
79 | + follower = fast_create(ProfileFollower, :profile_id => fast_create(Person).id, | |
80 | + :circle_id => circle.id) | |
81 | + | |
82 | + assert_difference "@person.circles.count", -1 do | |
83 | + post :destroy, :profile => @person.identifier, :id => circle.id | |
84 | + end | |
85 | + | |
86 | + follower.reload | |
87 | + assert_nil follower.circle | |
88 | + end | |
89 | + | |
90 | + should 'not destroy an existing circle if action is not post' do | |
91 | + circle = Circle.create!(:name => "circle", :person => @person, :profile_type => 'Person') | |
92 | + | |
93 | + assert_no_difference "@person.circles.count" do | |
94 | + get :destroy, :profile => @person.identifier, :id => circle.id | |
95 | + end | |
96 | + assert_response 404 | |
97 | + end | |
98 | + | |
99 | + should 'return 404 when deleting and circle that does not exist' do | |
100 | + get :destroy, :profile => @person.identifier, :id => "nope" | |
101 | + assert_response 404 | |
102 | + end | |
103 | + | |
104 | + should 'return 404 for xhr_create if request is not xhr' do | |
105 | + post :xhr_create, :profile => @person.identifier | |
106 | + assert_response 404 | |
107 | + end | |
108 | + | |
109 | + should 'return 400 if not possible to create circle via xhr' do | |
110 | + xhr :post, :xhr_create, :profile => @person.identifier, | |
111 | + :circle => { :name => 'Invalid Circle' } | |
112 | + assert_response 400 | |
113 | + end | |
114 | + | |
115 | + should 'create a new circle via xhr' do | |
116 | + xhr :post, :xhr_create, :profile => @person.identifier, | |
117 | + :circle => { :name => 'A Brand New Circle', | |
118 | + :profile_type => Person.name } | |
119 | + assert_response 201 | |
120 | + assert_match /A Brand New Circle/, response.body | |
121 | + end | |
122 | + | |
123 | + should 'not create a new circle via xhr with an invalid profile_type' do | |
124 | + xhr :post, :xhr_create, :profile => @person.identifier, | |
125 | + :circle => { :name => 'A Brand New Circle', | |
126 | + :profile_type => '__invalid__' } | |
127 | + assert_response 400 | |
128 | + end | |
129 | +end | ... | ... |
... | ... | @@ -0,0 +1,58 @@ |
1 | +require_relative "../test_helper" | |
2 | +require 'followers_controller' | |
3 | + | |
4 | +class FollowersControllerTest < ActionController::TestCase | |
5 | + def setup | |
6 | + @profile = create_user('testuser').person | |
7 | + end | |
8 | + | |
9 | + should 'return followed people list' do | |
10 | + login_as(@profile.identifier) | |
11 | + person = fast_create(Person) | |
12 | + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person') | |
13 | + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle.id) | |
14 | + | |
15 | + get :index, :profile => @profile.identifier | |
16 | + assert_includes assigns(:followed_people), person | |
17 | + end | |
18 | + | |
19 | + should 'return filtered followed people list' do | |
20 | + login_as(@profile.identifier) | |
21 | + person = fast_create(Person) | |
22 | + community = fast_create(Community) | |
23 | + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person') | |
24 | + circle2 = Circle.create!(:person=> @profile, :name => "Teams", :profile_type => 'Community') | |
25 | + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle.id) | |
26 | + fast_create(ProfileFollower, :profile_id => community.id, :circle_id => circle2.id) | |
27 | + | |
28 | + get :index, :profile => @profile.identifier, :filter => "Community" | |
29 | + assert_equal assigns(:followed_people), [community] | |
30 | + | |
31 | + get :index, :profile => @profile.identifier, :filter => "Person" | |
32 | + assert_equal assigns(:followed_people), [person] | |
33 | + end | |
34 | + | |
35 | + should 'redirect to login page if not logged in' do | |
36 | + get :index, :profile => @profile.identifier | |
37 | + assert_redirected_to :controller => 'account', :action => 'login' | |
38 | + end | |
39 | + | |
40 | + should 'render set category modal' do | |
41 | + login_as(@profile.identifier) | |
42 | + person = fast_create(Person) | |
43 | + get :set_category_modal, :profile => @profile.identifier, :followed_profile_id => person.id | |
44 | + assert_tag :tag => "input", :attributes => { :id => "followed_profile_id", :value => person.id } | |
45 | + end | |
46 | + | |
47 | + should 'update followed person category' do | |
48 | + login_as(@profile.identifier) | |
49 | + person = fast_create(Person) | |
50 | + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person') | |
51 | + circle2 = Circle.create!(:person=> @profile, :name => "DotA", :profile_type => 'Person') | |
52 | + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle.id) | |
53 | + | |
54 | + post :update_category, :profile => @profile.identifier, :circles => {"DotA"=> circle2.id}, :followed_profile_id => person.id | |
55 | + assert_equivalent ProfileFollower.with_profile(person).with_follower(@profile).map(&:circle), [circle2] | |
56 | + end | |
57 | + | |
58 | +end | ... | ... |
test/functional/profile_controller_test.rb
... | ... | @@ -771,12 +771,15 @@ class ProfileControllerTest < ActionController::TestCase |
771 | 771 | assert_equal 15, assigns(:activities).size |
772 | 772 | end |
773 | 773 | |
774 | - should 'not see the friends activities in the current profile' do | |
774 | + should 'not see the followers activities in the current profile' do | |
775 | + circle = Circle.create!(:person=> profile, :name => "Zombies", :profile_type => 'Person') | |
776 | + | |
775 | 777 | p2 = create_user.person |
776 | - refute profile.is_a_friend?(p2) | |
778 | + refute profile.follows?(p2) | |
777 | 779 | p3 = create_user.person |
778 | - p3.add_friend(profile) | |
779 | - assert p3.is_a_friend?(profile) | |
780 | + profile.follow(p3, circle) | |
781 | + assert profile.follows?(p3) | |
782 | + | |
780 | 783 | ActionTracker::Record.destroy_all |
781 | 784 | |
782 | 785 | scrap1 = create(Scrap, defaults_for_scrap(:sender => p2, :receiver => p3)) |
... | ... | @@ -964,7 +967,11 @@ class ProfileControllerTest < ActionController::TestCase |
964 | 967 | should 'have activities defined if logged in and is following profile' do |
965 | 968 | login_as(profile.identifier) |
966 | 969 | p1= fast_create(Person) |
967 | - p1.add_friend(profile) | |
970 | + | |
971 | + circle = Circle.create!(:person=> profile, :name => "Zombies", :profile_type => 'Person') | |
972 | + | |
973 | + profile.follow(p1, circle) | |
974 | + | |
968 | 975 | ActionTracker::Record.destroy_all |
969 | 976 | get :index, :profile => p1.identifier |
970 | 977 | assert_equal [], assigns(:activities) |
... | ... | @@ -1932,4 +1939,110 @@ class ProfileControllerTest < ActionController::TestCase |
1932 | 1939 | assert_redirected_to :controller => 'account', :action => 'login' |
1933 | 1940 | end |
1934 | 1941 | |
1942 | + should 'not follow a user without defining a circle' do | |
1943 | + login_as(@profile.identifier) | |
1944 | + person = fast_create(Person) | |
1945 | + assert_no_difference 'ProfileFollower.count' do | |
1946 | + post :follow, :profile => person.identifier, :circles => {} | |
1947 | + end | |
1948 | + end | |
1949 | + | |
1950 | + should "not follow user if not logged" do | |
1951 | + person = fast_create(Person) | |
1952 | + get :follow, :profile => person.identifier | |
1953 | + | |
1954 | + assert_redirected_to :controller => 'account', :action => 'login' | |
1955 | + end | |
1956 | + | |
1957 | + should 'follow a user with a circle' do | |
1958 | + login_as(@profile.identifier) | |
1959 | + person = fast_create(Person) | |
1960 | + | |
1961 | + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person') | |
1962 | + | |
1963 | + assert_difference 'ProfileFollower.count' do | |
1964 | + post :follow, :profile => person.identifier, :circles => {"Zombies" => circle.id} | |
1965 | + end | |
1966 | + end | |
1967 | + | |
1968 | + should 'follow a user with more than one circle' do | |
1969 | + login_as(@profile.identifier) | |
1970 | + person = fast_create(Person) | |
1971 | + | |
1972 | + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person') | |
1973 | + circle2 = Circle.create!(:person=> @profile, :name => "Brainsss", :profile_type => 'Person') | |
1974 | + | |
1975 | + assert_difference 'ProfileFollower.count', 2 do | |
1976 | + post :follow, :profile => person.identifier, :circles => {"Zombies" => circle.id, "Brainsss"=> circle2.id} | |
1977 | + end | |
1978 | + end | |
1979 | + | |
1980 | + should 'not follow a user with no circle selected' do | |
1981 | + login_as(@profile.identifier) | |
1982 | + person = fast_create(Person) | |
1983 | + | |
1984 | + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person') | |
1985 | + circle2 = Circle.create!(:person=> @profile, :name => "Brainsss", :profile_type => 'Person') | |
1986 | + | |
1987 | + assert_no_difference 'ProfileFollower.count' do | |
1988 | + post :follow, :profile => person.identifier, :circles => {"Zombies" => "0", "Brainsss" => "0"} | |
1989 | + end | |
1990 | + | |
1991 | + assert_match /Select at least one circle to follow/, response.body | |
1992 | + end | |
1993 | + | |
1994 | + should 'not follow if current_person already follows the person' do | |
1995 | + login_as(@profile.identifier) | |
1996 | + person = fast_create(Person) | |
1997 | + | |
1998 | + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person') | |
1999 | + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle.id) | |
2000 | + | |
2001 | + assert_no_difference 'ProfileFollower.count' do | |
2002 | + post :follow, :profile => person.identifier, :follow => { :circles => {"Zombies" => circle.id} } | |
2003 | + end | |
2004 | + assert_response 400 | |
2005 | + end | |
2006 | + | |
2007 | + should "not unfollow user if not logged" do | |
2008 | + person = fast_create(Person) | |
2009 | + get :unfollow, :profile => person.identifier | |
2010 | + | |
2011 | + assert_redirected_to :controller => 'account', :action => 'login' | |
2012 | + end | |
2013 | + | |
2014 | + should "unfollow a followed person" do | |
2015 | + login_as(@profile.identifier) | |
2016 | + person = fast_create(Person) | |
2017 | + | |
2018 | + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person') | |
2019 | + follower = fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle.id) | |
2020 | + | |
2021 | + assert_not_nil follower | |
2022 | + | |
2023 | + get :unfollow, :profile => person.identifier | |
2024 | + follower = ProfileFollower.find_by(:profile_id => person.id, :circle_id => circle.id) | |
2025 | + assert_nil follower | |
2026 | + end | |
2027 | + | |
2028 | + should "not unfollow a not followed person" do | |
2029 | + login_as(@profile.identifier) | |
2030 | + person = fast_create(Person) | |
2031 | + | |
2032 | + assert_no_difference 'ProfileFollower.count' do | |
2033 | + get :unfollow, :profile => person.identifier | |
2034 | + end | |
2035 | + end | |
2036 | + | |
2037 | + should "redirect to page after unfollow" do | |
2038 | + login_as(@profile.identifier) | |
2039 | + person = fast_create(Person) | |
2040 | + | |
2041 | + circle = Circle.create!(:person=> @profile, :name => "Zombies", :profile_type => 'Person') | |
2042 | + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle.id) | |
2043 | + | |
2044 | + get :unfollow, :profile => person.identifier, :redirect_to => "/some/url" | |
2045 | + assert_redirected_to "/some/url" | |
2046 | + end | |
2047 | + | |
1935 | 2048 | end | ... | ... |
test/unit/article_test.rb
... | ... | @@ -1099,9 +1099,10 @@ class ArticleTest < ActiveSupport::TestCase |
1099 | 1099 | assert_equal 3, ActionTrackerNotification.where(action_tracker_id: second_activity.id).count |
1100 | 1100 | end |
1101 | 1101 | |
1102 | - should 'create notifications to friends when creating an article' do | |
1102 | + should 'create notifications to followers when creating an article' do | |
1103 | 1103 | friend = fast_create(Person) |
1104 | - profile.add_friend(friend) | |
1104 | + circle = Circle.create!(:person=> friend, :name => "Zombies", :profile_type => 'Person') | |
1105 | + friend.follow(profile, circle) | |
1105 | 1106 | Article.destroy_all |
1106 | 1107 | ActionTracker::Record.destroy_all |
1107 | 1108 | ActionTrackerNotification.destroy_all |
... | ... | @@ -1112,9 +1113,10 @@ class ArticleTest < ActiveSupport::TestCase |
1112 | 1113 | assert_equal friend, ActionTrackerNotification.last.profile |
1113 | 1114 | end |
1114 | 1115 | |
1115 | - should 'create the notification to the friend when one friend has the notification and the other no' do | |
1116 | + should 'create the notification to the follower when one follower has the notification and the other no' do | |
1116 | 1117 | f1 = fast_create(Person) |
1117 | - profile.add_friend(f1) | |
1118 | + circle = Circle.create!(:person=> f1, :name => "Zombies", :profile_type => 'Person') | |
1119 | + f1.follow(profile, circle) | |
1118 | 1120 | |
1119 | 1121 | User.current = profile.user |
1120 | 1122 | article = create TinyMceArticle, :name => 'Tracked Article 1', :profile_id => profile.id |
... | ... | @@ -1123,16 +1125,22 @@ class ArticleTest < ActiveSupport::TestCase |
1123 | 1125 | assert_equal 2, ActionTrackerNotification.where(action_tracker_id: article.activity.id).count |
1124 | 1126 | |
1125 | 1127 | f2 = fast_create(Person) |
1126 | - profile.add_friend(f2) | |
1128 | + circle2 = Circle.create!(:person=> f2, :name => "Zombies", :profile_type => 'Person') | |
1129 | + f2.follow(profile, circle2) | |
1130 | + | |
1127 | 1131 | article2 = create TinyMceArticle, :name => 'Tracked Article 2', :profile_id => profile.id |
1128 | 1132 | assert_equal 2, ActionTracker::Record.where(verb: 'create_article').count |
1129 | 1133 | process_delayed_job_queue |
1130 | 1134 | assert_equal 3, ActionTrackerNotification.where(action_tracker_id: article2.activity.id).count |
1131 | 1135 | end |
1132 | 1136 | |
1133 | - should 'destroy activity and notifications of friends when destroying an article' do | |
1137 | + should 'destroy activity and notifications of followers when destroying an article' do | |
1134 | 1138 | friend = fast_create(Person) |
1135 | - profile.add_friend(friend) | |
1139 | + | |
1140 | + circle = Circle.create!(:person=> friend, :name => "Zombies", :profile_type => 'Person') | |
1141 | + | |
1142 | + friend.follow(profile, circle) | |
1143 | + | |
1136 | 1144 | Article.destroy_all |
1137 | 1145 | ActionTracker::Record.destroy_all |
1138 | 1146 | ActionTrackerNotification.destroy_all | ... | ... |
test/unit/friendship_test.rb
... | ... | @@ -28,14 +28,14 @@ class FriendshipTest < ActiveSupport::TestCase |
28 | 28 | f.person = a |
29 | 29 | f.friend = b |
30 | 30 | f.save! |
31 | - ta = ActionTracker::Record.last | |
31 | + ta = ActionTracker::Record.where(:target_type => "Friendship").last | |
32 | 32 | assert_equal a, ta.user |
33 | 33 | assert_equal 'b', ta.get_friend_name[0] |
34 | 34 | f = Friendship.new |
35 | 35 | f.person = a |
36 | 36 | f.friend = c |
37 | 37 | f.save! |
38 | - ta = ActionTracker::Record.last | |
38 | + ta = ActionTracker::Record.where(:target_type => "Friendship").last | |
39 | 39 | assert_equal a, ta.user |
40 | 40 | assert_equal 'c', ta.get_friend_name[1] |
41 | 41 | end |
... | ... | @@ -46,14 +46,14 @@ class FriendshipTest < ActiveSupport::TestCase |
46 | 46 | f.person = a |
47 | 47 | f.friend = b |
48 | 48 | f.save! |
49 | - ta = ActionTracker::Record.last | |
49 | + ta = ActionTracker::Record.where(:target_type => "Friendship").last | |
50 | 50 | assert_equal a, ta.user |
51 | 51 | assert_equal ['b'], ta.get_friend_name |
52 | 52 | f = Friendship.new |
53 | 53 | f.person = b |
54 | 54 | f.friend = a |
55 | 55 | f.save! |
56 | - ta = ActionTracker::Record.last | |
56 | + ta = ActionTracker::Record.where(:target_type => "Friendship").last | |
57 | 57 | assert_equal b, ta.user |
58 | 58 | assert_equal ['a'], ta.get_friend_name |
59 | 59 | end |
... | ... | @@ -72,4 +72,55 @@ class FriendshipTest < ActiveSupport::TestCase |
72 | 72 | assert_not_includes p2.friends(true), p1 |
73 | 73 | end |
74 | 74 | |
75 | + should 'add follower when adding friend' do | |
76 | + p1 = create_user('testuser1').person | |
77 | + p2 = create_user('testuser2').person | |
78 | + | |
79 | + assert_difference 'ProfileFollower.count', 2 do | |
80 | + p1.add_friend(p2, 'friends') | |
81 | + p2.add_friend(p1, 'friends') | |
82 | + end | |
83 | + | |
84 | + assert_includes p1.followers(true), p2 | |
85 | + assert_includes p2.followers(true), p1 | |
86 | + end | |
87 | + | |
88 | + should 'remove follower when a friend removal occurs' do | |
89 | + p1 = create_user('testuser1').person | |
90 | + p2 = create_user('testuser2').person | |
91 | + | |
92 | + p1.add_friend(p2, 'friends') | |
93 | + p2.add_friend(p1, 'friends') | |
94 | + | |
95 | + Friendship.remove_friendship(p1, p2) | |
96 | + | |
97 | + assert_not_includes p1.followers(true), p2 | |
98 | + assert_not_includes p2.followers(true), p1 | |
99 | + end | |
100 | + | |
101 | + should 'keep friendship intact when stop following' do | |
102 | + p1 = create_user('testuser1').person | |
103 | + p2 = create_user('testuser2').person | |
104 | + | |
105 | + p1.add_friend(p2, 'friends') | |
106 | + p2.add_friend(p1, 'friends') | |
107 | + | |
108 | + p1.unfollow(p2) | |
109 | + | |
110 | + assert_includes p1.friends(true), p2 | |
111 | + assert_includes p2.friends(true), p1 | |
112 | + end | |
113 | + | |
114 | + should 'do not add friendship when start following' do | |
115 | + p1 = create_user('testuser1').person | |
116 | + p2 = create_user('testuser2').person | |
117 | + | |
118 | + circle1 = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person') | |
119 | + circle2 = Circle.create!(:person=> p2, :name => "Zombies", :profile_type => 'Person') | |
120 | + p1.follow(p2, circle1) | |
121 | + p2.follow(p1, circle2) | |
122 | + | |
123 | + assert_not_includes p1.friends(true), p2 | |
124 | + assert_not_includes p2.friends(true), p1 | |
125 | + end | |
75 | 126 | end | ... | ... |
test/unit/notify_activity_to_profiles_job_test.rb
... | ... | @@ -24,15 +24,21 @@ class NotifyActivityToProfilesJobTest < ActiveSupport::TestCase |
24 | 24 | end |
25 | 25 | end |
26 | 26 | |
27 | - should 'notify just the users and his friends tracking user actions' do | |
27 | + should 'notify just the users and his followers tracking user actions' do | |
28 | 28 | person = fast_create(Person) |
29 | 29 | community = fast_create(Community) |
30 | 30 | action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :verb => 'create_article') |
31 | 31 | refute NotifyActivityToProfilesJob::NOTIFY_ONLY_COMMUNITY.include?(action_tracker.verb) |
32 | 32 | p1, p2, m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person) |
33 | - fast_create(Friendship, :person_id => person.id, :friend_id => p1.id) | |
34 | - fast_create(Friendship, :person_id => person.id, :friend_id => p2.id) | |
35 | - fast_create(Friendship, :person_id => p1.id, :friend_id => m1.id) | |
33 | + | |
34 | + circle1 = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person') | |
35 | + circle2 = Circle.create!(:person=> p2, :name => "Zombies", :profile_type => 'Person') | |
36 | + circle = Circle.create!(:person=> person, :name => "Zombies", :profile_type => 'Person') | |
37 | + | |
38 | + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle1.id) | |
39 | + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle2.id) | |
40 | + fast_create(ProfileFollower, :profile_id => m1.id, :circle_id => circle.id) | |
41 | + | |
36 | 42 | fast_create(RoleAssignment, :accessor_id => m2.id, :role_id => 3, :resource_id => community.id) |
37 | 43 | ActionTrackerNotification.delete_all |
38 | 44 | job = NotifyActivityToProfilesJob.new(action_tracker.id) |
... | ... | @@ -66,23 +72,24 @@ class NotifyActivityToProfilesJobTest < ActiveSupport::TestCase |
66 | 72 | end |
67 | 73 | end |
68 | 74 | |
69 | - should 'notify users its friends, the community and its members' do | |
75 | + should 'notify users its followers, the community and its members' do | |
70 | 76 | person = fast_create(Person) |
71 | 77 | community = fast_create(Community) |
72 | 78 | action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :target_id => community.id, :verb => 'create_article') |
73 | 79 | refute NotifyActivityToProfilesJob::NOTIFY_ONLY_COMMUNITY.include?(action_tracker.verb) |
74 | 80 | p1, p2, m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person) |
75 | - fast_create(Friendship, :person_id => person.id, :friend_id => p1.id) | |
76 | - fast_create(Friendship, :person_id => person.id, :friend_id => p2.id) | |
81 | + | |
82 | + circle1 = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person') | |
83 | + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle1.id) | |
84 | + | |
77 | 85 | fast_create(RoleAssignment, :accessor_id => m1.id, :role_id => 3, :resource_id => community.id) |
78 | 86 | fast_create(RoleAssignment, :accessor_id => m2.id, :role_id => 3, :resource_id => community.id) |
79 | 87 | ActionTrackerNotification.delete_all |
80 | 88 | job = NotifyActivityToProfilesJob.new(action_tracker.id) |
81 | 89 | job.perform |
82 | 90 | process_delayed_job_queue |
83 | - | |
84 | - assert_equal 6, ActionTrackerNotification.count | |
85 | - [person, community, p1, p2, m1, m2].each do |profile| | |
91 | + assert_equal 5, ActionTrackerNotification.count | |
92 | + [person, community, p1, m1, m2].each do |profile| | |
86 | 93 | notification = ActionTrackerNotification.find_by profile_id: profile.id |
87 | 94 | assert_equal action_tracker, notification.action_tracker |
88 | 95 | end |
... | ... | @@ -119,8 +126,13 @@ class NotifyActivityToProfilesJobTest < ActiveSupport::TestCase |
119 | 126 | action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :target_id => community.id, :verb => 'join_community') |
120 | 127 | refute NotifyActivityToProfilesJob::NOTIFY_ONLY_COMMUNITY.include?(action_tracker.verb) |
121 | 128 | p1, p2, m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person) |
122 | - fast_create(Friendship, :person_id => person.id, :friend_id => p1.id) | |
123 | - fast_create(Friendship, :person_id => person.id, :friend_id => p2.id) | |
129 | + | |
130 | + circle1 = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person') | |
131 | + circle2 = Circle.create!(:person=> p2, :name => "Zombies", :profile_type => 'Person') | |
132 | + | |
133 | + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle1.id) | |
134 | + fast_create(ProfileFollower, :profile_id => person.id, :circle_id => circle2.id) | |
135 | + | |
124 | 136 | fast_create(RoleAssignment, :accessor_id => m1.id, :role_id => 3, :resource_id => community.id) |
125 | 137 | fast_create(RoleAssignment, :accessor_id => m2.id, :role_id => 3, :resource_id => community.id) |
126 | 138 | ActionTrackerNotification.delete_all | ... | ... |
test/unit/person_notifier_test.rb
... | ... | @@ -178,6 +178,7 @@ class PersonNotifierTest < ActiveSupport::TestCase |
178 | 178 | update_product: -> { create Product, profile: @profile, product_category: create(ProductCategory, environment: Environment.default) }, |
179 | 179 | remove_product: -> { create Product, profile: @profile, product_category: create(ProductCategory, environment: Environment.default) }, |
180 | 180 | favorite_enterprise: -> { create FavoriteEnterprisePerson, enterprise: create(Enterprise), person: @member }, |
181 | + new_follower: -> { @member } | |
181 | 182 | } |
182 | 183 | |
183 | 184 | ActionTrackerConfig.verb_names.each do |verb| |
... | ... | @@ -197,6 +198,7 @@ class PersonNotifierTest < ActiveSupport::TestCase |
197 | 198 | 'friend_url' => '/', 'friend_profile_custom_icon' => [], 'friend_name' => ['joe'], |
198 | 199 | 'resource_name' => ['resource'], 'resource_profile_custom_icon' => [], 'resource_url' => ['/'], |
199 | 200 | 'enterprise_name' => 'coop', 'enterprise_url' => '/coop', |
201 | + 'follower_url' => '/', 'follower_profile_custom_icon' => [], 'follower_name' => ['joe'], | |
200 | 202 | 'view_url'=> ['/'], 'thumbnail_path' => ['1'], |
201 | 203 | } |
202 | 204 | a.get_url = '' | ... | ... |
test/unit/person_test.rb
... | ... | @@ -728,7 +728,7 @@ class PersonTest < ActiveSupport::TestCase |
728 | 728 | assert_equal [s4], p2.scraps_received.not_replies |
729 | 729 | end |
730 | 730 | |
731 | - should "the followed_by method be protected and true to the person friends and herself by default" do | |
731 | + should "the followed_by method return true to the person friends and herself by default" do | |
732 | 732 | p1 = fast_create(Person) |
733 | 733 | p2 = fast_create(Person) |
734 | 734 | p3 = fast_create(Person) |
... | ... | @@ -740,9 +740,9 @@ class PersonTest < ActiveSupport::TestCase |
740 | 740 | assert p1.is_a_friend?(p4) |
741 | 741 | |
742 | 742 | assert_equal true, p1.send(:followed_by?,p1) |
743 | - assert_equal true, p1.send(:followed_by?,p2) | |
744 | - assert_equal true, p1.send(:followed_by?,p4) | |
745 | - assert_equal false, p1.send(:followed_by?,p3) | |
743 | + assert_equal true, p2.send(:followed_by?,p1) | |
744 | + assert_equal true, p4.send(:followed_by?,p1) | |
745 | + assert_equal false, p3.send(:followed_by?,p1) | |
746 | 746 | end |
747 | 747 | |
748 | 748 | should "the person follows her friends and herself by default" do |
... | ... | @@ -757,9 +757,9 @@ class PersonTest < ActiveSupport::TestCase |
757 | 757 | assert p4.is_a_friend?(p1) |
758 | 758 | |
759 | 759 | assert_equal true, p1.follows?(p1) |
760 | - assert_equal true, p1.follows?(p2) | |
761 | - assert_equal true, p1.follows?(p4) | |
762 | - assert_equal false, p1.follows?(p3) | |
760 | + assert_equal true, p2.follows?(p1) | |
761 | + assert_equal true, p4.follows?(p1) | |
762 | + assert_equal false, p3.follows?(p1) | |
763 | 763 | end |
764 | 764 | |
765 | 765 | should "a person member of a community follows the community" do |
... | ... | @@ -836,18 +836,21 @@ class PersonTest < ActiveSupport::TestCase |
836 | 836 | assert_nil Scrap.find_by(id: scrap.id) |
837 | 837 | end |
838 | 838 | |
839 | - should "the tracked action be notified to person friends and herself" do | |
839 | + should "the tracked action be notified to person followers and herself" do | |
840 | 840 | Person.destroy_all |
841 | 841 | p1 = fast_create(Person) |
842 | 842 | p2 = fast_create(Person) |
843 | 843 | p3 = fast_create(Person) |
844 | 844 | p4 = fast_create(Person) |
845 | 845 | |
846 | - p1.add_friend(p2) | |
847 | - assert p1.is_a_friend?(p2) | |
848 | - refute p1.is_a_friend?(p3) | |
849 | - p1.add_friend(p4) | |
850 | - assert p1.is_a_friend?(p4) | |
846 | + circle2 = Circle.create!(:person=> p2, :name => "Zombies", :profile_type => 'Person') | |
847 | + circle4 = Circle.create!(:person=> p4, :name => "Zombies", :profile_type => 'Person') | |
848 | + | |
849 | + p2.follow(p1, circle2) | |
850 | + assert p2.follows?(p1) | |
851 | + refute p3.follows?(p1) | |
852 | + p4.follow(p1, circle4) | |
853 | + assert p4.follows?(p1) | |
851 | 854 | |
852 | 855 | action_tracker = fast_create(ActionTracker::Record, :user_id => p1.id) |
853 | 856 | ActionTrackerNotification.delete_all |
... | ... | @@ -880,17 +883,19 @@ class PersonTest < ActiveSupport::TestCase |
880 | 883 | end |
881 | 884 | end |
882 | 885 | |
883 | - should "the tracked action notify friends with one delayed job process" do | |
886 | + should "the tracked action notify followers with one delayed job process" do | |
884 | 887 | p1 = fast_create(Person) |
885 | 888 | p2 = fast_create(Person) |
886 | 889 | p3 = fast_create(Person) |
887 | 890 | p4 = fast_create(Person) |
888 | 891 | |
889 | - p1.add_friend(p2) | |
890 | - assert p1.is_a_friend?(p2) | |
891 | - refute p1.is_a_friend?(p3) | |
892 | - p1.add_friend(p4) | |
893 | - assert p1.is_a_friend?(p4) | |
892 | + circle2 = Circle.create!(:person=> p2, :name => "Zombies", :profile_type => 'Person') | |
893 | + circle4 = Circle.create!(:person=> p4, :name => "Zombies", :profile_type => 'Person') | |
894 | + p2.follow(p1, circle2) | |
895 | + assert p2.follows?(p1) | |
896 | + refute p3.follows?(p1) | |
897 | + p4.follow(p1, circle4) | |
898 | + assert p4.follows?(p1) | |
894 | 899 | |
895 | 900 | action_tracker = fast_create(ActionTracker::Record, :user_id => p1.id) |
896 | 901 | |
... | ... | @@ -1035,11 +1040,13 @@ class PersonTest < ActiveSupport::TestCase |
1035 | 1040 | p2 = create_user('p2').person |
1036 | 1041 | p3 = create_user('p3').person |
1037 | 1042 | c = fast_create(Community, :name => "Foo") |
1043 | + | |
1038 | 1044 | c.add_member(p1) |
1039 | 1045 | process_delayed_job_queue |
1040 | 1046 | c.add_member(p3) |
1041 | 1047 | process_delayed_job_queue |
1042 | - assert_equal 4, ActionTracker::Record.count | |
1048 | + | |
1049 | + assert_equal 5, ActionTracker::Record.count | |
1043 | 1050 | assert_equal 5, ActionTrackerNotification.count |
1044 | 1051 | has_add_member_notification = false |
1045 | 1052 | ActionTrackerNotification.all.map do |notification| |
... | ... | @@ -1951,4 +1958,51 @@ class PersonTest < ActiveSupport::TestCase |
1951 | 1958 | person.save! |
1952 | 1959 | end |
1953 | 1960 | |
1961 | + should 'update profile circles for a person' do | |
1962 | + person = create_user('testuser').person | |
1963 | + community = fast_create(Community) | |
1964 | + circle = Circle.create!(:person=> person, :name => "Zombies", :profile_type => 'Community') | |
1965 | + circle2 = Circle.create!(:person=> person, :name => "Dota", :profile_type => 'Community') | |
1966 | + circle3 = Circle.create!(:person=> person, :name => "Quadrado", :profile_type => 'Community') | |
1967 | + person.follow(community, [circle, circle2]) | |
1968 | + person.update_profile_circles(community, [circle2, circle3]) | |
1969 | + assert_equivalent [circle2, circle3], ProfileFollower.with_profile(community).with_follower(person).map(&:circle) | |
1970 | + end | |
1971 | + | |
1972 | + should 'a person follow a profile' do | |
1973 | + person = create_user('testuser').person | |
1974 | + community = fast_create(Community) | |
1975 | + circle = Circle.create!(:person=> person, :name => "Zombies", :profile_type => 'Community') | |
1976 | + person.follow(community, circle) | |
1977 | + assert_includes person.followed_profiles, community | |
1978 | + end | |
1979 | + | |
1980 | + should 'a person follow a profile with more than one circle' do | |
1981 | + person = create_user('testuser').person | |
1982 | + community = fast_create(Community) | |
1983 | + circle = Circle.create!(:person=> person, :name => "Zombies", :profile_type => 'Community') | |
1984 | + circle2 = Circle.create!(:person=> person, :name => "Dota", :profile_type => 'Community') | |
1985 | + person.follow(community, [circle, circle2]) | |
1986 | + assert_includes person.followed_profiles, community | |
1987 | + assert_equivalent [circle, circle2], ProfileFollower.with_profile(community).with_follower(person).map(&:circle) | |
1988 | + end | |
1989 | + | |
1990 | + should 'a person unfollow a profile' do | |
1991 | + person = create_user('testuser').person | |
1992 | + community = fast_create(Community) | |
1993 | + circle = Circle.create!(:person=> person, :name => "Zombies", :profile_type => 'Community') | |
1994 | + person.follow(community, circle) | |
1995 | + person.unfollow(community) | |
1996 | + assert_not_includes person.followed_profiles, community | |
1997 | + end | |
1998 | + | |
1999 | + should 'a person remove a profile from a circle' do | |
2000 | + person = create_user('testuser').person | |
2001 | + community = fast_create(Community) | |
2002 | + circle = Circle.create!(:person=> person, :name => "Zombies", :profile_type => 'Community') | |
2003 | + circle2 = Circle.create!(:person=> person, :name => "Dota", :profile_type => 'Community') | |
2004 | + person.follow(community, [circle, circle2]) | |
2005 | + person.remove_profile_from_circle(community, circle) | |
2006 | + assert_equivalent [circle2], ProfileFollower.with_profile(community).with_follower(person).map(&:circle) | |
2007 | + end | |
1954 | 2008 | end | ... | ... |
... | ... | @@ -0,0 +1,73 @@ |
1 | +require_relative "../test_helper" | |
2 | + | |
3 | +class ProfileFollowersTest < ActiveSupport::TestCase | |
4 | + | |
5 | + should 'a person follow another' do | |
6 | + p1 = create_user('person_test').person | |
7 | + p2 = create_user('person_test_2').person | |
8 | + circle = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person') | |
9 | + | |
10 | + assert_difference 'ProfileFollower.count' do | |
11 | + p1.follow(p2, circle) | |
12 | + end | |
13 | + | |
14 | + assert_includes p2.followers(true), p1 | |
15 | + assert_not_includes p1.followers(true), p2 | |
16 | + end | |
17 | + | |
18 | + should 'a person unfollow another person' do | |
19 | + p1 = create_user('person_test').person | |
20 | + p2 = create_user('person_test_2').person | |
21 | + circle = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person') | |
22 | + | |
23 | + p1.follow(p2,circle) | |
24 | + | |
25 | + assert_difference 'ProfileFollower.count', -1 do | |
26 | + p1.unfollow(p2) | |
27 | + end | |
28 | + | |
29 | + assert_not_includes p2.followers(true), p1 | |
30 | + end | |
31 | + | |
32 | + should 'get the followed persons for a profile' do | |
33 | + p1 = create_user('person_test').person | |
34 | + p2 = create_user('person_test_2').person | |
35 | + p3 = create_user('person_test_3').person | |
36 | + circle = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person') | |
37 | + | |
38 | + p1.follow(p2, circle) | |
39 | + p1.follow(p3, circle) | |
40 | + | |
41 | + assert_equivalent p1.followed_profiles, [p2,p3] | |
42 | + assert_equivalent Profile.followed_by(p1), [p2,p3] | |
43 | + end | |
44 | + | |
45 | + should 'not follow same person twice' do | |
46 | + p1 = create_user('person_test').person | |
47 | + p2 = create_user('person_test_2').person | |
48 | + circle = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person') | |
49 | + | |
50 | + assert_difference 'ProfileFollower.count' do | |
51 | + p1.follow(p2, circle) | |
52 | + p1.follow(p2, circle) | |
53 | + end | |
54 | + | |
55 | + assert_equivalent p1.followed_profiles, [p2] | |
56 | + assert_equivalent p2.followers, [p1] | |
57 | + end | |
58 | + | |
59 | + should 'show the correct message when a profile is followed by the same person' do | |
60 | + p1 = create_user('person_test').person | |
61 | + p2 = create_user('person_test_2').person | |
62 | + circle = Circle.create!(:person=> p1, :name => "Zombies", :profile_type => 'Person') | |
63 | + | |
64 | + p1.follow(p2, circle) | |
65 | + profile_follower = ProfileFollower.new | |
66 | + profile_follower.circle = circle | |
67 | + profile_follower.profile = p2 | |
68 | + profile_follower.valid? | |
69 | + | |
70 | + assert_includes profile_follower.errors.messages[:profile_id], | |
71 | + "can't put a profile in the same circle twice" | |
72 | + end | |
73 | +end | ... | ... |
test/unit/scrap_test.rb
... | ... | @@ -125,11 +125,11 @@ class ScrapTest < ActiveSupport::TestCase |
125 | 125 | assert_equal c, ta.target |
126 | 126 | end |
127 | 127 | |
128 | - should "notify leave_scrap action tracker verb to friends and itself" do | |
128 | + should "notify leave_scrap action tracker verb to followers and itself" do | |
129 | 129 | User.current = create_user |
130 | 130 | p1 = User.current.person |
131 | 131 | p2 = create_user.person |
132 | - p1.add_friend(p2) | |
132 | + p2.add_friend(p1) | |
133 | 133 | process_delayed_job_queue |
134 | 134 | s = Scrap.new |
135 | 135 | s.sender= p1 |
... | ... | @@ -180,11 +180,11 @@ class ScrapTest < ActiveSupport::TestCase |
180 | 180 | assert_equal p, ta.user |
181 | 181 | end |
182 | 182 | |
183 | - should "notify leave_scrap_to_self action tracker verb to friends and itself" do | |
183 | + should "notify leave_scrap_to_self action tracker verb to followers and itself" do | |
184 | 184 | User.current = create_user |
185 | 185 | p1 = User.current.person |
186 | 186 | p2 = create_user.person |
187 | - p1.add_friend(p2) | |
187 | + p2.add_friend(p1) | |
188 | 188 | ActionTrackerNotification.delete_all |
189 | 189 | Delayed::Job.delete_all |
190 | 190 | s = Scrap.new | ... | ... |