commit: 9fdf0720cf12e7cf2dfd75defdd5bda520f604de
parent 8cfd527589517176add18bb130e3d7d5875e74e3
Author: lanodan <lanodan@noreply.codeberg.org>
Date: Mon, 24 Jan 2022 19:15:31 +0100
Merge pull request 'Cherry-Picked Merges from Alex Gleason' (#5) from features/cherry-pick-merges into develop
Reviewed-on: https://codeberg.org/newroma-dev/newroma/pulls/5
Reviewed-by: floatingghost <floatingghost@noreply.codeberg.org>
Diffstat:
21 files changed, 679 insertions(+), 23 deletions(-)
diff --git 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
diff --git a/docs/development/API/nodeinfo.md 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"
+}
+```
+
diff --git a/docs/development/API/pleroma_api.md b/docs/development/API/pleroma_api.md
@@ -159,10 +159,12 @@ See [Admin-API](admin_api.md)
"muting": false,
"muting_notifications": false,
"subscribing": true,
+ "notifying": true,
"requested": false,
"domain_blocking": false,
"showing_reblogs": true,
- "endorsed": false
+ "endorsed": false,
+ "note": ""
}
```
@@ -183,10 +185,12 @@ See [Admin-API](admin_api.md)
"muting": false,
"muting_notifications": false,
"subscribing": false,
+ "notifying": false,
"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
@@ -0,0 +1,52 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# 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/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -200,7 +200,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
{:ok, notifications} = Notification.create_notifications(activity, do_send: false)
{:ok, _user} = ActivityPub.increase_note_count_if_public(user, object)
- if in_reply_to = object.data["inReplyTo"] && object.data["type"] != "Answer" do
+ if in_reply_to = object.data["type"] != "Answer" && object.data["inReplyTo"] do
Object.increase_replies_count(in_reply_to)
end
diff --git 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/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
}
}
},
@@ -328,6 +334,29 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
}
end
+ def note_operation do
+ %Operation{
+ tags: ["Account actions"],
+ summary: "Set a private note about a user.",
+ 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,9 +714,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
"blocked_by" => true,
"muting" => false,
"muting_notifications" => false,
+ "note" => "",
"requested" => false,
"domain_blocking" => false,
"subscribing" => false,
+ "notifying" => false,
"endorsed" => true
},
%{
@@ -699,9 +730,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
"blocked_by" => true,
"muting" => true,
"muting_notifications" => false,
+ "note" => "",
"requested" => true,
"domain_blocking" => false,
"subscribing" => false,
+ "notifying" => false,
"endorsed" => false
},
%{
@@ -713,9 +746,11 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
"blocked_by" => false,
"muting" => true,
"muting_notifications" => false,
+ "note" => "",
"requested" => false,
"domain_blocking" => true,
"subscribing" => true,
+ "notifying" => true,
"endorsed" => false
}
]
@@ -760,6 +795,23 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
}
end
+ defp note_request do
+ %Schema{
+ title: "AccountNoteRequest",
+ description: "POST body for adding a note 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
@@ -194,9 +194,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
"id" => "9tKi3esbG7OQgZ2920",
"muting" => false,
"muting_notifications" => false,
+ "note" => "",
"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
@@ -22,9 +22,11 @@ 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}
+ subscribing: %Schema{type: :boolean},
+ notifying: %Schema{type: :boolean}
},
example: %{
"blocked_by" => false,
@@ -36,9 +38,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
"id" => "9tKi3esbG7OQgZ2920",
"muting" => false,
"muting_notifications" => false,
+ "note" => "",
"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
@@ -282,9 +282,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
"id" => "9toJCsKN7SmSf3aj5c",
"muting" => false,
"muting_notifications" => false,
+ "note" => "",
"requested" => false,
"showing_reblogs" => true,
- "subscribing" => false
+ "subscribing" => false,
+ "notifying" => false
},
"skip_thread_containment" => false,
"tags" => []
diff --git 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,16 @@ 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)
+ 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/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,16 @@ 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(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
@@ -73,7 +84,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))
diff --git 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
@@ -101,6 +102,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 +148,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:
@@ -156,7 +160,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/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex
@@ -83,7 +83,10 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
"safe_dm_mentions"
end,
"pleroma_emoji_reactions",
- "pleroma_chat_messages"
+ "pleroma_chat_messages",
+ if Config.get([:instance, :show_reactions]) do
+ "exposable_reactions"
+ end
]
|> Enum.filter(& &1)
end
diff --git a/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex b/lib/pleroma/web/pleroma_api/controllers/chat_controller.ex
@@ -151,7 +151,9 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
index_query(user, params)
|> Pagination.fetch_paginated(params)
- render(conn, "index.json", chats: chats)
+ conn
+ |> add_link_headers(chats)
+ |> render("index.json", chats: chats)
end
defp index_query(%{id: user_id} = user, params) do
diff --git 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
@@ -0,0 +1,15 @@
+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/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,
@@ -542,4 +552,74 @@ 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
diff --git 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
diff --git 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" 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", %{notify: false})
+
+ 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")
@@ -1776,4 +1797,21 @@ 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", "read:follows"])
+ other_user = insert(:user)
+
+ conn
+ |> put_req_header("content-type", "application/json")
+ |> post("/api/v1/accounts/#{other_user.id}/note", %{
+ "comment" => "Example note"
+ })
+
+ 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
diff --git a/test/pleroma/web/mastodon_api/views/account_view_test.exs b/test/pleroma/web/mastodon_api/views/account_view_test.exs
@@ -268,10 +268,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
muting: false,
muting_notifications: false,
subscribing: false,
+ notifying: false,
requested: false,
domain_blocking: false,
showing_reblogs: true,
- endorsed: false
+ endorsed: false,
+ note: ""
}
test "represent a relationship for the following and followed user" do
@@ -293,6 +295,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)
}