From 4745a41393cddd9bbc5a14affa77595204488b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 10 Aug 2023 23:03:19 +0200 Subject: Allow to specify post language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/constants.ex | 6 +- .../article_note_page_validator.ex | 1 + .../object_validators/common_fields.ex | 1 + lib/pleroma/web/activity_pub/transmogrifier.ex | 78 ++++++++++++++++++++-- lib/pleroma/web/activity_pub/utils.ex | 11 ++- lib/pleroma/web/activity_pub/views/object_view.ex | 6 +- lib/pleroma/web/common_api/activity_draft.ex | 13 ++++ lib/pleroma/web/common_api/utils.ex | 16 +++++ lib/pleroma/web/mastodon_api/views/status_view.ex | 4 +- 9 files changed, 121 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex index 6befc6897..c2e577b49 100644 --- a/lib/pleroma/constants.ex +++ b/lib/pleroma/constants.ex @@ -19,7 +19,8 @@ defmodule Pleroma.Constants do "context_id", "deleted_activity_id", "pleroma_internal", - "generator" + "generator", + "language" ] ) @@ -38,7 +39,8 @@ defmodule Pleroma.Constants do "summary", "sensitive", "attachment", - "generator" + "generator", + "language" ] ) 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 2670e3f17..73101f20f 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 @@ -86,6 +86,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do |> fix_attachments() |> Transmogrifier.fix_emoji() |> Transmogrifier.fix_content_map() + |> Transmogrifier.maybe_add_language() end def changeset(struct, data) 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 d580208df..5ed3ea023 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex @@ -57,6 +57,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do field(:replies_count, :integer, default: 0) field(:like_count, :integer, default: 0) field(:announcement_count, :integer, default: 0) + field(:language, :string) field(:inReplyTo, ObjectValidators.ObjectID) field(:url, ObjectValidators.BareUri) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 0e6c429f9..732d878c4 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -22,6 +22,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do alias Pleroma.Web.Federator import Ecto.Query + import Pleroma.Web.CommonAPI.Utils, only: [is_good_locale_code?: 1] + import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1] require Logger require Pleroma.Constants @@ -42,6 +44,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> fix_content_map() |> fix_addressing() |> fix_summary() + |> maybe_add_language() end def fix_summary(%{"summary" => nil} = object) do @@ -318,6 +321,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_tag(object), do: object + def fix_content_map(%{"content" => content} = object) when not_empty_string(content), do: object + # content map usually only has one language so this will do for now. def fix_content_map(%{"contentMap" => content_map} = object) do content_groups = Map.to_list(content_map) @@ -454,6 +459,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> strip_internal_fields() |> fix_type(fetch_options) |> fix_in_reply_to(fetch_options) + |> maybe_add_language_from_activity(data) data = Map.put(data, "object", object) options = Keyword.put(options, :local, false) @@ -679,6 +685,7 @@ 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 @@ -722,7 +729,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data = data |> Map.put("object", object) - |> Map.merge(Utils.make_json_ld_header()) + |> Map.merge(Utils.make_json_ld_header(data)) |> Map.delete("bcc") {:ok, data} @@ -737,7 +744,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data = data |> Map.put("object", object) - |> Map.merge(Utils.make_json_ld_header()) + |> Map.merge(Utils.make_json_ld_header(data)) |> Map.delete("bcc") {:ok, data} @@ -758,7 +765,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data = data |> strip_internal_fields - |> Map.merge(Utils.make_json_ld_header()) + |> Map.merge(Utils.make_json_ld_header(data)) |> Map.delete("bcc") {:ok, data} @@ -778,7 +785,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data = data |> Map.put("object", object) - |> Map.merge(Utils.make_json_ld_header()) + |> Map.merge(Utils.make_json_ld_header(data)) {:ok, data} end @@ -796,7 +803,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data = data |> Map.put("object", object) - |> Map.merge(Utils.make_json_ld_header()) + |> Map.merge(Utils.make_json_ld_header(data)) {:ok, data} end @@ -807,7 +814,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data |> strip_internal_fields |> maybe_fix_object_url - |> Map.merge(Utils.make_json_ld_header()) + |> Map.merge(Utils.make_json_ld_header(data)) {:ok, data} end @@ -952,4 +959,63 @@ 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 + + def maybe_add_language(object) do + language = + [ + get_language_from_context(object), + get_language_from_content_map(object), + get_language_from_content(object) + ] + |> Enum.find(&is_good_locale_code?(&1)) + + if language do + Map.put(object, "language", language) + else + object + end + end + + def maybe_add_language_from_activity(object, activity) do + language = get_language_from_context(activity) + + if is_good_locale_code?(language) do + Map.put(object, "language", language) + else + object + end + end + + defp get_language_from_context(%{"@context" => context}) when is_list(context) do + case context + |> Enum.find(fn + %{"@language" => language} -> language != "und" + _ -> nil + end) do + %{"@language" => language} -> language + _ -> nil + end + end + + defp get_language_from_context(_), do: nil + + defp get_language_from_content_map(%{"contentMap" => content_map, "content" => source_content}) do + content_groups = Map.to_list(content_map) + + case Enum.find(content_groups, fn {_, content} -> content == source_content end) do + {language, _} -> language + _ -> nil + end + end + + defp get_language_from_content_map(_), do: nil + + defp get_language_from_content(_), do: nil end diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 437220077..2866cf2ce 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -19,6 +19,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do alias Pleroma.Web.Router.Helpers import Ecto.Query + import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1] require Logger require Pleroma.Constants @@ -108,18 +109,24 @@ defmodule Pleroma.Web.ActivityPub.Utils do end end - def make_json_ld_header do + def make_json_ld_header(data \\ %{}) do %{ "@context" => [ "https://www.w3.org/ns/activitystreams", "#{Endpoint.url()}/schemas/litepub-0.1.jsonld", %{ - "@language" => "und" + "@language" => get_language(data) } ] } end + defp get_language(%{"language" => language}) when not_empty_string(language) do + language + end + + defp get_language(_), do: "und" + def make_date do DateTime.utc_now() |> DateTime.to_iso8601() end diff --git a/lib/pleroma/web/activity_pub/views/object_view.ex b/lib/pleroma/web/activity_pub/views/object_view.ex index 63caa915c..13b5b2542 100644 --- a/lib/pleroma/web/activity_pub/views/object_view.ex +++ b/lib/pleroma/web/activity_pub/views/object_view.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do alias Pleroma.Web.ActivityPub.Transmogrifier def render("object.json", %{object: %Object{} = object}) do - base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header() + base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header(object.data) additional = Transmogrifier.prepare_object(object.data) Map.merge(base, additional) @@ -17,7 +17,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} = activity}) when activity_type in ["Create", "Listen"] do - base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header() + base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header(activity.data) object = Object.normalize(activity, fetch: false) additional = @@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do end def render("object.json", %{object: %Activity{} = activity}) do - base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header() + base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header(activity.data) object_id = Object.normalize(activity, id_only: true) additional = diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index 9af635da8..bcbb134bb 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -33,6 +33,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do cc: [], context: nil, sensitive: false, + language: nil, object: nil, preview?: false, changes: %{} @@ -57,6 +58,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do |> content() |> with_valid(&to_and_cc/1) |> with_valid(&context/1) + |> with_valid(&language/1) |> sensitive() |> with_valid(&object/1) |> preview?() @@ -190,6 +192,16 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do %__MODULE__{draft | sensitive: sensitive} end + defp language(draft) do + language = draft.params[:language] + + if Utils.is_good_locale_code?(language) do + %__MODULE__{draft | language: language} + else + draft + end + end + defp object(draft) do emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji) @@ -229,6 +241,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do "mediaType" => Utils.get_content_type(draft.params[:content_type]) }) |> Map.put("generator", draft.params[:generator]) + |> Map.put("language", draft.language) %__MODULE__{draft | object: object} end diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index b9fe0224c..28553c35a 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -494,4 +494,20 @@ defmodule Pleroma.Web.CommonAPI.Utils do {:error, dgettext("errors", "Too many attachments")} end end + + def is_good_locale_code?(code) when is_binary(code) do + code + |> String.codepoints() + |> Enum.all?(&valid_char?/1) + end + + def is_good_locale_code?(_code), do: false + + # [a-zA-Z0-9-] + defp valid_char?(char) do + ("a" <= char and char <= "z") or + ("A" <= char and char <= "Z") or + ("0" <= char and char <= "9") or + char == "-" + 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 dea22f9c2..50d8ebde9 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -200,7 +200,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do mentions: mentions, tags: reblogged[:tags] || [], application: build_application(object.data["generator"]), - language: nil, + language: object.data["language"], emojis: [], pleroma: %{ local: activity.local, @@ -391,7 +391,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do mentions: mentions, tags: build_tags(tags), application: build_application(object.data["generator"]), - language: nil, + language: object.data["language"], emojis: build_emojis(object.data["emoji"]), pleroma: %{ local: activity.local, -- cgit v1.2.3 From 049045cf2ac90dcca074be9b5cf2d8264828f834 Mon Sep 17 00:00:00 2001 From: Haelwenn Date: Fri, 11 Aug 2023 11:44:13 +0000 Subject: Apply lanodan's suggestion --- lib/pleroma/web/common_api/utils.ex | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 28553c35a..229e13504 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -494,20 +494,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do {:error, dgettext("errors", "Too many attachments")} end end - - def is_good_locale_code?(code) when is_binary(code) do - code - |> String.codepoints() - |> Enum.all?(&valid_char?/1) - end + def is_good_locale_code?(code) when is_binary(code), do: code =~ ~r<[A-zA-Z0-9\-]+> def is_good_locale_code?(_code), do: false - - # [a-zA-Z0-9-] - defp valid_char?(char) do - ("a" <= char and char <= "z") or - ("A" <= char and char <= "Z") or - ("0" <= char and char <= "9") or - char == "-" - end end -- cgit v1.2.3 From 04c8f6b4d1e2a9a30f66b0ffb99d7a17a1510a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 11 Aug 2023 13:44:30 +0200 Subject: Add ObjectValidators.LanguageCode type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../object_validators/language_code.ex | 25 ++++++++++++++++++++++ .../object_validators/common_fields.ex | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex (limited to 'lib') diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex new file mode 100644 index 000000000..327279bf8 --- /dev/null +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex @@ -0,0 +1,25 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2023 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode do + use Ecto.Type + + import Pleroma.Web.CommonAPI.Utils, only: [is_good_locale_code?: 1] + + def type, do: :string + + def cast(language) when is_binary(language) do + if is_good_locale_code?(language) do + {:ok, language} + else + {:error, :invalid_language} + end + end + + def cast(_), do: :error + + def dump(data), do: {:ok, data} + + def load(data), do: {:ok, data} +end 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 5ed3ea023..7ba393270 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex @@ -57,7 +57,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do field(:replies_count, :integer, default: 0) field(:like_count, :integer, default: 0) field(:announcement_count, :integer, default: 0) - field(:language, :string) + field(:language, ObjectValidators.LanguageCode) field(:inReplyTo, ObjectValidators.ObjectID) field(:url, ObjectValidators.BareUri) -- cgit v1.2.3 From 366559c5a33c30de181782418cd1b52f65d0ca5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 11 Aug 2023 14:59:58 +0200 Subject: Make status.language == nil for 'und' value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/mastodon_api/views/status_view.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 50d8ebde9..2ae5e14aa 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -200,7 +200,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do mentions: mentions, tags: reblogged[:tags] || [], application: build_application(object.data["generator"]), - language: object.data["language"], + language: get_language(object), emojis: [], pleroma: %{ local: activity.local, @@ -391,7 +391,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do mentions: mentions, tags: build_tags(tags), application: build_application(object.data["generator"]), - language: object.data["language"], + language: get_language(object), emojis: build_emojis(object.data["emoji"]), pleroma: %{ local: activity.local, @@ -756,4 +756,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do defp get_source_content_type(_source) do Utils.get_content_type(nil) end + + defp get_language(%{data: %{"language" => "und"}}), do: nil + + defp get_language(object), do: object.data["language"] end -- cgit v1.2.3 From b430b805c469b33b9862d8f402fa8e63e6bdee8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 11 Aug 2023 16:44:13 +0200 Subject: Lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/common_api/utils.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 229e13504..05a5b818e 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -494,7 +494,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do {:error, dgettext("errors", "Too many attachments")} end end - def is_good_locale_code?(code) when is_binary(code), do: code =~ ~r<[A-zA-Z0-9\-]+> + + def is_good_locale_code?(code) when is_binary(code), do: code =~ ~r<^[a-zA-Z0-9\-]+$> def is_good_locale_code?(_code), do: false end -- cgit v1.2.3 From edc8689d9176e0134dc9d3a45dae5b530f8950e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 19 Aug 2023 15:28:19 +0200 Subject: Move `maybe_add_language` to CommonFixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/activity_pub/object_validator.ex | 27 ++++++----- .../article_note_page_validator.ex | 20 ++++---- .../activity_pub/object_validators/common_fixes.ex | 42 +++++++++++++++++ lib/pleroma/web/activity_pub/transmogrifier.ex | 55 ---------------------- 4 files changed, 67 insertions(+), 77 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 5e0d1aa8e..4ef036f34 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -103,7 +103,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do meta ) when objtype in ~w[Question Answer Audio Video Image Event Article Note Page] do - with {:ok, object_data} <- cast_and_apply_and_stringify_with_history(object), + with {:ok, object_data} <- + cast_and_apply_and_stringify_with_history(object, activity_data: create_activity), meta = Keyword.put(meta, :object_data, object_data), {:ok, create_activity} <- create_activity @@ -213,40 +214,42 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do def validate(o, m), do: {:error, {:validator_not_set, {o, m}}} - def cast_and_apply_and_stringify_with_history(object) do + def cast_and_apply_and_stringify_with_history(object, meta \\ []) do do_separate_with_history(object, fn object -> - with {:ok, object_data} <- cast_and_apply(object), + with {:ok, object_data} <- cast_and_apply(object, meta), object_data <- object_data |> stringify_keys() do {:ok, object_data} end end) end - def cast_and_apply(%{"type" => "ChatMessage"} = object) do + def cast_and_apply(object, meta \\ []) + + def cast_and_apply(%{"type" => "ChatMessage"} = object, _) do ChatMessageValidator.cast_and_apply(object) end - def cast_and_apply(%{"type" => "Question"} = object) do + def cast_and_apply(%{"type" => "Question"} = object, _) do QuestionValidator.cast_and_apply(object) end - def cast_and_apply(%{"type" => "Answer"} = object) do + def cast_and_apply(%{"type" => "Answer"} = object, _) do AnswerValidator.cast_and_apply(object) end - def cast_and_apply(%{"type" => type} = object) when type in ~w[Audio Image Video] do + def cast_and_apply(%{"type" => type} = object, _) when type in ~w[Audio Image Video] do AudioImageVideoValidator.cast_and_apply(object) end - def cast_and_apply(%{"type" => "Event"} = object) do - EventValidator.cast_and_apply(object) + def cast_and_apply(%{"type" => "Event"} = object, meta) do + EventValidator.cast_and_apply(object, meta) end - def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note Page] do - ArticleNotePageValidator.cast_and_apply(object) + def cast_and_apply(%{"type" => type} = object, meta) when type in ~w[Article Note Page] do + ArticleNotePageValidator.cast_and_apply(object, meta) end - def cast_and_apply(o), do: {:error, {:validator_not_set, o}} + def cast_and_apply(o, _), do: {:error, {:validator_not_set, o}} def stringify_keys(object) when is_struct(object) do object 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 73101f20f..9e6a1b0fb 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 @@ -28,21 +28,21 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do field(:replies, {:array, ObjectValidators.ObjectID}, default: []) 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_url(%{"url" => url} = data) when is_bitstring(url), do: data @@ -76,7 +76,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do def fix_attachments(data), do: data - defp fix(data) do + defp fix(data, meta) do data |> CommonFixes.fix_actor() |> CommonFixes.fix_object_defaults() @@ -86,11 +86,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do |> fix_attachments() |> Transmogrifier.fix_emoji() |> Transmogrifier.fix_content_map() - |> Transmogrifier.maybe_add_language() + |> CommonFixes.maybe_add_language(meta) 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/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex index add46d561..66e44afe6 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -10,6 +10,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils + import Pleroma.Web.CommonAPI.Utils, only: [is_good_locale_code?: 1] + def cast_and_filter_recipients(message, field, follower_collection, field_fallback \\ []) do {:ok, data} = ObjectValidators.Recipients.cast(message[field] || field_fallback) @@ -76,4 +78,44 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do Map.put(data, "to", to) end + + def maybe_add_language(object, meta \\ []) do + language = + [ + get_language_from_context(object), + get_language_from_context(Keyword.get(meta, :activity_data)), + get_language_from_content_map(object) + ] + |> Enum.find(&is_good_locale_code?(&1)) + + if language do + Map.put(object, "language", language) + else + object + end + end + + defp get_language_from_context(%{"@context" => context}) when is_list(context) do + case context + |> Enum.find(fn + %{"@language" => language} -> language != "und" + _ -> nil + end) do + %{"@language" => language} -> language + _ -> nil + end + end + + defp get_language_from_context(_), do: nil + + defp get_language_from_content_map(%{"contentMap" => content_map, "content" => source_content}) do + content_groups = Map.to_list(content_map) + + case Enum.find(content_groups, fn {_, content} -> content == source_content end) do + {language, _} -> language + _ -> nil + end + end + + defp get_language_from_content_map(_), do: nil end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 732d878c4..fd7059dea 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -22,7 +22,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do alias Pleroma.Web.Federator import Ecto.Query - import Pleroma.Web.CommonAPI.Utils, only: [is_good_locale_code?: 1] import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1] require Logger @@ -44,7 +43,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> fix_content_map() |> fix_addressing() |> fix_summary() - |> maybe_add_language() end def fix_summary(%{"summary" => nil} = object) do @@ -459,7 +457,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> strip_internal_fields() |> fix_type(fetch_options) |> fix_in_reply_to(fetch_options) - |> maybe_add_language_from_activity(data) data = Map.put(data, "object", object) options = Keyword.put(options, :local, false) @@ -966,56 +963,4 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do end defp maybe_add_content_map(object), do: object - - def maybe_add_language(object) do - language = - [ - get_language_from_context(object), - get_language_from_content_map(object), - get_language_from_content(object) - ] - |> Enum.find(&is_good_locale_code?(&1)) - - if language do - Map.put(object, "language", language) - else - object - end - end - - def maybe_add_language_from_activity(object, activity) do - language = get_language_from_context(activity) - - if is_good_locale_code?(language) do - Map.put(object, "language", language) - else - object - end - end - - defp get_language_from_context(%{"@context" => context}) when is_list(context) do - case context - |> Enum.find(fn - %{"@language" => language} -> language != "und" - _ -> nil - end) do - %{"@language" => language} -> language - _ -> nil - end - end - - defp get_language_from_context(_), do: nil - - defp get_language_from_content_map(%{"contentMap" => content_map, "content" => source_content}) do - content_groups = Map.to_list(content_map) - - case Enum.find(content_groups, fn {_, content} -> content == source_content end) do - {language, _} -> language - _ -> nil - end - end - - defp get_language_from_content_map(_), do: nil - - defp get_language_from_content(_), do: nil end -- cgit v1.2.3 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: 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 + .../activity_pub/object_validators/common_fixes.ex | 8 ++++ .../object_validators/event_validator.ex | 20 +++++---- lib/pleroma/web/activity_pub/transmogrifier.ex | 8 ---- 6 files changed, 69 insertions(+), 17 deletions(-) create mode 100644 lib/pleroma/ecto_type/activity_pub/object_validators/map_of_string.ex (limited to 'lib') 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 -- cgit v1.2.3 From c160ef7b6a4c8d214a7abbb5054993341ee66b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sat, 19 Aug 2023 20:33:42 +0200 Subject: Remove test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/ecto_type/activity_pub/object_validators/map_of_string.ex | 2 +- lib/pleroma/web/activity_pub/object_validators/common_fixes.ex | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') 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 index e86275f92..96b7f2da6 100644 --- 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 @@ -1,5 +1,5 @@ # Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors +# Copyright © 2017-2023 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.MapOfString do 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 b141cc74c..ccc76beed 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -121,7 +121,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do defp get_language_from_content_map(_), do: nil def maybe_add_content_map(%{"language" => language, "content" => content} = object) - when not_empty_string(language) do + when not_empty_string(language) do Map.put(object, "contentMap", Map.put(%{}, language, content)) end -- cgit v1.2.3 From b52d189fcca13088531002ef0bdc0dc5e5df6569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 31 Aug 2023 11:35:09 +0200 Subject: Move is_good_locale_code? to object validator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../ecto_type/activity_pub/object_validators/language_code.ex | 6 ++++-- .../ecto_type/activity_pub/object_validators/map_of_string.ex | 3 ++- lib/pleroma/web/activity_pub/object_validators/common_fixes.ex | 4 +++- lib/pleroma/web/common_api/activity_draft.ex | 5 ++++- lib/pleroma/web/common_api/utils.ex | 4 ---- 5 files changed, 13 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex index 327279bf8..b15e9ec5e 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex @@ -5,8 +5,6 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode do use Ecto.Type - import Pleroma.Web.CommonAPI.Utils, only: [is_good_locale_code?: 1] - def type, do: :string def cast(language) when is_binary(language) do @@ -22,4 +20,8 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode do def dump(data), do: {:ok, data} def load(data), do: {:ok, data} + + def is_good_locale_code?(code) when is_binary(code), do: code =~ ~r<^[a-zA-Z0-9\-]+$> + + def is_good_locale_code?(_code), do: false end 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 index 96b7f2da6..2228edd24 100644 --- 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 @@ -5,7 +5,8 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.MapOfString do use Ecto.Type - import Pleroma.Web.CommonAPI.Utils, only: [is_good_locale_code?: 1] + import Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode, + only: [is_good_locale_code?: 1] def type, do: :map 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 ccc76beed..fa581eba4 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -10,7 +10,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Utils - import Pleroma.Web.CommonAPI.Utils, only: [is_good_locale_code?: 1] + import Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode, + 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 diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex index bcbb134bb..1b6118cf8 100644 --- a/lib/pleroma/web/common_api/activity_draft.ex +++ b/lib/pleroma/web/common_api/activity_draft.ex @@ -10,6 +10,9 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils + import Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode, + only: [is_good_locale_code?: 1] + import Pleroma.Web.Gettext defstruct valid?: true, @@ -195,7 +198,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do defp language(draft) do language = draft.params[:language] - if Utils.is_good_locale_code?(language) do + if is_good_locale_code?(language) do %__MODULE__{draft | language: language} else draft diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 05a5b818e..b9fe0224c 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -494,8 +494,4 @@ defmodule Pleroma.Web.CommonAPI.Utils do {:error, dgettext("errors", "Too many attachments")} end end - - def is_good_locale_code?(code) when is_binary(code), do: code =~ ~r<^[a-zA-Z0-9\-]+$> - - def is_good_locale_code?(_code), do: false end -- cgit v1.2.3 From c5ed684273fa329bc955c59dbc7beed9804fb0f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 7 Sep 2023 15:12:15 +0200 Subject: Rename MapOfString to ContentLanguageMap 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 | 49 ---------------------- .../object_validators/common_fields.ex | 2 +- 2 files changed, 1 insertion(+), 50 deletions(-) delete mode 100644 lib/pleroma/ecto_type/activity_pub/object_validators/map_of_string.ex (limited to 'lib') 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 deleted file mode 100644 index 2228edd24..000000000 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/map_of_string.ex +++ /dev/null @@ -1,49 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2023 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.MapOfString do - use Ecto.Type - - import Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode, - 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/common_fields.ex b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex index 0cef5b533..4a385633a 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex @@ -31,7 +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(:contentMap, ObjectValidators.ContentLanguageMap) field(:published, ObjectValidators.DateTime) field(:updated, ObjectValidators.DateTime) -- cgit v1.2.3 From a3b17dac0bf5da57cbd08335379ddfe4f8919bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 7 Sep 2023 15:14:18 +0200 Subject: Rename test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../object_validators/content_language_map.ex | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 lib/pleroma/ecto_type/activity_pub/object_validators/content_language_map.ex (limited to 'lib') diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/content_language_map.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/content_language_map.ex new file mode 100644 index 000000000..2cc0fda00 --- /dev/null +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/content_language_map.ex @@ -0,0 +1,49 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2023 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ContentLanguageMap do + use Ecto.Type + + import Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode, + 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 -- cgit v1.2.3 From 51aef6b78dcf709872de32a02533e943f08858d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 28 Dec 2023 15:52:59 +0100 Subject: Add language from activity context in ObjectValidator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/activity_pub/object_validator.ex | 35 ++++++++++++---------- .../article_note_page_validator.ex | 20 ++++++------- .../activity_pub/object_validators/common_fixes.ex | 13 ++++++-- .../object_validators/event_validator.ex | 21 ++++++------- 4 files changed, 52 insertions(+), 37 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 4ef036f34..61d896a5b 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -24,6 +24,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do alias Pleroma.Web.ActivityPub.ObjectValidators.AudioImageVideoValidator alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator @@ -104,7 +105,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do ) when objtype in ~w[Question Answer Audio Video Image Event Article Note Page] do with {:ok, object_data} <- - cast_and_apply_and_stringify_with_history(object, activity_data: create_activity), + object + |> CommonFixes.maybe_add_language_from_activity(create_activity) + |> cast_and_apply_and_stringify_with_history(), meta = Keyword.put(meta, :object_data, object_data), {:ok, create_activity} <- create_activity @@ -154,7 +157,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do ) when objtype in ~w[Question Answer Audio Video Event Article Note Page] do with {_, false} <- {:local, Access.get(meta, :local, false)}, - {_, {:ok, object_data, _}} <- {:object_validation, validate(object, meta)}, + {_, {:ok, object_data, _}} <- + {:object_validation, + object + |> CommonFixes.maybe_add_language_from_activity(update_activity) + |> validate(meta)}, meta = Keyword.put(meta, :object_data, object_data), {:ok, update_activity} <- update_activity @@ -214,42 +221,40 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do def validate(o, m), do: {:error, {:validator_not_set, {o, m}}} - def cast_and_apply_and_stringify_with_history(object, meta \\ []) do + def cast_and_apply_and_stringify_with_history(object) do do_separate_with_history(object, fn object -> - with {:ok, object_data} <- cast_and_apply(object, meta), + with {:ok, object_data} <- cast_and_apply(object), object_data <- object_data |> stringify_keys() do {:ok, object_data} end end) end - def cast_and_apply(object, meta \\ []) - - def cast_and_apply(%{"type" => "ChatMessage"} = object, _) do + def cast_and_apply(%{"type" => "ChatMessage"} = object) do ChatMessageValidator.cast_and_apply(object) end - def cast_and_apply(%{"type" => "Question"} = object, _) do + def cast_and_apply(%{"type" => "Question"} = object) do QuestionValidator.cast_and_apply(object) end - def cast_and_apply(%{"type" => "Answer"} = object, _) do + def cast_and_apply(%{"type" => "Answer"} = object) do AnswerValidator.cast_and_apply(object) end - def cast_and_apply(%{"type" => type} = object, _) when type in ~w[Audio Image Video] do + def cast_and_apply(%{"type" => type} = object) when type in ~w[Audio Image Video] do AudioImageVideoValidator.cast_and_apply(object) end - def cast_and_apply(%{"type" => "Event"} = object, meta) do - EventValidator.cast_and_apply(object, meta) + def cast_and_apply(%{"type" => "Event"} = object) do + EventValidator.cast_and_apply(object) end - def cast_and_apply(%{"type" => type} = object, meta) when type in ~w[Article Note Page] do - ArticleNotePageValidator.cast_and_apply(object, meta) + def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note Page] do + ArticleNotePageValidator.cast_and_apply(object) end - def cast_and_apply(o, _), do: {:error, {:validator_not_set, o}} + def cast_and_apply(o), do: {:error, {:validator_not_set, o}} def stringify_keys(object) when is_struct(object) do object 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 417f04312..4e27284aa 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 @@ -28,21 +28,21 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do field(:replies, {:array, ObjectValidators.ObjectID}, default: []) end - def cast_and_apply(data, meta \\ []) do + def cast_and_apply(data) do data - |> cast_data(meta) + |> cast_data() |> apply_action(:insert) end - def cast_and_validate(data, meta \\ []) do + def cast_and_validate(data) do data - |> cast_data(meta) + |> cast_data() |> validate_data() end - def cast_data(data, meta \\ []) do + def cast_data(data) do %__MODULE__{} - |> changeset(data, meta) + |> changeset(data) end defp fix_url(%{"url" => url} = data) when is_bitstring(url), do: data @@ -76,7 +76,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do def fix_attachments(data), do: data - defp fix(data, meta) do + defp fix(data) do data |> CommonFixes.fix_actor() |> CommonFixes.fix_object_defaults() @@ -87,12 +87,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do |> CommonFixes.fix_quote_url() |> Transmogrifier.fix_emoji() |> Transmogrifier.fix_content_map() - |> CommonFixes.maybe_add_language(meta) + |> CommonFixes.maybe_add_language() |> CommonFixes.maybe_add_content_map() end - def changeset(struct, data, meta \\ []) do - data = fix(data, meta) + def changeset(struct, data) do + data = fix(data) struct |> cast(data, __schema__(:fields) -- [:attachment, :tag]) 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 218342136..e732a6430 100644 --- a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -128,11 +128,20 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do def is_object_link_tag(_), do: false - def maybe_add_language(object, meta \\ []) do + def maybe_add_language_from_activity(object, activity) do + language = get_language_from_context(activity) + + if language do + Map.put(object, "language", language) + else + object + end + end + + def maybe_add_language(object) do language = [ get_language_from_context(object), - get_language_from_context(Keyword.get(meta, :activity_data)), get_language_from_content_map(object) ] |> Enum.find(&is_good_locale_code?(&1)) 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 56ca6fe40..ec23770ad 100644 --- a/lib/pleroma/web/activity_pub/object_validators/event_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/event_validator.ex @@ -26,34 +26,35 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do end end - def cast_and_apply(data, meta \\ []) do + def cast_and_apply(data) do data - |> cast_data(meta) + |> cast_data() |> apply_action(:insert) end - def cast_and_validate(data, meta \\ []) do + def cast_and_validate(data) do data - |> cast_data(meta) + |> cast_data() |> validate_data() end - def cast_data(data, meta \\ []) do + @spec cast_data(map()) :: map() + def cast_data(data) do %__MODULE__{} - |> changeset(data, meta) + |> changeset(data) end - defp fix(data, meta) do + defp fix(data) do data |> CommonFixes.fix_actor() |> CommonFixes.fix_object_defaults() |> Transmogrifier.fix_emoji() - |> CommonFixes.maybe_add_language(meta) + |> CommonFixes.maybe_add_language() |> CommonFixes.maybe_add_content_map() end - def changeset(struct, data, meta \\ []) do - data = fix(data, meta) + def changeset(struct, data) do + data = fix(data) struct |> cast(data, __schema__(:fields) -- [:attachment, :tag]) -- cgit v1.2.3 From a6e066a77d5ce65b034cd62775614d5902d29d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Thu, 7 Mar 2024 14:05:45 +0100 Subject: Fix adding language to json ld header, add transmogrifier test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/activity_pub/transmogrifier.ex | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 169ba5db9..b3a3777a2 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -751,12 +751,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do object_id |> Object.normalize(fetch: false) |> Map.get(:data) - |> prepare_object data = data - |> Map.put("object", object) - |> Map.merge(Utils.make_json_ld_header(data)) + |> Map.put("object", prepare_object(object)) + |> Map.merge(Utils.make_json_ld_header(object)) |> Map.delete("bcc") {:ok, data} @@ -764,14 +763,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def prepare_outgoing(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data) when objtype in Pleroma.Constants.updatable_object_types() do - object = - object - |> prepare_object - data = data - |> Map.put("object", object) - |> Map.merge(Utils.make_json_ld_header(data)) + |> Map.put("object", prepare_object(object)) + |> Map.merge(Utils.make_json_ld_header(object)) |> Map.delete("bcc") {:ok, data} @@ -792,7 +787,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data = data |> strip_internal_fields - |> Map.merge(Utils.make_json_ld_header(data)) + |> Map.merge(Utils.make_json_ld_header()) |> Map.delete("bcc") {:ok, data} @@ -812,7 +807,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data = data |> Map.put("object", object) - |> Map.merge(Utils.make_json_ld_header(data)) + |> Map.merge(Utils.make_json_ld_header()) {:ok, data} end @@ -830,7 +825,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do data = data |> Map.put("object", object) - |> Map.merge(Utils.make_json_ld_header(data)) + |> Map.merge(Utils.make_json_ld_header()) {:ok, data} end -- cgit v1.2.3 From a40bf5d24fb75b246b9e11908b24cdcedabcb3af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 28 Jul 2024 13:44:17 +0200 Subject: Fix good_locale_code?/1 regex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex index 3135af1fa..4779deeb0 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/language_code.ex @@ -21,7 +21,7 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode do def load(data), do: {:ok, data} - def good_locale_code?(code) when is_binary(code), do: code =~ ~r<^[a-zA-Z0-9\-]+$> + def good_locale_code?(code) when is_binary(code), do: code =~ ~r<^[a-zA-Z0-9\-]+\z$> def good_locale_code?(_code), do: false end -- cgit v1.2.3 From c94c6eac22663a46d8c2822953e3b8b959a3d1fb Mon Sep 17 00:00:00 2001 From: floatingghost Date: Mon, 5 Dec 2022 12:58:48 +0000 Subject: Remerge of hashtag following (#341) this time with less idiot Co-authored-by: FloatingGhost Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/341 Signed-off-by: mkljczk --- lib/pleroma/hashtag.ex | 27 +++++++++ lib/pleroma/user.ex | 58 +++++++++++++++++++ lib/pleroma/user/hashtag_follow.ex | 49 ++++++++++++++++ lib/pleroma/web/activity_pub/activity_pub.ex | 27 ++++++++- .../web/api_spec/operations/tag_operation.ex | 65 ++++++++++++++++++++++ lib/pleroma/web/api_spec/schemas/tag.ex | 7 ++- .../web/mastodon_api/controllers/tag_controller.ex | 47 ++++++++++++++++ .../controllers/timeline_controller.ex | 6 ++ lib/pleroma/web/mastodon_api/views/tag_view.ex | 21 +++++++ lib/pleroma/web/router.ex | 4 ++ lib/pleroma/web/streamer.ex | 13 ++++- 11 files changed, 321 insertions(+), 3 deletions(-) create mode 100644 lib/pleroma/user/hashtag_follow.ex create mode 100644 lib/pleroma/web/api_spec/operations/tag_operation.ex create mode 100644 lib/pleroma/web/mastodon_api/controllers/tag_controller.ex create mode 100644 lib/pleroma/web/mastodon_api/views/tag_view.ex (limited to 'lib') diff --git a/lib/pleroma/hashtag.ex b/lib/pleroma/hashtag.ex index a43d88220..29e95e3a0 100644 --- a/lib/pleroma/hashtag.ex +++ b/lib/pleroma/hashtag.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Hashtag do alias Ecto.Multi alias Pleroma.Hashtag + alias Pleroma.User.HashtagFollow alias Pleroma.Object alias Pleroma.Repo @@ -27,6 +28,14 @@ defmodule Pleroma.Hashtag do |> String.trim() end + def get_by_id(id) do + Repo.get(Hashtag, id) + end + + def get_by_name(name) do + Repo.get_by(Hashtag, name: normalize_name(name)) + end + def get_or_create_by_name(name) do changeset = changeset(%Hashtag{}, %{name: name}) @@ -103,4 +112,22 @@ defmodule Pleroma.Hashtag do {:ok, deleted_count} end end + + def get_followers(%Hashtag{id: hashtag_id}) do + from(hf in HashtagFollow) + |> where([hf], hf.hashtag_id == ^hashtag_id) + |> join(:inner, [hf], u in assoc(hf, :user)) + |> select([hf, u], u.id) + |> Repo.all() + end + + def get_recipients_for_activity(%Pleroma.Activity{object: %{hashtags: tags}}) + when is_list(tags) do + tags + |> Enum.map(&get_followers/1) + |> List.flatten() + |> Enum.uniq() + end + + def get_recipients_for_activity(_activity), do: [] end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 7a36ece77..ed9421c44 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -19,6 +19,8 @@ defmodule Pleroma.User do alias Pleroma.Emoji alias Pleroma.FollowingRelationship alias Pleroma.Formatter + alias Pleroma.Hashtag + alias Pleroma.User.HashtagFollow alias Pleroma.HTML alias Pleroma.Keys alias Pleroma.MFA @@ -174,6 +176,12 @@ defmodule Pleroma.User do has_many(:outgoing_relationships, UserRelationship, foreign_key: :source_id) has_many(:incoming_relationships, UserRelationship, foreign_key: :target_id) + many_to_many(:followed_hashtags, Hashtag, + on_replace: :delete, + on_delete: :delete_all, + join_through: HashtagFollow + ) + for {relationship_type, [ {outgoing_relation, outgoing_relation_target}, @@ -2861,4 +2869,54 @@ defmodule Pleroma.User do birthday_month: month }) end + + defp maybe_load_followed_hashtags(%User{followed_hashtags: follows} = user) + when is_list(follows), + do: user + + defp maybe_load_followed_hashtags(%User{} = user) do + followed_hashtags = HashtagFollow.get_by_user(user) + %{user | followed_hashtags: followed_hashtags} + end + + def followed_hashtags(%User{followed_hashtags: follows}) + when is_list(follows), + do: follows + + def followed_hashtags(%User{} = user) do + {:ok, user} = + user + |> maybe_load_followed_hashtags() + |> set_cache() + + user.followed_hashtags + end + + def follow_hashtag(%User{} = user, %Hashtag{} = hashtag) do + Logger.debug("Follow hashtag #{hashtag.name} for user #{user.nickname}") + user = maybe_load_followed_hashtags(user) + + with {:ok, _} <- HashtagFollow.new(user, hashtag), + follows <- HashtagFollow.get_by_user(user), + %User{} = user <- user |> Map.put(:followed_hashtags, follows) do + user + |> set_cache() + end + end + + def unfollow_hashtag(%User{} = user, %Hashtag{} = hashtag) do + Logger.debug("Unfollow hashtag #{hashtag.name} for user #{user.nickname}") + user = maybe_load_followed_hashtags(user) + + with {:ok, _} <- HashtagFollow.delete(user, hashtag), + follows <- HashtagFollow.get_by_user(user), + %User{} = user <- user |> Map.put(:followed_hashtags, follows) do + user + |> set_cache() + end + end + + def following_hashtag?(%User{} = user, %Hashtag{} = hashtag) do + not is_nil(HashtagFollow.get(user, hashtag)) + end end diff --git a/lib/pleroma/user/hashtag_follow.ex b/lib/pleroma/user/hashtag_follow.ex new file mode 100644 index 000000000..43ed93f4d --- /dev/null +++ b/lib/pleroma/user/hashtag_follow.ex @@ -0,0 +1,49 @@ +defmodule Pleroma.User.HashtagFollow do + use Ecto.Schema + import Ecto.Query + import Ecto.Changeset + + alias Pleroma.User + alias Pleroma.Hashtag + alias Pleroma.Repo + + schema "user_follows_hashtag" do + belongs_to(:user, User, type: FlakeId.Ecto.CompatType) + belongs_to(:hashtag, Hashtag) + end + + def changeset(%__MODULE__{} = user_hashtag_follow, attrs) do + user_hashtag_follow + |> cast(attrs, [:user_id, :hashtag_id]) + |> unique_constraint(:hashtag_id, + name: :user_hashtag_follows_user_id_hashtag_id_index, + message: "already following" + ) + |> validate_required([:user_id, :hashtag_id]) + end + + def new(%User{} = user, %Hashtag{} = hashtag) do + %__MODULE__{} + |> changeset(%{user_id: user.id, hashtag_id: hashtag.id}) + |> Repo.insert(on_conflict: :nothing) + end + + def delete(%User{} = user, %Hashtag{} = hashtag) do + with %__MODULE__{} = user_hashtag_follow <- get(user, hashtag) do + Repo.delete(user_hashtag_follow) + else + _ -> {:ok, nil} + end + end + + def get(%User{} = user, %Hashtag{} = hashtag) do + from(hf in __MODULE__) + |> where([hf], hf.user_id == ^user.id and hf.hashtag_id == ^hashtag.id) + |> Repo.one() + end + + def get_by_user(%User{} = user) do + Ecto.assoc(user, :followed_hashtags) + |> Repo.all() + end +end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index df8795fe4..62c7a7b31 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -924,6 +924,31 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ) end + # Essentially, either look for activities addressed to `recipients`, _OR_ ones + # that reference a hashtag that the user follows + # Firstly, two fallbacks in case there's no hashtag constraint, or the user doesn't + # follow any + defp restrict_recipients_or_hashtags(query, recipients, user, nil) do + restrict_recipients(query, recipients, user) + end + + defp restrict_recipients_or_hashtags(query, recipients, user, []) do + restrict_recipients(query, recipients, user) + end + + defp restrict_recipients_or_hashtags(query, recipients, _user, hashtag_ids) do + from([activity, object] in query) + |> join(:left, [activity, object], hto in "hashtags_objects", + on: hto.object_id == object.id, + as: :hto + ) + |> where( + [activity, object, hto: hto], + (hto.hashtag_id in ^hashtag_ids and ^Constants.as_public() in activity.recipients) or + fragment("? && ?", ^recipients, activity.recipients) + ) + end + defp restrict_local(query, %{local_only: true}) do from(activity in query, where: activity.local == true) end @@ -1414,7 +1439,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> maybe_preload_report_notes(opts) |> maybe_set_thread_muted_field(opts) |> maybe_order(opts) - |> restrict_recipients(recipients, opts[:user]) + |> restrict_recipients_or_hashtags(recipients, opts[:user], opts[:followed_hashtags]) |> restrict_replies(opts) |> restrict_since(opts) |> restrict_local(opts) diff --git a/lib/pleroma/web/api_spec/operations/tag_operation.ex b/lib/pleroma/web/api_spec/operations/tag_operation.ex new file mode 100644 index 000000000..e22457159 --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/tag_operation.ex @@ -0,0 +1,65 @@ +defmodule Pleroma.Web.ApiSpec.TagOperation do + alias OpenApiSpex.Operation + alias OpenApiSpex.Schema + alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.Tag + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def show_operation do + %Operation{ + tags: ["Tags"], + summary: "Hashtag", + description: "View a hashtag", + security: [%{"oAuth" => ["read"]}], + parameters: [id_param()], + operationId: "TagController.show", + responses: %{ + 200 => Operation.response("Hashtag", "application/json", Tag), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def follow_operation do + %Operation{ + tags: ["Tags"], + summary: "Follow a hashtag", + description: "Follow a hashtag", + security: [%{"oAuth" => ["write:follows"]}], + parameters: [id_param()], + operationId: "TagController.follow", + responses: %{ + 200 => Operation.response("Hashtag", "application/json", Tag), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + def unfollow_operation do + %Operation{ + tags: ["Tags"], + summary: "Unfollow a hashtag", + description: "Unfollow a hashtag", + security: [%{"oAuth" => ["write:follow"]}], + parameters: [id_param()], + operationId: "TagController.unfollow", + responses: %{ + 200 => Operation.response("Hashtag", "application/json", Tag), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + + defp id_param do + Operation.parameter( + :id, + :path, + %Schema{type: :string}, + "Name of the hashtag" + ) + end +end diff --git a/lib/pleroma/web/api_spec/schemas/tag.ex b/lib/pleroma/web/api_spec/schemas/tag.ex index 66bf0ca71..f68dc3f2a 100644 --- a/lib/pleroma/web/api_spec/schemas/tag.ex +++ b/lib/pleroma/web/api_spec/schemas/tag.ex @@ -17,11 +17,16 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Tag do type: :string, format: :uri, description: "A link to the hashtag on the instance" + }, + following: %Schema{ + type: :boolean, + description: "Whether the authenticated user is following the hashtag" } }, example: %{ name: "cofe", - url: "https://lain.com/tag/cofe" + url: "https://lain.com/tag/cofe", + following: false } }) end diff --git a/lib/pleroma/web/mastodon_api/controllers/tag_controller.ex b/lib/pleroma/web/mastodon_api/controllers/tag_controller.ex new file mode 100644 index 000000000..b8995eb00 --- /dev/null +++ b/lib/pleroma/web/mastodon_api/controllers/tag_controller.ex @@ -0,0 +1,47 @@ +defmodule Pleroma.Web.MastodonAPI.TagController do + @moduledoc "Hashtag routes for mastodon API" + use Pleroma.Web, :controller + + alias Pleroma.User + alias Pleroma.Hashtag + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action in [:show]) + + plug( + Pleroma.Web.Plugs.OAuthScopesPlug, + %{scopes: ["write:follows"]} when action in [:follow, :unfollow] + ) + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TagOperation + + def show(conn, %{id: id}) do + with %Hashtag{} = hashtag <- Hashtag.get_by_name(id) do + render(conn, "show.json", tag: hashtag, for_user: conn.assigns.user) + else + _ -> conn |> render_error(:not_found, "Hashtag not found") + end + end + + def follow(conn, %{id: id}) do + with %Hashtag{} = hashtag <- Hashtag.get_by_name(id), + %User{} = user <- conn.assigns.user, + {:ok, _} <- + User.follow_hashtag(user, hashtag) do + render(conn, "show.json", tag: hashtag, for_user: user) + else + _ -> render_error(conn, :not_found, "Hashtag not found") + end + end + + def unfollow(conn, %{id: id}) do + with %Hashtag{} = hashtag <- Hashtag.get_by_name(id), + %User{} = user <- conn.assigns.user, + {:ok, _} <- + User.unfollow_hashtag(user, hashtag) do + render(conn, "show.json", tag: hashtag, for_user: user) + else + _ -> render_error(conn, :not_found, "Hashtag not found") + end + end +end diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 293c61b41..5ee74a80e 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -40,6 +40,11 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do # GET /api/v1/timelines/home def home(%{assigns: %{user: user}} = conn, params) do + followed_hashtags = + user + |> User.followed_hashtags() + |> Enum.map(& &1.id) + params = params |> Map.put(:type, ["Create", "Announce"]) @@ -49,6 +54,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do |> Map.put(:announce_filtering_user, user) |> Map.put(:user, user) |> Map.put(:local_only, params[:local]) + |> Map.put(:followed_hashtags, followed_hashtags) |> Map.delete(:local) activities = diff --git a/lib/pleroma/web/mastodon_api/views/tag_view.ex b/lib/pleroma/web/mastodon_api/views/tag_view.ex new file mode 100644 index 000000000..6e491c261 --- /dev/null +++ b/lib/pleroma/web/mastodon_api/views/tag_view.ex @@ -0,0 +1,21 @@ +defmodule Pleroma.Web.MastodonAPI.TagView do + use Pleroma.Web, :view + alias Pleroma.User + alias Pleroma.Web.Router.Helpers + + def render("show.json", %{tag: tag, for_user: user}) do + following = + with %User{} <- user do + User.following_hashtag?(user, tag) + else + _ -> false + end + + %{ + name: tag.name, + url: Helpers.tag_feed_url(Pleroma.Web.Endpoint, :feed, tag.name), + history: [], + following: following + } + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 0423ca9e2..4bbddbef7 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -755,6 +755,10 @@ defmodule Pleroma.Web.Router do get("/announcements", AnnouncementController, :index) post("/announcements/:id/dismiss", AnnouncementController, :mark_read) + + get("/tags/:id", TagController, :show) + post("/tags/:id/follow", TagController, :follow) + post("/tags/:id/unfollow", TagController, :unfollow) end scope "/api/v1", Pleroma.Web.MastodonAPI do diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index 76dc0f42d..cc149e04c 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -19,6 +19,7 @@ defmodule Pleroma.Web.Streamer do alias Pleroma.Web.OAuth.Token alias Pleroma.Web.Plugs.OAuthScopesPlug alias Pleroma.Web.StreamerView + require Pleroma.Constants @registry Pleroma.Web.StreamerRegistry @@ -305,7 +306,17 @@ defmodule Pleroma.Web.Streamer do User.get_recipients_from_activity(item) |> Enum.map(fn %{id: id} -> "user:#{id}" end) - Enum.each(recipient_topics, fn topic -> + hashtag_recipients = + if Pleroma.Constants.as_public() in item.recipients do + Pleroma.Hashtag.get_recipients_for_activity(item) + |> Enum.map(fn id -> "user:#{id}" end) + else + [] + end + + all_recipients = Enum.uniq(recipient_topics ++ hashtag_recipients) + + Enum.each(all_recipients, fn topic -> push_to_socket(topic, item) end) end -- cgit v1.2.3 From bdb9f888d731e9ac59fe17457eacc49d81c2a54c Mon Sep 17 00:00:00 2001 From: FloatingGhost Date: Sat, 31 Dec 2022 18:05:21 +0000 Subject: Add /api/v1/followed_tags Signed-off-by: mkljczk --- lib/pleroma/pagination.ex | 6 ++-- lib/pleroma/user/hashtag_follow.ex | 8 ++++- .../web/api_spec/operations/tag_operation.ex | 40 +++++++++++++++++++++- lib/pleroma/web/api_spec/schemas/tag.ex | 6 ++++ .../web/mastodon_api/controllers/tag_controller.ex | 32 ++++++++++++++++- lib/pleroma/web/mastodon_api/views/tag_view.ex | 4 +++ lib/pleroma/web/router.ex | 1 + 7 files changed, 91 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex index 8db732cc9..66812b17b 100644 --- a/lib/pleroma/pagination.ex +++ b/lib/pleroma/pagination.ex @@ -89,9 +89,9 @@ defmodule Pleroma.Pagination do defp cast_params(params) do param_types = %{ - min_id: :string, - since_id: :string, - max_id: :string, + min_id: params[:id_type] || :string, + since_id: params[:id_type] || :string, + max_id: params[:id_type] || :string, offset: :integer, limit: :integer, skip_extra_order: :boolean, diff --git a/lib/pleroma/user/hashtag_follow.ex b/lib/pleroma/user/hashtag_follow.ex index 43ed93f4d..dd0254ef4 100644 --- a/lib/pleroma/user/hashtag_follow.ex +++ b/lib/pleroma/user/hashtag_follow.ex @@ -43,7 +43,13 @@ defmodule Pleroma.User.HashtagFollow do end def get_by_user(%User{} = user) do - Ecto.assoc(user, :followed_hashtags) + user + |> followed_hashtags_query() |> Repo.all() end + + def followed_hashtags_query(%User{} = user) do + Ecto.assoc(user, :followed_hashtags) + |> Ecto.Query.order_by([h], desc: h.id) + end end diff --git a/lib/pleroma/web/api_spec/operations/tag_operation.ex b/lib/pleroma/web/api_spec/operations/tag_operation.ex index e22457159..ce4f4ad5b 100644 --- a/lib/pleroma/web/api_spec/operations/tag_operation.ex +++ b/lib/pleroma/web/api_spec/operations/tag_operation.ex @@ -44,7 +44,7 @@ defmodule Pleroma.Web.ApiSpec.TagOperation do tags: ["Tags"], summary: "Unfollow a hashtag", description: "Unfollow a hashtag", - security: [%{"oAuth" => ["write:follow"]}], + security: [%{"oAuth" => ["write:follows"]}], parameters: [id_param()], operationId: "TagController.unfollow", responses: %{ @@ -54,6 +54,26 @@ defmodule Pleroma.Web.ApiSpec.TagOperation do } end + def show_followed_operation do + %Operation{ + tags: ["Tags"], + summary: "Followed hashtags", + description: "View a list of hashtags the currently authenticated user is following", + parameters: pagination_params(), + security: [%{"oAuth" => ["read:follows"]}], + operationId: "TagController.show_followed", + responses: %{ + 200 => + Operation.response("Hashtags", "application/json", %Schema{ + type: :array, + items: Tag + }), + 403 => Operation.response("Forbidden", "application/json", ApiError), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + defp id_param do Operation.parameter( :id, @@ -62,4 +82,22 @@ defmodule Pleroma.Web.ApiSpec.TagOperation do "Name of the hashtag" ) end + + def pagination_params do + [ + Operation.parameter(:max_id, :query, :integer, "Return items older than this ID"), + Operation.parameter( + :min_id, + :query, + :integer, + "Return the oldest items newer than this ID" + ), + Operation.parameter( + :limit, + :query, + %Schema{type: :integer, default: 20}, + "Maximum number of items to return. Will be ignored if it's more than 40" + ) + ] + end end diff --git a/lib/pleroma/web/api_spec/schemas/tag.ex b/lib/pleroma/web/api_spec/schemas/tag.ex index f68dc3f2a..05ff10cd3 100644 --- a/lib/pleroma/web/api_spec/schemas/tag.ex +++ b/lib/pleroma/web/api_spec/schemas/tag.ex @@ -21,6 +21,12 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Tag do following: %Schema{ type: :boolean, description: "Whether the authenticated user is following the hashtag" + }, + history: %Schema{ + type: :array, + items: %Schema{type: :string}, + description: + "A list of historical uses of the hashtag (not implemented, for compatibility only)" } }, example: %{ diff --git a/lib/pleroma/web/mastodon_api/controllers/tag_controller.ex b/lib/pleroma/web/mastodon_api/controllers/tag_controller.ex index b8995eb00..ca5ee48ac 100644 --- a/lib/pleroma/web/mastodon_api/controllers/tag_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/tag_controller.ex @@ -4,9 +4,24 @@ defmodule Pleroma.Web.MastodonAPI.TagController do alias Pleroma.User alias Pleroma.Hashtag + alias Pleroma.Pagination + + import Pleroma.Web.ControllerHelper, + only: [ + add_link_headers: 2 + ] plug(Pleroma.Web.ApiSpec.CastAndValidate) - plug(Pleroma.Web.Plugs.OAuthScopesPlug, %{scopes: ["read"]} when action in [:show]) + + plug( + Pleroma.Web.Plugs.OAuthScopesPlug, + %{scopes: ["read"]} when action in [:show] + ) + + plug( + Pleroma.Web.Plugs.OAuthScopesPlug, + %{scopes: ["read:follows"]} when action in [:show_followed] + ) plug( Pleroma.Web.Plugs.OAuthScopesPlug, @@ -44,4 +59,19 @@ defmodule Pleroma.Web.MastodonAPI.TagController do _ -> render_error(conn, :not_found, "Hashtag not found") end end + + def show_followed(conn, params) do + with %{assigns: %{user: %User{} = user}} <- conn do + params = Map.put(params, :id_type, :integer) + + hashtags = + user + |> User.HashtagFollow.followed_hashtags_query() + |> Pagination.fetch_paginated(params) + + conn + |> add_link_headers(hashtags) + |> render("index.json", tags: hashtags, for_user: user) + end + end end diff --git a/lib/pleroma/web/mastodon_api/views/tag_view.ex b/lib/pleroma/web/mastodon_api/views/tag_view.ex index 6e491c261..e24d423c2 100644 --- a/lib/pleroma/web/mastodon_api/views/tag_view.ex +++ b/lib/pleroma/web/mastodon_api/views/tag_view.ex @@ -3,6 +3,10 @@ defmodule Pleroma.Web.MastodonAPI.TagView do alias Pleroma.User alias Pleroma.Web.Router.Helpers + def render("index.json", %{tags: tags, for_user: user}) do + safe_render_many(tags, __MODULE__, "show.json", %{for_user: user}) + end + def render("show.json", %{tag: tag, for_user: user}) do following = with %User{} <- user do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 4bbddbef7..ca76427ac 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -759,6 +759,7 @@ defmodule Pleroma.Web.Router do get("/tags/:id", TagController, :show) post("/tags/:id/follow", TagController, :follow) post("/tags/:id/unfollow", TagController, :unfollow) + get("/followed_tags", TagController, :show_followed) end scope "/api/v1", Pleroma.Web.MastodonAPI do -- cgit v1.2.3 From f565cf2b5b9d13a407e18aa2f7c52fb12588117b Mon Sep 17 00:00:00 2001 From: mkljczk Date: Mon, 30 Dec 2024 18:11:21 +0100 Subject: update spec Signed-off-by: mkljczk --- lib/pleroma/web/api_spec.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/api_spec.ex b/lib/pleroma/web/api_spec.ex index 314782818..63409870e 100644 --- a/lib/pleroma/web/api_spec.ex +++ b/lib/pleroma/web/api_spec.ex @@ -139,7 +139,8 @@ defmodule Pleroma.Web.ApiSpec do "Search", "Status actions", "Media attachments", - "Bookmark folders" + "Bookmark folders", + "Tags" ] }, %{ -- cgit v1.2.3 From 36b71733a06e1ab6288c2f74968e6f04a002e7ec Mon Sep 17 00:00:00 2001 From: mkljczk Date: Mon, 30 Dec 2024 18:43:21 +0100 Subject: fix alias ordering Signed-off-by: mkljczk --- lib/pleroma/hashtag.ex | 2 +- lib/pleroma/user.ex | 2 +- lib/pleroma/user/hashtag_follow.ex | 2 +- lib/pleroma/web/mastodon_api/controllers/tag_controller.ex | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/hashtag.ex b/lib/pleroma/hashtag.ex index 29e95e3a0..3682f0c14 100644 --- a/lib/pleroma/hashtag.ex +++ b/lib/pleroma/hashtag.ex @@ -10,9 +10,9 @@ defmodule Pleroma.Hashtag do alias Ecto.Multi alias Pleroma.Hashtag - alias Pleroma.User.HashtagFollow alias Pleroma.Object alias Pleroma.Repo + alias Pleroma.User.HashtagFollow schema "hashtags" do field(:name, :string) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index ed9421c44..d9da9ede1 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -20,7 +20,6 @@ defmodule Pleroma.User do alias Pleroma.FollowingRelationship alias Pleroma.Formatter alias Pleroma.Hashtag - alias Pleroma.User.HashtagFollow alias Pleroma.HTML alias Pleroma.Keys alias Pleroma.MFA @@ -29,6 +28,7 @@ defmodule Pleroma.User do alias Pleroma.Registration alias Pleroma.Repo alias Pleroma.User + alias Pleroma.User.HashtagFollow alias Pleroma.UserRelationship alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Builder diff --git a/lib/pleroma/user/hashtag_follow.ex b/lib/pleroma/user/hashtag_follow.ex index dd0254ef4..3e28b130b 100644 --- a/lib/pleroma/user/hashtag_follow.ex +++ b/lib/pleroma/user/hashtag_follow.ex @@ -3,9 +3,9 @@ defmodule Pleroma.User.HashtagFollow do import Ecto.Query import Ecto.Changeset - alias Pleroma.User alias Pleroma.Hashtag alias Pleroma.Repo + alias Pleroma.User schema "user_follows_hashtag" do belongs_to(:user, User, type: FlakeId.Ecto.CompatType) diff --git a/lib/pleroma/web/mastodon_api/controllers/tag_controller.ex b/lib/pleroma/web/mastodon_api/controllers/tag_controller.ex index ca5ee48ac..21c21e984 100644 --- a/lib/pleroma/web/mastodon_api/controllers/tag_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/tag_controller.ex @@ -2,9 +2,9 @@ defmodule Pleroma.Web.MastodonAPI.TagController do @moduledoc "Hashtag routes for mastodon API" use Pleroma.Web, :controller - alias Pleroma.User alias Pleroma.Hashtag alias Pleroma.Pagination + alias Pleroma.User import Pleroma.Web.ControllerHelper, only: [ -- cgit v1.2.3 From 120fbbc97e4430fb87749ca9271d318889dba7ff Mon Sep 17 00:00:00 2001 From: mkljczk Date: Mon, 17 Feb 2025 17:55:03 +0100 Subject: Include contentMap in outgoing posts Signed-off-by: mkljczk --- lib/pleroma/constants.ex | 1 + lib/pleroma/web/activity_pub/transmogrifier.ex | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex index 2d08cd7a1..42751940a 100644 --- a/lib/pleroma/constants.ex +++ b/lib/pleroma/constants.ex @@ -37,6 +37,7 @@ defmodule Pleroma.Constants do "updated", "emoji", "content", + "contentMap", "summary", "sensitive", "attachment", diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index a6f711733..1cea12aa3 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do @moduledoc """ A module to handle coding from internal to wire ActivityPub and back. """ + alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Activity alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Maps @@ -167,7 +168,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_quote_url_and_maybe_fetch(object, options \\ []) do quote_url = - case Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes.fix_quote_url(object) do + case CommonFixes.fix_quote_url(object) do %{"quoteUrl" => quote_url} -> quote_url _ -> nil end @@ -720,6 +721,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> set_reply_to_uri |> set_quote_url |> set_replies + |> CommonFixes.maybe_add_content_map() |> strip_internal_fields |> strip_internal_tags |> set_type -- cgit v1.2.3 From 04af8bfd9c884dde39dd2073402e70cc219d3c6d Mon Sep 17 00:00:00 2001 From: mkljczk Date: Mon, 17 Feb 2025 18:26:24 +0100 Subject: credo Signed-off-by: mkljczk --- lib/pleroma/web/activity_pub/transmogrifier.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 1cea12aa3..4c9956c7a 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -6,7 +6,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do @moduledoc """ A module to handle coding from internal to wire ActivityPub and back. """ - alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Activity alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Maps @@ -17,6 +16,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.Pipeline alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Visibility -- cgit v1.2.3