def run(["update_users_following_followers_counts"]) do
start_pleroma()
- users = Repo.all(User)
- Enum.each(users, &User.remove_duplicated_following/1)
- Enum.each(users, &User.update_follower_count/1)
+ User
+ |> Repo.all()
+ |> Enum.each(&User.update_follower_count/1)
end
def run(["prune_objects" | args]) do
def run(["list"]) do
start_pleroma()
- with %User{following: following} = _user <- Relay.get_actor() do
- following
+ with %User{} = user <- Relay.get_actor() do
+ user
+ |> User.following()
|> Enum.map(fn entry -> URI.parse(entry).host end)
|> Enum.uniq()
|> Enum.each(&shell_info(&1))
user = User.get_cached_by_id(user.id)
- if Enum.empty?(user.following) do
+ if Enum.empty?(User.get_friends(user)) do
shell_info("Successfully unsubscribed all followers from #{user.nickname}")
end
else
|> Map.put("user", user)
activities =
- [user.ap_id | user.following]
+ [user.ap_id | Pleroma.User.following(user)]
|> ActivityPub.fetch_activities(params)
Enum.each(activities, fn activity ->
--- /dev/null
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.FollowingRelationship do
+ use Ecto.Schema
+
+ import Ecto.Changeset
+ import Ecto.Query
+
+ alias FlakeId.Ecto.CompatType
+ alias Pleroma.Repo
+ alias Pleroma.User
+
+ schema "following_relationships" do
+ field(:state, :string, default: "accept")
+
+ belongs_to(:follower, User, type: CompatType)
+ belongs_to(:following, User, type: CompatType)
+
+ timestamps()
+ end
+
+ def changeset(%__MODULE__{} = following_relationship, attrs) do
+ following_relationship
+ |> cast(attrs, [:state])
+ |> put_assoc(:follower, attrs.follower)
+ |> put_assoc(:following, attrs.following)
+ |> validate_required([:state, :follower, :following])
+ end
+
+ def get(%User{} = follower, %User{} = following) do
+ __MODULE__
+ |> where(follower_id: ^follower.id, following_id: ^following.id)
+ |> Repo.one()
+ end
+
+ def update(follower, following, "reject"), do: unfollow(follower, following)
+
+ def update(%User{} = follower, %User{} = following, state) do
+ case get(follower, following) do
+ nil ->
+ follow(follower, following, state)
+
+ following_relationship ->
+ following_relationship
+ |> cast(%{state: state}, [:state])
+ |> validate_required([:state])
+ |> Repo.update()
+ end
+ end
+
+ def follow(%User{} = follower, %User{} = following, state \\ "accept") do
+ %__MODULE__{}
+ |> changeset(%{follower: follower, following: following, state: state})
+ |> Repo.insert(on_conflict: :nothing)
+ end
+
+ def unfollow(%User{} = follower, %User{} = following) do
+ case get(follower, following) do
+ nil -> {:ok, nil}
+ %__MODULE__{} = following_relationship -> Repo.delete(following_relationship)
+ end
+ end
+
+ def follower_count(%User{} = user) do
+ %{followers: user, deactivated: false}
+ |> User.Query.build()
+ |> Repo.aggregate(:count, :id)
+ end
+
+ def following_count(%User{id: nil}), do: 0
+
+ def following_count(%User{} = user) do
+ %{friends: user, deactivated: false}
+ |> User.Query.build()
+ |> Repo.aggregate(:count, :id)
+ end
+
+ def get_follow_requests(%User{id: id}) do
+ __MODULE__
+ |> join(:inner, [r], f in assoc(r, :follower))
+ |> where([r], r.state == "pending")
+ |> where([r], r.following_id == ^id)
+ |> select([r, f], f)
+ |> Repo.all()
+ end
+
+ def following?(%User{id: follower_id}, %User{id: followed_id}) do
+ __MODULE__
+ |> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept")
+ |> Repo.exists?()
+ end
+
+ def following(%User{} = user) do
+ following =
+ __MODULE__
+ |> join(:inner, [r], u in User, on: r.following_id == u.id)
+ |> where([r], r.follower_id == ^user.id)
+ |> where([r], r.state == "accept")
+ |> select([r, u], u.follower_address)
+ |> Repo.all()
+
+ if user.nickname in [nil, "internal.fetch"] do
+ following
+ else
+ [user.follower_address | following]
+ end
+ end
+end
alias Pleroma.Activity
alias Pleroma.Conversation.Participation
alias Pleroma.Delivery
+ alias Pleroma.FollowingRelationship
alias Pleroma.Keys
alias Pleroma.Notification
alias Pleroma.Object
field(:password, :string, virtual: true)
field(:password_confirmation, :string, virtual: true)
field(:keys, :string)
- field(:following, {:array, :string}, default: [])
field(:ap_id, :string)
field(:avatar, :map)
field(:local, :boolean, default: true)
)
end
- def following_count(%User{following: []}), do: 0
-
- def following_count(%User{} = user) do
- user
- |> get_friends_query()
- |> Repo.aggregate(:count, :id)
- end
+ defdelegate following_count(user), to: FollowingRelationship
defp truncate_if_exists(params, key, max_length) do
if Map.has_key?(params, key) and is_binary(params[key]) do
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
struct
- |> cast(params, [:bio, :name, :avatar, :following])
+ |> cast(params, [:bio, :name, :avatar])
|> unique_constraint(:nickname)
|> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: bio_limit)
followers = ap_followers(%User{nickname: get_field(changeset, :nickname)})
changeset
- |> put_change(:following, [followers])
|> put_change(:follower_address, followers)
end
def needs_update?(_), do: true
@spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
- def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do
- {:ok, follower}
+ def maybe_direct_follow(
+ %User{} = follower,
+ %User{local: true, info: %{locked: true}} = followed
+ ) do
+ follow(follower, followed, "pending")
end
def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do
@doc "A mass follow for local users. Respects blocks in both directions but does not create activities."
@spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
def follow_all(follower, followeds) do
- followed_addresses =
- followeds
- |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end)
- |> Enum.map(fn %{follower_address: fa} -> fa end)
-
- q =
- from(u in User,
- where: u.id == ^follower.id,
- update: [
- set: [
- following:
- fragment(
- "array(select distinct unnest (array_cat(?, ?)))",
- u.following,
- ^followed_addresses
- )
- ]
- ],
- select: u
- )
+ followeds =
+ Enum.reject(followeds, fn followed ->
+ blocks?(follower, followed) || blocks?(followed, follower)
+ end)
- {1, [follower]} = Repo.update_all(q, [])
+ Enum.each(followeds, &follow(follower, &1, "accept"))
Enum.each(followeds, &update_follower_count/1)
set_cache(follower)
end
- def follow(%User{} = follower, %User{info: info} = followed) do
+ defdelegate following(user), to: FollowingRelationship
+
+ def follow(%User{} = follower, %User{info: info} = followed, state \\ "accept") do
deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
- ap_followers = followed.follower_address
cond do
info.deactivated ->
Websub.subscribe(follower, followed)
end
- q =
- from(u in User,
- where: u.id == ^follower.id,
- update: [push: [following: ^ap_followers]],
- select: u
- )
-
- {1, [follower]} = Repo.update_all(q, [])
+ FollowingRelationship.follow(follower, followed, state)
follower = maybe_update_following_count(follower)
end
def unfollow(%User{} = follower, %User{} = followed) do
- ap_followers = followed.follower_address
-
if following?(follower, followed) and follower.ap_id != followed.ap_id do
- q =
- from(u in User,
- where: u.id == ^follower.id,
- update: [pull: [following: ^ap_followers]],
- select: u
- )
-
- {1, [follower]} = Repo.update_all(q, [])
+ FollowingRelationship.unfollow(follower, followed)
follower = maybe_update_following_count(follower)
end
end
- @spec following?(User.t(), User.t()) :: boolean
- def following?(%User{} = follower, %User{} = followed) do
- Enum.member?(follower.following, followed.follower_address)
- end
+ defdelegate following?(follower, followed), to: FollowingRelationship
def locked?(%User{} = user) do
user.info.locked || false
|> Repo.all()
end
- @spec get_follow_requests(User.t()) :: {:ok, [User.t()]}
- def get_follow_requests(%User{} = user) do
- user
- |> Activity.follow_requests_for_actor()
- |> join(:inner, [a], u in User, on: a.actor == u.ap_id)
- |> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address]))
- |> group_by([a, u], u.id)
- |> select([a, u], u)
- |> Repo.all()
- end
+ defdelegate get_follow_requests(user), to: FollowingRelationship
def increase_note_count(%User{} = user) do
User
def increment_unread_conversation_count(_, _), do: :noop
- def remove_duplicated_following(%User{following: following} = user) do
- uniq_following = Enum.uniq(following)
-
- if length(following) == length(uniq_following) do
- {:ok, user}
- else
- user
- |> update_changeset(%{following: uniq_following})
- |> update_and_set_cache()
- end
- end
-
@spec get_users_from_set([String.t()], boolean()) :: [User.t()]
def get_users_from_set(ap_ids, local_only \\ true) do
criteria = %{ap_id: ap_ids, deactivated: false}
"""
import Ecto.Query
import Pleroma.Web.AdminAPI.Search, only: [not_empty_string: 1]
+
+ alias Pleroma.FollowingRelationship
alias Pleroma.User
@type criteria ::
|> where([u], not is_nil(u.nickname))
end
- defp compose_query({:followers, %User{id: id, follower_address: follower_address}}, query) do
- where(query, [u], fragment("? <@ ?", ^[follower_address], u.following))
+ defp compose_query({:followers, %User{id: id}}, query) do
+ query
|> where([u], u.id != ^id)
+ |> join(:inner, [u], r in FollowingRelationship,
+ as: :relationships,
+ on: r.following_id == ^id and r.follower_id == u.id
+ )
+ |> where([relationships: r], r.state == "accept")
end
- defp compose_query({:friends, %User{id: id, following: following}}, query) do
- where(query, [u], u.follower_address in ^following)
+ defp compose_query({:friends, %User{id: id}}, query) do
+ query
|> where([u], u.id != ^id)
+ |> join(:inner, [u], r in FollowingRelationship,
+ as: :relationships,
+ on: r.following_id == u.id and r.follower_id == ^id
+ )
+ |> where([relationships: r], r.state == "accept")
end
defp compose_query({:recipients_from_activity, to}, query) do
- where(query, [u], u.ap_id in ^to or fragment("? && ?", u.following, ^to))
+ query
+ |> join(:left, [u], r in FollowingRelationship,
+ as: :relationships,
+ on: r.follower_id == u.id
+ )
+ |> join(:left, [relationships: r], f in User,
+ as: :following,
+ on: f.id == r.following_id
+ )
+ |> where(
+ [u, following: f, relationships: r],
+ u.ap_id in ^to or (f.follower_address in ^to and r.state == "accept")
+ )
end
defp compose_query({:order_by, key}, query) do
public = [Pleroma.Constants.as_public()]
recipients =
- if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public
+ if opts["user"],
+ do: [opts["user"].ap_id | User.following(opts["user"])] ++ public,
+ else: public
from(activity in Activity)
|> maybe_preload_objects(opts)
defp user_activities_recipients(%{"reading_user" => reading_user}) do
if reading_user do
- [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]
+ [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
else
[Pleroma.Constants.as_public()]
end
when page? in [true, "true"] do
activities =
if params["max_id"] do
- ActivityPub.fetch_activities([user.ap_id | user.following], %{
+ ActivityPub.fetch_activities([user.ap_id | User.following(user)], %{
"max_id" => params["max_id"],
"limit" => 10
})
else
- ActivityPub.fetch_activities([user.ap_id | user.following], %{"limit" => 10})
+ ActivityPub.fetch_activities([user.ap_id | User.following(user)], %{"limit" => 10})
end
conn
A module to handle coding from internal to wire ActivityPub and back.
"""
alias Pleroma.Activity
+ alias Pleroma.FollowingRelationship
alias Pleroma.Object
alias Pleroma.Object.Containment
alias Pleroma.Repo
{_, false} <- {:user_locked, User.locked?(followed)},
{_, {:ok, follower}} <- {:follow, User.follow(follower, followed)},
{_, {:ok, _}} <-
- {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")} do
+ {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")},
+ {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do
ActivityPub.accept(%{
to: [follower.ap_id],
actor: followed,
else
{:user_blocked, true} ->
{:ok, _} = Utils.update_follow_state_for_all(activity, "reject")
+ {:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject")
ActivityPub.reject(%{
to: [follower.ap_id],
{:follow, {:error, _}} ->
{:ok, _} = Utils.update_follow_state_for_all(activity, "reject")
+ {:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject")
ActivityPub.reject(%{
to: [follower.ap_id],
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
- {:ok, _follower} = User.follow(follower, followed) do
+ {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do
ActivityPub.accept(%{
to: follow_activity.data["to"],
type: "Accept",
{:ok, follow_activity} <- get_follow_activity(follow_object, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"),
%User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]),
+ {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"),
{:ok, activity} <-
ActivityPub.reject(%{
to: follow_activity.data["to"],
object: follow_activity.data["id"],
local: false
}) do
- User.unfollow(follower, followed)
-
{:ok, activity}
else
_e -> :error
def perform(:user_upgrade, user) do
# we pass a fake user so that the followers collection is stripped away
old_follower_address = User.ap_followers(%User{nickname: user.nickname})
-
- q =
- from(
- u in User,
- where: ^old_follower_address in u.following,
- update: [
- set: [
- following:
- fragment(
- "array_replace(?,?,?)",
- u.following,
- ^old_follower_address,
- ^user.follower_address
- )
- ]
- ]
- )
-
- Repo.update_all(q, [])
-
maybe_retire_websub(user.ap_id)
- q =
- from(
- a in Activity,
- where: ^old_follower_address in a.recipients,
- update: [
- set: [
- recipients:
- fragment(
- "array_replace(?,?,?)",
- a.recipients,
- ^old_follower_address,
- ^user.follower_address
- )
- ]
+ from(
+ a in Activity,
+ where: ^old_follower_address in a.recipients,
+ update: [
+ set: [
+ recipients:
+ fragment(
+ "array_replace(?,?,?)",
+ a.recipients,
+ ^old_follower_address,
+ ^user.follower_address
+ )
]
- )
-
- Repo.update_all(q, [])
+ ]
+ )
+ |> Repo.update_all([])
end
def upgrade_user_from_ap_id(ap_id) do
end
def visible_for_user?(activity, user) do
- x = [user.ap_id | user.following]
+ x = [user.ap_id | User.following(user)]
y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || [])
visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y))
end
alias Pleroma.Activity
alias Pleroma.ActivityExpiration
alias Pleroma.Conversation.Participation
+ alias Pleroma.FollowingRelationship
alias Pleroma.Object
alias Pleroma.ThreadMute
alias Pleroma.User
with {:ok, follower} <- User.follow(follower, followed),
%Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
+ {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept"),
{:ok, _activity} <-
ActivityPub.accept(%{
to: [follower.ap_id],
def reject_follow_request(follower, followed) do
with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed),
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"),
+ {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"),
{:ok, _activity} <-
ActivityPub.reject(%{
to: [follower.ap_id],
alias Pleroma.Pagination
alias Pleroma.Plugs.OAuthScopesPlug
+ alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct])
|> Map.put("muting_user", user)
|> Map.put("user", user)
- recipients = [user.ap_id | user.following]
+ recipients = [user.ap_id | User.following(user)]
activities =
recipients
# we must filter the following list for the user to avoid leaking statuses the user
# does not actually have permission to see (for more info, peruse security issue #270).
+
+ user_following = User.following(user)
+
activities =
following
- |> Enum.filter(fn x -> x in user.following end)
+ |> Enum.filter(fn x -> x in user_following end)
|> ActivityPub.fetch_activities_bounded(following, params)
|> Enum.reverse()
recipients =
if for_user do
- [Pleroma.Constants.as_public()] ++ [for_user.ap_id | for_user.following]
+ [Pleroma.Constants.as_public()] ++ [for_user.ap_id | User.following(for_user)]
else
[Pleroma.Constants.as_public()]
end
execute(update_thread_visibility(), restore_thread_visibility())
end
- # The only difference with the original verion: `actor_user` replaced with `actor_user_following`
+ # The only difference between the original version: `actor_user` replaced with `actor_user_following`
def update_thread_visibility do
"""
CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar) RETURNS boolean AS $$
user
| ap_id: User.ap_id(user),
follower_address: User.ap_followers(user),
- following_address: User.ap_following(user),
- following: [User.ap_id(user)]
+ following_address: User.ap_following(user)
}
end
describe "running update_users_following_followers_counts" do
test "following and followers count are updated" do
[user, user2] = insert_pair(:user)
- {:ok, %User{following: following, info: info} = user} = User.follow(user, user2)
+ {:ok, %User{info: info} = user} = User.follow(user, user2)
+
+ following = User.following(user)
assert length(following) == 2
assert info.follower_count == 0
{:ok, user} =
user
- |> Ecto.Changeset.change(%{following: following ++ following})
|> User.change_info(&Ecto.Changeset.change(&1, %{follower_count: 3}))
|> Repo.update()
- assert length(user.following) == 4
assert user.info.follower_count == 3
assert :ok == Mix.Tasks.Pleroma.Database.run(["update_users_following_followers_counts"])
user = User.get_by_id(user.id)
- assert length(user.following) == 2
+ assert length(User.following(user)) == 2
assert user.info.follower_count == 0
end
end
target_user = User.get_cached_by_ap_id(target_instance)
follow_activity = Utils.fetch_latest_follow(local_user, target_user)
User.follow(local_user, target_user)
- assert "#{target_instance}/followers" in refresh_record(local_user).following
+ assert "#{target_instance}/followers" in User.following(local_user)
Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance])
cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"])
assert undo_activity.data["type"] == "Undo"
assert undo_activity.data["actor"] == local_user.ap_id
assert undo_activity.data["object"] == cancelled_activity.data
- refute "#{target_instance}/followers" in refresh_record(local_user).following
+ refute "#{target_instance}/followers" in User.following(local_user)
end
end
refute_receive {:mix_shell, :info, _}
- Pleroma.Web.ActivityPub.Relay.get_actor()
- |> Ecto.Changeset.change(
- following: [
- "http://test-app.com/user/test1",
- "http://test-app.com/user/test1",
- "http://test-app-42.com/user/test1"
- ]
- )
- |> Pleroma.User.update_and_set_cache()
+ relay_user = Relay.get_actor()
+
+ ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"]
+ |> Enum.each(fn ap_id ->
+ {:ok, user} = User.get_or_fetch_by_ap_id(ap_id)
+ User.follow(relay_user, user)
+ end)
:ok = Mix.Tasks.Pleroma.Relay.run(["list"])
- assert_receive {:mix_shell, :info, ["test-app.com"]}
- assert_receive {:mix_shell, :info, ["test-app-42.com"]}
+ assert_receive {:mix_shell, :info, ["mstdn.io"]}
+ assert_receive {:mix_shell, :info, ["mastodon.example.org"]}
end
end
end
describe "running unsubscribe" do
test "user is unsubscribed" do
followed = insert(:user)
- user = insert(:user, %{following: [User.ap_followers(followed)]})
+ user = insert(:user)
+ User.follow(user, followed, "accept")
Mix.Tasks.Pleroma.User.run(["unsubscribe", user.nickname])
assert message =~ "Successfully unsubscribed"
user = User.get_cached_by_nickname(user.nickname)
- assert Enum.empty?(user.following)
+ assert Enum.empty?(User.get_friends(user))
assert user.info.deactivated
end
CommonAPI.follow(pending_follower, locked)
CommonAPI.follow(pending_follower, locked)
CommonAPI.follow(accepted_follower, locked)
- User.follow(accepted_follower, locked)
+ Pleroma.FollowingRelationship.update(accepted_follower, locked, "accept")
- assert [activity] = User.get_follow_requests(locked)
- assert activity
+ assert [^pending_follower] = User.get_follow_requests(locked)
end
test "clears follow requests when requester is blocked" do
followed_two = insert(:user)
{:ok, user} = User.follow_all(user, [followed_zero, followed_one])
- assert length(user.following) == 3
+ assert length(User.following(user)) == 3
{:ok, user} = User.follow_all(user, [followed_one, followed_two])
- assert length(user.following) == 4
+ assert length(User.following(user)) == 4
end
test "follow takes a user and another user" do
followed = User.get_cached_by_ap_id(followed.ap_id)
assert followed.info.follower_count == 1
- assert User.ap_followers(followed) in user.following
+ assert User.ap_followers(followed) in User.following(user)
end
test "can't follow a deactivated users" do
# assert followed.local == false
# {:ok, user} = User.follow(user, followed)
- # assert User.ap_followers(followed) in user.following
+ # assert User.ap_followers(followed) in User.following(user)
# query = from w in WebsubClientSubscription,
# where: w.topic == ^followed.info["topic"]
nickname: "fuser2",
ap_id: "http://localhost:4001/users/fuser2",
follower_address: "http://localhost:4001/users/fuser2/followers",
- following_address: "http://localhost:4001/users/fuser2/following",
- following: [User.ap_followers(followed)]
+ following_address: "http://localhost:4001/users/fuser2/following"
})
+ {:ok, user} = User.follow(user, followed, "accept")
+
{:ok, user, _activity} = User.unfollow(user, followed)
user = User.get_cached_by_id(user.id)
- assert user.following == []
+ assert User.following(user) == [user.follower_address]
end
test "unfollow takes a user and another user" do
followed = insert(:user)
- user = insert(:user, %{following: [User.ap_followers(followed)]})
+ user = insert(:user)
- {:ok, user, _activity} = User.unfollow(user, followed)
+ {:ok, user} = User.follow(user, followed, "accept")
- user = User.get_cached_by_id(user.id)
+ assert User.following(user) == [user.follower_address, followed.follower_address]
+
+ {:ok, user, _activity} = User.unfollow(user, followed)
- assert user.following == []
+ assert User.following(user) == [user.follower_address]
end
test "unfollow doesn't unfollow yourself" do
{:error, _} = User.unfollow(user, user)
- user = User.get_cached_by_id(user.id)
- assert user.following == [user.ap_id]
+ assert User.following(user) == [user.follower_address]
end
end
test "test if a user is following another user" do
followed = insert(:user)
- user = insert(:user, %{following: [User.ap_followers(followed)]})
+ user = insert(:user)
+ User.follow(user, followed, "accept")
assert User.following?(user, followed)
refute User.following?(followed, user)
refute changeset.valid?
end
- test "it sets the password_hash, ap_id and following fields" do
+ test "it sets the password_hash and ap_id" do
changeset = User.register_changeset(%User{}, @full_user_data)
assert changeset.valid?
assert is_binary(changeset.changes[:password_hash])
assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname})
- assert changeset.changes[:following] == [
- User.ap_followers(%User{nickname: @full_user_data.nickname})
- ]
-
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
end
end
end
- describe "remove duplicates from following list" do
- test "it removes duplicates" do
- user = insert(:user)
- follower = insert(:user)
-
- {:ok, %User{following: following} = follower} = User.follow(follower, user)
- assert length(following) == 2
-
- {:ok, follower} =
- follower
- |> User.update_changeset(%{following: following ++ following})
- |> Repo.update()
-
- assert length(follower.following) == 4
-
- {:ok, follower} = User.remove_duplicated_following(follower)
- assert length(follower.following) == 2
- end
-
- test "it does nothing when following is uniq" do
- user = insert(:user)
- follower = insert(:user)
-
- {:ok, follower} = User.follow(follower, user)
- assert length(follower.following) == 2
-
- {:ok, follower} = User.remove_duplicated_following(follower)
- assert length(follower.following) == 2
- end
- end
-
describe "follow_import" do
test "it imports user followings from list" do
[user1, user2, user3] = insert_list(3, :user)
assert [activity] == ActivityPub.fetch_public_activities(%{}) |> Repo.preload(:bookmark)
assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] ==
- ActivityPub.fetch_activities([user2.ap_id | user2.following], %{"user" => user2})
+ ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
+ "user" => user2
+ })
{:ok, _user} = User.deactivate(user)
assert [] == Pleroma.Notification.for_user(user2)
assert [] ==
- ActivityPub.fetch_activities([user2.ap_id | user2.following], %{"user" => user2})
+ ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{
+ "user" => user2
+ })
end
end
{:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster)
- [announce_activity] = ActivityPub.fetch_activities([user.ap_id | user.following])
+ [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)])
assert announce_activity.id == announce.id
end
})
activities =
- ActivityPub.fetch_activities([user1.ap_id | user1.following])
+ ActivityPub.fetch_activities([user1.ap_id | User.following(user1)])
|> Enum.map(fn a -> a.id end)
private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"])
assert length(activities) == 3
activities =
- ActivityPub.fetch_activities([user1.ap_id | user1.following], %{"user" => user1})
+ ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1})
|> Enum.map(fn a -> a.id end)
assert [public_activity.id, private_activity_1.id] == activities
alias Pleroma.Activity
alias Pleroma.Object
+ alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Relay
service_actor = Relay.get_actor()
ActivityPub.follow(service_actor, user)
Pleroma.User.follow(service_actor, user)
- assert "#{user.ap_id}/followers" in refresh_record(service_actor).following
+ assert "#{user.ap_id}/followers" in User.following(service_actor)
assert {:ok, %Activity{} = activity} = Relay.unfollow(user.ap_id)
assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay"
assert user.ap_id in activity.recipients
assert activity.data["type"] == "Undo"
assert activity.data["actor"] == service_actor.ap_id
assert activity.data["to"] == [user.ap_id]
- refute "#{user.ap_id}/followers" in refresh_record(service_actor).following
+ refute "#{user.ap_id}/followers" in User.following(service_actor)
end
end
follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})
})
- user_two = insert(:user, %{following: [user.follower_address]})
+ user_two = insert(:user)
+ Pleroma.FollowingRelationship.follow(user_two, user, "accept")
{:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
{:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"})
refute user.follower_address in unrelated_activity.recipients
user_two = User.get_cached_by_id(user_two.id)
- assert user.follower_address in user_two.following
- refute "..." in user_two.following
+ assert User.following?(user_two, user)
+ refute "..." in User.following(user_two)
end
end
test "returns true if user following to author" do
author = insert(:user)
- user = insert(:user, following: [author.ap_id])
+ user = insert(:user)
+ Pleroma.User.follow(user, author)
activity =
insert(:note_activity,
conn =
build_conn()
- |> assign(:user, follower)
+ |> assign(:user, User.get_cached_by_id(follower.id))
|> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true")
assert %{"showing_reblogs" => true} = json_response(conn, 200)
other_user = insert(:user)
{:ok, _activity} = ActivityPub.follow(other_user, user)
-
- user = User.get_cached_by_id(user.id)
- other_user = User.get_cached_by_id(other_user.id)
+ {:ok, other_user} = User.follow(other_user, user, "pending")
assert User.following?(other_user, user) == false
other_user = insert(:user)
{:ok, _activity} = ActivityPub.follow(other_user, user)
+ {:ok, other_user} = User.follow(other_user, user, "pending")
user = User.get_cached_by_id(user.id)
other_user = User.get_cached_by_id(other_user.id)
test "it doesn't send to user if recipients invalid and thread containment is enabled" do
Pleroma.Config.put([:instance, :skip_thread_containment], false)
author = insert(:user)
- user = insert(:user, following: [author.ap_id])
+ user = insert(:user)
+ User.follow(user, author, "accept")
activity =
insert(:note_activity,
test "it sends message if recipients invalid and thread containment is disabled" do
Pleroma.Config.put([:instance, :skip_thread_containment], true)
author = insert(:user)
- user = insert(:user, following: [author.ap_id])
+ user = insert(:user)
+ User.follow(user, author, "accept")
activity =
insert(:note_activity,
test "it sends message if recipients invalid and thread containment is enabled but user's thread containment is disabled" do
Pleroma.Config.put([:instance, :skip_thread_containment], false)
author = insert(:user)
- user = insert(:user, following: [author.ap_id], info: %{skip_thread_containment: true})
+ user = insert(:user, info: %{skip_thread_containment: true})
+ User.follow(user, author, "accept")
activity =
insert(:note_activity,
|> response(200)
assert response =~ "Account followed!"
- assert user2.follower_address in refresh_record(user).following
+ assert user2.follower_address in User.following(user)
end
test "returns error when user is deactivated", %{conn: conn} do
|> response(200)
assert response =~ "Account followed!"
- assert user2.follower_address in refresh_record(user).following
+ assert user2.follower_address in User.following(user)
end
test "returns error when followee not found", %{conn: conn} do