diff --git a/lib/pleroma/web/metadata/opengraph.ex b/lib/pleroma/web/metadata/opengraph.ex index 190377767..4d6639c84 100644 --- a/lib/pleroma/web/metadata/opengraph.ex +++ b/lib/pleroma/web/metadata/opengraph.ex @@ -3,12 +3,10 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.OpenGraph do - alias Pleroma.HTML - alias Pleroma.Formatter alias Pleroma.User alias Pleroma.Web.Metadata - alias Pleroma.Web.MediaProxy alias Pleroma.Web.Metadata.Providers.Provider + alias Pleroma.Web.Metadata.Utils @behaviour Provider @@ -19,7 +17,7 @@ def build_tags(%{ user: user }) do attachments = build_attachments(object) - scrubbed_content = scrub_html_and_truncate(object) + scrubbed_content = Utils.scrub_html_and_truncate(object) # Zero width space content = if scrubbed_content != "" and scrubbed_content != "\u200B" do @@ -44,13 +42,14 @@ def build_tags(%{ {:meta, [ property: "og:description", - content: "#{user_name_string(user)}" <> content + content: "#{Utils.user_name_string(user)}" <> content ], []}, {:meta, [property: "og:type", content: "website"], []} ] ++ if attachments == [] or Metadata.activity_nsfw?(object) do [ - {:meta, [property: "og:image", content: attachment_url(User.avatar_url(user))], []}, + {:meta, [property: "og:image", content: Utils.attachment_url(User.avatar_url(user))], + []}, {:meta, [property: "og:image:width", content: 150], []}, {:meta, [property: "og:image:height", content: 150], []} ] @@ -61,17 +60,17 @@ def build_tags(%{ @impl Provider def build_tags(%{user: user}) do - with truncated_bio = scrub_html_and_truncate(user.bio || "") do + with truncated_bio = Utils.scrub_html_and_truncate(user.bio || "") do [ {:meta, [ property: "og:title", - content: user_name_string(user) + content: Utils.user_name_string(user) ], []}, {:meta, [property: "og:url", content: User.profile_url(user)], []}, {:meta, [property: "og:description", content: truncated_bio], []}, {:meta, [property: "og:type", content: "website"], []}, - {:meta, [property: "og:image", content: attachment_url(User.avatar_url(user))], []}, + {:meta, [property: "og:image", content: Utils.attachment_url(User.avatar_url(user))], []}, {:meta, [property: "og:image:width", content: 150], []}, {:meta, [property: "og:image:height", content: 150], []} ] @@ -93,13 +92,14 @@ defp build_attachments(%{data: %{"attachment" => attachments}}) do case media_type do "audio" -> [ - {:meta, [property: "og:" <> media_type, content: attachment_url(url["href"])], []} + {:meta, + [property: "og:" <> media_type, content: Utils.attachment_url(url["href"])], []} | acc ] "image" -> [ - {:meta, [property: "og:" <> media_type, content: attachment_url(url["href"])], + {:meta, [property: "og:" <> media_type, content: Utils.attachment_url(url["href"])], []}, {:meta, [property: "og:image:width", content: 150], []}, {:meta, [property: "og:image:height", content: 150], []} @@ -108,7 +108,8 @@ defp build_attachments(%{data: %{"attachment" => attachments}}) do "video" -> [ - {:meta, [property: "og:" <> media_type, content: attachment_url(url["href"])], []} + {:meta, + [property: "og:" <> media_type, content: Utils.attachment_url(url["href"])], []} | acc ] @@ -120,37 +121,4 @@ defp build_attachments(%{data: %{"attachment" => attachments}}) do acc ++ rendered_tags end) end - - defp scrub_html_and_truncate(%{data: %{"content" => content}} = object) do - content - # html content comes from DB already encoded, decode first and scrub after - |> HtmlEntities.decode() - |> String.replace(~r//, " ") - |> HTML.get_cached_stripped_html_for_object(object, __MODULE__) - |> Formatter.demojify() - |> Formatter.truncate() - end - - defp scrub_html_and_truncate(content) when is_binary(content) do - content - # html content comes from DB already encoded, decode first and scrub after - |> HtmlEntities.decode() - |> String.replace(~r//, " ") - |> HTML.strip_tags() - |> Formatter.demojify() - |> Formatter.truncate() - end - - defp attachment_url(url) do - MediaProxy.url(url) - end - - defp user_name_string(user) do - "#{user.name} " <> - if user.local do - "(@#{user.nickname}@#{Pleroma.Web.Endpoint.host()})" - else - "(@#{user.nickname})" - end - end end diff --git a/lib/pleroma/web/metadata/twitter_card.ex b/lib/pleroma/web/metadata/twitter_card.ex index 32b979357..0365e4647 100644 --- a/lib/pleroma/web/metadata/twitter_card.ex +++ b/lib/pleroma/web/metadata/twitter_card.ex @@ -3,44 +3,120 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.Metadata.Providers.TwitterCard do - alias Pleroma.Web.Metadata.Providers.Provider + alias Pleroma.User alias Pleroma.Web.Metadata + alias Pleroma.Web.Metadata.Providers.Provider + alias Pleroma.Web.Metadata.Utils @behaviour Provider @impl Provider - def build_tags(%{object: object}) do - if Metadata.activity_nsfw?(object) or object.data["attachment"] == [] do - build_tags(nil) - else - case find_first_acceptable_media_type(object) do - "image" -> - [{:meta, [property: "twitter:card", content: "summary_large_image"], []}] - - "audio" -> - [{:meta, [property: "twitter:card", content: "player"], []}] - - "video" -> - [{:meta, [property: "twitter:card", content: "player"], []}] - - _ -> - build_tags(nil) + def build_tags(%{ + object: object, + user: user + }) do + attachments = build_attachments(object) + scrubbed_content = Utils.scrub_html_and_truncate(object) + # Zero width space + content = + if scrubbed_content != "" and scrubbed_content != "\u200B" do + "“" <> scrubbed_content <> "”" + else + "" + end + + [ + {:meta, + [ + property: "twitter:title", + content: Utils.user_name_string(user) + ], []}, + {:meta, + [ + property: "twitter:description", + content: content + ], []} + ] ++ + if attachments == [] or Metadata.activity_nsfw?(object) do + [ + {:meta, + [property: "twitter:image", content: Utils.attachment_url(User.avatar_url(user))], []}, + {:meta, [property: "twitter:card", content: "summary_large_image"], []} + ] + else + attachments end - end end @impl Provider - def build_tags(_) do - [{:meta, [property: "twitter:card", content: "summary"], []}] + def build_tags(%{user: user}) do + with truncated_bio = Utils.scrub_html_and_truncate(user.bio || "") do + [ + {:meta, + [ + property: "twitter:title", + content: Utils.user_name_string(user) + ], []}, + {:meta, [property: "twitter:description", content: truncated_bio], []}, + {:meta, [property: "twitter:image", content: Utils.attachment_url(User.avatar_url(user))], + []}, + {:meta, [property: "twitter:card", content: "summary"], []} + ] + end end - def find_first_acceptable_media_type(%{data: %{"attachment" => attachment}}) do - Enum.find_value(attachment, fn attachment -> - Enum.find_value(attachment["url"], fn url -> - Enum.find(["image", "audio", "video"], fn media_type -> - String.starts_with?(url["mediaType"], media_type) + defp build_attachments(%{data: %{"attachment" => attachments}}) do + Enum.reduce(attachments, [], fn attachment, acc -> + rendered_tags = + Enum.reduce(attachment["url"], [], fn url, acc -> + content_type = url["mediaType"] + + media_type = + Enum.find(["image", "audio", "video"], fn media_type -> + String.starts_with?(url["mediaType"], media_type) + end) + + # TODO: Add additional properties to objects when we have the data available. + case media_type do + "audio" -> + [ + {:meta, [property: "twitter:card", content: "player"], []}, + {:meta, [property: "twitter:player", content: Utils.attachment_url(url["href"])], + []} + | acc + ] + + "image" -> + [ + {:meta, [property: "twitter:card", content: "summary_large_image"], []}, + {:meta, + [ + property: "twitter:player", + content: + Utils.attachment_url( + Pleroma.Uploaders.Uploader.preview_url(content_type, url["href"]) + ) + ], []} + | acc + ] + + # TODO: Need the true width and height values here or Twitter renders an iFrame with a bad aspect ratio + "video" -> + [ + {:meta, [property: "twitter:card", content: "player"], []}, + {:meta, [property: "twitter:player", content: Utils.attachment_url(url["href"])], + []}, + {:meta, [property: "twitter:player:width", content: "1280"], []}, + {:meta, [property: "twitter:player:height", content: "720"], []} + | acc + ] + + _ -> + acc + end end) - end) + + acc ++ rendered_tags end) end end diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex new file mode 100644 index 000000000..a166800d4 --- /dev/null +++ b/lib/pleroma/web/metadata/utils.ex @@ -0,0 +1,42 @@ +# Pleroma: A lightweight social networking server +# Copyright \xc2\xa9 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.Metadata.Utils do + alias Pleroma.HTML + alias Pleroma.Formatter + alias Pleroma.Web.MediaProxy + + def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do + content + # html content comes from DB already encoded, decode first and scrub after + |> HtmlEntities.decode() + |> String.replace(~r//, " ") + |> HTML.get_cached_stripped_html_for_object(object, __MODULE__) + |> Formatter.demojify() + |> Formatter.truncate() + end + + def scrub_html_and_truncate(content) when is_binary(content) do + content + # html content comes from DB already encoded, decode first and scrub after + |> HtmlEntities.decode() + |> String.replace(~r//, " ") + |> HTML.strip_tags() + |> Formatter.demojify() + |> Formatter.truncate() + end + + def attachment_url(url) do + MediaProxy.url(url) + end + + def user_name_string(user) do + "#{user.name} " <> + if user.local do + "(@#{user.nickname}@#{Pleroma.Web.Endpoint.host()})" + else + "(@#{user.nickname})" + end + end +end