forked from AkkomaGang/akkoma
Merge remote-tracking branch 'remotes/origin/develop' into 1427-oauth-admin-scopes
This commit is contained in:
commit
13926537b6
75 changed files with 1493 additions and 390 deletions
|
@ -31,6 +31,7 @@ build:
|
||||||
|
|
||||||
benchmark:
|
benchmark:
|
||||||
stage: benchmark
|
stage: benchmark
|
||||||
|
when: manual
|
||||||
variables:
|
variables:
|
||||||
MIX_ENV: benchmark
|
MIX_ENV: benchmark
|
||||||
services:
|
services:
|
||||||
|
@ -55,6 +56,19 @@ unit-testing:
|
||||||
- mix ecto.migrate
|
- mix ecto.migrate
|
||||||
- mix coveralls --preload-modules
|
- 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:
|
unit-testing-rum:
|
||||||
stage: test
|
stage: test
|
||||||
services:
|
services:
|
||||||
|
|
|
@ -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.
|
||||||
- OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`).
|
- OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`).
|
||||||
|
@ -66,10 +67,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body)
|
- `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body)
|
||||||
- Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
|
- Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
|
||||||
- Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read
|
- Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read
|
||||||
|
- ActivityPub: Support `Move` activities
|
||||||
- Mastodon API: Add `/api/v1/markers` for managing timeline read markers
|
- Mastodon API: Add `/api/v1/markers` for managing timeline read markers
|
||||||
- Mastodon API: Add the `recipients` parameter to `GET /api/v1/conversations`
|
- Mastodon API: Add the `recipients` parameter to `GET /api/v1/conversations`
|
||||||
- Configuration: `feed` option for user atom feed.
|
- Configuration: `feed` option for user atom feed.
|
||||||
- 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: `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
|
||||||
|
@ -80,6 +84,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
- Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`)
|
- Mastodon API: Fix private and direct statuses not being filtered out from the public timeline for an authenticated user (`GET /api/v1/timelines/public`)
|
||||||
- Mastodon API: Inability to get some local users by nickname in `/api/v1/accounts/:id_or_nickname`
|
- Mastodon API: Inability to get some local users by nickname in `/api/v1/accounts/:id_or_nickname`
|
||||||
|
- Admin API: Error when trying to update reports in the "old" format
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## [1.1.6] - 2019-11-19
|
## [1.1.6] - 2019-11-19
|
||||||
|
|
|
@ -95,7 +95,36 @@ def query_timelines(user) do
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity
|
as: :activity
|
||||||
})
|
})
|
||||||
end
|
end,
|
||||||
|
"Rendering favorites timeline" => fn ->
|
||||||
|
conn = Phoenix.ConnTest.build_conn(:get, "http://localhost:4001/api/v1/favourites", nil)
|
||||||
|
Pleroma.Web.MastodonAPI.StatusController.favourites(
|
||||||
|
%Plug.Conn{conn |
|
||||||
|
assigns: %{user: user},
|
||||||
|
query_params: %{"limit" => "0"},
|
||||||
|
body_params: %{},
|
||||||
|
cookies: %{},
|
||||||
|
params: %{},
|
||||||
|
path_params: %{},
|
||||||
|
private: %{
|
||||||
|
Pleroma.Web.Router => {[], %{}},
|
||||||
|
phoenix_router: Pleroma.Web.Router,
|
||||||
|
phoenix_action: :favourites,
|
||||||
|
phoenix_controller: Pleroma.Web.MastodonAPI.StatusController,
|
||||||
|
phoenix_endpoint: Pleroma.Web.Endpoint,
|
||||||
|
phoenix_format: "json",
|
||||||
|
phoenix_layout: {Pleroma.Web.LayoutView, "app.html"},
|
||||||
|
phoenix_recycled: true,
|
||||||
|
|
||||||
|
phoenix_view: Pleroma.Web.MastodonAPI.StatusView,
|
||||||
|
plug_session: %{"user_id" => user.id},
|
||||||
|
plug_session_fetch: :done,
|
||||||
|
plug_session_info: :write,
|
||||||
|
plug_skip_csrf_protection: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
%{})
|
||||||
|
end,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,24 @@ defmodule Pleroma.LoadTesting.Generator do
|
||||||
use Pleroma.LoadTesting.Helper
|
use Pleroma.LoadTesting.Helper
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
def generate_like_activities(user, posts) do
|
||||||
|
count_likes = Kernel.trunc(length(posts) / 4)
|
||||||
|
IO.puts("Starting generating #{count_likes} like activities...")
|
||||||
|
|
||||||
|
{time, _} =
|
||||||
|
:timer.tc(fn ->
|
||||||
|
Task.async_stream(
|
||||||
|
Enum.take_random(posts, count_likes),
|
||||||
|
fn post -> {:ok, _, _} = CommonAPI.favorite(post.id, user) end,
|
||||||
|
max_concurrency: 10,
|
||||||
|
timeout: 30_000
|
||||||
|
)
|
||||||
|
|> Stream.run()
|
||||||
|
end)
|
||||||
|
|
||||||
|
IO.puts("Inserting like activities take #{to_sec(time)} sec.\n")
|
||||||
|
end
|
||||||
|
|
||||||
def generate_users(opts) do
|
def generate_users(opts) do
|
||||||
IO.puts("Starting generating #{opts[:users_max]} users...")
|
IO.puts("Starting generating #{opts[:users_max]} users...")
|
||||||
{time, _} = :timer.tc(fn -> do_generate_users(opts) end)
|
{time, _} = :timer.tc(fn -> do_generate_users(opts) end)
|
||||||
|
@ -31,7 +49,6 @@ defp generate_user_data(i) do
|
||||||
password_hash:
|
password_hash:
|
||||||
"$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg",
|
"$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg",
|
||||||
bio: "Tester Number #{i}",
|
bio: "Tester Number #{i}",
|
||||||
info: %{},
|
|
||||||
local: remote
|
local: remote
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,10 @@ def run(args) do
|
||||||
|
|
||||||
generate_remote_activities(user, remote_users)
|
generate_remote_activities(user, remote_users)
|
||||||
|
|
||||||
|
generate_like_activities(
|
||||||
|
user, Pleroma.Repo.all(Pleroma.Activity.Queries.by_type("Create"))
|
||||||
|
)
|
||||||
|
|
||||||
generate_dms(user, users, opts)
|
generate_dms(user, users, opts)
|
||||||
|
|
||||||
{:ok, activity} = generate_long_thread(user, users, opts)
|
{:ok, activity} = generate_long_thread(user, users, opts)
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -208,6 +209,7 @@
|
||||||
config :pleroma, :http,
|
config :pleroma, :http,
|
||||||
proxy_url: nil,
|
proxy_url: nil,
|
||||||
send_user_agent: true,
|
send_user_agent: true,
|
||||||
|
user_agent: :default,
|
||||||
adapter: [
|
adapter: [
|
||||||
ssl_options: [
|
ssl_options: [
|
||||||
# Workaround for remote server certificate chain issues
|
# Workaround for remote server certificate chain issues
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
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
|
||||||
|
config :logger, :ex_syslogger, 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
|
||||||
|
|
|
@ -57,6 +57,7 @@ Has these additional fields under the `pleroma` object:
|
||||||
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
|
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
|
||||||
- `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
|
- `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
|
||||||
- `deactivated`: boolean, true when the user is deactivated
|
- `deactivated`: boolean, true when the user is deactivated
|
||||||
|
- `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts
|
||||||
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
|
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
|
||||||
|
|
||||||
### Source
|
### Source
|
||||||
|
@ -91,6 +92,12 @@ Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
- `is_seen`: true if the notification was read by the user
|
- `is_seen`: true if the notification was read by the user
|
||||||
|
|
||||||
|
### Move Notification
|
||||||
|
|
||||||
|
The `type` value is `move`. Has an additional field:
|
||||||
|
|
||||||
|
- `target`: new account
|
||||||
|
|
||||||
## GET `/api/v1/notifications`
|
## GET `/api/v1/notifications`
|
||||||
|
|
||||||
Accepts additional parameters:
|
Accepts additional parameters:
|
||||||
|
@ -136,6 +143,7 @@ Additional parameters can be added to the JSON body/Form data:
|
||||||
- `default_scope` - the scope returned under `privacy` key in Source subentity
|
- `default_scope` - the scope returned under `privacy` key in Source subentity
|
||||||
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
|
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
|
||||||
- `skip_thread_containment` - if true, skip filtering out broken threads
|
- `skip_thread_containment` - if true, skip filtering out broken threads
|
||||||
|
- `allow_following_move` - if true, allows automatically follow moved following accounts
|
||||||
- `pleroma_background_image` - sets the background image of the user.
|
- `pleroma_background_image` - sets the background image of the user.
|
||||||
|
|
||||||
### Pleroma Settings Store
|
### Pleroma Settings Store
|
||||||
|
|
|
@ -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>]
|
||||||
|
|
|
@ -348,7 +348,17 @@ Available caches:
|
||||||
* `:activity_pub` - activity pub routes (except question activities). Defaults to `nil` (no expiration).
|
* `:activity_pub` - activity pub routes (except question activities). Defaults to `nil` (no expiration).
|
||||||
* `:activity_pub_question` - activity pub routes (question activities). Defaults to `30_000` (30 seconds).
|
* `:activity_pub_question` - activity pub routes (question activities). Defaults to `30_000` (30 seconds).
|
||||||
|
|
||||||
## :hackney_pools
|
## HTTP client
|
||||||
|
|
||||||
|
### :http
|
||||||
|
|
||||||
|
* `proxy_url`: an upstream proxy to fetch posts and/or media with, (default: `nil`)
|
||||||
|
* `send_user_agent`: should we include a user agent with HTTP requests? (default: `true`)
|
||||||
|
* `user_agent`: what user agent should we use? (default: `:default`), must be string or `:default`
|
||||||
|
* `adapter`: array of hackney options
|
||||||
|
|
||||||
|
|
||||||
|
### :hackney_pools
|
||||||
|
|
||||||
Advanced. Tweaks Hackney (http client) connections pools.
|
Advanced. Tweaks Hackney (http client) connections pools.
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -362,6 +362,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
|
||||||
|
|
|
@ -28,7 +28,8 @@ defmodule Pleroma.Activity do
|
||||||
"Create" => "mention",
|
"Create" => "mention",
|
||||||
"Follow" => "follow",
|
"Follow" => "follow",
|
||||||
"Announce" => "reblog",
|
"Announce" => "reblog",
|
||||||
"Like" => "favourite"
|
"Like" => "favourite",
|
||||||
|
"Move" => "move"
|
||||||
}
|
}
|
||||||
|
|
||||||
@mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types,
|
@mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types,
|
||||||
|
@ -303,4 +304,17 @@ def restrict_deactivated_users(query) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
|
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
|
end
|
||||||
|
|
|
@ -17,8 +17,14 @@ def named_version, do: @name <> " " <> @version
|
||||||
def repository, do: @repository
|
def repository, do: @repository
|
||||||
|
|
||||||
def user_agent do
|
def user_agent do
|
||||||
info = "#{Pleroma.Web.base_url()} <#{Pleroma.Config.get([:instance, :email], "")}>"
|
case Pleroma.Config.get([:http, :user_agent], :default) do
|
||||||
named_version() <> "; " <> info
|
:default ->
|
||||||
|
info = "#{Pleroma.Web.base_url()} <#{Pleroma.Config.get([:instance, :email], "")}>"
|
||||||
|
named_version() <> "; " <> info
|
||||||
|
|
||||||
|
custom ->
|
||||||
|
custom
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# See http://elixir-lang.org/docs/stable/elixir/Application.html
|
# See http://elixir-lang.org/docs/stable/elixir/Application.html
|
||||||
|
|
|
@ -101,10 +101,28 @@ 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]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def move_following(origin, target) do
|
||||||
|
__MODULE__
|
||||||
|
|> join(:inner, [r], f in assoc(r, :follower))
|
||||||
|
|> where(following_id: ^origin.id)
|
||||||
|
|> where([r, f], f.allow_following_move == true)
|
||||||
|
|> limit(50)
|
||||||
|
|> preload([:follower])
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.map(fn following_relationship ->
|
||||||
|
Repo.delete(following_relationship)
|
||||||
|
Pleroma.Web.CommonAPI.follow(following_relationship.follower, target)
|
||||||
|
end)
|
||||||
|
|> case do
|
||||||
|
[] -> :ok
|
||||||
|
_ -> move_following(origin, target)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -251,10 +251,13 @@ def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = act
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_notifications(%Activity{data: %{"to" => _, "type" => type}} = activity)
|
def create_notifications(%Activity{data: %{"type" => type}} = activity)
|
||||||
when type in ["Like", "Announce", "Follow"] do
|
when type in ["Like", "Announce", "Follow", "Move"] do
|
||||||
users = get_notified_from_activity(activity)
|
notifications =
|
||||||
notifications = Enum.map(users, fn user -> create_notification(activity, user) end)
|
activity
|
||||||
|
|> get_notified_from_activity()
|
||||||
|
|> Enum.map(&create_notification(activity, &1))
|
||||||
|
|
||||||
{:ok, notifications}
|
{:ok, notifications}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -276,19 +279,15 @@ def create_notification(%Activity{} = activity, %User{} = user) do
|
||||||
|
|
||||||
def get_notified_from_activity(activity, local_only \\ true)
|
def get_notified_from_activity(activity, local_only \\ true)
|
||||||
|
|
||||||
def get_notified_from_activity(
|
def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only)
|
||||||
%Activity{data: %{"to" => _, "type" => type} = _data} = activity,
|
when type in ["Create", "Like", "Announce", "Follow", "Move"] do
|
||||||
local_only
|
[]
|
||||||
)
|
|> Utils.maybe_notify_to_recipients(activity)
|
||||||
when type in ["Create", "Like", "Announce", "Follow"] do
|
|> Utils.maybe_notify_mentioned_recipients(activity)
|
||||||
recipients =
|
|> Utils.maybe_notify_subscribers(activity)
|
||||||
[]
|
|> Utils.maybe_notify_followers(activity)
|
||||||
|> Utils.maybe_notify_to_recipients(activity)
|
|> Enum.uniq()
|
||||||
|> Utils.maybe_notify_mentioned_recipients(activity)
|
|> User.get_users_from_set(local_only)
|
||||||
|> Utils.maybe_notify_subscribers(activity)
|
|
||||||
|> Enum.uniq()
|
|
||||||
|
|
||||||
User.get_users_from_set(recipients, local_only)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_notified_from_activity(_, _local_only), do: []
|
def get_notified_from_activity(_, _local_only), do: []
|
||||||
|
|
|
@ -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))}")
|
||||||
|
@ -255,4 +255,8 @@ def update_data(%Object{data: data} = object, attrs \\ %{}) do
|
||||||
|> Object.change(%{data: Map.merge(data || %{}, attrs)})
|
|> Object.change(%{data: Map.merge(data || %{}, attrs)})
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def local?(%Object{data: %{"id" => id}}) do
|
||||||
|
String.starts_with?(id, Pleroma.Web.base_url() <> "/")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,7 +49,7 @@ defp reinject_object(struct, data) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def refetch_object(%Object{data: %{"id" => id}} = object) do
|
def refetch_object(%Object{data: %{"id" => id}} = object) do
|
||||||
with {:local, false} <- {:local, String.starts_with?(id, Pleroma.Web.base_url() <> "/")},
|
with {:local, false} <- {:local, Object.local?(object)},
|
||||||
{:ok, data} <- fetch_and_contain_remote_object_from_id(id),
|
{:ok, data} <- fetch_and_contain_remote_object_from_id(id),
|
||||||
{:ok, object} <- reinject_object(object, data) do
|
{:ok, object} <- reinject_object(object, data) do
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -104,7 +103,9 @@ defmodule Pleroma.User do
|
||||||
field(:raw_fields, {:array, :map}, default: [])
|
field(:raw_fields, {:array, :map}, default: [])
|
||||||
field(:discoverable, :boolean, default: false)
|
field(:discoverable, :boolean, default: false)
|
||||||
field(:invisible, :boolean, default: false)
|
field(:invisible, :boolean, default: false)
|
||||||
|
field(:allow_following_move, :boolean, default: true)
|
||||||
field(:skip_thread_containment, :boolean, default: false)
|
field(:skip_thread_containment, :boolean, default: false)
|
||||||
|
field(:also_known_as, {:array, :string}, default: [])
|
||||||
|
|
||||||
field(:notification_settings, :map,
|
field(:notification_settings, :map,
|
||||||
default: %{
|
default: %{
|
||||||
|
@ -119,8 +120,6 @@ defmodule Pleroma.User do
|
||||||
has_many(:registrations, Registration)
|
has_many(:registrations, Registration)
|
||||||
has_many(:deliveries, Delivery)
|
has_many(:deliveries, Delivery)
|
||||||
|
|
||||||
field(:info, :map, default: %{})
|
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -176,22 +177,6 @@ def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
|
||||||
def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
|
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
|
|
||||||
following_count =
|
|
||||||
Map.get(args, :following_count, user.following_count || following_count(user))
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|> Map.put(:following_count, following_count)
|
|
||||||
|> Map.put(:follower_count, follower_count)
|
|
||||||
end
|
|
||||||
|
|
||||||
def follow_state(%User{} = user, %User{} = target) do
|
def follow_state(%User{} = user, %User{} = target) do
|
||||||
case Utils.fetch_latest_follow(user, target) do
|
case Utils.fetch_latest_follow(user, target) do
|
||||||
%{data: %{"state" => state}} -> state
|
%{data: %{"state" => state}} -> state
|
||||||
|
@ -210,10 +195,6 @@ def set_follow_state_cache(user_ap_id, target_ap_id, state) do
|
||||||
Cachex.put(:user_cache, "follow_state:#{user_ap_id}|#{target_ap_id}", state)
|
Cachex.put(:user_cache, "follow_state:#{user_ap_id}|#{target_ap_id}", state)
|
||||||
end
|
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()
|
@spec restrict_deactivated(Ecto.Query.t()) :: Ecto.Query.t()
|
||||||
def restrict_deactivated(query) do
|
def restrict_deactivated(query) do
|
||||||
from(u in query, where: u.deactivated != ^true)
|
from(u in query, where: u.deactivated != ^true)
|
||||||
|
@ -244,7 +225,6 @@ def remote_user_creation(params) do
|
||||||
|
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.put(:info, params[:info] || %{})
|
|
||||||
|> truncate_if_exists(:name, name_limit)
|
|> truncate_if_exists(:name, name_limit)
|
||||||
|> truncate_if_exists(:bio, bio_limit)
|
|> truncate_if_exists(:bio, bio_limit)
|
||||||
|> truncate_fields_param()
|
|> truncate_fields_param()
|
||||||
|
@ -273,7 +253,8 @@ def remote_user_creation(params) do
|
||||||
:fields,
|
:fields,
|
||||||
:following_count,
|
:following_count,
|
||||||
:discoverable,
|
:discoverable,
|
||||||
:invisible
|
:invisible,
|
||||||
|
:also_known_as
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|> validate_required([:name, :ap_id])
|
|> validate_required([:name, :ap_id])
|
||||||
|
@ -315,13 +296,15 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
:hide_followers_count,
|
:hide_followers_count,
|
||||||
:hide_follows_count,
|
:hide_follows_count,
|
||||||
:hide_favorites,
|
:hide_favorites,
|
||||||
|
:allow_following_move,
|
||||||
:background,
|
:background,
|
||||||
:show_role,
|
:show_role,
|
||||||
:skip_thread_containment,
|
:skip_thread_containment,
|
||||||
:fields,
|
:fields,
|
||||||
:raw_fields,
|
:raw_fields,
|
||||||
:pleroma_settings_store,
|
:pleroma_settings_store,
|
||||||
:discoverable
|
:discoverable,
|
||||||
|
:also_known_as
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|> unique_constraint(:nickname)
|
|> unique_constraint(:nickname)
|
||||||
|
@ -359,9 +342,11 @@ def upgrade_changeset(struct, params \\ %{}, remote? \\ false) do
|
||||||
:hide_follows,
|
:hide_follows,
|
||||||
:fields,
|
:fields,
|
||||||
:hide_followers,
|
:hide_followers,
|
||||||
|
:allow_following_move,
|
||||||
:discoverable,
|
:discoverable,
|
||||||
:hide_followers_count,
|
:hide_followers_count,
|
||||||
:hide_follows_count
|
:hide_follows_count,
|
||||||
|
:also_known_as
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|> unique_constraint(:nickname)
|
|> unique_constraint(:nickname)
|
||||||
|
@ -492,6 +477,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 +511,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 +533,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 +545,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
|
||||||
|
@ -615,7 +600,6 @@ def set_cache({:error, err}), do: {:error, err}
|
||||||
def set_cache(%User{} = user) do
|
def set_cache(%User{} = user) do
|
||||||
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
|
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
|
||||||
Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
|
Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
|
||||||
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user))
|
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -634,7 +618,6 @@ def update_and_set_cache(changeset) do
|
||||||
def invalidate_cache(user) do
|
def invalidate_cache(user) do
|
||||||
Cachex.del(:user_cache, "ap_id:#{user.ap_id}")
|
Cachex.del(:user_cache, "ap_id:#{user.ap_id}")
|
||||||
Cachex.del(:user_cache, "nickname:#{user.nickname}")
|
Cachex.del(:user_cache, "nickname:#{user.nickname}")
|
||||||
Cachex.del(:user_cache, "user_info:#{user.id}")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_cached_by_ap_id(ap_id) do
|
def get_cached_by_ap_id(ap_id) do
|
||||||
|
@ -702,11 +685,6 @@ def get_by_nickname_or_email(nickname_or_email) do
|
||||||
get_by_nickname(nickname_or_email) || get_by_email(nickname_or_email)
|
get_by_nickname(nickname_or_email) || get_by_email(nickname_or_email)
|
||||||
end
|
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 fetch_by_nickname(nickname), do: ActivityPub.make_user_from_nickname(nickname)
|
||||||
|
|
||||||
def get_or_fetch_by_nickname(nickname) do
|
def get_or_fetch_by_nickname(nickname) do
|
||||||
|
@ -895,8 +873,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 +882,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 +1081,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
|
||||||
|
@ -1226,7 +1215,7 @@ def external_users_query do
|
||||||
def external_users(opts \\ []) do
|
def external_users(opts \\ []) do
|
||||||
query =
|
query =
|
||||||
external_users_query()
|
external_users_query()
|
||||||
|> select([u], struct(u, [:id, :ap_id, :info]))
|
|> select([u], struct(u, [:id, :ap_id]))
|
||||||
|
|
||||||
query =
|
query =
|
||||||
if opts[:max_id],
|
if opts[:max_id],
|
||||||
|
@ -1317,22 +1306,23 @@ 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
|
{:ok, user} =
|
||||||
else
|
%User{
|
||||||
_ ->
|
invisible: true,
|
||||||
{:ok, user} =
|
local: true,
|
||||||
%User{}
|
ap_id: uri,
|
||||||
|> cast(%{}, [:ap_id, :nickname, :local])
|
nickname: nickname,
|
||||||
|> put_change(:ap_id, uri)
|
follower_address: uri <> "/followers"
|
||||||
|> put_change(:nickname, nickname)
|
}
|
||||||
|> put_change(:local, true)
|
|> Repo.insert()
|
||||||
|> put_change(:follower_address, uri <> "/followers")
|
|
||||||
|> Repo.insert()
|
|
||||||
|
|
||||||
user
|
user
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1575,6 +1565,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))
|
||||||
|
|
|
@ -541,6 +541,30 @@ def flag(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def move(%User{} = origin, %User{} = target, local \\ true) do
|
||||||
|
params = %{
|
||||||
|
"type" => "Move",
|
||||||
|
"actor" => origin.ap_id,
|
||||||
|
"object" => origin.ap_id,
|
||||||
|
"target" => target.ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
with true <- origin.ap_id in target.also_known_as,
|
||||||
|
{:ok, activity} <- insert(params, local) do
|
||||||
|
maybe_federate(activity)
|
||||||
|
|
||||||
|
BackgroundWorker.enqueue("move_following", %{
|
||||||
|
"origin_id" => origin.id,
|
||||||
|
"target_id" => target.id
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
false -> {:error, "Target account must have the origin in `alsoKnownAs`"}
|
||||||
|
err -> err
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp fetch_activities_for_context_query(context, opts) do
|
defp fetch_activities_for_context_query(context, opts) do
|
||||||
public = [Pleroma.Constants.as_public()]
|
public = [Pleroma.Constants.as_public()]
|
||||||
|
|
||||||
|
@ -734,6 +758,17 @@ def fetch_user_activities(user, reading_user, params \\ %{}) do
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def fetch_instance_activities(params) do
|
||||||
|
params =
|
||||||
|
params
|
||||||
|
|> Map.put("type", ["Create", "Announce"])
|
||||||
|
|> Map.put("instance", params["instance"])
|
||||||
|
|> Map.put("whole_db", true)
|
||||||
|
|
||||||
|
fetch_activities([Pleroma.Constants.as_public()], params, :offset)
|
||||||
|
|> Enum.reverse()
|
||||||
|
end
|
||||||
|
|
||||||
defp user_activities_recipients(%{"godmode" => true}) do
|
defp user_activities_recipients(%{"godmode" => true}) do
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
@ -961,6 +996,20 @@ defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do
|
||||||
|
|
||||||
defp restrict_muted_reblogs(query, _), do: query
|
defp restrict_muted_reblogs(query, _), do: query
|
||||||
|
|
||||||
|
defp restrict_instance(query, %{"instance" => instance}) do
|
||||||
|
users =
|
||||||
|
from(
|
||||||
|
u in User,
|
||||||
|
select: u.ap_id,
|
||||||
|
where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
|
||||||
|
)
|
||||||
|
|> Repo.all()
|
||||||
|
|
||||||
|
from(activity in query, where: activity.actor in ^users)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_instance(query, _), do: query
|
||||||
|
|
||||||
defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
|
defp exclude_poll_votes(query, %{"include_poll_votes" => true}), do: query
|
||||||
|
|
||||||
defp exclude_poll_votes(query, _) do
|
defp exclude_poll_votes(query, _) do
|
||||||
|
@ -1041,6 +1090,7 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
|> restrict_reblogs(opts)
|
|> restrict_reblogs(opts)
|
||||||
|> restrict_pinned(opts)
|
|> restrict_pinned(opts)
|
||||||
|> restrict_muted_reblogs(opts)
|
|> restrict_muted_reblogs(opts)
|
||||||
|
|> restrict_instance(opts)
|
||||||
|> Activity.restrict_deactivated_users()
|
|> Activity.restrict_deactivated_users()
|
||||||
|> exclude_poll_votes(opts)
|
|> exclude_poll_votes(opts)
|
||||||
|> exclude_visibility(opts)
|
|> exclude_visibility(opts)
|
||||||
|
@ -1145,7 +1195,8 @@ defp object_to_user_data(data) do
|
||||||
name: data["name"],
|
name: data["name"],
|
||||||
follower_address: data["followers"],
|
follower_address: data["followers"],
|
||||||
following_address: data["following"],
|
following_address: data["following"],
|
||||||
bio: data["summary"]
|
bio: data["summary"],
|
||||||
|
also_known_as: Map.get(data, "alsoKnownAs", [])
|
||||||
}
|
}
|
||||||
|
|
||||||
# nickname can be nil because of virtual actors
|
# nickname can be nil because of virtual actors
|
||||||
|
@ -1207,13 +1258,13 @@ defp maybe_update_follow_information(data) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp collection_private(data) do
|
defp collection_private(%{"first" => first}) do
|
||||||
if is_map(data["first"]) and
|
if is_map(first) and
|
||||||
data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
|
first["type"] in ["CollectionPage", "OrderedCollectionPage"] do
|
||||||
{:ok, false}
|
{:ok, false}
|
||||||
else
|
else
|
||||||
with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
|
with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
|
||||||
Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
|
Fetcher.fetch_and_contain_remote_object_from_id(first) do
|
||||||
{:ok, false}
|
{:ok, false}
|
||||||
else
|
else
|
||||||
{:error, {:ok, %{status: code}}} when code in [401, 403] ->
|
{:error, {:ok, %{status: code}}} when code in [401, 403] ->
|
||||||
|
@ -1228,6 +1279,8 @@ defp collection_private(data) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp collection_private(_data), do: {:ok, true}
|
||||||
|
|
||||||
def user_data_from_user_object(data) do
|
def user_data_from_user_object(data) do
|
||||||
with {:ok, data} <- MRF.filter(data),
|
with {:ok, data} <- MRF.filter(data),
|
||||||
{:ok, data} <- object_to_user_data(data) do
|
{:ok, data} <- object_to_user_data(data) do
|
||||||
|
|
|
@ -45,7 +45,7 @@ def relay_active?(conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def user(conn, %{"nickname" => nickname}) do
|
def user(conn, %{"nickname" => nickname}) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{local: true} = user <- User.get_cached_by_nickname(nickname),
|
||||||
{:ok, user} <- User.ensure_keys_present(user) do
|
{:ok, user} <- User.ensure_keys_present(user) do
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("application/activity+json")
|
|> put_resp_content_type("application/activity+json")
|
||||||
|
@ -53,6 +53,7 @@ def user(conn, %{"nickname" => nickname}) do
|
||||||
|> render("user.json", %{user: user})
|
|> render("user.json", %{user: user})
|
||||||
else
|
else
|
||||||
nil -> {:error, :not_found}
|
nil -> {:error, :not_found}
|
||||||
|
%{local: false} -> {:error, :not_found}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -669,7 +669,7 @@ def handle_incoming(
|
||||||
|
|
||||||
update_data =
|
update_data =
|
||||||
new_user_data
|
new_user_data
|
||||||
|> Map.take([:avatar, :banner, :bio, :name])
|
|> Map.take([:avatar, :banner, :bio, :name, :also_known_as])
|
||||||
|> Map.put(:fields, fields)
|
|> Map.put(:fields, fields)
|
||||||
|> Map.put(:locked, locked)
|
|> Map.put(:locked, locked)
|
||||||
|> Map.put(:invisible, invisible)
|
|> Map.put(:invisible, invisible)
|
||||||
|
@ -857,6 +857,24 @@ def handle_incoming(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_incoming(
|
||||||
|
%{
|
||||||
|
"type" => "Move",
|
||||||
|
"actor" => origin_actor,
|
||||||
|
"object" => origin_actor,
|
||||||
|
"target" => target_actor
|
||||||
|
},
|
||||||
|
_options
|
||||||
|
) do
|
||||||
|
with %User{} = origin_user <- User.get_cached_by_ap_id(origin_actor),
|
||||||
|
{:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_actor),
|
||||||
|
true <- origin_actor in target_user.also_known_as do
|
||||||
|
ActivityPub.move(origin_user, target_user, false)
|
||||||
|
else
|
||||||
|
_e -> :error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def handle_incoming(_, _), do: :error
|
def handle_incoming(_, _), do: :error
|
||||||
|
|
||||||
@spec get_obj_helper(String.t(), Keyword.t()) :: {:ok, Object.t()} | nil
|
@spec get_obj_helper(String.t(), Keyword.t()) :: {:ok, Object.t()} | nil
|
||||||
|
|
|
@ -903,7 +903,13 @@ def update_report_state(_, _), do: {:error, "Unsupported state"}
|
||||||
|
|
||||||
def strip_report_status_data(activity) do
|
def strip_report_status_data(activity) do
|
||||||
[actor | reported_activities] = activity.data["object"]
|
[actor | reported_activities] = activity.data["object"]
|
||||||
stripped_activities = Enum.map(reported_activities, & &1["id"])
|
|
||||||
|
stripped_activities =
|
||||||
|
Enum.map(reported_activities, fn
|
||||||
|
act when is_map(act) -> act["id"]
|
||||||
|
act when is_binary(act) -> act
|
||||||
|
end)
|
||||||
|
|
||||||
new_data = put_in(activity.data, ["object"], [actor | stripped_activities])
|
new_data = put_in(activity.data, ["object"], [actor | stripped_activities])
|
||||||
|
|
||||||
{:ok, %{activity | data: new_data}}
|
{:ok, %{activity | data: new_data}}
|
||||||
|
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
||||||
@spec is_public?(Object.t() | Activity.t() | map()) :: boolean()
|
@spec is_public?(Object.t() | Activity.t() | map()) :: boolean()
|
||||||
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
||||||
def is_public?(%Object{data: data}), do: is_public?(data)
|
def is_public?(%Object{data: data}), do: is_public?(data)
|
||||||
|
def is_public?(%Activity{data: %{"type" => "Move"}}), do: true
|
||||||
def is_public?(%Activity{data: data}), do: is_public?(data)
|
def is_public?(%Activity{data: data}), do: is_public?(data)
|
||||||
def is_public?(%{"directMessage" => true}), do: false
|
def is_public?(%{"directMessage" => true}), do: false
|
||||||
def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data)
|
def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data)
|
||||||
|
|
|
@ -229,6 +229,21 @@ def user_show(conn, %{"nickname" => nickname}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def list_instance_statuses(conn, %{"instance" => instance} = params) do
|
||||||
|
{page, page_size} = page_params(params)
|
||||||
|
|
||||||
|
activities =
|
||||||
|
ActivityPub.fetch_instance_activities(%{
|
||||||
|
"instance" => instance,
|
||||||
|
"limit" => page_size,
|
||||||
|
"offset" => (page - 1) * page_size
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(StatusView)
|
||||||
|
|> render("index.json", %{activities: activities, as: :activity})
|
||||||
|
end
|
||||||
|
|
||||||
def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
||||||
godmode = params["godmode"] == "true" || params["godmode"] == true
|
godmode = params["godmode"] == "true" || params["godmode"] == true
|
||||||
|
|
||||||
|
@ -337,7 +352,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(
|
||||||
|
@ -349,15 +364,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)
|
||||||
|
@ -801,6 +817,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
|
||||||
|
|
||||||
|
|
|
@ -451,6 +451,8 @@ def maybe_notify_to_recipients(
|
||||||
recipients ++ to
|
recipients ++ to
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def maybe_notify_to_recipients(recipients, _), do: recipients
|
||||||
|
|
||||||
def maybe_notify_mentioned_recipients(
|
def maybe_notify_mentioned_recipients(
|
||||||
recipients,
|
recipients,
|
||||||
%Activity{data: %{"to" => _to, "type" => type} = data} = activity
|
%Activity{data: %{"to" => _to, "type" => type} = data} = activity
|
||||||
|
@ -502,6 +504,17 @@ def maybe_notify_subscribers(
|
||||||
|
|
||||||
def maybe_notify_subscribers(recipients, _), do: recipients
|
def maybe_notify_subscribers(recipients, _), do: recipients
|
||||||
|
|
||||||
|
def maybe_notify_followers(recipients, %Activity{data: %{"type" => "Move"}} = activity) do
|
||||||
|
with %User{} = user <- User.get_cached_by_ap_id(activity.actor) do
|
||||||
|
user
|
||||||
|
|> User.get_followers()
|
||||||
|
|> Enum.map(& &1.ap_id)
|
||||||
|
|> Enum.concat(recipients)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def maybe_notify_followers(recipients, _), do: recipients
|
||||||
|
|
||||||
def maybe_extract_mentions(%{"tag" => tag}) do
|
def maybe_extract_mentions(%{"tag" => tag}) do
|
||||||
tag
|
tag
|
||||||
|> Enum.filter(fn x -> is_map(x) && x["type"] == "Mention" end)
|
|> Enum.filter(fn x -> is_map(x) && x["type"] == "Mention" end)
|
||||||
|
|
|
@ -152,6 +152,7 @@ def update_credentials(%{assigns: %{user: original_user}} = conn, params) do
|
||||||
:hide_favorites,
|
:hide_favorites,
|
||||||
:show_role,
|
:show_role,
|
||||||
:skip_thread_containment,
|
:skip_thread_containment,
|
||||||
|
:allow_following_move,
|
||||||
:discoverable
|
:discoverable
|
||||||
]
|
]
|
||||||
|> Enum.reduce(%{}, fn key, acc ->
|
|> Enum.reduce(%{}, fn key, acc ->
|
||||||
|
@ -238,7 +239,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")
|
||||||
|
|
|
@ -71,18 +71,17 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
|
|
||||||
image = User.avatar_url(user) |> MediaProxy.url()
|
image = User.avatar_url(user) |> MediaProxy.url()
|
||||||
header = User.banner_url(user) |> MediaProxy.url()
|
header = User.banner_url(user) |> MediaProxy.url()
|
||||||
user_info = User.get_cached_user_info(user)
|
|
||||||
|
|
||||||
following_count =
|
following_count =
|
||||||
if !user.hide_follows_count or !user.hide_follows or opts[:for] == user do
|
if !user.hide_follows_count or !user.hide_follows or opts[:for] == user do
|
||||||
user_info.following_count
|
user.following_count || 0
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
end
|
end
|
||||||
|
|
||||||
followers_count =
|
followers_count =
|
||||||
if !user.hide_followers_count or !user.hide_followers or opts[:for] == user do
|
if !user.hide_followers_count or !user.hide_followers or opts[:for] == user do
|
||||||
user_info.follower_count
|
user.follower_count || 0
|
||||||
else
|
else
|
||||||
0
|
0
|
||||||
end
|
end
|
||||||
|
@ -144,7 +143,7 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
|
|
||||||
# Pleroma extension
|
# Pleroma extension
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
confirmation_pending: user_info.confirmation_pending,
|
confirmation_pending: user.confirmation_pending,
|
||||||
tags: user.tags,
|
tags: user.tags,
|
||||||
hide_followers_count: user.hide_followers_count,
|
hide_followers_count: user.hide_followers_count,
|
||||||
hide_follows_count: user.hide_follows_count,
|
hide_follows_count: user.hide_follows_count,
|
||||||
|
@ -157,12 +156,13 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> maybe_put_role(user, opts[:for])
|
|> 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_notification_settings(user, opts[:for])
|
||||||
|> maybe_put_settings_store(user, opts[:for], opts)
|
|> maybe_put_settings_store(user, opts[:for], opts)
|
||||||
|> maybe_put_chat_token(user, opts[:for], opts)
|
|> maybe_put_chat_token(user, opts[:for], opts)
|
||||||
|> maybe_put_activation_status(user, opts[:for])
|
|> maybe_put_activation_status(user, opts[:for])
|
||||||
|> maybe_put_follow_requests_count(user, opts[:for])
|
|> maybe_put_follow_requests_count(user, opts[:for])
|
||||||
|
|> maybe_put_allow_following_move(user, opts[:for])
|
||||||
|> maybe_put_unread_conversation_count(user, opts[:for])
|
|> maybe_put_unread_conversation_count(user, opts[:for])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ defp maybe_put_settings(
|
||||||
data,
|
data,
|
||||||
%User{id: user_id} = user,
|
%User{id: user_id} = user,
|
||||||
%User{id: user_id},
|
%User{id: user_id},
|
||||||
_user_info
|
_opts
|
||||||
) do
|
) do
|
||||||
data
|
data
|
||||||
|> Kernel.put_in([:source, :privacy], user.default_scope)
|
|> Kernel.put_in([:source, :privacy], user.default_scope)
|
||||||
|
@ -239,6 +239,12 @@ defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id:
|
||||||
|
|
||||||
defp maybe_put_notification_settings(data, _, _), do: data
|
defp maybe_put_notification_settings(data, _, _), do: data
|
||||||
|
|
||||||
|
defp maybe_put_allow_following_move(data, %User{id: user_id} = user, %User{id: user_id}) do
|
||||||
|
Kernel.put_in(data, [:pleroma, :allow_following_move], user.allow_following_move)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_put_allow_following_move(data, _, _), do: data
|
||||||
|
|
||||||
defp maybe_put_activation_status(data, user, %User{is_admin: true}) do
|
defp maybe_put_activation_status(data, user, %User{is_admin: true}) do
|
||||||
Kernel.put_in(data, [:pleroma, :deactivated], user.deactivated)
|
Kernel.put_in(data, [:pleroma, :deactivated], user.deactivated)
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,32 +37,24 @@ def render("show.json", %{
|
||||||
}
|
}
|
||||||
|
|
||||||
case mastodon_type do
|
case mastodon_type do
|
||||||
"mention" ->
|
"mention" -> put_status(response, activity, user)
|
||||||
response
|
"favourite" -> put_status(response, parent_activity, user)
|
||||||
|> Map.merge(%{
|
"reblog" -> put_status(response, parent_activity, user)
|
||||||
status: StatusView.render("show.json", %{activity: activity, for: user})
|
"move" -> put_target(response, activity, user)
|
||||||
})
|
"follow" -> response
|
||||||
|
_ -> nil
|
||||||
"favourite" ->
|
|
||||||
response
|
|
||||||
|> Map.merge(%{
|
|
||||||
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
|
||||||
})
|
|
||||||
|
|
||||||
"reblog" ->
|
|
||||||
response
|
|
||||||
|> Map.merge(%{
|
|
||||||
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
|
||||||
})
|
|
||||||
|
|
||||||
"follow" ->
|
|
||||||
response
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
nil
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
_ -> nil
|
_ -> nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp put_status(response, activity, user) do
|
||||||
|
Map.put(response, :status, StatusView.render("show.json", %{activity: activity, for: user}))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp put_target(response, activity, user) do
|
||||||
|
target = User.get_cached_by_ap_id(activity.data["target"])
|
||||||
|
Map.put(response, :target, AccountView.render("show.json", %{user: target, for: user}))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,8 +9,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.ActivityExpiration
|
alias Pleroma.ActivityExpiration
|
||||||
alias Pleroma.Conversation
|
|
||||||
alias Pleroma.Conversation.Participation
|
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -245,12 +243,8 @@ def render("show.json", %{activity: %{data: %{"object" => _object}} = activity}
|
||||||
direct_conversation_id =
|
direct_conversation_id =
|
||||||
with {_, nil} <- {:direct_conversation_id, opts[:direct_conversation_id]},
|
with {_, nil} <- {:direct_conversation_id, opts[:direct_conversation_id]},
|
||||||
{_, true} <- {:include_id, opts[:with_direct_conversation_id]},
|
{_, true} <- {:include_id, opts[:with_direct_conversation_id]},
|
||||||
{_, %User{} = for_user} <- {:for_user, opts[:for]},
|
{_, %User{} = for_user} <- {:for_user, opts[:for]} do
|
||||||
%{data: %{"context" => context}} when is_binary(context) <- activity,
|
Activity.direct_conversation_id(activity, for_user)
|
||||||
%Conversation{} = conversation <- Conversation.get_for_ap_id(context),
|
|
||||||
%Participation{id: participation_id} <-
|
|
||||||
Participation.for_user_and_conversation(for_user, conversation) do
|
|
||||||
participation_id
|
|
||||||
else
|
else
|
||||||
{:direct_conversation_id, participation_id} when is_integer(participation_id) ->
|
{:direct_conversation_id, participation_id} when is_integer(participation_id) ->
|
||||||
participation_id
|
participation_id
|
||||||
|
|
|
@ -11,7 +11,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPubController
|
alias Pleroma.Web.ActivityPub.ActivityPubController
|
||||||
alias Pleroma.Web.ActivityPub.ObjectView
|
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.Metadata.PlayerView
|
alias Pleroma.Web.Metadata.PlayerView
|
||||||
|
@ -38,11 +37,9 @@ def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
|
||||||
with id <- o_status_url(conn, :object, uuid),
|
with id <- o_status_url(conn, :object, uuid),
|
||||||
{_, %Activity{} = activity} <-
|
{_, %Activity{} = activity} <-
|
||||||
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
|
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
|
||||||
{_, true} <- {:public?, Visibility.is_public?(activity)},
|
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
|
||||||
case format do
|
case format do
|
||||||
"html" -> redirect(conn, to: "/notice/#{activity.id}")
|
_ -> redirect(conn, to: "/notice/#{activity.id}")
|
||||||
_ -> represent_activity(conn, nil, activity, user)
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||||
|
@ -61,11 +58,9 @@ def activity(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid})
|
||||||
def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
|
def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do
|
||||||
with id <- o_status_url(conn, :activity, uuid),
|
with id <- o_status_url(conn, :activity, uuid),
|
||||||
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
|
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
|
||||||
{_, true} <- {:public?, Visibility.is_public?(activity)},
|
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
|
||||||
case format do
|
case format do
|
||||||
"html" -> redirect(conn, to: "/notice/#{activity.id}")
|
_ -> redirect(conn, to: "/notice/#{activity.id}")
|
||||||
_ -> represent_activity(conn, format, activity, user)
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||||
|
@ -81,7 +76,15 @@ def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do
|
||||||
{_, true} <- {:public?, Visibility.is_public?(activity)},
|
{_, true} <- {:public?, Visibility.is_public?(activity)},
|
||||||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||||
cond do
|
cond do
|
||||||
format == "html" && activity.data["type"] == "Create" ->
|
format in ["json", "activity+json"] ->
|
||||||
|
if activity.local do
|
||||||
|
%{data: %{"id" => redirect_url}} = Object.normalize(activity)
|
||||||
|
redirect(conn, external: redirect_url)
|
||||||
|
else
|
||||||
|
{:error, :not_found}
|
||||||
|
end
|
||||||
|
|
||||||
|
activity.data["type"] == "Create" ->
|
||||||
%Object{} = object = Object.normalize(activity)
|
%Object{} = object = Object.normalize(activity)
|
||||||
|
|
||||||
RedirectController.redirector_with_meta(
|
RedirectController.redirector_with_meta(
|
||||||
|
@ -94,11 +97,8 @@ def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
format == "html" ->
|
|
||||||
RedirectController.redirector(conn, nil)
|
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
represent_activity(conn, format, activity, user)
|
RedirectController.redirector(conn, nil)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||||
|
@ -135,24 +135,6 @@ def notice_player(conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp represent_activity(
|
|
||||||
conn,
|
|
||||||
"activity+json",
|
|
||||||
%Activity{data: %{"type" => "Create"}} = activity,
|
|
||||||
_user
|
|
||||||
) do
|
|
||||||
object = Object.normalize(activity)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_resp_header("content-type", "application/activity+json")
|
|
||||||
|> put_view(ObjectView)
|
|
||||||
|> render("object.json", %{object: object})
|
|
||||||
end
|
|
||||||
|
|
||||||
defp represent_activity(_conn, _, _, _) do
|
|
||||||
{:error, :not_found}
|
|
||||||
end
|
|
||||||
|
|
||||||
def errors(conn, {:error, :not_found}) do
|
def errors(conn, {:error, :not_found}) do
|
||||||
render_error(conn, :not_found, "Not found")
|
render_error(conn, :not_found, "Not found")
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,7 +16,7 @@ defmodule Pleroma.Web.Push.Impl do
|
||||||
require Logger
|
require Logger
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
@types ["Create", "Follow", "Announce", "Like"]
|
@types ["Create", "Follow", "Announce", "Like", "Move"]
|
||||||
|
|
||||||
@doc "Performs sending notifications for user subscriptions"
|
@doc "Performs sending notifications for user subscriptions"
|
||||||
@spec perform(Notification.t()) :: list(any) | :error
|
@spec perform(Notification.t()) :: list(any) | :error
|
||||||
|
@ -33,6 +33,8 @@ def perform(
|
||||||
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
|
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
|
||||||
avatar_url = User.avatar_url(actor)
|
avatar_url = User.avatar_url(actor)
|
||||||
object = Object.normalize(activity)
|
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),
|
for subscription <- fetch_subsriptions(user_id),
|
||||||
get_in(subscription.data, ["alerts", type]) do
|
get_in(subscription.data, ["alerts", type]) do
|
||||||
|
@ -45,7 +47,8 @@ def perform(
|
||||||
icon: avatar_url,
|
icon: avatar_url,
|
||||||
preferred_locale: "en",
|
preferred_locale: "en",
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
activity_id: activity_id
|
activity_id: activity_id,
|
||||||
|
direct_conversation_id: direct_conversation_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> Jason.encode!()
|
|> Jason.encode!()
|
||||||
|
|
|
@ -178,6 +178,11 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/users/:nickname", AdminAPIController, :user_show)
|
get("/users/:nickname", AdminAPIController, :user_show)
|
||||||
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
|
get("/users/:nickname/statuses", AdminAPIController, :list_user_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)
|
||||||
|
|
|
@ -71,4 +71,11 @@ def perform(%{"op" => "fetch_data_for_activity", "activity_id" => activity_id},
|
||||||
activity = Activity.get_by_id(activity_id)
|
activity = Activity.get_by_id(activity_id)
|
||||||
Pleroma.Web.RichMedia.Helpers.perform(:fetch, activity)
|
Pleroma.Web.RichMedia.Helpers.perform(:fetch, activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def perform(%{"op" => "move_following", "origin_id" => origin_id, "target_id" => target_id}, _) do
|
||||||
|
origin = User.get_cached_by_id(origin_id)
|
||||||
|
target = User.get_cached_by_id(target_id)
|
||||||
|
|
||||||
|
Pleroma.FollowingRelationship.move_following(origin, target)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
24
mix.exs
24
mix.exs
|
@ -102,7 +102,7 @@ defp deps do
|
||||||
{:phoenix_ecto, "~> 4.0"},
|
{:phoenix_ecto, "~> 4.0"},
|
||||||
{:ecto_sql, "~> 3.2"},
|
{:ecto_sql, "~> 3.2"},
|
||||||
{:postgrex, ">= 0.13.5"},
|
{:postgrex, ">= 0.13.5"},
|
||||||
{:oban, "~> 0.8.1"},
|
{:oban, "~> 0.12.0"},
|
||||||
{:quantum, "~> 2.3"},
|
{:quantum, "~> 2.3"},
|
||||||
{:gettext, "~> 0.15"},
|
{:gettext, "~> 0.15"},
|
||||||
{:comeonin, "~> 4.1.1"},
|
{:comeonin, "~> 4.1.1"},
|
||||||
|
@ -194,27 +194,21 @@ 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
|
end
|
||||||
|
|
||||||
if git_tag && version != git_tag do
|
|
||||||
Mix.shell().error(
|
|
||||||
"Application version #{inspect(version)} does not match git tag #{inspect(git_tag)}"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Branch name as pre-release version component, denoted with a dot
|
# Branch name as pre-release version component, denoted with a dot
|
||||||
branch_name =
|
branch_name =
|
||||||
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
|
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
|
||||||
|
|
10
mix.lock
10
mix.lock
|
@ -23,8 +23,8 @@
|
||||||
"decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
|
"decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
|
||||||
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"},
|
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"},
|
||||||
"earmark": {:hex, :earmark, "1.4.2", "3aa0bd23bc4c61cf2f1e5d752d1bb470560a6f8539974f767a38923bb20e1d7f", [: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": {: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.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_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"},
|
"esshd": {:hex, :esshd, "0.1.0", "6f93a2062adb43637edad0ea7357db2702a4b80dd9683482fe00f5134e97f4c1", [:mix], [], "hexpm"},
|
||||||
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm"},
|
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm"},
|
||||||
"ex2ms": {:hex, :ex2ms, "1.5.0", "19e27f9212be9a96093fed8cdfbef0a2b56c21237196d26760f11dfcfae58e97", [: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_html": {:hex, :fast_html, "0.99.4", "d80812664f0429607e1d880fba0ef04da87a2e4fa596701bcaae17953535695c", [:make, :mix], [], "hexpm"},
|
||||||
"fast_sanitize": {:hex, :fast_sanitize, "0.1.4", "6c2e7203ca2f8275527a3021ba6e9d5d4ee213a47dc214a97c128737c9e56df1", [:mix], [{:fast_html, "~> 0.99", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
"fast_sanitize": {:hex, :fast_sanitize, "0.1.4", "6c2e7203ca2f8275527a3021ba6e9d5d4ee213a47dc214a97c128737c9e56df1", [:mix], [{:fast_html, "~> 0.99", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
"floki": {:hex, :floki, "0.23.0", "956ab6dba828c96e732454809fb0bd8d43ce0979b75f34de6322e73d4c917829", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm"},
|
"floki": {:hex, :floki, "0.23.1", "e100306ce7d8841d70a559748e5091542e2cfc67ffb3ade92b89a8435034dab1", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm"},
|
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm"},
|
||||||
"gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm"},
|
"gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm"},
|
||||||
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
|
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
"myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]},
|
"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"},
|
"nimble_parsec": {:hex, :nimble_parsec, "0.5.1", "c90796ecee0289dbb5ad16d3ad06f957b0cd1199769641c961cfe0b97db190e0", [:mix], [], "hexpm"},
|
||||||
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
|
"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"},
|
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
|
||||||
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "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"},
|
"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"},
|
"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"},
|
"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"]},
|
"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"},
|
"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"},
|
"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"},
|
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddMoveSupportToUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
add(:also_known_as, {:array, :string}, default: [], null: false)
|
||||||
|
add(:allow_following_move, :boolean, default: true, null: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,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
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.RemoveInfoFromUsers do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:users) do
|
||||||
|
remove(:info, :map, default: %{})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,53 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.FixMissingFollowingCount do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
"""
|
||||||
|
UPDATE
|
||||||
|
users
|
||||||
|
SET
|
||||||
|
following_count = sub.count
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
users.id AS sub_id
|
||||||
|
,COUNT (following_relationships.id)
|
||||||
|
FROM
|
||||||
|
following_relationships
|
||||||
|
,users
|
||||||
|
WHERE
|
||||||
|
users.id = following_relationships.follower_id
|
||||||
|
AND following_relationships.state = 'accept'
|
||||||
|
GROUP BY
|
||||||
|
users.id
|
||||||
|
) AS sub
|
||||||
|
WHERE
|
||||||
|
users.id = sub.sub_id
|
||||||
|
AND users.local = TRUE
|
||||||
|
;
|
||||||
|
"""
|
||||||
|
|> execute()
|
||||||
|
|
||||||
|
"""
|
||||||
|
UPDATE
|
||||||
|
users
|
||||||
|
SET
|
||||||
|
following_count = 0
|
||||||
|
WHERE
|
||||||
|
following_count IS NULL
|
||||||
|
"""
|
||||||
|
|> execute()
|
||||||
|
|
||||||
|
execute("ALTER TABLE users
|
||||||
|
ALTER COLUMN following_count SET DEFAULT 0,
|
||||||
|
ALTER COLUMN following_count SET NOT NULL
|
||||||
|
")
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
execute("ALTER TABLE users
|
||||||
|
ALTER COLUMN following_count DROP DEFAULT,
|
||||||
|
ALTER COLUMN following_count DROP NOT NULL
|
||||||
|
")
|
||||||
|
end
|
||||||
|
end
|
|
@ -29,7 +29,11 @@
|
||||||
"@id": "litepub:oauthRegistrationEndpoint",
|
"@id": "litepub:oauthRegistrationEndpoint",
|
||||||
"@type": "@id"
|
"@type": "@id"
|
||||||
},
|
},
|
||||||
"EmojiReaction": "litepub:EmojiReaction"
|
"EmojiReaction": "litepub:EmojiReaction",
|
||||||
|
"alsoKnownAs": {
|
||||||
|
"@id": "as:alsoKnownAs",
|
||||||
|
"@type": "@id"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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/federation/federation_test.exs
Normal file
47
test/federation/federation_test.exs
Normal file
|
@ -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
|
|
@ -9,7 +9,11 @@
|
||||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||||
"conversation": "ostatus:conversation",
|
"conversation": "ostatus:conversation",
|
||||||
"toot": "http://joinmastodon.org/ns#",
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
"Emoji": "toot:Emoji"
|
"Emoji": "toot:Emoji",
|
||||||
|
"alsoKnownAs": {
|
||||||
|
"@id": "as:alsoKnownAs",
|
||||||
|
"@type": "@id"
|
||||||
|
}
|
||||||
}],
|
}],
|
||||||
"id": "http://mastodon.example.org/users/admin",
|
"id": "http://mastodon.example.org/users/admin",
|
||||||
"type": "Person",
|
"type": "Person",
|
||||||
|
@ -50,5 +54,6 @@
|
||||||
"type": "Image",
|
"type": "Image",
|
||||||
"mediaType": "image/png",
|
"mediaType": "image/png",
|
||||||
"url": "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
|
"url": "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
|
||||||
}
|
},
|
||||||
|
"alsoKnownAs": ["http://example.org/users/foo"]
|
||||||
}
|
}
|
||||||
|
|
19
test/fixtures/users_mock/friendica_followers.json
vendored
Normal file
19
test/fixtures/users_mock/friendica_followers.json
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
{
|
||||||
|
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||||
|
"dfrn": "http://purl.org/macgirvin/dfrn/1.0/",
|
||||||
|
"diaspora": "https://diasporafoundation.org/ns/",
|
||||||
|
"litepub": "http://litepub.social/ns#",
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"directMessage": "litepub:directMessage"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "http://localhost:8080/followers/fuser3",
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"totalItems": 296
|
||||||
|
}
|
19
test/fixtures/users_mock/friendica_following.json
vendored
Normal file
19
test/fixtures/users_mock/friendica_following.json
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
{
|
||||||
|
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||||
|
"dfrn": "http://purl.org/macgirvin/dfrn/1.0/",
|
||||||
|
"diaspora": "https://diasporafoundation.org/ns/",
|
||||||
|
"litepub": "http://litepub.social/ns#",
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"directMessage": "litepub:directMessage"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "http://localhost:8080/following/fuser3",
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"totalItems": 32
|
||||||
|
}
|
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
|
|
@ -228,5 +228,16 @@ test "skips microformats hashtags" do
|
||||||
|
|
||||||
assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
|
assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not crash when there is an HTML entity in a link" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{"status" => "\"http://cofe.com/?boomer=ok&foo=bar\""})
|
||||||
|
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
|
assert {:ok, nil} = HTML.extract_first_external_url(object, object.data["content"])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,11 +16,21 @@ test "don't send pleroma user agent" do
|
||||||
|
|
||||||
test "send pleroma user agent" do
|
test "send pleroma user agent" do
|
||||||
Pleroma.Config.put([:http, :send_user_agent], true)
|
Pleroma.Config.put([:http, :send_user_agent], true)
|
||||||
|
Pleroma.Config.put([:http, :user_agent], :default)
|
||||||
|
|
||||||
assert RequestBuilder.headers(%{}, []) == %{
|
assert RequestBuilder.headers(%{}, []) == %{
|
||||||
headers: [{"User-Agent", Pleroma.Application.user_agent()}]
|
headers: [{"User-Agent", Pleroma.Application.user_agent()}]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "send custom user agent" do
|
||||||
|
Pleroma.Config.put([:http, :send_user_agent], true)
|
||||||
|
Pleroma.Config.put([:http, :user_agent], "totally-not-pleroma")
|
||||||
|
|
||||||
|
assert RequestBuilder.headers(%{}, []) == %{
|
||||||
|
headers: [{"User-Agent", "totally-not-pleroma"}]
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "add_optional_params/3" do
|
describe "add_optional_params/3" do
|
||||||
|
|
|
@ -630,6 +630,35 @@ test "notifications are deleted if a remote user is deleted" do
|
||||||
|
|
||||||
assert Enum.empty?(Notification.for_user(local_user))
|
assert Enum.empty?(Notification.for_user(local_user))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "move activity generates a notification" do
|
||||||
|
%{ap_id: old_ap_id} = old_user = insert(:user)
|
||||||
|
%{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
|
||||||
|
follower = insert(:user)
|
||||||
|
other_follower = insert(:user, %{allow_following_move: false})
|
||||||
|
|
||||||
|
User.follow(follower, old_user)
|
||||||
|
User.follow(other_follower, old_user)
|
||||||
|
|
||||||
|
Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
assert [
|
||||||
|
%{
|
||||||
|
activity: %{
|
||||||
|
data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
] = Notification.for_user(follower)
|
||||||
|
|
||||||
|
assert [
|
||||||
|
%{
|
||||||
|
activity: %{
|
||||||
|
data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
] = Notification.for_user(other_follower)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "for_user" do
|
describe "for_user" do
|
||||||
|
|
218
test/support/cluster.ex
Normal file
218
test/support/cluster.ex
Normal file
|
@ -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
|
|
@ -31,7 +31,6 @@ def user_factory do
|
||||||
nickname: sequence(:nickname, &"nick#{&1}"),
|
nickname: sequence(:nickname, &"nick#{&1}"),
|
||||||
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
|
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
|
||||||
bio: sequence(:bio, &"Tester Number #{&1}"),
|
bio: sequence(:bio, &"Tester Number #{&1}"),
|
||||||
info: %{},
|
|
||||||
last_digest_emailed_at: NaiveDateTime.utc_now()
|
last_digest_emailed_at: NaiveDateTime.utc_now()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1035,6 +1035,22 @@ def get("http://localhost:4001/users/masto_closed/following?page=1", _, _, _) do
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get("http://localhost:8080/followers/fuser3", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/users_mock/friendica_followers.json")
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get("http://localhost:8080/following/fuser3", _, _, _) do
|
||||||
|
{:ok,
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/users_mock/friendica_following.json")
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
def get("http://localhost:4001/users/fuser2/followers", _, _, _) do
|
def get("http://localhost:4001/users/fuser2/followers", _, _, _) do
|
||||||
{:ok,
|
{:ok,
|
||||||
%Tesla.Env{
|
%Tesla.Env{
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
os_exclude = if :os.type() == {:unix, :darwin}, do: [skip_on_mac: true], else: []
|
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)
|
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)
|
||||||
Mox.defmock(Pleroma.ReverseProxy.ClientMock, for: Pleroma.ReverseProxy.Client)
|
Mox.defmock(Pleroma.ReverseProxy.ClientMock, for: Pleroma.ReverseProxy.Client)
|
||||||
{:ok, _} = Application.ensure_all_started(:ex_machina)
|
{:ok, _} = Application.ensure_all_started(:ex_machina)
|
||||||
|
|
|
@ -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
|
||||||
|
@ -347,18 +367,6 @@ test "it sets the password_hash and ap_id" do
|
||||||
|
|
||||||
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
|
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it ensures info is not nil" do
|
|
||||||
changeset = User.register_changeset(%User{}, @full_user_data)
|
|
||||||
|
|
||||||
assert changeset.valid?
|
|
||||||
|
|
||||||
{:ok, user} =
|
|
||||||
changeset
|
|
||||||
|> Repo.insert()
|
|
||||||
|
|
||||||
refute is_nil(user.info)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "user registration, with :account_activation_required" do
|
describe "user registration, with :account_activation_required" do
|
||||||
|
@ -412,8 +420,7 @@ test "gets an existing user by ap_id" do
|
||||||
:user,
|
:user,
|
||||||
local: false,
|
local: false,
|
||||||
nickname: "admin@mastodon.example.org",
|
nickname: "admin@mastodon.example.org",
|
||||||
ap_id: ap_id,
|
ap_id: ap_id
|
||||||
info: %{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
||||||
|
@ -474,8 +481,7 @@ test "updates an existing user, if stale" do
|
||||||
local: false,
|
local: false,
|
||||||
nickname: "admin@mastodon.example.org",
|
nickname: "admin@mastodon.example.org",
|
||||||
ap_id: "http://mastodon.example.org/users/admin",
|
ap_id: "http://mastodon.example.org/users/admin",
|
||||||
last_refreshed_at: a_week_ago,
|
last_refreshed_at: a_week_ago
|
||||||
info: %{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert orig_user.last_refreshed_at == a_week_ago
|
assert orig_user.last_refreshed_at == a_week_ago
|
||||||
|
@ -516,7 +522,6 @@ test "returns an ap_followers link for a user" do
|
||||||
name: "Someone",
|
name: "Someone",
|
||||||
nickname: "a@b.de",
|
nickname: "a@b.de",
|
||||||
ap_id: "http...",
|
ap_id: "http...",
|
||||||
info: %{some: "info"},
|
|
||||||
avatar: %{some: "avatar"}
|
avatar: %{some: "avatar"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -941,9 +946,9 @@ test "hide a user from followers" do
|
||||||
{:ok, user} = User.follow(user, user2)
|
{:ok, user} = User.follow(user, user2)
|
||||||
{:ok, _user} = User.deactivate(user)
|
{: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)
|
assert [] = User.get_followers(user2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -952,13 +957,15 @@ 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)
|
user2 = User.get_cached_by_id(user2.id)
|
||||||
|
|
||||||
assert info.following_count == 0
|
assert refresh_record(user2).following_count == 0
|
||||||
|
assert user2.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)
|
||||||
end
|
end
|
||||||
|
@ -1121,8 +1128,7 @@ test "with an overly long bio" do
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
name: user.name,
|
name: user.name,
|
||||||
nickname: user.nickname,
|
nickname: user.nickname,
|
||||||
bio: String.duplicate("h", current_max_length + 1),
|
bio: String.duplicate("h", current_max_length + 1)
|
||||||
info: %{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||||
|
@ -1135,8 +1141,7 @@ test "with an overly long display name" do
|
||||||
data = %{
|
data = %{
|
||||||
ap_id: user.ap_id,
|
ap_id: user.ap_id,
|
||||||
name: String.duplicate("h", current_max_length + 1),
|
name: String.duplicate("h", current_max_length + 1),
|
||||||
nickname: user.nickname,
|
nickname: user.nickname
|
||||||
info: %{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||||
|
@ -1160,13 +1165,12 @@ test "html_filter_policy returns TwitterText scrubber when rich-text is disabled
|
||||||
describe "caching" do
|
describe "caching" do
|
||||||
test "invalidate_cache works" do
|
test "invalidate_cache works" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
_user_info = User.get_cached_user_info(user)
|
|
||||||
|
|
||||||
|
User.set_cache(user)
|
||||||
User.invalidate_cache(user)
|
User.invalidate_cache(user)
|
||||||
|
|
||||||
{:ok, nil} = Cachex.get(:user_cache, "ap_id:#{user.ap_id}")
|
{: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, "nickname:#{user.nickname}")
|
||||||
{:ok, nil} = Cachex.get(:user_cache, "user_info:#{user.id}")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "User.delete() plugs any possible zombie objects" do
|
test "User.delete() plugs any possible zombie objects" do
|
||||||
|
@ -1322,7 +1326,7 @@ test "follower count is updated when a follower is blocked" do
|
||||||
|
|
||||||
{:ok, user} = User.block(user, follower)
|
{:ok, user} = User.block(user, follower)
|
||||||
|
|
||||||
assert User.user_info(user).follower_count == 2
|
assert user.follower_count == 2
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "list_inactive_users_query/1" do
|
describe "list_inactive_users_query/1" do
|
||||||
|
@ -1499,51 +1503,6 @@ test "external_users/1 external active users with limit", %{user1: user1, user2:
|
||||||
end
|
end
|
||||||
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
|
describe "is_internal_user?/1" do
|
||||||
test "non-internal user returns false" do
|
test "non-internal user returns false" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -1600,14 +1559,14 @@ test "updates the counters normally on following/getting a follow when disabled"
|
||||||
ap_enabled: true
|
ap_enabled: true
|
||||||
)
|
)
|
||||||
|
|
||||||
assert User.user_info(other_user).following_count == 0
|
assert other_user.following_count == 0
|
||||||
assert User.user_info(other_user).follower_count == 0
|
assert other_user.follower_count == 0
|
||||||
|
|
||||||
{:ok, user} = Pleroma.User.follow(user, other_user)
|
{:ok, user} = Pleroma.User.follow(user, other_user)
|
||||||
other_user = Pleroma.User.get_by_id(other_user.id)
|
other_user = Pleroma.User.get_by_id(other_user.id)
|
||||||
|
|
||||||
assert User.user_info(user).following_count == 1
|
assert user.following_count == 1
|
||||||
assert User.user_info(other_user).follower_count == 1
|
assert other_user.follower_count == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
test "syncronizes the counters with the remote instance for the followed when enabled" do
|
test "syncronizes the counters with the remote instance for the followed when enabled" do
|
||||||
|
@ -1623,14 +1582,14 @@ test "syncronizes the counters with the remote instance for the followed when en
|
||||||
ap_enabled: true
|
ap_enabled: true
|
||||||
)
|
)
|
||||||
|
|
||||||
assert User.user_info(other_user).following_count == 0
|
assert other_user.following_count == 0
|
||||||
assert User.user_info(other_user).follower_count == 0
|
assert other_user.follower_count == 0
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
||||||
{:ok, _user} = User.follow(user, other_user)
|
{:ok, _user} = User.follow(user, other_user)
|
||||||
other_user = User.get_by_id(other_user.id)
|
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
|
end
|
||||||
|
|
||||||
test "syncronizes the counters with the remote instance for the follower when enabled" do
|
test "syncronizes the counters with the remote instance for the follower when enabled" do
|
||||||
|
@ -1646,13 +1605,13 @@ test "syncronizes the counters with the remote instance for the follower when en
|
||||||
ap_enabled: true
|
ap_enabled: true
|
||||||
)
|
)
|
||||||
|
|
||||||
assert User.user_info(other_user).following_count == 0
|
assert other_user.following_count == 0
|
||||||
assert User.user_info(other_user).follower_count == 0
|
assert other_user.follower_count == 0
|
||||||
|
|
||||||
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
||||||
{:ok, other_user} = User.follow(other_user, user)
|
{: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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,19 @@ test "it returns a json representation of the user with accept application/ld+js
|
||||||
|
|
||||||
assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
|
assert json_response(conn, 200) == UserView.render("user.json", %{user: user})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it returns 404 for remote users", %{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
user = insert(:user, local: false, nickname: "remoteuser@example.com")
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> get("/users/#{user.nickname}.json")
|
||||||
|
|
||||||
|
assert json_response(conn, 404)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "/object/:uuid" do
|
describe "/object/:uuid" do
|
||||||
|
|
|
@ -4,8 +4,11 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Builders.ActivityBuilder
|
alias Pleroma.Builders.ActivityBuilder
|
||||||
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
@ -1554,5 +1557,80 @@ test "detects hidden follows" do
|
||||||
assert follow_info.hide_followers == false
|
assert follow_info.hide_followers == false
|
||||||
assert follow_info.hide_follows == true
|
assert follow_info.hide_follows == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "detects hidden follows/followers for friendica" do
|
||||||
|
user =
|
||||||
|
insert(:user,
|
||||||
|
local: false,
|
||||||
|
follower_address: "http://localhost:8080/followers/fuser3",
|
||||||
|
following_address: "http://localhost:8080/following/fuser3"
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, follow_info} = ActivityPub.fetch_follow_information_for_user(user)
|
||||||
|
assert follow_info.hide_followers == true
|
||||||
|
assert follow_info.follower_count == 296
|
||||||
|
assert follow_info.following_count == 32
|
||||||
|
assert follow_info.hide_follows == true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Move activity" do
|
||||||
|
test "create" do
|
||||||
|
%{ap_id: old_ap_id} = old_user = insert(:user)
|
||||||
|
%{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
|
||||||
|
follower = insert(:user)
|
||||||
|
follower_move_opted_out = insert(:user, allow_following_move: false)
|
||||||
|
|
||||||
|
User.follow(follower, old_user)
|
||||||
|
User.follow(follower_move_opted_out, old_user)
|
||||||
|
|
||||||
|
assert User.following?(follower, old_user)
|
||||||
|
assert User.following?(follower_move_opted_out, old_user)
|
||||||
|
|
||||||
|
assert {:ok, activity} = ActivityPub.move(old_user, new_user)
|
||||||
|
|
||||||
|
assert %Activity{
|
||||||
|
actor: ^old_ap_id,
|
||||||
|
data: %{
|
||||||
|
"actor" => ^old_ap_id,
|
||||||
|
"object" => ^old_ap_id,
|
||||||
|
"target" => ^new_ap_id,
|
||||||
|
"type" => "Move"
|
||||||
|
},
|
||||||
|
local: true
|
||||||
|
} = activity
|
||||||
|
|
||||||
|
params = %{
|
||||||
|
"op" => "move_following",
|
||||||
|
"origin_id" => old_user.id,
|
||||||
|
"target_id" => new_user.id
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_enqueued(worker: Pleroma.Workers.BackgroundWorker, args: params)
|
||||||
|
|
||||||
|
Pleroma.Workers.BackgroundWorker.perform(params, nil)
|
||||||
|
|
||||||
|
refute User.following?(follower, old_user)
|
||||||
|
assert User.following?(follower, new_user)
|
||||||
|
|
||||||
|
assert User.following?(follower_move_opted_out, old_user)
|
||||||
|
refute User.following?(follower_move_opted_out, new_user)
|
||||||
|
|
||||||
|
activity = %Activity{activity | object: nil}
|
||||||
|
|
||||||
|
assert [%Notification{activity: ^activity}] =
|
||||||
|
Notification.for_user_since(follower, ~N[2019-04-13 11:22:33])
|
||||||
|
|
||||||
|
assert [%Notification{activity: ^activity}] =
|
||||||
|
Notification.for_user_since(follower_move_opted_out, ~N[2019-04-13 11:22:33])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "old user must be in the new user's `also_known_as` list" do
|
||||||
|
old_user = insert(:user)
|
||||||
|
new_user = insert(:user)
|
||||||
|
|
||||||
|
assert {:error, "Target account must have the origin in `alsoKnownAs`"} =
|
||||||
|
ActivityPub.move(old_user, new_user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,6 +39,7 @@ test "it ignores an incoming notice if we already have it" do
|
||||||
assert activity == returned_activity
|
assert activity == returned_activity
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag capture_log: true
|
||||||
test "it fetches replied-to activities if we don't have them" do
|
test "it fetches replied-to activities if we don't have them" do
|
||||||
data =
|
data =
|
||||||
File.read!("test/fixtures/mastodon-post-activity.json")
|
File.read!("test/fixtures/mastodon-post-activity.json")
|
||||||
|
@ -533,6 +534,7 @@ test "it works for incoming announces with an inlined activity" do
|
||||||
assert object.data["content"] == "this is a private toot"
|
assert object.data["content"] == "this is a private toot"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag capture_log: true
|
||||||
test "it rejects incoming announces with an inlined activity from another origin" do
|
test "it rejects incoming announces with an inlined activity from another origin" do
|
||||||
data =
|
data =
|
||||||
File.read!("test/fixtures/bogus-mastodon-announce.json")
|
File.read!("test/fixtures/bogus-mastodon-announce.json")
|
||||||
|
@ -681,6 +683,37 @@ test "it works for incoming update activities" do
|
||||||
assert user.bio == "<p>Some bio</p>"
|
assert user.bio == "<p>Some bio</p>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it works with alsoKnownAs" do
|
||||||
|
{:ok, %Activity{data: %{"actor" => actor}}} =
|
||||||
|
"test/fixtures/mastodon-post-activity.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Transmogrifier.handle_incoming()
|
||||||
|
|
||||||
|
assert User.get_cached_by_ap_id(actor).also_known_as == ["http://example.org/users/foo"]
|
||||||
|
|
||||||
|
{:ok, _activity} =
|
||||||
|
"test/fixtures/mastodon-update.json"
|
||||||
|
|> File.read!()
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("actor", actor)
|
||||||
|
|> Map.update!("object", fn object ->
|
||||||
|
object
|
||||||
|
|> Map.put("actor", actor)
|
||||||
|
|> Map.put("id", actor)
|
||||||
|
|> Map.put("alsoKnownAs", [
|
||||||
|
"http://mastodon.example.org/users/foo",
|
||||||
|
"http://example.org/users/bar"
|
||||||
|
])
|
||||||
|
end)
|
||||||
|
|> Transmogrifier.handle_incoming()
|
||||||
|
|
||||||
|
assert User.get_cached_by_ap_id(actor).also_known_as == [
|
||||||
|
"http://mastodon.example.org/users/foo",
|
||||||
|
"http://example.org/users/bar"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
test "it works with custom profile fields" do
|
test "it works with custom profile fields" do
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
"test/fixtures/mastodon-post-activity.json"
|
"test/fixtures/mastodon-post-activity.json"
|
||||||
|
@ -814,6 +847,7 @@ test "it fails for incoming deletes with spoofed origin" do
|
||||||
assert Activity.get_by_id(activity.id)
|
assert Activity.get_by_id(activity.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag capture_log: true
|
||||||
test "it works for incoming user deletes" do
|
test "it works for incoming user deletes" do
|
||||||
%{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
|
%{ap_id: ap_id} = insert(:user, ap_id: "http://mastodon.example.org/users/admin")
|
||||||
|
|
||||||
|
@ -1269,6 +1303,30 @@ test "it correctly processes messages with non-array cc field" do
|
||||||
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
|
assert ["https://www.w3.org/ns/activitystreams#Public"] == activity.data["cc"]
|
||||||
assert [user.follower_address] == activity.data["to"]
|
assert [user.follower_address] == activity.data["to"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it accepts Move activities" do
|
||||||
|
old_user = insert(:user)
|
||||||
|
new_user = insert(:user)
|
||||||
|
|
||||||
|
message = %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"type" => "Move",
|
||||||
|
"actor" => old_user.ap_id,
|
||||||
|
"object" => old_user.ap_id,
|
||||||
|
"target" => new_user.ap_id
|
||||||
|
}
|
||||||
|
|
||||||
|
assert :error = Transmogrifier.handle_incoming(message)
|
||||||
|
|
||||||
|
{:ok, _new_user} = User.update_and_set_cache(new_user, %{also_known_as: [old_user.ap_id]})
|
||||||
|
|
||||||
|
assert {:ok, %Activity{} = activity} = Transmogrifier.handle_incoming(message)
|
||||||
|
assert activity.actor == old_user.ap_id
|
||||||
|
assert activity.data["actor"] == old_user.ap_id
|
||||||
|
assert activity.data["object"] == old_user.ap_id
|
||||||
|
assert activity.data["target"] == new_user.ap_id
|
||||||
|
assert activity.data["type"] == "Move"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "prepare outgoing" do
|
describe "prepare outgoing" do
|
||||||
|
@ -1749,6 +1807,7 @@ test "returns object with inReplyToAtomUri when denied incoming reply", %{data:
|
||||||
assert modified_object["inReplyToAtomUri"] == ""
|
assert modified_object["inReplyToAtomUri"] == ""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag capture_log: true
|
||||||
test "returns modified object when allowed incoming reply", %{data: data} do
|
test "returns modified object when allowed incoming reply", %{data: data} do
|
||||||
object_with_reply =
|
object_with_reply =
|
||||||
Map.put(
|
Map.put(
|
||||||
|
@ -1868,6 +1927,7 @@ test "returns nil when cannot normalize object" do
|
||||||
end) =~ "Unsupported URI scheme"
|
end) =~ "Unsupported URI scheme"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag capture_log: true
|
||||||
test "returns {:ok, %Object{}} for success case" do
|
test "returns {:ok, %Object{}} for success case" do
|
||||||
assert {:ok, %Object{}} =
|
assert {:ok, %Object{}} =
|
||||||
Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873")
|
Transmogrifier.get_obj_helper("https://shitposter.club/notice/2827873")
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -1902,6 +1924,7 @@ test "with settings in db", %{conn: conn} do
|
||||||
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag capture_log: true
|
||||||
test "create new config setting in db", %{conn: conn} do
|
test "create new config setting in db", %{conn: conn} do
|
||||||
conn =
|
conn =
|
||||||
post(conn, "/api/pleroma/admin/config", %{
|
post(conn, "/api/pleroma/admin/config", %{
|
||||||
|
@ -2840,6 +2863,105 @@ test "DELETE /relay", %{admin: admin} do
|
||||||
"@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
|
"@#{admin.nickname} unfollowed relay: http://mastodon.example.org/users/admin"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "instances" do
|
||||||
|
test "GET /instances/:instance/statuses" do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
user = insert(:user, local: false, nickname: "archaeme@archae.me")
|
||||||
|
user2 = insert(:user, local: false, nickname: "test@test.com")
|
||||||
|
insert_pair(:note_activity, user: user)
|
||||||
|
insert(:note_activity, user: user2)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> get("/api/pleroma/admin/instances/archae.me/statuses")
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert length(response) == 2
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> get("/api/pleroma/admin/instances/test.com/statuses")
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert length(response) == 1
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> get("/api/pleroma/admin/instances/nonexistent.com/statuses")
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert length(response) == 0
|
||||||
|
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
|
||||||
|
|
|
@ -103,6 +103,21 @@ test "updates the user's locking status", %{conn: conn} do
|
||||||
assert user["locked"] == true
|
assert user["locked"] == true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "updates the user's allow_following_move", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
assert user.allow_following_move == true
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{allow_following_move: "false"})
|
||||||
|
|
||||||
|
assert refresh_record(user).allow_following_move == false
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["pleroma"]["allow_following_move"] == false
|
||||||
|
end
|
||||||
|
|
||||||
test "updates the user's default scope", %{conn: conn} do
|
test "updates the user's default scope", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
|
defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
|
||||||
use Pleroma.Web.ConnCase, async: true
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
alias Pleroma.Web.MastodonAPI.FilterView
|
alias Pleroma.Web.MastodonAPI.FilterView
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do
|
defmodule Pleroma.Web.MastodonAPI.ScheduledActivityControllerTest do
|
||||||
use Pleroma.Web.ConnCase, async: true
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.ScheduledActivity
|
alias Pleroma.ScheduledActivity
|
||||||
|
|
|
@ -102,7 +102,7 @@ test "Represent the user account for the account owner" do
|
||||||
privacy = user.default_scope
|
privacy = user.default_scope
|
||||||
|
|
||||||
assert %{
|
assert %{
|
||||||
pleroma: %{notification_settings: ^notification_settings},
|
pleroma: %{notification_settings: ^notification_settings, allow_following_move: true},
|
||||||
source: %{privacy: ^privacy}
|
source: %{privacy: ^privacy}
|
||||||
} = AccountView.render("show.json", %{user: user, for: user})
|
} = AccountView.render("show.json", %{user: user, for: user})
|
||||||
end
|
end
|
||||||
|
@ -350,7 +350,8 @@ test "represent an embedded relationship" 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
|
end
|
||||||
|
|
||||||
test "returns the settings store if the requesting user is the represented user and it's requested specifically" do
|
test "returns the settings store if the requesting user is the represented user and it's requested specifically" do
|
||||||
|
@ -374,6 +375,14 @@ test "sanitizes display names" do
|
||||||
refute result.display_name == "<marquee> username </marquee>"
|
refute result.display_name == "<marquee> username </marquee>"
|
||||||
end
|
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
|
describe "hiding follows/following" do
|
||||||
test "shows when follows/followers stats are hidden and sets follow/follower count to 0" do
|
test "shows when follows/followers stats are hidden and sets follow/follower count to 0" do
|
||||||
user =
|
user =
|
||||||
|
|
|
@ -107,4 +107,31 @@ test "Follow notification" do
|
||||||
assert [] ==
|
assert [] ==
|
||||||
NotificationView.render("index.json", %{notifications: [notification], for: followed})
|
NotificationView.render("index.json", %{notifications: [notification], for: followed})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "Move notification" do
|
||||||
|
%{ap_id: old_ap_id} = old_user = insert(:user)
|
||||||
|
%{ap_id: _new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
|
||||||
|
follower = insert(:user)
|
||||||
|
|
||||||
|
User.follow(follower, old_user)
|
||||||
|
Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
|
||||||
|
Pleroma.Tests.ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
old_user = refresh_record(old_user)
|
||||||
|
new_user = refresh_record(new_user)
|
||||||
|
|
||||||
|
[notification] = Notification.for_user(follower)
|
||||||
|
|
||||||
|
expected = %{
|
||||||
|
id: to_string(notification.id),
|
||||||
|
pleroma: %{is_seen: false},
|
||||||
|
type: "move",
|
||||||
|
account: AccountView.render("show.json", %{user: old_user, for: follower}),
|
||||||
|
target: AccountView.render("show.json", %{user: new_user, for: follower}),
|
||||||
|
created_at: Utils.to_masto_date(notification.inserted_at)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert [expected] ==
|
||||||
|
NotificationView.render("index.json", %{notifications: [notification], for: follower})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,23 +35,6 @@ test "redirects to /notice/id for html format", %{conn: conn} do
|
||||||
assert redirected_to(conn) == "/notice/#{note_activity.id}"
|
assert redirected_to(conn) == "/notice/#{note_activity.id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "500s when user not found", %{conn: conn} do
|
|
||||||
note_activity = insert(:note_activity)
|
|
||||||
object = Object.normalize(note_activity)
|
|
||||||
user = User.get_cached_by_ap_id(note_activity.data["actor"])
|
|
||||||
User.invalidate_cache(user)
|
|
||||||
Pleroma.Repo.delete(user)
|
|
||||||
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, object.data["id"]))
|
|
||||||
url = "/objects/#{uuid}"
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put_req_header("accept", "application/xml")
|
|
||||||
|> get(url)
|
|
||||||
|
|
||||||
assert response(conn, 500) == ~S({"error":"Something went wrong"})
|
|
||||||
end
|
|
||||||
|
|
||||||
test "404s on private objects", %{conn: conn} do
|
test "404s on private objects", %{conn: conn} do
|
||||||
note_activity = insert(:direct_note_activity)
|
note_activity = insert(:direct_note_activity)
|
||||||
object = Object.normalize(note_activity)
|
object = Object.normalize(note_activity)
|
||||||
|
@ -82,21 +65,6 @@ test "redirects to /notice/id for html format", %{conn: conn} do
|
||||||
assert redirected_to(conn) == "/notice/#{note_activity.id}"
|
assert redirected_to(conn) == "/notice/#{note_activity.id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "505s when user not found", %{conn: conn} do
|
|
||||||
note_activity = insert(:note_activity)
|
|
||||||
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
|
|
||||||
user = User.get_cached_by_ap_id(note_activity.data["actor"])
|
|
||||||
User.invalidate_cache(user)
|
|
||||||
Pleroma.Repo.delete(user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put_req_header("accept", "text/html")
|
|
||||||
|> get("/activities/#{uuid}")
|
|
||||||
|
|
||||||
assert response(conn, 500) == ~S({"error":"Something went wrong"})
|
|
||||||
end
|
|
||||||
|
|
||||||
test "404s on private activities", %{conn: conn} do
|
test "404s on private activities", %{conn: conn} do
|
||||||
note_activity = insert(:direct_note_activity)
|
note_activity = insert(:direct_note_activity)
|
||||||
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
|
[_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]))
|
||||||
|
@ -127,21 +95,28 @@ test "gets an activity in AS2 format", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET notice/2" do
|
describe "GET notice/2" do
|
||||||
test "gets a notice in xml format", %{conn: conn} do
|
test "redirects to a proper object URL when json requested and the object is local", %{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
note_activity = insert(:note_activity)
|
note_activity = insert(:note_activity)
|
||||||
|
expected_redirect_url = Object.normalize(note_activity).data["id"]
|
||||||
|
|
||||||
conn
|
redirect_url =
|
||||||
|> get("/notice/#{note_activity.id}")
|
conn
|
||||||
|> response(200)
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|
|> get("/notice/#{note_activity.id}")
|
||||||
|
|> redirected_to()
|
||||||
|
|
||||||
|
assert redirect_url == expected_redirect_url
|
||||||
end
|
end
|
||||||
|
|
||||||
test "gets a notice in AS2 format", %{conn: conn} do
|
test "returns a 404 on remote notice when json requested", %{conn: conn} do
|
||||||
note_activity = insert(:note_activity)
|
note_activity = insert(:note_activity, local: false)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_req_header("accept", "application/activity+json")
|
|> put_req_header("accept", "application/activity+json")
|
||||||
|> get("/notice/#{note_activity.id}")
|
|> get("/notice/#{note_activity.id}")
|
||||||
|> json_response(200)
|
|> response(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "500s when actor not found", %{conn: conn} do
|
test "500s when actor not found", %{conn: conn} do
|
||||||
|
@ -157,32 +132,6 @@ test "500s when actor not found", %{conn: conn} do
|
||||||
assert response(conn, 500) == ~S({"error":"Something went wrong"})
|
assert response(conn, 500) == ~S({"error":"Something went wrong"})
|
||||||
end
|
end
|
||||||
|
|
||||||
test "only gets a notice in AS2 format for Create messages", %{conn: conn} do
|
|
||||||
note_activity = insert(:note_activity)
|
|
||||||
url = "/notice/#{note_activity.id}"
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put_req_header("accept", "application/activity+json")
|
|
||||||
|> get(url)
|
|
||||||
|
|
||||||
assert json_response(conn, 200)
|
|
||||||
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
{:ok, like_activity, _} = CommonAPI.favorite(note_activity.id, user)
|
|
||||||
url = "/notice/#{like_activity.id}"
|
|
||||||
|
|
||||||
assert like_activity.data["type"] == "Like"
|
|
||||||
|
|
||||||
conn =
|
|
||||||
build_conn()
|
|
||||||
|> put_req_header("accept", "application/activity+json")
|
|
||||||
|> get(url)
|
|
||||||
|
|
||||||
assert response(conn, 404)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "render html for redirect for html format", %{conn: conn} do
|
test "render html for redirect for html format", %{conn: conn} do
|
||||||
note_activity = insert(:note_activity)
|
note_activity = insert(:note_activity)
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ defmodule Pleroma.Web.StreamerTest do
|
||||||
alias Pleroma.Web.Streamer.StreamerSocket
|
alias Pleroma.Web.Streamer.StreamerSocket
|
||||||
alias Pleroma.Web.Streamer.Worker
|
alias Pleroma.Web.Streamer.Worker
|
||||||
|
|
||||||
@moduletag needs_streamer: true
|
@moduletag needs_streamer: true, capture_log: true
|
||||||
clear_config_all([:instance, :skip_thread_containment])
|
clear_config_all([:instance, :skip_thread_containment])
|
||||||
|
|
||||||
describe "user streams" do
|
describe "user streams" do
|
||||||
|
|
Loading…
Reference in a new issue