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)
field(:source_data, :map, default: %{})
field(:note_count, :integer, default: 0)
field(:follower_count, :integer, default: 0)
- # Should be filled in only for remote users
- field(:following_count, :integer, default: nil)
+ field(:following_count, :integer, default: 0)
field(:locked, :boolean, default: false)
field(:confirmation_pending, :boolean, default: false)
field(:password_reset_pending, :boolean, default: false)
field(:mascot, :map, default: nil)
field(:emoji, {:array, :map}, default: [])
field(:pleroma_settings_store, :map, default: %{})
- field(:fields, {:array, :map}, default: nil)
+ field(:fields, {:array, :map}, default: [])
field(:raw_fields, {:array, :map}, default: [])
field(:discoverable, :boolean, default: false)
field(:invisible, :boolean, default: false)
timestamps()
end
+ @doc "Returns if the user should be allowed to authenticate"
+ def auth_active?(%User{deactivated: true}), do: false
+
def auth_active?(%User{confirmation_pending: true}),
do: !Pleroma.Config.get([:instance, :account_activation_required])
def visible_for?(user, for_user \\ nil)
+ def visible_for?(%User{invisible: true}, _), do: false
+
def visible_for?(%User{id: user_id}, %User{id: for_id}) when user_id == for_id, do: true
def visible_for?(%User{} = user, for_user) do
def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
def user_info(%User{} = user, args \\ %{}) do
- following_count =
- Map.get(args, :following_count, user.following_count || following_count(user))
-
+ following_count = Map.get(args, :following_count, user.following_count)
follower_count = Map.get(args, :follower_count, user.follower_count)
%{
note_count: user.note_count,
locked: user.locked,
confirmation_pending: user.confirmation_pending,
- default_scope: user.default_scope
+ default_scope: user.default_scope,
+ follower_count: follower_count,
+ following_count: following_count
}
- |> Map.put(:following_count, following_count)
- |> Map.put(:follower_count, follower_count)
end
def follow_state(%User{} = user, %User{} = target) do
from(u in query, where: u.deactivated != ^true)
end
- def following_count(%User{following: []}), do: 0
-
- def following_count(%User{} = user) do
- user
- |> get_friends_query()
- |> Repo.aggregate(:count, :id)
- end
-
- @info_fields [
- :banner,
- :background,
- :source_data,
- :note_count,
- :follower_count,
- :following_count,
- :locked,
- :confirmation_pending,
- :password_reset_pending,
- :confirmation_token,
- :default_scope,
- :blocks,
- :domain_blocks,
- :mutes,
- :muted_reblogs,
- :muted_notifications,
- :subscribers,
- :deactivated,
- :no_rich_text,
- :ap_enabled,
- :is_moderator,
- :is_admin,
- :show_role,
- :settings,
- :magic_key,
- :uri,
- :hide_followers_count,
- :hide_follows_count,
- :hide_followers,
- :hide_follows,
- :hide_favorites,
- :unread_conversation_count,
- :pinned_activities,
- :email_notifications,
- :mascot,
- :emoji,
- :pleroma_settings_store,
- :fields,
- :raw_fields,
- :discoverable,
- :invisible,
- :skip_thread_containment,
- :notification_settings
- ]
-
- def info_fields, do: @info_fields
+ defdelegate following_count(user), to: FollowingRelationship
defp truncate_fields_param(params) do
if Map.has_key?(params, :fields) do
:bio,
:name,
:avatar,
- :following,
:locked,
:no_rich_text,
:default_scope,
followers = ap_followers(%User{nickname: get_field(changeset, :nickname)})
changeset
- |> put_change(:following, [followers])
|> put_change(:follower_address, followers)
end
end
end
+ def try_send_confirmation_email(users) do
+ Enum.each(users, &try_send_confirmation_email/1)
+ end
+
def needs_update?(%User{local: true}), do: false
def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true
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, locked: true}) do
- {:ok, follower}
+ def maybe_direct_follow(%User{} = follower, %User{local: true, 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
- )
-
- {1, [follower]} = Repo.update_all(q, [])
-
- Enum.each(followeds, &update_follower_count/1)
+ followeds
+ |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end)
+ |> Enum.each(&follow(follower, &1, "accept"))
set_cache(follower)
end
- def follow(%User{} = follower, %User{} = followed) do
+ defdelegate following(user), to: FollowingRelationship
+
+ def follow(%User{} = follower, %User{} = followed, state \\ "accept") do
deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
- ap_followers = followed.follower_address
cond do
followed.deactivated ->
{:error, "Could not follow user: #{followed.nickname} blocked you."}
true ->
- q =
- from(u in User,
- where: u.id == ^follower.id,
- update: [push: [following: ^ap_followers]],
- select: u
- )
-
- {1, [follower]} = Repo.update_all(q, [])
-
- follower = maybe_update_following_count(follower)
+ FollowingRelationship.follow(follower, followed, state)
{:ok, _} = update_follower_count(followed)
- set_cache(follower)
+ follower
+ |> update_following_count()
+ |> set_cache()
end
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, [])
-
- follower = maybe_update_following_count(follower)
+ FollowingRelationship.unfollow(follower, followed)
{:ok, followed} = update_follower_count(followed)
- set_cache(follower)
+ {:ok, follower} =
+ follower
+ |> update_following_count()
+ |> set_cache()
{:ok, follower, Utils.fetch_latest_follow(follower, followed)}
else
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.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
end
end
- @spec maybe_update_following_count(User.t()) :: User.t()
- def maybe_update_following_count(%User{local: false} = user) do
+ @spec update_following_count(User.t()) :: User.t()
+ def update_following_count(%User{local: false} = user) do
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
maybe_fetch_follow_information(user)
else
end
end
- def maybe_update_following_count(user), do: user
+ def update_following_count(%User{local: true} = user) do
+ following_count = FollowingRelationship.following_count(user)
+
+ user
+ |> follow_information_changeset(%{following_count: following_count})
+ |> Repo.update!()
+ end
def set_unread_conversation_count(%User{local: true} = user) do
unread_query = Participation.unread_conversation_count_for_user(user)
end
end
- def set_unread_conversation_count(_), do: :noop
+ def set_unread_conversation_count(user), do: {:ok, user}
def increment_unread_conversation_count(conversation, %User{local: true} = user) do
unread_query =
end
end
- 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
+ def increment_unread_conversation_count(_, user), do: {:ok, user}
@spec get_users_from_set([String.t()], boolean()) :: [User.t()]
def get_users_from_set(ap_ids, local_only \\ true) do
if following?(blocked, blocker), do: unfollow(blocked, blocker)
{:ok, blocker} = update_follower_count(blocker)
-
+ {:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked)
add_to_block(blocker, ap_id)
end
def deactivate(%User{} = user, status) do
with {:ok, user} <- set_activation_status(user, status) do
- Enum.each(get_followers(user), &invalidate_cache/1)
- Enum.each(get_friends(user), &update_follower_count/1)
+ user
+ |> get_followers()
+ |> Enum.filter(& &1.local)
+ |> Enum.each(fn follower ->
+ follower |> update_following_count() |> set_cache()
+ end)
+
+ # Only update local user counts, remote will be update during the next pull.
+ user
+ |> get_friends()
+ |> Enum.filter(& &1.local)
+ |> Enum.each(&update_follower_count/1)
{:ok, user}
end
end
end
- @doc "Creates an internal service actor by URI if missing. Optionally takes nickname for addressing."
+ @doc """
+ Creates an internal service actor by URI if missing.
+ Optionally takes nickname for addressing.
+ """
def get_or_create_service_actor_by_ap_id(uri, nickname \\ nil) do
- with %User{} = user <- get_cached_by_ap_id(uri) do
- user
- else
- _ ->
- {:ok, user} =
- %User{}
- |> cast(%{}, [:ap_id, :nickname, :local])
- |> put_change(:ap_id, uri)
- |> put_change(:nickname, nickname)
- |> put_change(:local, true)
- |> put_change(:follower_address, uri <> "/followers")
- |> Repo.insert()
+ with user when is_nil(user) <- get_cached_by_ap_id(uri) do
+ {:ok, user} =
+ %User{
+ invisible: true,
+ local: true,
+ ap_id: uri,
+ nickname: nickname,
+ follower_address: uri <> "/followers"
+ }
+ |> Repo.insert()
- user
+ user
end
end
|> update_and_set_cache()
end
+ @spec toggle_confirmation([User.t()]) :: [{:ok, User.t()} | {:error, Changeset.t()}]
+ def toggle_confirmation(users) do
+ Enum.map(users, &toggle_confirmation/1)
+ end
+
def get_mascot(%{mascot: %{} = mascot}) when not is_nil(mascot) do
mascot
end