From 62340b50b57eeab0b7ab4093e07d05080991bfc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 19 Aug 2023 19:03:25 +0200 Subject: [PATCH] Move maybe_add_content_map out of Transmogrifier, use code from tusooa's branch for MapOfString MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../object_validators/map_of_string.ex | 48 +++++++++++++ .../article_note_page_validator.ex | 1 + .../object_validators/common_fields.ex | 1 + .../object_validators/common_fixes.ex | 8 +++ .../object_validators/event_validator.ex | 20 +++--- .../web/activity_pub/transmogrifier.ex | 8 --- .../object_validators/map_of_string_test.exs | 55 +++++++++++++++ .../article_note_page_validator_test.exs | 70 +++++++++++++++++++ .../transmogrifier/note_handling_test.exs | 54 -------------- 9 files changed, 194 insertions(+), 71 deletions(-) create mode 100644 lib/pleroma/ecto_type/activity_pub/object_validators/map_of_string.ex create mode 100644 test/pleroma/ecto_type/activity_pub/object_validators/map_of_string_test.exs diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/map_of_string.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/map_of_string.ex new file mode 100644 index 000000000..e86275f92 --- /dev/null +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/map_of_string.ex @@ -0,0 +1,48 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.MapOfString do + use Ecto.Type + + import Pleroma.Web.CommonAPI.Utils, only: [is_good_locale_code?: 1] + + def type, do: :map + + def cast(%{} = object) do + with {status, %{} = data} when status in [:modified, :ok] <- validate_map(object) do + {:ok, data} + else + {_, nil} -> {:ok, nil} + {:error, _} -> :error + end + end + + def cast(_), do: :error + + def dump(data), do: {:ok, data} + + def load(data), do: {:ok, data} + + defp validate_map(%{} = object) do + {status, data} = + object + |> Enum.reduce({:ok, %{}}, fn + {lang, value}, {status, acc} when is_binary(lang) and is_binary(value) -> + if is_good_locale_code?(lang) do + {status, Map.put(acc, lang, value)} + else + {:modified, acc} + end + + _, {_status, acc} -> + {:modified, acc} + end) + + if data == %{} do + {status, nil} + else + {status, data} + end + end +end diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex index 9e6a1b0fb..0c7aa769b 100644 --- a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex @@ -87,6 +87,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do |> Transmogrifier.fix_emoji() |> Transmogrifier.fix_content_map() |> CommonFixes.maybe_add_language(meta) + |> CommonFixes.maybe_add_content_map() end def changeset(struct, data, meta \\ []) do diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex index 7ba393270..0cef5b533 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex @@ -31,6 +31,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do defmacro object_fields do quote bind_quoted: binding() do field(:content, :string) + field(:contentMap, ObjectValidators.MapOfString) field(:published, ObjectValidators.DateTime) field(:updated, ObjectValidators.DateTime) diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex index 66e44afe6..b141cc74c 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do alias Pleroma.Web.ActivityPub.Utils import Pleroma.Web.CommonAPI.Utils, only: [is_good_locale_code?: 1] + import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1] def cast_and_filter_recipients(message, field, follower_collection, field_fallback \\ []) do {:ok, data} = ObjectValidators.Recipients.cast(message[field] || field_fallback) @@ -118,4 +119,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do end defp get_language_from_content_map(_), do: nil + + def maybe_add_content_map(%{"language" => language, "content" => content} = object) + when not_empty_string(language) do + Map.put(object, "contentMap", Map.put(%{}, language, content)) + end + + def maybe_add_content_map(object), do: object end diff --git a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex index ab204f69a..56ca6fe40 100644 --- a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex @@ -26,32 +26,34 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do end end - def cast_and_apply(data) do + def cast_and_apply(data, meta \\ []) do data - |> cast_data + |> cast_data(meta) |> apply_action(:insert) end - def cast_and_validate(data) do + def cast_and_validate(data, meta \\ []) do data - |> cast_data() + |> cast_data(meta) |> validate_data() end - def cast_data(data) do + def cast_data(data, meta \\ []) do %__MODULE__{} - |> changeset(data) + |> changeset(data, meta) end - defp fix(data) do + defp fix(data, meta) do data |> CommonFixes.fix_actor() |> CommonFixes.fix_object_defaults() |> Transmogrifier.fix_emoji() + |> CommonFixes.maybe_add_language(meta) + |> CommonFixes.maybe_add_content_map() end - def changeset(struct, data) do - data = fix(data) + def changeset(struct, data, meta \\ []) do + data = fix(data, meta) struct |> cast(data, __schema__(:fields) -- [:attachment, :tag]) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index fd7059dea..a60e98c28 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -682,7 +682,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> add_mention_tags |> add_emoji_tags |> add_attributed_to - |> maybe_add_content_map |> prepare_attachments |> set_conversation |> set_reply_to_uri @@ -956,11 +955,4 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def maybe_fix_user_url(data), do: data def maybe_fix_user_object(data), do: maybe_fix_user_url(data) - - defp maybe_add_content_map(%{"language" => language, "content" => content} = object) - when not_empty_string(language) do - Map.put(object, "contentMap", Map.put(%{}, language, content)) - end - - defp maybe_add_content_map(object), do: object end diff --git a/test/pleroma/ecto_type/activity_pub/object_validators/map_of_string_test.exs b/test/pleroma/ecto_type/activity_pub/object_validators/map_of_string_test.exs new file mode 100644 index 000000000..4ee179dc8 --- /dev/null +++ b/test/pleroma/ecto_type/activity_pub/object_validators/map_of_string_test.exs @@ -0,0 +1,55 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.MapOfStringTest do + alias Pleroma.EctoType.ActivityPub.ObjectValidators.MapOfString + use Pleroma.DataCase, async: true + + test "it validates" do + data = %{ + "en-US" => "mew mew", + "en-GB" => "meow meow" + } + + assert {:ok, ^data} = MapOfString.cast(data) + end + + test "it validates empty strings" do + data = %{ + "en-US" => "mew mew", + "en-GB" => "" + } + + assert {:ok, ^data} = MapOfString.cast(data) + end + + test "it ignores non-strings within the map" do + data = %{ + "en-US" => "mew mew", + "en-GB" => 123 + } + + assert {:ok, validated_data} = MapOfString.cast(data) + + assert validated_data == %{"en-US" => "mew mew"} + end + + test "it ignores bad locale codes" do + data = %{ + "en-US" => "mew mew", + "en_GB" => "meow meow", + "en<<#@!$#!@%!GB" => "meow meow" + } + + assert {:ok, validated_data} = MapOfString.cast(data) + + assert validated_data == %{"en-US" => "mew mew"} + end + + test "it complains with non-map data" do + assert :error = MapOfString.cast("mew") + assert :error = MapOfString.cast(["mew"]) + assert :error = MapOfString.cast([%{"en-US" => "mew"}]) + end +end diff --git a/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs b/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs index c7a62be18..a0c50b10d 100644 --- a/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs @@ -116,4 +116,74 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest %{valid?: true} = ArticleNotePageValidator.cast_and_validate(note) end + + describe "Note language" do + test "it detects language from context" do + user = insert(:user) + + note_activity = %{ + "@context" => ["https://www.w3.org/ns/activitystreams", %{"@language" => "pl"}], + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [], + "type" => "Create", + "object" => %{ + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [], + "id" => Utils.generate_object_id(), + "type" => "Note", + "content" => "Szczęść Boże", + "attributedTo" => user.ap_id + }, + "actor" => user.ap_id + } + + {:ok, object} = + ArticleNotePageValidator.cast_and_apply(note_activity["object"], + activity_data: note_activity + ) + + assert object.language == "pl" + end + + test "it detects language from contentMap" do + user = insert(:user) + + note = %{ + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [], + "id" => Utils.generate_object_id(), + "type" => "Note", + "content" => "Szczęść Boże", + "contentMap" => %{ + "de" => "Gott segne", + "pl" => "Szczęść Boże" + }, + "attributedTo" => user.ap_id + } + + {:ok, object} = ArticleNotePageValidator.cast_and_apply(note) + + assert object.language == "pl" + end + + test "it adds contentMap if language is specified" do + user = insert(:user) + + note = %{ + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [], + "id" => Utils.generate_object_id(), + "type" => "Note", + "content" => "тест", + "language" => "uk", + "attributedTo" => user.ap_id + } + + {:ok, object} = ArticleNotePageValidator.cast_and_apply(note) + + assert object.contentMap == %{ + "uk" => "тест" + } + end + end end diff --git a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs index 70283ac5a..1e57ebabe 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs @@ -388,60 +388,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.NoteHandlingTest do end end - test "it detects language from context" do - user = insert(:user) - - message = %{ - "@context" => ["https://www.w3.org/ns/activitystreams", %{"@language" => "pl"}], - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Create", - "object" => %{ - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "id" => Utils.generate_object_id(), - "type" => "Note", - "content" => "Szczęść Boże", - "attributedTo" => user.ap_id - }, - "actor" => user.ap_id - } - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(message) - object = Object.normalize(data["object"], fetch: false) - - assert object.data["language"] == "pl" - end - - test "it detects language from contentMap" do - user = insert(:user) - - message = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Create", - "object" => %{ - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "id" => Utils.generate_object_id(), - "type" => "Note", - "content" => "Szczęść Boże", - "contentMap" => %{ - "de" => "Gott segne", - "pl" => "Szczęść Boże" - }, - "attributedTo" => user.ap_id - }, - "actor" => user.ap_id - } - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(message) - object = Object.normalize(data["object"], fetch: false) - - assert object.data["language"] == "pl" - end - describe "`handle_incoming/2`, Mastodon format `replies` handling" do setup do: clear_config([:activitypub, :note_replies_output_limit], 5) setup do: clear_config([:instance, :federation_incoming_replies_max_depth])