1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2021 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
34 |> to(recipient(user))
35 |> from(Map.get(opts, :sender, sender()))
40 Gettext.dpgettext("static_pages", "welcome email subject", "Welcome to %{instance_name}!",
41 instance_name: instance_name()
51 "welcome email html body",
52 "Welcome to %{instance_name}!",
53 instance_name: instance_name()
63 "welcome email text body",
64 "Welcome to %{instance_name}!",
65 instance_name: instance_name()
71 def password_reset_email(user, token) when is_binary(token) do
72 password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token)
77 "password reset email body",
79 <h3>Reset your password at %{instance_name}</h3>
80 <p>Someone has requested password change for your account at %{instance_name}.</p>
81 <p>If it was you, visit the following link to proceed: <a href="%{password_reset_url}">reset password</a>.</p>
82 <p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p>
84 instance_name: instance_name(),
85 password_reset_url: password_reset_url
89 |> to(recipient(user))
92 Gettext.dpgettext("static_pages", "password reset email subject", "Password reset")
94 |> html_body(html_body)
97 def user_invitation_email(
99 %Pleroma.UserInviteToken{} = user_invite_token,
104 Router.Helpers.redirect_url(
107 user_invite_token.token
113 "user invitation email body",
115 <h3>You are invited to %{instance_name}</h3>
116 <p>%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.</p>
117 <p>Click the following link to register: <a href="%{registration_url}">accept invitation</a>.</p>
119 instance_name: instance_name(),
120 inviter_name: user.name,
121 registration_url: registration_url
125 |> to(recipient(to_email, to_name))
130 "user invitation email subject",
131 "Invitation to %{instance_name}",
132 instance_name: instance_name()
135 |> html_body(html_body)
138 def account_confirmation_email(user) do
140 Router.Helpers.confirm_email_url(
144 to_string(user.confirmation_token)
150 "confirmation email body",
152 <h3>Thank you for registering on %{instance_name}</h3>
153 <p>Email confirmation is required to activate the account.</p>
154 <p>Please click the following link to <a href="%{confirmation_url}">activate your account</a>.</p>
156 instance_name: instance_name(),
157 confirmation_url: confirmation_url
161 |> to(recipient(user))
166 "confirmation email subject",
167 "%{instance_name} account confirmation",
168 instance_name: instance_name()
171 |> html_body(html_body)
174 def approval_pending_email(user) do
178 "approval pending email body",
180 <h3>Awaiting Approval</h3>
181 <p>Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.</p>
183 instance_name: instance_name()
187 |> to(recipient(user))
192 "approval pending email subject",
193 "Your account is awaiting approval"
196 |> html_body(html_body)
199 def successful_registration_email(user) do
203 "successful registration email body",
205 <h3>Hello @%{nickname},</h3>
206 <p>Your account at %{instance_name} has been registered successfully.</p>
207 <p>No further action is required to activate your account.</p>
209 nickname: user.nickname,
210 instance_name: instance_name()
214 |> to(recipient(user))
219 "successful registration email subject",
220 "Account registered on %{instance_name}",
221 instance_name: instance_name()
224 |> html_body(html_body)
228 Email used in digest email notifications
229 Includes Mentions and New Followers data
230 If there are no mentions (even when new followers exist), the function will return nil
232 @spec digest_email(User.t()) :: Swoosh.Email.t() | nil
233 def digest_email(user) do
234 notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
238 |> Enum.filter(&(&1.activity.data["type"] == "Create"))
239 |> Enum.map(fn notification ->
240 object = Pleroma.Object.normalize(notification.activity, fetch: false)
242 if not is_nil(object) do
243 object = update_in(object.data["content"], &format_links/1)
248 from: User.get_by_ap_id(notification.activity.actor)
256 |> Enum.filter(&(&1.activity.data["type"] == "Follow"))
257 |> Enum.map(fn notification ->
258 from = User.get_by_ap_id(notification.activity.actor)
260 if not is_nil(from) do
263 object: Pleroma.Object.normalize(notification.activity, fetch: false),
264 from: User.get_by_ap_id(notification.activity.actor)
270 unless Enum.empty?(mentions) do
271 styling = Config.get([__MODULE__, :styling])
272 logo = Config.get([__MODULE__, :logo])
275 instance: instance_name(),
278 followers: followers,
279 unsubscribe_link: unsubscribe_url(user, "digest"),
285 Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg")
287 Path.join(Config.get([:instance, :static_dir]), logo)
291 |> to(recipient(user))
296 "digest email subject",
297 "Your digest from %{instance_name}",
298 instance_name: instance_name()
302 |> render_body("digest.html", html_data)
303 |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline))
307 defp format_links(str) do
308 re = ~r/<a.+href=['"].*>/iU
309 %{link_color: color} = Config.get([__MODULE__, :styling])
311 Regex.replace(re, str, fn link ->
312 String.replace(link, "<a", "<a style=\"color: #{color};text-decoration: none;\"")
317 Generate unsubscribe link for given user and notifications type.
318 The link contains JWT token with the data, and subscription can be modified without
321 @spec unsubscribe_url(User.t(), String.t()) :: String.t()
322 def unsubscribe_url(user, notifications_type) do
324 %{"sub" => user.id, "act" => %{"unsubscribe" => notifications_type}, "exp" => false}
325 |> Pleroma.JWT.generate_and_sign!()
328 Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
331 def backup_is_ready_email(backup, admin_user_id \\ nil) do
332 %{user: user} = Pleroma.Repo.preload(backup, :user)
333 download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
336 if is_nil(admin_user_id) do
339 "account archive email body - self-requested",
341 <p>You requested a full backup of your Pleroma account. It's ready for download:</p>
342 <p><a href="%{download_url}">%{download_url}</a></p>
344 download_url: download_url
347 admin = Pleroma.Repo.get(User, admin_user_id)
351 "account archive email body - admin requested",
353 <p>Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:</p>
354 <p><a href="%{download_url}">%{download_url}</a></p>
356 admin_nickname: admin.nickname,
357 download_url: download_url
362 |> to(recipient(user))
367 "account archive email subject",
368 "Your account archive is ready"
371 |> html_body(html_body)