alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web
+ alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Visibility
+ alias Pleroma.Web.AdminAPI.AccountView
alias Pleroma.Web.Endpoint
alias Pleroma.Web.Router.Helpers
require Logger
require Pleroma.Constants
- @supported_object_types ["Article", "Note", "Video", "Page", "Question", "Answer", "Audio"]
+ @supported_object_types [
+ "Article",
+ "Note",
+ "Event",
+ "Video",
+ "Page",
+ "Question",
+ "Answer",
+ "Audio"
+ ]
+ @strip_status_report_states ~w(closed resolved)
@supported_report_states ~w(open closed resolved)
@valid_visibilities ~w(public unlisted private direct)
def determine_explicit_mentions(_), do: []
- @spec recipient_in_collection(any(), any()) :: boolean()
- defp recipient_in_collection(ap_id, coll) when is_binary(coll), do: ap_id == coll
- defp recipient_in_collection(ap_id, coll) when is_list(coll), do: ap_id in coll
- defp recipient_in_collection(_, _), do: false
+ @spec label_in_collection?(any(), any()) :: boolean()
+ defp label_in_collection?(ap_id, coll) when is_binary(coll), do: ap_id == coll
+ defp label_in_collection?(ap_id, coll) when is_list(coll), do: ap_id in coll
+ defp label_in_collection?(_, _), do: false
+
+ @spec label_in_message?(String.t(), map()) :: boolean()
+ def label_in_message?(label, params),
+ do:
+ [params["to"], params["cc"], params["bto"], params["bcc"]]
+ |> Enum.any?(&label_in_collection?(label, &1))
+
+ @spec unaddressed_message?(map()) :: boolean()
+ def unaddressed_message?(params),
+ do:
+ [params["to"], params["cc"], params["bto"], params["bcc"]]
+ |> Enum.all?(&is_nil(&1))
@spec recipient_in_message(User.t(), User.t(), map()) :: boolean()
- def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params) do
- addresses = [params["to"], params["cc"], params["bto"], params["bcc"]]
-
- cond do
- Enum.any?(addresses, &recipient_in_collection(ap_id, &1)) -> true
- # if the message is unaddressed at all, then assume it is directly addressed
- # to the recipient
- Enum.all?(addresses, &is_nil(&1)) -> true
- # if the message is sent from somebody the user is following, then assume it
- # is addressed to the recipient
- User.following?(recipient, actor) -> true
- true -> false
- end
- end
+ def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params),
+ do:
+ label_in_message?(ap_id, params) || unaddressed_message?(params) ||
+ User.following?(recipient, actor)
defp extract_list(target) when is_binary(target), do: [target]
defp extract_list(lst) when is_list(lst), do: lst
def maybe_splice_recipient(ap_id, params) do
need_splice? =
- !recipient_in_collection(ap_id, params["to"]) &&
- !recipient_in_collection(ap_id, params["cc"])
+ !label_in_collection?(ap_id, params["to"]) &&
+ !label_in_collection?(ap_id, params["cc"])
if need_splice? do
cc_list = extract_list(params["cc"])
%Activity{data: %{"actor" => actor}},
object
) do
- announcements = take_announcements(object)
+ unless actor |> User.get_cached_by_ap_id() |> User.invisible?() do
+ announcements = take_announcements(object)
- with announcements <- Enum.uniq([actor | announcements]) do
- update_element_in_object("announcement", announcements, object)
+ with announcements <- Enum.uniq([actor | announcements]) do
+ update_element_in_object("announcement", announcements, object)
+ end
+ else
+ {:ok, object}
end
end
def make_flag_data(_, _), do: %{}
defp build_flag_object(%{account: account, statuses: statuses} = _) do
- [account.ap_id] ++
- Enum.map(statuses || [], fn
+ [account.ap_id] ++ build_flag_object(%{statuses: statuses})
+ end
+
+ defp build_flag_object(%{statuses: statuses}) do
+ Enum.map(statuses || [], &build_flag_object/1)
+ end
+
+ defp build_flag_object(act) when is_map(act) or is_binary(act) do
+ id =
+ case act do
%Activity{} = act -> act.data["id"]
act when is_map(act) -> act["id"]
act when is_binary(act) -> act
- end)
+ end
+
+ case Activity.get_by_ap_id_with_object(id) do
+ %Activity{} = activity ->
+ %{
+ "type" => "Note",
+ "id" => activity.data["id"],
+ "content" => activity.object.data["content"],
+ "published" => activity.object.data["published"],
+ "actor" =>
+ AccountView.render("show.json", %{
+ user: User.get_by_ap_id(activity.object.data["actor"])
+ })
+ }
+
+ _ ->
+ %{"id" => id, "deleted" => true}
+ end
end
defp build_flag_object(_), do: []
end
#### Report-related helpers
+ def get_reports(params, page, page_size) do
+ params =
+ params
+ |> Map.put("type", "Flag")
+ |> Map.put("skip_preload", true)
+ |> Map.put("preload_report_notes", true)
+ |> Map.put("total", true)
+ |> Map.put("limit", page_size)
+ |> Map.put("offset", (page - 1) * page_size)
+
+ ActivityPub.fetch_activities([], params, :offset)
+ end
+
+ def parse_report_group(activity) do
+ reports = get_reports_by_status_id(activity["id"])
+ max_date = Enum.max_by(reports, &NaiveDateTime.from_iso8601!(&1.data["published"]))
+ actors = Enum.map(reports, & &1.user_actor)
+ [%{data: %{"object" => [account_id | _]}} | _] = reports
+
+ account =
+ AccountView.render("show.json", %{
+ user: User.get_by_ap_id(account_id)
+ })
+
+ status = get_status_data(activity)
+
+ %{
+ date: max_date.data["published"],
+ account: account,
+ status: status,
+ actors: Enum.uniq(actors),
+ reports: reports
+ }
+ end
+
+ defp get_status_data(status) do
+ case status["deleted"] do
+ true ->
+ %{
+ "id" => status["id"],
+ "deleted" => true
+ }
+
+ _ ->
+ Activity.get_by_ap_id(status["id"])
+ end
+ end
+
+ def get_reports_by_status_id(ap_id) do
+ from(a in Activity,
+ where: fragment("(?)->>'type' = 'Flag'", a.data),
+ where: fragment("(?)->'object' @> ?", a.data, ^[%{id: ap_id}]),
+ or_where: fragment("(?)->'object' @> ?", a.data, ^[ap_id])
+ )
+ |> Activity.with_preloaded_user_actor()
+ |> Repo.all()
+ end
+
+ @spec get_reports_grouped_by_status([String.t()]) :: %{
+ required(:groups) => [
+ %{
+ required(:date) => String.t(),
+ required(:account) => %{},
+ required(:status) => %{},
+ required(:actors) => [%User{}],
+ required(:reports) => [%Activity{}]
+ }
+ ]
+ }
+ def get_reports_grouped_by_status(activity_ids) do
+ parsed_groups =
+ activity_ids
+ |> Enum.map(fn id ->
+ id
+ |> build_flag_object()
+ |> parse_report_group()
+ end)
+
+ %{
+ groups: parsed_groups
+ }
+ end
+
+ @spec get_reported_activities() :: [
+ %{
+ required(:activity) => String.t(),
+ required(:date) => String.t()
+ }
+ ]
+ def get_reported_activities do
+ reported_activities_query =
+ from(a in Activity,
+ where: fragment("(?)->>'type' = 'Flag'", a.data),
+ select: %{
+ activity: fragment("jsonb_array_elements((? #- '{object,0}')->'object')", a.data)
+ },
+ group_by: fragment("activity")
+ )
+
+ from(a in subquery(reported_activities_query),
+ distinct: true,
+ select: %{
+ id: fragment("COALESCE(?->>'id'::text, ? #>> '{}')", a.activity, a.activity)
+ }
+ )
+ |> Repo.all()
+ |> Enum.map(& &1.id)
+ end
+
+ def update_report_state(%Activity{} = activity, state)
+ when state in @strip_status_report_states do
+ {:ok, stripped_activity} = strip_report_status_data(activity)
+
+ new_data =
+ activity.data
+ |> Map.put("state", state)
+ |> Map.put("object", stripped_activity.data["object"])
+
+ activity
+ |> Changeset.change(data: new_data)
+ |> Repo.update()
+ end
def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do
new_data = Map.put(activity.data, "state", state)
|> Repo.update()
end
+ def update_report_state(activity_ids, state) when state in @supported_report_states do
+ activities_num = length(activity_ids)
+
+ from(a in Activity, where: a.id in ^activity_ids)
+ |> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)])
+ |> Repo.update_all([])
+ |> case do
+ {^activities_num, _} -> :ok
+ _ -> {:error, activity_ids}
+ end
+ end
+
def update_report_state(_, _), do: {:error, "Unsupported state"}
+ def strip_report_status_data(activity) do
+ [actor | reported_activities] = activity.data["object"]
+
+ stripped_activities =
+ Enum.map(reported_activities, fn
+ act when is_map(act) -> act["id"]
+ act when is_binary(act) -> act
+ end)
+
+ new_data = put_in(activity.data, ["object"], [actor | stripped_activities])
+
+ {:ok, %{activity | data: new_data}}
+ end
+
def update_activity_visibility(activity, visibility) when visibility in @valid_visibilities do
[to, cc, recipients] =
activity
|> Repo.all()
end
- defp maybe_put(map, _key, nil), do: map
- defp maybe_put(map, key, value), do: Map.put(map, key, value)
+ def maybe_put(map, _key, nil), do: map
+ def maybe_put(map, key, value), do: Map.put(map, key, value)
end