- Mastodon API: Add `upload_limit`, `avatar_upload_limit`, `background_upload_limit`, and `banner_upload_limit` to `/api/v1/instance`
- Mastodon API: Add `pleroma.unread_conversation_count` to the Account entity
- OAuth: support for hierarchical permissions / [Mastodon 2.4.3 OAuth permissions](https://docs.joinmastodon.org/api/permissions/)
- - Authentication: Added rate limit for password-authorized actions / login existence checks
- Metadata Link: Atom syndication Feed
- - Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
+ - Mastodon API: Add `exclude_visibilities` parameter to the timeline and notification endpoints
+ - Admin API: `/users/:nickname/toggle_activation` endpoint is now deprecated in favor of: `/users/activate`, `/users/deactivate`, both accept `nicknames` array
+ - Admin API: `POST/DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group` are deprecated in favor of: `POST/DELETE /api/pleroma/admin/users/permission_group/:permission_group` (both accept `nicknames` array), `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body).
+ - Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
+ - Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read
+ - Mastodon API: Add `/api/v1/markers` for managing timeline read markers
+- Admin API: Add ability to fetch reports, grouped by status `GET /api/pleroma/admin/grouped_reports`
### Changed
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
- Params: none
- Response:
- On failure: `{"error": "…"}`
- - On success: JSON of the `user.info`
+ - On success: JSON of the user
+
+ ## `POST /api/pleroma/admin/users/permission_group/:permission_group`
+
+ ### Add users to permission group
+
+ - Params:
+ - `nicknames`: nicknames array
+ - Response:
+ - On failure: `{"error": "…"}`
+ - On success: JSON of the user
+
+ ## DEPRECATED `DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group`
+## `DELETE /api/pleroma/admin/users/:nickname/permission_group/:permission_group`
+
### Remove user from permission group
- Params: none
- Response:
- On success: URL of the unfollowed relay
-## `/api/pleroma/admin/users/invite_token`
+ ## `GET /api/pleroma/admin/relay`
+
+ ### List Relays
+
+ - Params: none
+ - Response:
+ - On success: JSON array of relays
+
+## `POST /api/pleroma/admin/users/invite_token`
### Create an account registration invite token
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
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("total", true)
+ |> Map.put("limit", page_size)
+ |> Map.put("offset", (page - 1) * page_size)
+
+ ActivityPub.fetch_activities([], params, :offset)
+ end
+
+ @spec get_reports_grouped_by_status() :: %{
+ required(:groups) => [
+ %{
+ required(:date) => String.t(),
+ required(:account) => %User{},
+ required(:status) => %Activity{},
+ required(:actors) => [%User{}],
+ required(:reports) => [%Activity{}]
+ }
+ ],
+ required(:total) => integer
+ }
+ def get_reports_grouped_by_status do
+ paginated_activities = get_reported_status_ids()
+
+ groups =
+ paginated_activities
+ |> Enum.map(fn entry ->
+ status =
+ Activity
+ |> Queries.by_ap_id(entry[:activity_id])
+ |> Activity.with_preloaded_object(:left)
+ |> Activity.with_preloaded_user_actor()
+ |> Repo.one()
+
+ reports = get_reports_by_status_id(status.data["id"])
+
+ max_date =
+ Enum.max_by(reports, &Pleroma.Web.CommonAPI.Utils.to_masto_date(&1.data["published"])).data[
+ "published"
+ ]
+
+ actors = Enum.map(reports, & &1.user_actor)
+
+ %{
+ date: max_date,
+ account: status.user_actor,
+ status: status,
+ actors: actors,
+ reports: reports
+ }
+ end)
+
+ %{
+ groups: groups
+ }
+ end
+
+ def get_reports_by_status_id(status_id) do
+ from(a in Activity,
+ where: fragment("(?)->>'type' = 'Flag'", a.data),
+ where: fragment("(?)->'object' \\? (?)", a.data, ^status_id)
+ )
+ |> Activity.with_preloaded_user_actor()
+ |> Repo.all()
+ end
+
+ @spec get_reported_status_ids() :: %{
+ required(:items) => [%Activity{}],
+ required(:total) => integer
+ }
+ def get_reported_status_ids do
+ from(a in Activity,
+ where: fragment("(?)->>'type' = 'Flag'", a.data),
+ select: %{
+ date: fragment("max(?->>'published') date", a.data),
+ activity_id:
+ fragment("jsonb_array_elements_text((? #- '{object,0}')->'object') activity_id", a.data)
+ },
+ group_by: fragment("activity_id"),
+ order_by: fragment("date DESC")
+ )
+ |> Repo.all()
+ 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, & &1["id"])
+ 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
end
end
- describe "PUT /api/pleroma/admin/reports/:id" do
+ describe "PATCH /api/pleroma/admin/reports" do
setup %{conn: conn} do
- admin = insert(:user, info: %{is_admin: true})
+ admin = insert(:user, is_admin: true)
[reporter, target_user] = insert_pair(:user)
activity = insert(:note_activity, user: target_user)
end
end
- #
+ describe "GET /api/pleroma/admin/grouped_reports" do
+ setup %{conn: conn} do
+ admin = insert(:user, info: %{is_admin: true})
+ [reporter, target_user] = insert_pair(:user)
+
+ date1 = (DateTime.to_unix(DateTime.utc_now()) + 1000) |> DateTime.from_unix!()
+ date2 = (DateTime.to_unix(DateTime.utc_now()) + 2000) |> DateTime.from_unix!()
+ date3 = (DateTime.to_unix(DateTime.utc_now()) + 3000) |> DateTime.from_unix!()
+
+ first_status =
+ insert(:note_activity, user: target_user, data_attrs: %{"published" => date1})
+
+ second_status =
+ insert(:note_activity, user: target_user, data_attrs: %{"published" => date2})
+
+ third_status =
+ insert(:note_activity, user: target_user, data_attrs: %{"published" => date3})
+
+ %{
+ conn: assign(conn, :user, admin),
+ reporter: reporter,
+ target_user: target_user,
+ first_status: first_status,
+ second_status: second_status,
+ third_status: third_status
+ }
+ end
+
+ test "returns reports grouped by status", %{
+ conn: conn,
+ reporter: reporter,
+ target_user: target_user,
+ first_status: first_status,
+ second_status: second_status,
+ third_status: third_status
+ } do
+ {:ok, %{id: _}} =
+ CommonAPI.report(reporter, %{
+ "account_id" => target_user.id,
+ "status_ids" => [first_status.id, second_status.id, third_status.id]
+ })
+
+ {:ok, %{id: _}} =
+ CommonAPI.report(reporter, %{
+ "account_id" => target_user.id,
+ "status_ids" => [first_status.id, second_status.id]
+ })
+
+ {:ok, %{id: _}} =
+ CommonAPI.report(reporter, %{
+ "account_id" => target_user.id,
+ "status_ids" => [first_status.id]
+ })
+
+ response =
+ conn
+ |> get("/api/pleroma/admin/grouped_reports")
+ |> json_response(:ok)
+
+ assert length(response["reports"]) == 3
+ [third_group, second_group, first_group] = response["reports"]
+
+ assert length(third_group["reports"]) == 3
+ assert length(second_group["reports"]) == 2
+ assert length(first_group["reports"]) == 1
+ end
+ end
+
describe "POST /api/pleroma/admin/reports/:id/respond" do
setup %{conn: conn} do
- admin = insert(:user, info: %{is_admin: true})
+ admin = insert(:user, is_admin: true)
%{conn: assign(conn, :user, admin), admin: admin}
end