Introduce group basic federation, event new page and notifications

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel
2020-02-18 08:57:00 +01:00
parent 300ef8f245
commit 4144e9ffd0
416 changed files with 32220 additions and 16750 deletions

View File

@@ -8,16 +8,18 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
A module to handle coding from internal to wire ActivityPub and back.
"""
alias Mobilizon.Actors
alias Mobilizon.Actors.{Actor, Follower}
alias Mobilizon.Events
alias Mobilizon.Events.{Comment, Event, Participant}
alias Mobilizon.{Actors, Conversations, Events, Resources, Todos}
alias Mobilizon.Actors.{Actor, Follower, Member}
alias Mobilizon.Conversations.Comment
alias Mobilizon.Events.{Event, Participant}
alias Mobilizon.Resources.Resource
alias Mobilizon.Todos.{Todo, TodoList}
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.{Activity, Utils}
alias Mobilizon.Federation.ActivityStream.{Converter, Convertible}
alias Mobilizon.Web.Email.Participation
alias Mobilizon.Web.Email.{Group, Participation}
require Logger
@@ -57,10 +59,10 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
def handle_incoming(%{"type" => "Create", "object" => %{"type" => "Note"} = object}) do
Logger.info("Handle incoming to create notes")
with {:ok, object_data} <-
with object_data <-
object |> Converter.Comment.as_to_model_data(),
{:existing_comment, {:error, :comment_not_found}} <-
{:existing_comment, Events.get_comment_from_url_with_preload(object_data.url)},
{:existing_comment, Conversations.get_comment_from_url_with_preload(object_data.url)},
{:ok, %Activity{} = activity, %Comment{} = comment} <-
ActivityPub.create(:comment, object_data, false) do
{:ok, activity, comment}
@@ -85,7 +87,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
def handle_incoming(%{"type" => "Create", "object" => %{"type" => "Event"} = object}) do
Logger.info("Handle incoming to create event")
with {:ok, object_data} <-
with object_data <-
object |> Converter.Event.as_to_model_data(),
{:existing_event, nil} <- {:existing_event, Events.get_event_by_url(object_data.url)},
{:ok, %Activity{} = activity, %Event{} = event} <-
@@ -110,6 +112,86 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
end
end
def handle_incoming(%{
"type" => "Create",
"object" => %{"type" => "TodoList", "id" => object_url} = object,
"actor" => actor_url
}) do
Logger.info("Handle incoming to create a todo list")
with {:existing_todo_list, nil} <-
{:existing_todo_list, Todos.get_todo_list_by_url(object_url)},
{:ok, %Actor{url: actor_url}} <- ActivityPub.get_or_fetch_actor_by_url(actor_url),
object_data when is_map(object_data) <-
object |> Converter.TodoList.as_to_model_data(),
{:ok, %Activity{} = activity, %TodoList{} = todo_list} <-
ActivityPub.create(:todo_list, object_data, false, %{"actor" => actor_url}) do
{:ok, activity, todo_list}
else
{:error, :group_not_found} -> :error
{:existing_todo_list, %TodoList{} = todo_list} -> {:ok, nil, todo_list}
end
end
def handle_incoming(%{
"type" => "Create",
"object" => %{"type" => "Todo", "id" => object_url} = object
}) do
Logger.info("Handle incoming to create a todo")
with {:existing_todo, nil} <-
{:existing_todo, Todos.get_todo_by_url(object_url)},
object_data <-
object |> Converter.Todo.as_to_model_data(),
{:ok, %Activity{} = activity, %Todo{} = todo} <-
ActivityPub.create(:todo, object_data, false) do
{:ok, activity, todo}
else
{:existing_todo, %Todo{} = todo} -> {:ok, nil, todo}
end
end
def handle_incoming(
%{
"type" => activity_type,
"object" => %{"type" => object_type, "id" => object_url} = object,
"to" => to
} = data
)
when activity_type in ["Create", "Add"] and
object_type in ["Document", "ResourceCollection"] do
Logger.info("Handle incoming to create a resource")
Logger.debug(inspect(data))
group_url = hd(to)
with {:existing_resource, nil} <-
{:existing_resource, Resources.get_resource_by_url(object_url)},
object_data when is_map(object_data) <-
object |> Converter.Resource.as_to_model_data(),
{:member, true} <-
{:member, Actors.is_member?(object_data.creator_id, object_data.actor_id)},
{:ok, %Activity{} = activity, %Resource{} = resource} <-
ActivityPub.create(:resource, object_data, false),
{:ok, %Actor{type: :Group, id: group_id} = group} <- Actors.get_actor_by_url(group_url),
announce_id <- "#{object_url}/announces/#{group_id}",
{:ok, _activity, _resource} <- ActivityPub.announce(group, object, announce_id) do
{:ok, activity, resource}
else
{:existing_resource, %Resource{} = resource} ->
{:ok, nil, resource}
{:member, false} ->
# At some point this should refresh the list of group members
# if the group is not local before simply returning an error
:error
{:error, e} ->
Logger.error(inspect(e))
:error
end
end
def handle_incoming(
%{
"type" => "Accept",
@@ -205,7 +287,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
})
when object_type in ["Person", "Group", "Application", "Service", "Organization"] do
with {:ok, %Actor{} = old_actor} <- Actors.get_actor_by_url(object["id"]),
{:ok, object_data} <-
object_data <-
object |> Converter.Actor.as_to_model_data(),
{:ok, %Activity{} = activity, %Actor{} = new_actor} <-
ActivityPub.update(:actor, old_actor, object_data, false) do
@@ -225,7 +307,7 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
{:ok, %Actor{url: actor_url}} <- Actors.get_actor_by_url(actor),
{:ok, %Event{} = old_event} <-
object |> Utils.get_url() |> ActivityPub.fetch_object_from_url(),
{:ok, object_data} <- Converter.Event.as_to_model_data(object),
object_data <- Converter.Event.as_to_model_data(object),
{:origin_check, true} <- {:origin_check, Utils.origin_check?(actor_url, update_data)},
{:ok, %Activity{} = activity, %Event{} = new_event} <-
ActivityPub.update(:event, old_event, object_data, false) do
@@ -350,6 +432,26 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
end
end
def handle_incoming(
%{
"type" => "Invite",
"object" => object,
"actor" => _actor,
"id" => id,
"target" => target
} = data
) do
with {:ok, %Actor{} = actor} <- data |> Utils.get_actor() |> Actors.get_actor_by_url(),
{:ok, object} <- object |> Utils.get_url() |> ActivityPub.fetch_object_from_url(),
{:ok, %Actor{} = target} <-
target |> Utils.get_url() |> ActivityPub.get_or_fetch_actor_by_url(),
{:ok, activity, %Member{} = member} <-
ActivityPub.invite(object, actor, target, false, %{url: id}),
:ok <- Group.send_invite_to_user(member) do
{:ok, activity, member}
end
end
#
# # TODO
# # Accept
@@ -373,8 +475,9 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
# end
# end
def handle_incoming(_) do
def handle_incoming(object) do
Logger.info("Handing something not supported")
Logger.debug(inspect(object))
{:error, :not_supported}
end
@@ -436,12 +539,37 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
# Handle incoming `Accept` activities wrapping a `Join` activity on an event
defp do_handle_incoming_accept_join(join_object, %Actor{} = actor_accepting) do
with {:join_event, {:ok, %Participant{role: role, event: event} = participant}}
when role in [:not_approved, :rejected] <-
{:join_event, get_participant(join_object)},
# TODO: The actor that accepts the Join activity may another one that the event organizer ?
# Or maybe for groups it's the group that sends the Accept activity
{:same_actor, true} <- {:same_actor, actor_accepting.id == event.organizer_actor_id},
case get_participant(join_object) do
{:ok, participant} ->
do_handle_incoming_accept_join_event(participant, actor_accepting)
{:error, _err} ->
case get_member(join_object) do
{:ok, member} ->
do_handle_incoming_accept_join_group(member, actor_accepting)
{:error, _err} ->
nil
end
end
end
defp do_handle_incoming_accept_join_event(%Participant{role: :participant}, _actor) do
Logger.debug(
"Tried to handle an Accept activity on a Join activity with a event object but the participant is already validated"
)
nil
end
defp do_handle_incoming_accept_join_event(
%Participant{role: role, event: event} = participant,
%Actor{} = actor_accepting
)
when role in [:not_approved, :rejected] do
# TODO: The actor that accepts the Join activity may another one that the event organizer ?
# Or maybe for groups it's the group that sends the Accept activity
with {:same_actor, true} <- {:same_actor, actor_accepting.id == event.organizer_actor_id},
{:ok, %Activity{} = activity, %Participant{role: :participant} = participant} <-
ActivityPub.accept(
:join,
@@ -452,20 +580,6 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
Participation.send_emails_to_local_user(participant) do
{:ok, activity, participant}
else
{:join_event, {:ok, %Participant{role: :participant}}} ->
Logger.debug(
"Tried to handle an Accept activity on a Join activity with a event object but the participant is already validated"
)
nil
{:join_event, _err} ->
Logger.debug(
"Tried to handle an Accept activity but it's not containing a Join activity on a event"
)
nil
{:same_actor} ->
{:error, "Actor who accepted the join wasn't the event organizer. Quite odd."}
@@ -474,6 +588,31 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
end
end
defp do_handle_incoming_accept_join_group(%Member{role: :member}, _actor) do
Logger.debug(
"Tried to handle an Accept activity on a Join activity with a group object but the member is already validated"
)
nil
end
defp do_handle_incoming_accept_join_group(
%Member{role: role, parent: _group} = member,
%Actor{} = _actor_accepting
)
when role in [:not_approved, :rejected, :invited] do
# TODO: The actor that accepts the Join activity may another one that the event organizer ?
# Or maybe for groups it's the group that sends the Accept activity
with {:ok, %Activity{} = activity, %Member{role: :member} = member} <-
ActivityPub.accept(
:invite,
member,
false
) do
{:ok, activity, member}
end
end
# Handle incoming `Reject` activities wrapping a `Join` activity on an event
defp do_handle_incoming_reject_join(join_object, %Actor{} = actor_accepting) do
with {:join_event, {:ok, %Participant{event: event, role: role} = participant}}
@@ -509,8 +648,6 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
end
end
# TODO: Add do_handle_incoming_accept_join/1 on Groups
defp get_follow(follow_object) do
with follow_object_id when not is_nil(follow_object_id) <- Utils.get_url(follow_object),
{:not_found, %Follower{} = follow} <-
@@ -539,6 +676,21 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do
end
end
@spec get_member(String.t() | map()) :: {:ok, Member.t()} | {:error, String.t()}
defp get_member(member_object) do
with member_object_id when not is_nil(member_object_id) <- Utils.get_url(member_object),
%Member{} = member <-
Actors.get_member_by_url(member_object_id) do
{:ok, member}
else
{:error, :member_not_found} ->
{:error, "Member URL not found"}
_ ->
{:error, "ActivityPub ID not found in Accept Join object"}
end
end
def prepare_outgoing(%{"type" => _type} = data) do
data =
data