Merge branch 'account-endorsements' into 'develop'
Account endorsements See merge request pleroma/pleroma!3601
This commit is contained in:
commit
84dcb55b0f
|
@ -258,7 +258,8 @@
|
||||||
show_reactions: true,
|
show_reactions: true,
|
||||||
password_reset_token_validity: 60 * 60 * 24,
|
password_reset_token_validity: 60 * 60 * 24,
|
||||||
profile_directory: true,
|
profile_directory: true,
|
||||||
privileged_staff: false
|
privileged_staff: false,
|
||||||
|
max_endorsed_users: 20
|
||||||
|
|
||||||
config :pleroma, :welcome,
|
config :pleroma, :welcome,
|
||||||
direct_message: [
|
direct_message: [
|
||||||
|
|
|
@ -742,6 +742,16 @@
|
||||||
3
|
3
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
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,
|
key: :autofollowed_nicknames,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
|
|
|
@ -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, `[]`
|
- `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
|
### Featured tags
|
||||||
|
|
||||||
*Added in Mastodon 3.0.0*
|
*Added in Mastodon 3.0.0*
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
reblog_mute: 3,
|
reblog_mute: 3,
|
||||||
notification_mute: 4,
|
notification_mute: 4,
|
||||||
inverse_subscription: 5,
|
inverse_subscription: 5,
|
||||||
suggestion_dismiss: 6
|
suggestion_dismiss: 6,
|
||||||
|
endorsement: 7
|
||||||
)
|
)
|
||||||
|
|
||||||
defenum(Pleroma.FollowingRelationship.State,
|
defenum(Pleroma.FollowingRelationship.State,
|
||||||
|
|
|
@ -78,6 +78,10 @@ defmodule Pleroma.User do
|
||||||
inverse_subscription: [
|
inverse_subscription: [
|
||||||
subscribee_subscriptions: :subscriber_users,
|
subscribee_subscriptions: :subscriber_users,
|
||||||
subscriber_subscriptions: :subscribee_users
|
subscriber_subscriptions: :subscribee_users
|
||||||
|
],
|
||||||
|
endorsement: [
|
||||||
|
endorser_endorsements: :endorsed_users,
|
||||||
|
endorsee_endorsements: :endorser_users
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -170,25 +174,25 @@ defmodule Pleroma.User do
|
||||||
{incoming_relation, incoming_relation_source}
|
{incoming_relation, incoming_relation_source}
|
||||||
]} <- @user_relationships_config do
|
]} <- @user_relationships_config do
|
||||||
# Definitions of `has_many` relations: :blocker_blocks, :muter_mutes, :reblog_muter_mutes,
|
# Definitions of `has_many` relations: :blocker_blocks, :muter_mutes, :reblog_muter_mutes,
|
||||||
# :notification_muter_mutes, :subscribee_subscriptions
|
# :notification_muter_mutes, :subscribee_subscriptions, :endorser_endorsements
|
||||||
has_many(outgoing_relation, UserRelationship,
|
has_many(outgoing_relation, UserRelationship,
|
||||||
foreign_key: :source_id,
|
foreign_key: :source_id,
|
||||||
where: [relationship_type: relationship_type]
|
where: [relationship_type: relationship_type]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Definitions of `has_many` relations: :blockee_blocks, :mutee_mutes, :reblog_mutee_mutes,
|
# Definitions of `has_many` relations: :blockee_blocks, :mutee_mutes, :reblog_mutee_mutes,
|
||||||
# :notification_mutee_mutes, :subscriber_subscriptions
|
# :notification_mutee_mutes, :subscriber_subscriptions, :endorsee_endorsements
|
||||||
has_many(incoming_relation, UserRelationship,
|
has_many(incoming_relation, UserRelationship,
|
||||||
foreign_key: :target_id,
|
foreign_key: :target_id,
|
||||||
where: [relationship_type: relationship_type]
|
where: [relationship_type: relationship_type]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Definitions of `has_many` relations: :blocked_users, :muted_users, :reblog_muted_users,
|
# Definitions of `has_many` relations: :blocked_users, :muted_users, :reblog_muted_users,
|
||||||
# :notification_muted_users, :subscriber_users
|
# :notification_muted_users, :subscriber_users, :endorsed_users
|
||||||
has_many(outgoing_relation_target, through: [outgoing_relation, :target])
|
has_many(outgoing_relation_target, through: [outgoing_relation, :target])
|
||||||
|
|
||||||
# Definitions of `has_many` relations: :blocker_users, :muter_users, :reblog_muter_users,
|
# Definitions of `has_many` relations: :blocker_users, :muter_users, :reblog_muter_users,
|
||||||
# :notification_muter_users, :subscribee_users
|
# :notification_muter_users, :subscribee_users, :endorser_users
|
||||||
has_many(incoming_relation_source, through: [incoming_relation, :source])
|
has_many(incoming_relation_source, through: [incoming_relation, :source])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -216,7 +220,7 @@ defmodule Pleroma.User do
|
||||||
@user_relationships_config do
|
@user_relationships_config do
|
||||||
# `def blocked_users_relation/2`, `def muted_users_relation/2`,
|
# `def blocked_users_relation/2`, `def muted_users_relation/2`,
|
||||||
# `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
|
# `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
|
||||||
# `def subscriber_users/2`
|
# `def subscriber_users/2`, `def endorsed_users_relation/2`
|
||||||
def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
|
def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
|
||||||
target_users_query = assoc(user, unquote(outgoing_relation_target))
|
target_users_query = assoc(user, unquote(outgoing_relation_target))
|
||||||
|
|
||||||
|
@ -229,7 +233,7 @@ def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated?
|
||||||
end
|
end
|
||||||
|
|
||||||
# `def blocked_users/2`, `def muted_users/2`, `def reblog_muted_users/2`,
|
# `def blocked_users/2`, `def muted_users/2`, `def reblog_muted_users/2`,
|
||||||
# `def notification_muted_users/2`, `def subscriber_users/2`
|
# `def notification_muted_users/2`, `def subscriber_users/2`, `def endorsed_users/2`
|
||||||
def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
|
def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
|
||||||
__MODULE__
|
__MODULE__
|
||||||
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
|
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
|
||||||
|
@ -240,7 +244,8 @@ def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
|
||||||
end
|
end
|
||||||
|
|
||||||
# `def blocked_users_ap_ids/2`, `def muted_users_ap_ids/2`, `def reblog_muted_users_ap_ids/2`,
|
# `def blocked_users_ap_ids/2`, `def muted_users_ap_ids/2`, `def reblog_muted_users_ap_ids/2`,
|
||||||
# `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2`
|
# `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2`,
|
||||||
|
# `def endorsed_users_ap_ids/2`
|
||||||
def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do
|
def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do
|
||||||
__MODULE__
|
__MODULE__
|
||||||
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
|
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
|
||||||
|
@ -1516,6 +1521,40 @@ def unblock(%User{} = blocker, %{ap_id: ap_id}) do
|
||||||
unblock(blocker, get_cached_by_ap_id(ap_id))
|
unblock(blocker, get_cached_by_ap_id(ap_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def endorse(%User{} = endorser, %User{} = target) do
|
||||||
|
with max_endorsed_users <- Pleroma.Config.get([:instance, :max_endorsed_users], 0),
|
||||||
|
endorsed_users <-
|
||||||
|
User.endorsed_users_relation(endorser)
|
||||||
|
|> Repo.aggregate(:count, :id) do
|
||||||
|
cond do
|
||||||
|
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
|
||||||
|
|
||||||
|
def endorse(%User{} = endorser, %{ap_id: ap_id}) do
|
||||||
|
with %User{} = endorsed <- get_cached_by_ap_id(ap_id) do
|
||||||
|
endorse(endorser, endorsed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unendorse(%User{} = unendorser, %User{} = target) do
|
||||||
|
UserRelationship.delete_endorsement(unendorser, target)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unendorse(%User{} = unendorser, %{ap_id: ap_id}) do
|
||||||
|
with %User{} = user <- get_cached_by_ap_id(ap_id) do
|
||||||
|
unendorse(unendorser, user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def mutes?(nil, _), do: false
|
def mutes?(nil, _), do: false
|
||||||
def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
|
def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
|
||||||
|
|
||||||
|
@ -1561,6 +1600,10 @@ def subscribed_to?(%User{} = user, %{ap_id: ap_id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def endorses?(%User{} = user, %User{} = target) do
|
||||||
|
UserRelationship.endorsement_exists?(user, target)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns map of outgoing (blocked, muted etc.) relationships' user AP IDs by relation type.
|
Returns map of outgoing (blocked, muted etc.) relationships' user AP IDs by relation type.
|
||||||
E.g. `outgoing_relationships_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}`
|
E.g. `outgoing_relationships_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}`
|
||||||
|
|
|
@ -24,17 +24,20 @@ defmodule Pleroma.UserRelationship do
|
||||||
|
|
||||||
for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do
|
for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do
|
||||||
# `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`,
|
# `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`,
|
||||||
# `def create_notification_mute/2`, `def create_inverse_subscription/2`
|
# `def create_notification_mute/2`, `def create_inverse_subscription/2`,
|
||||||
|
# `def endorsement/2`
|
||||||
def unquote(:"create_#{relationship_type}")(source, target),
|
def unquote(:"create_#{relationship_type}")(source, target),
|
||||||
do: create(unquote(relationship_type), source, target)
|
do: create(unquote(relationship_type), source, target)
|
||||||
|
|
||||||
# `def delete_block/2`, `def delete_mute/2`, `def delete_reblog_mute/2`,
|
# `def delete_block/2`, `def delete_mute/2`, `def delete_reblog_mute/2`,
|
||||||
# `def delete_notification_mute/2`, `def delete_inverse_subscription/2`
|
# `def delete_notification_mute/2`, `def delete_inverse_subscription/2`,
|
||||||
|
# `def delete_endorsement/2`
|
||||||
def unquote(:"delete_#{relationship_type}")(source, target),
|
def unquote(:"delete_#{relationship_type}")(source, target),
|
||||||
do: delete(unquote(relationship_type), source, target)
|
do: delete(unquote(relationship_type), source, target)
|
||||||
|
|
||||||
# `def block_exists?/2`, `def mute_exists?/2`, `def reblog_mute_exists?/2`,
|
# `def block_exists?/2`, `def mute_exists?/2`, `def reblog_mute_exists?/2`,
|
||||||
# `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2`
|
# `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2`,
|
||||||
|
# `def inverse_endorsement?/2`
|
||||||
def unquote(:"#{relationship_type}_exists?")(source, target),
|
def unquote(:"#{relationship_type}_exists?")(source, target),
|
||||||
do: exists?(unquote(relationship_type), source, target)
|
do: exists?(unquote(relationship_type), source, target)
|
||||||
end
|
end
|
||||||
|
|
|
@ -334,6 +334,42 @@ def unblock_operation do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def endorse_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Account actions"],
|
||||||
|
summary: "Endorse",
|
||||||
|
operationId: "AccountController.endorse",
|
||||||
|
security: [%{"oAuth" => ["follow", "write:accounts"]}],
|
||||||
|
description: "Addds the given account to endorsed accounts list.",
|
||||||
|
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||||
|
responses: %{
|
||||||
|
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
|
||||||
|
|
||||||
|
def unendorse_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Account actions"],
|
||||||
|
summary: "Unendorse",
|
||||||
|
operationId: "AccountController.unendorse",
|
||||||
|
security: [%{"oAuth" => ["follow", "write:accounts"]}],
|
||||||
|
description: "Removes the given account from endorsed accounts list.",
|
||||||
|
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def note_operation do
|
def note_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Account actions"],
|
tags: ["Account actions"],
|
||||||
|
@ -425,10 +461,10 @@ def endorsements_operation do
|
||||||
tags: ["Retrieve account information"],
|
tags: ["Retrieve account information"],
|
||||||
summary: "Endorsements",
|
summary: "Endorsements",
|
||||||
operationId: "AccountController.endorsements",
|
operationId: "AccountController.endorsements",
|
||||||
description: "Not implemented",
|
description: "Returns endorsed accounts",
|
||||||
security: [%{"oAuth" => ["read:accounts"]}],
|
security: [%{"oAuth" => ["read:accounts"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => empty_array_response()
|
200 => Operation.response("Array of Accounts", "application/json", array_of_accounts())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
|
defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
|
||||||
alias OpenApiSpex.Operation
|
alias OpenApiSpex.Operation
|
||||||
|
alias Pleroma.Web.ApiSpec.AccountOperation
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
|
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||||
|
@ -62,6 +63,25 @@ def favourites_operation do
|
||||||
}
|
}
|
||||||
end
|
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
|
def subscribe_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Account actions"],
|
tags: ["Account actions"],
|
||||||
|
|
|
@ -117,7 +117,8 @@ def follow(follower, followed) do
|
||||||
def unfollow(follower, unfollowed) do
|
def unfollow(follower, unfollowed) do
|
||||||
with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed),
|
with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed),
|
||||||
{:ok, _activity} <- ActivityPub.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}
|
{:ok, follower}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -57,7 +57,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:accounts"]}
|
%{scopes: ["write:accounts"]}
|
||||||
when action in [:update_credentials, :note]
|
when action in [:update_credentials, :note, :endorse, :unendorse]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists)
|
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists)
|
||||||
|
@ -84,7 +84,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
|
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
|
||||||
|
|
||||||
@relationship_actions [:follow, :unfollow]
|
@relationship_actions [:follow, :unfollow]
|
||||||
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock note)a
|
@needs_account ~W(
|
||||||
|
followers following lists follow unfollow mute unmute block unblock note endorse unendorse
|
||||||
|
)a
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
RateLimiter,
|
RateLimiter,
|
||||||
|
@ -450,6 +452,24 @@ def note(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@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, :bad_request, %{error: message})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@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)
|
||||||
|
else
|
||||||
|
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc "POST /api/v1/follows"
|
@doc "POST /api/v1/follows"
|
||||||
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
|
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
|
||||||
case User.get_cached_by_nickname(uri) do
|
case User.get_cached_by_nickname(uri) do
|
||||||
|
@ -505,7 +525,20 @@ def lookup(conn, %{acct: nickname} = _params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/endorsements"
|
@doc "GET /api/v1/endorsements"
|
||||||
def endorsements(conn, params), do: MastodonAPIController.empty_array(conn, params)
|
def endorsements(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
users =
|
||||||
|
user
|
||||||
|
|> User.endorsed_users_relation(_restrict_deactivated = true)
|
||||||
|
|> Pleroma.Repo.all()
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> render("index.json",
|
||||||
|
users: users,
|
||||||
|
for: user,
|
||||||
|
as: :user,
|
||||||
|
embed_relationships: embed_relationships?(params)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/identity_proofs"
|
@doc "GET /api/v1/identity_proofs"
|
||||||
def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params)
|
def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params)
|
||||||
|
|
|
@ -160,11 +160,18 @@ def render(
|
||||||
target,
|
target,
|
||||||
&User.muting_reblogs?(&1, &2)
|
&User.muting_reblogs?(&1, &2)
|
||||||
),
|
),
|
||||||
endorsed: false,
|
|
||||||
note:
|
note:
|
||||||
UserNote.show(
|
UserNote.show(
|
||||||
reading_user,
|
reading_user,
|
||||||
target
|
target
|
||||||
|
),
|
||||||
|
endorsed:
|
||||||
|
UserRelationship.exists?(
|
||||||
|
user_relationships,
|
||||||
|
:endorsement,
|
||||||
|
target,
|
||||||
|
reading_user,
|
||||||
|
&User.endorses?(&2, &1)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,12 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper,
|
import Pleroma.Web.ControllerHelper,
|
||||||
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2]
|
only: [
|
||||||
|
json_response: 3,
|
||||||
|
add_link_headers: 2,
|
||||||
|
embed_relationships?: 1,
|
||||||
|
assign_account_by_id: 2
|
||||||
|
]
|
||||||
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
@ -40,9 +45,18 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
|
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
|
||||||
)
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
|
||||||
|
when action == :endorsements
|
||||||
|
)
|
||||||
|
|
||||||
plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend)
|
plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend)
|
||||||
|
|
||||||
plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe])
|
plug(
|
||||||
|
:assign_account_by_id
|
||||||
|
when action in [:favourites, :endorsements, :subscribe, :unsubscribe]
|
||||||
|
)
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation
|
||||||
|
|
||||||
|
@ -90,6 +104,22 @@ def favourites(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
||||||
)
|
)
|
||||||
end
|
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"
|
@doc "POST /api/v1/pleroma/accounts/:id/subscribe"
|
||||||
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
|
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
|
||||||
with {:ok, _subscription} <- User.subscribe(user, subscription_target) do
|
with {:ok, _subscription} <- User.subscribe(user, subscription_target) do
|
||||||
|
|
|
@ -440,6 +440,7 @@ defmodule Pleroma.Web.Router do
|
||||||
scope [] do
|
scope [] do
|
||||||
pipe_through(:api)
|
pipe_through(:api)
|
||||||
get("/accounts/:id/favourites", AccountController, :favourites)
|
get("/accounts/:id/favourites", AccountController, :favourites)
|
||||||
|
get("/accounts/:id/endorsements", AccountController, :endorsements)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope [] do
|
scope [] do
|
||||||
|
@ -486,6 +487,8 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/accounts/:id/mute", AccountController, :mute)
|
post("/accounts/:id/mute", AccountController, :mute)
|
||||||
post("/accounts/:id/unmute", AccountController, :unmute)
|
post("/accounts/:id/unmute", AccountController, :unmute)
|
||||||
post("/accounts/:id/note", AccountController, :note)
|
post("/accounts/:id/note", AccountController, :note)
|
||||||
|
post("/accounts/:id/pin", AccountController, :endorse)
|
||||||
|
post("/accounts/:id/unpin", AccountController, :unendorse)
|
||||||
|
|
||||||
get("/conversations", ConversationController, :index)
|
get("/conversations", ConversationController, :index)
|
||||||
post("/conversations/:id/read", ConversationController, :mark_as_read)
|
post("/conversations/:id/read", ConversationController, :mark_as_read)
|
||||||
|
|
|
@ -2498,4 +2498,39 @@ defp object_id_from_created_activity(user) do
|
||||||
%{object: %{data: %{"id" => object_id}}} = Activity.get_by_id_with_object(id)
|
%{object: %{data: %{"id" => object_id}}} = Activity.get_by_id_with_object(id)
|
||||||
object_id
|
object_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "account endorsements" do
|
||||||
|
test "it pins people" do
|
||||||
|
user = insert(:user)
|
||||||
|
pinned_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _pinned_user, _user} = User.follow(user, pinned_user)
|
||||||
|
|
||||||
|
refute User.endorses?(user, pinned_user)
|
||||||
|
|
||||||
|
{:ok, _user_relationship} = User.endorse(user, pinned_user)
|
||||||
|
|
||||||
|
assert User.endorses?(user, pinned_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it unpins users" do
|
||||||
|
user = insert(:user)
|
||||||
|
pinned_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _pinned_user, _user} = User.follow(user, pinned_user)
|
||||||
|
{:ok, _user_relationship} = User.endorse(user, pinned_user)
|
||||||
|
{:ok, _user_pin} = User.unendorse(user, pinned_user)
|
||||||
|
|
||||||
|
refute User.endorses?(user, pinned_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it doesn't pin users you do not follow" do
|
||||||
|
user = insert(:user)
|
||||||
|
pinned_user = insert(:user)
|
||||||
|
|
||||||
|
assert {:error, _message} = User.endorse(user, pinned_user)
|
||||||
|
|
||||||
|
refute User.endorses?(user, pinned_user)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1207,6 +1207,18 @@ test "also unsubscribes a user" do
|
||||||
refute User.subscribed_to?(follower, followed)
|
refute User.subscribed_to?(follower, followed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "also unpins a user" do
|
||||||
|
[follower, followed] = insert_pair(:user)
|
||||||
|
{:ok, follower, followed, _} = CommonAPI.follow(follower, followed)
|
||||||
|
{:ok, _endorsement} = User.endorse(follower, followed)
|
||||||
|
|
||||||
|
assert User.endorses?(follower, followed)
|
||||||
|
|
||||||
|
{:ok, follower} = CommonAPI.unfollow(follower, followed)
|
||||||
|
|
||||||
|
refute User.endorses?(follower, followed)
|
||||||
|
end
|
||||||
|
|
||||||
test "cancels a pending follow for a local user" do
|
test "cancels a pending follow for a local user" do
|
||||||
follower = insert(:user)
|
follower = insert(:user)
|
||||||
followed = insert(:user, is_locked: true)
|
followed = insert(:user, is_locked: true)
|
||||||
|
|
|
@ -1838,4 +1838,66 @@ test "create a note on a user" do
|
||||||
|> get("/api/v1/accounts/relationships?id=#{other_user.id}")
|
|> get("/api/v1/accounts/relationships?id=#{other_user.id}")
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
end
|
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} = other_user1 = insert(:user)
|
||||||
|
|
||||||
|
CommonAPI.follow(user, other_user1)
|
||||||
|
|
||||||
|
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 "unpin account", %{user: user, conn: conn} do
|
||||||
|
%{id: id1} = other_user1 = insert(:user)
|
||||||
|
|
||||||
|
CommonAPI.follow(user, other_user1)
|
||||||
|
User.endorse(user, other_user1)
|
||||||
|
|
||||||
|
assert %{"id" => ^id1, "endorsed" => false} =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/accounts/#{id1}/unpin")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [] =
|
||||||
|
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} = other_user1 = insert(:user)
|
||||||
|
%{id: id2} = other_user2 = insert(:user)
|
||||||
|
|
||||||
|
CommonAPI.follow(user, other_user1)
|
||||||
|
CommonAPI.follow(user, other_user2)
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|
|
@ -279,4 +279,29 @@ test "returns 404 when subscription_target not found" do
|
||||||
assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404)
|
assert %{"error" => "Record not found"} = json_response_and_validate_schema(conn, 404)
|
||||||
end
|
end
|
||||||
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(user1, user2)
|
||||||
|
CommonAPI.follow(user1, user3)
|
||||||
|
|
||||||
|
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
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue