1
0
Fork 0
mirror of https://git.pleroma.social/pleroma/pleroma.git synced 2025-12-09 05:20:38 +00:00

Merge branch 'mastodon-quotes-updates' into 'develop'

Use Mastodon-compatible route for quotes list and param for quotes count

See merge request pleroma/pleroma!4367
This commit is contained in:
nicole mikołajczyk 2025-12-02 14:34:16 +01:00
commit d7b0115124
12 changed files with 137 additions and 81 deletions

View file

@ -0,0 +1 @@
Use Mastodon-compatible route for quotes list and param for quotes count

View file

@ -39,7 +39,6 @@ Has these additional fields under the `pleroma` object:
- `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint.
- `parent_visible`: If the parent of this post is visible to the user or not.
- `pinned_at`: a datetime (iso8601) when status was pinned, `null` otherwise.
- `quotes_count`: the count of status quotes.
- `bookmark_folder`: the ID of the folder bookmark is stored within (if any).
- `list_id`: the ID of the list the post is addressed to (if any, only returned to author).

View file

@ -19,7 +19,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaStatusOperation do
%Operation{
tags: ["Retrieve status information"],
summary: "Quoted by",
description: "View quotes for a given status",
deprecated: true,
description: "View quotes for a given status. Use /api/v1/statuses/:id/quotes instead.",
operationId: "PleromaAPI.StatusController.quotes",
parameters: [id_param() | pagination_params()],
security: [%{"oAuth" => ["read:statuses"]}],

View file

@ -549,6 +549,27 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
}
end
def quotes_operation do
%Operation{
tags: ["Retrieve status information"],
summary: "Quoted by",
description: "View quotes for a given status",
operationId: "StatusController.quotes",
parameters: [id_param() | pagination_params()],
security: [%{"oAuth" => ["read:statuses"]}],
responses: %{
200 =>
Operation.response(
"Array of Status",
"application/json",
array_of_statuses()
),
403 => Operation.response("Forbidden", "application/json", ApiError),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
end
def array_of_statuses do
%Schema{type: :array, items: Status, example: [Status.schema().example]}
end

View file

@ -219,7 +219,9 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
},
quotes_count: %Schema{
type: :integer,
description: "How many statuses quoted this status"
deprecated: true,
description:
"How many statuses quoted this status. Deprecated, use `quotes_count` from parent object instead."
},
local: %Schema{
type: :boolean,
@ -259,6 +261,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
}
},
poll: %Schema{allOf: [Poll], nullable: true, description: "The poll attached to the status"},
quotes_count: %Schema{
type: :integer,
description: "How many statuses quoted this status."
},
reblog: %Schema{
allOf: [%OpenApiSpex.Reference{"$ref": "#/components/schemas/Status"}],
nullable: true,
@ -385,6 +391,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
"quotes_count" => 0
},
"poll" => nil,
"quotes_count" => 0,
"reblog" => nil,
"reblogged" => false,
"reblogs_count" => 0,

View file

@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
only: [try_render: 3, add_link_headers: 2]
require Ecto.Query
require Pleroma.Constants
alias Pleroma.Activity
alias Pleroma.Bookmark
@ -41,7 +42,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
:show,
:context,
:show_history,
:show_source
:show_source,
:quotes
]
)
@ -629,6 +631,45 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
)
end
@doc "GET /api/v1/statuses/:id/quotes"
def quotes(
%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id} = params}}} =
conn,
_
) do
with %Activity{object: object} = activity <- Activity.get_by_id_with_object(id),
true <- Visibility.visible_for_user?(activity, user) do
params =
params
|> Map.put(:type, "Create")
|> Map.put(:blocking_user, user)
|> Map.put(:quote_url, object.data["id"])
recipients =
if user do
[Pleroma.Constants.as_public()] ++ [user.ap_id | User.following(user)]
else
[Pleroma.Constants.as_public()]
end
activities =
recipients
|> ActivityPub.fetch_activities(params)
|> Enum.reverse()
conn
|> add_link_headers(activities)
|> render("index.json",
activities: activities,
for: user,
as: :activity
)
else
nil -> {:error, :not_found}
false -> {:error, :not_found}
end
end
defp put_application(params, %{assigns: %{token: %Token{user: %User{} = user} = token}} = _conn) do
if user.disclose_client do
%{client_name: client_name, website: website} = Repo.preload(token, :app).app

View file

@ -447,6 +447,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
application: build_application(object.data["generator"]),
language: get_language(object),
emojis: build_emojis(object.data["emoji"]),
quotes_count: object.data["quotesCount"] || 0,
pleroma: %{
local: activity.local,
conversation_id: get_context_id(activity),

View file

@ -5,16 +5,9 @@
defmodule Pleroma.Web.PleromaAPI.StatusController do
use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
require Ecto.Query
require Pleroma.Constants
alias Pleroma.Activity
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.MastodonAPI.StatusView
alias Pleroma.Web.Plugs.OAuthScopesPlug
plug(Pleroma.Web.ApiSpec.CastAndValidate)
@ -29,38 +22,9 @@ defmodule Pleroma.Web.PleromaAPI.StatusController do
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaStatusOperation
@doc "GET /api/v1/pleroma/statuses/:id/quotes"
def quotes(%{assigns: %{user: user}} = conn, %{id: id} = params) do
with %Activity{object: object} = activity <- Activity.get_by_id_with_object(id),
true <- Visibility.visible_for_user?(activity, user) do
params =
params
|> Map.put(:type, "Create")
|> Map.put(:blocking_user, user)
|> Map.put(:quote_url, object.data["id"])
recipients =
if user do
[Pleroma.Constants.as_public()] ++ [user.ap_id | User.following(user)]
else
[Pleroma.Constants.as_public()]
end
activities =
recipients
|> ActivityPub.fetch_activities(params)
|> Enum.reverse()
conn
|> add_link_headers(activities)
|> put_view(StatusView)
|> render("index.json",
activities: activities,
for: user,
as: :activity
)
else
nil -> {:error, :not_found}
false -> {:error, :not_found}
end
def quotes(conn, _params) do
conn
|> put_view(Pleroma.Web.MastodonAPI.StatusView)
|> Pleroma.Web.MastodonAPI.StatusController.call(:quotes)
end
end

View file

@ -752,6 +752,7 @@ defmodule Pleroma.Web.Router do
post("/statuses/:id/mute", StatusController, :mute_conversation)
post("/statuses/:id/unmute", StatusController, :unmute_conversation)
post("/statuses/:id/translate", StatusController, :translate)
get("/statuses/:id/quotes", StatusController, :quotes)
post("/push/subscription", SubscriptionController, :create)
get("/push/subscription", SubscriptionController, :show)

View file

@ -2541,4 +2541,47 @@ defmodule Pleroma.Web.MastodonAPI.StatusControllerTest do
|> json_response_and_validate_schema(404)
end
end
describe "getting quotes of a specified post" do
setup do
[current_user, user] = insert_pair(:user)
%{user: current_user, conn: conn} = oauth_access(["read:statuses"], user: current_user)
[current_user: current_user, user: user, conn: conn]
end
test "shows quotes of a post", %{conn: conn} do
user = insert(:user)
activity = insert(:note_activity)
{:ok, quote_post} = CommonAPI.post(user, %{status: "quoat", quote_id: activity.id})
response =
conn
|> get("/api/v1/statuses/#{activity.id}/quotes")
|> json_response_and_validate_schema(:ok)
[status] = response
assert length(response) == 1
assert status["id"] == quote_post.id
end
test "returns 404 error when a post can't be seen", %{conn: conn} do
activity = insert(:direct_note_activity)
response =
conn
|> get("/api/v1/statuses/#{activity.id}/quotes")
assert json_response_and_validate_schema(response, 404) == %{"error" => "Record not found"}
end
test "returns 404 error when a post does not exist", %{conn: conn} do
response =
conn
|> get("/api/v1/statuses/idontexist/quotes")
assert json_response_and_validate_schema(response, 404) == %{"error" => "Record not found"}
end
end
end

View file

@ -344,7 +344,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
quotes_count: 0,
bookmark_folder: nil,
list_id: nil
}
},
quotes_count: 0
}
assert status == expected

View file

@ -9,46 +9,22 @@ defmodule Pleroma.Web.PleromaAPI.StatusControllerTest do
import Pleroma.Factory
describe "getting quotes of a specified post" do
setup do
[current_user, user] = insert_pair(:user)
%{user: current_user, conn: conn} = oauth_access(["read:statuses"], user: current_user)
[current_user: current_user, user: user, conn: conn]
end
test "/quotes fallback works" do
[current_user, user] = insert_pair(:user)
%{conn: conn} = oauth_access(["read:statuses"], user: current_user)
test "shows quotes of a post", %{conn: conn} do
user = insert(:user)
activity = insert(:note_activity)
activity = insert(:note_activity)
{:ok, quote_post} = CommonAPI.post(user, %{status: "quoat", quote_id: activity.id})
{:ok, quote_post} = CommonAPI.post(user, %{status: "quoat", quote_id: activity.id})
response =
conn
|> get("/api/v1/pleroma/statuses/#{activity.id}/quotes")
|> json_response_and_validate_schema(:ok)
response =
conn
|> get("/api/v1/pleroma/statuses/#{activity.id}/quotes")
|> json_response_and_validate_schema(:ok)
[status] = response
[status] = response
assert length(response) == 1
assert status["id"] == quote_post.id
end
test "returns 404 error when a post can't be seen", %{conn: conn} do
activity = insert(:direct_note_activity)
response =
conn
|> get("/api/v1/pleroma/statuses/#{activity.id}/quotes")
assert json_response_and_validate_schema(response, 404) == %{"error" => "Record not found"}
end
test "returns 404 error when a post does not exist", %{conn: conn} do
response =
conn
|> get("/api/v1/pleroma/statuses/idontexist/quotes")
assert json_response_and_validate_schema(response, 404) == %{"error" => "Record not found"}
end
assert length(response) == 1
assert status["id"] == quote_post.id
end
end