X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fweb%2Fmastodon_api%2Fviews%2Fstatus_view.ex;h=db543ffe57a88e40479ee74ba981d694526a1c85;hb=54e7087ab412a488f8ad7286aef89d313e5e7b14;hp=27fe23594f07ae849d77477efeccfebb73a7d2f1;hpb=4d8f0761258e0ea7cfca2f4f93b47e55bb657c66;p=akkoma diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 27fe23594..bf3aaf025 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -1,50 +1,70 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MastodonAPI.StatusView do use Pleroma.Web, :view - alias Pleroma.Web.MastodonAPI.{AccountView, StatusView} - alias Pleroma.{User, Activity} + + alias Pleroma.Activity + alias Pleroma.HTML + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI.Utils + alias Pleroma.Web.MastodonAPI.AccountView + alias Pleroma.Web.MastodonAPI.StatusView alias Pleroma.Web.MediaProxy - alias Pleroma.Repo - alias Pleroma.HTML # TODO: Add cached version. defp get_replied_to_activities(activities) do activities |> Enum.map(fn - %{data: %{"type" => "Create", "object" => %{"inReplyTo" => inReplyTo}}} -> - inReplyTo != "" && inReplyTo + %{data: %{"type" => "Create", "object" => %{"inReplyTo" => in_reply_to}}} -> + in_reply_to != "" && in_reply_to _ -> nil end) |> Enum.filter(& &1) - |> Activity.create_activity_by_object_id_query() + |> Activity.create_by_object_ap_id() |> Repo.all() |> Enum.reduce(%{}, fn activity, acc -> Map.put(acc, activity.data["object"]["id"], activity) end) end + defp get_user(ap_id) do + cond do + user = User.get_cached_by_ap_id(ap_id) -> + user + + user = User.get_by_guessed_nickname(ap_id) -> + user + + true -> + User.error_user(ap_id) + end + end + def render("index.json", opts) do replied_to_activities = get_replied_to_activities(opts.activities) - render_many( - opts.activities, + opts.activities + |> safe_render_many( StatusView, "status.json", Map.put(opts, :replied_to_activities, replied_to_activities) ) - |> Enum.filter(fn x -> not is_nil(x) end) end def render( "status.json", %{activity: %{data: %{"type" => "Announce", "object" => object}} = activity} = opts ) do - user = User.get_cached_by_ap_id(activity.data["actor"]) + user = get_user(activity.data["actor"]) created_at = Utils.to_masto_date(activity.data["published"]) - reblogged = Activity.get_create_activity_by_object_ap_id(object) + reblogged = Activity.get_create_by_object_ap_id(object) reblogged = render("status.json", Map.put(opts, :activity, reblogged)) mentions = @@ -61,31 +81,36 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do in_reply_to_id: nil, in_reply_to_account_id: nil, reblog: reblogged, - content: reblogged[:content], + content: reblogged[:content] || "", created_at: created_at, reblogs_count: 0, replies_count: 0, favourites_count: 0, reblogged: false, favourited: false, + bookmarked: false, muted: false, + pinned: pinned?(activity, user), sensitive: false, spoiler_text: "", visibility: "public", - media_attachments: [], + media_attachments: reblogged[:media_attachments] || [], mentions: mentions, - tags: [], + tags: reblogged[:tags] || [], application: %{ name: "Web", website: nil }, language: nil, - emojis: [] + emojis: [], + pleroma: %{ + local: activity.local + } } end def render("status.json", %{activity: %{data: %{"object" => object}} = activity} = opts) do - user = User.get_cached_by_ap_id(activity.data["actor"]) + user = get_user(activity.data["actor"]) like_count = object["like_count"] || 0 announcement_count = object["announcement_count"] || 0 @@ -101,61 +126,68 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || []) favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) + bookmarked = opts[:for] && object["id"] in opts[:for].bookmarks attachment_data = object["attachment"] || [] - attachment_data = attachment_data ++ if object["type"] == "Video", do: [object], else: [] attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment) created_at = Utils.to_masto_date(object["published"]) reply_to = get_reply_to(activity, opts) - reply_to_user = reply_to && User.get_cached_by_ap_id(reply_to.data["actor"]) - - emojis = - (activity.data["object"]["emoji"] || []) - |> Enum.map(fn {name, url} -> - name = HTML.strip_tags(name) - - url = - HTML.strip_tags(url) - |> MediaProxy.url() - - %{shortcode: name, url: url, static_url: url, visible_in_picker: false} - end) + reply_to_user = reply_to && get_user(reply_to.data["actor"]) content = - render_content(object) - |> HTML.filter_tags(User.html_filter_policy(opts[:for])) + object + |> render_content() + |> HTML.get_cached_scrubbed_html_for_object( + User.html_filter_policy(opts[:for]), + activity, + __MODULE__ + ) + + card = render("card.json", Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)) + + url = + if user.local do + Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, activity) + else + object["external_url"] || object["id"] + end %{ id: to_string(activity.id), uri: object["id"], - url: object["external_url"] || object["id"], + url: url, account: AccountView.render("account.json", %{user: user}), in_reply_to_id: reply_to && to_string(reply_to.id), in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id), reblog: nil, + card: card, content: content, created_at: created_at, reblogs_count: announcement_count, replies_count: 0, favourites_count: like_count, - reblogged: !!repeated, - favourited: !!favorited, - muted: false, + reblogged: present?(repeated), + favourited: present?(favorited), + bookmarked: present?(bookmarked), + muted: CommonAPI.thread_muted?(user, activity) || User.mutes?(opts[:for], user), + pinned: pinned?(activity, user), sensitive: sensitive, spoiler_text: object["summary"] || "", visibility: get_visibility(object), - media_attachments: attachments |> Enum.take(4), + media_attachments: attachments, mentions: mentions, - # fix, - tags: [], + tags: build_tags(tags), application: %{ name: "Web", website: nil }, language: nil, - emojis: emojis + emojis: build_emojis(activity.data["object"]["emoji"]), + pleroma: %{ + local: activity.local + } } end @@ -163,6 +195,46 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do nil end + def render("card.json", %{rich_media: rich_media, page_url: page_url}) do + page_url_data = URI.parse(page_url) + + page_url_data = + if rich_media[:url] != nil do + URI.merge(page_url_data, URI.parse(rich_media[:url])) + else + page_url_data + end + + page_url = page_url_data |> to_string + + image_url = + if rich_media[:image] != nil do + URI.merge(page_url_data, URI.parse(rich_media[:image])) + |> to_string + else + nil + end + + site_name = rich_media[:site_name] || page_url_data.host + + %{ + type: "link", + provider_name: site_name, + provider_url: page_url_data.scheme <> "://" <> page_url_data.host, + url: page_url, + image: image_url |> MediaProxy.url(), + title: rich_media[:title], + description: rich_media[:description], + pleroma: %{ + opengraph: rich_media + } + } + end + + def render("card.json", _) do + nil + end + def render("attachment.json", %{attachment: attachment}) do [attachment_url | _] = attachment["url"] media_type = attachment_url["mediaType"] || attachment_url["mimeType"] || "image" @@ -196,7 +268,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do def get_reply_to(%{data: %{"object" => object}}, _) do if object["inReplyTo"] && object["inReplyTo"] != "" do - Activity.get_create_activity_by_object_ap_id(object["inReplyTo"]) + Activity.get_create_by_object_ap_id(object["inReplyTo"]) else nil end @@ -218,36 +290,89 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do Enum.any?(to, &String.contains?(&1, "/followers")) -> "private" + length(cc) > 0 -> + "private" + true -> "direct" end end def render_content(%{"type" => "Video"} = object) do - name = object["name"] + with name when not is_nil(name) and name != "" <- object["name"] do + "

#{name}

#{object["content"]}" + else + _ -> object["content"] || "" + end + end - content = - if !!name and name != "" do - "

#{name}

#{object["content"]}" - else - object["content"] - end + def render_content(%{"type" => object_type} = object) + when object_type in ["Article", "Page"] do + with summary when not is_nil(summary) and summary != "" <- object["name"], + url when is_bitstring(url) <- object["url"] do + "

#{summary}

#{object["content"]}" + else + _ -> object["content"] || "" + end + end + + def render_content(object), do: object["content"] || "" + + @doc """ + Builds a dictionary tags. - content + ## Examples + + iex> Pleroma.Web.MastodonAPI.StatusView.build_tags(["fediverse", "nextcloud"]) + [{"name": "fediverse", "url": "/tag/fediverse"}, + {"name": "nextcloud", "url": "/tag/nextcloud"}] + + """ + @spec build_tags(list(any())) :: list(map()) + def build_tags(object_tags) when is_list(object_tags) do + object_tags = for tag when is_binary(tag) <- object_tags, do: tag + + Enum.reduce(object_tags, [], fn tag, tags -> + tags ++ [%{name: tag, url: "/tag/#{tag}"}] + end) end - def render_content(%{"type" => object_type} = object) when object_type in ["Article", "Page"] do - summary = object["name"] + def build_tags(_), do: [] - content = - if !!summary and summary != "" and is_bitstring(object["url"]) do - "

#{summary}

#{object["content"]}" - else - object["content"] - end + @doc """ + Builds list emojis. + + Arguments: `nil` or list tuple of name and url. - content + Returns list emojis. + + ## Examples + + iex> Pleroma.Web.MastodonAPI.StatusView.build_emojis([{"2hu", "corndog.png"}]) + [%{shortcode: "2hu", static_url: "corndog.png", url: "corndog.png", visible_in_picker: false}] + + """ + @spec build_emojis(nil | list(tuple())) :: list(map()) + def build_emojis(nil), do: [] + + def build_emojis(emojis) do + emojis + |> Enum.map(fn {name, url} -> + name = HTML.strip_tags(name) + + url = + url + |> HTML.strip_tags() + |> MediaProxy.url() + + %{shortcode: name, url: url, static_url: url, visible_in_picker: false} + end) end - def render_content(object), do: object["content"] + defp present?(nil), do: false + defp present?(false), do: false + defp present?(_), do: true + + defp pinned?(%Activity{id: id}, %User{info: %{pinned_activities: pinned_activities}}), + do: id in pinned_activities end