From f4ff4ffba2761925dde59ff1989dfdb232732dd7 Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Tue, 5 Feb 2019 13:35:24 +0100 Subject: [PATCH 01/25] Migration and some boilerplate stuff --- .../mastodon_api/mastodon_api_controller.ex | 16 ++++++++++++ lib/pleroma/web/router.ex | 2 ++ lib/pleroma/web/thread_mute.ex | 26 +++++++++++++++++++ .../20190205114625_create_thread_mutes.exs | 12 +++++++++ 4 files changed, 56 insertions(+) create mode 100644 lib/pleroma/web/thread_mute.ex create mode 100644 priv/repo/migrations/20190205114625_create_thread_mutes.exs diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 7f3fbff4a..00b39d76b 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -445,6 +445,22 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end + def mute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do + with {:ok, activity} <- Pleroma.Web.ThreadMute.add_mute(user, id) do + conn + |> put_view(StatusView) + |> try_render("status.json", %{activity: activity, for: user, as: :activity}) + end + end + + def unmute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do + with {:ok, activity} <- Pleroma.Web.ThreadMute.remove_mute(user, id) do + conn + |> put_view(StatusView) + |> try_render("status.json", %{activity: activity, for: user, as: :activity}) + end + end + def notifications(%{assigns: %{user: user}} = conn, params) do notifications = Notification.for_user(user, params) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index c6b4d37ab..3f9759ca9 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -198,6 +198,8 @@ defmodule Pleroma.Web.Router do post("/statuses/:id/unpin", MastodonAPIController, :unpin_status) post("/statuses/:id/bookmark", MastodonAPIController, :bookmark_status) post("/statuses/:id/unbookmark", MastodonAPIController, :unbookmark_status) + post("/statuses/:id/mute", MastodonAPIController, :mute_conversation) + post("/statuses/:id/unmute", MastodonAPIController, :unmute_conversation) post("/notifications/clear", MastodonAPIController, :clear_notifications) post("/notifications/dismiss", MastodonAPIController, :dismiss_notification) diff --git a/lib/pleroma/web/thread_mute.ex b/lib/pleroma/web/thread_mute.ex new file mode 100644 index 000000000..8892155cd --- /dev/null +++ b/lib/pleroma/web/thread_mute.ex @@ -0,0 +1,26 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ThreadMute do + use Ecto.Schema + + alias Pleroma.{Activity, Notification, User} + + schema "thread_mutes" do + field(:user_id, :string) + field(:context, :string) + end + + def add_mute(user, id) do + %{id: user_id} = user + %{data: %{"context" => context}} = Activity.get_by_id(id) + Pleroma.Repo.insert(%Pleroma.Web.ThreadMute{user_id: user_id, context: context}) + end + + def remove_mute(user, id) do + end + + def mute_thread() do + end +end diff --git a/priv/repo/migrations/20190205114625_create_thread_mutes.exs b/priv/repo/migrations/20190205114625_create_thread_mutes.exs new file mode 100644 index 000000000..b16f557f0 --- /dev/null +++ b/priv/repo/migrations/20190205114625_create_thread_mutes.exs @@ -0,0 +1,12 @@ +defmodule Pleroma.Repo.Migrations.CreateThreadMutes do + use Ecto.Migration + + def change do + create table(:thread_mutes) do + add :user_id, references(:users, type: :uuid, on_delete: :delete_all) + add :context, :string + end + + create index(:thread_mutes, [:user_id]) + end +end From 77448de4925f3793bde201a49d204f881163b1b8 Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Thu, 7 Feb 2019 22:25:07 +0100 Subject: [PATCH 02/25] ugghhhh --- lib/pleroma/web/thread_mute.ex | 6 +++--- priv/repo/migrations/20190205114625_create_thread_mutes.exs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/thread_mute.ex b/lib/pleroma/web/thread_mute.ex index 8892155cd..50a6219d1 100644 --- a/lib/pleroma/web/thread_mute.ex +++ b/lib/pleroma/web/thread_mute.ex @@ -8,14 +8,14 @@ defmodule Pleroma.Web.ThreadMute do alias Pleroma.{Activity, Notification, User} schema "thread_mutes" do - field(:user_id, :string) + belongs_to(:user, User, type: Pleroma.FlakeId) field(:context, :string) end def add_mute(user, id) do - %{id: user_id} = user + user_id = user.id %{data: %{"context" => context}} = Activity.get_by_id(id) - Pleroma.Repo.insert(%Pleroma.Web.ThreadMute{user_id: user_id, context: context}) + Pleroma.Repo.insert(%Pleroma.Web.ThreadMute{user: user_id, context: context}) end def remove_mute(user, id) do diff --git a/priv/repo/migrations/20190205114625_create_thread_mutes.exs b/priv/repo/migrations/20190205114625_create_thread_mutes.exs index b16f557f0..16083332b 100644 --- a/priv/repo/migrations/20190205114625_create_thread_mutes.exs +++ b/priv/repo/migrations/20190205114625_create_thread_mutes.exs @@ -3,10 +3,10 @@ defmodule Pleroma.Repo.Migrations.CreateThreadMutes do def change do create table(:thread_mutes) do - add :user_id, references(:users, type: :uuid, on_delete: :delete_all) + add :user, references(:users, type: :uuid, on_delete: :delete_all) add :context, :string end - create index(:thread_mutes, [:user_id]) + create index(:thread_mutes, [:user]) end end From 6b5032e10eb64c466e320f870b3cdad611b100d5 Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Thu, 7 Feb 2019 22:38:54 +0100 Subject: [PATCH 03/25] made a silly oopsie --- lib/pleroma/web/thread_mute.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/thread_mute.ex b/lib/pleroma/web/thread_mute.ex index 50a6219d1..f2283cf86 100644 --- a/lib/pleroma/web/thread_mute.ex +++ b/lib/pleroma/web/thread_mute.ex @@ -15,7 +15,7 @@ defmodule Pleroma.Web.ThreadMute do def add_mute(user, id) do user_id = user.id %{data: %{"context" => context}} = Activity.get_by_id(id) - Pleroma.Repo.insert(%Pleroma.Web.ThreadMute{user: user_id, context: context}) + Pleroma.Repo.insert(%Pleroma.Web.ThreadMute{}, %{user: user_id, context: context}) end def remove_mute(user, id) do From 7e3ec93ed0047063c54a9e2a31be803e7ec4780f Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Thu, 7 Feb 2019 22:38:54 +0100 Subject: [PATCH 04/25] made a silly oopsie --- lib/pleroma/web/thread_mute.ex | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/thread_mute.ex b/lib/pleroma/web/thread_mute.ex index 50a6219d1..333b26246 100644 --- a/lib/pleroma/web/thread_mute.ex +++ b/lib/pleroma/web/thread_mute.ex @@ -5,7 +5,7 @@ defmodule Pleroma.Web.ThreadMute do use Ecto.Schema - alias Pleroma.{Activity, Notification, User} + alias Pleroma.{Activity, Notification, User, Repo} schema "thread_mutes" do belongs_to(:user, User, type: Pleroma.FlakeId) @@ -13,9 +13,8 @@ defmodule Pleroma.Web.ThreadMute do end def add_mute(user, id) do - user_id = user.id %{data: %{"context" => context}} = Activity.get_by_id(id) - Pleroma.Repo.insert(%Pleroma.Web.ThreadMute{user: user_id, context: context}) + Repo.insert(%Pleroma.Web.ThreadMute{}, %{user_id: user.id, context: context}) end def remove_mute(user, id) do From c43f414a79ff9b276b8162ac1ab10e84651e881d Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Thu, 7 Feb 2019 23:44:30 +0100 Subject: [PATCH 05/25] Somehow fixed the repo insert [skip-ci] --- lib/pleroma/web/thread_mute.ex | 3 ++- priv/repo/migrations/20190205114625_create_thread_mutes.exs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/web/thread_mute.ex b/lib/pleroma/web/thread_mute.ex index 333b26246..b37dda58b 100644 --- a/lib/pleroma/web/thread_mute.ex +++ b/lib/pleroma/web/thread_mute.ex @@ -14,7 +14,8 @@ defmodule Pleroma.Web.ThreadMute do def add_mute(user, id) do %{data: %{"context" => context}} = Activity.get_by_id(id) - Repo.insert(%Pleroma.Web.ThreadMute{}, %{user_id: user.id, context: context}) + mute = %Pleroma.Web.ThreadMute{user_id: user.id, context: context} + Repo.insert(mute) end def remove_mute(user, id) do diff --git a/priv/repo/migrations/20190205114625_create_thread_mutes.exs b/priv/repo/migrations/20190205114625_create_thread_mutes.exs index 16083332b..b16f557f0 100644 --- a/priv/repo/migrations/20190205114625_create_thread_mutes.exs +++ b/priv/repo/migrations/20190205114625_create_thread_mutes.exs @@ -3,10 +3,10 @@ defmodule Pleroma.Repo.Migrations.CreateThreadMutes do def change do create table(:thread_mutes) do - add :user, references(:users, type: :uuid, on_delete: :delete_all) + add :user_id, references(:users, type: :uuid, on_delete: :delete_all) add :context, :string end - create index(:thread_mutes, [:user]) + create index(:thread_mutes, [:user_id]) end end From a44e532fb1be973d6974aa9e357096764252796d Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Fri, 8 Feb 2019 13:17:11 +0100 Subject: [PATCH 06/25] Added thread unmuting (still a bit buggy maybe) --- lib/pleroma/web/thread_mute.ex | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/thread_mute.ex b/lib/pleroma/web/thread_mute.ex index b37dda58b..a0d564e82 100644 --- a/lib/pleroma/web/thread_mute.ex +++ b/lib/pleroma/web/thread_mute.ex @@ -4,8 +4,8 @@ defmodule Pleroma.Web.ThreadMute do use Ecto.Schema - - alias Pleroma.{Activity, Notification, User, Repo} + alias Pleroma.{Activity, Repo, User} + require Ecto.Query schema "thread_mutes" do belongs_to(:user, User, type: Pleroma.FlakeId) @@ -19,8 +19,9 @@ defmodule Pleroma.Web.ThreadMute do end def remove_mute(user, id) do - end - - def mute_thread() do + user_id = Pleroma.FlakeId.from_string(user.id) + %{data: %{"context" => context}} = Activity.get_by_id(id) + Ecto.Query.from(m in "thread_mutes", where: m.user_id == ^user_id and m.context == ^context) + |> Repo.delete_all end end From 5c5b228f21a75c665ab7501ab02765183d00f410 Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Fri, 8 Feb 2019 13:17:11 +0100 Subject: [PATCH 07/25] Added thread unmuting (still a bit buggy maybe) --- lib/pleroma/web/thread_mute.ex | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/thread_mute.ex b/lib/pleroma/web/thread_mute.ex index b37dda58b..a0d564e82 100644 --- a/lib/pleroma/web/thread_mute.ex +++ b/lib/pleroma/web/thread_mute.ex @@ -4,8 +4,8 @@ defmodule Pleroma.Web.ThreadMute do use Ecto.Schema - - alias Pleroma.{Activity, Notification, User, Repo} + alias Pleroma.{Activity, Repo, User} + require Ecto.Query schema "thread_mutes" do belongs_to(:user, User, type: Pleroma.FlakeId) @@ -19,8 +19,9 @@ defmodule Pleroma.Web.ThreadMute do end def remove_mute(user, id) do - end - - def mute_thread() do + user_id = Pleroma.FlakeId.from_string(user.id) + %{data: %{"context" => context}} = Activity.get_by_id(id) + Ecto.Query.from(m in "thread_mutes", where: m.user_id == ^user_id and m.context == ^context) + |> Repo.delete_all end end From 09189c3a7c61d15dc8c73c2474df19320f5032d0 Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Sat, 9 Feb 2019 14:23:51 +0100 Subject: [PATCH 08/25] Made a test! --- test/web/thread_mute_test.exs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/web/thread_mute_test.exs diff --git a/test/web/thread_mute_test.exs b/test/web/thread_mute_test.exs new file mode 100644 index 000000000..4dea44d2f --- /dev/null +++ b/test/web/thread_mute_test.exs @@ -0,0 +1,25 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ThreadMuteTest do + use Pleroma.DataCase + import Pleroma.Web.ThreadMute + + import Pleroma.Factory + + test "add a mute" do + user = insert(:user, %{id: "1"}) + + activity = + insert(:note_activity, %{ + data: %{"context" => "http://localhost:4000/contexts/361ca23e-ffa7-4773-b981-a355a18dc592"} + }) + + id = activity.id + {:ok, mute} = add_mute(user, id) + + assert mute.user_id == "1" + assert mute.context == "http://localhost:4000/contexts/361ca23e-ffa7-4773-b981-a355a18dc592" + end +end From 4430a0ad127411b818d875dbbaf14369c109f331 Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Sat, 9 Feb 2019 14:34:42 +0100 Subject: [PATCH 09/25] added another test --- test/web/thread_mute_test.exs | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/test/web/thread_mute_test.exs b/test/web/thread_mute_test.exs index 4dea44d2f..75277ef62 100644 --- a/test/web/thread_mute_test.exs +++ b/test/web/thread_mute_test.exs @@ -8,18 +8,33 @@ defmodule Pleroma.Web.ThreadMuteTest do import Pleroma.Factory - test "add a mute" do - user = insert(:user, %{id: "1"}) + describe "mute tests" do + setup do + user = insert(:user, %{id: "1"}) - activity = - insert(:note_activity, %{ - data: %{"context" => "http://localhost:4000/contexts/361ca23e-ffa7-4773-b981-a355a18dc592"} - }) + activity = + insert(:note_activity, %{ + data: %{ + "context" => "http://localhost:4000/contexts/361ca23e-ffa7-4773-b981-a355a18dc592" + } + }) - id = activity.id - {:ok, mute} = add_mute(user, id) + [user: user, activity: activity] + end - assert mute.user_id == "1" - assert mute.context == "http://localhost:4000/contexts/361ca23e-ffa7-4773-b981-a355a18dc592" + test "add mute", %{user: user, activity: activity} do + id = activity.id + {:ok, mute} = add_mute(user, id) + + assert mute.user_id == "1" + assert mute.context == "http://localhost:4000/contexts/361ca23e-ffa7-4773-b981-a355a18dc592" + end + + test "remove mute", %{user: user, activity: activity} do + id = activity.id + + add_mute(user, id) + {1, nil} = remove_mute(user, id) + end end end From a0d732ec55fcbce8cf345344d1456dc77bb0f3bf Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Sat, 9 Feb 2019 17:47:57 +0100 Subject: [PATCH 10/25] it works!! --- lib/pleroma/notification.ex | 2 ++ lib/pleroma/web/thread_mute.ex | 28 +++++++++++++++++++++++++--- test/web/thread_mute_test.exs | 25 +++++++++++++------------ 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 2364d36da..d05708e41 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Notification do use Ecto.Schema alias Pleroma.{User, Activity, Notification, Repo} alias Pleroma.Web.CommonAPI.Utils + alias Pleroma.Web.ThreadMute import Ecto.Query schema "notifications" do @@ -112,6 +113,7 @@ defmodule Pleroma.Notification do # TODO move to sql, too. def create_notification(%Activity{} = activity, %User{} = user) do unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or + ThreadMute.muted?(user, activity) or user.ap_id == activity.data["actor"] or (activity.data["type"] == "Follow" and Enum.any?(Notification.for_user(user), fn notif -> diff --git a/lib/pleroma/web/thread_mute.ex b/lib/pleroma/web/thread_mute.ex index 3a950f474..146de0d80 100644 --- a/lib/pleroma/web/thread_mute.ex +++ b/lib/pleroma/web/thread_mute.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.ThreadMute do use Ecto.Schema + alias Pleroma.Web.ThreadMute alias Pleroma.{Activity, Repo, User} require Ecto.Query @@ -13,16 +14,37 @@ defmodule Pleroma.Web.ThreadMute do end def add_mute(user, id) do - %{data: %{"context" => context}} = Activity.get_by_id(id) + activity = Activity.get_by_id(id) + context = activity.data["context"] mute = %Pleroma.Web.ThreadMute{user_id: user.id, context: context} Repo.insert(mute) + {:ok, activity} end def remove_mute(user, id) do user_id = Pleroma.FlakeId.from_string(user.id) - %{data: %{"context" => context}} = Activity.get_by_id(id) + activity = Activity.get_by_id(id) + context = activity.data["context"] - Ecto.Query.from(m in "thread_mutes", where: m.user_id == ^user_id and m.context == ^context) + Ecto.Query.from(m in ThreadMute, where: m.user_id == ^user_id and m.context == ^context) |> Repo.delete_all() + + {:ok, activity} + end + + def muted?(user, activity) do + user_id = Pleroma.FlakeId.from_string(user.id) + context = activity.data["context"] + + result = + Ecto.Query.from(m in ThreadMute, + where: m.user_id == ^user_id and m.context == ^context + ) + |> Repo.all() + + case result do + [] -> false + _ -> true + end end end diff --git a/test/web/thread_mute_test.exs b/test/web/thread_mute_test.exs index 75277ef62..212cae860 100644 --- a/test/web/thread_mute_test.exs +++ b/test/web/thread_mute_test.exs @@ -10,31 +10,32 @@ defmodule Pleroma.Web.ThreadMuteTest do describe "mute tests" do setup do - user = insert(:user, %{id: "1"}) + user = insert(:user) - activity = - insert(:note_activity, %{ - data: %{ - "context" => "http://localhost:4000/contexts/361ca23e-ffa7-4773-b981-a355a18dc592" - } - }) + activity = insert(:note_activity) [user: user, activity: activity] end test "add mute", %{user: user, activity: activity} do id = activity.id - {:ok, mute} = add_mute(user, id) - - assert mute.user_id == "1" - assert mute.context == "http://localhost:4000/contexts/361ca23e-ffa7-4773-b981-a355a18dc592" + {:ok, _activity} = add_mute(user, id) end test "remove mute", %{user: user, activity: activity} do id = activity.id add_mute(user, id) - {1, nil} = remove_mute(user, id) + {:ok, _activity} = remove_mute(user, id) + end + + test "check mute", %{user: user, activity: activity} do + id = activity.id + + add_mute(user, id) + assert muted?(user, activity) + remove_mute(user, id) + refute muted?(user, activity) end end end From 638456ce8f6b7f44356e69bac89d794c19e969d7 Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Sat, 9 Feb 2019 18:08:46 +0100 Subject: [PATCH 11/25] elixir too new for CI's mix format lol --- lib/pleroma/notification.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index d05708e41..ce4826b23 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -113,8 +113,7 @@ defmodule Pleroma.Notification do # TODO move to sql, too. def create_notification(%Activity{} = activity, %User{} = user) do unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or - ThreadMute.muted?(user, activity) or - user.ap_id == activity.data["actor"] or + ThreadMute.muted?(user, activity) or user.ap_id == activity.data["actor"] or (activity.data["type"] == "Follow" and Enum.any?(Notification.for_user(user), fn notif -> notif.activity.data["type"] == "Follow" and From 478a05b4c99d696846b71d0eb4d6982ba422bfb8 Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Sat, 9 Feb 2019 18:34:00 +0100 Subject: [PATCH 12/25] Merged "check mute" test into the other two --- test/web/thread_mute_test.exs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/test/web/thread_mute_test.exs b/test/web/thread_mute_test.exs index 212cae860..119a06f52 100644 --- a/test/web/thread_mute_test.exs +++ b/test/web/thread_mute_test.exs @@ -20,6 +20,7 @@ defmodule Pleroma.Web.ThreadMuteTest do test "add mute", %{user: user, activity: activity} do id = activity.id {:ok, _activity} = add_mute(user, id) + assert muted?(user, activity) end test "remove mute", %{user: user, activity: activity} do @@ -27,14 +28,6 @@ defmodule Pleroma.Web.ThreadMuteTest do add_mute(user, id) {:ok, _activity} = remove_mute(user, id) - end - - test "check mute", %{user: user, activity: activity} do - id = activity.id - - add_mute(user, id) - assert muted?(user, activity) - remove_mute(user, id) refute muted?(user, activity) end end From 6a150de3bd416cfe0b4870deee2e6557791345f8 Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Sat, 9 Feb 2019 20:52:11 +0100 Subject: [PATCH 13/25] Add unique index and unique constraint check, uniqueness test fails --- lib/pleroma/web/thread_mute.ex | 16 +++++++++++++--- .../20190205114625_create_thread_mutes.exs | 2 +- test/web/thread_mute_test.exs | 15 +++++++++------ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/pleroma/web/thread_mute.ex b/lib/pleroma/web/thread_mute.ex index 146de0d80..b5bff86be 100644 --- a/lib/pleroma/web/thread_mute.ex +++ b/lib/pleroma/web/thread_mute.ex @@ -13,12 +13,22 @@ defmodule Pleroma.Web.ThreadMute do field(:context, :string) end + def changeset(mute, params \\ %{}) do + mute + |> Ecto.Changeset.cast(params, [:user_id, :context]) + |> Ecto.Changeset.foreign_key_constraint(:user_id) + |> Ecto.Changeset.unique_constraint(:user_id, name: :unique_index) + end + def add_mute(user, id) do activity = Activity.get_by_id(id) context = activity.data["context"] - mute = %Pleroma.Web.ThreadMute{user_id: user.id, context: context} - Repo.insert(mute) - {:ok, activity} + changeset = changeset(%Pleroma.Web.ThreadMute{}, %{user_id: user.id, context: context}) + + case Repo.insert(changeset) do + {:ok, _} -> {:ok, activity} + {:error, _} -> {:error, "conversation is already muted"} + end end def remove_mute(user, id) do diff --git a/priv/repo/migrations/20190205114625_create_thread_mutes.exs b/priv/repo/migrations/20190205114625_create_thread_mutes.exs index b16f557f0..8e9eccbae 100644 --- a/priv/repo/migrations/20190205114625_create_thread_mutes.exs +++ b/priv/repo/migrations/20190205114625_create_thread_mutes.exs @@ -7,6 +7,6 @@ defmodule Pleroma.Repo.Migrations.CreateThreadMutes do add :context, :string end - create index(:thread_mutes, [:user_id]) + create unique_index(:thread_mutes, [:user_id, :context], name: :unique_index) end end diff --git a/test/web/thread_mute_test.exs b/test/web/thread_mute_test.exs index 119a06f52..f3a24613c 100644 --- a/test/web/thread_mute_test.exs +++ b/test/web/thread_mute_test.exs @@ -18,17 +18,20 @@ defmodule Pleroma.Web.ThreadMuteTest do end test "add mute", %{user: user, activity: activity} do - id = activity.id - {:ok, _activity} = add_mute(user, id) + {:ok, _activity} = add_mute(user, activity.id) assert muted?(user, activity) end test "remove mute", %{user: user, activity: activity} do - id = activity.id - - add_mute(user, id) - {:ok, _activity} = remove_mute(user, id) + add_mute(user, activity.id) + {:ok, _activity} = remove_mute(user, activity.id) refute muted?(user, activity) end + + test "check that mutes can't be duplicate", %{user: user, activity: activity} do + add_mute(user, activity.id) + assert muted?(user, activity) + {:error, _} = add_mute(user, activity.id) + end end end From cc21fc5f537510416a088adff085b675de1be58e Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Sun, 10 Feb 2019 09:31:20 +0100 Subject: [PATCH 14/25] refactor, status view updating, error handling --- .../mastodon_api/mastodon_api_controller.ex | 5 +++ .../web/mastodon_api/views/status_view.ex | 2 +- lib/pleroma/web/thread_mute.ex | 38 ++++++++++--------- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index a93f4297b..073e0a5ea 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -450,6 +450,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) + else + {:error, reason} -> + conn + |> put_resp_content_type("application/json") + |> send_resp(:bad_request, Jason.encode!(%{"error" => reason})) end end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index a227d742d..d6176a68a 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -160,7 +160,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do reblogged: present?(repeated), favourited: present?(favorited), bookmarked: present?(bookmarked), - muted: false, + muted: Pleroma.Web.ThreadMute.muted?(user, activity), pinned: pinned?(activity, user), sensitive: sensitive, spoiler_text: object["summary"] || "", diff --git a/lib/pleroma/web/thread_mute.ex b/lib/pleroma/web/thread_mute.ex index b5bff86be..695ea2512 100644 --- a/lib/pleroma/web/thread_mute.ex +++ b/lib/pleroma/web/thread_mute.ex @@ -20,40 +20,42 @@ defmodule Pleroma.Web.ThreadMute do |> Ecto.Changeset.unique_constraint(:user_id, name: :unique_index) end + def query(user, context) do + user_id = Pleroma.FlakeId.from_string(user.id) + + ThreadMute + |> Ecto.Query.where(user_id: ^user_id) + |> Ecto.Query.where(context: ^context) + end + def add_mute(user, id) do activity = Activity.get_by_id(id) - context = activity.data["context"] - changeset = changeset(%Pleroma.Web.ThreadMute{}, %{user_id: user.id, context: context}) - case Repo.insert(changeset) do - {:ok, _} -> {:ok, activity} + with changeset <- + changeset(%ThreadMute{}, %{user_id: user.id, context: activity.data["context"]}), + {:ok, _} <- Repo.insert(changeset) do + {:ok, activity} + else {:error, _} -> {:error, "conversation is already muted"} end end def remove_mute(user, id) do - user_id = Pleroma.FlakeId.from_string(user.id) activity = Activity.get_by_id(id) - context = activity.data["context"] - Ecto.Query.from(m in ThreadMute, where: m.user_id == ^user_id and m.context == ^context) + query(user, activity.data["context"]) |> Repo.delete_all() {:ok, activity} end + def muted?(%{id: nil} = _user, _), do: false + def muted?(user, activity) do - user_id = Pleroma.FlakeId.from_string(user.id) - context = activity.data["context"] - - result = - Ecto.Query.from(m in ThreadMute, - where: m.user_id == ^user_id and m.context == ^context - ) - |> Repo.all() - - case result do - [] -> false + with query <- query(user, activity.data["context"]), + [] <- Repo.all(query) do + false + else _ -> true end end From 45e57dd187ecb9463f0114f75a05f03dbc9e206a Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Sun, 10 Feb 2019 21:37:51 +0000 Subject: [PATCH 15/25] rich media: tighten fetching timeouts and size limits --- lib/pleroma/web/rich_media/parser.ex | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index 38f1cdeec..4341141df 100644 --- a/lib/pleroma/web/rich_media/parser.ex +++ b/lib/pleroma/web/rich_media/parser.ex @@ -9,6 +9,13 @@ defmodule Pleroma.Web.RichMedia.Parser do Pleroma.Web.RichMedia.Parsers.OEmbed ] + @hackney_options [ + pool: :media, + timeout: 2_000, + recv_timeout: 2_000, + max_body: 2_000_000 + ] + def parse(nil), do: {:error, "No URL provided"} if Mix.env() == :test do @@ -28,7 +35,7 @@ defmodule Pleroma.Web.RichMedia.Parser do defp parse_url(url) do try do - {:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: [pool: :media]) + {:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options) html |> maybe_parse() |> clean_parsed_data() |> check_parsed_data() rescue From c01ef574c192488c2643a20b4064439757613449 Mon Sep 17 00:00:00 2001 From: Karen Konou Date: Mon, 11 Feb 2019 11:59:51 +0100 Subject: [PATCH 16/25] Refactor as per Rin's suggestions, add endpoint tests --- lib/pleroma/notification.ex | 4 +- lib/pleroma/thread_mute.ex | 45 ++++++++++++++ lib/pleroma/web/common_api/common_api.ex | 25 +++++++- .../mastodon_api/mastodon_api_controller.ex | 6 +- .../web/mastodon_api/views/status_view.ex | 3 +- lib/pleroma/web/thread_mute.ex | 62 ------------------- test/web/common_api/common_api_test.exs | 26 ++++++++ .../mastodon_api_controller_test.exs | 33 ++++++++++ test/web/thread_mute_test.exs | 37 ----------- 9 files changed, 136 insertions(+), 105 deletions(-) create mode 100644 lib/pleroma/thread_mute.ex delete mode 100644 lib/pleroma/web/thread_mute.ex delete mode 100644 test/web/thread_mute_test.exs diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index ce4826b23..9ebfd5cb0 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Notification do use Ecto.Schema alias Pleroma.{User, Activity, Notification, Repo} alias Pleroma.Web.CommonAPI.Utils - alias Pleroma.Web.ThreadMute + alias Pleroma.Web.CommonAPI import Ecto.Query schema "notifications" do @@ -113,7 +113,7 @@ defmodule Pleroma.Notification do # TODO move to sql, too. def create_notification(%Activity{} = activity, %User{} = user) do unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or - ThreadMute.muted?(user, activity) or user.ap_id == activity.data["actor"] or + CommonAPI.thread_muted?(user, activity) or user.ap_id == activity.data["actor"] or (activity.data["type"] == "Follow" and Enum.any?(Notification.for_user(user), fn notif -> notif.activity.data["type"] == "Follow" and diff --git a/lib/pleroma/thread_mute.ex b/lib/pleroma/thread_mute.ex new file mode 100644 index 000000000..0b577113d --- /dev/null +++ b/lib/pleroma/thread_mute.ex @@ -0,0 +1,45 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ThreadMute do + use Ecto.Schema + alias Pleroma.{Repo, User, ThreadMute} + require Ecto.Query + + schema "thread_mutes" do + belongs_to(:user, User, type: Pleroma.FlakeId) + field(:context, :string) + end + + def changeset(mute, params \\ %{}) do + mute + |> Ecto.Changeset.cast(params, [:user_id, :context]) + |> Ecto.Changeset.foreign_key_constraint(:user_id) + |> Ecto.Changeset.unique_constraint(:user_id, name: :unique_index) + end + + def query(user_id, context) do + user_id = Pleroma.FlakeId.from_string(user_id) + + ThreadMute + |> Ecto.Query.where(user_id: ^user_id) + |> Ecto.Query.where(context: ^context) + end + + def add_mute(user_id, context) do + %ThreadMute{} + |> changeset(%{user_id: user_id, context: context}) + |> Repo.insert() + end + + def remove_mute(user_id, context) do + query(user_id, context) + |> Repo.delete_all() + end + + def check_muted(user_id, context) do + query(user_id, context) + |> Repo.all() + end +end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 7084da6de..7782c64dd 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -3,7 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.CommonAPI do - alias Pleroma.{User, Repo, Activity, Object} + alias Pleroma.{User, Repo, Activity, Object, ThreadMute} alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Formatter @@ -216,4 +216,27 @@ defmodule Pleroma.Web.CommonAPI do {:error, "Could not unpin"} end end + + def add_mute(user, activity) do + with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]) do + {:ok, activity} + else + {:error, _} -> {:error, "conversation is already muted"} + end + end + + def remove_mute(user, activity) do + ThreadMute.remove_mute(user.id, activity.data["context"]) + {:ok, activity} + end + + def thread_muted?(%{id: nil} = _user, _activity), do: false + + def thread_muted?(user, activity) do + with [] <- ThreadMute.check_muted(user.id, activity.data["context"]) do + false + else + _ -> true + end + end end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 073e0a5ea..18fa16b03 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -446,7 +446,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def mute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with {:ok, activity} <- Pleroma.Web.ThreadMute.add_mute(user, id) do + activity = Activity.get_by_id(id) + with {:ok, activity} <- CommonAPI.add_mute(user, activity) do conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) @@ -459,7 +460,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def unmute_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with {:ok, activity} <- Pleroma.Web.ThreadMute.remove_mute(user, id) do + activity = Activity.get_by_id(id) + with {:ok, activity} <- CommonAPI.remove_mute(user, activity) do conn |> put_view(StatusView) |> try_render("status.json", %{activity: activity, for: user, as: :activity}) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index d6176a68a..368b79ea7 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do alias Pleroma.HTML alias Pleroma.Repo alias Pleroma.User + alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.MediaProxy alias Pleroma.Web.MastodonAPI.AccountView @@ -160,7 +161,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do reblogged: present?(repeated), favourited: present?(favorited), bookmarked: present?(bookmarked), - muted: Pleroma.Web.ThreadMute.muted?(user, activity), + muted: CommonAPI.thread_muted?(user, activity), pinned: pinned?(activity, user), sensitive: sensitive, spoiler_text: object["summary"] || "", diff --git a/lib/pleroma/web/thread_mute.ex b/lib/pleroma/web/thread_mute.ex deleted file mode 100644 index 695ea2512..000000000 --- a/lib/pleroma/web/thread_mute.ex +++ /dev/null @@ -1,62 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ThreadMute do - use Ecto.Schema - alias Pleroma.Web.ThreadMute - alias Pleroma.{Activity, Repo, User} - require Ecto.Query - - schema "thread_mutes" do - belongs_to(:user, User, type: Pleroma.FlakeId) - field(:context, :string) - end - - def changeset(mute, params \\ %{}) do - mute - |> Ecto.Changeset.cast(params, [:user_id, :context]) - |> Ecto.Changeset.foreign_key_constraint(:user_id) - |> Ecto.Changeset.unique_constraint(:user_id, name: :unique_index) - end - - def query(user, context) do - user_id = Pleroma.FlakeId.from_string(user.id) - - ThreadMute - |> Ecto.Query.where(user_id: ^user_id) - |> Ecto.Query.where(context: ^context) - end - - def add_mute(user, id) do - activity = Activity.get_by_id(id) - - with changeset <- - changeset(%ThreadMute{}, %{user_id: user.id, context: activity.data["context"]}), - {:ok, _} <- Repo.insert(changeset) do - {:ok, activity} - else - {:error, _} -> {:error, "conversation is already muted"} - end - end - - def remove_mute(user, id) do - activity = Activity.get_by_id(id) - - query(user, activity.data["context"]) - |> Repo.delete_all() - - {:ok, activity} - end - - def muted?(%{id: nil} = _user, _), do: false - - def muted?(user, activity) do - with query <- query(user, activity.data["context"]), - [] <- Repo.all(query) do - false - else - _ -> true - end - end -end diff --git a/test/web/common_api/common_api_test.exs b/test/web/common_api/common_api_test.exs index a7d9e6161..d26b6e49c 100644 --- a/test/web/common_api/common_api_test.exs +++ b/test/web/common_api/common_api_test.exs @@ -164,4 +164,30 @@ defmodule Pleroma.Web.CommonAPI.Test do assert %User{info: %{pinned_activities: []}} = user end end + + describe "mute tests" do + setup do + user = insert(:user) + + activity = insert(:note_activity) + + [user: user, activity: activity] + end + + test "add mute", %{user: user, activity: activity} do + {:ok, _} = CommonAPI.add_mute(user, activity) + assert CommonAPI.thread_muted?(user, activity) + end + + test "remove mute", %{user: user, activity: activity} do + CommonAPI.add_mute(user, activity) + {:ok, _} = CommonAPI.remove_mute(user, activity) + refute CommonAPI.thread_muted?(user, activity) + end + + test "check that mutes can't be duplicate", %{user: user, activity: activity} do + CommonAPI.add_mute(user, activity) + {:error, _} = CommonAPI.add_mute(user, activity) + end + end end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index f8da86004..f7c059663 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -1749,4 +1749,37 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do assert [json_response(response2, 200)] == json_response(bookmarks, 200) end + + describe "conversation muting" do + setup do + + user = insert(:user) + {:ok, activity} = CommonAPI.post(user, %{"status" => "HIE"}) + + [user: user, activity: activity] + end + + test "mute conversation", %{conn: conn, user: user, activity: activity} do + id_str = to_string(activity.id) + + assert %{"id" => ^id_str, "muted" => true} = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/mute") + |> json_response(200) + end + + test "unmute conversation", %{conn: conn, user: user, activity: activity} do + {:ok, _} = CommonAPI.add_mute(user, activity) + + id_str = to_string(activity.id) + user = refresh_record(user) + + assert %{"id" => ^id_str, "muted" => false} = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/unmute") + |> json_response(200) + end + end end diff --git a/test/web/thread_mute_test.exs b/test/web/thread_mute_test.exs deleted file mode 100644 index f3a24613c..000000000 --- a/test/web/thread_mute_test.exs +++ /dev/null @@ -1,37 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.ThreadMuteTest do - use Pleroma.DataCase - import Pleroma.Web.ThreadMute - - import Pleroma.Factory - - describe "mute tests" do - setup do - user = insert(:user) - - activity = insert(:note_activity) - - [user: user, activity: activity] - end - - test "add mute", %{user: user, activity: activity} do - {:ok, _activity} = add_mute(user, activity.id) - assert muted?(user, activity) - end - - test "remove mute", %{user: user, activity: activity} do - add_mute(user, activity.id) - {:ok, _activity} = remove_mute(user, activity.id) - refute muted?(user, activity) - end - - test "check that mutes can't be duplicate", %{user: user, activity: activity} do - add_mute(user, activity.id) - assert muted?(user, activity) - {:error, _} = add_mute(user, activity.id) - end - end -end From 379d04692cdbf558c611c588c0e6a4262c02a58c Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 11 Feb 2019 21:35:40 +0300 Subject: [PATCH 17/25] Filter summary in keywordpolicy --- .../web/activity_pub/mrf/keyword_policy.ex | 28 ++-- .../activity_pub/mrf/keyword_policy_test.exs | 132 ++++++++++++++++-- 2 files changed, 138 insertions(+), 22 deletions(-) diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex index ce6d2e529..5fdc03414 100644 --- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex @@ -12,9 +12,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do String.match?(string, pattern) end - defp check_reject(%{"object" => %{"content" => content}} = message) do + defp check_reject(%{"object" => %{"content" => content, "summary" => summary}} = message) do if Enum.any?(Pleroma.Config.get([:mrf_keyword, :reject]), fn pattern -> - string_matches?(content, pattern) + string_matches?(content, pattern) or string_matches?(summary, pattern) end) do {:reject, nil} else @@ -22,10 +22,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do end end - defp check_ftl_removal(%{"to" => to, "object" => %{"content" => content}} = message) do + defp check_ftl_removal( + %{"to" => to, "object" => %{"content" => content, "summary" => summary}} = message + ) do if "https://www.w3.org/ns/activitystreams#Public" in to and Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern -> - string_matches?(content, pattern) + string_matches?(content, pattern) or string_matches?(summary, pattern) end) do to = List.delete(to, "https://www.w3.org/ns/activitystreams#Public") cc = ["https://www.w3.org/ns/activitystreams#Public" | message["cc"] || []] @@ -41,14 +43,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do end end - defp check_replace(%{"object" => %{"content" => content}} = message) do - content = - Enum.reduce(Pleroma.Config.get([:mrf_keyword, :replace]), content, fn {pattern, replacement}, - acc -> - String.replace(acc, pattern, replacement) + defp check_replace(%{"object" => %{"content" => content, "summary" => summary}} = message) do + {content, summary} = + Enum.reduce(Pleroma.Config.get([:mrf_keyword, :replace]), {content, summary}, fn {pattern, + replacement}, + {content_acc, + summary_acc} -> + {String.replace(content_acc, pattern, replacement), + String.replace(summary_acc, pattern, replacement)} end) - {:ok, put_in(message["object"]["content"], content)} + {:ok, + message + |> put_in(["object", "content"], content) + |> put_in(["object", "summary"], summary)} end @impl true diff --git a/test/web/activity_pub/mrf/keyword_policy_test.exs b/test/web/activity_pub/mrf/keyword_policy_test.exs index 67a5858d7..602892a37 100644 --- a/test/web/activity_pub/mrf/keyword_policy_test.exs +++ b/test/web/activity_pub/mrf/keyword_policy_test.exs @@ -12,18 +12,35 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do end describe "rejecting based on keywords" do - test "rejects if string matches" do + test "rejects if string matches in content" do Pleroma.Config.put([:mrf_keyword, :reject], ["pun"]) message = %{ "type" => "Create", - "object" => %{"content" => "just a daily reminder that compLAINer is a good pun"} + "object" => %{ + "content" => "just a daily reminder that compLAINer is a good pun", + "summary" => "" + } } assert {:reject, nil} == KeywordPolicy.filter(message) end - test "rejects if regex matches" do + test "rejects if string matches in summary" do + Pleroma.Config.put([:mrf_keyword, :reject], ["pun"]) + + message = %{ + "type" => "Create", + "object" => %{ + "summary" => "just a daily reminder that compLAINer is a good pun", + "content" => "" + } + } + + assert {:reject, nil} == KeywordPolicy.filter(message) + end + + test "rejects if regex matches in content" do Pleroma.Config.put([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/]) assert true == @@ -31,7 +48,25 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do message = %{ "type" => "Create", "object" => %{ - "content" => "just a daily reminder that #{content} is a good pun" + "content" => "just a daily reminder that #{content} is a good pun", + "summary" => "" + } + } + + {:reject, nil} == KeywordPolicy.filter(message) + end) + end + + test "rejects if regex matches in summary" do + Pleroma.Config.put([:mrf_keyword, :reject], [~r/comp[lL][aA][iI][nN]er/]) + + assert true == + Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> + message = %{ + "type" => "Create", + "object" => %{ + "summary" => "just a daily reminder that #{content} is a good pun", + "content" => "" } } @@ -41,13 +76,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do end describe "delisting from ftl based on keywords" do - test "delists if string matches" do + test "delists if string matches in content" do Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], ["pun"]) message = %{ "to" => ["https://www.w3.org/ns/activitystreams#Public"], "type" => "Create", - "object" => %{"content" => "just a daily reminder that compLAINer is a good pun"} + "object" => %{ + "content" => "just a daily reminder that compLAINer is a good pun", + "summary" => "" + } } {:ok, result} = KeywordPolicy.filter(message) @@ -55,7 +93,24 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"] end - test "delists if regex matches" do + test "delists if string matches in summary" do + Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], ["pun"]) + + message = %{ + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create", + "object" => %{ + "summary" => "just a daily reminder that compLAINer is a good pun", + "content" => "" + } + } + + {:ok, result} = KeywordPolicy.filter(message) + assert ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] + refute ["https://www.w3.org/ns/activitystreams#Public"] == result["to"] + end + + test "delists if regex matches in content" do Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/]) assert true == @@ -64,7 +119,29 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do "type" => "Create", "to" => ["https://www.w3.org/ns/activitystreams#Public"], "object" => %{ - "content" => "just a daily reminder that #{content} is a good pun" + "content" => "just a daily reminder that #{content} is a good pun", + "summary" => "" + } + } + + {:ok, result} = KeywordPolicy.filter(message) + + ["https://www.w3.org/ns/activitystreams#Public"] == result["cc"] and + not (["https://www.w3.org/ns/activitystreams#Public"] == result["to"]) + end) + end + + test "delists if regex matches in summary" do + Pleroma.Config.put([:mrf_keyword, :federated_timeline_removal], [~r/comp[lL][aA][iI][nN]er/]) + + assert true == + Enum.all?(["complainer", "compLainer", "compLAiNer", "compLAINer"], fn content -> + message = %{ + "type" => "Create", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "object" => %{ + "summary" => "just a daily reminder that #{content} is a good pun", + "content" => "" } } @@ -77,20 +154,33 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do end describe "replacing keywords" do - test "replaces keyword if string matches" do + test "replaces keyword if string matches in content" do Pleroma.Config.put([:mrf_keyword, :replace], [{"opensource", "free software"}]) message = %{ "type" => "Create", "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{"content" => "ZFS is opensource"} + "object" => %{"content" => "ZFS is opensource", "summary" => ""} } {:ok, %{"object" => %{"content" => result}}} = KeywordPolicy.filter(message) assert result == "ZFS is free software" end - test "replaces keyword if regex matches" do + test "replaces keyword if string matches in summary" do + Pleroma.Config.put([:mrf_keyword, :replace], [{"opensource", "free software"}]) + + message = %{ + "type" => "Create", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "object" => %{"summary" => "ZFS is opensource", "content" => ""} + } + + {:ok, %{"object" => %{"summary" => result}}} = KeywordPolicy.filter(message) + assert result == "ZFS is free software" + end + + test "replaces keyword if regex matches in content" do Pleroma.Config.put([:mrf_keyword, :replace], [ {~r/open(-|\s)?source\s?(software)?/, "free software"} ]) @@ -100,12 +190,30 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicyTest do message = %{ "type" => "Create", "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "object" => %{"content" => "ZFS is #{content}"} + "object" => %{"content" => "ZFS is #{content}", "summary" => ""} } {:ok, %{"object" => %{"content" => result}}} = KeywordPolicy.filter(message) result == "ZFS is free software" end) end + + test "replaces keyword if regex matches in summary" do + Pleroma.Config.put([:mrf_keyword, :replace], [ + {~r/open(-|\s)?source\s?(software)?/, "free software"} + ]) + + assert true == + Enum.all?(["opensource", "open-source", "open source"], fn content -> + message = %{ + "type" => "Create", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "object" => %{"summary" => "ZFS is #{content}", "content" => ""} + } + + {:ok, %{"object" => %{"summary" => result}}} = KeywordPolicy.filter(message) + result == "ZFS is free software" + end) + end end end From ea1058929c4dd569c00864f5292ec0a689acd1c6 Mon Sep 17 00:00:00 2001 From: shibayashi Date: Tue, 12 Feb 2019 00:08:52 +0100 Subject: [PATCH 18/25] Use url[:scheme] instead of protocol to determine if https is enabled --- lib/pleroma/plugs/http_security_plug.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex index 2a266c407..3c8e6a18f 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/plugs/http_security_plug.ex @@ -33,7 +33,7 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do end defp csp_string do - protocol = Config.get([Pleroma.Web.Endpoint, :protocol]) + scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme] [ "default-src 'none'", @@ -46,7 +46,7 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do "script-src 'self'", "connect-src 'self' " <> String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws"), "manifest-src 'self'", - if protocol == "https" do + if scheme == "https" do "upgrade-insecure-requests" end ] From 84f22d1cb8cf953bf8f48c04d82ae05f780ec407 Mon Sep 17 00:00:00 2001 From: Hakaba Hitoyo Date: Tue, 12 Feb 2019 02:35:15 +0000 Subject: [PATCH 19/25] Mark streaming feature for mobile/web apps in Clients.md --- docs/Clients.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/Clients.md b/docs/Clients.md index 057f12392..043d04a0f 100644 --- a/docs/Clients.md +++ b/docs/Clients.md @@ -26,60 +26,71 @@ Feel free to contact us to be added to this list! - Source Code: - Contact: [@eurasierboy@mastodon.social](https://mastodon.social/users/eurasierboy) - Platforms: iOS +- Features: No Streaming ### Nekonium - Homepage: [F-Droid Repository](https://repo.gdgd.jp.net/), [Google Play](https://play.google.com/store/apps/details?id=com.apps.nekonium), [Amazon](https://www.amazon.co.jp/dp/B076FXPRBC/) - Source: - Contact: [@lin@pleroma.gdgd.jp.net](https://pleroma.gdgd.jp.net/users/lin) - Platforms: Android +- Features: Streaming Ready ### Mastalab - Source Code: - Contact: [@tom79@mastodon.social](https://mastodon.social/users/tom79) - Platforms: Android +- Features: Streaming Ready ### Roma - Homepage: - Source Code: ??? - Platforms: iOS, Android +- Features: No Streaming ### Tootdon - Homepage: , - Source Code: ??? - Contact: [@tootdon@mstdn.jp](https://mstdn.jp/users/tootdon) - Platforms: Android, iOS +- Features: No Streaming ### Tusky - Homepage: - Source Code: - Contact: [@ConnyDuck@mastodon.social](https://mastodon.social/users/ConnyDuck) - Platforms: Android +- Features: No Streaming ### Twidere - Homepage: - Source Code: , - Contact: - Platform: Android, iOS +- Features: No Streaming ## Alternative Web Interfaces ### Brutaldon - Homepage: - Source Code: - Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc) +- Features: No Streaming ### Feather - Source Code: - Contact: [@kaniini@pleroma.site](https://pleroma.site/kaniini) +- Features: No Streaming ### Halcyon - Source Code: - Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon) +- Features: Streaming Ready ### Pinafore - Homepage: - Source Code: - Contact: [@pinafore@mastodon.technology](https://mastodon.technology/users/pinafore) - Note: Pleroma support is a secondary goal +- Features: No Streaming ### Sengi - Source Code: From 71ce564ecc442614995c021281a8f1e1a67fabc1 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Thu, 22 Nov 2018 07:12:13 +0100 Subject: [PATCH 20/25] =?UTF-8?q?config/dev.exs:=20Don=E2=80=99t=20put=20s?= =?UTF-8?q?ecure=20cookies=20on=20dev?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/dev.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/dev.exs b/config/dev.exs index 8f89aa03c..f77bb9976 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -16,7 +16,8 @@ config :pleroma, Pleroma.Web.Endpoint, debug_errors: true, code_reloader: true, check_origin: false, - watchers: [] + watchers: [], + secure_cookie_flag: false config :pleroma, Pleroma.Mailer, adapter: Swoosh.Adapters.Local From 00e8f0b07dd3dced84b0317e1c5c4156d249dec4 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Fri, 1 Feb 2019 13:10:50 +0100 Subject: [PATCH 21/25] Plugs.HTTPSecurityPlug: Add unsafe-eval to script-src when in dev mode This is needed to run dev mode mastofe at the same time --- lib/pleroma/plugs/http_security_plug.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex index 3c8e6a18f..05e935f2c 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/plugs/http_security_plug.ex @@ -43,9 +43,11 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do "media-src 'self' https:", "style-src 'self' 'unsafe-inline'", "font-src 'self'", - "script-src 'self'", "connect-src 'self' " <> String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws"), "manifest-src 'self'", + if Mix.env() == :dev do + "script-src 'self' 'unsafe-eval'" + end, if scheme == "https" do "upgrade-insecure-requests" end From da4c662af31a2c45c767f2a9ed136272ee9fc2c8 Mon Sep 17 00:00:00 2001 From: "Haelwenn (lanodan) Monnier" Date: Sat, 2 Feb 2019 19:06:26 +0100 Subject: [PATCH 22/25] Plugs.HTTPSecurityPlug: Add webpacker to connect-src --- lib/pleroma/plugs/http_security_plug.ex | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex index 05e935f2c..057553e24 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/plugs/http_security_plug.ex @@ -34,6 +34,21 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do defp csp_string do scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme] + websocket_url = String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws") + + connect_src = + if Mix.env() == :dev do + "connect-src 'self' http://localhost:3035/ " <> websocket_url + else + "connect-src 'self' " <> websocket_url + end + + script_src = + if Mix.env() == :dev do + "script-src 'self' 'unsafe-eval'" + else + "script-src 'self'" + end [ "default-src 'none'", @@ -43,11 +58,9 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do "media-src 'self' https:", "style-src 'self' 'unsafe-inline'", "font-src 'self'", - "connect-src 'self' " <> String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws"), "manifest-src 'self'", - if Mix.env() == :dev do - "script-src 'self' 'unsafe-eval'" - end, + connect_src, + script_src, if scheme == "https" do "upgrade-insecure-requests" end From b7bc666200d6cd113365af7456b4135c86f1db03 Mon Sep 17 00:00:00 2001 From: hakabahitoyo Date: Wed, 13 Feb 2019 15:46:42 +0900 Subject: [PATCH 23/25] bugfix mdii uploader --- lib/pleroma/uploaders/mdii.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/uploaders/mdii.ex b/lib/pleroma/uploaders/mdii.ex index 320b07abd..190ed9f3a 100644 --- a/lib/pleroma/uploaders/mdii.ex +++ b/lib/pleroma/uploaders/mdii.ex @@ -25,7 +25,7 @@ defmodule Pleroma.Uploaders.MDII do query = "#{cgi}?#{extension}" with {:ok, %{status: 200, body: body}} <- - @httpoison.post(query, file_data, adapter: [pool: :default]) do + @httpoison.post(query, file_data, [], adapter: [pool: :default]) do remote_file_name = String.split(body) |> List.first() public_url = "#{files}/#{remote_file_name}.#{extension}" {:ok, {:url, public_url}} From 16b7c07115ae5359541ac07752dd7d433035046d Mon Sep 17 00:00:00 2001 From: Hakaba Hitoyo Date: Wed, 13 Feb 2019 07:51:14 +0000 Subject: [PATCH 24/25] Mark streaming feature for desktop apps in Clients.md --- docs/Clients.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Clients.md b/docs/Clients.md index 043d04a0f..c3d776488 100644 --- a/docs/Clients.md +++ b/docs/Clients.md @@ -7,6 +7,7 @@ Feel free to contact us to be added to this list! - Homepage: - Source Code: ??? - Platforms: Windows, Mac, (Linux?) +- Features: Streaming Ready ### Social - Source Code: @@ -19,6 +20,7 @@ Feel free to contact us to be added to this list! - Source Code: - Contact: [@h3poteto@pleroma.io](https://pleroma.io/users/h3poteto) - Platforms: Windows, Mac, Linux +- Features: Streaming Ready ## Handheld ### Amaroq From 88a4de24f9bc6fc73696cb5c986440c2c659b636 Mon Sep 17 00:00:00 2001 From: lain Date: Wed, 13 Feb 2019 13:52:27 +0100 Subject: [PATCH 25/25] User.follow_all: Respect blocks in both directions. --- lib/pleroma/user.ex | 4 ++-- test/user_test.exs | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 0060d966b..3232cb842 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -311,12 +311,12 @@ defmodule Pleroma.User do end end - @doc "A mass follow for local users. Respects blocks but does not create activities." + @doc "A mass follow for local users. Respects blocks in both directions but does not create activities." @spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()} def follow_all(follower, followeds) do followed_addresses = followeds - |> Enum.reject(fn %{ap_id: ap_id} -> ap_id in follower.info.blocks end) + |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end) |> Enum.map(fn %{follower_address: fa} -> fa end) q = diff --git a/test/user_test.exs b/test/user_test.exs index 523ab1ea4..a282274ce 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -55,18 +55,21 @@ defmodule Pleroma.UserTest do followed_two = insert(:user) blocked = insert(:user) not_followed = insert(:user) + reverse_blocked = insert(:user) {:ok, user} = User.block(user, blocked) + {:ok, reverse_blocked} = User.block(reverse_blocked, user) {:ok, user} = User.follow(user, followed_zero) - {:ok, user} = User.follow_all(user, [followed_one, followed_two, blocked]) + {:ok, user} = User.follow_all(user, [followed_one, followed_two, blocked, reverse_blocked]) assert User.following?(user, followed_one) assert User.following?(user, followed_two) assert User.following?(user, followed_zero) refute User.following?(user, not_followed) refute User.following?(user, blocked) + refute User.following?(user, reverse_blocked) end test "follow_all follows mutliple users without duplicating" do