MRF.InlineQuotePolicy: Add link to post URL, not ID
ci/woodpecker/pr/lint Pipeline was successful Details
ci/woodpecker/pr/test Pipeline failed Details
ci/woodpecker/pr/build-arm64 unknown status Details
ci/woodpecker/pr/build-amd64 unknown status Details
ci/woodpecker/pr/docs unknown status Details

"id" is used for the canonical link to the AS2 representation of an object.
"url" is typically used for the canonical link to the HTTP representation.
It is what we use, for example, when following the "external source" link
in the frontend. However, it's not the link we include in the post contents
for quote posts.

Using URL instead means we include a more user-friendly URL for Mastodon,
and a working (in the browser) URL for Threads
This commit is contained in:
Erin Shepherd 2024-04-02 23:57:27 +02:00
parent 2d439034ca
commit 75d9e2b375
2 changed files with 35 additions and 7 deletions

View File

@ -6,14 +6,29 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
@moduledoc "Force a quote line into the message content."
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Object
defp build_inline_quote(prefix, url) do
"<span class=\"quote-inline\"><br/><br/>#{prefix}: <a href=\"#{url}\">#{url}</a></span>"
end
defp has_inline_quote?(content, quote_url) do
defp resolve_urls(quote_url) do
# Fetching here can cause infinite recursion as we run this logic on inbound objects too
# This is probably not a problem - its an exceptional corner case for a local user to quote
# a post which doesn't exist
with %Object{} = obj <- Object.normalize(quote_url, fetch: false) do
id = obj.data["id"]
url = Map.get(obj.data, "url", id)
{id, url, [id, url, quote_url]}
else
_ -> {quote_url, quote_url, [quote_url]}
end
end
defp has_inline_quote?(content, urls) do
cond do
# Does the quote URL exist in the content?
content =~ quote_url -> true
Enum.any?(urls, fn url -> content =~ url end) -> true
# Does the content already have a .quote-inline span?
content =~ "<span class=\"quote-inline\">" -> true
# No inline quote found
@ -22,18 +37,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
end
defp filter_object(%{"quoteUri" => quote_url} = object) do
{id, preferred_url, all_urls} = resolve_urls(quote_url)
object = Map.put(object, "quoteUri", id)
content = object["content"] || ""
if has_inline_quote?(content, quote_url) do
if has_inline_quote?(content, all_urls) do
object
else
prefix = Pleroma.Config.get([:mrf_inline_quote, :prefix])
content =
if String.ends_with?(content, "</p>") do
String.trim_trailing(content, "</p>") <> build_inline_quote(prefix, quote_url) <> "</p>"
String.trim_trailing(content, "</p>") <>
build_inline_quote(prefix, preferred_url) <> "</p>"
else
content <> build_inline_quote(prefix, quote_url)
content <> build_inline_quote(prefix, preferred_url)
end
Map.put(object, "content", content)

View File

@ -4,10 +4,16 @@
defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicyTest do
alias Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy
alias Pleroma.Object
use Pleroma.DataCase
setup_all do
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
end
test "adds quote URL to post content" do
quote_url = "https://example.com/objects/1234"
quote_url = "https://mastodon.social/users/emelie/statuses/101849165031453009"
activity = %{
"type" => "Create",
@ -19,10 +25,13 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicyTest do
}
}
# Prefetch the quoted post
%Object{} = Object.normalize(quote_url, fetch: true)
{:ok, %{"object" => %{"content" => filtered}}} = InlineQuotePolicy.filter(activity)
assert filtered ==
"<p>Nice post<span class=\"quote-inline\"><br/><br/>RE: <a href=\"https://example.com/objects/1234\">https://example.com/objects/1234</a></span></p>"
"<p>Nice post<span class=\"quote-inline\"><br/><br/>RE: <a href=\"https://mastodon.social/@emelie/101849165031453009\">https://mastodon.social/@emelie/101849165031453009</a></span></p>"
end
test "ignores Misskey quote posts" do