[#58] ensure all users are linked in MFM content (#61)
authorfloatingghost <hannah@coffee-and-dreams.uk>
Mon, 11 Jul 2022 11:48:29 +0000 (11:48 +0000)
committerfloatingghost <hannah@coffee-and-dreams.uk>
Mon, 11 Jul 2022 11:48:29 +0000 (11:48 +0000)
Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/61

lib/pleroma/formatter.ex
lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex
test/fixtures/misskey/mfm_x_format.json
test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs

index 78325095aac79befc8f5674870548d3c5e278b36..ad29d21d77d464622c2bf73b0cf775b5d83accab 100644 (file)
@@ -32,26 +32,29 @@ defmodule Pleroma.Formatter do
     end
   end
 
+  def mention_tag(%User{id: id} = user, nickname, opts \\ []) do
+    user_url = user.uri || user.ap_id
+    nickname_text = get_nickname_text(nickname, opts)
+
+    :span
+    |> Phoenix.HTML.Tag.content_tag(
+      Phoenix.HTML.Tag.content_tag(
+        :a,
+        ["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)],
+        "data-user": id,
+        class: "u-url mention",
+        href: user_url,
+        rel: "ugc"
+      ),
+      class: "h-card"
+    )
+    |> Phoenix.HTML.safe_to_string()
+  end
+
   def mention_handler("@" <> nickname, buffer, opts, acc) do
     case User.get_cached_by_nickname(nickname) do
-      %User{id: id} = user ->
-        user_url = user.uri || user.ap_id
-        nickname_text = get_nickname_text(nickname, opts)
-
-        link =
-          Phoenix.HTML.Tag.content_tag(
-            :span,
-            Phoenix.HTML.Tag.content_tag(
-              :a,
-              ["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)],
-              "data-user": id,
-              class: "u-url mention",
-              href: user_url,
-              rel: "ugc"
-            ),
-            class: "h-card"
-          )
-          |> Phoenix.HTML.safe_to_string()
+      %User{id: _id} = user ->
+        link = mention_tag(user, nickname, opts)
 
         {link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
 
index e11335170a6fc60252bee6944f12a36415589449..243a25b18f2b3ba976ac9bcb04348d3b43aba6bc 100644 (file)
@@ -4,7 +4,7 @@
 
 defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
   use Ecto.Schema
-
+  alias Pleroma.User
   alias Pleroma.EctoType.ActivityPub.ObjectValidators
   alias Pleroma.Object.Fetcher
   alias Pleroma.Web.CommonAPI.Utils
@@ -81,16 +81,39 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
 
   defp fix_replies(data), do: data
 
+  defp remote_mention_resolver(%{"tag" => tags}, "@" <> nickname = mention, buffer, opts, acc) do
+    with mention_tag <-
+           Enum.find(tags, fn t -> t["type"] == "Mention" && t["name"] == mention end),
+         false <- is_nil(mention_tag),
+         {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(mention_tag["href"]) do
+      link = Pleroma.Formatter.mention_tag(user, nickname, opts)
+      {link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
+    else
+      _ -> {buffer, acc}
+    end
+  end
+
   # https://github.com/misskey-dev/misskey/pull/8787
   defp fix_misskey_content(
          %{"source" => %{"mediaType" => "text/x.misskeymarkdown", "content" => content}} = object
        ) do
-    {linked, _, _} = Utils.format_input(content, "text/x.misskeymarkdown")
+    mention_handler = fn nick, buffer, opts, acc ->
+      remote_mention_resolver(object, nick, buffer, opts, acc)
+    end
+
+    {linked, _, _} =
+      Utils.format_input(content, "text/x.misskeymarkdown", mention_handler: mention_handler)
+
     Map.put(object, "content", linked)
   end
 
   defp fix_misskey_content(%{"_misskey_content" => content} = object) do
-    {linked, _, _} = Utils.format_input(content, "text/x.misskeymarkdown")
+    mention_handler = fn nick, buffer, opts, acc ->
+      remote_mention_resolver(object, nick, buffer, opts, acc)
+    end
+
+    {linked, _, _} =
+      Utils.format_input(content, "text/x.misskeymarkdown", mention_handler: mention_handler)
 
     object
     |> Map.put("source", %{
index 31d6e53689a5941625bdf0ce322fed588d4b53db..a24e90e7e592242e6537d2add7759e95ab6858a6 100644 (file)
@@ -3,9 +3,9 @@
   "type": "Note",
   "attributedTo": "https://misskey.local.live/users/92hzkskwgy",
   "summary": null,
-  "content": "<p><a href=\"https://akkoma.local.live/users/akkoma_user\" class=\"u-url mention\">@akkoma_user@akkoma.local.live</a><span> linkifylink </span><a href=\"https://misskey.local.live/tags/dancedance\" rel=\"tag\">#dancedance</a><span> </span><i><span> mfm goes here</span></i><span> <br><br>## aaa</span></p>",
+  "content": "this gets replaced",
   "source": {
-    "content": "@akkoma_user linkifylink #dancedance $[jelly mfm goes here] \n\n## aaa",
+    "content": "@akkoma_user @remote_user @oops_not_a_mention linkifylink #dancedance $[jelly mfm goes here] \n\n## aaa",
     "mediaType": "text/x.misskeymarkdown"
   },
   "published": "2022-07-10T15:37:36.368Z",
       "type": "Mention",
       "href": "http://localhost:4001/users/akkoma_user",
       "name": "@akkoma_user"
+    },
+    {
+      "type": "Mention",
+      "href": "http://misskey.local.live/users/remote_user",
+      "name": "@remote_user"
     }
   ]
 }
index 62526d95203551b8644498c1047cce549921f712..4ea7a5bc029f375630f6a01479afbd91ff4d223c 100644 (file)
@@ -71,7 +71,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
     end
 
     test "a misskey MFM status with a content field should work and be linked", _ do
-      local_user = insert(:user, %{nickname: "akkoma_user"})
+      local_user =
+        insert(:user, %{nickname: "akkoma_user", ap_id: "http://localhost:4001/users/akkoma_user"})
+
+      remote_user =
+        insert(:user, %{
+          nickname: "remote_user",
+          ap_id: "http://misskey.local.live/users/remote_user"
+        })
 
       insert(:user, %{ap_id: "https://misskey.local.live/users/92hzkskwgy"})
 
@@ -80,23 +87,31 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidatorTest
         |> File.read!()
         |> Jason.decode!()
 
-      expected_content =
-        "<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{local_user.id}\" href=\"#{local_user.ap_id}\" rel=\"ugc\">@<span>akkoma_user</span></a></span> linkifylink <a class=\"hashtag\" data-tag=\"dancedance\" href=\"http://localhost:4001/tag/dancedance\" rel=\"tag ugc\">#dancedance</a> $[jelly mfm goes here] <br><br>## aaa"
-
       %{
         valid?: true,
         changes: %{
-          content: ^expected_content,
+          content: content,
           source: %{
-            "content" => "@akkoma_user linkifylink #dancedance $[jelly mfm goes here] \n\n## aaa",
+            "content" =>
+              "@akkoma_user @remote_user @oops_not_a_mention linkifylink #dancedance $[jelly mfm goes here] \n\n## aaa",
             "mediaType" => "text/x.misskeymarkdown"
           }
         }
       } = ArticleNotePageValidator.cast_and_validate(note)
+
+      assert content =~
+               "<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{local_user.id}\" href=\"#{local_user.ap_id}\" rel=\"ugc\">@<span>akkoma_user</span></a></span>"
+
+      assert content =~
+               "<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"#{remote_user.id}\" href=\"#{remote_user.ap_id}\" rel=\"ugc\">@<span>remote_user</span></a></span>"
+
+      assert content =~ "@oops_not_a_mention"
+      assert content =~ "$[jelly mfm goes here] <br><br>## aaa"
     end
 
     test "a misskey MFM status with a _misskey_content field should work and be linked", _ do
-      local_user = insert(:user, %{nickname: "akkoma_user"})
+      local_user =
+        insert(:user, %{nickname: "akkoma_user", ap_id: "http://localhost:4001/users/akkoma_user"})
 
       insert(:user, %{ap_id: "https://misskey.local.live/users/92hzkskwgy"})