improve invites, record usage
This commit is contained in:
parent
eb75937587
commit
cd7220cea3
@ -1,6 +1,7 @@
|
|||||||
# v0.1.8
|
# v0.1.8
|
||||||
- Fix bug with public registration
|
- Fix bug with public registration
|
||||||
- Improve templates
|
- Improve templates
|
||||||
|
- Improve invites, record usage
|
||||||
|
|
||||||
# v0.1.7
|
# v0.1.7
|
||||||
- Update dependencies
|
- Update dependencies
|
||||||
|
@ -5,7 +5,7 @@ defmodule Memex.Accounts do
|
|||||||
|
|
||||||
import Ecto.Query, warn: false
|
import Ecto.Query, warn: false
|
||||||
alias Memex.{Mailer, Repo}
|
alias Memex.{Mailer, Repo}
|
||||||
alias Memex.Accounts.{User, UserToken}
|
alias Memex.Accounts.{Invite, Invites, User, UserToken}
|
||||||
alias Ecto.{Changeset, Multi}
|
alias Ecto.{Changeset, Multi}
|
||||||
alias Oban.Job
|
alias Oban.Job
|
||||||
|
|
||||||
@ -25,7 +25,9 @@ defmodule Memex.Accounts do
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
@spec get_user_by_email(email :: String.t()) :: User.t() | nil
|
@spec get_user_by_email(email :: String.t()) :: User.t() | nil
|
||||||
def get_user_by_email(email) when is_binary(email), do: Repo.get_by(User, email: email)
|
def get_user_by_email(email) when is_binary(email) do
|
||||||
|
Repo.get_by(User, email: email)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Gets a user by email and password.
|
Gets a user by email and password.
|
||||||
@ -64,7 +66,9 @@ defmodule Memex.Accounts do
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
@spec get_user!(User.t()) :: User.t()
|
@spec get_user!(User.t()) :: User.t()
|
||||||
def get_user!(id), do: Repo.get!(User, id)
|
def get_user!(id) do
|
||||||
|
Repo.get!(User, id)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns all users grouped by role.
|
Returns all users grouped by role.
|
||||||
@ -113,19 +117,27 @@ defmodule Memex.Accounts do
|
|||||||
:passed
|
:passed
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@spec register_user(attrs :: map()) :: {:ok, User.t()} | {:error, User.changeset()}
|
@spec register_user(attrs :: map(), Invite.token() | nil) ::
|
||||||
def register_user(attrs) do
|
{:ok, User.t()} | {:error, :invalid_token | User.changeset()}
|
||||||
|
def register_user(attrs, invite_token \\ nil) do
|
||||||
Multi.new()
|
Multi.new()
|
||||||
|> Multi.one(:users_count, from(u in User, select: count(u.id), distinct: true))
|
|> Multi.one(:users_count, from(u in User, select: count(u.id), distinct: true))
|
||||||
|> Multi.insert(:add_user, fn %{users_count: count} ->
|
|> Multi.run(:use_invite, fn _changes_so_far, _repo ->
|
||||||
|
if allow_registration?() and invite_token |> is_nil() do
|
||||||
|
{:ok, nil}
|
||||||
|
else
|
||||||
|
Invites.use_invite(invite_token)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> Multi.insert(:add_user, fn %{users_count: count, use_invite: invite} ->
|
||||||
# if no registered users, make first user an admin
|
# if no registered users, make first user an admin
|
||||||
role = if count == 0, do: :admin, else: :user
|
role = if count == 0, do: :admin, else: :user
|
||||||
|
User.registration_changeset(attrs, invite) |> User.role_changeset(role)
|
||||||
User.registration_changeset(attrs) |> User.role_changeset(role)
|
|
||||||
end)
|
end)
|
||||||
|> Repo.transaction()
|
|> Repo.transaction()
|
||||||
|> case do
|
|> case do
|
||||||
{:ok, %{add_user: user}} -> {:ok, user}
|
{:ok, %{add_user: user}} -> {:ok, user}
|
||||||
|
{:error, :use_invite, :invalid_token, _changes_so_far} -> {:error, :invalid_token}
|
||||||
{:error, :add_user, changeset, _changes_so_far} -> {:error, changeset}
|
{:error, :add_user, changeset, _changes_so_far} -> {:error, changeset}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -144,8 +156,9 @@ defmodule Memex.Accounts do
|
|||||||
"""
|
"""
|
||||||
@spec change_user_registration() :: User.changeset()
|
@spec change_user_registration() :: User.changeset()
|
||||||
@spec change_user_registration(attrs :: map()) :: User.changeset()
|
@spec change_user_registration(attrs :: map()) :: User.changeset()
|
||||||
def change_user_registration(attrs \\ %{}),
|
def change_user_registration(attrs \\ %{}) do
|
||||||
do: User.registration_changeset(attrs, hash_password: false)
|
User.registration_changeset(attrs, nil, hash_password: false)
|
||||||
|
end
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
@ -160,7 +173,9 @@ defmodule Memex.Accounts do
|
|||||||
"""
|
"""
|
||||||
@spec change_user_email(User.t()) :: User.changeset()
|
@spec change_user_email(User.t()) :: User.changeset()
|
||||||
@spec change_user_email(User.t(), attrs :: map()) :: User.changeset()
|
@spec change_user_email(User.t(), attrs :: map()) :: User.changeset()
|
||||||
def change_user_email(user, attrs \\ %{}), do: User.email_changeset(user, attrs)
|
def change_user_email(user, attrs \\ %{}) do
|
||||||
|
User.email_changeset(user, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns an `%Changeset{}` for changing the user role.
|
Returns an `%Changeset{}` for changing the user role.
|
||||||
@ -172,7 +187,9 @@ defmodule Memex.Accounts do
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
@spec change_user_role(User.t(), User.role()) :: User.changeset()
|
@spec change_user_role(User.t(), User.role()) :: User.changeset()
|
||||||
def change_user_role(user, role), do: User.role_changeset(user, role)
|
def change_user_role(user, role) do
|
||||||
|
User.role_changeset(user, role)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Emulates that the email will change without actually changing
|
Emulates that the email will change without actually changing
|
||||||
@ -262,8 +279,9 @@ defmodule Memex.Accounts do
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
@spec change_user_password(User.t(), attrs :: map()) :: User.changeset()
|
@spec change_user_password(User.t(), attrs :: map()) :: User.changeset()
|
||||||
def change_user_password(user, attrs \\ %{}),
|
def change_user_password(user, attrs \\ %{}) do
|
||||||
do: User.password_changeset(user, attrs, hash_password: false)
|
User.password_changeset(user, attrs, hash_password: false)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Updates the user password.
|
Updates the user password.
|
||||||
@ -314,7 +332,9 @@ defmodule Memex.Accounts do
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
@spec change_user_locale(User.t()) :: User.changeset()
|
@spec change_user_locale(User.t()) :: User.changeset()
|
||||||
def change_user_locale(%{locale: locale} = user), do: User.locale_changeset(user, locale)
|
def change_user_locale(%{locale: locale} = user) do
|
||||||
|
User.locale_changeset(user, locale)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Updates the user locale.
|
Updates the user locale.
|
||||||
@ -328,8 +348,9 @@ defmodule Memex.Accounts do
|
|||||||
"""
|
"""
|
||||||
@spec update_user_locale(User.t(), locale :: String.t()) ::
|
@spec update_user_locale(User.t(), locale :: String.t()) ::
|
||||||
{:ok, User.t()} | {:error, User.changeset()}
|
{:ok, User.t()} | {:error, User.changeset()}
|
||||||
def update_user_locale(user, locale),
|
def update_user_locale(user, locale) do
|
||||||
do: user |> User.locale_changeset(locale) |> Repo.update()
|
user |> User.locale_changeset(locale) |> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Deletes a user. must be performed by an admin or the same user!
|
Deletes a user. must be performed by an admin or the same user!
|
||||||
@ -346,8 +367,13 @@ defmodule Memex.Accounts do
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
@spec delete_user!(user_to_delete :: User.t(), User.t()) :: User.t()
|
@spec delete_user!(user_to_delete :: User.t(), User.t()) :: User.t()
|
||||||
def delete_user!(user, %User{role: :admin}), do: user |> Repo.delete!()
|
def delete_user!(user, %User{role: :admin}) do
|
||||||
def delete_user!(%User{id: user_id} = user, %User{id: user_id}), do: user |> Repo.delete!()
|
user |> Repo.delete!()
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_user!(%User{id: user_id} = user, %User{id: user_id}) do
|
||||||
|
user |> Repo.delete!()
|
||||||
|
end
|
||||||
|
|
||||||
## Session
|
## Session
|
||||||
|
|
||||||
@ -375,7 +401,7 @@ defmodule Memex.Accounts do
|
|||||||
"""
|
"""
|
||||||
@spec delete_session_token(token :: String.t()) :: :ok
|
@spec delete_session_token(token :: String.t()) :: :ok
|
||||||
def delete_session_token(token) do
|
def delete_session_token(token) do
|
||||||
Repo.delete_all(UserToken.token_and_context_query(token, "session"))
|
UserToken.token_and_context_query(token, "session") |> Repo.delete_all()
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
defmodule Memex.Invites.Invite do
|
defmodule Memex.Accounts.Invite do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
An invite, created by an admin to allow someone to join their instance. An
|
An invite, created by an admin to allow someone to join their instance. An
|
||||||
invite can be enabled or disabled, and can have an optional number of uses if
|
invite can be enabled or disabled, and can have an optional number of uses if
|
||||||
@ -7,8 +7,8 @@ defmodule Memex.Invites.Invite do
|
|||||||
|
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
alias Ecto.{Changeset, UUID}
|
alias Ecto.{Association, Changeset, UUID}
|
||||||
alias Memex.{Accounts.User, Invites.Invite}
|
alias Memex.Accounts.User
|
||||||
|
|
||||||
@primary_key {:id, :binary_id, autogenerate: true}
|
@primary_key {:id, :binary_id, autogenerate: true}
|
||||||
@foreign_key_type :binary_id
|
@foreign_key_type :binary_id
|
||||||
@ -18,40 +18,46 @@ defmodule Memex.Invites.Invite do
|
|||||||
field :uses_left, :integer, default: nil
|
field :uses_left, :integer, default: nil
|
||||||
field :disabled_at, :naive_datetime
|
field :disabled_at, :naive_datetime
|
||||||
|
|
||||||
belongs_to :user, User
|
belongs_to :created_by, User
|
||||||
|
|
||||||
|
has_many :users, User
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@type t :: %Invite{
|
@type t :: %__MODULE__{
|
||||||
id: id(),
|
id: id(),
|
||||||
name: String.t(),
|
name: String.t(),
|
||||||
token: String.t(),
|
token: token(),
|
||||||
uses_left: integer() | nil,
|
uses_left: integer() | nil,
|
||||||
disabled_at: NaiveDateTime.t(),
|
disabled_at: NaiveDateTime.t(),
|
||||||
user: User.t(),
|
created_by: User.t() | nil | Association.NotLoaded.t(),
|
||||||
user_id: User.id(),
|
created_by_id: User.id() | nil,
|
||||||
|
users: [User.t()] | Association.NotLoaded.t(),
|
||||||
inserted_at: NaiveDateTime.t(),
|
inserted_at: NaiveDateTime.t(),
|
||||||
updated_at: NaiveDateTime.t()
|
updated_at: NaiveDateTime.t()
|
||||||
}
|
}
|
||||||
@type new_invite :: %Invite{}
|
@type new_invite :: %__MODULE__{}
|
||||||
@type id :: UUID.t()
|
@type id :: UUID.t()
|
||||||
|
@type changeset :: Changeset.t(t() | new_invite())
|
||||||
|
@type token :: String.t()
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
@spec create_changeset(new_invite(), attrs :: map()) :: Changeset.t(new_invite())
|
@spec create_changeset(User.t(), token(), attrs :: map()) :: changeset()
|
||||||
def create_changeset(invite, attrs) do
|
def create_changeset(%User{id: user_id}, token, attrs) do
|
||||||
invite
|
%__MODULE__{}
|
||||||
|> cast(attrs, [:name, :token, :uses_left, :disabled_at, :user_id])
|
|> change(token: token, created_by_id: user_id)
|
||||||
|> validate_required([:name, :token, :user_id])
|
|> cast(attrs, [:name, :uses_left, :disabled_at])
|
||||||
|
|> validate_required([:name, :token, :created_by_id])
|
||||||
|> validate_number(:uses_left, greater_than_or_equal_to: 0)
|
|> validate_number(:uses_left, greater_than_or_equal_to: 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
@spec update_changeset(t() | new_invite(), attrs :: map()) :: Changeset.t(t() | new_invite())
|
@spec update_changeset(t() | new_invite(), attrs :: map()) :: changeset()
|
||||||
def update_changeset(invite, attrs) do
|
def update_changeset(invite, attrs) do
|
||||||
invite
|
invite
|
||||||
|> cast(attrs, [:name, :uses_left, :disabled_at])
|
|> cast(attrs, [:name, :uses_left, :disabled_at])
|
||||||
|> validate_required([:name, :token, :user_id])
|
|> validate_required([:name])
|
||||||
|> validate_number(:uses_left, greater_than_or_equal_to: 0)
|
|> validate_number(:uses_left, greater_than_or_equal_to: 0)
|
||||||
end
|
end
|
||||||
end
|
end
|
196
lib/memex/accounts/invites.ex
Normal file
196
lib/memex/accounts/invites.ex
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
defmodule Memex.Accounts.Invites do
|
||||||
|
@moduledoc """
|
||||||
|
The Invites context.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import Ecto.Query, warn: false
|
||||||
|
alias Ecto.Multi
|
||||||
|
alias Memex.Accounts.{Invite, User}
|
||||||
|
alias Memex.Repo
|
||||||
|
|
||||||
|
@invite_token_length 20
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns the list of invites.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> list_invites(%User{id: 123, role: :admin})
|
||||||
|
[%Invite{}, ...]
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec list_invites(User.t()) :: [Invite.t()]
|
||||||
|
def list_invites(%User{role: :admin}) do
|
||||||
|
Repo.all(from i in Invite, order_by: i.name)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets a single invite for a user
|
||||||
|
|
||||||
|
Raises `Ecto.NoResultsError` if the Invite does not exist.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> get_invite!(123, %User{id: 123, role: :admin})
|
||||||
|
%Invite{}
|
||||||
|
|
||||||
|
> get_invite!(456, %User{id: 123, role: :admin})
|
||||||
|
** (Ecto.NoResultsError)
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec get_invite!(Invite.id(), User.t()) :: Invite.t()
|
||||||
|
def get_invite!(id, %User{role: :admin}) do
|
||||||
|
Repo.get!(Invite, id)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns if an invite token is still valid
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> valid_invite_token?("valid_token")
|
||||||
|
%Invite{}
|
||||||
|
|
||||||
|
iex> valid_invite_token?("invalid_token")
|
||||||
|
nil
|
||||||
|
"""
|
||||||
|
@spec valid_invite_token?(Invite.token() | nil) :: boolean()
|
||||||
|
def valid_invite_token?(token) when token in [nil, ""], do: false
|
||||||
|
|
||||||
|
def valid_invite_token?(token) do
|
||||||
|
Repo.exists?(
|
||||||
|
from i in Invite,
|
||||||
|
where: i.token == ^token,
|
||||||
|
where: i.disabled_at |> is_nil()
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Uses invite by decrementing uses_left, or marks invite invalid if it's been
|
||||||
|
completely used.
|
||||||
|
"""
|
||||||
|
@spec use_invite(Invite.token()) :: {:ok, Invite.t()} | {:error, :invalid_token}
|
||||||
|
def use_invite(invite_token) do
|
||||||
|
Multi.new()
|
||||||
|
|> Multi.run(:invite, fn _changes_so_far, _repo ->
|
||||||
|
invite_token |> get_invite_by_token()
|
||||||
|
end)
|
||||||
|
|> Multi.update(:decrement_invite, fn %{invite: invite} ->
|
||||||
|
decrement_invite_changeset(invite)
|
||||||
|
end)
|
||||||
|
|> Repo.transaction()
|
||||||
|
|> case do
|
||||||
|
{:ok, %{decrement_invite: invite}} -> {:ok, invite}
|
||||||
|
{:error, :invite, :invalid_token, _changes_so_far} -> {:error, :invalid_token}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_invite_by_token(Invite.token()) :: {:ok, Invite.t()} | {:error, :invalid_token}
|
||||||
|
defp get_invite_by_token(token) do
|
||||||
|
Repo.one(
|
||||||
|
from i in Invite,
|
||||||
|
where: i.token == ^token,
|
||||||
|
where: i.disabled_at |> is_nil()
|
||||||
|
)
|
||||||
|
|> case do
|
||||||
|
nil -> {:error, :invalid_token}
|
||||||
|
invite -> {:ok, invite}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_use_count(Invite.t(), User.t()) :: non_neg_integer()
|
||||||
|
def get_use_count(%Invite{id: invite_id}, %User{role: :admin}) do
|
||||||
|
Repo.one(
|
||||||
|
from u in User,
|
||||||
|
where: u.invite_id == ^invite_id,
|
||||||
|
select: count(u.id)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec decrement_invite_changeset(Invite.t()) :: Invite.changeset()
|
||||||
|
defp decrement_invite_changeset(%Invite{uses_left: nil} = invite) do
|
||||||
|
invite |> Invite.update_changeset(%{})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp decrement_invite_changeset(%Invite{uses_left: 1} = invite) do
|
||||||
|
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
||||||
|
invite |> Invite.update_changeset(%{uses_left: 0, disabled_at: now})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp decrement_invite_changeset(%Invite{uses_left: uses_left} = invite) do
|
||||||
|
invite |> Invite.update_changeset(%{uses_left: uses_left - 1})
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Creates a invite.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> create_invite(%User{id: 123, role: :admin}, %{field: value})
|
||||||
|
{:ok, %Invite{}}
|
||||||
|
|
||||||
|
iex> create_invite(%User{id: 123, role: :admin}, %{field: bad_value})
|
||||||
|
{:error, %Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec create_invite(User.t(), attrs :: map()) ::
|
||||||
|
{:ok, Invite.t()} | {:error, Invite.changeset()}
|
||||||
|
def create_invite(%User{role: :admin} = user, attrs) do
|
||||||
|
token =
|
||||||
|
:crypto.strong_rand_bytes(@invite_token_length)
|
||||||
|
|> Base.url_encode64()
|
||||||
|
|> binary_part(0, @invite_token_length)
|
||||||
|
|
||||||
|
Invite.create_changeset(user, token, attrs) |> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Updates a invite.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> update_invite(invite, %{field: new_value}, %User{id: 123, role: :admin})
|
||||||
|
{:ok, %Invite{}}
|
||||||
|
|
||||||
|
iex> update_invite(invite, %{field: bad_value}, %User{id: 123, role: :admin})
|
||||||
|
{:error, %Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec update_invite(Invite.t(), attrs :: map(), User.t()) ::
|
||||||
|
{:ok, Invite.t()} | {:error, Invite.changeset()}
|
||||||
|
def update_invite(invite, attrs, %User{role: :admin}) do
|
||||||
|
invite |> Invite.update_changeset(attrs) |> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Deletes a invite.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> delete_invite(invite, %User{id: 123, role: :admin})
|
||||||
|
{:ok, %Invite{}}
|
||||||
|
|
||||||
|
iex> delete_invite(invite, %User{id: 123, role: :admin})
|
||||||
|
{:error, %Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec delete_invite(Invite.t(), User.t()) ::
|
||||||
|
{:ok, Invite.t()} | {:error, Invite.changeset()}
|
||||||
|
def delete_invite(invite, %User{role: :admin}) do
|
||||||
|
invite |> Repo.delete()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Deletes a invite.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> delete_invite(invite, %User{id: 123, role: :admin})
|
||||||
|
%Invite{}
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec delete_invite!(Invite.t(), User.t()) :: Invite.t()
|
||||||
|
def delete_invite!(invite, %User{role: :admin}) do
|
||||||
|
invite |> Repo.delete!()
|
||||||
|
end
|
||||||
|
end
|
@ -6,8 +6,8 @@ defmodule Memex.Accounts.User do
|
|||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
import MemexWeb.Gettext
|
import MemexWeb.Gettext
|
||||||
alias Ecto.{Changeset, UUID}
|
alias Ecto.{Association, Changeset, UUID}
|
||||||
alias Memex.Invites.Invite
|
alias Memex.Accounts.{Invite, User}
|
||||||
|
|
||||||
@derive {Jason.Encoder,
|
@derive {Jason.Encoder,
|
||||||
only: [
|
only: [
|
||||||
@ -30,27 +30,31 @@ defmodule Memex.Accounts.User do
|
|||||||
field :role, Ecto.Enum, values: [:admin, :user], default: :user
|
field :role, Ecto.Enum, values: [:admin, :user], default: :user
|
||||||
field :locale, :string
|
field :locale, :string
|
||||||
|
|
||||||
has_many :invites, Invite, on_delete: :delete_all
|
has_many :created_invites, Invite, foreign_key: :created_by_id
|
||||||
|
|
||||||
|
belongs_to :invite, Invite
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
@type t :: %__MODULE__{
|
@type t :: %User{
|
||||||
id: id(),
|
id: id(),
|
||||||
email: String.t(),
|
email: String.t(),
|
||||||
password: String.t(),
|
password: String.t(),
|
||||||
hashed_password: String.t(),
|
hashed_password: String.t(),
|
||||||
confirmed_at: NaiveDateTime.t(),
|
confirmed_at: NaiveDateTime.t(),
|
||||||
role: role(),
|
role: role(),
|
||||||
invites: [Invite.t()],
|
|
||||||
locale: String.t() | nil,
|
locale: String.t() | nil,
|
||||||
|
created_invites: [Invite.t()] | Association.NotLoaded.t(),
|
||||||
|
invite: Invite.t() | nil | Association.NotLoaded.t(),
|
||||||
|
invite_id: Invite.id() | nil,
|
||||||
inserted_at: NaiveDateTime.t(),
|
inserted_at: NaiveDateTime.t(),
|
||||||
updated_at: NaiveDateTime.t()
|
updated_at: NaiveDateTime.t()
|
||||||
}
|
}
|
||||||
@type new_user :: %__MODULE__{}
|
@type new_user :: %User{}
|
||||||
@type id :: UUID.t()
|
@type id :: UUID.t()
|
||||||
@type changeset :: Changeset.t(t() | new_user())
|
@type changeset :: Changeset.t(t() | new_user())
|
||||||
@type role :: :user | :admin
|
@type role :: :admin | :user
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
A user changeset for registration.
|
A user changeset for registration.
|
||||||
@ -69,18 +73,18 @@ defmodule Memex.Accounts.User do
|
|||||||
validations on a LiveView form), this option can be set to `false`.
|
validations on a LiveView form), this option can be set to `false`.
|
||||||
Defaults to `true`.
|
Defaults to `true`.
|
||||||
"""
|
"""
|
||||||
@spec registration_changeset(attrs :: map()) :: changeset()
|
@spec registration_changeset(attrs :: map(), Invite.t() | nil) :: changeset()
|
||||||
@spec registration_changeset(attrs :: map(), opts :: keyword()) :: changeset()
|
@spec registration_changeset(attrs :: map(), Invite.t() | nil, opts :: keyword()) :: changeset()
|
||||||
def registration_changeset(attrs, opts \\ []) do
|
def registration_changeset(attrs, invite, opts \\ []) do
|
||||||
%__MODULE__{}
|
%User{}
|
||||||
|> cast(attrs, [:email, :password, :locale])
|
|> cast(attrs, [:email, :password, :locale])
|
||||||
|
|> put_change(:invite_id, if(invite, do: invite.id))
|
||||||
|> validate_email()
|
|> validate_email()
|
||||||
|> validate_password(opts)
|
|> validate_password(opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
A user changeset for role.
|
A user changeset for role.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@spec role_changeset(t() | new_user() | changeset(), role()) :: changeset()
|
@spec role_changeset(t() | new_user() | changeset(), role()) :: changeset()
|
||||||
def role_changeset(user, role) do
|
def role_changeset(user, role) do
|
||||||
@ -99,7 +103,8 @@ defmodule Memex.Accounts.User do
|
|||||||
|> unique_constraint(:email)
|
|> unique_constraint(:email)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec validate_password(changeset(), opts :: keyword()) :: changeset()
|
@spec validate_password(changeset(), opts :: keyword()) ::
|
||||||
|
changeset()
|
||||||
defp validate_password(changeset, opts) do
|
defp validate_password(changeset, opts) do
|
||||||
changeset
|
changeset
|
||||||
|> validate_required([:password])
|
|> validate_required([:password])
|
||||||
@ -177,12 +182,12 @@ defmodule Memex.Accounts.User do
|
|||||||
`Bcrypt.no_user_verify/0` to avoid timing attacks.
|
`Bcrypt.no_user_verify/0` to avoid timing attacks.
|
||||||
"""
|
"""
|
||||||
@spec valid_password?(t(), String.t()) :: boolean()
|
@spec valid_password?(t(), String.t()) :: boolean()
|
||||||
def valid_password?(%__MODULE__{hashed_password: hashed_password}, password)
|
def valid_password?(%User{hashed_password: hashed_password}, password)
|
||||||
when is_binary(hashed_password) and byte_size(password) > 0 do
|
when is_binary(hashed_password) and byte_size(password) > 0 do
|
||||||
Bcrypt.verify_pass(password, hashed_password)
|
Bcrypt.verify_pass(password, hashed_password)
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_password?(_, _) do
|
def valid_password?(_invalid_user, _invalid_password) do
|
||||||
Bcrypt.no_user_verify()
|
Bcrypt.no_user_verify()
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
@ -5,6 +5,8 @@ defmodule Memex.Accounts.UserToken do
|
|||||||
|
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
alias Ecto.{Association, UUID}
|
||||||
|
alias Memex.Accounts.User
|
||||||
|
|
||||||
@hash_algorithm :sha256
|
@hash_algorithm :sha256
|
||||||
@rand_size 32
|
@rand_size 32
|
||||||
@ -22,11 +24,25 @@ defmodule Memex.Accounts.UserToken do
|
|||||||
field :token, :binary
|
field :token, :binary
|
||||||
field :context, :string
|
field :context, :string
|
||||||
field :sent_to, :string
|
field :sent_to, :string
|
||||||
belongs_to :user, Memex.Accounts.User
|
|
||||||
|
belongs_to :user, User
|
||||||
|
|
||||||
timestamps(updated_at: false)
|
timestamps(updated_at: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
id: id(),
|
||||||
|
token: token(),
|
||||||
|
context: String.t(),
|
||||||
|
sent_to: String.t(),
|
||||||
|
user: User.t() | Association.NotLoaded.t(),
|
||||||
|
user_id: User.id() | nil,
|
||||||
|
inserted_at: NaiveDateTime.t()
|
||||||
|
}
|
||||||
|
@type new_user_token :: %__MODULE__{}
|
||||||
|
@type id :: UUID.t()
|
||||||
|
@type token :: binary()
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Generates a token that will be stored in a signed place,
|
Generates a token that will be stored in a signed place,
|
||||||
such as session or cookie. As they are signed, those
|
such as session or cookie. As they are signed, those
|
||||||
@ -34,7 +50,7 @@ defmodule Memex.Accounts.UserToken do
|
|||||||
"""
|
"""
|
||||||
def build_session_token(user) do
|
def build_session_token(user) do
|
||||||
token = :crypto.strong_rand_bytes(@rand_size)
|
token = :crypto.strong_rand_bytes(@rand_size)
|
||||||
{token, %Memex.Accounts.UserToken{token: token, context: "session", user_id: user.id}}
|
{token, %__MODULE__{token: token, context: "session", user_id: user.id}}
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
@ -69,7 +85,7 @@ defmodule Memex.Accounts.UserToken do
|
|||||||
hashed_token = :crypto.hash(@hash_algorithm, token)
|
hashed_token = :crypto.hash(@hash_algorithm, token)
|
||||||
|
|
||||||
{Base.url_encode64(token, padding: false),
|
{Base.url_encode64(token, padding: false),
|
||||||
%Memex.Accounts.UserToken{
|
%__MODULE__{
|
||||||
token: hashed_token,
|
token: hashed_token,
|
||||||
context: context,
|
context: context,
|
||||||
sent_to: sent_to,
|
sent_to: sent_to,
|
||||||
@ -129,17 +145,17 @@ defmodule Memex.Accounts.UserToken do
|
|||||||
Returns the given token with the given context.
|
Returns the given token with the given context.
|
||||||
"""
|
"""
|
||||||
def token_and_context_query(token, context) do
|
def token_and_context_query(token, context) do
|
||||||
from Memex.Accounts.UserToken, where: [token: ^token, context: ^context]
|
from __MODULE__, where: [token: ^token, context: ^context]
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Gets all tokens for the given user for the given contexts.
|
Gets all tokens for the given user for the given contexts.
|
||||||
"""
|
"""
|
||||||
def user_and_contexts_query(user, :all) do
|
def user_and_contexts_query(user, :all) do
|
||||||
from t in Memex.Accounts.UserToken, where: t.user_id == ^user.id
|
from t in __MODULE__, where: t.user_id == ^user.id
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_and_contexts_query(user, [_ | _] = contexts) do
|
def user_and_contexts_query(user, [_ | _] = contexts) do
|
||||||
from t in Memex.Accounts.UserToken, where: t.user_id == ^user.id and t.context in ^contexts
|
from t in __MODULE__, where: t.user_id == ^user.id and t.context in ^contexts
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,173 +0,0 @@
|
|||||||
defmodule Memex.Invites do
|
|
||||||
@moduledoc """
|
|
||||||
The Invites context.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import Ecto.Query, warn: false
|
|
||||||
alias Ecto.Changeset
|
|
||||||
alias Memex.{Accounts.User, Invites.Invite, Repo}
|
|
||||||
|
|
||||||
@invite_token_length 20
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns the list of invites.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> list_invites(%User{id: 123, role: :admin})
|
|
||||||
[%Invite{}, ...]
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec list_invites(User.t()) :: [Invite.t()]
|
|
||||||
def list_invites(%User{role: :admin}) do
|
|
||||||
Repo.all(from i in Invite, order_by: i.name)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Gets a single invite.
|
|
||||||
|
|
||||||
Raises `Ecto.NoResultsError` if the Invite does not exist.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> get_invite!(123, %User{id: 123, role: :admin})
|
|
||||||
%Invite{}
|
|
||||||
|
|
||||||
iex> get_invite!(456, %User{id: 123, role: :admin})
|
|
||||||
** (Ecto.NoResultsError)
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec get_invite!(Invite.id(), User.t()) :: Invite.t()
|
|
||||||
def get_invite!(id, %User{role: :admin}) do
|
|
||||||
Repo.get!(Invite, id)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns a valid invite or nil based on the attempted token
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> get_invite_by_token("valid_token")
|
|
||||||
%Invite{}
|
|
||||||
|
|
||||||
iex> get_invite_by_token("invalid_token")
|
|
||||||
nil
|
|
||||||
"""
|
|
||||||
@spec get_invite_by_token(token :: String.t() | nil) :: Invite.t() | nil
|
|
||||||
def get_invite_by_token(nil), do: nil
|
|
||||||
def get_invite_by_token(""), do: nil
|
|
||||||
|
|
||||||
def get_invite_by_token(token) do
|
|
||||||
Repo.one(
|
|
||||||
from(i in Invite,
|
|
||||||
where: i.token == ^token and i.disabled_at |> is_nil()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Uses invite by decrementing uses_left, or marks invite invalid if it's been
|
|
||||||
completely used.
|
|
||||||
"""
|
|
||||||
@spec use_invite!(Invite.t()) :: Invite.t()
|
|
||||||
def use_invite!(%Invite{uses_left: nil} = invite), do: invite
|
|
||||||
|
|
||||||
def use_invite!(%Invite{uses_left: uses_left} = invite) do
|
|
||||||
new_uses_left = uses_left - 1
|
|
||||||
|
|
||||||
attrs =
|
|
||||||
if new_uses_left <= 0 do
|
|
||||||
now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
|
|
||||||
%{"uses_left" => 0, "disabled_at" => now}
|
|
||||||
else
|
|
||||||
%{"uses_left" => new_uses_left}
|
|
||||||
end
|
|
||||||
|
|
||||||
invite |> Invite.update_changeset(attrs) |> Repo.update!()
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Creates a invite.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> create_invite(%User{id: 123, role: :admin}, %{field: value})
|
|
||||||
{:ok, %Invite{}}
|
|
||||||
|
|
||||||
iex> create_invite(%User{id: 123, role: :admin}, %{field: bad_value})
|
|
||||||
{:error, %Changeset{}}
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec create_invite(User.t(), attrs :: map()) ::
|
|
||||||
{:ok, Invite.t()} | {:error, Changeset.t(Invite.new_invite())}
|
|
||||||
def create_invite(%User{id: user_id, role: :admin}, attrs) do
|
|
||||||
token =
|
|
||||||
:crypto.strong_rand_bytes(@invite_token_length)
|
|
||||||
|> Base.url_encode64()
|
|
||||||
|> binary_part(0, @invite_token_length)
|
|
||||||
|
|
||||||
attrs = attrs |> Map.merge(%{"user_id" => user_id, "token" => token})
|
|
||||||
|
|
||||||
%Invite{} |> Invite.create_changeset(attrs) |> Repo.insert()
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Updates a invite.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> update_invite(invite, %{field: new_value}, %User{id: 123, role: :admin})
|
|
||||||
{:ok, %Invite{}}
|
|
||||||
|
|
||||||
iex> update_invite(invite, %{field: bad_value}, %User{id: 123, role: :admin})
|
|
||||||
{:error, %Changeset{}}
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec update_invite(Invite.t(), attrs :: map(), User.t()) ::
|
|
||||||
{:ok, Invite.t()} | {:error, Changeset.t(Invite.t())}
|
|
||||||
def update_invite(invite, attrs, %User{role: :admin}),
|
|
||||||
do: invite |> Invite.update_changeset(attrs) |> Repo.update()
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Deletes a invite.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> delete_invite(invite, %User{id: 123, role: :admin})
|
|
||||||
{:ok, %Invite{}}
|
|
||||||
|
|
||||||
iex> delete_invite(invite, %User{id: 123, role: :admin})
|
|
||||||
{:error, %Changeset{}}
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec delete_invite(Invite.t(), User.t()) ::
|
|
||||||
{:ok, Invite.t()} | {:error, Changeset.t(Invite.t())}
|
|
||||||
def delete_invite(invite, %User{role: :admin}), do: invite |> Repo.delete()
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Deletes a invite.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> delete_invite(invite, %User{id: 123, role: :admin})
|
|
||||||
%Invite{}
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec delete_invite!(Invite.t(), User.t()) :: Invite.t()
|
|
||||||
def delete_invite!(invite, %User{role: :admin}), do: invite |> Repo.delete!()
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns an `%Changeset{}` for tracking invite changes.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> change_invite(invite)
|
|
||||||
%Changeset{data: %Invite{}}
|
|
||||||
|
|
||||||
"""
|
|
||||||
@spec change_invite(Invite.t() | Invite.new_invite()) ::
|
|
||||||
Changeset.t(Invite.t() | Invite.new_invite())
|
|
||||||
@spec change_invite(Invite.t() | Invite.new_invite(), attrs :: map()) ::
|
|
||||||
Changeset.t(Invite.t() | Invite.new_invite())
|
|
||||||
def change_invite(invite, attrs \\ %{}), do: invite |> Invite.update_changeset(attrs)
|
|
||||||
end
|
|
@ -4,9 +4,19 @@ defmodule MemexWeb.Components.InviteCard do
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
use MemexWeb, :component
|
use MemexWeb, :component
|
||||||
|
alias Memex.Accounts.{Invite, Invites, User}
|
||||||
|
alias MemexWeb.Endpoint
|
||||||
|
|
||||||
def invite_card(assigns) do
|
attr :invite, Invite, required: true
|
||||||
assigns = assigns |> assign_new(:code_actions, fn -> [] end)
|
attr :current_user, User, required: true
|
||||||
|
slot(:inner_block)
|
||||||
|
slot(:code_actions)
|
||||||
|
|
||||||
|
def invite_card(%{invite: invite, current_user: current_user} = assigns) do
|
||||||
|
assigns =
|
||||||
|
assigns
|
||||||
|
|> assign(:use_count, Invites.get_use_count(invite, current_user))
|
||||||
|
|> assign_new(:code_actions, fn -> [] end)
|
||||||
|
|
||||||
~H"""
|
~H"""
|
||||||
<div class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center space-y-4
|
<div class="mx-4 my-2 px-8 py-4 flex flex-col justify-center items-center space-y-4
|
||||||
@ -20,8 +30,8 @@ defmodule MemexWeb.Components.InviteCard do
|
|||||||
<h2 class="title text-md">
|
<h2 class="title text-md">
|
||||||
<%= if @invite.uses_left do %>
|
<%= if @invite.uses_left do %>
|
||||||
<%= gettext(
|
<%= gettext(
|
||||||
"uses left: %{uses_left}",
|
"uses left: %{uses_left_count}",
|
||||||
uses_left: @invite.uses_left
|
uses_left_count: @invite.uses_left
|
||||||
) %>
|
) %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= gettext("uses left: unlimited") %>
|
<%= gettext("uses left: unlimited") %>
|
||||||
@ -38,6 +48,10 @@ defmodule MemexWeb.Components.InviteCard do
|
|||||||
filename={@invite.name}
|
filename={@invite.name}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<h2 :if={@use_count != 0} class="title text-md">
|
||||||
|
<%= gettext("uses: %{uses_count}", uses_count: @use_count) %>
|
||||||
|
</h2>
|
||||||
|
|
||||||
<div class="flex flex-row flex-wrap justify-center items-center">
|
<div class="flex flex-row flex-wrap justify-center items-center">
|
||||||
<code
|
<code
|
||||||
id={"code-#{@invite.id}"}
|
id={"code-#{@invite.id}"}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
defmodule MemexWeb.UserRegistrationController do
|
defmodule MemexWeb.UserRegistrationController do
|
||||||
use MemexWeb, :controller
|
use MemexWeb, :controller
|
||||||
import MemexWeb.Gettext
|
import MemexWeb.Gettext
|
||||||
alias Memex.{Accounts, Invites}
|
alias Memex.{Accounts, Accounts.Invites}
|
||||||
alias MemexWeb.HomeLive
|
alias MemexWeb.{Endpoint, HomeLive}
|
||||||
|
|
||||||
def new(conn, %{"invite" => invite_token}) do
|
def new(conn, %{"invite" => invite_token}) do
|
||||||
invite = Invites.get_invite_by_token(invite_token)
|
if Invites.valid_invite_token?(invite_token) do
|
||||||
|
conn |> render_new(invite_token)
|
||||||
if invite do
|
|
||||||
conn |> render_new(invite)
|
|
||||||
else
|
else
|
||||||
conn
|
conn
|
||||||
|> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired"))
|
|> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired"))
|
||||||
@ -27,19 +25,17 @@ defmodule MemexWeb.UserRegistrationController do
|
|||||||
end
|
end
|
||||||
|
|
||||||
# renders new user registration page
|
# renders new user registration page
|
||||||
defp render_new(conn, invite \\ nil) do
|
defp render_new(conn, invite_token \\ nil) do
|
||||||
render(conn, "new.html",
|
render(conn, "new.html",
|
||||||
changeset: Accounts.change_user_registration(),
|
changeset: Accounts.change_user_registration(),
|
||||||
invite: invite,
|
invite_token: invite_token,
|
||||||
page_title: gettext("register")
|
page_title: gettext("register")
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(conn, %{"user" => %{"invite_token" => invite_token}} = attrs) do
|
def create(conn, %{"user" => %{"invite_token" => invite_token}} = attrs) do
|
||||||
invite = Invites.get_invite_by_token(invite_token)
|
if Invites.valid_invite_token?(invite_token) do
|
||||||
|
conn |> create_user(attrs, invite_token)
|
||||||
if invite do
|
|
||||||
conn |> create_user(attrs, invite)
|
|
||||||
else
|
else
|
||||||
conn
|
conn
|
||||||
|> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired"))
|
|> put_flash(:error, dgettext("errors", "Sorry, this invite was not found or expired"))
|
||||||
@ -57,24 +53,25 @@ defmodule MemexWeb.UserRegistrationController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp create_user(conn, %{"user" => user_params}, invite \\ nil) do
|
defp create_user(conn, %{"user" => user_params}, invite_token \\ nil) do
|
||||||
case Accounts.register_user(user_params) do
|
case Accounts.register_user(user_params, invite_token) do
|
||||||
{:ok, user} ->
|
{:ok, user} ->
|
||||||
unless invite |> is_nil() do
|
|
||||||
invite |> Invites.use_invite!()
|
|
||||||
end
|
|
||||||
|
|
||||||
Accounts.deliver_user_confirmation_instructions(
|
Accounts.deliver_user_confirmation_instructions(
|
||||||
user,
|
user,
|
||||||
&Routes.user_confirmation_url(conn, :confirm, &1)
|
&Routes.user_confirmation_url(conn, :confirm, &1)
|
||||||
)
|
)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, dgettext("prompts", "Please check your email to verify your account"))
|
|> put_flash(:info, dgettext("prompts", "please check your email to verify your account"))
|
||||||
|> redirect(to: Routes.user_session_path(Endpoint, :new))
|
|> redirect(to: Routes.user_session_path(Endpoint, :new))
|
||||||
|
|
||||||
|
{:error, :invalid_token} ->
|
||||||
|
conn
|
||||||
|
|> put_flash(:error, dgettext("errors", "sorry, this invite was not found or expired"))
|
||||||
|
|> redirect(to: Routes.live_path(Endpoint, HomeLive))
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
{:error, %Ecto.Changeset{} = changeset} ->
|
||||||
conn |> render("new.html", changeset: changeset, invite: invite)
|
conn |> render("new.html", changeset: changeset, invite_token: invite_token)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -7,7 +7,7 @@ defmodule MemexWeb.UserSettingsController do
|
|||||||
plug :assign_email_and_password_changesets
|
plug :assign_email_and_password_changesets
|
||||||
|
|
||||||
def edit(conn, _params) do
|
def edit(conn, _params) do
|
||||||
render(conn, "edit.html", page_title: gettext("Settings"))
|
render(conn, "edit.html", page_title: gettext("settings"))
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(%{assigns: %{current_user: user}} = conn, %{
|
def update(%{assigns: %{current_user: user}} = conn, %{
|
||||||
@ -28,7 +28,7 @@ defmodule MemexWeb.UserSettingsController do
|
|||||||
:info,
|
:info,
|
||||||
dgettext(
|
dgettext(
|
||||||
"prompts",
|
"prompts",
|
||||||
"A link to confirm your email change has been sent to the new address."
|
"a link to confirm your email change has been sent to the new address."
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
||||||
@ -46,7 +46,7 @@ defmodule MemexWeb.UserSettingsController do
|
|||||||
case Accounts.update_user_password(user, password, user_params) do
|
case Accounts.update_user_password(user, password, user_params) do
|
||||||
{:ok, user} ->
|
{:ok, user} ->
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, dgettext("prompts", "Password updated successfully."))
|
|> put_flash(:info, dgettext("prompts", "password updated successfully."))
|
||||||
|> put_session(:user_return_to, Routes.user_settings_path(conn, :edit))
|
|> put_session(:user_return_to, Routes.user_settings_path(conn, :edit))
|
||||||
|> UserAuth.log_in_user(user)
|
|> UserAuth.log_in_user(user)
|
||||||
|
|
||||||
@ -74,14 +74,14 @@ defmodule MemexWeb.UserSettingsController do
|
|||||||
case Accounts.update_user_email(user, token) do
|
case Accounts.update_user_email(user, token) do
|
||||||
:ok ->
|
:ok ->
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, dgettext("prompts", "Email changed successfully."))
|
|> put_flash(:info, dgettext("prompts", "email changed successfully."))
|
||||||
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
||||||
|
|
||||||
:error ->
|
:error ->
|
||||||
conn
|
conn
|
||||||
|> put_flash(
|
|> put_flash(
|
||||||
:error,
|
:error,
|
||||||
dgettext("errors", "Email change link is invalid or it has expired.")
|
dgettext("errors", "email change link is invalid or it has expired.")
|
||||||
)
|
)
|
||||||
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
||||||
end
|
end
|
||||||
@ -92,11 +92,11 @@ defmodule MemexWeb.UserSettingsController do
|
|||||||
current_user |> Accounts.delete_user!(current_user)
|
current_user |> Accounts.delete_user!(current_user)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_flash(:error, dgettext("prompts", "Your account has been deleted"))
|
|> put_flash(:error, dgettext("prompts", "your account has been deleted"))
|
||||||
|> redirect(to: Routes.live_path(conn, HomeLive))
|
|> redirect(to: Routes.live_path(conn, HomeLive))
|
||||||
else
|
else
|
||||||
conn
|
conn
|
||||||
|> put_flash(:error, dgettext("errors", "Unable to delete user"))
|
|> put_flash(:error, dgettext("errors", "unable to delete user"))
|
||||||
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
|> redirect(to: Routes.user_settings_path(conn, :edit))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<.modal
|
<.modal
|
||||||
if={@live_action == :edit}
|
:if={@live_action == :edit}
|
||||||
return_to={Routes.context_show_path(@socket, :show, @context.slug)}
|
return_to={Routes.context_show_path(@socket, :show, @context.slug)}
|
||||||
>
|
>
|
||||||
<.live_component
|
<.live_component
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
defmodule MemexWeb.InviteLive.FormComponent do
|
defmodule MemexWeb.InviteLive.FormComponent do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Livecomponent that can update or create an Memex.Invites.Invite
|
Livecomponent that can update or create an Memex.Accounts.Invite
|
||||||
"""
|
"""
|
||||||
|
|
||||||
use MemexWeb, :live_component
|
use MemexWeb, :live_component
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
alias Memex.{Accounts.User, Invites, Invites.Invite}
|
alias Memex.Accounts.{Invite, Invites, User}
|
||||||
alias Phoenix.LiveView.Socket
|
alias Phoenix.LiveView.Socket
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@ -13,23 +13,44 @@ defmodule MemexWeb.InviteLive.FormComponent do
|
|||||||
%{:invite => Invite.t(), :current_user => User.t(), optional(any) => any},
|
%{:invite => Invite.t(), :current_user => User.t(), optional(any) => any},
|
||||||
Socket.t()
|
Socket.t()
|
||||||
) :: {:ok, Socket.t()}
|
) :: {:ok, Socket.t()}
|
||||||
def update(%{invite: invite} = assigns, socket) do
|
def update(%{invite: _invite} = assigns, socket) do
|
||||||
{:ok, socket |> assign(assigns) |> assign(:changeset, Invites.change_invite(invite))}
|
{:ok, socket |> assign(assigns) |> assign_changeset(%{})}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event(
|
def handle_event("validate", %{"invite" => invite_params}, socket) do
|
||||||
"validate",
|
{:noreply, socket |> assign_changeset(invite_params)}
|
||||||
%{"invite" => invite_params},
|
|
||||||
%{assigns: %{invite: invite}} = socket
|
|
||||||
) do
|
|
||||||
{:noreply, socket |> assign(:changeset, invite |> Invites.change_invite(invite_params))}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("save", %{"invite" => invite_params}, %{assigns: %{action: action}} = socket) do
|
def handle_event("save", %{"invite" => invite_params}, %{assigns: %{action: action}} = socket) do
|
||||||
save_invite(socket, action, invite_params)
|
save_invite(socket, action, invite_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp assign_changeset(
|
||||||
|
%{assigns: %{action: action, current_user: user, invite: invite}} = socket,
|
||||||
|
invite_params
|
||||||
|
) do
|
||||||
|
changeset_action =
|
||||||
|
case action do
|
||||||
|
:new -> :insert
|
||||||
|
:edit -> :update
|
||||||
|
end
|
||||||
|
|
||||||
|
changeset =
|
||||||
|
case action do
|
||||||
|
:new -> Invite.create_changeset(user, "example_token", invite_params)
|
||||||
|
:edit -> invite |> Invite.update_changeset(invite_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
changeset =
|
||||||
|
case changeset |> Changeset.apply_action(changeset_action) do
|
||||||
|
{:ok, _data} -> changeset
|
||||||
|
{:error, changeset} -> changeset
|
||||||
|
end
|
||||||
|
|
||||||
|
socket |> assign(:changeset, changeset)
|
||||||
|
end
|
||||||
|
|
||||||
defp save_invite(
|
defp save_invite(
|
||||||
%{assigns: %{current_user: current_user, invite: invite, return_to: return_to}} = socket,
|
%{assigns: %{current_user: current_user, invite: invite, return_to: return_to}} = socket,
|
||||||
:edit,
|
:edit,
|
||||||
@ -38,9 +59,7 @@ defmodule MemexWeb.InviteLive.FormComponent do
|
|||||||
socket =
|
socket =
|
||||||
case invite |> Invites.update_invite(invite_params, current_user) do
|
case invite |> Invites.update_invite(invite_params, current_user) do
|
||||||
{:ok, %{name: invite_name}} ->
|
{:ok, %{name: invite_name}} ->
|
||||||
prompt =
|
prompt = dgettext("prompts", "%{name} updated successfully", name: invite_name)
|
||||||
dgettext("prompts", "%{invite_name} updated successfully", invite_name: invite_name)
|
|
||||||
|
|
||||||
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
|
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
|
||||||
|
|
||||||
{:error, %Changeset{} = changeset} ->
|
{:error, %Changeset{} = changeset} ->
|
||||||
@ -58,9 +77,7 @@ defmodule MemexWeb.InviteLive.FormComponent do
|
|||||||
socket =
|
socket =
|
||||||
case current_user |> Invites.create_invite(invite_params) do
|
case current_user |> Invites.create_invite(invite_params) do
|
||||||
{:ok, %{name: invite_name}} ->
|
{:ok, %{name: invite_name}} ->
|
||||||
prompt =
|
prompt = dgettext("prompts", "%{name} created successfully", name: invite_name)
|
||||||
dgettext("prompts", "%{invite_name} created successfully", invite_name: invite_name)
|
|
||||||
|
|
||||||
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
|
socket |> put_flash(:info, prompt) |> push_navigate(to: return_to)
|
||||||
|
|
||||||
{:error, %Changeset{} = changeset} ->
|
{:error, %Changeset{} = changeset} ->
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
defmodule MemexWeb.InviteLive.Index do
|
defmodule MemexWeb.InviteLive.Index do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Liveview to show a Memex.Invites.Invite index
|
Liveview to show a Memex.Accounts.Invite index
|
||||||
"""
|
"""
|
||||||
|
|
||||||
use MemexWeb, :live_view
|
use MemexWeb, :live_view
|
||||||
import MemexWeb.Components.{InviteCard, UserCard}
|
import MemexWeb.Components.{InviteCard, UserCard}
|
||||||
alias Memex.{Accounts, Invites, Invites.Invite}
|
alias Memex.Accounts
|
||||||
alias MemexWeb.HomeLive
|
alias Memex.Accounts.{Invite, Invites}
|
||||||
|
alias MemexWeb.{Endpoint, HomeLive}
|
||||||
alias Phoenix.LiveView.JS
|
alias Phoenix.LiveView.JS
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
@ -15,9 +16,9 @@ defmodule MemexWeb.InviteLive.Index do
|
|||||||
if current_user |> Map.get(:role) == :admin do
|
if current_user |> Map.get(:role) == :admin do
|
||||||
socket |> display_invites()
|
socket |> display_invites()
|
||||||
else
|
else
|
||||||
prompt = dgettext("errors", "You are not authorized to view this page")
|
prompt = dgettext("errors", "you are not authorized to view this page")
|
||||||
return_to = Routes.live_path(Endpoint, HomeLive)
|
return_to = Routes.live_path(Endpoint, HomeLive)
|
||||||
socket |> put_flash(:error, prompt) |> push_navigate(to: return_to)
|
socket |> put_flash(:error, prompt) |> push_redirect(to: return_to)
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok, socket}
|
{:ok, socket}
|
||||||
@ -61,7 +62,7 @@ defmodule MemexWeb.InviteLive.Index do
|
|||||||
) do
|
) do
|
||||||
socket =
|
socket =
|
||||||
Invites.get_invite!(id, current_user)
|
Invites.get_invite!(id, current_user)
|
||||||
|> Invites.update_invite(%{"uses_left" => nil}, current_user)
|
|> Invites.update_invite(%{uses_left: nil}, current_user)
|
||||||
|> case do
|
|> case do
|
||||||
{:ok, %{name: invite_name}} ->
|
{:ok, %{name: invite_name}} ->
|
||||||
prompt =
|
prompt =
|
||||||
@ -83,7 +84,7 @@ defmodule MemexWeb.InviteLive.Index do
|
|||||||
) do
|
) do
|
||||||
socket =
|
socket =
|
||||||
Invites.get_invite!(id, current_user)
|
Invites.get_invite!(id, current_user)
|
||||||
|> Invites.update_invite(%{"uses_left" => nil, "disabled_at" => nil}, current_user)
|
|> Invites.update_invite(%{uses_left: nil, disabled_at: nil}, current_user)
|
||||||
|> case do
|
|> case do
|
||||||
{:ok, %{name: invite_name}} ->
|
{:ok, %{name: invite_name}} ->
|
||||||
prompt =
|
prompt =
|
||||||
@ -107,7 +108,7 @@ defmodule MemexWeb.InviteLive.Index do
|
|||||||
|
|
||||||
socket =
|
socket =
|
||||||
Invites.get_invite!(id, current_user)
|
Invites.get_invite!(id, current_user)
|
||||||
|> Invites.update_invite(%{"uses_left" => 0, "disabled_at" => now}, current_user)
|
|> Invites.update_invite(%{uses_left: 0, disabled_at: now}, current_user)
|
||||||
|> case do
|
|> case do
|
||||||
{:ok, %{name: invite_name}} ->
|
{:ok, %{name: invite_name}} ->
|
||||||
prompt =
|
prompt =
|
||||||
@ -124,7 +125,7 @@ defmodule MemexWeb.InviteLive.Index do
|
|||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("copy_to_clipboard", _, socket) do
|
def handle_event("copy_to_clipboard", _, socket) do
|
||||||
prompt = dgettext("prompts", "Copied to clipboard")
|
prompt = dgettext("prompts", "copied to clipboard")
|
||||||
{:noreply, socket |> put_flash(:info, prompt)}
|
{:noreply, socket |> put_flash(:info, prompt)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="w-full flex flex-row flex-wrap justify-center items-center">
|
<div class="w-full flex flex-row flex-wrap justify-center items-center">
|
||||||
<.invite_card :for={invite <- @invites} invite={invite}>
|
<.invite_card :for={invite <- @invites} invite={invite} current_user={@current_user}>
|
||||||
<:code_actions>
|
<:code_actions>
|
||||||
<form phx-submit="copy_to_clipboard">
|
<form phx-submit="copy_to_clipboard">
|
||||||
<button
|
<button
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
defmodule MemexWeb.LiveHelpers do
|
defmodule MemexWeb.LiveHelpers do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Contains resuable methods for all liveviews
|
Contains common helper functions for liveviews
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import Phoenix.Component
|
import Phoenix.Component
|
||||||
@ -31,7 +31,7 @@ defmodule MemexWeb.LiveHelpers do
|
|||||||
patch={@return_to}
|
patch={@return_to}
|
||||||
id="modal-bg"
|
id="modal-bg"
|
||||||
class="fade-in fixed z-10 left-0 top-0
|
class="fade-in fixed z-10 left-0 top-0
|
||||||
w-screen h-screen overflow-hidden
|
w-full h-full overflow-hidden
|
||||||
p-8 flex flex-col justify-center items-center cursor-auto"
|
p-8 flex flex-col justify-center items-center cursor-auto"
|
||||||
style="background-color: rgba(0,0,0,0.4);"
|
style="background-color: rgba(0,0,0,0.4);"
|
||||||
phx-remove={hide_modal()}
|
phx-remove={hide_modal()}
|
||||||
@ -63,7 +63,7 @@ defmodule MemexWeb.LiveHelpers do
|
|||||||
<i class="fa-fw fa-lg fas fa-times"></i>
|
<i class="fa-fw fa-lg fas fa-times"></i>
|
||||||
</.link>
|
</.link>
|
||||||
|
|
||||||
<div class="overflow-x-hidden overflow-y-visible p-8 flex flex-col space-y-4 justify-start items-stretch">
|
<div class="overflow-x-hidden overflow-y-auto w-full p-8 flex flex-col space-y-4 justify-start items-stretch">
|
||||||
<%= render_slot(@inner_block) %>
|
<%= render_slot(@inner_block) %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -71,10 +71,57 @@ defmodule MemexWeb.LiveHelpers do
|
|||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
def hide_modal(js \\ %JS{}) do
|
defp hide_modal(js \\ %JS{}) do
|
||||||
js
|
js
|
||||||
|> JS.hide(to: "#modal", transition: "fade-out")
|
|> JS.hide(to: "#modal", transition: "fade-out")
|
||||||
|> JS.hide(to: "#modal-bg", transition: "fade-out")
|
|> JS.hide(to: "#modal-bg", transition: "fade-out")
|
||||||
|> JS.hide(to: "#modal-content", transition: "fade-out-scale")
|
|> JS.hide(to: "#modal-content", transition: "fade-out-scale")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
A toggle button element that can be directed to a liveview or a
|
||||||
|
live_component's `handle_event/3`.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
<.toggle_button action="my_liveview_action" value={@some_value}>
|
||||||
|
<span>Toggle me!</span>
|
||||||
|
</.toggle_button>
|
||||||
|
<.toggle_button action="my_live_component_action" target={@myself} value={@some_value}>
|
||||||
|
<span>Whatever you want</span>
|
||||||
|
</.toggle_button>
|
||||||
|
"""
|
||||||
|
def toggle_button(assigns) do
|
||||||
|
assigns = assigns |> assign_new(:id, fn -> assigns.action end)
|
||||||
|
|
||||||
|
~H"""
|
||||||
|
<label for={@id} class="inline-flex relative items-center cursor-pointer">
|
||||||
|
<input
|
||||||
|
id={@id}
|
||||||
|
type="checkbox"
|
||||||
|
value={@value}
|
||||||
|
checked={@value}
|
||||||
|
class="sr-only peer"
|
||||||
|
data-qa={@id}
|
||||||
|
{
|
||||||
|
if assigns |> Map.has_key?(:target),
|
||||||
|
do: %{"phx-click": @action, "phx-value-value": @value, "phx-target": @target},
|
||||||
|
else: %{"phx-click": @action, "phx-value-value": @value}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div class="w-11 h-6 bg-gray-300 rounded-full peer
|
||||||
|
peer-focus:ring-4 peer-focus:ring-teal-300 dark:peer-focus:ring-teal-800
|
||||||
|
peer-checked:bg-gray-600
|
||||||
|
peer-checked:after:translate-x-full peer-checked:after:border-white
|
||||||
|
after:content-[''] after:absolute after:top-1 after:left-[2px] after:bg-white after:border-gray-300
|
||||||
|
after:border after:rounded-full after:h-5 after:w-5
|
||||||
|
after:transition-all after:duration-250 after:ease-in-out
|
||||||
|
transition-colors duration-250 ease-in-out">
|
||||||
|
</div>
|
||||||
|
<span class="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300">
|
||||||
|
<%= render_slot(@inner_block) %>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
"""
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -9,14 +9,12 @@
|
|||||||
action={Routes.user_registration_path(@conn, :create)}
|
action={Routes.user_registration_path(@conn, :create)}
|
||||||
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
|
class="flex flex-col space-y-4 sm:space-y-0 sm:grid sm:grid-cols-3 sm:gap-4 justify-center items-center"
|
||||||
>
|
>
|
||||||
<div :if={@changeset.action && not @changeset.valid?()} class="alert alert-danger col-span-3">
|
<p :if={@changeset.action && not @changeset.valid?()} class="alert alert-danger col-span-3">
|
||||||
<p>
|
<%= dgettext("errors", "oops, something went wrong! please check the errors below.") %>
|
||||||
<%= dgettext("errors", "Oops, something went wrong! Please check the errors below.") %>
|
</p>
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<%= if @invite do %>
|
<%= if @invite_token do %>
|
||||||
<%= hidden_input(f, :invite_token, value: @invite.token) %>
|
<%= hidden_input(f, :invite_token, value: @invite_token) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= label(f, :email, class: "title text-lg text-primary-400") %>
|
<%= label(f, :email, class: "title text-lg text-primary-400") %>
|
||||||
|
@ -89,7 +89,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: lib/memex_web/components/topbar.ex:124
|
#: lib/memex_web/components/topbar.ex:124
|
||||||
#: lib/memex_web/templates/user_confirmation/new.html.heex:31
|
#: lib/memex_web/templates/user_confirmation/new.html.heex:31
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:46
|
#: lib/memex_web/templates/user_registration/new.html.heex:44
|
||||||
#: lib/memex_web/templates/user_reset_password/edit.html.heex:47
|
#: lib/memex_web/templates/user_reset_password/edit.html.heex:47
|
||||||
#: lib/memex_web/templates/user_reset_password/new.html.heex:31
|
#: lib/memex_web/templates/user_reset_password/new.html.heex:31
|
||||||
#: lib/memex_web/templates/user_session/new.html.heex:3
|
#: lib/memex_web/templates/user_session/new.html.heex:3
|
||||||
@ -116,7 +116,7 @@ msgstr ""
|
|||||||
#: lib/memex_web/components/topbar.ex:115
|
#: lib/memex_web/components/topbar.ex:115
|
||||||
#: lib/memex_web/templates/user_confirmation/new.html.heex:28
|
#: lib/memex_web/templates/user_confirmation/new.html.heex:28
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:3
|
#: lib/memex_web/templates/user_registration/new.html.heex:3
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:39
|
#: lib/memex_web/templates/user_registration/new.html.heex:37
|
||||||
#: lib/memex_web/templates/user_reset_password/edit.html.heex:44
|
#: lib/memex_web/templates/user_reset_password/edit.html.heex:44
|
||||||
#: lib/memex_web/templates/user_reset_password/new.html.heex:28
|
#: lib/memex_web/templates/user_reset_password/new.html.heex:28
|
||||||
#: lib/memex_web/templates/user_session/new.html.heex:41
|
#: lib/memex_web/templates/user_session/new.html.heex:41
|
||||||
@ -144,7 +144,7 @@ msgstr ""
|
|||||||
msgid "add step"
|
msgid "add step"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:49
|
#: lib/memex_web/templates/user_registration/new.html.heex:47
|
||||||
#: lib/memex_web/templates/user_reset_password/new.html.heex:3
|
#: lib/memex_web/templates/user_reset_password/new.html.heex:3
|
||||||
#: lib/memex_web/templates/user_session/new.html.heex:44
|
#: lib/memex_web/templates/user_session/new.html.heex:44
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
|
@ -89,7 +89,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: lib/memex_web/components/topbar.ex:124
|
#: lib/memex_web/components/topbar.ex:124
|
||||||
#: lib/memex_web/templates/user_confirmation/new.html.heex:31
|
#: lib/memex_web/templates/user_confirmation/new.html.heex:31
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:46
|
#: lib/memex_web/templates/user_registration/new.html.heex:44
|
||||||
#: lib/memex_web/templates/user_reset_password/edit.html.heex:47
|
#: lib/memex_web/templates/user_reset_password/edit.html.heex:47
|
||||||
#: lib/memex_web/templates/user_reset_password/new.html.heex:31
|
#: lib/memex_web/templates/user_reset_password/new.html.heex:31
|
||||||
#: lib/memex_web/templates/user_session/new.html.heex:3
|
#: lib/memex_web/templates/user_session/new.html.heex:3
|
||||||
@ -116,7 +116,7 @@ msgstr ""
|
|||||||
#: lib/memex_web/components/topbar.ex:115
|
#: lib/memex_web/components/topbar.ex:115
|
||||||
#: lib/memex_web/templates/user_confirmation/new.html.heex:28
|
#: lib/memex_web/templates/user_confirmation/new.html.heex:28
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:3
|
#: lib/memex_web/templates/user_registration/new.html.heex:3
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:39
|
#: lib/memex_web/templates/user_registration/new.html.heex:37
|
||||||
#: lib/memex_web/templates/user_reset_password/edit.html.heex:44
|
#: lib/memex_web/templates/user_reset_password/edit.html.heex:44
|
||||||
#: lib/memex_web/templates/user_reset_password/new.html.heex:28
|
#: lib/memex_web/templates/user_reset_password/new.html.heex:28
|
||||||
#: lib/memex_web/templates/user_session/new.html.heex:41
|
#: lib/memex_web/templates/user_session/new.html.heex:41
|
||||||
@ -144,7 +144,7 @@ msgstr ""
|
|||||||
msgid "add step"
|
msgid "add step"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:49
|
#: lib/memex_web/templates/user_registration/new.html.heex:47
|
||||||
#: lib/memex_web/templates/user_reset_password/new.html.heex:3
|
#: lib/memex_web/templates/user_reset_password/new.html.heex:3
|
||||||
#: lib/memex_web/templates/user_session/new.html.heex:44
|
#: lib/memex_web/templates/user_session/new.html.heex:44
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
@ -51,11 +51,6 @@ msgstr ""
|
|||||||
msgid "Reset your password"
|
msgid "Reset your password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:10
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Settings"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/form_component.html.heex:25
|
#: lib/memex_web/live/invite_live/form_component.html.heex:25
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Uses left"
|
msgid "Uses left"
|
||||||
@ -132,7 +127,7 @@ msgstr ""
|
|||||||
msgid "document your processes, attaching contexts to each step"
|
msgid "document your processes, attaching contexts to each step"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:33
|
#: lib/memex_web/live/invite_live/index.ex:34
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "edit invite"
|
msgid "edit invite"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -152,7 +147,7 @@ msgstr ""
|
|||||||
msgid "enable"
|
msgid "enable"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:34
|
#: lib/memex_web/templates/user_registration/new.html.heex:32
|
||||||
#: lib/memex_web/templates/user_settings/edit.html.heex:129
|
#: lib/memex_web/templates/user_settings/edit.html.heex:129
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "english"
|
msgid "english"
|
||||||
@ -178,7 +173,7 @@ msgstr ""
|
|||||||
msgid "instance information"
|
msgid "instance information"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/components/invite_card.ex:32
|
#: lib/memex_web/components/invite_card.ex:42
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "invite disabled"
|
msgid "invite disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -189,7 +184,7 @@ msgid "invite only"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/components/topbar.ex:73
|
#: lib/memex_web/components/topbar.ex:73
|
||||||
#: lib/memex_web/live/invite_live/index.ex:41
|
#: lib/memex_web/live/invite_live/index.ex:42
|
||||||
#: lib/memex_web/live/invite_live/index.html.heex:3
|
#: lib/memex_web/live/invite_live/index.html.heex:3
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "invites"
|
msgid "invites"
|
||||||
@ -205,7 +200,7 @@ msgstr ""
|
|||||||
msgid "multi-user:"
|
msgid "multi-user:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:37
|
#: lib/memex_web/live/invite_live/index.ex:38
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "new invite"
|
msgid "new invite"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -271,7 +266,7 @@ msgstr ""
|
|||||||
msgid "public signups"
|
msgid "public signups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_registration_controller.ex:34
|
#: lib/memex_web/controllers/user_registration_controller.ex:32
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "register"
|
msgid "register"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -306,6 +301,7 @@ msgstr ""
|
|||||||
msgid "set unlimited"
|
msgid "set unlimited"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:10
|
||||||
#: lib/memex_web/templates/user_settings/edit.html.heex:3
|
#: lib/memex_web/templates/user_settings/edit.html.heex:3
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "settings"
|
msgid "settings"
|
||||||
@ -632,7 +628,7 @@ msgstr ""
|
|||||||
msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
|
msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:30
|
#: lib/memex_web/templates/user_registration/new.html.heex:28
|
||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "language"
|
msgid "language"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -647,12 +643,7 @@ msgstr ""
|
|||||||
msgid "user registered on%{registered_datetime}"
|
msgid "user registered on%{registered_datetime}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/components/invite_card.ex:22
|
#: lib/memex_web/components/invite_card.ex:37
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "uses left: %{uses_left}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/components/invite_card.ex:27
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "uses left: unlimited"
|
msgid "uses left: unlimited"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -661,3 +652,13 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "read more on how to use memEx"
|
msgid "read more on how to use memEx"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/components/invite_card.ex:32
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "uses left: %{uses_left_count}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/components/invite_card.ex:52
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "uses: %{uses_count}"
|
||||||
|
msgstr ""
|
||||||
|
@ -10,11 +10,6 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:84
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Email change link is invalid or it has expired."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/templates/error/error.html.heex:8
|
#: lib/memex_web/templates/error/error.html.heex:8
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
@ -25,7 +20,6 @@ msgstr ""
|
|||||||
msgid "Invalid email or password"
|
msgid "Invalid email or password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:14
|
|
||||||
#: lib/memex_web/templates/user_reset_password/edit.html.heex:14
|
#: lib/memex_web/templates/user_reset_password/edit.html.heex:14
|
||||||
#: lib/memex_web/templates/user_settings/edit.html.heex:67
|
#: lib/memex_web/templates/user_settings/edit.html.heex:67
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@ -37,33 +31,23 @@ msgstr ""
|
|||||||
msgid "Reset password link is invalid or it has expired."
|
msgid "Reset password link is invalid or it has expired."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_registration_controller.ex:24
|
#: lib/memex_web/controllers/user_registration_controller.ex:22
|
||||||
#: lib/memex_web/controllers/user_registration_controller.ex:55
|
#: lib/memex_web/controllers/user_registration_controller.ex:51
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Sorry, public registration is disabled"
|
msgid "Sorry, public registration is disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_registration_controller.ex:14
|
#: lib/memex_web/controllers/user_registration_controller.ex:12
|
||||||
#: lib/memex_web/controllers/user_registration_controller.ex:45
|
#: lib/memex_web/controllers/user_registration_controller.ex:41
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Sorry, this invite was not found or expired"
|
msgid "Sorry, this invite was not found or expired"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:99
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Unable to delete user"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_confirmation_controller.ex:54
|
#: lib/memex_web/controllers/user_confirmation_controller.ex:54
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "User confirmation link is invalid or it has expired."
|
msgid "User confirmation link is invalid or it has expired."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:18
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "You are not authorized to view this page"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_auth.ex:177
|
#: lib/memex_web/controllers/user_auth.ex:177
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "You are not authorized to view this page."
|
msgid "You are not authorized to view this page."
|
||||||
@ -75,22 +59,22 @@ msgstr ""
|
|||||||
msgid "You must confirm your account and log in to access this page."
|
msgid "You must confirm your account and log in to access this page."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex/accounts/user.ex:139
|
#: lib/memex/accounts/user.ex:144
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "did not change"
|
msgid "did not change"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex/accounts/user.ex:160
|
#: lib/memex/accounts/user.ex:165
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "does not match password"
|
msgid "does not match password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex/accounts/user.ex:197
|
#: lib/memex/accounts/user.ex:202
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "is not valid"
|
msgid "is not valid"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex/accounts/user.ex:95
|
#: lib/memex/accounts/user.ex:99
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "must have the @ sign and no spaces"
|
msgid "must have the @ sign and no spaces"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -137,3 +121,28 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
|
msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/templates/user_registration/new.html.heex:13
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "oops, something went wrong! please check the errors below."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_registration_controller.ex:70
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "sorry, this invite was not found or expired"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/live/invite_live/index.ex:19
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "you are not authorized to view this page"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:84
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "email change link is invalid or it has expired."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:99
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "unable to delete user"
|
||||||
|
msgstr ""
|
||||||
|
@ -15,56 +15,31 @@ msgstr ""
|
|||||||
msgid "%{email} confirmed successfully."
|
msgid "%{email} confirmed successfully."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/form_component.ex:62
|
#: lib/memex_web/live/invite_live/index.ex:54
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "%{invite_name} created successfully"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:53
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{invite_name} deleted succesfully"
|
msgid "%{invite_name} deleted succesfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:114
|
#: lib/memex_web/live/invite_live/index.ex:115
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{invite_name} disabled succesfully"
|
msgid "%{invite_name} disabled succesfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:90
|
#: lib/memex_web/live/invite_live/index.ex:91
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{invite_name} enabled succesfully"
|
msgid "%{invite_name} enabled succesfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:68
|
#: lib/memex_web/live/invite_live/index.ex:69
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{invite_name} updated succesfully"
|
msgid "%{invite_name} updated succesfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/form_component.ex:42
|
#: lib/memex_web/live/invite_live/index.ex:140
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "%{invite_name} updated successfully"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:139
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{user_email} deleted succesfully"
|
msgid "%{user_email} deleted succesfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:29
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "A link to confirm your email change has been sent to the new address."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:127
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Copied to clipboard"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:77
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Email changed successfully."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_confirmation_controller.ex:23
|
#: lib/memex_web/controllers/user_confirmation_controller.ex:23
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
|
msgid "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
|
||||||
@ -80,26 +55,11 @@ msgstr ""
|
|||||||
msgid "Password reset successfully."
|
msgid "Password reset successfully."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:49
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Password updated successfully."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_registration_controller.ex:73
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Please check your email to verify your account"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/form_component.html.heex:31
|
#: lib/memex_web/live/invite_live/form_component.html.heex:31
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Saving..."
|
msgid "Saving..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:95
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Your account has been deleted"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/templates/user_settings/edit.html.heex:136
|
#: lib/memex_web/templates/user_settings/edit.html.heex:136
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "are you sure you want to change your language?"
|
msgid "are you sure you want to change your language?"
|
||||||
@ -156,3 +116,43 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format, fuzzy
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
msgid "register to setup memEx"
|
msgid "register to setup memEx"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/live/invite_live/form_component.ex:80
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "%{name} created successfully"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/live/invite_live/form_component.ex:62
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "%{name} updated successfully"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/live/invite_live/index.ex:128
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "copied to clipboard"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_registration_controller.ex:65
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "please check your email to verify your account"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:29
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "a link to confirm your email change has been sent to the new address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:77
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "email changed successfully."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:49
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "password updated successfully."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:95
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "your account has been deleted"
|
||||||
|
msgstr ""
|
||||||
|
@ -40,11 +40,6 @@ msgstr ""
|
|||||||
msgid "Reset your password"
|
msgid "Reset your password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:10
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Settings"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/form_component.html.heex:25
|
#: lib/memex_web/live/invite_live/form_component.html.heex:25
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Uses left"
|
msgid "Uses left"
|
||||||
@ -121,7 +116,7 @@ msgstr ""
|
|||||||
msgid "document your processes, attaching contexts to each step"
|
msgid "document your processes, attaching contexts to each step"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:33
|
#: lib/memex_web/live/invite_live/index.ex:34
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "edit invite"
|
msgid "edit invite"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -141,7 +136,7 @@ msgstr ""
|
|||||||
msgid "enable"
|
msgid "enable"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:34
|
#: lib/memex_web/templates/user_registration/new.html.heex:32
|
||||||
#: lib/memex_web/templates/user_settings/edit.html.heex:129
|
#: lib/memex_web/templates/user_settings/edit.html.heex:129
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "english"
|
msgid "english"
|
||||||
@ -167,7 +162,7 @@ msgstr ""
|
|||||||
msgid "instance information"
|
msgid "instance information"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/components/invite_card.ex:32
|
#: lib/memex_web/components/invite_card.ex:42
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "invite disabled"
|
msgid "invite disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -178,7 +173,7 @@ msgid "invite only"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/components/topbar.ex:73
|
#: lib/memex_web/components/topbar.ex:73
|
||||||
#: lib/memex_web/live/invite_live/index.ex:41
|
#: lib/memex_web/live/invite_live/index.ex:42
|
||||||
#: lib/memex_web/live/invite_live/index.html.heex:3
|
#: lib/memex_web/live/invite_live/index.html.heex:3
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "invites"
|
msgid "invites"
|
||||||
@ -194,7 +189,7 @@ msgstr ""
|
|||||||
msgid "multi-user:"
|
msgid "multi-user:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:37
|
#: lib/memex_web/live/invite_live/index.ex:38
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "new invite"
|
msgid "new invite"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -260,7 +255,7 @@ msgstr ""
|
|||||||
msgid "public signups"
|
msgid "public signups"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_registration_controller.ex:34
|
#: lib/memex_web/controllers/user_registration_controller.ex:32
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "register"
|
msgid "register"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -295,6 +290,7 @@ msgstr ""
|
|||||||
msgid "set unlimited"
|
msgid "set unlimited"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:10
|
||||||
#: lib/memex_web/templates/user_settings/edit.html.heex:3
|
#: lib/memex_web/templates/user_settings/edit.html.heex:3
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "settings"
|
msgid "settings"
|
||||||
@ -621,7 +617,7 @@ msgstr ""
|
|||||||
msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
|
msgid "while memEx fully supports multiple users, each memEx instance should be treated as a single cohesive and collaborative document."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:30
|
#: lib/memex_web/templates/user_registration/new.html.heex:28
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "language"
|
msgid "language"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -636,12 +632,7 @@ msgstr ""
|
|||||||
msgid "user registered on%{registered_datetime}"
|
msgid "user registered on%{registered_datetime}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/components/invite_card.ex:22
|
#: lib/memex_web/components/invite_card.ex:37
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "uses left: %{uses_left}"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/components/invite_card.ex:27
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "uses left: unlimited"
|
msgid "uses left: unlimited"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -650,3 +641,13 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "read more on how to use memEx"
|
msgid "read more on how to use memEx"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/components/invite_card.ex:32
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "uses left: %{uses_left_count}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/components/invite_card.ex:52
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "uses: %{uses_count}"
|
||||||
|
msgstr ""
|
||||||
|
@ -10,11 +10,6 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:84
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Email change link is invalid or it has expired."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/templates/error/error.html.heex:8
|
#: lib/memex_web/templates/error/error.html.heex:8
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Error"
|
msgid "Error"
|
||||||
@ -25,7 +20,6 @@ msgstr ""
|
|||||||
msgid "Invalid email or password"
|
msgid "Invalid email or password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/templates/user_registration/new.html.heex:14
|
|
||||||
#: lib/memex_web/templates/user_reset_password/edit.html.heex:14
|
#: lib/memex_web/templates/user_reset_password/edit.html.heex:14
|
||||||
#: lib/memex_web/templates/user_settings/edit.html.heex:67
|
#: lib/memex_web/templates/user_settings/edit.html.heex:67
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@ -37,33 +31,23 @@ msgstr ""
|
|||||||
msgid "Reset password link is invalid or it has expired."
|
msgid "Reset password link is invalid or it has expired."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_registration_controller.ex:24
|
#: lib/memex_web/controllers/user_registration_controller.ex:22
|
||||||
#: lib/memex_web/controllers/user_registration_controller.ex:55
|
#: lib/memex_web/controllers/user_registration_controller.ex:51
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Sorry, public registration is disabled"
|
msgid "Sorry, public registration is disabled"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_registration_controller.ex:14
|
#: lib/memex_web/controllers/user_registration_controller.ex:12
|
||||||
#: lib/memex_web/controllers/user_registration_controller.ex:45
|
#: lib/memex_web/controllers/user_registration_controller.ex:41
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Sorry, this invite was not found or expired"
|
msgid "Sorry, this invite was not found or expired"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:99
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Unable to delete user"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_confirmation_controller.ex:54
|
#: lib/memex_web/controllers/user_confirmation_controller.ex:54
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "User confirmation link is invalid or it has expired."
|
msgid "User confirmation link is invalid or it has expired."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:18
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "You are not authorized to view this page"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_auth.ex:177
|
#: lib/memex_web/controllers/user_auth.ex:177
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "You are not authorized to view this page."
|
msgid "You are not authorized to view this page."
|
||||||
@ -75,22 +59,22 @@ msgstr ""
|
|||||||
msgid "You must confirm your account and log in to access this page."
|
msgid "You must confirm your account and log in to access this page."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex/accounts/user.ex:139
|
#: lib/memex/accounts/user.ex:144
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "did not change"
|
msgid "did not change"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex/accounts/user.ex:160
|
#: lib/memex/accounts/user.ex:165
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "does not match password"
|
msgid "does not match password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex/accounts/user.ex:197
|
#: lib/memex/accounts/user.ex:202
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "is not valid"
|
msgid "is not valid"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex/accounts/user.ex:95
|
#: lib/memex/accounts/user.ex:99
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "must have the @ sign and no spaces"
|
msgid "must have the @ sign and no spaces"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -137,3 +121,28 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
|
msgid "invalid format: only numbers, letters and hyphen are accepted. tags must be comma-delimited"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/templates/user_registration/new.html.heex:13
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "oops, something went wrong! please check the errors below."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_registration_controller.ex:70
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "sorry, this invite was not found or expired"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/live/invite_live/index.ex:19
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "you are not authorized to view this page"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:84
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "email change link is invalid or it has expired."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:99
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "unable to delete user"
|
||||||
|
msgstr ""
|
||||||
|
@ -15,56 +15,31 @@ msgstr ""
|
|||||||
msgid "%{email} confirmed successfully."
|
msgid "%{email} confirmed successfully."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/form_component.ex:62
|
#: lib/memex_web/live/invite_live/index.ex:54
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "%{invite_name} created successfully"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:53
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{invite_name} deleted succesfully"
|
msgid "%{invite_name} deleted succesfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:114
|
#: lib/memex_web/live/invite_live/index.ex:115
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{invite_name} disabled succesfully"
|
msgid "%{invite_name} disabled succesfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:90
|
#: lib/memex_web/live/invite_live/index.ex:91
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{invite_name} enabled succesfully"
|
msgid "%{invite_name} enabled succesfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:68
|
#: lib/memex_web/live/invite_live/index.ex:69
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{invite_name} updated succesfully"
|
msgid "%{invite_name} updated succesfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/form_component.ex:42
|
#: lib/memex_web/live/invite_live/index.ex:140
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "%{invite_name} updated successfully"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:139
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "%{user_email} deleted succesfully"
|
msgid "%{user_email} deleted succesfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:29
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "A link to confirm your email change has been sent to the new address."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/index.ex:127
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Copied to clipboard"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:77
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Email changed successfully."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_confirmation_controller.ex:23
|
#: lib/memex_web/controllers/user_confirmation_controller.ex:23
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
|
msgid "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."
|
||||||
@ -80,26 +55,11 @@ msgstr ""
|
|||||||
msgid "Password reset successfully."
|
msgid "Password reset successfully."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:49
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Password updated successfully."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_registration_controller.ex:73
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Please check your email to verify your account"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/live/invite_live/form_component.html.heex:31
|
#: lib/memex_web/live/invite_live/form_component.html.heex:31
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Saving..."
|
msgid "Saving..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/memex_web/controllers/user_settings_controller.ex:95
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Your account has been deleted"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/memex_web/templates/user_settings/edit.html.heex:136
|
#: lib/memex_web/templates/user_settings/edit.html.heex:136
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "are you sure you want to change your language?"
|
msgid "are you sure you want to change your language?"
|
||||||
@ -156,3 +116,43 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "register to setup memEx"
|
msgid "register to setup memEx"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/live/invite_live/form_component.ex:80
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "%{name} created successfully"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/live/invite_live/form_component.ex:62
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "%{name} updated successfully"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/live/invite_live/index.ex:128
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "copied to clipboard"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_registration_controller.ex:65
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "please check your email to verify your account"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:29
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "a link to confirm your email change has been sent to the new address."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:77
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "email changed successfully."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:49
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "password updated successfully."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/memex_web/controllers/user_settings_controller.ex:95
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "your account has been deleted"
|
||||||
|
msgstr ""
|
||||||
|
11
priv/repo/migrations/20230204191547_record_invites.exs
Normal file
11
priv/repo/migrations/20230204191547_record_invites.exs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
defmodule Memex.Repo.Migrations.RecordInvites do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add :invite_id, references(:invites, type: :binary_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
rename table(:invites), :user_id, to: :created_by_id
|
||||||
|
end
|
||||||
|
end
|
176
test/memex/accounts/invites_text.exs
Normal file
176
test/memex/accounts/invites_text.exs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
defmodule Memex.InvitesTest do
|
||||||
|
@moduledoc """
|
||||||
|
This module tests the Memex.Accounts.Invites context
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Memex.DataCase
|
||||||
|
alias Ecto.Changeset
|
||||||
|
alias Memex.Accounts
|
||||||
|
alias Memex.Accounts.{Invite, Invites}
|
||||||
|
|
||||||
|
@moduletag :invites_test
|
||||||
|
|
||||||
|
@valid_attrs %{
|
||||||
|
"name" => "some name"
|
||||||
|
}
|
||||||
|
@invalid_attrs %{
|
||||||
|
"name" => nil,
|
||||||
|
"token" => nil
|
||||||
|
}
|
||||||
|
|
||||||
|
describe "invites" do
|
||||||
|
setup do
|
||||||
|
current_user = admin_fixture()
|
||||||
|
{:ok, invite} = Invites.create_invite(current_user, @valid_attrs)
|
||||||
|
[invite: invite, current_user: current_user]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "list_invites/0 returns all invites", %{invite: invite, current_user: current_user} do
|
||||||
|
assert Invites.list_invites(current_user) == [invite]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "get_invite!/1 returns the invite with given id",
|
||||||
|
%{invite: invite, current_user: current_user} do
|
||||||
|
assert Invites.get_invite!(invite.id, current_user) == invite
|
||||||
|
end
|
||||||
|
|
||||||
|
test "valid_invite_token? returns for valid and invalid invite tokens",
|
||||||
|
%{invite: %{token: token}} do
|
||||||
|
refute Invites.valid_invite_token?(nil)
|
||||||
|
refute Invites.valid_invite_token?("")
|
||||||
|
assert Invites.valid_invite_token?(token)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "valid_invite_token? does not return true for a disabled invite by token",
|
||||||
|
%{invite: %{token: token} = invite, current_user: current_user} do
|
||||||
|
assert Invites.valid_invite_token?(token)
|
||||||
|
|
||||||
|
{:ok, _invite} = Invites.update_invite(invite, %{uses_left: 1}, current_user)
|
||||||
|
{:ok, _invite} = Invites.use_invite(token)
|
||||||
|
|
||||||
|
refute Invites.valid_invite_token?(token)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "get_use_count/2 returns the correct invite usage",
|
||||||
|
%{invite: %{token: token} = invite, current_user: current_user} do
|
||||||
|
assert 0 == Invites.get_use_count(invite, current_user)
|
||||||
|
|
||||||
|
assert {:ok, _user} =
|
||||||
|
Accounts.register_user(
|
||||||
|
%{"email" => unique_user_email(), "password" => valid_user_password()},
|
||||||
|
token
|
||||||
|
)
|
||||||
|
|
||||||
|
assert 1 == Invites.get_use_count(invite, current_user)
|
||||||
|
|
||||||
|
assert {:ok, _user} =
|
||||||
|
Accounts.register_user(
|
||||||
|
%{"email" => unique_user_email(), "password" => valid_user_password()},
|
||||||
|
token
|
||||||
|
)
|
||||||
|
|
||||||
|
assert 2 == Invites.get_use_count(invite, current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "use_invite/1 successfully uses an unlimited invite",
|
||||||
|
%{invite: %{token: token} = invite, current_user: current_user} do
|
||||||
|
{:ok, invite} = Invites.update_invite(invite, %{uses_left: nil}, current_user)
|
||||||
|
assert {:ok, ^invite} = Invites.use_invite(token)
|
||||||
|
assert {:ok, ^invite} = Invites.use_invite(token)
|
||||||
|
assert {:ok, ^invite} = Invites.use_invite(token)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "use_invite/1 successfully decrements an invite",
|
||||||
|
%{invite: %{token: token} = invite, current_user: current_user} do
|
||||||
|
{:ok, _invite} = Invites.update_invite(invite, %{uses_left: 10}, current_user)
|
||||||
|
assert {:ok, %{uses_left: 9}} = Invites.use_invite(token)
|
||||||
|
assert {:ok, %{uses_left: 8}} = Invites.use_invite(token)
|
||||||
|
assert {:ok, %{uses_left: 7}} = Invites.use_invite(token)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "use_invite/1 successfully disactivates an invite",
|
||||||
|
%{invite: %{token: token} = invite, current_user: current_user} do
|
||||||
|
{:ok, _invite} = Invites.update_invite(invite, %{uses_left: 1}, current_user)
|
||||||
|
assert {:ok, %{uses_left: 0, disabled_at: disabled_at}} = Invites.use_invite(token)
|
||||||
|
assert not is_nil(disabled_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "use_invite/1 does not work on disactivated invite",
|
||||||
|
%{invite: %{token: token} = invite, current_user: current_user} do
|
||||||
|
{:ok, _invite} = Invites.update_invite(invite, %{uses_left: 1}, current_user)
|
||||||
|
{:ok, _invite} = Invites.use_invite(token)
|
||||||
|
assert {:error, :invalid_token} = Invites.use_invite(token)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create_invite/1 with valid data creates an unlimited invite",
|
||||||
|
%{current_user: current_user} do
|
||||||
|
assert {:ok, %Invite{} = invite} =
|
||||||
|
Invites.create_invite(current_user, %{
|
||||||
|
"name" => "some name"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert invite.name == "some name"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create_invite/1 with valid data creates a limited invite",
|
||||||
|
%{current_user: current_user} do
|
||||||
|
assert {:ok, %Invite{} = invite} =
|
||||||
|
Invites.create_invite(current_user, %{
|
||||||
|
"name" => "some name",
|
||||||
|
"uses_left" => 10
|
||||||
|
})
|
||||||
|
|
||||||
|
assert invite.name == "some name"
|
||||||
|
assert invite.uses_left == 10
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create_invite/1 with invalid data returns error changeset",
|
||||||
|
%{current_user: current_user} do
|
||||||
|
assert {:error, %Changeset{}} = Invites.create_invite(current_user, @invalid_attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update_invite/2 can set an invite to be limited",
|
||||||
|
%{invite: invite, current_user: current_user} do
|
||||||
|
assert {:ok, %Invite{} = new_invite} =
|
||||||
|
Invites.update_invite(
|
||||||
|
invite,
|
||||||
|
%{"name" => "some updated name", "uses_left" => 5},
|
||||||
|
current_user
|
||||||
|
)
|
||||||
|
|
||||||
|
assert new_invite.name == "some updated name"
|
||||||
|
assert new_invite.uses_left == 5
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update_invite/2 can set an invite to be unlimited",
|
||||||
|
%{invite: invite, current_user: current_user} do
|
||||||
|
{:ok, invite} = Invites.update_invite(invite, %{"uses_left" => 5}, current_user)
|
||||||
|
|
||||||
|
assert {:ok, %Invite{} = new_invite} =
|
||||||
|
Invites.update_invite(
|
||||||
|
invite,
|
||||||
|
%{"name" => "some updated name", "uses_left" => nil},
|
||||||
|
current_user
|
||||||
|
)
|
||||||
|
|
||||||
|
assert new_invite.name == "some updated name"
|
||||||
|
assert new_invite.uses_left |> is_nil()
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update_invite/2 with invalid data returns error changeset",
|
||||||
|
%{invite: invite, current_user: current_user} do
|
||||||
|
assert {:error, %Changeset{}} = Invites.update_invite(invite, @invalid_attrs, current_user)
|
||||||
|
assert invite == Invites.get_invite!(invite.id, current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "delete_invite/1 deletes the invite", %{invite: invite, current_user: current_user} do
|
||||||
|
assert {:ok, %Invite{}} = Invites.delete_invite(invite, current_user)
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Invites.get_invite!(invite.id, current_user) end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "delete_invite!/1 deletes the invite", %{invite: invite, current_user: current_user} do
|
||||||
|
assert %Invite{} = Invites.delete_invite!(invite, current_user)
|
||||||
|
assert_raise Ecto.NoResultsError, fn -> Invites.get_invite!(invite.id, current_user) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -6,7 +6,7 @@ defmodule Memex.AccountsTest do
|
|||||||
use Memex.DataCase
|
use Memex.DataCase
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
alias Memex.Accounts
|
alias Memex.Accounts
|
||||||
alias Memex.Accounts.{User, UserToken}
|
alias Memex.Accounts.{Invites, User, UserToken}
|
||||||
|
|
||||||
@moduletag :accounts_test
|
@moduletag :accounts_test
|
||||||
|
|
||||||
@ -102,6 +102,17 @@ defmodule Memex.AccountsTest do
|
|||||||
assert is_nil(user.confirmed_at)
|
assert is_nil(user.confirmed_at)
|
||||||
assert is_nil(user.password)
|
assert is_nil(user.password)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "records used invite during registration" do
|
||||||
|
{:ok, %{id: invite_id, token: token}} =
|
||||||
|
admin_fixture() |> Invites.create_invite(%{"name" => "my invite"})
|
||||||
|
|
||||||
|
assert {:ok, %{invite_id: ^invite_id}} =
|
||||||
|
Accounts.register_user(
|
||||||
|
%{"email" => unique_user_email(), "password" => valid_user_password()},
|
||||||
|
token
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "change_user_registration/2" do
|
describe "change_user_registration/2" do
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
defmodule Memex.InvitesTest do
|
|
||||||
@moduledoc """
|
|
||||||
This module tests the Invites context
|
|
||||||
"""
|
|
||||||
|
|
||||||
use Memex.DataCase
|
|
||||||
alias Ecto.Changeset
|
|
||||||
alias Memex.{Invites, Invites.Invite}
|
|
||||||
|
|
||||||
@moduletag :invites_test
|
|
||||||
|
|
||||||
@valid_attrs %{
|
|
||||||
"name" => "some name",
|
|
||||||
"token" => "some token"
|
|
||||||
}
|
|
||||||
@update_attrs %{
|
|
||||||
"name" => "some updated name",
|
|
||||||
"token" => "some updated token"
|
|
||||||
}
|
|
||||||
@invalid_attrs %{
|
|
||||||
"name" => nil,
|
|
||||||
"token" => nil
|
|
||||||
}
|
|
||||||
|
|
||||||
describe "invites" do
|
|
||||||
setup do
|
|
||||||
current_user = admin_fixture()
|
|
||||||
{:ok, invite} = Invites.create_invite(current_user, @valid_attrs)
|
|
||||||
[invite: invite, current_user: current_user]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "list_invites/0 returns all invites", %{invite: invite, current_user: current_user} do
|
|
||||||
assert Invites.list_invites(current_user) == [invite]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "get_invite!/1 returns the invite with given id",
|
|
||||||
%{invite: invite, current_user: current_user} do
|
|
||||||
assert Invites.get_invite!(invite.id, current_user) == invite
|
|
||||||
end
|
|
||||||
|
|
||||||
test "create_invite/1 with valid data creates a invite",
|
|
||||||
%{current_user: current_user} do
|
|
||||||
assert {:ok, %Invite{} = invite} = Invites.create_invite(current_user, @valid_attrs)
|
|
||||||
assert invite.name == "some name"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "create_invite/1 with invalid data returns error changeset",
|
|
||||||
%{current_user: current_user} do
|
|
||||||
assert {:error, %Changeset{}} = Invites.create_invite(current_user, @invalid_attrs)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "update_invite/2 with valid data updates the invite",
|
|
||||||
%{invite: invite, current_user: current_user} do
|
|
||||||
assert {:ok, %Invite{} = new_invite} =
|
|
||||||
Invites.update_invite(invite, @update_attrs, current_user)
|
|
||||||
|
|
||||||
assert new_invite.name == "some updated name"
|
|
||||||
assert new_invite.token == new_invite.token
|
|
||||||
end
|
|
||||||
|
|
||||||
test "update_invite/2 with invalid data returns error changeset",
|
|
||||||
%{invite: invite, current_user: current_user} do
|
|
||||||
assert {:error, %Changeset{}} = Invites.update_invite(invite, @invalid_attrs, current_user)
|
|
||||||
assert invite == Invites.get_invite!(invite.id, current_user)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "delete_invite/1 deletes the invite", %{invite: invite, current_user: current_user} do
|
|
||||||
assert {:ok, %Invite{}} = Invites.delete_invite(invite, current_user)
|
|
||||||
assert_raise Ecto.NoResultsError, fn -> Invites.get_invite!(invite.id, current_user) end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "change_invite/1 returns a invite changeset", %{invite: invite} do
|
|
||||||
assert %Changeset{} = Invites.change_invite(invite)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -8,14 +8,7 @@ defmodule MemexWeb.ExportControllerTest do
|
|||||||
|
|
||||||
@moduletag :export_controller_test
|
@moduletag :export_controller_test
|
||||||
|
|
||||||
setup %{conn: conn} do
|
setup [:register_and_log_in_user]
|
||||||
current_user = user_fixture() |> confirm_user()
|
|
||||||
|
|
||||||
[
|
|
||||||
current_user: current_user,
|
|
||||||
conn: conn |> log_in_user(current_user)
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
defp add_data(%{current_user: current_user}) do
|
defp add_data(%{current_user: current_user}) do
|
||||||
note = note_fixture(current_user)
|
note = note_fixture(current_user)
|
||||||
|
@ -33,7 +33,7 @@ defmodule MemexWeb.UserRegistrationControllerTest do
|
|||||||
})
|
})
|
||||||
|
|
||||||
assert get_session(conn, :phoenix_flash) == %{
|
assert get_session(conn, :phoenix_flash) == %{
|
||||||
"info" => dgettext("prompts", "Please check your email to verify your account")
|
"info" => dgettext("prompts", "please check your email to verify your account")
|
||||||
}
|
}
|
||||||
|
|
||||||
assert redirected_to(conn) =~ "/"
|
assert redirected_to(conn) =~ "/"
|
||||||
|
@ -15,7 +15,7 @@ defmodule MemexWeb.UserSettingsControllerTest do
|
|||||||
test "renders settings page", %{conn: conn} do
|
test "renders settings page", %{conn: conn} do
|
||||||
conn = get(conn, Routes.user_settings_path(conn, :edit))
|
conn = get(conn, Routes.user_settings_path(conn, :edit))
|
||||||
response = html_response(conn, 200)
|
response = html_response(conn, 200)
|
||||||
assert response =~ gettext("Settings")
|
assert response =~ gettext("settings")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "redirects if user is not logged in" do
|
test "redirects if user is not logged in" do
|
||||||
@ -27,7 +27,7 @@ defmodule MemexWeb.UserSettingsControllerTest do
|
|||||||
|
|
||||||
describe "PUT /users/settings (change password form)" do
|
describe "PUT /users/settings (change password form)" do
|
||||||
test "updates the user password and resets tokens",
|
test "updates the user password and resets tokens",
|
||||||
%{conn: conn, user: user} do
|
%{conn: conn, current_user: current_user} do
|
||||||
new_password_conn =
|
new_password_conn =
|
||||||
put(conn, Routes.user_settings_path(conn, :update), %{
|
put(conn, Routes.user_settings_path(conn, :update), %{
|
||||||
"action" => "update_password",
|
"action" => "update_password",
|
||||||
@ -42,9 +42,9 @@ defmodule MemexWeb.UserSettingsControllerTest do
|
|||||||
assert get_session(new_password_conn, :user_token) != get_session(conn, :user_token)
|
assert get_session(new_password_conn, :user_token) != get_session(conn, :user_token)
|
||||||
|
|
||||||
assert get_flash(new_password_conn, :info) =~
|
assert get_flash(new_password_conn, :info) =~
|
||||||
dgettext("actions", "Password updated successfully")
|
dgettext("actions", "password updated successfully")
|
||||||
|
|
||||||
assert Accounts.get_user_by_email_and_password(user.email, "new valid password")
|
assert Accounts.get_user_by_email_and_password(current_user.email, "new valid password")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not update password on invalid data", %{conn: conn} do
|
test "does not update password on invalid data", %{conn: conn} do
|
||||||
@ -70,7 +70,7 @@ defmodule MemexWeb.UserSettingsControllerTest do
|
|||||||
|
|
||||||
describe "PUT /users/settings (change email form)" do
|
describe "PUT /users/settings (change email form)" do
|
||||||
@tag :capture_log
|
@tag :capture_log
|
||||||
test "updates the user email", %{conn: conn, user: user} do
|
test "updates the user email", %{conn: conn, current_user: current_user} do
|
||||||
conn =
|
conn =
|
||||||
put(conn, Routes.user_settings_path(conn, :update), %{
|
put(conn, Routes.user_settings_path(conn, :update), %{
|
||||||
"action" => "update_email",
|
"action" => "update_email",
|
||||||
@ -83,10 +83,10 @@ defmodule MemexWeb.UserSettingsControllerTest do
|
|||||||
assert get_flash(conn, :info) =~
|
assert get_flash(conn, :info) =~
|
||||||
dgettext(
|
dgettext(
|
||||||
"prompts",
|
"prompts",
|
||||||
"A link to confirm your email change has been sent to the new address."
|
"a link to confirm your email change has been sent to the new address."
|
||||||
)
|
)
|
||||||
|
|
||||||
assert Accounts.get_user_by_email(user.email)
|
assert Accounts.get_user_by_email(current_user.email)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not update email on invalid data", %{conn: conn} do
|
test "does not update email on invalid data", %{conn: conn} do
|
||||||
@ -105,14 +105,14 @@ defmodule MemexWeb.UserSettingsControllerTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /users/settings/confirm_email/:token" do
|
describe "GET /users/settings/confirm_email/:token" do
|
||||||
setup %{user: user} do
|
setup %{current_user: current_user} do
|
||||||
email = unique_user_email()
|
email = unique_user_email()
|
||||||
|
|
||||||
token =
|
token =
|
||||||
extract_user_token(fn url ->
|
extract_user_token(fn url ->
|
||||||
Accounts.deliver_update_email_instructions(
|
Accounts.deliver_update_email_instructions(
|
||||||
%{user | email: email},
|
%{current_user | email: email},
|
||||||
user.email,
|
current_user.email,
|
||||||
url
|
url
|
||||||
)
|
)
|
||||||
end)
|
end)
|
||||||
@ -121,28 +121,28 @@ defmodule MemexWeb.UserSettingsControllerTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
test "updates the user email once",
|
test "updates the user email once",
|
||||||
%{conn: conn, user: user, token: token, email: email} do
|
%{conn: conn, current_user: current_user, token: token, email: email} do
|
||||||
conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token))
|
conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token))
|
||||||
assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
|
assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
|
||||||
assert get_flash(conn, :info) =~ dgettext("prompts", "Email changed successfully")
|
assert get_flash(conn, :info) =~ dgettext("prompts", "email changed successfully")
|
||||||
refute Accounts.get_user_by_email(user.email)
|
refute Accounts.get_user_by_email(current_user.email)
|
||||||
assert Accounts.get_user_by_email(email)
|
assert Accounts.get_user_by_email(email)
|
||||||
|
|
||||||
conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token))
|
conn = get(conn, Routes.user_settings_path(conn, :confirm_email, token))
|
||||||
assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
|
assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
|
||||||
|
|
||||||
assert get_flash(conn, :error) =~
|
assert get_flash(conn, :error) =~
|
||||||
dgettext("errors", "Email change link is invalid or it has expired")
|
dgettext("errors", "email change link is invalid or it has expired")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not update email with invalid token", %{conn: conn, user: user} do
|
test "does not update email with invalid token", %{conn: conn, current_user: current_user} do
|
||||||
conn = get(conn, Routes.user_settings_path(conn, :confirm_email, "oops"))
|
conn = get(conn, Routes.user_settings_path(conn, :confirm_email, "oops"))
|
||||||
assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
|
assert redirected_to(conn) == Routes.user_settings_path(conn, :edit)
|
||||||
|
|
||||||
assert get_flash(conn, :error) =~
|
assert get_flash(conn, :error) =~
|
||||||
dgettext("errors", "Email change link is invalid or it has expired")
|
dgettext("errors", "email change link is invalid or it has expired")
|
||||||
|
|
||||||
assert Accounts.get_user_by_email(user.email)
|
assert Accounts.get_user_by_email(current_user.email)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "redirects if user is not logged in", %{token: token} do
|
test "redirects if user is not logged in", %{token: token} do
|
||||||
|
@ -23,8 +23,8 @@ defmodule MemexWeb.ContextLiveTest do
|
|||||||
"visibility" => nil
|
"visibility" => nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defp create_context(%{user: user}) do
|
defp create_context(%{current_user: current_user}) do
|
||||||
[context: context_fixture(user)]
|
[context: context_fixture(current_user)]
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "Index" do
|
describe "Index" do
|
||||||
@ -148,13 +148,16 @@ defmodule MemexWeb.ContextLiveTest do
|
|||||||
describe "show with note" do
|
describe "show with note" do
|
||||||
setup [:register_and_log_in_user]
|
setup [:register_and_log_in_user]
|
||||||
|
|
||||||
setup %{user: user} do
|
setup %{current_user: current_user} do
|
||||||
%{slug: note_slug} = note = note_fixture(user)
|
%{slug: note_slug} = note = note_fixture(current_user)
|
||||||
|
|
||||||
[
|
[
|
||||||
note: note,
|
note: note,
|
||||||
context:
|
context:
|
||||||
context_fixture(%{content: "example with backlink to [[#{note_slug}]] note"}, user)
|
context_fixture(
|
||||||
|
%{content: "example with backlink to [[#{note_slug}]] note"},
|
||||||
|
current_user
|
||||||
|
)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ defmodule MemexWeb.InviteLiveTest do
|
|||||||
use MemexWeb.ConnCase
|
use MemexWeb.ConnCase
|
||||||
import Phoenix.LiveViewTest
|
import Phoenix.LiveViewTest
|
||||||
import MemexWeb.Gettext
|
import MemexWeb.Gettext
|
||||||
alias Memex.Invites
|
alias Memex.Accounts.Invites
|
||||||
|
|
||||||
@moduletag :invite_live_test
|
@moduletag :invite_live_test
|
||||||
@create_attrs %{"name" => "some name"}
|
@create_attrs %{"name" => "some name"}
|
||||||
@ -16,9 +16,9 @@ defmodule MemexWeb.InviteLiveTest do
|
|||||||
describe "Index" do
|
describe "Index" do
|
||||||
setup [:register_and_log_in_user]
|
setup [:register_and_log_in_user]
|
||||||
|
|
||||||
setup %{user: user} do
|
setup %{current_user: current_user} do
|
||||||
{:ok, invite} = Invites.create_invite(user, @create_attrs)
|
{:ok, invite} = Invites.create_invite(current_user, @create_attrs)
|
||||||
%{invite: invite, user: user}
|
%{invite: invite, current_user: current_user}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "lists all invites", %{conn: conn, invite: invite} do
|
test "lists all invites", %{conn: conn, invite: invite} do
|
||||||
|
@ -24,8 +24,8 @@ defmodule MemexWeb.NoteLiveTest do
|
|||||||
"visibility" => nil
|
"visibility" => nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defp create_note(%{user: user}) do
|
defp create_note(%{current_user: current_user}) do
|
||||||
[note: note_fixture(user)]
|
[note: note_fixture(current_user)]
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "Index" do
|
describe "Index" do
|
||||||
@ -149,13 +149,13 @@ defmodule MemexWeb.NoteLiveTest do
|
|||||||
describe "show with note" do
|
describe "show with note" do
|
||||||
setup [:register_and_log_in_user]
|
setup [:register_and_log_in_user]
|
||||||
|
|
||||||
setup %{user: user} do
|
setup %{current_user: current_user} do
|
||||||
%{slug: note_slug} = note = note_fixture(user)
|
%{slug: note_slug} = note = note_fixture(current_user)
|
||||||
|
|
||||||
[
|
[
|
||||||
note: note,
|
note: note,
|
||||||
backlinked_note:
|
backlinked_note:
|
||||||
note_fixture(%{content: "example with backlink to [[#{note_slug}]] note"}, user)
|
note_fixture(%{content: "example with backlink to [[#{note_slug}]] note"}, current_user)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ defmodule MemexWeb.PipelineLiveTest do
|
|||||||
"title" => nil
|
"title" => nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defp create_pipeline(%{user: user}) do
|
defp create_pipeline(%{current_user: current_user}) do
|
||||||
[pipeline: pipeline_fixture(user)]
|
[pipeline: pipeline_fixture(current_user)]
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "Index" do
|
describe "Index" do
|
||||||
@ -181,9 +181,9 @@ defmodule MemexWeb.PipelineLiveTest do
|
|||||||
describe "show with a step" do
|
describe "show with a step" do
|
||||||
setup [:register_and_log_in_user, :create_pipeline]
|
setup [:register_and_log_in_user, :create_pipeline]
|
||||||
|
|
||||||
setup %{pipeline: pipeline, user: user} do
|
setup %{pipeline: pipeline, current_user: current_user} do
|
||||||
[
|
[
|
||||||
step: step_fixture(0, pipeline, user)
|
step: step_fixture(0, pipeline, current_user)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -236,11 +236,11 @@ defmodule MemexWeb.PipelineLiveTest do
|
|||||||
describe "show with multiple steps" do
|
describe "show with multiple steps" do
|
||||||
setup [:register_and_log_in_user, :create_pipeline]
|
setup [:register_and_log_in_user, :create_pipeline]
|
||||||
|
|
||||||
setup %{pipeline: pipeline, user: user} do
|
setup %{pipeline: pipeline, current_user: current_user} do
|
||||||
[
|
[
|
||||||
first_step: step_fixture(%{title: "first step"}, 0, pipeline, user),
|
first_step: step_fixture(%{title: "first step"}, 0, pipeline, current_user),
|
||||||
second_step: step_fixture(%{title: "second step"}, 1, pipeline, user),
|
second_step: step_fixture(%{title: "second step"}, 1, pipeline, current_user),
|
||||||
third_step: step_fixture(%{title: "third step"}, 2, pipeline, user)
|
third_step: step_fixture(%{title: "third step"}, 2, pipeline, current_user)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -23,10 +23,11 @@ defmodule MemexWeb.ConnCase do
|
|||||||
using do
|
using do
|
||||||
quote do
|
quote do
|
||||||
# Import conveniences for testing with connections
|
# Import conveniences for testing with connections
|
||||||
import Plug.Conn
|
# credo:disable-for-next-line Credo.Check.Consistency.MultiAliasImportRequireUse
|
||||||
import Phoenix.ConnTest
|
|
||||||
import Memex.Fixtures
|
import Memex.Fixtures
|
||||||
import MemexWeb.ConnCase
|
import MemexWeb.ConnCase
|
||||||
|
import Phoenix.ConnTest
|
||||||
|
import Plug.Conn
|
||||||
|
|
||||||
alias MemexWeb.Router.Helpers, as: Routes
|
alias MemexWeb.Router.Helpers, as: Routes
|
||||||
|
|
||||||
@ -50,10 +51,10 @@ defmodule MemexWeb.ConnCase do
|
|||||||
test context.
|
test context.
|
||||||
"""
|
"""
|
||||||
@spec register_and_log_in_user(%{conn: Plug.Conn.t()}) ::
|
@spec register_and_log_in_user(%{conn: Plug.Conn.t()}) ::
|
||||||
%{conn: Plug.Conn.t(), user: User.t()}
|
%{conn: Plug.Conn.t(), current_user: User.t()}
|
||||||
def register_and_log_in_user(%{conn: conn}) do
|
def register_and_log_in_user(%{conn: conn}) do
|
||||||
user = user_fixture() |> confirm_user()
|
current_user = user_fixture() |> confirm_user()
|
||||||
%{conn: log_in_user(conn, user), user: user}
|
%{conn: log_in_user(conn, current_user), current_user: current_user}
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec confirm_user(User.t()) :: User.t()
|
@spec confirm_user(User.t()) :: User.t()
|
||||||
|
@ -22,10 +22,8 @@ defmodule Memex.DataCase do
|
|||||||
alias Memex.Repo
|
alias Memex.Repo
|
||||||
|
|
||||||
import Ecto
|
import Ecto
|
||||||
import Ecto.Changeset
|
import Ecto.{Changeset, Query}
|
||||||
import Ecto.Query
|
import Memex.{DataCase, Fixtures}
|
||||||
import Memex.DataCase
|
|
||||||
import Memex.Fixtures
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -45,7 +43,7 @@ defmodule Memex.DataCase do
|
|||||||
"""
|
"""
|
||||||
def errors_on(changeset) do
|
def errors_on(changeset) do
|
||||||
Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
|
Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
|
||||||
Regex.replace(~r"%{(\w+)}", message, fn _, key ->
|
Regex.replace(~r"%{(\w+)}", message, fn _capture, key ->
|
||||||
opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
|
opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user