From 25b9e7a8c39602e6463a867089948a7957cfab9f Mon Sep 17 00:00:00 2001 From: eugenijm Date: Tue, 19 Feb 2019 18:40:57 +0300 Subject: [PATCH 01/10] Added admin API for changing user activation status --- docs/Admin-API.md | 8 ++++ .../web/admin_api/admin_api_controller.ex | 7 +++ lib/pleroma/web/router.ex | 2 + .../admin_api/admin_api_controller_test.exs | 48 +++++++++++++++++++ 4 files changed, 65 insertions(+) diff --git a/docs/Admin-API.md b/docs/Admin-API.md index 3b19d1aa6..016444d58 100644 --- a/docs/Admin-API.md +++ b/docs/Admin-API.md @@ -66,6 +66,14 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret * On success: JSON of the ``user.info`` * Note: An admin cannot revoke their own admin status. +## `/api/pleroma/admin/activation_status/:nickname` + +### Active or deactivate a user +* Method: `PUT` +* Params: + * `nickname` + * `status` BOOLEAN field, false value means deactivation. + ## `/api/pleroma/admin/relay` ### Follow a Relay * Methods: `POST` diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index dc01f46f3..9ec50bb90 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -124,6 +124,13 @@ def right_delete(conn, _) do |> json(%{error: "No such permission_group"}) end + def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do + with {:ok, status} <- Ecto.Type.cast(:boolean, status), + %User{} = user <- User.get_by_nickname(nickname), + {:ok, _} <- User.deactivate(user, !status), + do: json_response(conn, :no_content, "") + end + def relay_follow(conn, %{"relay_url" => target}) do with {:ok, _message} <- Relay.follow(target) do json(conn, target) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 9a6cf2232..a4a382110 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -124,6 +124,8 @@ defmodule Pleroma.Web.Router do post("/permission_group/:nickname/:permission_group", AdminAPIController, :right_add) delete("/permission_group/:nickname/:permission_group", AdminAPIController, :right_delete) + put("/activation_status/:nickname", AdminAPIController, :set_activation_status) + post("/relay", AdminAPIController, :relay_follow) delete("/relay", AdminAPIController, :relay_unfollow) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index a27c26f95..9fbaaba39 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -159,6 +159,54 @@ test "/:right DELETE, can remove from a permission group" do end end + describe "PUT /api/pleroma/admin/activation_status" do + setup %{conn: conn} do + admin = insert(:user, info: %{is_admin: true}) + + conn = + conn + |> assign(:user, admin) + |> put_req_header("accept", "application/json") + + %{conn: conn} + end + + test "deactivates the user", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> put("/api/pleroma/admin/activation_status/#{user.nickname}", %{status: false}) + + user = Repo.get(User, user.id) + assert user.info.deactivated == true + assert json_response(conn, :no_content) + end + + test "activates the user", %{conn: conn} do + user = insert(:user, info: %{deactivated: true}) + + conn = + conn + |> put("/api/pleroma/admin/activation_status/#{user.nickname}", %{status: true}) + + user = Repo.get(User, user.id) + assert user.info.deactivated == false + assert json_response(conn, :no_content) + end + + test "returns 403 when requested by a non-admin", %{conn: conn} do + user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> put("/api/pleroma/admin/activation_status/#{user.nickname}", %{status: false}) + + assert json_response(conn, :forbidden) + end + end + describe "POST /api/pleroma/admin/email_invite, with valid config" do setup do registrations_open = Pleroma.Config.get([:instance, :registrations_open]) From f41f017bbca232c2ab3cd41ad23ef95d5ad13e36 Mon Sep 17 00:00:00 2001 From: Ekaterina Vaartis Date: Wed, 5 Sep 2018 23:49:15 +0300 Subject: [PATCH 02/10] Implement muting, add it to the mastodon API --- lib/pleroma/user.ex | 24 +++++++++++++++++++ lib/pleroma/web/activity_pub/activity_pub.ex | 13 ++++++++++ .../mastodon_api/mastodon_api_controller.ex | 18 ++++++++++++++ .../web/mastodon_api/views/account_view.ex | 1 + lib/pleroma/web/router.ex | 4 ++-- 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 18bb56667..9a774e7b7 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -888,6 +888,28 @@ def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_i ) end + def mute(muter, %User{ap_id: ap_id} = muted) do + if following?(muter, muter) do + unfollow(muter, muter) + end + + mutes = muter.info["mutes"] || [] + new_mutes = Enum.uniq([ap_id | mutes]) + new_info = Map.put(muter.info, "mutes", new_mutes) + + cs = User.info_changeset(muter, %{info: new_info}) + update_and_set_cache(cs) + end + + def unmute(user, %{ap_id: ap_id}) do + mutes = user.info["mutes"] || [] + new_mutes = List.delete(mutes, ap_id) + new_info = Map.put(user.info, "mutes", new_mutes) + + cs = User.info_changeset(user, %{info: new_info}) + update_and_set_cache(cs) + end + def block(blocker, %User{ap_id: ap_id} = blocked) do # sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213) blocker = @@ -930,6 +952,8 @@ def unblock(blocker, %{ap_id: ap_id}) do update_and_set_cache(cng) end + def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info["mutes"] || [], ap_id) + def blocks?(user, %{ap_id: ap_id}) do blocks = user.info.blocks domain_blocks = user.info.domain_blocks diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 8d3116839..36e5e23bf 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -576,6 +576,18 @@ defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or defp restrict_reblogs(query, _), do: query + defp restrict_muted(query, %{"muting_user" => %User{info: info}}) do + mutes = info["mutes"] || [] + + from( + activity in query, + where: fragment("not (? = ANY(?))", activity.actor, ^mutes), + where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes) + ) + end + + defp restrict_muted(query, _), do: query + defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do blocks = info.blocks || [] domain_blocks = info.domain_blocks || [] @@ -629,6 +641,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do |> restrict_type(opts) |> restrict_favorited_by(opts) |> restrict_blocked(opts) + |> restrict_muted(opts) |> restrict_media(opts) |> restrict_visibility(opts) |> restrict_replies(opts) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index e2715bd08..af16264ee 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -232,6 +232,7 @@ def home_timeline(%{assigns: %{user: user}} = conn, params) do params |> Map.put("type", ["Create", "Announce"]) |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) |> Map.put("user", user) activities = @@ -254,6 +255,7 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do |> Map.put("type", ["Create", "Announce"]) |> Map.put("local_only", local_only) |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) |> ActivityPub.fetch_public_activities() |> Enum.reverse() @@ -620,6 +622,7 @@ def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do |> Map.put("type", "Create") |> Map.put("local_only", local_only) |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) |> Map.put("tag", tags) |> Map.put("tag_all", tag_all) |> Map.put("tag_reject", tag_reject) @@ -763,6 +766,20 @@ def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do end end + def mute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do + with %User{} = muted <- Repo.get(User, id), + {:ok, muter} <- User.mute(muter, muted) do + render(conn, AccountView, "relationship.json", %{user: muter, target: muted}) + end + end + + def unmute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do + with %User{} = muted <- Repo.get(User, id), + {:ok, muter} <- User.unmute(muter, muted) do + render(conn, AccountView, "relationship.json", %{user: muter, target: muted}) + end + end + def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do with %User{} = blocked <- Repo.get(User, id), {:ok, blocker} <- User.block(blocker, blocked), @@ -1018,6 +1035,7 @@ def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) params |> Map.put("type", "Create") |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) # we must filter the following list for the user to avoid leaking statuses the user # does not actually have permission to see (for more info, peruse security issue #270). diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 9df9f14b2..91b3e034f 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -47,6 +47,7 @@ def render("relationship.json", %{user: user, target: target}) do following: User.following?(user, target), followed_by: User.following?(target, user), blocking: User.blocks?(user, target), + muting: User.mutes?(user, target), muting: false, muting_notifications: false, requested: requested, diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 9a6cf2232..fb7a8d448 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -166,8 +166,8 @@ defmodule Pleroma.Web.Router do post("/accounts/:id/unfollow", MastodonAPIController, :unfollow) post("/accounts/:id/block", MastodonAPIController, :block) post("/accounts/:id/unblock", MastodonAPIController, :unblock) - post("/accounts/:id/mute", MastodonAPIController, :relationship_noop) - post("/accounts/:id/unmute", MastodonAPIController, :relationship_noop) + post("/accounts/:id/mute", MastodonAPIController, :mute) + post("/accounts/:id/unmute", MastodonAPIController, :unmute) get("/accounts/:id/lists", MastodonAPIController, :account_lists) get("/follow_requests", MastodonAPIController, :follow_requests) From da64ea4a5540bf21770de039e27a33b5ef9fb377 Mon Sep 17 00:00:00 2001 From: Ekaterina Vaartis Date: Sun, 2 Sep 2018 00:33:13 +0300 Subject: [PATCH 03/10] Implement mastodon mutes endpoint Aparently i forgot to add it, it gets a list of muted users --- lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 9 +++++++++ lib/pleroma/web/router.ex | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index af16264ee..49b49be19 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -780,6 +780,15 @@ def unmute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do end end + # TODO: Use proper query + def mutes(%{assigns: %{user: user}} = conn, _) do + with muted_users <- user.info["mutes"] || [], + accounts <- Enum.map(muted_users, fn ap_id -> User.get_cached_by_ap_id(ap_id) end) do + res = AccountView.render("accounts.json", users: accounts, for: user, as: :user) + json(conn, res) + end + end + def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do with %User{} = blocked <- Repo.get(User, id), {:ok, blocker} <- User.block(blocker, blocked), diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index fb7a8d448..1b62d02ea 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -178,7 +178,7 @@ defmodule Pleroma.Web.Router do get("/blocks", MastodonAPIController, :blocks) - get("/mutes", MastodonAPIController, :empty_array) + get("/mutes", MastodonAPIController, :mutes) get("/timelines/home", MastodonAPIController, :home_timeline) From ad2cf4fd86b811a9d2a4c152bcaaa0f0b8e25341 Mon Sep 17 00:00:00 2001 From: Ekaterina Vaartis Date: Sun, 2 Sep 2018 00:34:15 +0300 Subject: [PATCH 04/10] Add test for mastodon muting endpoints --- .../mastodon_api_controller_test.exs | 56 +++++++++++++------ 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index e43bc4508..e804ae203 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -1206,6 +1206,42 @@ test "following / unfollowing a user", %{conn: conn} do assert id == to_string(other_user.id) end + test "muting / unmuting a user", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + conn = + conn + |> assign(:user, user) + |> post("/api/v1/accounts/#{other_user.id}/mute") + + assert %{"id" => _id, "muting" => true} = json_response(conn, 200) + + user = Repo.get(User, user.id) + + conn = + build_conn() + |> assign(:user, user) + |> post("/api/v1/accounts/#{other_user.id}/unmute") + + assert %{"id" => _id, "muting" => false} = json_response(conn, 200) + end + + test "getting a list of mutes", %{conn: conn} do + user = insert(:user) + other_user = insert(:user) + + {:ok, user} = User.mute(user, other_user) + + conn = + conn + |> assign(:user, user) + |> get("/api/v1/mutes") + + other_user_id = to_string(other_user.id) + assert [%{"id" => ^other_user_id}] = json_response(conn, 200) + end + test "blocking / unblocking a user", %{conn: conn} do user = insert(:user) other_user = insert(:user) @@ -1282,26 +1318,10 @@ test "getting a list of domain blocks", %{conn: conn} do assert "even.worse.site" in domain_blocks end - test "unimplemented mute endpoints" do - user = insert(:user) - other_user = insert(:user) - - ["mute", "unmute"] - |> Enum.each(fn endpoint -> - conn = - build_conn() - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/#{endpoint}") - - assert %{"id" => id} = json_response(conn, 200) - assert id == to_string(other_user.id) - end) - end - - test "unimplemented mutes, follow_requests, blocks, domain blocks" do + test "unimplemented follow_requests, blocks, domain blocks" do user = insert(:user) - ["blocks", "domain_blocks", "mutes", "follow_requests"] + ["blocks", "domain_blocks", "follow_requests"] |> Enum.each(fn endpoint -> conn = build_conn() From 092b1b145335d530d7343116eb452e37cc112a9e Mon Sep 17 00:00:00 2001 From: Ekaterina Vaartis Date: Tue, 4 Sep 2018 00:17:25 +0300 Subject: [PATCH 05/10] Do not unfollow muted users --- lib/pleroma/user.ex | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 9a774e7b7..da25a91c8 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -889,10 +889,6 @@ def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_i end def mute(muter, %User{ap_id: ap_id} = muted) do - if following?(muter, muter) do - unfollow(muter, muter) - end - mutes = muter.info["mutes"] || [] new_mutes = Enum.uniq([ap_id | mutes]) new_info = Map.put(muter.info, "mutes", new_mutes) From 465b547c905b4faa26ec1cf5f0175c55430e8041 Mon Sep 17 00:00:00 2001 From: Ekaterina Vaartis Date: Tue, 4 Sep 2018 00:20:22 +0300 Subject: [PATCH 06/10] Remove unused "muted" parameter, use piping for mute/block fns --- lib/pleroma/user.ex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index da25a91c8..cfb44ebaa 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -888,13 +888,13 @@ def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_i ) end - def mute(muter, %User{ap_id: ap_id} = muted) do + def mute(muter, %User{ap_id: ap_id}) do mutes = muter.info["mutes"] || [] new_mutes = Enum.uniq([ap_id | mutes]) new_info = Map.put(muter.info, "mutes", new_mutes) - cs = User.info_changeset(muter, %{info: new_info}) - update_and_set_cache(cs) + User.info_changeset(muter, %{info: new_info}) + |> update_and_set_cache() end def unmute(user, %{ap_id: ap_id}) do @@ -902,8 +902,8 @@ def unmute(user, %{ap_id: ap_id}) do new_mutes = List.delete(mutes, ap_id) new_info = Map.put(user.info, "mutes", new_mutes) - cs = User.info_changeset(user, %{info: new_info}) - update_and_set_cache(cs) + User.info_changeset(user, %{info: new_info}) + |> update_and_set_cache() end def block(blocker, %User{ap_id: ap_id} = blocked) do From 5a46d37af9fb1914d795cb90d28356efcd0790d5 Mon Sep 17 00:00:00 2001 From: Ekaterina Vaartis Date: Tue, 19 Feb 2019 23:09:16 +0300 Subject: [PATCH 07/10] Update the mute implementation to the current codebase Make it part of the info thing (and do a migration to ensure it's there) --- lib/pleroma/user.ex | 33 ++++++++++++------- lib/pleroma/user/info.ex | 17 ++++++++++ lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- .../mastodon_api/mastodon_api_controller.ex | 24 ++++++++++---- .../web/mastodon_api/views/account_view.ex | 1 - .../20190219192317_create_user_mutes.exs | 7 ++++ 6 files changed, 64 insertions(+), 20 deletions(-) create mode 100644 priv/repo/migrations/20190219192317_create_user_mutes.exs diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index cfb44ebaa..35ba4ad99 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -889,21 +889,27 @@ def blocks_import(%User{} = blocker, blocked_identifiers) when is_list(blocked_i end def mute(muter, %User{ap_id: ap_id}) do - mutes = muter.info["mutes"] || [] - new_mutes = Enum.uniq([ap_id | mutes]) - new_info = Map.put(muter.info, "mutes", new_mutes) + info_cng = + muter.info + |> User.Info.add_to_mutes(ap_id) - User.info_changeset(muter, %{info: new_info}) - |> update_and_set_cache() + cng = + change(muter) + |> put_embed(:info, info_cng) + + update_and_set_cache(cng) end - def unmute(user, %{ap_id: ap_id}) do - mutes = user.info["mutes"] || [] - new_mutes = List.delete(mutes, ap_id) - new_info = Map.put(user.info, "mutes", new_mutes) + def unmute(muter, %{ap_id: ap_id}) do + info_cng = + muter.info + |> User.Info.remove_from_mutes(ap_id) - User.info_changeset(user, %{info: new_info}) - |> update_and_set_cache() + cng = + change(muter) + |> put_embed(:info, info_cng) + + update_and_set_cache(cng) end def block(blocker, %User{ap_id: ap_id} = blocked) do @@ -948,7 +954,7 @@ def unblock(blocker, %{ap_id: ap_id}) do update_and_set_cache(cng) end - def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info["mutes"] || [], ap_id) + def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.info.mutes, ap_id) def blocks?(user, %{ap_id: ap_id}) do blocks = user.info.blocks @@ -961,6 +967,9 @@ def blocks?(user, %{ap_id: ap_id}) do end) end + def muted_users(user), + do: Repo.all(from(u in User, where: u.ap_id in ^user.info.mutes)) + def blocked_users(user), do: Repo.all(from(u in User, where: u.ap_id in ^user.info.blocks)) diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 9099d7fbb..00a0f6df3 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -19,6 +19,7 @@ defmodule Pleroma.User.Info do field(:default_scope, :string, default: "public") field(:blocks, {:array, :string}, default: []) field(:domain_blocks, {:array, :string}, default: []) + field(:mutes, {:array, :string}, default: []) field(:deactivated, :boolean, default: false) field(:no_rich_text, :boolean, default: false) field(:ap_enabled, :boolean, default: false) @@ -74,6 +75,14 @@ def set_follower_count(info, number) do |> validate_required([:follower_count]) end + def set_mutes(info, mutes) do + params = %{mutes: mutes} + + info + |> cast(params, [:mutes]) + |> validate_required([:mutes]) + end + def set_blocks(info, blocks) do params = %{blocks: blocks} @@ -82,6 +91,14 @@ def set_blocks(info, blocks) do |> validate_required([:blocks]) end + def add_to_mutes(info, muted) do + set_mutes(info, Enum.uniq([muted | info.mutes])) + end + + def remove_from_mutes(info, muted) do + set_mutes(info, List.delete(info.mutes, muted)) + end + def add_to_block(info, blocked) do set_blocks(info, Enum.uniq([blocked | info.blocks])) end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 36e5e23bf..cb8a2139e 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -577,7 +577,7 @@ defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or defp restrict_reblogs(query, _), do: query defp restrict_muted(query, %{"muting_user" => %User{info: info}}) do - mutes = info["mutes"] || [] + mutes = info.mutes from( activity in query, diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 49b49be19..3a343f3d8 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -769,22 +769,34 @@ def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do def mute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do with %User{} = muted <- Repo.get(User, id), {:ok, muter} <- User.mute(muter, muted) do - render(conn, AccountView, "relationship.json", %{user: muter, target: muted}) + conn + |> put_view(AccountView) + |> render("relationship.json", %{user: muter, target: muted}) + else + {:error, message} -> + conn + |> put_resp_content_type("application/json") + |> send_resp(403, Jason.encode!(%{"error" => message})) end end def unmute(%{assigns: %{user: muter}} = conn, %{"id" => id}) do with %User{} = muted <- Repo.get(User, id), {:ok, muter} <- User.unmute(muter, muted) do - render(conn, AccountView, "relationship.json", %{user: muter, target: muted}) + conn + |> put_view(AccountView) + |> render("relationship.json", %{user: muter, target: muted}) + else + {:error, message} -> + conn + |> put_resp_content_type("application/json") + |> send_resp(403, Jason.encode!(%{"error" => message})) end end - # TODO: Use proper query def mutes(%{assigns: %{user: user}} = conn, _) do - with muted_users <- user.info["mutes"] || [], - accounts <- Enum.map(muted_users, fn ap_id -> User.get_cached_by_ap_id(ap_id) end) do - res = AccountView.render("accounts.json", users: accounts, for: user, as: :user) + with muted_accounts <- User.muted_users(user) do + res = AccountView.render("accounts.json", users: muted_accounts, for: user, as: :user) json(conn, res) 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 91b3e034f..8fdefdebd 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -48,7 +48,6 @@ def render("relationship.json", %{user: user, target: target}) do followed_by: User.following?(target, user), blocking: User.blocks?(user, target), muting: User.mutes?(user, target), - muting: false, muting_notifications: false, requested: requested, domain_blocking: false, diff --git a/priv/repo/migrations/20190219192317_create_user_mutes.exs b/priv/repo/migrations/20190219192317_create_user_mutes.exs new file mode 100644 index 000000000..304074567 --- /dev/null +++ b/priv/repo/migrations/20190219192317_create_user_mutes.exs @@ -0,0 +1,7 @@ +defmodule Pleroma.Repo.Migrations.CreateUserMutes do + use Ecto.Migration + + def change do + execute "UPDATE users SET info = jsonb_set(info, '{mutes}', '[]'::jsonb);" + end +end From 840d2c9f0bcc493703d12409bc64d2931b9ee3f5 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 20 Feb 2019 13:47:24 +0100 Subject: [PATCH 08/10] Remove superfluous migration. --- priv/repo/migrations/20190219192317_create_user_mutes.exs | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 priv/repo/migrations/20190219192317_create_user_mutes.exs diff --git a/priv/repo/migrations/20190219192317_create_user_mutes.exs b/priv/repo/migrations/20190219192317_create_user_mutes.exs deleted file mode 100644 index 304074567..000000000 --- a/priv/repo/migrations/20190219192317_create_user_mutes.exs +++ /dev/null @@ -1,7 +0,0 @@ -defmodule Pleroma.Repo.Migrations.CreateUserMutes do - use Ecto.Migration - - def change do - execute "UPDATE users SET info = jsonb_set(info, '{mutes}', '[]'::jsonb);" - end -end From 9ae79bb71a2f0e9f88275f350126f1b1ba02c734 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 20 Feb 2019 13:47:44 +0100 Subject: [PATCH 09/10] Add test for muting functionality. --- test/web/activity_pub/activity_pub_test.exs | 42 +++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index a6f8b822a..33ed17434 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -277,6 +277,48 @@ test "doesn't return blocked activities" do assert Enum.member?(activities, activity_one) end + test "doesn't return muted activities" do + activity_one = insert(:note_activity) + activity_two = insert(:note_activity) + activity_three = insert(:note_activity) + user = insert(:user) + booster = insert(:user) + {:ok, user} = User.mute(user, %User{ap_id: activity_one.data["actor"]}) + + activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) + + assert Enum.member?(activities, activity_two) + assert Enum.member?(activities, activity_three) + refute Enum.member?(activities, activity_one) + + {:ok, user} = User.unmute(user, %User{ap_id: activity_one.data["actor"]}) + + activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) + + assert Enum.member?(activities, activity_two) + assert Enum.member?(activities, activity_three) + assert Enum.member?(activities, activity_one) + + {:ok, user} = User.mute(user, %User{ap_id: activity_three.data["actor"]}) + {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.repeat(activity_three.id, booster) + %Activity{} = boost_activity = Activity.get_create_by_object_ap_id(id) + activity_three = Repo.get(Activity, activity_three.id) + + activities = ActivityPub.fetch_activities([], %{"muting_user" => user}) + + assert Enum.member?(activities, activity_two) + refute Enum.member?(activities, activity_three) + refute Enum.member?(activities, boost_activity) + assert Enum.member?(activities, activity_one) + + activities = ActivityPub.fetch_activities([], %{"muting_user" => nil}) + + assert Enum.member?(activities, activity_two) + assert Enum.member?(activities, activity_three) + assert Enum.member?(activities, boost_activity) + assert Enum.member?(activities, activity_one) + end + test "excludes reblogs on request" do user = insert(:user) {:ok, expected_activity} = ActivityBuilder.insert(%{"type" => "Create"}, %{:user => user}) From 4196d9af111893e186bfedd8a03994cd02cf87a2 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 20 Feb 2019 14:14:52 +0100 Subject: [PATCH 10/10] Add test for User.mutes and so on. --- test/user_test.exs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/user_test.exs b/test/user_test.exs index 92991d063..0b1c39ecf 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -594,6 +594,29 @@ test "it imports user followings from list" do end end + describe "mutes" do + test "it mutes people" do + user = insert(:user) + muted_user = insert(:user) + + refute User.mutes?(user, muted_user) + + {:ok, user} = User.mute(user, muted_user) + + assert User.mutes?(user, muted_user) + end + + test "it unmutes users" do + user = insert(:user) + muted_user = insert(:user) + + {:ok, user} = User.mute(user, muted_user) + {:ok, user} = User.unmute(user, muted_user) + + refute User.mutes?(user, muted_user) + end + end + describe "blocks" do test "it blocks people" do user = insert(:user)