Expose posts media through AP
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
4e7ab231ad
commit
d1472d94de
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue