Introduce basic user and profile management
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
@@ -73,9 +73,9 @@ defmodule Mobilizon.Actors do
|
||||
Gets an actor with preloaded relations.
|
||||
"""
|
||||
@spec get_actor_with_preload(integer | String.t()) :: Actor.t() | nil
|
||||
def get_actor_with_preload(id) do
|
||||
def get_actor_with_preload(id, include_suspended \\ false) do
|
||||
id
|
||||
|> actor_with_preload_query()
|
||||
|> actor_with_preload_query(include_suspended)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
@@ -90,6 +90,14 @@ defmodule Mobilizon.Actors do
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
@spec get_remote_actor_with_preload(integer | String.t(), boolean()) :: Actor.t() | nil
|
||||
def get_remote_actor_with_preload(id, include_suspended \\ false) do
|
||||
id
|
||||
|> actor_with_preload_query(include_suspended)
|
||||
|> filter_external()
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets an actor by its URL (ActivityPub ID). The `:preload` option allows to
|
||||
preload the followers relation.
|
||||
@@ -255,27 +263,41 @@ defmodule Mobilizon.Actors do
|
||||
end
|
||||
end
|
||||
|
||||
def delete_actor(%Actor{} = actor) do
|
||||
Workers.Background.enqueue("delete_actor", %{"actor_id" => actor.id})
|
||||
@delete_actor_default_options [reserve_username: true]
|
||||
|
||||
def delete_actor(%Actor{} = actor, options \\ @delete_actor_default_options) do
|
||||
delete_actor_options = Keyword.merge(@delete_actor_default_options, options)
|
||||
|
||||
Workers.Background.enqueue("delete_actor", %{
|
||||
"actor_id" => actor.id,
|
||||
"reserve_username" => Keyword.get(delete_actor_options, :reserve_username, true)
|
||||
})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes an actor.
|
||||
"""
|
||||
@spec perform(atom(), Actor.t()) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()}
|
||||
def perform(:delete_actor, %Actor{} = actor) do
|
||||
def perform(:delete_actor, %Actor{} = actor, options \\ @delete_actor_default_options) do
|
||||
actor = Repo.preload(actor, @actor_preloads)
|
||||
|
||||
transaction =
|
||||
delete_actor_options = Keyword.merge(@delete_actor_default_options, options)
|
||||
|
||||
multi =
|
||||
Multi.new()
|
||||
|> Multi.run(:delete_organized_events, fn _, _ -> delete_actor_organized_events(actor) end)
|
||||
|> Multi.run(:empty_comments, fn _, _ -> delete_actor_empty_comments(actor) end)
|
||||
|> Multi.run(:remove_banner, fn _, _ -> remove_banner(actor) end)
|
||||
|> Multi.run(:remove_avatar, fn _, _ -> remove_avatar(actor) end)
|
||||
|> Multi.update(:actor, Actor.delete_changeset(actor))
|
||||
|> Repo.transaction()
|
||||
|
||||
case transaction do
|
||||
multi =
|
||||
if Keyword.get(delete_actor_options, :reserve_username, true) do
|
||||
Multi.update(multi, :actor, Actor.delete_changeset(actor))
|
||||
else
|
||||
Multi.delete(multi, :actor, actor)
|
||||
end
|
||||
|
||||
case Repo.transaction(multi) do
|
||||
{:ok, %{actor: %Actor{} = actor}} ->
|
||||
{:ok, true} = Cachex.del(:activity_pub, "actor_#{actor.preferred_username}")
|
||||
{:ok, actor}
|
||||
@@ -295,8 +317,57 @@ defmodule Mobilizon.Actors do
|
||||
@doc """
|
||||
Returns the list of actors.
|
||||
"""
|
||||
@spec list_actors :: [Actor.t()]
|
||||
def list_actors, do: Repo.all(Actor)
|
||||
@spec list_actors(String.t(), String.t(), boolean, boolean, integer, integer) :: Page.t()
|
||||
def list_actors(
|
||||
type \\ :Person,
|
||||
preferred_username \\ "",
|
||||
name \\ "",
|
||||
domain \\ "",
|
||||
local \\ true,
|
||||
suspended \\ false,
|
||||
page \\ nil,
|
||||
limit \\ nil
|
||||
)
|
||||
|
||||
def list_actors(
|
||||
:Person,
|
||||
preferred_username,
|
||||
name,
|
||||
domain,
|
||||
local,
|
||||
suspended,
|
||||
page,
|
||||
limit
|
||||
) do
|
||||
person_query()
|
||||
|> filter_suspended(suspended)
|
||||
|> filter_preferred_username(preferred_username)
|
||||
|> filter_name(name)
|
||||
|> filter_domain(domain)
|
||||
|> filter_remote(local)
|
||||
|> Page.build_page(page, limit)
|
||||
end
|
||||
|
||||
defp filter_preferred_username(query, ""), do: query
|
||||
|
||||
defp filter_preferred_username(query, preferred_username),
|
||||
do: where(query, [a], ilike(a.preferred_username, ^"%#{preferred_username}%"))
|
||||
|
||||
defp filter_name(query, ""), do: query
|
||||
|
||||
defp filter_name(query, name),
|
||||
do: where(query, [a], ilike(a.name, ^"%#{name}%"))
|
||||
|
||||
defp filter_domain(query, ""), do: query
|
||||
|
||||
defp filter_domain(query, domain),
|
||||
do: where(query, [a], ilike(a.domain, ^"%#{domain}%"))
|
||||
|
||||
defp filter_remote(query, true), do: filter_local(query)
|
||||
defp filter_remote(query, false), do: filter_external(query)
|
||||
|
||||
defp filter_suspended(query, true), do: where(query, [a], a.suspended)
|
||||
defp filter_suspended(query, false), do: where(query, [a], not a.suspended)
|
||||
|
||||
@doc """
|
||||
Returns the list of local actors by their username.
|
||||
@@ -945,13 +1016,19 @@ defmodule Mobilizon.Actors do
|
||||
changeset
|
||||
end
|
||||
|
||||
@spec actor_with_preload_query(integer | String.t()) :: Ecto.Query.t()
|
||||
defp actor_with_preload_query(actor_id) do
|
||||
from(
|
||||
a in Actor,
|
||||
where: a.id == ^actor_id and not a.suspended,
|
||||
preload: [:organized_events, :followers, :followings]
|
||||
)
|
||||
@spec actor_with_preload_query(integer | String.t(), boolean()) :: Ecto.Query.t()
|
||||
defp actor_with_preload_query(actor_id, include_suspended \\ false)
|
||||
|
||||
defp actor_with_preload_query(actor_id, false) do
|
||||
actor_id
|
||||
|> actor_with_preload_query(true)
|
||||
|> where([a], not a.suspended)
|
||||
end
|
||||
|
||||
defp actor_with_preload_query(actor_id, true) do
|
||||
Actor
|
||||
|> where([a], a.id == ^actor_id)
|
||||
|> preload([a], [:organized_events, :followers, :followings])
|
||||
end
|
||||
|
||||
@spec actor_by_username_query(String.t()) :: Ecto.Query.t()
|
||||
@@ -1000,6 +1077,11 @@ defmodule Mobilizon.Actors do
|
||||
)
|
||||
end
|
||||
|
||||
@spec person_query :: Ecto.Query.t()
|
||||
defp person_query do
|
||||
from(a in Actor, where: a.type == ^:Person)
|
||||
end
|
||||
|
||||
@spec group_query :: Ecto.Query.t()
|
||||
defp group_query do
|
||||
from(a in Actor, where: a.type == ^:Group)
|
||||
|
||||
@@ -16,7 +16,9 @@ defmodule Mobilizon.Admin do
|
||||
defenum(ActionLogAction, [
|
||||
"update",
|
||||
"create",
|
||||
"delete"
|
||||
"delete",
|
||||
"suspend",
|
||||
"unsuspend"
|
||||
])
|
||||
|
||||
alias Ecto.Multi
|
||||
|
||||
@@ -401,6 +401,14 @@ defmodule Mobilizon.Events do
|
||||
{:ok, events, events_count}
|
||||
end
|
||||
|
||||
@spec list_organized_events_for_actor(Actor.t(), integer | nil, integer | nil) :: Page.t()
|
||||
def list_organized_events_for_actor(%Actor{id: actor_id}, page \\ nil, limit \\ nil) do
|
||||
actor_id
|
||||
|> event_for_actor_query()
|
||||
|> preload_for_event()
|
||||
|> Page.build_page(page, limit)
|
||||
end
|
||||
|
||||
@spec list_organized_events_for_group(Actor.t(), integer | nil, integer | nil) :: Page.t()
|
||||
def list_organized_events_for_group(%Actor{id: group_id}, page \\ nil, limit \\ nil) do
|
||||
group_id
|
||||
@@ -842,13 +850,11 @@ defmodule Mobilizon.Events do
|
||||
@doc """
|
||||
Returns the list of participations for an actor.
|
||||
"""
|
||||
@spec list_event_participations_for_actor(Actor.t(), integer | nil, integer | nil) ::
|
||||
[Participant.t()]
|
||||
@spec list_event_participations_for_actor(Actor.t(), integer | nil, integer | nil) :: Page.t()
|
||||
def list_event_participations_for_actor(%Actor{id: actor_id}, page \\ nil, limit \\ nil) do
|
||||
actor_id
|
||||
|> event_participations_for_actor_query()
|
||||
|> Page.paginate(page, limit)
|
||||
|> Repo.all()
|
||||
|> Page.build_page(page, limit)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@@ -1414,7 +1420,8 @@ defmodule Mobilizon.Events do
|
||||
join: e in Event,
|
||||
on: p.event_id == e.id,
|
||||
where: p.actor_id == ^actor_id and p.role != ^:not_approved,
|
||||
preload: [:event]
|
||||
preload: [:event],
|
||||
order_by: [desc: e.begins_on]
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ defmodule Mobilizon.Media.File do
|
||||
@optional_attrs [:content_type, :size]
|
||||
@attrs @required_attrs ++ @optional_attrs
|
||||
|
||||
@derive Jason.Encoder
|
||||
embedded_schema do
|
||||
field(:name, :string)
|
||||
field(:url, :string)
|
||||
|
||||
@@ -25,6 +25,7 @@ defmodule Mobilizon.Users.User do
|
||||
reset_password_token: String.t(),
|
||||
locale: String.t(),
|
||||
default_actor: Actor.t(),
|
||||
disabled: boolean(),
|
||||
actors: [Actor.t()],
|
||||
feed_tokens: [FeedToken.t()]
|
||||
}
|
||||
@@ -40,7 +41,8 @@ defmodule Mobilizon.Users.User do
|
||||
:reset_password_sent_at,
|
||||
:reset_password_token,
|
||||
:locale,
|
||||
:unconfirmed_email
|
||||
:unconfirmed_email,
|
||||
:disabled
|
||||
]
|
||||
@attrs @required_attrs ++ @optional_attrs
|
||||
|
||||
@@ -64,6 +66,7 @@ defmodule Mobilizon.Users.User do
|
||||
field(:reset_password_token, :string)
|
||||
field(:unconfirmed_email, :string)
|
||||
field(:locale, :string, default: "en")
|
||||
field(:disabled, :boolean, default: false)
|
||||
|
||||
belongs_to(:default_actor, Actor)
|
||||
has_many(:actors, Actor)
|
||||
@@ -91,6 +94,13 @@ defmodule Mobilizon.Users.User do
|
||||
end
|
||||
end
|
||||
|
||||
def delete_changeset(%__MODULE__{} = user) do
|
||||
user
|
||||
|> change()
|
||||
|> put_change(:disabled, true)
|
||||
|> put_change(:default_actor_id, nil)
|
||||
end
|
||||
|
||||
@doc false
|
||||
@spec registration_changeset(t, map) :: Ecto.Changeset.t()
|
||||
def registration_changeset(%__MODULE__{} = user, attrs) do
|
||||
|
||||
@@ -8,8 +8,10 @@ defmodule Mobilizon.Users do
|
||||
|
||||
import Mobilizon.Storage.Ecto
|
||||
|
||||
alias Ecto.Multi
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Events
|
||||
alias Mobilizon.Events.FeedToken
|
||||
alias Mobilizon.Storage.{Page, Repo}
|
||||
alias Mobilizon.Users.{Setting, User}
|
||||
|
||||
@@ -46,7 +48,8 @@ defmodule Mobilizon.Users do
|
||||
@spec get_user!(integer | String.t()) :: User.t()
|
||||
def get_user!(id), do: Repo.get!(User, id)
|
||||
|
||||
@spec get_user(integer | String.t()) :: User.t() | nil
|
||||
@spec get_user(integer | String.t() | nil) :: User.t() | nil
|
||||
def get_user(nil), do: nil
|
||||
def get_user(id), do: Repo.get(User, id)
|
||||
|
||||
def get_user_with_settings!(id) do
|
||||
@@ -105,11 +108,35 @@ defmodule Mobilizon.Users do
|
||||
end
|
||||
end
|
||||
|
||||
@delete_user_default_options [reserve_email: true]
|
||||
|
||||
@doc """
|
||||
Deletes an user.
|
||||
"""
|
||||
@spec delete_user(User.t()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||
def delete_user(%User{} = user), do: Repo.delete(user)
|
||||
def delete_user(%User{id: user_id} = user, options \\ @delete_user_default_options) do
|
||||
delete_user_options = Keyword.merge(@delete_user_default_options, options)
|
||||
|
||||
multi =
|
||||
Multi.new()
|
||||
|> Multi.delete_all(:settings, from(s in Setting, where: s.user_id == ^user_id))
|
||||
|> Multi.delete_all(:feed_tokens, from(f in FeedToken, where: f.user_id == ^user_id))
|
||||
|
||||
multi =
|
||||
if Keyword.get(delete_user_options, :reserve_email, true) do
|
||||
Multi.update(multi, :user, User.delete_changeset(user))
|
||||
else
|
||||
Multi.delete(multi, :user, user)
|
||||
end
|
||||
|
||||
case Repo.transaction(multi) do
|
||||
{:ok, %{user: %User{} = user}} ->
|
||||
{:ok, user}
|
||||
|
||||
{:error, remove, error, _} when remove in [:settings, :feed_tokens] ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Get an user with its actors
|
||||
@@ -196,12 +223,22 @@ defmodule Mobilizon.Users do
|
||||
@doc """
|
||||
Returns the list of users.
|
||||
"""
|
||||
@spec list_users(integer | nil, integer | nil, atom | nil, atom | nil) :: [User.t()]
|
||||
def list_users(page \\ nil, limit \\ nil, sort \\ nil, direction \\ nil) do
|
||||
@spec list_users(String.t(), integer | nil, integer | nil, atom | nil, atom | nil) :: Page.t()
|
||||
def list_users(email \\ "", page \\ nil, limit \\ nil, sort \\ nil, direction \\ nil)
|
||||
|
||||
def list_users("", page, limit, sort, direction) do
|
||||
User
|
||||
|> Page.paginate(page, limit)
|
||||
|> sort(sort, direction)
|
||||
|> Repo.all()
|
||||
|> preload([u], [:actors, :feed_tokens, :settings, :default_actor])
|
||||
|> Page.build_page(page, limit)
|
||||
end
|
||||
|
||||
def list_users(email, page, limit, sort, direction) do
|
||||
User
|
||||
|> where([u], ilike(u.email, ^"%#{email}%"))
|
||||
|> sort(sort, direction)
|
||||
|> preload([u], [:actors, :feed_tokens, :settings, :default_actor])
|
||||
|> Page.build_page(page, limit)
|
||||
end
|
||||
|
||||
@doc """
|
||||
|
||||
Reference in New Issue
Block a user