From 065200e92e3917a84ad64d1e9a4e9361cf5e62a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicole=20Miko=C5=82ajczyk?= Date: Tue, 6 May 2025 12:37:37 +0200 Subject: [PATCH 01/23] Support new Mastodon API for endorsed accounts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nicole Mikołajczyk --- changelog.d/endorsements-api.change | 1 + .../api_spec/operations/account_operation.ex | 26 +++++++++++++-- .../operations/pleroma_account_operation.ex | 19 ----------- .../controllers/account_controller.ex | 24 +++++++++++--- .../controllers/account_controller.ex | 25 +------------- lib/pleroma/web/router.ex | 11 +++++-- .../controllers/account_controller_test.exs | 33 ++++++++++++++++--- .../controllers/account_controller_test.exs | 25 -------------- 8 files changed, 84 insertions(+), 80 deletions(-) create mode 100644 changelog.d/endorsements-api.change diff --git a/changelog.d/endorsements-api.change b/changelog.d/endorsements-api.change new file mode 100644 index 000000000..279392c66 --- /dev/null +++ b/changelog.d/endorsements-api.change @@ -0,0 +1 @@ +Support new Mastodon API for endorsed accounts diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 21a779dcb..d0e0b45ab 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -383,6 +383,28 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end + def endorsements_operation do + %Operation{ + tags: ["Retrieve account information"], + summary: "Endorsements", + description: "Returns endorsed accounts", + operationId: "AccountController.endorsements", + parameters: [ + with_relationships_param(), + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"} + ], + responses: %{ + 200 => + Operation.response( + "Array of Accounts", + "application/json", + array_of_accounts() + ), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end + def remove_from_followers_operation do %Operation{ tags: ["Account actions"], @@ -485,11 +507,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end - def endorsements_operation do + def own_endorsements_operation do %Operation{ tags: ["Retrieve account information"], summary: "Endorsements", - operationId: "AccountController.endorsements", + operationId: "AccountController.own_endorsements", description: "Returns endorsed accounts", security: [%{"oAuth" => ["read:accounts"]}], responses: %{ diff --git a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex index b8b37d7cf..b144c2ac0 100644 --- a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex @@ -64,25 +64,6 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do } end - def endorsements_operation do - %Operation{ - tags: ["Retrieve account information"], - summary: "Endorsements", - description: "Returns endorsed accounts", - operationId: "PleromaAPI.AccountController.endorsements", - parameters: [with_relationships_param(), id_param()], - responses: %{ - 200 => - Operation.response( - "Array of Accounts", - "application/json", - AccountOperation.array_of_accounts() - ), - 404 => Operation.response("Not Found", "application/json", ApiError) - } - } - end - def subscribe_operation do %Operation{ deprecated: true, diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 68157b0c4..4bd6891d0 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -38,7 +38,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( OAuthScopesPlug, %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]} - when action in [:show, :followers, :following] + when action in [:show, :followers, :following, :endorsements] ) plug( @@ -50,7 +50,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug( OAuthScopesPlug, %{scopes: ["read:accounts"]} - when action in [:verify_credentials, :endorsements] + when action in [:verify_credentials, :endorsements, :own_endorsements] ) plug( @@ -89,7 +89,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do @relationship_actions [:follow, :unfollow, :remove_from_followers] @needs_account ~W( followers following lists follow unfollow mute unmute block unblock - note endorse unendorse remove_from_followers + note endorse unendorse endorsements remove_from_followers )a plug( @@ -549,6 +549,22 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end end + @doc "GET /api/v1/accounts/:id/endorsements" + def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do + users = + user + |> User.endorsed_users_relation(_restrict_deactivated = true) + |> Pleroma.Repo.all() + + conn + |> render("index.json", + for: for_user, + users: users, + as: :user, + embed_relationships: embed_relationships?(params) + ) + end + @doc "POST /api/v1/accounts/:id/remove_from_followers" def remove_from_followers(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do {:error, "Can not unfollow yourself"} @@ -624,7 +640,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "GET /api/v1/endorsements" - def endorsements(%{assigns: %{user: user}} = conn, params) do + def own_endorsements(%{assigns: %{user: user}} = conn, params) do users = user |> User.endorsed_users_relation(_restrict_deactivated = true) diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index 591391b60..0b8e0b7ad 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -9,7 +9,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do only: [ json_response: 3, add_link_headers: 2, - embed_relationships?: 1, assign_account_by_id: 2 ] @@ -45,12 +44,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do %{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites ) - plug( - OAuthScopesPlug, - %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]} - when action == :endorsements - ) - plug( OAuthScopesPlug, %{scopes: ["read:accounts"]} when action == :birthdays @@ -60,7 +53,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do plug( :assign_account_by_id - when action in [:favourites, :endorsements, :subscribe, :unsubscribe] + when action in [:favourites, :subscribe, :unsubscribe] ) defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation @@ -109,22 +102,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do ) end - @doc "GET /api/v1/pleroma/accounts/:id/endorsements" - def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do - users = - user - |> User.endorsed_users_relation(_restrict_deactivated = true) - |> Pleroma.Repo.all() - - conn - |> render("index.json", - for: for_user, - users: users, - as: :user, - embed_relationships: embed_relationships?(params) - ) - end - @doc "POST /api/v1/pleroma/accounts/:id/subscribe" def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do with {:ok, _subscription} <- User.subscribe(user, subscription_target) do diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index f2f9d7246..b3f915bd9 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -607,7 +607,6 @@ defmodule Pleroma.Web.Router do scope [] do pipe_through(:api) get("/accounts/:id/favourites", AccountController, :favourites) - get("/accounts/:id/endorsements", AccountController, :endorsements) get("/statuses/:id/quotes", StatusController, :quotes) end @@ -636,6 +635,11 @@ defmodule Pleroma.Web.Router do get("/accounts/:id/scrobbles", ScrobbleController, :index) end + scope "/api/v1/pleroma", Pleroma.Web.MastodonAPI do + pipe_through(:api) + get("/accounts/:id/endorsements", AccountController, :endorsements) + end + scope "/api/v2/pleroma", Pleroma.Web.PleromaAPI do scope [] do pipe_through(:authenticated_api) @@ -652,7 +656,7 @@ defmodule Pleroma.Web.Router do get("/accounts/relationships", AccountController, :relationships) get("/accounts/familiar_followers", AccountController, :familiar_followers) get("/accounts/:id/lists", AccountController, :lists) - get("/endorsements", AccountController, :endorsements) + get("/endorsements", AccountController, :own_endorsements) get("/blocks", AccountController, :blocks) get("/mutes", AccountController, :mutes) @@ -666,6 +670,8 @@ defmodule Pleroma.Web.Router do post("/accounts/:id/note", AccountController, :note) post("/accounts/:id/pin", AccountController, :endorse) post("/accounts/:id/unpin", AccountController, :unendorse) + post("/accounts/:id/endorse", AccountController, :endorse) + post("/accounts/:id/unendorse", AccountController, :unendorse) post("/accounts/:id/remove_from_followers", AccountController, :remove_from_followers) get("/conversations", ConversationController, :index) @@ -781,6 +787,7 @@ defmodule Pleroma.Web.Router do get("/accounts/:id/statuses", AccountController, :statuses) get("/accounts/:id/followers", AccountController, :followers) get("/accounts/:id/following", AccountController, :following) + get("/accounts/:id/endorsements", AccountController, :endorsements) get("/accounts/:id", AccountController, :show) post("/accounts", AccountController, :create) diff --git a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs index cd3107f32..245d4374c 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -2123,7 +2123,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert %{"id" => ^id1, "endorsed" => true} = conn |> put_req_header("content-type", "application/json") - |> post("/api/v1/accounts/#{id1}/pin") + |> post("/api/v1/accounts/#{id1}/endorse") |> json_response_and_validate_schema(200) assert [%{"id" => ^id1}] = @@ -2142,7 +2142,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert %{"id" => ^id1, "endorsed" => false} = conn |> put_req_header("content-type", "application/json") - |> post("/api/v1/accounts/#{id1}/unpin") + |> post("/api/v1/accounts/#{id1}/unendorse") |> json_response_and_validate_schema(200) assert [] = @@ -2161,15 +2161,40 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do conn |> put_req_header("content-type", "application/json") - |> post("/api/v1/accounts/#{id1}/pin") + |> post("/api/v1/accounts/#{id1}/endorse") |> json_response_and_validate_schema(200) assert %{"error" => "You have already pinned the maximum number of users"} = conn |> assign(:user, user) - |> post("/api/v1/accounts/#{id2}/pin") + |> post("/api/v1/accounts/#{id2}/endorse") |> json_response_and_validate_schema(400) end + + test "returns a list of pinned accounts", %{conn: conn} do + clear_config([:instance, :max_endorsed_users], 3) + + %{id: id1} = user1 = insert(:user) + %{id: id2} = user2 = insert(:user) + %{id: id3} = user3 = insert(:user) + + CommonAPI.follow(user2, user1) + CommonAPI.follow(user3, user1) + + User.endorse(user1, user2) + User.endorse(user1, user3) + + [%{"id" => ^id2}, %{"id" => ^id3}] = + conn + |> get("/api/v1/accounts/#{id1}/endorsements") + |> json_response_and_validate_schema(200) + end + + test "returns 404 error when specified user is not exist", %{conn: conn} do + conn = get(conn, "/api/v1/accounts/test/endorsements") + + assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} + end end describe "familiar followers" do diff --git a/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs index 61880e2c0..38a928f30 100644 --- a/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/pleroma_api/controllers/account_controller_test.exs @@ -280,31 +280,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountControllerTest do end end - describe "account endorsements" do - test "returns a list of pinned accounts", %{conn: conn} do - %{id: id1} = user1 = insert(:user) - %{id: id2} = user2 = insert(:user) - %{id: id3} = user3 = insert(:user) - - CommonAPI.follow(user2, user1) - CommonAPI.follow(user3, user1) - - User.endorse(user1, user2) - User.endorse(user1, user3) - - [%{"id" => ^id2}, %{"id" => ^id3}] = - conn - |> get("/api/v1/pleroma/accounts/#{id1}/endorsements") - |> json_response_and_validate_schema(200) - end - - test "returns 404 error when specified user is not exist", %{conn: conn} do - conn = get(conn, "/api/v1/pleroma/accounts/test/endorsements") - - assert json_response_and_validate_schema(conn, 404) == %{"error" => "Record not found"} - end - end - describe "birthday reminders" do test "returns a list of friends having birthday on specified day" do %{user: user, conn: conn} = oauth_access(["read:accounts"]) From cb7086cb187cff202803806ede509b3075a3718f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Sun, 7 Sep 2025 22:37:48 +0200 Subject: [PATCH 02/23] Use end-of-string in regex for local `get_by_nickname` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- changelog.d/local-nickname-regex.fix | 1 + lib/pleroma/user.ex | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/local-nickname-regex.fix diff --git a/changelog.d/local-nickname-regex.fix b/changelog.d/local-nickname-regex.fix new file mode 100644 index 000000000..81ddd9cff --- /dev/null +++ b/changelog.d/local-nickname-regex.fix @@ -0,0 +1 @@ +Use end-of-string in regex for local `get_by_nickname` diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 84551afd5..21ac31f33 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1364,7 +1364,7 @@ defmodule Pleroma.User do @spec get_by_nickname(String.t()) :: User.t() | nil def get_by_nickname(nickname) do Repo.get_by(User, nickname: nickname) || - if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do + if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()}$)i, nickname) do Repo.get_by(User, nickname: local_nickname(nickname)) end end From 546c03b2c6eb56aa35f583a9ad103679171f3b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Fri, 19 Sep 2025 16:27:34 +0200 Subject: [PATCH 03/23] `remote_url` links to unproxied URL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- changelog.d/remote-url.fix | 1 + lib/pleroma/web/mastodon_api/views/status_view.ex | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 changelog.d/remote-url.fix diff --git a/changelog.d/remote-url.fix b/changelog.d/remote-url.fix new file mode 100644 index 000000000..9be84a878 --- /dev/null +++ b/changelog.d/remote-url.fix @@ -0,0 +1 @@ +`remote_url` links to unproxied URL diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 4b5ac9c3b..c1c695419 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -602,7 +602,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do def render("attachment.json", %{attachment: attachment}) do [attachment_url | _] = attachment["url"] media_type = attachment_url["mediaType"] || attachment_url["mimeType"] || "image" - href = attachment_url["href"] |> MediaProxy.url() + href_remote = attachment_url["href"] + href = href_remote |> MediaProxy.url() href_preview = attachment_url["href"] |> MediaProxy.preview_url() meta = render("attachment_meta.json", %{attachment: attachment}) @@ -641,7 +642,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do %{ id: attachment_id, url: href, - remote_url: href, + remote_url: href_remote, preview_url: href_preview, text_url: href, type: type, From 94188a293c1e51af86aba3b3e52c34a063d0cae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Sun, 4 Aug 2024 00:23:00 +0200 Subject: [PATCH 04/23] Update *Differences in Mastodon API responses from vanilla Mastodon* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- changelog.d/docs.skip | 1 + docs/development/API/differences_in_mastoapi_responses.md | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 changelog.d/docs.skip diff --git a/changelog.d/docs.skip b/changelog.d/docs.skip new file mode 100644 index 000000000..fd1aae513 --- /dev/null +++ b/changelog.d/docs.skip @@ -0,0 +1 @@ +Update *Differences in Mastodon API responses from vanilla Mastodon* \ No newline at end of file diff --git a/docs/development/API/differences_in_mastoapi_responses.md b/docs/development/API/differences_in_mastoapi_responses.md index e3b6a3c77..5cda57ab0 100644 --- a/docs/development/API/differences_in_mastoapi_responses.md +++ b/docs/development/API/differences_in_mastoapi_responses.md @@ -40,9 +40,12 @@ Has these additional fields under the `pleroma` object: - `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. -- `non_anonymous`: true if the source post specifies the poll results are not anonymous. Currently only implemented by Smithereen. - `bookmark_folder`: the ID of the folder bookmark is stored within (if any). +Has these additional fields under the `poll.pleroma` object: + +- `non_anonymous`: true if the source post specifies the poll results are not anonymous. Currently only implemented by Smithereen. + The `GET /api/v1/statuses/:id/source` endpoint additionally has the following attributes: - `content_type`: The content type of the status source. @@ -96,6 +99,9 @@ Endpoints which accept `with_relationships` parameter: - `/api/v1/accounts/:id/followers` - `/api/v1/accounts/:id/following` - `/api/v1/mutes` +- `/api/v1/blocks` +- `/api/v1/search` +- `/api/v2/search` Has these additional fields under the `pleroma` object: From 0b8b98f979cf7e4f0b018f9b319e14bf3366ffcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicole=20Miko=C5=82ajczyk?= Date: Sat, 17 May 2025 16:05:13 +0200 Subject: [PATCH 05/23] Add a failing test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nicole Mikołajczyk Signed-off-by: nicole mikołajczyk --- changelog.d/fix-lists-bcc.fix | 1 + .../web/activity_pub/publisher_test.exs | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 changelog.d/fix-lists-bcc.fix diff --git a/changelog.d/fix-lists-bcc.fix b/changelog.d/fix-lists-bcc.fix new file mode 100644 index 000000000..cd819fea3 --- /dev/null +++ b/changelog.d/fix-lists-bcc.fix @@ -0,0 +1 @@ +Fix publisher when publishing to a list of users diff --git a/test/pleroma/web/activity_pub/publisher_test.exs b/test/pleroma/web/activity_pub/publisher_test.exs index a55993978..1f9e0bfe5 100644 --- a/test/pleroma/web/activity_pub/publisher_test.exs +++ b/test/pleroma/web/activity_pub/publisher_test.exs @@ -332,6 +332,33 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do ) end + test "activity with BCC is published to a list member." do + actor = insert(:user) + {:ok, list} = Pleroma.List.create("list", actor) + list_member = insert(:user, %{local: false}) + + Pleroma.List.follow(list, list_member) + + note_activity = + insert(:note_activity, + # recipients: [follower.ap_id], + data_attrs: %{"bcc" => [list.ap_id]} + ) + + res = Publisher.publish(actor, note_activity) + assert res == :ok + + assert_enqueued( + worker: "Pleroma.Workers.PublisherWorker", + args: %{ + "params" => %{ + inbox: list_member.inbox, + activity_id: note_activity.id + } + } + ) + end + test "publishes a delete activity to peers who signed fetch requests to the create acitvity/object." do fetcher = insert(:user, From f989626bad1498fd8d29f2a474e585610941938c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Fri, 10 Oct 2025 05:36:55 +0200 Subject: [PATCH 06/23] Fix publisher when publishing to a list of users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- lib/pleroma/web/activity_pub/publisher.ex | 27 ++++++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index b1c8a8fc1..b6c814aed 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -331,17 +331,21 @@ defmodule Pleroma.Web.ActivityPub.Publisher do Repo.checkout(fn -> Enum.each([priority_inboxes, other_inboxes], fn inboxes -> Enum.each(inboxes, fn inbox -> - %User{ap_id: ap_id} = Enum.find(recipients, fn actor -> actor.inbox == inbox end) + {%User{ap_id: ap_id}, priority} = + get_user_with_priority(inbox, priority_recipients, recipients) # Get all the recipients on the same host and add them to cc. Otherwise, a remote # instance would only accept a first message for the first recipient and ignore the rest. cc = get_cc_ap_ids(ap_id, recipients) - __MODULE__.enqueue_one(%{ - inbox: inbox, - cc: cc, - activity_id: activity.id - }) + __MODULE__.enqueue_one( + %{ + inbox: inbox, + cc: cc, + activity_id: activity.id + }, + priority: priority + ) end) end) end) @@ -403,4 +407,15 @@ defmodule Pleroma.Web.ActivityPub.Publisher do end def gather_nodeinfo_protocol_names, do: ["activitypub"] + + defp get_user_with_priority(inbox, priority_recipients, recipients) do + [{priority_recipients, 0}, {recipients, 1}] + |> Enum.find_value(fn {recipients, priority} -> + with %User{} = user <- Enum.find(recipients, fn actor -> actor.inbox == inbox end) do + {user, priority} + else + _ -> nil + end + end) + end end From 50e3cc67fc09bd9baf4dcb8f8abaa1d3b7fca374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Tue, 14 Oct 2025 20:29:06 +0200 Subject: [PATCH 07/23] Redirect /users/:nickname.rss to /users/:nickname/feed.rss instead of .atom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- changelog.d/rss-redirect.change | 1 + lib/pleroma/web/feed/user_controller.ex | 7 +++++-- test/pleroma/web/feed/user_controller_test.exs | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 changelog.d/rss-redirect.change diff --git a/changelog.d/rss-redirect.change b/changelog.d/rss-redirect.change new file mode 100644 index 000000000..cd8b099aa --- /dev/null +++ b/changelog.d/rss-redirect.change @@ -0,0 +1 @@ +Redirect /users/:nickname.rss to /users/:nickname/feed.rss instead of .atom \ No newline at end of file diff --git a/lib/pleroma/web/feed/user_controller.ex b/lib/pleroma/web/feed/user_controller.ex index 304313068..8a52d98e0 100644 --- a/lib/pleroma/web/feed/user_controller.ex +++ b/lib/pleroma/web/feed/user_controller.ex @@ -28,9 +28,12 @@ defmodule Pleroma.Web.Feed.UserController do ActivityPubController.call(conn, :user) end - def feed_redirect(conn, %{"nickname" => nickname}) do + def feed_redirect(%{assigns: assigns} = conn, %{"nickname" => nickname}) do + format = Map.get(assigns, :format, "atom") + format = if format in ["atom", "rss"], do: format, else: "atom" + with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do - redirect(conn, external: "#{Routes.user_feed_url(conn, :feed, user.nickname)}.atom") + redirect(conn, external: "#{Routes.user_feed_url(conn, :feed, user.nickname)}.#{format}") end end diff --git a/test/pleroma/web/feed/user_controller_test.exs b/test/pleroma/web/feed/user_controller_test.exs index 0a3aaff5c..916531cd5 100644 --- a/test/pleroma/web/feed/user_controller_test.exs +++ b/test/pleroma/web/feed/user_controller_test.exs @@ -282,6 +282,21 @@ defmodule Pleroma.Web.Feed.UserControllerTest do "#{Pleroma.Web.Endpoint.url()}/users/#{user.nickname}/feed.atom" end + test "redirects to rss feed when explicitly requested", %{conn: conn} do + note_activity = insert(:note_activity) + user = User.get_cached_by_ap_id(note_activity.data["actor"]) + + conn = + conn + |> put_req_header("accept", "application/xml") + |> get("/users/#{user.nickname}.rss") + + assert conn.status == 302 + + assert redirected_to(conn) == + "#{Pleroma.Web.Endpoint.url()}/users/#{user.nickname}/feed.rss" + end + test "with non-html / non-json format, it returns error when user is not found", %{conn: conn} do response = conn From 7e34d72860decc2d32704689903d3ccb0338f88f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Thu, 2 Oct 2025 13:33:50 +0200 Subject: [PATCH 08/23] Support Mozhi as translation provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- config/description.exs | 13 +++ lib/pleroma/language/translation/mozhi.ex | 109 ++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 lib/pleroma/language/translation/mozhi.ex diff --git a/config/description.exs b/config/description.exs index c61a344e8..68a2fc34e 100644 --- a/config/description.exs +++ b/config/description.exs @@ -3588,6 +3588,19 @@ config :pleroma, :config_description, [ label: "LibreTranslate API Key", type: :string, suggestions: ["YOUR_API_KEY"] + }, + %{ + group: {:subgroup, Pleroma.Language.Translation.Mozhi}, + key: :base_url, + label: "Mozhi instance URL", + type: :string + }, + %{ + group: {:subgroup, Pleroma.Language.Translation.Mozhi}, + key: :engine, + label: "Engine used for Mozhi", + type: :string, + suggestions: ["libretranslate"] } ] } diff --git a/lib/pleroma/language/translation/mozhi.ex b/lib/pleroma/language/translation/mozhi.ex new file mode 100644 index 000000000..958f2ef57 --- /dev/null +++ b/lib/pleroma/language/translation/mozhi.ex @@ -0,0 +1,109 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2024 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Language.Translation.Mozhi do + import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1] + + alias Pleroma.Language.Translation.Provider + + use Provider + + @behaviour Provider + + @name "Mozhi" + + @impl Provider + def configured?, do: not_empty_string(base_url()) and not_empty_string(engine()) + + @impl Provider + def translate(content, source_language, target_language) do + endpoint = + base_url() + |> URI.merge("/api/translate") + |> URI.to_string() + + case Pleroma.HTTP.get( + endpoint <> + "?" <> + URI.encode_query(%{ + engine: engine(), + text: content, + from: source_language, + to: target_language + }), + [{"Accept", "application/json"}] + ) do + {:ok, %{status: 200} = res} -> + %{ + "translated-text" => content, + "source_language" => source_language + } = Jason.decode!(res.body) + + {:ok, + %{ + content: content, + detected_source_language: source_language, + provider: @name + }} + + _ -> + {:error, :internal_server_error} + end + end + + @impl Provider + def supported_languages(type) when type in [:source, :target] do + path = + case type do + :source -> "/api/source_languages" + :target -> "/api/target_languages" + end + + endpoint = + base_url() + |> URI.merge(path) + |> URI.to_string() + + case Pleroma.HTTP.get( + endpoint <> + "?" <> + URI.encode_query(%{ + engine: engine() + }), + [{"Accept", "application/json"}] + ) do + {:ok, %{status: 200} = res} -> + languages = + Jason.decode!(res.body) + |> Enum.map(fn %{"Id" => language} -> language end) + + {:ok, languages} + + _ -> + {:error, :internal_server_error} + end + end + + @impl Provider + def languages_matrix do + with {:ok, source_languages} <- supported_languages(:source), + {:ok, target_languages} <- supported_languages(:target) do + {:ok, + Map.new(source_languages, fn language -> {language, target_languages -- [language]} end)} + else + {:error, error} -> {:error, error} + end + end + + @impl Provider + def name, do: @name + + defp base_url do + Pleroma.Config.get([__MODULE__, :base_url]) + end + + defp engine do + Pleroma.Config.get([__MODULE__, :engine]) + end +end From d56433be6994f44895a623a5f600195a9aacb02c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Wed, 15 Oct 2025 10:30:26 +0200 Subject: [PATCH 09/23] List Mozhi in suggestions for translation providers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- config/description.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/description.exs b/config/description.exs index 68a2fc34e..2755c36f9 100644 --- a/config/description.exs +++ b/config/description.exs @@ -3558,7 +3558,8 @@ config :pleroma, :config_description, [ type: :module, suggestions: [ Pleroma.Language.Translation.Deepl, - Pleroma.Language.Translation.Libretranslate + Pleroma.Language.Translation.Libretranslate, + Pleroma.Language.Translation.Mozhi ] }, %{ From bb1e643996cb3d7455411a6d4da25533c3d4b219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Wed, 15 Oct 2025 11:01:18 +0200 Subject: [PATCH 10/23] Update changelog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- changelog.d/translation-provider-mozhi.add | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/translation-provider-mozhi.add diff --git a/changelog.d/translation-provider-mozhi.add b/changelog.d/translation-provider-mozhi.add new file mode 100644 index 000000000..c3cf5940a --- /dev/null +++ b/changelog.d/translation-provider-mozhi.add @@ -0,0 +1 @@ +Support Mozhi translation provider From 1610d39f3645ad664dfde06940ba891082ded752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Tue, 21 Oct 2025 21:41:33 +0200 Subject: [PATCH 11/23] Revert "User.get_or_fetch_public_key_for_ap_id/1 is no longer required." This reverts commit c0a50b7c3e340cd621827922200daa0f29dc6e15. --- lib/pleroma/user.ex | 9 +++++++++ test/pleroma/user_test.exs | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 468e124b5..3d4815aca 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -2307,6 +2307,15 @@ defmodule Pleroma.User do def public_key(_), do: {:error, "key not found"} + def get_or_fetch_public_key_for_ap_id(ap_id) do + with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id), + {:ok, public_key} <- public_key(user) do + {:ok, public_key} + else + _ -> :error + end + end + def get_public_key_for_ap_id(ap_id) do with %User{} = user <- get_cached_by_ap_id(ap_id), {:ok, public_key} <- public_key(user) do diff --git a/test/pleroma/user_test.exs b/test/pleroma/user_test.exs index 0b4dc9197..b2533e9f1 100644 --- a/test/pleroma/user_test.exs +++ b/test/pleroma/user_test.exs @@ -1881,6 +1881,11 @@ defmodule Pleroma.UserTest do end end + test "get_or_fetch_public_key_for_ap_id fetches a user that's not in the db" do + assert {:ok, _key} = + User.get_or_fetch_public_key_for_ap_id("http://mastodon.example.org/users/admin") + end + test "get_public_key_for_ap_id returns correctly for user that's not in the db" do assert :error = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin") end From b38fedf342e0392d5cff2b64569f0c24981b57bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Tue, 21 Oct 2025 21:46:03 +0200 Subject: [PATCH 12/23] Fix fetching public keys with authorized fetch enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- changelog.d/authorized_fetch.fix | 1 + lib/pleroma/signature.ex | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/authorized_fetch.fix diff --git a/changelog.d/authorized_fetch.fix b/changelog.d/authorized_fetch.fix new file mode 100644 index 000000000..1db8e88c9 --- /dev/null +++ b/changelog.d/authorized_fetch.fix @@ -0,0 +1 @@ +Fix fetching public keys with authorized fetch enabled \ No newline at end of file diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index 47d9d46f6..fca61799b 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -54,7 +54,7 @@ defmodule Pleroma.Signature do def fetch_public_key(conn) do with {:ok, actor_id} <- get_actor_id(conn), - {:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do + {:ok, public_key} <- User.get_or_fetch_public_key_for_ap_id(actor_id) do {:ok, public_key} else e -> From 4f31cadbccdb2bf214159a2a408253cabf2f39a5 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 22 Oct 2025 13:39:10 -0700 Subject: [PATCH 13/23] Enable expiration of CICD job artifacts We have accumulated nearly 200GB of old artifacts that we do not need. --- .gitlab-ci.yml | 4 ++++ changelog.d/ci-artifacts.skip | 0 2 files changed, 4 insertions(+) create mode 100644 changelog.d/ci-artifacts.skip diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 47b66ae69..549a7853a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,9 @@ image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.15.8-otp-26 +default: + artifacts: + expire_in: 1 week + variables: &global_variables # Only used for the release ELIXIR_VER: 1.17.3 diff --git a/changelog.d/ci-artifacts.skip b/changelog.d/ci-artifacts.skip new file mode 100644 index 000000000..e69de29bb From a07305ca34c783c3a54210e93238682187729451 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 23 Oct 2025 10:58:11 -0700 Subject: [PATCH 14/23] GitLab support for default artifacts setting is broken https://gitlab.com/gitlab-org/gitlab/-/issues/404563 --- .gitlab-ci.yml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 549a7853a..33123737f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,9 +1,5 @@ image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.15.8-otp-26 -default: - artifacts: - expire_in: 1 week - variables: &global_variables # Only used for the release ELIXIR_VER: 1.17.3 @@ -23,6 +19,10 @@ workflow: - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS when: never +# Default artifacts configuration +.default_artifacts: &default_artifacts + expire_in: 30 days + cache: &global_cache_policy key: $CI_JOB_IMAGE-$CI_COMMIT_SHORT_SHA paths: @@ -60,6 +60,7 @@ check-changelog: before_script: '' after_script: '' cache: {} + artifacts: *default_artifacts script: - apk add git - sh ./tools/check-changelog @@ -75,6 +76,7 @@ check-changelog: .using-ci-base: tags: - amd64 + artifacts: *default_artifacts build-1.15.8-otp-26: extends: @@ -105,6 +107,7 @@ spec-build: artifacts: paths: - spec.json + expire_in: 42 years script: - mix pleroma.openapi_spec spec.json @@ -157,6 +160,7 @@ unit-testing-1.15.8-otp-26: - su testuser -c "HOME=/home/testuser mix pleroma.test_runner --cover --preload-modules" coverage: '/^Line total: ([^ ]*%)$/' artifacts: + expire_in: 30 days reports: coverage_report: coverage_format: cobertura @@ -175,6 +179,7 @@ unit-testing-1.18.3-otp-27: formatting-1.15: extends: .build_changes_policy + artifacts: *default_artifacts image: &formatting_elixir elixir:1.15-alpine stage: lint cache: *testing_cache_policy @@ -189,6 +194,7 @@ formatting-1.15: cycles-1.15: extends: .build_changes_policy + artifacts: *default_artifacts image: *formatting_elixir stage: lint cache: {} @@ -212,7 +218,7 @@ dialyzer: - .using-ci-base stage: lint allow_failure: true - when: manual + when: manual cache: *testing_cache_policy tags: - feld @@ -221,6 +227,7 @@ dialyzer: docs-deploy: stage: deploy + artifacts: *default_artifacts cache: *testing_cache_policy image: alpine:latest only: @@ -245,6 +252,7 @@ review_app: except: - master - develop + artifacts: *default_artifacts script: - echo "$CI_ENVIRONMENT_SLUG" - mkdir -p ~/.ssh @@ -264,6 +272,7 @@ spec-deploy: artifacts: paths: - spec.json + expire_in: 30 days only: - develop@pleroma/pleroma image: alpine:latest @@ -276,6 +285,7 @@ spec-deploy: stop_review_app: image: alpine:3.9 stage: deploy + artifacts: *default_artifacts before_script: - apk update && apk add openssh-client git when: manual From a0225ddc7931f28e1d34af613e0532cbfc8bbc3f Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 23 Oct 2025 11:44:11 -0700 Subject: [PATCH 15/23] CI: Allow running pipelines from web or directly for a tag --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 33123737f..3f1656a08 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,6 +16,8 @@ workflow: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == "develop" - if: $CI_COMMIT_BRANCH == "stable" + - if: $CI_PIPELINE_SOURCE == "web" + - if: $CI_COMMIT_TAG - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS when: never From d09ec25454e7634b2bac9c00a347a197a489d7bb Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 23 Oct 2025 12:12:19 -0700 Subject: [PATCH 16/23] CI: use triggers for docs and api-docs deployments This is a "bridge job" which is more efficient. We do not need a token or API call then to make the CI run in the target repos. --- .gitlab-ci.yml | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3f1656a08..3d8fc1d2b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -229,16 +229,13 @@ dialyzer: docs-deploy: stage: deploy - artifacts: *default_artifacts - cache: *testing_cache_policy - image: alpine:latest + trigger: + project: pleroma/docs + branch: develop + strategy: depend only: - stable@pleroma/pleroma - develop@pleroma/pleroma - before_script: - - apk add curl - script: - - curl --fail-with-body -X POST -F"token=$DOCS_PIPELINE_TRIGGER" -F'ref=master' -F"variables[BRANCH]=$CI_COMMIT_REF_NAME" https://git.pleroma.social/api/v4/projects/673/trigger/pipeline review_app: image: alpine:3.9 stage: deploy @@ -271,17 +268,12 @@ review_app: spec-deploy: stage: deploy - artifacts: - paths: - - spec.json - expire_in: 30 days + trigger: + project: pleroma/api-docs + branch: develop + strategy: depend only: - develop@pleroma/pleroma - image: alpine:latest - before_script: - - apk add curl - script: - - curl --fail-with-body -X POST -F"token=$API_DOCS_PIPELINE_TRIGGER" -F'ref=master' -F"variables[BRANCH]=$CI_COMMIT_REF_NAME" -F"variables[JOB_REF]=$CI_JOB_ID" https://git.pleroma.social/api/v4/projects/1130/trigger/pipeline stop_review_app: From b6da3f490b1f5b1eb38cbd5ab9a117186cdb0daf Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 23 Oct 2025 13:41:26 -0700 Subject: [PATCH 17/23] Fix branch names for pleroma/docs and pleroma/api-docs triggers --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3d8fc1d2b..f46aafcea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -231,7 +231,7 @@ docs-deploy: stage: deploy trigger: project: pleroma/docs - branch: develop + branch: master strategy: depend only: - stable@pleroma/pleroma @@ -270,7 +270,7 @@ spec-deploy: stage: deploy trigger: project: pleroma/api-docs - branch: develop + branch: master strategy: depend only: - develop@pleroma/pleroma From 6f7e521488e0c8dba8a9e8e10577f241fa7a010f Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 23 Oct 2025 16:41:52 -0700 Subject: [PATCH 18/23] CI: pass the variable CI_PIPELINE_ID through to the api-docs build job --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f46aafcea..ed51169e0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -274,7 +274,8 @@ spec-deploy: strategy: depend only: - develop@pleroma/pleroma - + variables: + PIPELINE_ID: $CI_PIPELINE_ID stop_review_app: image: alpine:3.9 From d15f98bdecbca504576feace7137487855f411fc Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 23 Oct 2025 21:10:13 -0700 Subject: [PATCH 19/23] CI: Use the dotenv report method to capture the spec-build internal job id and pass it through to the spec-deploy job --- .gitlab-ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ed51169e0..941514e18 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -109,9 +109,12 @@ spec-build: artifacts: paths: - spec.json + reports: + dotenv: build.env expire_in: 42 years script: - mix pleroma.openapi_spec spec.json + - echo "SPEC_BUILD_JOB_ID=$CI_JOB_ID" >> build.env benchmark: extends: @@ -275,7 +278,7 @@ spec-deploy: only: - develop@pleroma/pleroma variables: - PIPELINE_ID: $CI_PIPELINE_ID + SPEC_BUILD_JOB_ID: $SPEC_BUILD_JOB_ID stop_review_app: image: alpine:3.9 From 724cdc44fc1c859c7ec804c3cbd3ca28001ae934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Sat, 1 Nov 2025 11:25:55 +0100 Subject: [PATCH 20/23] Fix typo in Pleroma name in docs --- changelog.d/plaroma.skip | 1 + docs/installation/optional/media_graphics_packages.md | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 changelog.d/plaroma.skip diff --git a/changelog.d/plaroma.skip b/changelog.d/plaroma.skip new file mode 100644 index 000000000..184ca07e0 --- /dev/null +++ b/changelog.d/plaroma.skip @@ -0,0 +1 @@ +i don't think it's called plaroma \ No newline at end of file diff --git a/docs/installation/optional/media_graphics_packages.md b/docs/installation/optional/media_graphics_packages.md index ad01d47d1..89125d1c6 100644 --- a/docs/installation/optional/media_graphics_packages.md +++ b/docs/installation/optional/media_graphics_packages.md @@ -16,7 +16,7 @@ Note: the packages are not required with the current default settings of Pleroma It is required for the following Pleroma features: -* `Pleroma.Upload.Filters.Mogrify`, `Pleroma.Upload.Filters.Mogrifun` upload filters (related config: `Plaroma.Upload/filters` in `config/config.exs`) +* `Pleroma.Upload.Filters.Mogrify`, `Pleroma.Upload.Filters.Mogrifun` upload filters (related config: `Pleroma.Upload/filters` in `config/config.exs`) * Media preview proxy for still images (related config: `media_preview_proxy/enabled` in `config/config.exs`) ## `ffmpeg` @@ -33,5 +33,5 @@ It is required for the following Pleroma features: It is required for the following Pleroma features: -* `Pleroma.Upload.Filters.Exiftool.StripLocation` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) -* `Pleroma.Upload.Filters.Exiftool.ReadDescription` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) +* `Pleroma.Upload.Filters.Exiftool.StripLocation` upload filter (related config: `Pleroma.Upload/filters` in `config/config.exs`) +* `Pleroma.Upload.Filters.Exiftool.ReadDescription` upload filter (related config: `Pleroma.Upload/filters` in `config/config.exs`) From b975dce9ba8d417f7886d68586de1fad5621f7ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Sat, 1 Nov 2025 11:43:08 +0100 Subject: [PATCH 21/23] Add `timelines_access` to InstanceView MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- changelog.d/instance-view-timeline-access.add | 1 + .../web/mastodon_api/views/instance_view.ex | 26 ++++++++++++++++++- .../controllers/instance_controller_test.exs | 24 +++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 changelog.d/instance-view-timeline-access.add diff --git a/changelog.d/instance-view-timeline-access.add b/changelog.d/instance-view-timeline-access.add new file mode 100644 index 000000000..eb414e786 --- /dev/null +++ b/changelog.d/instance-view-timeline-access.add @@ -0,0 +1 @@ +Add `timelines_access` to InstanceView diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index 1b6f26af7..b21383659 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -257,10 +257,34 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do vapid: %{ public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) }, - translation: %{enabled: Pleroma.Language.Translation.configured?()} + translation: %{enabled: Pleroma.Language.Translation.configured?()}, + timelines_access: %{ + live_feeds: timelines_access(), + hashtag_feeds: timelines_access(), + # not implemented in Pleroma + trending_link_feeds: %{ + local: "disabled", + remote: "disabled" + } + } }) end + defp timelines_access do + %{ + local: timeline_access(:local), + remote: timeline_access(:federated) + } + end + + defp timeline_access(kind) do + if Config.restrict_unauthenticated_access?(:timelines, kind) do + "authenticated" + else + "public" + end + end + defp pleroma_configuration(instance) do base_urls = %{} diff --git a/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs index 8a0fe5259..10c0b6ea7 100644 --- a/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/instance_controller_test.exs @@ -194,4 +194,28 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do refute Map.has_key?(result["pleroma"]["metadata"]["base_urls"], "media_proxy") refute Map.has_key?(result["pleroma"]["metadata"]["base_urls"], "upload") end + + test "display timeline access restrictions", %{conn: conn} do + clear_config([:restrict_unauthenticated, :timelines, :local], true) + clear_config([:restrict_unauthenticated, :timelines, :federated], false) + + conn = get(conn, "/api/v2/instance") + + assert result = json_response_and_validate_schema(conn, 200) + + assert result["configuration"]["timelines_access"] == %{ + "live_feeds" => %{ + "local" => "authenticated", + "remote" => "public" + }, + "hashtag_feeds" => %{ + "local" => "authenticated", + "remote" => "public" + }, + "trending_link_feeds" => %{ + "local" => "disabled", + "remote" => "disabled" + } + } + end end From 9da1875c36caa5872e2e241a5f322f5747d4d5de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Sat, 22 Nov 2025 16:51:21 +0100 Subject: [PATCH 22/23] Send push notifications for statuses from subscribed accounts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- changelog.d/status-push-notification.fix | 1 + lib/pleroma/web/push/subscription.ex | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/status-push-notification.fix diff --git a/changelog.d/status-push-notification.fix b/changelog.d/status-push-notification.fix new file mode 100644 index 000000000..ed0bbff33 --- /dev/null +++ b/changelog.d/status-push-notification.fix @@ -0,0 +1 @@ +Send push notifications for statuses from subscribed accounts diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex index 6fc45bd61..c6fe69e84 100644 --- a/lib/pleroma/web/push/subscription.ex +++ b/lib/pleroma/web/push/subscription.ex @@ -26,7 +26,7 @@ defmodule Pleroma.Web.Push.Subscription do end # credo:disable-for-next-line Credo.Check.Readability.MaxLineLength - @supported_alert_types ~w[follow favourite mention reblog poll pleroma:chat_mention pleroma:emoji_reaction]a + @supported_alert_types ~w[follow favourite mention status reblog poll pleroma:chat_mention pleroma:emoji_reaction]a defp alerts(%{data: %{alerts: alerts}}) do alerts = Map.take(alerts, @supported_alert_types) From dc85b2799063ab5f04a64a0b10a1a91842550d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Mon, 24 Nov 2025 22:17:05 +0100 Subject: [PATCH 23/23] Minor cleanup and comment fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- changelog.d/notification-cleanup.skip | 0 lib/pleroma/notification.ex | 4 +-- lib/pleroma/user.ex | 4 +-- lib/pleroma/web/common_api/utils.ex | 43 +++++++++++++-------------- 4 files changed, 24 insertions(+), 27 deletions(-) create mode 100644 changelog.d/notification-cleanup.skip diff --git a/changelog.d/notification-cleanup.skip b/changelog.d/notification-cleanup.skip new file mode 100644 index 000000000..e69de29bb diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 80844ed71..000bc0dc7 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -526,9 +526,7 @@ defmodule Pleroma.Notification do %Activity{data: %{"type" => "Create"}} = activity, local_only ) do - notification_enabled_ap_ids = - [] - |> Utils.maybe_notify_subscribers(activity) + notification_enabled_ap_ids = Utils.get_notified_subscribers(activity) potential_receivers = User.get_users_from_set(notification_enabled_ap_ids, local_only: local_only) diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 540d0af6a..f8e32af8a 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -233,8 +233,8 @@ defmodule Pleroma.User do for {_relationship_type, [{_outgoing_relation, outgoing_relation_target}, _]} <- @user_relationships_config do # `def blocked_users_relation/2`, `def muted_users_relation/2`, - # `def reblog_muted_users_relation/2`, `def notification_muted_users/2`, - # `def subscriber_users/2`, `def endorsed_users_relation/2` + # `def reblog_muted_users_relation/2`, `def notification_muted_users_relation/2`, + # `def subscriber_users_relation/2`, `def endorsed_users_relation/2` def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do target_users_query = assoc(user, unquote(outgoing_relation_target)) diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 52c08f00f..91bf9c502 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -402,28 +402,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do def maybe_notify_mentioned_recipients(recipients, _), do: recipients - def maybe_notify_subscribers( - recipients, - %Activity{data: %{"actor" => actor, "type" => "Create"}} = activity - ) do - # Do not notify subscribers if author is making a reply - with %Object{data: object} <- Object.normalize(activity, fetch: false), - nil <- object["inReplyTo"], - %User{} = user <- User.get_cached_by_ap_id(actor) do - subscriber_ids = - user - |> User.subscriber_users() - |> Enum.filter(&Visibility.visible_for_user?(activity, &1)) - |> Enum.map(& &1.ap_id) - - recipients ++ subscriber_ids - else - _e -> recipients - end - end - - def maybe_notify_subscribers(recipients, _), do: recipients - def maybe_notify_followers(recipients, %Activity{data: %{"type" => "Move"}} = activity) do with %User{} = user <- User.get_cached_by_ap_id(activity.actor) do user @@ -437,6 +415,27 @@ defmodule Pleroma.Web.CommonAPI.Utils do def maybe_notify_followers(recipients, _), do: recipients + def get_notified_subscribers( + %Activity{data: %{"actor" => actor, "type" => "Create"}} = activity + ) do + # Do not notify subscribers if author is making a reply + with %Object{data: object} <- Object.normalize(activity, fetch: false), + nil <- object["inReplyTo"], + %User{} = user <- User.get_cached_by_ap_id(actor) do + subscriber_ids = + user + |> User.subscriber_users() + |> Enum.filter(&Visibility.visible_for_user?(activity, &1)) + |> Enum.map(& &1.ap_id) + + subscriber_ids + else + _e -> [] + end + end + + def get_notified_subscribers(_), do: [] + def maybe_extract_mentions(%{"tag" => tag}) do tag |> Enum.filter(fn x -> is_map(x) && x["type"] == "Mention" end)