summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormarcin mikołajczak <git@mkljczk.pl>2022-01-10 21:35:55 +0100
committermarcin mikołajczak <git@mkljczk.pl>2022-01-12 18:15:10 +0100
commit0f90fd58052aa372aaad63d769cd724046c9f61f (patch)
tree9ead892e5f740df707e9f6172845de74e4792c35
parent4f249b23977d7426c6249875b538c7a6c1b239ff (diff)
WIP account endorsements
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
-rw-r--r--config/config.exs3
-rw-r--r--config/description.exs10
-rw-r--r--docs/development/API/differences_in_mastoapi_responses.md6
-rw-r--r--lib/pleroma/pagination.ex7
-rw-r--r--lib/pleroma/user.ex20
-rw-r--r--lib/pleroma/web/api_spec/operations/account_operation.ex14
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex15
-rw-r--r--lib/pleroma/web/common_api.ex3
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/account_controller.ex10
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/account_controller.ex17
-rw-r--r--test/pleroma/web/mastodon_api/controllers/account_controller_test.exs53
11 files changed, 107 insertions, 51 deletions
diff --git a/config/config.exs b/config/config.exs
index 2bde5b826..1385ce5de 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -258,7 +258,8 @@ config :pleroma, :instance,
show_reactions: true,
password_reset_token_validity: 60 * 60 * 24,
profile_directory: true,
- privileged_staff: false
+ privileged_staff: false,
+ max_endorsed_users: 20
config :pleroma, :welcome,
direct_message: [
diff --git a/config/description.exs b/config/description.exs
index ea3f34abe..644c60a63 100644
--- a/config/description.exs
+++ b/config/description.exs
@@ -743,6 +743,16 @@ config :pleroma, :config_description, [
]
},
%{
+ key: :max_endorsed_users,
+ type: :integer,
+ description: "The maximum number of recommended accounts. 0 will disable the feature.",
+ suggestions: [
+ 0,
+ 1,
+ 3
+ ]
+ },
+ %{
key: :autofollowed_nicknames,
type: {:list, :string},
description:
diff --git a/docs/development/API/differences_in_mastoapi_responses.md b/docs/development/API/differences_in_mastoapi_responses.md
index 518aca114..0e6bcb79b 100644
--- a/docs/development/API/differences_in_mastoapi_responses.md
+++ b/docs/development/API/differences_in_mastoapi_responses.md
@@ -377,12 +377,6 @@ Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer feat
- `GET /api/v1/identity_proofs`: Returns an empty array, `[]`
-### Endorsements
-
-*Added in Mastodon 2.5.0*
-
-- `GET /api/v1/endorsements`: Returns an empty array, `[]`
-
### Featured tags
*Added in Mastodon 3.0.0*
diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex
index 2ce243845..33e45a0eb 100644
--- a/lib/pleroma/pagination.ex
+++ b/lib/pleroma/pagination.ex
@@ -94,8 +94,7 @@ defmodule Pleroma.Pagination do
offset: :integer,
limit: :integer,
skip_extra_order: :boolean,
- skip_order: :boolean,
- shuffle: :boolean,
+ skip_order: :boolean
}
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
@@ -114,10 +113,6 @@ defmodule Pleroma.Pagination do
where(query, [{q, table_position(query, table_binding)}], q.id < ^max_id)
end
- defp restrict(query, :order, %{shuffle: true}, _) do
- order_by(query, [u], fragment("RANDOM()"))
- end
-
defp restrict(query, :order, %{skip_order: true}, _), do: query
defp restrict(%{order_bys: [_ | _]} = query, :order, %{skip_extra_order: true}, _), do: query
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index ea72af517..1b426c9d7 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -82,7 +82,7 @@ defmodule Pleroma.User do
endorsement: [
endorser_endorsements: :endorsed_users,
endorsee_endorsements: :endorser_users
- ],
+ ]
]
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
@@ -1522,10 +1522,20 @@ defmodule Pleroma.User do
end
def endorse(%User{} = endorser, %User{} = target) do
- if not following?(endorser, target) do
- {:error, "Could not endorse: You are not following #{target.nickname}"}
- else
- UserRelationship.create_endorsement(endorser, target)
+ with max_endorsed_users <- Pleroma.Config.get([:instance, :max_endorsed_users], 0),
+ endorsed_users <-
+ User.endorsed_users_relation(endorser)
+ |> Pleroma.Repo.all() do
+ cond do
+ Enum.count(endorsed_users) >= max_endorsed_users ->
+ {:error, "You have already pinned the maximum number of users"}
+
+ not following?(endorser, target) ->
+ {:error, "Could not endorse: You are not following #{target.nickname}"}
+
+ true ->
+ UserRelationship.create_endorsement(endorser, target)
+ end
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 35d8609ef..768d3c720 100644
--- a/lib/pleroma/web/api_spec/operations/account_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/account_operation.ex
@@ -343,7 +343,15 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
description: "Addds the given account to endorsed accounts list.",
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
responses: %{
- 200 => Operation.response("Relationship", "application/json", AccountRelationship)
+ 200 => Operation.response("Relationship", "application/json", AccountRelationship),
+ 400 =>
+ Operation.response("Bad Request", "application/json", %Schema{
+ allOf: [ApiError],
+ title: "Unprocessable Entity",
+ example: %{
+ "error" => "You have already pinned the maximum number of users"
+ }
+ })
}
}
end
@@ -453,10 +461,10 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
tags: ["Retrieve account information"],
summary: "Endorsements",
operationId: "AccountController.endorsements",
- description: "Not implemented",
+ description: "Returns endorsed accounts",
security: [%{"oAuth" => ["read:accounts"]}],
responses: %{
- 200 => empty_array_response()
+ 200 => Operation.response("Array of Accounts", "application/json", array_of_accounts())
}
}
end
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 9996ff68b..ed0db173e 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex
@@ -4,10 +4,10 @@
defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
alias OpenApiSpex.Operation
+ alias Pleroma.Web.ApiSpec.AccountOperation
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
alias Pleroma.Web.ApiSpec.Schemas.ApiError
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
- alias Pleroma.Web.ApiSpec.AccountOperation
alias Pleroma.Web.ApiSpec.StatusOperation
import Pleroma.Web.ApiSpec.Helpers
@@ -69,17 +69,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
summary: "Endorsements",
description: "Returns endorsed accounts",
operationId: "PleromaAPI.AccountController.endorsements",
- parameters:
- [
- Operation.parameter(
- :shuffle,
- :query,
- :boolean,
- "Show endorsed accounts in random order"
- ),
- id_param()
- ] ++ pagination_params(),
- security: [%{"oAuth" => ["read:account"]}],
+ parameters: [with_relationships_param(), id_param()],
responses: %{
200 =>
Operation.response(
@@ -87,7 +77,6 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
"application/json",
AccountOperation.array_of_accounts()
),
- 403 => Operation.response("Forbidden", "application/json", ApiError),
404 => Operation.response("Not Found", "application/json", ApiError)
}
}
diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex
index 6f685cb7b..2481e4e16 100644
--- a/lib/pleroma/web/common_api.ex
+++ b/lib/pleroma/web/common_api.ex
@@ -117,7 +117,8 @@ defmodule Pleroma.Web.CommonAPI do
def unfollow(follower, unfollowed) do
with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed),
{:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed),
- {:ok, _subscription} <- User.unsubscribe(follower, unfollowed) do
+ {:ok, _subscription} <- User.unsubscribe(follower, unfollowed),
+ {:ok, _endorsement} <- User.unendorse(follower, unfollowed) do
{:ok, follower}
end
end
diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
index 1e9ce2927..0c0548828 100644
--- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
@@ -84,7 +84,9 @@ 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 endorse unendorse endorse unendorse)a
+ @needs_account ~W(
+ followers following lists follow unfollow mute unmute block unblock note endorse unendorse
+ )a
plug(
RateLimiter,
@@ -450,16 +452,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
end
end
- @doc "POST /api/v1/accounts/:id/mute"
+ @doc "POST /api/v1/accounts/:id/pin"
def endorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
with {:ok, _user_relationships} <- User.endorse(endorser, endorsed) do
render(conn, "relationship.json", user: endorser, target: endorsed)
else
- {:error, message} -> json_response(conn, :forbidden, %{error: message})
+ {:error, message} -> json_response(conn, :bad_request, %{error: message})
end
end
- @doc "POST /api/v1/accounts/:id/unmute"
+ @doc "POST /api/v1/accounts/:id/unpin"
def unendorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
with {:ok, _user_relationships} <- User.unendorse(endorser, endorsed) do
render(conn, "relationship.json", user: endorser, target: endorsed)
diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex
index 805a1d7af..549a08f61 100644
--- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex
@@ -53,7 +53,10 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend)
- plug(:assign_account_by_id when action in [:favourites, :endorsements, :subscribe, :unsubscribe])
+ plug(
+ :assign_account_by_id
+ when action in [:favourites, :endorsements, :subscribe, :unsubscribe]
+ )
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation
@@ -106,7 +109,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
users =
user
|> User.endorsed_users_relation(_restrict_deactivated = true)
- |> fetch_paginated_endorsements(params)
+ |> Pleroma.Repo.all()
conn
|> add_link_headers(users)
@@ -118,16 +121,6 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
)
end
- defp fetch_paginated_endorsements(user, %{shuffle: true} = params) do
- user
- |> Pleroma.Pagination.fetch_paginated(Map.put(params, :shuffle, true))
- end
-
- defp fetch_paginated_endorsements(user, params) do
- user
- |> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
- 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/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs
index 374e2048a..828ebddd6 100644
--- a/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs
+++ b/test/pleroma/web/mastodon_api/controllers/account_controller_test.exs
@@ -1838,4 +1838,57 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|> get("/api/v1/accounts/relationships?id=#{other_user.id}")
|> json_response_and_validate_schema(200)
end
+
+ describe "account endorsements" do
+ setup do: oauth_access(["read:accounts", "write:accounts", "write:follows"])
+
+ setup do: clear_config([:instance, :max_endorsed_users], 1)
+
+ test "pin account", %{user: user, conn: conn} do
+ %{id: id1} = insert(:user)
+
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/accounts/#{id1}/follow")
+ |> json_response_and_validate_schema(200)
+
+ assert %{"id" => ^id1, "endorsed" => true} =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/accounts/#{id1}/pin")
+ |> json_response_and_validate_schema(200)
+
+ assert [%{"id" => ^id1}] =
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> get("/api/v1/endorsements")
+ |> json_response_and_validate_schema(200)
+ end
+
+ test "max pinned accounts", %{user: user, conn: conn} do
+ %{id: id1} = insert(:user)
+ %{id: id2} = insert(:user)
+
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/accounts/#{id1}/follow")
+ |> json_response_and_validate_schema(200)
+
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/accounts/#{id2}/follow")
+ |> json_response_and_validate_schema(200)
+
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/accounts/#{id1}/pin")
+ |> 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")
+ |> json_response_and_validate_schema(400)
+ end
+ end
end