First
[anni] / lib / pleroma / web / feed / feed_view.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.Feed.FeedView do
6   use Phoenix.HTML
7   use Pleroma.Web, :view
8
9   alias Pleroma.Object
10   alias Pleroma.User
11   alias Pleroma.Web.Gettext
12   alias Pleroma.Web.MediaProxy
13
14   require Pleroma.Constants
15
16   @days ~w(Mon Tue Wed Thu Fri Sat Sun)
17   @months ~w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
18
19   def prepare_activity(activity, opts \\ []) do
20     object = Object.normalize(activity, fetch: false)
21
22     actor =
23       if opts[:actor] do
24         Pleroma.User.get_cached_by_ap_id(activity.actor)
25       end
26
27     %{
28       activity: activity,
29       object: object,
30       data: Map.get(object, :data),
31       actor: actor
32     }
33   end
34
35   def most_recent_update(activities) do
36     with %{updated_at: updated_at} <- List.first(activities) do
37       to_rfc3339(updated_at)
38     end
39   end
40
41   def most_recent_update(activities, user, :atom) do
42     (List.first(activities) || user).updated_at
43     |> to_rfc3339()
44   end
45
46   def most_recent_update(activities, user, :rss) do
47     (List.first(activities) || user).updated_at
48     |> to_rfc2822()
49   end
50
51   def feed_logo do
52     case Pleroma.Config.get([:feed, :logo]) do
53       nil ->
54         "#{Pleroma.Web.Endpoint.url()}/static/logo.svg"
55
56       logo ->
57         "#{Pleroma.Web.Endpoint.url()}#{logo}"
58     end
59     |> MediaProxy.url()
60   end
61
62   def email(user) do
63     user.nickname <> "@" <> Pleroma.Web.Endpoint.host()
64   end
65
66   def logo(user) do
67     user
68     |> User.avatar_url()
69     |> MediaProxy.url()
70   end
71
72   def last_activity(activities), do: List.last(activities)
73
74   def activity_title(%{"content" => content} = data, opts \\ %{}) do
75     summary = Map.get(data, "summary", "")
76
77     title =
78       cond do
79         summary != "" -> summary
80         content != "" -> activity_content(data)
81         true -> "a post"
82       end
83
84     title
85     |> Pleroma.Web.Metadata.Utils.scrub_html_and_truncate(opts[:max_length], opts[:omission])
86     |> HtmlEntities.encode()
87   end
88
89   def activity_description(data) do
90     content = activity_content(data)
91     summary = data["summary"]
92
93     cond do
94       content != "" -> escape(content)
95       summary != "" -> escape(summary)
96       true -> escape(data["type"])
97     end
98   end
99
100   def activity_content(%{"content" => content}) do
101     content
102     |> String.replace(~r/[\n\r]/, "")
103   end
104
105   def activity_content(_), do: ""
106
107   def activity_context(activity), do: escape(activity.data["context"])
108
109   def attachment_href(attachment) do
110     attachment["url"]
111     |> hd()
112     |> Map.get("href")
113   end
114
115   def attachment_type(attachment) do
116     attachment["url"]
117     |> hd()
118     |> Map.get("mediaType")
119   end
120
121   def get_href(id) do
122     with %Object{data: %{"external_url" => external_url}} <- Object.get_cached_by_ap_id(id) do
123       external_url
124     else
125       _e -> id
126     end
127   end
128
129   def escape(html) do
130     html
131     |> html_escape()
132     |> safe_to_string()
133   end
134
135   @spec to_rfc3339(String.t() | NativeDateTime.t()) :: String.t()
136   def to_rfc3339(date) when is_binary(date) do
137     date
138     |> Timex.parse!("{ISO:Extended}")
139     |> to_rfc3339()
140   end
141
142   def to_rfc3339(nd) do
143     nd
144     |> Timex.to_datetime()
145     |> Timex.format!("{RFC3339}")
146   end
147
148   @spec to_rfc2822(String.t() | DateTime.t() | NativeDateTime.t()) :: String.t()
149   def to_rfc2822(datestr) when is_binary(datestr) do
150     datestr
151     |> Timex.parse!("{ISO:Extended}")
152     |> to_rfc2822()
153   end
154
155   def to_rfc2822(%DateTime{} = date) do
156     date
157     |> DateTime.to_naive()
158     |> NaiveDateTime.to_erl()
159     |> rfc2822_from_erl()
160   end
161
162   def to_rfc2822(nd) do
163     nd
164     |> Timex.to_datetime()
165     |> DateTime.to_naive()
166     |> NaiveDateTime.to_erl()
167     |> rfc2822_from_erl()
168   end
169
170   @doc """
171   Builds a RFC2822 timestamp from an Erlang timestamp
172   [RFC2822 3.3 - Date and Time Specification](https://tools.ietf.org/html/rfc2822#section-3.3)
173   This function always assumes the Erlang timestamp is in Universal time, not Local time
174   """
175   def rfc2822_from_erl({{year, month, day} = date, {hour, minute, second}}) do
176     day_name = Enum.at(@days, :calendar.day_of_the_week(date) - 1)
177     month_name = Enum.at(@months, month - 1)
178
179     date_part = "#{day_name}, #{day} #{month_name} #{year}"
180     time_part = "#{pad(hour)}:#{pad(minute)}:#{pad(second)}"
181
182     date_part <> " " <> time_part <> " +0000"
183   end
184
185   defp pad(num) do
186     num
187     |> Integer.to_string()
188     |> String.pad_leading(2, "0")
189   end
190 end