mobilizon.chapril.org-mobil.../lib/federation/activity_pub/types/discussions.ex

163 lines
5.6 KiB
Elixir

defmodule Mobilizon.Federation.ActivityPub.Types.Discussions do
@moduledoc false
alias Mobilizon.{Actors, Discussions}
alias Mobilizon.Actors.Actor
alias Mobilizon.Discussions.{Comment, Discussion}
alias Mobilizon.Federation.ActivityPub.{Audience, Permission}
alias Mobilizon.Federation.ActivityPub.Types.Entity
alias Mobilizon.Federation.ActivityStream
alias Mobilizon.Federation.ActivityStream.Convertible
alias Mobilizon.GraphQL.API.Utils, as: APIUtils
alias Mobilizon.Service.Activity.Discussion, as: DiscussionActivity
alias Mobilizon.Web.Endpoint
import Mobilizon.Federation.ActivityPub.Utils, only: [make_create_data: 2, make_update_data: 2]
require Logger
@behaviour Entity
@impl Entity
@spec create(map(), map()) ::
{:ok, Discussion.t(), ActivityStream.t()}
| {:error, :discussion_not_found | :last_comment_not_found | Ecto.Changeset.t()}
def create(%{discussion_id: discussion_id} = args, additional) when not is_nil(discussion_id) do
args = prepare_args(args)
case Discussions.get_discussion(discussion_id) do
%Discussion{} = discussion ->
case Discussions.reply_to_discussion(discussion, args) do
{:ok, %Discussion{last_comment_id: last_comment_id} = discussion} ->
DiscussionActivity.insert_activity(discussion,
subject: "discussion_replied",
actor_id: Map.get(args, :creator_id, args.actor_id)
)
case Discussions.get_comment_with_preload(last_comment_id) do
%Comment{} = last_comment ->
maybe_publish_graphql_subscription(discussion)
comment_as_data = Convertible.model_to_as(last_comment)
audience = Audience.get_audience(discussion)
create_data = make_create_data(comment_as_data, Map.merge(audience, additional))
{:ok, discussion, create_data}
nil ->
{:error, :last_comment_not_found}
end
{:error, _, %Ecto.Changeset{} = err, _} ->
{:error, err}
end
nil ->
{:error, :discussion_not_found}
end
end
@impl Entity
def create(args, additional) do
args = prepare_args(args)
case Discussions.create_discussion(args) do
{:ok, %Discussion{} = discussion} ->
DiscussionActivity.insert_activity(discussion, subject: "discussion_created")
discussion_as_data = Convertible.model_to_as(discussion)
audience = Audience.get_audience(discussion)
create_data = make_create_data(discussion_as_data, Map.merge(audience, additional))
{:ok, discussion, create_data}
{:error, _, %Ecto.Changeset{} = err, _} ->
{:error, err}
end
end
@impl Entity
@spec update(Discussion.t(), map(), map()) ::
{:ok, Discussion.t(), ActivityStream.t()} | {:error, Ecto.Changeset.t()}
def update(%Discussion{} = old_discussion, args, additional) do
case Discussions.update_discussion(old_discussion, args) do
{:ok, %Discussion{} = new_discussion} ->
DiscussionActivity.insert_activity(new_discussion,
subject: "discussion_renamed",
old_discussion: old_discussion
)
Cachex.del(:activity_pub, "discussion_#{new_discussion.slug}")
discussion_as_data = Convertible.model_to_as(new_discussion)
audience = Audience.get_audience(new_discussion)
update_data = make_update_data(discussion_as_data, Map.merge(audience, additional))
{:ok, new_discussion, update_data}
{:error, %Ecto.Changeset{} = err} ->
{:error, err}
end
end
@impl Entity
@spec delete(Discussion.t(), Actor.t(), boolean, map()) ::
{:error, Ecto.Changeset.t()} | {:ok, ActivityStream.t(), Actor.t(), Discussion.t()}
def delete(
%Discussion{actor: group, url: url} = discussion,
%Actor{} = actor,
_local,
_additionnal
) do
case Discussions.delete_discussion(discussion) do
{:error, _, %Ecto.Changeset{} = err, _} ->
{:error, err}
{:ok, %{comments: {_, _}}} ->
DiscussionActivity.insert_activity(discussion,
subject: "discussion_deleted",
moderator: actor
)
# This is just fake
activity_data = %{
"type" => "Delete",
"actor" => actor.url,
"object" => Convertible.model_to_as(discussion),
"id" => url <> "/delete",
"to" => [group.members_url]
}
{:ok, activity_data, actor, discussion}
end
end
@spec actor(Discussion.t()) :: Actor.t() | nil
def actor(%Discussion{creator_id: creator_id}), do: Actors.get_actor(creator_id)
@spec group_actor(Discussion.t()) :: Actor.t() | nil
def group_actor(%Discussion{actor_id: actor_id}), do: Actors.get_actor(actor_id)
@spec permissions(Discussion.t()) :: Permission.t()
def permissions(%Discussion{}) do
%Permission{access: :member, create: :member, update: :moderator, delete: :moderator}
end
@spec maybe_publish_graphql_subscription(Discussion.t()) :: :ok
defp maybe_publish_graphql_subscription(%Discussion{} = discussion) do
Absinthe.Subscription.publish(Endpoint, discussion,
discussion_comment_changed: discussion.slug
)
:ok
end
@spec prepare_args(map) :: map
defp prepare_args(args) do
{text, _mentions, _tags} =
APIUtils.make_content_html(
args |> Map.get(:text, "") |> String.trim(),
# Can't put additional tags on a comment
[],
"text/html"
)
args
# title might be nil
|> Map.update(:title, "", fn title -> String.trim(title || "") end)
|> Map.put(:text, text)
end
end