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

@@ -25,7 +25,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Actor do
Converts an AP object data to our internal data structure.
"""
@impl Converter
@spec as_to_model_data(map) :: map
@spec as_to_model_data(map()) :: {:ok, map()}
def as_to_model_data(data) do
avatar =
data["icon"]["url"] &&
@@ -41,7 +41,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Actor do
"url" => MediaProxy.url(data["image"]["url"])
}
actor_data = %{
%{
url: data["id"],
avatar: avatar,
banner: banner,
@@ -53,20 +53,20 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Actor do
outbox_url: data["outbox"],
following_url: data["following"],
followers_url: data["followers"],
members_url: data["members"],
resources_url: data["resources"],
shared_inbox_url: data["endpoints"]["sharedInbox"],
domain: URI.parse(data["id"]).host,
manually_approves_followers: data["manuallyApprovesFollowers"],
type: data["type"]
}
{:ok, actor_data}
end
@doc """
Convert an actor struct to an ActivityStream representation.
"""
@impl Converter
@spec model_to_as(ActorModel.t()) :: map
@spec model_to_as(ActorModel.t()) :: map()
def model_to_as(%ActorModel{} = actor) do
actor_data = %{
"id" => actor.url,
@@ -76,6 +76,9 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Actor do
"summary" => actor.summary,
"following" => actor.following_url,
"followers" => actor.followers_url,
"members" => actor.members_url,
"resources" => actor.resources_url,
"todos" => actor.todos_url,
"inbox" => actor.inbox_url,
"outbox" => actor.outbox_url,
"url" => actor.url,
@@ -94,6 +97,13 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Actor do
}
}
actor_data =
if actor.type == :Group do
Map.put(actor_data, "members", actor.members_url)
else
actor_data
end
actor_data =
if is_nil(actor.avatar) do
actor_data

View File

@@ -7,7 +7,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
"""
alias Mobilizon.Actors.Actor
alias Mobilizon.Events.Comment, as: CommentModel
alias Mobilizon.Conversations.Comment, as: CommentModel
alias Mobilizon.Events.Event
alias Mobilizon.Tombstone, as: TombstoneModel
@@ -60,42 +60,36 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Comment do
# We fetch the parent object
Logger.debug("We're fetching the parent object")
data =
if Map.has_key?(object, "inReplyTo") && object["inReplyTo"] != nil &&
object["inReplyTo"] != "" do
Logger.debug(fn -> "Object has inReplyTo #{object["inReplyTo"]}" end)
if Map.has_key?(object, "inReplyTo") && object["inReplyTo"] != nil &&
object["inReplyTo"] != "" do
Logger.debug(fn -> "Object has inReplyTo #{object["inReplyTo"]}" end)
case ActivityPub.fetch_object_from_url(object["inReplyTo"]) do
# Reply to an event (Event)
{:ok, %Event{id: id}} ->
Logger.debug("Parent object is an event")
data |> Map.put(:event_id, id)
case ActivityPub.fetch_object_from_url(object["inReplyTo"]) do
# Reply to an event (Event)
{:ok, %Event{id: id}} ->
Logger.debug("Parent object is an event")
data |> Map.put(:event_id, id)
# Reply to a comment (Comment)
{:ok, %CommentModel{id: id} = comment} ->
Logger.debug("Parent object is another comment")
# Reply to a comment (Comment)
{:ok, %CommentModel{id: id} = comment} ->
Logger.debug("Parent object is another comment")
data
|> Map.put(:in_reply_to_comment_id, id)
|> Map.put(:origin_comment_id, comment |> CommentModel.get_thread_id())
|> Map.put(:event_id, comment.event_id)
data
|> Map.put(:in_reply_to_comment_id, id)
|> Map.put(:origin_comment_id, comment |> CommentModel.get_thread_id())
|> Map.put(:event_id, comment.event_id)
# Anything else is kind of a MP
{:error, parent} ->
Logger.warn("Parent object is something we don't handle")
Logger.debug(inspect(parent))
data
end
else
Logger.debug("No parent object for this comment")
data
# Anything else is kind of a MP
{:error, parent} ->
Logger.warn("Parent object is something we don't handle")
Logger.debug(inspect(parent))
data
end
else
Logger.debug("No parent object for this comment")
{:ok, data}
else
err ->
{:error, err}
data
end
end
end

View File

@@ -63,7 +63,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
nil
end
entity = %{
%{
title: object["name"],
description: object["content"],
organizer_actor_id: actor_id,
@@ -87,11 +87,6 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
updated_at: object["updated"],
publish_at: object["published"]
}
{:ok, entity}
else
error ->
{:error, error}
end
end

View File

@@ -10,6 +10,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Flag do
alias Mobilizon.Actors
alias Mobilizon.Actors.Actor
alias Mobilizon.Conversations
alias Mobilizon.Events
alias Mobilizon.Events.Event
alias Mobilizon.Reports.Report
@@ -91,7 +92,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Flag do
Enum.filter(objects, fn url ->
!(url == reported.url || (!is_nil(event) && event.url == url))
end),
comments <- Enum.map(comments, &Events.get_comment_from_url/1) do
comments <- Enum.map(comments, &Conversations.get_comment_from_url/1) do
%{
"reporter" => reporter,
"uri" => object["id"],

View File

@@ -0,0 +1,57 @@
defmodule Mobilizon.Federation.ActivityStream.Converter.Member do
@moduledoc """
Member converter.
This module allows to convert members from ActivityStream format to our own
internal one, and back.
"""
alias Mobilizon.Actors.Actor
alias Mobilizon.Actors.Member, as: MemberModel
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.Utils
alias Mobilizon.Federation.ActivityStream.Convertible
defimpl Convertible, for: MemberModel do
alias Mobilizon.Federation.ActivityStream.Converter.Member, as: MemberConverter
defdelegate model_to_as(member), to: MemberConverter
end
@doc """
Convert an event struct to an ActivityStream representation.
"""
@spec model_to_as(MemberModel.t()) :: map
def model_to_as(%MemberModel{} = member) do
%{
"type" => "Member",
"id" => member.url,
"actor" => member.actor.url,
"object" => member.parent.url,
"role" => member.role
}
end
def as_to_model_data(%{
"type" => "Member",
"actor" => actor,
"object" => group,
"role" => role,
"id" => url
}) do
with {:ok, %Actor{id: group_id}} <- get_actor(group),
{:ok, %Actor{id: actor_id}} <- get_actor(actor) do
%{
url: url,
actor_id: actor_id,
parent_id: group_id,
role: role
}
end
end
@spec get_actor(String.t() | map() | nil) :: {:ok, Actor.t()} | {:error, String.t()}
defp get_actor(nil), do: {:error, "nil property found for actor data"}
defp get_actor(actor), do: actor |> Utils.get_url() |> ActivityPub.get_or_fetch_actor_by_url()
end

View File

@@ -11,6 +11,10 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Picture do
alias Mobilizon.Web.Upload
@http_options [
ssl: [{:versions, [:"tlsv1.2"]}]
]
@doc """
Convert a picture struct to an ActivityStream representation.
"""
@@ -35,7 +39,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Picture do
actor_id
)
when is_bitstring(picture_url) do
with {:ok, %HTTPoison.Response{body: body}} <- HTTPoison.get(picture_url),
with {:ok, %HTTPoison.Response{body: body}} <- HTTPoison.get(picture_url, [], @http_options),
{:ok, %{name: name, url: url, content_type: content_type, size: size}} <-
Upload.store(%{body: body, name: name}),
{:picture_exists, nil} <- {:picture_exists, Media.get_picture_by_url(url)} do

View File

@@ -0,0 +1,129 @@
defmodule Mobilizon.Federation.ActivityStream.Converter.Resource do
@moduledoc """
Resource converter.
This module allows to convert resources from ActivityStream format to our own
internal one, and back.
"""
alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.Utils
alias Mobilizon.Federation.ActivityStream.{Converter, Convertible}
alias Mobilizon.Resources
alias Mobilizon.Resources.Resource
require Logger
@behaviour Converter
defimpl Convertible, for: Resource do
alias Mobilizon.Federation.ActivityStream.Converter.Resource, as: ResourceConverter
defdelegate model_to_as(resource), to: ResourceConverter
end
@doc """
Convert an resource struct to an ActivityStream representation
"""
@impl Converter
@spec model_to_as(Resource.t()) :: map
def model_to_as(
%Resource{actor: %Actor{url: actor_url}, creator: %Actor{url: creator_url}, type: type} =
resource
) do
res = %{
"actor" => creator_url,
"id" => resource.url,
"name" => resource.title,
"summary" => resource.summary,
"context" => get_context(resource),
"attributedTo" => actor_url
}
case type do
:folder ->
Map.put(res, "type", "ResourceCollection")
_ ->
res
|> Map.put("type", "Document")
|> Map.put("url", resource.resource_url)
end
end
@doc """
Converts an AP object data to our internal data structure.
"""
@impl Converter
@spec as_to_model_data(map) :: {:ok, map} | {:error, any()}
def as_to_model_data(%{"type" => type, "actor" => creator, "attributedTo" => group} = object) do
with {:ok, %Actor{id: actor_id, resources_url: resources_url}} <- get_actor(group),
{:ok, %Actor{id: creator_id}} <- get_actor(creator),
parent_id <- get_parent_id(object["context"], resources_url) do
data = %{
title: object["name"],
summary: object["summary"],
url: object["id"],
actor_id: actor_id,
creator_id: creator_id,
parent_id: parent_id
}
case type do
"Document" ->
data
|> Map.put(:type, :link)
|> Map.put(:resource_url, object["url"])
"ResourceCollection" ->
data
|> Map.put(:type, :folder)
end
else
{:error, err} -> {:error, err}
err -> {:error, err}
end
end
@spec get_actor(String.t() | map() | nil) :: {:ok, Actor.t()} | {:error, String.t()}
defp get_actor(nil), do: {:error, "nil property found for actor data"}
defp get_actor(actor), do: actor |> Utils.get_url() |> ActivityPub.get_or_fetch_actor_by_url()
defp get_context(%Resource{parent_id: nil, actor: %Actor{resources_url: resources_url}}),
do: resources_url
defp get_context(%Resource{parent: %Resource{url: url}}), do: url
defp get_context(%Resource{parent_id: parent_id}),
do: parent_id |> Resources.get_resource() |> Map.get(:url)
@spec get_parent_id(String.t(), String.t()) :: Resource.t() | map()
defp get_parent_id(context, resources_url) do
Logger.debug(
"Getting parentID for context #{inspect(context)} and with resources_url #{
inspect(resources_url)
}"
)
case Utils.get_url(context) do
nil -> nil
^resources_url -> nil
context_url -> fetch_resource(context_url)
end
end
defp fetch_resource(context_url) do
case Resources.get_resource_by_url(context_url) do
%Resource{id: resource_id} = _resource ->
resource_id
nil ->
case ActivityPub.fetch_object_from_url(context_url) do
{:ok, %Resource{id: resource_id} = _resource} ->
resource_id
_ ->
nil
end
end
end
end

View File

@@ -0,0 +1,70 @@
defmodule Mobilizon.Federation.ActivityStream.Converter.Todo do
@moduledoc """
TodoList converter.
This module allows to convert todo lists from ActivityStream format to our own
internal one, and back.
"""
alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityStream.{Converter, Convertible}
alias Mobilizon.Todos
alias Mobilizon.Todos.{Todo, TodoList}
@behaviour Converter
defimpl Convertible, for: Todo do
alias Mobilizon.Federation.ActivityStream.Converter.Todo, as: TodoConverter
defdelegate model_to_as(todo), to: TodoConverter
end
@doc """
Convert an todo list struct to an ActivityStream representation
"""
@impl Converter
@spec model_to_as(Todo.t()) :: map
def model_to_as(
%Todo{
todo_list: %TodoList{actor: %Actor{url: group_url} = _group, url: todo_list_url},
creator: %Actor{url: creator_url}
} = todo
) do
%{
"type" => "Todo",
"actor" => creator_url,
"attributedTo" => group_url,
"id" => todo.url,
"name" => todo.title,
"status" => todo.status,
"todoList" => todo_list_url
}
end
@doc """
Converts an AP object data to our internal data structure.
"""
@impl Converter
@spec as_to_model_data(map) :: {:ok, map} | {:error, any()}
def as_to_model_data(
%{"type" => "Todo", "actor" => actor_url, "todoList" => todo_list_url} = object
) do
with {:ok, %Actor{id: creator_id} = _creator} <-
ActivityPub.get_or_fetch_actor_by_url(actor_url),
{:todo_list, %TodoList{id: todo_list_id}} <-
{:todo_list, Todos.get_todo_list_by_url(todo_list_url)} do
%{
title: object["name"],
status: object["status"],
url: object["id"],
todo_list_id: todo_list_id,
creator_id: creator_id
}
else
{:todo_list, nil} ->
with {:ok, %TodoList{}} <- ActivityPub.fetch_object_from_url(todo_list_url) do
as_to_model_data(object)
end
end
end
end

View File

@@ -0,0 +1,53 @@
defmodule Mobilizon.Federation.ActivityStream.Converter.TodoList do
@moduledoc """
TodoList converter.
This module allows to convert todo lists from ActivityStream format to our own
internal one, and back.
"""
alias Mobilizon.Actors.Actor
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityStream.{Converter, Convertible}
alias Mobilizon.Todos.TodoList
@behaviour Converter
defimpl Convertible, for: TodoList do
alias Mobilizon.Federation.ActivityStream.Converter.TodoList, as: TodoListConverter
defdelegate model_to_as(report), to: TodoListConverter
end
@doc """
Convert an todo list struct to an ActivityStream representation
"""
@impl Converter
@spec model_to_as(TodoList.t()) :: map
def model_to_as(%TodoList{actor: %Actor{url: group_url} = _group} = todo_list) do
%{
"type" => "TodoList",
"actor" => group_url,
"id" => todo_list.url,
"title" => todo_list.title
}
end
@doc """
Converts an AP object data to our internal data structure.
"""
@impl Converter
@spec as_to_model_data(map) :: {:ok, map} | {:error, any()}
def as_to_model_data(%{"type" => "TodoList", "actor" => actor_url} = object) do
case ActivityPub.get_or_fetch_actor_by_url(actor_url) do
{:ok, %Actor{type: :Group, id: group_id} = _group} ->
%{
title: object["name"],
url: object["id"],
actor_id: group_id
}
_ ->
{:error, :group_not_found}
end
end
end