From 1bb4d5d65be725f374e06da88a5e8e826660596b Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 29 Mar 2019 21:59:04 +0300 Subject: [PATCH 1/8] Implement fake status submit --- lib/pleroma/web/activity_pub/activity_pub.ex | 31 ++++++++++---- lib/pleroma/web/common_api/common_api.ex | 17 ++++---- .../mastodon_api_controller_test.exs | 42 +++++++++++++++++++ 3 files changed, 76 insertions(+), 14 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 6e1ed7ec9..b459fd882 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -113,15 +113,15 @@ def decrease_replies_count_if_reply(%Object{ def decrease_replies_count_if_reply(_object), do: :noop - def insert(map, local \\ true) when is_map(map) do + def insert(map, local \\ true, fake \\ false) when is_map(map) do with nil <- Activity.normalize(map), map <- lazy_put_activity_defaults(map), :ok <- check_actor_is_active(map["actor"]), {_, true} <- {:remote_limit_error, check_remote_limit(map)}, {:ok, map} <- MRF.filter(map), + {recipients, _, _} = get_recipients(map), + {:fake, false, map, recipients} <- {:fake, fake, map, recipients}, {:ok, object} <- insert_full_object(map) do - {recipients, _, _} = get_recipients(map) - {:ok, activity} = Repo.insert(%Activity{ data: map, @@ -146,8 +146,21 @@ def insert(map, local \\ true) when is_map(map) do stream_out(activity) {:ok, activity} else - %Activity{} = activity -> {:ok, activity} - error -> {:error, error} + %Activity{} = activity -> + {:ok, activity} + + {:fake, true, map, recipients} -> + {:ok, + %Activity{ + data: map, + local: local, + actor: map["actor"], + recipients: recipients, + id: "pleroma:fakeid" + }} + + error -> + {:error, error} end end @@ -190,7 +203,7 @@ def stream_out(activity) do end end - def create(%{to: to, actor: actor, context: context, object: object} = params) do + def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do additional = params[:additional] || %{} # only accept false as false value local = !(params[:local] == false) @@ -201,13 +214,17 @@ def create(%{to: to, actor: actor, context: context, object: object} = params) d %{to: to, actor: actor, published: published, context: context, object: object}, additional ), - {:ok, activity} <- insert(create_data, local), + {:ok, activity} <- insert(create_data, local, fake), + {:fake, false, activity} <- {:fake, fake, activity}, _ <- increase_replies_count_if_reply(create_data), # Changing note count prior to enqueuing federation task in order to avoid # race conditions on updating user.info {:ok, _actor} <- increase_note_count_if_public(actor, activity), :ok <- maybe_federate(activity) do {:ok, activity} + else + {:fake, true, activity} -> + {:ok, activity} end end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 25b990677..8e2937ac5 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -172,13 +172,16 @@ def post(user, %{"status" => status} = data) do end) ) do res = - ActivityPub.create(%{ - to: to, - actor: user, - context: context, - object: object, - additional: %{"cc" => cc, "directMessage" => visibility == "direct"} - }) + ActivityPub.create( + %{ + to: to, + actor: user, + context: context, + object: object, + additional: %{"cc" => cc, "directMessage" => visibility == "direct"} + }, + data["fake"] || false + ) res end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index d9bcbf5a9..3395c3689 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -143,6 +143,48 @@ test "posting a sensitive status", %{conn: conn} do assert Repo.get(Activity, id) end + test "posting a fake status", %{conn: conn} do + user = insert(:user) + + real_conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses", %{ + "status" => + "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it" + }) + + real_status = + json_response(real_conn, 200) + |> Map.put("id", nil) + |> Map.put("url", nil) + |> Map.put("uri", nil) + |> Map.put("created_at", nil) + |> Kernel.put_in(["pleroma", "conversation_id"], nil) + + assert real_status + + fake_conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses", %{ + "status" => + "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it", + "fake" => true + }) + + fake_status = + json_response(fake_conn, 200) + |> Map.put("id", nil) + |> Map.put("url", nil) + |> Map.put("uri", nil) + |> Map.put("created_at", nil) + |> Kernel.put_in(["pleroma", "conversation_id"], nil) + + assert fake_status + assert real_status == fake_status + end + test "posting a status with OGP link preview", %{conn: conn} do Pleroma.Config.put([:rich_media, :enabled], true) user = insert(:user) From 42b779527c551595399771fbc3c0701d38a3ed3d Mon Sep 17 00:00:00 2001 From: rinpatch Date: Fri, 29 Mar 2019 22:15:20 +0300 Subject: [PATCH 2/8] document fake option --- docs/api/differences_in_mastoapi_responses.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/api/differences_in_mastoapi_responses.md b/docs/api/differences_in_mastoapi_responses.md index d993d1383..f5ce7493d 100644 --- a/docs/api/differences_in_mastoapi_responses.md +++ b/docs/api/differences_in_mastoapi_responses.md @@ -44,3 +44,9 @@ Has these additional fields under the `pleroma` object: Has these additional fields under the `pleroma` object: - `is_seen`: true if the notification was read by the user + +## POST `/api/v1/statuses` + +Additional parameters can be added to the JSON body: + +- `fake`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example. From cd387f8693c57b925576ab92f8202ef28007cfc0 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 30 Mar 2019 13:57:54 +0300 Subject: [PATCH 3/8] Add a fake option to lazy_put_actvity_defaults --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- lib/pleroma/web/activity_pub/utils.ex | 30 +++++++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index b459fd882..a94040d01 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -115,7 +115,7 @@ def decrease_replies_count_if_reply(_object), do: :noop def insert(map, local \\ true, fake \\ false) when is_map(map) do with nil <- Activity.normalize(map), - map <- lazy_put_activity_defaults(map), + map <- lazy_put_activity_defaults(map, fake), :ok <- check_actor_is_active(map["actor"]), {_, true} <- {:remote_limit_error, check_remote_limit(map)}, {:ok, map} <- MRF.filter(map), diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 2e9ffe41c..3959e9bd9 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -175,21 +175,29 @@ def maybe_federate(_), do: :ok Adds an id and a published data if they aren't there, also adds it to an included object """ - def lazy_put_activity_defaults(map) do - %{data: %{"id" => context}, id: context_id} = create_context(map["context"]) + def lazy_put_activity_defaults(map, fake \\ false) do + unless fake do + %{data: %{"id" => context}, id: context_id} = create_context(map["context"]) - map = - map - |> Map.put_new_lazy("id", &generate_activity_id/0) - |> Map.put_new_lazy("published", &make_date/0) - |> Map.put_new("context", context) - |> Map.put_new("context_id", context_id) + map = + map + |> Map.put_new_lazy("id", &generate_activity_id/0) + |> Map.put_new_lazy("published", &make_date/0) + |> Map.put_new("context", context) + |> Map.put_new("context_id", context_id) - if is_map(map["object"]) do - object = lazy_put_object_defaults(map["object"], map) - %{map | "object" => object} + if is_map(map["object"]) do + object = lazy_put_object_defaults(map["object"], map) + %{map | "object" => object} + else + map + end else map + |> Map.put_new("id", "pleroma:fakeid") + |> Map.put_new_lazy("published", &make_date/0) + |> Map.put_new("context", "pleroma:fakecontext") + |> Map.put_new("context_id", -1) end end From 45ba10bf47baf350fd4d538cbe32cec447d496e6 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 1 Apr 2019 11:55:59 +0300 Subject: [PATCH 4/8] Fix the issue with HTML scrubber --- lib/pleroma/html.ex | 17 +++++++++++++-- lib/pleroma/object.ex | 5 +++++ lib/pleroma/web/activity_pub/activity_pub.ex | 22 +++++++++++++------- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index 5b152d926..f19b42b42 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -28,9 +28,13 @@ def filter_tags(html, scrubber), do: Scrubber.scrub(html, scrubber) def filter_tags(html), do: filter_tags(html, nil) def strip_tags(html), do: Scrubber.scrub(html, Scrubber.StripTags) + # TODO: rename object to activity because that's what it is really working with def get_cached_scrubbed_html_for_object(content, scrubbers, object, module) do key = "#{module}#{generate_scrubber_signature(scrubbers)}|#{object.id}" - Cachex.fetch!(:scrubber_cache, key, fn _key -> ensure_scrubbed_html(content, scrubbers) end) + + Cachex.fetch!(:scrubber_cache, key, fn _key -> + ensure_scrubbed_html(content, scrubbers, object.data["object"]["fake"] || false) + end) end def get_cached_stripped_html_for_object(content, object, module) do @@ -44,11 +48,20 @@ def get_cached_stripped_html_for_object(content, object, module) do def ensure_scrubbed_html( content, - scrubbers + scrubbers, + _fake = false ) do {:commit, filter_tags(content, scrubbers)} end + def ensure_scrubbed_html( + content, + scrubbers, + _fake = true + ) do + {:ignore, filter_tags(content, scrubbers)} + end + defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do generate_scrubber_signature([scrubber]) end diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 8a670645d..013d62157 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -44,6 +44,11 @@ def get_by_ap_id(ap_id) do # Use this whenever possible, especially when walking graphs in an O(N) loop! def normalize(%Activity{object: %Object{} = object}), do: object + # A hack for fake activities + def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}) do + %Object{id: "pleroma:fake_object_id", data: data} + end + # Catch and log Object.normalize() calls where the Activity's child object is not # preloaded. def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}) do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index a94040d01..716a40419 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -150,14 +150,20 @@ def insert(map, local \\ true, fake \\ false) when is_map(map) do {:ok, activity} {:fake, true, map, recipients} -> - {:ok, - %Activity{ - data: map, - local: local, - actor: map["actor"], - recipients: recipients, - id: "pleroma:fakeid" - }} + map = + map + |> put_in(["object", "fake"], true) + + activity = %Activity{ + data: map, + local: local, + actor: map["actor"], + recipients: recipients, + id: "pleroma:fakeid" + } + + # Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + {:ok, activity} error -> {:error, error} From d866b59eeaed25a1ad19581bd6c942f9f2d2711e Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 1 Apr 2019 11:58:08 +0300 Subject: [PATCH 5/8] oof --- lib/pleroma/web/activity_pub/activity_pub.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 716a40419..9cb4a0542 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -162,7 +162,7 @@ def insert(map, local \\ true, fake \\ false) when is_map(map) do id: "pleroma:fakeid" } - # Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) + Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) {:ok, activity} error -> From 975482f091f2f957c138d1b4f2d37e6b5d2b82a8 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 1 Apr 2019 12:16:51 +0300 Subject: [PATCH 6/8] insert object defaults for fake activities and make credo happy --- lib/pleroma/html.ex | 4 ++-- lib/pleroma/web/activity_pub/utils.ex | 34 +++++++++++++++++---------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index f19b42b42..1e48749a8 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -49,7 +49,7 @@ def get_cached_stripped_html_for_object(content, object, module) do def ensure_scrubbed_html( content, scrubbers, - _fake = false + false = _fake ) do {:commit, filter_tags(content, scrubbers)} end @@ -57,7 +57,7 @@ def ensure_scrubbed_html( def ensure_scrubbed_html( content, scrubbers, - _fake = true + true = _fake ) do {:ignore, filter_tags(content, scrubbers)} end diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 3959e9bd9..feb73518e 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -176,35 +176,45 @@ def maybe_federate(_), do: :ok also adds it to an included object """ def lazy_put_activity_defaults(map, fake \\ false) do - unless fake do - %{data: %{"id" => context}, id: context_id} = create_context(map["context"]) + map = + unless fake do + %{data: %{"id" => context}, id: context_id} = create_context(map["context"]) - map = map |> Map.put_new_lazy("id", &generate_activity_id/0) |> Map.put_new_lazy("published", &make_date/0) |> Map.put_new("context", context) |> Map.put_new("context_id", context_id) - - if is_map(map["object"]) do - object = lazy_put_object_defaults(map["object"], map) - %{map | "object" => object} else map + |> Map.put_new("id", "pleroma:fakeid") + |> Map.put_new_lazy("published", &make_date/0) + |> Map.put_new("context", "pleroma:fakecontext") + |> Map.put_new("context_id", -1) end + + if is_map(map["object"]) do + object = lazy_put_object_defaults(map["object"], map, fake) + %{map | "object" => object} else map - |> Map.put_new("id", "pleroma:fakeid") - |> Map.put_new_lazy("published", &make_date/0) - |> Map.put_new("context", "pleroma:fakecontext") - |> Map.put_new("context_id", -1) end end @doc """ Adds an id and published date if they aren't there. """ - def lazy_put_object_defaults(map, activity \\ %{}) do + def lazy_put_object_defaults(map, activity \\ %{}, fake) + + def lazy_put_object_defaults(map, activity, true = _fake) do + map + |> Map.put_new_lazy("published", &make_date/0) + |> Map.put_new("id", "pleroma:fakeid") + |> Map.put_new("context", activity["context"]) + |> Map.put_new("context_id", activity["context_id"]) + end + + def lazy_put_object_defaults(map, activity, _fake) do map |> Map.put_new_lazy("id", &generate_object_id/0) |> Map.put_new_lazy("published", &make_date/0) From fe5145eeaab81573614a3475463a24229a6a58a3 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 1 Apr 2019 12:25:53 +0300 Subject: [PATCH 7/8] Move putting fake attribute to lib/pleroma/web/activity_pub/utils.ex --- lib/pleroma/web/activity_pub/activity_pub.ex | 4 ---- lib/pleroma/web/activity_pub/utils.ex | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 9cb4a0542..f217e7bac 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -150,10 +150,6 @@ def insert(map, local \\ true, fake \\ false) when is_map(map) do {:ok, activity} {:fake, true, map, recipients} -> - map = - map - |> put_in(["object", "fake"], true) - activity = %Activity{ data: map, local: local, diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index feb73518e..d22da6f40 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -209,8 +209,9 @@ def lazy_put_object_defaults(map, activity \\ %{}, fake) def lazy_put_object_defaults(map, activity, true = _fake) do map |> Map.put_new_lazy("published", &make_date/0) - |> Map.put_new("id", "pleroma:fakeid") + |> Map.put_new("id", "pleroma:fake_object_id") |> Map.put_new("context", activity["context"]) + |> Map.put_new("fake", true) |> Map.put_new("context_id", activity["context_id"]) end From fdb4357e9ba7a34a603997d50d85593ca2bf6395 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 2 Apr 2019 14:31:18 +0300 Subject: [PATCH 8/8] Rename fake param to preview and make the tests check that the object was not inserted to the db --- docs/api/differences_in_mastoapi_responses.md | 2 +- lib/pleroma/web/common_api/common_api.ex | 2 +- .../mastodon_api_controller_test.exs | 19 +++++++++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/api/differences_in_mastoapi_responses.md b/docs/api/differences_in_mastoapi_responses.md index f5ce7493d..7adf29676 100644 --- a/docs/api/differences_in_mastoapi_responses.md +++ b/docs/api/differences_in_mastoapi_responses.md @@ -49,4 +49,4 @@ Has these additional fields under the `pleroma` object: Additional parameters can be added to the JSON body: -- `fake`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example. +- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example. diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 8e2937ac5..2f82a32f3 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -180,7 +180,7 @@ def post(user, %{"status" => status} = data) do object: object, additional: %{"cc" => cc, "directMessage" => visibility == "direct"} }, - data["fake"] || false + data["preview"] || false ) res diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 3395c3689..d17d58962 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -154,34 +154,41 @@ test "posting a fake status", %{conn: conn} do "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it" }) + real_status = json_response(real_conn, 200) + + assert real_status + assert Object.get_by_ap_id(real_status["uri"]) + real_status = - json_response(real_conn, 200) + real_status |> Map.put("id", nil) |> Map.put("url", nil) |> Map.put("uri", nil) |> Map.put("created_at", nil) |> Kernel.put_in(["pleroma", "conversation_id"], nil) - assert real_status - fake_conn = conn |> assign(:user, user) |> post("/api/v1/statuses", %{ "status" => "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it", - "fake" => true + "preview" => true }) + fake_status = json_response(fake_conn, 200) + + assert fake_status + refute Object.get_by_ap_id(fake_status["uri"]) + fake_status = - json_response(fake_conn, 200) + fake_status |> Map.put("id", nil) |> Map.put("url", nil) |> Map.put("uri", nil) |> Map.put("created_at", nil) |> Kernel.put_in(["pleroma", "conversation_id"], nil) - assert fake_status assert real_status == fake_status end