de047c8939
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
215 lines
6.4 KiB
Elixir
215 lines
6.4 KiB
Elixir
defmodule Mobilizon.Web.Email.User do
|
|
@moduledoc """
|
|
Handles emails sent to users.
|
|
"""
|
|
|
|
use Bamboo.Phoenix, view: Mobilizon.Web.EmailView
|
|
|
|
import Bamboo.Phoenix
|
|
|
|
import Mobilizon.Web.Gettext, only: [gettext: 1, gettext: 2]
|
|
|
|
alias Mobilizon.{Config, Crypto, Users}
|
|
alias Mobilizon.Storage.Repo
|
|
alias Mobilizon.Users.User
|
|
|
|
alias Mobilizon.Web.Email
|
|
|
|
require Logger
|
|
|
|
@spec confirmation_email(User.t(), String.t()) :: Bamboo.Email.t()
|
|
def confirmation_email(
|
|
%User{email: email, confirmation_token: confirmation_token},
|
|
locale \\ "en"
|
|
) do
|
|
Gettext.put_locale(locale)
|
|
|
|
subject =
|
|
gettext(
|
|
"Instructions to confirm your Mobilizon account on %{instance}",
|
|
instance: Config.instance_name()
|
|
)
|
|
|
|
Email.base_email(to: email, subject: subject)
|
|
|> assign(:locale, locale)
|
|
|> assign(:token, confirmation_token)
|
|
|> assign(:subject, subject)
|
|
|> render(:registration_confirmation)
|
|
end
|
|
|
|
@spec reset_password_email(User.t(), String.t()) :: Bamboo.Email.t()
|
|
def reset_password_email(
|
|
%User{email: email, reset_password_token: reset_password_token},
|
|
locale \\ "en"
|
|
) do
|
|
Gettext.put_locale(locale)
|
|
|
|
subject =
|
|
gettext(
|
|
"Instructions to reset your password on %{instance}",
|
|
instance: Config.instance_name()
|
|
)
|
|
|
|
Email.base_email(to: email, subject: subject)
|
|
|> assign(:locale, locale)
|
|
|> assign(:token, reset_password_token)
|
|
|> assign(:subject, subject)
|
|
|> render(:password_reset)
|
|
end
|
|
|
|
@spec check_confirmation_token(String.t()) :: {:ok, User.t()} | {:error, :invalid_token}
|
|
def check_confirmation_token(token) when is_binary(token) do
|
|
with %User{} = user <- Users.get_user_by_activation_token(token),
|
|
{:ok, %User{} = user} <-
|
|
Users.update_user(user, %{
|
|
confirmed_at: DateTime.utc_now() |> DateTime.truncate(:second),
|
|
confirmation_sent_at: nil,
|
|
confirmation_token: nil,
|
|
email: user.unconfirmed_email || user.email
|
|
}) do
|
|
Logger.info("User #{user.email} has been confirmed")
|
|
{:ok, user}
|
|
else
|
|
_err ->
|
|
{:error, :invalid_token}
|
|
end
|
|
end
|
|
|
|
def resend_confirmation_email(%User{} = user, locale \\ "en") do
|
|
with :ok <- we_can_send_email(user, :confirmation_sent_at),
|
|
{:ok, user} <-
|
|
Users.update_user(user, %{
|
|
"confirmation_sent_at" => DateTime.utc_now() |> DateTime.truncate(:second)
|
|
}),
|
|
%Bamboo.Email{} <- send_confirmation_email(user, locale) do
|
|
Logger.info("Sent confirmation email again to #{user.email}")
|
|
{:ok, user.email}
|
|
end
|
|
end
|
|
|
|
@spec send_confirmation_email(User.t(), String.t()) :: Bamboo.Email.t()
|
|
def send_confirmation_email(%User{} = user, locale \\ "en") do
|
|
user
|
|
|> Email.User.confirmation_email(locale)
|
|
|> Email.Mailer.send_email_later()
|
|
end
|
|
|
|
@doc """
|
|
Check that the provided token is correct and update provided password
|
|
"""
|
|
@spec check_reset_password_token(String.t(), String.t()) ::
|
|
{:ok, User.t()} | {:error, String.t()}
|
|
def check_reset_password_token(password, token) do
|
|
with %User{} = user <- Users.get_user_by_reset_password_token(token),
|
|
{:ok, %User{} = user} <-
|
|
Repo.update(
|
|
User.password_reset_changeset(user, %{
|
|
"password" => password,
|
|
"reset_password_sent_at" => nil,
|
|
"reset_password_token" => nil
|
|
})
|
|
) do
|
|
{:ok, user}
|
|
else
|
|
{:error, %Ecto.Changeset{errors: [password: {"registration.error.password_too_short", _}]}} ->
|
|
{:error,
|
|
gettext(
|
|
"The password you have choosen is too short. Please make sure your password contains at least 6 charaters."
|
|
)}
|
|
|
|
_err ->
|
|
{:error,
|
|
gettext(
|
|
"The token you provided is invalid. Make sure that the URL is exactly the one provided inside the email you got."
|
|
)}
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Send the email reset password, if it's not too soon since the last send
|
|
"""
|
|
@spec send_password_reset_email(User.t(), String.t()) :: tuple
|
|
def send_password_reset_email(%User{} = user, locale \\ "en") do
|
|
with :ok <- we_can_send_email(user, :reset_password_sent_at),
|
|
{:ok, %User{} = user_updated} <-
|
|
Repo.update(
|
|
User.send_password_reset_changeset(user, %{
|
|
"reset_password_token" => Crypto.random_string(30),
|
|
"reset_password_sent_at" => DateTime.utc_now() |> DateTime.truncate(:second)
|
|
})
|
|
),
|
|
%Bamboo.Email{} = mail <-
|
|
user_updated
|
|
|> Email.User.reset_password_email(locale)
|
|
|> Email.Mailer.send_email_later() do
|
|
{:ok, mail}
|
|
else
|
|
{:error, reason} -> {:error, reason}
|
|
end
|
|
end
|
|
|
|
def send_email_reset_old_email(
|
|
%User{locale: user_locale, email: email, unconfirmed_email: unconfirmed_email} = _user,
|
|
_locale \\ "en"
|
|
) do
|
|
Gettext.put_locale(user_locale)
|
|
|
|
subject =
|
|
gettext(
|
|
"Mobilizon on %{instance}: email changed",
|
|
instance: Config.instance_name()
|
|
)
|
|
|
|
Email.base_email(to: email, subject: subject)
|
|
|> assign(:locale, user_locale)
|
|
|> assign(:subject, subject)
|
|
|> assign(:new_email, unconfirmed_email)
|
|
|> render(:email_changed_old)
|
|
end
|
|
|
|
def send_email_reset_new_email(
|
|
%User{
|
|
locale: user_locale,
|
|
unconfirmed_email: unconfirmed_email,
|
|
confirmation_token: confirmation_token
|
|
} = _user,
|
|
_locale \\ "en"
|
|
) do
|
|
Gettext.put_locale(user_locale)
|
|
instance_name = Config.instance_name()
|
|
|
|
subject =
|
|
gettext(
|
|
"Mobilizon on %{instance}: confirm your email address",
|
|
instance: instance_name
|
|
)
|
|
|
|
Email.base_email(to: unconfirmed_email, subject: subject)
|
|
|> assign(:locale, user_locale)
|
|
|> assign(:subject, subject)
|
|
|> assign(:token, confirmation_token)
|
|
|> assign(:instance_name, instance_name)
|
|
|> render(:email_changed_new)
|
|
end
|
|
|
|
@spec we_can_send_email(User.t(), atom) :: :ok | {:error, :email_too_soon}
|
|
defp we_can_send_email(%User{} = user, key) do
|
|
case Map.get(user, key) do
|
|
nil ->
|
|
:ok
|
|
|
|
_ ->
|
|
case Timex.before?(
|
|
Timex.shift(Map.get(user, key), hours: 1),
|
|
DateTime.utc_now() |> DateTime.truncate(:second)
|
|
) do
|
|
true ->
|
|
:ok
|
|
|
|
false ->
|
|
{:error, :email_too_soon}
|
|
end
|
|
end
|
|
end
|
|
end
|