From 3b63c2928e526f2af1af927174f7444acd012a34 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Thu, 21 Oct 2021 17:55:16 +0200 Subject: [PATCH] Add more metadata elements Signed-off-by: Thomas Citharel --- lib/service/metadata/actor.ex | 28 ++++++++- lib/service/metadata/event.ex | 58 +++++++++++++++++++ lib/service/metadata/instance.ex | 24 ++++---- lib/service/metadata/post.ex | 29 ++++++++++ lib/web/views/json_ld/object_view.ex | 38 +++++++++++- test/service/metadata/instance_test.exs | 4 +- test/service/metadata/metadata_test.exs | 14 +++-- test/support/factory.ex | 4 +- test/tasks/users_test.exs | 2 +- test/web/controllers/feed_controller_test.exs | 4 +- 10 files changed, 180 insertions(+), 25 deletions(-) diff --git a/lib/service/metadata/actor.ex b/lib/service/metadata/actor.ex index e0fc59dc..90046c36 100644 --- a/lib/service/metadata/actor.ex +++ b/lib/service/metadata/actor.ex @@ -20,14 +20,24 @@ defimpl Mobilizon.Service.Metadata, for: Mobilizon.Actors.Actor do [ Tag.tag(:meta, property: "og:title", content: Actor.display_name_and_username(group)), - Tag.tag(:meta, property: "og:url", content: group.url), + Tag.tag(:meta, + property: "og:url", + content: + Endpoint + |> Routes.page_url( + :actor, + Actor.preferred_username_and_domain(group) + ) + |> URI.decode() + ), Tag.tag(:meta, property: "og:description", content: group.summary), Tag.tag(:meta, property: "og:type", content: "profile"), Tag.tag(:meta, property: "profile:username", content: Actor.preferred_username_and_domain(group) ), - Tag.tag(:meta, property: "twitter:card", content: "summary") + Tag.tag(:meta, property: "twitter:card", content: "summary"), + Tag.tag(:meta, property: "twitter:site", content: "@joinmobilizon") ] |> maybe_add_avatar(group) |> add_group_schema(group) @@ -50,8 +60,22 @@ defimpl Mobilizon.Service.Metadata, for: Mobilizon.Actors.Actor do @spec add_group_schema(list(Tag.t()), Actor.t()) :: list(Tag.t()) defp add_group_schema(tags, %Actor{} = group) do + breadcrumbs = %{ + "@context" => "https://schema.org", + "@type" => "BreadcrumbList", + "itemListElement" => [ + %{ + "@type" => "ListItem", + "position" => 1, + "name" => Actor.display_name(group) + } + ] + } + tags ++ [ + ~s{} + |> HTML.raw(), ~s{} |> HTML.raw() ] end diff --git a/lib/service/metadata/event.ex b/lib/service/metadata/event.ex index 6dede3f2..0e361f41 100644 --- a/lib/service/metadata/event.ex +++ b/lib/service/metadata/event.ex @@ -1,9 +1,12 @@ defimpl Mobilizon.Service.Metadata, for: Mobilizon.Events.Event do alias Phoenix.HTML alias Phoenix.HTML.Tag + alias Mobilizon.Actors.Actor alias Mobilizon.Addresses.Address alias Mobilizon.Events.{Event, EventOptions} + alias Mobilizon.Web.Endpoint alias Mobilizon.Web.JsonLD.ObjectView + alias Mobilizon.Web.Router.Helpers, as: Routes import Mobilizon.Service.Metadata.Utils, only: [process_description: 2, strip_tags: 1, datetime_to_string: 2, render_address: 1] @@ -35,9 +38,64 @@ defimpl Mobilizon.Service.Metadata, for: Mobilizon.Events.Event do ] end + breadcrumbs = + if event.attributed_to do + [ + %{ + "@context" => "https://schema.org", + "@type" => "BreadcrumbList", + "itemListElement" => [ + %{ + "@type" => "ListItem", + "position" => 1, + "name" => Actor.display_name(event.attributed_to), + "item" => + Endpoint + |> Routes.page_url( + :actor, + Actor.preferred_username_and_domain(event.attributed_to) + ) + |> URI.decode() + }, + %{ + "@type" => "ListItem", + "position" => 2, + "name" => event.title + } + ] + } + ] + else + [] + end + + breadcrumbs = + breadcrumbs ++ + [ + %{ + "@context" => "https://schema.org", + "@type" => "BreadcrumbList", + "itemListElement" => [ + %{ + "@type" => "ListItem", + "position" => 1, + "name" => "Events", + "item" => "#{Endpoint.url()}/search" + }, + %{ + "@type" => "ListItem", + "position" => 2, + "name" => event.title + } + ] + } + ] + tags ++ [ Tag.tag(:meta, property: "twitter:card", content: "summary_large_image"), + ~s{} + |> HTML.raw(), ~s{} |> HTML.raw() ] end diff --git a/lib/service/metadata/instance.ex b/lib/service/metadata/instance.ex index 776a613d..3372b582 100644 --- a/lib/service/metadata/instance.ex +++ b/lib/service/metadata/instance.ex @@ -20,18 +20,20 @@ defmodule Mobilizon.Service.Metadata.Instance do description = Utils.process_description(Config.instance_description()) title = "#{Config.instance_name()} - Mobilizon" - instance_json_ld = """ - + + instance_json_ld = """ + """ [ diff --git a/lib/service/metadata/post.ex b/lib/service/metadata/post.ex index db3486eb..a0de60ae 100644 --- a/lib/service/metadata/post.ex +++ b/lib/service/metadata/post.ex @@ -1,9 +1,12 @@ defimpl Mobilizon.Service.Metadata, for: Mobilizon.Posts.Post do alias Phoenix.HTML alias Phoenix.HTML.Tag + alias Mobilizon.Actors.Actor alias Mobilizon.Medias.{File, Media} alias Mobilizon.Posts.Post + alias Mobilizon.Web.Endpoint alias Mobilizon.Web.JsonLD.ObjectView + alias Mobilizon.Web.Router.Helpers, as: Routes import Mobilizon.Service.Metadata.Utils, only: [process_description: 2, strip_tags: 1] def build_tags(%Post{} = post, locale \\ "en") do @@ -21,9 +24,35 @@ defimpl Mobilizon.Service.Metadata, for: Mobilizon.Posts.Post do ] |> maybe_add_post_picture(post) + breadcrumbs = %{ + "@context" => "https://schema.org", + "@type" => "BreadcrumbList", + "itemListElement" => [ + %{ + "@type" => "ListItem", + "position" => 1, + "name" => Actor.display_name(post.attributed_to), + "item" => + Endpoint + |> Routes.page_url( + :actor, + Actor.preferred_username_and_domain(post.attributed_to) + ) + |> URI.decode() + }, + %{ + "@type" => "ListItem", + "position" => 2, + "name" => post.title + } + ] + } + tags ++ [ Tag.tag(:meta, property: "twitter:card", content: "summary_large_image"), + ~s{} + |> HTML.raw(), ~s{} |> HTML.raw() ] end diff --git a/lib/web/views/json_ld/object_view.ex b/lib/web/views/json_ld/object_view.ex index f4de971f..9f16009b 100644 --- a/lib/web/views/json_ld/object_view.ex +++ b/lib/web/views/json_ld/object_view.ex @@ -11,13 +11,30 @@ defmodule Mobilizon.Web.JsonLD.ObjectView do @spec render(String.t(), map()) :: map() def render("group.json", %{group: %Actor{} = group}) do - %{ + res = %{ "@context" => "http://schema.org", "@type" => "Organization", "url" => group.url, "name" => group.name || group.preferred_username, "address" => render_address(group) } + + res = + if group.banner do + Map.put(res, "image", group.banner.url) + else + res + end + + if group.physical_address do + Map.put( + res, + "address", + render_one(group.physical_address, ObjectView, "address.json", as: :address) + ) + else + res + end end def render("event.json", %{event: %Event{} = event}) do @@ -93,12 +110,27 @@ defmodule Mobilizon.Web.JsonLD.ObjectView do "@context" => "https://schema.org", "@type" => "Article", "name" => post.title, + "headline" => post.title, "author" => %{ "@type" => "Organization", - "name" => Actor.display_name(post.attributed_to) + "name" => Actor.display_name(post.attributed_to), + "url" => + Endpoint + |> Routes.page_url( + :actor, + Actor.preferred_username_and_domain(post.attributed_to) + ) + |> URI.decode() }, "datePublished" => post.publish_at, - "dateModified" => post.updated_at + "dateModified" => post.updated_at, + "image" => + if(post.picture, + do: [ + post.picture.file.url + ], + else: ["#{Endpoint.url()}/img/mobilizon_default_card.png"] + ) } end diff --git a/test/service/metadata/instance_test.exs b/test/service/metadata/instance_test.exs index e39c67e8..89adc955 100644 --- a/test/service/metadata/instance_test.exs +++ b/test/service/metadata/instance_test.exs @@ -10,7 +10,9 @@ defmodule Mobilizon.Service.Metadata.InstanceTest do description = Utils.process_description(Config.instance_description()) assert Instance.build_tags() |> Utils.stringify_tags() == - "#{title}\n" + """ + #{title} + """ end end end diff --git a/test/service/metadata/metadata_test.exs b/test/service/metadata/metadata_test.exs index 64f0e340..43bdfc8f 100644 --- a/test/service/metadata/metadata_test.exs +++ b/test/service/metadata/metadata_test.exs @@ -18,7 +18,7 @@ defmodule Mobilizon.Service.MetadataTest do assert group |> Metadata.build_tags() |> Metadata.Utils.stringify_tags() == String.trim(""" - + """) assert group @@ -26,7 +26,7 @@ defmodule Mobilizon.Service.MetadataTest do |> Metadata.build_tags() |> Metadata.Utils.stringify_tags() == String.trim(""" - + """) end @@ -100,7 +100,13 @@ defmodule Mobilizon.Service.MetadataTest do |> Floki.attribute("content") |> hd - assert "event.json" |> ObjectView.render(%{event: event}) |> Jason.encode!() == + event_json_ld = "event.json" |> ObjectView.render(%{event: event}) |> Jason.encode!() + + breadcrumbs = """ + [{\"@context\":\"https://schema.org\",\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"item\":\"http://mobilizon.test/search\",\"name\":\"Events\",\"position\":1},{\"@type\":\"ListItem\",\"name\":\"#{event.title}\",\"position\":2}]}] + """ + + assert String.trim(breadcrumbs) <> event_json_ld == document |> Floki.find("script[type=\"application/ld+json\"]") |> Floki.text(js: true) @@ -125,7 +131,7 @@ defmodule Mobilizon.Service.MetadataTest do |> Metadata.build_tags() |> Metadata.Utils.stringify_tags() == String.trim(""" - + """) end end diff --git a/test/support/factory.ex b/test/support/factory.ex index 4294ffcd..4629d928 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -45,6 +45,7 @@ defmodule Mobilizon.Factory do %Mobilizon.Actors.Actor{ preferred_username: preferred_username, + name: sequence("Thomas Named"), domain: nil, followers: [], followings: [], @@ -81,7 +82,8 @@ defmodule Mobilizon.Factory do resources_url: Actor.build_url(preferred_username, :resources), inbox_url: Actor.build_url(preferred_username, :inbox), outbox_url: Actor.build_url(preferred_username, :outbox), - user: nil + user: nil, + physical_address: build(:address) } ) end diff --git a/test/tasks/users_test.exs b/test/tasks/users_test.exs index a7f920ad..22d455fb 100644 --- a/test/tasks/users_test.exs +++ b/test/tasks/users_test.exs @@ -216,7 +216,7 @@ defmodule Mix.Tasks.Mobilizon.UsersTest do actor2 = insert(:actor, user: user) output = - "Informations for the user #{@email}:\n - account status: Activated on #{confirmed_at} (UTC)\n - Role: #{role}\n Identities (2):\n - @#{actor1.preferred_username} / \n - @#{actor2.preferred_username} / \n\n\n" + "Informations for the user #{@email}:\n - account status: Activated on #{confirmed_at} (UTC)\n - Role: #{role}\n Identities (2):\n - @#{actor1.preferred_username} / #{actor1.name}\n - @#{actor2.preferred_username} / #{actor2.name}\n\n\n" Show.run([@email]) assert_received {:mix_shell, :info, [output_received]} diff --git a/test/web/controllers/feed_controller_test.exs b/test/web/controllers/feed_controller_test.exs index c4335dfb..8b8c549e 100644 --- a/test/web/controllers/feed_controller_test.exs +++ b/test/web/controllers/feed_controller_test.exs @@ -30,7 +30,7 @@ defmodule Mobilizon.Web.FeedControllerTest do {:ok, feed} = ElixirFeedParser.parse(conn.resp_body) assert feed.title == - actor.preferred_username <> "'s public events feed on #{Config.instance_name()}" + actor.name <> "'s public events feed on #{Config.instance_name()}" [entry1, entry2] = entries = feed.entries @@ -270,7 +270,7 @@ defmodule Mobilizon.Web.FeedControllerTest do {:ok, feed} = ElixirFeedParser.parse(conn.resp_body) assert feed.title == - "#{actor1.preferred_username}'s private events feed on #{Config.instance_name()}" + "#{actor1.name}'s private events feed on #{Config.instance_name()}" [entry] = feed.entries assert entry.title == event1.title