d3625dbf2bb7bc54f3f27a901261460570eff0a8
[akkoma] / lib / pleroma / emails / user_email.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 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>Welcome to #{instance_name()}!</h3>
85 <p>Email confirmation is required to activate the account.</p>
86 <p>Click the following link to proceed: <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 @doc """
110 Email used in digest email notifications
111 Includes Mentions and New Followers data
112 If there are no mentions (even when new followers exist), the function will return nil
113 """
114 @spec digest_email(User.t()) :: Swoosh.Email.t() | nil
115 def digest_email(user) do
116 notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
117
118 mentions =
119 notifications
120 |> Enum.filter(&(&1.activity.data["type"] == "Create"))
121 |> Enum.map(fn notification ->
122 object = Pleroma.Object.normalize(notification.activity)
123
124 if not is_nil(object) do
125 object = update_in(object.data["content"], &format_links/1)
126
127 %{
128 data: notification,
129 object: object,
130 from: User.get_by_ap_id(notification.activity.actor)
131 }
132 end
133 end)
134 |> Enum.filter(& &1)
135
136 followers =
137 notifications
138 |> Enum.filter(&(&1.activity.data["type"] == "Follow"))
139 |> Enum.map(fn notification ->
140 from = User.get_by_ap_id(notification.activity.actor)
141
142 if not is_nil(from) do
143 %{
144 data: notification,
145 object: Pleroma.Object.normalize(notification.activity),
146 from: User.get_by_ap_id(notification.activity.actor)
147 }
148 end
149 end)
150 |> Enum.filter(& &1)
151
152 unless Enum.empty?(mentions) do
153 styling = Config.get([__MODULE__, :styling])
154 logo = Config.get([__MODULE__, :logo])
155
156 html_data = %{
157 instance: instance_name(),
158 user: user,
159 mentions: mentions,
160 followers: followers,
161 unsubscribe_link: unsubscribe_url(user, "digest"),
162 styling: styling
163 }
164
165 logo_path =
166 if is_nil(logo) do
167 Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg")
168 else
169 Path.join(Config.get([:instance, :static_dir]), logo)
170 end
171
172 new()
173 |> to(recipient(user))
174 |> from(sender())
175 |> subject("Your digest from #{instance_name()}")
176 |> put_layout(false)
177 |> render_body("digest.html", html_data)
178 |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline))
179 end
180 end
181
182 defp format_links(str) do
183 re = ~r/<a.+href=['"].*>/iU
184 %{link_color: color} = Config.get([__MODULE__, :styling])
185
186 Regex.replace(re, str, fn link ->
187 String.replace(link, "<a", "<a style=\"color: #{color};text-decoration: none;\"")
188 end)
189 end
190
191 @doc """
192 Generate unsubscribe link for given user and notifications type.
193 The link contains JWT token with the data, and subscription can be modified without
194 authorization.
195 """
196 @spec unsubscribe_url(User.t(), String.t()) :: String.t()
197 def unsubscribe_url(user, notifications_type) do
198 token =
199 %{"sub" => user.id, "act" => %{"unsubscribe" => notifications_type}, "exp" => false}
200 |> Pleroma.JWT.generate_and_sign!()
201 |> Base.encode64()
202
203 Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
204 end
205
206 def backup_is_ready_email(backup, admin_user_id \\ nil) do
207 %{user: user} = Pleroma.Repo.preload(backup, :user)
208 download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
209
210 html_body =
211 if is_nil(admin_user_id) do
212 """
213 <p>You requested a full backup of your Pleroma account. It's ready for download:</p>
214 <p><a href="#{download_url}">#{download_url}</a></p>
215 """
216 else
217 admin = Pleroma.Repo.get(User, admin_user_id)
218
219 """
220 <p>Admin @#{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:</p>
221 <p><a href="#{download_url}">#{download_url}</a></p>
222 """
223 end
224
225 new()
226 |> to(recipient(user))
227 |> from(sender())
228 |> subject("Your account archive is ready")
229 |> html_body(html_body)
230 end
231 end