Slightly better formatting
[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 use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email}
9
10 alias Pleroma.Config
11 alias Pleroma.HTML
12 alias Pleroma.User
13 alias Pleroma.Web.Endpoint
14 alias Pleroma.Web.Router
15
16 import Pleroma.Config.Helpers, only: [instance_name: 0, sender: 0]
17
18 defp recipient(email, nil), do: email
19 defp recipient(email, name), do: {name, email}
20 defp recipient(%User{} = user), do: recipient(user.email, user.name)
21
22 @spec welcome(User.t(), map()) :: Swoosh.Email.t()
23 def welcome(user, opts \\ %{}) do
24 new()
25 |> to(recipient(user))
26 |> from(Map.get(opts, :sender, sender()))
27 |> subject(Map.get(opts, :subject, "Welcome to #{instance_name()}!"))
28 |> html_body(Map.get(opts, :html, "Welcome to #{instance_name()}!"))
29 |> text_body(Map.get(opts, :text, "Welcome to #{instance_name()}!"))
30 end
31
32 def password_reset_email(user, token) when is_binary(token) do
33 password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token)
34
35 html_body = """
36 <h3>Reset your password at #{instance_name()}</h3>
37 <p>Someone has requested password change for your account at #{instance_name()}.</p>
38 <p>If it was you, visit the following link to proceed: <a href="#{password_reset_url}">reset password</a>.</p>
39 <p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p>
40 """
41
42 new()
43 |> to(recipient(user))
44 |> from(sender())
45 |> subject("Password reset")
46 |> html_body(html_body)
47 |> text_body(HTML.strip_tags(html_body))
48 end
49
50 def user_invitation_email(
51 user,
52 %Pleroma.UserInviteToken{} = user_invite_token,
53 to_email,
54 to_name \\ nil
55 ) do
56 registration_url =
57 Router.Helpers.redirect_url(
58 Endpoint,
59 :registration_page,
60 user_invite_token.token
61 )
62
63 html_body = """
64 <h3>You are invited to #{instance_name()}</h3>
65 <p>#{user.name} invites you to join #{instance_name()}, an instance of Pleroma federated social networking platform.</p>
66 <p>Click the following link to register: <a href="#{registration_url}">accept invitation</a>.</p>
67 """
68
69 new()
70 |> to(recipient(to_email, to_name))
71 |> from(sender())
72 |> subject("Invitation to #{instance_name()}")
73 |> html_body(html_body)
74 |> text_body(HTML.strip_tags(html_body))
75 end
76
77 def account_confirmation_email(user) do
78 confirmation_url =
79 Router.Helpers.confirm_email_url(
80 Endpoint,
81 :confirm_email,
82 user.id,
83 to_string(user.confirmation_token)
84 )
85
86 html_body = """
87 <h3>Thank you for registering on #{instance_name()}</h3>
88 <p>Email confirmation is required to activate the account.</p>
89 <p>Please click the following link to <a href="#{confirmation_url}">activate your account</a>.</p>
90 """
91
92 new()
93 |> to(recipient(user))
94 |> from(sender())
95 |> subject("#{instance_name()} account confirmation")
96 |> html_body(html_body)
97 |> text_body(HTML.strip_tags(html_body))
98 end
99
100 def approval_pending_email(user) do
101 html_body = """
102 <h3>Awaiting Approval</h3>
103 <p>Your account at #{instance_name()} is being reviewed by staff. You will receive another email once your account is approved.</p>
104 """
105
106 new()
107 |> to(recipient(user))
108 |> from(sender())
109 |> subject("Your account is awaiting approval")
110 |> html_body(html_body)
111 |> text_body(HTML.strip_tags(html_body))
112 end
113
114 def successful_registration_email(user) do
115 html_body = """
116 <h3>Hello @#{user.nickname},</h3>
117 <p>Your account at #{instance_name()} has been registered successfully.</p>
118 <p>No further action is required to activate your account.</p>
119 """
120
121 new()
122 |> to(recipient(user))
123 |> from(sender())
124 |> subject("Account registered on #{instance_name()}")
125 |> html_body(html_body)
126 |> text_body(HTML.strip_tags(html_body))
127 end
128
129 @doc """
130 Email used in digest email notifications
131 Includes Mentions and New Followers data
132 If there are no mentions (even when new followers exist), the function will return nil
133 """
134 @spec digest_email(User.t()) :: Swoosh.Email.t() | nil
135 def digest_email(user) do
136 notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
137
138 mentions =
139 notifications
140 |> Enum.filter(&(&1.activity.data["type"] == "Create"))
141 |> Enum.map(fn notification ->
142 object = Pleroma.Object.normalize(notification.activity, fetch: false)
143
144 if not is_nil(object) do
145 object = update_in(object.data["content"], &format_links/1)
146
147 %{
148 data: notification,
149 object: object,
150 from: User.get_by_ap_id(notification.activity.actor)
151 }
152 end
153 end)
154 |> Enum.filter(& &1)
155
156 followers =
157 notifications
158 |> Enum.filter(&(&1.activity.data["type"] == "Follow"))
159 |> Enum.map(fn notification ->
160 from = User.get_by_ap_id(notification.activity.actor)
161
162 if not is_nil(from) do
163 %{
164 data: notification,
165 object: Pleroma.Object.normalize(notification.activity, fetch: false),
166 from: User.get_by_ap_id(notification.activity.actor)
167 }
168 end
169 end)
170 |> Enum.filter(& &1)
171
172 unless Enum.empty?(mentions) do
173 styling = Config.get([__MODULE__, :styling])
174 logo = Config.get([__MODULE__, :logo])
175
176 html_data = %{
177 instance: instance_name(),
178 user: user,
179 mentions: mentions,
180 followers: followers,
181 unsubscribe_link: unsubscribe_url(user, "digest"),
182 styling: styling
183 }
184
185 logo_path =
186 if is_nil(logo) do
187 Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg")
188 else
189 Path.join(Config.get([:instance, :static_dir]), logo)
190 end
191
192 new()
193 |> to(recipient(user))
194 |> from(sender())
195 |> subject("Your digest from #{instance_name()}")
196 |> put_layout(false)
197 |> render_body("digest.html", html_data)
198 |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline))
199 end
200 end
201
202 defp format_links(str) do
203 re = ~r/<a.+href=['"].*>/iU
204 %{link_color: color} = Config.get([__MODULE__, :styling])
205
206 Regex.replace(re, str, fn link ->
207 String.replace(link, "<a", "<a style=\"color: #{color};text-decoration: none;\"")
208 end)
209 end
210
211 @doc """
212 Generate unsubscribe link for given user and notifications type.
213 The link contains JWT token with the data, and subscription can be modified without
214 authorization.
215 """
216 @spec unsubscribe_url(User.t(), String.t()) :: String.t()
217 def unsubscribe_url(user, notifications_type) do
218 token =
219 %{"sub" => user.id, "act" => %{"unsubscribe" => notifications_type}, "exp" => false}
220 |> Pleroma.JWT.generate_and_sign!()
221 |> Base.encode64()
222
223 Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
224 end
225
226 def backup_is_ready_email(backup, admin_user_id \\ nil) do
227 %{user: user} = Pleroma.Repo.preload(backup, :user)
228 download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
229
230 html_body =
231 if is_nil(admin_user_id) do
232 """
233 <p>You requested a full backup of your Pleroma account. It's ready for download:</p>
234 <p><a href="#{download_url}">#{download_url}</a></p>
235 """
236 else
237 admin = Pleroma.Repo.get(User, admin_user_id)
238
239 """
240 <p>Admin @#{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:</p>
241 <p><a href="#{download_url}">#{download_url}</a></p>
242 """
243 end
244
245 new()
246 |> to(recipient(user))
247 |> from(sender())
248 |> subject("Your account archive is ready")
249 |> html_body(html_body)
250 |> text_body(HTML.strip_tags(html_body))
251 end
252 end