Merge pull request 'web/metadata: provide alternate link for ActivityPub' (#905) from Oneric/akkoma:metadata_aplink into develop

Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/905
This commit is contained in:
Oneric 2025-05-09 20:20:04 +00:00
commit 487473cd75
4 changed files with 91 additions and 1 deletions

View file

@ -64,7 +64,11 @@ def activity_nsfw?(_) do
defp activated_providers do defp activated_providers do
unless Pleroma.Config.restrict_unauthenticated_access?(:activities, :local) do unless Pleroma.Config.restrict_unauthenticated_access?(:activities, :local) do
[Pleroma.Web.Metadata.Providers.Feed | Pleroma.Config.get([__MODULE__, :providers], [])] [
Pleroma.Web.Metadata.Providers.Feed,
Pleroma.Web.Metadata.Providers.ApUrl
| Pleroma.Config.get([__MODULE__, :providers], [])
]
else else
[] []
end end

View file

@ -0,0 +1,35 @@
# Akkoma: Magically expressive social media
# Copyright © 2025 Akkoma Authors <https://akkoma.dev/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata.Providers.ApUrl do
alias Pleroma.Web.Metadata.Providers.Provider
@behaviour Provider
defp alt_link(uri, type) do
{
:link,
[rel: "alternate", href: uri, type: type],
[]
}
end
defp ap_alt_links(uri) do
[
alt_link(uri, "application/activity+json"),
alt_link(uri, "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"")
]
end
@impl Provider
def build_tags(%{object: %{data: %{"id" => ap_id}}}) when is_binary(ap_id) do
ap_alt_links(ap_id)
end
def build_tags(%{user: %{ap_id: ap_id}}) when is_binary(ap_id) do
ap_alt_links(ap_id)
end
def build_tags(_), do: []
end

View file

@ -0,0 +1,31 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Metadata.Providers.ApUrlTest do
use Pleroma.DataCase, async: true
import Pleroma.Factory
alias Pleroma.Web.Metadata.Providers.ApUrl
@ap_type_compliant "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
@ap_type_mastodon "application/activity+json"
test "it preferentially renders a link to a post" do
user = insert(:user)
note = insert(:note, user: user)
assert ApUrl.build_tags(%{object: note, user: user}) == [
{:link, [rel: "alternate", href: note.data["id"], type: @ap_type_mastodon], []},
{:link, [rel: "alternate", href: note.data["id"], type: @ap_type_compliant], []}
]
end
test "it renders a link to a user" do
user = insert(:user)
assert ApUrl.build_tags(%{user: user}) == [
{:link, [rel: "alternate", href: user.ap_id, type: @ap_type_mastodon], []},
{:link, [rel: "alternate", href: user.ap_id, type: @ap_type_compliant], []}
]
end
end

View file

@ -321,6 +321,16 @@ defp meta_find_twitter(document, name) do
Floki.find(document, "head>meta[name=\"twitter:" <> name <> "\"]") Floki.find(document, "head>meta[name=\"twitter:" <> name <> "\"]")
end end
defp meta_find_alt_links(document) do
Floki.find(document, "head>link[rel=\"alternate\"]")
|> Enum.map(fn {_, attr, _} ->
{
:proplists.get_value("type", attr),
:proplists.get_value("href", attr)
}
end)
end
# Detailed metadata tests are already done for each builder individually, so just # Detailed metadata tests are already done for each builder individually, so just
# one check per type of content should suffice to ensure we're calling the providers correctly # one check per type of content should suffice to ensure we're calling the providers correctly
describe "metadata tags for" do describe "metadata tags for" do
@ -350,6 +360,8 @@ test "user profile", %{conn: conn, user: user, user_avatar_url: user_avatar_url}
[{"meta", tw_desc, _}] = meta_find_twitter(document, "description") [{"meta", tw_desc, _}] = meta_find_twitter(document, "description")
[{"meta", tw_img, _}] = meta_find_twitter(document, "image") [{"meta", tw_img, _}] = meta_find_twitter(document, "image")
alt_links = meta_find_alt_links(document)
assert meta_content(og_type) == "article" assert meta_content(og_type) == "article"
assert meta_content(og_title) == Pleroma.Web.Metadata.Utils.user_name_string(user) assert meta_content(og_title) == Pleroma.Web.Metadata.Utils.user_name_string(user)
assert meta_content(og_url) == user.ap_id assert meta_content(og_url) == user.ap_id
@ -362,6 +374,8 @@ test "user profile", %{conn: conn, user: user, user_avatar_url: user_avatar_url}
assert meta_content(tw_title) == meta_content(og_title) assert meta_content(tw_title) == meta_content(og_title)
assert meta_content(tw_desc) == meta_content(og_desc) assert meta_content(tw_desc) == meta_content(og_desc)
assert meta_content(tw_img) == meta_content(og_img) assert meta_content(tw_img) == meta_content(og_img)
assert Enum.any?(alt_links, fn e -> e == {"application/activity+json", user.ap_id} end)
end end
test "text-only post", %{conn: conn, user: user, user_avatar_url: user_avatar_url} do test "text-only post", %{conn: conn, user: user, user_avatar_url: user_avatar_url} do
@ -386,6 +400,8 @@ test "text-only post", %{conn: conn, user: user, user_avatar_url: user_avatar_ur
[{"meta", tw_desc, _}] = meta_find_twitter(document, "description") [{"meta", tw_desc, _}] = meta_find_twitter(document, "description")
[{"meta", tw_img, _}] = meta_find_twitter(document, "image") [{"meta", tw_img, _}] = meta_find_twitter(document, "image")
alt_links = meta_find_alt_links(document)
assert meta_content(og_type) == "article" assert meta_content(og_type) == "article"
assert meta_content(og_title) == Pleroma.Web.Metadata.Utils.user_name_string(user) assert meta_content(og_title) == Pleroma.Web.Metadata.Utils.user_name_string(user)
assert meta_content(og_url) == activity.data["id"] assert meta_content(og_url) == activity.data["id"]
@ -398,6 +414,10 @@ test "text-only post", %{conn: conn, user: user, user_avatar_url: user_avatar_ur
assert meta_content(tw_title) == meta_content(og_title) assert meta_content(tw_title) == meta_content(og_title)
assert meta_content(tw_desc) == meta_content(og_desc) assert meta_content(tw_desc) == meta_content(og_desc)
assert meta_content(tw_img) == meta_content(og_img) assert meta_content(tw_img) == meta_content(og_img)
assert Enum.any?(alt_links, fn e ->
e == {"application/activity+json", activity.object.data["id"]}
end)
end end
test "post with attachments", %{conn: conn, user: user} do test "post with attachments", %{conn: conn, user: user} do