Merge pull request 'Add htmlMfm key when relevant' (#878) from ilja/akkoma:add_fep-c16b_discovery_mechanism_to_not_always_reparse_mfm into develop
Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/878 Reviewed-by: Oneric <oneric@noreply.akkoma>
This commit is contained in:
commit
f2c2ec5e27
10 changed files with 170 additions and 14 deletions
|
|
@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
## Unreleased
|
||||
|
||||
### Added
|
||||
- We mark our MFM posts as FEP-c16b compliant, and retain remote HTML representations for incoming posts marked as FEP-c16b-compliant. (Safety scrubbers are still applied)
|
||||
- Prometheus stats now exposes failed ActivityPub deliveries
|
||||
which failed all attempts and the failure reason
|
||||
- status and user HTML pages now provide ActivityPub alternate links
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@ Depending on instance configuration the same may be true for GET requests.
|
|||
|
||||
### FEP-c16b: Formatting MFM functions
|
||||
|
||||
The optional extension term `htmlMfm` is currently not used.
|
||||
We set the optional extension term `htmlMfm: true` when using content type "text/x.misskeymarkdown".
|
||||
Incoming messages containing `htmlMfm: true` will not have their content re-parsed.
|
||||
|
||||
## Nodeinfo
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,8 @@ defp remote_mention_resolver(
|
|||
end
|
||||
end
|
||||
|
||||
defp fix_misskey_content(object = %{"htmlMfm" => true}), do: object
|
||||
|
||||
# See https://akkoma.dev/FoundKeyGang/FoundKey/issues/343
|
||||
# Misskey/Foundkey drops some of the custom formatting when it sends remotely
|
||||
# So this basically reprocesses the MFM source
|
||||
|
|
@ -144,20 +146,13 @@ defp fix_misskey_content(
|
|||
# See https://github.com/misskey-dev/misskey/pull/8787
|
||||
# This is for compatibility with older Misskey instances
|
||||
defp fix_misskey_content(%{"_misskey_content" => content} = object) when is_binary(content) do
|
||||
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", %{
|
||||
"content" => content,
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
})
|
||||
|> Map.put("content", linked)
|
||||
|> Map.delete("_misskey_content")
|
||||
|> fix_misskey_content()
|
||||
end
|
||||
|
||||
defp fix_misskey_content(data), do: data
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ defmacro activity_fields do
|
|||
defmacro object_fields do
|
||||
quote bind_quoted: binding() do
|
||||
field(:content, :string)
|
||||
field(:htmlMfm, :boolean)
|
||||
|
||||
field(:published, ObjectValidators.DateTime)
|
||||
field(:updated, ObjectValidators.DateTime)
|
||||
|
|
|
|||
|
|
@ -102,7 +102,8 @@ def make_json_ld_header do
|
|||
"https://www.w3.org/ns/activitystreams",
|
||||
"#{Endpoint.url()}/schemas/litepub-0.1.jsonld",
|
||||
%{
|
||||
"@language" => "und"
|
||||
"@language" => "und",
|
||||
"htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,8 +47,7 @@ defp new(user, params) do
|
|||
end
|
||||
|
||||
def create(user, params) do
|
||||
user
|
||||
|> new(params)
|
||||
new(user, params)
|
||||
|> status()
|
||||
|> summary()
|
||||
|> with_valid(&attachments/1)
|
||||
|
|
@ -236,6 +235,7 @@ defp object(draft) do
|
|||
end
|
||||
|
||||
emoji = Map.merge(emoji, summary_emoji)
|
||||
media_type = Utils.get_content_type(draft.params[:content_type])
|
||||
{:ok, note_data, _meta} = Builder.note(draft)
|
||||
|
||||
object =
|
||||
|
|
@ -243,14 +243,18 @@ defp object(draft) do
|
|||
|> Map.put("emoji", emoji)
|
||||
|> Map.put("source", %{
|
||||
"content" => draft.status,
|
||||
"mediaType" => Utils.get_content_type(draft.params[:content_type])
|
||||
"mediaType" => media_type
|
||||
})
|
||||
|> maybe_put("htmlMfm", true, media_type == "text/x.misskeymarkdown")
|
||||
|> Map.put("generator", draft.params[:generator])
|
||||
|> Map.put("contentMap", draft.content_map)
|
||||
|
||||
%__MODULE__{draft | object: object}
|
||||
end
|
||||
|
||||
defp maybe_put(map, key, value, true), do: map |> Map.put(key, value)
|
||||
defp maybe_put(map, _, _, _), do: map
|
||||
|
||||
defp preview?(draft) do
|
||||
preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview])
|
||||
%__MODULE__{draft | preview?: preview?}
|
||||
|
|
|
|||
|
|
@ -191,6 +191,31 @@ test "a misskey MFM status with a _misskey_content field should work and be link
|
|||
end
|
||||
end
|
||||
|
||||
test "an MFM status with htmlMfm should not have its content re-parsed" do
|
||||
insert(:user, %{ap_id: "https://misskey.local.live/users/92hzkskwgy"})
|
||||
|
||||
note =
|
||||
"test/fixtures/misskey/mfm_x_format.json"
|
||||
|> File.read!()
|
||||
|> Jason.decode!()
|
||||
|> Map.put("htmlMfm", true)
|
||||
|> Map.put("content", "do not re-parse")
|
||||
|
||||
changes = ArticleNotePageValidator.cast_and_validate(note)
|
||||
|
||||
%{
|
||||
valid?: true,
|
||||
changes: %{
|
||||
content: content,
|
||||
source: %{
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
}
|
||||
}
|
||||
} = changes
|
||||
|
||||
assert content == "do not re-parse"
|
||||
end
|
||||
|
||||
test "a Note without replies/first/items validates" do
|
||||
insert(:user, ap_id: "https://mastodon.social/users/emelie")
|
||||
|
||||
|
|
|
|||
|
|
@ -144,7 +144,8 @@ test "make_json_ld_header/0" do
|
|||
"https://www.w3.org/ns/activitystreams",
|
||||
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||
%{
|
||||
"@language" => "und"
|
||||
"@language" => "und",
|
||||
"htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -534,6 +534,34 @@ test "it allows to address a list" do
|
|||
assert activity.data["listMessage"] == list.ap_id
|
||||
end
|
||||
|
||||
test "it adds the htmlMFM term to MFM posts and properly processes it" do
|
||||
user = insert(:user)
|
||||
|
||||
assert {:ok,
|
||||
%Pleroma.Activity{
|
||||
object: %Pleroma.Object{
|
||||
data: %{
|
||||
"content" => content,
|
||||
"source" => %{
|
||||
"content" => source_content,
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
},
|
||||
"htmlMfm" => html_mfm
|
||||
}
|
||||
}
|
||||
}} =
|
||||
CommonAPI.post(user, %{
|
||||
status: "<p class='scrub-this'>$[spin 13:37]</p>",
|
||||
content_type: "text/x.misskeymarkdown"
|
||||
})
|
||||
|
||||
assert html_mfm == true
|
||||
assert content =~ "mfm-spin"
|
||||
assert content =~ "13:37"
|
||||
refute content =~ "scrub-this"
|
||||
assert source_content == "<p class='scrub-this'>$[spin 13:37]</p>"
|
||||
end
|
||||
|
||||
test "it returns error when status is empty and no attachments" do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
|
|||
|
|
@ -135,6 +135,105 @@ test "successfully processes incoming AP docs with correct origin" do
|
|||
assert {:cancel, :already_present} = ObanHelpers.perform(job)
|
||||
end
|
||||
|
||||
test "properly processes objects with the htmlMfm attribute true" do
|
||||
params = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"actor" => "http://mastodon.example.org/users/admin",
|
||||
"type" => "Create",
|
||||
"id" => "http://mastodon.example.org/users/admin/activities/1",
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"content" => "<p this-should-be-scrubbed-away>this is the original content</p>",
|
||||
"source" => %{
|
||||
"content" =>
|
||||
"this source content is irrelevant because the object content should be kept",
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
},
|
||||
"htmlMfm" => true,
|
||||
"id" => "http://mastodon.example.org/users/admin/objects/1",
|
||||
"attributedTo" => "http://mastodon.example.org/users/admin",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
||||
},
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
||||
}
|
||||
|
||||
{:ok, job} = Federator.incoming_ap_doc(params)
|
||||
{:ok, %Pleroma.Activity{data: %{"object" => object_ap_id}}} = ObanHelpers.perform(job)
|
||||
|
||||
%Pleroma.Object{data: %{"content" => content, "htmlMfm" => html_mfm}} =
|
||||
Pleroma.Object.get_by_ap_id(object_ap_id)
|
||||
|
||||
assert html_mfm == true
|
||||
refute content =~ "this-should-be-scrubbed-away"
|
||||
refute content =~ "source content is irrelevant"
|
||||
assert content =~ "this is the original content"
|
||||
end
|
||||
|
||||
test "properly processes objects with the htmlMfm attribute false" do
|
||||
params = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"actor" => "http://mastodon.example.org/users/admin",
|
||||
"type" => "Create",
|
||||
"id" => "http://mastodon.example.org/users/admin/activities/1",
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"content" => "<p this-should-be-scrubbed-away>this is the original content</p>",
|
||||
"source" => %{
|
||||
"content" => "<p this-should-be-scrubbed-away>$[spin the source content is used]</p>",
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
},
|
||||
"htmlMfm" => false,
|
||||
"id" => "http://mastodon.example.org/users/admin/objects/1",
|
||||
"attributedTo" => "http://mastodon.example.org/users/admin",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
||||
},
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
||||
}
|
||||
|
||||
{:ok, job} = Federator.incoming_ap_doc(params)
|
||||
{:ok, %Pleroma.Activity{data: %{"object" => object_ap_id}}} = ObanHelpers.perform(job)
|
||||
|
||||
%Pleroma.Object{data: %{"content" => content, "htmlMfm" => html_mfm}} =
|
||||
Pleroma.Object.get_by_ap_id(object_ap_id)
|
||||
|
||||
assert html_mfm == false
|
||||
refute content =~ "this-should-be-scrubbed-away"
|
||||
assert content =~ "the source content is used"
|
||||
refute content =~ "this is the original content"
|
||||
end
|
||||
|
||||
test "properly processes objects with the htmlMfm attribute not set" do
|
||||
params = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"actor" => "http://mastodon.example.org/users/admin",
|
||||
"type" => "Create",
|
||||
"id" => "http://mastodon.example.org/users/admin/activities/1",
|
||||
"object" => %{
|
||||
"type" => "Note",
|
||||
"content" => "<p this-should-be-scrubbed-away>this is the original content</p>",
|
||||
"source" => %{
|
||||
"content" => "<p this-should-be-scrubbed-away>$[spin the source content is used]</p>",
|
||||
"mediaType" => "text/x.misskeymarkdown"
|
||||
},
|
||||
"id" => "http://mastodon.example.org/users/admin/objects/1",
|
||||
"attributedTo" => "http://mastodon.example.org/users/admin",
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
||||
},
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"]
|
||||
}
|
||||
|
||||
{:ok, job} = Federator.incoming_ap_doc(params)
|
||||
{:ok, %Pleroma.Activity{data: %{"object" => object_ap_id}}} = ObanHelpers.perform(job)
|
||||
|
||||
%Pleroma.Object{data: %{"content" => content} = data} =
|
||||
Pleroma.Object.get_by_ap_id(object_ap_id)
|
||||
|
||||
assert Map.get(data, "htmlMfm") == nil
|
||||
refute content =~ "this-should-be-scrubbed-away"
|
||||
assert content =~ "the source content is used"
|
||||
refute content =~ "this is the original content"
|
||||
end
|
||||
|
||||
test "successfully normalises public scope descriptors" do
|
||||
params = %{
|
||||
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||
|
|
|
|||
Loading…
Reference in a new issue