diff --git a/docs/Admin-API.md b/docs/Admin-API.md index ed19bc875..4403620bf 100644 --- a/docs/Admin-API.md +++ b/docs/Admin-API.md @@ -20,13 +20,14 @@ Authentication is required and the user must be an admin. ] ``` -## `/api/pleroma/admin/users/search?query={query}` +## `/api/pleroma/admin/users/search?query={query}&local={local}` -### Search users +### Search users by name or nickname - Method `GET` - Params: - - `query` + - `query`: **string** search term + - `local`: **bool** whether to return only local users - Response: ```JSON diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 52df171c5..af3ce705d 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -755,18 +755,25 @@ def get_recipients_from_activity(%Activity{recipients: to}) do Repo.all(query) end - def search(query, resolve \\ false, for_user \\ nil, limit \\ 20) do + def search(term, options \\ %{}) do # Strip the beginning @ off if there is a query - query = String.trim_leading(query, "@") + term = String.trim_leading(term, "@") + query = options[:query] || User - if resolve, do: get_or_fetch(query) + if options[:resolve], do: get_or_fetch(term) - fts_results = do_search(fts_search_subquery(query), for_user, %{limit: limit}) + fts_results = + do_search(fts_search_subquery(term, query), options[:for_user], %{ + limit: options[:limit] + }) {:ok, trigram_results} = Repo.transaction(fn -> Ecto.Adapters.SQL.query(Repo, "select set_limit(0.25)", []) - do_search(trigram_search_subquery(query), for_user, %{limit: limit}) + + do_search(trigram_search_subquery(term, query), options[:for_user], %{ + limit: options[:limit] + }) end) Enum.uniq_by(fts_results ++ trigram_results, & &1.id) @@ -809,9 +816,9 @@ defp do_search(subquery, for_user, options) do boost_search_results(results, for_user) end - defp fts_search_subquery(query) do + defp fts_search_subquery(term, query) do processed_query = - query + term |> String.replace(~r/\W+/, " ") |> String.trim() |> String.split() @@ -819,7 +826,7 @@ defp fts_search_subquery(query) do |> Enum.join(" | ") from( - u in User, + u in query, select_merge: %{ search_rank: fragment( @@ -849,19 +856,19 @@ defp fts_search_subquery(query) do ) end - defp trigram_search_subquery(query) do + defp trigram_search_subquery(term, query) do from( - u in User, + u in query, select_merge: %{ search_rank: fragment( "similarity(?, trim(? || ' ' || coalesce(?, '')))", - ^query, + ^term, u.nickname, u.name ) }, - where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^query) + where: fragment("trim(? || ' ' || coalesce(?, '')) % ?", u.nickname, u.name, ^term) ) end @@ -1018,6 +1025,14 @@ def unblock_domain(user, domain) do update_and_set_cache(cng) end + def maybe_local_user_query(local) when local == true do + local_user_query() + end + + def maybe_local_user_query(local) when local == false do + User + end + def local_user_query do from( u in User, diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 37159cd40..a8f9e5012 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -78,8 +78,14 @@ def list_users(%{assigns: %{user: admin}} = conn, %{"page" => page_string}) do ) end - def search_users(%{assigns: %{user: admin}} = conn, %{"query" => query}) do - users = User.search(query, true, admin, @users_page_size) + def search_users(%{assigns: %{user: admin}} = conn, %{"query" => term} = params) do + users = + User.search(term, + query: User.maybe_local_user_query(params["local"] == "true"), + resolve: true, + for_user: admin, + limit: @users_page_size + ) conn |> json( diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 12987442a..056be49b0 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -894,7 +894,7 @@ def status_search(user, query) do end def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do - accounts = User.search(query, params["resolve"] == "true", user) + accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user) statuses = status_search(user, query) @@ -919,7 +919,7 @@ def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do end def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do - accounts = User.search(query, params["resolve"] == "true", user) + accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user) statuses = status_search(user, query) @@ -941,7 +941,7 @@ def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do end def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do - accounts = User.search(query, params["resolve"] == "true", user) + accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user) res = AccountView.render("accounts.json", users: accounts, for: user, as: :user) diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 0d74c30c3..19264a93f 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -702,7 +702,7 @@ def search(%{assigns: %{user: user}} = conn, %{"q" => _query} = params) do end def search_user(%{assigns: %{user: user}} = conn, %{"query" => query}) do - users = User.search(query, true, user) + users = User.search(query, resolve: true, for_user: user) conn |> put_view(UserView) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 14625af32..460f2a6bd 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -388,25 +388,51 @@ test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do } end - test "GET /api/pleroma/admin/users/search" do - admin = insert(:user, info: %{is_admin: true}) - user = insert(:user, nickname: "bob") + describe "GET /api/pleroma/admin/users/search" do + test "regular search" do + admin = insert(:user, info: %{is_admin: true}) + user = insert(:user, nickname: "bob") - conn = - build_conn() - |> assign(:user, admin) - |> get("/api/pleroma/admin/users/search?query=bo") + conn = + build_conn() + |> assign(:user, admin) + |> get("/api/pleroma/admin/users/search?query=bo") - assert json_response(conn, 200) == %{ - "count" => 1, - "page_size" => 50, - "users" => [ - %{ - "deactivated" => user.info.deactivated, - "id" => user.id, - "nickname" => user.nickname - } - ] - } + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + %{ + "deactivated" => user.info.deactivated, + "id" => user.id, + "nickname" => user.nickname + } + ] + } + end + + test "only local users" do + admin = insert(:user, info: %{is_admin: true}) + user = insert(:user, nickname: "bob") + + insert(:user, nickname: "bobb", local: false) + + conn = + build_conn() + |> assign(:user, admin) + |> get("/api/pleroma/admin/users/search?query=bo&local=true") + + assert json_response(conn, 200) == %{ + "count" => 1, + "page_size" => 50, + "users" => [ + %{ + "deactivated" => user.info.deactivated, + "id" => user.id, + "nickname" => user.nickname + } + ] + } + end end end