diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/pleroma/gopher/server.ex | 3 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 52 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub_controller.ex | 9 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/transmogrifier.ex | 3 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/views/user_view.ex | 24 | ||||
| -rw-r--r-- | lib/pleroma/web/activity_pub/visibility.ex | 56 | ||||
| -rw-r--r-- | lib/pleroma/web/federator/federator.ex | 3 | ||||
| -rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 13 | ||||
| -rw-r--r-- | lib/pleroma/web/metadata/opengraph.ex | 60 | ||||
| -rw-r--r-- | lib/pleroma/web/metadata/player_view.ex | 21 | ||||
| -rw-r--r-- | lib/pleroma/web/metadata/twitter_card.ex | 128 | ||||
| -rw-r--r-- | lib/pleroma/web/metadata/utils.ex | 42 | ||||
| -rw-r--r-- | lib/pleroma/web/ostatus/ostatus_controller.ex | 32 | ||||
| -rw-r--r-- | lib/pleroma/web/router.ex | 1 | ||||
| -rw-r--r-- | lib/pleroma/web/streamer.ex | 6 | ||||
| -rw-r--r-- | lib/pleroma/web/templates/layout/metadata_player.html.eex | 16 | ||||
| -rw-r--r-- | lib/pleroma/web/twitter_api/twitter_api_controller.ex | 3 | 
17 files changed, 322 insertions, 150 deletions
| diff --git a/lib/pleroma/gopher/server.ex b/lib/pleroma/gopher/server.ex index 32cb817d2..ba9614029 100644 --- a/lib/pleroma/gopher/server.ex +++ b/lib/pleroma/gopher/server.ex @@ -37,6 +37,7 @@ end  defmodule Pleroma.Gopher.Server.ProtocolHandler do    alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.ActivityPub.Visibility    alias Pleroma.Activity    alias Pleroma.HTML    alias Pleroma.User @@ -110,7 +111,7 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do    def response("/notices/" <> id) do      with %Activity{} = activity <- Repo.get(Activity, id), -         true <- ActivityPub.is_public?(activity) do +         true <- Visibility.is_public?(activity) do        activities =          ActivityPub.fetch_activities_for_context(activity.data["context"])          |> render_activities diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 7e153f396..cc255cc9e 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -18,6 +18,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    import Ecto.Query    import Pleroma.Web.ActivityPub.Utils +  import Pleroma.Web.ActivityPub.Visibility    require Logger @@ -912,57 +913,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do      end    end -  def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false -  def is_public?(%Object{data: data}), do: is_public?(data) -  def is_public?(%Activity{data: data}), do: is_public?(data) -  def is_public?(%{"directMessage" => true}), do: false - -  def is_public?(data) do -    "https://www.w3.org/ns/activitystreams#Public" in (data["to"] ++ (data["cc"] || [])) -  end - -  def is_private?(activity) do -    unless is_public?(activity) do -      follower_address = User.get_cached_by_ap_id(activity.data["actor"]).follower_address -      Enum.any?(activity.data["to"], &(&1 == follower_address)) -    else -      false -    end -  end - -  def is_direct?(%Activity{data: %{"directMessage" => true}}), do: true -  def is_direct?(%Object{data: %{"directMessage" => true}}), do: true - -  def is_direct?(activity) do -    !is_public?(activity) && !is_private?(activity) -  end - -  def visible_for_user?(activity, nil) do -    is_public?(activity) -  end - -  def visible_for_user?(activity, user) do -    x = [user.ap_id | user.following] -    y = activity.data["to"] ++ (activity.data["cc"] || []) -    visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y)) -  end - -  # guard -  def entire_thread_visible_for_user?(nil, _user), do: false - -  # child -  def entire_thread_visible_for_user?( -        %Activity{data: %{"object" => %{"inReplyTo" => parent_id}}} = tail, -        user -      ) -      when is_binary(parent_id) do -    parent = Activity.get_in_reply_to_activity(tail) -    visible_for_user?(tail, user) && entire_thread_visible_for_user?(parent, user) -  end - -  # root -  def entire_thread_visible_for_user?(tail, user), do: visible_for_user?(tail, user) -    # filter out broken threads    def contain_broken_threads(%Activity{} = activity, %User{} = user) do      entire_thread_visible_for_user?(activity, user) diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 2bea51311..ff924a536 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -11,6 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    alias Pleroma.Web.ActivityPub.ObjectView    alias Pleroma.Web.ActivityPub.UserView    alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.ActivityPub.Visibility    alias Pleroma.Web.ActivityPub.Relay    alias Pleroma.Web.ActivityPub.Transmogrifier    alias Pleroma.Web.ActivityPub.Utils @@ -49,7 +50,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    def object(conn, %{"uuid" => uuid}) do      with ap_id <- o_status_url(conn, :object, uuid),           %Object{} = object <- Object.get_cached_by_ap_id(ap_id), -         {_, true} <- {:public?, ActivityPub.is_public?(object)} do +         {_, true} <- {:public?, Visibility.is_public?(object)} do        conn        |> put_resp_header("content-type", "application/activity+json")        |> json(ObjectView.render("object.json", %{object: object})) @@ -62,7 +63,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    def object_likes(conn, %{"uuid" => uuid, "page" => page}) do      with ap_id <- o_status_url(conn, :object, uuid),           %Object{} = object <- Object.get_cached_by_ap_id(ap_id), -         {_, true} <- {:public?, ActivityPub.is_public?(object)}, +         {_, true} <- {:public?, Visibility.is_public?(object)},           likes <- Utils.get_object_likes(object) do        {page, _} = Integer.parse(page) @@ -78,7 +79,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    def object_likes(conn, %{"uuid" => uuid}) do      with ap_id <- o_status_url(conn, :object, uuid),           %Object{} = object <- Object.get_cached_by_ap_id(ap_id), -         {_, true} <- {:public?, ActivityPub.is_public?(object)}, +         {_, true} <- {:public?, Visibility.is_public?(object)},           likes <- Utils.get_object_likes(object) do        conn        |> put_resp_header("content-type", "application/activity+json") @@ -92,7 +93,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do    def activity(conn, %{"uuid" => uuid}) do      with ap_id <- o_status_url(conn, :activity, uuid),           %Activity{} = activity <- Activity.normalize(ap_id), -         {_, true} <- {:public?, ActivityPub.is_public?(activity)} do +         {_, true} <- {:public?, Visibility.is_public?(activity)} do        conn        |> put_resp_header("content-type", "application/activity+json")        |> json(ObjectView.render("object.json", %{object: activity})) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 41d89a02b..88007aa16 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -12,6 +12,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do    alias Pleroma.Repo    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.ActivityPub.Utils +  alias Pleroma.Web.ActivityPub.Visibility    import Ecto.Query @@ -489,7 +490,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      with actor <- get_actor(data),           %User{} = actor <- User.get_or_fetch_by_ap_id(actor),           {:ok, object} <- get_obj_helper(object_id) || fetch_obj_helper(object_id), -         public <- ActivityPub.is_public?(data), +         public <- Visibility.is_public?(data),           {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false, public) do        {:ok, activity}      else diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index c8e154989..415cbd47a 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -188,14 +188,24 @@ defmodule Pleroma.Web.ActivityPub.UserView do        end      activities = ActivityPub.fetch_user_activities(user, nil, params) -    min_id = Enum.at(Enum.reverse(activities), 0).id -    max_id = Enum.at(activities, 0).id -    collection = -      Enum.map(activities, fn act -> -        {:ok, data} = Transmogrifier.prepare_outgoing(act.data) -        data -      end) +    {max_id, min_id, collection} = +      if length(activities) > 0 do +        { +          Enum.at(Enum.reverse(activities), 0).id, +          Enum.at(activities, 0).id, +          Enum.map(activities, fn act -> +            {:ok, data} = Transmogrifier.prepare_outgoing(act.data) +            data +          end) +        } +      else +        { +          0, +          0, +          [] +        } +      end      iri = "#{user.ap_id}/outbox" diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex new file mode 100644 index 000000000..db52fe933 --- /dev/null +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -0,0 +1,56 @@ +defmodule Pleroma.Web.ActivityPub.Visibility do +  alias Pleroma.Activity +  alias Pleroma.Object +  alias Pleroma.User + +  def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false +  def is_public?(%Object{data: data}), do: is_public?(data) +  def is_public?(%Activity{data: data}), do: is_public?(data) +  def is_public?(%{"directMessage" => true}), do: false + +  def is_public?(data) do +    "https://www.w3.org/ns/activitystreams#Public" in (data["to"] ++ (data["cc"] || [])) +  end + +  def is_private?(activity) do +    unless is_public?(activity) do +      follower_address = User.get_cached_by_ap_id(activity.data["actor"]).follower_address +      Enum.any?(activity.data["to"], &(&1 == follower_address)) +    else +      false +    end +  end + +  def is_direct?(%Activity{data: %{"directMessage" => true}}), do: true +  def is_direct?(%Object{data: %{"directMessage" => true}}), do: true + +  def is_direct?(activity) do +    !is_public?(activity) && !is_private?(activity) +  end + +  def visible_for_user?(activity, nil) do +    is_public?(activity) +  end + +  def visible_for_user?(activity, user) do +    x = [user.ap_id | user.following] +    y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || []) +    visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y)) +  end + +  # guard +  def entire_thread_visible_for_user?(nil, _user), do: false + +  # child +  def entire_thread_visible_for_user?( +        %Activity{data: %{"object" => %{"inReplyTo" => parent_id}}} = tail, +        user +      ) +      when is_binary(parent_id) do +    parent = Activity.get_in_reply_to_activity(tail) +    visible_for_user?(tail, user) && entire_thread_visible_for_user?(parent, user) +  end + +  # root +  def entire_thread_visible_for_user?(tail, user), do: visible_for_user?(tail, user) +end diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index d4e2a9742..fbfe97dbc 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.Federator do    alias Pleroma.Web.Websub    alias Pleroma.Web.Salmon    alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.ActivityPub.Visibility    alias Pleroma.Web.ActivityPub.Relay    alias Pleroma.Web.ActivityPub.Transmogrifier    alias Pleroma.Web.ActivityPub.Utils @@ -94,7 +95,7 @@ defmodule Pleroma.Web.Federator do      with actor when not is_nil(actor) <- User.get_cached_by_ap_id(activity.data["actor"]) do        {:ok, actor} = WebFinger.ensure_keys_present(actor) -      if ActivityPub.is_public?(activity) do +      if Visibility.is_public?(activity) do          if OStatus.is_representable?(activity) do            Logger.info(fn -> "Sending #{activity.data["id"]} out via WebSub" end)            Websub.publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 60738301b..12987442a 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -27,6 +27,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    alias Pleroma.Web.MastodonAPI.ReportView    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.ActivityPub.Utils +  alias Pleroma.Web.ActivityPub.Visibility    alias Pleroma.Web.OAuth.App    alias Pleroma.Web.OAuth.Authorization    alias Pleroma.Web.OAuth.Token @@ -307,7 +308,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do      with %Activity{} = activity <- Repo.get(Activity, id), -         true <- ActivityPub.visible_for_user?(activity, user) do +         true <- Visibility.visible_for_user?(activity, user) do        conn        |> put_view(StatusView)        |> try_render("status.json", %{activity: activity, for: user}) @@ -449,7 +450,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    def bookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do      with %Activity{} = activity <- Repo.get(Activity, id),           %User{} = user <- User.get_by_nickname(user.nickname), -         true <- ActivityPub.visible_for_user?(activity, user), +         true <- Visibility.visible_for_user?(activity, user),           {:ok, user} <- User.bookmark(user, activity.data["object"]["id"]) do        conn        |> put_view(StatusView) @@ -460,7 +461,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    def unbookmark_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do      with %Activity{} = activity <- Repo.get(Activity, id),           %User{} = user <- User.get_by_nickname(user.nickname), -         true <- ActivityPub.visible_for_user?(activity, user), +         true <- Visibility.visible_for_user?(activity, user),           {:ok, user} <- User.unbookmark(user, activity.data["object"]["id"]) do        conn        |> put_view(StatusView) @@ -867,7 +868,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do        if Regex.match?(~r/https?:/, query) do          with {:ok, object} <- ActivityPub.fetch_object_from_id(query),               %Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]), -             true <- ActivityPub.visible_for_user?(activity, user) do +             true <- Visibility.visible_for_user?(activity, user) do            [activity]          else            _e -> [] @@ -1518,9 +1519,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      end    end -  def status_card(conn, %{"id" => status_id}) do +  def status_card(%{assigns: %{user: user}} = conn, %{"id" => status_id}) do      with %Activity{} = activity <- Repo.get(Activity, status_id), -         true <- ActivityPub.is_public?(activity) do +         true <- Visibility.visible_for_user?(activity, user) do        data =          StatusView.render(            "card.json", diff --git a/lib/pleroma/web/metadata/opengraph.ex b/lib/pleroma/web/metadata/opengraph.ex index 190377767..cafb8134b 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 @@ 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 new file mode 100644 index 000000000..e9a8cfc8d --- /dev/null +++ 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 index 32b979357..a0be383e5 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(%{ +        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, %{data: %{"attachment" => attachments}}) do +    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 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 <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 index ee2e3d6ec..4e963774a 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do    alias Pleroma.Object    alias Pleroma.User    alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.ActivityPub.Visibility    alias Pleroma.Web.ActivityPub.ActivityPubController    alias Pleroma.Web.ActivityPub.ObjectView    alias Pleroma.Web.OStatus.ActivityRepresenter @@ -102,7 +103,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do      else        with id <- o_status_url(conn, :object, uuid),             {_, %Activity{} = activity} <- {:activity, Activity.get_create_by_object_ap_id(id)}, -           {_, true} <- {:public?, ActivityPub.is_public?(activity)}, +           {_, true} <- {:public?, Visibility.is_public?(activity)},             %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do          case get_format(conn) do            "html" -> redirect(conn, to: "/notice/#{activity.id}") @@ -127,7 +128,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do      else        with id <- o_status_url(conn, :activity, uuid),             {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)}, -           {_, true} <- {:public?, ActivityPub.is_public?(activity)}, +           {_, true} <- {:public?, Visibility.is_public?(activity)},             %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do          case format = get_format(conn) do            "html" -> redirect(conn, to: "/notice/#{activity.id}") @@ -148,7 +149,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do    def notice(conn, %{"id" => id}) do      with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(id)}, -         {_, true} <- {:public?, ActivityPub.is_public?(activity)}, +         {_, true} <- {:public?, Visibility.is_public?(activity)},           %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do        case format = get_format(conn) do          "html" -> @@ -156,6 +157,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 +189,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 <- Visibility.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 index a372610d4..5aebcb353 100644 --- 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/streamer.ex b/lib/pleroma/web/streamer.ex index 4de7608e4..477481bb9 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Web.Streamer do    alias Pleroma.Activity    alias Pleroma.Object    alias Pleroma.Repo -  alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.ActivityPub.Visibility    @keepalive_interval :timer.seconds(30) @@ -73,7 +73,7 @@ defmodule Pleroma.Web.Streamer do    def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do      # filter the recipient list if the activity is not public, see #270.      recipient_lists = -      case ActivityPub.is_public?(item) do +      case Visibility.is_public?(item) do          true ->            Pleroma.List.get_lists_from_activity(item) @@ -82,7 +82,7 @@ defmodule Pleroma.Web.Streamer do            |> Enum.filter(fn list ->              owner = Repo.get(User, list.user_id) -            ActivityPub.visible_for_user?(item, owner) +            Visibility.visible_for_user?(item, owner)            end)        end diff --git a/lib/pleroma/web/templates/layout/metadata_player.html.eex b/lib/pleroma/web/templates/layout/metadata_player.html.eex new file mode 100644 index 000000000..460f28094 --- /dev/null +++ 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> diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index b815379fd..0d74c30c3 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do    alias Pleroma.{Repo, Activity, Object, User, Notification}    alias Pleroma.Web.OAuth.Token    alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.ActivityPub.Visibility    alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.CommonAPI    alias Pleroma.Web.TwitterAPI.ActivityView @@ -268,7 +269,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do    def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do      with %Activity{} = activity <- Repo.get(Activity, id), -         true <- ActivityPub.visible_for_user?(activity, user) do +         true <- Visibility.visible_for_user?(activity, user) do        conn        |> put_view(ActivityView)        |> render("activity.json", %{activity: activity, for: user}) | 
