forked from AkkomaGang/akkoma
Merge branch 'develop' into issue/1342
This commit is contained in:
commit
3fe7a1fd35
36 changed files with 563 additions and 84 deletions
|
@ -66,6 +66,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body)
|
- `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body)
|
||||||
- Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
|
- Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
|
||||||
- Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read
|
- Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read
|
||||||
|
- ActivityPub: Support `Move` activities
|
||||||
- Mastodon API: Add `/api/v1/markers` for managing timeline read markers
|
- Mastodon API: Add `/api/v1/markers` for managing timeline read markers
|
||||||
- Mastodon API: Add the `recipients` parameter to `GET /api/v1/conversations`
|
- Mastodon API: Add the `recipients` parameter to `GET /api/v1/conversations`
|
||||||
- Configuration: `feed` option for user atom feed.
|
- Configuration: `feed` option for user atom feed.
|
||||||
|
|
|
@ -95,7 +95,36 @@ def query_timelines(user) do
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity
|
as: :activity
|
||||||
})
|
})
|
||||||
end
|
end,
|
||||||
|
"Rendering favorites timeline" => fn ->
|
||||||
|
conn = Phoenix.ConnTest.build_conn(:get, "http://localhost:4001/api/v1/favourites", nil)
|
||||||
|
Pleroma.Web.MastodonAPI.StatusController.favourites(
|
||||||
|
%Plug.Conn{conn |
|
||||||
|
assigns: %{user: user},
|
||||||
|
query_params: %{"limit" => "0"},
|
||||||
|
body_params: %{},
|
||||||
|
cookies: %{},
|
||||||
|
params: %{},
|
||||||
|
path_params: %{},
|
||||||
|
private: %{
|
||||||
|
Pleroma.Web.Router => {[], %{}},
|
||||||
|
phoenix_router: Pleroma.Web.Router,
|
||||||
|
phoenix_action: :favourites,
|
||||||
|
phoenix_controller: Pleroma.Web.MastodonAPI.StatusController,
|
||||||
|
phoenix_endpoint: Pleroma.Web.Endpoint,
|
||||||
|
phoenix_format: "json",
|
||||||
|
phoenix_layout: {Pleroma.Web.LayoutView, "app.html"},
|
||||||
|
phoenix_recycled: true,
|
||||||
|
|
||||||
|
phoenix_view: Pleroma.Web.MastodonAPI.StatusView,
|
||||||
|
plug_session: %{"user_id" => user.id},
|
||||||
|
plug_session_fetch: :done,
|
||||||
|
plug_session_info: :write,
|
||||||
|
plug_skip_csrf_protection: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
%{})
|
||||||
|
end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,24 @@ defmodule Pleroma.LoadTesting.Generator do
|
||||||
use Pleroma.LoadTesting.Helper
|
use Pleroma.LoadTesting.Helper
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
def generate_like_activities(user, posts) do
|
||||||
|
count_likes = Kernel.trunc(length(posts) / 4)
|
||||||
|
IO.puts("Starting generating #{count_likes} like activities...")
|
||||||
|
|
||||||
|
{time, _} =
|
||||||
|
:timer.tc(fn ->
|
||||||
|
Task.async_stream(
|
||||||
|
Enum.take_random(posts, count_likes),
|
||||||
|
fn post -> {:ok, _, _} = CommonAPI.favorite(post.id, user) end,
|
||||||
|
max_concurrency: 10,
|
||||||
|
timeout: 30_000
|
||||||
|
)
|
||||||
|
|> Stream.run()
|
||||||
|
end)
|
||||||
|
|
||||||
|
IO.puts("Inserting like activities take #{to_sec(time)} sec.\n")
|
||||||
|
end
|
||||||
|
|
||||||
def generate_users(opts) do
|
def generate_users(opts) do
|
||||||
IO.puts("Starting generating #{opts[:users_max]} users...")
|
IO.puts("Starting generating #{opts[:users_max]} users...")
|
||||||
{time, _} = :timer.tc(fn -> do_generate_users(opts) end)
|
{time, _} = :timer.tc(fn -> do_generate_users(opts) end)
|
||||||
|
@ -31,7 +49,6 @@ defp generate_user_data(i) do
|
||||||
password_hash:
|
password_hash:
|
||||||
"$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg",
|
"$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg",
|
||||||
bio: "Tester Number #{i}",
|
bio: "Tester Number #{i}",
|
||||||
info: %{},
|
|
||||||
local: remote
|
local: remote
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,10 @@ def run(args) do
|
||||||
|
|
||||||
generate_remote_activities(user, remote_users)
|
generate_remote_activities(user, remote_users)
|
||||||
|
|
||||||
|
generate_like_activities(
|
||||||
|
user, Pleroma.Repo.all(Pleroma.Activity.Queries.by_type("Create"))
|
||||||
|
)
|
||||||
|
|
||||||
generate_dms(user, users, opts)
|
generate_dms(user, users, opts)
|
||||||
|
|
||||||
{:ok, activity} = generate_long_thread(user, users, opts)
|
{:ok, activity} = generate_long_thread(user, users, opts)
|
||||||
|
|
|
@ -57,6 +57,7 @@ Has these additional fields under the `pleroma` object:
|
||||||
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
|
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
|
||||||
- `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
|
- `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
|
||||||
- `deactivated`: boolean, true when the user is deactivated
|
- `deactivated`: boolean, true when the user is deactivated
|
||||||
|
- `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts
|
||||||
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
|
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
|
||||||
|
|
||||||
### Source
|
### Source
|
||||||
|
@ -91,6 +92,12 @@ Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
- `is_seen`: true if the notification was read by the user
|
- `is_seen`: true if the notification was read by the user
|
||||||
|
|
||||||
|
### Move Notification
|
||||||
|
|
||||||
|
The `type` value is `move`. Has an additional field:
|
||||||
|
|
||||||
|
- `target`: new account
|
||||||
|
|
||||||
## GET `/api/v1/notifications`
|
## GET `/api/v1/notifications`
|
||||||
|
|
||||||
Accepts additional parameters:
|
Accepts additional parameters:
|
||||||
|
@ -136,6 +143,7 @@ Additional parameters can be added to the JSON body/Form data:
|
||||||
- `default_scope` - the scope returned under `privacy` key in Source subentity
|
- `default_scope` - the scope returned under `privacy` key in Source subentity
|
||||||
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
|
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
|
||||||
- `skip_thread_containment` - if true, skip filtering out broken threads
|
- `skip_thread_containment` - if true, skip filtering out broken threads
|
||||||
|
- `allow_following_move` - if true, allows automatically follow moved following accounts
|
||||||
- `pleroma_background_image` - sets the background image of the user.
|
- `pleroma_background_image` - sets the background image of the user.
|
||||||
|
|
||||||
### Pleroma Settings Store
|
### Pleroma Settings Store
|
||||||
|
|
|
@ -28,7 +28,8 @@ defmodule Pleroma.Activity do
|
||||||
"Create" => "mention",
|
"Create" => "mention",
|
||||||
"Follow" => "follow",
|
"Follow" => "follow",
|
||||||
"Announce" => "reblog",
|
"Announce" => "reblog",
|
||||||
"Like" => "favourite"
|
"Like" => "favourite",
|
||||||
|
"Move" => "move"
|
||||||
}
|
}
|
||||||
|
|
||||||
@mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types,
|
@mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types,
|
||||||
|
|
|
@ -107,4 +107,22 @@ def following(%User{} = user) do
|
||||||
[user.follower_address | following]
|
[user.follower_address | following]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def move_following(origin, target) do
|
||||||
|
__MODULE__
|
||||||
|
|> join(:inner, [r], f in assoc(r, :follower))
|
||||||
|
|> where(following_id: ^origin.id)
|
||||||
|
|> where([r, f], f.allow_following_move == true)
|
||||||
|
|> limit(50)
|
||||||
|
|> preload([:follower])
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.map(fn following_relationship ->
|
||||||
|
Repo.delete(following_relationship)
|
||||||
|
Pleroma.Web.CommonAPI.follow(following_relationship.follower, target)
|
||||||
|
end)
|
||||||
|
|> case do
|
||||||
|
[] -> :ok
|
||||||
|
_ -> move_following(origin, target)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -251,10 +251,13 @@ def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = act
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_notifications(%Activity{data: %{"to" => _, "type" => type}} = activity)
|
def create_notifications(%Activity{data: %{"type" => type}} = activity)
|
||||||
when type in ["Like", "Announce", "Follow"] do
|
when type in ["Like", "Announce", "Follow", "Move"] do
|
||||||
users = get_notified_from_activity(activity)
|
notifications =
|
||||||
notifications = Enum.map(users, fn user -> create_notification(activity, user) end)
|
activity
|
||||||
|
|> get_notified_from_activity()
|
||||||
|
|> Enum.map(&create_notification(activity, &1))
|
||||||
|
|
||||||
{:ok, notifications}
|
{:ok, notifications}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -276,19 +279,15 @@ def create_notification(%Activity{} = activity, %User{} = user) do
|
||||||
|
|
||||||
def get_notified_from_activity(activity, local_only \\ true)
|
def get_notified_from_activity(activity, local_only \\ true)
|
||||||
|
|
||||||
def get_notified_from_activity(
|
def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only)
|
||||||
%Activity{data: %{"to" => _, "type" => type} = _data} = activity,
|
when type in ["Create", "Like", "Announce", "Follow", "Move"] do
|
||||||
local_only
|
[]
|
||||||
)
|
|> Utils.maybe_notify_to_recipients(activity)
|
||||||
when type in ["Create", "Like", "Announce", "Follow"] do
|
|> Utils.maybe_notify_mentioned_recipients(activity)
|
||||||
recipients =
|
|> Utils.maybe_notify_subscribers(activity)
|
||||||
[]
|
|> Utils.maybe_notify_followers(activity)
|
||||||
|> Utils.maybe_notify_to_recipients(activity)
|
|> Enum.uniq()
|
||||||
|> Utils.maybe_notify_mentioned_recipients(activity)
|
|> User.get_users_from_set(local_only)
|
||||||
|> Utils.maybe_notify_subscribers(activity)
|
|
||||||
|> Enum.uniq()
|
|
||||||
|
|
||||||
User.get_users_from_set(recipients, local_only)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_notified_from_activity(_, _local_only), do: []
|
def get_notified_from_activity(_, _local_only), do: []
|
||||||
|
|
|
@ -103,7 +103,9 @@ defmodule Pleroma.User do
|
||||||
field(:raw_fields, {:array, :map}, default: [])
|
field(:raw_fields, {:array, :map}, default: [])
|
||||||
field(:discoverable, :boolean, default: false)
|
field(:discoverable, :boolean, default: false)
|
||||||
field(:invisible, :boolean, default: false)
|
field(:invisible, :boolean, default: false)
|
||||||
|
field(:allow_following_move, :boolean, default: true)
|
||||||
field(:skip_thread_containment, :boolean, default: false)
|
field(:skip_thread_containment, :boolean, default: false)
|
||||||
|
field(:also_known_as, {:array, :string}, default: [])
|
||||||
|
|
||||||
embeds_one(
|
embeds_one(
|
||||||
:notification_settings,
|
:notification_settings,
|
||||||
|
@ -115,8 +117,6 @@ defmodule Pleroma.User do
|
||||||
has_many(:registrations, Registration)
|
has_many(:registrations, Registration)
|
||||||
has_many(:deliveries, Delivery)
|
has_many(:deliveries, Delivery)
|
||||||
|
|
||||||
field(:info, :map, default: %{})
|
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -222,7 +222,6 @@ def remote_user_creation(params) do
|
||||||
|
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.put(:info, params[:info] || %{})
|
|
||||||
|> truncate_if_exists(:name, name_limit)
|
|> truncate_if_exists(:name, name_limit)
|
||||||
|> truncate_if_exists(:bio, bio_limit)
|
|> truncate_if_exists(:bio, bio_limit)
|
||||||
|> truncate_fields_param()
|
|> truncate_fields_param()
|
||||||
|
@ -251,7 +250,8 @@ def remote_user_creation(params) do
|
||||||
:fields,
|
:fields,
|
||||||
:following_count,
|
:following_count,
|
||||||
:discoverable,
|
:discoverable,
|
||||||
:invisible
|
:invisible,
|
||||||
|
:also_known_as
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|> validate_required([:name, :ap_id])
|
|> validate_required([:name, :ap_id])
|
||||||
|
@ -293,13 +293,15 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
:hide_followers_count,
|
:hide_followers_count,
|
||||||
:hide_follows_count,
|
:hide_follows_count,
|
||||||
:hide_favorites,
|
:hide_favorites,
|
||||||
|
:allow_following_move,
|
||||||
:background,
|
:background,
|
||||||
:show_role,
|
:show_role,
|
||||||
:skip_thread_containment,
|
:skip_thread_containment,
|
||||||
:fields,
|
:fields,
|
||||||
:raw_fields,
|
:raw_fields,
|
||||||
:pleroma_settings_store,
|
:pleroma_settings_store,
|
||||||
:discoverable
|
:discoverable,
|
||||||
|
:also_known_as
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|> unique_constraint(:nickname)
|
|> unique_constraint(:nickname)
|
||||||
|
@ -337,9 +339,11 @@ def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
|
||||||
:hide_follows,
|
:hide_follows,
|
||||||
:fields,
|
:fields,
|
||||||
:hide_followers,
|
:hide_followers,
|
||||||
|
:allow_following_move,
|
||||||
:discoverable,
|
:discoverable,
|
||||||
:hide_followers_count,
|
:hide_followers_count,
|
||||||
:hide_follows_count
|
:hide_follows_count,
|
||||||
|
:also_known_as
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|> unique_constraint(:nickname)
|
|> unique_constraint(:nickname)
|
||||||
|
@ -1197,7 +1201,7 @@ def external_users_query do
|
||||||
def external_users(opts \\ []) do
|
def external_users(opts \\ []) do
|
||||||
query =
|
query =
|
||||||
external_users_query()
|
external_users_query()
|
||||||
|> select([u], struct(u, [:id, :ap_id, :info]))
|
|> select([u], struct(u, [:id, :ap_id]))
|
||||||
|
|
||||||
query =
|
query =
|
||||||
if opts[:max_id],
|
if opts[:max_id],
|
||||||
|
|
|
@ -541,6 +541,30 @@ def flag(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def move(%User{} = origin, %User{} = target, local \\ true) do
|
||||||
|
params = %{
|
||||||
|
"type" => "Move",
|
||||||
|
"actor" => origin.ap_id,
|
||||||
|
"object" => origin.ap_id,
|
||||||
|
"target" => target.ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
with true <- origin.ap_id in target.also_known_as,
|
||||||
|
{:ok, activity} <- insert(params, local) do
|
||||||
|
maybe_federate(activity)
|
||||||
|
|
||||||
|
BackgroundWorker.enqueue("move_following", %{
|
||||||
|
"origin_id" => origin.id,
|
||||||
|
"target_id" => target.id
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
false -> {:error, "Target account must have the origin in `alsoKnownAs`"}
|
||||||
|
err -> err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp fetch_activities_for_context_query(context, opts) do
|
defp fetch_activities_for_context_query(context, opts) do
|
||||||
public = [Pleroma.Constants.as_public()]
|
public = [Pleroma.Constants.as_public()]
|
||||||
|
|
||||||
|
@ -1171,7 +1195,8 @@ defp object_to_user_data(data) do
|
||||||
name: data["name"],
|
name: data["name"],
|
||||||
follower_address: data["followers"],
|
follower_address: data["followers"],
|
||||||
following_address: data["following"],
|
following_address: data["following"],
|
||||||
bio: data["summary"]
|
bio: data["summary"],
|
||||||
|
also_known_as: Map.get(data, "alsoKnownAs", [])
|
||||||
}
|
}
|
||||||
|
|
||||||
# nickname can be nil because of virtual actors
|
# nickname can be nil because of virtual actors
|
||||||
|
@ -1233,13 +1258,13 @@ defp maybe_update_follow_information(data) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp collection_private(data) do
|
defp collection_private(%{"first" => first}) do
|
||||||
if is_map(data["first"]) and
|
if is_map(first) and
|
||||||
data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
|
first["type"] in ["CollectionPage", "OrderedCollectionPage"] do
|
||||||
{:ok, false}
|
{:ok, false}
|
||||||
else
|
else
|
||||||
with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
|
with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
|
||||||
Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
|
Fetcher.fetch_and_contain_remote_object_from_id(first) do
|
||||||
{:ok, false}
|
{:ok, false}
|
||||||
else
|
else
|
||||||
{:error, {:ok, %{status: code}}} when code in [401, 403] ->
|
{:error, {:ok, %{status: code}}} when code in [401, 403] ->
|
||||||
|
@ -1254,6 +1279,8 @@ defp collection_private(data) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp collection_private(_data), do: {:ok, true}
|
||||||
|
|
||||||
def user_data_from_user_object(data) do
|
def user_data_from_user_object(data) do
|
||||||
with {:ok, data} <- MRF.filter(data),
|
with {:ok, data} <- MRF.filter(data),
|
||||||
{:ok, data} <- object_to_user_data(data) do
|
{:ok, data} <- object_to_user_data(data) do
|
||||||
|
|
|
@ -669,7 +669,7 @@ def handle_incoming(
|
||||||
|
|
||||||
update_data =
|
update_data =
|
||||||
new_user_data
|
new_user_data
|
||||||
|> Map.take([:avatar, :banner, :bio, :name])
|
|> Map.take([:avatar, :banner, :bio, :name, :also_known_as])
|
||||||
|> Map.put(:fields, fields)
|
|> Map.put(:fields, fields)
|
||||||
|> Map.put(:locked, locked)
|
|> Map.put(:locked, locked)
|
||||||
|> Map.put(:invisible, invisible)
|
|> Map.put(:invisible, invisible)
|
||||||
|
@ -857,6 +857,24 @@ def handle_incoming(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_incoming(
|
||||||
|
%{
|
||||||
|
"type" => "Move",
|
||||||
|
"actor" => origin_actor,
|
||||||
|
"object" => origin_actor,
|
||||||
|
"target" => target_actor
|
||||||
|
},
|
||||||
|
_options
|
||||||
|
) do
|
||||||
|
with %User{} = origin_user <- User.get_cached_by_ap_id(origin_actor),
|
||||||
|
{:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_actor),
|
||||||
|
true <- origin_actor in target_user.also_known_as do
|
||||||
|
ActivityPub.move(origin_user, target_user, false)
|
||||||
|
else
|
||||||
|
_e -> :error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def handle_incoming(_, _), do: :error
|
def handle_incoming(_, _), do: :error
|
||||||
|
|
||||||
@spec get_obj_helper(String.t(), Keyword.t()) :: {:ok, Object.t()} | nil
|
@spec get_obj_helper(String.t(), Keyword.t()) :: {:ok, Object.t()} | nil
|
||||||
|
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
||||||
@spec is_public?(Object.t() | Activity.t() | map()) :: boolean()
|
@spec is_public?(Object.t() | Activity.t() | map()) :: boolean()
|
||||||
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
||||||
def is_public?(%Object{data: data}), do: is_public?(data)
|
def is_public?(%Object{data: data}), do: is_public?(data)
|
||||||
|
def is_public?(%Activity{data: %{"type" => "Move"}}), do: true
|
||||||
def is_public?(%Activity{data: data}), do: is_public?(data)
|
def is_public?(%Activity{data: data}), do: is_public?(data)
|
||||||
def is_public?(%{"directMessage" => true}), do: false
|
def is_public?(%{"directMessage" => true}), do: false
|
||||||
def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data)
|
def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data)
|
||||||
|
|
|
@ -451,6 +451,8 @@ def maybe_notify_to_recipients(
|
||||||
recipients ++ to
|
recipients ++ to
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def maybe_notify_to_recipients(recipients, _), do: recipients
|
||||||
|
|
||||||
def maybe_notify_mentioned_recipients(
|
def maybe_notify_mentioned_recipients(
|
||||||
recipients,
|
recipients,
|
||||||
%Activity{data: %{"to" => _to, "type" => type} = data} = activity
|
%Activity{data: %{"to" => _to, "type" => type} = data} = activity
|
||||||
|
@ -502,6 +504,17 @@ def maybe_notify_subscribers(
|
||||||
|
|
||||||
def maybe_notify_subscribers(recipients, _), do: recipients
|
def maybe_notify_subscribers(recipients, _), do: recipients
|
||||||
|
|
||||||
|
def maybe_notify_followers(recipients, %Activity{data: %{"type" => "Move"}} = activity) do
|
||||||
|
with %User{} = user <- User.get_cached_by_ap_id(activity.actor) do
|
||||||
|
user
|
||||||
|
|> User.get_followers()
|
||||||
|
|> Enum.map(& &1.ap_id)
|
||||||
|
|> Enum.concat(recipients)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def maybe_notify_followers(recipients, _), do: recipients
|
||||||
|
|
||||||
def maybe_extract_mentions(%{"tag" => tag}) do
|
def maybe_extract_mentions(%{"tag" => tag}) do
|
||||||
tag
|
tag
|
||||||
|> Enum.filter(fn x -> is_map(x) && x["type"] == "Mention" end)
|
|> Enum.filter(fn x -> is_map(x) && x["type"] == "Mention" end)
|
||||||
|
|
|
@ -152,6 +152,7 @@ def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
||||||
:hide_favorites,
|
:hide_favorites,
|
||||||
:show_role,
|
:show_role,
|
||||||
:skip_thread_containment,
|
:skip_thread_containment,
|
||||||
|
:allow_following_move,
|
||||||
:discoverable
|
:discoverable
|
||||||
]
|
]
|
||||||
|> Enum.reduce(%{}, fn key, acc ->
|
|> Enum.reduce(%{}, fn key, acc ->
|
||||||
|
|
|
@ -162,6 +162,7 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
|> maybe_put_chat_token(user, opts[:for], opts)
|
|> maybe_put_chat_token(user, opts[:for], opts)
|
||||||
|> maybe_put_activation_status(user, opts[:for])
|
|> maybe_put_activation_status(user, opts[:for])
|
||||||
|> maybe_put_follow_requests_count(user, opts[:for])
|
|> maybe_put_follow_requests_count(user, opts[:for])
|
||||||
|
|> maybe_put_allow_following_move(user, opts[:for])
|
||||||
|> maybe_put_unread_conversation_count(user, opts[:for])
|
|> maybe_put_unread_conversation_count(user, opts[:for])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -238,6 +239,12 @@ defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id:
|
||||||
|
|
||||||
defp maybe_put_notification_settings(data, _, _), do: data
|
defp maybe_put_notification_settings(data, _, _), do: data
|
||||||
|
|
||||||
|
defp maybe_put_allow_following_move(data, %User{id: user_id} = user, %User{id: user_id}) do
|
||||||
|
Kernel.put_in(data, [:pleroma, :allow_following_move], user.allow_following_move)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_put_allow_following_move(data, _, _), do: data
|
||||||
|
|
||||||
defp maybe_put_activation_status(data, user, %User{is_admin: true}) do
|
defp maybe_put_activation_status(data, user, %User{is_admin: true}) do
|
||||||
Kernel.put_in(data, [:pleroma, :deactivated], user.deactivated)
|
Kernel.put_in(data, [:pleroma, :deactivated], user.deactivated)
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,32 +37,24 @@ def render("show.json", %{
|
||||||
}
|
}
|
||||||
|
|
||||||
case mastodon_type do
|
case mastodon_type do
|
||||||
"mention" ->
|
"mention" -> put_status(response, activity, user)
|
||||||
response
|
"favourite" -> put_status(response, parent_activity, user)
|
||||||
|> Map.merge(%{
|
"reblog" -> put_status(response, parent_activity, user)
|
||||||
status: StatusView.render("show.json", %{activity: activity, for: user})
|
"move" -> put_target(response, activity, user)
|
||||||
})
|
"follow" -> response
|
||||||
|
_ -> nil
|
||||||
"favourite" ->
|
|
||||||
response
|
|
||||||
|> Map.merge(%{
|
|
||||||
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
|
||||||
})
|
|
||||||
|
|
||||||
"reblog" ->
|
|
||||||
response
|
|
||||||
|> Map.merge(%{
|
|
||||||
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
|
||||||
})
|
|
||||||
|
|
||||||
"follow" ->
|
|
||||||
response
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
nil
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
_ -> nil
|
_ -> nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp put_status(response, activity, user) do
|
||||||
|
Map.put(response, :status, StatusView.render("show.json", %{activity: activity, for: user}))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp put_target(response, activity, user) do
|
||||||
|
target = User.get_cached_by_ap_id(activity.data["target"])
|
||||||
|
Map.put(response, :target, AccountView.render("show.json", %{user: target, for: user}))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,7 +16,7 @@ defmodule Pleroma.Web.Push.Impl do
|
||||||
require Logger
|
require Logger
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
@types ["Create", "Follow", "Announce", "Like"]
|
@types ["Create", "Follow", "Announce", "Like", "Move"]
|
||||||
|
|
||||||
@doc "Performs sending notifications for user subscriptions"
|
@doc "Performs sending notifications for user subscriptions"
|
||||||
@spec perform(Notification.t()) :: list(any) | :error
|
@spec perform(Notification.t()) :: list(any) | :error
|
||||||
|
|
|
@ -71,4 +71,11 @@ def perform(%{"op" => "fetch_data_for_activity", "activity_id" => activity_id},
|
||||||
activity = Activity.get_by_id(activity_id)
|
activity = Activity.get_by_id(activity_id)
|
||||||
Pleroma.Web.RichMedia.Helpers.perform(:fetch, activity)
|
Pleroma.Web.RichMedia.Helpers.perform(:fetch, activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def perform(%{"op" => "move_following", "origin_id" => origin_id, "target_id" => target_id}, _) do
|
||||||
|
origin = User.get_cached_by_id(origin_id)
|
||||||
|
target = User.get_cached_by_id(target_id)
|
||||||
|
|
||||||
|
Pleroma.FollowingRelationship.move_following(origin, target)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
2
mix.lock
2
mix.lock
|
@ -38,7 +38,7 @@
|
||||||
"fast_html": {:hex, :fast_html, "0.99.4", "d80812664f0429607e1d880fba0ef04da87a2e4fa596701bcaae17953535695c", [:make, :mix], [], "hexpm"},
|
"fast_html": {:hex, :fast_html, "0.99.4", "d80812664f0429607e1d880fba0ef04da87a2e4fa596701bcaae17953535695c", [:make, :mix], [], "hexpm"},
|
||||||
"fast_sanitize": {:hex, :fast_sanitize, "0.1.4", "6c2e7203ca2f8275527a3021ba6e9d5d4ee213a47dc214a97c128737c9e56df1", [:mix], [{:fast_html, "~> 0.99", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
"fast_sanitize": {:hex, :fast_sanitize, "0.1.4", "6c2e7203ca2f8275527a3021ba6e9d5d4ee213a47dc214a97c128737c9e56df1", [:mix], [{:fast_html, "~> 0.99", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
"floki": {:hex, :floki, "0.23.0", "956ab6dba828c96e732454809fb0bd8d43ce0979b75f34de6322e73d4c917829", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm"},
|
"floki": {:hex, :floki, "0.23.1", "e100306ce7d8841d70a559748e5091542e2cfc67ffb3ade92b89a8435034dab1", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm"},
|
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm"},
|
||||||
"gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm"},
|
"gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm"},
|
||||||
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
|
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddMoveSupportToUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add(:also_known_as, {:array, :string}, default: [], null: false)
|
||||||
|
add(:allow_following_move, :boolean, default: true, null: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.RemoveInfoFromUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
remove(:info, :map, default: %{})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,53 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.FixMissingFollowingCount do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
"""
|
||||||
|
UPDATE
|
||||||
|
users
|
||||||
|
SET
|
||||||
|
following_count = sub.count
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
users.id AS sub_id
|
||||||
|
,COUNT (following_relationships.id)
|
||||||
|
FROM
|
||||||
|
following_relationships
|
||||||
|
,users
|
||||||
|
WHERE
|
||||||
|
users.id = following_relationships.follower_id
|
||||||
|
AND following_relationships.state = 'accept'
|
||||||
|
GROUP BY
|
||||||
|
users.id
|
||||||
|
) AS sub
|
||||||
|
WHERE
|
||||||
|
users.id = sub.sub_id
|
||||||
|
AND users.local = TRUE
|
||||||
|
;
|
||||||
|
"""
|
||||||
|
|> execute()
|
||||||
|
|
||||||
|
"""
|
||||||
|
UPDATE
|
||||||
|
users
|
||||||
|
SET
|
||||||
|
following_count = 0
|
||||||
|
WHERE
|
||||||
|
following_count IS NULL
|
||||||
|
"""
|
||||||
|
|> execute()
|
||||||
|
|
||||||
|
execute("ALTER TABLE users
|
||||||
|
ALTER COLUMN following_count SET DEFAULT 0,
|
||||||
|
ALTER COLUMN following_count SET NOT NULL
|
||||||
|
")
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
execute("ALTER TABLE users
|
||||||
|
ALTER COLUMN following_count DROP DEFAULT,
|
||||||
|
ALTER COLUMN following_count DROP NOT NULL
|
||||||
|
")
|
||||||
|
end
|
||||||
|
end
|
|
@ -29,7 +29,11 @@
|
||||||
"@id": "litepub:oauthRegistrationEndpoint",
|
"@id": "litepub:oauthRegistrationEndpoint",
|
||||||
"@type": "@id"
|
"@type": "@id"
|
||||||
},
|
},
|
||||||
"EmojiReaction": "litepub:EmojiReaction"
|
"EmojiReaction": "litepub:EmojiReaction",
|
||||||
|
"alsoKnownAs": {
|
||||||
|
"@id": "as:alsoKnownAs",
|
||||||
|
"@type": "@id"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,11 @@
|
||||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||||
"conversation": "ostatus:conversation",
|
"conversation": "ostatus:conversation",
|
||||||
"toot": "http://joinmastodon.org/ns#",
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
"Emoji": "toot:Emoji"
|
"Emoji": "toot:Emoji",
|
||||||
|
"alsoKnownAs": {
|
||||||
|
"@id": "as:alsoKnownAs",
|
||||||
|
"@type": "@id"
|
||||||
|
}
|
||||||
}],
|
}],
|
||||||
"id": "http://mastodon.example.org/users/admin",
|
"id": "http://mastodon.example.org/users/admin",
|
||||||
"type": "Person",
|
"type": "Person",
|
||||||
|
@ -50,5 +54,6 @@
|
||||||
"type": "Image",
|
"type": "Image",
|
||||||
"mediaType": "image/png",
|
"mediaType": "image/png",
|
||||||
"url": "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
|
"url": "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
|
||||||
}
|
},
|
||||||
|
"alsoKnownAs": ["http://example.org/users/foo"]
|
||||||
}
|
}
|
||||||
|
|
19
test/fixtures/users_mock/friendica_followers.json
vendored
Normal file
19
test/fixtures/users_mock/friendica_followers.json
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
{
|
||||||
|
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||||
|
"dfrn": "http://purl.org/macgirvin/dfrn/1.0/",
|
||||||
|
"diaspora": "https://diasporafoundation.org/ns/",
|
||||||
|
"litepub": "http://litepub.social/ns#",
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"directMessage": "litepub:directMessage"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "http://localhost:8080/followers/fuser3",
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"totalItems": 296
|
||||||
|
}
|
19
test/fixtures/users_mock/friendica_following.json
vendored
Normal file
19
test/fixtures/users_mock/friendica_following.json
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
{
|
||||||
|
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||||
|
"dfrn": "http://purl.org/macgirvin/dfrn/1.0/",
|
||||||
|
"diaspora": "https://diasporafoundation.org/ns/",
|
||||||
|
"litepub": "http://litepub.social/ns#",
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"directMessage": "litepub:directMessage"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "http://localhost:8080/following/fuser3",
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"totalItems": 32
|
||||||
|
}
|
|
@ -228,5 +228,16 @@ test "skips microformats hashtags" do
|
||||||
|
|
||||||
assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
|
assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not crash when there is an HTML entity in a link" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{"status" => "\"http://cofe.com/?boomer=ok&foo=bar\""})
|
||||||
|
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
|
assert {:ok, nil} = HTML.extract_first_external_url(object, object.data["content"])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -642,6 +642,35 @@ test "notifications are deleted if a remote user is deleted" do
|
||||||
|
|
||||||
assert Enum.empty?(Notification.for_user(local_user))
|
assert Enum.empty?(Notification.for_user(local_user))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "move activity generates a notification" do
|
||||||
|
%{ap_id: old_ap_id} = old_user = insert(:user)
|
||||||
|
%{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
|
||||||
|
follower = insert(:user)
|
||||||
|
other_follower = insert(:user, %{allow_following_move: false})
|
||||||
|
|
||||||
|
User.follow(follower, old_user)
|
||||||
|
User.follow(other_follower, old_user)
|
||||||
|
|
||||||
|
Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
assert [
|
||||||
|
%{
|
||||||
|
activity: %{
|
||||||
|
data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
] = Notification.for_user(follower)
|
||||||
|
|
||||||
|
assert [
|
||||||
|
%{
|
||||||
|
activity: %{
|
||||||
|
data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
] = Notification.for_user(other_follower)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "for_user" do
|
describe "for_user" do
|
||||||
|
|
|
@ -31,7 +31,6 @@ def user_factory do
|
||||||
nickname: sequence(:nickname, &"nick#{&1}"),
|
nickname: sequence(:nickname, &"nick#{&1}"),
|
||||||
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
|
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
|
||||||
bio: sequence(:bio, &"Tester Number #{&1}"),
|
bio: sequence(:bio, &"Tester Number #{&1}"),
|
||||||
info: %{},
|
|
||||||
last_digest_emailed_at: NaiveDateTime.utc_now(),
|
last_digest_emailed_at: NaiveDateTime.utc_now(),
|
||||||
notification_settings: %Pleroma.User.NotificationSetting{}
|
notification_settings: %Pleroma.User.NotificationSetting{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1035,6 +1035,22 @@ def get("http://localhost:4001/users/masto_closed/following?page=1", _, _, _) do
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get("http://localhost:8080/followers/fuser3", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/users_mock/friendica_followers.json")
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get("http://localhost:8080/following/fuser3", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/users_mock/friendica_following.json")
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
def get("http://localhost:4001/users/fuser2/followers", _, _, _) do
|
def get("http://localhost:4001/users/fuser2/followers", _, _, _) do
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
|
|
|
@ -367,18 +367,6 @@ test "it sets the password_hash and ap_id" do
|
||||||
|
|
||||||
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
|
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it ensures info is not nil" do
|
|
||||||
changeset = User.register_changeset(%User{}, @full_user_data)
|
|
||||||
|
|
||||||
assert changeset.valid?
|
|
||||||
|
|
||||||
{:ok, user} =
|
|
||||||
changeset
|
|
||||||
|> Repo.insert()
|
|
||||||
|
|
||||||
refute is_nil(user.info)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "user registration, with :account_activation_required" do
|
describe "user registration, with :account_activation_required" do
|
||||||
|
@ -432,8 +420,7 @@ test "gets an existing user by ap_id" do
|
||||||
:user,
|
:user,
|
||||||
local: false,
|
local: false,
|
||||||
nickname: "admin@mastodon.example.org",
|
nickname: "admin@mastodon.example.org",
|
||||||
ap_id: ap_id,
|
ap_id: ap_id
|
||||||
info: %{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
||||||
|
@ -494,8 +481,7 @@ test "updates an existing user, if stale" do
|
||||||
local: false,
|
local: false,
|
||||||
nickname: "admin@mastodon.example.org",
|
nickname: "admin@mastodon.example.org",
|
||||||
ap_id: "http://mastodon.example.org/users/admin",
|
ap_id: "http://mastodon.example.org/users/admin",
|
||||||
last_refreshed_at: a_week_ago,
|
last_refreshed_at: a_week_ago
|
||||||
info: %{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert orig_user.last_refreshed_at == a_week_ago
|
assert orig_user.last_refreshed_at == a_week_ago
|
||||||
|
@ -536,7 +522,6 @@ test "returns an ap_followers link for a user" do
|
||||||
name: "Someone",
|
name: "Someone",
|
||||||
nickname: "a@b.de",
|
nickname: "a@b.de",
|
||||||
ap_id: "http...",
|
ap_id: "http...",
|
||||||
info: %{some: "info"},
|
|
||||||
avatar: %{some: "avatar"}
|
avatar: %{some: "avatar"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1143,8 +1128,7 @@ test "with an overly long bio" do
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
name: user.name,
|
name: user.name,
|
||||||
nickname: user.nickname,
|
nickname: user.nickname,
|
||||||
bio: String.duplicate("h", current_max_length + 1),
|
bio: String.duplicate("h", current_max_length + 1)
|
||||||
info: %{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||||
|
@ -1157,8 +1141,7 @@ test "with an overly long display name" do
|
||||||
data = %{
|
data = %{
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
name: String.duplicate("h", current_max_length + 1),
|
name: String.duplicate("h", current_max_length + 1),
|
||||||
nickname: user.nickname,
|
nickname: user.nickname
|
||||||
info: %{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||||
|
|
|
@ -4,8 +4,11 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Builders.ActivityBuilder
|
alias Pleroma.Builders.ActivityBuilder
|
||||||
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
@ -1554,5 +1557,80 @@ test "detects hidden follows" do
|
||||||
assert follow_info.hide_followers == false
|
assert follow_info.hide_followers == false
|
||||||
assert follow_info.hide_follows == true
|
assert follow_info.hide_follows == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "detects hidden follows/followers for friendica" do
|
||||||
|
user =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
follower_address: "http://localhost:8080/followers/fuser3",
|
||||||
|
following_address: "http://localhost:8080/following/fuser3"
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
|
||||||
|
assert follow_info.hide_followers == true
|
||||||
|
assert follow_info.follower_count == 296
|
||||||
|
assert follow_info.following_count == 32
|
||||||
|
assert follow_info.hide_follows == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Move activity" do
|
||||||
|
test "create" do
|
||||||
|
%{ap_id: old_ap_id} = old_user = insert(:user)
|
||||||
|
%{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
|
||||||
|
follower = insert(:user)
|
||||||
|
follower_move_opted_out = insert(:user, allow_following_move: false)
|
||||||
|
|
||||||
|
User.follow(follower, old_user)
|
||||||
|
User.follow(follower_move_opted_out, old_user)
|
||||||
|
|
||||||
|
assert User.following?(follower, old_user)
|
||||||
|
assert User.following?(follower_move_opted_out, old_user)
|
||||||
|
|
||||||
|
assert {:ok, activity} = ActivityPub.move(old_user, new_user)
|
||||||
|
|
||||||
|
assert %Activity{
|
||||||
|
actor: ^old_ap_id,
|
||||||
|
data: %{
|
||||||
|
"actor" => ^old_ap_id,
|
||||||
|
"object" => ^old_ap_id,
|
||||||
|
"target" => ^new_ap_id,
|
||||||
|
"type" => "Move"
|
||||||
|
},
|
||||||
|
local: true
|
||||||
|
} = activity
|
||||||
|
|
||||||
|
params = %{
|
||||||
|
"op" => "move_following",
|
||||||
|
"origin_id" => old_user.id,
|
||||||
|
"target_id" => new_user.id
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
|
||||||
|
|
||||||
|
Pleroma.Workers.BackgroundWorker.perform(params, nil)
|
||||||
|
|
||||||
|
refute User.following?(follower, old_user)
|
||||||
|
assert User.following?(follower, new_user)
|
||||||
|
|
||||||
|
assert User.following?(follower_move_opted_out, old_user)
|
||||||
|
refute User.following?(follower_move_opted_out, new_user)
|
||||||
|
|
||||||
|
activity = %Activity{activity | object: nil}
|
||||||
|
|
||||||
|
assert [%Notification{activity: ^activity}] =
|
||||||
|
Notification.for_user_since(follower, ~N[2019-04-13 11:22:33])
|
||||||
|
|
||||||
|
assert [%Notification{activity: ^activity}] =
|
||||||
|
Notification.for_user_since(follower_move_opted_out, ~N[2019-04-13 11:22:33])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "old user must be in the new user's `also_known_as` list" do
|
||||||
|
old_user = insert(:user)
|
||||||
|
new_user = insert(:user)
|
||||||
|
|
||||||
|
assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
|
||||||
|
ActivityPub.move(old_user, new_user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -683,6 +683,37 @@ test "it works for incoming update activities" do
|
||||||
assert user.bio == "<p>Some bio</p>"
|
assert user.bio == "<p>Some bio</p>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it works with alsoKnownAs" do
|
||||||
|
{:ok, %Activity{data: %{"actor" => actor}}} =
|
||||||
|
"test/fixtures/mastodon-post-activity.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Transmogrifier.handle_incoming()
|
||||||
|
|
||||||
|
assert User.get_cached_by_ap_id(actor).also_known_as == ["http://example.org/users/foo"]
|
||||||
|
|
||||||
|
{:ok, _activity} =
|
||||||
|
"test/fixtures/mastodon-update.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("actor", actor)
|
||||||
|
|> Map.update!("object", fn object ->
|
||||||
|
object
|
||||||
|
|> Map.put("actor", actor)
|
||||||
|
|> Map.put("id", actor)
|
||||||
|
|> Map.put("alsoKnownAs", [
|
||||||
|
"http://mastodon.example.org/users/foo",
|
||||||
|
"http://example.org/users/bar"
|
||||||
|
])
|
||||||
|
end)
|
||||||
|
|> Transmogrifier.handle_incoming()
|
||||||
|
|
||||||
|
assert User.get_cached_by_ap_id(actor).also_known_as == [
|
||||||
|
"http://mastodon.example.org/users/foo",
|
||||||
|
"http://example.org/users/bar"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
test "it works with custom profile fields" do
|
test "it works with custom profile fields" do
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
"test/fixtures/mastodon-post-activity.json"
|
"test/fixtures/mastodon-post-activity.json"
|
||||||
|
@ -1272,6 +1303,30 @@ test "it correctly processes messages with non-array cc field" do
|
||||||
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
|
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
|
||||||
assert [user.follower_address] == activity.data["to"]
|
assert [user.follower_address] == activity.data["to"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it accepts Move activities" do
|
||||||
|
old_user = insert(:user)
|
||||||
|
new_user = insert(:user)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"type" => "Move",
|
||||||
|
"actor" => old_user.ap_id,
|
||||||
|
"object" => old_user.ap_id,
|
||||||
|
"target" => new_user.ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
assert :error = Transmogrifier.handle_incoming(message)
|
||||||
|
|
||||||
|
{:ok, _new_user} = User.update_and_set_cache(new_user, %{also_known_as: [old_user.ap_id]})
|
||||||
|
|
||||||
|
assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(message)
|
||||||
|
assert activity.actor == old_user.ap_id
|
||||||
|
assert activity.data["actor"] == old_user.ap_id
|
||||||
|
assert activity.data["object"] == old_user.ap_id
|
||||||
|
assert activity.data["target"] == new_user.ap_id
|
||||||
|
assert activity.data["type"] == "Move"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "prepare outgoing" do
|
describe "prepare outgoing" do
|
||||||
|
|
|
@ -103,6 +103,21 @@ test "updates the user's locking status", %{conn: conn} do
|
||||||
assert user["locked"] == true
|
assert user["locked"] == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "updates the user's allow_following_move", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
assert user.allow_following_move == true
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{allow_following_move: "false"})
|
||||||
|
|
||||||
|
assert refresh_record(user).allow_following_move == false
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["pleroma"]["allow_following_move"] == false
|
||||||
|
end
|
||||||
|
|
||||||
test "updates the user's default scope", %{conn: conn} do
|
test "updates the user's default scope", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ test "Represent the user account for the account owner" do
|
||||||
privacy = user.default_scope
|
privacy = user.default_scope
|
||||||
|
|
||||||
assert %{
|
assert %{
|
||||||
pleroma: %{notification_settings: ^notification_settings},
|
pleroma: %{notification_settings: ^notification_settings, allow_following_move: true},
|
||||||
source: %{privacy: ^privacy}
|
source: %{privacy: ^privacy}
|
||||||
} = AccountView.render("show.json", %{user: user, for: user})
|
} = AccountView.render("show.json", %{user: user, for: user})
|
||||||
end
|
end
|
||||||
|
|
|
@ -107,4 +107,31 @@ test "Follow notification" do
|
||||||
assert [] ==
|
assert [] ==
|
||||||
NotificationView.render("index.json", %{notifications: [notification], for: followed})
|
NotificationView.render("index.json", %{notifications: [notification], for: followed})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "Move notification" do
|
||||||
|
%{ap_id: old_ap_id} = old_user = insert(:user)
|
||||||
|
%{ap_id: _new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
|
||||||
|
follower = insert(:user)
|
||||||
|
|
||||||
|
User.follow(follower, old_user)
|
||||||
|
Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
|
||||||
|
Pleroma.Tests.ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
old_user = refresh_record(old_user)
|
||||||
|
new_user = refresh_record(new_user)
|
||||||
|
|
||||||
|
[notification] = Notification.for_user(follower)
|
||||||
|
|
||||||
|
expected = %{
|
||||||
|
id: to_string(notification.id),
|
||||||
|
pleroma: %{is_seen: false},
|
||||||
|
type: "move",
|
||||||
|
account: AccountView.render("show.json", %{user: old_user, for: follower}),
|
||||||
|
target: AccountView.render("show.json", %{user: new_user, for: follower}),
|
||||||
|
created_at: Utils.to_masto_date(notification.inserted_at)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert [expected] ==
|
||||||
|
NotificationView.render("index.json", %{notifications: [notification], for: follower})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue