a10f88f936cc23bc55d7c757b93ac6ec0f33e3b9
[akkoma] / lib / pleroma / emails / user_email.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 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 defp instance_name, do: Config.get([:instance, :name])
16
17 defp sender do
18 email = Config.get([:instance, :notify_email]) || Config.get([:instance, :email])
19 {instance_name(), email}
20 end
21
22 defp recipient(email, nil), do: email
23 defp recipient(email, name), do: {name, email}
24 defp recipient(%User{} = user), do: recipient(user.email, user.name)
25
26 def password_reset_email(user, token) when is_binary(token) do
27 password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token)
28
29 html_body = """
30 <h3>Reset your password at #{instance_name()}</h3>
31 <p>Someone has requested password change for your account at #{instance_name()}.</p>
32 <p>If it was you, visit the following link to proceed: <a href="#{password_reset_url}">reset password</a>.</p>
33 <p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p>
34 """
35
36 new()
37 |> to(recipient(user))
38 |> from(sender())
39 |> subject("Password reset")
40 |> html_body(html_body)
41 end
42
43 def user_invitation_email(
44 user,
45 %Pleroma.UserInviteToken{} = user_invite_token,
46 to_email,
47 to_name \\ nil
48 ) do
49 registration_url =
50 Router.Helpers.redirect_url(
51 Endpoint,
52 :registration_page,
53 user_invite_token.token
54 )
55
56 html_body = """
57 <h3>You are invited to #{instance_name()}</h3>
58 <p>#{user.name} invites you to join #{instance_name()}, an instance of Pleroma federated social networking platform.</p>
59 <p>Click the following link to register: <a href="#{registration_url}">accept invitation</a>.</p>
60 """
61
62 new()
63 |> to(recipient(to_email, to_name))
64 |> from(sender())
65 |> subject("Invitation to #{instance_name()}")
66 |> html_body(html_body)
67 end
68
69 def account_confirmation_email(user) do
70 confirmation_url =
71 Router.Helpers.confirm_email_url(
72 Endpoint,
73 :confirm_email,
74 user.id,
75 to_string(user.confirmation_token)
76 )
77
78 html_body = """
79 <h3>Welcome to #{instance_name()}!</h3>
80 <p>Email confirmation is required to activate the account.</p>
81 <p>Click the following link to proceed: <a href="#{confirmation_url}">activate your account</a>.</p>
82 """
83
84 new()
85 |> to(recipient(user))
86 |> from(sender())
87 |> subject("#{instance_name()} account confirmation")
88 |> html_body(html_body)
89 end
90
91 @doc """
92 Email used in digest email notifications
93 Includes Mentions and New Followers data
94 If there are no mentions (even when new followers exist), the function will return nil
95 """
96 @spec digest_email(User.t()) :: Swoosh.Email.t() | nil
97 def digest_email(user) do
98 notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
99
100 mentions =
101 notifications
102 |> Enum.filter(&(&1.activity.data["type"] == "Create"))
103 |> Enum.map(fn notification ->
104 object = Pleroma.Object.normalize(notification.activity)
105 object = update_in(object.data["content"], &format_links/1)
106
107 %{
108 data: notification,
109 object: object,
110 from: User.get_by_ap_id(notification.activity.actor)
111 }
112 end)
113
114 followers =
115 notifications
116 |> Enum.filter(&(&1.activity.data["type"] == "Follow"))
117 |> Enum.map(fn notification ->
118 %{
119 data: notification,
120 object: Pleroma.Object.normalize(notification.activity),
121 from: User.get_by_ap_id(notification.activity.actor)
122 }
123 end)
124
125 unless Enum.empty?(mentions) do
126 styling = Config.get([__MODULE__, :styling])
127 logo = Config.get([__MODULE__, :logo])
128
129 html_data = %{
130 instance: instance_name(),
131 user: user,
132 mentions: mentions,
133 followers: followers,
134 unsubscribe_link: unsubscribe_url(user, "digest"),
135 styling: styling
136 }
137
138 logo_path =
139 if is_nil(logo) do
140 Path.join(:code.priv_dir(:pleroma), "static/static/logo.png")
141 else
142 Path.join(Config.get([:instance, :static_dir]), logo)
143 end
144
145 new()
146 |> to(recipient(user))
147 |> from(sender())
148 |> subject("Your digest from #{instance_name()}")
149 |> put_layout(false)
150 |> render_body("digest.html", html_data)
151 |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.png", type: :inline))
152 end
153 end
154
155 defp format_links(str) do
156 re = ~r/<a.+href=['"].*>/iU
157 %{link_color: color} = Config.get([__MODULE__, :styling])
158
159 Regex.replace(re, str, fn link ->
160 String.replace(link, "<a", "<a style=\"color: #{color};text-decoration: none;\"")
161 end)
162 end
163
164 @doc """
165 Generate unsubscribe link for given user and notifications type.
166 The link contains JWT token with the data, and subscription can be modified without
167 authorization.
168 """
169 @spec unsubscribe_url(User.t(), String.t()) :: String.t()
170 def unsubscribe_url(user, notifications_type) do
171 token =
172 %{"sub" => user.id, "act" => %{"unsubscribe" => notifications_type}, "exp" => false}
173 |> Pleroma.JWT.generate_and_sign!()
174 |> Base.encode64()
175
176 Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
177 end
178 end