From 0d7462de06f77c8bb21b2a2e623b5306da6dc50f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Menrath?= Date: Sat, 20 Jan 2024 09:28:07 +0100 Subject: [PATCH] Improve the detection of a banner image. - Also accept attachments of type Image - Prefer the key "image" if it is set as the banner image (https://www.w3.org/TR/activitystreams-vocabulary/#dfn-object) - solves #1399 Co-authored-by: Thomas Citharel Signed-off-by: Thomas Citharel --- .../activity_stream/converter/utils.ex | 50 ++++++--- .../activity_stream/converter/utils_test.exs | 76 +++++++++++++ .../mobilizon-post-activity-media-1.json | 106 ++++++++++++++++++ .../mobilizon-post-activity-media-2.json | 106 ++++++++++++++++++ .../mobilizon-post-activity-media.json | 99 ++++++++++++++++ 5 files changed, 422 insertions(+), 15 deletions(-) create mode 100644 test/federation/activity_stream/converter/utils_test.exs create mode 100644 test/fixtures/mobilizon-post-activity-media-1.json create mode 100644 test/fixtures/mobilizon-post-activity-media-2.json create mode 100644 test/fixtures/mobilizon-post-activity-media.json diff --git a/lib/federation/activity_stream/converter/utils.ex b/lib/federation/activity_stream/converter/utils.ex index c0b1100a5..760fdc28c 100644 --- a/lib/federation/activity_stream/converter/utils.ex +++ b/lib/federation/activity_stream/converter/utils.ex @@ -205,9 +205,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do @spec process_pictures(map(), integer()) :: Keyword.t() def process_pictures(object, actor_id) do - attachements = Map.get(object, "attachment", []) - - {banner, media_attachements} = get_medias(attachements) + {banner, media_attachements} = get_medias(object) media_attachements_map = media_attachements @@ -259,24 +257,46 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do do: String.replace(body, old_url, new_url) @spec get_medias(list(map())) :: {map(), list(map())} - defp get_medias(attachments) do - banner = get_banner_picture(attachments) - {banner, Enum.filter(attachments, &(&1["type"] == "Document" && &1["url"] != banner["url"]))} + def get_medias(object) do + banner = get_banner_picture(object) + attachments = Map.get(object, "attachment", []) + {banner, Enum.filter(attachments, &(valid_banner_media?(&1) && &1["url"] != banner["url"]))} end - @spec get_banner_picture(list(map())) :: map() - defp get_banner_picture(attachments) do - # Prefer media with - media_with_picture_name = - Enum.find(attachments, &(&1["type"] == "Document" && &1["name"] == @banner_picture_name)) + @spec get_banner_picture(map()) :: map() + defp get_banner_picture(object) do + attachments = Map.get(object, "attachment", []) + image = Map.get(object, "image", %{}) - case media_with_picture_name do - # If no banner found, use the first media - nil -> Enum.find(attachments, &(&1["type"] == "Document")) - media_with_picture_name -> media_with_picture_name + media_with_picture_name = + Enum.find(attachments, &(valid_banner_media?(&1) && &1["name"] == @banner_picture_name)) + + cond do + # Check if the "image" key is set and of type "Document" or "Image" + is_nil(media_with_picture_name) and valid_banner_media?(image) -> + image + + is_nil(media_with_picture_name) and Enum.find(attachments, &valid_banner_media?/1) -> + Enum.find(attachments, &valid_banner_media?/1) + + !is_nil(media_with_picture_name) -> + media_with_picture_name + + true -> + nil end end + @spec valid_banner_media?(map()) :: boolean() + defp valid_banner_media?(media) do + media |> Map.get("type") |> valid_attachment_type?() + end + + @spec valid_attachment_type?(any()) :: boolean() + defp valid_attachment_type?(type) do + type in ["Document", "Image"] + end + @spec get_address(map | binary | nil) :: Address.t() | nil def get_address(text_address) when is_binary(text_address) do get_address(%{"type" => "Place", "name" => text_address}) diff --git a/test/federation/activity_stream/converter/utils_test.exs b/test/federation/activity_stream/converter/utils_test.exs new file mode 100644 index 000000000..c26f7b2a0 --- /dev/null +++ b/test/federation/activity_stream/converter/utils_test.exs @@ -0,0 +1,76 @@ +defmodule Mobilizon.Federation.ActivityStream.Converter.UtilsTest do + @moduledoc """ + Module to test converting from EventMetadata to AS + """ + use Mobilizon.DataCase + import Mobilizon.Factory + alias Mobilizon.Federation.ActivityStream.Converter.Utils + + describe "get_medias/1" do + test "getting banner from Document attachment" do + data = + File.read!("test/fixtures/mobilizon-post-activity-media.json") + |> Jason.decode!() + |> Map.get("object") + + assert Utils.get_medias(data) == + {%{ + "blurhash" => "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{", + "mediaType" => "image/png", + "name" => nil, + "type" => "Document", + "url" => "https://mobilizon.fr/some-image" + }, []} + end + + test "getting banner from image property" do + data = + File.read!("test/fixtures/mobilizon-post-activity-media-1.json") + |> Jason.decode!() + |> Map.get("object") + + assert Utils.get_medias(data) == + {%{ + "blurhash" => "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{", + "mediaType" => "image/png", + "name" => nil, + "type" => "Image", + "url" => "https://mobilizon.fr/some-image-1" + }, + [ + %{ + "blurhash" => "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{", + "mediaType" => "image/png", + "name" => nil, + "type" => "Document", + "url" => "https://mobilizon.fr/some-image" + } + ]} + end + + test "getting banner from attachment named \"Banner\"" do + data = + File.read!("test/fixtures/mobilizon-post-activity-media-2.json") + |> Jason.decode!() + |> Map.get("object") + + assert Utils.get_medias(data) == + {%{ + "blurhash" => "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{", + "mediaType" => "image/png", + "name" => "Banner", + "type" => "Document", + "url" => "https://mobilizon.fr/some-image-2" + }, + [ + %{ + "blurhash" => "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{", + "mediaType" => "image/png", + "name" => nil, + "type" => "Document", + "url" => "https://mobilizon.fr/some-image-1" + } + ]} + end + end +end diff --git a/test/fixtures/mobilizon-post-activity-media-1.json b/test/fixtures/mobilizon-post-activity-media-1.json new file mode 100644 index 000000000..542111d1d --- /dev/null +++ b/test/fixtures/mobilizon-post-activity-media-1.json @@ -0,0 +1,106 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://litepub.social/litepub/context.jsonld", + { + "Hashtag": "as:Hashtag", + "category": "sc:category", + "ical": "http://www.w3.org/2002/12/cal/ical#", + "joinMode": { + "@id": "mz:joinMode", + "@type": "mz:joinModeType" + }, + "joinModeType": { + "@id": "mz:joinModeType", + "@type": "rdfs:Class" + }, + "maximumAttendeeCapacity": "sc:maximumAttendeeCapacity", + "mz": "https://joinmobilizon.org/ns#", + "repliesModerationOption": { + "@id": "mz:repliesModerationOption", + "@type": "mz:repliesModerationOptionType" + }, + "repliesModerationOptionType": { + "@id": "mz:repliesModerationOptionType", + "@type": "rdfs:Class" + }, + "sc": "http://schema.org#", + "uuid": "sc:identifier" + } + ], + "actor": "https://mobilizon.fr/@metacartes", + "cc": [ + "https://framapiaf.org/users/admin/followers", + "https://framapiaf.org/users/tcit" + ], + "id": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93/activity", + "object": { + "attachment": [ + { + "href": "https://something.org", + "mediaType": "text/html", + "name": "Another link", + "type": "Link" + }, + { + "href": "https://google.com", + "mediaType": "text/html", + "name": "Website", + "type": "Link" + }, + { + "type": "Document", + "mediaType": "image/png", + "url": "https://mobilizon.fr/some-image", + "name": null, + "blurhash": "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{" + } + ], + "attributedTo": "https://mobilizon.fr/@metacartes", + "startTime": "2018-02-12T14:08:20Z", + "cc": [ + "https://framapiaf.org/users/admin/followers", + "https://framapiaf.org/users/tcit" + ], + "content": "

@tcit

", + "category": "TODO remove me", + "id": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93", + "image": { + "type": "Image", + "mediaType": "image/png", + "url": "https://mobilizon.fr/some-image-1", + "name": null, + "blurhash": "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{" + }, + "inReplyTo": null, + "location": { + "type": "Place", + "name": "Locaux de Framasoft", + "id": "https://event1.tcit.fr/address/eeecc11d-0030-43e8-a897-6422876372jd", + "address": { + "type": "PostalAddress", + "streetAddress": "10 Rue Jangot", + "postalCode": "69007", + "addressLocality": "Lyon", + "addressRegion": "Auvergne Rhône Alpes", + "addressCountry": "France" + } + }, + "name": "My first event", + "published": "2018-02-12T14:08:20Z", + "tag": [ + { + "href": "https://framapiaf.org/users/tcit", + "name": "@tcit@framapiaf.org", + "type": "Mention" + } + ], + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Event", + "url": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93", + "uuid": "109ccdfd-ee3e-46e1-a877-6c228763df0c" + }, + "published": "2018-02-12T14:08:20Z", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Create" +} diff --git a/test/fixtures/mobilizon-post-activity-media-2.json b/test/fixtures/mobilizon-post-activity-media-2.json new file mode 100644 index 000000000..b6cdc51ff --- /dev/null +++ b/test/fixtures/mobilizon-post-activity-media-2.json @@ -0,0 +1,106 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://litepub.social/litepub/context.jsonld", + { + "Hashtag": "as:Hashtag", + "category": "sc:category", + "ical": "http://www.w3.org/2002/12/cal/ical#", + "joinMode": { + "@id": "mz:joinMode", + "@type": "mz:joinModeType" + }, + "joinModeType": { + "@id": "mz:joinModeType", + "@type": "rdfs:Class" + }, + "maximumAttendeeCapacity": "sc:maximumAttendeeCapacity", + "mz": "https://joinmobilizon.org/ns#", + "repliesModerationOption": { + "@id": "mz:repliesModerationOption", + "@type": "mz:repliesModerationOptionType" + }, + "repliesModerationOptionType": { + "@id": "mz:repliesModerationOptionType", + "@type": "rdfs:Class" + }, + "sc": "http://schema.org#", + "uuid": "sc:identifier" + } + ], + "actor": "https://mobilizon.fr/@metacartes", + "cc": [ + "https://framapiaf.org/users/admin/followers", + "https://framapiaf.org/users/tcit" + ], + "id": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93/activity", + "object": { + "attachment": [ + { + "href": "https://something.org", + "mediaType": "text/html", + "name": "Another link", + "type": "Link" + }, + { + "href": "https://google.com", + "mediaType": "text/html", + "name": "Website", + "type": "Link" + }, + { + "type": "Document", + "mediaType": "image/png", + "url": "https://mobilizon.fr/some-image-1", + "name": null, + "blurhash": "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{" + }, + { + "type": "Document", + "mediaType": "image/png", + "url": "https://mobilizon.fr/some-image-2", + "name": "Banner", + "blurhash": "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{" + } + ], + "attributedTo": "https://mobilizon.fr/@metacartes", + "startTime": "2018-02-12T14:08:20Z", + "cc": [ + "https://framapiaf.org/users/admin/followers", + "https://framapiaf.org/users/tcit" + ], + "content": "

@tcit

", + "category": "TODO remove me", + "id": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93", + "inReplyTo": null, + "location": { + "type": "Place", + "name": "Locaux de Framasoft", + "id": "https://event1.tcit.fr/address/eeecc11d-0030-43e8-a897-6422876372jd", + "address": { + "type": "PostalAddress", + "streetAddress": "10 Rue Jangot", + "postalCode": "69007", + "addressLocality": "Lyon", + "addressRegion": "Auvergne Rhône Alpes", + "addressCountry": "France" + } + }, + "name": "My first event", + "published": "2018-02-12T14:08:20Z", + "tag": [ + { + "href": "https://framapiaf.org/users/tcit", + "name": "@tcit@framapiaf.org", + "type": "Mention" + } + ], + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Event", + "url": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93", + "uuid": "109ccdfd-ee3e-46e1-a877-6c228763df0c" + }, + "published": "2018-02-12T14:08:20Z", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Create" +} diff --git a/test/fixtures/mobilizon-post-activity-media.json b/test/fixtures/mobilizon-post-activity-media.json new file mode 100644 index 000000000..d6906f2e8 --- /dev/null +++ b/test/fixtures/mobilizon-post-activity-media.json @@ -0,0 +1,99 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://litepub.social/litepub/context.jsonld", + { + "Hashtag": "as:Hashtag", + "category": "sc:category", + "ical": "http://www.w3.org/2002/12/cal/ical#", + "joinMode": { + "@id": "mz:joinMode", + "@type": "mz:joinModeType" + }, + "joinModeType": { + "@id": "mz:joinModeType", + "@type": "rdfs:Class" + }, + "maximumAttendeeCapacity": "sc:maximumAttendeeCapacity", + "mz": "https://joinmobilizon.org/ns#", + "repliesModerationOption": { + "@id": "mz:repliesModerationOption", + "@type": "mz:repliesModerationOptionType" + }, + "repliesModerationOptionType": { + "@id": "mz:repliesModerationOptionType", + "@type": "rdfs:Class" + }, + "sc": "http://schema.org#", + "uuid": "sc:identifier" + } + ], + "actor": "https://mobilizon.fr/@metacartes", + "cc": [ + "https://framapiaf.org/users/admin/followers", + "https://framapiaf.org/users/tcit" + ], + "id": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93/activity", + "object": { + "attachment": [ + { + "href": "https://something.org", + "mediaType": "text/html", + "name": "Another link", + "type": "Link" + }, + { + "href": "https://google.com", + "mediaType": "text/html", + "name": "Website", + "type": "Link" + }, + { + "type": "Document", + "mediaType": "image/png", + "url": "https://mobilizon.fr/some-image", + "name": null, + "blurhash": "U5SY?Z00nOxu7ORP.8-pU^kVS#NGXyxbMxM{" + } + ], + "attributedTo": "https://mobilizon.fr/@metacartes", + "startTime": "2018-02-12T14:08:20Z", + "cc": [ + "https://framapiaf.org/users/admin/followers", + "https://framapiaf.org/users/tcit" + ], + "content": "

@tcit

", + "category": "TODO remove me", + "id": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93", + "inReplyTo": null, + "location": { + "type": "Place", + "name": "Locaux de Framasoft", + "id": "https://event1.tcit.fr/address/eeecc11d-0030-43e8-a897-6422876372jd", + "address": { + "type": "PostalAddress", + "streetAddress": "10 Rue Jangot", + "postalCode": "69007", + "addressLocality": "Lyon", + "addressRegion": "Auvergne Rhône Alpes", + "addressCountry": "France" + } + }, + "name": "My first event", + "published": "2018-02-12T14:08:20Z", + "tag": [ + { + "href": "https://framapiaf.org/users/tcit", + "name": "@tcit@framapiaf.org", + "type": "Mention" + } + ], + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Event", + "url": "https://mobilizon.fr/events/39a0c4a6-f2b6-41dc-bbe2-fc5bff76cc93", + "uuid": "109ccdfd-ee3e-46e1-a877-6c228763df0c" + }, + "published": "2018-02-12T14:08:20Z", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "type": "Create" +}