Fix buckets for query timing
[akkoma] / lib / pleroma / emails / user_email.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Emails.UserEmail do
6 @moduledoc "User emails"
7
8 require Pleroma.Web.Gettext
9
10 alias Pleroma.Config
11 alias Pleroma.User
12 alias Pleroma.Web.Endpoint
13 alias Pleroma.Web.Gettext
14 alias Pleroma.Web.Router
15
16 import Swoosh.Email
17 import Phoenix.Swoosh, except: [render_body: 3]
18 import Pleroma.Config.Helpers, only: [instance_name: 0, sender: 0]
19
20 def render_body(email, template, assigns \\ %{}) do
21 email
22 |> put_new_layout({Pleroma.Web.LayoutView, :email})
23 |> put_new_view(Pleroma.Web.EmailView)
24 |> Phoenix.Swoosh.render_body(template, assigns)
25 end
26
27 defp recipient(email, nil), do: email
28 defp recipient(email, name), do: {name, email}
29 defp recipient(%User{} = user), do: recipient(user.email, user.name)
30
31 @spec welcome(User.t(), map()) :: Swoosh.Email.t()
32 def welcome(user, opts \\ %{}) do
33 Gettext.with_locale_or_default user.language do
34 new()
35 |> to(recipient(user))
36 |> from(Map.get(opts, :sender, sender()))
37 |> subject(
38 Map.get(
39 opts,
40 :subject,
41 Gettext.dpgettext(
42 "static_pages",
43 "welcome email subject",
44 "Welcome to %{instance_name}!",
45 instance_name: instance_name()
46 )
47 )
48 )
49 |> html_body(
50 Map.get(
51 opts,
52 :html,
53 Gettext.dpgettext(
54 "static_pages",
55 "welcome email html body",
56 "Welcome to %{instance_name}!",
57 instance_name: instance_name()
58 )
59 )
60 )
61 |> text_body(
62 Map.get(
63 opts,
64 :text,
65 Gettext.dpgettext(
66 "static_pages",
67 "welcome email text body",
68 "Welcome to %{instance_name}!",
69 instance_name: instance_name()
70 )
71 )
72 )
73 end
74 end
75
76 def password_reset_email(user, token) when is_binary(token) do
77 Gettext.with_locale_or_default user.language do
78 password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token)
79
80 html_body =
81 Gettext.dpgettext(
82 "static_pages",
83 "password reset email body",
84 """
85 <h3>Reset your password at %{instance_name}</h3>
86 <p>Someone has requested password change for your account at %{instance_name}.</p>
87 <p>If it was you, visit the following link to proceed: <a href="%{password_reset_url}">reset password</a>.</p>
88 <p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p>
89 """,
90 instance_name: instance_name(),
91 password_reset_url: password_reset_url
92 )
93
94 new()
95 |> to(recipient(user))
96 |> from(sender())
97 |> subject(
98 Gettext.dpgettext("static_pages", "password reset email subject", "Password reset")
99 )
100 |> html_body(html_body)
101 end
102 end
103
104 def user_invitation_email(
105 user,
106 %Pleroma.UserInviteToken{} = user_invite_token,
107 to_email,
108 to_name \\ nil
109 ) do
110 Gettext.with_locale_or_default user.language do
111 registration_url =
112 Router.Helpers.redirect_url(
113 Endpoint,
114 :registration_page,
115 user_invite_token.token
116 )
117
118 html_body =
119 Gettext.dpgettext(
120 "static_pages",
121 "user invitation email body",
122 """
123 <h3>You are invited to %{instance_name}</h3>
124 <p>%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.</p>
125 <p>Click the following link to register: <a href="%{registration_url}">accept invitation</a>.</p>
126 """,
127 instance_name: instance_name(),
128 inviter_name: user.name,
129 registration_url: registration_url
130 )
131
132 new()
133 |> to(recipient(to_email, to_name))
134 |> from(sender())
135 |> subject(
136 Gettext.dpgettext(
137 "static_pages",
138 "user invitation email subject",
139 "Invitation to %{instance_name}",
140 instance_name: instance_name()
141 )
142 )
143 |> html_body(html_body)
144 end
145 end
146
147 def account_confirmation_email(user) do
148 Gettext.with_locale_or_default user.language do
149 confirmation_url =
150 Router.Helpers.confirm_email_url(
151 Endpoint,
152 :confirm_email,
153 user.id,
154 to_string(user.confirmation_token)
155 )
156
157 html_body =
158 Gettext.dpgettext(
159 "static_pages",
160 "confirmation email body",
161 """
162 <h3>Thank you for registering on %{instance_name}</h3>
163 <p>Email confirmation is required to activate the account.</p>
164 <p>Please click the following link to <a href="%{confirmation_url}">activate your account</a>.</p>
165 """,
166 instance_name: instance_name(),
167 confirmation_url: confirmation_url
168 )
169
170 new()
171 |> to(recipient(user))
172 |> from(sender())
173 |> subject(
174 Gettext.dpgettext(
175 "static_pages",
176 "confirmation email subject",
177 "%{instance_name} account confirmation",
178 instance_name: instance_name()
179 )
180 )
181 |> html_body(html_body)
182 end
183 end
184
185 def approval_pending_email(user) do
186 Gettext.with_locale_or_default user.language do
187 html_body =
188 Gettext.dpgettext(
189 "static_pages",
190 "approval pending email body",
191 """
192 <h3>Awaiting Approval</h3>
193 <p>Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.</p>
194 """,
195 instance_name: instance_name()
196 )
197
198 new()
199 |> to(recipient(user))
200 |> from(sender())
201 |> subject(
202 Gettext.dpgettext(
203 "static_pages",
204 "approval pending email subject",
205 "Your account is awaiting approval"
206 )
207 )
208 |> html_body(html_body)
209 end
210 end
211
212 def successful_registration_email(user) do
213 Gettext.with_locale_or_default user.language do
214 html_body =
215 Gettext.dpgettext(
216 "static_pages",
217 "successful registration email body",
218 """
219 <h3>Hello @%{nickname},</h3>
220 <p>Your account at %{instance_name} has been registered successfully.</p>
221 <p>No further action is required to activate your account.</p>
222 """,
223 nickname: user.nickname,
224 instance_name: instance_name()
225 )
226
227 new()
228 |> to(recipient(user))
229 |> from(sender())
230 |> subject(
231 Gettext.dpgettext(
232 "static_pages",
233 "successful registration email subject",
234 "Account registered on %{instance_name}",
235 instance_name: instance_name()
236 )
237 )
238 |> html_body(html_body)
239 end
240 end
241
242 @doc """
243 Email used in digest email notifications
244 Includes Mentions and New Followers data
245 If there are no mentions (even when new followers exist), the function will return nil
246 """
247 @spec digest_email(User.t()) :: Swoosh.Email.t() | nil
248 def digest_email(user) do
249 Gettext.with_locale_or_default user.language do
250 notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
251
252 mentions =
253 notifications
254 |> Enum.filter(&(&1.activity.data["type"] == "Create"))
255 |> Enum.map(fn notification ->
256 object = Pleroma.Object.normalize(notification.activity, fetch: false)
257
258 if not is_nil(object) do
259 object = update_in(object.data["content"], &format_links/1)
260
261 %{
262 data: notification,
263 object: object,
264 from: User.get_by_ap_id(notification.activity.actor)
265 }
266 end
267 end)
268 |> Enum.filter(& &1)
269
270 followers =
271 notifications
272 |> Enum.filter(&(&1.activity.data["type"] == "Follow"))
273 |> Enum.map(fn notification ->
274 from = User.get_by_ap_id(notification.activity.actor)
275
276 if not is_nil(from) do
277 %{
278 data: notification,
279 object: Pleroma.Object.normalize(notification.activity, fetch: false),
280 from: User.get_by_ap_id(notification.activity.actor)
281 }
282 end
283 end)
284 |> Enum.filter(& &1)
285
286 unless Enum.empty?(mentions) do
287 styling = Config.get([__MODULE__, :styling])
288 logo = Config.get([__MODULE__, :logo])
289
290 html_data = %{
291 instance: instance_name(),
292 user: user,
293 mentions: mentions,
294 followers: followers,
295 unsubscribe_link: unsubscribe_url(user, "digest"),
296 styling: styling
297 }
298
299 logo_path =
300 if is_nil(logo) do
301 Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg")
302 else
303 Path.join(Config.get([:instance, :static_dir]), logo)
304 end
305
306 new()
307 |> to(recipient(user))
308 |> from(sender())
309 |> subject(
310 Gettext.dpgettext(
311 "static_pages",
312 "digest email subject",
313 "Your digest from %{instance_name}",
314 instance_name: instance_name()
315 )
316 )
317 |> put_layout(false)
318 |> render_body("digest.html", html_data)
319 |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline))
320 end
321 end
322 end
323
324 defp format_links(str) do
325 re = ~r/<a.+href=['"].*>/iU
326 %{link_color: color} = Config.get([__MODULE__, :styling])
327
328 Regex.replace(re, str, fn link ->
329 String.replace(link, "<a", "<a style=\"color: #{color};text-decoration: none;\"")
330 end)
331 end
332
333 @doc """
334 Generate unsubscribe link for given user and notifications type.
335 The link contains JWT token with the data, and subscription can be modified without
336 authorization.
337 """
338 @spec unsubscribe_url(User.t(), String.t()) :: String.t()
339 def unsubscribe_url(user, notifications_type) do
340 token =
341 %{"sub" => user.id, "act" => %{"unsubscribe" => notifications_type}, "exp" => false}
342 |> Pleroma.JWT.generate_and_sign!()
343 |> Base.encode64()
344
345 Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
346 end
347
348 def backup_is_ready_email(backup, admin_user_id \\ nil) do
349 %{user: user} = Pleroma.Repo.preload(backup, :user)
350
351 Gettext.with_locale_or_default user.language do
352 download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
353
354 html_body =
355 if is_nil(admin_user_id) do
356 Gettext.dpgettext(
357 "static_pages",
358 "account archive email body - self-requested",
359 """
360 <p>You requested a full backup of your Pleroma account. It's ready for download:</p>
361 <p><a href="%{download_url}">%{download_url}</a></p>
362 """,
363 download_url: download_url
364 )
365 else
366 admin = Pleroma.Repo.get(User, admin_user_id)
367
368 Gettext.dpgettext(
369 "static_pages",
370 "account archive email body - admin requested",
371 """
372 <p>Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:</p>
373 <p><a href="%{download_url}">%{download_url}</a></p>
374 """,
375 admin_nickname: admin.nickname,
376 download_url: download_url
377 )
378 end
379
380 new()
381 |> to(recipient(user))
382 |> from(sender())
383 |> subject(
384 Gettext.dpgettext(
385 "static_pages",
386 "account archive email subject",
387 "Your account archive is ready"
388 )
389 )
390 |> html_body(html_body)
391 end
392 end
393 end