X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;ds=sidebyside;f=lib%2Fpleroma%2Fuser.ex;h=692ae836c36ff0d265c6fa0e39a053e48eb56d0b;hb=e8c2f9a73a37636a9a8ed5c2998617b841f482da;hp=3a8a51e33d875324bf0bb271446ce0f0adf9f746;hpb=c1ae495878cc95d09a051cc56e2bf7dc7ed0a032;p=akkoma diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 3a8a51e33..6aaa3244f 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -8,20 +8,22 @@ defmodule Pleroma.User do import Ecto.Changeset import Ecto.Query + alias Comeonin.Pbkdf2 + alias Pleroma.Activity + alias Pleroma.Formatter + alias Pleroma.Notification + alias Pleroma.Object + alias Pleroma.Registration alias Pleroma.Repo alias Pleroma.User - alias Pleroma.Object alias Pleroma.Web - alias Pleroma.Activity - alias Pleroma.Notification - alias Comeonin.Pbkdf2 - alias Pleroma.Formatter + alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils + alias Pleroma.Web.OAuth alias Pleroma.Web.OStatus + alias Pleroma.Web.RelMe alias Pleroma.Web.Websub - alias Pleroma.Web.OAuth - alias Pleroma.Web.ActivityPub.Utils - alias Pleroma.Web.ActivityPub.ActivityPub require Logger @@ -29,6 +31,7 @@ defmodule Pleroma.User do @primary_key {:id, Pleroma.FlakeId, autogenerate: true} + # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength @email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ @strict_local_nickname_regex ~r/^[a-zA-Z\d]+$/ @@ -48,23 +51,21 @@ defmodule Pleroma.User do field(:local, :boolean, default: true) field(:follower_address, :string) field(:search_rank, :float, virtual: true) + field(:search_type, :integer, virtual: true) field(:tags, {:array, :string}, default: []) field(:bookmarks, {:array, :string}, default: []) - field(:last_refreshed_at, :naive_datetime) + field(:last_refreshed_at, :naive_datetime_usec) has_many(:notifications, Notification) + has_many(:registrations, Registration) embeds_one(:info, Pleroma.User.Info) timestamps() end - def auth_active?(%User{local: false}), do: true - - def auth_active?(%User{info: %User.Info{confirmation_pending: false}}), do: true - def auth_active?(%User{info: %User.Info{confirmation_pending: true}}), do: !Pleroma.Config.get([:instance, :account_activation_required]) - def auth_active?(_), do: false + def auth_active?(%User{}), do: true def visible_for?(user, for_user \\ nil) @@ -80,17 +81,17 @@ defmodule Pleroma.User do def superuser?(%User{local: true, info: %User.Info{is_moderator: true}}), do: true def superuser?(_), do: false - def avatar_url(user) do + def avatar_url(user, options \\ []) do case user.avatar do %{"url" => [%{"href" => href} | _]} -> href - _ -> "#{Web.base_url()}/images/avi.png" + _ -> !options[:no_default] && "#{Web.base_url()}/images/avi.png" end end - def banner_url(user) do + def banner_url(user, options \\ []) do case user.info.banner do %{"url" => [%{"href" => href} | _]} -> href - _ -> "#{Web.base_url()}/images/banner.png" + _ -> !options[:no_default] && "#{Web.base_url()}/images/banner.png" end end @@ -102,15 +103,12 @@ defmodule Pleroma.User do "#{Web.base_url()}/users/#{nickname}" end - def ap_followers(%User{} = user) do - "#{ap_id(user)}/followers" - end + def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa + def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers" def user_info(%User{} = user) do - oneself = if user.local, do: 1, else: 0 - %{ - following_count: length(user.following) - oneself, + following_count: following_count(user), note_count: user.info.note_count, follower_count: user.info.follower_count, locked: user.info.locked, @@ -119,6 +117,23 @@ defmodule Pleroma.User do } end + defp restrict_deactivated(query) do + from(u in query, + where: not fragment("? \\? 'deactivated' AND ?->'deactivated' @> 'true'", u.info, u.info) + ) + end + + def following_count(%User{following: []}), do: 0 + + def following_count(%User{following: following, id: id}) do + from(u in User, + where: u.follower_address in ^following, + where: u.id != ^id + ) + |> restrict_deactivated() + |> Repo.aggregate(:count, :id) + end + def remote_user_creation(params) do params = params @@ -218,7 +233,7 @@ defmodule Pleroma.User do changeset = struct |> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation]) - |> validate_required([:email, :name, :nickname, :password, :password_confirmation]) + |> validate_required([:name, :nickname, :password, :password_confirmation]) |> validate_confirmation(:password) |> unique_constraint(:email) |> unique_constraint(:nickname) @@ -229,6 +244,13 @@ defmodule Pleroma.User do |> validate_length(:name, min: 1, max: 100) |> put_change(:info, info_change) + changeset = + if opts[:external] do + changeset + else + validate_required(changeset, [:email]) + end + if changeset.valid? do hashed = Pbkdf2.hashpwsalt(changeset.changes[:password]) ap_id = User.ap_id(%User{nickname: changeset.changes[:nickname]}) @@ -272,8 +294,10 @@ defmodule Pleroma.User do if user.info.confirmation_pending && Pleroma.Config.get([:instance, :account_activation_required]) do user - |> Pleroma.UserEmail.account_confirmation_email() - |> Pleroma.Mailer.deliver_async() + |> Pleroma.Emails.UserEmail.account_confirmation_email() + |> Pleroma.Emails.Mailer.deliver_async() + + {:ok, :enqueued} else {:ok, :noop} end @@ -284,7 +308,7 @@ defmodule Pleroma.User do def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true def needs_update?(%User{local: false} = user) do - NaiveDateTime.diff(NaiveDateTime.utc_now(), user.last_refreshed_at) >= 86400 + NaiveDateTime.diff(NaiveDateTime.utc_now(), user.last_refreshed_at) >= 86_400 end def needs_update?(_), do: true @@ -333,10 +357,11 @@ defmodule Pleroma.User do ^followed_addresses ) ] - ] + ], + select: u ) - {1, [follower]} = Repo.update_all(q, [], returning: true) + {1, [follower]} = Repo.update_all(q, []) Enum.each(followeds, fn followed -> update_follower_count(followed) @@ -366,10 +391,11 @@ defmodule Pleroma.User do q = from(u in User, where: u.id == ^follower.id, - update: [push: [following: ^ap_followers]] + update: [push: [following: ^ap_followers]], + select: u ) - {1, [follower]} = Repo.update_all(q, [], returning: true) + {1, [follower]} = Repo.update_all(q, []) {:ok, _} = update_follower_count(followed) @@ -384,10 +410,11 @@ defmodule Pleroma.User do q = from(u in User, where: u.id == ^follower.id, - update: [pull: [following: ^ap_followers]] + update: [pull: [following: ^ap_followers]], + select: u ) - {1, [follower]} = Repo.update_all(q, [], returning: true) + {1, [follower]} = Repo.update_all(q, []) {:ok, followed} = update_follower_count(followed) @@ -434,7 +461,8 @@ defmodule Pleroma.User do Repo.get_by(User, ap_id: ap_id) end - # This is mostly an SPC migration fix. This guesses the user nickname (by taking the last part of the ap_id and the domain) and tries to get that user + # This is mostly an SPC migration fix. This guesses the user nickname by taking the last part + # of the ap_id and the domain and tries to get that user def get_by_guessed_nickname(ap_id) do domain = URI.parse(ap_id).host name = List.last(String.split(ap_id, "/")) @@ -503,11 +531,10 @@ defmodule Pleroma.User do end end + def get_by_email(email), do: Repo.get_by(User, email: email) + def get_by_nickname_or_email(nickname_or_email) do - case user = Repo.get_by(User, nickname: nickname_or_email) do - %User{} -> user - nil -> Repo.get_by(User, email: nickname_or_email) - end + get_by_nickname(nickname_or_email) || get_by_email(nickname_or_email) end def get_cached_user_info(user) do @@ -531,6 +558,10 @@ defmodule Pleroma.User do _e -> with [_nick, _domain] <- String.split(nickname, "@"), {:ok, user} <- fetch_by_nickname(nickname) do + if Pleroma.Config.get([:fetch_initial_posts, :enabled]) do + {:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user]) + end + user else _e -> nil @@ -538,20 +569,29 @@ defmodule Pleroma.User do end end + @doc "Fetch some posts when the user has just been federated with" + def fetch_initial_posts(user) do + 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 + Enum.reverse(Utils.fetch_ordered_collection(user.info.source_data["outbox"], pages)), + &Pleroma.Web.Federator.incoming_ap_doc/1 + ) + end + def get_followers_query(%User{id: id, follower_address: follower_address}, nil) do from( u in User, where: fragment("? <@ ?", ^[follower_address], u.following), where: u.id != ^id ) + |> restrict_deactivated() end def get_followers_query(user, page) do - from( - u in get_followers_query(user, nil), - limit: 20, - offset: ^((page - 1) * 20) - ) + from(u in get_followers_query(user, nil)) + |> paginate(page, 20) end def get_followers_query(user), do: get_followers_query(user, nil) @@ -574,14 +614,12 @@ defmodule Pleroma.User do where: u.follower_address in ^following, where: u.id != ^id ) + |> restrict_deactivated() end def get_friends_query(user, page) do - from( - u in get_friends_query(user, nil), - limit: 20, - offset: ^((page - 1) * 20) - ) + from(u in get_friends_query(user, nil)) + |> paginate(page, 20) end def get_friends_query(user), do: get_friends_query(user, nil) @@ -613,71 +651,67 @@ defmodule Pleroma.User do ), where: fragment( - "? @> ?", + "coalesce((?)->'object'->>'id', (?)->>'object') = ?", a.data, - ^%{"object" => user.ap_id} + a.data, + ^user.ap_id ) ) end - def update_follow_request_count(%User{} = user) do - subquery = + def get_follow_requests(%User{} = user) do + users = user |> User.get_follow_requests_query() - |> select([a], %{count: count(a.id)}) + |> 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() + {:ok, users} + end + + def increase_note_count(%User{} = user) do User |> where(id: ^user.id) - |> join(:inner, [u], s in subquery(subquery)) - |> update([u, s], + |> update([u], set: [ info: fragment( - "jsonb_set(?, '{follow_request_count}', ?::varchar::jsonb, true)", + "jsonb_set(?, '{note_count}', ((?->>'note_count')::int + 1)::varchar::jsonb, true)", u.info, - s.count + u.info ) ] ) - |> Repo.update_all([], returning: true) + |> select([u], u) + |> Repo.update_all([]) |> case do - {1, [user]} -> {:ok, user} + {1, [user]} -> set_cache(user) _ -> {:error, user} end end - def get_follow_requests(%User{} = user) do - q = get_follow_requests_query(user) - reqs = Repo.all(q) - - users = - Enum.map(reqs, fn req -> req.actor end) - |> Enum.uniq() - |> Enum.map(fn ap_id -> get_by_ap_id(ap_id) end) - |> Enum.filter(fn u -> !is_nil(u) end) - |> Enum.filter(fn u -> !following?(u, user) end) - - {:ok, users} - end - - def increase_note_count(%User{} = user) do - info_cng = User.Info.add_to_note_count(user.info, 1) - - cng = - change(user) - |> put_embed(:info, info_cng) - - update_and_set_cache(cng) - end - def decrease_note_count(%User{} = user) do - info_cng = User.Info.add_to_note_count(user.info, -1) - - cng = - change(user) - |> put_embed(:info, info_cng) - - update_and_set_cache(cng) + User + |> where(id: ^user.id) + |> update([u], + set: [ + info: + fragment( + "jsonb_set(?, '{note_count}', (greatest(0, (?->>'note_count')::int - 1))::varchar::jsonb, true)", + u.info, + u.info + ) + ] + ) + |> select([u], u) + |> Repo.update_all([]) + |> case do + {1, [user]} -> set_cache(user) + _ -> {:error, user} + end end def update_note_count(%User{} = user) do @@ -692,33 +726,39 @@ defmodule Pleroma.User do info_cng = User.Info.set_note_count(user.info, note_count) - cng = - change(user) - |> put_embed(:info, info_cng) - - update_and_set_cache(cng) + user + |> change() + |> put_embed(:info, info_cng) + |> update_and_set_cache() end def update_follower_count(%User{} = user) do follower_count_query = - from( - u in User, - where: ^user.follower_address in u.following, - where: u.id != ^user.id, - select: count(u.id) - ) - - follower_count = Repo.one(follower_count_query) - - info_cng = - user.info - |> User.Info.set_follower_count(follower_count) - - cng = - change(user) - |> put_embed(:info, info_cng) + User + |> where([u], ^user.follower_address in u.following) + |> where([u], u.id != ^user.id) + |> select([u], %{count: count(u.id)}) + |> restrict_deactivated() - update_and_set_cache(cng) + User + |> where(id: ^user.id) + |> join(:inner, [u], s in subquery(follower_count_query)) + |> update([u, s], + set: [ + info: + fragment( + "jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)", + u.info, + s.count + ) + ] + ) + |> select([u], u) + |> Repo.update_all([]) + |> case do + {1, [user]} -> set_cache(user) + _ -> {:error, user} + end end def get_users_from_set_query(ap_ids, false) do @@ -761,42 +801,58 @@ defmodule Pleroma.User do if resolve, do: get_or_fetch(query) - fts_results = do_search(fts_search_subquery(query), for_user) - - {:ok, trigram_results} = + {:ok, results} = Repo.transaction(fn -> Ecto.Adapters.SQL.query(Repo, "select set_limit(0.25)", []) - do_search(trigram_search_subquery(query), for_user) + Repo.all(search_query(query, for_user)) end) - Enum.uniq_by(fts_results ++ trigram_results, & &1.id) + results end - def all_except_one(user) do - query = from(u in User, where: u.id != ^user.id) + 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) - Repo.all(query) + from(s in subquery(boost_search_rank_query(distinct_query, for_user)), + order_by: [desc: s.search_rank], + limit: 20 + ) end - defp do_search(subquery, for_user, options \\ []) do - q = - from( - s in subquery(subquery), - order_by: [desc: s.search_rank], - limit: ^(options[:limit] || 20) - ) + defp boost_search_rank_query(query, nil), do: query - results = - q - |> Repo.all() - |> Enum.filter(&(&1.search_rank > 0)) + defp boost_search_rank_query(query, for_user) do + friends_ids = get_friends_ids(for_user) + followers_ids = get_followers_ids(for_user) - boost_search_results(results, 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(query) do + defp fts_search_subquery(term, query \\ User) do processed_query = - query + term |> String.replace(~r/\W+/, " ") |> String.trim() |> String.split() @@ -804,8 +860,9 @@ defmodule Pleroma.User do |> Enum.join(" | ") from( - u in User, + u in query, select_merge: %{ + search_type: ^0, search_rank: fragment( """ @@ -832,49 +889,26 @@ defmodule Pleroma.User do ^processed_query ) ) + |> restrict_deactivated() end - defp trigram_search_subquery(query) do + 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(?, '')))", - ^query, + ^term, u.nickname, u.name ) }, - where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^query) + where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term) ) - end - - defp boost_search_results(results, nil), do: results - - defp boost_search_results(results, for_user) do - friends_ids = get_friends_ids(for_user) - followers_ids = get_followers_ids(for_user) - - Enum.map( - results, - fn u -> - search_rank_coef = - cond do - u.id in friends_ids -> - 1.2 - - u.id in followers_ids -> - 1.1 - - true -> - 1 - end - - Map.put(u, :search_rank, u.search_rank * search_rank_coef) - end - ) - |> Enum.sort_by(&(-&1.search_rank)) + |> restrict_deactivated() end def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do @@ -918,6 +952,38 @@ defmodule Pleroma.User do update_and_set_cache(cng) end + 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 + blocked = blocks?(subscribed, subscriber) and deny_follow_blocked + + if blocked do + {:error, "Could not subscribe: #{subscribed.nickname} is blocking you"} + else + info_cng = + subscribed.info + |> User.Info.add_to_subscribers(subscriber.ap_id) + + change(subscribed) + |> put_embed(:info, info_cng) + |> update_and_set_cache() + end + end + end + + def unsubscribe(unsubscriber, %{ap_id: ap_id}) do + with %User{} = user <- get_cached_by_ap_id(ap_id) do + info_cng = + user.info + |> User.Info.remove_from_subscribers(unsubscriber.ap_id) + + change(user) + |> put_embed(:info, info_cng) + |> update_and_set_cache() + end + end + def block(blocker, %User{ap_id: ap_id} = blocked) do # sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213) blocker = @@ -928,10 +994,20 @@ defmodule Pleroma.User do blocker end + blocker = + if subscribed_to?(blocked, blocker) do + {:ok, blocker} = unsubscribe(blocked, blocker) + blocker + else + blocker + end + if following?(blocked, blocker) do unfollow(blocked, blocker) end + {:ok, blocker} = update_follower_count(blocker) + info_cng = blocker.info |> User.Info.add_to_block(ap_id) @@ -974,12 +1050,21 @@ defmodule Pleroma.User do end) end + def subscribed_to?(user, %{ap_id: ap_id}) do + with %User{} = target <- User.get_by_ap_id(ap_id) do + Enum.member?(target.info.subscribers, user.ap_id) + end + end + def muted_users(user), do: Repo.all(from(u in User, where: u.ap_id in ^user.info.mutes)) def blocked_users(user), do: Repo.all(from(u in User, where: u.ap_id in ^user.info.blocks)) + def subscribers(user), + do: Repo.all(from(u in User, where: u.ap_id in ^user.info.subscribers)) + def block_domain(user, domain) do info_cng = user.info @@ -1004,14 +1089,54 @@ defmodule Pleroma.User do update_and_set_cache(cng) end - def local_user_query do + def maybe_local_user_query(query, local) do + if local, do: local_user_query(query), else: query + end + + def local_user_query(query \\ User) do from( - u in User, + u in query, where: u.local == true, where: not is_nil(u.nickname) ) end + def maybe_external_user_query(query, external) do + if external, do: external_user_query(query), else: query + end + + def external_user_query(query \\ User) do + from( + u in query, + where: u.local == false, + where: not is_nil(u.nickname) + ) + end + + def maybe_active_user_query(query, active) do + if active, do: active_user_query(query), else: query + end + + def active_user_query(query \\ User) do + from( + u in query, + where: fragment("not (?->'deactivated' @> 'true')", u.info), + where: not is_nil(u.nickname) + ) + end + + def maybe_deactivated_user_query(query, deactivated) do + if deactivated, do: deactivated_user_query(query), else: query + end + + def deactivated_user_query(query \\ User) do + from( + u in query, + where: fragment("(?->'deactivated' @> 'true')", u.info), + where: not is_nil(u.nickname) + ) + end + def active_local_user_query do from( u in local_user_query(), @@ -1027,14 +1152,35 @@ defmodule Pleroma.User do ) end + def deactivate_async(user, status \\ true) do + PleromaJobQueue.enqueue(:user, __MODULE__, [:deactivate_async, user, status]) + end + + def perform(:deactivate_async, user, status), do: deactivate(user, status) + def deactivate(%User{} = user, status \\ true) do info_cng = User.Info.set_activation_status(user.info, status) - cng = - change(user) - |> put_embed(:info, info_cng) + with {:ok, friends} <- User.get_friends(user), + {: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)) - update_and_set_cache(cng) + {:ok, user} + end + end + + def update_notification_settings(%User{} = user, settings \\ %{}) do + info_changeset = User.Info.update_notification_settings(user.info, settings) + + change(user) + |> put_embed(:info, info_changeset) + |> update_and_set_cache() end def delete(%User{} = user) do @@ -1043,26 +1189,27 @@ defmodule Pleroma.User do # Remove all relationships {:ok, followers} = User.get_followers(user) - followers - |> Enum.each(fn follower -> User.unfollow(follower, user) end) + Enum.each(followers, fn follower -> User.unfollow(follower, user) end) {:ok, friends} = User.get_friends(user) - friends - |> Enum.each(fn followed -> User.unfollow(user, followed) end) + Enum.each(friends, fn followed -> User.unfollow(user, followed) end) - query = from(a in Activity, where: a.actor == ^user.ap_id) + delete_user_activities(user) + end - Repo.all(query) - |> Enum.each(fn activity -> - case activity.data["type"] do - "Create" -> - ActivityPub.delete(Object.normalize(activity.data["object"])) + def delete_user_activities(%User{ap_id: ap_id} = user) do + Activity + |> where(actor: ^ap_id) + |> Activity.with_preloaded_object() + |> Repo.all() + |> Enum.each(fn + %{data: %{"type" => "Create"}} = activity -> + activity |> Object.normalize() |> ActivityPub.delete() - # TODO: Do something with likes, follows, repeats. - _ -> - "Doing nothing" - end + # TODO: Do something with likes, follows, repeats. + _ -> + "Doing nothing" end) {:ok, user} @@ -1076,24 +1223,39 @@ defmodule Pleroma.User do def html_filter_policy(_), do: @default_scrubbers + def fetch_by_ap_id(ap_id) do + ap_try = ActivityPub.make_user_from_ap_id(ap_id) + + case ap_try do + {:ok, user} -> + user + + _ -> + case OStatus.make_user(ap_id) do + {:ok, user} -> user + _ -> {:error, "Could not fetch by AP id"} + end + end + end + def get_or_fetch_by_ap_id(ap_id) do user = get_by_ap_id(ap_id) if !is_nil(user) and !User.needs_update?(user) do user else - ap_try = ActivityPub.make_user_from_ap_id(ap_id) + # Whether to fetch initial posts for the user (if it's a new user & the fetching is enabled) + should_fetch_initial = is_nil(user) and Pleroma.Config.get([:fetch_initial_posts, :enabled]) - case ap_try do - {:ok, user} -> - user + user = fetch_by_ap_id(ap_id) - _ -> - case OStatus.make_user(ap_id) do - {:ok, user} -> user - _ -> {:error, "Could not fetch by AP id"} - end + if should_fetch_initial do + with %User{} = user do + {:ok, _} = Task.start(__MODULE__, :fetch_initial_posts, [user]) + end end + + user end end @@ -1169,8 +1331,8 @@ defmodule Pleroma.User do # this is because we have synchronous follow APIs and need to simulate them # with an async handshake def wait_and_refresh(_, %User{local: true} = a, %User{local: true} = b) do - with %User{} = a <- Repo.get(User, a.id), - %User{} = b <- Repo.get(User, b.id) do + with %User{} = a <- User.get_by_id(a.id), + %User{} = b <- User.get_by_id(b.id) do {:ok, a, b} else _e -> @@ -1180,8 +1342,8 @@ defmodule Pleroma.User do def wait_and_refresh(timeout, %User{} = a, %User{} = b) do with :ok <- :timer.sleep(timeout), - %User{} = a <- Repo.get(User, a.id), - %User{} = b <- Repo.get(User, b.id) do + %User{} = a <- User.get_by_id(a.id), + %User{} = b <- User.get_by_id(b.id) do {:ok, a, b} else _e -> @@ -1201,8 +1363,14 @@ defmodule Pleroma.User do {String.trim(name, ":"), url} end) + # TODO: get profile URLs other than user.ap_id + profile_urls = [user.ap_id] + bio - |> CommonUtils.format_input("text/plain", mentions_format: :full) + |> CommonUtils.format_input("text/plain", + mentions_format: :full, + rel: &RelMe.maybe_put_rel_me(&1, profile_urls) + ) |> elem(0) |> Formatter.emojify(emoji) end @@ -1262,7 +1430,7 @@ defmodule Pleroma.User do |> Enum.map(&String.downcase(&1)) end - defp local_nickname_regex() do + defp local_nickname_regex do if Pleroma.Config.get([:instance, :extended_nickname_format]) do @extended_local_nickname_regex else @@ -1298,4 +1466,15 @@ defmodule Pleroma.User do ) |> Repo.all() end + + defp paginate(query, page, page_size) do + from(u in query, + limit: ^page_size, + offset: ^((page - 1) * page_size) + ) + end + + def showing_reblogs?(%User{} = user, %User{} = target) do + target.ap_id not in user.info.muted_reblogs + end end