logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma git clone https://anongit.hacktivis.me/git/pleroma.git/
commit: d7b0115124d1968a817c944001455f691d9b09ff
parent ca03d94f52f3494c767267a31840224b23c7b1b4
Author: nicole mikołajczyk <me@mkljczk.pl>
Date:   Tue,  2 Dec 2025 14:34:16 +0100

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

Diffstat:

Achangelog.d/mastodon-quotes-updates.change1+
Mdocs/development/API/differences_in_mastoapi_responses.md1-
Mlib/pleroma/web/api_spec/operations/pleroma_status_operation.ex3++-
Mlib/pleroma/web/api_spec/operations/status_operation.ex21+++++++++++++++++++++
Mlib/pleroma/web/api_spec/schemas/status.ex9++++++++-
Mlib/pleroma/web/mastodon_api/controllers/status_controller.ex43++++++++++++++++++++++++++++++++++++++++++-
Mlib/pleroma/web/mastodon_api/views/status_view.ex1+
Mlib/pleroma/web/pleroma_api/controllers/status_controller.ex44++++----------------------------------------
Mlib/pleroma/web/router.ex1+
Mtest/pleroma/web/mastodon_api/controllers/status_controller_test.exs43+++++++++++++++++++++++++++++++++++++++++++
Mtest/pleroma/web/mastodon_api/views/status_view_test.exs3++-
Mtest/pleroma/web/pleroma_api/controllers/status_controller_test.exs48++++++++++++------------------------------------
12 files changed, 137 insertions(+), 81 deletions(-)

diff --git a/changelog.d/mastodon-quotes-updates.change b/changelog.d/mastodon-quotes-updates.change @@ -0,0 +1 @@ +Use Mastodon-compatible route for quotes list and param for quotes count diff --git a/docs/development/API/differences_in_mastoapi_responses.md b/docs/development/API/differences_in_mastoapi_responses.md @@ -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). diff --git a/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex @@ -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"]}], diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex @@ -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 diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex @@ -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, diff --git a/lib/pleroma/web/mastodon_api/controllers/status_controller.ex b/lib/pleroma/web/mastodon_api/controllers/status_controller.ex @@ -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 diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -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), diff --git a/lib/pleroma/web/pleroma_api/controllers/status_controller.ex b/lib/pleroma/web/pleroma_api/controllers/status_controller.ex @@ -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 diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex @@ -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) diff --git a/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/status_controller_test.exs @@ -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 diff --git a/test/pleroma/web/mastodon_api/views/status_view_test.exs b/test/pleroma/web/mastodon_api/views/status_view_test.exs @@ -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 diff --git a/test/pleroma/web/pleroma_api/controllers/status_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/status_controller_test.exs @@ -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