logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma
commit: 5a4e2905fecfd21cf92f6b2844f15f5ee84b33f5
parent: eeeb8124b95871853ec41f3ccf44f6168713a8d8
Author: kaniini <nenolod@gmail.com>
Date:   Fri, 22 Feb 2019 04:38:14 +0000

Merge branch 'fix/twittercards' into 'develop'

Fix Twitter Cards

See merge request pleroma/pleroma!815

Diffstat:

Mlib/pleroma/web/metadata/opengraph.ex60++++++++++++++----------------------------------------------
Alib/pleroma/web/metadata/player_view.ex21+++++++++++++++++++++
Mlib/pleroma/web/metadata/twitter_card.ex130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Alib/pleroma/web/metadata/utils.ex42++++++++++++++++++++++++++++++++++++++++++
Mlib/pleroma/web/ostatus/ostatus_controller.ex25+++++++++++++++++++++++++
Mlib/pleroma/web/router.ex1+
Alib/pleroma/web/templates/layout/metadata_player.html.eex16++++++++++++++++
7 files changed, 223 insertions(+), 72 deletions(-)

diff --git 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 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do 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 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do {: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 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do @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,14 +92,15 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph 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], []} | acc @@ -108,7 +108,8 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph 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 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph 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/<br\s?\/?>/, " ") - |> 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/<br\s?\/?>/, " ") - |> 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/player_view.ex b/lib/pleroma/web/metadata/player_view.ex @@ -0,0 +1,21 @@ +defmodule Pleroma.Web.Metadata.PlayerView do + use Pleroma.Web, :view + import Phoenix.HTML.Tag, only: [content_tag: 3, tag: 2] + + def render("player.html", %{"mediaType" => type, "href" => href}) do + {tag_type, tag_attrs} = + case type do + "audio" <> _ -> {:audio, []} + "video" <> _ -> {:video, [loop: true]} + end + + content_tag( + tag_type, + [ + tag(:source, src: href, type: type), + "Your browser does not support #{type} playback." + ], + [controls: true] ++ tag_attrs + ) + end +end diff --git a/lib/pleroma/web/metadata/twitter_card.ex b/lib/pleroma/web/metadata/twitter_card.ex @@ -3,44 +3,122 @@ # 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(%{ + activity_id: id, + object: object, + user: user + }) do + attachments = build_attachments(id, 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(id, z = %{data: %{"attachment" => attachments}}) do + IO.puts(inspect(z)) + + Enum.reduce(attachments, [], fn attachment, acc -> + rendered_tags = + Enum.reduce(attachment["url"], [], fn url, acc -> + 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:width", content: "480"], []}, + {:meta, [property: "twitter:player:height", content: "80"], []}, + {:meta, [property: "twitter:player", content: player_url(id)], []} + | acc + ] + + "image" -> + [ + {:meta, [property: "twitter:card", content: "summary_large_image"], []}, + {:meta, + [ + property: "twitter:player", + content: Utils.attachment_url(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: player_url(id)], []}, + {:meta, [property: "twitter:player:width", content: "480"], []}, + {:meta, [property: "twitter:player:height", content: "480"], []} + | acc + ] + + _ -> + acc + end end) - end) + + acc ++ rendered_tags end) end + + defp player_url(id) do + Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice_player, id) + end end diff --git a/lib/pleroma/web/metadata/utils.ex b/lib/pleroma/web/metadata/utils.ex @@ -0,0 +1,42 @@ +# Pleroma: A lightweight social networking server +# Copyright \xc2\xa9 2017-2019 Pleroma Authors <https://pleroma.social/> +# 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/<br\s?\/?>/, " ") + |> 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/<br\s?\/?>/, " ") + |> 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 diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -156,6 +156,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do %Object{} = object = Object.normalize(activity.data["object"]) Fallback.RedirectController.redirector_with_meta(conn, %{ + activity_id: activity.id, object: object, url: Pleroma.Web.Router.Helpers.o_status_url( @@ -187,6 +188,30 @@ defmodule Pleroma.Web.OStatus.OStatusController do end end + # Returns an HTML embedded <audio> or <video> player suitable for embed iframes. + def notice_player(conn, %{"id" => id}) do + with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id), + true <- ActivityPub.is_public?(activity), + %Object{} = object <- Object.normalize(activity.data["object"]), + %{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object, + true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do + conn + |> put_layout(:metadata_player) + |> put_resp_header("x-frame-options", "ALLOW") + |> put_resp_header( + "content-security-policy", + "default-src 'none';style-src 'self' 'unsafe-inline';img-src 'self' data: https:; media-src 'self' https:;" + ) + |> put_view(Pleroma.Web.Metadata.PlayerView) + |> render("player.html", url) + else + _error -> + conn + |> put_status(404) + |> Fallback.RedirectController.redirector(nil, 404) + end + end + defp represent_activity( conn, "activity+json", diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex @@ -505,6 +505,7 @@ defmodule Pleroma.Web.Router do get("/objects/:uuid", OStatus.OStatusController, :object) get("/activities/:uuid", OStatus.OStatusController, :activity) get("/notice/:id", OStatus.OStatusController, :notice) + get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player) get("/users/:nickname/feed", OStatus.OStatusController, :feed) get("/users/:nickname", OStatus.OStatusController, :feed_redirect) diff --git a/lib/pleroma/web/templates/layout/metadata_player.html.eex b/lib/pleroma/web/templates/layout/metadata_player.html.eex @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html> +<body> + +<style type="text/css"> +video, audio { + width:100%; + max-width:600px; + height: auto; +} +</style> + +<%= render @view_module, @view_template, assigns %> + +</body> +</html>