logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://anongit.hacktivis.me/git/pleroma.git/
commit: 2330c506668e8365868ea0126aedd7eb17404e5d
parent e74b6ed348e36ac99bdb1983412ad44c45abdc51
Author: nicole mikołajczyk <me@mkljczk.pl>
Date:   Sat, 29 Nov 2025 18:12:33 +0100

Merge branch 'inlinequotes-mastodon' into 'develop'

MRF InlineQuotePolicy: Don't inline quoted post URL in Mastodon quotes

See merge request pleroma/pleroma!4371

Diffstat:

Achangelog.d/mrf-inlinequotes-mastodon.fix1+
Mlib/pleroma/web/activity_pub/mrf/inline_quote_policy.ex2++
Atest/fixtures/quote_post/mastodon_quote_post.json93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/pleroma/web/activity_pub/mrf/inline_quote_policy_test.exs18++++++++++++++++++
4 files changed, 114 insertions(+), 0 deletions(-)

diff --git a/changelog.d/mrf-inlinequotes-mastodon.fix b/changelog.d/mrf-inlinequotes-mastodon.fix @@ -0,0 +1 @@ +MRF InlineQuotePolicy: Don't inline quoted post URL in Mastodon quote posts diff --git a/lib/pleroma/web/activity_pub/mrf/inline_quote_policy.ex b/lib/pleroma/web/activity_pub/mrf/inline_quote_policy.ex @@ -18,6 +18,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do content =~ quote_url -> true # Does the content already have a .quote-inline span? content =~ "<span class=\"quote-inline\">" -> true + # Does the content already have a .quote-inline p? (Mastodon) + content =~ "<p class=\"quote-inline\">" -> true # No inline quote found true -> false end diff --git a/test/fixtures/quote_post/mastodon_quote_post.json b/test/fixtures/quote_post/mastodon_quote_post.json @@ -0,0 +1,93 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + { + "ostatus": "http://ostatus.org#", + "atomUri": "ostatus:atomUri", + "inReplyToAtomUri": "ostatus:inReplyToAtomUri", + "conversation": "ostatus:conversation", + "sensitive": "as:sensitive", + "toot": "http://joinmastodon.org/ns#", + "votersCount": "toot:votersCount", + "quote": "https://w3id.org/fep/044f#quote", + "quoteUri": "http://fedibird.com/ns#quoteUri", + "_misskey_quote": "https://misskey-hub.net/ns#_misskey_quote", + "quoteAuthorization": { + "@id": "https://w3id.org/fep/044f#quoteAuthorization", + "@type": "@id" + }, + "gts": "https://gotosocial.org/ns#", + "interactionPolicy": { + "@id": "gts:interactionPolicy", + "@type": "@id" + }, + "canQuote": { + "@id": "gts:canQuote", + "@type": "@id" + }, + "automaticApproval": { + "@id": "gts:automaticApproval", + "@type": "@id" + }, + "manualApproval": { + "@id": "gts:manualApproval", + "@type": "@id" + } + } + ], + "id": "https://mastodon.social/users/gwynnion/statuses/115345489087257171", + "type": "Note", + "summary": null, + "inReplyTo": null, + "published": "2025-10-09T17:54:47Z", + "url": "https://mastodon.social/@gwynnion/115345489087257171", + "attributedTo": "https://mastodon.social/users/gwynnion", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://mastodon.social/users/gwynnion/followers" + ], + "sensitive": false, + "atomUri": "https://mastodon.social/users/gwynnion/statuses/115345489087257171", + "inReplyToAtomUri": null, + "conversation": "https://mastodon.social/contexts/109836797527169643-115345489087257171", + "context": "https://mastodon.social/contexts/109836797527169643-115345489087257171", + "content": "<p class=\"quote-inline\">RE: <a href=\"https://mastodon.social/@404mediaco/115344945575874225\" target=\"_blank\" rel=\"nofollow noopener\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"ellipsis\">mastodon.social/@404mediaco/11</span><span class=\"invisible\">5344945575874225</span></a></p><p>Every age verification system is just a scheme for companies and hackers to steal your identity.</p>", + "contentMap": { + "en": "<p class=\"quote-inline\">RE: <a href=\"https://mastodon.social/@404mediaco/115344945575874225\" target=\"_blank\" rel=\"nofollow noopener\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"ellipsis\">mastodon.social/@404mediaco/11</span><span class=\"invisible\">5344945575874225</span></a></p><p>Every age verification system is just a scheme for companies and hackers to steal your identity.</p>" + }, + "quote": "https://mastodon.social/users/404mediaco/statuses/115344945575874225", + "_misskey_quote": "https://mastodon.social/users/404mediaco/statuses/115344945575874225", + "quoteUri": "https://mastodon.social/users/404mediaco/statuses/115344945575874225", + "quoteAuthorization": "https://mastodon.social/users/404mediaco/quote_authorizations/115345489087269783", + "interactionPolicy": { + "canQuote": { + "automaticApproval": [ + "https://www.w3.org/ns/activitystreams#Public" + ] + } + }, + "attachment": [], + "tag": [], + "replies": { + "id": "https://mastodon.social/users/gwynnion/statuses/115345489087257171/replies", + "type": "Collection", + "first": { + "type": "CollectionPage", + "next": "https://mastodon.social/users/gwynnion/statuses/115345489087257171/replies?only_other_accounts=true&page=true", + "partOf": "https://mastodon.social/users/gwynnion/statuses/115345489087257171/replies", + "items": [] + } + }, + "likes": { + "id": "https://mastodon.social/users/gwynnion/statuses/115345489087257171/likes", + "type": "Collection", + "totalItems": 26 + }, + "shares": { + "id": "https://mastodon.social/users/gwynnion/statuses/115345489087257171/shares", + "type": "Collection", + "totalItems": 28 + } +} diff --git a/test/pleroma/web/activity_pub/mrf/inline_quote_policy_test.exs b/test/pleroma/web/activity_pub/mrf/inline_quote_policy_test.exs @@ -109,4 +109,22 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicyTest do {:ok, filtered} = InlineQuotePolicy.filter(activity) assert filtered == activity end + + # Mastodon uses p tags instead of span in their quote posts + # URLs in quoteUri and post content are already mismatched + test "skips objects which already have an .inline-quote p" do + object = File.read!("test/fixtures/quote_post/mastodon_quote_post.json") |> Jason.decode!() + + # Normally the ObjectValidator will fix this before it reaches MRF + object = Map.put(object, "quoteUrl", object["quoteUri"]) + + activity = %{ + "type" => "Create", + "actor" => "https://mastodon.social/users/gwynnion", + "object" => object + } + + {:ok, filtered} = InlineQuotePolicy.filter(activity) + assert filtered == activity + end end