alias Comeonin.Pbkdf2
alias Pleroma.Activity
+ alias Pleroma.Keys
alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.Registration
field(:last_refreshed_at, :naive_datetime_usec)
has_many(:notifications, Notification)
has_many(:registrations, Registration)
- embeds_one(:info, Pleroma.User.Info)
+ embeds_one(:info, User.Info)
timestamps()
end
def update_changeset(struct, params \\ %{}) do
struct
- |> cast(params, [:bio, :name, :avatar])
+ |> cast(params, [:bio, :name, :avatar, :following])
|> unique_constraint(:nickname)
|> validate_format(:nickname, local_nickname_regex())
|> validate_length(:bio, max: 5000)
|> validate_confirmation(:password)
|> unique_constraint(:email)
|> unique_constraint(:nickname)
- |> validate_exclusion(:nickname, Pleroma.Config.get([Pleroma.User, :restricted_nicknames]))
+ |> validate_exclusion(:nickname, Pleroma.Config.get([User, :restricted_nicknames]))
|> validate_format(:nickname, local_nickname_regex())
|> validate_format(:email, @email_regex)
|> validate_length(:bio, max: 1000)
with {:ok, user} <- Repo.insert(changeset),
{:ok, user} <- autofollow_users(user),
{:ok, user} <- set_cache(user),
- {:ok, _} <- Pleroma.User.WelcomeMessage.post_welcome_message_to_user(user),
+ {:ok, _} <- User.WelcomeMessage.post_welcome_message_to_user(user),
{:ok, _} <- try_send_confirmation_email(user) do
{:ok, user}
end
end
end
- def maybe_follow(%User{} = follower, %User{info: _info} = followed) do
- if not following?(follower, followed) do
- follow(follower, followed)
- else
- {:ok, follower}
- end
- end
-
@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
end
def follow(%User{} = follower, %User{info: info} = followed) do
- user_config = Application.get_env(:pleroma, :user)
- deny_follow_blocked = Keyword.get(user_config, :deny_follow_blocked)
-
+ deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
ap_followers = followed.follower_address
cond do
- following?(follower, followed) or info.deactivated ->
- {:error, "Could not follow user: #{followed.nickname} is already on your list."}
+ info.deactivated ->
+ {:error, "Could not follow user: You are deactivated."}
deny_follow_blocked and blocks?(followed, follower) ->
{:error, "Could not follow user: #{followed.nickname} blocked you."}
end
end
+ 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}
|> Repo.all()
end
- def search(query, resolve \\ false, for_user \\ nil) do
- # Strip the beginning @ off if there is a query
- query = String.trim_leading(query, "@")
-
- if resolve, do: get_or_fetch(query)
-
- {:ok, results} =
- Repo.transaction(fn ->
- Ecto.Adapters.SQL.query(Repo, "select set_limit(0.25)", [])
- Repo.all(search_query(query, for_user))
- end)
-
- results
- end
-
- def search_query(query, for_user) do
- fts_subquery = fts_search_subquery(query)
- trigram_subquery = trigram_search_subquery(query)
- union_query = from(s in trigram_subquery, union_all: ^fts_subquery)
- distinct_query = from(s in subquery(union_query), order_by: s.search_type, distinct: s.id)
-
- from(s in subquery(boost_search_rank_query(distinct_query, for_user)),
- order_by: [desc: s.search_rank],
- limit: 20
- )
- end
-
- defp boost_search_rank_query(query, nil), do: query
-
- defp boost_search_rank_query(query, for_user) do
- friends_ids = get_friends_ids(for_user)
- followers_ids = get_followers_ids(for_user)
-
- from(u in subquery(query),
- select_merge: %{
- search_rank:
- fragment(
- """
- CASE WHEN (?) THEN (?) * 1.3
- WHEN (?) THEN (?) * 1.2
- WHEN (?) THEN (?) * 1.1
- ELSE (?) END
- """,
- u.id in ^friends_ids and u.id in ^followers_ids,
- u.search_rank,
- u.id in ^friends_ids,
- u.search_rank,
- u.id in ^followers_ids,
- u.search_rank,
- u.search_rank
- )
- }
- )
- end
-
- defp fts_search_subquery(term, query \\ User) do
- processed_query =
- term
- |> String.replace(~r/\W+/, " ")
- |> String.trim()
- |> String.split()
- |> Enum.map(&(&1 <> ":*"))
- |> Enum.join(" | ")
-
- from(
- u in query,
- select_merge: %{
- search_type: ^0,
- search_rank:
- fragment(
- """
- ts_rank_cd(
- setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
- setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B'),
- to_tsquery('simple', ?),
- 32
- )
- """,
- u.nickname,
- u.name,
- ^processed_query
- )
- },
- where:
- fragment(
- """
- (setweight(to_tsvector('simple', regexp_replace(?, '\\W', ' ', 'g')), 'A') ||
- setweight(to_tsvector('simple', regexp_replace(coalesce(?, ''), '\\W', ' ', 'g')), 'B')) @@ to_tsquery('simple', ?)
- """,
- u.nickname,
- u.name,
- ^processed_query
- )
- )
- |> restrict_deactivated()
- end
-
- defp trigram_search_subquery(term) do
- from(
- u in User,
- select_merge: %{
- # ^1 gives 'Postgrex expected a binary, got 1' for some weird reason
- search_type: fragment("?", 1),
- search_rank:
- fragment(
- "similarity(?, trim(? || ' ' || coalesce(?, '')))",
- ^term,
- u.nickname,
- u.name
- )
- },
- where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term)
- )
- |> restrict_deactivated()
- end
-
def mute(muter, %User{ap_id: ap_id}) do
info_cng =
muter.info
stream =
ap_id
|> Activity.query_by_actor()
- |> Activity.with_preloaded_object()
|> Repo.stream()
Repo.transaction(fn -> Enum.each(stream, &delete_activity(&1)) end, timeout: :infinity)
def showing_reblogs?(%User{} = user, %User{} = target) do
target.ap_id not in user.info.muted_reblogs
end
+
+ @spec toggle_confirmation(User.t()) :: {:ok, User.t()} | {:error, Changeset.t()}
+ def toggle_confirmation(%User{} = user) do
+ need_confirmation? = !user.info.confirmation_pending
+
+ info_changeset =
+ User.Info.confirmation_changeset(user.info, need_confirmation: need_confirmation?)
+
+ user
+ |> change()
+ |> put_embed(:info, info_changeset)
+ |> update_and_set_cache()
+ end
+
+ def get_mascot(%{info: %{mascot: %{} = mascot}}) when not is_nil(mascot) do
+ mascot
+ end
+
+ def get_mascot(%{info: %{mascot: mascot}}) when is_nil(mascot) do
+ # use instance-default
+ config = Pleroma.Config.get([:assets, :mascots])
+ default_mascot = Pleroma.Config.get([:assets, :default_mascot])
+ mascot = Keyword.get(config, default_mascot)
+
+ %{
+ "id" => "default-mascot",
+ "url" => mascot[:url],
+ "preview_url" => mascot[:url],
+ "pleroma" => %{
+ "mime_type" => mascot[:mime_type]
+ }
+ }
+ end
+
+ def ensure_keys_present(user) do
+ info = user.info
+
+ if info.keys do
+ {:ok, user}
+ else
+ {:ok, pem} = Keys.generate_rsa_pem()
+
+ info_cng =
+ info
+ |> User.Info.set_keys(pem)
+
+ cng =
+ Ecto.Changeset.change(user)
+ |> Ecto.Changeset.put_embed(:info, info_cng)
+
+ update_and_set_cache(cng)
+ end
+ end
+
+ def get_ap_ids_by_nicknames(nicknames) do
+ from(u in User,
+ where: u.nickname in ^nicknames,
+ select: u.ap_id
+ )
+ |> Repo.all()
+ end
+
+ defdelegate search(query, opts \\ []), to: User.Search
end