forked from AkkomaGang/akkoma
Merge branch 'refactor/user' into 'develop'
Simplify updating user's `info` See merge request pleroma/pleroma!1712
This commit is contained in:
commit
48a82c4609
13 changed files with 307 additions and 524 deletions
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
defmodule Mix.Tasks.Pleroma.User do
|
defmodule Mix.Tasks.Pleroma.User do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
import Ecto.Changeset
|
|
||||||
import Mix.Pleroma
|
import Mix.Pleroma
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserInviteToken
|
alias Pleroma.UserInviteToken
|
||||||
|
@ -228,9 +227,9 @@ def run(["unsubscribe", nickname]) do
|
||||||
shell_info("Deactivating #{user.nickname}")
|
shell_info("Deactivating #{user.nickname}")
|
||||||
User.deactivate(user)
|
User.deactivate(user)
|
||||||
|
|
||||||
{:ok, friends} = User.get_friends(user)
|
user
|
||||||
|
|> User.get_friends()
|
||||||
Enum.each(friends, fn friend ->
|
|> Enum.each(fn friend ->
|
||||||
user = User.get_cached_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
shell_info("Unsubscribing #{friend.nickname} from #{user.nickname}")
|
shell_info("Unsubscribing #{friend.nickname} from #{user.nickname}")
|
||||||
|
@ -405,7 +404,7 @@ def run(["delete_activities", nickname]) do
|
||||||
start_pleroma()
|
start_pleroma()
|
||||||
|
|
||||||
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
{:ok, _} = User.delete_user_activities(user)
|
User.delete_user_activities(user)
|
||||||
shell_info("User #{nickname} statuses deleted.")
|
shell_info("User #{nickname} statuses deleted.")
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -443,39 +442,21 @@ def run(["sign_out", nickname]) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp set_moderator(user, value) do
|
defp set_moderator(user, value) do
|
||||||
info_cng = User.Info.admin_api_update(user.info, %{is_moderator: value})
|
{:ok, user} = User.update_info(user, &User.Info.admin_api_update(&1, %{is_moderator: value}))
|
||||||
|
|
||||||
user_cng =
|
|
||||||
Ecto.Changeset.change(user)
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
{:ok, user} = User.update_and_set_cache(user_cng)
|
|
||||||
|
|
||||||
shell_info("Moderator status of #{user.nickname}: #{user.info.is_moderator}")
|
shell_info("Moderator status of #{user.nickname}: #{user.info.is_moderator}")
|
||||||
user
|
user
|
||||||
end
|
end
|
||||||
|
|
||||||
defp set_admin(user, value) do
|
defp set_admin(user, value) do
|
||||||
info_cng = User.Info.admin_api_update(user.info, %{is_admin: value})
|
{:ok, user} = User.update_info(user, &User.Info.admin_api_update(&1, %{is_admin: value}))
|
||||||
|
|
||||||
user_cng =
|
|
||||||
Ecto.Changeset.change(user)
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
{:ok, user} = User.update_and_set_cache(user_cng)
|
|
||||||
|
|
||||||
shell_info("Admin status of #{user.nickname}: #{user.info.is_admin}")
|
shell_info("Admin status of #{user.nickname}: #{user.info.is_admin}")
|
||||||
user
|
user
|
||||||
end
|
end
|
||||||
|
|
||||||
defp set_locked(user, value) do
|
defp set_locked(user, value) do
|
||||||
info_cng = User.Info.user_upgrade(user.info, %{locked: value})
|
{:ok, user} = User.update_info(user, &User.Info.user_upgrade(&1, %{locked: value}))
|
||||||
|
|
||||||
user_cng =
|
|
||||||
Ecto.Changeset.change(user)
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
{:ok, user} = User.update_and_set_cache(user_cng)
|
|
||||||
|
|
||||||
shell_info("Locked status of #{user.nickname}: #{user.info.locked}")
|
shell_info("Locked status of #{user.nickname}: #{user.info.locked}")
|
||||||
user
|
user
|
||||||
|
|
|
@ -106,9 +106,7 @@ def profile_url(%User{info: %{source_data: %{"url" => url}}}), do: url
|
||||||
def profile_url(%User{ap_id: ap_id}), do: ap_id
|
def profile_url(%User{ap_id: ap_id}), do: ap_id
|
||||||
def profile_url(_), do: nil
|
def profile_url(_), do: nil
|
||||||
|
|
||||||
def ap_id(%User{nickname: nickname}) do
|
def ap_id(%User{nickname: nickname}), do: "#{Web.base_url()}/users/#{nickname}"
|
||||||
"#{Web.base_url()}/users/#{nickname}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
|
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
|
||||||
def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
|
def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
|
||||||
|
@ -119,12 +117,9 @@ def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
|
||||||
|
|
||||||
def user_info(%User{} = user, args \\ %{}) do
|
def user_info(%User{} = user, args \\ %{}) do
|
||||||
following_count =
|
following_count =
|
||||||
if args[:following_count],
|
Map.get(args, :following_count, user.info.following_count || following_count(user))
|
||||||
do: args[:following_count],
|
|
||||||
else: user.info.following_count || following_count(user)
|
|
||||||
|
|
||||||
follower_count =
|
follower_count = Map.get(args, :follower_count, user.info.follower_count)
|
||||||
if args[:follower_count], do: args[:follower_count], else: user.info.follower_count
|
|
||||||
|
|
||||||
%{
|
%{
|
||||||
note_count: user.info.note_count,
|
note_count: user.info.note_count,
|
||||||
|
@ -137,12 +132,11 @@ def user_info(%User{} = user, args \\ %{}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def follow_state(%User{} = user, %User{} = target) do
|
def follow_state(%User{} = user, %User{} = target) do
|
||||||
follow_activity = Utils.fetch_latest_follow(user, target)
|
case Utils.fetch_latest_follow(user, target) do
|
||||||
|
%{data: %{"state" => state}} -> state
|
||||||
if follow_activity,
|
|
||||||
do: follow_activity.data["state"],
|
|
||||||
# Ideally this would be nil, but then Cachex does not commit the value
|
# Ideally this would be nil, but then Cachex does not commit the value
|
||||||
else: false
|
_ -> false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_cached_follow_state(user, target) do
|
def get_cached_follow_state(user, target) do
|
||||||
|
@ -152,11 +146,7 @@ def get_cached_follow_state(user, target) do
|
||||||
|
|
||||||
@spec set_follow_state_cache(String.t(), String.t(), String.t()) :: {:ok | :error, boolean()}
|
@spec set_follow_state_cache(String.t(), String.t(), String.t()) :: {:ok | :error, boolean()}
|
||||||
def set_follow_state_cache(user_ap_id, target_ap_id, state) do
|
def set_follow_state_cache(user_ap_id, target_ap_id, state) do
|
||||||
Cachex.put(
|
Cachex.put(:user_cache, "follow_state:#{user_ap_id}|#{target_ap_id}", state)
|
||||||
:user_cache,
|
|
||||||
"follow_state:#{user_ap_id}|#{target_ap_id}",
|
|
||||||
state
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_info_cache(user, args) do
|
def set_info_cache(user, args) do
|
||||||
|
@ -197,34 +187,25 @@ def remote_user_creation(params) do
|
||||||
|> truncate_if_exists(:name, name_limit)
|
|> truncate_if_exists(:name, name_limit)
|
||||||
|> truncate_if_exists(:bio, bio_limit)
|
|> truncate_if_exists(:bio, bio_limit)
|
||||||
|
|
||||||
info_cng = User.Info.remote_user_creation(%User.Info{}, params[:info])
|
changeset =
|
||||||
|
%User{local: false}
|
||||||
changes =
|
|
||||||
%User{}
|
|
||||||
|> cast(params, [:bio, :name, :ap_id, :nickname, :avatar])
|
|> cast(params, [:bio, :name, :ap_id, :nickname, :avatar])
|
||||||
|> validate_required([:name, :ap_id])
|
|> validate_required([:name, :ap_id])
|
||||||
|> unique_constraint(:nickname)
|
|> unique_constraint(:nickname)
|
||||||
|> validate_format(:nickname, @email_regex)
|
|> validate_format(:nickname, @email_regex)
|
||||||
|> validate_length(:bio, max: bio_limit)
|
|> validate_length(:bio, max: bio_limit)
|
||||||
|> validate_length(:name, max: name_limit)
|
|> validate_length(:name, max: name_limit)
|
||||||
|> put_change(:local, false)
|
|> change_info(&User.Info.remote_user_creation(&1, params[:info]))
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
if changes.valid? do
|
case params[:info][:source_data] do
|
||||||
case info_cng.changes[:source_data] do
|
%{"followers" => followers, "following" => following} ->
|
||||||
%{"followers" => followers, "following" => following} ->
|
changeset
|
||||||
changes
|
|> put_change(:follower_address, followers)
|
||||||
|> put_change(:follower_address, followers)
|
|> put_change(:following_address, following)
|
||||||
|> put_change(:following_address, following)
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
followers = User.ap_followers(%User{nickname: changes.changes[:nickname]})
|
followers = ap_followers(%User{nickname: get_field(changeset, :nickname)})
|
||||||
|
put_change(changeset, :follower_address, followers)
|
||||||
changes
|
|
||||||
|> put_change(:follower_address, followers)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
changes
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -245,7 +226,6 @@ def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
|
||||||
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
|
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
|
||||||
|
|
||||||
params = Map.put(params, :last_refreshed_at, NaiveDateTime.utc_now())
|
params = Map.put(params, :last_refreshed_at, NaiveDateTime.utc_now())
|
||||||
info_cng = User.Info.user_upgrade(struct.info, params[:info], remote?)
|
|
||||||
|
|
||||||
struct
|
struct
|
||||||
|> cast(params, [
|
|> cast(params, [
|
||||||
|
@ -260,7 +240,7 @@ def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
|
||||||
|> validate_format(:nickname, local_nickname_regex())
|
|> validate_format(:nickname, local_nickname_regex())
|
||||||
|> validate_length(:bio, max: bio_limit)
|
|> validate_length(:bio, max: bio_limit)
|
||||||
|> validate_length(:name, max: name_limit)
|
|> validate_length(:name, max: name_limit)
|
||||||
|> put_embed(:info, info_cng)
|
|> change_info(&User.Info.user_upgrade(&1, params[:info], remote?))
|
||||||
end
|
end
|
||||||
|
|
||||||
def password_update_changeset(struct, params) do
|
def password_update_changeset(struct, params) do
|
||||||
|
@ -311,43 +291,39 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
|
||||||
opts[:need_confirmation]
|
opts[:need_confirmation]
|
||||||
end
|
end
|
||||||
|
|
||||||
info_change =
|
struct
|
||||||
User.Info.confirmation_changeset(%User.Info{}, need_confirmation: need_confirmation?)
|
|> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation])
|
||||||
|
|> validate_required([:name, :nickname, :password, :password_confirmation])
|
||||||
|
|> validate_confirmation(:password)
|
||||||
|
|> unique_constraint(:email)
|
||||||
|
|> unique_constraint(:nickname)
|
||||||
|
|> validate_exclusion(:nickname, Pleroma.Config.get([User, :restricted_nicknames]))
|
||||||
|
|> validate_format(:nickname, local_nickname_regex())
|
||||||
|
|> validate_format(:email, @email_regex)
|
||||||
|
|> validate_length(:bio, max: bio_limit)
|
||||||
|
|> validate_length(:name, min: 1, max: name_limit)
|
||||||
|
|> change_info(&User.Info.confirmation_changeset(&1, need_confirmation: need_confirmation?))
|
||||||
|
|> maybe_validate_required_email(opts[:external])
|
||||||
|
|> put_password_hash
|
||||||
|
|> put_ap_id()
|
||||||
|
|> unique_constraint(:ap_id)
|
||||||
|
|> put_following_and_follower_address()
|
||||||
|
end
|
||||||
|
|
||||||
changeset =
|
def maybe_validate_required_email(changeset, true), do: changeset
|
||||||
struct
|
def maybe_validate_required_email(changeset, _), do: validate_required(changeset, [:email])
|
||||||
|> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation])
|
|
||||||
|> validate_required([:name, :nickname, :password, :password_confirmation])
|
|
||||||
|> validate_confirmation(:password)
|
|
||||||
|> unique_constraint(:email)
|
|
||||||
|> unique_constraint(:nickname)
|
|
||||||
|> validate_exclusion(:nickname, Pleroma.Config.get([User, :restricted_nicknames]))
|
|
||||||
|> validate_format(:nickname, local_nickname_regex())
|
|
||||||
|> validate_format(:email, @email_regex)
|
|
||||||
|> validate_length(:bio, max: bio_limit)
|
|
||||||
|> validate_length(:name, min: 1, max: name_limit)
|
|
||||||
|> put_change(:info, info_change)
|
|
||||||
|
|
||||||
changeset =
|
defp put_ap_id(changeset) do
|
||||||
if opts[:external] do
|
ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)})
|
||||||
changeset
|
put_change(changeset, :ap_id, ap_id)
|
||||||
else
|
end
|
||||||
validate_required(changeset, [:email])
|
|
||||||
end
|
|
||||||
|
|
||||||
if changeset.valid? do
|
defp put_following_and_follower_address(changeset) do
|
||||||
ap_id = User.ap_id(%User{nickname: changeset.changes[:nickname]})
|
followers = ap_followers(%User{nickname: get_field(changeset, :nickname)})
|
||||||
followers = User.ap_followers(%User{nickname: changeset.changes[:nickname]})
|
|
||||||
|
|
||||||
changeset
|
changeset
|
||||||
|> put_password_hash
|
|> put_change(:following, [followers])
|
||||||
|> put_change(:ap_id, ap_id)
|
|> put_change(:follower_address, followers)
|
||||||
|> unique_constraint(:ap_id)
|
|
||||||
|> put_change(:following, [followers])
|
|
||||||
|> put_change(:follower_address, followers)
|
|
||||||
else
|
|
||||||
changeset
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp autofollow_users(user) do
|
defp autofollow_users(user) do
|
||||||
|
@ -362,9 +338,8 @@ defp autofollow_users(user) do
|
||||||
|
|
||||||
@doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
|
@doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
|
||||||
def register(%Ecto.Changeset{} = changeset) do
|
def register(%Ecto.Changeset{} = changeset) do
|
||||||
with {:ok, user} <- Repo.insert(changeset),
|
with {:ok, user} <- Repo.insert(changeset) do
|
||||||
{:ok, user} <- post_register_action(user) do
|
post_register_action(user)
|
||||||
{:ok, user}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -410,7 +385,7 @@ def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def maybe_direct_follow(%User{} = follower, %User{} = followed) do
|
def maybe_direct_follow(%User{} = follower, %User{} = followed) do
|
||||||
if not User.ap_enabled?(followed) do
|
if not ap_enabled?(followed) do
|
||||||
follow(follower, followed)
|
follow(follower, followed)
|
||||||
else
|
else
|
||||||
{:ok, follower}
|
{:ok, follower}
|
||||||
|
@ -443,9 +418,7 @@ def follow_all(follower, followeds) do
|
||||||
|
|
||||||
{1, [follower]} = Repo.update_all(q, [])
|
{1, [follower]} = Repo.update_all(q, [])
|
||||||
|
|
||||||
Enum.each(followeds, fn followed ->
|
Enum.each(followeds, &update_follower_count/1)
|
||||||
update_follower_count(followed)
|
|
||||||
end)
|
|
||||||
|
|
||||||
set_cache(follower)
|
set_cache(follower)
|
||||||
end
|
end
|
||||||
|
@ -555,8 +528,6 @@ def set_cache(%User{} = user) do
|
||||||
def update_and_set_cache(changeset) do
|
def update_and_set_cache(changeset) do
|
||||||
with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do
|
with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do
|
||||||
set_cache(user)
|
set_cache(user)
|
||||||
else
|
|
||||||
e -> e
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -593,9 +564,7 @@ def get_cached_by_nickname(nickname) do
|
||||||
key = "nickname:#{nickname}"
|
key = "nickname:#{nickname}"
|
||||||
|
|
||||||
Cachex.fetch!(:user_cache, key, fn ->
|
Cachex.fetch!(:user_cache, key, fn ->
|
||||||
user_result = get_or_fetch_by_nickname(nickname)
|
case get_or_fetch_by_nickname(nickname) do
|
||||||
|
|
||||||
case user_result do
|
|
||||||
{:ok, user} -> {:commit, user}
|
{:ok, user} -> {:commit, user}
|
||||||
{:error, _error} -> {:ignore, nil}
|
{:error, _error} -> {:ignore, nil}
|
||||||
end
|
end
|
||||||
|
@ -635,13 +604,11 @@ def get_by_nickname_or_email(nickname_or_email) do
|
||||||
|
|
||||||
def get_cached_user_info(user) do
|
def get_cached_user_info(user) do
|
||||||
key = "user_info:#{user.id}"
|
key = "user_info:#{user.id}"
|
||||||
Cachex.fetch!(:user_cache, key, fn _ -> user_info(user) end)
|
Cachex.fetch!(:user_cache, key, fn -> user_info(user) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_by_nickname(nickname) do
|
def fetch_by_nickname(nickname) do
|
||||||
ap_try = ActivityPub.make_user_from_nickname(nickname)
|
case ActivityPub.make_user_from_nickname(nickname) do
|
||||||
|
|
||||||
case ap_try do
|
|
||||||
{:ok, user} -> {:ok, user}
|
{:ok, user} -> {:ok, user}
|
||||||
_ -> OStatus.make_user(nickname)
|
_ -> OStatus.make_user(nickname)
|
||||||
end
|
end
|
||||||
|
@ -676,7 +643,8 @@ def get_followers_query(%User{} = user, nil) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_followers_query(user, page) do
|
def get_followers_query(user, page) do
|
||||||
from(u in get_followers_query(user, nil))
|
user
|
||||||
|
|> get_followers_query(nil)
|
||||||
|> User.Query.paginate(page, 20)
|
|> User.Query.paginate(page, 20)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -685,25 +653,24 @@ def get_followers_query(user), do: get_followers_query(user, nil)
|
||||||
|
|
||||||
@spec get_followers(User.t(), pos_integer()) :: {:ok, list(User.t())}
|
@spec get_followers(User.t(), pos_integer()) :: {:ok, list(User.t())}
|
||||||
def get_followers(user, page \\ nil) do
|
def get_followers(user, page \\ nil) do
|
||||||
q = get_followers_query(user, page)
|
user
|
||||||
|
|> get_followers_query(page)
|
||||||
{:ok, Repo.all(q)}
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_external_followers(User.t(), pos_integer()) :: {:ok, list(User.t())}
|
@spec get_external_followers(User.t(), pos_integer()) :: {:ok, list(User.t())}
|
||||||
def get_external_followers(user, page \\ nil) do
|
def get_external_followers(user, page \\ nil) do
|
||||||
q =
|
user
|
||||||
user
|
|> get_followers_query(page)
|
||||||
|> get_followers_query(page)
|
|> User.Query.build(%{external: true})
|
||||||
|> User.Query.build(%{external: true})
|
|> Repo.all()
|
||||||
|
|
||||||
{:ok, Repo.all(q)}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_followers_ids(user, page \\ nil) do
|
def get_followers_ids(user, page \\ nil) do
|
||||||
q = get_followers_query(user, page)
|
user
|
||||||
|
|> get_followers_query(page)
|
||||||
Repo.all(from(u in q, select: u.id))
|
|> select([u], u.id)
|
||||||
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_friends_query(User.t(), pos_integer() | nil) :: Ecto.Query.t()
|
@spec get_friends_query(User.t(), pos_integer() | nil) :: Ecto.Query.t()
|
||||||
|
@ -712,7 +679,8 @@ def get_friends_query(%User{} = user, nil) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_friends_query(user, page) do
|
def get_friends_query(user, page) do
|
||||||
from(u in get_friends_query(user, nil))
|
user
|
||||||
|
|> get_friends_query(nil)
|
||||||
|> User.Query.paginate(page, 20)
|
|> User.Query.paginate(page, 20)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -720,28 +688,27 @@ def get_friends_query(user, page) do
|
||||||
def get_friends_query(user), do: get_friends_query(user, nil)
|
def get_friends_query(user), do: get_friends_query(user, nil)
|
||||||
|
|
||||||
def get_friends(user, page \\ nil) do
|
def get_friends(user, page \\ nil) do
|
||||||
q = get_friends_query(user, page)
|
user
|
||||||
|
|> get_friends_query(page)
|
||||||
{:ok, Repo.all(q)}
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_friends_ids(user, page \\ nil) do
|
def get_friends_ids(user, page \\ nil) do
|
||||||
q = get_friends_query(user, page)
|
user
|
||||||
|
|> get_friends_query(page)
|
||||||
Repo.all(from(u in q, select: u.id))
|
|> select([u], u.id)
|
||||||
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_follow_requests(User.t()) :: {:ok, [User.t()]}
|
@spec get_follow_requests(User.t()) :: {:ok, [User.t()]}
|
||||||
def get_follow_requests(%User{} = user) do
|
def get_follow_requests(%User{} = user) do
|
||||||
users =
|
user
|
||||||
Activity.follow_requests_for_actor(user)
|
|> Activity.follow_requests_for_actor()
|
||||||
|> join(:inner, [a], u in User, on: a.actor == u.ap_id)
|
|> join(:inner, [a], u in User, on: a.actor == u.ap_id)
|
||||||
|> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address]))
|
|> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address]))
|
||||||
|> group_by([a, u], u.id)
|
|> group_by([a, u], u.id)
|
||||||
|> select([a, u], u)
|
|> select([a, u], u)
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|
|
||||||
{:ok, users}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def increase_note_count(%User{} = user) do
|
def increase_note_count(%User{} = user) do
|
||||||
|
@ -787,21 +754,15 @@ def decrease_note_count(%User{} = user) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_note_count(%User{} = user) do
|
def update_note_count(%User{} = user) do
|
||||||
note_count_query =
|
note_count =
|
||||||
from(
|
from(
|
||||||
a in Object,
|
a in Object,
|
||||||
where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data),
|
where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data),
|
||||||
select: count(a.id)
|
select: count(a.id)
|
||||||
)
|
)
|
||||||
|
|> Repo.one()
|
||||||
|
|
||||||
note_count = Repo.one(note_count_query)
|
update_info(user, &User.Info.set_note_count(&1, note_count))
|
||||||
|
|
||||||
info_cng = User.Info.set_note_count(user.info, note_count)
|
|
||||||
|
|
||||||
user
|
|
||||||
|> change()
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|> update_and_set_cache()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec maybe_fetch_follow_information(User.t()) :: User.t()
|
@spec maybe_fetch_follow_information(User.t()) :: User.t()
|
||||||
|
@ -818,17 +779,7 @@ def maybe_fetch_follow_information(user) do
|
||||||
|
|
||||||
def fetch_follow_information(user) do
|
def fetch_follow_information(user) do
|
||||||
with {:ok, info} <- ActivityPub.fetch_follow_information_for_user(user) do
|
with {:ok, info} <- ActivityPub.fetch_follow_information_for_user(user) do
|
||||||
info_cng = User.Info.follow_information_update(user.info, info)
|
update_info(user, &User.Info.follow_information_update(&1, info))
|
||||||
|
|
||||||
changeset =
|
|
||||||
user
|
|
||||||
|> change()
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
update_and_set_cache(changeset)
|
|
||||||
else
|
|
||||||
{:error, _} = e -> e
|
|
||||||
e -> {:error, e}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -902,62 +853,28 @@ def get_recipients_from_activity(%Activity{recipients: to}) do
|
||||||
|
|
||||||
@spec mute(User.t(), User.t(), boolean()) :: {:ok, User.t()} | {:error, String.t()}
|
@spec mute(User.t(), User.t(), boolean()) :: {:ok, User.t()} | {:error, String.t()}
|
||||||
def mute(muter, %User{ap_id: ap_id}, notifications? \\ true) do
|
def mute(muter, %User{ap_id: ap_id}, notifications? \\ true) do
|
||||||
info = muter.info
|
update_info(muter, &User.Info.add_to_mutes(&1, ap_id, notifications?))
|
||||||
|
|
||||||
info_cng =
|
|
||||||
User.Info.add_to_mutes(info, ap_id)
|
|
||||||
|> User.Info.add_to_muted_notifications(info, ap_id, notifications?)
|
|
||||||
|
|
||||||
cng =
|
|
||||||
change(muter)
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
update_and_set_cache(cng)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def unmute(muter, %{ap_id: ap_id}) do
|
def unmute(muter, %{ap_id: ap_id}) do
|
||||||
info = muter.info
|
update_info(muter, &User.Info.remove_from_mutes(&1, ap_id))
|
||||||
|
|
||||||
info_cng =
|
|
||||||
User.Info.remove_from_mutes(info, ap_id)
|
|
||||||
|> User.Info.remove_from_muted_notifications(info, ap_id)
|
|
||||||
|
|
||||||
cng =
|
|
||||||
change(muter)
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
update_and_set_cache(cng)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def subscribe(subscriber, %{ap_id: ap_id}) do
|
def subscribe(subscriber, %{ap_id: ap_id}) do
|
||||||
deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
|
|
||||||
|
|
||||||
with %User{} = subscribed <- get_cached_by_ap_id(ap_id) do
|
with %User{} = subscribed <- get_cached_by_ap_id(ap_id) do
|
||||||
blocked = blocks?(subscribed, subscriber) and deny_follow_blocked
|
deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
|
||||||
|
|
||||||
if blocked do
|
if blocks?(subscribed, subscriber) and deny_follow_blocked do
|
||||||
{:error, "Could not subscribe: #{subscribed.nickname} is blocking you"}
|
{:error, "Could not subscribe: #{subscribed.nickname} is blocking you"}
|
||||||
else
|
else
|
||||||
info_cng =
|
update_info(subscribed, &User.Info.add_to_subscribers(&1, subscriber.ap_id))
|
||||||
subscribed.info
|
|
||||||
|> User.Info.add_to_subscribers(subscriber.ap_id)
|
|
||||||
|
|
||||||
change(subscribed)
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|> update_and_set_cache()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def unsubscribe(unsubscriber, %{ap_id: ap_id}) do
|
def unsubscribe(unsubscriber, %{ap_id: ap_id}) do
|
||||||
with %User{} = user <- get_cached_by_ap_id(ap_id) do
|
with %User{} = user <- get_cached_by_ap_id(ap_id) do
|
||||||
info_cng =
|
update_info(user, &User.Info.remove_from_subscribers(&1, unsubscriber.ap_id))
|
||||||
user.info
|
|
||||||
|> User.Info.remove_from_subscribers(unsubscriber.ap_id)
|
|
||||||
|
|
||||||
change(user)
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|> update_and_set_cache()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -986,21 +903,11 @@ def block(blocker, %User{ap_id: ap_id} = blocked) do
|
||||||
blocker
|
blocker
|
||||||
end
|
end
|
||||||
|
|
||||||
if following?(blocked, blocker) do
|
if following?(blocked, blocker), do: unfollow(blocked, blocker)
|
||||||
unfollow(blocked, blocker)
|
|
||||||
end
|
|
||||||
|
|
||||||
{:ok, blocker} = update_follower_count(blocker)
|
{:ok, blocker} = update_follower_count(blocker)
|
||||||
|
|
||||||
info_cng =
|
update_info(blocker, &User.Info.add_to_block(&1, ap_id))
|
||||||
blocker.info
|
|
||||||
|> User.Info.add_to_block(ap_id)
|
|
||||||
|
|
||||||
cng =
|
|
||||||
change(blocker)
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
update_and_set_cache(cng)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# helper to handle the block given only an actor's AP id
|
# helper to handle the block given only an actor's AP id
|
||||||
|
@ -1009,15 +916,7 @@ def block(blocker, %{ap_id: ap_id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def unblock(blocker, %{ap_id: ap_id}) do
|
def unblock(blocker, %{ap_id: ap_id}) do
|
||||||
info_cng =
|
update_info(blocker, &User.Info.remove_from_block(&1, ap_id))
|
||||||
blocker.info
|
|
||||||
|> User.Info.remove_from_block(ap_id)
|
|
||||||
|
|
||||||
cng =
|
|
||||||
change(blocker)
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
update_and_set_cache(cng)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def mutes?(nil, _), do: false
|
def mutes?(nil, _), do: false
|
||||||
|
@ -1074,27 +973,11 @@ def subscribers(user) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def block_domain(user, domain) do
|
def block_domain(user, domain) do
|
||||||
info_cng =
|
update_info(user, &User.Info.add_to_domain_block(&1, domain))
|
||||||
user.info
|
|
||||||
|> User.Info.add_to_domain_block(domain)
|
|
||||||
|
|
||||||
cng =
|
|
||||||
change(user)
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
update_and_set_cache(cng)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def unblock_domain(user, domain) do
|
def unblock_domain(user, domain) do
|
||||||
info_cng =
|
update_info(user, &User.Info.remove_from_domain_block(&1, domain))
|
||||||
user.info
|
|
||||||
|> User.Info.remove_from_domain_block(domain)
|
|
||||||
|
|
||||||
cng =
|
|
||||||
change(user)
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
update_and_set_cache(cng)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def deactivate_async(user, status \\ true) do
|
def deactivate_async(user, status \\ true) do
|
||||||
|
@ -1102,28 +985,16 @@ def deactivate_async(user, status \\ true) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def deactivate(%User{} = user, status \\ true) do
|
def deactivate(%User{} = user, status \\ true) do
|
||||||
info_cng = User.Info.set_activation_status(user.info, status)
|
with {:ok, user} <- update_info(user, &User.Info.set_activation_status(&1, status)) do
|
||||||
|
Enum.each(get_followers(user), &invalidate_cache/1)
|
||||||
with {:ok, friends} <- User.get_friends(user),
|
Enum.each(get_friends(user), &update_follower_count/1)
|
||||||
{:ok, followers} <- User.get_followers(user),
|
|
||||||
{:ok, user} <-
|
|
||||||
user
|
|
||||||
|> change()
|
|
||||||
|> put_embed(:info, info_cng)
|
|
||||||
|> update_and_set_cache() do
|
|
||||||
Enum.each(followers, &invalidate_cache(&1))
|
|
||||||
Enum.each(friends, &update_follower_count(&1))
|
|
||||||
|
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_notification_settings(%User{} = user, settings \\ %{}) do
|
def update_notification_settings(%User{} = user, settings \\ %{}) do
|
||||||
info_changeset = User.Info.update_notification_settings(user.info, settings)
|
update_info(user, &User.Info.update_notification_settings(&1, settings))
|
||||||
|
|
||||||
change(user)
|
|
||||||
|> put_embed(:info, info_changeset)
|
|
||||||
|> update_and_set_cache()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(%User{} = user) do
|
def delete(%User{} = user) do
|
||||||
|
@ -1137,18 +1008,18 @@ def perform(:delete, %User{} = user) do
|
||||||
{:ok, _user} = ActivityPub.delete(user)
|
{:ok, _user} = ActivityPub.delete(user)
|
||||||
|
|
||||||
# Remove all relationships
|
# Remove all relationships
|
||||||
{:ok, followers} = User.get_followers(user)
|
user
|
||||||
|
|> get_followers()
|
||||||
Enum.each(followers, fn follower ->
|
|> Enum.each(fn follower ->
|
||||||
ActivityPub.unfollow(follower, user)
|
ActivityPub.unfollow(follower, user)
|
||||||
User.unfollow(follower, user)
|
unfollow(follower, user)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:ok, friends} = User.get_friends(user)
|
user
|
||||||
|
|> get_friends()
|
||||||
Enum.each(friends, fn followed ->
|
|> Enum.each(fn followed ->
|
||||||
ActivityPub.unfollow(user, followed)
|
ActivityPub.unfollow(user, followed)
|
||||||
User.unfollow(user, followed)
|
unfollow(user, followed)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
delete_user_activities(user)
|
delete_user_activities(user)
|
||||||
|
@ -1160,13 +1031,11 @@ def perform(:delete, %User{} = user) do
|
||||||
def perform(:fetch_initial_posts, %User{} = user) do
|
def perform(:fetch_initial_posts, %User{} = user) do
|
||||||
pages = Pleroma.Config.get!([:fetch_initial_posts, :pages])
|
pages = Pleroma.Config.get!([:fetch_initial_posts, :pages])
|
||||||
|
|
||||||
Enum.each(
|
# Insert all the posts in reverse order, so they're in the right order on the timeline
|
||||||
# Insert all the posts in reverse order, so they're in the right order on the timeline
|
user.info.source_data["outbox"]
|
||||||
Enum.reverse(Utils.fetch_ordered_collection(user.info.source_data["outbox"], pages)),
|
|> Utils.fetch_ordered_collection(pages)
|
||||||
&Pleroma.Web.Federator.incoming_ap_doc/1
|
|> Enum.reverse()
|
||||||
)
|
|> Enum.each(&Pleroma.Web.Federator.incoming_ap_doc/1)
|
||||||
|
|
||||||
{:ok, user}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(:deactivate_async, user, status), do: deactivate(user, status)
|
def perform(:deactivate_async, user, status), do: deactivate(user, status)
|
||||||
|
@ -1252,16 +1121,12 @@ def follow_import(%User{} = follower, followed_identifiers)
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_user_activities(%User{ap_id: ap_id} = user) do
|
def delete_user_activities(%User{ap_id: ap_id}) do
|
||||||
ap_id
|
ap_id
|
||||||
|> Activity.Queries.by_actor()
|
|> Activity.Queries.by_actor()
|
||||||
|> RepoStreamer.chunk_stream(50)
|
|> RepoStreamer.chunk_stream(50)
|
||||||
|> Stream.each(fn activities ->
|
|> Stream.each(fn activities -> Enum.each(activities, &delete_activity/1) end)
|
||||||
Enum.each(activities, &delete_activity(&1))
|
|
||||||
end)
|
|
||||||
|> Stream.run()
|
|> Stream.run()
|
||||||
|
|
||||||
{:ok, user}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp delete_activity(%{data: %{"type" => "Create"}} = activity) do
|
defp delete_activity(%{data: %{"type" => "Create"}} = activity) do
|
||||||
|
@ -1271,17 +1136,19 @@ defp delete_activity(%{data: %{"type" => "Create"}} = activity) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp delete_activity(%{data: %{"type" => "Like"}} = activity) do
|
defp delete_activity(%{data: %{"type" => "Like"}} = activity) do
|
||||||
user = get_cached_by_ap_id(activity.actor)
|
|
||||||
object = Object.normalize(activity)
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
ActivityPub.unlike(user, object)
|
activity.actor
|
||||||
|
|> get_cached_by_ap_id()
|
||||||
|
|> ActivityPub.unlike(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp delete_activity(%{data: %{"type" => "Announce"}} = activity) do
|
defp delete_activity(%{data: %{"type" => "Announce"}} = activity) do
|
||||||
user = get_cached_by_ap_id(activity.actor)
|
|
||||||
object = Object.normalize(activity)
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
ActivityPub.unannounce(user, object)
|
activity.actor
|
||||||
|
|> get_cached_by_ap_id()
|
||||||
|
|> ActivityPub.unannounce(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp delete_activity(_activity), do: "Doing nothing"
|
defp delete_activity(_activity), do: "Doing nothing"
|
||||||
|
@ -1293,9 +1160,7 @@ def html_filter_policy(%User{info: %{no_rich_text: true}}) do
|
||||||
def html_filter_policy(_), do: Pleroma.Config.get([:markup, :scrub_policy])
|
def html_filter_policy(_), do: Pleroma.Config.get([:markup, :scrub_policy])
|
||||||
|
|
||||||
def fetch_by_ap_id(ap_id) do
|
def fetch_by_ap_id(ap_id) do
|
||||||
ap_try = ActivityPub.make_user_from_ap_id(ap_id)
|
case ActivityPub.make_user_from_ap_id(ap_id) do
|
||||||
|
|
||||||
case ap_try do
|
|
||||||
{:ok, user} ->
|
{:ok, user} ->
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
|
|
||||||
|
@ -1310,7 +1175,7 @@ def fetch_by_ap_id(ap_id) do
|
||||||
def get_or_fetch_by_ap_id(ap_id) do
|
def get_or_fetch_by_ap_id(ap_id) do
|
||||||
user = get_cached_by_ap_id(ap_id)
|
user = get_cached_by_ap_id(ap_id)
|
||||||
|
|
||||||
if !is_nil(user) and !User.needs_update?(user) do
|
if !is_nil(user) and !needs_update?(user) do
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
else
|
else
|
||||||
# Whether to fetch initial posts for the user (if it's a new user & the fetching is enabled)
|
# Whether to fetch initial posts for the user (if it's a new user & the fetching is enabled)
|
||||||
|
@ -1330,19 +1195,20 @@ def get_or_fetch_by_ap_id(ap_id) do
|
||||||
|
|
||||||
@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
|
def get_or_create_service_actor_by_ap_id(uri, nickname \\ nil) do
|
||||||
if user = get_cached_by_ap_id(uri) do
|
with %User{} = user <- get_cached_by_ap_id(uri) do
|
||||||
user
|
user
|
||||||
else
|
else
|
||||||
changes =
|
_ ->
|
||||||
%User{info: %User.Info{}}
|
{:ok, user} =
|
||||||
|> cast(%{}, [:ap_id, :nickname, :local])
|
%User{info: %User.Info{}}
|
||||||
|> put_change(:ap_id, uri)
|
|> cast(%{}, [:ap_id, :nickname, :local])
|
||||||
|> put_change(:nickname, nickname)
|
|> put_change(:ap_id, uri)
|
||||||
|> put_change(:local, true)
|
|> put_change(:nickname, nickname)
|
||||||
|> put_change(:follower_address, uri <> "/followers")
|
|> put_change(:local, true)
|
||||||
|
|> put_change(:follower_address, uri <> "/followers")
|
||||||
|
|> Repo.insert()
|
||||||
|
|
||||||
{:ok, user} = Repo.insert(changes)
|
user
|
||||||
user
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1399,23 +1265,21 @@ def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname)
|
||||||
# this is because we have synchronous follow APIs and need to simulate them
|
# this is because we have synchronous follow APIs and need to simulate them
|
||||||
# with an async handshake
|
# with an async handshake
|
||||||
def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do
|
def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do
|
||||||
with %User{} = a <- User.get_cached_by_id(a.id),
|
with %User{} = a <- get_cached_by_id(a.id),
|
||||||
%User{} = b <- User.get_cached_by_id(b.id) do
|
%User{} = b <- get_cached_by_id(b.id) do
|
||||||
{:ok, a, b}
|
{:ok, a, b}
|
||||||
else
|
else
|
||||||
_e ->
|
nil -> :error
|
||||||
:error
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def wait_and_refresh(timeout, %User{} = a, %User{} = b) do
|
def wait_and_refresh(timeout, %User{} = a, %User{} = b) do
|
||||||
with :ok <- :timer.sleep(timeout),
|
with :ok <- :timer.sleep(timeout),
|
||||||
%User{} = a <- User.get_cached_by_id(a.id),
|
%User{} = a <- get_cached_by_id(a.id),
|
||||||
%User{} = b <- User.get_cached_by_id(b.id) do
|
%User{} = b <- get_cached_by_id(b.id) do
|
||||||
{:ok, a, b}
|
{:ok, a, b}
|
||||||
else
|
else
|
||||||
_e ->
|
nil -> :error
|
||||||
:error
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1477,7 +1341,7 @@ defp update_tags(%User{} = user, new_tags) do
|
||||||
defp normalize_tags(tags) do
|
defp normalize_tags(tags) do
|
||||||
[tags]
|
[tags]
|
||||||
|> List.flatten()
|
|> List.flatten()
|
||||||
|> Enum.map(&String.downcase(&1))
|
|> Enum.map(&String.downcase/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp local_nickname_regex do
|
defp local_nickname_regex do
|
||||||
|
@ -1570,11 +1434,7 @@ def list_inactive_users_query(inactivity_threshold \\ 7) do
|
||||||
@spec switch_email_notifications(t(), String.t(), boolean()) ::
|
@spec switch_email_notifications(t(), String.t(), boolean()) ::
|
||||||
{:ok, t()} | {:error, Ecto.Changeset.t()}
|
{:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
def switch_email_notifications(user, type, status) do
|
def switch_email_notifications(user, type, status) do
|
||||||
info = Pleroma.User.Info.update_email_notifications(user.info, %{type => status})
|
update_info(user, &User.Info.update_email_notifications(&1, %{type => status}))
|
||||||
|
|
||||||
change(user)
|
|
||||||
|> put_embed(:info, info)
|
|
||||||
|> update_and_set_cache()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
@ -1596,13 +1456,8 @@ def touch_last_digest_emailed_at(user) do
|
||||||
def toggle_confirmation(%User{} = user) do
|
def toggle_confirmation(%User{} = user) do
|
||||||
need_confirmation? = !user.info.confirmation_pending
|
need_confirmation? = !user.info.confirmation_pending
|
||||||
|
|
||||||
info_changeset =
|
|
||||||
User.Info.confirmation_changeset(user.info, need_confirmation: need_confirmation?)
|
|
||||||
|
|
||||||
user
|
user
|
||||||
|> change()
|
|> update_info(&User.Info.confirmation_changeset(&1, need_confirmation: need_confirmation?))
|
||||||
|> put_embed(:info, info_changeset)
|
|
||||||
|> update_and_set_cache()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_mascot(%{info: %{mascot: %{} = mascot}}) when not is_nil(mascot) do
|
def get_mascot(%{info: %{mascot: %{} = mascot}}) when not is_nil(mascot) do
|
||||||
|
@ -1625,16 +1480,11 @@ def get_mascot(%{info: %{mascot: mascot}}) when is_nil(mascot) do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_keys_present(%User{info: info} = user) do
|
def ensure_keys_present(%{info: %{keys: keys}} = user) when not is_nil(keys), do: {:ok, user}
|
||||||
if info.keys do
|
|
||||||
{:ok, user}
|
|
||||||
else
|
|
||||||
{:ok, pem} = Keys.generate_rsa_pem()
|
|
||||||
|
|
||||||
user
|
def ensure_keys_present(%User{} = user) do
|
||||||
|> Ecto.Changeset.change()
|
with {:ok, pem} <- Keys.generate_rsa_pem() do
|
||||||
|> Ecto.Changeset.put_embed(:info, User.Info.set_keys(info, pem))
|
update_info(user, &User.Info.set_keys(&1, pem))
|
||||||
|> update_and_set_cache()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1680,4 +1530,26 @@ def change_email(user, email) do
|
||||||
|> validate_format(:email, @email_regex)
|
|> validate_format(:email, @email_regex)
|
||||||
|> update_and_set_cache()
|
|> update_and_set_cache()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Changes `user.info` and returns the user changeset.
|
||||||
|
|
||||||
|
`fun` is called with the `user.info`.
|
||||||
|
"""
|
||||||
|
def change_info(user, fun) do
|
||||||
|
changeset = change(user)
|
||||||
|
info = get_field(changeset, :info) || %User.Info{}
|
||||||
|
put_embed(changeset, :info, fun.(info))
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Updates `user.info` and sets cache.
|
||||||
|
|
||||||
|
`fun` is called with the `user.info`.
|
||||||
|
"""
|
||||||
|
def update_info(user, fun) do
|
||||||
|
user
|
||||||
|
|> change_info(fun)
|
||||||
|
|> update_and_set_cache()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -188,16 +188,11 @@ def set_subscribers(info, subscribers) do
|
||||||
|> validate_required([:subscribers])
|
|> validate_required([:subscribers])
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec add_to_mutes(Info.t(), String.t()) :: Changeset.t()
|
@spec add_to_mutes(Info.t(), String.t(), boolean()) :: Changeset.t()
|
||||||
def add_to_mutes(info, muted) do
|
def add_to_mutes(info, muted, notifications?) do
|
||||||
set_mutes(info, Enum.uniq([muted | info.mutes]))
|
info
|
||||||
end
|
|> set_mutes(Enum.uniq([muted | info.mutes]))
|
||||||
|
|> set_notification_mutes(
|
||||||
@spec add_to_muted_notifications(Changeset.t(), Info.t(), String.t(), boolean()) ::
|
|
||||||
Changeset.t()
|
|
||||||
def add_to_muted_notifications(changeset, info, muted, notifications?) do
|
|
||||||
set_notification_mutes(
|
|
||||||
changeset,
|
|
||||||
Enum.uniq([muted | info.muted_notifications]),
|
Enum.uniq([muted | info.muted_notifications]),
|
||||||
notifications?
|
notifications?
|
||||||
)
|
)
|
||||||
|
@ -205,12 +200,9 @@ def add_to_muted_notifications(changeset, info, muted, notifications?) do
|
||||||
|
|
||||||
@spec remove_from_mutes(Info.t(), String.t()) :: Changeset.t()
|
@spec remove_from_mutes(Info.t(), String.t()) :: Changeset.t()
|
||||||
def remove_from_mutes(info, muted) do
|
def remove_from_mutes(info, muted) do
|
||||||
set_mutes(info, List.delete(info.mutes, muted))
|
info
|
||||||
end
|
|> set_mutes(List.delete(info.mutes, muted))
|
||||||
|
|> set_notification_mutes(List.delete(info.muted_notifications, muted), true)
|
||||||
@spec remove_from_muted_notifications(Changeset.t(), Info.t(), String.t()) :: Changeset.t()
|
|
||||||
def remove_from_muted_notifications(changeset, info, muted) do
|
|
||||||
set_notification_mutes(changeset, List.delete(info.muted_notifications, muted), true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_to_block(info, blocked) do
|
def add_to_block(info, blocked) do
|
||||||
|
|
|
@ -111,11 +111,11 @@ defp should_federate?(inbox, public) do
|
||||||
|
|
||||||
@spec recipients(User.t(), Activity.t()) :: list(User.t()) | []
|
@spec recipients(User.t(), Activity.t()) :: list(User.t()) | []
|
||||||
defp recipients(actor, activity) do
|
defp recipients(actor, activity) do
|
||||||
{:ok, followers} =
|
followers =
|
||||||
if actor.follower_address in activity.recipients do
|
if actor.follower_address in activity.recipients do
|
||||||
User.get_external_followers(actor)
|
User.get_external_followers(actor)
|
||||||
else
|
else
|
||||||
{:ok, []}
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
fetchers =
|
fetchers =
|
||||||
|
|
|
@ -254,18 +254,12 @@ def right_add(%{assigns: %{user: admin}} = conn, %{
|
||||||
"nickname" => nickname
|
"nickname" => nickname
|
||||||
})
|
})
|
||||||
when permission_group in ["moderator", "admin"] do
|
when permission_group in ["moderator", "admin"] do
|
||||||
user = User.get_cached_by_nickname(nickname)
|
info = Map.put(%{}, "is_" <> permission_group, true)
|
||||||
|
|
||||||
info =
|
{:ok, user} =
|
||||||
%{}
|
nickname
|
||||||
|> Map.put("is_" <> permission_group, true)
|
|> User.get_cached_by_nickname()
|
||||||
|
|> User.update_info(&User.Info.admin_api_update(&1, info))
|
||||||
info_cng = User.Info.admin_api_update(user.info, info)
|
|
||||||
|
|
||||||
cng =
|
|
||||||
user
|
|
||||||
|> Ecto.Changeset.change()
|
|
||||||
|> Ecto.Changeset.put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
action: "grant",
|
action: "grant",
|
||||||
|
@ -274,8 +268,6 @@ def right_add(%{assigns: %{user: admin}} = conn, %{
|
||||||
permission: permission_group
|
permission: permission_group
|
||||||
})
|
})
|
||||||
|
|
||||||
{:ok, _user} = User.update_and_set_cache(cng)
|
|
||||||
|
|
||||||
json(conn, info)
|
json(conn, info)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -293,40 +285,33 @@ def right_get(conn, %{"nickname" => nickname}) do
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
|
||||||
|
render_error(conn, :forbidden, "You can't revoke your own admin status.")
|
||||||
|
end
|
||||||
|
|
||||||
def right_delete(
|
def right_delete(
|
||||||
%{assigns: %{user: %User{:nickname => admin_nickname} = admin}} = conn,
|
%{assigns: %{user: admin}} = conn,
|
||||||
%{
|
%{
|
||||||
"permission_group" => permission_group,
|
"permission_group" => permission_group,
|
||||||
"nickname" => nickname
|
"nickname" => nickname
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
when permission_group in ["moderator", "admin"] do
|
when permission_group in ["moderator", "admin"] do
|
||||||
if admin_nickname == nickname do
|
info = Map.put(%{}, "is_" <> permission_group, false)
|
||||||
render_error(conn, :forbidden, "You can't revoke your own admin status.")
|
|
||||||
else
|
|
||||||
user = User.get_cached_by_nickname(nickname)
|
|
||||||
|
|
||||||
info =
|
{:ok, user} =
|
||||||
%{}
|
nickname
|
||||||
|> Map.put("is_" <> permission_group, false)
|
|> User.get_cached_by_nickname()
|
||||||
|
|> User.update_info(&User.Info.admin_api_update(&1, info))
|
||||||
|
|
||||||
info_cng = User.Info.admin_api_update(user.info, info)
|
ModerationLog.insert_log(%{
|
||||||
|
action: "revoke",
|
||||||
|
actor: admin,
|
||||||
|
subject: user,
|
||||||
|
permission: permission_group
|
||||||
|
})
|
||||||
|
|
||||||
cng =
|
json(conn, info)
|
||||||
Ecto.Changeset.change(user)
|
|
||||||
|> Ecto.Changeset.put_embed(:info, info_cng)
|
|
||||||
|
|
||||||
{:ok, _user} = User.update_and_set_cache(cng)
|
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
action: "revoke",
|
|
||||||
actor: admin,
|
|
||||||
subject: user,
|
|
||||||
permission: permission_group
|
|
||||||
})
|
|
||||||
|
|
||||||
json(conn, info)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def right_delete(conn, _) do
|
def right_delete(conn, _) do
|
||||||
|
|
|
@ -306,16 +306,14 @@ defp put_emoji(map, text, emojis) do
|
||||||
|
|
||||||
# Updates the emojis for a user based on their profile
|
# Updates the emojis for a user based on their profile
|
||||||
def update(user) do
|
def update(user) do
|
||||||
|
emoji = emoji_from_profile(user)
|
||||||
|
source_data = user.info |> Map.get(:source_data, {}) |> Map.put("tag", emoji)
|
||||||
|
|
||||||
user =
|
user =
|
||||||
with emoji <- emoji_from_profile(user),
|
with {:ok, user} <- User.update_info(user, &User.Info.set_source_data(&1, source_data)) do
|
||||||
source_data <- (user.info.source_data || %{}) |> Map.put("tag", emoji),
|
|
||||||
info_cng <- User.Info.set_source_data(user.info, source_data),
|
|
||||||
change <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng),
|
|
||||||
{:ok, user} <- User.update_and_set_cache(change) do
|
|
||||||
user
|
user
|
||||||
else
|
else
|
||||||
_e ->
|
_e -> user
|
||||||
user
|
|
||||||
end
|
end
|
||||||
|
|
||||||
ActivityPub.update(%{
|
ActivityPub.update(%{
|
||||||
|
@ -340,34 +338,21 @@ def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do
|
||||||
}
|
}
|
||||||
} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
true <- Visibility.is_public?(activity),
|
true <- Visibility.is_public?(activity),
|
||||||
%{valid?: true} = info_changeset <- User.Info.add_pinnned_activity(user.info, activity),
|
{:ok, _user} <- User.update_info(user, &User.Info.add_pinnned_activity(&1, activity)) do
|
||||||
changeset <-
|
|
||||||
Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset),
|
|
||||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
%{errors: [pinned_activities: {err, _}]} ->
|
{:error, %{changes: %{info: %{errors: [pinned_activities: {err, _}]}}}} -> {:error, err}
|
||||||
{:error, err}
|
_ -> {:error, dgettext("errors", "Could not pin")}
|
||||||
|
|
||||||
_ ->
|
|
||||||
{:error, dgettext("errors", "Could not pin")}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def unpin(id_or_ap_id, user) do
|
def unpin(id_or_ap_id, user) do
|
||||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
%{valid?: true} = info_changeset <-
|
{:ok, _user} <- User.update_info(user, &User.Info.remove_pinnned_activity(&1, activity)) do
|
||||||
User.Info.remove_pinnned_activity(user.info, activity),
|
|
||||||
changeset <-
|
|
||||||
Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset),
|
|
||||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
%{errors: [pinned_activities: {err, _}]} ->
|
%{errors: [pinned_activities: {err, _}]} -> {:error, err}
|
||||||
{:error, err}
|
_ -> {:error, dgettext("errors", "Could not unpin")}
|
||||||
|
|
||||||
_ ->
|
|
||||||
{:error, dgettext("errors", "Could not unpin")}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -462,23 +447,15 @@ defp set_visibility(activity, %{"visibility" => visibility}) do
|
||||||
|
|
||||||
defp set_visibility(activity, _), do: {:ok, activity}
|
defp set_visibility(activity, _), do: {:ok, activity}
|
||||||
|
|
||||||
def hide_reblogs(user, muted) do
|
def hide_reblogs(user, %{ap_id: ap_id} = _muted) do
|
||||||
ap_id = muted.ap_id
|
|
||||||
|
|
||||||
if ap_id not in user.info.muted_reblogs do
|
if ap_id not in user.info.muted_reblogs do
|
||||||
info_changeset = User.Info.add_reblog_mute(user.info, ap_id)
|
User.update_info(user, &User.Info.add_reblog_mute(&1, ap_id))
|
||||||
changeset = Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset)
|
|
||||||
User.update_and_set_cache(changeset)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_reblogs(user, muted) do
|
def show_reblogs(user, %{ap_id: ap_id} = _muted) do
|
||||||
ap_id = muted.ap_id
|
|
||||||
|
|
||||||
if ap_id in user.info.muted_reblogs do
|
if ap_id in user.info.muted_reblogs do
|
||||||
info_changeset = User.Info.remove_reblog_mute(user.info, ap_id)
|
User.update_info(user, &User.Info.remove_reblog_mute(&1, ap_id))
|
||||||
changeset = Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset)
|
|
||||||
User.update_and_set_cache(changeset)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -189,14 +189,13 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||||
end)
|
end)
|
||||||
|> Map.put(:emoji, user_info_emojis)
|
|> Map.put(:emoji, user_info_emojis)
|
||||||
|
|
||||||
info_cng = User.Info.profile_update(user.info, info_params)
|
changeset =
|
||||||
|
user
|
||||||
|
|> User.update_changeset(user_params)
|
||||||
|
|> User.change_info(&User.Info.profile_update(&1, info_params))
|
||||||
|
|
||||||
with changeset <- User.update_changeset(user, user_params),
|
with {:ok, user} <- User.update_and_set_cache(changeset) do
|
||||||
changeset <- Changeset.put_embed(changeset, :info, info_cng),
|
if original_user != user, do: CommonAPI.update(user)
|
||||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
|
||||||
if original_user != user do
|
|
||||||
CommonAPI.update(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
json(
|
json(
|
||||||
conn,
|
conn,
|
||||||
|
@ -226,12 +225,10 @@ def update_avatar(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
|
def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
|
||||||
with new_info <- %{"banner" => %{}},
|
new_info = %{"banner" => %{}}
|
||||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
|
||||||
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_cng),
|
|
||||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
|
||||||
CommonAPI.update(user)
|
|
||||||
|
|
||||||
|
with {:ok, user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do
|
||||||
|
CommonAPI.update(user)
|
||||||
json(conn, %{url: nil})
|
json(conn, %{url: nil})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -239,9 +236,7 @@ def update_banner(%{assigns: %{user: user}} = conn, %{"banner" => ""}) do
|
||||||
def update_banner(%{assigns: %{user: user}} = conn, params) do
|
def update_banner(%{assigns: %{user: user}} = conn, params) do
|
||||||
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
|
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner),
|
||||||
new_info <- %{"banner" => object.data},
|
new_info <- %{"banner" => object.data},
|
||||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
{:ok, user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do
|
||||||
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_cng),
|
|
||||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
|
||||||
CommonAPI.update(user)
|
CommonAPI.update(user)
|
||||||
%{"url" => [%{"href" => href} | _]} = object.data
|
%{"url" => [%{"href" => href} | _]} = object.data
|
||||||
|
|
||||||
|
@ -250,10 +245,9 @@ def update_banner(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
|
def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
|
||||||
with new_info <- %{"background" => %{}},
|
new_info = %{"background" => %{}}
|
||||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
|
||||||
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_cng),
|
with {:ok, _user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do
|
||||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
|
||||||
json(conn, %{url: nil})
|
json(conn, %{url: nil})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -261,9 +255,7 @@ def update_background(%{assigns: %{user: user}} = conn, %{"img" => ""}) do
|
||||||
def update_background(%{assigns: %{user: user}} = conn, params) do
|
def update_background(%{assigns: %{user: user}} = conn, params) do
|
||||||
with {:ok, object} <- ActivityPub.upload(params, type: :background),
|
with {:ok, object} <- ActivityPub.upload(params, type: :background),
|
||||||
new_info <- %{"background" => object.data},
|
new_info <- %{"background" => object.data},
|
||||||
info_cng <- User.Info.profile_update(user.info, new_info),
|
{:ok, _user} <- User.update_info(user, &User.Info.profile_update(&1, new_info)) do
|
||||||
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_cng),
|
|
||||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
|
||||||
%{"url" => [%{"href" => href} | _]} = object.data
|
%{"url" => [%{"href" => href} | _]} = object.data
|
||||||
|
|
||||||
json(conn, %{url: href})
|
json(conn, %{url: href})
|
||||||
|
@ -817,26 +809,16 @@ def upload(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
|
||||||
def set_mascot(%{assigns: %{user: user}} = conn, %{"file" => file}) do
|
def set_mascot(%{assigns: %{user: user}} = conn, %{"file" => file}) do
|
||||||
with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)),
|
with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)),
|
||||||
%{} = attachment_data <- Map.put(object.data, "id", object.id),
|
%{} = attachment_data <- Map.put(object.data, "id", object.id),
|
||||||
%{type: type} = rendered <-
|
# Reject if not an image
|
||||||
|
%{type: "image"} = rendered <-
|
||||||
StatusView.render("attachment.json", %{attachment: attachment_data}) do
|
StatusView.render("attachment.json", %{attachment: attachment_data}) do
|
||||||
# Reject if not an image
|
# Sure!
|
||||||
if type == "image" do
|
# Save to the user's info
|
||||||
# Sure!
|
{:ok, _user} = User.update_info(user, &User.Info.mascot_update(&1, rendered))
|
||||||
# Save to the user's info
|
|
||||||
info_changeset = User.Info.mascot_update(user.info, rendered)
|
|
||||||
|
|
||||||
user_changeset =
|
json(conn, rendered)
|
||||||
user
|
else
|
||||||
|> Changeset.change()
|
%{type: _} -> render_error(conn, :unsupported_media_type, "mascots can only be images")
|
||||||
|> Changeset.put_embed(:info, info_changeset)
|
|
||||||
|
|
||||||
{:ok, _user} = User.update_and_set_cache(user_changeset)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> json(rendered)
|
|
||||||
else
|
|
||||||
render_error(conn, :unsupported_media_type, "mascots can only be images")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -959,11 +941,11 @@ def following(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def follow_requests(%{assigns: %{user: followed}} = conn, _params) do
|
def follow_requests(%{assigns: %{user: followed}} = conn, _params) do
|
||||||
with {:ok, follow_requests} <- User.get_follow_requests(followed) do
|
follow_requests = User.get_follow_requests(followed)
|
||||||
conn
|
|
||||||
|> put_view(AccountView)
|
conn
|
||||||
|> render("accounts.json", %{for: followed, users: follow_requests, as: :user})
|
|> put_view(AccountView)
|
||||||
end
|
|> render("accounts.json", %{for: followed, users: follow_requests, as: :user})
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
def authorize_follow_request(%{assigns: %{user: followed}} = conn, %{"id" => id}) do
|
||||||
|
@ -1367,11 +1349,7 @@ def index(%{assigns: %{user: user}} = conn, _params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
|
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
|
||||||
info_cng = User.Info.mastodon_settings_update(user.info, settings)
|
with {:ok, _} <- User.update_info(user, &User.Info.mastodon_settings_update(&1, settings)) do
|
||||||
|
|
||||||
with changeset <- Changeset.change(user),
|
|
||||||
changeset <- Changeset.put_embed(changeset, :info, info_cng),
|
|
||||||
{:ok, _user} <- User.update_and_set_cache(changeset) do
|
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
else
|
else
|
||||||
e ->
|
e ->
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
defmodule Pleroma.Web.TwitterAPI.Controller do
|
defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Ecto.Changeset
|
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
@ -16,15 +15,12 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
|
def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
|
||||||
with %User{} = user <- User.get_cached_by_id(uid),
|
new_info = [need_confirmation: false]
|
||||||
true <- user.local,
|
|
||||||
true <- user.info.confirmation_pending,
|
with %User{info: info} = user <- User.get_cached_by_id(uid),
|
||||||
true <- user.info.confirmation_token == token,
|
true <- user.local and info.confirmation_pending and info.confirmation_token == token,
|
||||||
info_change <- User.Info.confirmation_changeset(user.info, need_confirmation: false),
|
{:ok, _} <- User.update_info(user, &User.Info.confirmation_changeset(&1, new_info)) do
|
||||||
changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_change),
|
redirect(conn, to: "/")
|
||||||
{:ok, _} <- User.update_and_set_cache(changeset) do
|
|
||||||
conn
|
|
||||||
|> redirect(to: "/")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -77,12 +77,10 @@ test "following and followers count are updated" do
|
||||||
assert length(following) == 2
|
assert length(following) == 2
|
||||||
assert info.follower_count == 0
|
assert info.follower_count == 0
|
||||||
|
|
||||||
info_cng = Ecto.Changeset.change(info, %{follower_count: 3})
|
|
||||||
|
|
||||||
{:ok, user} =
|
{:ok, user} =
|
||||||
user
|
user
|
||||||
|> Ecto.Changeset.change(%{following: following ++ following})
|
|> Ecto.Changeset.change(%{following: following ++ following})
|
||||||
|> Ecto.Changeset.put_embed(:info, info_cng)
|
|> User.change_info(&Ecto.Changeset.change(&1, %{follower_count: 3}))
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
|
|
||||||
assert length(user.following) == 4
|
assert length(user.following) == 4
|
||||||
|
|
|
@ -74,8 +74,8 @@ test "returns all pending follow requests" do
|
||||||
CommonAPI.follow(follower, unlocked)
|
CommonAPI.follow(follower, unlocked)
|
||||||
CommonAPI.follow(follower, locked)
|
CommonAPI.follow(follower, locked)
|
||||||
|
|
||||||
assert {:ok, []} = User.get_follow_requests(unlocked)
|
assert [] = User.get_follow_requests(unlocked)
|
||||||
assert {:ok, [activity]} = User.get_follow_requests(locked)
|
assert [activity] = User.get_follow_requests(locked)
|
||||||
|
|
||||||
assert activity
|
assert activity
|
||||||
end
|
end
|
||||||
|
@ -90,7 +90,7 @@ test "doesn't return already accepted or duplicate follow requests" do
|
||||||
CommonAPI.follow(accepted_follower, locked)
|
CommonAPI.follow(accepted_follower, locked)
|
||||||
User.follow(accepted_follower, locked)
|
User.follow(accepted_follower, locked)
|
||||||
|
|
||||||
assert {:ok, [activity]} = User.get_follow_requests(locked)
|
assert [activity] = User.get_follow_requests(locked)
|
||||||
assert activity
|
assert activity
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -99,10 +99,10 @@ test "clears follow requests when requester is blocked" do
|
||||||
follower = insert(:user)
|
follower = insert(:user)
|
||||||
|
|
||||||
CommonAPI.follow(follower, followed)
|
CommonAPI.follow(follower, followed)
|
||||||
assert {:ok, [_activity]} = User.get_follow_requests(followed)
|
assert [_activity] = User.get_follow_requests(followed)
|
||||||
|
|
||||||
{:ok, _follower} = User.block(followed, follower)
|
{:ok, _follower} = User.block(followed, follower)
|
||||||
assert {:ok, []} = User.get_follow_requests(followed)
|
assert [] = User.get_follow_requests(followed)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "follow_all follows mutliple users" do
|
test "follow_all follows mutliple users" do
|
||||||
|
@ -560,7 +560,7 @@ test "it sets the follower_adress" do
|
||||||
|
|
||||||
test "it enforces the fqn format for nicknames" do
|
test "it enforces the fqn format for nicknames" do
|
||||||
cs = User.remote_user_creation(%{@valid_remote | nickname: "bla"})
|
cs = User.remote_user_creation(%{@valid_remote | nickname: "bla"})
|
||||||
assert cs.changes.local == false
|
assert Ecto.Changeset.get_field(cs, :local) == false
|
||||||
assert cs.changes.avatar
|
assert cs.changes.avatar
|
||||||
refute cs.valid?
|
refute cs.valid?
|
||||||
end
|
end
|
||||||
|
@ -584,7 +584,7 @@ test "gets all followers for a given user" do
|
||||||
{:ok, follower_one} = User.follow(follower_one, user)
|
{:ok, follower_one} = User.follow(follower_one, user)
|
||||||
{:ok, follower_two} = User.follow(follower_two, user)
|
{:ok, follower_two} = User.follow(follower_two, user)
|
||||||
|
|
||||||
{:ok, res} = User.get_followers(user)
|
res = User.get_followers(user)
|
||||||
|
|
||||||
assert Enum.member?(res, follower_one)
|
assert Enum.member?(res, follower_one)
|
||||||
assert Enum.member?(res, follower_two)
|
assert Enum.member?(res, follower_two)
|
||||||
|
@ -600,7 +600,7 @@ test "gets all friends (followed users) for a given user" do
|
||||||
{:ok, user} = User.follow(user, followed_one)
|
{:ok, user} = User.follow(user, followed_one)
|
||||||
{:ok, user} = User.follow(user, followed_two)
|
{:ok, user} = User.follow(user, followed_two)
|
||||||
|
|
||||||
{:ok, res} = User.get_friends(user)
|
res = User.get_friends(user)
|
||||||
|
|
||||||
followed_one = User.get_cached_by_ap_id(followed_one.ap_id)
|
followed_one = User.get_cached_by_ap_id(followed_one.ap_id)
|
||||||
followed_two = User.get_cached_by_ap_id(followed_two.ap_id)
|
followed_two = User.get_cached_by_ap_id(followed_two.ap_id)
|
||||||
|
@ -975,7 +975,7 @@ test "hide a user from followers " do
|
||||||
info = User.get_cached_user_info(user2)
|
info = User.get_cached_user_info(user2)
|
||||||
|
|
||||||
assert info.follower_count == 0
|
assert info.follower_count == 0
|
||||||
assert {:ok, []} = User.get_followers(user2)
|
assert [] = User.get_followers(user2)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "hide a user from friends" do
|
test "hide a user from friends" do
|
||||||
|
@ -991,7 +991,7 @@ test "hide a user from friends" do
|
||||||
|
|
||||||
assert info.following_count == 0
|
assert info.following_count == 0
|
||||||
assert User.following_count(user2) == 0
|
assert User.following_count(user2) == 0
|
||||||
assert {:ok, []} = User.get_friends(user2)
|
assert [] = User.get_friends(user2)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "hide a user's statuses from timelines and notifications" do
|
test "hide a user's statuses from timelines and notifications" do
|
||||||
|
@ -1034,7 +1034,7 @@ test "hide a user's statuses from timelines and notifications" do
|
||||||
test ".delete_user_activities deletes all create activities", %{user: user} do
|
test ".delete_user_activities deletes all create activities", %{user: user} do
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
|
||||||
|
|
||||||
{:ok, _} = User.delete_user_activities(user)
|
User.delete_user_activities(user)
|
||||||
|
|
||||||
# TODO: Remove favorites, repeats, delete activities.
|
# TODO: Remove favorites, repeats, delete activities.
|
||||||
refute Activity.get_by_id(activity.id)
|
refute Activity.get_by_id(activity.id)
|
||||||
|
@ -1707,4 +1707,22 @@ test "sets password_reset_pending to true", %{user: user} do
|
||||||
assert password_reset_pending
|
assert password_reset_pending
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "change_info/2" do
|
||||||
|
user = insert(:user)
|
||||||
|
assert user.info.hide_follows == false
|
||||||
|
|
||||||
|
changeset = User.change_info(user, &User.Info.profile_update(&1, %{hide_follows: true}))
|
||||||
|
assert changeset.changes.info.changes.hide_follows == true
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update_info/2" do
|
||||||
|
user = insert(:user)
|
||||||
|
assert user.info.hide_follows == false
|
||||||
|
|
||||||
|
assert {:ok, _} = User.update_info(user, &User.Info.profile_update(&1, %{hide_follows: true}))
|
||||||
|
|
||||||
|
assert %{info: %{hide_follows: true}} = Repo.get(User, user.id)
|
||||||
|
assert {:ok, %{info: %{hide_follows: true}}} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2613,14 +2613,11 @@ test "get instance stats", %{conn: conn} do
|
||||||
{:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
|
{:ok, _} = CommonAPI.post(user, %{"status" => "cofe"})
|
||||||
|
|
||||||
# Stats should count users with missing or nil `info.deactivated` value
|
# Stats should count users with missing or nil `info.deactivated` value
|
||||||
user = User.get_cached_by_id(user.id)
|
|
||||||
info_change = Changeset.change(user.info, %{deactivated: nil})
|
|
||||||
|
|
||||||
{:ok, _user} =
|
{:ok, _user} =
|
||||||
user
|
user.id
|
||||||
|> Changeset.change()
|
|> User.get_cached_by_id()
|
||||||
|> Changeset.put_embed(:info, info_change)
|
|> User.update_info(&Changeset.change(&1, %{deactivated: nil}))
|
||||||
|> User.update_and_set_cache()
|
|
||||||
|
|
||||||
Pleroma.Stats.force_update()
|
Pleroma.Stats.force_update()
|
||||||
|
|
||||||
|
@ -3953,13 +3950,9 @@ test "it returns 400 when user is not local", %{conn: conn, user: user} do
|
||||||
|
|
||||||
describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
|
describe "POST /api/v1/pleroma/accounts/confirmation_resend" do
|
||||||
setup do
|
setup do
|
||||||
user = insert(:user)
|
|
||||||
info_change = User.Info.confirmation_changeset(user.info, need_confirmation: true)
|
|
||||||
|
|
||||||
{:ok, user} =
|
{:ok, user} =
|
||||||
user
|
insert(:user)
|
||||||
|> Changeset.change()
|
|> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
|
||||||
|> Changeset.put_embed(:info, info_change)
|
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
|
|
||||||
assert user.info.confirmation_pending
|
assert user.info.confirmation_pending
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.OAuthControllerTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.Authorization
|
alias Pleroma.Web.OAuth.Authorization
|
||||||
alias Pleroma.Web.OAuth.OAuthController
|
alias Pleroma.Web.OAuth.OAuthController
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
@ -775,15 +776,11 @@ test "rejects token exchange with invalid client credentials" do
|
||||||
|
|
||||||
test "rejects token exchange for valid credentials belonging to unconfirmed user and confirmation is required" do
|
test "rejects token exchange for valid credentials belonging to unconfirmed user and confirmation is required" do
|
||||||
Pleroma.Config.put([:instance, :account_activation_required], true)
|
Pleroma.Config.put([:instance, :account_activation_required], true)
|
||||||
|
|
||||||
password = "testpassword"
|
password = "testpassword"
|
||||||
user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password))
|
|
||||||
info_change = Pleroma.User.Info.confirmation_changeset(user.info, need_confirmation: true)
|
|
||||||
|
|
||||||
{:ok, user} =
|
{:ok, user} =
|
||||||
user
|
insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt(password))
|
||||||
|> Ecto.Changeset.change()
|
|> User.change_info(&User.Info.confirmation_changeset(&1, need_confirmation: true))
|
||||||
|> Ecto.Changeset.put_embed(:info, info_change)
|
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
|
|
||||||
refute Pleroma.User.auth_active?(user)
|
refute Pleroma.User.auth_active?(user)
|
||||||
|
|
|
@ -50,20 +50,16 @@ test "decodes a salmon with a changed magic key", %{conn: conn} do
|
||||||
assert response(conn, 200)
|
assert response(conn, 200)
|
||||||
end) =~ "[error]"
|
end) =~ "[error]"
|
||||||
|
|
||||||
# Set a wrong magic-key for a user so it has to refetch
|
|
||||||
salmon_user = User.get_cached_by_ap_id("http://gs.example.org:4040/index.php/user/1")
|
|
||||||
|
|
||||||
# Wrong key
|
# Wrong key
|
||||||
info_cng =
|
info = %{
|
||||||
User.Info.remote_user_creation(salmon_user.info, %{
|
magic_key:
|
||||||
magic_key:
|
"RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwrong1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB"
|
||||||
"RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwrong1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB"
|
}
|
||||||
})
|
|
||||||
|
|
||||||
salmon_user
|
# Set a wrong magic-key for a user so it has to refetch
|
||||||
|> Ecto.Changeset.change()
|
"http://gs.example.org:4040/index.php/user/1"
|
||||||
|> Ecto.Changeset.put_embed(:info, info_cng)
|
|> User.get_cached_by_ap_id()
|
||||||
|> User.update_and_set_cache()
|
|> User.update_info(&User.Info.remote_user_creation(&1, info))
|
||||||
|
|
||||||
assert capture_log(fn ->
|
assert capture_log(fn ->
|
||||||
conn =
|
conn =
|
||||||
|
|
Loading…
Reference in a new issue