aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml14
-rw-r--r--CHANGELOG.md1
-rw-r--r--benchmarks/load_testing/fetcher.ex31
-rw-r--r--benchmarks/load_testing/generator.ex19
-rw-r--r--benchmarks/mix/tasks/pleroma/load_testing.ex4
-rw-r--r--config/prod.exs1
-rw-r--r--docs/API/differences_in_mastoapi_responses.md8
-rw-r--r--lib/pleroma/activity.ex16
-rw-r--r--lib/pleroma/following_relationship.ex18
-rw-r--r--lib/pleroma/notification.ex33
-rw-r--r--lib/pleroma/user.ex43
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex37
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex20
-rw-r--r--lib/pleroma/web/activity_pub/visibility.ex1
-rw-r--r--lib/pleroma/web/common_api/utils.ex13
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/account_controller.ex1
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex18
-rw-r--r--lib/pleroma/web/mastodon_api/views/notification_view.ex38
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex10
-rw-r--r--lib/pleroma/web/push/impl.ex7
-rw-r--r--lib/pleroma/workers/background_worker.ex7
-rw-r--r--mix.exs2
-rw-r--r--mix.lock10
-rw-r--r--priv/repo/migrations/20191025081729_add_move_support_to_users.exs10
-rw-r--r--priv/repo/migrations/20191123103423_remove_info_from_users.exs9
-rw-r--r--priv/repo/migrations/20191128153944_fix_missing_following_count.exs53
-rw-r--r--priv/static/schemas/litepub-0.1.jsonld6
-rw-r--r--test/federation/federation_test.exs47
-rw-r--r--test/fixtures/tesla_mock/admin@mastdon.example.org.json9
-rw-r--r--test/fixtures/users_mock/friendica_followers.json19
-rw-r--r--test/fixtures/users_mock/friendica_following.json19
-rw-r--r--test/html_test.exs11
-rw-r--r--test/notification_test.exs29
-rw-r--r--test/support/cluster.ex218
-rw-r--r--test/support/factory.ex1
-rw-r--r--test/support/http_request_mock.ex16
-rw-r--r--test/test_helper.exs3
-rw-r--r--test/user_test.exs103
-rw-r--r--test/web/activity_pub/activity_pub_test.exs78
-rw-r--r--test/web/activity_pub/transmogrifier_test.exs60
-rw-r--r--test/web/admin_api/admin_api_controller_test.exs1
-rw-r--r--test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs15
-rw-r--r--test/web/mastodon_api/views/account_view_test.exs13
-rw-r--r--test/web/mastodon_api/views/notification_view_test.exs27
-rw-r--r--test/web/streamer/streamer_test.exs2
45 files changed, 907 insertions, 194 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ab62c8827..88789035d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -31,6 +31,7 @@ build:
benchmark:
stage: benchmark
+ when: manual
variables:
MIX_ENV: benchmark
services:
@@ -55,6 +56,19 @@ unit-testing:
- mix ecto.migrate
- mix coveralls --preload-modules
+federated-testing:
+ stage: test
+ services:
+ - name: minibikini/postgres-with-rum:12
+ alias: postgres
+ command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
+ script:
+ - mix deps.get
+ - mix ecto.create
+ - mix ecto.migrate
+ - epmd -daemon
+ - mix test --trace --only federated
+
unit-testing-rum:
stage: test
services:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d0ac33b32..6cbaaff2c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -67,6 +67,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)
- 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
+- ActivityPub: Support `Move` activities
- Mastodon API: Add `/api/v1/markers` for managing timeline read markers
- Mastodon API: Add the `recipients` parameter to `GET /api/v1/conversations`
- Configuration: `feed` option for user atom feed.
diff --git a/benchmarks/load_testing/fetcher.ex b/benchmarks/load_testing/fetcher.ex
index cdc073b2e..a45a71d4a 100644
--- a/benchmarks/load_testing/fetcher.ex
+++ b/benchmarks/load_testing/fetcher.ex
@@ -95,7 +95,36 @@ defmodule Pleroma.LoadTesting.Fetcher do
for: user,
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
diff --git a/benchmarks/load_testing/generator.ex b/benchmarks/load_testing/generator.ex
index b4432bdb7..a957e0ffb 100644
--- a/benchmarks/load_testing/generator.ex
+++ b/benchmarks/load_testing/generator.ex
@@ -2,6 +2,24 @@ defmodule Pleroma.LoadTesting.Generator do
use Pleroma.LoadTesting.Helper
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
IO.puts("Starting generating #{opts[:users_max]} users...")
{time, _} = :timer.tc(fn -> do_generate_users(opts) end)
@@ -31,7 +49,6 @@ defmodule Pleroma.LoadTesting.Generator do
password_hash:
"$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg",
bio: "Tester Number #{i}",
- info: %{},
local: remote
}
diff --git a/benchmarks/mix/tasks/pleroma/load_testing.ex b/benchmarks/mix/tasks/pleroma/load_testing.ex
index 4fa3eec49..0a751adac 100644
--- a/benchmarks/mix/tasks/pleroma/load_testing.ex
+++ b/benchmarks/mix/tasks/pleroma/load_testing.ex
@@ -100,6 +100,10 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do
generate_remote_activities(user, remote_users)
+ generate_like_activities(
+ user, Pleroma.Repo.all(Pleroma.Activity.Queries.by_type("Create"))
+ )
+
generate_dms(user, users, opts)
{:ok, activity} = generate_long_thread(user, users, opts)
diff --git a/config/prod.exs b/config/prod.exs
index 301d2b9cb..25873f360 100644
--- a/config/prod.exs
+++ b/config/prod.exs
@@ -21,6 +21,7 @@ config :phoenix, serve_endpoints: true
# Do not print debug messages in production
config :logger, :console, level: :warn
+config :logger, :ex_syslogger, level: :warn
# ## SSL Support
#
diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md
index 7fbe17130..006d17c1b 100644
--- a/docs/API/differences_in_mastoapi_responses.md
+++ b/docs/API/differences_in_mastoapi_responses.md
@@ -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`
- `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
- `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.
### Source
@@ -91,6 +92,12 @@ Has these additional fields under the `pleroma` object:
- `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`
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
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
- `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 Settings Store
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index 7e283df32..f180c1e33 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -28,7 +28,8 @@ defmodule Pleroma.Activity do
"Create" => "mention",
"Follow" => "follow",
"Announce" => "reblog",
- "Like" => "favourite"
+ "Like" => "favourite",
+ "Move" => "move"
}
@mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types,
@@ -303,4 +304,17 @@ defmodule Pleroma.Activity do
end
defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
+
+ def direct_conversation_id(activity, for_user) do
+ alias Pleroma.Conversation.Participation
+
+ with %{data: %{"context" => context}} when is_binary(context) <- activity,
+ %Pleroma.Conversation{} = conversation <- Pleroma.Conversation.get_for_ap_id(context),
+ %Participation{id: participation_id} <-
+ Participation.for_user_and_conversation(for_user, conversation) do
+ participation_id
+ else
+ _ -> nil
+ end
+ end
end
diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex
index 3aff9fb76..a03c9bd30 100644
--- a/lib/pleroma/following_relationship.ex
+++ b/lib/pleroma/following_relationship.ex
@@ -107,4 +107,22 @@ defmodule Pleroma.FollowingRelationship do
[user.follower_address | following]
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
diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex
index b7ecf51e4..f37e7ec67 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -251,10 +251,13 @@ defmodule Pleroma.Notification do
end
end
- def create_notifications(%Activity{data: %{"to" => _, "type" => type}} = activity)
- when type in ["Like", "Announce", "Follow"] do
- users = get_notified_from_activity(activity)
- notifications = Enum.map(users, fn user -> create_notification(activity, user) end)
+ def create_notifications(%Activity{data: %{"type" => type}} = activity)
+ when type in ["Like", "Announce", "Follow", "Move"] do
+ notifications =
+ activity
+ |> get_notified_from_activity()
+ |> Enum.map(&create_notification(activity, &1))
+
{:ok, notifications}
end
@@ -276,19 +279,15 @@ defmodule Pleroma.Notification do
def get_notified_from_activity(activity, local_only \\ true)
- def get_notified_from_activity(
- %Activity{data: %{"to" => _, "type" => type} = _data} = activity,
- local_only
- )
- when type in ["Create", "Like", "Announce", "Follow"] do
- recipients =
- []
- |> Utils.maybe_notify_to_recipients(activity)
- |> Utils.maybe_notify_mentioned_recipients(activity)
- |> Utils.maybe_notify_subscribers(activity)
- |> Enum.uniq()
-
- User.get_users_from_set(recipients, local_only)
+ def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only)
+ when type in ["Create", "Like", "Announce", "Follow", "Move"] do
+ []
+ |> Utils.maybe_notify_to_recipients(activity)
+ |> Utils.maybe_notify_mentioned_recipients(activity)
+ |> Utils.maybe_notify_subscribers(activity)
+ |> Utils.maybe_notify_followers(activity)
+ |> Enum.uniq()
+ |> User.get_users_from_set(local_only)
end
def get_notified_from_activity(_, _local_only), do: []
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 3010fe87f..120cb2641 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -103,7 +103,9 @@ defmodule Pleroma.User do
field(:raw_fields, {:array, :map}, default: [])
field(:discoverable, :boolean, default: false)
field(:invisible, :boolean, default: false)
+ field(:allow_following_move, :boolean, default: true)
field(:skip_thread_containment, :boolean, default: false)
+ field(:also_known_as, {:array, :string}, default: [])
field(:notification_settings, :map,
default: %{
@@ -118,8 +120,6 @@ defmodule Pleroma.User do
has_many(:registrations, Registration)
has_many(:deliveries, Delivery)
- field(:info, :map, default: %{})
-
timestamps()
end
@@ -177,20 +177,6 @@ defmodule Pleroma.User do
def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
- def user_info(%User{} = user, args \\ %{}) do
- following_count = Map.get(args, :following_count, user.following_count)
- follower_count = Map.get(args, :follower_count, user.follower_count)
-
- %{
- note_count: user.note_count,
- locked: user.locked,
- confirmation_pending: user.confirmation_pending,
- default_scope: user.default_scope,
- follower_count: follower_count,
- following_count: following_count
- }
- end
-
def follow_state(%User{} = user, %User{} = target) do
case Utils.fetch_latest_follow(user, target) do
%{data: %{"state" => state}} -> state
@@ -209,10 +195,6 @@ defmodule Pleroma.User do
Cachex.put(:user_cache, "follow_state:#{user_ap_id}|#{target_ap_id}", state)
end
- def set_info_cache(user, args) do
- Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user, args))
- end
-
@spec restrict_deactivated(Ecto.Query.t()) :: Ecto.Query.t()
def restrict_deactivated(query) do
from(u in query, where: u.deactivated != ^true)
@@ -243,7 +225,6 @@ defmodule Pleroma.User do
params =
params
- |> Map.put(:info, params[:info] || %{})
|> truncate_if_exists(:name, name_limit)
|> truncate_if_exists(:bio, bio_limit)
|> truncate_fields_param()
@@ -272,7 +253,8 @@ defmodule Pleroma.User do
:fields,
:following_count,
:discoverable,
- :invisible
+ :invisible,
+ :also_known_as
]
)
|> validate_required([:name, :ap_id])
@@ -314,13 +296,15 @@ defmodule Pleroma.User do
:hide_followers_count,
:hide_follows_count,
:hide_favorites,
+ :allow_following_move,
:background,
:show_role,
:skip_thread_containment,
:fields,
:raw_fields,
:pleroma_settings_store,
- :discoverable
+ :discoverable,
+ :also_known_as
]
)
|> unique_constraint(:nickname)
@@ -358,9 +342,11 @@ defmodule Pleroma.User do
:hide_follows,
:fields,
:hide_followers,
+ :allow_following_move,
:discoverable,
:hide_followers_count,
- :hide_follows_count
+ :hide_follows_count,
+ :also_known_as
]
)
|> unique_constraint(:nickname)
@@ -614,7 +600,6 @@ defmodule Pleroma.User do
def set_cache(%User{} = user) do
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
- Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user))
{:ok, user}
end
@@ -633,7 +618,6 @@ defmodule Pleroma.User do
def invalidate_cache(user) do
Cachex.del(:user_cache, "ap_id:#{user.ap_id}")
Cachex.del(:user_cache, "nickname:#{user.nickname}")
- Cachex.del(:user_cache, "user_info:#{user.id}")
end
def get_cached_by_ap_id(ap_id) do
@@ -701,11 +685,6 @@ defmodule Pleroma.User do
get_by_nickname(nickname_or_email) || get_by_email(nickname_or_email)
end
- def get_cached_user_info(user) do
- key = "user_info:#{user.id}"
- Cachex.fetch!(:user_cache, key, fn -> user_info(user) end)
- end
-
def fetch_by_nickname(nickname), do: ActivityPub.make_user_from_nickname(nickname)
def get_or_fetch_by_nickname(nickname) do
@@ -1236,7 +1215,7 @@ defmodule Pleroma.User do
def external_users(opts \\ []) do
query =
external_users_query()
- |> select([u], struct(u, [:id, :ap_id, :info]))
+ |> select([u], struct(u, [:id, :ap_id]))
query =
if opts[:max_id],
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index f25314ff6..d6a425d8b 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -541,6 +541,30 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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
public = [Pleroma.Constants.as_public()]
@@ -1171,7 +1195,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
name: data["name"],
follower_address: data["followers"],
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
@@ -1233,13 +1258,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
- defp collection_private(data) do
- if is_map(data["first"]) and
- data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
+ defp collection_private(%{"first" => first}) do
+ if is_map(first) and
+ first["type"] in ["CollectionPage", "OrderedCollectionPage"] do
{:ok, false}
else
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}
else
{:error, {:ok, %{status: code}}} when code in [401, 403] ->
@@ -1254,6 +1279,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
end
+ defp collection_private(_data), do: {:ok, true}
+
def user_data_from_user_object(data) do
with {:ok, data} <- MRF.filter(data),
{:ok, data} <- object_to_user_data(data) do
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 15612545b..ce95fb6ba 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -669,7 +669,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
update_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(:locked, locked)
|> Map.put(:invisible, invisible)
@@ -857,6 +857,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
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
@spec get_obj_helper(String.t(), Keyword.t()) :: {:ok, Object.t()} | nil
diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex
index cd4097493..e172f6d3f 100644
--- a/lib/pleroma/web/activity_pub/visibility.ex
+++ b/lib/pleroma/web/activity_pub/visibility.ex
@@ -14,6 +14,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
@spec is_public?(Object.t() | Activity.t() | map()) :: boolean()
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
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?(%{"directMessage" => true}), do: false
def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data)
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 88a5f434a..cbb64f8d2 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -451,6 +451,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
recipients ++ to
end
+ def maybe_notify_to_recipients(recipients, _), do: recipients
+
def maybe_notify_mentioned_recipients(
recipients,
%Activity{data: %{"to" => _to, "type" => type} = data} = activity
@@ -502,6 +504,17 @@ defmodule Pleroma.Web.CommonAPI.Utils do
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
tag
|> Enum.filter(fn x -> is_map(x) && x["type"] == "Mention" end)
diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
index 5f2544640..a69423f60 100644
--- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
@@ -152,6 +152,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
:hide_favorites,
:show_role,
:skip_thread_containment,
+ :allow_following_move,
:discoverable
]
|> Enum.reduce(%{}, fn key, acc ->
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index e30fed610..ec720e472 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -71,18 +71,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
image = User.avatar_url(user) |> MediaProxy.url()
header = User.banner_url(user) |> MediaProxy.url()
- user_info = User.get_cached_user_info(user)
following_count =
if !user.hide_follows_count or !user.hide_follows or opts[:for] == user do
- user_info.following_count
+ user.following_count || 0
else
0
end
followers_count =
if !user.hide_followers_count or !user.hide_followers or opts[:for] == user do
- user_info.follower_count
+ user.follower_count || 0
else
0
end
@@ -144,7 +143,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
# Pleroma extension
pleroma: %{
- confirmation_pending: user_info.confirmation_pending,
+ confirmation_pending: user.confirmation_pending,
tags: user.tags,
hide_followers_count: user.hide_followers_count,
hide_follows_count: user.hide_follows_count,
@@ -157,12 +156,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
}
}
|> maybe_put_role(user, opts[:for])
- |> maybe_put_settings(user, opts[:for], user_info)
+ |> maybe_put_settings(user, opts[:for], opts)
|> maybe_put_notification_settings(user, opts[:for])
|> maybe_put_settings_store(user, opts[:for], opts)
|> maybe_put_chat_token(user, opts[:for], opts)
|> maybe_put_activation_status(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])
end
@@ -191,7 +191,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
data,
%User{id: user_id} = user,
%User{id: user_id},
- _user_info
+ _opts
) do
data
|> Kernel.put_in([:source, :privacy], user.default_scope)
@@ -239,6 +239,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
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
Kernel.put_in(data, [:pleroma, :deactivated], user.deactivated)
end
diff --git a/lib/pleroma/web/mastodon_api/views/notification_view.ex b/lib/pleroma/web/mastodon_api/views/notification_view.ex
index 5e3dbe728..ddd7f5318 100644
--- a/lib/pleroma/web/mastodon_api/views/notification_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/notification_view.ex
@@ -37,32 +37,24 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
}
case mastodon_type do
- "mention" ->
- response
- |> Map.merge(%{
- status: StatusView.render("show.json", %{activity: activity, for: user})
- })
-
- "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
+ "mention" -> put_status(response, activity, user)
+ "favourite" -> put_status(response, parent_activity, user)
+ "reblog" -> put_status(response, parent_activity, user)
+ "move" -> put_target(response, activity, user)
+ "follow" -> response
+ _ -> nil
end
else
_ -> nil
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
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index baff54151..a0257dfa6 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -9,8 +9,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
alias Pleroma.Activity
alias Pleroma.ActivityExpiration
- alias Pleroma.Conversation
- alias Pleroma.Conversation.Participation
alias Pleroma.HTML
alias Pleroma.Object
alias Pleroma.Repo
@@ -245,12 +243,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
direct_conversation_id =
with {_, nil} <- {:direct_conversation_id, opts[:direct_conversation_id]},
{_, true} <- {:include_id, opts[:with_direct_conversation_id]},
- {_, %User{} = for_user} <- {:for_user, opts[:for]},
- %{data: %{"context" => context}} when is_binary(context) <- activity,
- %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
- %Participation{id: participation_id} <-
- Participation.for_user_and_conversation(for_user, conversation) do
- participation_id
+ {_, %User{} = for_user} <- {:for_user, opts[:for]} do
+ Activity.direct_conversation_id(activity, for_user)
else
{:direct_conversation_id, participation_id} when is_integer(participation_id) ->
participation_id
diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex
index dd445e8bf..a6a924d02 100644
--- a/lib/pleroma/web/push/impl.ex
+++ b/lib/pleroma/web/push/impl.ex
@@ -16,7 +16,7 @@ defmodule Pleroma.Web.Push.Impl do
require Logger
import Ecto.Query
- @types ["Create", "Follow", "Announce", "Like"]
+ @types ["Create", "Follow", "Announce", "Like", "Move"]
@doc "Performs sending notifications for user subscriptions"
@spec perform(Notification.t()) :: list(any) | :error
@@ -33,6 +33,8 @@ defmodule Pleroma.Web.Push.Impl do
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
avatar_url = User.avatar_url(actor)
object = Object.normalize(activity)
+ user = User.get_cached_by_id(user_id)
+ direct_conversation_id = Activity.direct_conversation_id(activity, user)
for subscription <- fetch_subsriptions(user_id),
get_in(subscription.data, ["alerts", type]) do
@@ -45,7 +47,8 @@ defmodule Pleroma.Web.Push.Impl do
icon: avatar_url,
preferred_locale: "en",
pleroma: %{
- activity_id: activity_id
+ activity_id: activity_id,
+ direct_conversation_id: direct_conversation_id
}
}
|> Jason.encode!()
diff --git a/lib/pleroma/workers/background_worker.ex b/lib/pleroma/workers/background_worker.ex
index 7ffc8eabe..323a4da1e 100644
--- a/lib/pleroma/workers/background_worker.ex
+++ b/lib/pleroma/workers/background_worker.ex
@@ -71,4 +71,11 @@ defmodule Pleroma.Workers.BackgroundWorker do
activity = Activity.get_by_id(activity_id)
Pleroma.Web.RichMedia.Helpers.perform(:fetch, activity)
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
diff --git a/mix.exs b/mix.exs
index 60c7fe3f6..eb2e54e4a 100644
--- a/mix.exs
+++ b/mix.exs
@@ -102,7 +102,7 @@ defmodule Pleroma.Mixfile do
{:phoenix_ecto, "~> 4.0"},
{:ecto_sql, "~> 3.2"},
{:postgrex, ">= 0.13.5"},
- {:oban, "~> 0.8.1"},
+ {:oban, "~> 0.12.0"},
{:quantum, "~> 2.3"},
{:gettext, "~> 0.15"},
{:comeonin, "~> 4.1.1"},
diff --git a/mix.lock b/mix.lock
index 3a664287c..49bdad128 100644
--- a/mix.lock
+++ b/mix.lock
@@ -23,8 +23,8 @@
"decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"},
"earmark": {:hex, :earmark, "1.4.2", "3aa0bd23bc4c61cf2f1e5d752d1bb470560a6f8539974f767a38923bb20e1d7f", [:mix], [], "hexpm"},
- "ecto": {:hex, :ecto, "3.2.3", "51274df79862845b388733fddcf6f107d0c8c86e27abe7131fa98f8d30761bda", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
- "ecto_sql": {:hex, :ecto_sql, "3.2.0", "751cea597e8deb616084894dd75cbabfdbe7255ff01e8c058ca13f0353a3921b", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.2.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
+ "ecto": {:hex, :ecto, "3.2.5", "76c864b77948a479e18e69cc1d0f0f4ee7cced1148ffe6a093ff91eba644f0b5", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
+ "ecto_sql": {:hex, :ecto_sql, "3.2.2", "d10845bc147b9f61ef485cbf0973c0a337237199bd9bd30dd9542db00aadc26b", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.2.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.2.0 or ~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
"esshd": {:hex, :esshd, "0.1.0", "6f93a2062adb43637edad0ea7357db2702a4b80dd9683482fe00f5134e97f4c1", [:mix], [], "hexpm"},
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm"},
"ex2ms": {:hex, :ex2ms, "1.5.0", "19e27f9212be9a96093fed8cdfbef0a2b56c21237196d26760f11dfcfae58e97", [:mix], [], "hexpm"},
@@ -38,7 +38,7 @@
"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"},
"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_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm"},
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
@@ -67,7 +67,7 @@
"myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]},
"nimble_parsec": {:hex, :nimble_parsec, "0.5.1", "c90796ecee0289dbb5ad16d3ad06f957b0cd1199769641c961cfe0b97db190e0", [:mix], [], "hexpm"},
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
- "oban": {:hex, :oban, "0.8.1", "4bbf62eb1829f856d69aeb5069ac7036afe07db8221a17de2a9169cc7a58a318", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
+ "oban": {:hex, :oban, "0.12.0", "5477d5ab4a5a201c0b6c89764040ebfc5d2c71c488a36f378016ce5990838f0f", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "hexpm"},
"phoenix": {:hex, :phoenix, "1.4.10", "619e4a545505f562cd294df52294372d012823f4fd9d34a6657a8b242898c255", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
@@ -97,7 +97,7 @@
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm"},
"swoosh": {:hex, :swoosh, "0.23.5", "bfd9404bbf5069b1be2ffd317923ce57e58b332e25dbca2a35dedd7820dfee5a", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
- "telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
+ "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm"},
"tesla": {:hex, :tesla, "1.3.0", "f35d72f029e608f9cdc6f6d6fcc7c66cf6d6512a70cfef9206b21b8bd0203a30", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 0.4", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.3", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
"timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
diff --git a/priv/repo/migrations/20191025081729_add_move_support_to_users.exs b/priv/repo/migrations/20191025081729_add_move_support_to_users.exs
new file mode 100644
index 000000000..580b9eb0f
--- /dev/null
+++ b/priv/repo/migrations/20191025081729_add_move_support_to_users.exs
@@ -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
diff --git a/priv/repo/migrations/20191123103423_remove_info_from_users.exs b/priv/repo/migrations/20191123103423_remove_info_from_users.exs
new file mode 100644
index 000000000..b251255ea
--- /dev/null
+++ b/priv/repo/migrations/20191123103423_remove_info_from_users.exs
@@ -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
diff --git a/priv/repo/migrations/20191128153944_fix_missing_following_count.exs b/priv/repo/migrations/20191128153944_fix_missing_following_count.exs
new file mode 100644
index 000000000..3236de7a4
--- /dev/null
+++ b/priv/repo/migrations/20191128153944_fix_missing_following_count.exs
@@ -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
diff --git a/priv/static/schemas/litepub-0.1.jsonld b/priv/static/schemas/litepub-0.1.jsonld
index 8bae42f6d..e7ebf72be 100644
--- a/priv/static/schemas/litepub-0.1.jsonld
+++ b/priv/static/schemas/litepub-0.1.jsonld
@@ -29,7 +29,11 @@
"@id": "litepub:oauthRegistrationEndpoint",
"@type": "@id"
},
- "EmojiReaction": "litepub:EmojiReaction"
+ "EmojiReaction": "litepub:EmojiReaction",
+ "alsoKnownAs": {
+ "@id": "as:alsoKnownAs",
+ "@type": "@id"
+ }
}
]
}
diff --git a/test/federation/federation_test.exs b/test/federation/federation_test.exs
new file mode 100644
index 000000000..45800568a
--- /dev/null
+++ b/test/federation/federation_test.exs
@@ -0,0 +1,47 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Integration.FederationTest do
+ use Pleroma.DataCase
+ @moduletag :federated
+ import Pleroma.Cluster
+
+ setup_all do
+ Pleroma.Cluster.spawn_default_cluster()
+ :ok
+ end
+
+ @federated1 :"federated1@127.0.0.1"
+ describe "federated cluster primitives" do
+ test "within/2 captures local bindings and executes block on remote node" do
+ captured_binding = :captured
+
+ result =
+ within @federated1 do
+ user = Pleroma.Factory.insert(:user)
+ {captured_binding, node(), user}
+ end
+
+ assert {:captured, @federated1, user} = result
+ refute Pleroma.User.get_by_id(user.id)
+ assert user.id == within(@federated1, do: Pleroma.User.get_by_id(user.id)).id
+ end
+
+ test "runs webserver on customized port" do
+ {nickname, url, url_404} =
+ within @federated1 do
+ import Pleroma.Web.Router.Helpers
+ user = Pleroma.Factory.insert(:user)
+ user_url = account_url(Pleroma.Web.Endpoint, :show, user)
+ url_404 = account_url(Pleroma.Web.Endpoint, :show, "not-exists")
+
+ {user.nickname, user_url, url_404}
+ end
+
+ assert {:ok, {{_, 200, _}, _headers, body}} = :httpc.request(~c"#{url}")
+ assert %{"acct" => ^nickname} = Jason.decode!(body)
+ assert {:ok, {{_, 404, _}, _headers, _body}} = :httpc.request(~c"#{url_404}")
+ end
+ end
+end
diff --git a/test/fixtures/tesla_mock/admin@mastdon.example.org.json b/test/fixtures/tesla_mock/admin@mastdon.example.org.json
index 8159dc20a..9fdd6557c 100644
--- a/test/fixtures/tesla_mock/admin@mastdon.example.org.json
+++ b/test/fixtures/tesla_mock/admin@mastdon.example.org.json
@@ -9,7 +9,11 @@
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
"conversation": "ostatus:conversation",
"toot": "http://joinmastodon.org/ns#",
- "Emoji": "toot:Emoji"
+ "Emoji": "toot:Emoji",
+ "alsoKnownAs": {
+ "@id": "as:alsoKnownAs",
+ "@type": "@id"
+ }
}],
"id": "http://mastodon.example.org/users/admin",
"type": "Person",
@@ -50,5 +54,6 @@
"type": "Image",
"mediaType": "image/png",
"url": "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
- }
+ },
+ "alsoKnownAs": ["http://example.org/users/foo"]
}
diff --git a/test/fixtures/users_mock/friendica_followers.json b/test/fixtures/users_mock/friendica_followers.json
new file mode 100644
index 000000000..7b86b5fe2
--- /dev/null
+++ b/test/fixtures/users_mock/friendica_followers.json
@@ -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
+}
diff --git a/test/fixtures/users_mock/friendica_following.json b/test/fixtures/users_mock/friendica_following.json
new file mode 100644
index 000000000..7c526befc
--- /dev/null
+++ b/test/fixtures/users_mock/friendica_following.json
@@ -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
+}
diff --git a/test/html_test.exs b/test/html_test.exs
index f0869534c..c918dbe20 100644
--- a/test/html_test.exs
+++ b/test/html_test.exs
@@ -228,5 +228,16 @@ defmodule Pleroma.HTMLTest do
assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
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
diff --git a/test/notification_test.exs b/test/notification_test.exs
index f8d429223..dcbffeafe 100644
--- a/test/notification_test.exs
+++ b/test/notification_test.exs
@@ -630,6 +630,35 @@ defmodule Pleroma.NotificationTest do
assert Enum.empty?(Notification.for_user(local_user))
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
describe "for_user" do
diff --git a/test/support/cluster.ex b/test/support/cluster.ex
new file mode 100644
index 000000000..deb37f361
--- /dev/null
+++ b/test/support/cluster.ex
@@ -0,0 +1,218 @@
+defmodule Pleroma.Cluster do
+ @moduledoc """
+ Facilities for managing a cluster of slave VM's for federated testing.
+
+ ## Spawning the federated cluster
+
+ `spawn_cluster/1` spawns a map of slave nodes that are started
+ within the running VM. During startup, the slave node is sent all configuration
+ from the parent node, as well as all code. After receiving configuration and
+ code, the slave then starts all applications currently running on the parent.
+ The configuration passed to `spawn_cluster/1` overrides any parent application
+ configuration for the provided OTP app and key. This is useful for customizing
+ the Ecto database, Phoenix webserver ports, etc.
+
+ For example, to start a single federated VM named ":federated1", with the
+ Pleroma Endpoint running on port 4123, and with a database named
+ "pleroma_test1", you would run:
+
+ endpoint_conf = Application.fetch_env!(:pleroma, Pleroma.Web.Endpoint)
+ repo_conf = Application.fetch_env!(:pleroma, Pleroma.Repo)
+
+ Pleroma.Cluster.spawn_cluster(%{
+ :"federated1@127.0.0.1" => [
+ {:pleroma, Pleroma.Repo, Keyword.merge(repo_conf, database: "pleroma_test1")},
+ {:pleroma, Pleroma.Web.Endpoint,
+ Keyword.merge(endpoint_conf, http: [port: 4011], url: [port: 4011], server: true)}
+ ]
+ })
+
+ *Note*: application configuration for a given key is not merged,
+ so any customization requires first fetching the existing values
+ and merging yourself by providing the merged configuration,
+ such as above with the endpoint config and repo config.
+
+ ## Executing code within a remote node
+
+ Use the `within/2` macro to execute code within the context of a remote
+ federated node. The code block captures all local variable bindings from
+ the parent's context and returns the result of the expression after executing
+ it on the remote node. For example:
+
+ import Pleroma.Cluster
+
+ parent_value = 123
+
+ result =
+ within :"federated1@127.0.0.1" do
+ {node(), parent_value}
+ end
+
+ assert result == {:"federated1@127.0.0.1, 123}
+
+ *Note*: while local bindings are captured and available within the block,
+ other parent contexts like required, aliased, or imported modules are not
+ in scope. Those will need to be reimported/aliases/required within the block
+ as `within/2` is a remote procedure call.
+ """
+
+ @extra_apps Pleroma.Mixfile.application()[:extra_applications]
+
+ @doc """
+ Spawns the default Pleroma federated cluster.
+
+ Values before may be customized as needed for the test suite.
+ """
+ def spawn_default_cluster do
+ endpoint_conf = Application.fetch_env!(:pleroma, Pleroma.Web.Endpoint)
+ repo_conf = Application.fetch_env!(:pleroma, Pleroma.Repo)
+
+ spawn_cluster(%{
+ :"federated1@127.0.0.1" => [
+ {:pleroma, Pleroma.Repo, Keyword.merge(repo_conf, database: "pleroma_test_federated1")},
+ {:pleroma, Pleroma.Web.Endpoint,
+ Keyword.merge(endpoint_conf, http: [port: 4011], url: [port: 4011], server: true)}
+ ],
+ :"federated2@127.0.0.1" => [
+ {:pleroma, Pleroma.Repo, Keyword.merge(repo_conf, database: "pleroma_test_federated2")},
+ {:pleroma, Pleroma.Web.Endpoint,
+ Keyword.merge(endpoint_conf, http: [port: 4012], url: [port: 4012], server: true)}
+ ]
+ })
+ end
+
+ @doc """
+ Spawns a configured map of federated nodes.
+
+ See `Pleroma.Cluster` module documentation for details.
+ """
+ def spawn_cluster(node_configs) do
+ # Turn node into a distributed node with the given long name
+ :net_kernel.start([:"primary@127.0.0.1"])
+
+ # Allow spawned nodes to fetch all code from this node
+ {:ok, _} = :erl_boot_server.start([])
+ allow_boot("127.0.0.1")
+
+ silence_logger_warnings(fn ->
+ node_configs
+ |> Enum.map(&Task.async(fn -> start_slave(&1) end))
+ |> Enum.map(&Task.await(&1, 60_000))
+ end)
+ end
+
+ @doc """
+ Executes block of code again remote node.
+
+ See `Pleroma.Cluster` module documentation for details.
+ """
+ defmacro within(node, do: block) do
+ quote do
+ rpc(unquote(node), unquote(__MODULE__), :eval_quoted, [
+ unquote(Macro.escape(block)),
+ binding()
+ ])
+ end
+ end
+
+ @doc false
+ def eval_quoted(block, binding) do
+ {result, _binding} = Code.eval_quoted(block, binding, __ENV__)
+ result
+ end
+
+ defp start_slave({node_host, override_configs}) do
+ log(node_host, "booting federated VM")
+ {:ok, node} = :slave.start(~c"127.0.0.1", node_name(node_host), vm_args())
+ add_code_paths(node)
+ load_apps_and_transfer_configuration(node, override_configs)
+ ensure_apps_started(node)
+ {:ok, node}
+ end
+
+ def rpc(node, module, function, args) do
+ :rpc.block_call(node, module, function, args)
+ end
+
+ defp vm_args do
+ ~c"-loader inet -hosts 127.0.0.1 -setcookie #{:erlang.get_cookie()}"
+ end
+
+ defp allow_boot(host) do
+ {:ok, ipv4} = :inet.parse_ipv4_address(~c"#{host}")
+ :ok = :erl_boot_server.add_slave(ipv4)
+ end
+
+ defp add_code_paths(node) do
+ rpc(node, :code, :add_paths, [:code.get_path()])
+ end
+
+ defp load_apps_and_transfer_configuration(node, override_configs) do
+ Enum.each(Application.loaded_applications(), fn {app_name, _, _} ->
+ app_name
+ |> Application.get_all_env()
+ |> Enum.each(fn {key, primary_config} ->
+ rpc(node, Application, :put_env, [app_name, key, primary_config, [persistent: true]])
+ end)
+ end)
+
+ Enum.each(override_configs, fn {app_name, key, val} ->
+ rpc(node, Application, :put_env, [app_name, key, val, [persistent: true]])
+ end)
+ end
+
+ defp log(node, msg), do: IO.puts("[#{node}] #{msg}")
+
+ defp ensure_apps_started(node) do
+ loaded_names = Enum.map(Application.loaded_applications(), fn {name, _, _} -> name end)
+ app_names = @extra_apps ++ (loaded_names -- @extra_apps)
+
+ rpc(node, Application, :ensure_all_started, [:mix])
+ rpc(node, Mix, :env, [Mix.env()])
+ rpc(node, __MODULE__, :prepare_database, [])
+
+ log(node, "starting application")
+
+ Enum.reduce(app_names, MapSet.new(), fn app, loaded ->
+ if Enum.member?(loaded, app) do
+ loaded
+ else
+ {:ok, started} = rpc(node, Application, :ensure_all_started, [app])
+ MapSet.union(loaded, MapSet.new(started))
+ end
+ end)
+ end
+
+ @doc false
+ def prepare_database do
+ log(node(), "preparing database")
+ repo_config = Application.get_env(:pleroma, Pleroma.Repo)
+ repo_config[:adapter].storage_down(repo_config)
+ repo_config[:adapter].storage_up(repo_config)
+
+ {:ok, _, _} =
+ Ecto.Migrator.with_repo(Pleroma.Repo, fn repo ->
+ Ecto.Migrator.run(repo, :up, log: false, all: true)
+ end)
+
+ Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)
+ {:ok, _} = Application.ensure_all_started(:ex_machina)
+ end
+
+ defp silence_logger_warnings(func) do
+ prev_level = Logger.level()
+ Logger.configure(level: :error)
+ res = func.()
+ Logger.configure(level: prev_level)
+
+ res
+ end
+
+ defp node_name(node_host) do
+ node_host
+ |> to_string()
+ |> String.split("@")
+ |> Enum.at(0)
+ |> String.to_atom()
+ end
+end
diff --git a/test/support/factory.ex b/test/support/factory.ex
index e3f797f64..bb8a64e72 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -31,7 +31,6 @@ defmodule Pleroma.Factory do
nickname: sequence(:nickname, &"nick#{&1}"),
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
bio: sequence(:bio, &"Tester Number #{&1}"),
- info: %{},
last_digest_emailed_at: NaiveDateTime.utc_now()
}
diff --git a/test/support/http_request_mock.ex b/test/support/http_request_mock.ex
index 965335e96..e3a621f49 100644
--- a/test/support/http_request_mock.ex
+++ b/test/support/http_request_mock.ex
@@ -1035,6 +1035,22 @@ defmodule HttpRequestMock do
}}
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
{:ok,
%Tesla.Env{
diff --git a/test/test_helper.exs b/test/test_helper.exs
index c8dbee010..241ad1f94 100644
--- a/test/test_helper.exs
+++ b/test/test_helper.exs
@@ -3,7 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only
os_exclude = if :os.type() == {:unix, :darwin}, do: [skip_on_mac: true], else: []
-ExUnit.start(exclude: os_exclude)
+ExUnit.start(exclude: [:federated | os_exclude])
+
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)
Mox.defmock(Pleroma.ReverseProxy.ClientMock, for: Pleroma.ReverseProxy.Client)
{:ok, _} = Application.ensure_all_started(:ex_machina)
diff --git a/test/user_test.exs b/test/user_test.exs
index e6302b525..c345e43e9 100644
--- a/test/user_test.exs
+++ b/test/user_test.exs
@@ -367,18 +367,6 @@ defmodule Pleroma.UserTest do
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
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
describe "user registration, with :account_activation_required" do
@@ -432,8 +420,7 @@ defmodule Pleroma.UserTest do
:user,
local: false,
nickname: "admin@mastodon.example.org",
- ap_id: ap_id,
- info: %{}
+ ap_id: ap_id
)
{:ok, fetched_user} = User.get_or_fetch(ap_id)
@@ -494,8 +481,7 @@ defmodule Pleroma.UserTest do
local: false,
nickname: "admin@mastodon.example.org",
ap_id: "http://mastodon.example.org/users/admin",
- last_refreshed_at: a_week_ago,
- info: %{}
+ last_refreshed_at: a_week_ago
)
assert orig_user.last_refreshed_at == a_week_ago
@@ -536,7 +522,6 @@ defmodule Pleroma.UserTest do
name: "Someone",
nickname: "a@b.de",
ap_id: "http...",
- info: %{some: "info"},
avatar: %{some: "avatar"}
}
@@ -961,9 +946,9 @@ defmodule Pleroma.UserTest do
{:ok, user} = User.follow(user, user2)
{:ok, _user} = User.deactivate(user)
- info = User.get_cached_user_info(user2)
+ user2 = User.get_cached_by_id(user2.id)
- assert info.follower_count == 0
+ assert user2.follower_count == 0
assert [] = User.get_followers(user2)
end
@@ -977,10 +962,10 @@ defmodule Pleroma.UserTest do
{:ok, _user} = User.deactivate(user)
- info = User.get_cached_user_info(user2)
+ user2 = User.get_cached_by_id(user2.id)
assert refresh_record(user2).following_count == 0
- assert info.following_count == 0
+ assert user2.following_count == 0
assert User.following_count(user2) == 0
assert [] = User.get_friends(user2)
end
@@ -1143,8 +1128,7 @@ defmodule Pleroma.UserTest do
ap_id: user.ap_id,
name: user.name,
nickname: user.nickname,
- bio: String.duplicate("h", current_max_length + 1),
- info: %{}
+ bio: String.duplicate("h", current_max_length + 1)
}
assert {:ok, %User{}} = User.insert_or_update_user(data)
@@ -1157,8 +1141,7 @@ defmodule Pleroma.UserTest do
data = %{
ap_id: user.ap_id,
name: String.duplicate("h", current_max_length + 1),
- nickname: user.nickname,
- info: %{}
+ nickname: user.nickname
}
assert {:ok, %User{}} = User.insert_or_update_user(data)
@@ -1182,13 +1165,12 @@ defmodule Pleroma.UserTest do
describe "caching" do
test "invalidate_cache works" do
user = insert(:user)
- _user_info = User.get_cached_user_info(user)
+ User.set_cache(user)
User.invalidate_cache(user)
{:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
{:ok, nil} = Cachex.get(:user_cache, "nickname:#{user.nickname}")
- {:ok, nil} = Cachex.get(:user_cache, "user_info:#{user.id}")
end
test "User.delete() plugs any possible zombie objects" do
@@ -1344,7 +1326,7 @@ defmodule Pleroma.UserTest do
{:ok, user} = User.block(user, follower)
- assert User.user_info(user).follower_count == 2
+ assert user.follower_count == 2
end
describe "list_inactive_users_query/1" do
@@ -1521,51 +1503,6 @@ defmodule Pleroma.UserTest do
end
end
- describe "set_info_cache/2" do
- setup do
- user = insert(:user)
- {:ok, user: user}
- end
-
- test "update from args", %{user: user} do
- User.set_info_cache(user, %{following_count: 15, follower_count: 18})
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user)
- assert followers == 18
- assert following == 15
- end
-
- test "without args", %{user: user} do
- User.set_info_cache(user, %{})
-
- %{follower_count: followers, following_count: following} = User.get_cached_user_info(user)
- assert followers == 0
- assert following == 0
- end
- end
-
- describe "user_info/2" do
- setup do
- user = insert(:user)
- {:ok, user: user}
- end
-
- test "update from args", %{user: user} do
- %{follower_count: followers, following_count: following} =
- User.user_info(user, %{following_count: 15, follower_count: 18})
-
- assert followers == 18
- assert following == 15
- end
-
- test "without args", %{user: user} do
- %{follower_count: followers, following_count: following} = User.user_info(user)
-
- assert followers == 0
- assert following == 0
- end
- end
-
describe "is_internal_user?/1" do
test "non-internal user returns false" do
user = insert(:user)
@@ -1622,14 +1559,14 @@ defmodule Pleroma.UserTest do
ap_enabled: true
)
- assert User.user_info(other_user).following_count == 0
- assert User.user_info(other_user).follower_count == 0
+ assert other_user.following_count == 0
+ assert other_user.follower_count == 0
{:ok, user} = Pleroma.User.follow(user, other_user)
other_user = Pleroma.User.get_by_id(other_user.id)
- assert User.user_info(user).following_count == 1
- assert User.user_info(other_user).follower_count == 1
+ assert user.following_count == 1
+ assert other_user.follower_count == 1
end
test "syncronizes the counters with the remote instance for the followed when enabled" do
@@ -1645,14 +1582,14 @@ defmodule Pleroma.UserTest do
ap_enabled: true
)
- assert User.user_info(other_user).following_count == 0
- assert User.user_info(other_user).follower_count == 0
+ assert other_user.following_count == 0
+ assert other_user.follower_count == 0
Pleroma.Config.put([:instance, :external_user_synchronization], true)
{:ok, _user} = User.follow(user, other_user)
other_user = User.get_by_id(other_user.id)
- assert User.user_info(other_user).follower_count == 437
+ assert other_user.follower_count == 437
end
test "syncronizes the counters with the remote instance for the follower when enabled" do
@@ -1668,13 +1605,13 @@ defmodule Pleroma.UserTest do
ap_enabled: true
)
- assert User.user_info(other_user).following_count == 0
- assert User.user_info(other_user).follower_count == 0
+ assert other_user.following_count == 0
+ assert other_user.follower_count == 0
Pleroma.Config.put([:instance, :external_user_synchronization], true)
{:ok, other_user} = User.follow(other_user, user)
- assert User.user_info(other_user).following_count == 152
+ assert other_user.following_count == 152
end
end
diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs
index d437ad456..2677b9e36 100644
--- a/test/web/activity_pub/activity_pub_test.exs
+++ b/test/web/activity_pub/activity_pub_test.exs
@@ -4,8 +4,11 @@
defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
use Pleroma.DataCase
+ use Oban.Testing, repo: Pleroma.Repo
+
alias Pleroma.Activity
alias Pleroma.Builders.ActivityBuilder
+ alias Pleroma.Notification
alias Pleroma.Object
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
@@ -1554,5 +1557,80 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert follow_info.hide_followers == false
assert follow_info.hide_follows == true
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
diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs
index 0bdd514e9..5da358c43 100644
--- a/test/web/activity_pub/transmogrifier_test.exs
+++ b/test/web/activity_pub/transmogrifier_test.exs
@@ -39,6 +39,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert activity == returned_activity
end
+ @tag capture_log: true
test "it fetches replied-to activities if we don't have them" do
data =
File.read!("test/fixtures/mastodon-post-activity.json")
@@ -533,6 +534,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert object.data["content"] == "this is a private toot"
end
+ @tag capture_log: true
test "it rejects incoming announces with an inlined activity from another origin" do
data =
File.read!("test/fixtures/bogus-mastodon-announce.json")
@@ -681,6 +683,37 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert user.bio == "<p>Some bio</p>"
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
{:ok, activity} =
"test/fixtures/mastodon-post-activity.json"
@@ -814,6 +847,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert Activity.get_by_id(activity.id)
end
+ @tag capture_log: true
test "it works for incoming user deletes" do
%{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
@@ -1269,6 +1303,30 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
assert [user.follower_address] == activity.data["to"]
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
describe "prepare outgoing" do
@@ -1749,6 +1807,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
assert modified_object["inReplyToAtomUri"] == ""
end
+ @tag capture_log: true
test "returns modified object when allowed incoming reply", %{data: data} do
object_with_reply =
Map.put(
@@ -1868,6 +1927,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
end) =~ "Unsupported URI scheme"
end
+ @tag capture_log: true
test "returns {:ok, %Object{}} for success case" do
assert {:ok, %Object{}} =
Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873")
diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs
index 108baad91..4148f04bc 100644
--- a/test/web/admin_api/admin_api_controller_test.exs
+++ b/test/web/admin_api/admin_api_controller_test.exs
@@ -1989,6 +1989,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
Pleroma.Config.put([:instance, :dynamic_configuration], true)
end
+ @tag capture_log: true
test "create new config setting in db", %{conn: conn} do
conn =
post(conn, "/api/pleroma/admin/config", %{
diff --git a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs
index 519b56d6c..77cfce4fa 100644
--- a/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs
+++ b/test/web/mastodon_api/controllers/account_controller/update_credentials_test.exs
@@ -103,6 +103,21 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
assert user["locked"] == true
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
user = insert(:user)
diff --git a/test/web/mastodon_api/views/account_view_test.exs b/test/web/mastodon_api/views/account_view_test.exs
index af88841ed..35aefb7dc 100644
--- a/test/web/mastodon_api/views/account_view_test.exs
+++ b/test/web/mastodon_api/views/account_view_test.exs
@@ -102,7 +102,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
privacy = user.default_scope
assert %{
- pleroma: %{notification_settings: ^notification_settings},
+ pleroma: %{notification_settings: ^notification_settings, allow_following_move: true},
source: %{privacy: ^privacy}
} = AccountView.render("show.json", %{user: user, for: user})
end
@@ -350,7 +350,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
}
}
- assert expected == AccountView.render("show.json", %{user: user, for: other_user})
+ assert expected ==
+ AccountView.render("show.json", %{user: refresh_record(user), for: other_user})
end
test "returns the settings store if the requesting user is the represented user and it's requested specifically" do
@@ -374,6 +375,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
refute result.display_name == "<marquee> username </marquee>"
end
+ test "never display nil user follow counts" do
+ user = insert(:user, following_count: 0, follower_count: 0)
+ result = AccountView.render("show.json", %{user: user})
+
+ assert result.following_count == 0
+ assert result.followers_count == 0
+ end
+
describe "hiding follows/following" do
test "shows when follows/followers stats are hidden and sets follow/follower count to 0" do
user =
diff --git a/test/web/mastodon_api/views/notification_view_test.exs b/test/web/mastodon_api/views/notification_view_test.exs
index c9043a69a..26e1afc85 100644
--- a/test/web/mastodon_api/views/notification_view_test.exs
+++ b/test/web/mastodon_api/views/notification_view_test.exs
@@ -107,4 +107,31 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
assert [] ==
NotificationView.render("index.json", %{notifications: [notification], for: followed})
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
diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs
index 80a7541b2..8265f18dd 100644
--- a/test/web/streamer/streamer_test.exs
+++ b/test/web/streamer/streamer_test.exs
@@ -15,7 +15,7 @@ defmodule Pleroma.Web.StreamerTest do
alias Pleroma.Web.Streamer.StreamerSocket
alias Pleroma.Web.Streamer.Worker
- @moduletag needs_streamer: true
+ @moduletag needs_streamer: true, capture_log: true
clear_config_all([:instance, :skip_thread_containment])
describe "user streams" do