From 7b194873895f510b3e31b00643b4570ba04cb728 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 6 Dec 2018 20:06:50 +0300 Subject: [PATCH 1/7] [#394] Added `users.tags` and admin routes to tag and untag users. Added tests. --- lib/pleroma/user.ex | 43 ++++++++++++++ .../web/admin_api/admin_api_controller.ex | 12 ++++ lib/pleroma/web/controller_helper.ex | 9 +++ .../web/mastodon_api/views/account_view.ex | 4 +- lib/pleroma/web/router.ex | 2 + .../web/twitter_api/views/user_view.ex | 4 +- .../20181206125616_add_tags_to_users.exs | 11 ++++ .../admin_api/admin_api_controller_test.exs | 56 +++++++++++++++++++ 8 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 lib/pleroma/web/controller_helper.ex create mode 100644 priv/repo/migrations/20181206125616_add_tags_to_users.exs diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 74ae5ef0d..24bc80894 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -23,6 +23,7 @@ defmodule Pleroma.User do field(:local, :boolean, default: true) field(:follower_address, :string) field(:search_distance, :float, virtual: true) + field(:tags, {:array, :string}, default: []) field(:last_refreshed_at, :naive_datetime) has_many(:notifications, Notification) embeds_one(:info, Pleroma.User.Info) @@ -819,4 +820,46 @@ def parse_bio(bio, user \\ %User{info: %{source_data: %{}}}) do CommonUtils.format_input(bio, mentions, tags, "text/plain") |> Formatter.emojify(emoji) end + + def tag(user_identifiers, tags), do: tag_or_untag(user_identifiers, tags, :tag) + + def untag(user_identifiers, tags), do: tag_or_untag(user_identifiers, tags, :untag) + + defp tag_or_untag(user_identifier, tags, action) when not is_list(user_identifier), + do: tag_or_untag([user_identifier], tags, action) + + defp tag_or_untag([hd | _] = nicknames, tags, action) when is_binary(hd) do + users = Repo.all(from(u in User, where: u.nickname in ^nicknames)) + + if length(users) == length(nicknames) do + tag_or_untag(users, tags, action) + else + {:error, :not_found} + end + end + + defp tag_or_untag([hd | _] = users, tags, action) when is_map(hd) do + tags = + [tags] + |> List.flatten() + |> Enum.map(&String.downcase(&1)) + + Repo.transaction(fn -> + for user <- users do + new_tags = + if action == :tag do + Enum.uniq(user.tags ++ tags) + else + user.tags -- tags + end + + {:ok, updated_user} = + user + |> change(%{tags: new_tags}) + |> Repo.update() + + updated_user + end + end) + end end diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 2c67d9cda..0bd85e0b6 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -3,6 +3,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do alias Pleroma.{User, Repo} alias Pleroma.Web.ActivityPub.Relay + import Pleroma.Web.ControllerHelper, only: [json_response: 3] + require Logger action_fallback(:errors) @@ -40,6 +42,16 @@ def user_create( |> json(new_user.nickname) end + def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do + with {:ok, _} <- User.tag(nicknames, tags), + do: json_response(conn, :no_content, "") + end + + def untag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do + with {:ok, _} <- User.untag(nicknames, tags), + do: json_response(conn, :no_content, "") + end + def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname}) when permission_group in ["moderator", "admin"] do user = User.get_by_nickname(nickname) diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex new file mode 100644 index 000000000..ddf958811 --- /dev/null +++ b/lib/pleroma/web/controller_helper.ex @@ -0,0 +1,9 @@ +defmodule Pleroma.Web.ControllerHelper do + use Pleroma.Web, :controller + + def json_response(conn, status, json) do + conn + |> put_status(status) + |> json(json) + end +end diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index bcfa8836e..0add1b686 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -58,7 +58,9 @@ def render("account.json", %{user: user} = opts) do note: "", privacy: user_info.default_scope, sensitive: false - } + }, + # Note: Mastodon does not return this field: + tags: user.tags } end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index b7c79d2eb..ae942701e 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -98,6 +98,8 @@ defmodule Pleroma.Web.Router do pipe_through(:admin_api) delete("/user", AdminAPIController, :user_delete) post("/user", AdminAPIController, :user_create) + put("/users/tag", AdminAPIController, :tag_users) + put("/users/untag", AdminAPIController, :untag_users) get("/permission_group/:nickname", AdminAPIController, :right_get) get("/permission_group/:nickname/:permission_group", AdminAPIController, :right_get) diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index b78024ed7..dae656372 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -77,7 +77,9 @@ def render("user.json", %{user: user = %User{}} = assigns) do "locked" => user.info.locked, "default_scope" => user.info.default_scope, "no_rich_text" => user.info.no_rich_text, - "fields" => fields + "fields" => fields, + # Note: twitter.com does not return this field: + "tags" => user.tags } if assigns[:token] do diff --git a/priv/repo/migrations/20181206125616_add_tags_to_users.exs b/priv/repo/migrations/20181206125616_add_tags_to_users.exs new file mode 100644 index 000000000..1502f63b6 --- /dev/null +++ b/priv/repo/migrations/20181206125616_add_tags_to_users.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.AddTagsToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add :tags, {:array, :string} + end + + create index(:users, [:tags], using: :gin) + end +end diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 9634ad7c5..6c86ea143 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -37,6 +37,62 @@ test "Create" do end end + describe "/api/pleroma/admin//users/tag" do + setup do + admin = insert(:user, info: %{is_admin: true}) + user1 = insert(:user, %{tags: ["x"]}) + user2 = insert(:user, %{tags: ["y"]}) + user3 = insert(:user, %{tags: ["unchanged"]}) + + conn = + build_conn() + |> assign(:user, admin) + |> put_req_header("accept", "application/json") + |> put("/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=#{user2.nickname}&tags[]=foo&tags[]=bar") + + %{conn: conn, user1: user1, user2: user2, user3: user3} + end + + test "it appends specified tags to users with specified nicknames", %{conn: conn, user1: user1, user2: user2} do + assert json_response(conn, :no_content) + assert Repo.get(User, user1.id).tags == ["x", "foo", "bar"] + assert Repo.get(User, user2.id).tags == ["y", "foo", "bar"] + end + + test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do + assert json_response(conn, :no_content) + assert Repo.get(User, user3.id).tags == ["unchanged"] + end + end + + describe "/api/pleroma/admin//users/untag" do + setup do + admin = insert(:user, info: %{is_admin: true}) + user1 = insert(:user, %{tags: ["x"]}) + user2 = insert(:user, %{tags: ["y", "z"]}) + user3 = insert(:user, %{tags: ["unchanged"]}) + + conn = + build_conn() + |> assign(:user, admin) + |> put_req_header("accept", "application/json") + |> put("/api/pleroma/admin/users/untag?nicknames[]=#{user1.nickname}&nicknames[]=#{user2.nickname}&tags[]=x&tags[]=z") + + %{conn: conn, user1: user1, user2: user2, user3: user3} + end + + test "it removes specified tags from users with specified nicknames", %{conn: conn, user1: user1, user2: user2} do + assert json_response(conn, :no_content) + assert Repo.get(User, user1.id).tags == [] + assert Repo.get(User, user2.id).tags == ["y"] + end + + test "it does not modify tags of not specified users", %{conn: conn, user3: user3} do + assert json_response(conn, :no_content) + assert Repo.get(User, user3.id).tags == ["unchanged"] + end + end + describe "/api/pleroma/admin/permission_group" do test "GET is giving user_info" do admin = insert(:user, info: %{is_admin: true}) From 22830c8fc99669362e59e6632bc9072b64550cb7 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 6 Dec 2018 20:13:07 +0300 Subject: [PATCH 2/7] [#394] Formatting fix. --- .../admin_api/admin_api_controller_test.exs | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 6c86ea143..55aa7418b 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -48,12 +48,20 @@ test "Create" do build_conn() |> assign(:user, admin) |> put_req_header("accept", "application/json") - |> put("/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=#{user2.nickname}&tags[]=foo&tags[]=bar") + |> put( + "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=#{ + user2.nickname + }&tags[]=foo&tags[]=bar" + ) %{conn: conn, user1: user1, user2: user2, user3: user3} end - test "it appends specified tags to users with specified nicknames", %{conn: conn, user1: user1, user2: user2} do + test "it appends specified tags to users with specified nicknames", %{ + conn: conn, + user1: user1, + user2: user2 + } do assert json_response(conn, :no_content) assert Repo.get(User, user1.id).tags == ["x", "foo", "bar"] assert Repo.get(User, user2.id).tags == ["y", "foo", "bar"] @@ -76,12 +84,20 @@ test "it does not modify tags of not specified users", %{conn: conn, user3: user build_conn() |> assign(:user, admin) |> put_req_header("accept", "application/json") - |> put("/api/pleroma/admin/users/untag?nicknames[]=#{user1.nickname}&nicknames[]=#{user2.nickname}&tags[]=x&tags[]=z") + |> put( + "/api/pleroma/admin/users/untag?nicknames[]=#{user1.nickname}&nicknames[]=#{ + user2.nickname + }&tags[]=x&tags[]=z" + ) %{conn: conn, user1: user1, user2: user2, user3: user3} end - test "it removes specified tags from users with specified nicknames", %{conn: conn, user1: user1, user2: user2} do + test "it removes specified tags from users with specified nicknames", %{ + conn: conn, + user1: user1, + user2: user2 + } do assert json_response(conn, :no_content) assert Repo.get(User, user1.id).tags == [] assert Repo.get(User, user2.id).tags == ["y"] From 7bcb6a183a13cabd32bafd0e3ca20f8ca543565b Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 6 Dec 2018 20:23:16 +0300 Subject: [PATCH 3/7] [#394] Refactoring. --- lib/pleroma/user.ex | 11 +++++------ lib/pleroma/web/mastodon_api/views/account_view.ex | 2 +- lib/pleroma/web/twitter_api/views/user_view.ex | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 24bc80894..3984e610e 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -846,12 +846,7 @@ defp tag_or_untag([hd | _] = users, tags, action) when is_map(hd) do Repo.transaction(fn -> for user <- users do - new_tags = - if action == :tag do - Enum.uniq(user.tags ++ tags) - else - user.tags -- tags - end + new_tags = mutate_tags(user, tags, action) {:ok, updated_user} = user @@ -862,4 +857,8 @@ defp tag_or_untag([hd | _] = users, tags, action) when is_map(hd) do end end) end + + defp mutate_tags(user, tags, :tag), do: Enum.uniq(user.tags ++ tags) + + defp mutate_tags(user, tags, :untag), do: user.tags -- tags end diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 0add1b686..2762813ae 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -59,7 +59,7 @@ def render("account.json", %{user: user} = opts) do privacy: user_info.default_scope, sensitive: false }, - # Note: Mastodon does not return this field: + # Pleroma extension tags: user.tags } end diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index dae656372..f460ddd80 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -78,7 +78,7 @@ def render("user.json", %{user: user = %User{}} = assigns) do "default_scope" => user.info.default_scope, "no_rich_text" => user.info.no_rich_text, "fields" => fields, - # Note: twitter.com does not return this field: + # Pleroma extension "tags" => user.tags } From abbf347dc7de80d2c09d461fd39f9959f8b666b9 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 6 Dec 2018 20:38:52 +0300 Subject: [PATCH 4/7] [#394] View tests fix. --- test/web/mastodon_api/account_view_test.exs | 6 ++++-- test/web/twitter_api/views/user_view_test.exs | 12 ++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/test/web/mastodon_api/account_view_test.exs b/test/web/mastodon_api/account_view_test.exs index a2d3a2547..d6c9f58c8 100644 --- a/test/web/mastodon_api/account_view_test.exs +++ b/test/web/mastodon_api/account_view_test.exs @@ -54,7 +54,8 @@ test "Represent a user account" do note: "", privacy: "public", sensitive: false - } + }, + tags: [] } assert expected == AccountView.render("account.json", %{user: user}) @@ -91,7 +92,8 @@ test "Represent a Service(bot) account" do note: "", privacy: "public", sensitive: false - } + }, + tags: [] } assert expected == AccountView.render("account.json", %{user: user}) diff --git a/test/web/twitter_api/views/user_view_test.exs b/test/web/twitter_api/views/user_view_test.exs index e69ca24a9..0bd06f256 100644 --- a/test/web/twitter_api/views/user_view_test.exs +++ b/test/web/twitter_api/views/user_view_test.exs @@ -96,7 +96,8 @@ test "A user" do "locked" => false, "default_scope" => "public", "no_rich_text" => false, - "fields" => [] + "fields" => [], + "tags" => [] } assert represented == UserView.render("show.json", %{user: user}) @@ -137,7 +138,8 @@ test "A user for a given other follower", %{user: user} do "locked" => false, "default_scope" => "public", "no_rich_text" => false, - "fields" => [] + "fields" => [], + "tags" => [] } assert represented == UserView.render("show.json", %{user: user, for: follower}) @@ -179,7 +181,8 @@ test "A user that follows you", %{user: user} do "locked" => false, "default_scope" => "public", "no_rich_text" => false, - "fields" => [] + "fields" => [], + "tags" => [] } assert represented == UserView.render("show.json", %{user: follower, for: user}) @@ -228,7 +231,8 @@ test "A blocked user for the blocker" do "locked" => false, "default_scope" => "public", "no_rich_text" => false, - "fields" => [] + "fields" => [], + "tags" => [] } blocker = Repo.get(User, blocker.id) From 7a2162bbcb2e3a64ed6b56229311aa9fd487351a Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Thu, 6 Dec 2018 22:26:25 +0300 Subject: [PATCH 5/7] [#394] User view (Twitter & Mastadon API): wrapped "tags" in "pleroma" map. --- lib/pleroma/web/mastodon_api/views/account_view.ex | 5 ++++- lib/pleroma/web/twitter_api/views/user_view.ex | 5 ++++- test/web/mastodon_api/account_view_test.exs | 4 ++-- test/web/twitter_api/views/user_view_test.exs | 8 ++++---- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 2762813ae..ebcf9230b 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -59,8 +59,11 @@ def render("account.json", %{user: user} = opts) do privacy: user_info.default_scope, sensitive: false }, + # Pleroma extension - tags: user.tags + pleroma: %{ + tags: user.tags + } } end diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index f460ddd80..b3459af9a 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -78,8 +78,11 @@ def render("user.json", %{user: user = %User{}} = assigns) do "default_scope" => user.info.default_scope, "no_rich_text" => user.info.no_rich_text, "fields" => fields, + # Pleroma extension - "tags" => user.tags + "pleroma" => %{ + "tags" => user.tags + } } if assigns[:token] do diff --git a/test/web/mastodon_api/account_view_test.exs b/test/web/mastodon_api/account_view_test.exs index d6c9f58c8..3cb9b9c5b 100644 --- a/test/web/mastodon_api/account_view_test.exs +++ b/test/web/mastodon_api/account_view_test.exs @@ -55,7 +55,7 @@ test "Represent a user account" do privacy: "public", sensitive: false }, - tags: [] + pleroma: %{tags: []} } assert expected == AccountView.render("account.json", %{user: user}) @@ -93,7 +93,7 @@ test "Represent a Service(bot) account" do privacy: "public", sensitive: false }, - tags: [] + pleroma: %{tags: []} } assert expected == AccountView.render("account.json", %{user: user}) diff --git a/test/web/twitter_api/views/user_view_test.exs b/test/web/twitter_api/views/user_view_test.exs index 0bd06f256..9898c217d 100644 --- a/test/web/twitter_api/views/user_view_test.exs +++ b/test/web/twitter_api/views/user_view_test.exs @@ -97,7 +97,7 @@ test "A user" do "default_scope" => "public", "no_rich_text" => false, "fields" => [], - "tags" => [] + "pleroma" => %{"tags" => []} } assert represented == UserView.render("show.json", %{user: user}) @@ -139,7 +139,7 @@ test "A user for a given other follower", %{user: user} do "default_scope" => "public", "no_rich_text" => false, "fields" => [], - "tags" => [] + "pleroma" => %{"tags" => []} } assert represented == UserView.render("show.json", %{user: user, for: follower}) @@ -182,7 +182,7 @@ test "A user that follows you", %{user: user} do "default_scope" => "public", "no_rich_text" => false, "fields" => [], - "tags" => [] + "pleroma" => %{"tags" => []} } assert represented == UserView.render("show.json", %{user: follower, for: user}) @@ -232,7 +232,7 @@ test "A blocked user for the blocker" do "default_scope" => "public", "no_rich_text" => false, "fields" => [], - "tags" => [] + "pleroma" => %{"tags" => []} } blocker = Repo.get(User, blocker.id) From 6ed5044c4e1889a51a1dc6015b602759b83fc3b7 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Fri, 7 Dec 2018 11:04:39 +0300 Subject: [PATCH 6/7] [#394] Refactoring (using Ecto.Multi; "untag" route change). --- lib/pleroma/user.ex | 16 ++++++---------- lib/pleroma/web/router.ex | 2 +- test/web/admin_api/admin_api_controller_test.exs | 8 ++++---- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 3984e610e..511e6956e 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -2,6 +2,7 @@ defmodule Pleroma.User do use Ecto.Schema import Ecto.{Changeset, Query} + alias Ecto.Multi alias Pleroma.{Repo, User, Object, Web, Activity, Notification} alias Comeonin.Pbkdf2 alias Pleroma.Formatter @@ -844,18 +845,13 @@ defp tag_or_untag([hd | _] = users, tags, action) when is_map(hd) do |> List.flatten() |> Enum.map(&String.downcase(&1)) - Repo.transaction(fn -> - for user <- users do + multi = + Enum.reduce(users, Multi.new(), fn user, multi -> new_tags = mutate_tags(user, tags, action) + Multi.update(multi, {:user, user.id}, change(user, %{tags: new_tags})) + end) - {:ok, updated_user} = - user - |> change(%{tags: new_tags}) - |> Repo.update() - - updated_user - end - end) + Repo.transaction(multi) end defp mutate_tags(user, tags, :tag), do: Enum.uniq(user.tags ++ tags) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index ae942701e..a07607366 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -99,7 +99,7 @@ defmodule Pleroma.Web.Router do delete("/user", AdminAPIController, :user_delete) post("/user", AdminAPIController, :user_create) put("/users/tag", AdminAPIController, :tag_users) - put("/users/untag", AdminAPIController, :untag_users) + delete("/users/tag", AdminAPIController, :untag_users) get("/permission_group/:nickname", AdminAPIController, :right_get) get("/permission_group/:nickname/:permission_group", AdminAPIController, :right_get) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 55aa7418b..ba3b77fb6 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -37,7 +37,7 @@ test "Create" do end end - describe "/api/pleroma/admin//users/tag" do + describe "PUT /api/pleroma/admin/users/tag" do setup do admin = insert(:user, info: %{is_admin: true}) user1 = insert(:user, %{tags: ["x"]}) @@ -73,7 +73,7 @@ test "it does not modify tags of not specified users", %{conn: conn, user3: user end end - describe "/api/pleroma/admin//users/untag" do + describe "DELETE /api/pleroma/admin/users/tag" do setup do admin = insert(:user, info: %{is_admin: true}) user1 = insert(:user, %{tags: ["x"]}) @@ -84,8 +84,8 @@ test "it does not modify tags of not specified users", %{conn: conn, user3: user build_conn() |> assign(:user, admin) |> put_req_header("accept", "application/json") - |> put( - "/api/pleroma/admin/users/untag?nicknames[]=#{user1.nickname}&nicknames[]=#{ + |> delete( + "/api/pleroma/admin/users/tag?nicknames[]=#{user1.nickname}&nicknames[]=#{ user2.nickname }&tags[]=x&tags[]=z" ) From 1cea97df646566bfd6e31d9696047ba87f1e82c1 Mon Sep 17 00:00:00 2001 From: Ivan Tashkinov Date: Fri, 7 Dec 2018 12:27:32 +0300 Subject: [PATCH 7/7] [#394] Refactoring of User.tag and User.untag (removed User.tag_or_untag etc.) --- lib/pleroma/user.ex | 61 +++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 511e6956e..198f05f8a 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -822,39 +822,40 @@ def parse_bio(bio, user \\ %User{info: %{source_data: %{}}}) do CommonUtils.format_input(bio, mentions, tags, "text/plain") |> Formatter.emojify(emoji) end - def tag(user_identifiers, tags), do: tag_or_untag(user_identifiers, tags, :tag) - - def untag(user_identifiers, tags), do: tag_or_untag(user_identifiers, tags, :untag) - - defp tag_or_untag(user_identifier, tags, action) when not is_list(user_identifier), - do: tag_or_untag([user_identifier], tags, action) - - defp tag_or_untag([hd | _] = nicknames, tags, action) when is_binary(hd) do - users = Repo.all(from(u in User, where: u.nickname in ^nicknames)) - - if length(users) == length(nicknames) do - tag_or_untag(users, tags, action) - else - {:error, :not_found} - end + def tag(user_identifiers, tags) when is_list(user_identifiers) do + Repo.transaction(fn -> + for user_identifier <- user_identifiers, do: tag(user_identifier, tags) + end) end - defp tag_or_untag([hd | _] = users, tags, action) when is_map(hd) do - tags = - [tags] - |> List.flatten() - |> Enum.map(&String.downcase(&1)) - - multi = - Enum.reduce(users, Multi.new(), fn user, multi -> - new_tags = mutate_tags(user, tags, action) - Multi.update(multi, {:user, user.id}, change(user, %{tags: new_tags})) - end) - - Repo.transaction(multi) + def untag(user_identifiers, tags) when is_list(user_identifiers) do + Repo.transaction(fn -> + for user_identifier <- user_identifiers, do: untag(user_identifier, tags) + end) end - defp mutate_tags(user, tags, :tag), do: Enum.uniq(user.tags ++ tags) + def tag(nickname, tags) when is_binary(nickname), do: tag(User.get_by_nickname(nickname), tags) - defp mutate_tags(user, tags, :untag), do: user.tags -- tags + def untag(nickname, tags) when is_binary(nickname), + do: untag(User.get_by_nickname(nickname), tags) + + def tag(%User{} = user, tags), + do: update_tags(user, Enum.uniq(user.tags ++ normalize_tags(tags))) + + def untag(%User{} = user, tags), do: update_tags(user, user.tags -- normalize_tags(tags)) + + defp update_tags(%User{} = user, new_tags) do + {:ok, updated_user} = + user + |> change(%{tags: new_tags}) + |> Repo.update() + + updated_user + end + + defp normalize_tags(tags) do + [tags] + |> List.flatten() + |> Enum.map(&String.downcase(&1)) + end end