summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/pleroma/ecto_enums.ex3
-rw-r--r--lib/pleroma/pagination.ex7
-rw-r--r--lib/pleroma/user.ex49
-rw-r--r--lib/pleroma/user_relationship.ex9
-rw-r--r--lib/pleroma/web/api_spec/operations/account_operation.ex28
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex31
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/account_controller.ex38
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex9
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/account_controller.ex42
-rw-r--r--lib/pleroma/web/router.ex3
10 files changed, 200 insertions, 19 deletions
diff --git a/lib/pleroma/ecto_enums.ex b/lib/pleroma/ecto_enums.ex
index 0e3e1e5de..48c609d45 100644
--- a/lib/pleroma/ecto_enums.ex
+++ b/lib/pleroma/ecto_enums.ex
@@ -10,7 +10,8 @@ defenum(Pleroma.UserRelationship.Type,
reblog_mute: 3,
notification_mute: 4,
inverse_subscription: 5,
- suggestion_dismiss: 6
+ suggestion_dismiss: 6,
+ endorsement: 7
)
defenum(Pleroma.FollowingRelationship.State,
diff --git a/lib/pleroma/pagination.ex b/lib/pleroma/pagination.ex
index 33e45a0eb..2ce243845 100644
--- a/lib/pleroma/pagination.ex
+++ b/lib/pleroma/pagination.ex
@@ -94,7 +94,8 @@ defmodule Pleroma.Pagination do
offset: :integer,
limit: :integer,
skip_extra_order: :boolean,
- skip_order: :boolean
+ skip_order: :boolean,
+ shuffle: :boolean,
}
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
@@ -113,6 +114,10 @@ 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 390de1e2d..ea72af517 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -78,7 +78,11 @@ defmodule Pleroma.User do
inverse_subscription: [
subscribee_subscriptions: :subscriber_users,
subscriber_subscriptions: :subscribee_users
- ]
+ ],
+ endorsement: [
+ endorser_endorsements: :endorsed_users,
+ endorsee_endorsements: :endorser_users
+ ],
]
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
@@ -170,25 +174,25 @@ defmodule Pleroma.User do
{incoming_relation, incoming_relation_source}
]} <- @user_relationships_config do
# 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,
foreign_key: :source_id,
where: [relationship_type: relationship_type]
)
# 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,
foreign_key: :target_id,
where: [relationship_type: relationship_type]
)
# 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])
# 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])
end
@@ -216,7 +220,7 @@ defmodule Pleroma.User do
@user_relationships_config do
# `def blocked_users_relation/2`, `def muted_users_relation/2`,
# `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
- # `def subscriber_users/2`
+ # `def subscriber_users/2`, `def endorsed_users_relation/2`
def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
target_users_query = assoc(user, unquote(outgoing_relation_target))
@@ -229,7 +233,7 @@ defmodule Pleroma.User do
end
# `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
__MODULE__
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
@@ -240,7 +244,8 @@ defmodule Pleroma.User do
end
# `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
__MODULE__
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
@@ -1516,6 +1521,30 @@ defmodule Pleroma.User do
unblock(blocker, get_cached_by_ap_id(ap_id))
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)
+ 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?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
@@ -1561,6 +1590,10 @@ defmodule Pleroma.User do
end
end
+ def endorses?(%User{} = user, %User{} = target) do
+ UserRelationship.endorsement_exists?(user, target)
+ end
+
@doc """
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"]}`
diff --git a/lib/pleroma/user_relationship.ex b/lib/pleroma/user_relationship.ex
index a467e9b65..8be5acc59 100644
--- a/lib/pleroma/user_relationship.ex
+++ b/lib/pleroma/user_relationship.ex
@@ -24,17 +24,20 @@ defmodule Pleroma.UserRelationship 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_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),
do: create(unquote(relationship_type), source, target)
# `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),
do: delete(unquote(relationship_type), source, target)
# `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),
do: exists?(unquote(relationship_type), source, target)
end
diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex
index f5304d7d6..35d8609ef 100644
--- a/lib/pleroma/web/api_spec/operations/account_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/account_operation.ex
@@ -334,6 +334,34 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
}
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)
+ }
+ }
+ 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
%Operation{
tags: ["Account actions"],
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 ad49f6426..9996ff68b 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_account_operation.ex
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
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
@@ -62,6 +63,36 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
}
end
+ def endorsements_operation do
+ %Operation{
+ tags: ["Retrieve account information"],
+ summary: "Endorsements",
+ description: "Returns endorsed accounts",
+ operationId: "PleromaAPI.AccountController.endorsements",
+ parameters:
+ [
+ Operation.parameter(
+ :shuffle,
+ :query,
+ :boolean,
+ "Show endorsed accounts in random order"
+ ),
+ id_param()
+ ] ++ pagination_params(),
+ security: [%{"oAuth" => ["read:account"]}],
+ responses: %{
+ 200 =>
+ Operation.response(
+ "Array of Accounts",
+ "application/json",
+ AccountOperation.array_of_accounts()
+ ),
+ 403 => Operation.response("Forbidden", "application/json", ApiError),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
def subscribe_operation do
%Operation{
tags: ["Account actions"],
diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
index a307807a9..1e9ce2927 100644
--- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
@@ -57,7 +57,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
plug(
OAuthScopesPlug,
%{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)
@@ -84,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 note)a
+ @needs_account ~W(followers following lists follow unfollow mute unmute block unblock endorse unendorse endorse unendorse)a
plug(
RateLimiter,
@@ -450,6 +450,24 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
end
end
+ @doc "POST /api/v1/accounts/:id/mute"
+ 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})
+ end
+ end
+
+ @doc "POST /api/v1/accounts/:id/unmute"
+ 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"
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
case User.get_cached_by_nickname(uri) do
@@ -505,7 +523,21 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
end
@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.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
+
+ conn
+ |> add_link_headers(users)
+ |> render("index.json",
+ users: users,
+ for: user,
+ as: :user,
+ embed_relationships: embed_relationships?(params)
+ )
+ end
@doc "GET /api/v1/identity_proofs"
def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params)
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index 4b15b1635..b964fdc54 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -160,11 +160,18 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
target,
&User.muting_reblogs?(&1, &2)
),
- endorsed: false,
note:
UserNote.show(
reading_user,
target
+ ),
+ endorsed:
+ UserRelationship.exists?(
+ user_relationships,
+ :endorsement,
+ target,
+ reading_user,
+ &User.endorses?(&2, &1)
)
}
end
diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex
index 8e4d3e7f7..805a1d7af 100644
--- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex
@@ -6,7 +6,12 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
use Pleroma.Web, :controller
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.Web.ActivityPub.ActivityPub
@@ -40,9 +45,15 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
)
+ plug(
+ OAuthScopesPlug,
+ %{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
+ when action == :endorsements
+ )
+
plug(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
@@ -90,6 +101,33 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
)
end
+ @doc "GET /api/v1/pleroma/accounts/:id/endorsements"
+ def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do
+ users =
+ user
+ |> User.endorsed_users_relation(_restrict_deactivated = true)
+ |> fetch_paginated_endorsements(params)
+
+ conn
+ |> add_link_headers(users)
+ |> render("index.json",
+ for: for_user,
+ users: users,
+ as: :user,
+ embed_relationships: embed_relationships?(params)
+ )
+ 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/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index b9b52b1e5..67c1a3e5c 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -440,6 +440,7 @@ defmodule Pleroma.Web.Router do
scope [] do
pipe_through(:api)
get("/accounts/:id/favourites", AccountController, :favourites)
+ get("/accounts/:id/endorsements", AccountController, :endorsements)
end
scope [] do
@@ -486,6 +487,8 @@ defmodule Pleroma.Web.Router do
post("/accounts/:id/mute", AccountController, :mute)
post("/accounts/:id/unmute", AccountController, :unmute)
post("/accounts/:id/note", AccountController, :note)
+ post("/accounts/:id/pin", AccountController, :endorse)
+ post("/accounts/:id/unpin", AccountController, :unendorse)
get("/conversations", ConversationController, :index)
post("/conversations/:id/read", ConversationController, :mark_as_read)