Improve digest email template
[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.Web.Endpoint
11 alias Pleroma.Web.Router
12
13 defp instance_config, do: Pleroma.Config.get(:instance)
14
15 defp instance_name, do: instance_config()[:name]
16
17 defp sender do
18 email = Keyword.get(instance_config(), :notify_email, instance_config()[: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(%Pleroma.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.info.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(Pleroma.User.t()) :: Swoosh.Email.t() | nil
97 def digest_email(user) do
98 new_notifications =
99 Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
100 |> Enum.reduce(%{followers: [], mentions: []}, fn
101 %{activity: %{data: %{"type" => "Create"}, actor: actor} = activity} = notification,
102 acc ->
103 new_mention = %{
104 data: notification,
105 object: Pleroma.Object.normalize(activity),
106 from: Pleroma.User.get_by_ap_id(actor)
107 }
108
109 %{acc | mentions: [new_mention | acc.mentions]}
110
111 %{activity: %{data: %{"type" => "Follow"}, actor: actor} = activity} = notification,
112 acc ->
113 new_follower = %{
114 data: notification,
115 object: Pleroma.Object.normalize(activity),
116 from: Pleroma.User.get_by_ap_id(actor)
117 }
118
119 %{acc | followers: [new_follower | acc.followers]}
120
121 _, acc ->
122 acc
123 end)
124
125 with [_ | _] = mentions <- new_notifications.mentions do
126 mentions =
127 Enum.map(mentions, fn mention ->
128 update_in(mention.object.data["content"], &format_links/1)
129 end)
130
131 html_data = %{
132 instance: instance_name(),
133 user: user,
134 mentions: mentions,
135 followers: new_notifications.followers,
136 unsubscribe_link: unsubscribe_url(user, "digest")
137 }
138
139 logo_path = Path.join(:code.priv_dir(:pleroma), "static/static/logo.png")
140
141 new()
142 |> to(recipient(user))
143 |> from(sender())
144 |> subject("Your digest from #{instance_name()}")
145 |> put_layout(false)
146 |> render_body("digest.html", html_data)
147 |> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.png", type: :inline))
148 else
149 _ ->
150 nil
151 end
152 end
153
154 defp format_links(str) do
155 re = ~r/<a.+href=['"].*>/iU
156
157 String.replace(str, re, fn link ->
158 String.replace(link, "<a", "<a style=\"color: #d8a070;text-decoration: none;\"")
159 end)
160 end
161
162 @doc """
163 Generate unsubscribe link for given user and notifications type.
164 The link contains JWT token with the data, and subscription can be modified without
165 authorization.
166 """
167 @spec unsubscribe_url(Pleroma.User.t(), String.t()) :: String.t()
168 def unsubscribe_url(user, notifications_type) do
169 token =
170 %{"sub" => user.id, "act" => %{"unsubscribe" => notifications_type}, "exp" => false}
171 |> Pleroma.JWT.generate_and_sign!()
172 |> Base.encode64()
173
174 Router.Helpers.subscription_url(Pleroma.Web.Endpoint, :unsubscribe, token)
175 end
176 end