From 5e4fde1d3d49ec56fae3b199fb4af51057e2dffd Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Tue, 27 Aug 2019 20:48:16 +0300 Subject: [PATCH 1/7] Filter logs by date --- lib/pleroma/moderation_log.ex | 37 ++++++++++++++- lib/pleroma/user/info.ex | 4 +- .../web/admin_api/admin_api_controller.ex | 8 +++- .../admin_api/admin_api_controller_test.exs | 46 +++++++++++++++++++ 4 files changed, 89 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index 1ef6fe67a..2164ecfc2 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -14,13 +14,46 @@ defmodule Pleroma.ModerationLog do timestamps() end - def get_all(page, page_size) do + def get_all(params) do + params + |> get_all_query() + |> maybe_filter_by_date(params) + |> Repo.all() + end + + defp maybe_filter_by_date(query, %{start_date: nil, end_date: nil}), do: query + + defp maybe_filter_by_date(query, %{start_date: start_date, end_date: nil}) do + from(q in query, + where: q.inserted_at >= ^parse_datetime(start_date) + ) + end + + defp maybe_filter_by_date(query, %{start_date: nil, end_date: end_date}) do + from(q in query, + where: q.inserted_at <= ^parse_datetime(end_date) + ) + end + + defp maybe_filter_by_date(query, %{start_date: start_date, end_date: end_date}) do + from(q in query, + where: q.inserted_at >= ^parse_datetime(start_date), + where: q.inserted_at <= ^parse_datetime(end_date) + ) + end + + defp get_all_query(%{page: page, page_size: page_size}) do from(q in __MODULE__, order_by: [desc: q.inserted_at], limit: ^page_size, offset: ^((page - 1) * page_size) ) - |> Repo.all() + end + + defp parse_datetime(datetime) do + {:ok, parsed_datetime, _} = DateTime.from_iso8601(datetime) + + parsed_datetime end def insert_log(%{ diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 779bfbc18..7027c947b 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -318,9 +318,7 @@ defp valid_field?(%{"name" => name, "value" => value}) do name_limit = Pleroma.Config.get([:instance, :account_field_name_length], 255) value_limit = Pleroma.Config.get([:instance, :account_field_value_length], 255) - is_binary(name) && - is_binary(value) && - String.length(name) <= name_limit && + is_binary(name) && is_binary(value) && String.length(name) <= name_limit && String.length(value) <= value_limit end diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 544b9d7d8..065394a24 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -539,7 +539,13 @@ def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do def list_log(conn, params) do {page, page_size} = page_params(params) - log = ModerationLog.get_all(page, page_size) + log = + ModerationLog.get_all(%{ + page: page, + page_size: page_size, + start_date: params["start_date"], + end_date: params["end_date"] + }) conn |> put_view(ModerationLogView) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 4e2c27431..a7269aee9 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -2348,6 +2348,52 @@ test "returns the log with pagination", %{conn: conn, admin: admin} do assert second_entry["message"] == "@#{admin.nickname} followed relay: https://example.org/relay" end + + test "filters log by date", %{conn: conn, admin: admin} do + first_date = "2017-08-15T15:47:06Z" + second_date = "2017-08-20T15:47:06Z" + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_follow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.from_iso8601!(first_date) + }) + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_unfollow", + target: "https://example.org/relay" + }, + inserted_at: NaiveDateTime.from_iso8601!(second_date) + }) + + conn1 = + get( + conn, + "/api/pleroma/admin/moderation_log?start_date=#{second_date}" + ) + + response1 = json_response(conn1, 200) + [first_entry] = response1 + + assert response1 |> length() == 1 + assert first_entry["data"]["action"] == "relay_unfollow" + + assert first_entry["message"] == + "@#{admin.nickname} unfollowed relay: https://example.org/relay" + end end end From f182f0f6bd89a2f2e3c4a6000c772512b239fe54 Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Sat, 31 Aug 2019 00:57:15 +0300 Subject: [PATCH 2/7] Add ability to search moderation logs --- lib/pleroma/moderation_log.ex | 209 ++++++++++++------ .../web/admin_api/admin_api_controller.ex | 4 +- test/moderation_log_test.exs | 36 ++- .../admin_api/admin_api_controller_test.exs | 61 ++++- 4 files changed, 220 insertions(+), 90 deletions(-) diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index 2164ecfc2..c72a413b6 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -18,6 +18,8 @@ def get_all(params) do params |> get_all_query() |> maybe_filter_by_date(params) + |> maybe_filter_by_user(params) + |> maybe_filter_by_search(params) |> Repo.all() end @@ -42,6 +44,23 @@ defp maybe_filter_by_date(query, %{start_date: start_date, end_date: end_date}) ) end + defp maybe_filter_by_user(query, %{user_id: nil}), do: query + + defp maybe_filter_by_user(query, %{user_id: user_id}) do + from(q in query, + where: fragment("(?)->'actor'->>'id' = ?", q.data, ^user_id) + ) + end + + defp maybe_filter_by_search(query, %{search: search}) when is_nil(search) or search == "", + do: query + + defp maybe_filter_by_search(query, %{search: search}) do + from(q in query, + where: fragment("(?)->>'message' ILIKE ?", q.data, ^"%#{search}%") + ) + end + defp get_all_query(%{page: page, page_size: page_size}) do from(q in __MODULE__, order_by: [desc: q.inserted_at], @@ -56,52 +75,71 @@ defp parse_datetime(datetime) do parsed_datetime end + @spec insert_log(%{actor: User, subject: User, action: String.t(), permission: String.t()}) :: + {:ok, ModerationLog} | {:error, any} def insert_log(%{ actor: %User{} = actor, subject: %User{} = subject, action: action, permission: permission }) do - Repo.insert(%ModerationLog{ + %ModerationLog{ data: %{ - actor: user_to_map(actor), - subject: user_to_map(subject), - action: action, - permission: permission + "actor" => user_to_map(actor), + "subject" => user_to_map(subject), + "action" => action, + "permission" => permission, + "message" => "" } - }) + } + |> insert_log_entry_with_message() end + @spec insert_log(%{actor: User, subject: User, action: String.t()}) :: + {:ok, ModerationLog} | {:error, any} def insert_log(%{ actor: %User{} = actor, action: "report_update", subject: %Activity{data: %{"type" => "Flag"}} = subject }) do - Repo.insert(%ModerationLog{ + %ModerationLog{ data: %{ - actor: user_to_map(actor), - action: "report_update", - subject: report_to_map(subject) + "actor" => user_to_map(actor), + "action" => "report_update", + "subject" => report_to_map(subject), + "message" => "" } - }) + } + |> 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_response", subject: %Activity{} = subject, text: text }) do - Repo.insert(%ModerationLog{ + %ModerationLog{ data: %{ - actor: user_to_map(actor), - action: "report_response", - subject: report_to_map(subject), - text: text + "actor" => user_to_map(actor), + "action" => "report_response", + "subject" => report_to_map(subject), + "text" => text, + "message" => "" } - }) + } + |> insert_log_entry_with_message() end + @spec insert_log(%{ + actor: User, + subject: Activity, + action: String.t(), + sensitive: String.t(), + visibility: String.t() + }) :: {:ok, ModerationLog} | {:error, any} def insert_log(%{ actor: %User{} = actor, action: "status_update", @@ -109,41 +147,49 @@ def insert_log(%{ sensitive: sensitive, visibility: visibility }) do - Repo.insert(%ModerationLog{ + %ModerationLog{ data: %{ - actor: user_to_map(actor), - action: "status_update", - subject: status_to_map(subject), - sensitive: sensitive, - visibility: visibility + "actor" => user_to_map(actor), + "action" => "status_update", + "subject" => status_to_map(subject), + "sensitive" => sensitive, + "visibility" => visibility, + "message" => "" } - }) + } + |> insert_log_entry_with_message() end + @spec insert_log(%{actor: User, action: String.t(), subject_id: String.t()}) :: + {:ok, ModerationLog} | {:error, any} def insert_log(%{ actor: %User{} = actor, action: "status_delete", subject_id: subject_id }) do - Repo.insert(%ModerationLog{ + %ModerationLog{ data: %{ - actor: user_to_map(actor), - action: "status_delete", - subject_id: subject_id + "actor" => user_to_map(actor), + "action" => "status_delete", + "subject_id" => subject_id, + "message" => "" } - }) + } + |> insert_log_entry_with_message() end @spec insert_log(%{actor: User, subject: User, action: String.t()}) :: {:ok, ModerationLog} | {:error, any} def insert_log(%{actor: %User{} = actor, subject: subject, action: action}) do - Repo.insert(%ModerationLog{ + %ModerationLog{ data: %{ - actor: user_to_map(actor), - action: action, - subject: user_to_map(subject) + "actor" => user_to_map(actor), + "action" => action, + "subject" => user_to_map(subject), + "message" => "" } - }) + } + |> insert_log_entry_with_message() end @spec insert_log(%{actor: User, subjects: [User], action: String.t()}) :: @@ -151,97 +197,124 @@ def insert_log(%{actor: %User{} = actor, subject: subject, action: action}) do def insert_log(%{actor: %User{} = actor, subjects: subjects, action: action}) do subjects = Enum.map(subjects, &user_to_map/1) - Repo.insert(%ModerationLog{ + %ModerationLog{ data: %{ - actor: user_to_map(actor), - action: action, - subjects: subjects + "actor" => user_to_map(actor), + "action" => action, + "subjects" => subjects, + "message" => "" } - }) + } + |> insert_log_entry_with_message() end + @spec insert_log(%{actor: User, action: String.t(), followed: User, follower: User}) :: + {:ok, ModerationLog} | {:error, any} def insert_log(%{ actor: %User{} = actor, followed: %User{} = followed, follower: %User{} = follower, action: "follow" }) do - Repo.insert(%ModerationLog{ + %ModerationLog{ data: %{ - actor: user_to_map(actor), - action: "follow", - followed: user_to_map(followed), - follower: user_to_map(follower) + "actor" => user_to_map(actor), + "action" => "follow", + "followed" => user_to_map(followed), + "follower" => user_to_map(follower), + "message" => "" } - }) + } + |> insert_log_entry_with_message() end + @spec insert_log(%{actor: User, action: String.t(), followed: User, follower: User}) :: + {:ok, ModerationLog} | {:error, any} def insert_log(%{ actor: %User{} = actor, followed: %User{} = followed, follower: %User{} = follower, action: "unfollow" }) do - Repo.insert(%ModerationLog{ + %ModerationLog{ data: %{ - actor: user_to_map(actor), - action: "unfollow", - followed: user_to_map(followed), - follower: user_to_map(follower) + "actor" => user_to_map(actor), + "action" => "unfollow", + "followed" => user_to_map(followed), + "follower" => user_to_map(follower), + "message" => "" } - }) + } + |> insert_log_entry_with_message() end + @spec insert_log(%{actor: User, action: String.t(), nicknames: [String.t()], tags: [String.t()]}) :: + {:ok, ModerationLog} | {:error, any} def insert_log(%{ actor: %User{} = actor, nicknames: nicknames, tags: tags, action: action }) do - Repo.insert(%ModerationLog{ + %ModerationLog{ data: %{ - actor: user_to_map(actor), - nicknames: nicknames, - tags: tags, - action: action + "actor" => user_to_map(actor), + "nicknames" => nicknames, + "tags" => tags, + "action" => action, + "message" => "" } - }) + } + |> insert_log_entry_with_message() end + @spec insert_log(%{actor: User, action: String.t(), target: String.t()}) :: + {:ok, ModerationLog} | {:error, any} def insert_log(%{ actor: %User{} = actor, action: action, target: target }) when action in ["relay_follow", "relay_unfollow"] do - Repo.insert(%ModerationLog{ + %ModerationLog{ data: %{ - actor: user_to_map(actor), - action: action, - target: target + "actor" => user_to_map(actor), + "action" => action, + "target" => target, + "message" => "" } - }) + } + |> insert_log_entry_with_message() + end + + @spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any} + + defp insert_log_entry_with_message(entry) do + entry.data["message"] + |> put_in(get_log_entry_message(entry)) + |> Repo.insert() end defp user_to_map(%User{} = user) do user |> Map.from_struct() |> Map.take([:id, :nickname]) - |> Map.put(:type, "user") + |> Map.new(fn {k, v} -> {Atom.to_string(k), v} end) + |> Map.put("type", "user") end defp report_to_map(%Activity{} = report) do %{ - type: "report", - id: report.id, - state: report.data["state"] + "type" => "report", + "id" => report.id, + "state" => report.data["state"] } end defp status_to_map(%Activity{} = status) do %{ - type: "status", - id: status.id + "type" => "status", + "id" => status.id } end diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 065394a24..135c6ae87 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -544,7 +544,9 @@ def list_log(conn, params) do page: page, page_size: page_size, start_date: params["start_date"], - end_date: params["end_date"] + end_date: params["end_date"], + user_id: params["user_id"], + search: params["search"] }) conn diff --git a/test/moderation_log_test.exs b/test/moderation_log_test.exs index c78708471..a39a00e02 100644 --- a/test/moderation_log_test.exs +++ b/test/moderation_log_test.exs @@ -30,8 +30,7 @@ test "logging user deletion by moderator", %{moderator: moderator, subject1: sub log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == - "@#{moderator.nickname} deleted user @#{subject1.nickname}" + assert log.data["message"] == "@#{moderator.nickname} deleted user @#{subject1.nickname}" end test "logging user creation by moderator", %{ @@ -48,7 +47,7 @@ test "logging user creation by moderator", %{ log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == + assert log.data["message"] == "@#{moderator.nickname} created users: @#{subject1.nickname}, @#{subject2.nickname}" end @@ -63,7 +62,7 @@ test "logging user follow by admin", %{admin: admin, subject1: subject1, subject log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == + assert log.data["message"] == "@#{admin.nickname} made @#{subject2.nickname} follow @#{subject1.nickname}" end @@ -78,7 +77,7 @@ test "logging user unfollow by admin", %{admin: admin, subject1: subject1, subje log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == + assert log.data["message"] == "@#{admin.nickname} made @#{subject2.nickname} unfollow @#{subject1.nickname}" end @@ -100,8 +99,7 @@ test "logging user tagged by admin", %{admin: admin, subject1: subject1, subject tags = ["foo", "bar"] |> Enum.join(", ") - assert ModerationLog.get_log_entry_message(log) == - "@#{admin.nickname} added tags: #{tags} to users: #{users}" + assert log.data["message"] == "@#{admin.nickname} added tags: #{tags} to users: #{users}" end test "logging user untagged by admin", %{admin: admin, subject1: subject1, subject2: subject2} do @@ -122,7 +120,7 @@ test "logging user untagged by admin", %{admin: admin, subject1: subject1, subje tags = ["foo", "bar"] |> Enum.join(", ") - assert ModerationLog.get_log_entry_message(log) == + assert log.data["message"] == "@#{admin.nickname} removed tags: #{tags} from users: #{users}" end @@ -137,8 +135,7 @@ test "logging user grant by moderator", %{moderator: moderator, subject1: subjec log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == - "@#{moderator.nickname} made @#{subject1.nickname} moderator" + assert log.data["message"] == "@#{moderator.nickname} made @#{subject1.nickname} moderator" end test "logging user revoke by moderator", %{moderator: moderator, subject1: subject1} do @@ -152,7 +149,7 @@ test "logging user revoke by moderator", %{moderator: moderator, subject1: subje log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == + assert log.data["message"] == "@#{moderator.nickname} revoked moderator role from @#{subject1.nickname}" end @@ -166,7 +163,7 @@ test "logging relay follow", %{moderator: moderator} do log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == + assert log.data["message"] == "@#{moderator.nickname} followed relay: https://example.org/relay" end @@ -180,7 +177,7 @@ test "logging relay unfollow", %{moderator: moderator} do log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == + assert log.data["message"] == "@#{moderator.nickname} unfollowed relay: https://example.org/relay" end @@ -202,7 +199,7 @@ test "logging report update", %{moderator: moderator} do log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == + assert log.data["message"] == "@#{moderator.nickname} updated report ##{report.id} with 'resolved' state" end @@ -224,7 +221,7 @@ test "logging report response", %{moderator: moderator} do log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == + assert log.data["message"] == "@#{moderator.nickname} responded with 'look at this' to report ##{report.id}" end @@ -242,7 +239,7 @@ test "logging status sensitivity update", %{moderator: moderator} do log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == + assert log.data["message"] == "@#{moderator.nickname} updated status ##{note.id}, set sensitive: 'true'" end @@ -260,7 +257,7 @@ test "logging status visibility update", %{moderator: moderator} do log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == + assert log.data["message"] == "@#{moderator.nickname} updated status ##{note.id}, set visibility: 'private'" end @@ -278,7 +275,7 @@ test "logging status sensitivity & visibility update", %{moderator: moderator} d log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == + assert log.data["message"] == "@#{moderator.nickname} updated status ##{note.id}, set sensitive: 'true', visibility: 'private'" end @@ -294,8 +291,7 @@ test "logging status deletion", %{moderator: moderator} do log = Repo.one(ModerationLog) - assert ModerationLog.get_log_entry_message(log) == - "@#{moderator.nickname} deleted status ##{note.id}" + assert log.data["message"] == "@#{moderator.nickname} deleted status ##{note.id}" end end end diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index a7269aee9..eaf847b25 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -2251,8 +2251,9 @@ test "returns private statuses with godmode on", %{conn: conn, user: user} do describe "GET /api/pleroma/admin/moderation_log" do setup %{conn: conn} do admin = insert(:user, info: %{is_admin: true}) + moderator = insert(:user, info: %{is_moderator: true}) - %{conn: assign(conn, :user, admin), admin: admin} + %{conn: assign(conn, :user, admin), admin: admin, moderator: moderator} end test "returns the log", %{conn: conn, admin: admin} do @@ -2394,6 +2395,64 @@ test "filters log by date", %{conn: conn, admin: admin} do assert first_entry["message"] == "@#{admin.nickname} unfollowed relay: https://example.org/relay" end + + test "returns log filtered by user", %{conn: conn, admin: admin, moderator: moderator} do + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => admin.id, + "nickname" => admin.nickname, + "type" => "user" + }, + action: "relay_follow", + target: "https://example.org/relay" + } + }) + + Repo.insert(%ModerationLog{ + data: %{ + actor: %{ + "id" => moderator.id, + "nickname" => moderator.nickname, + "type" => "user" + }, + action: "relay_unfollow", + target: "https://example.org/relay" + } + }) + + conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}") + + response1 = json_response(conn1, 200) + [first_entry] = response1 + + assert response1 |> length() == 1 + assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id + end + + test "returns log filtered by search", %{conn: conn, moderator: moderator} do + ModerationLog.insert_log(%{ + actor: moderator, + action: "relay_follow", + target: "https://example.org/relay" + }) + + ModerationLog.insert_log(%{ + actor: moderator, + action: "relay_unfollow", + target: "https://example.org/relay" + }) + + conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo") + + response1 = json_response(conn1, 200) + [first_entry] = response1 + + assert response1 |> length() == 1 + + assert get_in(first_entry, ["data", "message"]) == + "@#{moderator.nickname} unfollowed relay: https://example.org/relay" + end end end From 4d6e22bb9b718846883e92851ba22e9809b6b93d Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Sat, 31 Aug 2019 01:09:48 +0300 Subject: [PATCH 3/7] Style --- lib/pleroma/moderation_log.ex | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index c72a413b6..89a5e13c3 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -248,8 +248,12 @@ def insert_log(%{ |> insert_log_entry_with_message() end - @spec insert_log(%{actor: User, action: String.t(), nicknames: [String.t()], tags: [String.t()]}) :: - {:ok, ModerationLog} | {:error, any} + @spec insert_log(%{ + actor: User, + action: String.t(), + nicknames: [String.t()], + tags: [String.t()] + }) :: {:ok, ModerationLog} | {:error, any} def insert_log(%{ actor: %User{} = actor, nicknames: nicknames, From 9c96b17e16a4911d3e20149e1b54b12baaf71617 Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Sun, 1 Sep 2019 21:23:30 +0300 Subject: [PATCH 4/7] Add pagination to logs --- lib/pleroma/moderation_log.ex | 29 +++++++++++++------ .../admin_api/views/moderation_log_view.ex | 5 +++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/lib/pleroma/moderation_log.ex b/lib/pleroma/moderation_log.ex index 89a5e13c3..352cad433 100644 --- a/lib/pleroma/moderation_log.ex +++ b/lib/pleroma/moderation_log.ex @@ -15,12 +15,18 @@ defmodule Pleroma.ModerationLog do end def get_all(params) do - params - |> get_all_query() - |> maybe_filter_by_date(params) - |> maybe_filter_by_user(params) - |> maybe_filter_by_search(params) - |> Repo.all() + base_query = + get_all_query() + |> maybe_filter_by_date(params) + |> maybe_filter_by_user(params) + |> maybe_filter_by_search(params) + + query_with_pagination = base_query |> paginate_query(params) + + %{ + items: Repo.all(query_with_pagination), + count: Repo.aggregate(base_query, :count, :id) + } end defp maybe_filter_by_date(query, %{start_date: nil, end_date: nil}), do: query @@ -61,14 +67,19 @@ defp maybe_filter_by_search(query, %{search: search}) do ) end - defp get_all_query(%{page: page, page_size: page_size}) do - from(q in __MODULE__, - order_by: [desc: q.inserted_at], + defp paginate_query(query, %{page: page, page_size: page_size}) do + from(q in query, limit: ^page_size, offset: ^((page - 1) * page_size) ) end + defp get_all_query do + from(q in __MODULE__, + order_by: [desc: q.inserted_at] + ) + end + defp parse_datetime(datetime) do {:ok, parsed_datetime, _} = DateTime.from_iso8601(datetime) diff --git a/lib/pleroma/web/admin_api/views/moderation_log_view.ex b/lib/pleroma/web/admin_api/views/moderation_log_view.ex index b3fc7cfe5..e7752d1f3 100644 --- a/lib/pleroma/web/admin_api/views/moderation_log_view.ex +++ b/lib/pleroma/web/admin_api/views/moderation_log_view.ex @@ -8,7 +8,10 @@ defmodule Pleroma.Web.AdminAPI.ModerationLogView do alias Pleroma.ModerationLog def render("index.json", %{log: log}) do - render_many(log, __MODULE__, "show.json", as: :log_entry) + %{ + items: render_many(log.items, __MODULE__, "show.json", as: :log_entry), + total: log.count + } end def render("show.json", %{log_entry: log_entry}) do From c5ffbfb8d547199f2345e28f085dd12e8b443f21 Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Sun, 1 Sep 2019 21:25:55 +0300 Subject: [PATCH 5/7] Changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fdcb014a..0d44944eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,6 +95,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Mix Tasks: `mix pleroma.database fix_likes_collections` - Federation: Remove `likes` from objects. - Admin API: Added moderation log +- Admin API: Added moderation log filters (user/start date/end date/search/pagination) ### Changed - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text From 6c2fd1b78bbbb4486a5dddeffa053199ba8cc015 Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Sun, 1 Sep 2019 21:38:15 +0300 Subject: [PATCH 6/7] Fix tests --- .../admin_api/admin_api_controller_test.exs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index eaf847b25..b87fffc34 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -2286,9 +2286,9 @@ test "returns the log", %{conn: conn, admin: admin} do conn = get(conn, "/api/pleroma/admin/moderation_log") response = json_response(conn, 200) - [first_entry, second_entry] = response + [first_entry, second_entry] = response["items"] - assert response |> length() == 2 + assert response["total"] == 2 assert first_entry["data"]["action"] == "relay_unfollow" assert first_entry["message"] == @@ -2330,9 +2330,10 @@ test "returns the log with pagination", %{conn: conn, admin: admin} do conn1 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=1") response1 = json_response(conn1, 200) - [first_entry] = response1 + [first_entry] = response1["items"] - assert response1 |> length() == 1 + assert response1["total"] == 2 + assert response1["items"] |> length() == 1 assert first_entry["data"]["action"] == "relay_unfollow" assert first_entry["message"] == @@ -2341,9 +2342,10 @@ test "returns the log with pagination", %{conn: conn, admin: admin} do conn2 = get(conn, "/api/pleroma/admin/moderation_log?page_size=1&page=2") response2 = json_response(conn2, 200) - [second_entry] = response2 + [second_entry] = response2["items"] - assert response2 |> length() == 1 + assert response2["total"] == 2 + assert response2["items"] |> length() == 1 assert second_entry["data"]["action"] == "relay_follow" assert second_entry["message"] == @@ -2387,9 +2389,9 @@ test "filters log by date", %{conn: conn, admin: admin} do ) response1 = json_response(conn1, 200) - [first_entry] = response1 + [first_entry] = response1["items"] - assert response1 |> length() == 1 + assert response1["total"] == 1 assert first_entry["data"]["action"] == "relay_unfollow" assert first_entry["message"] == @@ -2424,9 +2426,9 @@ test "returns log filtered by user", %{conn: conn, admin: admin, moderator: mode conn1 = get(conn, "/api/pleroma/admin/moderation_log?user_id=#{moderator.id}") response1 = json_response(conn1, 200) - [first_entry] = response1 + [first_entry] = response1["items"] - assert response1 |> length() == 1 + assert response1["total"] == 1 assert get_in(first_entry, ["data", "actor", "id"]) == moderator.id end @@ -2446,9 +2448,9 @@ test "returns log filtered by search", %{conn: conn, moderator: moderator} do conn1 = get(conn, "/api/pleroma/admin/moderation_log?search=unfo") response1 = json_response(conn1, 200) - [first_entry] = response1 + [first_entry] = response1["items"] - assert response1 |> length() == 1 + assert response1["total"] == 1 assert get_in(first_entry, ["data", "message"]) == "@#{moderator.nickname} unfollowed relay: https://example.org/relay" From 39a4892929e160186c7cbeef0f2abe6131758511 Mon Sep 17 00:00:00 2001 From: Maxim Filippov Date: Thu, 26 Sep 2019 19:20:47 +0300 Subject: [PATCH 7/7] Add docs --- docs/api/admin_api.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/api/admin_api.md b/docs/api/admin_api.md index d4e08f221..573111416 100644 --- a/docs/api/admin_api.md +++ b/docs/api/admin_api.md @@ -731,7 +731,11 @@ Compile time settings (need instance reboot): - Method `GET` - Params: - *optional* `page`: **integer** page number - - *optional* `page_size`: **integer** number of users per page (default is `50`) + - *optional* `page_size`: **integer** number of log entries per page (default is `50`) + - *optional* `start_date`: **datetime (ISO 8601)** filter logs by creation date, start from `start_date`. Accepts datetime in ISO 8601 format (YYYY-MM-DDThh:mm:ss), e.g. `2005-08-09T18:31:42` + - *optional* `end_date`: **datetime (ISO 8601)** filter logs by creation date, end by from `end_date`. Accepts datetime in ISO 8601 format (YYYY-MM-DDThh:mm:ss), e.g. 2005-08-09T18:31:42 + - *optional* `user_id`: **integer** filter logs by actor's id + - *optional* `search`: **string** search logs by the log message - Response: ```json