From 4b60d41db9d10e971ee91202389991da294c72de Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Tue, 3 Dec 2019 23:54:07 +0900 Subject: Add report notes --- lib/pleroma/activity.ex | 13 +++++++ lib/pleroma/report_note.ex | 46 +++++++++++++++++++++++ lib/pleroma/web/activity_pub/activity_pub.ex | 8 ++++ lib/pleroma/web/activity_pub/utils.ex | 1 + lib/pleroma/web/admin_api/admin_api_controller.ex | 34 +++++++---------- lib/pleroma/web/admin_api/views/report_view.ex | 18 ++++++++- lib/pleroma/web/router.ex | 2 +- 7 files changed, 99 insertions(+), 23 deletions(-) create mode 100644 lib/pleroma/report_note.ex (limited to 'lib') diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index cd7a5aae9..37b2c041e 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -12,6 +12,7 @@ defmodule Pleroma.Activity do alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Repo + alias Pleroma.ReportNote alias Pleroma.ThreadMute alias Pleroma.User @@ -47,6 +48,8 @@ defmodule Pleroma.Activity do has_one(:user_actor, User, on_delete: :nothing, foreign_key: :id) # This is a fake relation, do not use outside of with_preloaded_bookmark/get_bookmark has_one(:bookmark, Bookmark) + # This is a fake relation, do not use outside of with_preloaded_report_notes + has_many(:report_notes, ReportNote) has_many(:notifications, Notification, on_delete: :delete_all) # Attention: this is a fake relation, don't try to preload it blindly and expect it to work! @@ -113,6 +116,16 @@ defmodule Pleroma.Activity do def with_preloaded_bookmark(query, _), do: query + def with_preloaded_report_notes(query) do + from([a] in query, + left_join: r in ReportNote, + on: a.id == r.activity_id, + preload: [report_notes: r] + ) + end + + def with_preloaded_report_notes(query, _), do: query + def with_set_thread_muted_field(query, %User{} = user) do from([a] in query, left_join: tm in ThreadMute, diff --git a/lib/pleroma/report_note.ex b/lib/pleroma/report_note.ex new file mode 100644 index 000000000..91102696b --- /dev/null +++ b/lib/pleroma/report_note.ex @@ -0,0 +1,46 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ReportNote do + use Ecto.Schema + + import Ecto.Changeset + import Ecto.Query + + alias Pleroma.Activity + alias Pleroma.Repo + alias Pleroma.ReportNote + alias Pleroma.User + + @type t :: %__MODULE__{} + + schema "report_notes" do + field(:content, :string) + belongs_to(:user, User, type: FlakeId.Ecto.CompatType) + belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType) + + timestamps() + end + + @spec create(FlakeId.Ecto.CompatType.t(), FlakeId.Ecto.CompatType.t(), String.t()) :: + {:ok, ReportNote.t()} | {:error, Changeset.t()} + def create(user_id, activity_id, content) do + attrs = %{ + user_id: user_id, + activity_id: activity_id, + content: content + } + + %ReportNote{} + |> cast(attrs, [:user_id, :activity_id, :content]) + |> validate_required([:user_id, :activity_id, :content]) + |> Repo.insert() + end + + def get_all_for_status(status_id) do + ReportNote + |> where(activity_id: ^status_id) + |> Repo.all() + end +end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index f32d04175..5c6cdfcf1 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1018,6 +1018,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Activity.with_preloaded_bookmark(opts["user"]) end + defp maybe_preload_report_notes(query, %{"preload_report_notes" => true}) do + query + |> Activity.with_preloaded_report_notes() + end + + defp maybe_preload_report_notes(query, _), do: query + defp maybe_set_thread_muted_field(query, %{"skip_preload" => true}), do: query defp maybe_set_thread_muted_field(query, opts) do @@ -1045,6 +1052,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do Activity |> maybe_preload_objects(opts) |> maybe_preload_bookmarks(opts) + |> maybe_preload_report_notes(opts) |> maybe_set_thread_muted_field(opts) |> maybe_order(opts) |> restrict_recipients(recipients, opts["user"]) diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 01aacbde3..079d458ba 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -781,6 +781,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do 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) diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 24fdc3c82..ee32bac45 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do alias Pleroma.Activity alias Pleroma.ModerationLog alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.ReportNote alias Pleroma.User alias Pleroma.UserInviteToken alias Pleroma.Web.ActivityPub.ActivityPub @@ -641,9 +642,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do def list_reports(conn, params) do {page, page_size} = page_params(params) + reports = Utils.get_reports(params, page, page_size) + conn |> put_view(ReportView) - |> render("index.json", %{reports: Utils.get_reports(params, page, page_size)}) + |> render("index.json", %{reports: reports}) end def list_grouped_reports(conn, _params) do @@ -687,32 +690,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end - def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do - with false <- is_nil(params["status"]), - %Activity{} <- Activity.get_by_id(id) do - params = - params - |> Map.put("in_reply_to_status_id", id) - |> Map.put("visibility", "direct") - - {:ok, activity} = CommonAPI.post(user, params) - + def report_notes_create(%{assigns: %{user: user}} = conn, %{ + "id" => status_id, + "content" => content + }) do + with {:ok, _} <- ReportNote.create(user.id, status_id, content) do ModerationLog.insert_log(%{ action: "report_response", actor: user, - subject: activity, - text: params["status"] + subject: Activity.get_by_id(status_id), + text: content }) - conn - |> put_view(StatusView) - |> render("show.json", %{activity: activity}) + json_response(conn, :no_content, "") else - true -> - {:param_cast, nil} - - nil -> - {:error, :not_found} + _ -> json_response(conn, :bad_request, "") end end diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex index ca88595c7..80ca62691 100644 --- a/lib/pleroma/web/admin_api/views/report_view.ex +++ b/lib/pleroma/web/admin_api/views/report_view.ex @@ -38,7 +38,8 @@ defmodule Pleroma.Web.AdminAPI.ReportView do content: content, created_at: created_at, statuses: StatusView.render("index.json", %{activities: statuses, as: :activity}), - state: report.data["state"] + state: report.data["state"], + notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes}) } end @@ -62,6 +63,21 @@ defmodule Pleroma.Web.AdminAPI.ReportView do } end + def render("index_notes.json", %{notes: notes}) when is_list(notes) do + Enum.map(notes, &render(__MODULE__, "show_note.json", &1)) + end + + def render("index_notes.json", _), do: [] + + def render("show_note.json", %{content: content, user_id: user_id}) do + user = User.get_by_id(user_id) + + %{ + content: content, + user: merge_account_views(user) + } + end + defp merge_account_views(%User{} = user) do Pleroma.Web.MastodonAPI.AccountView.render("show.json", %{user: user}) |> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user})) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index e6c4f6f14..af220a98b 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -187,7 +187,7 @@ defmodule Pleroma.Web.Router do get("/grouped_reports", AdminAPIController, :list_grouped_reports) get("/reports/:id", AdminAPIController, :report_show) patch("/reports", AdminAPIController, :reports_update) - post("/reports/:id/respond", AdminAPIController, :report_respond) + post("/reports/:id/notes", AdminAPIController, :report_notes_create) put("/statuses/:id", AdminAPIController, :status_update) delete("/statuses/:id", AdminAPIController, :status_delete) -- cgit v1.2.3 From 08c89fd2b89614baaf4bfce067cfec9db96f2d2c Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Fri, 6 Dec 2019 17:17:24 +0900 Subject: Fix incorrect report count --- lib/pleroma/pagination.ex | 5 ++++- lib/pleroma/web/admin_api/views/report_view.ex | 5 +++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex index 9d279fba7..183ef770e 100644 --- a/lib/pleroma/pagination.ex +++ b/lib/pleroma/pagination.ex @@ -35,7 +35,10 @@ defmodule Pleroma.Pagination do end def fetch_paginated(query, %{"total" => true} = params, :offset) do - total = Repo.aggregate(query, :count, :id) + total = + query + |> Ecto.Query.exclude(:left_join) + |> Repo.aggregate(:count, :id) %{ total: total, diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex index 80ca62691..f5c6ba401 100644 --- a/lib/pleroma/web/admin_api/views/report_view.ex +++ b/lib/pleroma/web/admin_api/views/report_view.ex @@ -69,12 +69,13 @@ defmodule Pleroma.Web.AdminAPI.ReportView do def render("index_notes.json", _), do: [] - def render("show_note.json", %{content: content, user_id: user_id}) do + def render("show_note.json", %{content: content, user_id: user_id, inserted_at: inserted_at}) do user = User.get_by_id(user_id) %{ content: content, - user: merge_account_views(user) + user: merge_account_views(user), + created_at: inserted_at } end -- cgit v1.2.3 From a7f77785c2675b5f9f7ede85e92ec50444945e54 Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Sun, 8 Dec 2019 11:27:23 +0300 Subject: Implement report notes destruction --- lib/pleroma/moderation_log.ex | 42 +++++++++++++++++++---- lib/pleroma/report_note.ex | 10 +++--- lib/pleroma/web/admin_api/admin_api_controller.ex | 26 +++++++++++--- lib/pleroma/web/admin_api/views/report_view.ex | 10 ++++-- lib/pleroma/web/router.ex | 1 + 5 files changed, 73 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index 706f089dc..c81477f48 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -128,17 +128,35 @@ defmodule Pleroma.ModerationLog do {:ok, ModerationLog} | {:error, any} def insert_log(%{ actor: %User{} = actor, - action: "report_response", + action: "report_note", subject: %Activity{} = subject, text: text }) do %ModerationLog{ data: %{ "actor" => user_to_map(actor), - "action" => "report_response", + "action" => "report_note", "subject" => report_to_map(subject), - "text" => text, - "message" => "" + "text" => text + } + } + |> insert_log_entry_with_message() + end + + @spec insert_log(%{actor: User, subject: Activity, action: String.t(), text: String.t()}) :: + {:ok, ModerationLog} | {:error, any} + def insert_log(%{ + actor: %User{} = actor, + action: "report_note_delete", + subject: %Activity{} = subject, + text: text + }) do + %ModerationLog{ + data: %{ + "actor" => user_to_map(actor), + "action" => "report_note_delete", + "subject" => report_to_map(subject), + "text" => text } } |> insert_log_entry_with_message() @@ -556,12 +574,24 @@ defmodule Pleroma.ModerationLog do def get_log_entry_message(%ModerationLog{ data: %{ "actor" => %{"nickname" => actor_nickname}, - "action" => "report_response", + "action" => "report_note", + "subject" => %{"id" => subject_id, "type" => "report"}, + "text" => text + } + }) do + "@#{actor_nickname} added note '#{text}' to report ##{subject_id}" + end + + @spec get_log_entry_message(ModerationLog) :: String.t() + def get_log_entry_message(%ModerationLog{ + data: %{ + "actor" => %{"nickname" => actor_nickname}, + "action" => "report_note_delete", "subject" => %{"id" => subject_id, "type" => "report"}, "text" => text } }) do - "@#{actor_nickname} responded with '#{text}' to report ##{subject_id}" + "@#{actor_nickname} deleted note '#{text}' from report ##{subject_id}" end @spec get_log_entry_message(ModerationLog) :: String.t() diff --git a/lib/pleroma/report_note.ex b/lib/pleroma/report_note.ex index 91102696b..0db86d1a1 100644 --- a/lib/pleroma/report_note.ex +++ b/lib/pleroma/report_note.ex @@ -38,9 +38,11 @@ defmodule Pleroma.ReportNote do |> Repo.insert() end - def get_all_for_status(status_id) do - ReportNote - |> where(activity_id: ^status_id) - |> Repo.all() + @spec destroy(FlakeId.Ecto.CompatType.t()) :: + {:ok, ReportNote.t()} | {:error, Changeset.t()} + def destroy(id) do + from(r in ReportNote, where: r.id == ^id) + |> Repo.one() + |> Repo.delete() end end diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index ee32bac45..d34952cda 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -691,14 +691,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end def report_notes_create(%{assigns: %{user: user}} = conn, %{ - "id" => status_id, + "id" => report_id, "content" => content }) do - with {:ok, _} <- ReportNote.create(user.id, status_id, content) do + with {:ok, _} <- ReportNote.create(user.id, report_id, content) do ModerationLog.insert_log(%{ - action: "report_response", + action: "report_note", actor: user, - subject: Activity.get_by_id(status_id), + subject: Activity.get_by_id(report_id), text: content }) @@ -708,6 +708,24 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do end end + def report_notes_delete(%{assigns: %{user: user}} = conn, %{ + "id" => note_id, + "report_id" => report_id + }) do + with {:ok, note} <- ReportNote.destroy(note_id) do + ModerationLog.insert_log(%{ + action: "report_note_delete", + actor: user, + subject: Activity.get_by_id(report_id), + text: note.content + }) + + json_response(conn, :no_content, "") + else + _ -> json_response(conn, :bad_request, "") + end + end + def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"]) diff --git a/lib/pleroma/web/admin_api/views/report_view.ex b/lib/pleroma/web/admin_api/views/report_view.ex index f5c6ba401..294861044 100644 --- a/lib/pleroma/web/admin_api/views/report_view.ex +++ b/lib/pleroma/web/admin_api/views/report_view.ex @@ -69,13 +69,19 @@ defmodule Pleroma.Web.AdminAPI.ReportView do def render("index_notes.json", _), do: [] - def render("show_note.json", %{content: content, user_id: user_id, inserted_at: inserted_at}) do + def render("show_note.json", %{ + id: id, + content: content, + user_id: user_id, + inserted_at: inserted_at + }) do user = User.get_by_id(user_id) %{ + id: id, content: content, user: merge_account_views(user), - created_at: inserted_at + created_at: Utils.to_masto_date(inserted_at) } end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index af220a98b..3baaa73d9 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -188,6 +188,7 @@ defmodule Pleroma.Web.Router do get("/reports/:id", AdminAPIController, :report_show) patch("/reports", AdminAPIController, :reports_update) post("/reports/:id/notes", AdminAPIController, :report_notes_create) + delete("/reports/:report_id/notes/:id", AdminAPIController, :report_notes_delete) put("/statuses/:id", AdminAPIController, :status_update) delete("/statuses/:id", AdminAPIController, :status_delete) -- cgit v1.2.3