forked from AkkomaGang/akkoma
Merge branch 'develop' into feature/admin-api-list-statuses-for-a-given-instance
This commit is contained in:
commit
82f4e4760e
27 changed files with 421 additions and 95 deletions
|
@ -42,6 +42,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Authentication: Added rate limit for password-authorized actions / login existence checks
|
- Authentication: Added rate limit for password-authorized actions / login existence checks
|
||||||
- Static Frontend: Add the ability to render user profiles and notices server-side without requiring JS app.
|
- Static Frontend: Add the ability to render user profiles and notices server-side without requiring JS app.
|
||||||
- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
|
- Mix task to re-count statuses for all users (`mix pleroma.count_statuses`)
|
||||||
|
- Mix task to list all users (`mix pleroma.user list`)
|
||||||
- Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache).
|
- Support for `X-Forwarded-For` and similar HTTP headers which used by reverse proxies to pass a real user IP address to the backend. Must not be enabled unless your instance is behind at least one reverse proxy (such as Nginx, Apache HTTPD or Varnish Cache).
|
||||||
- MRF: New module which handles incoming posts based on their age. By default, all incoming posts that are older than 2 days will be unlisted and not shown to their followers.
|
- MRF: New module which handles incoming posts based on their age. By default, all incoming posts that are older than 2 days will be unlisted and not shown to their followers.
|
||||||
<details>
|
<details>
|
||||||
|
@ -70,6 +71,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Configuration: `feed` option for user atom feed.
|
- Configuration: `feed` option for user atom feed.
|
||||||
- Pleroma API: Add Emoji reactions
|
- Pleroma API: Add Emoji reactions
|
||||||
- Admin API: Add `/api/pleroma/admin/instances/:instance/statuses` - lists all statuses from a given instance
|
- Admin API: Add `/api/pleroma/admin/instances/:instance/statuses` - lists all statuses from a given instance
|
||||||
|
- Admin API: `PATCH /api/pleroma/users/confirm_email` to confirm email for multiple users, `PATCH /api/pleroma/users/resend_confirmation_email` to resend confirmation email for multiple users
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -180,7 +180,8 @@
|
||||||
|
|
||||||
# Configures Elixir's Logger
|
# Configures Elixir's Logger
|
||||||
config :logger, :console,
|
config :logger, :console,
|
||||||
format: "$time $metadata[$level] $message\n",
|
level: :debug,
|
||||||
|
format: "\n$time $metadata[$level] $message\n",
|
||||||
metadata: [:request_id]
|
metadata: [:request_id]
|
||||||
|
|
||||||
config :logger, :ex_syslogger,
|
config :logger, :ex_syslogger,
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
config :phoenix, serve_endpoints: true
|
config :phoenix, serve_endpoints: true
|
||||||
|
|
||||||
# Do not print debug messages in production
|
# Do not print debug messages in production
|
||||||
config :logger, level: :warn
|
config :logger, :console, level: :warn
|
||||||
|
|
||||||
# ## SSL Support
|
# ## SSL Support
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Config
|
import Config
|
||||||
|
|
||||||
config :pleroma, :instance, static: "/var/lib/pleroma/static"
|
config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
|
||||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
|
||||||
|
|
||||||
config_path = System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs"
|
config_path = System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs"
|
||||||
|
|
|
@ -15,7 +15,9 @@
|
||||||
method: Pleroma.Captcha.Mock
|
method: Pleroma.Captcha.Mock
|
||||||
|
|
||||||
# Print only warnings and errors during test
|
# Print only warnings and errors during test
|
||||||
config :logger, level: :warn
|
config :logger, :console,
|
||||||
|
level: :warn,
|
||||||
|
format: "\n[$level] $message\n"
|
||||||
|
|
||||||
config :pleroma, :auth, oauth_consumer_strategies: []
|
config :pleroma, :auth, oauth_consumer_strategies: []
|
||||||
|
|
||||||
|
|
|
@ -870,3 +870,19 @@ Compile time settings (need instance reboot):
|
||||||
- Authentication: required
|
- Authentication: required
|
||||||
- Params: None
|
- Params: None
|
||||||
- Response: JSON, "ok" and 200 status
|
- Response: JSON, "ok" and 200 status
|
||||||
|
|
||||||
|
## `PATCH /api/pleroma/admin/users/confirm_email`
|
||||||
|
|
||||||
|
### Confirm users' emails
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `nicknames`
|
||||||
|
- Response: Array of user nicknames
|
||||||
|
|
||||||
|
## `PATCH /api/pleroma/admin/users/resend_confirmation_email`
|
||||||
|
|
||||||
|
### Resend confirmation email
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
- `nicknames`
|
||||||
|
- Response: Array of user nicknames
|
||||||
|
|
|
@ -15,6 +15,11 @@ $PREFIX new <nickname> <email> [<options>]
|
||||||
- `--admin`/`--no-admin` - whether the user should be an admin
|
- `--admin`/`--no-admin` - whether the user should be an admin
|
||||||
- `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions
|
- `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions
|
||||||
|
|
||||||
|
## List local users
|
||||||
|
```sh
|
||||||
|
$PREFIX list
|
||||||
|
```
|
||||||
|
|
||||||
## Generate an invite link
|
## Generate an invite link
|
||||||
```sh
|
```sh
|
||||||
$PREFIX invite [<options>]
|
$PREFIX invite [<options>]
|
||||||
|
|
|
@ -6,6 +6,11 @@ defmodule Mix.Pleroma do
|
||||||
@doc "Common functions to be reused in mix tasks"
|
@doc "Common functions to be reused in mix tasks"
|
||||||
def start_pleroma do
|
def start_pleroma do
|
||||||
Application.put_env(:phoenix, :serve_endpoints, false, persistent: true)
|
Application.put_env(:phoenix, :serve_endpoints, false, persistent: true)
|
||||||
|
|
||||||
|
if Pleroma.Config.get(:env) != :test do
|
||||||
|
Application.put_env(:logger, :console, level: :debug)
|
||||||
|
end
|
||||||
|
|
||||||
{:ok, _} = Application.ensure_all_started(:pleroma)
|
{:ok, _} = Application.ensure_all_started(:pleroma)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -364,6 +364,24 @@ def run(["sign_out", nickname]) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def run(["list"]) do
|
||||||
|
start_pleroma()
|
||||||
|
|
||||||
|
Pleroma.User.Query.build(%{local: true})
|
||||||
|
|> Pleroma.RepoStreamer.chunk_stream(500)
|
||||||
|
|> Stream.each(fn users ->
|
||||||
|
users
|
||||||
|
|> Enum.each(fn user ->
|
||||||
|
shell_info(
|
||||||
|
"#{user.nickname} moderator: #{user.info.is_moderator}, admin: #{user.info.is_admin}, locked: #{
|
||||||
|
user.info.locked
|
||||||
|
}, deactivated: #{user.info.deactivated}"
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|> Stream.run()
|
||||||
|
end
|
||||||
|
|
||||||
defp set_moderator(user, value) do
|
defp set_moderator(user, value) do
|
||||||
{:ok, user} =
|
{:ok, user} =
|
||||||
user
|
user
|
||||||
|
|
|
@ -101,7 +101,7 @@ def following(%User{} = user) do
|
||||||
|> select([r, u], u.follower_address)
|
|> select([r, u], u.follower_address)
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|
|
||||||
if not user.local or user.nickname in [nil, "internal.fetch"] do
|
if not user.local or user.invisible do
|
||||||
following
|
following
|
||||||
else
|
else
|
||||||
[user.follower_address | following]
|
[user.follower_address | following]
|
||||||
|
|
|
@ -624,7 +624,31 @@ def get_log_entry_message(%ModerationLog{
|
||||||
"subject" => subjects
|
"subject" => subjects
|
||||||
}
|
}
|
||||||
}) do
|
}) do
|
||||||
"@#{actor_nickname} force password reset for users: #{users_to_nicknames_string(subjects)}"
|
"@#{actor_nickname} forced password reset for users: #{users_to_nicknames_string(subjects)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "confirm_email",
|
||||||
|
"subject" => subjects
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} confirmed email for users: #{users_to_nicknames_string(subjects)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "resend_confirmation_email",
|
||||||
|
"subject" => subjects
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} re-sent confirmation email for users: #{
|
||||||
|
users_to_nicknames_string(subjects)
|
||||||
|
}"
|
||||||
end
|
end
|
||||||
|
|
||||||
defp nicknames_to_string(nicknames) do
|
defp nicknames_to_string(nicknames) do
|
||||||
|
|
|
@ -63,7 +63,7 @@ def get_by_ap_id(ap_id) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp warn_on_no_object_preloaded(ap_id) do
|
defp warn_on_no_object_preloaded(ap_id) do
|
||||||
"Object.normalize() called without preloaded object (#{ap_id}). Consider preloading the object"
|
"Object.normalize() called without preloaded object (#{inspect(ap_id)}). Consider preloading the object"
|
||||||
|> Logger.debug()
|
|> Logger.debug()
|
||||||
|
|
||||||
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
|
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
|
||||||
|
|
|
@ -67,8 +67,7 @@ defmodule Pleroma.User do
|
||||||
field(:source_data, :map, default: %{})
|
field(:source_data, :map, default: %{})
|
||||||
field(:note_count, :integer, default: 0)
|
field(:note_count, :integer, default: 0)
|
||||||
field(:follower_count, :integer, default: 0)
|
field(:follower_count, :integer, default: 0)
|
||||||
# Should be filled in only for remote users
|
field(:following_count, :integer, default: 0)
|
||||||
field(:following_count, :integer, default: nil)
|
|
||||||
field(:locked, :boolean, default: false)
|
field(:locked, :boolean, default: false)
|
||||||
field(:confirmation_pending, :boolean, default: false)
|
field(:confirmation_pending, :boolean, default: false)
|
||||||
field(:password_reset_pending, :boolean, default: false)
|
field(:password_reset_pending, :boolean, default: false)
|
||||||
|
@ -134,6 +133,8 @@ def auth_active?(%User{}), do: true
|
||||||
|
|
||||||
def visible_for?(user, for_user \\ nil)
|
def visible_for?(user, for_user \\ nil)
|
||||||
|
|
||||||
|
def visible_for?(%User{invisible: true}, _), do: false
|
||||||
|
|
||||||
def visible_for?(%User{id: user_id}, %User{id: for_id}) when user_id == for_id, do: true
|
def visible_for?(%User{id: user_id}, %User{id: for_id}) when user_id == for_id, do: true
|
||||||
|
|
||||||
def visible_for?(%User{} = user, for_user) do
|
def visible_for?(%User{} = user, for_user) do
|
||||||
|
@ -177,19 +178,17 @@ def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
|
||||||
def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
|
def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
|
||||||
|
|
||||||
def user_info(%User{} = user, args \\ %{}) do
|
def user_info(%User{} = user, args \\ %{}) do
|
||||||
following_count =
|
following_count = Map.get(args, :following_count, user.following_count)
|
||||||
Map.get(args, :following_count, user.following_count || following_count(user))
|
|
||||||
|
|
||||||
follower_count = Map.get(args, :follower_count, user.follower_count)
|
follower_count = Map.get(args, :follower_count, user.follower_count)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
note_count: user.note_count,
|
note_count: user.note_count,
|
||||||
locked: user.locked,
|
locked: user.locked,
|
||||||
confirmation_pending: user.confirmation_pending,
|
confirmation_pending: user.confirmation_pending,
|
||||||
default_scope: user.default_scope
|
default_scope: user.default_scope,
|
||||||
|
follower_count: follower_count,
|
||||||
|
following_count: following_count
|
||||||
}
|
}
|
||||||
|> Map.put(:following_count, following_count)
|
|
||||||
|> Map.put(:follower_count, follower_count)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def follow_state(%User{} = user, %User{} = target) do
|
def follow_state(%User{} = user, %User{} = target) do
|
||||||
|
@ -492,6 +491,10 @@ def try_send_confirmation_email(%User{} = user) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def try_send_confirmation_email(users) do
|
||||||
|
Enum.each(users, &try_send_confirmation_email/1)
|
||||||
|
end
|
||||||
|
|
||||||
def needs_update?(%User{local: true}), do: false
|
def needs_update?(%User{local: true}), do: false
|
||||||
|
|
||||||
def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true
|
def needs_update?(%User{local: false, last_refreshed_at: nil}), do: true
|
||||||
|
@ -522,14 +525,9 @@ def maybe_direct_follow(%User{} = follower, %User{} = followed) do
|
||||||
@doc "A mass follow for local users. Respects blocks in both directions but does not create activities."
|
@doc "A mass follow for local users. Respects blocks in both directions but does not create activities."
|
||||||
@spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
|
@spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()}
|
||||||
def follow_all(follower, followeds) do
|
def follow_all(follower, followeds) do
|
||||||
followeds =
|
followeds
|
||||||
Enum.reject(followeds, fn followed ->
|
|> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end)
|
||||||
blocks?(follower, followed) || blocks?(followed, follower)
|
|> Enum.each(&follow(follower, &1, "accept"))
|
||||||
end)
|
|
||||||
|
|
||||||
Enum.each(followeds, &follow(follower, &1, "accept"))
|
|
||||||
|
|
||||||
Enum.each(followeds, &update_follower_count/1)
|
|
||||||
|
|
||||||
set_cache(follower)
|
set_cache(follower)
|
||||||
end
|
end
|
||||||
|
@ -549,11 +547,11 @@ def follow(%User{} = follower, %User{} = followed, state \\ "accept") do
|
||||||
true ->
|
true ->
|
||||||
FollowingRelationship.follow(follower, followed, state)
|
FollowingRelationship.follow(follower, followed, state)
|
||||||
|
|
||||||
follower = maybe_update_following_count(follower)
|
|
||||||
|
|
||||||
{:ok, _} = update_follower_count(followed)
|
{:ok, _} = update_follower_count(followed)
|
||||||
|
|
||||||
set_cache(follower)
|
follower
|
||||||
|
|> update_following_count()
|
||||||
|
|> set_cache()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -561,11 +559,12 @@ def unfollow(%User{} = follower, %User{} = followed) do
|
||||||
if following?(follower, followed) and follower.ap_id != followed.ap_id do
|
if following?(follower, followed) and follower.ap_id != followed.ap_id do
|
||||||
FollowingRelationship.unfollow(follower, followed)
|
FollowingRelationship.unfollow(follower, followed)
|
||||||
|
|
||||||
follower = maybe_update_following_count(follower)
|
|
||||||
|
|
||||||
{:ok, followed} = update_follower_count(followed)
|
{:ok, followed} = update_follower_count(followed)
|
||||||
|
|
||||||
set_cache(follower)
|
{:ok, follower} =
|
||||||
|
follower
|
||||||
|
|> update_following_count()
|
||||||
|
|> set_cache()
|
||||||
|
|
||||||
{:ok, follower, Utils.fetch_latest_follow(follower, followed)}
|
{:ok, follower, Utils.fetch_latest_follow(follower, followed)}
|
||||||
else
|
else
|
||||||
|
@ -895,8 +894,8 @@ def update_follower_count(%User{} = user) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec maybe_update_following_count(User.t()) :: User.t()
|
@spec update_following_count(User.t()) :: User.t()
|
||||||
def maybe_update_following_count(%User{local: false} = user) do
|
def update_following_count(%User{local: false} = user) do
|
||||||
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
|
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
|
||||||
maybe_fetch_follow_information(user)
|
maybe_fetch_follow_information(user)
|
||||||
else
|
else
|
||||||
|
@ -904,7 +903,13 @@ def maybe_update_following_count(%User{local: false} = user) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def maybe_update_following_count(user), do: user
|
def update_following_count(%User{local: true} = user) do
|
||||||
|
following_count = FollowingRelationship.following_count(user)
|
||||||
|
|
||||||
|
user
|
||||||
|
|> follow_information_changeset(%{following_count: following_count})
|
||||||
|
|> Repo.update!()
|
||||||
|
end
|
||||||
|
|
||||||
def set_unread_conversation_count(%User{local: true} = user) do
|
def set_unread_conversation_count(%User{local: true} = user) do
|
||||||
unread_query = Participation.unread_conversation_count_for_user(user)
|
unread_query = Participation.unread_conversation_count_for_user(user)
|
||||||
|
@ -1097,7 +1102,12 @@ def deactivate(users, status) when is_list(users) do
|
||||||
|
|
||||||
def deactivate(%User{} = user, status) do
|
def deactivate(%User{} = user, status) do
|
||||||
with {:ok, user} <- set_activation_status(user, status) do
|
with {:ok, user} <- set_activation_status(user, status) do
|
||||||
Enum.each(get_followers(user), &invalidate_cache/1)
|
user
|
||||||
|
|> get_followers()
|
||||||
|
|> Enum.filter(& &1.local)
|
||||||
|
|> Enum.each(fn follower ->
|
||||||
|
follower |> update_following_count() |> set_cache()
|
||||||
|
end)
|
||||||
|
|
||||||
# Only update local user counts, remote will be update during the next pull.
|
# Only update local user counts, remote will be update during the next pull.
|
||||||
user
|
user
|
||||||
|
@ -1317,19 +1327,20 @@ def get_or_fetch_by_ap_id(ap_id) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Creates an internal service actor by URI if missing. Optionally takes nickname for addressing."
|
@doc """
|
||||||
|
Creates an internal service actor by URI if missing.
|
||||||
|
Optionally takes nickname for addressing.
|
||||||
|
"""
|
||||||
def get_or_create_service_actor_by_ap_id(uri, nickname \\ nil) do
|
def get_or_create_service_actor_by_ap_id(uri, nickname \\ nil) do
|
||||||
with %User{} = user <- get_cached_by_ap_id(uri) do
|
with user when is_nil(user) <- get_cached_by_ap_id(uri) do
|
||||||
user
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
{:ok, user} =
|
{:ok, user} =
|
||||||
%User{}
|
%User{
|
||||||
|> cast(%{}, [:ap_id, :nickname, :local])
|
invisible: true,
|
||||||
|> put_change(:ap_id, uri)
|
local: true,
|
||||||
|> put_change(:nickname, nickname)
|
ap_id: uri,
|
||||||
|> put_change(:local, true)
|
nickname: nickname,
|
||||||
|> put_change(:follower_address, uri <> "/followers")
|
follower_address: uri <> "/followers"
|
||||||
|
}
|
||||||
|> Repo.insert()
|
|> Repo.insert()
|
||||||
|
|
||||||
user
|
user
|
||||||
|
@ -1575,6 +1586,11 @@ def toggle_confirmation(%User{} = user) do
|
||||||
|> update_and_set_cache()
|
|> update_and_set_cache()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec toggle_confirmation([User.t()]) :: [{:ok, User.t()} | {:error, Changeset.t()}]
|
||||||
|
def toggle_confirmation(users) do
|
||||||
|
Enum.map(users, &toggle_confirmation/1)
|
||||||
|
end
|
||||||
|
|
||||||
def get_mascot(%{mascot: %{} = mascot}) when not is_nil(mascot) do
|
def get_mascot(%{mascot: %{} = mascot}) when not is_nil(mascot) do
|
||||||
mascot
|
mascot
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,6 +45,7 @@ defp search_query(query_string, for_user, following) do
|
||||||
for_user
|
for_user
|
||||||
|> base_query(following)
|
|> base_query(following)
|
||||||
|> filter_blocked_user(for_user)
|
|> filter_blocked_user(for_user)
|
||||||
|
|> filter_invisible_users()
|
||||||
|> filter_blocked_domains(for_user)
|
|> filter_blocked_domains(for_user)
|
||||||
|> fts_search(query_string)
|
|> fts_search(query_string)
|
||||||
|> trigram_rank(query_string)
|
|> trigram_rank(query_string)
|
||||||
|
@ -98,6 +99,10 @@ defp trigram_rank(query, query_string) do
|
||||||
defp base_query(_user, false), do: User
|
defp base_query(_user, false), do: User
|
||||||
defp base_query(user, true), do: User.get_followers_query(user)
|
defp base_query(user, true), do: User.get_followers_query(user)
|
||||||
|
|
||||||
|
defp filter_invisible_users(query) do
|
||||||
|
from(q in query, where: q.invisible == false)
|
||||||
|
end
|
||||||
|
|
||||||
defp filter_blocked_user(query, %User{blocks: blocks})
|
defp filter_blocked_user(query, %User{blocks: blocks})
|
||||||
when length(blocks) > 0 do
|
when length(blocks) > 0 do
|
||||||
from(q in query, where: not (q.ap_id in ^blocks))
|
from(q in query, where: not (q.ap_id in ^blocks))
|
||||||
|
|
|
@ -14,7 +14,6 @@ def get_actor do
|
||||||
relay_ap_id()
|
relay_ap_id()
|
||||||
|> User.get_or_create_service_actor_by_ap_id()
|
|> User.get_or_create_service_actor_by_ap_id()
|
||||||
|
|
||||||
{:ok, actor} = User.set_invisible(actor, true)
|
|
||||||
actor
|
actor
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -350,7 +350,7 @@ def list_users(conn, params) do
|
||||||
}
|
}
|
||||||
|
|
||||||
with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
|
with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
|
||||||
{:ok, users, count} <- filter_relay_user(users, count),
|
{:ok, users, count} <- filter_service_users(users, count),
|
||||||
do:
|
do:
|
||||||
conn
|
conn
|
||||||
|> json(
|
|> json(
|
||||||
|
@ -362,15 +362,16 @@ def list_users(conn, params) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp filter_relay_user(users, count) do
|
defp filter_service_users(users, count) do
|
||||||
filtered_users = Enum.reject(users, &relay_user?/1)
|
filtered_users = Enum.reject(users, &service_user?/1)
|
||||||
count = if Enum.any?(users, &relay_user?/1), do: length(filtered_users), else: count
|
count = if Enum.any?(users, &service_user?/1), do: length(filtered_users), else: count
|
||||||
|
|
||||||
{:ok, filtered_users, count}
|
{:ok, filtered_users, count}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp relay_user?(user) do
|
defp service_user?(user) do
|
||||||
user.ap_id == Relay.relay_ap_id()
|
String.match?(user.ap_id, ~r/.*\/relay$/) or
|
||||||
|
String.match?(user.ap_id, ~r/.*\/internal\/fetch$/)
|
||||||
end
|
end
|
||||||
|
|
||||||
@filters ~w(local external active deactivated is_admin is_moderator)
|
@filters ~w(local external active deactivated is_admin is_moderator)
|
||||||
|
@ -814,6 +815,34 @@ def reload_emoji(conn, _params) do
|
||||||
conn |> json("ok")
|
conn |> json("ok")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
|
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
|
||||||
|
|
||||||
|
User.toggle_confirmation(users)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "confirm_email"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn |> json("")
|
||||||
|
end
|
||||||
|
|
||||||
|
def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
|
users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
|
||||||
|
|
||||||
|
User.try_send_confirmation_email(users)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "resend_confirmation_email"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn |> json("")
|
||||||
|
end
|
||||||
|
|
||||||
def errors(conn, {:error, :not_found}) do
|
def errors(conn, {:error, :not_found}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(:not_found)
|
|> put_status(:not_found)
|
||||||
|
|
|
@ -36,7 +36,8 @@ def render("show.json", %{user: user}) do
|
||||||
"deactivated" => user.deactivated,
|
"deactivated" => user.deactivated,
|
||||||
"local" => user.local,
|
"local" => user.local,
|
||||||
"roles" => User.roles(user),
|
"roles" => User.roles(user),
|
||||||
"tags" => user.tags || []
|
"tags" => user.tags || [],
|
||||||
|
"confirmation_pending" => user.confirmation_pending
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -238,7 +238,7 @@ def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, [])
|
||||||
@doc "GET /api/v1/accounts/:id"
|
@doc "GET /api/v1/accounts/:id"
|
||||||
def show(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do
|
def show(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user),
|
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user),
|
||||||
true <- User.auth_active?(user) || user.id == for_user.id || User.superuser?(for_user) do
|
true <- User.visible_for?(user, for_user) do
|
||||||
render(conn, "show.json", user: user, for: for_user)
|
render(conn, "show.json", user: user, for: for_user)
|
||||||
else
|
else
|
||||||
_e -> render_error(conn, :not_found, "Can't find user")
|
_e -> render_error(conn, :not_found, "Can't find user")
|
||||||
|
|
|
@ -180,6 +180,9 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
get("/instances/:instance/statuses", AdminAPIController, :list_instance_statuses)
|
get("/instances/:instance/statuses", AdminAPIController, :list_instance_statuses)
|
||||||
|
|
||||||
|
patch("/users/confirm_email", AdminAPIController, :confirm_email)
|
||||||
|
patch("/users/resend_confirmation_email", AdminAPIController, :resend_confirmation_email)
|
||||||
|
|
||||||
get("/reports", AdminAPIController, :list_reports)
|
get("/reports", AdminAPIController, :list_reports)
|
||||||
get("/grouped_reports", AdminAPIController, :list_grouped_reports)
|
get("/grouped_reports", AdminAPIController, :list_grouped_reports)
|
||||||
get("/reports/:id", AdminAPIController, :report_show)
|
get("/reports/:id", AdminAPIController, :report_show)
|
||||||
|
|
22
mix.exs
22
mix.exs
|
@ -194,25 +194,19 @@ defp version(version) do
|
||||||
identifier_filter = ~r/[^0-9a-z\-]+/i
|
identifier_filter = ~r/[^0-9a-z\-]+/i
|
||||||
|
|
||||||
# Pre-release version, denoted from patch version with a hyphen
|
# Pre-release version, denoted from patch version with a hyphen
|
||||||
{git_tag, git_pre_release} =
|
git_pre_release =
|
||||||
with {tag, 0} <-
|
with {tag, 0} <-
|
||||||
System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true),
|
System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true),
|
||||||
tag = String.trim(tag),
|
{describe, 0} <- System.cmd("git", ["describe", "--tags", "--abbrev=8"]) do
|
||||||
{describe, 0} <- System.cmd("git", ["describe", "--tags", "--abbrev=8"]),
|
describe
|
||||||
describe = String.trim(describe),
|
|> String.trim()
|
||||||
ahead <- String.replace(describe, tag, ""),
|
|> String.replace(String.trim(tag), "")
|
||||||
ahead <- String.trim_leading(ahead, "-") do
|
|> String.trim_leading("-")
|
||||||
{String.replace_prefix(tag, "v", ""), if(ahead != "", do: String.trim(ahead))}
|
|> String.trim()
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
{commit_hash, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
|
{commit_hash, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
|
||||||
{nil, "0-g" <> String.trim(commit_hash)}
|
"0-g" <> String.trim(commit_hash)
|
||||||
end
|
|
||||||
|
|
||||||
if git_tag && version != git_tag do
|
|
||||||
Mix.shell().error(
|
|
||||||
"Application version #{inspect(version)} does not match git tag #{inspect(git_tag)}"
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Branch name as pre-release version component, denoted with a dot
|
# Branch name as pre-release version component, denoted with a dot
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.SetVisibleServiceActors do
|
||||||
|
use Ecto.Migration
|
||||||
|
import Ecto.Query
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
def up do
|
||||||
|
user_nicknames = ["relay", "internal.fetch"]
|
||||||
|
|
||||||
|
from(
|
||||||
|
u in "users",
|
||||||
|
where: u.nickname in ^user_nicknames,
|
||||||
|
update: [
|
||||||
|
set: [invisible: true]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|> Repo.update_all([])
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,5 +8,5 @@
|
||||||
# fi
|
# fi
|
||||||
|
|
||||||
# Set the release to work across nodes
|
# Set the release to work across nodes
|
||||||
export RELEASE_DISTRIBUTION=name
|
export RELEASE_DISTRIBUTION="${RELEASE_DISTRIBUTION:-name}"
|
||||||
export RELEASE_NODE=<%= @release.name %>@127.0.0.1
|
export RELEASE_NODE="${RELEASE_NODE:-<%= @release.name %>@127.0.0.1}"
|
||||||
|
|
47
test/following_relationship_test.exs
Normal file
47
test/following_relationship_test.exs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.FollowingRelationshipTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
alias Pleroma.FollowingRelationship
|
||||||
|
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||||
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "following/1" do
|
||||||
|
test "returns following addresses without internal.fetch" do
|
||||||
|
user = insert(:user)
|
||||||
|
fetch_actor = InternalFetchActor.get_actor()
|
||||||
|
FollowingRelationship.follow(fetch_actor, user, "accept")
|
||||||
|
assert FollowingRelationship.following(fetch_actor) == [user.follower_address]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns following addresses without relay" do
|
||||||
|
user = insert(:user)
|
||||||
|
relay_actor = Relay.get_actor()
|
||||||
|
FollowingRelationship.follow(relay_actor, user, "accept")
|
||||||
|
assert FollowingRelationship.following(relay_actor) == [user.follower_address]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns following addresses without remote user" do
|
||||||
|
user = insert(:user)
|
||||||
|
actor = insert(:user, local: false)
|
||||||
|
FollowingRelationship.follow(actor, user, "accept")
|
||||||
|
assert FollowingRelationship.following(actor) == [user.follower_address]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns following addresses with local user" do
|
||||||
|
user = insert(:user)
|
||||||
|
actor = insert(:user, local: true)
|
||||||
|
FollowingRelationship.follow(actor, user, "accept")
|
||||||
|
|
||||||
|
assert FollowingRelationship.following(actor) == [
|
||||||
|
actor.follower_address,
|
||||||
|
user.follower_address
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -15,6 +15,14 @@ defmodule Pleroma.UserSearchTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "User.search" do
|
describe "User.search" do
|
||||||
|
test "excluded invisible users from results" do
|
||||||
|
user = insert(:user, %{nickname: "john t1000"})
|
||||||
|
insert(:user, %{invisible: true, nickname: "john t800"})
|
||||||
|
|
||||||
|
[found_user] = User.search("john")
|
||||||
|
assert found_user.id == user.id
|
||||||
|
end
|
||||||
|
|
||||||
test "accepts limit parameter" do
|
test "accepts limit parameter" do
|
||||||
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
|
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
|
||||||
assert length(User.search("john", limit: 3)) == 3
|
assert length(User.search("john", limit: 3)) == 3
|
||||||
|
|
|
@ -25,6 +25,25 @@ defmodule Pleroma.UserTest do
|
||||||
|
|
||||||
clear_config([:instance, :account_activation_required])
|
clear_config([:instance, :account_activation_required])
|
||||||
|
|
||||||
|
describe "service actors" do
|
||||||
|
test "returns invisible actor" do
|
||||||
|
uri = "#{Pleroma.Web.Endpoint.url()}/internal/fetch-test"
|
||||||
|
followers_uri = "#{uri}/followers"
|
||||||
|
user = User.get_or_create_service_actor_by_ap_id(uri, "internal.fetch-test")
|
||||||
|
|
||||||
|
assert %User{
|
||||||
|
nickname: "internal.fetch-test",
|
||||||
|
invisible: true,
|
||||||
|
local: true,
|
||||||
|
ap_id: ^uri,
|
||||||
|
follower_address: ^followers_uri
|
||||||
|
} = user
|
||||||
|
|
||||||
|
user2 = User.get_or_create_service_actor_by_ap_id(uri, "internal.fetch-test")
|
||||||
|
assert user.id == user2.id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "when tags are nil" do
|
describe "when tags are nil" do
|
||||||
test "tagging a user" do
|
test "tagging a user" do
|
||||||
user = insert(:user, %{tags: nil})
|
user = insert(:user, %{tags: nil})
|
||||||
|
@ -148,9 +167,10 @@ test "follow takes a user and another user" do
|
||||||
{:ok, user} = User.follow(user, followed)
|
{:ok, user} = User.follow(user, followed)
|
||||||
|
|
||||||
user = User.get_cached_by_id(user.id)
|
user = User.get_cached_by_id(user.id)
|
||||||
|
|
||||||
followed = User.get_cached_by_ap_id(followed.ap_id)
|
followed = User.get_cached_by_ap_id(followed.ap_id)
|
||||||
|
|
||||||
assert followed.follower_count == 1
|
assert followed.follower_count == 1
|
||||||
|
assert user.following_count == 1
|
||||||
|
|
||||||
assert User.ap_followers(followed) in User.following(user)
|
assert User.ap_followers(followed) in User.following(user)
|
||||||
end
|
end
|
||||||
|
@ -952,12 +972,14 @@ test "hide a user from friends" do
|
||||||
user2 = insert(:user)
|
user2 = insert(:user)
|
||||||
|
|
||||||
{:ok, user2} = User.follow(user2, user)
|
{:ok, user2} = User.follow(user2, user)
|
||||||
|
assert user2.following_count == 1
|
||||||
assert User.following_count(user2) == 1
|
assert User.following_count(user2) == 1
|
||||||
|
|
||||||
{:ok, _user} = User.deactivate(user)
|
{:ok, _user} = User.deactivate(user)
|
||||||
|
|
||||||
info = User.get_cached_user_info(user2)
|
info = User.get_cached_user_info(user2)
|
||||||
|
|
||||||
|
assert refresh_record(user2).following_count == 0
|
||||||
assert info.following_count == 0
|
assert info.following_count == 0
|
||||||
assert User.following_count(user2) == 0
|
assert User.following_count(user2) == 0
|
||||||
assert [] = User.get_friends(user2)
|
assert [] = User.get_friends(user2)
|
||||||
|
|
|
@ -225,7 +225,8 @@ test "Show", %{conn: conn} do
|
||||||
"roles" => %{"admin" => false, "moderator" => false},
|
"roles" => %{"admin" => false, "moderator" => false},
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
|
|
||||||
assert expected == json_response(conn, 200)
|
assert expected == json_response(conn, 200)
|
||||||
|
@ -634,7 +635,8 @@ test "renders users array for the first page", %{conn: conn, admin: admin} do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(admin) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(admin) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(admin.name || admin.nickname)
|
"display_name" => HTML.strip_tags(admin.name || admin.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
"deactivated" => user.deactivated,
|
"deactivated" => user.deactivated,
|
||||||
|
@ -644,7 +646,8 @@ test "renders users array for the first page", %{conn: conn, admin: admin} do
|
||||||
"local" => false,
|
"local" => false,
|
||||||
"tags" => ["foo", "bar"],
|
"tags" => ["foo", "bar"],
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|> Enum.sort_by(& &1["nickname"])
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
@ -685,7 +688,8 @@ test "regular search", %{conn: conn} do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -709,7 +713,8 @@ test "search by domain", %{conn: conn} do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -733,7 +738,8 @@ test "search by full nickname", %{conn: conn} do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -757,7 +763,8 @@ test "search by display name", %{conn: conn} do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -781,7 +788,8 @@ test "search by email", %{conn: conn} do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -805,7 +813,8 @@ test "regular search with page size", %{conn: conn} do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -824,7 +833,8 @@ test "regular search with page size", %{conn: conn} do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(user2) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user2) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user2.name || user2.nickname)
|
"display_name" => HTML.strip_tags(user2.name || user2.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -853,7 +863,8 @@ test "only local users" do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -880,7 +891,8 @@ test "only local users with no query", %{admin: old_admin} do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
"deactivated" => admin.deactivated,
|
"deactivated" => admin.deactivated,
|
||||||
|
@ -890,7 +902,8 @@ test "only local users with no query", %{admin: old_admin} do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(admin) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(admin) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(admin.name || admin.nickname)
|
"display_name" => HTML.strip_tags(admin.name || admin.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
"deactivated" => false,
|
"deactivated" => false,
|
||||||
|
@ -900,7 +913,8 @@ test "only local users with no query", %{admin: old_admin} do
|
||||||
"roles" => %{"admin" => true, "moderator" => false},
|
"roles" => %{"admin" => true, "moderator" => false},
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(old_admin) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname)
|
"display_name" => HTML.strip_tags(old_admin.name || old_admin.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|> Enum.sort_by(& &1["nickname"])
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
@ -929,7 +943,8 @@ test "load only admins", %{conn: conn, admin: admin} do
|
||||||
"local" => admin.local,
|
"local" => admin.local,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(admin) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(admin) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(admin.name || admin.nickname)
|
"display_name" => HTML.strip_tags(admin.name || admin.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
"deactivated" => false,
|
"deactivated" => false,
|
||||||
|
@ -939,7 +954,8 @@ test "load only admins", %{conn: conn, admin: admin} do
|
||||||
"local" => second_admin.local,
|
"local" => second_admin.local,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(second_admin) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname)
|
"display_name" => HTML.strip_tags(second_admin.name || second_admin.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|> Enum.sort_by(& &1["nickname"])
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
@ -970,7 +986,8 @@ test "load only moderators", %{conn: conn} do
|
||||||
"local" => moderator.local,
|
"local" => moderator.local,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(moderator) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(moderator.name || moderator.nickname)
|
"display_name" => HTML.strip_tags(moderator.name || moderator.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -994,7 +1011,8 @@ test "load users with tags list", %{conn: conn} do
|
||||||
"local" => user1.local,
|
"local" => user1.local,
|
||||||
"tags" => ["first"],
|
"tags" => ["first"],
|
||||||
"avatar" => User.avatar_url(user1) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user1) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user1.name || user1.nickname)
|
"display_name" => HTML.strip_tags(user1.name || user1.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
"deactivated" => false,
|
"deactivated" => false,
|
||||||
|
@ -1004,7 +1022,8 @@ test "load users with tags list", %{conn: conn} do
|
||||||
"local" => user2.local,
|
"local" => user2.local,
|
||||||
"tags" => ["second"],
|
"tags" => ["second"],
|
||||||
"avatar" => User.avatar_url(user2) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user2) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user2.name || user2.nickname)
|
"display_name" => HTML.strip_tags(user2.name || user2.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|> Enum.sort_by(& &1["nickname"])
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
@ -1040,7 +1059,8 @@ test "it works with multiple filters" do
|
||||||
"local" => user.local,
|
"local" => user.local,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1066,7 +1086,8 @@ test "it omits relay user", %{admin: admin} do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(admin) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(admin) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(admin.name || admin.nickname)
|
"display_name" => HTML.strip_tags(admin.name || admin.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1135,7 +1156,8 @@ test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation" do
|
||||||
"local" => true,
|
"local" => true,
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
"display_name" => HTML.strip_tags(user.name || user.nickname)
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false
|
||||||
}
|
}
|
||||||
|
|
||||||
log_entry = Repo.one(ModerationLog)
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
@ -2876,6 +2898,68 @@ test "GET /instances/:instance/statuses" do
|
||||||
assert length(response) == 0
|
assert length(response) == 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "PATCH /confirm_email" do
|
||||||
|
setup %{conn: conn} do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
|
||||||
|
%{conn: assign(conn, :user, admin), admin: admin}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it confirms emails of two users", %{admin: admin} do
|
||||||
|
[first_user, second_user] = insert_pair(:user, confirmation_pending: true)
|
||||||
|
|
||||||
|
assert first_user.confirmation_pending == true
|
||||||
|
assert second_user.confirmation_pending == true
|
||||||
|
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> patch("/api/pleroma/admin/users/confirm_email", %{
|
||||||
|
nicknames: [
|
||||||
|
first_user.nickname,
|
||||||
|
second_user.nickname
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert first_user.confirmation_pending == true
|
||||||
|
assert second_user.confirmation_pending == true
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} confirmed email for users: @#{first_user.nickname}, @#{
|
||||||
|
second_user.nickname
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PATCH /resend_confirmation_email" do
|
||||||
|
setup %{conn: conn} do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
|
||||||
|
%{conn: assign(conn, :user, admin), admin: admin}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it resend emails for two users", %{admin: admin} do
|
||||||
|
[first_user, second_user] = insert_pair(:user, confirmation_pending: true)
|
||||||
|
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> patch("/api/pleroma/admin/users/resend_confirmation_email", %{
|
||||||
|
nicknames: [
|
||||||
|
first_user.nickname,
|
||||||
|
second_user.nickname
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} re-sent confirmation email for users: @#{first_user.nickname}, @#{
|
||||||
|
second_user.nickname
|
||||||
|
}"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Needed for testing
|
# Needed for testing
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
|
||||||
|
@ -118,6 +119,28 @@ test "accounts fetches correct account for nicknames beginning with numbers", %{
|
||||||
refute acc_one == acc_two
|
refute acc_one == acc_two
|
||||||
assert acc_two == acc_three
|
assert acc_two == acc_three
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns 404 when user is invisible", %{conn: conn} do
|
||||||
|
user = insert(:user, %{invisible: true})
|
||||||
|
|
||||||
|
resp =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/accounts/#{user.nickname}")
|
||||||
|
|> json_response(404)
|
||||||
|
|
||||||
|
assert %{"error" => "Can't find user"} = resp
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 404 for internal.fetch actor", %{conn: conn} do
|
||||||
|
%User{nickname: "internal.fetch"} = InternalFetchActor.get_actor()
|
||||||
|
|
||||||
|
resp =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/accounts/internal.fetch")
|
||||||
|
|> json_response(404)
|
||||||
|
|
||||||
|
assert %{"error" => "Can't find user"} = resp
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "user timelines" do
|
describe "user timelines" do
|
||||||
|
|
Loading…
Reference in a new issue