Merge remote-tracking branch 'pleroma/develop' into feature/disable-account
[akkoma] / lib / pleroma / user.ex
index 15f606c5fc91443478e0fde6750a030509c066fd..6aaa3244f41188da2181dec5ce5db4ca50d63284 100644 (file)
@@ -13,6 +13,7 @@ defmodule Pleroma.User do
   alias Pleroma.Formatter
   alias Pleroma.Notification
   alias Pleroma.Object
+  alias Pleroma.Registration
   alias Pleroma.Repo
   alias Pleroma.User
   alias Pleroma.Web
@@ -55,6 +56,7 @@ defmodule Pleroma.User do
     field(:bookmarks, {:array, :string}, default: [])
     field(:last_refreshed_at, :naive_datetime_usec)
     has_many(:notifications, Notification)
+    has_many(:registrations, Registration)
     embeds_one(:info, Pleroma.User.Info)
 
     timestamps()
@@ -105,10 +107,8 @@ defmodule Pleroma.User do
   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,
@@ -117,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
@@ -216,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)
@@ -227,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]})
@@ -270,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
@@ -505,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
@@ -561,6 +586,7 @@ defmodule Pleroma.User do
       where: fragment("? <@ ?", ^[follower_address], u.following),
       where: u.id != ^id
     )
+    |> restrict_deactivated()
   end
 
   def get_followers_query(user, page) do
@@ -588,6 +614,7 @@ defmodule Pleroma.User do
       where: u.follower_address in ^following,
       where: u.id != ^id
     )
+    |> restrict_deactivated()
   end
 
   def get_friends_query(user, page) do
@@ -699,11 +726,10 @@ 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
@@ -712,6 +738,7 @@ defmodule Pleroma.User do
       |> where([u], ^user.follower_address in u.following)
       |> where([u], u.id != ^user.id)
       |> select([u], %{count: count(u.id)})
+      |> restrict_deactivated()
 
     User
     |> where(id: ^user.id)
@@ -862,6 +889,7 @@ defmodule Pleroma.User do
           ^processed_query
         )
     )
+    |> restrict_deactivated()
   end
 
   defp trigram_search_subquery(term) do
@@ -880,6 +908,7 @@ defmodule Pleroma.User do
       },
       where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term)
     )
+    |> restrict_deactivated()
   end
 
   def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_identifiers) do
@@ -926,7 +955,7 @@ defmodule Pleroma.User do
   def subscribe(subscriber, %{ap_id: ap_id}) do
     deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
 
-    with %User{} = subscribed <- get_or_fetch_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
 
       if blocked do
@@ -944,7 +973,7 @@ defmodule Pleroma.User do
   end
 
   def unsubscribe(unsubscriber, %{ap_id: ap_id}) do
-    with %User{} = user <- get_or_fetch_by_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)
@@ -977,6 +1006,8 @@ defmodule Pleroma.User do
       unfollow(blocked, blocker)
     end
 
+    {:ok, blocker} = update_follower_count(blocker)
+
     info_cng =
       blocker.info
       |> User.Info.add_to_block(ap_id)
@@ -1121,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