defmodule MobilizonWeb.API.Reports do @moduledoc """ API for Reports. """ import Mobilizon.Service.Admin.ActionLogService import MobilizonWeb.API.Utils alias Mobilizon.Actors alias Mobilizon.Actors.Actor alias Mobilizon.Events alias Mobilizon.Reports, as: ReportsAction alias Mobilizon.Reports.{Note, Report, ReportStatus} alias Mobilizon.Service.ActivityPub alias Mobilizon.Service.ActivityPub.Activity alias Mobilizon.Users alias Mobilizon.Users.User @doc """ Create a report/flag on an actor, and optionally on an event or on comments. """ def report( %{ reporter_actor_id: reporter_actor_id, reported_actor_id: reported_actor_id } = args ) do with {:reporter, %Actor{url: reporter_url} = _reporter_actor} <- {:reporter, Actors.get_actor!(reporter_actor_id)}, {:reported, %Actor{url: reported_actor_url} = reported_actor} <- {:reported, Actors.get_actor!(reported_actor_id)}, {:ok, content} <- args |> Map.get(:content, nil) |> make_report_content_text(), {:ok, event} <- args |> Map.get(:event_id, nil) |> get_event(), {:get_report_comments, comments_urls} <- get_report_comments(reported_actor, Map.get(args, :comments_ids, [])), {:make_activity, {:ok, %Activity{} = activity, %Report{} = report}} <- {:make_activity, ActivityPub.flag(%{ reporter_url: reporter_url, reported_actor_url: reported_actor_url, event_url: (!is_nil(event) && event.url) || nil, comments_url: comments_urls, content: content, forward: args[:forward] || false, local: args[:local] || args[:forward] || false })} do {:ok, activity, report} else {:make_activity, err} -> {:error, err} {:error, err} -> {:error, err} {:actor_id, %{}} -> {:error, "Valid `actor_id` required"} {:reporter, nil} -> {:error, "Reporter Actor not found"} {:reported, nil} -> {:error, "Reported Actor not found"} end end defp get_event(nil), do: {:ok, nil} defp get_event(event_id), do: Events.get_event(event_id) @doc """ Update the state of a report """ def update_report_status(%Actor{} = actor, %Report{} = report, state) do with {:valid_state, true} <- {:valid_state, ReportStatus.valid_value?(state)}, {:ok, report} <- ReportsAction.update_report(report, %{"status" => state}), {:ok, _} <- log_action(actor, "update", report) do {:ok, report} else {:valid_state, false} -> {:error, "Unsupported state"} end end defp get_report_comments(%Actor{id: actor_id}, comment_ids) do {:get_report_comments, actor_id |> Events.list_comments_by_actor_and_ids(comment_ids) |> Enum.map(& &1.url)} end defp get_report_comments(_, _), do: {:get_report_comments, nil} @doc """ Create a note on a report """ @spec create_report_note(Report.t(), Actor.t(), String.t()) :: {:ok, Note.t()} def create_report_note( %Report{id: report_id}, %Actor{id: moderator_id, user_id: user_id} = moderator, content ) do with %User{role: role} <- Users.get_user!(user_id), {:role, true} <- {:role, role in [:administrator, :moderator]}, {:ok, %Note{} = note} <- Mobilizon.Reports.create_note(%{ "report_id" => report_id, "moderator_id" => moderator_id, "content" => content }), {:ok, _} <- log_action(moderator, "create", note) do {:ok, note} else {:role, false} -> {:error, "You need to be a moderator or an administrator to create a note on a report"} end end @doc """ Delete a report note """ @spec delete_report_note(Note.t(), Actor.t()) :: {:ok, Note.t()} def delete_report_note( %Note{moderator_id: note_moderator_id} = note, %Actor{id: moderator_id, user_id: user_id} = moderator ) do with {:same_actor, true} <- {:same_actor, note_moderator_id == moderator_id}, %User{role: role} <- Users.get_user!(user_id), {:role, true} <- {:role, role in [:administrator, :moderator]}, {:ok, %Note{} = note} <- Mobilizon.Reports.delete_note(note), {:ok, _} <- log_action(moderator, "delete", note) do {:ok, note} else {:role, false} -> {:error, "You need to be a moderator or an administrator to create a note on a report"} {:same_actor, false} -> {:error, "You can only remove your own notes"} end end end