From 50e3750758510a2790ce6229d9194ace72d1e012 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 5 May 2021 13:58:50 -0500 Subject: [PATCH 01/22] Add notice compatibility routes for other frontends Fixes: https://git.pleroma.social/pleroma/pleroma/-/issues/1785 --- lib/pleroma/web/router.ex | 5 ++ .../web/static_fe/static_fe_controller.ex | 9 ++++ .../web/o_status/o_status_controller_test.exs | 50 +++++++++++++++++++ .../web/plugs/frontend_static_plug_test.exs | 2 + 4 files changed, 66 insertions(+) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 72ad14f05..5e732e4bb 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -637,6 +637,11 @@ defmodule Pleroma.Web.Router do get("/activities/:uuid", OStatus.OStatusController, :activity) get("/notice/:id", OStatus.OStatusController, :notice) + # Notice compatibility routes for other frontends + get("/@:nickname/:id", OStatus.OStatusController, :notice) + get("/@:nickname/posts/:id", OStatus.OStatusController, :notice) + get("/:nickname/status/:id", OStatus.OStatusController, :notice) + # Mastodon compatibility routes get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object) get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity) diff --git a/lib/pleroma/web/static_fe/static_fe_controller.ex b/lib/pleroma/web/static_fe/static_fe_controller.ex index fe485d10d..421070636 100644 --- a/lib/pleroma/web/static_fe/static_fe_controller.ex +++ b/lib/pleroma/web/static_fe/static_fe_controller.ex @@ -168,6 +168,15 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts), do: assign(conn, :notice_id, notice_id) + defp assign_id(%{path_info: ["@" <> _nickname, notice_id]} = conn, _opts), + do: assign(conn, :notice_id, notice_id) + + defp assign_id(%{path_info: ["@" <> _nickname, "posts", notice_id]} = conn, _opts), + do: assign(conn, :notice_id, notice_id) + + defp assign_id(%{path_info: [_nickname, "status", notice_id]} = conn, _opts), + do: assign(conn, :notice_id, notice_id) + defp assign_id(%{path_info: ["users", user_id]} = conn, _opts), do: assign(conn, :username_or_id, user_id) diff --git a/test/pleroma/web/o_status/o_status_controller_test.exs b/test/pleroma/web/o_status/o_status_controller_test.exs index 2038f4ddd..fab042439 100644 --- a/test/pleroma/web/o_status/o_status_controller_test.exs +++ b/test/pleroma/web/o_status/o_status_controller_test.exs @@ -343,4 +343,54 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do |> response(200) end end + + describe "notice compatibility routes" do + test "Soapbox FE", %{conn: conn} do + user = insert(:user) + note_activity = insert(:note_activity, user: user) + + resp = + conn + |> put_req_header("accept", "text/html") + |> get("/@#{user.nickname}/posts/#{note_activity.id}") + |> response(200) + + expected = + "" + + assert resp =~ expected + end + + test "Mastodon", %{conn: conn} do + user = insert(:user) + note_activity = insert(:note_activity, user: user) + + resp = + conn + |> put_req_header("accept", "text/html") + |> get("/@#{user.nickname}/#{note_activity.id}") + |> response(200) + + expected = + "" + + assert resp =~ expected + end + + test "Twitter", %{conn: conn} do + user = insert(:user) + note_activity = insert(:note_activity, user: user) + + resp = + conn + |> put_req_header("accept", "text/html") + |> get("/#{user.nickname}/status/#{note_activity.id}") + |> response(200) + + expected = + "" + + assert resp =~ expected + end + end end diff --git a/test/pleroma/web/plugs/frontend_static_plug_test.exs b/test/pleroma/web/plugs/frontend_static_plug_test.exs index 100b83d6a..7596a9a54 100644 --- a/test/pleroma/web/plugs/frontend_static_plug_test.exs +++ b/test/pleroma/web/plugs/frontend_static_plug_test.exs @@ -86,6 +86,8 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do "objects", "activities", "notice", + "@:nickname", + ":nickname", "users", "tags", "mailer", From b15c4629ff3093353ac5e37d381db1cdc4da1c3a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 5 May 2021 14:36:27 -0500 Subject: [PATCH 02/22] CHANGELOG: notice routes --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bb4b1e73..625cf3266 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - Don't crash so hard when email settings are invalid. +- Display OpenGraph data on alternative notice routes. ## Unreleased (Patch) From 079afd32d86ae5211106cdc9e1916c6640bedd39 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 23 Jun 2021 14:19:26 -0500 Subject: [PATCH 03/22] Enable :warnings_as_errors for CI only --- mix.exs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index a0a6106a9..85d5c7b55 100644 --- a/mix.exs +++ b/mix.exs @@ -8,7 +8,7 @@ defmodule Pleroma.Mixfile do elixir: "~> 1.9", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), - elixirc_options: [warnings_as_errors: warnings_as_errors(Mix.env())], + elixirc_options: [warnings_as_errors: warnings_as_errors()], xref: [exclude: [:eldap]], start_permanent: Mix.env() == :prod, aliases: aliases(), @@ -90,8 +90,7 @@ defmodule Pleroma.Mixfile do defp elixirc_paths(:test), do: ["lib", "test/support"] defp elixirc_paths(_), do: ["lib"] - defp warnings_as_errors(:prod), do: false - defp warnings_as_errors(_), do: true + defp warnings_as_errors, do: System.get_env("CI") == "true" # Specifies OAuth dependencies. defp oauth_deps do From 40414bf177c93b39d75c6091ef0ce1db093edb6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 21 Nov 2021 16:53:30 +0100 Subject: [PATCH 04/22] MastoAPI: Add user notes on accounts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- docs/development/API/pleroma_api.md | 6 ++- lib/pleroma/user_note.ex | 52 +++++++++++++++++++ .../api_spec/operations/account_operation.ex | 43 +++++++++++++++ lib/pleroma/web/api_spec/schemas/account.ex | 1 + .../api_spec/schemas/account_relationship.ex | 2 + lib/pleroma/web/api_spec/schemas/status.ex | 1 + .../controllers/account_controller.ex | 18 ++++++- .../web/mastodon_api/views/account_view.ex | 8 ++- lib/pleroma/web/router.ex | 1 + .../20211121000000_create_user_notes.exs | 17 ++++++ .../controllers/account_controller_test.exs | 14 +++++ .../mastodon_api/views/account_view_test.exs | 3 +- 12 files changed, 160 insertions(+), 6 deletions(-) create mode 100644 lib/pleroma/user_note.ex create mode 100644 priv/repo/migrations/20211121000000_create_user_notes.exs diff --git a/docs/development/API/pleroma_api.md b/docs/development/API/pleroma_api.md index 8f6422da0..b401a7cc7 100644 --- a/docs/development/API/pleroma_api.md +++ b/docs/development/API/pleroma_api.md @@ -162,7 +162,8 @@ See [Admin-API](admin_api.md) "requested": false, "domain_blocking": false, "showing_reblogs": true, - "endorsed": false + "endorsed": false, + "note": "" } ``` @@ -186,7 +187,8 @@ See [Admin-API](admin_api.md) "requested": false, "domain_blocking": false, "showing_reblogs": true, - "endorsed": false + "endorsed": false, + "note": "" } ``` diff --git a/lib/pleroma/user_note.ex b/lib/pleroma/user_note.ex new file mode 100644 index 000000000..5e82d359f --- /dev/null +++ b/lib/pleroma/user_note.ex @@ -0,0 +1,52 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.UserNote do + use Ecto.Schema + + import Ecto.Changeset + import Ecto.Query + + alias Pleroma.Repo + alias Pleroma.User + alias Pleroma.UserNote + + schema "user_notes" do + belongs_to(:source, User, type: FlakeId.Ecto.CompatType) + belongs_to(:target, User, type: FlakeId.Ecto.CompatType) + field(:comment, :string) + + timestamps() + end + + def changeset(%UserNote{} = user_note, params \\ %{}) do + user_note + |> cast(params, [:source_id, :target_id, :comment]) + |> validate_required([:source_id, :target_id]) + end + + def show(%User{} = source, %User{} = target) do + with %UserNote{} = note <- + UserNote + |> where(source_id: ^source.id, target_id: ^target.id) + |> Repo.one() do + note.comment + else + _ -> "" + end + end + + def create(%User{} = source, %User{} = target, comment) do + %UserNote{} + |> changeset(%{ + source_id: source.id, + target_id: target.id, + comment: comment + }) + |> Repo.insert( + on_conflict: {:replace, [:comment]}, + conflict_target: [:source_id, :target_id] + ) + end +end diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 54e5ebc76..6bec9f178 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -328,6 +328,29 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end + def note_operation do + %Operation{ + tags: ["Account actions"], + summary: "Create note", + operationId: "AccountController.note", + security: [%{"oAuth" => ["follow", "write:accounts"]}], + requestBody: request_body("Parameters", note_request()), + description: "Create a note for the given account.", + parameters: [ + %Reference{"$ref": "#/components/parameters/accountIdOrNickname"}, + Operation.parameter( + :comment, + :query, + %Schema{type: :string}, + "Account note body" + ) + ], + responses: %{ + 200 => Operation.response("Relationship", "application/json", AccountRelationship) + } + } + end + def follow_by_uri_operation do %Operation{ tags: ["Account actions"], @@ -685,6 +708,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "blocked_by" => true, "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "domain_blocking" => false, "subscribing" => false, @@ -699,6 +723,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "blocked_by" => true, "muting" => true, "muting_notifications" => false, + "note" => "", "requested" => true, "domain_blocking" => false, "subscribing" => false, @@ -713,6 +738,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "blocked_by" => false, "muting" => true, "muting_notifications" => false, + "note" => "", "requested" => false, "domain_blocking" => true, "subscribing" => true, @@ -760,6 +786,23 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do } end + defp note_request do + %Schema{ + title: "AccountNoteRequest", + description: "POST body for adding anote for an account", + type: :object, + properties: %{ + comment: %Schema{ + type: :string, + description: "Account note body", + } + }, + example: %{ + "comment" => "Example note" + } + } + end + defp array_of_lists do %Schema{ title: "ArrayOfLists", diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index bd7143ab9..e0bd2728b 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -194,6 +194,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do "id" => "9tKi3esbG7OQgZ2920", "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "showing_reblogs" => true, "subscribing" => false diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship.ex b/lib/pleroma/web/api_spec/schemas/account_relationship.ex index 16b73ebb4..163066032 100644 --- a/lib/pleroma/web/api_spec/schemas/account_relationship.ex +++ b/lib/pleroma/web/api_spec/schemas/account_relationship.ex @@ -22,6 +22,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do id: FlakeID, muting: %Schema{type: :boolean}, muting_notifications: %Schema{type: :boolean}, + note: %Schema{type: :string}, requested: %Schema{type: :boolean}, showing_reblogs: %Schema{type: :boolean}, subscribing: %Schema{type: :boolean} @@ -36,6 +37,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do "id" => "9tKi3esbG7OQgZ2920", "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "showing_reblogs" => true, "subscribing" => false diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 3d042dc19..60801f322 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -282,6 +282,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "id" => "9toJCsKN7SmSf3aj5c", "muting" => false, "muting_notifications" => false, + "note" => "", "requested" => false, "showing_reblogs" => true, "subscribing" => false diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 5fcbffc34..8a43d49d3 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -15,6 +15,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do alias Pleroma.Maps alias Pleroma.User + alias Pleroma.UserNote alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.Pipeline @@ -53,7 +54,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do when action in [:verify_credentials, :endorsements, :identity_proofs] ) - plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :update_credentials) + plug( + OAuthScopesPlug, + %{scopes: ["write:accounts"]} + when action in [:update_credentials, :note] + ) plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists) @@ -79,7 +84,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute]) @relationship_actions [:follow, :unfollow] - @needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a + @needs_account ~W(followers following lists follow unfollow mute unmute block unblock note)a plug( RateLimiter, @@ -435,6 +440,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end end + @doc "POST /api/v1/accounts/:id/note" + def note(%{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn, _params) do + with {:ok, _user_note} <- UserNote.create(noter, target, comment) do + render(conn, "relationship.json", user: noter, target: target) + else + {:error, message} -> json_response(conn, :forbidden, %{error: message}) + end + end + @doc "POST /api/v1/follows" def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do case User.get_cached_by_nickname(uri) do diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 9e9de33f6..a3a9f9548 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do alias Pleroma.FollowingRelationship alias Pleroma.User + alias Pleroma.UserNote alias Pleroma.UserRelationship alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.MastodonAPI.AccountView @@ -156,7 +157,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do target, &User.muting_reblogs?(&1, &2) ), - endorsed: false + endorsed: false, + note: + UserNote.show( + reading_user, + target + ) } end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index abb332ec2..ca5db8ea3 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -456,6 +456,7 @@ defmodule Pleroma.Web.Router do post("/accounts/:id/unblock", AccountController, :unblock) post("/accounts/:id/mute", AccountController, :mute) post("/accounts/:id/unmute", AccountController, :unmute) + post("/accounts/:id/note", AccountController, :note) get("/conversations", ConversationController, :index) post("/conversations/:id/read", ConversationController, :mark_as_read) diff --git a/priv/repo/migrations/20211121000000_create_user_notes.exs b/priv/repo/migrations/20211121000000_create_user_notes.exs new file mode 100644 index 000000000..8fc23749f --- /dev/null +++ b/priv/repo/migrations/20211121000000_create_user_notes.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.CreateUserNotes do + use Ecto.Migration + + def change do + create_if_not_exists table(:user_notes) do + add(:source_id, references(:users, type: :uuid, on_delete: :delete_all)) + add(:target_id, references(:users, type: :uuid, on_delete: :delete_all)) + add(:comment, :string) + + timestamps() + end + + create_if_not_exists( + unique_index(:user_notes, [:source_id, :target_id]) + ) + end +end 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 a92a58224..48e658dd2 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1776,4 +1776,18 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do assert [%{"id" => ^id2}] = result end + + test "create a note on a user" do + %{conn: conn} = oauth_access(["write:accounts"]) + other_user = insert(:user) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{other_user.id}/note", %{ + "comment" => "Example note" + }) + + assert %{"note" => "Example note"} = json_response_and_validate_schema(ret_conn, 200) + end end diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs index 60881756d..9fe9d73bc 100644 --- a/test/pleroma/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -271,7 +271,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do requested: false, domain_blocking: false, showing_reblogs: true, - endorsed: false + endorsed: false, + note: "" } test "represent a relationship for the following and followed user" do From 106b5c26781dd1e92b6cd820b3dff41a27a4c4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 21 Nov 2021 17:36:37 +0100 Subject: [PATCH 05/22] Fix a typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 6bec9f178..6cedada2c 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -789,7 +789,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do defp note_request do %Schema{ title: "AccountNoteRequest", - description: "POST body for adding anote for an account", + description: "POST body for adding a note for an account", type: :object, properties: %{ comment: %Schema{ From cb76faece99c706685b71ad5a13943036b481645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 21 Nov 2021 17:50:42 +0100 Subject: [PATCH 06/22] Update test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../controllers/account_controller_test.exs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) 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 48e658dd2..4f855ac5c 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -1778,16 +1778,19 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do end test "create a note on a user" do - %{conn: conn} = oauth_access(["write:accounts"]) + %{conn: conn} = oauth_access(["write:accounts", "read:follows"]) other_user = insert(:user) - ret_conn = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/v1/accounts/#{other_user.id}/note", %{ - "comment" => "Example note" - }) + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{other_user.id}/note", %{ + "comment" => "Example note" + }) - assert %{"note" => "Example note"} = json_response_and_validate_schema(ret_conn, 200) + assert [%{"note" => "Example note"}] = + conn + |> put_req_header("content-type", "application/json") + |> get("/api/v1/accounts/relationships?id=#{other_user.id}") + |> json_response_and_validate_schema(200) end end From 8e040e098b1176098123e52608a9a73adec2b5e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 21 Nov 2021 18:17:06 +0100 Subject: [PATCH 07/22] Lint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- .../web/mastodon_api/controllers/account_controller.ex | 5 ++++- priv/repo/migrations/20211121000000_create_user_notes.exs | 4 +--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 6cedada2c..4aca16e72 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -794,7 +794,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do properties: %{ comment: %Schema{ type: :string, - description: "Account note body", + description: "Account note body" } }, example: %{ diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 8a43d49d3..a2c4920c1 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -441,7 +441,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do end @doc "POST /api/v1/accounts/:id/note" - def note(%{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn, _params) do + def note( + %{assigns: %{user: noter, account: target}, body_params: %{comment: comment}} = conn, + _params + ) do with {:ok, _user_note} <- UserNote.create(noter, target, comment) do render(conn, "relationship.json", user: noter, target: target) else diff --git a/priv/repo/migrations/20211121000000_create_user_notes.exs b/priv/repo/migrations/20211121000000_create_user_notes.exs index 8fc23749f..b75e11695 100644 --- a/priv/repo/migrations/20211121000000_create_user_notes.exs +++ b/priv/repo/migrations/20211121000000_create_user_notes.exs @@ -10,8 +10,6 @@ defmodule Pleroma.Repo.Migrations.CreateUserNotes do timestamps() end - create_if_not_exists( - unique_index(:user_notes, [:source_id, :target_id]) - ) + create_if_not_exists(unique_index(:user_notes, [:source_id, :target_id])) end end From 588bcbac55ebbaa1ea68792a1f60aa92c9915f69 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Mon, 22 Nov 2021 10:54:44 +0000 Subject: [PATCH 08/22] Apply 2 suggestion(s) to 2 file(s) --- lib/pleroma/web/api_spec/operations/account_operation.ex | 2 +- lib/pleroma/web/mastodon_api/controllers/account_controller.ex | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 4aca16e72..8613f3a98 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -331,7 +331,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do def note_operation do %Operation{ tags: ["Account actions"], - summary: "Create note", + summary: "Set a private note about a user.", operationId: "AccountController.note", security: [%{"oAuth" => ["follow", "write:accounts"]}], requestBody: request_body("Parameters", note_request()), diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index a2c4920c1..5dfbecf5a 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -447,8 +447,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do ) do with {:ok, _user_note} <- UserNote.create(noter, target, comment) do render(conn, "relationship.json", user: noter, target: target) - else - {:error, message} -> json_response(conn, :forbidden, %{error: message}) end end From cd5fb84b76a51fe6c7b5d672298a87c34737c303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 22 Nov 2021 19:44:30 +0100 Subject: [PATCH 09/22] remote_interaction API endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../operations/twitter_util_operation.ex | 26 +++++++++++++++++++ lib/pleroma/web/router.ex | 1 + .../controllers/util_controller.ex | 9 +++++++ 3 files changed, 36 insertions(+) diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex index ebcfd3be2..1a2dbb166 100644 --- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex +++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex @@ -237,4 +237,30 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})} } end + + def remote_interaction_operation do + %Operation{ + tags: ["Accounts"], + summary: "Remote interaction", + operationId: "UtilController.remote_interaction", + requestBody: request_body("Parameters", remote_interaction_request(), required: true), + responses: %{ + 200 => + Operation.response("Remote interaction URL", "application/json", %Schema{type: :object}) + } + } + end + + defp remote_interaction_request do + %Schema{ + title: "RemoteInteractionRequest", + description: "POST body for remote interaction", + type: :object, + required: [:ap_id, :profile], + properties: %{ + ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"}, + profile: %Schema{type: :string, description: "Remote profile webfinger"} + } + } + end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index abb332ec2..f8bafd3c2 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -150,6 +150,7 @@ defmodule Pleroma.Web.Router do get("/emoji", UtilController, :emoji) get("/captcha", UtilController, :captcha) get("/healthcheck", UtilController, :healthcheck) + post("/remote_interaction", UtilController, :remote_interaction) end scope "/api/v1/pleroma", Pleroma.Web do diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index ef43f7682..cbcef7475 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -62,6 +62,15 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do end end + def remote_interaction(%{body_params: %{ap_id: ap_id, profile: profile}} = conn, _params) do + with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile) do + conn + |> json(%{url: String.replace(template, "{uri}", ap_id)}) + else + _e -> json(conn, %{error: "Couldn't find user"}) + end + end + def frontend_configurations(conn, _params) do render(conn, "frontend_configurations.json") end From 64a4c147b1836be8af0c87b23073ed82bb9cf67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 6 Dec 2021 18:00:58 +0100 Subject: [PATCH 10/22] MastoAPI: accept notify param in follow request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../web/api_spec/operations/account_operation.ex | 6 ++++++ lib/pleroma/web/mastodon_api/mastodon_api.ex | 12 +++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index 54e5ebc76..cb978c775 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -226,6 +226,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do type: :boolean, description: "Receive this account's reblogs in home timeline? Defaults to true.", default: true + }, + notify: %Schema{ + type: :boolean, + description: + "Receive notifications for all statuses posted by the account? Defaults to false.", + default: false } } }, diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index 71479550e..fb713d47c 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -24,6 +24,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do with {:ok, follower, _followed, _} <- result do options = cast_params(params) set_reblogs_visibility(options[:reblogs], result) + set_subscription(options[:notify], result) {:ok, follower} end end @@ -36,6 +37,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do CommonAPI.show_reblogs(follower, followed) end + defp set_subscription(true, {:ok, follower, followed, _}) do + User.subscribe(follower, followed) + end + + defp set_subscription(_, {:ok, follower, followed, _}) do + User.unsubscribe(follower, followed) + end + @spec get_followers(User.t(), map()) :: list(User.t()) def get_followers(user, params \\ %{}) do user @@ -73,7 +82,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do exclude_visibilities: {:array, :string}, reblogs: :boolean, with_muted: :boolean, - account_ap_id: :string + account_ap_id: :string, + notify: :boolean } changeset = cast({%{}, param_types}, params, Map.keys(param_types)) From 3892bd353b68aff51fd596239de43fb320616eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 6 Dec 2021 21:13:14 +0100 Subject: [PATCH 11/22] Add test for following with subscription MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- .../controllers/account_controller_test.exs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) 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 a92a58224..70c6b152e 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -922,6 +922,27 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> json_response_and_validate_schema(200) end + test "following with subscription and unsubscribing when notify is nil" do + %{conn: conn} = oauth_access(["follow"]) + followed = insert(:user) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: true}) + + assert %{"id" => _id, "subscribing" => true} = + json_response_and_validate_schema(ret_conn, 200) + + ret_conn = + conn + |> put_req_header("content-type", "application/json") + |> post("/api/v1/accounts/#{followed.id}/follow") + + assert %{"id" => _id, "subscribing" => false} = + json_response_and_validate_schema(ret_conn, 200) + end + test "following / unfollowing errors", %{user: user, conn: conn} do # self follow conn_res = post(conn, "/api/v1/accounts/#{user.id}/follow") From c96e52b88c47371de1cc4ed70786baf20008a811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Mon, 6 Dec 2021 21:23:34 +0100 Subject: [PATCH 12/22] Add 'notifying' to relationship for compatibility with Mastodon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- docs/development/API/pleroma_api.md | 2 ++ .../api_spec/operations/account_operation.ex | 3 +++ lib/pleroma/web/api_spec/schemas/account.ex | 3 ++- .../api_spec/schemas/account_relationship.ex | 6 ++++-- lib/pleroma/web/api_spec/schemas/status.ex | 3 ++- lib/pleroma/web/mastodon_api/mastodon_api.ex | 4 +++- .../web/mastodon_api/views/account_view.ex | 19 +++++++++++-------- .../controllers/account_controller_test.exs | 4 ++-- .../mastodon_api/views/account_view_test.exs | 2 ++ 9 files changed, 31 insertions(+), 15 deletions(-) diff --git a/docs/development/API/pleroma_api.md b/docs/development/API/pleroma_api.md index 8f6422da0..74a1ad206 100644 --- a/docs/development/API/pleroma_api.md +++ b/docs/development/API/pleroma_api.md @@ -159,6 +159,7 @@ See [Admin-API](admin_api.md) "muting": false, "muting_notifications": false, "subscribing": true, + "notifying": true, "requested": false, "domain_blocking": false, "showing_reblogs": true, @@ -183,6 +184,7 @@ See [Admin-API](admin_api.md) "muting": false, "muting_notifications": false, "subscribing": false, + "notifying": false, "requested": false, "domain_blocking": false, "showing_reblogs": true, diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex index cb978c775..4fe5a3c03 100644 --- a/lib/pleroma/web/api_spec/operations/account_operation.ex +++ b/lib/pleroma/web/api_spec/operations/account_operation.ex @@ -694,6 +694,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "requested" => false, "domain_blocking" => false, "subscribing" => false, + "notifying" => false, "endorsed" => true }, %{ @@ -708,6 +709,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "requested" => true, "domain_blocking" => false, "subscribing" => false, + "notifying" => false, "endorsed" => false }, %{ @@ -722,6 +724,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do "requested" => false, "domain_blocking" => true, "subscribing" => true, + "notifying" => true, "endorsed" => false } ] diff --git a/lib/pleroma/web/api_spec/schemas/account.ex b/lib/pleroma/web/api_spec/schemas/account.ex index bd7143ab9..ad1a85544 100644 --- a/lib/pleroma/web/api_spec/schemas/account.ex +++ b/lib/pleroma/web/api_spec/schemas/account.ex @@ -196,7 +196,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do "muting_notifications" => false, "requested" => false, "showing_reblogs" => true, - "subscribing" => false + "subscribing" => false, + "notifying" => false }, "settings_store" => %{ "pleroma-fe" => %{} diff --git a/lib/pleroma/web/api_spec/schemas/account_relationship.ex b/lib/pleroma/web/api_spec/schemas/account_relationship.ex index 16b73ebb4..b4f6d25b0 100644 --- a/lib/pleroma/web/api_spec/schemas/account_relationship.ex +++ b/lib/pleroma/web/api_spec/schemas/account_relationship.ex @@ -24,7 +24,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do muting_notifications: %Schema{type: :boolean}, requested: %Schema{type: :boolean}, showing_reblogs: %Schema{type: :boolean}, - subscribing: %Schema{type: :boolean} + subscribing: %Schema{type: :boolean}, + notifying: %Schema{type: :boolean} }, example: %{ "blocked_by" => false, @@ -38,7 +39,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do "muting_notifications" => false, "requested" => false, "showing_reblogs" => true, - "subscribing" => false + "subscribing" => false, + "notifying" => false } }) end diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex index 3d042dc19..0bf3312d1 100644 --- a/lib/pleroma/web/api_spec/schemas/status.ex +++ b/lib/pleroma/web/api_spec/schemas/status.ex @@ -284,7 +284,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do "muting_notifications" => false, "requested" => false, "showing_reblogs" => true, - "subscribing" => false + "subscribing" => false, + "notifying" => false }, "skip_thread_containment" => false, "tags" => [] diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index fb713d47c..23846b36a 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -41,10 +41,12 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do User.subscribe(follower, followed) end - defp set_subscription(_, {:ok, follower, followed, _}) do + defp set_subscription(false, {:ok, follower, followed, _}) do User.unsubscribe(follower, followed) end + defp set_subscription(_, _), do: {:ok, nil} + @spec get_followers(User.t(), map()) :: list(User.t()) def get_followers(user, params \\ %{}) do user diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 9e9de33f6..25752cf56 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -101,6 +101,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do User.following?(target, reading_user) end + subscribing = + UserRelationship.exists?( + user_relationships, + :inverse_subscription, + target, + reading_user, + &User.subscribed_to?(&2, &1) + ) + # NOTE: adjust UserRelationship.view_relationships_option/2 on new relation-related flags %{ id: to_string(target.id), @@ -138,14 +147,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do target, &User.muted_notifications?(&1, &2) ), - subscribing: - UserRelationship.exists?( - user_relationships, - :inverse_subscription, - target, - reading_user, - &User.subscribed_to?(&2, &1) - ), + subscribing: subscribing, + notifying: subscribing, requested: follow_state == :follow_pending, domain_blocking: User.blocks_domain?(reading_user, target), showing_reblogs: 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 70c6b152e..581944b8a 100644 --- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs @@ -922,7 +922,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do |> json_response_and_validate_schema(200) end - test "following with subscription and unsubscribing when notify is nil" do + test "following with subscription and unsubscribing" do %{conn: conn} = oauth_access(["follow"]) followed = insert(:user) @@ -937,7 +937,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do ret_conn = conn |> put_req_header("content-type", "application/json") - |> post("/api/v1/accounts/#{followed.id}/follow") + |> post("/api/v1/accounts/#{followed.id}/follow", %{notify: false}) assert %{"id" => _id, "subscribing" => false} = json_response_and_validate_schema(ret_conn, 200) diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs index 60881756d..aeddd6b4c 100644 --- a/test/pleroma/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -268,6 +268,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do muting: false, muting_notifications: false, subscribing: false, + notifying: false, requested: false, domain_blocking: false, showing_reblogs: true, @@ -293,6 +294,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do muting: true, muting_notifications: true, subscribing: true, + notifying: true, showing_reblogs: false, id: to_string(other_user.id) } From 3d41ccc47bd59cb17e7c18a368e3da3fd885ff29 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 17 Dec 2021 14:17:51 -0500 Subject: [PATCH 13/22] Allow updating accepted follow activities in Web.ActivityPub.Utils.update_follow_state_for_all/2 Mastodon uses the Reject activity also for the purpose of removing a follower, in addition to reject a follow request. We should also update the original Follow activity in this case. --- lib/pleroma/web/activity_pub/utils.ex | 2 +- test/pleroma/web/activity_pub/utils_test.exs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 1df53f79a..c1f6b2b49 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -446,7 +446,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do |> Activity.Queries.by_type() |> Activity.Queries.by_actor(actor) |> Activity.Queries.by_object_id(object) - |> where(fragment("data->>'state' = 'pending'")) + |> where(fragment("data->>'state' = 'pending'") or fragment("data->>'state' = 'accept'")) |> update(set: [data: fragment("jsonb_set(data, '{state}', ?)", ^state)]) |> Repo.update_all([]) diff --git a/test/pleroma/web/activity_pub/utils_test.exs b/test/pleroma/web/activity_pub/utils_test.exs index ee3e1014e..62dc02f61 100644 --- a/test/pleroma/web/activity_pub/utils_test.exs +++ b/test/pleroma/web/activity_pub/utils_test.exs @@ -213,6 +213,20 @@ defmodule Pleroma.Web.ActivityPub.UtilsTest do assert refresh_record(follow_activity).data["state"] == "accept" assert refresh_record(follow_activity_two).data["state"] == "accept" end + + test "also updates the state of accepted follows" do + user = insert(:user) + follower = insert(:user) + + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, user) + {:ok, _, _, follow_activity_two} = CommonAPI.follow(follower, user) + + {:ok, follow_activity_two} = + Utils.update_follow_state_for_all(follow_activity_two, "reject") + + assert refresh_record(follow_activity).data["state"] == "reject" + assert refresh_record(follow_activity_two).data["state"] == "reject" + end end describe "update_follow_state/2" do From bfd870380c6dca1c3d460991181438a02c4915f9 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 17 Dec 2021 14:42:45 -0500 Subject: [PATCH 14/22] Add test to ensure the blocked cease to have follow relationship to the blocker https://git.pleroma.social/pleroma/pleroma/-/issues/2766 --- test/pleroma/web/activity_pub/side_effects_test.exs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs index d0988619d..5ca68ccc8 100644 --- a/test/pleroma/web/activity_pub/side_effects_test.exs +++ b/test/pleroma/web/activity_pub/side_effects_test.exs @@ -88,6 +88,16 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do assert User.blocks?(user, blocked) end + test "it updates following relationship", %{user: user, blocked: blocked, block: block} do + {:ok, _, _} = SideEffects.handle(block) + + refute Pleroma.FollowingRelationship.get(user, blocked) + assert User.get_follow_state(user, blocked) == nil + assert User.get_follow_state(blocked, user) == nil + assert User.get_follow_state(user, blocked, nil) == nil + assert User.get_follow_state(blocked, user, nil) == nil + end + test "it blocks but does not unfollow if the relevant setting is set", %{ user: user, blocked: blocked, From 951d1592c7958c2225a868360455693dd36def96 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 17 Dec 2021 16:44:22 -0500 Subject: [PATCH 15/22] Add test to ensure removed follower cease to have relationship with ex-followee https://git.pleroma.social/pleroma/pleroma/-/issues/2802 --- .../web/activity_pub/side_effects_test.exs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs index 5ca68ccc8..30dd63d4d 100644 --- a/test/pleroma/web/activity_pub/side_effects_test.exs +++ b/test/pleroma/web/activity_pub/side_effects_test.exs @@ -552,4 +552,71 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do end end end + + describe "removing a follower" do + setup do + user = insert(:user) + followed = insert(:user) + + {:ok, _, _, follow_activity} = CommonAPI.follow(user, followed) + + {:ok, reject_data, []} = Builder.reject(followed, follow_activity) + {:ok, reject, _meta} = ActivityPub.persist(reject_data, local: true) + + %{user: user, followed: followed, reject: reject} + end + + test "", %{user: user, followed: followed, reject: reject} do + assert User.following?(user, followed) + assert Pleroma.FollowingRelationship.get(user, followed) + + {:ok, _, _} = SideEffects.handle(reject) + + refute User.following?(user, followed) + refute Pleroma.FollowingRelationship.get(user, followed) + assert User.get_follow_state(user, followed) == nil + assert User.get_follow_state(user, followed, nil) == nil + end + end + + describe "removing a follower from remote" do + setup do + user = insert(:user) + followed = insert(:user, local: false) + + # Mock a local-to-remote follow + {:ok, follow_data, []} = Builder.follow(user, followed) + follow_data = + follow_data + |> Map.put("state", "accept") + {:ok, follow, _meta} = ActivityPub.persist(follow_data, local: true) + {:ok, _, _} = SideEffects.handle(follow) + + # Mock a remote-to-local accept + {:ok, accept_data, _} = Builder.accept(followed, follow) + {:ok, accept, _} = ActivityPub.persist(accept_data, local: false) + {:ok, _, _} = SideEffects.handle(accept) + + # Mock a remote-to-local reject + {:ok, reject_data, []} = Builder.reject(followed, follow) + {:ok, reject, _meta} = ActivityPub.persist(reject_data, local: false) + + %{user: user, followed: followed, reject: reject} + end + + test "", %{user: user, followed: followed, reject: reject} do + assert User.following?(user, followed) + assert Pleroma.FollowingRelationship.get(user, followed) + + {:ok, _, _} = SideEffects.handle(reject) + + refute User.following?(user, followed) + refute Pleroma.FollowingRelationship.get(user, followed) + + assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] == "reject" + + assert User.get_follow_state(user, followed) == nil + assert User.get_follow_state(user, followed, nil) == nil + end + end end From 538d5ac2100aac57814fbc11bb205be7bb205b96 Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 17 Dec 2021 16:47:48 -0500 Subject: [PATCH 16/22] Add changelog for https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3568 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecefba381..a3034c53b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies +- Handle Reject for already-accepted Follows properly ### Removed From 8376e83f61b8dbe61134e814e093e8fe7288440f Mon Sep 17 00:00:00 2001 From: Tusooa Zhu Date: Fri, 17 Dec 2021 16:52:50 -0500 Subject: [PATCH 17/22] Lint --- test/pleroma/web/activity_pub/side_effects_test.exs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/pleroma/web/activity_pub/side_effects_test.exs b/test/pleroma/web/activity_pub/side_effects_test.exs index 30dd63d4d..c6155ed18 100644 --- a/test/pleroma/web/activity_pub/side_effects_test.exs +++ b/test/pleroma/web/activity_pub/side_effects_test.exs @@ -586,9 +586,11 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do # Mock a local-to-remote follow {:ok, follow_data, []} = Builder.follow(user, followed) + follow_data = follow_data |> Map.put("state", "accept") + {:ok, follow, _meta} = ActivityPub.persist(follow_data, local: true) {:ok, _, _} = SideEffects.handle(follow) @@ -613,7 +615,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do refute User.following?(user, followed) refute Pleroma.FollowingRelationship.get(user, followed) - assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] == "reject" + assert Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, followed).data["state"] == + "reject" assert User.get_follow_state(user, followed) == nil assert User.get_follow_state(user, followed, nil) == nil From 87871ac85722b0cec067decf698bf02fbf36dc93 Mon Sep 17 00:00:00 2001 From: Hakaba Hitoyo Date: Thu, 23 Dec 2021 16:01:02 +0000 Subject: [PATCH 18/22] Add initial Nodeinfo document --- docs/development/API/nodeinfo.md | 347 +++++++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 docs/development/API/nodeinfo.md diff --git a/docs/development/API/nodeinfo.md b/docs/development/API/nodeinfo.md new file mode 100644 index 000000000..0f998a1e6 --- /dev/null +++ b/docs/development/API/nodeinfo.md @@ -0,0 +1,347 @@ +# Nodeinfo + +See also [the Nodeinfo standard](https://nodeinfo.diaspora.software/). + +## `/.well-known/nodeinfo` +### The well-known path +* Method: `GET` +* Authentication: not required +* Params: none +* Response: JSON +* Example response: +```json +{ + "links":[ + { + "href":"https://example.com/nodeinfo/2.0.json", + "rel":"http://nodeinfo.diaspora.software/ns/schema/2.0" + }, + { + "href":"https://example.com/nodeinfo/2.1.json", + "rel":"http://nodeinfo.diaspora.software/ns/schema/2.1" + } + ] +} +``` + +## `/nodeinfo/2.0.json` +### Nodeinfo 2.0 +* Method: `GET` +* Authentication: not required +* Params: none +* Response: JSON +* Example response: +```json +{ + "metadata":{ + "accountActivationRequired":false, + "features":[ + "pleroma_api", + "mastodon_api", + "mastodon_api_streaming", + "polls", + "pleroma_explicit_addressing", + "shareable_emoji_packs", + "multifetch", + "pleroma:api/v1/notifications:include_types_filter", + "chat", + "shout", + "relay", + "pleroma_emoji_reactions", + "pleroma_chat_messages" + ], + "federation":{ + "enabled":true, + "exclusions":false, + "mrf_hashtag":{ + "federated_timeline_removal":[ + + ], + "reject":[ + + ], + "sensitive":[ + "nsfw" + ] + }, + "mrf_object_age":{ + "actions":[ + "delist", + "strip_followers" + ], + "threshold":604800 + }, + "mrf_policies":[ + "ObjectAgePolicy", + "TagPolicy", + "HashtagPolicy" + ], + "quarantined_instances":[ + + ] + }, + "fieldsLimits":{ + "maxFields":10, + "maxRemoteFields":20, + "nameLength":512, + "valueLength":2048 + }, + "invitesEnabled":false, + "mailerEnabled":false, + "nodeDescription":"Pleroma: An efficient and flexible fediverse server", + "nodeName":"Example", + "pollLimits":{ + "max_expiration":31536000, + "max_option_chars":200, + "max_options":20, + "min_expiration":0 + }, + "postFormats":[ + "text/plain", + "text/html", + "text/markdown", + "text/bbcode" + ], + "private":false, + "restrictedNicknames":[ + ".well-known", + "~", + "about", + "activities", + "api", + "auth", + "check_password", + "dev", + "friend-requests", + "inbox", + "internal", + "main", + "media", + "nodeinfo", + "notice", + "oauth", + "objects", + "ostatus_subscribe", + "pleroma", + "proxy", + "push", + "registration", + "relay", + "settings", + "status", + "tag", + "user-search", + "user_exists", + "users", + "web", + "verify_credentials", + "update_credentials", + "relationships", + "search", + "confirmation_resend", + "mfa" + ], + "skipThreadContainment":true, + "staffAccounts":[ + "https://example.com/users/admin", + "https://example.com/users/staff" + ], + "suggestions":{ + "enabled":false + }, + "uploadLimits":{ + "avatar":2000000, + "background":4000000, + "banner":4000000, + "general":16000000 + } + }, + "openRegistrations":true, + "protocols":[ + "activitypub" + ], + "services":{ + "inbound":[ + + ], + "outbound":[ + + ] + }, + "software":{ + "name":"pleroma", + "version":"2.4.1" + }, + "usage":{ + "localPosts":27, + "users":{ + "activeHalfyear":129, + "activeMonth":70, + "total":235 + } + }, + "version":"2.0" +} +``` + +## `/nodeinfo/2.1.json` +### Nodeinfo 2.1 +* Method: `GET` +* Authentication: not required +* Params: none +* Response: JSON +* Example response: +```json +{ + "metadata":{ + "accountActivationRequired":false, + "features":[ + "pleroma_api", + "mastodon_api", + "mastodon_api_streaming", + "polls", + "pleroma_explicit_addressing", + "shareable_emoji_packs", + "multifetch", + "pleroma:api/v1/notifications:include_types_filter", + "chat", + "shout", + "relay", + "pleroma_emoji_reactions", + "pleroma_chat_messages" + ], + "federation":{ + "enabled":true, + "exclusions":false, + "mrf_hashtag":{ + "federated_timeline_removal":[ + + ], + "reject":[ + + ], + "sensitive":[ + "nsfw" + ] + }, + "mrf_object_age":{ + "actions":[ + "delist", + "strip_followers" + ], + "threshold":604800 + }, + "mrf_policies":[ + "ObjectAgePolicy", + "TagPolicy", + "HashtagPolicy" + ], + "quarantined_instances":[ + + ] + }, + "fieldsLimits":{ + "maxFields":10, + "maxRemoteFields":20, + "nameLength":512, + "valueLength":2048 + }, + "invitesEnabled":false, + "mailerEnabled":false, + "nodeDescription":"Pleroma: An efficient and flexible fediverse server", + "nodeName":"Example", + "pollLimits":{ + "max_expiration":31536000, + "max_option_chars":200, + "max_options":20, + "min_expiration":0 + }, + "postFormats":[ + "text/plain", + "text/html", + "text/markdown", + "text/bbcode" + ], + "private":false, + "restrictedNicknames":[ + ".well-known", + "~", + "about", + "activities", + "api", + "auth", + "check_password", + "dev", + "friend-requests", + "inbox", + "internal", + "main", + "media", + "nodeinfo", + "notice", + "oauth", + "objects", + "ostatus_subscribe", + "pleroma", + "proxy", + "push", + "registration", + "relay", + "settings", + "status", + "tag", + "user-search", + "user_exists", + "users", + "web", + "verify_credentials", + "update_credentials", + "relationships", + "search", + "confirmation_resend", + "mfa" + ], + "skipThreadContainment":true, + "staffAccounts":[ + "https://example.com/users/admin", + "https://example.com/users/staff" + ], + "suggestions":{ + "enabled":false + }, + "uploadLimits":{ + "avatar":2000000, + "background":4000000, + "banner":4000000, + "general":16000000 + } + }, + "openRegistrations":true, + "protocols":[ + "activitypub" + ], + "services":{ + "inbound":[ + + ], + "outbound":[ + + ] + }, + "software":{ + "name":"pleroma", + "repository":"https://git.pleroma.social/pleroma/pleroma", + "version":"2.4.1" + }, + "usage":{ + "localPosts":27, + "users":{ + "activeHalfyear":129, + "activeMonth":70, + "total":235 + } + }, + "version":"2.1" +} +``` + From 2c06eff519f63e67aada70d492094e6e56bbfccd Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 25 Dec 2021 20:11:14 -0600 Subject: [PATCH 19/22] Pleroma.Web.base_url() --> Endpoint.url() --- test/pleroma/web/o_status/o_status_controller_test.exs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/pleroma/web/o_status/o_status_controller_test.exs b/test/pleroma/web/o_status/o_status_controller_test.exs index b243e1692..41aef98b1 100644 --- a/test/pleroma/web/o_status/o_status_controller_test.exs +++ b/test/pleroma/web/o_status/o_status_controller_test.exs @@ -356,7 +356,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do |> response(200) expected = - "" + "" assert resp =~ expected end @@ -372,7 +372,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do |> response(200) expected = - "" + "" assert resp =~ expected end @@ -388,7 +388,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do |> response(200) expected = - "" + "" assert resp =~ expected end From cac4ed5eb0b860acb077baefcc2e3b3447c89751 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 25 Dec 2021 20:15:21 -0600 Subject: [PATCH 20/22] GitLab CI: don't retry failed jobs --- .gitlab-ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 844f5888e..f296f7bd9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -79,7 +79,6 @@ unit-testing: - "**/*.ex" - "**/*.exs" - "mix.lock" - retry: 2 cache: &testing_cache_policy <<: *global_cache_policy policy: pull @@ -117,7 +116,6 @@ unit-testing-rum: - "**/*.ex" - "**/*.exs" - "mix.lock" - retry: 2 cache: *testing_cache_policy services: - name: minibikini/postgres-with-rum:12 From de006443f0bc8cfb3ad28b29b2d8ea9581e760b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Sun, 26 Dec 2021 02:35:17 +0000 Subject: [PATCH 21/22] MastoAPI: Profile directory --- config/config.exs | 3 +- config/description.exs | 5 ++ .../API/differences_in_mastoapi_responses.md | 6 -- lib/pleroma/user.ex | 13 +++ lib/pleroma/user/query.ex | 5 ++ lib/pleroma/web/activity_pub/activity_pub.ex | 5 ++ lib/pleroma/web/activity_pub/side_effects.ex | 1 + .../operations/directory_operation.ex | 41 ++++++++++ .../controllers/directory_controller.ex | 82 +++++++++++++++++++ .../web/mastodon_api/views/account_view.ex | 1 + .../web/mastodon_api/views/instance_view.ex | 3 + lib/pleroma/web/router.ex | 2 + ...1222165256_add_last_status_at_to_users.exs | 11 +++ ...802_add_is_discoverable_index_to_users.exs | 7 ++ .../controllers/directory_controller_test.exs | 46 +++++++++++ .../mastodon_api/views/account_view_test.exs | 2 + 16 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 lib/pleroma/web/api_spec/operations/directory_operation.ex create mode 100644 lib/pleroma/web/mastodon_api/controllers/directory_controller.ex create mode 100644 priv/repo/migrations/20211222165256_add_last_status_at_to_users.exs create mode 100644 priv/repo/migrations/20211225154802_add_is_discoverable_index_to_users.exs create mode 100644 test/pleroma/web/mastodon_api/controllers/directory_controller_test.exs diff --git a/config/config.exs b/config/config.exs index c9592511f..23c41eddd 100644 --- a/config/config.exs +++ b/config/config.exs @@ -254,7 +254,8 @@ config :pleroma, :instance, ] ], show_reactions: true, - password_reset_token_validity: 60 * 60 * 24 + password_reset_token_validity: 60 * 60 * 24, + profile_directory: true config :pleroma, :welcome, direct_message: [ diff --git a/config/description.exs b/config/description.exs index 1c8c3b4a0..517077acf 100644 --- a/config/description.exs +++ b/config/description.exs @@ -936,6 +936,11 @@ config :pleroma, :config_description, [ key: :show_reactions, type: :boolean, description: "Let favourites and emoji reactions be viewed through the API." + }, + %{ + key: :profile_directory, + type: :boolean, + description: "Enable profile directory." } ] }, diff --git a/docs/development/API/differences_in_mastoapi_responses.md b/docs/development/API/differences_in_mastoapi_responses.md index 6c1ecb559..518aca114 100644 --- a/docs/development/API/differences_in_mastoapi_responses.md +++ b/docs/development/API/differences_in_mastoapi_responses.md @@ -383,12 +383,6 @@ Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer feat - `GET /api/v1/endorsements`: Returns an empty array, `[]` -### Profile directory - -*Added in Mastodon 3.0.0* - -- `GET /api/v1/directory`: Returns HTTP 404 - ### Featured tags *Added in Mastodon 3.0.0* diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index c25023dc1..390de1e2d 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -149,6 +149,7 @@ defmodule Pleroma.User do field(:disclose_client, :boolean, default: true) field(:pinned_objects, :map, default: %{}) field(:is_suggested, :boolean, default: false) + field(:last_status_at, :naive_datetime) embeds_one( :notification_settings, @@ -2499,4 +2500,16 @@ defmodule Pleroma.User do |> where([u], u.local == true) |> Repo.aggregate(:count) end + + def update_last_status_at(user) do + User + |> where(id: ^user.id) + |> update([u], set: [last_status_at: fragment("NOW()")]) + |> select([u], u) + |> Repo.update_all([]) + |> case do + {1, [user]} -> set_cache(user) + _ -> {:error, user} + end + end end diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index 6d4a4ead6..bf78cb32d 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -47,6 +47,7 @@ defmodule Pleroma.User.Query do is_admin: boolean(), is_moderator: boolean(), is_suggested: boolean(), + is_discoverable: boolean(), super_users: boolean(), invisible: boolean(), internal: boolean(), @@ -172,6 +173,10 @@ defmodule Pleroma.User.Query do where(query, [u], u.is_suggested == ^bool) end + defp compose_query({:is_discoverable, bool}, query) do + where(query, [u], u.is_discoverable == ^bool) + end + defp compose_query({:followers, %User{id: id}}, query) do query |> where([u], u.id != ^id) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 8324ca22c..756096952 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -81,6 +81,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor} end + def update_last_status_at_if_public(actor, object) do + if is_public?(object), do: User.update_last_status_at(actor), else: {:ok, actor} + end + defp increase_replies_count_if_reply(%{ "object" => %{"inReplyTo" => reply_ap_id} = object, "type" => "Create" @@ -288,6 +292,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do _ <- increase_replies_count_if_reply(create_data), {:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity}, {:ok, _actor} <- increase_note_count_if_public(actor, activity), + {:ok, _actor} <- update_last_status_at_if_public(actor, activity), _ <- notify_and_stream(activity), :ok <- maybe_schedule_poll_notifications(activity), :ok <- maybe_federate(activity) do diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index d55a4b340..39d37fbcb 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -199,6 +199,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do {:ok, notifications} = Notification.create_notifications(activity, do_send: false) {:ok, _user} = ActivityPub.increase_note_count_if_public(user, object) + {:ok, _user} = ActivityPub.update_last_status_at_if_public(user, object) if in_reply_to = object.data["type"] != "Answer" && object.data["inReplyTo"] do Object.increase_replies_count(in_reply_to) diff --git a/lib/pleroma/web/api_spec/operations/directory_operation.ex b/lib/pleroma/web/api_spec/operations/directory_operation.ex new file mode 100644 index 000000000..9be965feb --- /dev/null +++ b/lib/pleroma/web/api_spec/operations/directory_operation.ex @@ -0,0 +1,41 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ApiSpec.DirectoryOperation do + alias OpenApiSpex.Operation + alias Pleroma.Web.ApiSpec.AccountOperation + alias Pleroma.Web.ApiSpec.Schemas.ApiError + alias Pleroma.Web.ApiSpec.Schemas.BooleanLike + + import Pleroma.Web.ApiSpec.Helpers + + def open_api_operation(action) do + operation = String.to_existing_atom("#{action}_operation") + apply(__MODULE__, operation, []) + end + + def index_operation do + %Operation{ + tags: ["Directory"], + summary: "Profile directory", + operationId: "DirectoryController.index", + parameters: + [ + Operation.parameter( + :order, + :query, + :string, + "Order by recent activity or account creation", + required: nil + ), + Operation.parameter(:local, :query, BooleanLike, "Include local users only") + ] ++ pagination_params(), + responses: %{ + 200 => + Operation.response("Accounts", "application/json", AccountOperation.array_of_accounts()), + 404 => Operation.response("Not Found", "application/json", ApiError) + } + } + end +end diff --git a/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex b/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex new file mode 100644 index 000000000..45ef227fb --- /dev/null +++ b/lib/pleroma/web/mastodon_api/controllers/directory_controller.ex @@ -0,0 +1,82 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.MastodonAPI.DirectoryController do + use Pleroma.Web, :controller + + import Ecto.Query + alias Pleroma.Pagination + alias Pleroma.User + alias Pleroma.UserRelationship + alias Pleroma.Web.MastodonAPI.AccountView + + require Logger + + plug(Pleroma.Web.ApiSpec.CastAndValidate) + + plug(:skip_auth when action == "index") + + defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.DirectoryOperation + + @doc "GET /api/v1/directory" + def index(%{assigns: %{user: user}} = conn, params) do + with true <- Pleroma.Config.get([:instance, :profile_directory]) do + limit = Map.get(params, :limit, 20) |> min(80) + + users = + User.Query.build(%{is_discoverable: true, invisible: false, limit: limit}) + |> order_by_creation_date(params) + |> exclude_remote(params) + |> exclude_user(user) + |> exclude_relationships(user, [:block, :mute]) + |> Pagination.fetch_paginated(params, :offset) + + conn + |> put_view(AccountView) + |> render("index.json", for: user, users: users, as: :user) + else + _ -> json(conn, []) + end + end + + defp order_by_creation_date(query, %{order: "new"}) do + query + end + + defp order_by_creation_date(query, _params) do + query + |> order_by([u], desc_nulls_last: u.last_status_at) + end + + defp exclude_remote(query, %{local: true}) do + where(query, [u], u.local == true) + end + + defp exclude_remote(query, _params) do + query + end + + defp exclude_user(query, %User{id: user_id}) do + where(query, [u], u.id != ^user_id) + end + + defp exclude_user(query, _user) do + query + end + + defp exclude_relationships(query, %User{id: user_id}, relationship_types) do + query + |> join(:left, [u], r in UserRelationship, + as: :user_relationships, + on: + r.target_id == u.id and r.source_id == ^user_id and + r.relationship_type in ^relationship_types + ) + |> where([user_relationships: r], is_nil(r.target_id)) + end + + defp exclude_relationships(query, _user, _relationship_types) do + query + end +end diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 3c8dd0353..4b15b1635 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -270,6 +270,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do actor_type: user.actor_type } }, + last_status_at: user.last_status_at, # Pleroma extensions # Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index ec7d150a9..7072d5d61 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -87,6 +87,9 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do "pleroma_chat_messages", if Config.get([:instance, :show_reactions]) do "exposable_reactions" + end, + if Config.get([:instance, :profile_directory]) do + "profile_directory" end ] |> Enum.filter(& &1) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 9ce35ad6b..e3659b87a 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -600,6 +600,8 @@ defmodule Pleroma.Web.Router do get("/timelines/tag/:tag", TimelineController, :hashtag) get("/polls/:id", PollController, :show) + + get("/directory", DirectoryController, :index) end scope "/api/v2", Pleroma.Web.MastodonAPI do diff --git a/priv/repo/migrations/20211222165256_add_last_status_at_to_users.exs b/priv/repo/migrations/20211222165256_add_last_status_at_to_users.exs new file mode 100644 index 000000000..906178216 --- /dev/null +++ b/priv/repo/migrations/20211222165256_add_last_status_at_to_users.exs @@ -0,0 +1,11 @@ +defmodule Pleroma.Repo.Migrations.AddLastStatusAtToUsers do + use Ecto.Migration + + def change do + alter table(:users) do + add(:last_status_at, :naive_datetime) + end + + create_if_not_exists(index(:users, [:last_status_at])) + end +end diff --git a/priv/repo/migrations/20211225154802_add_is_discoverable_index_to_users.exs b/priv/repo/migrations/20211225154802_add_is_discoverable_index_to_users.exs new file mode 100644 index 000000000..9f8f52b65 --- /dev/null +++ b/priv/repo/migrations/20211225154802_add_is_discoverable_index_to_users.exs @@ -0,0 +1,7 @@ +defmodule Pleroma.Repo.Migrations.AddIsDiscoverableIndexToUsers do + use Ecto.Migration + + def change do + create(index(:users, [:is_discoverable])) + end +end diff --git a/test/pleroma/web/mastodon_api/controllers/directory_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/directory_controller_test.exs new file mode 100644 index 000000000..b8f55f832 --- /dev/null +++ b/test/pleroma/web/mastodon_api/controllers/directory_controller_test.exs @@ -0,0 +1,46 @@ +defmodule Pleroma.Web.MastodonAPI.DirectoryControllerTest do + use Pleroma.Web.ConnCase, async: true + alias Pleroma.Web.CommonAPI + import Pleroma.Factory + + test "GET /api/v1/directory with :profile_directory disabled returns empty array", %{conn: conn} do + clear_config([:instance, :profile_directory], false) + + insert(:user, is_discoverable: true) + insert(:user, is_discoverable: true) + + result = + conn + |> get("/api/v1/directory") + |> json_response_and_validate_schema(200) + + assert result == [] + end + + test "GET /api/v1/directory returns discoverable users only", %{conn: conn} do + %{id: user_id} = insert(:user, is_discoverable: true) + insert(:user, is_discoverable: false) + + result = + conn + |> get("/api/v1/directory") + |> json_response_and_validate_schema(200) + + assert [%{"id" => ^user_id}] = result + end + + test "GET /api/v1/directory returns users sorted by most recent statuses", %{conn: conn} do + insert(:user, is_discoverable: true) + %{id: user_id} = user = insert(:user, is_discoverable: true) + insert(:user, is_discoverable: true) + + {:ok, _activity} = CommonAPI.post(user, %{status: "yay i'm discoverable"}) + + result = + conn + |> get("/api/v1/directory?order=active") + |> json_response_and_validate_schema(200) + + assert [%{"id" => ^user_id} | _tail] = result + end +end diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs index 39b9b0cef..c23ffb966 100644 --- a/test/pleroma/web/mastodon_api/views/account_view_test.exs +++ b/test/pleroma/web/mastodon_api/views/account_view_test.exs @@ -74,6 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do fields: [] }, fqn: "shp@shitposter.club", + last_status_at: nil, pleroma: %{ ap_id: user.ap_id, also_known_as: ["https://shitposter.zone/users/shp"], @@ -175,6 +176,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do fields: [] }, fqn: "shp@shitposter.club", + last_status_at: nil, pleroma: %{ ap_id: user.ap_id, also_known_as: [], From e8e8d2262ec55acabb7f7749f915e24d06df601a Mon Sep 17 00:00:00 2001 From: Lain Soykaf Date: Sun, 26 Dec 2021 16:14:56 +0100 Subject: [PATCH 22/22] CI: Start testing erratic test again Erratic tests are now ran in their own task, so we don't block normal testing. The runtime is under a minute, so even if this one has to be retried, it shouldn't take forever. --- .gitlab-ci.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f296f7bd9..3860f1db9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -93,6 +93,27 @@ unit-testing: - mix ecto.migrate - mix coveralls --preload-modules +unit-testing-erratic: + stage: test + retry: 2 + only: + changes: + - "**/*.ex" + - "**/*.exs" + - "mix.lock" + cache: &testing_cache_policy + <<: *global_cache_policy + policy: pull + + services: + - name: postgres:13 + alias: postgres + command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] + script: + - mix ecto.create + - mix ecto.migrate + - mix test --only=erratic + # Removed to fix CI issue. In this early state it wasn't adding much value anyway. # TODO Fix and reinstate federated testing # federated-testing: