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