1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Emails.UserEmail do
6 @moduledoc "User emails"
8 require Pleroma.Web.Gettext
12 alias Pleroma.Web.Endpoint
13 alias Pleroma.Web.Gettext
14 alias Pleroma.Web.Router
17 import Phoenix.Swoosh, except: [render_body: 3]
18 import Pleroma.Config.Helpers, only: [instance_name: 0, sender: 0]
20 def render_body(email, template, assigns \\ %{}) do
22 |> put_new_layout({Pleroma.Web.LayoutView, :email})
23 |> put_new_view(Pleroma.Web.EmailView)
24 |> Phoenix.Swoosh.render_body(template, assigns)
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)
31 @spec welcome(User.t(), map()) :: Swoosh.Email.t()
32 def welcome(user, opts \\ %{}) do
33 Gettext.with_locale_or_default user.language do
35 |> to(recipient(user))
36 |> from(Map.get(opts, :sender, sender()))
43 "welcome email subject",
44 "Welcome to %{instance_name}!",
45 instance_name: instance_name()
55 "welcome email html body",
56 "Welcome to %{instance_name}!",
57 instance_name: instance_name()
67 "welcome email text body",
68 "Welcome to %{instance_name}!",
69 instance_name: instance_name()
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)
83 "password reset email body",
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>
90 instance_name: instance_name(),
91 password_reset_url: password_reset_url
95 |> to(recipient(user))
98 Gettext.dpgettext("static_pages", "password reset email subject", "Password reset")
100 |> html_body(html_body)
104 def user_invitation_email(
106 %Pleroma.UserInviteToken{} = user_invite_token,
110 Gettext.with_locale_or_default user.language do
112 Router.Helpers.redirect_url(
115 user_invite_token.token
121 "user invitation email body",
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>
127 instance_name: instance_name(),
128 inviter_name: user.name,
129 registration_url: registration_url
133 |> to(recipient(to_email, to_name))
138 "user invitation email subject",
139 "Invitation to %{instance_name}",
140 instance_name: instance_name()
143 |> html_body(html_body)
147 def account_confirmation_email(user) do
148 Gettext.with_locale_or_default user.language do
150 Router.Helpers.confirm_email_url(
154 to_string(user.confirmation_token)
160 "confirmation email body",
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>
166 instance_name: instance_name(),
167 confirmation_url: confirmation_url
171 |> to(recipient(user))
176 "confirmation email subject",
177 "%{instance_name} account confirmation",
178 instance_name: instance_name()
181 |> html_body(html_body)
185 def approval_pending_email(user) do
186 Gettext.with_locale_or_default user.language do
190 "approval pending email body",
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>
195 instance_name: instance_name()
199 |> to(recipient(user))
204 "approval pending email subject",
205 "Your account is awaiting approval"
208 |> html_body(html_body)
212 def successful_registration_email(user) do
213 Gettext.with_locale_or_default user.language do
217 "successful registration email body",
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>
223 nickname: user.nickname,
224 instance_name: instance_name()
228 |> to(recipient(user))
233 "successful registration email subject",
234 "Account registered on %{instance_name}",
235 instance_name: instance_name()
238 |> html_body(html_body)
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
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)
254 |> Enum.filter(&(&1.activity.data["type"] == "Create"))
255 |> Enum.map(fn notification ->
256 object = Pleroma.Object.normalize(notification.activity, fetch: false)
258 if not is_nil(object) do
259 object = update_in(object.data["content"], &format_links/1)
264 from: User.get_by_ap_id(notification.activity.actor)
272 |> Enum.filter(&(&1.activity.data["type"] == "Follow"))
273 |> Enum.map(fn notification ->
274 from = User.get_by_ap_id(notification.activity.actor)
276 if not is_nil(from) do
279 object: Pleroma.Object.normalize(notification.activity, fetch: false),
280 from: User.get_by_ap_id(notification.activity.actor)
286 unless Enum.empty?(mentions) do
287 styling = Config.get([__MODULE__, :styling])
288 logo = Config.get([__MODULE__, :logo])
291 instance: instance_name(),
294 followers: followers,
295 unsubscribe_link: unsubscribe_url(user, "digest"),
301 Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg")
303 Path.join(Config.get([:instance, :static_dir]), logo)
307 |> to(recipient(user))
312 "digest email subject",
313 "Your digest from %{instance_name}",
314 instance_name: instance_name()
318 |> render_body("digest.html", html_data)
319 |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline))
324 defp format_links(str) do
325 re = ~r/<a.+href=['"].*>/iU
326 %{link_color: color} = Config.get([__MODULE__, :styling])
328 Regex.replace(re, str, fn link ->
329 String.replace(link, "<a", "<a style=\"color: #{color};text-decoration: none;\"")
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
338 @spec unsubscribe_url(User.t(), String.t()) :: String.t()
339 def unsubscribe_url(user, notifications_type) do
341 %{"sub" => user.id, "act" => %{"unsubscribe" => notifications_type}, "exp" => false}
342 |> Pleroma.JWT.generate_and_sign!()
345 Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
348 def backup_is_ready_email(backup, admin_user_id \\ nil) do
349 %{user: user} = Pleroma.Repo.preload(backup, :user)
351 Gettext.with_locale_or_default user.language do
352 download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
355 if is_nil(admin_user_id) do
358 "account archive email body - self-requested",
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>
363 download_url: download_url
366 admin = Pleroma.Repo.get(User, admin_user_id)
370 "account archive email body - admin requested",
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>
375 admin_nickname: admin.nickname,
376 download_url: download_url
381 |> to(recipient(user))
386 "account archive email subject",
387 "Your account archive is ready"
390 |> html_body(html_body)