+ <%= labelled_check_box _("Allow other users to follow me"), 'profile_data[allow_following]', true, @profile.allow_following?, :class => "person-can-be-followed" %>
+
<% if profile.person? %>
+
<%= labelled_radio_button _('Public — show my contents to all internet users').html_safe, 'profile_data[public_profile]', true, @profile.public_profile? %>
diff --git a/app/views/profile_editor/index.html.erb b/app/views/profile_editor/index.html.erb
index 9c99687..dac82de 100644
--- a/app/views/profile_editor/index.html.erb
+++ b/app/views/profile_editor/index.html.erb
@@ -72,6 +72,12 @@
<%= control_panel_button(_('Email Templates'), 'email-templates', :controller => :profile_email_templates) if profile.organization? %>
+ <% if profile.person? %>
+ <%= control_panel_button(_('Manage followed profiles'), 'manage-followed-people', :controller => :followers) %>
+ <% end %>
+
+ <%= control_panel_button(_('Manage follow categories'), 'manage-follow-categories', :controller => :follow_categories) if profile.person? %>
+
<% @plugins.dispatch(:control_panel_buttons).each do |button| %>
<%= control_panel_button(button[:title], button[:icon], button[:url], button[:html_options]) %>
<% end %>
diff --git a/config/initializers/action_tracker.rb b/config/initializers/action_tracker.rb
index 7080775..9bfcfa3 100644
--- a/config/initializers/action_tracker.rb
+++ b/config/initializers/action_tracker.rb
@@ -12,6 +12,10 @@ ActionTrackerConfig.verbs = {
type: :groupable
},
+ new_follower: {
+ type: :groupable
+ },
+
join_community: {
type: :groupable
},
diff --git a/db/migrate/20160608123748_create_profile_followers_table.rb b/db/migrate/20160608123748_create_profile_followers_table.rb
new file mode 100644
index 0000000..3e8a853
--- /dev/null
+++ b/db/migrate/20160608123748_create_profile_followers_table.rb
@@ -0,0 +1,41 @@
+class CreateProfileFollowersTable < ActiveRecord::Migration
+ def up
+ create_table :profile_followers do |t|
+ t.column :profile_id, :integer
+ t.column :follower_id, :integer
+ t.column :follow_category_id, :integer
+ t.timestamps
+ end
+
+ create_table :follow_categories do |t|
+ t.column :name, :string
+ t.belongs_to :person
+ end
+
+ add_foreign_key :profile_followers, :follow_categories, :on_delete => :nullify
+
+ add_index :profile_followers, [:profile_id, :follower_id], :name => "profile_followers_composite_key_index", :unique => true
+
+ #insert one category for each friend group a person has
+ execute("INSERT INTO follow_categories(name, person_id) SELECT DISTINCT (CASE WHEN (f.group IS NULL OR f.group = '') THEN 'friendships' ELSE f.group END), f.person_id FROM friendships as f")
+ #insert 'memberships' category if a person is in a community as a member, moderator or profile admin
+ execute("INSERT INTO follow_categories(name, person_id) SELECT DISTINCT 'memberships', ra.accessor_id FROM role_assignments as ra JOIN roles ON ra.role_id = roles.id WHERE roles.name IN ('Member','Moderator','Profile Administrator')")
+ #insert 'favorites' category if a person has any favorited enterprise
+ execute("INSERT INTO follow_categories(name, person_id) SELECT DISTINCT 'favorites', person_id FROM favorite_enterprise_people")
+
+ #insert a follower entry for each friend, with the category the same as the friendship group or equals 'friendships'
+ execute("INSERT INTO profile_followers(follower_id, profile_id, follow_category_id) SELECT DISTINCT f.person_id, f.friend_id, c.id FROM friendships as f JOIN follow_categories as c ON f.person_id = c.person_id WHERE c.name = f.group OR c.name = 'friendships'")
+ #insert a follower entry for each favorited enterprise, with the category 'favorites'
+ execute("INSERT INTO profile_followers(follower_id, profile_id, follow_category_id) SELECT DISTINCT f.person_id, f.enterprise_id, c.id FROM favorite_enterprise_people AS f JOIN follow_categories as c ON f.person_id = c.person_id WHERE c.name = 'favorites' ")
+ #insert a follower entry for each community a person participates as a member, moderator or admininstrator
+ execute("INSERT INTO profile_followers(follower_id, profile_id, follow_category_id) SELECT DISTINCT ra.accessor_id, ra.resource_id, c.id FROM role_assignments as ra JOIN roles ON ra.role_id = roles.id JOIN follow_categories as c ON ra.accessor_id = c.person_id WHERE roles.name IN ('Member','Moderator','Profile Administrator') AND c.name = 'memberships'")
+
+ end
+
+ def down
+ remove_foreign_key :profile_followers, :follow_categories
+ drop_table :follow_categories
+ remove_index :profile_followers, :name => "profile_followers_composite_key_index"
+ drop_table :profile_followers
+ end
+end
diff --git a/features/follow_profile.feature b/features/follow_profile.feature
new file mode 100644
index 0000000..a129ef6
--- /dev/null
+++ b/features/follow_profile.feature
@@ -0,0 +1,55 @@
+Feature: follow profile
+ As a noosfero user
+ I want to follow a profile
+ So I can receive notifications from it
+
+ Background:
+ Given the following community
+ | identifier | name |
+ | nightswatch | Nights Watch |
+ And the following users
+ | login |
+ | johnsnow |
+ And the user "johnsnow" has the following categories to follow
+ | name |
+ | Family |
+ | Work |
+
+ @selenium
+ Scenario: Common noofero user follow a community with a category
+ Given I am logged in as "johnsnow"
+ When I go to nightswatch's homepage
+ When I follow "Follow"
+ And I should see "Family"
+ And I should see "Work"
+ And I should see "No Category"
+ Then "johnsnow" should be a follower of "nightswatch" with no category
+ When I follow "Work"
+ Then "johnsnow" should be a follower of "nightswatch" with category "Work"
+
+ @selenium
+ Scenario: Common noofero user follow a community with no category
+ Given I am logged in as "johnsnow"
+ When I go to nightswatch's homepage
+ When I follow "Follow"
+ When I follow "No Category"
+ Then "johnsnow" should be a follower of "nightswatch" with no category
+
+ @selenium
+ Scenario: Common noofero user follow a community with a new category
+ Given I am logged in as "johnsnow"
+ When I go to nightswatch's homepage
+ When I follow "Follow"
+ And I fill in "category_name" with "Winterfell"
+ When I click on anything with selector "#new-category-submit-inline"
+ And I wait 3 second
+ Then "johnsnow" should be a follower of "nightswatch" with category "Winterfell"
+
+ @selenium
+ Scenario: Common noofero user unfollow a community
+ Given "johnsnow" is a follower of "nightswatch" with no category
+ And I am logged in as "johnsnow"
+ When I go to nightswatch's homepage
+ When I follow "Unfollow"
+ Then "johnsnow" should not be a follower of "nightswatch"
+
diff --git a/features/step_definitions/followers_steps.rb b/features/step_definitions/followers_steps.rb
new file mode 100644
index 0000000..3e24929
--- /dev/null
+++ b/features/step_definitions/followers_steps.rb
@@ -0,0 +1,39 @@
+Given /^the user "(.+)" has the following categories to follow$/ do |user_name,table|
+ person = User.find_by(:login => user_name).person
+ table.hashes.each do |category|
+ FollowCategory.create!(:person => person, :name => category[:name])
+ end
+end
+
+Then /^"(.+)" should be a follower of "(.+)" (?:with no category|with category "(.+)")$/ do |person, profile, category|
+ profile = Profile.find_by(identifier: profile)
+ followers = profile.followers
+ person = Person.find_by(identifier: person)
+ followers.should include(person)
+
+ if category
+ ProfileFollower.find_by(:follower => person, :profile => profile).follow_category.name.should == category
+ else
+ ProfileFollower.find_by(:follower => person, :profile => profile).follow_category.should == nil
+ end
+end
+
+Then /^"(.+)" should not be a follower of "(.+)"$/ do |person, profile|
+ profile = Profile.find_by(identifier: profile)
+ followers = profile.followers
+ person = Person.find_by(identifier: person)
+ followers.should_not include(person)
+end
+
+Given /^"(.+)" is a follower of "(.+)" (?:with no category|with category "(.+)")$/ do |person, profile, category|
+ profile = Profile.find_by(identifier: profile)
+ person = Person.find_by(identifier: person)
+ params = {:follower => person, :profile => profile}
+
+ if category
+ category = FollowCategory.find_by(:name => category, :person => person)
+ params.merge!({:follow_category => category})
+ end
+ ProfileFollower.create!(params)
+end
+
diff --git a/features/step_definitions/web_steps.rb b/features/step_definitions/web_steps.rb
index a0e0c6c..d12a33a 100644
--- a/features/step_definitions/web_steps.rb
+++ b/features/step_definitions/web_steps.rb
@@ -298,3 +298,6 @@ When /^(?:|I )wait ([^ ]+) seconds?(?:| .+)$/ do |seconds|
sleep seconds.to_f
end
+Given /^I click on anything with selector "([^"]*)"$/ do |selector|
+ page.evaluate_script("jQuery('#{selector}').click();")
+end
diff --git a/public/javascripts/application.js b/public/javascripts/application.js
index fff7e9b..778c508 100644
--- a/public/javascripts/application.js
+++ b/public/javascripts/application.js
@@ -26,6 +26,7 @@
*= require pagination.js
* views speficics
*= require add-and-join.js
+*= require followers.js
*= require report-abuse.js
*= require autogrow.js
*= require require_login.js
@@ -550,6 +551,11 @@ function loading_for_button(selector) {
jQuery(selector).css('cursor', 'progress');
}
+function hide_loading_for_button(selector) {
+ selector.css("cursor","");
+ $(".small-loading").remove();
+}
+
function new_qualifier_row(selector, select_qualifiers, delete_button) {
index = jQuery(selector + ' tr').size() - 1;
jQuery(selector).append("
" + select_qualifiers + "
" + delete_button + "
");
diff --git a/public/javascripts/followers.js b/public/javascripts/followers.js
new file mode 100644
index 0000000..6d4687b
--- /dev/null
+++ b/public/javascripts/followers.js
@@ -0,0 +1,18 @@
+$(".action-follow").live("click", function() {
+ var button = $(this);
+ var url = button.attr("href");
+ loading_for_button(button);
+
+ $.post(url, function(data) {
+ button.fadeOut('fast', function() {
+ $("#follow-categories-container").html(data);
+ $("#follow-categories-container").fadeIn();
+ });
+ }).fail(function(response) {
+ display_notice(response.responseText);
+ }).always(function() {
+ hide_loading_for_button(button);
+ });
+
+ return false;
+});
diff --git a/public/stylesheets/blocks/profile-info.scss b/public/stylesheets/blocks/profile-info.scss
index 80fd7f0..1190e3a 100644
--- a/public/stylesheets/blocks/profile-info.scss
+++ b/public/stylesheets/blocks/profile-info.scss
@@ -99,3 +99,25 @@
margin: 0px 0px 5px 0px;
padding: 2px;
}
+#follow-categories-container {
+ background-color: #eee;
+ padding: 5px;
+ display: flex;
+}
+#follow-categories-container p {
+ font-size: 12px;
+ margin-bottom: 5px;
+}
+#no-category-link {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+#new-category-field-actions-block {
+ float: left;
+ width: 80%;
+}
+#new-category-submit-actions-block {
+ float: right;
+ width: 10%;
+ padding-right: 10px;
+}
diff --git a/public/stylesheets/profile-activity.scss b/public/stylesheets/profile-activity.scss
index 91848a1..6163fe1 100644
--- a/public/stylesheets/profile-activity.scss
+++ b/public/stylesheets/profile-activity.scss
@@ -167,7 +167,9 @@ li.profile-activity-item.upload_image .activity-gallery-images-count-1 img {
#profile-wall li.profile-activity-item.join_community .profile-activity-text a img,
#profile-wall li.profile-activity-item.new_friendship .profile-activity-text a img,
+#profile-wall li.profile-activity-item.new_follower .profile-activity-text a img,
#profile-network li.profile-activity-item.join_community .profile-activity-text a img,
+#profile-network li.profile-activity-item.new_follower .profile-activity-text a img,
#profile-network li.profile-activity-item.new_friendship .profile-activity-text a img {
margin: 5px 5px 0 0;
padding: 1px;
diff --git a/public/stylesheets/profile-list.scss b/public/stylesheets/profile-list.scss
index cbd9bc1..3458bb9 100644
--- a/public/stylesheets/profile-list.scss
+++ b/public/stylesheets/profile-list.scss
@@ -23,6 +23,7 @@
}
.controller-favorite_enterprises .profile-list a.profile-link,
.controller-friends .profile-list a.profile-link,
+.controller-followers .profile-list a.profile-link,
.list-profile-connections .profile-list a.profile-link,
.profiles-suggestions .profile-list a.profile-link {
text-decoration: none;
@@ -32,11 +33,13 @@
}
.controller-favorite_enterprises .profile-list a.profile-link:hover,
.controller-friends .profile-list a.profile-link:hover,
+.controller-followers .profile-list a.profile-link:hover,
.profiles-suggestions .profile-list a.profile-link:hover {
color: #FFF;
}
.controller-favorite_enterprises .profile-list .profile_link span,
.controller-friends .profile-list .profile_link span,
+.controller-followers .profile-list .profile_link span,
.box-1 .profiles-suggestions .profile-list .profile_link span {
width: 80px;
display: block;
@@ -44,12 +47,14 @@
}
.controller-favorite_enterprises .profile-list,
.controller-friends .profile-list,
+.controller-followers .profile-list,
.profiles-suggestions .profile-list {
position: relative;
}
.controller-favorite_enterprises .profile-list .controll,
.controller-friends .profile-list .controll,
+.controller-followers .profile-list .controll,
.profiles-suggestions .profile-list .controll {
position: absolute;
top: 7px;
@@ -57,17 +62,20 @@
}
.controller-favorite_enterprises .profile-list .controll a,
.controller-friends .profile-list .controll a,
+.controller-followers .profile-list .controll a,
.profiles-suggestions .profile-list .controll a {
display: block;
margin-bottom: 2px;
}
.controller-favorite_enterprises .msie6 .profile-list .controll a,
.controller-friends .msie6 .profile-list .controll a,
+.controller-folloed_people .msie6 .profile-list .controll a,
.profiles-suggestions .msie6 .profile-list .controll a {
width: 0px;
}
.controller-favorite_enterprises .button-bar,
.controller-friends .button-bar,
+.controller-followers .button-bar,
.profiles-suggestions .button-bar {
clear: both;
padding-top: 20px;
@@ -208,22 +216,35 @@
font-size: 12px;
}
.action-profile-members .profile_link{
- position: relative;
+ position: relative;
}
.action-profile-members .profile_link span.new-profile:last-child{
- position: absolute;
- top: 3px;
- right: 2px;
- text-transform: uppercase;
- color: #FFF;
- font-size: 9px;
- background: #66CC33;
- padding: 2px;
- display: block;
- width: 35px;
- font-weight: 700;
+ position: absolute;
+ top: 3px;
+ right: 2px;
+ text-transform: uppercase;
+ color: #FFF;
+ font-size: 9px;
+ background: #66CC33;
+ padding: 2px;
+ display: block;
+ width: 35px;
+ font-weight: 700;
}
.action-profile-members .profile_link .fn{
- font-style: normal;
- color: #000;
+ font-style: normal;
+ color: #000;
+}
+.category-name {
+ margin-top: 0px;
+ margin-bottom: 0px;
+ font-style: italic;
+ color: #888a85;
+ text-align: center;
+}
+.set-category-modal {
+ width: 250px;
+}
+.set-category-modal #actions-container {
+ margin-top: 20px
}
diff --git a/test/functional/follow_categories_controller_tests.rb b/test/functional/follow_categories_controller_tests.rb
new file mode 100644
index 0000000..32848d3
--- /dev/null
+++ b/test/functional/follow_categories_controller_tests.rb
@@ -0,0 +1,103 @@
+require_relative "../test_helper"
+require 'follow_categories_controller'
+
+class FollowCategoriesControllerTest < ActionController::TestCase
+
+ def setup
+ @controller = FollowCategoriesController.new
+ @person = create_user('person').person
+ login_as(@person.identifier)
+ end
+
+ should 'return all categories of a profile' do
+ category1 = FollowCategory.create(:name => "category1", :person => @person)
+ category2 = FollowCategory.create(:name => "category2", :person => @person)
+ get :index, :profile => @person.identifier
+
+ assert_equivalent [category1, category2], assigns[:categories]
+ end
+
+ should 'initialize an empty category for creation' do
+ get :new, :profile => @person.identifier
+ assert_nil assigns[:follow_category].id
+ assert_nil assigns[:follow_category].name
+ end
+
+ should 'create a new category' do
+ assert_difference '@person.follow_categories.count' do
+ post :create, :profile => @person.identifier,
+ :follow_category => { :name => 'category' }
+ end
+ assert_redirected_to :action => :index
+ end
+
+ should 'not create a category without a name' do
+ assert_no_difference '@person.follow_categories.count' do
+ post :create, :profile => @person.identifier, :follow_category => { :name => nil }
+ end
+ assert_template :new
+ end
+
+ should 'retrieve an existing category when editing' do
+ category = FollowCategory.create(:name => "category", :person => @person)
+ get :edit, :profile => @person.identifier, :id => category.id
+ assert_equal category.name, assigns[:follow_category].name
+ end
+
+ should 'return 404 when editing a category that does not exist' do
+ get :edit, :profile => @person.identifier, :id => "nope"
+ assert_response 404
+ end
+
+ should 'update an existing category' do
+ category = FollowCategory.create(:name => "category", :person => @person)
+ get :update, :profile => @person.identifier, :id => category.id,
+ :follow_category => { :name => "new name" }
+
+ category.reload
+ assert_equal "new name", category.name
+ assert_redirected_to :action => :index
+ end
+
+ should 'not update an existing category without a name' do
+ category = FollowCategory.create(:name => "category", :person => @person)
+ get :update, :profile => @person.identifier, :id => category.id,
+ :follow_category => { :name => nil }
+
+ category.reload
+ assert_equal "category", category.name
+ assert_template :edit
+ end
+
+ should 'return 404 when updating a category that does not exist' do
+ get :update, :profile => @person.identifier, :id => "nope", :name => "new name"
+ assert_response 404
+ end
+
+ should 'destroy an existing category and update related profiles' do
+ category = FollowCategory.create(:name => "category", :person => @person)
+ follower = fast_create(ProfileFollower, :profile_id => fast_create(Person).id,
+ :follower_id => @person.id, :follow_category_id => category.id)
+
+ assert_difference "@person.follow_categories.count", -1 do
+ get :destroy, :profile => @person.identifier, :id => category.id
+ end
+
+ follower.reload
+ assert_nil follower.follow_category
+ end
+
+ should 'return 404 when deleting and category that does not exist' do
+ get :destroy, :profile => @person.identifier, :id => "nope"
+ assert_response 404
+ end
+
+ should 'display notice when ' do
+ category = FollowCategory.create(:name => "category", :person => @person)
+ FollowCategory.any_instance.stubs(:destroy).returns(false)
+
+ get :destroy, :profile => @person.identifier, :id => category.id
+ assert_not_nil session[:notice]
+ end
+
+end
diff --git a/test/functional/followers_controller_test.rb b/test/functional/followers_controller_test.rb
new file mode 100644
index 0000000..a071cdc
--- /dev/null
+++ b/test/functional/followers_controller_test.rb
@@ -0,0 +1,48 @@
+require_relative "../test_helper"
+require 'followers_controller'
+
+class FollowersControllerTest < ActionController::TestCase
+ def setup
+ @profile = create_user('testuser').person
+ end
+
+ should 'return followed people list' do
+ login_as(@profile.identifier)
+ person = fast_create(Person)
+ fast_create(ProfileFollower, :profile_id => person.id, :follower_id => @profile.id)
+
+ get :index, :profile => @profile.identifier
+ assert_includes assigns(:followed_people), person
+ end
+
+ should 'redirect to login page if not logged in' do
+ person = fast_create(Person)
+ get :index, :profile => @profile.identifier
+ assert_redirected_to :controller => 'account', :action => 'login'
+ end
+
+ should 'render set category modal' do
+ login_as(@profile.identifier)
+ get :set_category, :profile => @profile.identifier, :followed_profile_id => 3
+ assert_tag :tag => "input", :attributes => { :id => "followed_profile_id", :value => 3 }
+ end
+
+ should 'update followed person category' do
+ login_as(@profile.identifier)
+ person = fast_create(Person)
+ fast_create(ProfileFollower, :profile_id => person.id, :follower_id => @profile.id)
+
+ post :set_category, :profile => @profile.identifier, :category_name => "category test", :followed_profile_id => person.id
+ follower = ProfileFollower.find_by(:profile_id => person.id, :follower_id => @profile.id)
+ assert_equal "WRONG","FIX THIS TEST TO USE CATEGORY INsTEAD OF GROUP" #follower.group, "category test"
+ end
+
+ should 'not update category of not followed person' do
+ login_as(@profile.identifier)
+ person = fast_create(Person)
+
+ post :set_category, :profile => @profile.identifier, :category_name => "category test", :followed_profile_id => person.id
+ follower = ProfileFollower.find_by(:profile_id => person.id, :follower_id => @profile.id)
+ ProfileFollower.any_instance.expects(:update_attributes).times(0)
+ end
+end
diff --git a/test/functional/profile_controller_test.rb b/test/functional/profile_controller_test.rb
index 57da6e0..67ae5e2 100644
--- a/test/functional/profile_controller_test.rb
+++ b/test/functional/profile_controller_test.rb
@@ -771,12 +771,13 @@ class ProfileControllerTest < ActionController::TestCase
assert_equal 15, assigns(:activities).size
end
- should 'not see the friends activities in the current profile' do
+ should 'not see the followers activities in the current profile' do
p2 = create_user.person
- refute profile.is_a_friend?(p2)
+ refute profile.follows?(p2)
p3 = create_user.person
- p3.add_friend(profile)
- assert p3.is_a_friend?(profile)
+ profile.follow(p3)
+ assert profile.follows?(p3)
+
ActionTracker::Record.destroy_all
scrap1 = create(Scrap, defaults_for_scrap(:sender => p2, :receiver => p3))
@@ -964,7 +965,9 @@ class ProfileControllerTest < ActionController::TestCase
should 'have activities defined if logged in and is following profile' do
login_as(profile.identifier)
p1= fast_create(Person)
- p1.add_friend(profile)
+
+ profile.follow(p1)
+
ActionTracker::Record.destroy_all
get :index, :profile => p1.identifier
assert_equal [], assigns(:activities)
@@ -1932,4 +1935,77 @@ class ProfileControllerTest < ActionController::TestCase
assert_redirected_to :controller => 'account', :action => 'login'
end
+ should 'follow a user without defining a group' do
+ login_as(@profile.identifier)
+ person = fast_create(Person)
+ get :follow, :profile => person.identifier
+
+ follower = ProfileFollower.find_by(:profile_id => person.id, :follower_id => @profile.id)
+ assert_not_nil follower
+ end
+
+ should "not follow user if not logged" do
+ person = fast_create(Person)
+ get :follow, :profile => person.identifier
+
+ assert_redirected_to :controller => 'account', :action => 'login'
+ end
+
+ should 'follow a user with a group' do
+ login_as(@profile.identifier)
+ person = fast_create(Person)
+ get :follow, :profile => person.identifier, :follow => { :category => "A Group" }
+
+ follower = ProfileFollower.find_by(:profile_id => person.id, :follower_id => @profile.id)
+ assert_not_nil follower
+ assert_equal "A Group", "FIX THIS TEST TO USE CATEGORY INsTEAD OF GROUP"#follower.category.name
+ end
+
+ should 'not follow if current_person already follows the person' do
+ login_as(@profile.identifier)
+ person = fast_create(Person)
+ fast_create(ProfileFollower, :profile_id => person.id, :follower_id => @profile.id)
+
+ assert_no_difference 'ProfileFollower.count' do
+ get :follow, :profile => person.identifier, :follow => { :category => "A Group" }
+ end
+ assert_response 400
+ end
+
+ should "not unfollow user if not logged" do
+ person = fast_create(Person)
+ get :unfollow, :profile => person.identifier
+
+ assert_redirected_to :controller => 'account', :action => 'login'
+ end
+
+ should "unfollow a followed person" do
+ login_as(@profile.identifier)
+ person = fast_create(Person)
+ follower = fast_create(ProfileFollower, :profile_id => person.id, :follower_id => @profile.id)
+ assert_not_nil follower
+
+ get :unfollow, :profile => person.identifier
+ follower = ProfileFollower.find_by(:profile_id => person.id, :follower_id => @profile.id)
+ assert_nil follower
+ end
+
+ should "not unfollow a not followed person" do
+ login_as(@profile.identifier)
+ person = fast_create(Person)
+
+ assert_no_difference 'ProfileFollower.count' do
+ get :unfollow, :profile => person.identifier
+ end
+ end
+
+ should "redirect to page after unfollow" do
+ login_as(@profile.identifier)
+ person = fast_create(Person)
+ fast_create(ProfileFollower, :profile_id => person.id, :follower_id => @profile.id)
+
+ get :unfollow, :profile => person.identifier, :redirect_to => "/some/url"
+ assert_redirected_to "/some/url"
+ end
+
end
diff --git a/test/unit/article_test.rb b/test/unit/article_test.rb
index 68cb57f..6226eaa 100644
--- a/test/unit/article_test.rb
+++ b/test/unit/article_test.rb
@@ -1099,9 +1099,9 @@ class ArticleTest < ActiveSupport::TestCase
assert_equal 3, ActionTrackerNotification.where(action_tracker_id: second_activity.id).count
end
- should 'create notifications to friends when creating an article' do
+ should 'create notifications to followers when creating an article' do
friend = fast_create(Person)
- profile.add_friend(friend)
+ friend.follow(profile)
Article.destroy_all
ActionTracker::Record.destroy_all
ActionTrackerNotification.destroy_all
@@ -1112,9 +1112,9 @@ class ArticleTest < ActiveSupport::TestCase
assert_equal friend, ActionTrackerNotification.last.profile
end
- should 'create the notification to the friend when one friend has the notification and the other no' do
+ should 'create the notification to the follower when one follower has the notification and the other no' do
f1 = fast_create(Person)
- profile.add_friend(f1)
+ f1.follow(profile)
User.current = profile.user
article = create TinyMceArticle, :name => 'Tracked Article 1', :profile_id => profile.id
@@ -1123,16 +1123,17 @@ class ArticleTest < ActiveSupport::TestCase
assert_equal 2, ActionTrackerNotification.where(action_tracker_id: article.activity.id).count
f2 = fast_create(Person)
- profile.add_friend(f2)
+ f2.follow(profile)
+
article2 = create TinyMceArticle, :name => 'Tracked Article 2', :profile_id => profile.id
assert_equal 2, ActionTracker::Record.where(verb: 'create_article').count
process_delayed_job_queue
assert_equal 3, ActionTrackerNotification.where(action_tracker_id: article2.activity.id).count
end
- should 'destroy activity and notifications of friends when destroying an article' do
+ should 'destroy activity and notifications of followers when destroying an article' do
friend = fast_create(Person)
- profile.add_friend(friend)
+ friend.follow(profile)
Article.destroy_all
ActionTracker::Record.destroy_all
ActionTrackerNotification.destroy_all
diff --git a/test/unit/friendship_test.rb b/test/unit/friendship_test.rb
index 8ab5729..4a37a7f 100644
--- a/test/unit/friendship_test.rb
+++ b/test/unit/friendship_test.rb
@@ -28,14 +28,14 @@ class FriendshipTest < ActiveSupport::TestCase
f.person = a
f.friend = b
f.save!
- ta = ActionTracker::Record.last
+ ta = ActionTracker::Record.where(:target_type => "Friendship").last
assert_equal a, ta.user
assert_equal 'b', ta.get_friend_name[0]
f = Friendship.new
f.person = a
f.friend = c
f.save!
- ta = ActionTracker::Record.last
+ ta = ActionTracker::Record.where(:target_type => "Friendship").last
assert_equal a, ta.user
assert_equal 'c', ta.get_friend_name[1]
end
@@ -46,14 +46,14 @@ class FriendshipTest < ActiveSupport::TestCase
f.person = a
f.friend = b
f.save!
- ta = ActionTracker::Record.last
+ ta = ActionTracker::Record.where(:target_type => "Friendship").last
assert_equal a, ta.user
assert_equal ['b'], ta.get_friend_name
f = Friendship.new
f.person = b
f.friend = a
f.save!
- ta = ActionTracker::Record.last
+ ta = ActionTracker::Record.where(:target_type => "Friendship").last
assert_equal b, ta.user
assert_equal ['a'], ta.get_friend_name
end
@@ -72,4 +72,53 @@ class FriendshipTest < ActiveSupport::TestCase
assert_not_includes p2.friends(true), p1
end
+ should 'add follower when adding friend' do
+ p1 = create_user('testuser1').person
+ p2 = create_user('testuser2').person
+
+ assert_difference 'ProfileFollower.count', 2 do
+ p1.add_friend(p2, 'friends')
+ p2.add_friend(p1, 'friends')
+ end
+
+ assert_includes p1.followers(true), p2
+ assert_includes p2.followers(true), p1
+ end
+
+ should 'remove follower when a friend removal occurs' do
+ p1 = create_user('testuser1').person
+ p2 = create_user('testuser2').person
+
+ p1.add_friend(p2, 'friends')
+ p2.add_friend(p1, 'friends')
+
+ Friendship.remove_friendship(p1, p2)
+
+ assert_not_includes p1.followers(true), p2
+ assert_not_includes p2.followers(true), p1
+ end
+
+ should 'keep friendship intact when stop following' do
+ p1 = create_user('testuser1').person
+ p2 = create_user('testuser2').person
+
+ p1.add_friend(p2, 'friends')
+ p2.add_friend(p1, 'friends')
+
+ p1.unfollow(p2)
+
+ assert_includes p1.friends(true), p2
+ assert_includes p2.friends(true), p1
+ end
+
+ should 'do not add friendship when start following' do
+ p1 = create_user('testuser1').person
+ p2 = create_user('testuser2').person
+
+ p1.follow(p2, 'favorites')
+ p2.follow(p1, 'favorites')
+
+ assert_not_includes p1.friends(true), p2
+ assert_not_includes p2.friends(true), p1
+ end
end
diff --git a/test/unit/notify_activity_to_profiles_job_test.rb b/test/unit/notify_activity_to_profiles_job_test.rb
index 311113d..afa5972 100644
--- a/test/unit/notify_activity_to_profiles_job_test.rb
+++ b/test/unit/notify_activity_to_profiles_job_test.rb
@@ -24,15 +24,15 @@ class NotifyActivityToProfilesJobTest < ActiveSupport::TestCase
end
end
- should 'notify just the users and his friends tracking user actions' do
+ should 'notify just the users and his followers tracking user actions' do
person = fast_create(Person)
community = fast_create(Community)
action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :verb => 'create_article')
refute NotifyActivityToProfilesJob::NOTIFY_ONLY_COMMUNITY.include?(action_tracker.verb)
p1, p2, m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person)
- fast_create(Friendship, :person_id => person.id, :friend_id => p1.id)
- fast_create(Friendship, :person_id => person.id, :friend_id => p2.id)
- fast_create(Friendship, :person_id => p1.id, :friend_id => m1.id)
+ fast_create(ProfileFollower, :profile_id => person.id, :follower_id => p1.id)
+ fast_create(ProfileFollower, :profile_id => person.id, :follower_id => p2.id)
+ fast_create(ProfileFollower, :profile_id => m1.id, :follower_id => person.id)
fast_create(RoleAssignment, :accessor_id => m2.id, :role_id => 3, :resource_id => community.id)
ActionTrackerNotification.delete_all
job = NotifyActivityToProfilesJob.new(action_tracker.id)
@@ -66,23 +66,21 @@ class NotifyActivityToProfilesJobTest < ActiveSupport::TestCase
end
end
- should 'notify users its friends, the community and its members' do
+ should 'notify users its followers, the community and its members' do
person = fast_create(Person)
community = fast_create(Community)
action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :target_id => community.id, :verb => 'create_article')
refute NotifyActivityToProfilesJob::NOTIFY_ONLY_COMMUNITY.include?(action_tracker.verb)
p1, p2, m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person)
- fast_create(Friendship, :person_id => person.id, :friend_id => p1.id)
- fast_create(Friendship, :person_id => person.id, :friend_id => p2.id)
+ fast_create(ProfileFollower, :profile_id => person.id, :follower_id => p1.id)
fast_create(RoleAssignment, :accessor_id => m1.id, :role_id => 3, :resource_id => community.id)
fast_create(RoleAssignment, :accessor_id => m2.id, :role_id => 3, :resource_id => community.id)
ActionTrackerNotification.delete_all
job = NotifyActivityToProfilesJob.new(action_tracker.id)
job.perform
process_delayed_job_queue
-
- assert_equal 6, ActionTrackerNotification.count
- [person, community, p1, p2, m1, m2].each do |profile|
+ assert_equal 5, ActionTrackerNotification.count
+ [person, community, p1, m1, m2].each do |profile|
notification = ActionTrackerNotification.find_by profile_id: profile.id
assert_equal action_tracker, notification.action_tracker
end
@@ -119,8 +117,8 @@ class NotifyActivityToProfilesJobTest < ActiveSupport::TestCase
action_tracker = fast_create(ActionTracker::Record, :user_type => 'Profile', :user_id => person.id, :target_type => 'Profile', :target_id => community.id, :verb => 'join_community')
refute NotifyActivityToProfilesJob::NOTIFY_ONLY_COMMUNITY.include?(action_tracker.verb)
p1, p2, m1, m2 = fast_create(Person), fast_create(Person), fast_create(Person), fast_create(Person)
- fast_create(Friendship, :person_id => person.id, :friend_id => p1.id)
- fast_create(Friendship, :person_id => person.id, :friend_id => p2.id)
+ fast_create(ProfileFollower, :profile_id => person.id, :follower_id => p1.id)
+ fast_create(ProfileFollower, :profile_id => person.id, :follower_id => p2.id)
fast_create(RoleAssignment, :accessor_id => m1.id, :role_id => 3, :resource_id => community.id)
fast_create(RoleAssignment, :accessor_id => m2.id, :role_id => 3, :resource_id => community.id)
ActionTrackerNotification.delete_all
diff --git a/test/unit/person_notifier_test.rb b/test/unit/person_notifier_test.rb
index 0a37d1e..6b1c4a1 100644
--- a/test/unit/person_notifier_test.rb
+++ b/test/unit/person_notifier_test.rb
@@ -178,6 +178,7 @@ class PersonNotifierTest < ActiveSupport::TestCase
update_product: -> { create Product, profile: @profile, product_category: create(ProductCategory, environment: Environment.default) },
remove_product: -> { create Product, profile: @profile, product_category: create(ProductCategory, environment: Environment.default) },
favorite_enterprise: -> { create FavoriteEnterprisePerson, enterprise: create(Enterprise), person: @member },
+ new_follower: -> { @member }
}
ActionTrackerConfig.verb_names.each do |verb|
@@ -197,6 +198,7 @@ class PersonNotifierTest < ActiveSupport::TestCase
'friend_url' => '/', 'friend_profile_custom_icon' => [], 'friend_name' => ['joe'],
'resource_name' => ['resource'], 'resource_profile_custom_icon' => [], 'resource_url' => ['/'],
'enterprise_name' => 'coop', 'enterprise_url' => '/coop',
+ 'follower_url' => '/', 'follower_profile_custom_icon' => [], 'follower_name' => ['joe'],
'view_url'=> ['/'], 'thumbnail_path' => ['1'],
}
a.get_url = ''
diff --git a/test/unit/person_test.rb b/test/unit/person_test.rb
index 440a8f6..67dffd0 100644
--- a/test/unit/person_test.rb
+++ b/test/unit/person_test.rb
@@ -728,7 +728,7 @@ class PersonTest < ActiveSupport::TestCase
assert_equal [s4], p2.scraps_received.not_replies
end
- should "the followed_by method be protected and true to the person friends and herself by default" do
+ should "the followed_by method return true to the person friends and herself by default" do
p1 = fast_create(Person)
p2 = fast_create(Person)
p3 = fast_create(Person)
@@ -740,9 +740,9 @@ class PersonTest < ActiveSupport::TestCase
assert p1.is_a_friend?(p4)
assert_equal true, p1.send(:followed_by?,p1)
- assert_equal true, p1.send(:followed_by?,p2)
- assert_equal true, p1.send(:followed_by?,p4)
- assert_equal false, p1.send(:followed_by?,p3)
+ assert_equal true, p2.send(:followed_by?,p1)
+ assert_equal true, p4.send(:followed_by?,p1)
+ assert_equal false, p3.send(:followed_by?,p1)
end
should "the person follows her friends and herself by default" do
@@ -757,9 +757,9 @@ class PersonTest < ActiveSupport::TestCase
assert p4.is_a_friend?(p1)
assert_equal true, p1.follows?(p1)
- assert_equal true, p1.follows?(p2)
- assert_equal true, p1.follows?(p4)
- assert_equal false, p1.follows?(p3)
+ assert_equal true, p2.follows?(p1)
+ assert_equal true, p4.follows?(p1)
+ assert_equal false, p3.follows?(p1)
end
should "a person member of a community follows the community" do
@@ -836,18 +836,18 @@ class PersonTest < ActiveSupport::TestCase
assert_nil Scrap.find_by(id: scrap.id)
end
- should "the tracked action be notified to person friends and herself" do
+ should "the tracked action be notified to person followers and herself" do
Person.destroy_all
p1 = fast_create(Person)
p2 = fast_create(Person)
p3 = fast_create(Person)
p4 = fast_create(Person)
- p1.add_friend(p2)
- assert p1.is_a_friend?(p2)
- refute p1.is_a_friend?(p3)
- p1.add_friend(p4)
- assert p1.is_a_friend?(p4)
+ p2.follow(p1)
+ assert p2.follows?(p1)
+ refute p3.follows?(p1)
+ p4.follow(p1)
+ assert p4.follows?(p1)
action_tracker = fast_create(ActionTracker::Record, :user_id => p1.id)
ActionTrackerNotification.delete_all
@@ -880,17 +880,17 @@ class PersonTest < ActiveSupport::TestCase
end
end
- should "the tracked action notify friends with one delayed job process" do
+ should "the tracked action notify followers with one delayed job process" do
p1 = fast_create(Person)
p2 = fast_create(Person)
p3 = fast_create(Person)
p4 = fast_create(Person)
- p1.add_friend(p2)
- assert p1.is_a_friend?(p2)
- refute p1.is_a_friend?(p3)
- p1.add_friend(p4)
- assert p1.is_a_friend?(p4)
+ p2.follow(p1)
+ assert p2.follows?(p1)
+ refute p3.follows?(p1)
+ p4.follow(p1)
+ assert p4.follows?(p1)
action_tracker = fast_create(ActionTracker::Record, :user_id => p1.id)
@@ -1035,11 +1035,13 @@ class PersonTest < ActiveSupport::TestCase
p2 = create_user('p2').person
p3 = create_user('p3').person
c = fast_create(Community, :name => "Foo")
+
c.add_member(p1)
process_delayed_job_queue
c.add_member(p3)
process_delayed_job_queue
- assert_equal 4, ActionTracker::Record.count
+
+ assert_equal 5, ActionTracker::Record.count
assert_equal 5, ActionTrackerNotification.count
has_add_member_notification = false
ActionTrackerNotification.all.map do |notification|
diff --git a/test/unit/profile_followers_test.rb b/test/unit/profile_followers_test.rb
new file mode 100644
index 0000000..e3fe36c
--- /dev/null
+++ b/test/unit/profile_followers_test.rb
@@ -0,0 +1,68 @@
+require_relative "../test_helper"
+
+class ProfileFollowersTest < ActiveSupport::TestCase
+
+ should 'a person follow another' do
+ p1 = create_user('person_test').person
+ p2 = create_user('person_test_2').person
+
+ assert_difference 'ProfileFollower.count' do
+ p1.follow(p2)
+ end
+
+ assert_includes p2.followers(true), p1
+ assert_not_includes p1.followers(true), p2
+ end
+
+ should 'a person unfollow another person' do
+ p1 = create_user('person_test').person
+ p2 = create_user('person_test_2').person
+
+ p1.follow(p2)
+
+ assert_difference 'ProfileFollower.count', -1 do
+ p1.unfollow(p2)
+ end
+
+ assert_not_includes p2.followers(true), p1
+ end
+
+ should 'get the followed persons for a profile' do
+ p1 = create_user('person_test').person
+ p2 = create_user('person_test_2').person
+ p3 = create_user('person_test_3').person
+
+ p1.follow(p2)
+ p1.follow(p3)
+
+ assert_equivalent p1.following_profiles, [p2,p3]
+ assert_equivalent Profile.following_profiles(p1), [p2,p3]
+ end
+
+ should 'not follow same person twice' do
+ p1 = create_user('person_test').person
+ p2 = create_user('person_test_2').person
+
+ assert_difference 'ProfileFollower.count' do
+ p1.follow(p2)
+ p1.follow(p2)
+ end
+
+ assert_equivalent p1.following_profiles, [p2]
+ assert_equivalent p2.followers, [p1]
+ end
+
+ should 'show the correct message when a profile is followed by the same person' do
+ p1 = create_user('person_test').person
+ p2 = create_user('person_test_2').person
+
+ p1.follow(p2)
+ profile_follower = ProfileFollower.new
+ profile_follower.follower = p1
+ profile_follower.profile = p2
+ profile_follower.valid?
+
+ assert_includes profile_follower.errors.messages[:profile_id],
+ "can't follow the same profile twice"
+ end
+end
diff --git a/test/unit/scrap_test.rb b/test/unit/scrap_test.rb
index fb68094..49e5c20 100644
--- a/test/unit/scrap_test.rb
+++ b/test/unit/scrap_test.rb
@@ -125,11 +125,11 @@ class ScrapTest < ActiveSupport::TestCase
assert_equal c, ta.target
end
- should "notify leave_scrap action tracker verb to friends and itself" do
+ should "notify leave_scrap action tracker verb to followers and itself" do
User.current = create_user
p1 = User.current.person
p2 = create_user.person
- p1.add_friend(p2)
+ p2.add_friend(p1)
process_delayed_job_queue
s = Scrap.new
s.sender= p1
@@ -180,11 +180,11 @@ class ScrapTest < ActiveSupport::TestCase
assert_equal p, ta.user
end
- should "notify leave_scrap_to_self action tracker verb to friends and itself" do
+ should "notify leave_scrap_to_self action tracker verb to followers and itself" do
User.current = create_user
p1 = User.current.person
p2 = create_user.person
- p1.add_friend(p2)
+ p2.add_friend(p1)
ActionTrackerNotification.delete_all
Delayed::Job.delete_all
s = Scrap.new
--
libgit2 0.21.2