Merge branch 'develop' into 'feature/local-only-scope'
authorminibikini <egor@kislitsyn.com>
Fri, 30 Oct 2020 11:18:55 +0000 (11:18 +0000)
committerminibikini <egor@kislitsyn.com>
Fri, 30 Oct 2020 11:18:55 +0000 (11:18 +0000)
# Conflicts:
#   CHANGELOG.md

23 files changed:
CHANGELOG.md
config/description.exs
docs/API/admin_api.md
docs/administration/CLI_tasks/frontend.md
lib/pleroma/conversation.ex
lib/pleroma/conversation/participation.ex
lib/pleroma/user.ex
lib/pleroma/user/query.ex
lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
lib/pleroma/web/admin_api/controllers/user_controller.ex [new file with mode: 0644]
lib/pleroma/web/api_spec/operations/account_operation.ex
lib/pleroma/web/mastodon_api/controllers/account_controller.ex
lib/pleroma/web/mastodon_api/views/account_view.ex
lib/pleroma/web/router.ex
priv/repo/migrations/20200831114918_remove_unread_conversation_count_from_user.exs [new file with mode: 0644]
priv/repo/migrations/20200831115854_add_unread_index_to_conversation_participation.exs [new file with mode: 0644]
test/pleroma/conversation/participation_test.exs
test/pleroma/web/admin_api/controllers/admin_api_controller_test.exs
test/pleroma/web/admin_api/controllers/user_controller_test.exs [new file with mode: 0644]
test/pleroma/web/admin_api/search_test.exs
test/pleroma/web/mastodon_api/controllers/account_controller_test.exs
test/pleroma/web/mastodon_api/controllers/conversation_controller_test.exs
test/pleroma/web/pleroma_api/controllers/conversation_controller_test.exs

index d2f536da8379d86d1400e74555e649a6d75efb87..d9410ccf39116169d21d76ab1f64129fbda4b139 100644 (file)
@@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Pleroma API: Importing the mutes users from CSV files.
 - Experimental websocket-based federation between Pleroma instances.
 - Support for local-only statuses
+- Support pagination of blocks and mutes
 - App metrics: ability to restrict access to specified IP whitelist.
 - Configuration: Add `:instance, autofollowing_nicknames` setting to provide a way to make accounts automatically follow new users that register on the local Pleroma instance.
 
@@ -37,6 +38,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Pleroma API: Importing the mutes users from CSV files.
 - Admin API: Importing emoji from a zip file
 - Pleroma API: Pagination for remote/local packs and emoji.
+- Admin API: (`GET /api/pleroma/admin/users`) added filters user by `unconfirmed` status
+- Admin API: (`GET /api/pleroma/admin/users`) added filters user by `actor_type`
 
 </details>
 
index 0bfa9979f91ab37f11226d60eec2b8f05b497116..798cbe2adcb43837633fd5dd32a3ca8c07e508b6 100644 (file)
@@ -1757,28 +1757,37 @@ config :pleroma, :config_description, [
     related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy",
     label: "MRF Keyword",
     type: :group,
-    description: "Reject or Word-Replace messages with a keyword or regex",
+    description:
+      "Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).",
     children: [
       %{
         key: :reject,
         type: {:list, :string},
-        description:
-          "A list of patterns which result in message being rejected. Each pattern can be a string or a regular expression.",
+        description: """
+          A list of patterns which result in message being rejected.
+
+          Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
+        """,
         suggestions: ["foo", ~r/foo/iu]
       },
       %{
         key: :federated_timeline_removal,
         type: {:list, :string},
-        description:
-          "A list of patterns which result in message being removed from federated timelines (a.k.a unlisted). Each pattern can be a string or a regular expression.",
+        description: """
+          A list of patterns which result in message being removed from federated timelines (a.k.a unlisted).
+
+          Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
+        """,
         suggestions: ["foo", ~r/foo/iu]
       },
       %{
         key: :replace,
         type: {:list, :tuple},
-        description:
-          "A list of tuples containing {pattern, replacement}. Each pattern can be a string or a regular expression.",
-        suggestions: [{"foo", "bar"}, {~r/foo/iu, "bar"}]
+        description: """
+          **Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
+
+          **Replacement**: a string. Leaving the field empty is permitted.
+        """
       }
     ]
   },
index 7bf13daef5f5251f4119081e0b7ad4e98fb61ab7..f7b5bcae77394a5854c3271d64ddbb60eba89531 100644 (file)
@@ -20,12 +20,14 @@ Configuration options:
     - `external`: only external users
     - `active`: only active users
     - `need_approval`: only unapproved users
+    - `unconfirmed`: only unconfirmed users
     - `deactivated`: only deactivated users
     - `is_admin`: users with admin role
     - `is_moderator`: users with moderator role
   - *optional* `page`: **integer** page number
   - *optional* `page_size`: **integer** number of users per page (default is `50`)
   - *optional* `tags`: **[string]** tags list
+  - *optional* `actor_types`: **[string]** actor type list (`Person`, `Service`, `Application`)
   - *optional* `name`: **string** user display name
   - *optional* `email`: **string** user email
 - Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10&tags[]=some_tag&tags[]=another_tag&name=display_name&email=email@example.com`
index 7d1c1e93767ca66404c146ecfa34a255be26fbe7..d4a48cb56b8f7b8089fe143fbdc0338531bd1446 100644 (file)
@@ -1,12 +1,23 @@
 # Managing frontends
 
-`mix pleroma.frontend install <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>]`
+=== "OTP"
+
+    ```sh
+    ./bin/pleroma_ctl frontend install <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>]
+    ```
+
+=== "From Source"
+
+    ```sh
+    mix pleroma.frontend install <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>]
+    ```
 
 Frontend can be installed either from local zip file, or automatically downloaded from the web.
 
-You can give all the options directly on the command like, but missing information will be filled out by looking at the data configured under `frontends.available` in the config files.
+You can give all the options directly on the command line, but missing information will be filled out by looking at the data configured under `frontends.available` in the config files.
+
+Currently, known `<frontend>` values are:
 
-Currently known `<frontend>` values are:
 - [admin-fe](https://git.pleroma.social/pleroma/admin-fe)
 - [kenoma](http://git.pleroma.social/lambadalambda/kenoma)
 - [pleroma-fe](http://git.pleroma.social/pleroma/pleroma-fe)
@@ -19,51 +30,67 @@ You can still install frontends that are not configured, see below.
 
 For a frontend configured under the `available` key, it's enough to install it by name.
 
-```sh tab="OTP"
-./bin/pleroma_ctl frontend install pleroma
-```
+=== "OTP"
+
+    ```sh
+    ./bin/pleroma_ctl frontend install pleroma
+    ```
+
+=== "From Source"
 
-```sh tab="From Source"
-mix pleroma.frontend install pleroma
-```
+    ```sh
+    mix pleroma.frontend install pleroma
+    ```
 
-This will download the latest build for the the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`).
+This will download the latest build for the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`).
 
-You can override any of the details. To install a pleroma build from a different url, you could do this:
+You can override any of the details. To install a pleroma build from a different URL, you could do this:
 
-```sh tab="OPT"
-./bin/pleroma_ctl frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
-```
+=== "OTP"
 
-```sh tab="From Source"
-mix pleroma.frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
-```
+    ```sh
+    ./bin/pleroma_ctl frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
+    ```
+
+=== "From Source"
+
+    ```sh
+    mix pleroma.frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
+    ```
 
 Similarly, you can also install from a local zip file.
 
-```sh tab="OTP"
-./bin/pleroma_ctl frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
-```
+=== "OTP"
+
+    ```sh
+    ./bin/pleroma_ctl frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
+    ```
 
-```sh tab="From Source"
-mix pleroma.frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
-```
+=== "From Source"
 
-The resulting frontend will always be installed into a folder of this template: `${instance_static}/frontends/${name}/${ref}`
+    ```sh
+    mix pleroma.frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
+    ```
 
-Careful: This folder will be completely replaced on installation
+The resulting frontend will always be installed into a folder of this template: `${instance_static}/frontends/${name}/${ref}`.
+
+Careful: This folder will be completely replaced on installation.
 
 ## Example installation for an unknown frontend
 
-The installation process is the same, but you will have to give all the needed options on the commond line. For example:
+The installation process is the same, but you will have to give all the needed options on the command line. For example:
+
+=== "OTP"
+
+    ```sh
+    ./bin/pleroma_ctl frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
+    ```
 
-```sh tab="OTP"
-./bin/pleroma_ctl frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
-```
+=== "From Source"
 
-```sh tab="From Source"
-mix pleroma.frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
-```
+    ```sh
+    mix pleroma.frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
+    ```
 
-If you don't have a zip file but just want to install a frontend from a local path, you can simply copy the files over a folder of this template: `${instance_static}/frontends/${name}/${ref}`
+If you don't have a zip file but just want to install a frontend from a local path, you can simply copy the files over a folder of this template: `${instance_static}/frontends/${name}/${ref}`.
 
index e76eb008746514f854533eb6fe4a23ff7fa15aac..77933f0bec8eaee3163fc82a729d91def78c9aa4 100644 (file)
@@ -43,7 +43,7 @@ defmodule Pleroma.Conversation do
   def maybe_create_recipientships(participation, activity) do
     participation = Repo.preload(participation, :recipients)
 
-    if participation.recipients |> Enum.empty?() do
+    if Enum.empty?(participation.recipients) do
       recipients = User.get_all_by_ap_id(activity.recipients)
       RecipientShip.create(recipients, participation)
     end
@@ -69,10 +69,6 @@ defmodule Pleroma.Conversation do
         Enum.map(users, fn user ->
           invisible_conversation = Enum.any?(users, &User.blocks?(user, &1))
 
-          unless invisible_conversation do
-            User.increment_unread_conversation_count(conversation, user)
-          end
-
           opts = Keyword.put(opts, :invisible_conversation, invisible_conversation)
 
           {:ok, participation} =
index 8bc3e85d6e1f50f65c199c52999e05e903508fcf..4c32b273aabe64fc4c14c8f7d89be09b5320e7b9 100644 (file)
@@ -63,21 +63,10 @@ defmodule Pleroma.Conversation.Participation do
     end
   end
 
-  def mark_as_read(participation) do
-    __MODULE__
-    |> where(id: ^participation.id)
-    |> update(set: [read: true])
-    |> select([p], p)
-    |> Repo.update_all([])
-    |> case do
-      {1, [participation]} ->
-        participation = Repo.preload(participation, :user)
-        User.set_unread_conversation_count(participation.user)
-        {:ok, participation}
-
-      error ->
-        error
-    end
+  def mark_as_read(%__MODULE__{} = participation) do
+    participation
+    |> change(read: true)
+    |> Repo.update()
   end
 
   def mark_all_as_read(%User{local: true} = user, %User{} = target_user) do
@@ -93,7 +82,6 @@ defmodule Pleroma.Conversation.Participation do
     |> update([p], set: [read: true])
     |> Repo.update_all([])
 
-    {:ok, user} = User.set_unread_conversation_count(user)
     {:ok, user, []}
   end
 
@@ -108,7 +96,6 @@ defmodule Pleroma.Conversation.Participation do
       |> select([p], p)
       |> Repo.update_all([])
 
-    {:ok, user} = User.set_unread_conversation_count(user)
     {:ok, user, participations}
   end
 
@@ -220,6 +207,12 @@ defmodule Pleroma.Conversation.Participation do
     {:ok, Repo.preload(participation, :recipients, force: true)}
   end
 
+  @spec unread_count(User.t()) :: integer()
+  def unread_count(%User{id: user_id}) do
+    from(q in __MODULE__, where: q.user_id == ^user_id and q.read == false)
+    |> Repo.aggregate(:count, :id)
+  end
+
   def unread_conversation_count_for_user(user) do
     from(p in __MODULE__,
       where: p.user_id == ^user.id,
index a1e546b2d165573eb79ea9d38c27e2d8ca6615a5..059d94e30f2361f3152e6502c66892f3644ad4e5 100644 (file)
@@ -128,7 +128,6 @@ defmodule Pleroma.User do
     field(:hide_followers, :boolean, default: false)
     field(:hide_follows, :boolean, default: false)
     field(:hide_favorites, :boolean, default: true)
-    field(:unread_conversation_count, :integer, default: 0)
     field(:pinned_activities, {:array, :string}, default: [])
     field(:email_notifications, :map, default: %{"digest" => false})
     field(:mascot, :map, default: nil)
@@ -1305,47 +1304,6 @@ defmodule Pleroma.User do
     |> update_and_set_cache()
   end
 
-  def set_unread_conversation_count(%User{local: true} = user) do
-    unread_query = Participation.unread_conversation_count_for_user(user)
-
-    User
-    |> join(:inner, [u], p in subquery(unread_query))
-    |> update([u, p],
-      set: [unread_conversation_count: p.count]
-    )
-    |> where([u], u.id == ^user.id)
-    |> select([u], u)
-    |> Repo.update_all([])
-    |> case do
-      {1, [user]} -> set_cache(user)
-      _ -> {:error, user}
-    end
-  end
-
-  def set_unread_conversation_count(user), do: {:ok, user}
-
-  def increment_unread_conversation_count(conversation, %User{local: true} = user) do
-    unread_query =
-      Participation.unread_conversation_count_for_user(user)
-      |> where([p], p.conversation_id == ^conversation.id)
-
-    User
-    |> join(:inner, [u], p in subquery(unread_query))
-    |> update([u, p],
-      inc: [unread_conversation_count: 1]
-    )
-    |> where([u], u.id == ^user.id)
-    |> where([u, p], p.count == 0)
-    |> select([u], u)
-    |> Repo.update_all([])
-    |> case do
-      {1, [user]} -> set_cache(user)
-      _ -> {:error, user}
-    end
-  end
-
-  def increment_unread_conversation_count(_, user), do: {:ok, user}
-
   @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)
index 2440bf890c1adf275b7341019f0e4738eb0bca46..7ef2a145592f70d1e78dc726c74ef66e698e3470 100644 (file)
@@ -43,6 +43,7 @@ defmodule Pleroma.User.Query do
             active: boolean(),
             deactivated: boolean(),
             need_approval: boolean(),
+            unconfirmed: boolean(),
             is_admin: boolean(),
             is_moderator: boolean(),
             super_users: boolean(),
@@ -55,7 +56,8 @@ defmodule Pleroma.User.Query do
             ap_id: [String.t()],
             order_by: term(),
             select: term(),
-            limit: pos_integer()
+            limit: pos_integer(),
+            actor_types: [String.t()]
           }
           | map()
 
@@ -114,6 +116,10 @@ defmodule Pleroma.User.Query do
     where(query, [u], u.is_admin == ^bool)
   end
 
+  defp compose_query({:actor_types, actor_types}, query) when is_list(actor_types) do
+    where(query, [u], u.actor_type in ^actor_types)
+  end
+
   defp compose_query({:is_moderator, bool}, query) do
     where(query, [u], u.is_moderator == ^bool)
   end
@@ -156,6 +162,10 @@ defmodule Pleroma.User.Query do
     where(query, [u], u.approval_pending)
   end
 
+  defp compose_query({:unconfirmed, _}, query) do
+    where(query, [u], u.confirmation_pending)
+  end
+
   defp compose_query({:followers, %User{id: id}}, query) do
     query
     |> where([u], u.id != ^id)
index bdd3e195d177b80b83bc21c92e792bd5c3b6ac14..df5817cfa4f44f8f2988e86fb85a9db4faba3335 100644 (file)
@@ -5,7 +5,8 @@
 defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   use Pleroma.Web, :controller
 
-  import Pleroma.Web.ControllerHelper, only: [json_response: 3]
+  import Pleroma.Web.ControllerHelper,
+    only: [json_response: 3, fetch_integer_param: 3]
 
   alias Pleroma.Config
   alias Pleroma.MFA
@@ -13,12 +14,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   alias Pleroma.Stats
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.ActivityPub.Builder
-  alias Pleroma.Web.ActivityPub.Pipeline
   alias Pleroma.Web.AdminAPI
   alias Pleroma.Web.AdminAPI.AccountView
   alias Pleroma.Web.AdminAPI.ModerationLogView
-  alias Pleroma.Web.AdminAPI.Search
   alias Pleroma.Web.Endpoint
   alias Pleroma.Web.Plugs.OAuthScopesPlug
   alias Pleroma.Web.Router
@@ -28,7 +26,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   plug(
     OAuthScopesPlug,
     %{scopes: ["read:accounts"], admin: true}
-    when action in [:list_users, :user_show, :right_get, :show_user_credentials]
+    when action in [:right_get, :show_user_credentials]
   )
 
   plug(
@@ -37,12 +35,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     when action in [
            :get_password_reset,
            :force_password_reset,
-           :user_delete,
-           :users_create,
-           :user_toggle_activation,
-           :user_activate,
-           :user_deactivate,
-           :user_approve,
            :tag_users,
            :untag_users,
            :right_add,
@@ -54,12 +46,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
          ]
   )
 
-  plug(
-    OAuthScopesPlug,
-    %{scopes: ["write:follows"], admin: true}
-    when action in [:user_follow, :user_unfollow]
-  )
-
   plug(
     OAuthScopesPlug,
     %{scopes: ["read:statuses"], admin: true}
@@ -95,132 +81,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
 
   action_fallback(AdminAPI.FallbackController)
 
-  def user_delete(conn, %{"nickname" => nickname}) do
-    user_delete(conn, %{"nicknames" => [nickname]})
-  end
-
-  def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
-    users =
-      nicknames
-      |> Enum.map(&User.get_cached_by_nickname/1)
-
-    users
-    |> Enum.each(fn user ->
-      {:ok, delete_data, _} = Builder.delete(admin, user.ap_id)
-      Pipeline.common_pipeline(delete_data, local: true)
-    end)
-
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: users,
-      action: "delete"
-    })
-
-    json(conn, nicknames)
-  end
-
-  def user_follow(%{assigns: %{user: admin}} = conn, %{
-        "follower" => follower_nick,
-        "followed" => followed_nick
-      }) do
-    with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
-         %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
-      User.follow(follower, followed)
-
-      ModerationLog.insert_log(%{
-        actor: admin,
-        followed: followed,
-        follower: follower,
-        action: "follow"
-      })
-    end
-
-    json(conn, "ok")
-  end
-
-  def user_unfollow(%{assigns: %{user: admin}} = conn, %{
-        "follower" => follower_nick,
-        "followed" => followed_nick
-      }) do
-    with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
-         %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
-      User.unfollow(follower, followed)
-
-      ModerationLog.insert_log(%{
-        actor: admin,
-        followed: followed,
-        follower: follower,
-        action: "unfollow"
-      })
-    end
-
-    json(conn, "ok")
-  end
-
-  def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
-    changesets =
-      Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
-        user_data = %{
-          nickname: nickname,
-          name: nickname,
-          email: email,
-          password: password,
-          password_confirmation: password,
-          bio: "."
-        }
-
-        User.register_changeset(%User{}, user_data, need_confirmation: false)
-      end)
-      |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
-        Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
-      end)
-
-    case Pleroma.Repo.transaction(changesets) do
-      {:ok, users} ->
-        res =
-          users
-          |> Map.values()
-          |> Enum.map(fn user ->
-            {:ok, user} = User.post_register_action(user)
-
-            user
-          end)
-          |> Enum.map(&AccountView.render("created.json", %{user: &1}))
-
-        ModerationLog.insert_log(%{
-          actor: admin,
-          subjects: Map.values(users),
-          action: "create"
-        })
-
-        json(conn, res)
-
-      {:error, id, changeset, _} ->
-        res =
-          Enum.map(changesets.operations, fn
-            {current_id, {:changeset, _current_changeset, _}} when current_id == id ->
-              AccountView.render("create-error.json", %{changeset: changeset})
-
-            {_, {:changeset, current_changeset, _}} ->
-              AccountView.render("create-error.json", %{changeset: current_changeset})
-          end)
-
-        conn
-        |> put_status(:conflict)
-        |> json(res)
-    end
-  end
-
-  def user_show(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
-    with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
-      conn
-      |> put_view(AccountView)
-      |> render("show.json", %{user: user})
-    else
-      _ -> {:error, :not_found}
-    end
-  end
-
   def list_instance_statuses(conn, %{"instance" => instance} = params) do
     with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
     {page, page_size} = page_params(params)
@@ -274,69 +134,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     end
   end
 
-  def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
-    user = User.get_cached_by_nickname(nickname)
-
-    {:ok, updated_user} = User.deactivate(user, !user.deactivated)
-
-    action = if user.deactivated, do: "activate", else: "deactivate"
-
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: [user],
-      action: action
-    })
-
-    conn
-    |> put_view(AccountView)
-    |> render("show.json", %{user: updated_user})
-  end
-
-  def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
-    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
-    {:ok, updated_users} = User.deactivate(users, false)
-
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: users,
-      action: "activate"
-    })
-
-    conn
-    |> put_view(AccountView)
-    |> render("index.json", %{users: Keyword.values(updated_users)})
-  end
-
-  def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
-    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
-    {:ok, updated_users} = User.deactivate(users, true)
-
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: users,
-      action: "deactivate"
-    })
-
-    conn
-    |> put_view(AccountView)
-    |> render("index.json", %{users: Keyword.values(updated_users)})
-  end
-
-  def user_approve(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
-    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
-    {:ok, updated_users} = User.approve(users)
-
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: users,
-      action: "approve"
-    })
-
-    conn
-    |> put_view(AccountView)
-    |> render("index.json", %{users: updated_users})
-  end
-
   def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
     with {:ok, _} <- User.tag(nicknames, tags) do
       ModerationLog.insert_log(%{
@@ -363,43 +160,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     end
   end
 
-  def list_users(conn, params) do
-    {page, page_size} = page_params(params)
-    filters = maybe_parse_filters(params["filters"])
-
-    search_params = %{
-      query: params["query"],
-      page: page,
-      page_size: page_size,
-      tags: params["tags"],
-      name: params["name"],
-      email: params["email"]
-    }
-
-    with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do
-      json(
-        conn,
-        AccountView.render("index.json",
-          users: users,
-          count: count,
-          page_size: page_size
-        )
-      )
-    end
-  end
-
-  @filters ~w(local external active deactivated need_approval is_admin is_moderator)
-
-  @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
-  defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
-
-  defp maybe_parse_filters(filters) do
-    filters
-    |> String.split(",")
-    |> Enum.filter(&Enum.member?(@filters, &1))
-    |> Map.new(&{String.to_existing_atom(&1), true})
-  end
-
   def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
         "permission_group" => permission_group,
         "nicknames" => nicknames
@@ -682,24 +442,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   end
 
   defp page_params(params) do
-    {get_page(params["page"]), get_page_size(params["page_size"])}
-  end
-
-  defp get_page(page_string) when is_nil(page_string), do: 1
-
-  defp get_page(page_string) do
-    case Integer.parse(page_string) do
-      {page, _} -> page
-      :error -> 1
-    end
-  end
-
-  defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
-
-  defp get_page_size(page_size_string) do
-    case Integer.parse(page_size_string) do
-      {page_size, _} -> page_size
-      :error -> @users_page_size
-    end
+    {
+      fetch_integer_param(params, "page", 1),
+      fetch_integer_param(params, "page_size", @users_page_size)
+    }
   end
 end
diff --git a/lib/pleroma/web/admin_api/controllers/user_controller.ex b/lib/pleroma/web/admin_api/controllers/user_controller.ex
new file mode 100644 (file)
index 0000000..a2a1c87
--- /dev/null
@@ -0,0 +1,281 @@
+# Pleroma: A lightweight social networking server
+# Copyright Â© 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.AdminAPI.UserController do
+  use Pleroma.Web, :controller
+
+  import Pleroma.Web.ControllerHelper,
+    only: [fetch_integer_param: 3]
+
+  alias Pleroma.ModerationLog
+  alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.Builder
+  alias Pleroma.Web.ActivityPub.Pipeline
+  alias Pleroma.Web.AdminAPI
+  alias Pleroma.Web.AdminAPI.AccountView
+  alias Pleroma.Web.AdminAPI.Search
+  alias Pleroma.Web.Plugs.OAuthScopesPlug
+
+  @users_page_size 50
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["read:accounts"], admin: true}
+    when action in [:list, :show]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:accounts"], admin: true}
+    when action in [
+           :delete,
+           :create,
+           :toggle_activation,
+           :activate,
+           :deactivate,
+           :approve
+         ]
+  )
+
+  plug(
+    OAuthScopesPlug,
+    %{scopes: ["write:follows"], admin: true}
+    when action in [:follow, :unfollow]
+  )
+
+  action_fallback(AdminAPI.FallbackController)
+
+  def delete(conn, %{"nickname" => nickname}) do
+    delete(conn, %{"nicknames" => [nickname]})
+  end
+
+  def delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+
+    Enum.each(users, fn user ->
+      {:ok, delete_data, _} = Builder.delete(admin, user.ap_id)
+      Pipeline.common_pipeline(delete_data, local: true)
+    end)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "delete"
+    })
+
+    json(conn, nicknames)
+  end
+
+  def follow(%{assigns: %{user: admin}} = conn, %{
+        "follower" => follower_nick,
+        "followed" => followed_nick
+      }) do
+    with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
+         %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
+      User.follow(follower, followed)
+
+      ModerationLog.insert_log(%{
+        actor: admin,
+        followed: followed,
+        follower: follower,
+        action: "follow"
+      })
+    end
+
+    json(conn, "ok")
+  end
+
+  def unfollow(%{assigns: %{user: admin}} = conn, %{
+        "follower" => follower_nick,
+        "followed" => followed_nick
+      }) do
+    with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
+         %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
+      User.unfollow(follower, followed)
+
+      ModerationLog.insert_log(%{
+        actor: admin,
+        followed: followed,
+        follower: follower,
+        action: "unfollow"
+      })
+    end
+
+    json(conn, "ok")
+  end
+
+  def create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
+    changesets =
+      Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
+        user_data = %{
+          nickname: nickname,
+          name: nickname,
+          email: email,
+          password: password,
+          password_confirmation: password,
+          bio: "."
+        }
+
+        User.register_changeset(%User{}, user_data, need_confirmation: false)
+      end)
+      |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
+        Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
+      end)
+
+    case Pleroma.Repo.transaction(changesets) do
+      {:ok, users} ->
+        res =
+          users
+          |> Map.values()
+          |> Enum.map(fn user ->
+            {:ok, user} = User.post_register_action(user)
+
+            user
+          end)
+          |> Enum.map(&AccountView.render("created.json", %{user: &1}))
+
+        ModerationLog.insert_log(%{
+          actor: admin,
+          subjects: Map.values(users),
+          action: "create"
+        })
+
+        json(conn, res)
+
+      {:error, id, changeset, _} ->
+        res =
+          Enum.map(changesets.operations, fn
+            {current_id, {:changeset, _current_changeset, _}} when current_id == id ->
+              AccountView.render("create-error.json", %{changeset: changeset})
+
+            {_, {:changeset, current_changeset, _}} ->
+              AccountView.render("create-error.json", %{changeset: current_changeset})
+          end)
+
+        conn
+        |> put_status(:conflict)
+        |> json(res)
+    end
+  end
+
+  def show(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
+    with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
+      conn
+      |> put_view(AccountView)
+      |> render("show.json", %{user: user})
+    else
+      _ -> {:error, :not_found}
+    end
+  end
+
+  def toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
+    user = User.get_cached_by_nickname(nickname)
+
+    {:ok, updated_user} = User.deactivate(user, !user.deactivated)
+
+    action = if user.deactivated, do: "activate", else: "deactivate"
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: [user],
+      action: action
+    })
+
+    conn
+    |> put_view(AccountView)
+    |> render("show.json", %{user: updated_user})
+  end
+
+  def activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+    {:ok, updated_users} = User.deactivate(users, false)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "activate"
+    })
+
+    conn
+    |> put_view(AccountView)
+    |> render("index.json", %{users: Keyword.values(updated_users)})
+  end
+
+  def deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+    {:ok, updated_users} = User.deactivate(users, true)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "deactivate"
+    })
+
+    conn
+    |> put_view(AccountView)
+    |> render("index.json", %{users: Keyword.values(updated_users)})
+  end
+
+  def approve(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
+    {:ok, updated_users} = User.approve(users)
+
+    ModerationLog.insert_log(%{
+      actor: admin,
+      subject: users,
+      action: "approve"
+    })
+
+    conn
+    |> put_view(AccountView)
+    |> render("index.json", %{users: updated_users})
+  end
+
+  def list(conn, params) do
+    {page, page_size} = page_params(params)
+    filters = maybe_parse_filters(params["filters"])
+
+    search_params =
+      %{
+        query: params["query"],
+        page: page,
+        page_size: page_size,
+        tags: params["tags"],
+        name: params["name"],
+        email: params["email"],
+        actor_types: params["actor_types"]
+      }
+      |> Map.merge(filters)
+
+    with {:ok, users, count} <- Search.user(search_params) do
+      json(
+        conn,
+        AccountView.render("index.json",
+          users: users,
+          count: count,
+          page_size: page_size
+        )
+      )
+    end
+  end
+
+  @filters ~w(local external active deactivated need_approval unconfirmed is_admin is_moderator)
+
+  @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
+  defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
+
+  defp maybe_parse_filters(filters) do
+    filters
+    |> String.split(",")
+    |> Enum.filter(&Enum.member?(@filters, &1))
+    |> Map.new(&{String.to_existing_atom(&1), true})
+  end
+
+  defp page_params(params) do
+    {
+      fetch_integer_param(params, "page", 1),
+      fetch_integer_param(params, "page_size", @users_page_size)
+    }
+  end
+end
index d90ddb78714151d513b798bc99e11e1db275dca0..4934b77885da13e5c2b673eb677ca1e616912c16 100644 (file)
@@ -335,6 +335,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
       operationId: "AccountController.mutes",
       description: "Accounts the user has muted.",
       security: [%{"oAuth" => ["follow", "read:mutes"]}],
+      parameters: pagination_params(),
       responses: %{
         200 => Operation.response("Accounts", "application/json", array_of_accounts())
       }
@@ -348,6 +349,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
       operationId: "AccountController.blocks",
       description: "View your blocks. See also accounts/:id/{block,unblock}",
       security: [%{"oAuth" => ["read:blocks"]}],
+      parameters: pagination_params(),
       responses: %{
         200 => Operation.response("Accounts", "application/json", array_of_accounts())
       }
index 3cfdab91461784be778748522c52d781ede96ce7..a2715cf28a3d35dc3363714c35f0f029d45523c8 100644 (file)
@@ -442,15 +442,27 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
   end
 
   @doc "GET /api/v1/mutes"
-  def mutes(%{assigns: %{user: user}} = conn, _) do
-    users = User.muted_users(user, _restrict_deactivated = true)
-    render(conn, "index.json", users: users, for: user, as: :user)
+  def mutes(%{assigns: %{user: user}} = conn, params) do
+    users =
+      user
+      |> User.muted_users_relation(_restrict_deactivated = true)
+      |> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
+
+    conn
+    |> add_link_headers(users)
+    |> render("index.json", users: users, for: user, as: :user)
   end
 
   @doc "GET /api/v1/blocks"
-  def blocks(%{assigns: %{user: user}} = conn, _) do
-    users = User.blocked_users(user, _restrict_deactivated = true)
-    render(conn, "index.json", users: users, for: user, as: :user)
+  def blocks(%{assigns: %{user: user}} = conn, params) do
+    users =
+      user
+      |> User.blocked_users_relation(_restrict_deactivated = true)
+      |> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
+
+    conn
+    |> add_link_headers(users)
+    |> render("index.json", users: users, for: user, as: :user)
   end
 
   @doc "GET /api/v1/endorsements"
index 82fdca5572984456403c76f565dc7a407195bcc0..3158d09ed8dcd11cd10ab82292c74acd814b5ac6 100644 (file)
@@ -388,7 +388,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
     data
     |> Kernel.put_in(
       [:pleroma, :unread_conversation_count],
-      user.unread_conversation_count
+      Pleroma.Conversation.Participation.unread_count(user)
     )
   end
 
index 07a574f35f46553ab1eb9290503d3ab2f251fd26..76ca2c9b51ab3a9be87b109dbba5a2e9a6fb5af2 100644 (file)
@@ -149,16 +149,7 @@ defmodule Pleroma.Web.Router do
   scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
     pipe_through(:admin_api)
 
-    post("/users/follow", AdminAPIController, :user_follow)
-    post("/users/unfollow", AdminAPIController, :user_unfollow)
-
     put("/users/disable_mfa", AdminAPIController, :disable_mfa)
-    delete("/users", AdminAPIController, :user_delete)
-    post("/users", AdminAPIController, :users_create)
-    patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
-    patch("/users/activate", AdminAPIController, :user_activate)
-    patch("/users/deactivate", AdminAPIController, :user_deactivate)
-    patch("/users/approve", AdminAPIController, :user_approve)
     put("/users/tag", AdminAPIController, :tag_users)
     delete("/users/tag", AdminAPIController, :untag_users)
 
@@ -181,6 +172,15 @@ defmodule Pleroma.Web.Router do
       :right_delete_multiple
     )
 
+    post("/users/follow", UserController, :follow)
+    post("/users/unfollow", UserController, :unfollow)
+    delete("/users", UserController, :delete)
+    post("/users", UserController, :create)
+    patch("/users/:nickname/toggle_activation", UserController, :toggle_activation)
+    patch("/users/activate", UserController, :activate)
+    patch("/users/deactivate", UserController, :deactivate)
+    patch("/users/approve", UserController, :approve)
+
     get("/relay", RelayController, :index)
     post("/relay", RelayController, :follow)
     delete("/relay", RelayController, :unfollow)
@@ -195,8 +195,8 @@ defmodule Pleroma.Web.Router do
     get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
     patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
 
-    get("/users", AdminAPIController, :list_users)
-    get("/users/:nickname", AdminAPIController, :user_show)
+    get("/users", UserController, :list)
+    get("/users/:nickname", UserController, :show)
     get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
     get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
 
diff --git a/priv/repo/migrations/20200831114918_remove_unread_conversation_count_from_user.exs b/priv/repo/migrations/20200831114918_remove_unread_conversation_count_from_user.exs
new file mode 100644 (file)
index 0000000..b7bdb91
--- /dev/null
@@ -0,0 +1,38 @@
+defmodule Pleroma.Repo.Migrations.RemoveUnreadConversationCountFromUser do
+  use Ecto.Migration
+  import Ecto.Query
+  alias Pleroma.Repo
+
+  def up do
+    alter table(:users) do
+      remove_if_exists(:unread_conversation_count, :integer)
+    end
+  end
+
+  def down do
+    alter table(:users) do
+      add_if_not_exists(:unread_conversation_count, :integer, default: 0)
+    end
+
+    flush()
+    recalc_unread_conversation_count()
+  end
+
+  defp recalc_unread_conversation_count do
+    participations_subquery =
+      from(
+        p in "conversation_participations",
+        where: p.read == false,
+        group_by: p.user_id,
+        select: %{user_id: p.user_id, unread_conversation_count: count(p.id)}
+      )
+
+    from(
+      u in "users",
+      join: p in subquery(participations_subquery),
+      on: p.user_id == u.id,
+      update: [set: [unread_conversation_count: p.unread_conversation_count]]
+    )
+    |> Repo.update_all([])
+  end
+end
diff --git a/priv/repo/migrations/20200831115854_add_unread_index_to_conversation_participation.exs b/priv/repo/migrations/20200831115854_add_unread_index_to_conversation_participation.exs
new file mode 100644 (file)
index 0000000..68771c6
--- /dev/null
@@ -0,0 +1,12 @@
+defmodule Pleroma.Repo.Migrations.AddUnreadIndexToConversationParticipation do
+  use Ecto.Migration
+
+  def change do
+    create(
+      index(:conversation_participations, [:user_id],
+        where: "read = false",
+        name: "unread_conversation_participation_count_index"
+      )
+    )
+  end
+end
index 59a1b6492d75df3e0924e69a1c5183c1522686dc..5a603dcc1cdb18994cbcb81f1efb8a409c35030f 100644 (file)
@@ -37,9 +37,8 @@ defmodule Pleroma.Conversation.ParticipationTest do
 
     [%{read: true}] = Participation.for_user(user)
     [%{read: false} = participation] = Participation.for_user(other_user)
-
-    assert User.get_cached_by_id(user.id).unread_conversation_count == 0
-    assert User.get_cached_by_id(other_user.id).unread_conversation_count == 1
+    assert Participation.unread_count(user) == 0
+    assert Participation.unread_count(other_user) == 1
 
     {:ok, _} =
       CommonAPI.post(other_user, %{
@@ -54,8 +53,8 @@ defmodule Pleroma.Conversation.ParticipationTest do
     [%{read: false}] = Participation.for_user(user)
     [%{read: true}] = Participation.for_user(other_user)
 
-    assert User.get_cached_by_id(user.id).unread_conversation_count == 1
-    assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0
+    assert Participation.unread_count(user) == 1
+    assert Participation.unread_count(other_user) == 0
   end
 
   test "for a new conversation, it sets the recipents of the participation" do
@@ -264,7 +263,7 @@ defmodule Pleroma.Conversation.ParticipationTest do
       assert [%{read: false}, %{read: false}, %{read: false}, %{read: false}] =
                Participation.for_user(blocker)
 
-      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 4
+      assert Participation.unread_count(blocker) == 4
 
       {:ok, _user_relationship} = User.block(blocker, blocked)
 
@@ -272,15 +271,15 @@ defmodule Pleroma.Conversation.ParticipationTest do
       assert [%{read: true}, %{read: true}, %{read: true}, %{read: false}] =
                Participation.for_user(blocker)
 
-      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 1
+      assert Participation.unread_count(blocker) == 1
 
       # The conversation is not marked as read for the blocked user
       assert [_, _, %{read: false}] = Participation.for_user(blocked)
-      assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
+      assert Participation.unread_count(blocker) == 1
 
       # The conversation is not marked as read for the third user
       assert [%{read: false}, _, _] = Participation.for_user(third_user)
-      assert User.get_cached_by_id(third_user.id).unread_conversation_count == 1
+      assert Participation.unread_count(third_user) == 1
     end
 
     test "the new conversation with the blocked user is not marked as unread " do
@@ -298,7 +297,7 @@ defmodule Pleroma.Conversation.ParticipationTest do
         })
 
       assert [%{read: true}] = Participation.for_user(blocker)
-      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
+      assert Participation.unread_count(blocker) == 0
 
       # When the blocked user is a recipient
       {:ok, _direct2} =
@@ -308,10 +307,10 @@ defmodule Pleroma.Conversation.ParticipationTest do
         })
 
       assert [%{read: true}, %{read: true}] = Participation.for_user(blocker)
-      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
+      assert Participation.unread_count(blocker) == 0
 
       assert [%{read: false}, _] = Participation.for_user(blocked)
-      assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
+      assert Participation.unread_count(blocked) == 1
     end
 
     test "the conversation with the blocked user is not marked as unread on a reply" do
@@ -327,8 +326,8 @@ defmodule Pleroma.Conversation.ParticipationTest do
 
       {:ok, _user_relationship} = User.block(blocker, blocked)
       assert [%{read: true}] = Participation.for_user(blocker)
-      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
 
+      assert Participation.unread_count(blocker) == 0
       assert [blocked_participation] = Participation.for_user(blocked)
 
       # When it's a reply from the blocked user
@@ -340,8 +339,8 @@ defmodule Pleroma.Conversation.ParticipationTest do
         })
 
       assert [%{read: true}] = Participation.for_user(blocker)
-      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
 
+      assert Participation.unread_count(blocker) == 0
       assert [third_user_participation] = Participation.for_user(third_user)
 
       # When it's a reply from the third user
@@ -353,11 +352,12 @@ defmodule Pleroma.Conversation.ParticipationTest do
         })
 
       assert [%{read: true}] = Participation.for_user(blocker)
-      assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
+      assert Participation.unread_count(blocker) == 0
 
       # Marked as unread for the blocked user
       assert [%{read: false}] = Participation.for_user(blocked)
-      assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
+
+      assert Participation.unread_count(blocked) == 1
     end
   end
 end
index cba6b43d32a7d1a3e8519d5ebcc74d55e025256b..34b26dddfe5c34c3ae38363e8d6bc60528fc7a77 100644 (file)
@@ -7,22 +7,17 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
   use Oban.Testing, repo: Pleroma.Repo
 
   import ExUnit.CaptureLog
-  import Mock
   import Pleroma.Factory
   import Swoosh.TestAssertions
 
   alias Pleroma.Activity
   alias Pleroma.Config
-  alias Pleroma.HTML
   alias Pleroma.MFA
   alias Pleroma.ModerationLog
   alias Pleroma.Repo
   alias Pleroma.Tests.ObanHelpers
   alias Pleroma.User
-  alias Pleroma.Web
-  alias Pleroma.Web.ActivityPub.Relay
   alias Pleroma.Web.CommonAPI
-  alias Pleroma.Web.MediaProxy
 
   setup_all do
     Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
@@ -153,300 +148,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
   end
 
-  describe "DELETE /api/pleroma/admin/users" do
-    test "single user", %{admin: admin, conn: conn} do
-      clear_config([:instance, :federating], true)
-
-      user =
-        insert(:user,
-          avatar: %{"url" => [%{"href" => "https://someurl"}]},
-          banner: %{"url" => [%{"href" => "https://somebanner"}]},
-          bio: "Hello world!",
-          name: "A guy"
-        )
-
-      # Create some activities to check they got deleted later
-      follower = insert(:user)
-      {:ok, _} = CommonAPI.post(user, %{status: "test"})
-      {:ok, _, _, _} = CommonAPI.follow(user, follower)
-      {:ok, _, _, _} = CommonAPI.follow(follower, user)
-      user = Repo.get(User, user.id)
-      assert user.note_count == 1
-      assert user.follower_count == 1
-      assert user.following_count == 1
-      refute user.deactivated
-
-      with_mock Pleroma.Web.Federator,
-        publish: fn _ -> nil end,
-        perform: fn _, _ -> nil end do
-        conn =
-          conn
-          |> put_req_header("accept", "application/json")
-          |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
-
-        ObanHelpers.perform_all()
-
-        assert User.get_by_nickname(user.nickname).deactivated
-
-        log_entry = Repo.one(ModerationLog)
-
-        assert ModerationLog.get_log_entry_message(log_entry) ==
-                 "@#{admin.nickname} deleted users: @#{user.nickname}"
-
-        assert json_response(conn, 200) == [user.nickname]
-
-        user = Repo.get(User, user.id)
-        assert user.deactivated
-
-        assert user.avatar == %{}
-        assert user.banner == %{}
-        assert user.note_count == 0
-        assert user.follower_count == 0
-        assert user.following_count == 0
-        assert user.bio == ""
-        assert user.name == nil
-
-        assert called(Pleroma.Web.Federator.publish(:_))
-      end
-    end
-
-    test "multiple users", %{admin: admin, conn: conn} do
-      user_one = insert(:user)
-      user_two = insert(:user)
-
-      conn =
-        conn
-        |> put_req_header("accept", "application/json")
-        |> delete("/api/pleroma/admin/users", %{
-          nicknames: [user_one.nickname, user_two.nickname]
-        })
-
-      log_entry = Repo.one(ModerationLog)
-
-      assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
-
-      response = json_response(conn, 200)
-      assert response -- [user_one.nickname, user_two.nickname] == []
-    end
-  end
-
-  describe "/api/pleroma/admin/users" do
-    test "Create", %{conn: conn} do
-      conn =
-        conn
-        |> put_req_header("accept", "application/json")
-        |> post("/api/pleroma/admin/users", %{
-          "users" => [
-            %{
-              "nickname" => "lain",
-              "email" => "lain@example.org",
-              "password" => "test"
-            },
-            %{
-              "nickname" => "lain2",
-              "email" => "lain2@example.org",
-              "password" => "test"
-            }
-          ]
-        })
-
-      response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
-      assert response == ["success", "success"]
-
-      log_entry = Repo.one(ModerationLog)
-
-      assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
-    end
-
-    test "Cannot create user with existing email", %{conn: conn} do
-      user = insert(:user)
-
-      conn =
-        conn
-        |> put_req_header("accept", "application/json")
-        |> post("/api/pleroma/admin/users", %{
-          "users" => [
-            %{
-              "nickname" => "lain",
-              "email" => user.email,
-              "password" => "test"
-            }
-          ]
-        })
-
-      assert json_response(conn, 409) == [
-               %{
-                 "code" => 409,
-                 "data" => %{
-                   "email" => user.email,
-                   "nickname" => "lain"
-                 },
-                 "error" => "email has already been taken",
-                 "type" => "error"
-               }
-             ]
-    end
-
-    test "Cannot create user with existing nickname", %{conn: conn} do
-      user = insert(:user)
-
-      conn =
-        conn
-        |> put_req_header("accept", "application/json")
-        |> post("/api/pleroma/admin/users", %{
-          "users" => [
-            %{
-              "nickname" => user.nickname,
-              "email" => "someuser@plerama.social",
-              "password" => "test"
-            }
-          ]
-        })
-
-      assert json_response(conn, 409) == [
-               %{
-                 "code" => 409,
-                 "data" => %{
-                   "email" => "someuser@plerama.social",
-                   "nickname" => user.nickname
-                 },
-                 "error" => "nickname has already been taken",
-                 "type" => "error"
-               }
-             ]
-    end
-
-    test "Multiple user creation works in transaction", %{conn: conn} do
-      user = insert(:user)
-
-      conn =
-        conn
-        |> put_req_header("accept", "application/json")
-        |> post("/api/pleroma/admin/users", %{
-          "users" => [
-            %{
-              "nickname" => "newuser",
-              "email" => "newuser@pleroma.social",
-              "password" => "test"
-            },
-            %{
-              "nickname" => "lain",
-              "email" => user.email,
-              "password" => "test"
-            }
-          ]
-        })
-
-      assert json_response(conn, 409) == [
-               %{
-                 "code" => 409,
-                 "data" => %{
-                   "email" => user.email,
-                   "nickname" => "lain"
-                 },
-                 "error" => "email has already been taken",
-                 "type" => "error"
-               },
-               %{
-                 "code" => 409,
-                 "data" => %{
-                   "email" => "newuser@pleroma.social",
-                   "nickname" => "newuser"
-                 },
-                 "error" => "",
-                 "type" => "error"
-               }
-             ]
-
-      assert User.get_by_nickname("newuser") === nil
-    end
-  end
-
-  describe "/api/pleroma/admin/users/:nickname" do
-    test "Show", %{conn: conn} do
-      user = insert(:user)
-
-      conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
-
-      expected = %{
-        "deactivated" => false,
-        "id" => to_string(user.id),
-        "local" => true,
-        "nickname" => user.nickname,
-        "roles" => %{"admin" => false, "moderator" => false},
-        "tags" => [],
-        "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-        "display_name" => HTML.strip_tags(user.name || user.nickname),
-        "confirmation_pending" => false,
-        "approval_pending" => false,
-        "url" => user.ap_id,
-        "registration_reason" => nil,
-        "actor_type" => "Person"
-      }
-
-      assert expected == json_response(conn, 200)
-    end
-
-    test "when the user doesn't exist", %{conn: conn} do
-      user = build(:user)
-
-      conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
-
-      assert %{"error" => "Not found"} == json_response(conn, 404)
-    end
-  end
-
-  describe "/api/pleroma/admin/users/follow" do
-    test "allows to force-follow another user", %{admin: admin, conn: conn} do
-      user = insert(:user)
-      follower = insert(:user)
-
-      conn
-      |> put_req_header("accept", "application/json")
-      |> post("/api/pleroma/admin/users/follow", %{
-        "follower" => follower.nickname,
-        "followed" => user.nickname
-      })
-
-      user = User.get_cached_by_id(user.id)
-      follower = User.get_cached_by_id(follower.id)
-
-      assert User.following?(follower, user)
-
-      log_entry = Repo.one(ModerationLog)
-
-      assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
-    end
-  end
-
-  describe "/api/pleroma/admin/users/unfollow" do
-    test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
-      user = insert(:user)
-      follower = insert(:user)
-
-      User.follow(follower, user)
-
-      conn
-      |> put_req_header("accept", "application/json")
-      |> post("/api/pleroma/admin/users/unfollow", %{
-        "follower" => follower.nickname,
-        "followed" => user.nickname
-      })
-
-      user = User.get_cached_by_id(user.id)
-      follower = User.get_cached_by_id(follower.id)
-
-      refute User.following?(follower, user)
-
-      log_entry = Repo.one(ModerationLog)
-
-      assert ModerationLog.get_log_entry_message(log_entry) ==
-               "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
-    end
-  end
-
   describe "PUT /api/pleroma/admin/users/tag" do
     setup %{conn: conn} do
       user1 = insert(:user, %{tags: ["x"]})
@@ -643,753 +344,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     assert Regex.match?(~r/(http:\/\/|https:\/\/)/, resp["link"])
   end
 
-  describe "GET /api/pleroma/admin/users" do
-    test "renders users array for the first page", %{conn: conn, admin: admin} do
-      user = insert(:user, local: false, tags: ["foo", "bar"])
-      user2 = insert(:user, approval_pending: true, registration_reason: "I'm a chill dude")
-
-      conn = get(conn, "/api/pleroma/admin/users?page=1")
-
-      users =
-        [
-          %{
-            "deactivated" => admin.deactivated,
-            "id" => admin.id,
-            "nickname" => admin.nickname,
-            "roles" => %{"admin" => true, "moderator" => false},
-            "local" => true,
-            "tags" => [],
-            "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
-            "display_name" => HTML.strip_tags(admin.name || admin.nickname),
-            "confirmation_pending" => false,
-            "approval_pending" => false,
-            "url" => admin.ap_id,
-            "registration_reason" => nil,
-            "actor_type" => "Person"
-          },
-          %{
-            "deactivated" => user.deactivated,
-            "id" => user.id,
-            "nickname" => user.nickname,
-            "roles" => %{"admin" => false, "moderator" => false},
-            "local" => false,
-            "tags" => ["foo", "bar"],
-            "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-            "display_name" => HTML.strip_tags(user.name || user.nickname),
-            "confirmation_pending" => false,
-            "approval_pending" => false,
-            "url" => user.ap_id,
-            "registration_reason" => nil,
-            "actor_type" => "Person"
-          },
-          %{
-            "deactivated" => user2.deactivated,
-            "id" => user2.id,
-            "nickname" => user2.nickname,
-            "roles" => %{"admin" => false, "moderator" => false},
-            "local" => true,
-            "tags" => [],
-            "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
-            "display_name" => HTML.strip_tags(user2.name || user2.nickname),
-            "confirmation_pending" => false,
-            "approval_pending" => true,
-            "url" => user2.ap_id,
-            "registration_reason" => "I'm a chill dude",
-            "actor_type" => "Person"
-          }
-        ]
-        |> Enum.sort_by(& &1["nickname"])
-
-      assert json_response(conn, 200) == %{
-               "count" => 3,
-               "page_size" => 50,
-               "users" => users
-             }
-    end
-
-    test "pagination works correctly with service users", %{conn: conn} do
-      service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido")
-
-      insert_list(25, :user)
-
-      assert %{"count" => 26, "page_size" => 10, "users" => users1} =
-               conn
-               |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
-               |> json_response(200)
-
-      assert Enum.count(users1) == 10
-      assert service1 not in users1
-
-      assert %{"count" => 26, "page_size" => 10, "users" => users2} =
-               conn
-               |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
-               |> json_response(200)
-
-      assert Enum.count(users2) == 10
-      assert service1 not in users2
-
-      assert %{"count" => 26, "page_size" => 10, "users" => users3} =
-               conn
-               |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
-               |> json_response(200)
-
-      assert Enum.count(users3) == 6
-      assert service1 not in users3
-    end
-
-    test "renders empty array for the second page", %{conn: conn} do
-      insert(:user)
-
-      conn = get(conn, "/api/pleroma/admin/users?page=2")
-
-      assert json_response(conn, 200) == %{
-               "count" => 2,
-               "page_size" => 50,
-               "users" => []
-             }
-    end
-
-    test "regular search", %{conn: conn} do
-      user = insert(:user, nickname: "bob")
-
-      conn = get(conn, "/api/pleroma/admin/users?query=bo")
-
-      assert json_response(conn, 200) == %{
-               "count" => 1,
-               "page_size" => 50,
-               "users" => [
-                 %{
-                   "deactivated" => user.deactivated,
-                   "id" => user.id,
-                   "nickname" => user.nickname,
-                   "roles" => %{"admin" => false, "moderator" => false},
-                   "local" => true,
-                   "tags" => [],
-                   "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-                   "display_name" => HTML.strip_tags(user.name || user.nickname),
-                   "confirmation_pending" => false,
-                   "approval_pending" => false,
-                   "url" => user.ap_id,
-                   "registration_reason" => nil,
-                   "actor_type" => "Person"
-                 }
-               ]
-             }
-    end
-
-    test "search by domain", %{conn: conn} do
-      user = insert(:user, nickname: "nickname@domain.com")
-      insert(:user)
-
-      conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
-
-      assert json_response(conn, 200) == %{
-               "count" => 1,
-               "page_size" => 50,
-               "users" => [
-                 %{
-                   "deactivated" => user.deactivated,
-                   "id" => user.id,
-                   "nickname" => user.nickname,
-                   "roles" => %{"admin" => false, "moderator" => false},
-                   "local" => true,
-                   "tags" => [],
-                   "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-                   "display_name" => HTML.strip_tags(user.name || user.nickname),
-                   "confirmation_pending" => false,
-                   "approval_pending" => false,
-                   "url" => user.ap_id,
-                   "registration_reason" => nil,
-                   "actor_type" => "Person"
-                 }
-               ]
-             }
-    end
-
-    test "search by full nickname", %{conn: conn} do
-      user = insert(:user, nickname: "nickname@domain.com")
-      insert(:user)
-
-      conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
-
-      assert json_response(conn, 200) == %{
-               "count" => 1,
-               "page_size" => 50,
-               "users" => [
-                 %{
-                   "deactivated" => user.deactivated,
-                   "id" => user.id,
-                   "nickname" => user.nickname,
-                   "roles" => %{"admin" => false, "moderator" => false},
-                   "local" => true,
-                   "tags" => [],
-                   "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-                   "display_name" => HTML.strip_tags(user.name || user.nickname),
-                   "confirmation_pending" => false,
-                   "approval_pending" => false,
-                   "url" => user.ap_id,
-                   "registration_reason" => nil,
-                   "actor_type" => "Person"
-                 }
-               ]
-             }
-    end
-
-    test "search by display name", %{conn: conn} do
-      user = insert(:user, name: "Display name")
-      insert(:user)
-
-      conn = get(conn, "/api/pleroma/admin/users?name=display")
-
-      assert json_response(conn, 200) == %{
-               "count" => 1,
-               "page_size" => 50,
-               "users" => [
-                 %{
-                   "deactivated" => user.deactivated,
-                   "id" => user.id,
-                   "nickname" => user.nickname,
-                   "roles" => %{"admin" => false, "moderator" => false},
-                   "local" => true,
-                   "tags" => [],
-                   "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-                   "display_name" => HTML.strip_tags(user.name || user.nickname),
-                   "confirmation_pending" => false,
-                   "approval_pending" => false,
-                   "url" => user.ap_id,
-                   "registration_reason" => nil,
-                   "actor_type" => "Person"
-                 }
-               ]
-             }
-    end
-
-    test "search by email", %{conn: conn} do
-      user = insert(:user, email: "email@example.com")
-      insert(:user)
-
-      conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
-
-      assert json_response(conn, 200) == %{
-               "count" => 1,
-               "page_size" => 50,
-               "users" => [
-                 %{
-                   "deactivated" => user.deactivated,
-                   "id" => user.id,
-                   "nickname" => user.nickname,
-                   "roles" => %{"admin" => false, "moderator" => false},
-                   "local" => true,
-                   "tags" => [],
-                   "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-                   "display_name" => HTML.strip_tags(user.name || user.nickname),
-                   "confirmation_pending" => false,
-                   "approval_pending" => false,
-                   "url" => user.ap_id,
-                   "registration_reason" => nil,
-                   "actor_type" => "Person"
-                 }
-               ]
-             }
-    end
-
-    test "regular search with page size", %{conn: conn} do
-      user = insert(:user, nickname: "aalice")
-      user2 = insert(:user, nickname: "alice")
-
-      conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
-
-      assert json_response(conn1, 200) == %{
-               "count" => 2,
-               "page_size" => 1,
-               "users" => [
-                 %{
-                   "deactivated" => user.deactivated,
-                   "id" => user.id,
-                   "nickname" => user.nickname,
-                   "roles" => %{"admin" => false, "moderator" => false},
-                   "local" => true,
-                   "tags" => [],
-                   "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-                   "display_name" => HTML.strip_tags(user.name || user.nickname),
-                   "confirmation_pending" => false,
-                   "approval_pending" => false,
-                   "url" => user.ap_id,
-                   "registration_reason" => nil,
-                   "actor_type" => "Person"
-                 }
-               ]
-             }
-
-      conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
-
-      assert json_response(conn2, 200) == %{
-               "count" => 2,
-               "page_size" => 1,
-               "users" => [
-                 %{
-                   "deactivated" => user2.deactivated,
-                   "id" => user2.id,
-                   "nickname" => user2.nickname,
-                   "roles" => %{"admin" => false, "moderator" => false},
-                   "local" => true,
-                   "tags" => [],
-                   "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
-                   "display_name" => HTML.strip_tags(user2.name || user2.nickname),
-                   "confirmation_pending" => false,
-                   "approval_pending" => false,
-                   "url" => user2.ap_id,
-                   "registration_reason" => nil,
-                   "actor_type" => "Person"
-                 }
-               ]
-             }
-    end
-
-    test "only local users" do
-      admin = insert(:user, is_admin: true, nickname: "john")
-      token = insert(:oauth_admin_token, user: admin)
-      user = insert(:user, nickname: "bob")
-
-      insert(:user, nickname: "bobb", local: false)
-
-      conn =
-        build_conn()
-        |> assign(:user, admin)
-        |> assign(:token, token)
-        |> get("/api/pleroma/admin/users?query=bo&filters=local")
-
-      assert json_response(conn, 200) == %{
-               "count" => 1,
-               "page_size" => 50,
-               "users" => [
-                 %{
-                   "deactivated" => user.deactivated,
-                   "id" => user.id,
-                   "nickname" => user.nickname,
-                   "roles" => %{"admin" => false, "moderator" => false},
-                   "local" => true,
-                   "tags" => [],
-                   "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-                   "display_name" => HTML.strip_tags(user.name || user.nickname),
-                   "confirmation_pending" => false,
-                   "approval_pending" => false,
-                   "url" => user.ap_id,
-                   "registration_reason" => nil,
-                   "actor_type" => "Person"
-                 }
-               ]
-             }
-    end
-
-    test "only local users with no query", %{conn: conn, admin: old_admin} do
-      admin = insert(:user, is_admin: true, nickname: "john")
-      user = insert(:user, nickname: "bob")
-
-      insert(:user, nickname: "bobb", local: false)
-
-      conn = get(conn, "/api/pleroma/admin/users?filters=local")
-
-      users =
-        [
-          %{
-            "deactivated" => user.deactivated,
-            "id" => user.id,
-            "nickname" => user.nickname,
-            "roles" => %{"admin" => false, "moderator" => false},
-            "local" => true,
-            "tags" => [],
-            "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-            "display_name" => HTML.strip_tags(user.name || user.nickname),
-            "confirmation_pending" => false,
-            "approval_pending" => false,
-            "url" => user.ap_id,
-            "registration_reason" => nil,
-            "actor_type" => "Person"
-          },
-          %{
-            "deactivated" => admin.deactivated,
-            "id" => admin.id,
-            "nickname" => admin.nickname,
-            "roles" => %{"admin" => true, "moderator" => false},
-            "local" => true,
-            "tags" => [],
-            "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
-            "display_name" => HTML.strip_tags(admin.name || admin.nickname),
-            "confirmation_pending" => false,
-            "approval_pending" => false,
-            "url" => admin.ap_id,
-            "registration_reason" => nil,
-            "actor_type" => "Person"
-          },
-          %{
-            "deactivated" => false,
-            "id" => old_admin.id,
-            "local" => true,
-            "nickname" => old_admin.nickname,
-            "roles" => %{"admin" => true, "moderator" => false},
-            "tags" => [],
-            "avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
-            "display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
-            "confirmation_pending" => false,
-            "approval_pending" => false,
-            "url" => old_admin.ap_id,
-            "registration_reason" => nil,
-            "actor_type" => "Person"
-          }
-        ]
-        |> Enum.sort_by(& &1["nickname"])
-
-      assert json_response(conn, 200) == %{
-               "count" => 3,
-               "page_size" => 50,
-               "users" => users
-             }
-    end
-
-    test "only unapproved users", %{conn: conn} do
-      user =
-        insert(:user,
-          nickname: "sadboy",
-          approval_pending: true,
-          registration_reason: "Plz let me in!"
-        )
-
-      insert(:user, nickname: "happyboy", approval_pending: false)
-
-      conn = get(conn, "/api/pleroma/admin/users?filters=need_approval")
-
-      users =
-        [
-          %{
-            "deactivated" => user.deactivated,
-            "id" => user.id,
-            "nickname" => user.nickname,
-            "roles" => %{"admin" => false, "moderator" => false},
-            "local" => true,
-            "tags" => [],
-            "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-            "display_name" => HTML.strip_tags(user.name || user.nickname),
-            "confirmation_pending" => false,
-            "approval_pending" => true,
-            "url" => user.ap_id,
-            "registration_reason" => "Plz let me in!",
-            "actor_type" => "Person"
-          }
-        ]
-        |> Enum.sort_by(& &1["nickname"])
-
-      assert json_response(conn, 200) == %{
-               "count" => 1,
-               "page_size" => 50,
-               "users" => users
-             }
-    end
-
-    test "load only admins", %{conn: conn, admin: admin} do
-      second_admin = insert(:user, is_admin: true)
-      insert(:user)
-      insert(:user)
-
-      conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
-
-      users =
-        [
-          %{
-            "deactivated" => false,
-            "id" => admin.id,
-            "nickname" => admin.nickname,
-            "roles" => %{"admin" => true, "moderator" => false},
-            "local" => admin.local,
-            "tags" => [],
-            "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
-            "display_name" => HTML.strip_tags(admin.name || admin.nickname),
-            "confirmation_pending" => false,
-            "approval_pending" => false,
-            "url" => admin.ap_id,
-            "registration_reason" => nil,
-            "actor_type" => "Person"
-          },
-          %{
-            "deactivated" => false,
-            "id" => second_admin.id,
-            "nickname" => second_admin.nickname,
-            "roles" => %{"admin" => true, "moderator" => false},
-            "local" => second_admin.local,
-            "tags" => [],
-            "avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
-            "display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
-            "confirmation_pending" => false,
-            "approval_pending" => false,
-            "url" => second_admin.ap_id,
-            "registration_reason" => nil,
-            "actor_type" => "Person"
-          }
-        ]
-        |> Enum.sort_by(& &1["nickname"])
-
-      assert json_response(conn, 200) == %{
-               "count" => 2,
-               "page_size" => 50,
-               "users" => users
-             }
-    end
-
-    test "load only moderators", %{conn: conn} do
-      moderator = insert(:user, is_moderator: true)
-      insert(:user)
-      insert(:user)
-
-      conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
-
-      assert json_response(conn, 200) == %{
-               "count" => 1,
-               "page_size" => 50,
-               "users" => [
-                 %{
-                   "deactivated" => false,
-                   "id" => moderator.id,
-                   "nickname" => moderator.nickname,
-                   "roles" => %{"admin" => false, "moderator" => true},
-                   "local" => moderator.local,
-                   "tags" => [],
-                   "avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
-                   "display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
-                   "confirmation_pending" => false,
-                   "approval_pending" => false,
-                   "url" => moderator.ap_id,
-                   "registration_reason" => nil,
-                   "actor_type" => "Person"
-                 }
-               ]
-             }
-    end
-
-    test "load users with tags list", %{conn: conn} do
-      user1 = insert(:user, tags: ["first"])
-      user2 = insert(:user, tags: ["second"])
-      insert(:user)
-      insert(:user)
-
-      conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
-
-      users =
-        [
-          %{
-            "deactivated" => false,
-            "id" => user1.id,
-            "nickname" => user1.nickname,
-            "roles" => %{"admin" => false, "moderator" => false},
-            "local" => user1.local,
-            "tags" => ["first"],
-            "avatar" => User.avatar_url(user1) |> MediaProxy.url(),
-            "display_name" => HTML.strip_tags(user1.name || user1.nickname),
-            "confirmation_pending" => false,
-            "approval_pending" => false,
-            "url" => user1.ap_id,
-            "registration_reason" => nil,
-            "actor_type" => "Person"
-          },
-          %{
-            "deactivated" => false,
-            "id" => user2.id,
-            "nickname" => user2.nickname,
-            "roles" => %{"admin" => false, "moderator" => false},
-            "local" => user2.local,
-            "tags" => ["second"],
-            "avatar" => User.avatar_url(user2) |> MediaProxy.url(),
-            "display_name" => HTML.strip_tags(user2.name || user2.nickname),
-            "confirmation_pending" => false,
-            "approval_pending" => false,
-            "url" => user2.ap_id,
-            "registration_reason" => nil,
-            "actor_type" => "Person"
-          }
-        ]
-        |> Enum.sort_by(& &1["nickname"])
-
-      assert json_response(conn, 200) == %{
-               "count" => 2,
-               "page_size" => 50,
-               "users" => users
-             }
-    end
-
-    test "`active` filters out users pending approval", %{token: token} do
-      insert(:user, approval_pending: true)
-      %{id: user_id} = insert(:user, approval_pending: false)
-      %{id: admin_id} = token.user
-
-      conn =
-        build_conn()
-        |> assign(:user, token.user)
-        |> assign(:token, token)
-        |> get("/api/pleroma/admin/users?filters=active")
-
-      assert %{
-               "count" => 2,
-               "page_size" => 50,
-               "users" => [
-                 %{"id" => ^admin_id},
-                 %{"id" => ^user_id}
-               ]
-             } = json_response(conn, 200)
-    end
-
-    test "it works with multiple filters" do
-      admin = insert(:user, nickname: "john", is_admin: true)
-      token = insert(:oauth_admin_token, user: admin)
-      user = insert(:user, nickname: "bob", local: false, deactivated: true)
-
-      insert(:user, nickname: "ken", local: true, deactivated: true)
-      insert(:user, nickname: "bobb", local: false, deactivated: false)
-
-      conn =
-        build_conn()
-        |> assign(:user, admin)
-        |> assign(:token, token)
-        |> get("/api/pleroma/admin/users?filters=deactivated,external")
-
-      assert json_response(conn, 200) == %{
-               "count" => 1,
-               "page_size" => 50,
-               "users" => [
-                 %{
-                   "deactivated" => user.deactivated,
-                   "id" => user.id,
-                   "nickname" => user.nickname,
-                   "roles" => %{"admin" => false, "moderator" => false},
-                   "local" => user.local,
-                   "tags" => [],
-                   "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-                   "display_name" => HTML.strip_tags(user.name || user.nickname),
-                   "confirmation_pending" => false,
-                   "approval_pending" => false,
-                   "url" => user.ap_id,
-                   "registration_reason" => nil,
-                   "actor_type" => "Person"
-                 }
-               ]
-             }
-    end
-
-    test "it omits relay user", %{admin: admin, conn: conn} do
-      assert %User{} = Relay.get_actor()
-
-      conn = get(conn, "/api/pleroma/admin/users")
-
-      assert json_response(conn, 200) == %{
-               "count" => 1,
-               "page_size" => 50,
-               "users" => [
-                 %{
-                   "deactivated" => admin.deactivated,
-                   "id" => admin.id,
-                   "nickname" => admin.nickname,
-                   "roles" => %{"admin" => true, "moderator" => false},
-                   "local" => true,
-                   "tags" => [],
-                   "avatar" => User.avatar_url(admin) |> MediaProxy.url(),
-                   "display_name" => HTML.strip_tags(admin.name || admin.nickname),
-                   "confirmation_pending" => false,
-                   "approval_pending" => false,
-                   "url" => admin.ap_id,
-                   "registration_reason" => nil,
-                   "actor_type" => "Person"
-                 }
-               ]
-             }
-    end
-  end
-
-  test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
-    user_one = insert(:user, deactivated: true)
-    user_two = insert(:user, deactivated: true)
-
-    conn =
-      patch(
-        conn,
-        "/api/pleroma/admin/users/activate",
-        %{nicknames: [user_one.nickname, user_two.nickname]}
-      )
-
-    response = json_response(conn, 200)
-    assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
-
-    log_entry = Repo.one(ModerationLog)
-
-    assert ModerationLog.get_log_entry_message(log_entry) ==
-             "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
-  end
-
-  test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
-    user_one = insert(:user, deactivated: false)
-    user_two = insert(:user, deactivated: false)
-
-    conn =
-      patch(
-        conn,
-        "/api/pleroma/admin/users/deactivate",
-        %{nicknames: [user_one.nickname, user_two.nickname]}
-      )
-
-    response = json_response(conn, 200)
-    assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
-
-    log_entry = Repo.one(ModerationLog)
-
-    assert ModerationLog.get_log_entry_message(log_entry) ==
-             "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
-  end
-
-  test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do
-    user_one = insert(:user, approval_pending: true)
-    user_two = insert(:user, approval_pending: true)
-
-    conn =
-      patch(
-        conn,
-        "/api/pleroma/admin/users/approve",
-        %{nicknames: [user_one.nickname, user_two.nickname]}
-      )
-
-    response = json_response(conn, 200)
-    assert Enum.map(response["users"], & &1["approval_pending"]) == [false, false]
-
-    log_entry = Repo.one(ModerationLog)
-
-    assert ModerationLog.get_log_entry_message(log_entry) ==
-             "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
-  end
-
-  test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
-    user = insert(:user)
-
-    conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
-
-    assert json_response(conn, 200) ==
-             %{
-               "deactivated" => !user.deactivated,
-               "id" => user.id,
-               "nickname" => user.nickname,
-               "roles" => %{"admin" => false, "moderator" => false},
-               "local" => true,
-               "tags" => [],
-               "avatar" => User.avatar_url(user) |> MediaProxy.url(),
-               "display_name" => HTML.strip_tags(user.name || user.nickname),
-               "confirmation_pending" => false,
-               "approval_pending" => false,
-               "url" => user.ap_id,
-               "registration_reason" => nil,
-               "actor_type" => "Person"
-             }
-
-    log_entry = Repo.one(ModerationLog)
-
-    assert ModerationLog.get_log_entry_message(log_entry) ==
-             "@#{admin.nickname} deactivated users: @#{user.nickname}"
-  end
-
   describe "PUT disable_mfa" do
     test "returns 200 and disable 2fa", %{conn: conn} do
       user =
diff --git a/test/pleroma/web/admin_api/controllers/user_controller_test.exs b/test/pleroma/web/admin_api/controllers/user_controller_test.exs
new file mode 100644 (file)
index 0000000..da26caf
--- /dev/null
@@ -0,0 +1,971 @@
+# Pleroma: A lightweight social networking server
+# Copyright Â© 2017-2020 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.AdminAPI.UserControllerTest do
+  use Pleroma.Web.ConnCase
+  use Oban.Testing, repo: Pleroma.Repo
+
+  import Mock
+  import Pleroma.Factory
+
+  alias Pleroma.Config
+  alias Pleroma.HTML
+  alias Pleroma.ModerationLog
+  alias Pleroma.Repo
+  alias Pleroma.Tests.ObanHelpers
+  alias Pleroma.User
+  alias Pleroma.Web
+  alias Pleroma.Web.ActivityPub.Relay
+  alias Pleroma.Web.CommonAPI
+  alias Pleroma.Web.MediaProxy
+
+  setup_all do
+    Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
+
+    :ok
+  end
+
+  setup do
+    admin = insert(:user, is_admin: true)
+    token = insert(:oauth_admin_token, user: admin)
+
+    conn =
+      build_conn()
+      |> assign(:user, admin)
+      |> assign(:token, token)
+
+    {:ok, %{admin: admin, token: token, conn: conn}}
+  end
+
+  test "with valid `admin_token` query parameter, skips OAuth scopes check" do
+    clear_config([:admin_token], "password123")
+
+    user = insert(:user)
+
+    conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123")
+
+    assert json_response(conn, 200)
+  end
+
+  describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
+    setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
+
+    test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
+         %{admin: admin} do
+      user = insert(:user)
+      url = "/api/pleroma/admin/users/#{user.nickname}"
+
+      good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
+      good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
+      good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
+
+      bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
+      bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
+      bad_token3 = nil
+
+      for good_token <- [good_token1, good_token2, good_token3] do
+        conn =
+          build_conn()
+          |> assign(:user, admin)
+          |> assign(:token, good_token)
+          |> get(url)
+
+        assert json_response(conn, 200)
+      end
+
+      for good_token <- [good_token1, good_token2, good_token3] do
+        conn =
+          build_conn()
+          |> assign(:user, nil)
+          |> assign(:token, good_token)
+          |> get(url)
+
+        assert json_response(conn, :forbidden)
+      end
+
+      for bad_token <- [bad_token1, bad_token2, bad_token3] do
+        conn =
+          build_conn()
+          |> assign(:user, admin)
+          |> assign(:token, bad_token)
+          |> get(url)
+
+        assert json_response(conn, :forbidden)
+      end
+    end
+  end
+
+  describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
+    setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
+
+    test "GET /api/pleroma/admin/users/:nickname requires " <>
+           "read:accounts or admin:read:accounts or broader scope",
+         %{admin: admin} do
+      user = insert(:user)
+      url = "/api/pleroma/admin/users/#{user.nickname}"
+
+      good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
+      good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
+      good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
+      good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
+      good_token5 = insert(:oauth_token, user: admin, scopes: ["read"])
+
+      good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5]
+
+      bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"])
+      bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
+      bad_token3 = nil
+
+      for good_token <- good_tokens do
+        conn =
+          build_conn()
+          |> assign(:user, admin)
+          |> assign(:token, good_token)
+          |> get(url)
+
+        assert json_response(conn, 200)
+      end
+
+      for good_token <- good_tokens do
+        conn =
+          build_conn()
+          |> assign(:user, nil)
+          |> assign(:token, good_token)
+          |> get(url)
+
+        assert json_response(conn, :forbidden)
+      end
+
+      for bad_token <- [bad_token1, bad_token2, bad_token3] do
+        conn =
+          build_conn()
+          |> assign(:user, admin)
+          |> assign(:token, bad_token)
+          |> get(url)
+
+        assert json_response(conn, :forbidden)
+      end
+    end
+  end
+
+  describe "DELETE /api/pleroma/admin/users" do
+    test "single user", %{admin: admin, conn: conn} do
+      clear_config([:instance, :federating], true)
+
+      user =
+        insert(:user,
+          avatar: %{"url" => [%{"href" => "https://someurl"}]},
+          banner: %{"url" => [%{"href" => "https://somebanner"}]},
+          bio: "Hello world!",
+          name: "A guy"
+        )
+
+      # Create some activities to check they got deleted later
+      follower = insert(:user)
+      {:ok, _} = CommonAPI.post(user, %{status: "test"})
+      {:ok, _, _, _} = CommonAPI.follow(user, follower)
+      {:ok, _, _, _} = CommonAPI.follow(follower, user)
+      user = Repo.get(User, user.id)
+      assert user.note_count == 1
+      assert user.follower_count == 1
+      assert user.following_count == 1
+      refute user.deactivated
+
+      with_mock Pleroma.Web.Federator,
+        publish: fn _ -> nil end,
+        perform: fn _, _ -> nil end do
+        conn =
+          conn
+          |> put_req_header("accept", "application/json")
+          |> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
+
+        ObanHelpers.perform_all()
+
+        assert User.get_by_nickname(user.nickname).deactivated
+
+        log_entry = Repo.one(ModerationLog)
+
+        assert ModerationLog.get_log_entry_message(log_entry) ==
+                 "@#{admin.nickname} deleted users: @#{user.nickname}"
+
+        assert json_response(conn, 200) == [user.nickname]
+
+        user = Repo.get(User, user.id)
+        assert user.deactivated
+
+        assert user.avatar == %{}
+        assert user.banner == %{}
+        assert user.note_count == 0
+        assert user.follower_count == 0
+        assert user.following_count == 0
+        assert user.bio == ""
+        assert user.name == nil
+
+        assert called(Pleroma.Web.Federator.publish(:_))
+      end
+    end
+
+    test "multiple users", %{admin: admin, conn: conn} do
+      user_one = insert(:user)
+      user_two = insert(:user)
+
+      conn =
+        conn
+        |> put_req_header("accept", "application/json")
+        |> delete("/api/pleroma/admin/users", %{
+          nicknames: [user_one.nickname, user_two.nickname]
+        })
+
+      log_entry = Repo.one(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry) ==
+               "@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
+
+      response = json_response(conn, 200)
+      assert response -- [user_one.nickname, user_two.nickname] == []
+    end
+  end
+
+  describe "/api/pleroma/admin/users" do
+    test "Create", %{conn: conn} do
+      conn =
+        conn
+        |> put_req_header("accept", "application/json")
+        |> post("/api/pleroma/admin/users", %{
+          "users" => [
+            %{
+              "nickname" => "lain",
+              "email" => "lain@example.org",
+              "password" => "test"
+            },
+            %{
+              "nickname" => "lain2",
+              "email" => "lain2@example.org",
+              "password" => "test"
+            }
+          ]
+        })
+
+      response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
+      assert response == ["success", "success"]
+
+      log_entry = Repo.one(ModerationLog)
+
+      assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
+    end
+
+    test "Cannot create user with existing email", %{conn: conn} do
+      user = insert(:user)
+
+      conn =
+        conn
+        |> put_req_header("accept", "application/json")
+        |> post("/api/pleroma/admin/users", %{
+          "users" => [
+            %{
+              "nickname" => "lain",
+              "email" => user.email,
+              "password" => "test"
+            }
+          ]
+        })
+
+      assert json_response(conn, 409) == [
+               %{
+                 "code" => 409,
+                 "data" => %{
+                   "email" => user.email,
+                   "nickname" => "lain"
+                 },
+                 "error" => "email has already been taken",
+                 "type" => "error"
+               }
+             ]
+    end
+
+    test "Cannot create user with existing nickname", %{conn: conn} do
+      user = insert(:user)
+
+      conn =
+        conn
+        |> put_req_header("accept", "application/json")
+        |> post("/api/pleroma/admin/users", %{
+          "users" => [
+            %{
+              "nickname" => user.nickname,
+              "email" => "someuser@plerama.social",
+              "password" => "test"
+            }
+          ]
+        })
+
+      assert json_response(conn, 409) == [
+               %{
+                 "code" => 409,
+                 "data" => %{
+                   "email" => "someuser@plerama.social",
+                   "nickname" => user.nickname
+                 },
+                 "error" => "nickname has already been taken",
+                 "type" => "error"
+               }
+             ]
+    end
+
+    test "Multiple user creation works in transaction", %{conn: conn} do
+      user = insert(:user)
+
+      conn =
+        conn
+        |> put_req_header("accept", "application/json")
+        |> post("/api/pleroma/admin/users", %{
+          "users" => [
+            %{
+              "nickname" => "newuser",
+              "email" => "newuser@pleroma.social",
+              "password" => "test"
+            },
+            %{
+              "nickname" => "lain",
+              "email" => user.email,
+              "password" => "test"
+            }
+          ]
+        })
+
+      assert json_response(conn, 409) == [
+               %{
+                 "code" => 409,
+                 "data" => %{
+                   "email" => user.email,
+                   "nickname" => "lain"
+                 },
+                 "error" => "email has already been taken",
+                 "type" => "error"
+               },
+               %{
+                 "code" => 409,
+                 "data" => %{
+                   "email" => "newuser@pleroma.social",
+                   "nickname" => "newuser"
+                 },
+                 "error" => "",
+                 "type" => "error"
+               }
+             ]
+
+      assert User.get_by_nickname("newuser") === nil
+    end
+  end
+
+  describe "/api/pleroma/admin/users/:nickname" do
+    test "Show", %{conn: conn} do
+      user = insert(:user)
+
+      conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
+
+      assert user_response(user) == json_response(conn, 200)
+    end
+
+    test "when the user doesn't exist", %{conn: conn} do
+      user = build(:user)
+
+      conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
+
+      assert %{"error" => "Not found"} == json_response(conn, 404)
+    end
+  end
+
+  describe "/api/pleroma/admin/users/follow" do
+    test "allows to force-follow another user", %{admin: admin, conn: conn} do
+      user = insert(:user)
+      follower = insert(:user)
+
+      conn
+      |> put_req_header("accept", "application/json")
+      |> post("/api/pleroma/admin/users/follow", %{
+        "follower" => follower.nickname,
+        "followed" => user.nickname
+      })
+
+      user = User.get_cached_by_id(user.id)
+      follower = User.get_cached_by_id(follower.id)
+
+      assert User.following?(follower, user)
+
+      log_entry = Repo.one(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry) ==
+               "@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
+    end
+  end
+
+  describe "/api/pleroma/admin/users/unfollow" do
+    test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
+      user = insert(:user)
+      follower = insert(:user)
+
+      User.follow(follower, user)
+
+      conn
+      |> put_req_header("accept", "application/json")
+      |> post("/api/pleroma/admin/users/unfollow", %{
+        "follower" => follower.nickname,
+        "followed" => user.nickname
+      })
+
+      user = User.get_cached_by_id(user.id)
+      follower = User.get_cached_by_id(follower.id)
+
+      refute User.following?(follower, user)
+
+      log_entry = Repo.one(ModerationLog)
+
+      assert ModerationLog.get_log_entry_message(log_entry) ==
+               "@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
+    end
+  end
+
+  describe "GET /api/pleroma/admin/users" do
+    test "renders users array for the first page", %{conn: conn, admin: admin} do
+      user = insert(:user, local: false, tags: ["foo", "bar"])
+      user2 = insert(:user, approval_pending: true, registration_reason: "I'm a chill dude")
+
+      conn = get(conn, "/api/pleroma/admin/users?page=1")
+
+      users =
+        [
+          user_response(
+            admin,
+            %{"roles" => %{"admin" => true, "moderator" => false}}
+          ),
+          user_response(user, %{"local" => false, "tags" => ["foo", "bar"]}),
+          user_response(
+            user2,
+            %{
+              "local" => true,
+              "approval_pending" => true,
+              "registration_reason" => "I'm a chill dude",
+              "actor_type" => "Person"
+            }
+          )
+        ]
+        |> Enum.sort_by(& &1["nickname"])
+
+      assert json_response(conn, 200) == %{
+               "count" => 3,
+               "page_size" => 50,
+               "users" => users
+             }
+    end
+
+    test "pagination works correctly with service users", %{conn: conn} do
+      service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido")
+
+      insert_list(25, :user)
+
+      assert %{"count" => 26, "page_size" => 10, "users" => users1} =
+               conn
+               |> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
+               |> json_response(200)
+
+      assert Enum.count(users1) == 10
+      assert service1 not in users1
+
+      assert %{"count" => 26, "page_size" => 10, "users" => users2} =
+               conn
+               |> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
+               |> json_response(200)
+
+      assert Enum.count(users2) == 10
+      assert service1 not in users2
+
+      assert %{"count" => 26, "page_size" => 10, "users" => users3} =
+               conn
+               |> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
+               |> json_response(200)
+
+      assert Enum.count(users3) == 6
+      assert service1 not in users3
+    end
+
+    test "renders empty array for the second page", %{conn: conn} do
+      insert(:user)
+
+      conn = get(conn, "/api/pleroma/admin/users?page=2")
+
+      assert json_response(conn, 200) == %{
+               "count" => 2,
+               "page_size" => 50,
+               "users" => []
+             }
+    end
+
+    test "regular search", %{conn: conn} do
+      user = insert(:user, nickname: "bob")
+
+      conn = get(conn, "/api/pleroma/admin/users?query=bo")
+
+      assert json_response(conn, 200) == %{
+               "count" => 1,
+               "page_size" => 50,
+               "users" => [user_response(user, %{"local" => true})]
+             }
+    end
+
+    test "search by domain", %{conn: conn} do
+      user = insert(:user, nickname: "nickname@domain.com")
+      insert(:user)
+
+      conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
+
+      assert json_response(conn, 200) == %{
+               "count" => 1,
+               "page_size" => 50,
+               "users" => [user_response(user)]
+             }
+    end
+
+    test "search by full nickname", %{conn: conn} do
+      user = insert(:user, nickname: "nickname@domain.com")
+      insert(:user)
+
+      conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
+
+      assert json_response(conn, 200) == %{
+               "count" => 1,
+               "page_size" => 50,
+               "users" => [user_response(user)]
+             }
+    end
+
+    test "search by display name", %{conn: conn} do
+      user = insert(:user, name: "Display name")
+      insert(:user)
+
+      conn = get(conn, "/api/pleroma/admin/users?name=display")
+
+      assert json_response(conn, 200) == %{
+               "count" => 1,
+               "page_size" => 50,
+               "users" => [user_response(user)]
+             }
+    end
+
+    test "search by email", %{conn: conn} do
+      user = insert(:user, email: "email@example.com")
+      insert(:user)
+
+      conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
+
+      assert json_response(conn, 200) == %{
+               "count" => 1,
+               "page_size" => 50,
+               "users" => [user_response(user)]
+             }
+    end
+
+    test "regular search with page size", %{conn: conn} do
+      user = insert(:user, nickname: "aalice")
+      user2 = insert(:user, nickname: "alice")
+
+      conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
+
+      assert json_response(conn1, 200) == %{
+               "count" => 2,
+               "page_size" => 1,
+               "users" => [user_response(user)]
+             }
+
+      conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
+
+      assert json_response(conn2, 200) == %{
+               "count" => 2,
+               "page_size" => 1,
+               "users" => [user_response(user2)]
+             }
+    end
+
+    test "only local users" do
+      admin = insert(:user, is_admin: true, nickname: "john")
+      token = insert(:oauth_admin_token, user: admin)
+      user = insert(:user, nickname: "bob")
+
+      insert(:user, nickname: "bobb", local: false)
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> assign(:token, token)
+        |> get("/api/pleroma/admin/users?query=bo&filters=local")
+
+      assert json_response(conn, 200) == %{
+               "count" => 1,
+               "page_size" => 50,
+               "users" => [user_response(user)]
+             }
+    end
+
+    test "only local users with no query", %{conn: conn, admin: old_admin} do
+      admin = insert(:user, is_admin: true, nickname: "john")
+      user = insert(:user, nickname: "bob")
+
+      insert(:user, nickname: "bobb", local: false)
+
+      conn = get(conn, "/api/pleroma/admin/users?filters=local")
+
+      users =
+        [
+          user_response(user),
+          user_response(admin, %{
+            "roles" => %{"admin" => true, "moderator" => false}
+          }),
+          user_response(old_admin, %{
+            "deactivated" => false,
+            "roles" => %{"admin" => true, "moderator" => false}
+          })
+        ]
+        |> Enum.sort_by(& &1["nickname"])
+
+      assert json_response(conn, 200) == %{
+               "count" => 3,
+               "page_size" => 50,
+               "users" => users
+             }
+    end
+
+    test "only unconfirmed users", %{conn: conn} do
+      sad_user = insert(:user, nickname: "sadboy", confirmation_pending: true)
+      old_user = insert(:user, nickname: "oldboy", confirmation_pending: true)
+
+      insert(:user, nickname: "happyboy", approval_pending: false)
+      insert(:user, confirmation_pending: false)
+
+      result =
+        conn
+        |> get("/api/pleroma/admin/users?filters=unconfirmed")
+        |> json_response(200)
+
+      users =
+        Enum.map([old_user, sad_user], fn user ->
+          user_response(user, %{
+            "confirmation_pending" => true,
+            "approval_pending" => false
+          })
+        end)
+        |> Enum.sort_by(& &1["nickname"])
+
+      assert result == %{"count" => 2, "page_size" => 50, "users" => users}
+    end
+
+    test "only unapproved users", %{conn: conn} do
+      user =
+        insert(:user,
+          nickname: "sadboy",
+          approval_pending: true,
+          registration_reason: "Plz let me in!"
+        )
+
+      insert(:user, nickname: "happyboy", approval_pending: false)
+
+      conn = get(conn, "/api/pleroma/admin/users?filters=need_approval")
+
+      users = [
+        user_response(
+          user,
+          %{"approval_pending" => true, "registration_reason" => "Plz let me in!"}
+        )
+      ]
+
+      assert json_response(conn, 200) == %{
+               "count" => 1,
+               "page_size" => 50,
+               "users" => users
+             }
+    end
+
+    test "load only admins", %{conn: conn, admin: admin} do
+      second_admin = insert(:user, is_admin: true)
+      insert(:user)
+      insert(:user)
+
+      conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
+
+      users =
+        [
+          user_response(admin, %{
+            "deactivated" => false,
+            "roles" => %{"admin" => true, "moderator" => false}
+          }),
+          user_response(second_admin, %{
+            "deactivated" => false,
+            "roles" => %{"admin" => true, "moderator" => false}
+          })
+        ]
+        |> Enum.sort_by(& &1["nickname"])
+
+      assert json_response(conn, 200) == %{
+               "count" => 2,
+               "page_size" => 50,
+               "users" => users
+             }
+    end
+
+    test "load only moderators", %{conn: conn} do
+      moderator = insert(:user, is_moderator: true)
+      insert(:user)
+      insert(:user)
+
+      conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
+
+      assert json_response(conn, 200) == %{
+               "count" => 1,
+               "page_size" => 50,
+               "users" => [
+                 user_response(moderator, %{
+                   "deactivated" => false,
+                   "roles" => %{"admin" => false, "moderator" => true}
+                 })
+               ]
+             }
+    end
+
+    test "load users with actor_type is Person", %{admin: admin, conn: conn} do
+      insert(:user, actor_type: "Service")
+      insert(:user, actor_type: "Application")
+
+      user1 = insert(:user)
+      user2 = insert(:user)
+
+      response =
+        conn
+        |> get(user_path(conn, :list), %{actor_types: ["Person"]})
+        |> json_response(200)
+
+      users =
+        [
+          user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}}),
+          user_response(user1),
+          user_response(user2)
+        ]
+        |> Enum.sort_by(& &1["nickname"])
+
+      assert response == %{"count" => 3, "page_size" => 50, "users" => users}
+    end
+
+    test "load users with actor_type is Person and Service", %{admin: admin, conn: conn} do
+      user_service = insert(:user, actor_type: "Service")
+      insert(:user, actor_type: "Application")
+
+      user1 = insert(:user)
+      user2 = insert(:user)
+
+      response =
+        conn
+        |> get(user_path(conn, :list), %{actor_types: ["Person", "Service"]})
+        |> json_response(200)
+
+      users =
+        [
+          user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}}),
+          user_response(user1),
+          user_response(user2),
+          user_response(user_service, %{"actor_type" => "Service"})
+        ]
+        |> Enum.sort_by(& &1["nickname"])
+
+      assert response == %{"count" => 4, "page_size" => 50, "users" => users}
+    end
+
+    test "load users with actor_type is Service", %{conn: conn} do
+      user_service = insert(:user, actor_type: "Service")
+      insert(:user, actor_type: "Application")
+      insert(:user)
+      insert(:user)
+
+      response =
+        conn
+        |> get(user_path(conn, :list), %{actor_types: ["Service"]})
+        |> json_response(200)
+
+      users = [user_response(user_service, %{"actor_type" => "Service"})]
+
+      assert response == %{"count" => 1, "page_size" => 50, "users" => users}
+    end
+
+    test "load users with tags list", %{conn: conn} do
+      user1 = insert(:user, tags: ["first"])
+      user2 = insert(:user, tags: ["second"])
+      insert(:user)
+      insert(:user)
+
+      conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
+
+      users =
+        [
+          user_response(user1, %{"tags" => ["first"]}),
+          user_response(user2, %{"tags" => ["second"]})
+        ]
+        |> Enum.sort_by(& &1["nickname"])
+
+      assert json_response(conn, 200) == %{
+               "count" => 2,
+               "page_size" => 50,
+               "users" => users
+             }
+    end
+
+    test "`active` filters out users pending approval", %{token: token} do
+      insert(:user, approval_pending: true)
+      %{id: user_id} = insert(:user, approval_pending: false)
+      %{id: admin_id} = token.user
+
+      conn =
+        build_conn()
+        |> assign(:user, token.user)
+        |> assign(:token, token)
+        |> get("/api/pleroma/admin/users?filters=active")
+
+      assert %{
+               "count" => 2,
+               "page_size" => 50,
+               "users" => [
+                 %{"id" => ^admin_id},
+                 %{"id" => ^user_id}
+               ]
+             } = json_response(conn, 200)
+    end
+
+    test "it works with multiple filters" do
+      admin = insert(:user, nickname: "john", is_admin: true)
+      token = insert(:oauth_admin_token, user: admin)
+      user = insert(:user, nickname: "bob", local: false, deactivated: true)
+
+      insert(:user, nickname: "ken", local: true, deactivated: true)
+      insert(:user, nickname: "bobb", local: false, deactivated: false)
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> assign(:token, token)
+        |> get("/api/pleroma/admin/users?filters=deactivated,external")
+
+      assert json_response(conn, 200) == %{
+               "count" => 1,
+               "page_size" => 50,
+               "users" => [user_response(user)]
+             }
+    end
+
+    test "it omits relay user", %{admin: admin, conn: conn} do
+      assert %User{} = Relay.get_actor()
+
+      conn = get(conn, "/api/pleroma/admin/users")
+
+      assert json_response(conn, 200) == %{
+               "count" => 1,
+               "page_size" => 50,
+               "users" => [
+                 user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}})
+               ]
+             }
+    end
+  end
+
+  test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
+    user_one = insert(:user, deactivated: true)
+    user_two = insert(:user, deactivated: true)
+
+    conn =
+      patch(
+        conn,
+        "/api/pleroma/admin/users/activate",
+        %{nicknames: [user_one.nickname, user_two.nickname]}
+      )
+
+    response = json_response(conn, 200)
+    assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
+  end
+
+  test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
+    user_one = insert(:user, deactivated: false)
+    user_two = insert(:user, deactivated: false)
+
+    conn =
+      patch(
+        conn,
+        "/api/pleroma/admin/users/deactivate",
+        %{nicknames: [user_one.nickname, user_two.nickname]}
+      )
+
+    response = json_response(conn, 200)
+    assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
+  end
+
+  test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do
+    user_one = insert(:user, approval_pending: true)
+    user_two = insert(:user, approval_pending: true)
+
+    conn =
+      patch(
+        conn,
+        "/api/pleroma/admin/users/approve",
+        %{nicknames: [user_one.nickname, user_two.nickname]}
+      )
+
+    response = json_response(conn, 200)
+    assert Enum.map(response["users"], & &1["approval_pending"]) == [false, false]
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
+  end
+
+  test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
+    user = insert(:user)
+
+    conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
+
+    assert json_response(conn, 200) ==
+             user_response(
+               user,
+               %{"deactivated" => !user.deactivated}
+             )
+
+    log_entry = Repo.one(ModerationLog)
+
+    assert ModerationLog.get_log_entry_message(log_entry) ==
+             "@#{admin.nickname} deactivated users: @#{user.nickname}"
+  end
+
+  defp user_response(user, attrs \\ %{}) do
+    %{
+      "deactivated" => user.deactivated,
+      "id" => user.id,
+      "nickname" => user.nickname,
+      "roles" => %{"admin" => false, "moderator" => false},
+      "local" => user.local,
+      "tags" => [],
+      "avatar" => User.avatar_url(user) |> MediaProxy.url(),
+      "display_name" => HTML.strip_tags(user.name || user.nickname),
+      "confirmation_pending" => false,
+      "approval_pending" => false,
+      "url" => user.ap_id,
+      "registration_reason" => nil,
+      "actor_type" => "Person"
+    }
+    |> Map.merge(attrs)
+  end
+end
index ceec64f1e73ec81c78015f5bbe41d5d38ffd90a3..92a116c659e337fceed074396ed1476af6c7494b 100644 (file)
@@ -143,6 +143,20 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do
       assert user2 in users
     end
 
+    test "it returns users by actor_types" do
+      user_service = insert(:user, actor_type: "Service")
+      user_application = insert(:user, actor_type: "Application")
+      user1 = insert(:user)
+      user2 = insert(:user)
+
+      {:ok, [^user_service], 1} = Search.user(%{actor_types: ["Service"]})
+      {:ok, [^user_application], 1} = Search.user(%{actor_types: ["Application"]})
+      {:ok, [^user1, ^user2], 2} = Search.user(%{actor_types: ["Person"]})
+
+      {:ok, [^user_service, ^user1, ^user2], 3} =
+        Search.user(%{actor_types: ["Person", "Service"]})
+    end
+
     test "it returns user by display name" do
       user = insert(:user, name: "Display name")
       insert(:user)
@@ -178,6 +192,17 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do
       assert count == 1
     end
 
+    test "it returns unconfirmed user" do
+      unconfirmed = insert(:user, confirmation_pending: true)
+      insert(:user)
+      insert(:user)
+
+      {:ok, _results, total} = Search.user()
+      {:ok, [^unconfirmed], count} = Search.user(%{unconfirmed: true})
+      assert total == 3
+      assert count == 1
+    end
+
     test "it returns non-discoverable users" do
       insert(:user)
       insert(:user, is_discoverable: false)
index dcc0c81ece43617fdc1c6a8080587d5089401509..58ce76ab88b1e7a449973b537057c63ac6b286e4 100644 (file)
@@ -1509,28 +1509,103 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
 
   test "getting a list of mutes" do
     %{user: user, conn: conn} = oauth_access(["read:mutes"])
-    other_user = insert(:user)
+    %{id: id1} = other_user1 = insert(:user)
+    %{id: id2} = other_user2 = insert(:user)
+    %{id: id3} = other_user3 = insert(:user)
+
+    {:ok, _user_relationships} = User.mute(user, other_user1)
+    {:ok, _user_relationships} = User.mute(user, other_user2)
+    {:ok, _user_relationships} = User.mute(user, other_user3)
+
+    result =
+      conn
+      |> assign(:user, user)
+      |> get("/api/v1/mutes")
+      |> json_response_and_validate_schema(200)
+
+    assert [id1, id2, id3] == Enum.map(result, & &1["id"])
+
+    result =
+      conn
+      |> assign(:user, user)
+      |> get("/api/v1/mutes?limit=1")
+      |> json_response_and_validate_schema(200)
 
-    {:ok, _user_relationships} = User.mute(user, other_user)
+    assert [%{"id" => ^id1}] = result
 
-    conn = get(conn, "/api/v1/mutes")
+    result =
+      conn
+      |> assign(:user, user)
+      |> get("/api/v1/mutes?since_id=#{id1}")
+      |> json_response_and_validate_schema(200)
+
+    assert [%{"id" => ^id2}, %{"id" => ^id3}] = result
+
+    result =
+      conn
+      |> assign(:user, user)
+      |> get("/api/v1/mutes?since_id=#{id1}&max_id=#{id3}")
+      |> json_response_and_validate_schema(200)
+
+    assert [%{"id" => ^id2}] = result
+
+    result =
+      conn
+      |> assign(:user, user)
+      |> get("/api/v1/mutes?since_id=#{id1}&limit=1")
+      |> json_response_and_validate_schema(200)
 
-    other_user_id = to_string(other_user.id)
-    assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200)
+    assert [%{"id" => ^id2}] = result
   end
 
   test "getting a list of blocks" do
     %{user: user, conn: conn} = oauth_access(["read:blocks"])
-    other_user = insert(:user)
+    %{id: id1} = other_user1 = insert(:user)
+    %{id: id2} = other_user2 = insert(:user)
+    %{id: id3} = other_user3 = insert(:user)
 
-    {:ok, _user_relationship} = User.block(user, other_user)
+    {:ok, _user_relationship} = User.block(user, other_user1)
+    {:ok, _user_relationship} = User.block(user, other_user3)
+    {:ok, _user_relationship} = User.block(user, other_user2)
 
-    conn =
+    result =
       conn
       |> assign(:user, user)
       |> get("/api/v1/blocks")
+      |> json_response_and_validate_schema(200)
+
+    assert [id1, id2, id3] == Enum.map(result, & &1["id"])
+
+    result =
+      conn
+      |> assign(:user, user)
+      |> get("/api/v1/blocks?limit=1")
+      |> json_response_and_validate_schema(200)
+
+    assert [%{"id" => ^id1}] = result
+
+    result =
+      conn
+      |> assign(:user, user)
+      |> get("/api/v1/blocks?since_id=#{id1}")
+      |> json_response_and_validate_schema(200)
+
+    assert [%{"id" => ^id2}, %{"id" => ^id3}] = result
+
+    result =
+      conn
+      |> assign(:user, user)
+      |> get("/api/v1/blocks?since_id=#{id1}&max_id=#{id3}")
+      |> json_response_and_validate_schema(200)
+
+    assert [%{"id" => ^id2}] = result
+
+    result =
+      conn
+      |> assign(:user, user)
+      |> get("/api/v1/blocks?since_id=#{id1}&limit=1")
+      |> json_response_and_validate_schema(200)
 
-    other_user_id = to_string(other_user.id)
-    assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200)
+    assert [%{"id" => ^id2}] = result
   end
 end
index 3e21e6bf178ddab076fbb7f2ebbb40cefbb81be1..b23b22752013359765111d5d867310491f2e429b 100644 (file)
@@ -5,6 +5,7 @@
 defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
   use Pleroma.Web.ConnCase
 
+  alias Pleroma.Conversation.Participation
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI
 
@@ -28,10 +29,10 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
       user_three: user_three,
       conn: conn
     } do
-      assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
+      assert Participation.unread_count(user_two) == 0
       {:ok, direct} = create_direct_message(user_one, [user_two, user_three])
 
-      assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1
+      assert Participation.unread_count(user_two) == 1
 
       {:ok, _follower_only} =
         CommonAPI.post(user_one, %{
@@ -59,7 +60,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
       assert is_binary(res_id)
       assert unread == false
       assert res_last_status["id"] == direct.id
-      assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
+      assert Participation.unread_count(user_one) == 0
     end
 
     test "observes limit params", %{
@@ -134,8 +135,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
     user_two = insert(:user)
     {:ok, direct} = create_direct_message(user_one, [user_two])
 
-    assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
-    assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1
+    assert Participation.unread_count(user_one) == 0
+    assert Participation.unread_count(user_two) == 1
 
     user_two_conn =
       build_conn()
@@ -155,8 +156,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
       |> post("/api/v1/conversations/#{direct_conversation_id}/read")
       |> json_response_and_validate_schema(200)
 
-    assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
-    assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
+    assert Participation.unread_count(user_one) == 0
+    assert Participation.unread_count(user_two) == 0
 
     # The conversation is marked as unread on reply
     {:ok, _} =
@@ -171,8 +172,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
       |> get("/api/v1/conversations")
       |> json_response_and_validate_schema(200)
 
-    assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1
-    assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
+    assert Participation.unread_count(user_one) == 1
+    assert Participation.unread_count(user_two) == 0
 
     # A reply doesn't increment the user's unread_conversation_count if the conversation is unread
     {:ok, _} =
@@ -182,8 +183,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
         in_reply_to_status_id: direct.id
       })
 
-    assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1
-    assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
+    assert Participation.unread_count(user_one) == 1
+    assert Participation.unread_count(user_two) == 0
   end
 
   test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do
index e6d0b3e371fca65059ed83cfc1e4c19b6c839983..f2feeaaef1f111220cdf4e21157ee653a08bcc54 100644 (file)
@@ -121,7 +121,7 @@ defmodule Pleroma.Web.PleromaAPI.ConversationControllerTest do
     [participation2, participation1] = Participation.for_user(other_user)
     assert Participation.get(participation2.id).read == false
     assert Participation.get(participation1.id).read == false
-    assert User.get_cached_by_id(other_user.id).unread_conversation_count == 2
+    assert Participation.unread_count(other_user) == 2
 
     [%{"unread" => false}, %{"unread" => false}] =
       conn
@@ -131,6 +131,6 @@ defmodule Pleroma.Web.PleromaAPI.ConversationControllerTest do
     [participation2, participation1] = Participation.for_user(other_user)
     assert Participation.get(participation2.id).read == true
     assert Participation.get(participation1.id).read == true
-    assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0
+    assert Participation.unread_count(other_user) == 0
   end
 end