X-Git-Url: https://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fuser.ex;h=b78777141b84a000bff81647790aafe22f7dc79c;hb=50a644a2215a144e54a9f9d2a5216bec4e717405;hp=cd0c64acc48df9e029f103ea08cef8fce95e29b5;hpb=c4439c630f46153c9f118d7f7e752d880206d262;p=akkoma diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index cd0c64acc..b78777141 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -99,6 +99,7 @@ defmodule Pleroma.User do field(:local, :boolean, default: true) field(:follower_address, :string) field(:following_address, :string) + field(:featured_address, :string) field(:search_rank, :float, virtual: true) field(:search_type, :integer, virtual: true) field(:tags, {:array, :string}, default: []) @@ -110,14 +111,14 @@ defmodule Pleroma.User do field(:follower_count, :integer, default: 0) field(:following_count, :integer, default: 0) field(:is_locked, :boolean, default: false) - field(:confirmation_pending, :boolean, default: false) + field(:is_confirmed, :boolean, default: true) field(:password_reset_pending, :boolean, default: false) - field(:approval_pending, :boolean, default: false) + field(:is_approved, :boolean, default: true) field(:registration_reason, :string, default: nil) field(:confirmation_token, :string, default: nil) field(:default_scope, :string, default: "public") field(:domain_blocks, {:array, :string}, default: []) - field(:deactivated, :boolean, default: false) + field(:is_active, :boolean, default: true) field(:no_rich_text, :boolean, default: false) field(:ap_enabled, :boolean, default: false) field(:is_moderator, :boolean, default: false) @@ -130,7 +131,6 @@ defmodule Pleroma.User do field(:hide_followers, :boolean, default: false) field(:hide_follows, :boolean, default: false) field(:hide_favorites, :boolean, default: true) - field(:pinned_activities, {:array, :string}, default: []) field(:email_notifications, :map, default: %{"digest" => false}) field(:mascot, :map, default: nil) field(:emoji, :map, default: %{}) @@ -146,6 +146,9 @@ defmodule Pleroma.User do field(:inbox, :string) field(:shared_inbox, :string) field(:accepts_chat_messages, :boolean, default: nil) + field(:last_active_at, :naive_datetime) + field(:disclose_client, :boolean, default: true) + field(:pinned_objects, :map, default: %{}) embeds_one( :notification_settings, @@ -217,7 +220,8 @@ defmodule Pleroma.User do target_users_query = assoc(user, unquote(outgoing_relation_target)) if restrict_deactivated? do - restrict_deactivated(target_users_query) + target_users_query + |> User.Query.build(%{deactivated: false}) else target_users_query end @@ -286,18 +290,10 @@ defmodule Pleroma.User do @doc "Returns status account" @spec account_status(User.t()) :: account_status() - def account_status(%User{deactivated: true}), do: :deactivated + def account_status(%User{is_active: false}), do: :deactivated def account_status(%User{password_reset_pending: true}), do: :password_reset_pending - def account_status(%User{local: true, approval_pending: true}), do: :approval_pending - - def account_status(%User{local: true, confirmation_pending: true}) do - if Config.get([:instance, :account_activation_required]) do - :confirmation_pending - else - :active - end - end - + def account_status(%User{local: true, is_approved: false}), do: :approval_pending + def account_status(%User{local: true, is_confirmed: false}), do: :confirmation_pending def account_status(%User{}), do: :active @spec visible_for(User.t(), User.t() | nil) :: @@ -377,8 +373,10 @@ defmodule Pleroma.User do end # Should probably be renamed or removed + @spec ap_id(User.t()) :: String.t() def ap_id(%User{nickname: nickname}), do: "#{Web.base_url()}/users/#{nickname}" + @spec ap_followers(User.t()) :: String.t() def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers" @@ -386,10 +384,10 @@ defmodule Pleroma.User do def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa def ap_following(%User{} = user), do: "#{ap_id(user)}/following" - @spec restrict_deactivated(Ecto.Query.t()) :: Ecto.Query.t() - def restrict_deactivated(query) do - from(u in query, where: u.deactivated != ^true) - end + @spec ap_featured_collection(User.t()) :: String.t() + def ap_featured_collection(%User{featured_address: fa}) when is_binary(fa), do: fa + + def ap_featured_collection(%User{} = user), do: "#{ap_id(user)}/collections/featured" defp truncate_fields_param(params) do if Map.has_key?(params, :fields) do @@ -453,6 +451,7 @@ defmodule Pleroma.User do :uri, :follower_address, :following_address, + :featured_address, :hide_followers, :hide_follows, :hide_followers_count, @@ -464,7 +463,8 @@ defmodule Pleroma.User do :invisible, :actor_type, :also_known_as, - :accepts_chat_messages + :accepts_chat_messages, + :pinned_objects ] ) |> cast(params, [:name], empty_values: []) @@ -524,7 +524,8 @@ defmodule Pleroma.User do :pleroma_settings_store, :is_discoverable, :actor_type, - :accepts_chat_messages + :accepts_chat_messages, + :disclose_client ] ) |> unique_constraint(:nickname) @@ -695,7 +696,7 @@ defmodule Pleroma.User do |> validate_format(:nickname, local_nickname_regex()) |> put_ap_id() |> unique_constraint(:ap_id) - |> put_following_and_follower_address() + |> put_following_and_follower_and_featured_address() end def register_changeset(struct, params \\ %{}, opts \\ []) do @@ -704,23 +705,23 @@ defmodule Pleroma.User do reason_limit = Config.get([:instance, :registration_reason_length], 500) params = Map.put_new(params, :accepts_chat_messages, true) - need_confirmation? = - if is_nil(opts[:need_confirmation]) do - Config.get([:instance, :account_activation_required]) + confirmed? = + if is_nil(opts[:confirmed]) do + !Config.get([:instance, :account_activation_required]) else - opts[:need_confirmation] + opts[:confirmed] end - need_approval? = - if is_nil(opts[:need_approval]) do - Config.get([:instance, :account_approval_required]) + approved? = + if is_nil(opts[:approved]) do + !Config.get([:instance, :account_approval_required]) else - opts[:need_approval] + opts[:approved] end struct - |> confirmation_changeset(need_confirmation: need_confirmation?) - |> approval_changeset(need_approval: need_approval?) + |> confirmation_changeset(set_confirmation: confirmed?) + |> approval_changeset(set_approval: approved?) |> cast(params, [ :bio, :raw_bio, @@ -756,7 +757,7 @@ defmodule Pleroma.User do |> put_password_hash |> put_ap_id() |> unique_constraint(:ap_id) - |> put_following_and_follower_address() + |> put_following_and_follower_and_featured_address() end def maybe_validate_required_email(changeset, true), do: changeset @@ -774,18 +775,23 @@ defmodule Pleroma.User do put_change(changeset, :ap_id, ap_id) end - defp put_following_and_follower_address(changeset) do - followers = ap_followers(%User{nickname: get_field(changeset, :nickname)}) + defp put_following_and_follower_and_featured_address(changeset) do + user = %User{nickname: get_field(changeset, :nickname)} + followers = ap_followers(user) + following = ap_following(user) + featured = ap_featured_collection(user) changeset |> put_change(:follower_address, followers) + |> put_change(:following_address, following) + |> put_change(:featured_address, featured) end defp autofollow_users(user) do candidates = Config.get([:instance, :autofollowed_nicknames]) autofollowed_users = - User.Query.build(%{nickname: candidates, local: true, deactivated: false}) + User.Query.build(%{nickname: candidates, local: true, is_active: true}) |> Repo.all() follow_all(user, autofollowed_users) @@ -808,26 +814,27 @@ defmodule Pleroma.User do end end - def post_register_action(%User{confirmation_pending: true} = user) do - with {:ok, _} <- try_send_confirmation_email(user) do + def post_register_action(%User{is_confirmed: false} = user) do + with {:ok, _} <- maybe_send_confirmation_email(user) do {:ok, user} end end - def post_register_action(%User{approval_pending: true} = user) do + def post_register_action(%User{is_approved: false} = user) do with {:ok, _} <- send_user_approval_email(user), {:ok, _} <- send_admin_approval_emails(user) do {:ok, user} end end - def post_register_action(%User{approval_pending: false, confirmation_pending: false} = user) do + def post_register_action(%User{is_approved: true, is_confirmed: true} = user) do with {:ok, user} <- autofollow_users(user), {:ok, _} <- autofollowing_users(user), {:ok, user} <- set_cache(user), - {:ok, _} <- send_welcome_email(user), - {:ok, _} <- send_welcome_message(user), - {:ok, _} <- send_welcome_chat_message(user) do + {:ok, _} <- maybe_send_registration_email(user), + {:ok, _} <- maybe_send_welcome_email(user), + {:ok, _} <- maybe_send_welcome_message(user), + {:ok, _} <- maybe_send_welcome_chat_message(user) do {:ok, user} end end @@ -852,7 +859,7 @@ defmodule Pleroma.User do {:ok, :enqueued} end - def send_welcome_message(user) do + defp maybe_send_welcome_message(user) do if User.WelcomeMessage.enabled?() do User.WelcomeMessage.post_message(user) {:ok, :enqueued} @@ -861,7 +868,7 @@ defmodule Pleroma.User do end end - def send_welcome_chat_message(user) do + defp maybe_send_welcome_chat_message(user) do if User.WelcomeChatMessage.enabled?() do User.WelcomeChatMessage.post_message(user) {:ok, :enqueued} @@ -870,7 +877,7 @@ defmodule Pleroma.User do end end - def send_welcome_email(%User{email: email} = user) when is_binary(email) do + defp maybe_send_welcome_email(%User{email: email} = user) when is_binary(email) do if User.WelcomeEmail.enabled?() do User.WelcomeEmail.send_email(user) {:ok, :enqueued} @@ -879,10 +886,10 @@ defmodule Pleroma.User do end end - def send_welcome_email(_), do: {:ok, :noop} + defp maybe_send_welcome_email(_), do: {:ok, :noop} - @spec try_send_confirmation_email(User.t()) :: {:ok, :enqueued | :noop} - def try_send_confirmation_email(%User{confirmation_pending: true, email: email} = user) + @spec maybe_send_confirmation_email(User.t()) :: {:ok, :enqueued | :noop} + def maybe_send_confirmation_email(%User{is_confirmed: false, email: email} = user) when is_binary(email) do if Config.get([:instance, :account_activation_required]) do send_confirmation_email(user) @@ -892,7 +899,7 @@ defmodule Pleroma.User do end end - def try_send_confirmation_email(_), do: {:ok, :noop} + def maybe_send_confirmation_email(_), do: {:ok, :noop} @spec send_confirmation_email(Uset.t()) :: User.t() def send_confirmation_email(%User{} = user) do @@ -903,6 +910,24 @@ defmodule Pleroma.User do user end + @spec maybe_send_registration_email(User.t()) :: {:ok, :enqueued | :noop} + defp maybe_send_registration_email(%User{email: email} = user) when is_binary(email) do + with false <- User.WelcomeEmail.enabled?(), + false <- Config.get([:instance, :account_activation_required], false), + false <- Config.get([:instance, :account_approval_required], false) do + user + |> Pleroma.Emails.UserEmail.successful_registration_email() + |> Pleroma.Emails.Mailer.deliver_async() + + {:ok, :enqueued} + else + _ -> + {:ok, :noop} + end + end + + defp maybe_send_registration_email(_), do: {:ok, :noop} + def needs_update?(%User{local: true}), do: false def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true @@ -946,7 +971,7 @@ defmodule Pleroma.User do deny_follow_blocked = Config.get([:user, :deny_follow_blocked]) cond do - followed.deactivated -> + not followed.is_active -> {:error, "Could not follow user: #{followed.nickname} is deactivated."} deny_follow_blocked and blocks?(followed, follower) -> @@ -1181,7 +1206,7 @@ defmodule Pleroma.User do @spec get_followers_query(User.t(), pos_integer() | nil) :: Ecto.Query.t() def get_followers_query(%User{} = user, nil) do - User.Query.build(%{followers: user, deactivated: false}) + User.Query.build(%{followers: user, is_active: true}) end def get_followers_query(%User{} = user, page) do @@ -1357,7 +1382,7 @@ defmodule Pleroma.User do @spec get_users_from_set([String.t()], keyword()) :: [User.t()] def get_users_from_set(ap_ids, opts \\ []) do local_only = Keyword.get(opts, :local_only, true) - criteria = %{ap_id: ap_ids, deactivated: false} + criteria = %{ap_id: ap_ids, is_active: true} criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria User.Query.build(criteria) @@ -1368,7 +1393,7 @@ defmodule Pleroma.User do def get_recipients_from_activity(%Activity{recipients: to, actor: actor}) do to = [actor | to] - query = User.Query.build(%{recipients_from_activity: to, local: true, deactivated: false}) + query = User.Query.build(%{recipients_from_activity: to, local: true, is_active: true}) query |> Repo.all() @@ -1587,19 +1612,19 @@ defmodule Pleroma.User do defp maybe_filter_on_ap_id(query, _ap_ids), do: query - def deactivate_async(user, status \\ true) do - BackgroundWorker.enqueue("deactivate_user", %{"user_id" => user.id, "status" => status}) + def set_activation_async(user, status \\ true) do + BackgroundWorker.enqueue("user_activation", %{"user_id" => user.id, "status" => status}) end - def deactivate(user, status \\ true) - - def deactivate(users, status) when is_list(users) do + @spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Changeset.t()} + def set_activation(users, status) when is_list(users) do Repo.transaction(fn -> - for user <- users, do: deactivate(user, status) + for user <- users, do: set_activation(user, status) end) end - def deactivate(%User{} = user, status) do + @spec set_activation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()} + def set_activation(%User{} = user, status) do with {:ok, user} <- set_activation_status(user, status) do user |> get_followers() @@ -1624,8 +1649,8 @@ defmodule Pleroma.User do end) end - def approve(%User{approval_pending: true} = user) do - with chg <- change(user, approval_pending: false), + def approve(%User{is_approved: false} = user) do + with chg <- change(user, is_approved: true), {:ok, user} <- update_and_set_cache(chg) do post_register_action(user) {:ok, user} @@ -1642,8 +1667,8 @@ defmodule Pleroma.User do end) end - def confirm(%User{confirmation_pending: true} = user) do - with chg <- confirmation_changeset(user, need_confirmation: false), + def confirm(%User{is_confirmed: false} = user) do + with chg <- confirmation_changeset(user, set_confirmation: true), {:ok, user} <- update_and_set_cache(chg) do post_register_action(user) {:ok, user} @@ -1682,13 +1707,13 @@ defmodule Pleroma.User do follower_count: 0, following_count: 0, is_locked: false, - confirmation_pending: false, + is_confirmed: true, password_reset_pending: false, - approval_pending: false, + is_approved: true, registration_reason: nil, confirmation_token: nil, domain_blocks: [], - deactivated: true, + is_active: false, ap_enabled: false, is_moderator: false, is_admin: false, @@ -1762,7 +1787,7 @@ defmodule Pleroma.User do delete_or_deactivate(user) end - def perform(:deactivate_async, user, status), do: deactivate(user, status) + def perform(:set_activation_async, user, status), do: set_activation(user, status) @spec external_users_query() :: Ecto.Query.t() def external_users_query do @@ -2042,6 +2067,15 @@ defmodule Pleroma.User do |> hd() end + def full_nickname(%User{} = user) do + if String.contains?(user.nickname, "@") do + user.nickname + else + %{host: host} = URI.parse(user.ap_id) + user.nickname <> "@" <> host + end + end + def full_nickname(nickname_or_mention), do: String.trim_leading(nickname_or_mention, "@") @@ -2056,7 +2090,7 @@ defmodule Pleroma.User do @spec all_superusers() :: [User.t()] def all_superusers do - User.Query.build(%{super_users: true, local: true, deactivated: false}) + User.Query.build(%{super_users: true, local: true, is_active: true}) |> Repo.all() end @@ -2097,7 +2131,7 @@ defmodule Pleroma.User do left_join: a in Pleroma.Activity, on: u.ap_id == a.actor, where: not is_nil(u.nickname), - where: u.deactivated != ^true, + where: u.is_active == ^true, where: u.id not in ^has_read_notifications, group_by: u.id, having: @@ -2138,10 +2172,10 @@ defmodule Pleroma.User do updated_user end - @spec need_confirmation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()} - def need_confirmation(%User{} = user, bool) do + @spec set_confirmation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()} + def set_confirmation(%User{} = user, bool) do user - |> confirmation_changeset(need_confirmation: bool) + |> confirmation_changeset(set_confirmation: bool) |> update_and_set_cache() end @@ -2187,7 +2221,7 @@ defmodule Pleroma.User do defp put_password_hash( %Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset ) do - change(changeset, password_hash: Pbkdf2.hash_pwd_salt(password)) + change(changeset, password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt(password)) end defp put_password_hash(changeset), do: changeset @@ -2218,9 +2252,9 @@ defmodule Pleroma.User do end # Internal function; public one is `deactivate/2` - defp set_activation_status(user, deactivated) do + defp set_activation_status(user, status) do user - |> cast(%{deactivated: deactivated}, [:deactivated]) + |> cast(%{is_active: status}, [:is_active]) |> update_and_set_cache() end @@ -2236,13 +2270,6 @@ defmodule Pleroma.User do |> update_and_set_cache() end - def roles(%{is_moderator: is_moderator, is_admin: is_admin}) do - %{ - admin: is_admin, - moderator: is_moderator - } - end - def validate_fields(changeset, remote? \\ false) do limit_name = if remote?, do: :max_remote_account_fields, else: :max_account_fields limit = Config.get([:instance, limit_name], 0) @@ -2309,68 +2336,57 @@ defmodule Pleroma.User do end @spec confirmation_changeset(User.t(), keyword()) :: Changeset.t() - def confirmation_changeset(user, need_confirmation: need_confirmation?) do + def confirmation_changeset(user, set_confirmation: confirmed?) do params = - if need_confirmation? do + if confirmed? do %{ - confirmation_pending: true, - confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64() + is_confirmed: true, + confirmation_token: nil } else %{ - confirmation_pending: false, - confirmation_token: nil + is_confirmed: false, + confirmation_token: :crypto.strong_rand_bytes(32) |> Base.url_encode64() } end - cast(user, params, [:confirmation_pending, :confirmation_token]) + cast(user, params, [:is_confirmed, :confirmation_token]) end @spec approval_changeset(User.t(), keyword()) :: Changeset.t() - def approval_changeset(user, need_approval: need_approval?) do - params = if need_approval?, do: %{approval_pending: true}, else: %{approval_pending: false} - cast(user, params, [:approval_pending]) + def approval_changeset(user, set_approval: approved?) do + cast(user, %{is_approved: approved?}, [:is_approved]) end - def add_pinnned_activity(user, %Pleroma.Activity{id: id}) do - if id not in user.pinned_activities do - max_pinned_statuses = Config.get([:instance, :max_pinned_statuses], 0) - params = %{pinned_activities: user.pinned_activities ++ [id]} - - # if pinned activity was scheduled for deletion, we remove job - if expiration = Pleroma.Workers.PurgeExpiredActivity.get_expiration(id) do - Oban.cancel_job(expiration.id) - end + @spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()} + def add_pinned_object_id(%User{} = user, object_id) do + if !user.pinned_objects[object_id] do + params = %{pinned_objects: Map.put(user.pinned_objects, object_id, NaiveDateTime.utc_now())} user - |> cast(params, [:pinned_activities]) - |> validate_length(:pinned_activities, - max: max_pinned_statuses, - message: "You have already pinned the maximum number of statuses" - ) + |> cast(params, [:pinned_objects]) + |> validate_change(:pinned_objects, fn :pinned_objects, pinned_objects -> + max_pinned_statuses = Config.get([:instance, :max_pinned_statuses], 0) + + if Enum.count(pinned_objects) <= max_pinned_statuses do + [] + else + [pinned_objects: "You have already pinned the maximum number of statuses"] + end + end) else change(user) end |> update_and_set_cache() end - def remove_pinnned_activity(user, %Pleroma.Activity{id: id, data: data}) do - params = %{pinned_activities: List.delete(user.pinned_activities, id)} - - # if pinned activity was scheduled for deletion, we reschedule it for deletion - if data["expires_at"] do - # MRF.ActivityExpirationPolicy used UTC timestamps for expires_at in original implementation - {:ok, expires_at} = - data["expires_at"] |> Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime.cast() - - Pleroma.Workers.PurgeExpiredActivity.enqueue(%{ - activity_id: id, - expires_at: expires_at - }) - end - + @spec remove_pinned_object_id(User.t(), String.t()) :: {:ok, t()} | {:error, term()} + def remove_pinned_object_id(%User{} = user, object_id) do user - |> cast(params, [:pinned_activities]) + |> cast( + %{pinned_objects: Map.delete(user.pinned_objects, object_id)}, + [:pinned_objects] + ) |> update_and_set_cache() end @@ -2457,4 +2473,19 @@ defmodule Pleroma.User do def get_host(%User{ap_id: ap_id} = _user) do URI.parse(ap_id).host end + + def update_last_active_at(%__MODULE__{local: true} = user) do + user + |> cast(%{last_active_at: NaiveDateTime.utc_now()}, [:last_active_at]) + |> update_and_set_cache() + end + + def active_user_count(weeks \\ 4) do + active_after = Timex.shift(NaiveDateTime.utc_now(), weeks: -weeks) + + __MODULE__ + |> where([u], u.last_active_at >= ^active_after) + |> where([u], u.local == true) + |> Repo.aggregate(:count) + end end