Merge branch 'feature/2438-users-posts-total-count' into 'develop'

Feature/2438 users/instances posts total count

Closes #2438

See merge request pleroma/pleroma!3270
This commit is contained in:
feld 2021-02-03 14:21:57 +00:00
commit 8d2ea95402
6 changed files with 125 additions and 64 deletions

View file

@ -10,19 +10,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- **Breaking**: Changed `mix pleroma.user toggle_confirmed` to `mix pleroma.user confirm` - **Breaking**: Changed `mix pleroma.user toggle_confirmed` to `mix pleroma.user confirm`
- **Breaking**: Changed `mix pleroma.user toggle_activated` to `mix pleroma.user activate/deactivate` - **Breaking**: Changed `mix pleroma.user toggle_activated` to `mix pleroma.user activate/deactivate`
- **Breaking**: AdminAPI changed User field `confirmation_pending` to `is_confirmed`
- **Breaking**: AdminAPI changed User field `approval_pending` to `is_approved`
- **Breaking**: AdminAPI changed User field `deactivated` to `is_active`
- Polls now always return a `voters_count`, even if they are single-choice. - Polls now always return a `voters_count`, even if they are single-choice.
- Admin Emails: The ap id is used as the user link in emails now. - Admin Emails: The ap id is used as the user link in emails now.
- Improved registration workflow for email confirmation and account approval modes. - Improved registration workflow for email confirmation and account approval modes.
- Search: When using Postgres 11+, Pleroma will use the `websearch_to_tsvector` function to parse search queries. - Search: When using Postgres 11+, Pleroma will use the `websearch_to_tsvector` function to parse search queries.
- Emoji: Support the full Unicode 13.1 set of Emoji for reactions, plus regional indicators. - Emoji: Support the full Unicode 13.1 set of Emoji for reactions, plus regional indicators.
- Admin API: Reports now ordered by newest
- Deprecated `Pleroma.Uploaders.S3, :public_endpoint`. Now `Pleroma.Upload, :base_url` is the standard configuration key for all uploaders. - Deprecated `Pleroma.Uploaders.S3, :public_endpoint`. Now `Pleroma.Upload, :base_url` is the standard configuration key for all uploaders.
- Improved Apache webserver support: updated sample configuration, MediaProxy cache invalidation verified with the included sample script - Improved Apache webserver support: updated sample configuration, MediaProxy cache invalidation verified with the included sample script
- Improve OAuth 2.0 provider support. A missing `fqn` field was added to the response, but does not expose the user's email address. - Improve OAuth 2.0 provider support. A missing `fqn` field was added to the response, but does not expose the user's email address.
<details>
<summary>API Changes</summary>
- **Breaking:** AdminAPI changed User field `confirmation_pending` to `is_confirmed`
- **Breaking:** AdminAPI changed User field `approval_pending` to `is_approved`
- **Breaking**: AdminAPI changed User field `deactivated` to `is_active`
- **Breaking:** AdminAPI `GET /api/pleroma/admin/users/:nickname_or_id/statuses` changed response format and added the number of total users posts.
- **Breaking:** AdminAPI `GET /api/pleroma/admin/instances/:instance/statuses` changed response format and added the number of total users posts.
- Admin API: Reports now ordered by newest
</details>
### Added ### Added
- Reports now generate notifications for admins and mods. - Reports now generate notifications for admins and mods.

View file

@ -287,7 +287,18 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- *optional* `with_reblogs`: `true`/`false` allows to see reblogs (default is false) - *optional* `with_reblogs`: `true`/`false` allows to see reblogs (default is false)
- Response: - Response:
- On failure: `Not found` - On failure: `Not found`
- On success: JSON array of user's latest statuses - On success: JSON, where:
- `total`: total count of the statuses for the user
- `activities`: list of the statuses for the user
```json
{
"total" : 1,
"activities": [
// activities list
]
}
```
## `GET /api/pleroma/admin/instances/:instance/statuses` ## `GET /api/pleroma/admin/instances/:instance/statuses`
@ -300,7 +311,18 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- *optional* `with_reblogs`: `true`/`false` allows to see reblogs (default is false) - *optional* `with_reblogs`: `true`/`false` allows to see reblogs (default is false)
- Response: - Response:
- On failure: `Not found` - On failure: `Not found`
- On success: JSON array of instance's latest statuses - On success: JSON, where:
- `total`: total count of the statuses for the instance
- `activities`: list of the statuses for the instance
```json
{
"total" : 1,
"activities": [
// activities list
]
}
```
## `GET /api/pleroma/admin/statuses` ## `GET /api/pleroma/admin/statuses`

View file

@ -591,7 +591,21 @@ def fetch_user_abstract_activities(user, reading_user, params \\ %{}) do
|> Enum.reverse() |> Enum.reverse()
end end
def fetch_user_activities(user, reading_user, params \\ %{}) do def fetch_user_activities(user, reading_user, params \\ %{})
def fetch_user_activities(user, reading_user, %{total: true} = params) do
result = fetch_activities_for_user(user, reading_user, params)
Keyword.put(result, :items, Enum.reverse(result[:items]))
end
def fetch_user_activities(user, reading_user, params) do
user
|> fetch_activities_for_user(reading_user, params)
|> Enum.reverse()
end
defp fetch_activities_for_user(user, reading_user, params) do
params = params =
params params
|> Map.put(:type, ["Create", "Announce"]) |> Map.put(:type, ["Create", "Announce"])
@ -616,10 +630,20 @@ def fetch_user_activities(user, reading_user, params \\ %{}) do
} }
|> user_activities_recipients() |> user_activities_recipients()
|> fetch_activities(params, pagination_type) |> fetch_activities(params, pagination_type)
|> Enum.reverse() end
def fetch_statuses(reading_user, %{total: true} = params) do
result = fetch_activities_for_reading_user(reading_user, params)
Keyword.put(result, :items, Enum.reverse(result[:items]))
end end
def fetch_statuses(reading_user, params) do def fetch_statuses(reading_user, params) do
reading_user
|> fetch_activities_for_reading_user(params)
|> Enum.reverse()
end
defp fetch_activities_for_reading_user(reading_user, params) do
params = Map.put(params, :type, ["Create", "Announce"]) params = Map.put(params, :type, ["Create", "Announce"])
%{ %{
@ -628,7 +652,6 @@ def fetch_statuses(reading_user, params) do
} }
|> user_activities_recipients() |> user_activities_recipients()
|> fetch_activities(params, :offset) |> fetch_activities(params, :offset)
|> Enum.reverse()
end end
defp user_activities_recipients(%{godmode: true}), do: [] defp user_activities_recipients(%{godmode: true}), do: []

View file

@ -85,17 +85,18 @@ def list_instance_statuses(conn, %{"instance" => instance} = params) do
with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
{page, page_size} = page_params(params) {page, page_size} = page_params(params)
activities = result =
ActivityPub.fetch_statuses(nil, %{ ActivityPub.fetch_statuses(nil, %{
instance: instance, instance: instance,
limit: page_size, limit: page_size,
offset: (page - 1) * page_size, offset: (page - 1) * page_size,
exclude_reblogs: not with_reblogs exclude_reblogs: not with_reblogs,
total: true
}) })
conn conn
|> put_view(AdminAPI.StatusView) |> put_view(AdminAPI.StatusView)
|> render("index.json", %{activities: activities, as: :activity}) |> render("index.json", %{total: result[:total], activities: result[:items], as: :activity})
end end
def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = params) do def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = params) do
@ -105,18 +106,19 @@ def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickna
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
{page, page_size} = page_params(params) {page, page_size} = page_params(params)
activities = result =
ActivityPub.fetch_user_activities(user, nil, %{ ActivityPub.fetch_user_activities(user, nil, %{
limit: page_size, limit: page_size,
offset: (page - 1) * page_size, offset: (page - 1) * page_size,
godmode: godmode, godmode: godmode,
exclude_reblogs: not with_reblogs, exclude_reblogs: not with_reblogs,
pagination_type: :offset pagination_type: :offset,
total: true
}) })
conn conn
|> put_view(AdminAPI.StatusView) |> put_view(AdminAPI.StatusView)
|> render("index.json", %{activities: activities, as: :activity}) |> render("index.json", %{total: result[:total], activities: result[:items], as: :activity})
else else
_ -> {:error, :not_found} _ -> {:error, :not_found}
end end

View file

@ -13,6 +13,10 @@ defmodule Pleroma.Web.AdminAPI.StatusView do
defdelegate merge_account_views(user), to: AdminAPI.AccountView defdelegate merge_account_views(user), to: AdminAPI.AccountView
def render("index.json", %{total: total} = opts) do
%{total: total, activities: safe_render_many(opts.activities, __MODULE__, "show.json", opts)}
end
def render("index.json", opts) do def render("index.json", opts) do
safe_render_many(opts.activities, __MODULE__, "show.json", opts) safe_render_many(opts.activities, __MODULE__, "show.json", opts)
end end

View file

@ -405,13 +405,9 @@ test "need_reboot flag", %{conn: conn} do
setup do setup do
user = insert(:user) user = insert(:user)
date1 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!() insert(:note_activity, user: user)
date2 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!() insert(:note_activity, user: user)
date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!() insert(:note_activity, user: user)
insert(:note_activity, user: user, published: date1)
insert(:note_activity, user: user, published: date2)
insert(:note_activity, user: user, published: date3)
%{user: user} %{user: user}
end end
@ -419,23 +415,22 @@ test "need_reboot flag", %{conn: conn} do
test "renders user's statuses", %{conn: conn, user: user} do test "renders user's statuses", %{conn: conn, user: user} do
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses") conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses")
assert json_response(conn, 200) |> length() == 3 assert %{"total" => 3, "activities" => activities} = json_response(conn, 200)
assert length(activities) == 3
end end
test "renders user's statuses with pagination", %{conn: conn, user: user} do test "renders user's statuses with pagination", %{conn: conn, user: user} do
conn1 = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=1&page=1") %{"total" => 3, "activities" => [activity1]} =
conn
|> get("/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=1&page=1")
|> json_response(200)
response1 = json_response(conn1, 200) %{"total" => 3, "activities" => [activity2]} =
conn
|> get("/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=1&page=2")
|> json_response(200)
assert response1 |> length() == 1 refute activity1 == activity2
conn2 = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?page_size=1&page=2")
response2 = json_response(conn2, 200)
assert response2 |> length() == 1
refute response1 == response2
end end
test "doesn't return private statuses by default", %{conn: conn, user: user} do test "doesn't return private statuses by default", %{conn: conn, user: user} do
@ -443,9 +438,12 @@ test "doesn't return private statuses by default", %{conn: conn, user: user} do
{:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses") %{"total" => 4, "activities" => activities} =
conn
|> get("/api/pleroma/admin/users/#{user.nickname}/statuses")
|> json_response(200)
assert json_response(conn, 200) |> length() == 4 assert length(activities) == 4
end end
test "returns private statuses with godmode on", %{conn: conn, user: user} do test "returns private statuses with godmode on", %{conn: conn, user: user} do
@ -453,9 +451,12 @@ test "returns private statuses with godmode on", %{conn: conn, user: user} do
{:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"}) {:ok, _public_status} = CommonAPI.post(user, %{status: "public", visibility: "public"})
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true") %{"total" => 5, "activities" => activities} =
conn
|> get("/api/pleroma/admin/users/#{user.nickname}/statuses?godmode=true")
|> json_response(200)
assert json_response(conn, 200) |> length() == 5 assert length(activities) == 5
end end
test "excludes reblogs by default", %{conn: conn, user: user} do test "excludes reblogs by default", %{conn: conn, user: user} do
@ -463,13 +464,17 @@ test "excludes reblogs by default", %{conn: conn, user: user} do
{:ok, activity} = CommonAPI.post(user, %{status: "."}) {:ok, activity} = CommonAPI.post(user, %{status: "."})
{:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user) {:ok, %Activity{}} = CommonAPI.repeat(activity.id, other_user)
conn_res = get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses") assert %{"total" => 0, "activities" => []} ==
assert json_response(conn_res, 200) |> length() == 0 conn
|> get("/api/pleroma/admin/users/#{other_user.nickname}/statuses")
|> json_response(200)
conn_res = assert %{"total" => 1, "activities" => [_]} =
get(conn, "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true") conn
|> get(
assert json_response(conn_res, 200) |> length() == 1 "/api/pleroma/admin/users/#{other_user.nickname}/statuses?with_reblogs=true"
)
|> json_response(200)
end end
end end
@ -859,33 +864,30 @@ test "GET /instances/:instance/statuses", %{conn: conn} do
insert_pair(:note_activity, user: user) insert_pair(:note_activity, user: user)
activity = insert(:note_activity, user: user2) activity = insert(:note_activity, user: user2)
ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses") %{"total" => 2, "activities" => activities} =
conn |> get("/api/pleroma/admin/instances/archae.me/statuses") |> json_response(200)
response = json_response(ret_conn, 200) assert length(activities) == 2
assert length(response) == 2 %{"total" => 1, "activities" => [_]} =
conn |> get("/api/pleroma/admin/instances/test.com/statuses") |> json_response(200)
ret_conn = get(conn, "/api/pleroma/admin/instances/test.com/statuses") %{"total" => 0, "activities" => []} =
conn |> get("/api/pleroma/admin/instances/nonexistent.com/statuses") |> json_response(200)
response = json_response(ret_conn, 200)
assert length(response) == 1
ret_conn = get(conn, "/api/pleroma/admin/instances/nonexistent.com/statuses")
response = json_response(ret_conn, 200)
assert Enum.empty?(response)
CommonAPI.repeat(activity.id, user) CommonAPI.repeat(activity.id, user)
ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses") %{"total" => 2, "activities" => activities} =
response = json_response(ret_conn, 200) conn |> get("/api/pleroma/admin/instances/archae.me/statuses") |> json_response(200)
assert length(response) == 2
ret_conn = get(conn, "/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true") assert length(activities) == 2
response = json_response(ret_conn, 200)
assert length(response) == 3 %{"total" => 3, "activities" => activities} =
conn
|> get("/api/pleroma/admin/instances/archae.me/statuses?with_reblogs=true")
|> json_response(200)
assert length(activities) == 3
end end
end end