Expose posts media through AP

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2020-12-16 09:54:56 +01:00
parent 4e7ab231ad
commit d1472d94de
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773
5 changed files with 150 additions and 29 deletions

View file

@ -10,7 +10,6 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
alias Mobilizon.Addresses
alias Mobilizon.Addresses.Address
alias Mobilizon.Events.Event, as: EventModel
alias Mobilizon.Medias.Media
alias Mobilizon.Federation.ActivityStream.{Converter, Convertible}
alias Mobilizon.Federation.ActivityStream.Converter.Address, as: AddressConverter
@ -21,7 +20,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
fetch_tags: 1,
fetch_mentions: 1,
build_tags: 1,
maybe_fetch_actor_and_attributed_to_id: 1
maybe_fetch_actor_and_attributed_to_id: 1,
process_pictures: 2
]
require Logger
@ -34,6 +34,9 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
defdelegate model_to_as(event), to: EventConverter
end
@online_address_name "Website"
@banner_picture_name "Banner"
@doc """
Converts an AP object data to our internal data structure.
"""
@ -47,30 +50,16 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
{:tags, tags} <- {:tags, fetch_tags(object["tag"])},
{:mentions, mentions} <- {:mentions, fetch_mentions(object["tag"])},
{:visibility, visibility} <- {:visibility, get_visibility(object)},
{:options, options} <- {:options, get_options(object)} do
attachments =
object
|> Map.get("attachment", [])
|> Enum.filter(fn attachment -> Map.get(attachment, "type", "Document") == "Document" end)
picture_id =
with true <- length(attachments) > 0,
{:ok, %Media{id: picture_id}} <-
attachments
|> hd()
|> MediaConverter.find_or_create_media(actor_id) do
picture_id
else
_err ->
nil
end
{:options, options} <- {:options, get_options(object)},
[description: description, picture_id: picture_id, medias: medias] <-
process_pictures(object, actor_id) do
%{
title: object["name"],
description: object["content"],
description: description,
organizer_actor_id: actor_id,
attributed_to_id: if(is_nil(attributed_to), do: nil, else: attributed_to.id),
picture_id: picture_id,
medias: medias,
begins_on: object["startTime"],
ends_on: object["endTime"],
category: object["category"],
@ -143,6 +132,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
|> maybe_add_physical_address(event)
|> maybe_add_event_picture(event)
|> maybe_add_online_address(event)
|> maybe_add_inline_media(event)
end
# Get only elements that we have in EventOptions
@ -213,7 +203,7 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
"type" => "Link",
"href" => url,
"mediaType" => "text/html",
"name" => "Website"
"name" => @online_address_name
} ->
url
@ -239,7 +229,12 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
res,
"attachment",
[],
&(&1 ++ [MediaConverter.model_to_as(event.picture)])
&(&1 ++
[
event.picture
|> MediaConverter.model_to_as()
|> Map.put("name", @banner_picture_name)
])
)
end
@ -258,9 +253,21 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Event do
"type" => "Link",
"href" => event.online_address,
"mediaType" => "text/html",
"name" => "Website"
"name" => @online_address_name
}
])
)
end
@spec maybe_add_inline_media(map(), Event.t()) :: map()
defp maybe_add_inline_media(res, event) do
medias = Enum.map(event.media, &MediaConverter.model_to_as/1)
Map.update(
res,
"attachment",
[],
&(&1 ++ medias)
)
end
end

View file

@ -9,9 +9,15 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityPub.Utils
alias Mobilizon.Federation.ActivityStream.{Converter, Convertible}
alias Mobilizon.Federation.ActivityStream.Converter.Media, as: MediaConverter
alias Mobilizon.Posts.Post
require Logger
import Mobilizon.Federation.ActivityStream.Converter.Utils,
only: [
process_pictures: 2
]
@behaviour Converter
defimpl Convertible, for: Post do
@ -20,6 +26,8 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do
defdelegate model_to_as(post), to: PostConverter
end
@banner_picture_name "Banner"
@doc """
Convert an post struct to an ActivityStream representation
"""
@ -35,8 +43,11 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do
"name" => post.title,
"content" => post.body,
"attributedTo" => creator_url,
"published" => (post.publish_at || post.inserted_at) |> to_date()
"published" => (post.publish_at || post.inserted_at) |> to_date(),
"attachment" => []
}
|> maybe_add_post_picture(post)
|> maybe_add_inline_media(post)
end
@doc """
@ -48,15 +59,19 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do
%{"type" => "Article", "actor" => creator, "attributedTo" => group} = object
) do
with {:ok, %Actor{id: attributed_to_id}} <- get_actor(group),
{:ok, %Actor{id: author_id}} <- get_actor(creator) do
{:ok, %Actor{id: author_id}} <- get_actor(creator),
[description: description, picture_id: picture_id, medias: medias] <-
process_pictures(object, attributed_to_id) do
%{
title: object["name"],
body: object["content"],
body: description,
url: object["id"],
attributed_to_id: attributed_to_id,
author_id: author_id,
local: false,
publish_at: object["published"]
publish_at: object["published"],
picture_id: picture_id,
medias: medias
}
else
{:error, err} -> {:error, err}
@ -70,4 +85,34 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Post do
defp to_date(%DateTime{} = date), do: DateTime.to_iso8601(date)
defp to_date(%NaiveDateTime{} = date), do: NaiveDateTime.to_iso8601(date)
@spec maybe_add_post_picture(map(), Post.t()) :: map()
defp maybe_add_post_picture(res, post) do
if is_nil(post.picture),
do: res,
else:
Map.update(
res,
"attachment",
[],
&(&1 ++
[
post.picture
|> MediaConverter.model_to_as()
|> Map.put("name", @banner_picture_name)
])
)
end
@spec maybe_add_inline_media(map(), Post.t()) :: map()
defp maybe_add_inline_media(res, post) do
medias = Enum.map(post.media, &MediaConverter.model_to_as/1)
Map.update(
res,
"attachment",
[],
&(&1 ++ medias)
)
end
end

View file

@ -6,15 +6,19 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
alias Mobilizon.{Actors, Events}
alias Mobilizon.Actors.Actor
alias Mobilizon.Events.Tag
alias Mobilizon.Medias.Media
alias Mobilizon.Mention
alias Mobilizon.Storage.Repo
alias Mobilizon.Federation.ActivityPub
alias Mobilizon.Federation.ActivityStream.Converter.Media, as: MediaConverter
alias Mobilizon.Web.Endpoint
require Logger
@banner_picture_name "Banner"
@spec fetch_tags([String.t()]) :: [Tag.t()]
def fetch_tags(tags) when is_list(tags) do
Logger.debug("fetching tags")
@ -169,4 +173,62 @@ defmodule Mobilizon.Federation.ActivityStream.Converter.Utils do
actor
end
end
@spec process_pictures(map(), integer()) :: Keyword.t()
def process_pictures(object, actor_id) do
attachements = Map.get(object, "attachment", [])
media_attachements = get_medias(attachements)
media_attachements_map =
media_attachements
|> Enum.map(fn media_attachement ->
{media_attachement["url"],
MediaConverter.find_or_create_media(media_attachement, actor_id)}
end)
|> Enum.reduce(%{}, fn {old_url, media}, acc ->
case media do
{:ok, %Media{} = media} ->
Map.put(acc, old_url, media)
_ ->
acc
end
end)
media_attachements_map_urls =
media_attachements_map
|> Enum.map(fn {old_url, new_media} -> {old_url, new_media.file.url} end)
|> Map.new()
picture_id =
with banner when is_map(banner) <- get_banner_picture(attachements),
{:ok, %Media{id: picture_id}} <-
MediaConverter.find_or_create_media(banner, actor_id) do
picture_id
else
_err ->
nil
end
description = replace_media_urls_in_body(object["content"], media_attachements_map_urls)
[description: description, picture_id: picture_id, medias: Map.values(media_attachements_map)]
end
defp replace_media_urls_in_body(body, media_urls),
do:
Enum.reduce(media_urls, body, fn media_url, body ->
replace_media_url_in_body(body, media_url)
end)
defp replace_media_url_in_body(body, {old_url, new_url}),
do: String.replace(body, old_url, new_url)
defp get_medias(attachments) do
Enum.filter(attachments, &(&1["type"] == "Document" && &1["name"] != @banner_picture_name))
end
defp get_banner_picture(attachments) do
Enum.find(attachments, &(&1["type"] == "Document" && &1["name"] == @banner_picture_name))
end
end

View file

@ -10,7 +10,7 @@ defmodule Mobilizon.Posts do
import Ecto.Query
require Logger
@post_preloads [:author, :attributed_to, :picture]
@post_preloads [:author, :attributed_to, :picture, :media]
import EctoEnum

View file

@ -8,6 +8,7 @@ defmodule Mobilizon.Web.PageView do
alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Events.Event
alias Mobilizon.Posts.Post
alias Mobilizon.Resources.Resource
alias Mobilizon.Tombstone
@ -54,6 +55,12 @@ defmodule Mobilizon.Web.PageView do
|> Map.merge(Utils.make_json_ld_header())
end
def render("post.activity-json", %{conn: %{assigns: %{object: %Post{} = post}}}) do
post
|> Convertible.model_to_as()
|> Map.merge(Utils.make_json_ld_header())
end
def render(page, %{object: object, conn: conn} = _assigns)
when page in ["actor.html", "event.html", "comment.html", "post.html"] do
locale = get_locale(conn)