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