Introduce group posts

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2020-07-09 17:24:28 +02:00
parent bec1c69d4b
commit 9c9f1385fb
249 changed files with 11886 additions and 5023 deletions

View File

@@ -55,6 +55,8 @@ defmodule Mobilizon.Actors do
@public_visibility [:public, :unlisted]
@administrator_roles [:creator, :administrator]
@moderator_roles [:moderator] ++ @administrator_roles
@member_roles [:member] ++ @moderator_roles
@actor_preloads [:user, :organized_events, :comments]
@doc """
@@ -118,6 +120,17 @@ defmodule Mobilizon.Actors do
end
end
@doc """
New function to replace `Mobilizon.Actors.get_actor_by_url/1` with
better signature
"""
@spec get_actor_by_url_2(String.t(), boolean) :: Actor.t() | nil
def get_actor_by_url_2(url, preload \\ false) do
Actor
|> Repo.get_by(url: url)
|> preload_followers(preload)
end
@doc """
Gets an actor by its URL (ActivityPub ID). The `:preload` option allows to
preload the followers relation.
@@ -181,9 +194,17 @@ defmodule Mobilizon.Actors do
"""
@spec create_actor(map) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()}
def create_actor(attrs \\ %{}) do
%Actor{}
|> Actor.changeset(attrs)
|> Repo.insert()
type = Map.get(attrs, :type, :Person)
case type do
:Person ->
%Actor{}
|> Actor.changeset(attrs)
|> Repo.insert()
:Group ->
create_group(attrs)
end
end
@doc """
@@ -238,7 +259,8 @@ defmodule Mobilizon.Actors do
name: name,
summary: summary,
avatar: transform_media_file(avatar),
banner: transform_media_file(banner)
banner: transform_media_file(banner),
last_refreshed_at: DateTime.utc_now()
]
],
conflict_target: [:url]
@@ -285,6 +307,7 @@ defmodule Mobilizon.Actors do
"""
@spec perform(atom(), Actor.t()) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()}
def perform(:delete_actor, %Actor{} = actor, options \\ @delete_actor_default_options) do
Logger.info("Going to delete actor #{actor.url}")
actor = Repo.preload(actor, @actor_preloads)
delete_actor_options = Keyword.merge(@delete_actor_default_options, options)
@@ -306,10 +329,18 @@ defmodule Mobilizon.Actors do
case Repo.transaction(multi) do
{:ok, %{actor: %Actor{} = actor}} ->
{:ok, true} = Cachex.del(:activity_pub, "actor_#{actor.preferred_username}")
Logger.info("Deleted actor #{actor.url}")
{:ok, actor}
{:error, remove, error, _} when remove in [:remove_banner, :remove_avatar] ->
Logger.error("Error while deleting actor's banner or avatar")
Logger.error(inspect(error, pretty: true))
{:error, error}
err ->
Logger.error("Unknown error while deleting actor")
Logger.error(inspect(err, pretty: true))
{:error, err}
end
end
@@ -438,23 +469,47 @@ defmodule Mobilizon.Actors do
end
end
@spec get_local_group_by_url(String.t()) :: Actor.t()
def get_local_group_by_url(group_url) do
group_query()
|> where([q], q.url == ^group_url and is_nil(q.domain))
|> Repo.one()
end
@spec get_group_by_members_url(String.t()) :: Actor.t()
def get_group_by_members_url(members_url) do
group_query()
|> where([q], q.members_url == ^members_url)
|> Repo.one()
end
@doc """
Creates a group.
If the group is local, creates an admin actor as well from `creator_actor_id`.
"""
@spec create_group(map) :: {:ok, Actor.t()} | {:error, Ecto.Changeset.t()}
def create_group(attrs \\ %{}) do
with {:ok, %{insert_group: %Actor{} = group, add_admin_member: %Member{} = _admin_member}} <-
Multi.new()
|> Multi.insert(:insert_group, Actor.group_creation_changeset(%Actor{}, attrs))
|> Multi.insert(:add_admin_member, fn %{insert_group: group} ->
Member.changeset(%Member{}, %{
parent_id: group.id,
actor_id: attrs.creator_actor_id,
role: :administrator
})
end)
|> Repo.transaction() do
{:ok, group}
local = Map.get(attrs, :local, true)
if local do
with {:ok, %{insert_group: %Actor{} = group, add_admin_member: %Member{} = _admin_member}} <-
Multi.new()
|> Multi.insert(:insert_group, Actor.group_creation_changeset(%Actor{}, attrs))
|> Multi.insert(:add_admin_member, fn %{insert_group: group} ->
Member.changeset(%Member{}, %{
parent_id: group.id,
actor_id: attrs.creator_actor_id,
role: :administrator
})
end)
|> Repo.transaction() do
{:ok, group}
end
else
%Actor{}
|> Actor.group_creation_changeset(attrs)
|> Repo.insert()
end
end
@@ -532,12 +587,7 @@ defmodule Mobilizon.Actors do
def is_member?(actor_id, parent_id) do
match?(
{:ok, %Member{}},
get_member(actor_id, parent_id, [
:member,
:moderator,
:administrator,
:creator
])
get_member(actor_id, parent_id, @member_roles)
)
end
@@ -552,6 +602,20 @@ defmodule Mobilizon.Actors do
|> Repo.one()
end
@spec get_single_group_member_actor(integer() | String.t()) :: Actor.t() | nil
def get_single_group_member_actor(group_id) do
Member
|> where(
[m],
m.parent_id == ^group_id and m.role in [^:member, ^:moderator, ^:administrator, ^:creator]
)
|> join(:inner, [m], a in Actor, on: m.actor_id == a.id)
|> where([_m, a], is_nil(a.domain))
|> limit(1)
|> select([_m, a], a)
|> Repo.one()
end
@doc """
Creates a member.
"""
@@ -616,25 +680,26 @@ defmodule Mobilizon.Actors do
@doc """
Returns the list of members for a group.
"""
@spec list_members_for_group(Actor.t(), integer | nil, integer | nil) :: Page.t()
def list_members_for_group(%Actor{id: group_id, type: :Group}, page \\ nil, limit \\ nil) do
group_id
|> members_for_group_query()
|> Page.build_page(page, limit)
end
@spec list_external_members_for_group(Actor.t(), integer | nil, integer | nil) :: Page.t()
def list_external_members_for_group(
@spec list_members_for_group(Actor.t(), list(atom()), integer | nil, integer | nil) :: Page.t()
def list_members_for_group(
%Actor{id: group_id, type: :Group},
roles \\ [],
page \\ nil,
limit \\ nil
) do
group_id
|> members_for_group_query()
|> filter_external()
|> filter_member_role(roles)
|> Page.build_page(page, limit)
end
@spec list_external_actors_members_for_group(Actor.t()) :: list(Actor.t())
def list_external_actors_members_for_group(%Actor{id: group_id, type: :Group}) do
group_id
|> group_external_member_actor_query()
|> Repo.all()
end
@doc """
Returns the list of administrator members for a group.
"""
@@ -1141,6 +1206,26 @@ defmodule Mobilizon.Actors do
)
end
@spec group_external_member_actor_query(integer()) :: Ecto.Query.t()
defp group_external_member_actor_query(group_id) do
Member
|> where([m], m.parent_id == ^group_id)
|> join(:inner, [m], a in Actor, on: m.actor_id == a.id)
|> where([_m, a], not is_nil(a.domain))
|> select([_m, a], a)
end
@spec filter_member_role(Ecto.Query.t(), list(atom()) | atom()) :: Ecto.Query.t()
def filter_member_role(query, []), do: query
def filter_member_role(query, roles) when is_list(roles) do
where(query, [m], m.role in ^roles)
end
def filter_member_role(query, role) when is_atom(role) do
from(m in query, where: m.role == ^role)
end
@spec administrator_members_for_group_query(integer | String.t()) :: Ecto.Query.t()
defp administrator_members_for_group_query(group_id) do
from(
@@ -1296,13 +1381,22 @@ defmodule Mobilizon.Actors do
defp preload_followers(actor, true), do: Repo.preload(actor, [:followers])
defp preload_followers(actor, false), do: actor
defp delete_actor_organized_events(%Actor{organized_events: organized_events}) do
defp delete_actor_organized_events(%Actor{organized_events: organized_events} = actor) do
res =
Enum.map(organized_events, fn event ->
event =
Repo.preload(event, [:organizer_actor, :participants, :picture, :mentions, :comments])
Repo.preload(event, [
:organizer_actor,
:participants,
:picture,
:mentions,
:comments,
:attributed_to,
:tags,
:physical_address
])
ActivityPub.delete(event, false)
ActivityPub.delete(event, actor, false)
end)
if Enum.all?(res, fn {status, _, _} -> status == :ok end) do
@@ -1312,13 +1406,21 @@ defmodule Mobilizon.Actors do
end
end
defp delete_actor_empty_comments(%Actor{comments: comments}) do
defp delete_actor_empty_comments(%Actor{comments: comments} = actor) do
res =
Enum.map(comments, fn comment ->
comment =
Repo.preload(comment, [:actor, :mentions, :event, :in_reply_to_comment, :origin_comment])
Repo.preload(comment, [
:actor,
:mentions,
:event,
:in_reply_to_comment,
:origin_comment,
:attributed_to,
:tags
])
ActivityPub.delete(comment, false)
ActivityPub.delete(comment, actor, false)
end)
if Enum.all?(res, fn {status, _, _} -> status == :ok end) do