Add admin interface to manage instances subscriptions
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
@@ -7,7 +7,7 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Actor do
|
||||
"""
|
||||
|
||||
alias Mobilizon.Actors.Actor, as: ActorModel
|
||||
alias Mobilizon.Service.ActivityPub.{Converter, Convertible}
|
||||
alias Mobilizon.Service.ActivityPub.{Converter, Convertible, Utils}
|
||||
|
||||
@behaviour Converter
|
||||
|
||||
@@ -22,33 +22,40 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Actor do
|
||||
"""
|
||||
@impl Converter
|
||||
@spec as_to_model_data(map) :: map
|
||||
def as_to_model_data(object) do
|
||||
def as_to_model_data(data) do
|
||||
avatar =
|
||||
object["icon"]["url"] &&
|
||||
data["icon"]["url"] &&
|
||||
%{
|
||||
"name" => object["icon"]["name"] || "avatar",
|
||||
"url" => object["icon"]["url"]
|
||||
"name" => data["icon"]["name"] || "avatar",
|
||||
"url" => MobilizonWeb.MediaProxy.url(data["icon"]["url"])
|
||||
}
|
||||
|
||||
banner =
|
||||
object["image"]["url"] &&
|
||||
data["image"]["url"] &&
|
||||
%{
|
||||
"name" => object["image"]["name"] || "banner",
|
||||
"url" => object["image"]["url"]
|
||||
"name" => data["image"]["name"] || "banner",
|
||||
"url" => MobilizonWeb.MediaProxy.url(data["image"]["url"])
|
||||
}
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
"type" => String.to_existing_atom(object["type"]),
|
||||
"preferred_username" => object["preferredUsername"],
|
||||
"summary" => object["summary"],
|
||||
"url" => object["id"],
|
||||
"name" => object["name"],
|
||||
"avatar" => avatar,
|
||||
"banner" => banner,
|
||||
"keys" => object["publicKey"]["publicKeyPem"],
|
||||
"manually_approves_followers" => object["manuallyApprovesFollowers"]
|
||||
}}
|
||||
actor_data = %{
|
||||
url: data["id"],
|
||||
avatar: avatar,
|
||||
banner: banner,
|
||||
name: data["name"],
|
||||
preferred_username: data["preferredUsername"],
|
||||
summary: data["summary"],
|
||||
keys: data["publicKey"]["publicKeyPem"],
|
||||
inbox_url: data["inbox"],
|
||||
outbox_url: data["outbox"],
|
||||
following_url: data["following"],
|
||||
followers_url: data["followers"],
|
||||
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 """
|
||||
@@ -57,18 +64,51 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Actor do
|
||||
@impl Converter
|
||||
@spec model_to_as(ActorModel.t()) :: map
|
||||
def model_to_as(%ActorModel{} = actor) do
|
||||
%{
|
||||
"type" => Atom.to_string(actor.type),
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"preferred_username" => actor.preferred_username,
|
||||
actor_data = %{
|
||||
"id" => actor.url,
|
||||
"type" => actor.type,
|
||||
"preferredUsername" => actor.preferred_username,
|
||||
"name" => actor.name,
|
||||
"summary" => actor.summary,
|
||||
"following" => ActorModel.build_url(actor.preferred_username, :following),
|
||||
"followers" => ActorModel.build_url(actor.preferred_username, :followers),
|
||||
"inbox" => ActorModel.build_url(actor.preferred_username, :inbox),
|
||||
"outbox" => ActorModel.build_url(actor.preferred_username, :outbox),
|
||||
"id" => ActorModel.build_url(actor.preferred_username, :page),
|
||||
"url" => actor.url
|
||||
"following" => actor.following_url,
|
||||
"followers" => actor.followers_url,
|
||||
"inbox" => actor.inbox_url,
|
||||
"outbox" => actor.outbox_url,
|
||||
"url" => actor.url,
|
||||
"endpoints" => %{
|
||||
"sharedInbox" => actor.shared_inbox_url
|
||||
},
|
||||
"manuallyApprovesFollowers" => actor.manually_approves_followers,
|
||||
"publicKey" => %{
|
||||
"id" => "#{actor.url}#main-key",
|
||||
"owner" => actor.url,
|
||||
"publicKeyPem" =>
|
||||
if(is_nil(actor.domain) and not is_nil(actor.keys),
|
||||
do: Utils.pem_to_public_key_pem(actor.keys),
|
||||
else: actor.keys
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
actor_data =
|
||||
if is_nil(actor.avatar) do
|
||||
actor_data
|
||||
else
|
||||
Map.put(actor_data, "icon", %{
|
||||
"type" => "Image",
|
||||
"mediaType" => actor.avatar.content_type,
|
||||
"url" => actor.avatar.url
|
||||
})
|
||||
end
|
||||
|
||||
if is_nil(actor.banner) do
|
||||
actor_data
|
||||
else
|
||||
Map.put(actor_data, "image", %{
|
||||
"type" => "Image",
|
||||
"mediaType" => actor.banner.content_type,
|
||||
"url" => actor.banner.url
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,6 +12,7 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Comment do
|
||||
alias Mobilizon.Service.ActivityPub
|
||||
alias Mobilizon.Service.ActivityPub.{Converter, Convertible}
|
||||
alias Mobilizon.Service.ActivityPub.Converter.Utils, as: ConverterUtils
|
||||
alias Mobilizon.Tombstone, as: TombstoneModel
|
||||
|
||||
require Logger
|
||||
|
||||
@@ -32,9 +33,11 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Comment do
|
||||
Logger.debug("We're converting raw ActivityStream data to a comment entity")
|
||||
Logger.debug(inspect(object))
|
||||
|
||||
with {:ok, %Actor{id: actor_id}} <- ActivityPub.get_or_fetch_actor_by_url(object["actor"]),
|
||||
{:tags, tags} <- {:tags, ConverterUtils.fetch_tags(object["tag"])},
|
||||
{:mentions, mentions} <- {:mentions, ConverterUtils.fetch_mentions(object["tag"])} do
|
||||
with author_url <- Map.get(object, "actor") || Map.get(object, "attributedTo"),
|
||||
{:ok, %Actor{id: actor_id}} <- ActivityPub.get_or_fetch_actor_by_url(author_url),
|
||||
{:tags, tags} <- {:tags, ConverterUtils.fetch_tags(Map.get(object, "tag", []))},
|
||||
{:mentions, mentions} <-
|
||||
{:mentions, ConverterUtils.fetch_mentions(Map.get(object, "tag", []))} do
|
||||
Logger.debug("Inserting full comment")
|
||||
Logger.debug(inspect(object))
|
||||
|
||||
@@ -70,6 +73,7 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Comment do
|
||||
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} ->
|
||||
@@ -106,6 +110,7 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Comment do
|
||||
"to" => to,
|
||||
"cc" => [],
|
||||
"content" => comment.text,
|
||||
"mediaType" => "text/html",
|
||||
"actor" => comment.actor.url,
|
||||
"attributedTo" => comment.actor.url,
|
||||
"uuid" => comment.uuid,
|
||||
@@ -114,23 +119,27 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Comment do
|
||||
ConverterUtils.build_mentions(comment.mentions) ++ ConverterUtils.build_tags(comment.tags)
|
||||
}
|
||||
|
||||
if comment.in_reply_to_comment do
|
||||
object |> Map.put("inReplyTo", comment.in_reply_to_comment.url || comment.event.url)
|
||||
else
|
||||
object
|
||||
cond do
|
||||
comment.in_reply_to_comment ->
|
||||
Map.put(object, "inReplyTo", comment.in_reply_to_comment.url)
|
||||
|
||||
comment.event ->
|
||||
Map.put(object, "inReplyTo", comment.event.url)
|
||||
|
||||
true ->
|
||||
object
|
||||
end
|
||||
end
|
||||
|
||||
@impl Converter
|
||||
@spec model_to_as(CommentModel.t()) :: map
|
||||
@doc """
|
||||
A "soft-deleted" comment is a tombstone
|
||||
"""
|
||||
def model_to_as(%CommentModel{} = comment) do
|
||||
%{
|
||||
"type" => "Tombstone",
|
||||
"uuid" => comment.uuid,
|
||||
"id" => comment.url,
|
||||
"published" => comment.inserted_at,
|
||||
"updated" => comment.updated_at,
|
||||
"deleted" => comment.deleted_at
|
||||
}
|
||||
Convertible.model_to_as(%TombstoneModel{
|
||||
uri: comment.url,
|
||||
inserted_at: comment.deleted_at
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,14 +6,13 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Event do
|
||||
internal one, and back.
|
||||
"""
|
||||
|
||||
alias Mobilizon.{Addresses, Media}
|
||||
alias Mobilizon.Addresses
|
||||
alias Mobilizon.Actors.Actor
|
||||
alias Mobilizon.Addresses.Address
|
||||
alias Mobilizon.Events.Event, as: EventModel
|
||||
alias Mobilizon.Events.EventOptions
|
||||
alias Mobilizon.Media.Picture
|
||||
alias Mobilizon.Service.ActivityPub
|
||||
alias Mobilizon.Service.ActivityPub.{Converter, Convertible, Utils}
|
||||
alias Mobilizon.Service.ActivityPub.{Converter, Convertible}
|
||||
alias Mobilizon.Service.ActivityPub.Converter.Address, as: AddressConverter
|
||||
alias Mobilizon.Service.ActivityPub.Converter.Picture, as: PictureConverter
|
||||
alias Mobilizon.Service.ActivityPub.Converter.Utils, as: ConverterUtils
|
||||
@@ -37,26 +36,25 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Event do
|
||||
Logger.debug("event as_to_model_data")
|
||||
Logger.debug(inspect(object))
|
||||
|
||||
with {:actor, {:ok, %Actor{id: actor_id}}} <-
|
||||
{:actor, ActivityPub.get_or_fetch_actor_by_url(object["actor"])},
|
||||
with author_url <- Map.get(object, "actor") || Map.get(object, "attributedTo"),
|
||||
{:actor, {:ok, %Actor{id: actor_id, domain: actor_domain}}} <-
|
||||
{:actor, ActivityPub.get_or_fetch_actor_by_url(author_url)},
|
||||
{:address, address_id} <-
|
||||
{:address, get_address(object["location"])},
|
||||
{:tags, tags} <- {:tags, ConverterUtils.fetch_tags(object["tag"])},
|
||||
{:mentions, mentions} <- {:mentions, ConverterUtils.fetch_mentions(object["tag"])},
|
||||
{:visibility, visibility} <- {:visibility, get_visibility(object)},
|
||||
{:options, options} <- {:options, get_options(object)} do
|
||||
picture_id =
|
||||
with true <- Map.has_key?(object, "attachment") && length(object["attachment"]) > 0,
|
||||
%Picture{id: picture_id} <-
|
||||
Media.get_picture_by_url(
|
||||
object["attachment"]
|
||||
|> hd
|
||||
|> Map.get("url")
|
||||
|> hd
|
||||
|> Map.get("href")
|
||||
) do
|
||||
{:ok, %Picture{id: picture_id}} <-
|
||||
object["attachment"]
|
||||
|> hd
|
||||
|> PictureConverter.find_or_create_picture(actor_id) do
|
||||
picture_id
|
||||
else
|
||||
_ -> nil
|
||||
_err ->
|
||||
nil
|
||||
end
|
||||
|
||||
entity = %{
|
||||
@@ -68,16 +66,20 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Event do
|
||||
ends_on: object["endTime"],
|
||||
category: object["category"],
|
||||
visibility: visibility,
|
||||
join_options: Map.get(object, "joinOptions", "free"),
|
||||
join_options: Map.get(object, "joinMode", "free"),
|
||||
local: is_nil(actor_domain),
|
||||
options: options,
|
||||
status: object["status"],
|
||||
status: object |> Map.get("ical:status", "CONFIRMED") |> String.downcase(),
|
||||
online_address: object["onlineAddress"],
|
||||
phone_address: object["phoneAddress"],
|
||||
draft: object["draft"] || false,
|
||||
draft: false,
|
||||
url: object["id"],
|
||||
uuid: object["uuid"],
|
||||
tags: tags,
|
||||
physical_address_id: address_id
|
||||
mentions: mentions,
|
||||
physical_address_id: address_id,
|
||||
updated_at: object["updated"],
|
||||
publish_at: object["published"]
|
||||
}
|
||||
|
||||
{:ok, entity}
|
||||
@@ -108,14 +110,17 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Event do
|
||||
"uuid" => event.uuid,
|
||||
"category" => event.category,
|
||||
"content" => event.description,
|
||||
"publish_at" => (event.publish_at || event.inserted_at) |> date_to_string(),
|
||||
"updated_at" => event.updated_at |> date_to_string(),
|
||||
"published" => (event.publish_at || event.inserted_at) |> date_to_string(),
|
||||
"updated" => event.updated_at |> date_to_string(),
|
||||
"mediaType" => "text/html",
|
||||
"startTime" => event.begins_on |> date_to_string(),
|
||||
"joinOptions" => to_string(event.join_options),
|
||||
"joinMode" => to_string(event.join_options),
|
||||
"endTime" => event.ends_on |> date_to_string(),
|
||||
"tag" => event.tags |> ConverterUtils.build_tags(),
|
||||
"draft" => event.draft,
|
||||
"maximumAttendeeCapacity" => event.options.maximum_attendee_capacity,
|
||||
"repliesModerationOption" => event.options.comment_moderation,
|
||||
# "draft" => event.draft,
|
||||
"ical:status" => event.status |> to_string |> String.upcase(),
|
||||
"id" => event.url,
|
||||
"url" => event.url
|
||||
}
|
||||
@@ -133,17 +138,10 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Event do
|
||||
# Get only elements that we have in EventOptions
|
||||
@spec get_options(map) :: map
|
||||
defp get_options(object) do
|
||||
keys =
|
||||
EventOptions
|
||||
|> struct
|
||||
|> Map.keys()
|
||||
|> List.delete(:__struct__)
|
||||
|> Enum.map(&Utils.camelize/1)
|
||||
|
||||
Enum.reduce(object, %{}, fn {key, value}, acc ->
|
||||
(!is_nil(value) && key in keys && Map.put(acc, Utils.underscore(key), value)) ||
|
||||
acc
|
||||
end)
|
||||
%{
|
||||
maximum_attendee_capacity: object["maximumAttendeeCapacity"],
|
||||
comment_moderation: object["repliesModerationOption"]
|
||||
}
|
||||
end
|
||||
|
||||
@spec get_address(map | binary | nil) :: integer | nil
|
||||
@@ -186,13 +184,7 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Event do
|
||||
|
||||
@ap_public "https://www.w3.org/ns/activitystreams#Public"
|
||||
|
||||
defp get_visibility(object) do
|
||||
cond do
|
||||
@ap_public in object["to"] -> :public
|
||||
@ap_public in object["cc"] -> :unlisted
|
||||
true -> :private
|
||||
end
|
||||
end
|
||||
defp get_visibility(object), do: if(@ap_public in object["to"], do: :public, else: :unlisted)
|
||||
|
||||
@spec date_to_string(DateTime.t() | nil) :: String.t()
|
||||
defp date_to_string(nil), do: nil
|
||||
|
||||
@@ -15,6 +15,7 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Flag do
|
||||
alias Mobilizon.Reports.Report
|
||||
alias Mobilizon.Service.ActivityPub.Converter
|
||||
alias Mobilizon.Service.ActivityPub.Convertible
|
||||
alias Mobilizon.Service.ActivityPub.Relay
|
||||
|
||||
@behaviour Converter
|
||||
|
||||
@@ -42,8 +43,6 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Flag do
|
||||
end
|
||||
end
|
||||
|
||||
@audience %{"to" => ["https://www.w3.org/ns/activitystreams#Public"], "cc" => []}
|
||||
|
||||
@doc """
|
||||
Convert an event struct to an ActivityStream representation
|
||||
"""
|
||||
@@ -54,17 +53,13 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Flag do
|
||||
|
||||
object = if report.event, do: object ++ [report.event.url], else: object
|
||||
|
||||
audience =
|
||||
if report.local, do: @audience, else: Map.put(@audience, "cc", [report.reported.url])
|
||||
|
||||
%{
|
||||
"type" => "Flag",
|
||||
"actor" => report.reporter.url,
|
||||
"actor" => Relay.get_actor().url,
|
||||
"id" => report.url,
|
||||
"content" => report.content,
|
||||
"object" => object
|
||||
}
|
||||
|> Map.merge(audience)
|
||||
end
|
||||
|
||||
@spec as_to_model(map) :: map
|
||||
@@ -91,7 +86,7 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Flag do
|
||||
end
|
||||
end),
|
||||
|
||||
# Remove the reported user from the object list.
|
||||
# Remove the reported actor and the event from the object list.
|
||||
comments <-
|
||||
Enum.filter(objects, fn url ->
|
||||
!(url == reported.url || (!is_nil(event) && event.url == url))
|
||||
|
||||
@@ -15,14 +15,48 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Picture do
|
||||
def model_to_as(%PictureModel{file: file}) do
|
||||
%{
|
||||
"type" => "Document",
|
||||
"url" => [
|
||||
%{
|
||||
"type" => "Link",
|
||||
"mediaType" => file.content_type,
|
||||
"href" => file.url
|
||||
}
|
||||
],
|
||||
"mediaType" => file.content_type,
|
||||
"url" => file.url,
|
||||
"name" => file.name
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Save picture data from raw data and return AS Link data.
|
||||
"""
|
||||
def find_or_create_picture(%{"type" => "Link", "href" => url}, actor_id),
|
||||
do: find_or_create_picture(url, actor_id)
|
||||
|
||||
def find_or_create_picture(
|
||||
%{"type" => "Document", "url" => picture_url, "name" => name},
|
||||
actor_id
|
||||
)
|
||||
when is_bitstring(picture_url) do
|
||||
with {:ok, %HTTPoison.Response{body: body}} <- HTTPoison.get(picture_url),
|
||||
{:ok,
|
||||
%{
|
||||
name: name,
|
||||
url: url,
|
||||
content_type: content_type,
|
||||
size: size
|
||||
}} <-
|
||||
MobilizonWeb.Upload.store(%{body: body, name: name}),
|
||||
{:picture_exists, nil} <- {:picture_exists, Mobilizon.Media.get_picture_by_url(url)} do
|
||||
Mobilizon.Media.create_picture(%{
|
||||
"file" => %{
|
||||
"url" => url,
|
||||
"name" => name,
|
||||
"content_type" => content_type,
|
||||
"size" => size
|
||||
},
|
||||
"actor_id" => actor_id
|
||||
})
|
||||
else
|
||||
{:picture_exists, %PictureModel{file: _file} = picture} ->
|
||||
{:ok, picture}
|
||||
|
||||
err ->
|
||||
err
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
40
lib/service/activity_pub/converter/tombstone.ex
Normal file
40
lib/service/activity_pub/converter/tombstone.ex
Normal file
@@ -0,0 +1,40 @@
|
||||
defmodule Mobilizon.Service.ActivityPub.Converter.Tombstone do
|
||||
@moduledoc """
|
||||
Comment converter.
|
||||
|
||||
This module allows to convert Tombstone models to ActivityStreams data
|
||||
"""
|
||||
|
||||
alias Mobilizon.Tombstone, as: TombstoneModel
|
||||
alias Mobilizon.Service.ActivityPub.{Converter, Convertible}
|
||||
|
||||
require Logger
|
||||
|
||||
@behaviour Converter
|
||||
|
||||
defimpl Convertible, for: TombstoneModel do
|
||||
alias Mobilizon.Service.ActivityPub.Converter.Tombstone, as: TombstoneConverter
|
||||
|
||||
defdelegate model_to_as(comment), to: TombstoneConverter
|
||||
end
|
||||
|
||||
@doc """
|
||||
Make an AS tombstone object from an existing `Tombstone` structure.
|
||||
"""
|
||||
@impl Converter
|
||||
@spec model_to_as(TombstoneModel.t()) :: map
|
||||
def model_to_as(%TombstoneModel{} = tombstone) do
|
||||
%{
|
||||
"type" => "Tombstone",
|
||||
"id" => tombstone.uri,
|
||||
"deleted" => tombstone.inserted_at
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Converting an Tombstone to an object makes no sense, nevertheless…
|
||||
"""
|
||||
@impl Converter
|
||||
@spec as_to_model_data(map) :: map
|
||||
def as_to_model_data(object), do: object
|
||||
end
|
||||
@@ -14,6 +14,7 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Utils do
|
||||
@spec fetch_tags([String.t()]) :: [Tag.t()]
|
||||
def fetch_tags(tags) when is_list(tags) do
|
||||
Logger.debug("fetching tags")
|
||||
Logger.debug(inspect(tags))
|
||||
|
||||
tags |> Enum.flat_map(&fetch_tag/1) |> Enum.uniq() |> Enum.map(&existing_tag_or_data/1)
|
||||
end
|
||||
@@ -64,6 +65,8 @@ defmodule Mobilizon.Service.ActivityPub.Converter.Utils do
|
||||
}
|
||||
end
|
||||
|
||||
defp fetch_tag(%{title: title}), do: [title]
|
||||
|
||||
defp fetch_tag(tag) when is_map(tag) do
|
||||
case tag["type"] do
|
||||
"Hashtag" ->
|
||||
|
||||
Reference in New Issue
Block a user