Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

232 lines
7.2KB

  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. defmodule Pleroma.Emails.UserEmail do
  5. @moduledoc "User emails"
  6. use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email}
  7. alias Pleroma.Config
  8. alias Pleroma.User
  9. alias Pleroma.Web.Endpoint
  10. alias Pleroma.Web.Router
  11. import Pleroma.Config.Helpers, only: [instance_name: 0, sender: 0]
  12. defp recipient(email, nil), do: email
  13. defp recipient(email, name), do: {name, email}
  14. defp recipient(%User{} = user), do: recipient(user.email, user.name)
  15. @spec welcome(User.t(), map()) :: Swoosh.Email.t()
  16. def welcome(user, opts \\ %{}) do
  17. new()
  18. |> to(recipient(user))
  19. |> from(Map.get(opts, :sender, sender()))
  20. |> subject(Map.get(opts, :subject, "Welcome to #{instance_name()}!"))
  21. |> html_body(Map.get(opts, :html, "Welcome to #{instance_name()}!"))
  22. |> text_body(Map.get(opts, :text, "Welcome to #{instance_name()}!"))
  23. end
  24. def password_reset_email(user, token) when is_binary(token) do
  25. password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token)
  26. html_body = """
  27. <h3>Reset your password at #{instance_name()}</h3>
  28. <p>Someone has requested password change for your account at #{instance_name()}.</p>
  29. <p>If it was you, visit the following link to proceed: <a href="#{password_reset_url}">reset password</a>.</p>
  30. <p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p>
  31. """
  32. new()
  33. |> to(recipient(user))
  34. |> from(sender())
  35. |> subject("Password reset")
  36. |> html_body(html_body)
  37. end
  38. def user_invitation_email(
  39. user,
  40. %Pleroma.UserInviteToken{} = user_invite_token,
  41. to_email,
  42. to_name \\ nil
  43. ) do
  44. registration_url =
  45. Router.Helpers.redirect_url(
  46. Endpoint,
  47. :registration_page,
  48. user_invite_token.token
  49. )
  50. html_body = """
  51. <h3>You are invited to #{instance_name()}</h3>
  52. <p>#{user.name} invites you to join #{instance_name()}, an instance of Pleroma federated social networking platform.</p>
  53. <p>Click the following link to register: <a href="#{registration_url}">accept invitation</a>.</p>
  54. """
  55. new()
  56. |> to(recipient(to_email, to_name))
  57. |> from(sender())
  58. |> subject("Invitation to #{instance_name()}")
  59. |> html_body(html_body)
  60. end
  61. def account_confirmation_email(user) do
  62. confirmation_url =
  63. Router.Helpers.confirm_email_url(
  64. Endpoint,
  65. :confirm_email,
  66. user.id,
  67. to_string(user.confirmation_token)
  68. )
  69. html_body = """
  70. <h3>Welcome to #{instance_name()}!</h3>
  71. <p>Email confirmation is required to activate the account.</p>
  72. <p>Click the following link to proceed: <a href="#{confirmation_url}">activate your account</a>.</p>
  73. """
  74. new()
  75. |> to(recipient(user))
  76. |> from(sender())
  77. |> subject("#{instance_name()} account confirmation")
  78. |> html_body(html_body)
  79. end
  80. def approval_pending_email(user) do
  81. html_body = """
  82. <h3>Awaiting Approval</h3>
  83. <p>Your account at #{instance_name()} is being reviewed by staff. You will receive another email once your account is approved.</p>
  84. """
  85. new()
  86. |> to(recipient(user))
  87. |> from(sender())
  88. |> subject("Your account is awaiting approval")
  89. |> html_body(html_body)
  90. end
  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. mentions =
  100. notifications
  101. |> Enum.filter(&(&1.activity.data["type"] == "Create"))
  102. |> Enum.map(fn notification ->
  103. object = Pleroma.Object.normalize(notification.activity, fetch: false)
  104. if not is_nil(object) do
  105. object = update_in(object.data["content"], &format_links/1)
  106. %{
  107. data: notification,
  108. object: object,
  109. from: User.get_by_ap_id(notification.activity.actor)
  110. }
  111. end
  112. end)
  113. |> Enum.filter(& &1)
  114. followers =
  115. notifications
  116. |> Enum.filter(&(&1.activity.data["type"] == "Follow"))
  117. |> Enum.map(fn notification ->
  118. from = User.get_by_ap_id(notification.activity.actor)
  119. if not is_nil(from) do
  120. %{
  121. data: notification,
  122. object: Pleroma.Object.normalize(notification.activity, fetch: false),
  123. from: User.get_by_ap_id(notification.activity.actor)
  124. }
  125. end
  126. end)
  127. |> Enum.filter(& &1)
  128. unless Enum.empty?(mentions) do
  129. styling = Config.get([__MODULE__, :styling])
  130. logo = Config.get([__MODULE__, :logo])
  131. html_data = %{
  132. instance: instance_name(),
  133. user: user,
  134. mentions: mentions,
  135. followers: followers,
  136. unsubscribe_link: unsubscribe_url(user, "digest"),
  137. styling: styling
  138. }
  139. logo_path =
  140. if is_nil(logo) do
  141. Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg")
  142. else
  143. Path.join(Config.get([:instance, :static_dir]), logo)
  144. end
  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.svg", type: :inline))
  152. end
  153. end
  154. defp format_links(str) do
  155. re = ~r/<a.+href=['"].*>/iU
  156. %{link_color: color} = Config.get([__MODULE__, :styling])
  157. Regex.replace(re, str, fn link ->
  158. String.replace(link, "<a", "<a style=\"color: #{color};text-decoration: none;\"")
  159. end)
  160. end
  161. @doc """
  162. Generate unsubscribe link for given user and notifications type.
  163. The link contains JWT token with the data, and subscription can be modified without
  164. authorization.
  165. """
  166. @spec unsubscribe_url(User.t(), String.t()) :: String.t()
  167. def unsubscribe_url(user, notifications_type) do
  168. token =
  169. %{"sub" => user.id, "act" => %{"unsubscribe" => notifications_type}, "exp" => false}
  170. |> Pleroma.JWT.generate_and_sign!()
  171. |> Base.encode64()
  172. Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
  173. end
  174. def backup_is_ready_email(backup, admin_user_id \\ nil) do
  175. %{user: user} = Pleroma.Repo.preload(backup, :user)
  176. download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
  177. html_body =
  178. if is_nil(admin_user_id) do
  179. """
  180. <p>You requested a full backup of your Pleroma account. It's ready for download:</p>
  181. <p><a href="#{download_url}">#{download_url}</a></p>
  182. """
  183. else
  184. admin = Pleroma.Repo.get(User, admin_user_id)
  185. """
  186. <p>Admin @#{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:</p>
  187. <p><a href="#{download_url}">#{download_url}</a></p>
  188. """
  189. end
  190. new()
  191. |> to(recipient(user))
  192. |> from(sender())
  193. |> subject("Your account archive is ready")
  194. |> html_body(html_body)
  195. end
  196. end