[#2497] Media preview proxy: misc. improvements (`static` param support, dynamic...
[akkoma] / lib / pleroma / web / mastodon_api / views / account_view.ex
index c1786a322d3e2c3c3a8dc7143b8b9243ea12ede7..121ba1693155adf30f4c17b9be4d0c9b111bf001 100644 (file)
@@ -12,17 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
   alias Pleroma.Web.MastodonAPI.AccountView
   alias Pleroma.Web.MediaProxy
 
-  # Default behaviour for account view is to include embedded relationships
-  #   (e.g. when accounts are rendered on their own [e.g. a list of search results], not as
-  #   embedded content in notifications / statuses).
-  # This option must be explicitly set to false when rendering accounts as embedded content.
-  defp initialize_skip_relationships(opts) do
-    Map.merge(%{skip_relationships: false}, opts)
-  end
-
   def render("index.json", %{users: users} = opts) do
-    opts = initialize_skip_relationships(opts)
-
     reading_user = opts[:for]
 
     relationships_opt =
@@ -30,28 +20,47 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
         Map.has_key?(opts, :relationships) ->
           opts[:relationships]
 
-        is_nil(reading_user) || opts[:skip_relationships] ->
+        is_nil(reading_user) || !opts[:embed_relationships] ->
           UserRelationship.view_relationships_option(nil, [])
 
         true ->
           UserRelationship.view_relationships_option(reading_user, users)
       end
 
-    opts = Map.put(opts, :relationships, relationships_opt)
+    opts =
+      opts
+      |> Map.merge(%{relationships: relationships_opt, as: :user})
+      |> Map.delete(:users)
 
     users
     |> render_many(AccountView, "show.json", opts)
     |> Enum.filter(&Enum.any?/1)
   end
 
-  def render("show.json", %{user: user} = opts) do
-    if User.visible_for?(user, opts[:for]) do
+  @doc """
+  Renders specified user account.
+    :skip_visibility_check option skips visibility check and renders any user (local or remote)
+      regardless of [:pleroma, :restrict_unauthenticated] setting.
+    :for option specifies the requester and can be a User record or nil.
+      Only use `user: user, for: user` when `user` is the actual requester of own profile.
+  """
+  def render("show.json", %{user: _user, skip_visibility_check: true} = opts) do
+    do_render("show.json", opts)
+  end
+
+  def render("show.json", %{user: user, for: for_user_or_nil} = opts) do
+    if User.visible_for(user, for_user_or_nil) == :visible do
       do_render("show.json", opts)
     else
       %{}
     end
   end
 
+  def render("show.json", _) do
+    raise "In order to prevent account accessibility issues, " <>
+            ":skip_visibility_check or :for option is required."
+  end
+
   def render("mention.json", %{user: user}) do
     %{
       id: to_string(user.id),
@@ -169,13 +178,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
   end
 
   defp do_render("show.json", %{user: user} = opts) do
-    opts = initialize_skip_relationships(opts)
-
     user = User.sanitize_html(user, User.html_filter_policy(opts[:for]))
     display_name = user.name || user.nickname
 
-    image = User.avatar_url(user) |> MediaProxy.url()
+    avatar = User.avatar_url(user) |> MediaProxy.url()
+    avatar_static = User.avatar_url(user) |> MediaProxy.preview_url(static: true)
     header = User.banner_url(user) |> MediaProxy.url()
+    header_static = User.banner_url(user) |> MediaProxy.preview_url(static: true)
 
     following_count =
       if !user.hide_follows_count or !user.hide_follows or opts[:for] == user do
@@ -191,27 +200,41 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
         0
       end
 
-    bot = user.actor_type in ["Application", "Service"]
+    bot = user.actor_type == "Service"
 
     emojis =
-      Enum.map(user.emoji, fn {shortcode, url} ->
+      Enum.map(user.emoji, fn {shortcode, raw_url} ->
+        url = MediaProxy.url(raw_url)
+
         %{
-          "shortcode" => shortcode,
-          "url" => url,
-          "static_url" => url,
-          "visible_in_picker" => false
+          shortcode: shortcode,
+          url: url,
+          static_url: url,
+          visible_in_picker: false
         }
       end)
 
     relationship =
-      if opts[:skip_relationships] do
-        %{}
-      else
+      if opts[:embed_relationships] do
         render("relationship.json", %{
           user: opts[:for],
           target: user,
           relationships: opts[:relationships]
         })
+      else
+        %{}
+      end
+
+    favicon =
+      if Pleroma.Config.get([:instances_favicons, :enabled]) do
+        user
+        |> Map.get(:ap_id, "")
+        |> URI.parse()
+        |> URI.merge("/")
+        |> Pleroma.Instances.Instance.get_or_update_favicon()
+        |> MediaProxy.url()
+      else
+        nil
       end
 
     %{
@@ -224,17 +247,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
       followers_count: followers_count,
       following_count: following_count,
       statuses_count: user.note_count,
-      note: user.bio || "",
+      note: user.bio,
       url: user.uri || user.ap_id,
-      avatar: image,
-      avatar_static: image,
+      avatar: avatar,
+      avatar_static: avatar_static,
       header: header,
-      header_static: header,
+      header_static: header_static,
       emojis: emojis,
       fields: user.fields,
       bot: bot,
       source: %{
-        note: prepare_user_bio(user),
+        note: user.raw_bio || "",
         sensitive: false,
         fields: user.raw_fields,
         pleroma: %{
@@ -245,6 +268,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
 
       # Pleroma extension
       pleroma: %{
+        ap_id: user.ap_id,
         confirmation_pending: user.confirmation_pending,
         tags: user.tags,
         hide_followers_count: user.hide_followers_count,
@@ -254,7 +278,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
         hide_favorites: user.hide_favorites,
         relationship: relationship,
         skip_thread_containment: user.skip_thread_containment,
-        background_image: image_url(user.background) |> MediaProxy.url()
+        background_image: image_url(user.background) |> MediaProxy.url(),
+        accepts_chat_messages: user.accepts_chat_messages,
+        favicon: favicon
       }
     }
     |> maybe_put_role(user, opts[:for])
@@ -269,14 +295,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
     |> maybe_put_unread_notification_count(user, opts[:for])
   end
 
-  defp prepare_user_bio(%User{bio: ""}), do: ""
-
-  defp prepare_user_bio(%User{bio: bio}) when is_binary(bio) do
-    bio |> String.replace(~r(<br */?>), "\n") |> Pleroma.HTML.strip_tags()
-  end
-
-  defp prepare_user_bio(_), do: ""
-
   defp username_from_nickname(string) when is_binary(string) do
     hd(String.split(string, "@"))
   end
@@ -345,7 +363,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
   defp maybe_put_role(data, _, _), do: data
 
   defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id: user_id}) do
-    Kernel.put_in(data, [:pleroma, :notification_settings], user.notification_settings)
+    Kernel.put_in(
+      data,
+      [:pleroma, :notification_settings],
+      Map.from_struct(user.notification_settings)
+    )
   end
 
   defp maybe_put_notification_settings(data, _, _), do: data