alias Pleroma.Conversation.Participation
alias Pleroma.Delivery
alias Pleroma.FollowingRelationship
+ alias Pleroma.Formatter
alias Pleroma.HTML
alias Pleroma.Keys
alias Pleroma.Notification
end
end
+ @doc """
+ Dumps Flake Id to SQL-compatible format (16-byte UUID).
+ E.g. "9pQtDGXuq4p3VlcJEm" -> <<0, 0, 1, 110, 179, 218, 42, 92, 213, 41, 44, 227, 95, 213, 0, 0>>
+ """
+ def binary_id(source_id) when is_binary(source_id) do
+ with {:ok, dumped_id} <- FlakeId.Ecto.CompatType.dump(source_id) do
+ dumped_id
+ else
+ _ -> source_id
+ end
+ end
+
+ def binary_id(source_ids) when is_list(source_ids) do
+ Enum.map(source_ids, &binary_id/1)
+ end
+
+ def binary_id(%User{} = user), do: binary_id(user.id)
+
@doc "Returns status account"
@spec account_status(User.t()) :: account_status()
def account_status(%User{deactivated: true}), do: :deactivated
end
end
- def profile_url(%User{source_data: %{"url" => url}}), do: url
- def profile_url(%User{ap_id: ap_id}), do: ap_id
- def profile_url(_), do: nil
-
def ap_id(%User{nickname: nickname}), do: "#{Web.base_url()}/users/#{nickname}"
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
- @spec ap_following(User.t()) :: Sring.t()
+ @spec ap_following(User.t()) :: String.t()
def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
|> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: bio_limit)
|> validate_length(:name, min: 1, max: name_limit)
+ |> put_fields()
+ |> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
+ |> put_change_if_present(:avatar, &put_upload(&1, :avatar))
+ |> put_change_if_present(:banner, &put_upload(&1, :banner))
+ |> put_change_if_present(:background, &put_upload(&1, :background))
+ |> put_change_if_present(
+ :pleroma_settings_store,
+ &{:ok, Map.merge(struct.pleroma_settings_store, &1)}
+ )
|> validate_fields(false)
end
+ defp put_fields(changeset) do
+ if raw_fields = get_change(changeset, :raw_fields) do
+ raw_fields =
+ raw_fields
+ |> Enum.filter(fn %{"name" => n} -> n != "" end)
+
+ fields =
+ raw_fields
+ |> Enum.map(fn f -> Map.update!(f, "value", &parse_fields(&1)) end)
+
+ changeset
+ |> put_change(:raw_fields, raw_fields)
+ |> put_change(:fields, fields)
+ else
+ changeset
+ end
+ end
+
+ defp parse_fields(value) do
+ value
+ |> Formatter.linkify(mentions_format: :full)
+ |> elem(0)
+ end
+
+ defp put_change_if_present(changeset, map_field, value_function) do
+ if value = get_change(changeset, map_field) do
+ with {:ok, new_value} <- value_function.(value) do
+ put_change(changeset, map_field, new_value)
+ else
+ _ -> changeset
+ end
+ else
+ changeset
+ end
+ end
+
+ defp put_upload(value, type) do
+ with %Plug.Upload{} <- value,
+ {:ok, object} <- ActivityPub.upload(value, type: type) do
+ {:ok, object.data}
+ end
+ end
+
def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
bio_limit = Pleroma.Config.get([:instance, :user_bio_length], 5000)
name_limit = Pleroma.Config.get([:instance, :user_name_length], 100)
|> validate_fields(remote?)
end
+ def update_as_admin_changeset(struct, params) do
+ struct
+ |> update_changeset(params)
+ |> cast(params, [:email])
+ |> delete_change(:also_known_as)
+ |> unique_constraint(:email)
+ |> validate_format(:email, @email_regex)
+ end
+
+ @spec update_as_admin(%User{}, map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
+ def update_as_admin(user, params) do
+ params = Map.put(params, "password_confirmation", params["password"])
+ changeset = update_as_admin_changeset(user, params)
+
+ if params["password"] do
+ reset_password(user, changeset, params)
+ else
+ User.update_and_set_cache(changeset)
+ end
+ end
+
def password_update_changeset(struct, params) do
struct
|> cast(params, [:password, :password_confirmation])
end
@spec reset_password(User.t(), map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
- def reset_password(%User{id: user_id} = user, data) do
+ def reset_password(%User{} = user, params) do
+ reset_password(user, user, params)
+ end
+
+ def reset_password(%User{id: user_id} = user, struct, params) do
multi =
Multi.new()
- |> Multi.update(:user, password_update_changeset(user, data))
+ |> Multi.update(:user, password_update_changeset(struct, params))
|> Multi.delete_all(:tokens, OAuth.Token.Query.get_by_user(user_id))
|> Multi.delete_all(:auth, OAuth.Authorization.delete_by_user_query(user))
def needs_update?(_), do: true
@spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
+
+ # "Locked" (self-locked) users demand explicit authorization of follow requests
def maybe_direct_follow(%User{} = follower, %User{local: true, locked: true} = followed) do
- follow(follower, followed, "pending")
+ follow(follower, followed, :follow_pending)
end
def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do
def follow_all(follower, followeds) do
followeds
|> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end)
- |> Enum.each(&follow(follower, &1, "accept"))
+ |> Enum.each(&follow(follower, &1, :follow_accept))
set_cache(follower)
end
defdelegate following(user), to: FollowingRelationship
- def follow(%User{} = follower, %User{} = followed, state \\ "accept") do
+ def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do
deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
cond do
def unfollow(%User{} = follower, %User{} = followed) do
case get_follow_state(follower, followed) do
- state when state in ["accept", "pending"] ->
+ state when state in [:follow_pending, :follow_accept] ->
FollowingRelationship.unfollow(follower, followed)
{:ok, followed} = update_follower_count(followed)
defdelegate following?(follower, followed), to: FollowingRelationship
+ @doc "Returns follow state as Pleroma.FollowingRelationship.State value"
def get_follow_state(%User{} = follower, %User{} = following) do
following_relationship = FollowingRelationship.get(follower, following)
case {following_relationship, following.local} do
{nil, false} ->
case Utils.fetch_latest_follow(follower, following) do
- %{data: %{"state" => state}} when state in ["pending", "accept"] -> state
- _ -> nil
+ %Activity{data: %{"state" => state}} when state in ["pending", "accept"] ->
+ FollowingRelationship.state_to_enum(state)
+
+ _ ->
+ nil
end
{%{state: state}, _} ->
def blocks?(%User{} = user, %User{} = target) do
blocks_user?(user, target) ||
- (!User.following?(user, target) && blocks_domain?(user, target))
+ (blocks_domain?(user, target) and not User.following?(user, target))
end
def blocks_user?(%User{} = user, %User{} = target) do
end)
delete_user_activities(user)
- invalidate_cache(user)
- Repo.delete(user)
+
+ if user.local do
+ user
+ |> change(%{deactivated: true, email: nil})
+ |> update_and_set_cache()
+ else
+ invalidate_cache(user)
+ Repo.delete(user)
+ end
end
def perform(:deactivate_async, user, status), do: deactivate(user, status)