Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into embedded-object...
[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 @doc """
97 Email used in digest email notifications
98 Includes Mentions and New Followers data
99 If there are no mentions (even when new followers exist), the function will return nil
100 """
101 @spec digest_email(User.t()) :: Swoosh.Email.t() | nil
102 def digest_email(user) do
103 notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
104
105 mentions =
106 notifications
107 |> Enum.filter(&(&1.activity.data["type"] == "Create"))
108 |> Enum.map(fn notification ->
109 object = Pleroma.Object.normalize(notification.activity)
110 object = update_in(object.data["content"], &format_links/1)
111
112 %{
113 data: notification,
114 object: object,
115 from: User.get_by_ap_id(notification.activity.actor)
116 }
117 end)
118
119 followers =
120 notifications
121 |> Enum.filter(&(&1.activity.data["type"] == "Follow"))
122 |> Enum.map(fn notification ->
123 %{
124 data: notification,
125 object: Pleroma.Object.normalize(notification.activity),
126 from: User.get_by_ap_id(notification.activity.actor)
127 }
128 end)
129
130 unless Enum.empty?(mentions) do
131 styling = Config.get([__MODULE__, :styling])
132 logo = Config.get([__MODULE__, :logo])
133
134 html_data = %{
135 instance: instance_name(),
136 user: user,
137 mentions: mentions,
138 followers: followers,
139 unsubscribe_link: unsubscribe_url(user, "digest"),
140 styling: styling
141 }
142
143 logo_path =
144 if is_nil(logo) do
145 Path.join(:code.priv_dir(:pleroma), "static/static/logo.png")
146 else
147 Path.join(Config.get([:instance, :static_dir]), logo)
148 end
149
150 new()
151 |> to(recipient(user))
152 |> from(sender())
153 |> subject("Your digest from #{instance_name()}")
154 |> put_layout(false)
155 |> render_body("digest.html", html_data)
156 |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.png", type: :inline))
157 end
158 end
159
160 defp format_links(str) do
161 re = ~r/<a.+href=['"].*>/iU
162 %{link_color: color} = Config.get([__MODULE__, :styling])
163
164 Regex.replace(re, str, fn link ->
165 String.replace(link, "<a", "<a style=\"color: #{color};text-decoration: none;\"")
166 end)
167 end
168
169 @doc """
170 Generate unsubscribe link for given user and notifications type.
171 The link contains JWT token with the data, and subscription can be modified without
172 authorization.
173 """
174 @spec unsubscribe_url(User.t(), String.t()) :: String.t()
175 def unsubscribe_url(user, notifications_type) do
176 token =
177 %{"sub" => user.id, "act" => %{"unsubscribe" => notifications_type}, "exp" => false}
178 |> Pleroma.JWT.generate_and_sign!()
179 |> Base.encode64()
180
181 Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
182 end
183 end