Merge branch 'develop' into 'domain-block-precedence'
# Conflicts: # lib/pleroma/user.ex
This commit is contained in:
commit
8cfaab8f04
@ -1,3 +1,3 @@
|
||||
[
|
||||
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}", "priv/repo/migrations/*.exs"]
|
||||
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}", "priv/repo/migrations/*.exs", "priv/scrubbers/*.ex"]
|
||||
]
|
||||
|
@ -31,6 +31,7 @@ build:
|
||||
|
||||
benchmark:
|
||||
stage: benchmark
|
||||
when: manual
|
||||
variables:
|
||||
MIX_ENV: benchmark
|
||||
services:
|
||||
@ -55,6 +56,19 @@ unit-testing:
|
||||
- mix ecto.migrate
|
||||
- mix coveralls --preload-modules
|
||||
|
||||
federated-testing:
|
||||
stage: test
|
||||
services:
|
||||
- name: minibikini/postgres-with-rum:12
|
||||
alias: postgres
|
||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||
script:
|
||||
- mix deps.get
|
||||
- mix ecto.create
|
||||
- mix ecto.migrate
|
||||
- epmd -daemon
|
||||
- mix test --trace --only federated
|
||||
|
||||
unit-testing-rum:
|
||||
stage: test
|
||||
services:
|
||||
|
15
CHANGELOG.md
15
CHANGELOG.md
@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- OStatus: Extract RSS functionality
|
||||
- Deprecated `User.Info` embedded schema (fields moved to `User`)
|
||||
- Store status data inside Flag activity
|
||||
- Deprecated (reorganized as `UserRelationship` entity) User fields with user AP IDs (`blocks`, `mutes`, `muted_reblogs`, `muted_notifications`, `subscribers`).
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
|
||||
@ -35,9 +36,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- Mastodon API: `pleroma.thread_muted` to the Status entity
|
||||
- Mastodon API: Mark the direct conversation as read for the author when they send a new direct message
|
||||
- Mastodon API, streaming: Add `pleroma.direct_conversation_id` to the `conversation` stream event payload.
|
||||
- Admin API: Render whole status in grouped reports
|
||||
- Mastodon API: User timelines will now respect blocks, unless you are getting the user timeline of somebody you blocked (which would be empty otherwise).
|
||||
</details>
|
||||
|
||||
### Added
|
||||
- `:chat_limit` option to limit chat characters.
|
||||
- Refreshing poll results for remote polls
|
||||
- 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.
|
||||
@ -45,6 +49,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- 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).
|
||||
- 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.
|
||||
- User notification settings: Add `privacy_option` option.
|
||||
- User settings: Add _This account is a_ option.
|
||||
- OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`).
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
|
||||
@ -66,22 +73,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
- `DELETE /api/pleroma/admin/users` (`nickname` query param or `nickname` sent in JSON body) is deprecated in favor of: `DELETE /api/pleroma/admin/users` (`nicknames` query array param or `nicknames` sent in JSON body)
|
||||
- Admin API: Add `GET /api/pleroma/admin/relay` endpoint - lists all followed relays
|
||||
- Pleroma API: `POST /api/v1/pleroma/conversations/read` to mark all conversations as read
|
||||
- ActivityPub: Support `Move` activities
|
||||
- Mastodon API: Add `/api/v1/markers` for managing timeline read markers
|
||||
- Mastodon API: Add the `recipients` parameter to `GET /api/v1/conversations`
|
||||
- Configuration: `feed` option for user atom feed.
|
||||
- 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
|
||||
- ActivityPub: Configurable `type` field of the actors.
|
||||
- Mastodon API: `/api/v1/accounts/:id` has `source/pleroma/actor_type` field.
|
||||
- Mastodon API: `/api/v1/update_credentials` accepts `actor_type` field.
|
||||
</details>
|
||||
|
||||
### Fixed
|
||||
- Report emails now include functional links to profiles of remote user accounts
|
||||
- Not being able to log in to some third-party apps when logged in to MastoFE
|
||||
- MRF: `Delete` activities being exempt from MRF policies
|
||||
- OTP releases: Not being able to configure OAuth expired token cleanup interval
|
||||
- OTP releases: Not being able to configure HTML sanitization policy
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
|
||||
- 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`
|
||||
- AdminAPI: If some status received reports both in the "new" format and "old" format it was considered reports on two different statuses (in the context of grouped reports)
|
||||
- Admin API: Error when trying to update reports in the "old" format
|
||||
</details>
|
||||
|
||||
|
@ -95,7 +95,36 @@ defmodule Pleroma.LoadTesting.Fetcher do
|
||||
for: user,
|
||||
as: :activity
|
||||
})
|
||||
end
|
||||
end,
|
||||
"Rendering favorites timeline" => fn ->
|
||||
conn = Phoenix.ConnTest.build_conn(:get, "http://localhost:4001/api/v1/favourites", nil)
|
||||
Pleroma.Web.MastodonAPI.StatusController.favourites(
|
||||
%Plug.Conn{conn |
|
||||
assigns: %{user: user},
|
||||
query_params: %{"limit" => "0"},
|
||||
body_params: %{},
|
||||
cookies: %{},
|
||||
params: %{},
|
||||
path_params: %{},
|
||||
private: %{
|
||||
Pleroma.Web.Router => {[], %{}},
|
||||
phoenix_router: Pleroma.Web.Router,
|
||||
phoenix_action: :favourites,
|
||||
phoenix_controller: Pleroma.Web.MastodonAPI.StatusController,
|
||||
phoenix_endpoint: Pleroma.Web.Endpoint,
|
||||
phoenix_format: "json",
|
||||
phoenix_layout: {Pleroma.Web.LayoutView, "app.html"},
|
||||
phoenix_recycled: true,
|
||||
|
||||
phoenix_view: Pleroma.Web.MastodonAPI.StatusView,
|
||||
plug_session: %{"user_id" => user.id},
|
||||
plug_session_fetch: :done,
|
||||
plug_session_info: :write,
|
||||
plug_skip_csrf_protection: true
|
||||
}
|
||||
},
|
||||
%{})
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -2,6 +2,24 @@ defmodule Pleroma.LoadTesting.Generator do
|
||||
use Pleroma.LoadTesting.Helper
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
def generate_like_activities(user, posts) do
|
||||
count_likes = Kernel.trunc(length(posts) / 4)
|
||||
IO.puts("Starting generating #{count_likes} like activities...")
|
||||
|
||||
{time, _} =
|
||||
:timer.tc(fn ->
|
||||
Task.async_stream(
|
||||
Enum.take_random(posts, count_likes),
|
||||
fn post -> {:ok, _, _} = CommonAPI.favorite(post.id, user) end,
|
||||
max_concurrency: 10,
|
||||
timeout: 30_000
|
||||
)
|
||||
|> Stream.run()
|
||||
end)
|
||||
|
||||
IO.puts("Inserting like activities take #{to_sec(time)} sec.\n")
|
||||
end
|
||||
|
||||
def generate_users(opts) do
|
||||
IO.puts("Starting generating #{opts[:users_max]} users...")
|
||||
{time, _} = :timer.tc(fn -> do_generate_users(opts) end)
|
||||
@ -31,7 +49,6 @@ defmodule Pleroma.LoadTesting.Generator do
|
||||
password_hash:
|
||||
"$pbkdf2-sha512$160000$bU.OSFI7H/yqWb5DPEqyjw$uKp/2rmXw12QqnRRTqTtuk2DTwZfF8VR4MYW2xMeIlqPR/UX1nT1CEKVUx2CowFMZ5JON8aDvURrZpJjSgqXrg",
|
||||
bio: "Tester Number #{i}",
|
||||
info: %{},
|
||||
local: remote
|
||||
}
|
||||
|
||||
|
@ -100,6 +100,10 @@ defmodule Mix.Tasks.Pleroma.LoadTesting do
|
||||
|
||||
generate_remote_activities(user, remote_users)
|
||||
|
||||
generate_like_activities(
|
||||
user, Pleroma.Repo.all(Pleroma.Activity.Queries.by_type("Create"))
|
||||
)
|
||||
|
||||
generate_dms(user, users, opts)
|
||||
|
||||
{:ok, activity} = generate_long_thread(user, users, opts)
|
||||
|
@ -225,6 +225,7 @@ config :pleroma, :instance,
|
||||
notify_email: "noreply@example.com",
|
||||
description: "A Pleroma instance, an alternative fediverse server",
|
||||
limit: 5_000,
|
||||
chat_limit: 5_000,
|
||||
remote_limit: 100_000,
|
||||
upload_limit: 16_000_000,
|
||||
avatar_upload_limit: 2_000_000,
|
||||
@ -562,7 +563,10 @@ config :ueberauth,
|
||||
base_path: "/oauth",
|
||||
providers: ueberauth_providers
|
||||
|
||||
config :pleroma, :auth, oauth_consumer_strategies: oauth_consumer_strategies
|
||||
config :pleroma,
|
||||
:auth,
|
||||
enforce_oauth_admin_scope_usage: false,
|
||||
oauth_consumer_strategies: oauth_consumer_strategies
|
||||
|
||||
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail, enabled: false
|
||||
|
||||
|
@ -2095,6 +2095,15 @@ config :pleroma, :config_description, [
|
||||
description: "Authentication / authorization settings",
|
||||
children: [
|
||||
%{
|
||||
key: :enforce_oauth_admin_scope_usage,
|
||||
type: :boolean,
|
||||
description:
|
||||
"OAuth admin scope requirement toggle. " <>
|
||||
"If `true`, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token " <>
|
||||
"(client app must support admin scopes). If `false` and token doesn't have admin scope(s)," <>
|
||||
"`is_admin` user flag grants access to admin-specific actions."
|
||||
},
|
||||
%{
|
||||
key: :auth_template,
|
||||
type: :string,
|
||||
description:
|
||||
|
@ -2,6 +2,13 @@
|
||||
|
||||
Authentication is required and the user must be an admin.
|
||||
|
||||
Configuration options:
|
||||
|
||||
* `[:auth, :enforce_oauth_admin_scope_usage]` — OAuth admin scope requirement toggle.
|
||||
If `true`, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token (client app must support admin scopes).
|
||||
If `false` and token doesn't have admin scope(s), `is_admin` user flag grants access to admin-specific actions.
|
||||
Note that client app needs to explicitly support admin scopes and request them when obtaining auth token.
|
||||
|
||||
## `GET /api/pleroma/admin/users`
|
||||
|
||||
### List users
|
||||
|
@ -57,6 +57,7 @@ Has these additional fields under the `pleroma` object:
|
||||
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
|
||||
- `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
|
||||
- `deactivated`: boolean, true when the user is deactivated
|
||||
- `allow_following_move`: boolean, true when the user allows automatically follow moved following accounts
|
||||
- `unread_conversation_count`: The count of unread conversations. Only returned to the account owner.
|
||||
|
||||
### Source
|
||||
@ -65,6 +66,8 @@ Has these additional fields under the `pleroma` object:
|
||||
|
||||
- `show_role`: boolean, nullable, true when the user wants his role (e.g admin, moderator) to be shown
|
||||
- `no_rich_text` - boolean, nullable, true when html tags are stripped from all statuses requested from the API
|
||||
- `discoverable`: boolean, true when the user allows discovery of the account in search results and other services.
|
||||
- `actor_type`: string, the type of this account.
|
||||
|
||||
## Conversations
|
||||
|
||||
@ -91,11 +94,18 @@ Has these additional fields under the `pleroma` object:
|
||||
|
||||
- `is_seen`: true if the notification was read by the user
|
||||
|
||||
### Move Notification
|
||||
|
||||
The `type` value is `move`. Has an additional field:
|
||||
|
||||
- `target`: new account
|
||||
|
||||
## GET `/api/v1/notifications`
|
||||
|
||||
Accepts additional parameters:
|
||||
|
||||
- `exclude_visibilities`: will exclude the notifications for activities with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`). Usage example: `GET /api/v1/notifications?exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
||||
- `with_move`: boolean, when set to `true` will include Move notifications. `false` by default.
|
||||
|
||||
## POST `/api/v1/statuses`
|
||||
|
||||
@ -136,7 +146,10 @@ Additional parameters can be added to the JSON body/Form data:
|
||||
- `default_scope` - the scope returned under `privacy` key in Source subentity
|
||||
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
|
||||
- `skip_thread_containment` - if true, skip filtering out broken threads
|
||||
- `allow_following_move` - if true, allows automatically follow moved following accounts
|
||||
- `pleroma_background_image` - sets the background image of the user.
|
||||
- `discoverable` - if true, discovery of this account in search results and other services is allowed.
|
||||
- `actor_type` - the type of this account.
|
||||
|
||||
### Pleroma Settings Store
|
||||
Pleroma has mechanism that allows frontends to save blobs of json for each user on the backend. This can be used to save frontend-specific settings for a user that the backend does not need to know about.
|
||||
|
@ -302,6 +302,7 @@ See [Admin-API](admin_api.md)
|
||||
* `follows`: BOOLEAN field, receives notifications from people the user follows
|
||||
* `remote`: BOOLEAN field, receives notifications from people on remote instances
|
||||
* `local`: BOOLEAN field, receives notifications from people on the local instance
|
||||
* `privacy_option`: BOOLEAN field. When set to true, it removes the contents of a message from the push notification.
|
||||
* Response: JSON. Returns `{"status": "success"}` if the update was successful, otherwise returns `{"error": "error_msg"}`
|
||||
|
||||
## `/api/pleroma/healthcheck`
|
||||
|
@ -3,17 +3,26 @@
|
||||
!!! danger
|
||||
This is a Work In Progress, not usable just yet.
|
||||
|
||||
Every command should be ran with a prefix, in case of OTP releases it is `./bin/pleroma_ctl config` and in case of source installs it's
|
||||
`mix pleroma.config`.
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Transfer config from file to DB.
|
||||
|
||||
```sh
|
||||
$PREFIX migrate_to_db
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl config migrate_to_db
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.config migrate_to_db
|
||||
```
|
||||
|
||||
|
||||
## Transfer config from DB to `config/env.exported_from_db.secret.exs`
|
||||
|
||||
```sh
|
||||
$PREFIX migrate_from_db <env>
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl config migrate_from_db <env>
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.config migrate_from_db <env>
|
||||
```
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Database maintenance tasks
|
||||
|
||||
Every command should be ran with a prefix, in case of OTP releases it is `./bin/pleroma_ctl database` and in case of source installs it's `mix pleroma.database`.
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
!!! danger
|
||||
These mix tasks can take a long time to complete. Many of them were written to address specific database issues that happened because of bugs in migrations or other specific scenarios. Do not run these tasks "just in case" if everything is fine your instance.
|
||||
@ -9,8 +9,12 @@ Every command should be ran with a prefix, in case of OTP releases it is `./bin/
|
||||
|
||||
Replaces embedded objects with references to them in the `objects` table. Only needs to be ran once if the instance was created before Pleroma 1.0.5. The reason why this is not a migration is because it could significantly increase the database size after being ran, however after this `VACUUM FULL` will be able to reclaim about 20% (really depends on what is in the database, your mileage may vary) of the db size before the migration.
|
||||
|
||||
```sh
|
||||
$PREFIX remove_embedded_objects [<options>]
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl database remove_embedded_objects [<options>]
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.database remove_embedded_objects [<options>]
|
||||
```
|
||||
|
||||
### Options
|
||||
@ -20,11 +24,15 @@ $PREFIX remove_embedded_objects [<options>]
|
||||
|
||||
This will prune remote posts older than 90 days (configurable with [`config :pleroma, :instance, remote_post_retention_days`](../../configuration/cheatsheet.md#instance)) from the database, they will be refetched from source when accessed.
|
||||
|
||||
!!! note
|
||||
The disk space will only be reclaimed after `VACUUM FULL`
|
||||
!!! danger
|
||||
The disk space will only be reclaimed after `VACUUM FULL`. You may run out of disk space during the execution of the task or vacuuming if you don't have about 1/3rds of the database size free.
|
||||
|
||||
```sh
|
||||
$PREFIX pleroma.database prune_objects [<options>]
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl database prune_objects [<options>]
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.database prune_objects [<options>]
|
||||
```
|
||||
|
||||
### Options
|
||||
@ -34,18 +42,30 @@ $PREFIX pleroma.database prune_objects [<options>]
|
||||
|
||||
Can be safely re-run
|
||||
|
||||
```sh
|
||||
$PREFIX bump_all_conversations
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl database bump_all_conversations
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.database bump_all_conversations
|
||||
```
|
||||
|
||||
## Remove duplicated items from following and update followers count for all users
|
||||
|
||||
```sh
|
||||
$PREFIX update_users_following_followers_counts
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl database update_users_following_followers_counts
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.database update_users_following_followers_counts
|
||||
```
|
||||
|
||||
## Fix the pre-existing "likes" collections for all objects
|
||||
|
||||
```sh
|
||||
$PREFIX fix_likes_collections
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl database fix_likes_collections
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.database fix_likes_collections
|
||||
```
|
||||
|
@ -1,13 +1,24 @@
|
||||
# Managing digest emails
|
||||
Every command should be ran with a prefix, in case of OTP releases it is `./bin/pleroma_ctl digest` and in case of source installs it's `mix pleroma.digest`.
|
||||
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Send digest email since given date (user registration date by default) ignoring user activity status.
|
||||
|
||||
```sh
|
||||
$PREFIX test <nickname> [<since_date>]
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl digest test <nickname> [<since_date>]
|
||||
```
|
||||
|
||||
Example:
|
||||
```sh
|
||||
$PREFIX test donaldtheduck 2019-05-20
|
||||
```sh tab="From Source"
|
||||
mix pleroma.digest test <nickname> [<since_date>]
|
||||
```
|
||||
|
||||
|
||||
Example:
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl digest test donaldtheduck 2019-05-20
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.digest test donaldtheduck 2019-05-20
|
||||
```
|
||||
|
||||
|
@ -1,28 +1,44 @@
|
||||
# Managing emoji packs
|
||||
|
||||
Every command should be ran with a prefix, in case of OTP releases it is `./bin/pleroma_ctl emoji` and in case of source installs it's `mix pleroma.emoji`.
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Lists emoji packs and metadata specified in the manifest
|
||||
|
||||
```sh
|
||||
$PREFIX ls-packs [<options>]
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl emoji ls-packs [<options>]
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.emoji ls-packs [<options>]
|
||||
```
|
||||
|
||||
|
||||
### Options
|
||||
- `-m, --manifest PATH/URL` - path to a custom manifest, it can either be an URL starting with `http`, in that case the manifest will be fetched from that address, or a local path
|
||||
|
||||
## Fetch, verify and install the specified packs from the manifest into `STATIC-DIR/emoji/PACK-NAME`
|
||||
```sh
|
||||
$PREFIX get-packs [<options>] <packs>
|
||||
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl emoji get-packs [<options>] <packs>
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.emoji get-packs [<options>] <packs>
|
||||
```
|
||||
|
||||
### Options
|
||||
- `-m, --manifest PATH/URL` - same as [`ls-packs`](#ls-packs)
|
||||
|
||||
## Create a new manifest entry and a file list from the specified remote pack file
|
||||
```sh
|
||||
$PREFIX gen-pack PACK-URL
|
||||
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl emoji gen-pack PACK-URL
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.emoji gen-pack PACK-URL
|
||||
```
|
||||
|
||||
Currently, only .zip archives are recognized as remote pack files and packs are therefore assumed to be zip archives. This command is intended to run interactively and will first ask you some basic questions about the pack, then download the remote file and generate an SHA256 checksum for it, then generate an emoji file list for you.
|
||||
|
||||
The manifest entry will either be written to a newly created `index.json` file or appended to the existing one, *replacing* the old pack with the same name if it was in the file previously.
|
||||
|
@ -0,0 +1,5 @@
|
||||
Every command should be ran as the `pleroma` user from it's home directory. For example if you are superuser, you would have to wrap the command in `su pleroma -s $SHELL -lc "$COMMAND"`.
|
||||
|
||||
??? note "From source note about `MIX_ENV`"
|
||||
|
||||
The `mix` command should be prefixed with the name of environment your Pleroma server is running in, usually it's `MIX_ENV=prod`
|
@ -1,12 +1,17 @@
|
||||
# Managing instance configuration
|
||||
|
||||
Every command should be ran with a prefix, in case of OTP releases it is `./bin/pleroma_ctl instance` and in case of source installs it's `mix pleroma.instance`.
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Generate a new configuration file
|
||||
```sh
|
||||
$PREFIX gen [<options>]
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl instance gen [<options>]
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.instance gen [<options>]
|
||||
```
|
||||
|
||||
|
||||
If any of the options are left unspecified, you will be prompted interactively.
|
||||
|
||||
### Options
|
||||
|
@ -1,30 +1,33 @@
|
||||
# Managing relays
|
||||
|
||||
Every command should be ran with a prefix, in case of OTP releases it is `./bin/pleroma_ctl relay` and in case of source installs it's `mix pleroma.relay`.
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Follow a relay
|
||||
```sh
|
||||
$PREFIX follow <relay_url>
|
||||
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl relay follow <relay_url>
|
||||
```
|
||||
|
||||
Example:
|
||||
```sh
|
||||
$PREFIX follow https://example.org/relay
|
||||
```sh tab="From Source"
|
||||
mix pleroma.relay follow <relay_url>
|
||||
```
|
||||
|
||||
## Unfollow a remote relay
|
||||
|
||||
```sh
|
||||
$PREFIX unfollow <relay_url>
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl relay unfollow <relay_url>
|
||||
```
|
||||
|
||||
Example:
|
||||
```sh
|
||||
$PREFIX unfollow https://example.org/relay
|
||||
```sh tab="From Source"
|
||||
mix pleroma.relay unfollow <relay_url>
|
||||
```
|
||||
|
||||
## List relay subscriptions
|
||||
|
||||
```sh
|
||||
$PREFIX list
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl relay list
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.relay list
|
||||
```
|
||||
|
@ -1,11 +1,16 @@
|
||||
# Managing uploads
|
||||
|
||||
Every command should be ran with a prefix, in case of OTP releases it is `./bin/pleroma_ctl uploads` and in case of source installs it's `mix pleroma.uploads`.
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Migrate uploads from local to remote storage
|
||||
```sh
|
||||
$PREFIX migrate_local <target_uploader> [<options>]
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl uploads migrate_local <target_uploader> [<options>]
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.uploads migrate_local <target_uploader> [<options>]
|
||||
```
|
||||
|
||||
### Options
|
||||
- `--delete` - delete local uploads after migrating them to the target uploader
|
||||
|
||||
|
@ -1,12 +1,18 @@
|
||||
# Managing users
|
||||
|
||||
Every command should be ran with a prefix, in case of OTP releases it is `./bin/pleroma_ctl user` and in case of source installs it's `mix pleroma.user`.
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Create a user
|
||||
```sh
|
||||
$PREFIX new <nickname> <email> [<options>]
|
||||
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user new <email> [<options>]
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user new <email> [<options>]
|
||||
```
|
||||
|
||||
|
||||
### Options
|
||||
- `--name <name>` - the user's display name
|
||||
- `--bio <bio>` - the user's bio
|
||||
@ -16,84 +22,159 @@ $PREFIX new <nickname> <email> [<options>]
|
||||
- `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions
|
||||
|
||||
## List local users
|
||||
```sh
|
||||
$PREFIX list
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user list
|
||||
```
|
||||
|
||||
## Generate an invite link
|
||||
```sh
|
||||
$PREFIX invite [<options>]
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user list
|
||||
```
|
||||
|
||||
|
||||
## Generate an invite link
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user invite [<options>]
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user invite [<options>]
|
||||
```
|
||||
|
||||
|
||||
### Options
|
||||
- `--expires-at DATE` - last day on which token is active (e.g. "2019-04-05")
|
||||
- `--max-use NUMBER` - maximum numbers of token uses
|
||||
|
||||
## List generated invites
|
||||
```sh
|
||||
$PREFIX invites
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user invites
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user invites
|
||||
```
|
||||
|
||||
|
||||
## Revoke invite
|
||||
```sh
|
||||
$PREFIX revoke_invite <token_or_id>
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user revoke_invite <token_or_id>
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user revoke_invite <token_or_id>
|
||||
```
|
||||
|
||||
|
||||
## Delete a user
|
||||
```sh
|
||||
$PREFIX rm <nickname>
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user rm <nickname>
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user rm <nickname>
|
||||
```
|
||||
|
||||
|
||||
## Delete user's posts and interactions
|
||||
```sh
|
||||
$PREFIX delete_activities <nickname>
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user delete_activities <nickname>
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user delete_activities <nickname>
|
||||
```
|
||||
|
||||
|
||||
## Sign user out from all applications (delete user's OAuth tokens and authorizations)
|
||||
```sh
|
||||
$PREFIX sign_out <nickname>
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user sign_out <nickname>
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user sign_out <nickname>
|
||||
```
|
||||
|
||||
|
||||
## Deactivate or activate a user
|
||||
```sh
|
||||
$PREFIX toggle_activated <nickname>
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user toggle_activated <nickname>
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user toggle_activated <nickname>
|
||||
```
|
||||
|
||||
|
||||
## Unsubscribe local users from a user and deactivate the user
|
||||
```sh
|
||||
$PREFIX unsubscribe NICKNAME
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user unsubscribe NICKNAME
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user unsubscribe NICKNAME
|
||||
```
|
||||
|
||||
|
||||
## Unsubscribe local users from an instance and deactivate all accounts on it
|
||||
```sh
|
||||
$PREFIX unsubscribe_all_from_instance <instance>
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user unsubscribe_all_from_instance <instance>
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user unsubscribe_all_from_instance <instance>
|
||||
```
|
||||
|
||||
|
||||
## Create a password reset link for user
|
||||
```sh
|
||||
$PREFIX reset_password <nickname>
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user reset_password <nickname>
|
||||
```
|
||||
|
||||
## Set the value of the given user's settings
|
||||
```sh
|
||||
$PREFIX set <nickname> [<options>]
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user reset_password <nickname>
|
||||
```
|
||||
|
||||
|
||||
## Set the value of the given user's settings
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user set <nickname> [<options>]
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user set <nickname> [<options>]
|
||||
```
|
||||
|
||||
### Options
|
||||
- `--locked`/`--no-locked` - whether the user should be locked
|
||||
- `--moderator`/`--no-moderator` - whether the user should be a moderator
|
||||
- `--admin`/`--no-admin` - whether the user should be an admin
|
||||
|
||||
## Add tags to a user
|
||||
```sh
|
||||
$PREFIX tag <nickname> <tags>
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user tag <nickname> <tags>
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user tag <nickname> <tags>
|
||||
```
|
||||
|
||||
|
||||
## Delete tags from a user
|
||||
```sh
|
||||
$PREFIX untag <nickname> <tags>
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user untag <nickname> <tags>
|
||||
```
|
||||
|
||||
## Toggle confirmation status of the user
|
||||
```sh
|
||||
$PREFIX toggle_confirmed <nickname>
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user untag <nickname> <tags>
|
||||
```
|
||||
|
||||
|
||||
## Toggle confirmation status of the user
|
||||
```sh tab="OTP"
|
||||
./bin/pleroma_ctl user toggle_confirmed <nickname>
|
||||
```
|
||||
|
||||
```sh tab="From Source"
|
||||
mix pleroma.user toggle_confirmed <nickname>
|
||||
```
|
||||
|
||||
|
@ -12,6 +12,7 @@ You shouldn't edit the base config directly to avoid breakages and merge conflic
|
||||
* `notify_email`: Email used for notifications.
|
||||
* `description`: The instance’s description, can be seen in nodeinfo and ``/api/v1/instance``.
|
||||
* `limit`: Posts character limit (CW/Subject included in the counter).
|
||||
* `chat_limit`: Character limit of the instance chat messages.
|
||||
* `remote_limit`: Hard character limit beyond which remote posts will be dropped.
|
||||
* `upload_limit`: File size limit of uploads (except for avatar, background, banner).
|
||||
* `avatar_upload_limit`: File size limit of user’s profile avatars.
|
||||
|
@ -52,7 +52,9 @@ defmodule Mix.Tasks.Pleroma.Config do
|
||||
|> Enum.each(fn config ->
|
||||
IO.write(
|
||||
file,
|
||||
"config :#{config.group}, #{config.key}, #{inspect(Config.from_binary(config.value))}\r\n\r\n"
|
||||
"config :#{config.group}, #{config.key}, #{
|
||||
inspect(Config.from_binary(config.value), limit: :infinity)
|
||||
}\r\n\r\n"
|
||||
)
|
||||
|
||||
if delete? do
|
||||
|
83
lib/mix/tasks/pleroma/notification_settings.ex
Normal file
83
lib/mix/tasks/pleroma/notification_settings.ex
Normal file
@ -0,0 +1,83 @@
|
||||
defmodule Mix.Tasks.Pleroma.NotificationSettings do
|
||||
@shortdoc "Enable&Disable privacy option for push notifications"
|
||||
@moduledoc """
|
||||
Example:
|
||||
|
||||
> mix pleroma.notification_settings --privacy-option=false --nickname-users="parallel588" # set false only for parallel588 user
|
||||
> mix pleroma.notification_settings --privacy-option=true # set true for all users
|
||||
|
||||
"""
|
||||
|
||||
use Mix.Task
|
||||
import Mix.Pleroma
|
||||
import Ecto.Query
|
||||
|
||||
def run(args) do
|
||||
start_pleroma()
|
||||
|
||||
{options, _, _} =
|
||||
OptionParser.parse(
|
||||
args,
|
||||
strict: [
|
||||
privacy_option: :boolean,
|
||||
email_users: :string,
|
||||
nickname_users: :string
|
||||
]
|
||||
)
|
||||
|
||||
privacy_option = Keyword.get(options, :privacy_option)
|
||||
|
||||
if not is_nil(privacy_option) do
|
||||
privacy_option
|
||||
|> build_query(options)
|
||||
|> Pleroma.Repo.update_all([])
|
||||
end
|
||||
|
||||
shell_info("Done")
|
||||
end
|
||||
|
||||
defp build_query(privacy_option, options) do
|
||||
query =
|
||||
from(u in Pleroma.User,
|
||||
update: [
|
||||
set: [
|
||||
notification_settings:
|
||||
fragment(
|
||||
"jsonb_set(notification_settings, '{privacy_option}', ?)",
|
||||
^privacy_option
|
||||
)
|
||||
]
|
||||
]
|
||||
)
|
||||
|
||||
user_emails =
|
||||
options
|
||||
|> Keyword.get(:email_users, "")
|
||||
|> String.split(",")
|
||||
|> Enum.map(&String.trim(&1))
|
||||
|> Enum.reject(&(&1 == ""))
|
||||
|
||||
query =
|
||||
if length(user_emails) > 0 do
|
||||
where(query, [u], u.email in ^user_emails)
|
||||
else
|
||||
query
|
||||
end
|
||||
|
||||
user_nicknames =
|
||||
options
|
||||
|> Keyword.get(:nickname_users, "")
|
||||
|> String.split(",")
|
||||
|> Enum.map(&String.trim(&1))
|
||||
|> Enum.reject(&(&1 == ""))
|
||||
|
||||
query =
|
||||
if length(user_nicknames) > 0 do
|
||||
where(query, [u], u.nickname in ^user_nicknames)
|
||||
else
|
||||
query
|
||||
end
|
||||
|
||||
query
|
||||
end
|
||||
end
|
@ -8,7 +8,6 @@ defmodule Mix.Tasks.Pleroma.User do
|
||||
alias Ecto.Changeset
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserInviteToken
|
||||
alias Pleroma.Web.OAuth
|
||||
|
||||
@shortdoc "Manages Pleroma users"
|
||||
@moduledoc File.read!("docs/administration/CLI_tasks/user.md")
|
||||
@ -354,8 +353,7 @@ defmodule Mix.Tasks.Pleroma.User do
|
||||
start_pleroma()
|
||||
|
||||
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
||||
OAuth.Token.delete_user_tokens(user)
|
||||
OAuth.Authorization.delete_user_authorizations(user)
|
||||
User.global_sign_out(user)
|
||||
|
||||
shell_info("#{nickname} signed out from all apps.")
|
||||
else
|
||||
@ -373,9 +371,9 @@ defmodule Mix.Tasks.Pleroma.User do
|
||||
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}"
|
||||
"#{user.nickname} moderator: #{user.is_moderator}, admin: #{user.is_admin}, locked: #{
|
||||
user.locked
|
||||
}, deactivated: #{user.deactivated}"
|
||||
)
|
||||
end)
|
||||
end)
|
||||
@ -393,10 +391,7 @@ defmodule Mix.Tasks.Pleroma.User do
|
||||
end
|
||||
|
||||
defp set_admin(user, value) do
|
||||
{:ok, user} =
|
||||
user
|
||||
|> Changeset.change(%{is_admin: value})
|
||||
|> User.update_and_set_cache()
|
||||
{:ok, user} = User.admin_api_update(user, %{is_admin: value})
|
||||
|
||||
shell_info("Admin status of #{user.nickname}: #{user.is_admin}")
|
||||
user
|
||||
|
@ -28,7 +28,8 @@ defmodule Pleroma.Activity do
|
||||
"Create" => "mention",
|
||||
"Follow" => "follow",
|
||||
"Announce" => "reblog",
|
||||
"Like" => "favourite"
|
||||
"Like" => "favourite",
|
||||
"Move" => "move"
|
||||
}
|
||||
|
||||
@mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types,
|
||||
@ -240,9 +241,10 @@ defmodule Pleroma.Activity do
|
||||
def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id_with_object(ap_id)
|
||||
def normalize(_), do: nil
|
||||
|
||||
def delete_by_ap_id(id) when is_binary(id) do
|
||||
def delete_all_by_object_ap_id(id) when is_binary(id) do
|
||||
id
|
||||
|> Queries.by_object_id()
|
||||
|> Queries.exclude_type("Delete")
|
||||
|> select([u], u)
|
||||
|> Repo.delete_all()
|
||||
|> elem(1)
|
||||
@ -254,7 +256,7 @@ defmodule Pleroma.Activity do
|
||||
|> purge_web_resp_cache()
|
||||
end
|
||||
|
||||
def delete_by_ap_id(_), do: nil
|
||||
def delete_all_by_object_ap_id(_), do: nil
|
||||
|
||||
defp purge_web_resp_cache(%Activity{} = activity) do
|
||||
%{path: path} = URI.parse(activity.data["id"])
|
||||
|
@ -64,4 +64,12 @@ defmodule Pleroma.Activity.Queries do
|
||||
where: fragment("(?)->>'type' = ?", activity.data, ^activity_type)
|
||||
)
|
||||
end
|
||||
|
||||
@spec exclude_type(query, String.t()) :: query
|
||||
def exclude_type(query \\ Activity, activity_type) do
|
||||
from(
|
||||
activity in query,
|
||||
where: fragment("(?)->>'type' != ?", activity.data, ^activity_type)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -86,7 +86,7 @@ defmodule Pleroma.Activity.Search do
|
||||
{:ok, object} <- Fetcher.fetch_object_from_id(search_query),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
activities ++ [activity]
|
||||
[activity | activities]
|
||||
else
|
||||
_ -> activities
|
||||
end
|
||||
|
@ -30,6 +30,7 @@ defmodule Pleroma.Application do
|
||||
# See http://elixir-lang.org/docs/stable/elixir/Application.html
|
||||
# for more information on OTP Applications
|
||||
def start(_type, _args) do
|
||||
Pleroma.HTML.compile_scrubbers()
|
||||
Pleroma.Config.DeprecationWarnings.warn()
|
||||
setup_instrumenters()
|
||||
|
||||
@ -147,8 +148,6 @@ defmodule Pleroma.Application do
|
||||
|
||||
defp oauth_cleanup_child(_), do: []
|
||||
|
||||
defp chat_child(:test, _), do: []
|
||||
|
||||
defp chat_child(_env, true) do
|
||||
[Pleroma.Web.ChatChannel.ChatChannelState]
|
||||
end
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
defmodule Pleroma.Clippy do
|
||||
@moduledoc false
|
||||
|
||||
# No software is complete until they have a Clippy implementation.
|
||||
# A ballmer peak _may_ be required to change this module.
|
||||
|
||||
|
@ -65,4 +65,11 @@ defmodule Pleroma.Config do
|
||||
def oauth_consumer_strategies, do: get([:auth, :oauth_consumer_strategies], [])
|
||||
|
||||
def oauth_consumer_enabled?, do: oauth_consumer_strategies() != []
|
||||
|
||||
def enforce_oauth_admin_scope_usage?, do: !!get([:auth, :enforce_oauth_admin_scope_usage])
|
||||
|
||||
def oauth_admin_scopes(scope) do
|
||||
["admin:#{scope}"] ++
|
||||
if enforce_oauth_admin_scope_usage?(), do: [], else: [scope]
|
||||
end
|
||||
end
|
||||
|
13
lib/pleroma/ecto_enums.ex
Normal file
13
lib/pleroma/ecto_enums.ex
Normal file
@ -0,0 +1,13 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import EctoEnum
|
||||
|
||||
defenum(UserRelationshipTypeEnum,
|
||||
block: 1,
|
||||
mute: 2,
|
||||
reblog_mute: 3,
|
||||
notification_mute: 4,
|
||||
inverse_subscription: 5
|
||||
)
|
@ -107,4 +107,26 @@ defmodule Pleroma.FollowingRelationship do
|
||||
[user.follower_address | following]
|
||||
end
|
||||
end
|
||||
|
||||
def move_following(origin, target) do
|
||||
__MODULE__
|
||||
|> join(:inner, [r], f in assoc(r, :follower))
|
||||
|> where(following_id: ^origin.id)
|
||||
|> where([r, f], f.allow_following_move == true)
|
||||
|> limit(50)
|
||||
|> preload([:follower])
|
||||
|> Repo.all()
|
||||
|> Enum.map(fn following_relationship ->
|
||||
Repo.delete(following_relationship)
|
||||
Pleroma.Web.CommonAPI.follow(following_relationship.follower, target)
|
||||
end)
|
||||
|> case do
|
||||
[] ->
|
||||
User.update_follower_count(origin)
|
||||
:ok
|
||||
|
||||
_ ->
|
||||
move_following(origin, target)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -3,6 +3,25 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTML do
|
||||
# Scrubbers are compiled on boot so they can be configured in OTP releases
|
||||
# @on_load :compile_scrubbers
|
||||
|
||||
def compile_scrubbers do
|
||||
dir = Path.join(:code.priv_dir(:pleroma), "scrubbers")
|
||||
|
||||
dir
|
||||
|> File.ls!()
|
||||
|> Enum.map(&Path.join(dir, &1))
|
||||
|> Kernel.ParallelCompiler.compile()
|
||||
|> case do
|
||||
{:error, _errors, _warnings} ->
|
||||
raise "Compiling scrubbers failed"
|
||||
|
||||
{:ok, _modules, _warnings} ->
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp get_scrubbers(scrubber) when is_atom(scrubber), do: [scrubber]
|
||||
defp get_scrubbers(scrubbers) when is_list(scrubbers), do: scrubbers
|
||||
defp get_scrubbers(_), do: [Pleroma.HTML.Scrubber.Default]
|
||||
@ -99,215 +118,3 @@ defmodule Pleroma.HTML do
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Pleroma.HTML.Scrubber.TwitterText do
|
||||
@moduledoc """
|
||||
An HTML scrubbing policy which limits to twitter-style text. Only
|
||||
paragraphs, breaks and links are allowed through the filter.
|
||||
"""
|
||||
|
||||
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
|
||||
|
||||
require FastSanitize.Sanitizer.Meta
|
||||
alias FastSanitize.Sanitizer.Meta
|
||||
|
||||
Meta.strip_comments()
|
||||
|
||||
# links
|
||||
Meta.allow_tag_with_uri_attributes(:a, ["href", "data-user", "data-tag"], @valid_schemes)
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:a, "class", [
|
||||
"hashtag",
|
||||
"u-url",
|
||||
"mention",
|
||||
"u-url mention",
|
||||
"mention u-url"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:a, "rel", [
|
||||
"tag",
|
||||
"nofollow",
|
||||
"noopener",
|
||||
"noreferrer"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:a, ["name", "title"])
|
||||
|
||||
# paragraphs and linebreaks
|
||||
Meta.allow_tag_with_these_attributes(:br, [])
|
||||
Meta.allow_tag_with_these_attributes(:p, [])
|
||||
|
||||
# microformats
|
||||
Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card"])
|
||||
Meta.allow_tag_with_these_attributes(:span, [])
|
||||
|
||||
# allow inline images for custom emoji
|
||||
if Pleroma.Config.get([:markup, :allow_inline_images]) do
|
||||
# restrict img tags to http/https only, because of MediaProxy.
|
||||
Meta.allow_tag_with_uri_attributes(:img, ["src"], ["http", "https"])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:img, [
|
||||
"width",
|
||||
"height",
|
||||
"class",
|
||||
"title",
|
||||
"alt"
|
||||
])
|
||||
end
|
||||
|
||||
Meta.strip_everything_not_covered()
|
||||
end
|
||||
|
||||
defmodule Pleroma.HTML.Scrubber.Default do
|
||||
@doc "The default HTML scrubbing policy: no "
|
||||
|
||||
require FastSanitize.Sanitizer.Meta
|
||||
alias FastSanitize.Sanitizer.Meta
|
||||
# credo:disable-for-previous-line
|
||||
# No idea how to fix this one…
|
||||
|
||||
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
|
||||
|
||||
Meta.strip_comments()
|
||||
|
||||
Meta.allow_tag_with_uri_attributes(:a, ["href", "data-user", "data-tag"], @valid_schemes)
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:a, "class", [
|
||||
"hashtag",
|
||||
"u-url",
|
||||
"mention",
|
||||
"u-url mention",
|
||||
"mention u-url"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:a, "rel", [
|
||||
"tag",
|
||||
"nofollow",
|
||||
"noopener",
|
||||
"noreferrer",
|
||||
"ugc"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:a, ["name", "title"])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:abbr, ["title"])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:b, [])
|
||||
Meta.allow_tag_with_these_attributes(:blockquote, [])
|
||||
Meta.allow_tag_with_these_attributes(:br, [])
|
||||
Meta.allow_tag_with_these_attributes(:code, [])
|
||||
Meta.allow_tag_with_these_attributes(:del, [])
|
||||
Meta.allow_tag_with_these_attributes(:em, [])
|
||||
Meta.allow_tag_with_these_attributes(:i, [])
|
||||
Meta.allow_tag_with_these_attributes(:li, [])
|
||||
Meta.allow_tag_with_these_attributes(:ol, [])
|
||||
Meta.allow_tag_with_these_attributes(:p, [])
|
||||
Meta.allow_tag_with_these_attributes(:pre, [])
|
||||
Meta.allow_tag_with_these_attributes(:strong, [])
|
||||
Meta.allow_tag_with_these_attributes(:sub, [])
|
||||
Meta.allow_tag_with_these_attributes(:sup, [])
|
||||
Meta.allow_tag_with_these_attributes(:u, [])
|
||||
Meta.allow_tag_with_these_attributes(:ul, [])
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card"])
|
||||
Meta.allow_tag_with_these_attributes(:span, [])
|
||||
|
||||
@allow_inline_images Pleroma.Config.get([:markup, :allow_inline_images])
|
||||
|
||||
if @allow_inline_images do
|
||||
# restrict img tags to http/https only, because of MediaProxy.
|
||||
Meta.allow_tag_with_uri_attributes(:img, ["src"], ["http", "https"])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:img, [
|
||||
"width",
|
||||
"height",
|
||||
"class",
|
||||
"title",
|
||||
"alt"
|
||||
])
|
||||
end
|
||||
|
||||
if Pleroma.Config.get([:markup, :allow_tables]) do
|
||||
Meta.allow_tag_with_these_attributes(:table, [])
|
||||
Meta.allow_tag_with_these_attributes(:tbody, [])
|
||||
Meta.allow_tag_with_these_attributes(:td, [])
|
||||
Meta.allow_tag_with_these_attributes(:th, [])
|
||||
Meta.allow_tag_with_these_attributes(:thead, [])
|
||||
Meta.allow_tag_with_these_attributes(:tr, [])
|
||||
end
|
||||
|
||||
if Pleroma.Config.get([:markup, :allow_headings]) do
|
||||
Meta.allow_tag_with_these_attributes(:h1, [])
|
||||
Meta.allow_tag_with_these_attributes(:h2, [])
|
||||
Meta.allow_tag_with_these_attributes(:h3, [])
|
||||
Meta.allow_tag_with_these_attributes(:h4, [])
|
||||
Meta.allow_tag_with_these_attributes(:h5, [])
|
||||
end
|
||||
|
||||
if Pleroma.Config.get([:markup, :allow_fonts]) do
|
||||
Meta.allow_tag_with_these_attributes(:font, ["face"])
|
||||
end
|
||||
|
||||
Meta.strip_everything_not_covered()
|
||||
end
|
||||
|
||||
defmodule Pleroma.HTML.Transform.MediaProxy do
|
||||
@moduledoc "Transforms inline image URIs to use MediaProxy."
|
||||
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
def before_scrub(html), do: html
|
||||
|
||||
def scrub_attribute(:img, {"src", "http" <> target}) do
|
||||
media_url =
|
||||
("http" <> target)
|
||||
|> MediaProxy.url()
|
||||
|
||||
{"src", media_url}
|
||||
end
|
||||
|
||||
def scrub_attribute(_tag, attribute), do: attribute
|
||||
|
||||
def scrub({:img, attributes, children}) do
|
||||
attributes =
|
||||
attributes
|
||||
|> Enum.map(fn attr -> scrub_attribute(:img, attr) end)
|
||||
|> Enum.reject(&is_nil(&1))
|
||||
|
||||
{:img, attributes, children}
|
||||
end
|
||||
|
||||
def scrub({:comment, _text, _children}), do: ""
|
||||
|
||||
def scrub({tag, attributes, children}), do: {tag, attributes, children}
|
||||
def scrub({_tag, children}), do: children
|
||||
def scrub(text), do: text
|
||||
end
|
||||
|
||||
defmodule Pleroma.HTML.Scrubber.LinksOnly do
|
||||
@moduledoc """
|
||||
An HTML scrubbing policy which limits to links only.
|
||||
"""
|
||||
|
||||
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
|
||||
|
||||
require FastSanitize.Sanitizer.Meta
|
||||
alias FastSanitize.Sanitizer.Meta
|
||||
|
||||
Meta.strip_comments()
|
||||
|
||||
# links
|
||||
Meta.allow_tag_with_uri_attributes(:a, ["href"], @valid_schemes)
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:a, "rel", [
|
||||
"tag",
|
||||
"nofollow",
|
||||
"noopener",
|
||||
"noreferrer",
|
||||
"me",
|
||||
"ugc"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:a, ["name", "title"])
|
||||
Meta.strip_everything_not_covered()
|
||||
end
|
||||
|
@ -21,6 +21,8 @@ defmodule Pleroma.Notification do
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
@include_muted_option :with_muted
|
||||
|
||||
schema "notifications" do
|
||||
field(:seen, :boolean, default: false)
|
||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||
@ -34,7 +36,25 @@ defmodule Pleroma.Notification do
|
||||
|> cast(attrs, [:seen])
|
||||
end
|
||||
|
||||
def for_user_query(user, opts \\ []) do
|
||||
defp for_user_query_ap_id_opts(user, opts) do
|
||||
ap_id_relations =
|
||||
[:block] ++
|
||||
if opts[@include_muted_option], do: [], else: [:notification_mute]
|
||||
|
||||
preloaded_ap_ids = User.outgoing_relations_ap_ids(user, ap_id_relations)
|
||||
|
||||
exclude_blocked_opts = Map.merge(%{blocked_users_ap_ids: preloaded_ap_ids[:block]}, opts)
|
||||
|
||||
exclude_notification_muted_opts =
|
||||
Map.merge(%{notification_muted_users_ap_ids: preloaded_ap_ids[:notification_mute]}, opts)
|
||||
|
||||
{exclude_blocked_opts, exclude_notification_muted_opts}
|
||||
end
|
||||
|
||||
def for_user_query(user, opts \\ %{}) do
|
||||
{exclude_blocked_opts, exclude_notification_muted_opts} =
|
||||
for_user_query_ap_id_opts(user, opts)
|
||||
|
||||
Notification
|
||||
|> where(user_id: ^user.id)
|
||||
|> where(
|
||||
@ -54,43 +74,75 @@ defmodule Pleroma.Notification do
|
||||
)
|
||||
)
|
||||
|> preload([n, a, o], activity: {a, object: o})
|
||||
|> exclude_muted(user, opts)
|
||||
|> exclude_blocked(user)
|
||||
|> exclude_notification_muted(user, exclude_notification_muted_opts)
|
||||
|> exclude_blocked(user, exclude_blocked_opts)
|
||||
|> exclude_visibility(opts)
|
||||
|> exclude_move(opts)
|
||||
end
|
||||
|
||||
defp exclude_blocked(query, user) do
|
||||
defp exclude_blocked(query, user, opts) do
|
||||
blocked_ap_ids = opts[:blocked_users_ap_ids] || User.blocked_users_ap_ids(user)
|
||||
|
||||
query
|
||||
|> where([n, a], a.actor not in ^user.blocks)
|
||||
|> where([n, a], a.actor not in ^blocked_ap_ids)
|
||||
|> where(
|
||||
[n, a],
|
||||
fragment("substring(? from '.*://([^/]*)')", a.actor) not in ^user.domain_blocks
|
||||
)
|
||||
end
|
||||
|
||||
defp exclude_muted(query, _, %{with_muted: true}) do
|
||||
defp exclude_notification_muted(query, _, %{@include_muted_option => true}) do
|
||||
query
|
||||
end
|
||||
|
||||
defp exclude_muted(query, user, _opts) do
|
||||
defp exclude_notification_muted(query, user, opts) do
|
||||
notification_muted_ap_ids =
|
||||
opts[:notification_muted_users_ap_ids] || User.notification_muted_users_ap_ids(user)
|
||||
|
||||
query
|
||||
|> where([n, a], a.actor not in ^user.muted_notifications)
|
||||
|> where([n, a], a.actor not in ^notification_muted_ap_ids)
|
||||
|> join(:left, [n, a], tm in Pleroma.ThreadMute,
|
||||
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data)
|
||||
)
|
||||
|> where([n, a, o, tm], is_nil(tm.user_id))
|
||||
end
|
||||
|
||||
defp exclude_move(query, %{with_move: true}) do
|
||||
query
|
||||
end
|
||||
|
||||
defp exclude_move(query, _opts) do
|
||||
where(query, [n, a], fragment("?->>'type' != 'Move'", a.data))
|
||||
end
|
||||
|
||||
@valid_visibilities ~w[direct unlisted public private]
|
||||
|
||||
defp exclude_visibility(query, %{exclude_visibilities: visibility})
|
||||
when is_list(visibility) do
|
||||
if Enum.all?(visibility, &(&1 in @valid_visibilities)) do
|
||||
query
|
||||
|> join(:left, [n, a], mutated_activity in Pleroma.Activity,
|
||||
on:
|
||||
fragment("?->>'context'", a.data) ==
|
||||
fragment("?->>'context'", mutated_activity.data) and
|
||||
fragment("(?->>'type' = 'Like' or ?->>'type' = 'Announce')", a.data, a.data) and
|
||||
fragment("?->>'type'", mutated_activity.data) == "Create",
|
||||
as: :mutated_activity
|
||||
)
|
||||
|> where(
|
||||
[n, a],
|
||||
[n, a, mutated_activity: mutated_activity],
|
||||
not fragment(
|
||||
"activity_visibility(?, ?, ?) = ANY (?)",
|
||||
"""
|
||||
CASE WHEN (?->>'type') = 'Like' or (?->>'type') = 'Announce'
|
||||
THEN (activity_visibility(?, ?, ?) = ANY (?))
|
||||
ELSE (activity_visibility(?, ?, ?) = ANY (?)) END
|
||||
""",
|
||||
a.data,
|
||||
a.data,
|
||||
mutated_activity.actor,
|
||||
mutated_activity.recipients,
|
||||
mutated_activity.data,
|
||||
^visibility,
|
||||
a.actor,
|
||||
a.recipients,
|
||||
a.data,
|
||||
@ -105,17 +157,7 @@ defmodule Pleroma.Notification do
|
||||
|
||||
defp exclude_visibility(query, %{exclude_visibilities: visibility})
|
||||
when visibility in @valid_visibilities do
|
||||
query
|
||||
|> where(
|
||||
[n, a],
|
||||
not fragment(
|
||||
"activity_visibility(?, ?, ?) = (?)",
|
||||
a.actor,
|
||||
a.recipients,
|
||||
a.data,
|
||||
^visibility
|
||||
)
|
||||
)
|
||||
exclude_visibility(query, [visibility])
|
||||
end
|
||||
|
||||
defp exclude_visibility(query, %{exclude_visibilities: visibility})
|
||||
@ -251,10 +293,13 @@ defmodule Pleroma.Notification do
|
||||
end
|
||||
end
|
||||
|
||||
def create_notifications(%Activity{data: %{"to" => _, "type" => type}} = activity)
|
||||
when type in ["Like", "Announce", "Follow"] do
|
||||
users = get_notified_from_activity(activity)
|
||||
notifications = Enum.map(users, fn user -> create_notification(activity, user) end)
|
||||
def create_notifications(%Activity{data: %{"type" => type}} = activity)
|
||||
when type in ["Like", "Announce", "Follow", "Move"] do
|
||||
notifications =
|
||||
activity
|
||||
|> get_notified_from_activity()
|
||||
|> Enum.map(&create_notification(activity, &1))
|
||||
|
||||
{:ok, notifications}
|
||||
end
|
||||
|
||||
@ -276,19 +321,15 @@ defmodule Pleroma.Notification do
|
||||
|
||||
def get_notified_from_activity(activity, local_only \\ true)
|
||||
|
||||
def get_notified_from_activity(
|
||||
%Activity{data: %{"to" => _, "type" => type} = _data} = activity,
|
||||
local_only
|
||||
)
|
||||
when type in ["Create", "Like", "Announce", "Follow"] do
|
||||
recipients =
|
||||
[]
|
||||
|> Utils.maybe_notify_to_recipients(activity)
|
||||
|> Utils.maybe_notify_mentioned_recipients(activity)
|
||||
|> Utils.maybe_notify_subscribers(activity)
|
||||
|> Enum.uniq()
|
||||
|
||||
User.get_users_from_set(recipients, local_only)
|
||||
def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only)
|
||||
when type in ["Create", "Like", "Announce", "Follow", "Move"] do
|
||||
[]
|
||||
|> Utils.maybe_notify_to_recipients(activity)
|
||||
|> Utils.maybe_notify_mentioned_recipients(activity)
|
||||
|> Utils.maybe_notify_subscribers(activity)
|
||||
|> Utils.maybe_notify_followers(activity)
|
||||
|> Enum.uniq()
|
||||
|> User.get_users_from_set(local_only)
|
||||
end
|
||||
|
||||
def get_notified_from_activity(_, _local_only), do: []
|
||||
@ -314,7 +355,7 @@ defmodule Pleroma.Notification do
|
||||
def skip?(
|
||||
:followers,
|
||||
activity,
|
||||
%{notification_settings: %{"followers" => false}} = user
|
||||
%{notification_settings: %{followers: false}} = user
|
||||
) do
|
||||
actor = activity.data["actor"]
|
||||
follower = User.get_cached_by_ap_id(actor)
|
||||
@ -324,14 +365,14 @@ defmodule Pleroma.Notification do
|
||||
def skip?(
|
||||
:non_followers,
|
||||
activity,
|
||||
%{notification_settings: %{"non_followers" => false}} = user
|
||||
%{notification_settings: %{non_followers: false}} = user
|
||||
) do
|
||||
actor = activity.data["actor"]
|
||||
follower = User.get_cached_by_ap_id(actor)
|
||||
!User.following?(follower, user)
|
||||
end
|
||||
|
||||
def skip?(:follows, activity, %{notification_settings: %{"follows" => false}} = user) do
|
||||
def skip?(:follows, activity, %{notification_settings: %{follows: false}} = user) do
|
||||
actor = activity.data["actor"]
|
||||
followed = User.get_cached_by_ap_id(actor)
|
||||
User.following?(user, followed)
|
||||
@ -340,7 +381,7 @@ defmodule Pleroma.Notification do
|
||||
def skip?(
|
||||
:non_follows,
|
||||
activity,
|
||||
%{notification_settings: %{"non_follows" => false}} = user
|
||||
%{notification_settings: %{non_follows: false}} = user
|
||||
) do
|
||||
actor = activity.data["actor"]
|
||||
followed = User.get_cached_by_ap_id(actor)
|
||||
|
@ -147,7 +147,7 @@ defmodule Pleroma.Object do
|
||||
|
||||
def delete(%Object{data: %{"id" => id}} = object) do
|
||||
with {:ok, _obj} = swap_object_with_tombstone(object),
|
||||
deleted_activity = Activity.delete_by_ap_id(id),
|
||||
deleted_activity = Activity.delete_all_by_object_ap_id(id),
|
||||
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}"),
|
||||
{:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path) do
|
||||
{:ok, object, deleted_activity}
|
||||
|
@ -6,6 +6,7 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
|
||||
import Plug.Conn
|
||||
import Pleroma.Web.Gettext
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
|
||||
@behaviour Plug
|
||||
@ -15,6 +16,14 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
|
||||
def call(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
||||
op = options[:op] || :|
|
||||
token = assigns[:token]
|
||||
|
||||
scopes =
|
||||
if options[:admin] do
|
||||
Config.oauth_admin_scopes(scopes)
|
||||
else
|
||||
scopes
|
||||
end
|
||||
|
||||
matched_scopes = token && filter_descendants(scopes, token.scopes)
|
||||
|
||||
cond do
|
||||
|
21
lib/pleroma/plugs/parsers_plug.ex
Normal file
21
lib/pleroma/plugs/parsers_plug.ex
Normal file
@ -0,0 +1,21 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Plugs.Parsers do
|
||||
@moduledoc "Initializes Plug.Parsers with upload limit set at boot time"
|
||||
|
||||
@behaviour Plug
|
||||
|
||||
def init(_opts) do
|
||||
Plug.Parsers.init(
|
||||
parsers: [:urlencoded, :multipart, :json],
|
||||
pass: ["*/*"],
|
||||
json_decoder: Jason,
|
||||
length: Pleroma.Config.get([:instance, :upload_limit]),
|
||||
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
|
||||
)
|
||||
end
|
||||
|
||||
defdelegate call(conn, opts), to: Plug.Parsers
|
||||
end
|
@ -5,19 +5,38 @@
|
||||
defmodule Pleroma.Plugs.UserIsAdminPlug do
|
||||
import Pleroma.Web.TranslationHelpers
|
||||
import Plug.Conn
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth
|
||||
|
||||
def init(options) do
|
||||
options
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _) do
|
||||
conn
|
||||
def call(%{assigns: %{user: %User{is_admin: true}} = assigns} = conn, _) do
|
||||
token = assigns[:token]
|
||||
|
||||
cond do
|
||||
not Pleroma.Config.enforce_oauth_admin_scope_usage?() ->
|
||||
conn
|
||||
|
||||
token && OAuth.Scopes.contains_admin_scopes?(token.scopes) ->
|
||||
# Note: checking for _any_ admin scope presence, not necessarily fitting requested action.
|
||||
# Thus, controller must explicitly invoke OAuthScopesPlug to verify scope requirements.
|
||||
conn
|
||||
|
||||
true ->
|
||||
fail(conn)
|
||||
end
|
||||
end
|
||||
|
||||
def call(conn, _) do
|
||||
fail(conn)
|
||||
end
|
||||
|
||||
defp fail(conn) do
|
||||
conn
|
||||
|> render_error(:forbidden, "User is not admin.")
|
||||
|> halt
|
||||
|> render_error(:forbidden, "User is not an admin or OAuth admin scope is not granted.")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
@ -7,6 +7,7 @@ defmodule Pleroma.User do
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
import Ecto, only: [assoc: 2]
|
||||
|
||||
alias Comeonin.Pbkdf2
|
||||
alias Ecto.Multi
|
||||
@ -21,6 +22,7 @@ defmodule Pleroma.User do
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.RepoStreamer
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
@ -42,6 +44,32 @@ defmodule Pleroma.User do
|
||||
@strict_local_nickname_regex ~r/^[a-zA-Z\d]+$/
|
||||
@extended_local_nickname_regex ~r/^[a-zA-Z\d_-]+$/
|
||||
|
||||
# AP ID user relationships (blocks, mutes etc.)
|
||||
# Format: [rel_type: [outgoing_rel: :outgoing_rel_target, incoming_rel: :incoming_rel_source]]
|
||||
@user_relationships_config [
|
||||
block: [
|
||||
blocker_blocks: :blocked_users,
|
||||
blockee_blocks: :blocker_users
|
||||
],
|
||||
mute: [
|
||||
muter_mutes: :muted_users,
|
||||
mutee_mutes: :muter_users
|
||||
],
|
||||
reblog_mute: [
|
||||
reblog_muter_mutes: :reblog_muted_users,
|
||||
reblog_mutee_mutes: :reblog_muter_users
|
||||
],
|
||||
notification_mute: [
|
||||
notification_muter_mutes: :notification_muted_users,
|
||||
notification_mutee_mutes: :notification_muter_users
|
||||
],
|
||||
# Note: `inverse_subscription` relationship is inverse: subscriber acts as relationship target
|
||||
inverse_subscription: [
|
||||
subscribee_subscriptions: :subscriber_users,
|
||||
subscriber_subscriptions: :subscribee_users
|
||||
]
|
||||
]
|
||||
|
||||
schema "users" do
|
||||
field(:bio, :string)
|
||||
field(:email, :string)
|
||||
@ -61,7 +89,6 @@ defmodule Pleroma.User do
|
||||
field(:tags, {:array, :string}, default: [])
|
||||
field(:last_refreshed_at, :naive_datetime_usec)
|
||||
field(:last_digest_emailed_at, :naive_datetime)
|
||||
|
||||
field(:banner, :map, default: %{})
|
||||
field(:background, :map, default: %{})
|
||||
field(:source_data, :map, default: %{})
|
||||
@ -73,12 +100,7 @@ defmodule Pleroma.User do
|
||||
field(:password_reset_pending, :boolean, default: false)
|
||||
field(:confirmation_token, :string, default: nil)
|
||||
field(:default_scope, :string, default: "public")
|
||||
field(:blocks, {:array, :string}, default: [])
|
||||
field(:domain_blocks, {:array, :string}, default: [])
|
||||
field(:mutes, {:array, :string}, default: [])
|
||||
field(:muted_reblogs, {:array, :string}, default: [])
|
||||
field(:muted_notifications, {:array, :string}, default: [])
|
||||
field(:subscribers, {:array, :string}, default: [])
|
||||
field(:deactivated, :boolean, default: false)
|
||||
field(:no_rich_text, :boolean, default: false)
|
||||
field(:ap_enabled, :boolean, default: false)
|
||||
@ -103,26 +125,97 @@ defmodule Pleroma.User do
|
||||
field(:raw_fields, {:array, :map}, default: [])
|
||||
field(:discoverable, :boolean, default: false)
|
||||
field(:invisible, :boolean, default: false)
|
||||
field(:allow_following_move, :boolean, default: true)
|
||||
field(:skip_thread_containment, :boolean, default: false)
|
||||
field(:actor_type, :string, default: "Person")
|
||||
field(:also_known_as, {:array, :string}, default: [])
|
||||
|
||||
field(:notification_settings, :map,
|
||||
default: %{
|
||||
"followers" => true,
|
||||
"follows" => true,
|
||||
"non_follows" => true,
|
||||
"non_followers" => true
|
||||
}
|
||||
embeds_one(
|
||||
:notification_settings,
|
||||
Pleroma.User.NotificationSetting,
|
||||
on_replace: :update
|
||||
)
|
||||
|
||||
has_many(:notifications, Notification)
|
||||
has_many(:registrations, Registration)
|
||||
has_many(:deliveries, Delivery)
|
||||
|
||||
field(:info, :map, default: %{})
|
||||
has_many(:outgoing_relationships, UserRelationship, foreign_key: :source_id)
|
||||
has_many(:incoming_relationships, UserRelationship, foreign_key: :target_id)
|
||||
|
||||
for {relationship_type,
|
||||
[
|
||||
{outgoing_relation, outgoing_relation_target},
|
||||
{incoming_relation, incoming_relation_source}
|
||||
]} <- @user_relationships_config do
|
||||
# Definitions of `has_many :blocker_blocks`, `has_many :muter_mutes` etc.
|
||||
has_many(outgoing_relation, UserRelationship,
|
||||
foreign_key: :source_id,
|
||||
where: [relationship_type: relationship_type]
|
||||
)
|
||||
|
||||
# Definitions of `has_many :blockee_blocks`, `has_many :mutee_mutes` etc.
|
||||
has_many(incoming_relation, UserRelationship,
|
||||
foreign_key: :target_id,
|
||||
where: [relationship_type: relationship_type]
|
||||
)
|
||||
|
||||
# Definitions of `has_many :blocked_users`, `has_many :muted_users` etc.
|
||||
has_many(outgoing_relation_target, through: [outgoing_relation, :target])
|
||||
|
||||
# Definitions of `has_many :blocker_users`, `has_many :muter_users` etc.
|
||||
has_many(incoming_relation_source, through: [incoming_relation, :source])
|
||||
end
|
||||
|
||||
# `:blocks` is deprecated (replaced with `blocked_users` relation)
|
||||
field(:blocks, {:array, :string}, default: [])
|
||||
# `:mutes` is deprecated (replaced with `muted_users` relation)
|
||||
field(:mutes, {:array, :string}, default: [])
|
||||
# `:muted_reblogs` is deprecated (replaced with `reblog_muted_users` relation)
|
||||
field(:muted_reblogs, {:array, :string}, default: [])
|
||||
# `:muted_notifications` is deprecated (replaced with `notification_muted_users` relation)
|
||||
field(:muted_notifications, {:array, :string}, default: [])
|
||||
# `:subscribers` is deprecated (replaced with `subscriber_users` relation)
|
||||
field(:subscribers, {:array, :string}, default: [])
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
for {_relationship_type, [{_outgoing_relation, outgoing_relation_target}, _]} <-
|
||||
@user_relationships_config do
|
||||
# Definitions of `blocked_users_relation/1`, `muted_users_relation/1`, etc.
|
||||
def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
|
||||
target_users_query = assoc(user, unquote(outgoing_relation_target))
|
||||
|
||||
if restrict_deactivated? do
|
||||
restrict_deactivated(target_users_query)
|
||||
else
|
||||
target_users_query
|
||||
end
|
||||
end
|
||||
|
||||
# Definitions of `blocked_users/1`, `muted_users/1`, etc.
|
||||
def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
|
||||
__MODULE__
|
||||
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
|
||||
user,
|
||||
restrict_deactivated?
|
||||
])
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
# Definitions of `blocked_users_ap_ids/1`, `muted_users_ap_ids/1`, etc.
|
||||
def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do
|
||||
__MODULE__
|
||||
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
|
||||
user,
|
||||
restrict_deactivated?
|
||||
])
|
||||
|> select([u], u.ap_id)
|
||||
|> Repo.all()
|
||||
end
|
||||
end
|
||||
|
||||
@doc "Returns if the user should be allowed to authenticate"
|
||||
def auth_active?(%User{deactivated: true}), do: false
|
||||
|
||||
@ -225,7 +318,6 @@ defmodule Pleroma.User do
|
||||
|
||||
params =
|
||||
params
|
||||
|> Map.put(:info, params[:info] || %{})
|
||||
|> truncate_if_exists(:name, name_limit)
|
||||
|> truncate_if_exists(:bio, bio_limit)
|
||||
|> truncate_fields_param()
|
||||
@ -254,7 +346,9 @@ defmodule Pleroma.User do
|
||||
:fields,
|
||||
:following_count,
|
||||
:discoverable,
|
||||
:invisible
|
||||
:invisible,
|
||||
:actor_type,
|
||||
:also_known_as
|
||||
]
|
||||
)
|
||||
|> validate_required([:name, :ap_id])
|
||||
@ -296,13 +390,16 @@ defmodule Pleroma.User do
|
||||
:hide_followers_count,
|
||||
:hide_follows_count,
|
||||
:hide_favorites,
|
||||
:allow_following_move,
|
||||
:background,
|
||||
:show_role,
|
||||
:skip_thread_containment,
|
||||
:fields,
|
||||
:raw_fields,
|
||||
:pleroma_settings_store,
|
||||
:discoverable
|
||||
:discoverable,
|
||||
:actor_type,
|
||||
:also_known_as
|
||||
]
|
||||
)
|
||||
|> unique_constraint(:nickname)
|
||||
@ -340,9 +437,12 @@ defmodule Pleroma.User do
|
||||
:hide_follows,
|
||||
:fields,
|
||||
:hide_followers,
|
||||
:allow_following_move,
|
||||
:discoverable,
|
||||
:hide_followers_count,
|
||||
:hide_follows_count
|
||||
:hide_follows_count,
|
||||
:actor_type,
|
||||
:also_known_as
|
||||
]
|
||||
)
|
||||
|> unique_constraint(:nickname)
|
||||
@ -949,34 +1049,45 @@ defmodule Pleroma.User do
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec mute(User.t(), User.t(), boolean()) :: {:ok, User.t()} | {:error, String.t()}
|
||||
def mute(muter, %User{ap_id: ap_id}, notifications? \\ true) do
|
||||
add_to_mutes(muter, ap_id, notifications?)
|
||||
@spec mute(User.t(), User.t(), boolean()) ::
|
||||
{:ok, list(UserRelationship.t())} | {:error, String.t()}
|
||||
def mute(%User{} = muter, %User{} = mutee, notifications? \\ true) do
|
||||
add_to_mutes(muter, mutee, notifications?)
|
||||
end
|
||||
|
||||
def unmute(muter, %{ap_id: ap_id}) do
|
||||
remove_from_mutes(muter, ap_id)
|
||||
def unmute(%User{} = muter, %User{} = mutee) do
|
||||
remove_from_mutes(muter, mutee)
|
||||
end
|
||||
|
||||
def subscribe(subscriber, %{ap_id: ap_id}) do
|
||||
with %User{} = subscribed <- get_cached_by_ap_id(ap_id) do
|
||||
deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
|
||||
def subscribe(%User{} = subscriber, %User{} = target) do
|
||||
deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked])
|
||||
|
||||
if blocks?(subscribed, subscriber) and deny_follow_blocked do
|
||||
{:error, "Could not subscribe: #{subscribed.nickname} is blocking you"}
|
||||
else
|
||||
User.add_to_subscribers(subscribed, subscriber.ap_id)
|
||||
end
|
||||
if blocks?(target, subscriber) and deny_follow_blocked do
|
||||
{:error, "Could not subscribe: #{target.nickname} is blocking you"}
|
||||
else
|
||||
# Note: the relationship is inverse: subscriber acts as relationship target
|
||||
UserRelationship.create_inverse_subscription(target, subscriber)
|
||||
end
|
||||
end
|
||||
|
||||
def unsubscribe(unsubscriber, %{ap_id: ap_id}) do
|
||||
def subscribe(%User{} = subscriber, %{ap_id: ap_id}) do
|
||||
with %User{} = subscribee <- get_cached_by_ap_id(ap_id) do
|
||||
subscribe(subscriber, subscribee)
|
||||
end
|
||||
end
|
||||
|
||||
def unsubscribe(%User{} = unsubscriber, %User{} = target) do
|
||||
# Note: the relationship is inverse: subscriber acts as relationship target
|
||||
UserRelationship.delete_inverse_subscription(target, unsubscriber)
|
||||
end
|
||||
|
||||
def unsubscribe(%User{} = unsubscriber, %{ap_id: ap_id}) do
|
||||
with %User{} = user <- get_cached_by_ap_id(ap_id) do
|
||||
User.remove_from_subscribers(user, unsubscriber.ap_id)
|
||||
unsubscribe(unsubscriber, user)
|
||||
end
|
||||
end
|
||||
|
||||
def block(blocker, %User{ap_id: ap_id} = blocked) do
|
||||
def block(%User{} = blocker, %User{} = blocked) do
|
||||
# sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213)
|
||||
blocker =
|
||||
if following?(blocker, blocked) do
|
||||
@ -993,51 +1104,54 @@ defmodule Pleroma.User do
|
||||
nil -> blocked
|
||||
end
|
||||
|
||||
blocker =
|
||||
if subscribed_to?(blocked, blocker) do
|
||||
{:ok, blocker} = unsubscribe(blocked, blocker)
|
||||
blocker
|
||||
else
|
||||
blocker
|
||||
end
|
||||
unsubscribe(blocked, blocker)
|
||||
|
||||
if following?(blocked, blocker), do: unfollow(blocked, blocker)
|
||||
|
||||
{:ok, blocker} = update_follower_count(blocker)
|
||||
{:ok, blocker, _} = Participation.mark_all_as_read(blocker, blocked)
|
||||
add_to_block(blocker, ap_id)
|
||||
add_to_block(blocker, blocked)
|
||||
end
|
||||
|
||||
# helper to handle the block given only an actor's AP id
|
||||
def block(blocker, %{ap_id: ap_id}) do
|
||||
def block(%User{} = blocker, %{ap_id: ap_id}) do
|
||||
block(blocker, get_cached_by_ap_id(ap_id))
|
||||
end
|
||||
|
||||
def unblock(blocker, %{ap_id: ap_id}) do
|
||||
remove_from_block(blocker, ap_id)
|
||||
def unblock(%User{} = blocker, %User{} = blocked) do
|
||||
remove_from_block(blocker, blocked)
|
||||
end
|
||||
|
||||
# helper to handle the block given only an actor's AP id
|
||||
def unblock(%User{} = blocker, %{ap_id: ap_id}) do
|
||||
unblock(blocker, get_cached_by_ap_id(ap_id))
|
||||
end
|
||||
|
||||
def mutes?(nil, _), do: false
|
||||
def mutes?(user, %{ap_id: ap_id}), do: Enum.member?(user.mutes, ap_id)
|
||||
def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
|
||||
|
||||
def mutes_user?(%User{} = user, %User{} = target) do
|
||||
UserRelationship.mute_exists?(user, target)
|
||||
end
|
||||
|
||||
@spec muted_notifications?(User.t() | nil, User.t() | map()) :: boolean()
|
||||
def muted_notifications?(nil, _), do: false
|
||||
|
||||
def muted_notifications?(user, %{ap_id: ap_id}),
|
||||
do: Enum.member?(user.muted_notifications, ap_id)
|
||||
def muted_notifications?(%User{} = user, %User{} = target),
|
||||
do: UserRelationship.notification_mute_exists?(user, target)
|
||||
|
||||
def blocks?(nil, _), do: false
|
||||
|
||||
def blocks?(%User{} = user, %User{} = target) do
|
||||
blocks_ap_id?(user, target) ||
|
||||
(!User.following?(user, target) && blocks_domain?(user, target))
|
||||
end
|
||||
|
||||
def blocks?(nil, _), do: false
|
||||
|
||||
def blocks_ap_id?(%User{} = user, %User{} = target) do
|
||||
Enum.member?(user.blocks, target.ap_id)
|
||||
def blocks_user?(%User{} = user, %User{} = target) do
|
||||
UserRelationship.block_exists?(user, target)
|
||||
end
|
||||
|
||||
def blocks_ap_id?(_, _), do: false
|
||||
def blocks_user?(_, _), do: false
|
||||
|
||||
def blocks_domain?(%User{} = user, %User{} = target) do
|
||||
domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks)
|
||||
@ -1047,28 +1161,41 @@ defmodule Pleroma.User do
|
||||
|
||||
def blocks_domain?(_, _), do: false
|
||||
|
||||
def subscribed_to?(user, %{ap_id: ap_id}) do
|
||||
def subscribed_to?(%User{} = user, %User{} = target) do
|
||||
# Note: the relationship is inverse: subscriber acts as relationship target
|
||||
UserRelationship.inverse_subscription_exists?(target, user)
|
||||
end
|
||||
|
||||
def subscribed_to?(%User{} = user, %{ap_id: ap_id}) do
|
||||
with %User{} = target <- get_cached_by_ap_id(ap_id) do
|
||||
Enum.member?(target.subscribers, user.ap_id)
|
||||
subscribed_to?(user, target)
|
||||
end
|
||||
end
|
||||
|
||||
@spec muted_users(User.t()) :: [User.t()]
|
||||
def muted_users(user) do
|
||||
User.Query.build(%{ap_id: user.mutes, deactivated: false})
|
||||
|> Repo.all()
|
||||
end
|
||||
@doc """
|
||||
Returns map of outgoing (blocked, muted etc.) relations' user AP IDs by relation type.
|
||||
E.g. `outgoing_relations_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}`
|
||||
"""
|
||||
@spec outgoing_relations_ap_ids(User.t(), list(atom())) :: %{atom() => list(String.t())}
|
||||
def outgoing_relations_ap_ids(_, []), do: %{}
|
||||
|
||||
@spec blocked_users(User.t()) :: [User.t()]
|
||||
def blocked_users(user) do
|
||||
User.Query.build(%{ap_id: user.blocks, deactivated: false})
|
||||
|> Repo.all()
|
||||
end
|
||||
def outgoing_relations_ap_ids(%User{} = user, relationship_types)
|
||||
when is_list(relationship_types) do
|
||||
db_result =
|
||||
user
|
||||
|> assoc(:outgoing_relationships)
|
||||
|> join(:inner, [user_rel], u in assoc(user_rel, :target))
|
||||
|> where([user_rel, u], user_rel.relationship_type in ^relationship_types)
|
||||
|> select([user_rel, u], [user_rel.relationship_type, fragment("array_agg(?)", u.ap_id)])
|
||||
|> group_by([user_rel, u], user_rel.relationship_type)
|
||||
|> Repo.all()
|
||||
|> Enum.into(%{}, fn [k, v] -> {k, v} end)
|
||||
|
||||
@spec subscribers(User.t()) :: [User.t()]
|
||||
def subscribers(user) do
|
||||
User.Query.build(%{ap_id: user.subscribers, deactivated: false})
|
||||
|> Repo.all()
|
||||
Enum.into(
|
||||
relationship_types,
|
||||
%{},
|
||||
fn rel_type -> {rel_type, db_result[rel_type] || []} end
|
||||
)
|
||||
end
|
||||
|
||||
def deactivate_async(user, status \\ true) do
|
||||
@ -1103,20 +1230,9 @@ defmodule Pleroma.User do
|
||||
end
|
||||
|
||||
def update_notification_settings(%User{} = user, settings) do
|
||||
settings =
|
||||
settings
|
||||
|> Enum.map(fn {k, v} -> {k, v in [true, "true", "True", "1"]} end)
|
||||
|> Map.new()
|
||||
|
||||
notification_settings =
|
||||
user.notification_settings
|
||||
|> Map.merge(settings)
|
||||
|> Map.take(["followers", "follows", "non_follows", "non_followers"])
|
||||
|
||||
params = %{notification_settings: notification_settings}
|
||||
|
||||
user
|
||||
|> cast(params, [:notification_settings])
|
||||
|> cast(%{notification_settings: settings}, [])
|
||||
|> cast_embed(:notification_settings)
|
||||
|> validate_required([:notification_settings])
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
@ -1175,7 +1291,7 @@ defmodule Pleroma.User do
|
||||
blocked_identifiers,
|
||||
fn blocked_identifier ->
|
||||
with {:ok, %User{} = blocked} <- get_or_fetch(blocked_identifier),
|
||||
{:ok, blocker} <- block(blocker, blocked),
|
||||
{:ok, _user_block} <- block(blocker, blocked),
|
||||
{:ok, _} <- ActivityPub.block(blocker, blocked) do
|
||||
blocked
|
||||
else
|
||||
@ -1219,7 +1335,7 @@ defmodule Pleroma.User do
|
||||
def external_users(opts \\ []) do
|
||||
query =
|
||||
external_users_query()
|
||||
|> select([u], struct(u, [:id, :ap_id, :info]))
|
||||
|> select([u], struct(u, [:id, :ap_id]))
|
||||
|
||||
query =
|
||||
if opts[:max_id],
|
||||
@ -1489,7 +1605,7 @@ defmodule Pleroma.User do
|
||||
end
|
||||
|
||||
def showing_reblogs?(%User{} = user, %User{} = target) do
|
||||
target.ap_id not in user.muted_reblogs
|
||||
not UserRelationship.reblog_mute_exists?(user, target)
|
||||
end
|
||||
|
||||
@doc """
|
||||
@ -1731,13 +1847,28 @@ defmodule Pleroma.User do
|
||||
end
|
||||
|
||||
def admin_api_update(user, params) do
|
||||
user
|
||||
|> cast(params, [
|
||||
:is_moderator,
|
||||
:is_admin,
|
||||
:show_role
|
||||
])
|
||||
|> update_and_set_cache()
|
||||
changeset =
|
||||
cast(user, params, [
|
||||
:is_moderator,
|
||||
:is_admin,
|
||||
:show_role
|
||||
])
|
||||
|
||||
with {:ok, updated_user} <- update_and_set_cache(changeset) do
|
||||
if user.is_admin && !updated_user.is_admin do
|
||||
# Tokens & authorizations containing any admin scopes must be revoked (revoking all).
|
||||
# This is an extra safety measure (tokens' admin scopes won't be accepted for non-admins).
|
||||
global_sign_out(user)
|
||||
end
|
||||
|
||||
{:ok, updated_user}
|
||||
end
|
||||
end
|
||||
|
||||
@doc "Signs user out of all applications"
|
||||
def global_sign_out(user) do
|
||||
OAuth.Authorization.delete_user_authorizations(user)
|
||||
OAuth.Token.delete_user_tokens(user)
|
||||
end
|
||||
|
||||
def mascot_update(user, url) do
|
||||
@ -1812,23 +1943,6 @@ defmodule Pleroma.User do
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
defp set_subscribers(user, subscribers) do
|
||||
params = %{subscribers: subscribers}
|
||||
|
||||
user
|
||||
|> cast(params, [:subscribers])
|
||||
|> validate_required([:subscribers])
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def add_to_subscribers(user, subscribed) do
|
||||
set_subscribers(user, Enum.uniq([subscribed | user.subscribers]))
|
||||
end
|
||||
|
||||
def remove_from_subscribers(user, subscribed) do
|
||||
set_subscribers(user, List.delete(user.subscribers, subscribed))
|
||||
end
|
||||
|
||||
defp set_domain_blocks(user, domain_blocks) do
|
||||
params = %{domain_blocks: domain_blocks}
|
||||
|
||||
@ -1846,81 +1960,35 @@ defmodule Pleroma.User do
|
||||
set_domain_blocks(user, List.delete(user.domain_blocks, domain_blocked))
|
||||
end
|
||||
|
||||
defp set_blocks(user, blocks) do
|
||||
params = %{blocks: blocks}
|
||||
|
||||
user
|
||||
|> cast(params, [:blocks])
|
||||
|> validate_required([:blocks])
|
||||
|> update_and_set_cache()
|
||||
@spec add_to_block(User.t(), User.t()) ::
|
||||
{:ok, UserRelationship.t()} | {:error, Ecto.Changeset.t()}
|
||||
defp add_to_block(%User{} = user, %User{} = blocked) do
|
||||
UserRelationship.create_block(user, blocked)
|
||||
end
|
||||
|
||||
def add_to_block(user, blocked) do
|
||||
set_blocks(user, Enum.uniq([blocked | user.blocks]))
|
||||
@spec add_to_block(User.t(), User.t()) ::
|
||||
{:ok, UserRelationship.t()} | {:ok, nil} | {:error, Ecto.Changeset.t()}
|
||||
defp remove_from_block(%User{} = user, %User{} = blocked) do
|
||||
UserRelationship.delete_block(user, blocked)
|
||||
end
|
||||
|
||||
def remove_from_block(user, blocked) do
|
||||
set_blocks(user, List.delete(user.blocks, blocked))
|
||||
end
|
||||
|
||||
defp set_mutes(user, mutes) do
|
||||
params = %{mutes: mutes}
|
||||
|
||||
user
|
||||
|> cast(params, [:mutes])
|
||||
|> validate_required([:mutes])
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def add_to_mutes(user, muted, notifications?) do
|
||||
with {:ok, user} <- set_mutes(user, Enum.uniq([muted | user.mutes])) do
|
||||
set_notification_mutes(
|
||||
user,
|
||||
Enum.uniq([muted | user.muted_notifications]),
|
||||
notifications?
|
||||
)
|
||||
defp add_to_mutes(%User{} = user, %User{} = muted_user, notifications?) do
|
||||
with {:ok, user_mute} <- UserRelationship.create_mute(user, muted_user),
|
||||
{:ok, user_notification_mute} <-
|
||||
(notifications? && UserRelationship.create_notification_mute(user, muted_user)) ||
|
||||
{:ok, nil} do
|
||||
{:ok, Enum.filter([user_mute, user_notification_mute], & &1)}
|
||||
end
|
||||
end
|
||||
|
||||
def remove_from_mutes(user, muted) do
|
||||
with {:ok, user} <- set_mutes(user, List.delete(user.mutes, muted)) do
|
||||
set_notification_mutes(
|
||||
user,
|
||||
List.delete(user.muted_notifications, muted),
|
||||
true
|
||||
)
|
||||
defp remove_from_mutes(user, %User{} = muted_user) do
|
||||
with {:ok, user_mute} <- UserRelationship.delete_mute(user, muted_user),
|
||||
{:ok, user_notification_mute} <-
|
||||
UserRelationship.delete_notification_mute(user, muted_user) do
|
||||
{:ok, [user_mute, user_notification_mute]}
|
||||
end
|
||||
end
|
||||
|
||||
defp set_notification_mutes(user, _muted_notifications, false = _notifications?) do
|
||||
{:ok, user}
|
||||
end
|
||||
|
||||
defp set_notification_mutes(user, muted_notifications, true = _notifications?) do
|
||||
params = %{muted_notifications: muted_notifications}
|
||||
|
||||
user
|
||||
|> cast(params, [:muted_notifications])
|
||||
|> validate_required([:muted_notifications])
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def add_reblog_mute(user, ap_id) do
|
||||
params = %{muted_reblogs: user.muted_reblogs ++ [ap_id]}
|
||||
|
||||
user
|
||||
|> cast(params, [:muted_reblogs])
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def remove_reblog_mute(user, ap_id) do
|
||||
params = %{muted_reblogs: List.delete(user.muted_reblogs, ap_id)}
|
||||
|
||||
user
|
||||
|> cast(params, [:muted_reblogs])
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def set_invisible(user, invisible) do
|
||||
params = %{invisible: invisible}
|
||||
|
||||
|
40
lib/pleroma/user/notification_setting.ex
Normal file
40
lib/pleroma/user/notification_setting.ex
Normal file
@ -0,0 +1,40 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.User.NotificationSetting do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@derive Jason.Encoder
|
||||
@primary_key false
|
||||
|
||||
embedded_schema do
|
||||
field(:followers, :boolean, default: true)
|
||||
field(:follows, :boolean, default: true)
|
||||
field(:non_follows, :boolean, default: true)
|
||||
field(:non_followers, :boolean, default: true)
|
||||
field(:privacy_option, :boolean, default: false)
|
||||
end
|
||||
|
||||
def changeset(schema, params) do
|
||||
schema
|
||||
|> cast(prepare_attrs(params), [
|
||||
:followers,
|
||||
:follows,
|
||||
:non_follows,
|
||||
:non_followers,
|
||||
:privacy_option
|
||||
])
|
||||
end
|
||||
|
||||
defp prepare_attrs(params) do
|
||||
Enum.reduce(params, %{}, fn
|
||||
{k, v}, acc when is_binary(v) ->
|
||||
Map.put(acc, k, String.downcase(v))
|
||||
|
||||
{k, v}, acc ->
|
||||
Map.put(acc, k, v)
|
||||
end)
|
||||
end
|
||||
end
|
@ -103,9 +103,13 @@ defmodule Pleroma.User.Search do
|
||||
from(q in query, where: q.invisible == false)
|
||||
end
|
||||
|
||||
defp filter_blocked_user(query, %User{blocks: blocks})
|
||||
when length(blocks) > 0 do
|
||||
from(q in query, where: not (q.ap_id in ^blocks))
|
||||
defp filter_blocked_user(query, %User{} = blocker) do
|
||||
query
|
||||
|> join(:left, [u], b in Pleroma.UserRelationship,
|
||||
as: :blocks,
|
||||
on: b.relationship_type == ^:block and b.source_id == ^blocker.id and u.id == b.target_id
|
||||
)
|
||||
|> where([blocks: b], is_nil(b.target_id))
|
||||
end
|
||||
|
||||
defp filter_blocked_user(query, _), do: query
|
||||
|
92
lib/pleroma/user_relationship.ex
Normal file
92
lib/pleroma/user_relationship.ex
Normal file
@ -0,0 +1,92 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.UserRelationship do
|
||||
use Ecto.Schema
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
|
||||
schema "user_relationships" do
|
||||
belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
|
||||
belongs_to(:target, User, type: FlakeId.Ecto.CompatType)
|
||||
field(:relationship_type, UserRelationshipTypeEnum)
|
||||
|
||||
timestamps(updated_at: false)
|
||||
end
|
||||
|
||||
for relationship_type <- Keyword.keys(UserRelationshipTypeEnum.__enum_map__()) do
|
||||
# Definitions of `create_block/2`, `create_mute/2` etc.
|
||||
def unquote(:"create_#{relationship_type}")(source, target),
|
||||
do: create(unquote(relationship_type), source, target)
|
||||
|
||||
# Definitions of `delete_block/2`, `delete_mute/2` etc.
|
||||
def unquote(:"delete_#{relationship_type}")(source, target),
|
||||
do: delete(unquote(relationship_type), source, target)
|
||||
|
||||
# Definitions of `block_exists?/2`, `mute_exists?/2` etc.
|
||||
def unquote(:"#{relationship_type}_exists?")(source, target),
|
||||
do: exists?(unquote(relationship_type), source, target)
|
||||
end
|
||||
|
||||
def changeset(%UserRelationship{} = user_relationship, params \\ %{}) do
|
||||
user_relationship
|
||||
|> cast(params, [:relationship_type, :source_id, :target_id])
|
||||
|> validate_required([:relationship_type, :source_id, :target_id])
|
||||
|> unique_constraint(:relationship_type,
|
||||
name: :user_relationships_source_id_relationship_type_target_id_index
|
||||
)
|
||||
|> validate_not_self_relationship()
|
||||
end
|
||||
|
||||
def exists?(relationship_type, %User{} = source, %User{} = target) do
|
||||
UserRelationship
|
||||
|> where(relationship_type: ^relationship_type, source_id: ^source.id, target_id: ^target.id)
|
||||
|> Repo.exists?()
|
||||
end
|
||||
|
||||
def create(relationship_type, %User{} = source, %User{} = target) do
|
||||
%UserRelationship{}
|
||||
|> changeset(%{
|
||||
relationship_type: relationship_type,
|
||||
source_id: source.id,
|
||||
target_id: target.id
|
||||
})
|
||||
|> Repo.insert(
|
||||
on_conflict: :replace_all_except_primary_key,
|
||||
conflict_target: [:source_id, :relationship_type, :target_id]
|
||||
)
|
||||
end
|
||||
|
||||
def delete(relationship_type, %User{} = source, %User{} = target) do
|
||||
attrs = %{relationship_type: relationship_type, source_id: source.id, target_id: target.id}
|
||||
|
||||
case Repo.get_by(UserRelationship, attrs) do
|
||||
%UserRelationship{} = existing_record -> Repo.delete(existing_record)
|
||||
nil -> {:ok, nil}
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_not_self_relationship(%Ecto.Changeset{} = changeset) do
|
||||
changeset
|
||||
|> validate_change(:target_id, fn _, target_id ->
|
||||
if target_id == get_field(changeset, :source_id) do
|
||||
[target_id: "can't be equal to source_id"]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end)
|
||||
|> validate_change(:source_id, fn _, source_id ->
|
||||
if source_id == get_field(changeset, :target_id) do
|
||||
[source_id: "can't be equal to target_id"]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
@ -456,17 +456,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
user = User.get_cached_by_ap_id(actor)
|
||||
to = (object.data["to"] || []) ++ (object.data["cc"] || [])
|
||||
|
||||
with {:ok, object, activity} <- Object.delete(object),
|
||||
with create_activity <- Activity.get_create_by_object_ap_id(id),
|
||||
data <-
|
||||
%{
|
||||
"type" => "Delete",
|
||||
"actor" => actor,
|
||||
"object" => id,
|
||||
"to" => to,
|
||||
"deleted_activity_id" => activity && activity.id
|
||||
"deleted_activity_id" => create_activity && create_activity.id
|
||||
}
|
||||
|> maybe_put("id", activity_id),
|
||||
{:ok, activity} <- insert(data, local, false),
|
||||
{:ok, object, _create_activity} <- Object.delete(object),
|
||||
stream_out_participations(object, user),
|
||||
_ <- decrease_replies_count_if_reply(object),
|
||||
{:ok, _actor} <- decrease_note_count_if_public(user, object),
|
||||
@ -541,6 +542,30 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
end
|
||||
end
|
||||
|
||||
def move(%User{} = origin, %User{} = target, local \\ true) do
|
||||
params = %{
|
||||
"type" => "Move",
|
||||
"actor" => origin.ap_id,
|
||||
"object" => origin.ap_id,
|
||||
"target" => target.ap_id
|
||||
}
|
||||
|
||||
with true <- origin.ap_id in target.also_known_as,
|
||||
{:ok, activity} <- insert(params, local) do
|
||||
maybe_federate(activity)
|
||||
|
||||
BackgroundWorker.enqueue("move_following", %{
|
||||
"origin_id" => origin.id,
|
||||
"target_id" => target.id
|
||||
})
|
||||
|
||||
{:ok, activity}
|
||||
else
|
||||
false -> {:error, "Target account must have the origin in `alsoKnownAs`"}
|
||||
err -> err
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_activities_for_context_query(context, opts) do
|
||||
public = [Pleroma.Constants.as_public()]
|
||||
|
||||
@ -724,6 +749,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|> Map.put("whole_db", true)
|
||||
|> Map.put("pinned_activity_ids", user.pinned_activities)
|
||||
|
||||
params =
|
||||
if User.blocks?(reading_user, user) do
|
||||
params
|
||||
else
|
||||
params
|
||||
|> Map.put("blocking_user", reading_user)
|
||||
|> Map.put("muting_user", reading_user)
|
||||
end
|
||||
|
||||
recipients =
|
||||
user_activities_recipients(%{
|
||||
"godmode" => params["godmode"],
|
||||
@ -895,7 +929,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
|
||||
|
||||
defp restrict_muted(query, %{"muting_user" => %User{} = user} = opts) do
|
||||
mutes = user.mutes
|
||||
mutes = opts["muted_users_ap_ids"] || User.muted_users_ap_ids(user)
|
||||
|
||||
query =
|
||||
from([activity] in query,
|
||||
@ -912,8 +946,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|
||||
defp restrict_muted(query, _), do: query
|
||||
|
||||
defp restrict_blocked(query, %{"blocking_user" => %User{} = user}) do
|
||||
blocks = user.blocks || []
|
||||
defp restrict_blocked(query, %{"blocking_user" => %User{} = user} = opts) do
|
||||
blocked_ap_ids = opts["blocked_users_ap_ids"] || User.blocked_users_ap_ids(user)
|
||||
domain_blocks = user.domain_blocks || []
|
||||
|
||||
following_ap_ids =
|
||||
@ -925,14 +959,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|
||||
from(
|
||||
[activity, object: o] in query,
|
||||
where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
|
||||
where: fragment("not (? && ?)", activity.recipients, ^blocks),
|
||||
where: fragment("not (? = ANY(?))", activity.actor, ^blocked_ap_ids),
|
||||
where: fragment("not (? && ?)", activity.recipients, ^blocked_ap_ids),
|
||||
where:
|
||||
fragment(
|
||||
"not (?->>'type' = 'Announce' and ?->'to' \\?| ?)",
|
||||
activity.data,
|
||||
activity.data,
|
||||
^blocks
|
||||
^blocked_ap_ids
|
||||
),
|
||||
where:
|
||||
fragment(
|
||||
@ -973,8 +1007,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|
||||
defp restrict_pinned(query, _), do: query
|
||||
|
||||
defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user}) do
|
||||
muted_reblogs = user.muted_reblogs || []
|
||||
defp restrict_muted_reblogs(query, %{"muting_user" => %User{} = user} = opts) do
|
||||
muted_reblogs = opts["reblog_muted_users_ap_ids"] || User.reblog_muted_users_ap_ids(user)
|
||||
|
||||
from(
|
||||
activity in query,
|
||||
@ -1055,7 +1089,33 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|
||||
defp maybe_order(query, _), do: query
|
||||
|
||||
defp fetch_activities_query_ap_ids_ops(opts) do
|
||||
source_user = opts["muting_user"]
|
||||
ap_id_relations = if source_user, do: [:mute, :reblog_mute], else: []
|
||||
|
||||
ap_id_relations =
|
||||
ap_id_relations ++
|
||||
if opts["blocking_user"] && opts["blocking_user"] == source_user do
|
||||
[:block]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
preloaded_ap_ids = User.outgoing_relations_ap_ids(source_user, ap_id_relations)
|
||||
|
||||
restrict_blocked_opts = Map.merge(%{"blocked_users_ap_ids" => preloaded_ap_ids[:block]}, opts)
|
||||
restrict_muted_opts = Map.merge(%{"muted_users_ap_ids" => preloaded_ap_ids[:mute]}, opts)
|
||||
|
||||
restrict_muted_reblogs_opts =
|
||||
Map.merge(%{"reblog_muted_users_ap_ids" => preloaded_ap_ids[:reblog_mute]}, opts)
|
||||
|
||||
{restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts}
|
||||
end
|
||||
|
||||
def fetch_activities_query(recipients, opts \\ %{}) do
|
||||
{restrict_blocked_opts, restrict_muted_opts, restrict_muted_reblogs_opts} =
|
||||
fetch_activities_query_ap_ids_ops(opts)
|
||||
|
||||
config = %{
|
||||
skip_thread_containment: Config.get([:instance, :skip_thread_containment])
|
||||
}
|
||||
@ -1075,15 +1135,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|> restrict_type(opts)
|
||||
|> restrict_state(opts)
|
||||
|> restrict_favorited_by(opts)
|
||||
|> restrict_blocked(opts)
|
||||
|> restrict_muted(opts)
|
||||
|> restrict_blocked(restrict_blocked_opts)
|
||||
|> restrict_muted(restrict_muted_opts)
|
||||
|> restrict_media(opts)
|
||||
|> restrict_visibility(opts)
|
||||
|> restrict_thread_visibility(opts, config)
|
||||
|> restrict_replies(opts)
|
||||
|> restrict_reblogs(opts)
|
||||
|> restrict_pinned(opts)
|
||||
|> restrict_muted_reblogs(opts)
|
||||
|> restrict_muted_reblogs(restrict_muted_reblogs_opts)
|
||||
|> restrict_instance(opts)
|
||||
|> Activity.restrict_deactivated_users()
|
||||
|> exclude_poll_votes(opts)
|
||||
@ -1175,6 +1235,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
data = Transmogrifier.maybe_fix_user_object(data)
|
||||
discoverable = data["discoverable"] || false
|
||||
invisible = data["invisible"] || false
|
||||
actor_type = data["type"] || "Person"
|
||||
|
||||
user_data = %{
|
||||
ap_id: data["id"],
|
||||
@ -1189,7 +1250,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
name: data["name"],
|
||||
follower_address: data["followers"],
|
||||
following_address: data["following"],
|
||||
bio: data["summary"]
|
||||
bio: data["summary"],
|
||||
actor_type: actor_type,
|
||||
also_known_as: Map.get(data, "alsoKnownAs", [])
|
||||
}
|
||||
|
||||
# nickname can be nil because of virtual actors
|
||||
@ -1251,13 +1314,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
end
|
||||
end
|
||||
|
||||
defp collection_private(data) do
|
||||
if is_map(data["first"]) and
|
||||
data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do
|
||||
defp collection_private(%{"first" => first}) do
|
||||
if is_map(first) and
|
||||
first["type"] in ["CollectionPage", "OrderedCollectionPage"] do
|
||||
{:ok, false}
|
||||
else
|
||||
with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <-
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do
|
||||
Fetcher.fetch_and_contain_remote_object_from_id(first) do
|
||||
{:ok, false}
|
||||
else
|
||||
{:error, {:ok, %{status: code}}} when code in [401, 403] ->
|
||||
@ -1272,6 +1335,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
end
|
||||
end
|
||||
|
||||
defp collection_private(_data), do: {:ok, true}
|
||||
|
||||
def user_data_from_user_object(data) do
|
||||
with {:ok, data} <- MRF.filter(data),
|
||||
{:ok, data} <- object_to_user_data(data) do
|
||||
|
@ -387,7 +387,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
def handle_incoming(%{"id" => nil}, _options), do: :error
|
||||
def handle_incoming(%{"id" => ""}, _options), do: :error
|
||||
# length of https:// = 8, should validate better, but good enough for now.
|
||||
def handle_incoming(%{"id" => id}, _options) when not (is_binary(id) and length(id) > 8),
|
||||
def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id) < 8,
|
||||
do: :error
|
||||
|
||||
# TODO: validate those with a Ecto scheme
|
||||
@ -669,7 +669,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
|
||||
update_data =
|
||||
new_user_data
|
||||
|> Map.take([:avatar, :banner, :bio, :name])
|
||||
|> Map.take([:avatar, :banner, :bio, :name, :also_known_as])
|
||||
|> Map.put(:fields, fields)
|
||||
|> Map.put(:locked, locked)
|
||||
|> Map.put(:invisible, invisible)
|
||||
@ -857,6 +857,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
end
|
||||
end
|
||||
|
||||
def handle_incoming(
|
||||
%{
|
||||
"type" => "Move",
|
||||
"actor" => origin_actor,
|
||||
"object" => origin_actor,
|
||||
"target" => target_actor
|
||||
},
|
||||
_options
|
||||
) do
|
||||
with %User{} = origin_user <- User.get_cached_by_ap_id(origin_actor),
|
||||
{:ok, %User{} = target_user} <- User.get_or_fetch_by_ap_id(target_actor),
|
||||
true <- origin_actor in target_user.also_known_as do
|
||||
ActivityPub.move(origin_user, target_user, false)
|
||||
else
|
||||
_e -> :error
|
||||
end
|
||||
end
|
||||
|
||||
def handle_incoming(_, _), do: :error
|
||||
|
||||
@spec get_obj_helper(String.t(), Keyword.t()) :: {:ok, Object.t()} | nil
|
||||
|
@ -722,16 +722,22 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||
act when is_binary(act) -> act
|
||||
end
|
||||
|
||||
activity = Activity.get_by_ap_id_with_object(id)
|
||||
actor = User.get_by_ap_id(activity.object.data["actor"])
|
||||
case Activity.get_by_ap_id_with_object(id) do
|
||||
%Activity{} = activity ->
|
||||
%{
|
||||
"type" => "Note",
|
||||
"id" => activity.data["id"],
|
||||
"content" => activity.object.data["content"],
|
||||
"published" => activity.object.data["published"],
|
||||
"actor" =>
|
||||
AccountView.render("show.json", %{
|
||||
user: User.get_by_ap_id(activity.object.data["actor"])
|
||||
})
|
||||
}
|
||||
|
||||
%{
|
||||
"type" => "Note",
|
||||
"id" => activity.data["id"],
|
||||
"content" => activity.object.data["content"],
|
||||
"published" => activity.object.data["published"],
|
||||
"actor" => AccountView.render("show.json", %{user: actor})
|
||||
}
|
||||
_ ->
|
||||
%{"id" => id, "deleted" => true}
|
||||
end
|
||||
end
|
||||
|
||||
defp build_flag_object(_), do: []
|
||||
@ -788,7 +794,52 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||
ActivityPub.fetch_activities([], params, :offset)
|
||||
end
|
||||
|
||||
@spec get_reports_grouped_by_status(%{required(:activity) => String.t()}) :: %{
|
||||
def parse_report_group(activity) do
|
||||
reports = get_reports_by_status_id(activity["id"])
|
||||
max_date = Enum.max_by(reports, &NaiveDateTime.from_iso8601!(&1.data["published"]))
|
||||
actors = Enum.map(reports, & &1.user_actor)
|
||||
[%{data: %{"object" => [account_id | _]}} | _] = reports
|
||||
|
||||
account =
|
||||
AccountView.render("show.json", %{
|
||||
user: User.get_by_ap_id(account_id)
|
||||
})
|
||||
|
||||
status = get_status_data(activity)
|
||||
|
||||
%{
|
||||
date: max_date.data["published"],
|
||||
account: account,
|
||||
status: status,
|
||||
actors: Enum.uniq(actors),
|
||||
reports: reports
|
||||
}
|
||||
end
|
||||
|
||||
defp get_status_data(status) do
|
||||
case status["deleted"] do
|
||||
true ->
|
||||
%{
|
||||
"id" => status["id"],
|
||||
"deleted" => true
|
||||
}
|
||||
|
||||
_ ->
|
||||
Activity.get_by_ap_id(status["id"])
|
||||
end
|
||||
end
|
||||
|
||||
def get_reports_by_status_id(ap_id) do
|
||||
from(a in Activity,
|
||||
where: fragment("(?)->>'type' = 'Flag'", a.data),
|
||||
where: fragment("(?)->'object' @> ?", a.data, ^[%{id: ap_id}]),
|
||||
or_where: fragment("(?)->'object' @> ?", a.data, ^[ap_id])
|
||||
)
|
||||
|> Activity.with_preloaded_user_actor()
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec get_reports_grouped_by_status([String.t()]) :: %{
|
||||
required(:groups) => [
|
||||
%{
|
||||
required(:date) => String.t(),
|
||||
@ -797,20 +848,15 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||
required(:actors) => [%User{}],
|
||||
required(:reports) => [%Activity{}]
|
||||
}
|
||||
],
|
||||
required(:total) => integer
|
||||
]
|
||||
}
|
||||
def get_reports_grouped_by_status(groups) do
|
||||
def get_reports_grouped_by_status(activity_ids) do
|
||||
parsed_groups =
|
||||
groups
|
||||
|> Enum.map(fn entry ->
|
||||
activity =
|
||||
case Jason.decode(entry.activity) do
|
||||
{:ok, activity} -> activity
|
||||
_ -> build_flag_object(entry.activity)
|
||||
end
|
||||
|
||||
parse_report_group(activity)
|
||||
activity_ids
|
||||
|> Enum.map(fn id ->
|
||||
id
|
||||
|> build_flag_object()
|
||||
|> parse_report_group()
|
||||
end)
|
||||
|
||||
%{
|
||||
@ -818,33 +864,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||
}
|
||||
end
|
||||
|
||||
def parse_report_group(activity) do
|
||||
reports = get_reports_by_status_id(activity["id"])
|
||||
max_date = Enum.max_by(reports, &NaiveDateTime.from_iso8601!(&1.data["published"]))
|
||||
actors = Enum.map(reports, & &1.user_actor)
|
||||
|
||||
%{
|
||||
date: max_date.data["published"],
|
||||
account: activity["actor"],
|
||||
status: %{
|
||||
id: activity["id"],
|
||||
content: activity["content"],
|
||||
published: activity["published"]
|
||||
},
|
||||
actors: Enum.uniq(actors),
|
||||
reports: reports
|
||||
}
|
||||
end
|
||||
|
||||
def get_reports_by_status_id(ap_id) do
|
||||
from(a in Activity,
|
||||
where: fragment("(?)->>'type' = 'Flag'", a.data),
|
||||
where: fragment("(?)->'object' @> ?", a.data, ^[%{id: ap_id}])
|
||||
)
|
||||
|> Activity.with_preloaded_user_actor()
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec get_reported_activities() :: [
|
||||
%{
|
||||
required(:activity) => String.t(),
|
||||
@ -852,17 +871,23 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||
}
|
||||
]
|
||||
def get_reported_activities do
|
||||
from(a in Activity,
|
||||
where: fragment("(?)->>'type' = 'Flag'", a.data),
|
||||
reported_activities_query =
|
||||
from(a in Activity,
|
||||
where: fragment("(?)->>'type' = 'Flag'", a.data),
|
||||
select: %{
|
||||
activity: fragment("jsonb_array_elements((? #- '{object,0}')->'object')", a.data)
|
||||
},
|
||||
group_by: fragment("activity")
|
||||
)
|
||||
|
||||
from(a in subquery(reported_activities_query),
|
||||
distinct: true,
|
||||
select: %{
|
||||
date: fragment("max(?->>'published') date", a.data),
|
||||
activity:
|
||||
fragment("jsonb_array_elements_text((? #- '{object,0}')->'object') activity", a.data)
|
||||
},
|
||||
group_by: fragment("activity"),
|
||||
order_by: fragment("date DESC")
|
||||
id: fragment("COALESCE(?->>'id'::text, ? #>> '{}')", a.activity, a.activity)
|
||||
}
|
||||
)
|
||||
|> Repo.all()
|
||||
|> Enum.map(& &1.id)
|
||||
end
|
||||
|
||||
def update_report_state(%Activity{} = activity, state)
|
||||
|
@ -91,7 +91,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
|
||||
%{
|
||||
"id" => user.ap_id,
|
||||
"type" => "Person",
|
||||
"type" => user.actor_type,
|
||||
"following" => "#{user.ap_id}/following",
|
||||
"followers" => "#{user.ap_id}/followers",
|
||||
"inbox" => "#{user.ap_id}/inbox",
|
||||
|
@ -14,6 +14,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
||||
@spec is_public?(Object.t() | Activity.t() | map()) :: boolean()
|
||||
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
||||
def is_public?(%Object{data: data}), do: is_public?(data)
|
||||
def is_public?(%Activity{data: %{"type" => "Move"}}), do: true
|
||||
def is_public?(%Activity{data: data}), do: is_public?(data)
|
||||
def is_public?(%{"directMessage" => true}), do: false
|
||||
def is_public?(data), do: Utils.label_in_message?(Pleroma.Constants.as_public(), data)
|
||||
|
@ -30,13 +30,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:accounts"]}
|
||||
%{scopes: ["read:accounts"], admin: true}
|
||||
when action in [:list_users, :user_show, :right_get, :invites]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:accounts"]}
|
||||
%{scopes: ["write:accounts"], admin: true}
|
||||
when action in [
|
||||
:get_invite_token,
|
||||
:revoke_invite,
|
||||
@ -58,35 +58,37 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:reports"]} when action in [:list_reports, :report_show]
|
||||
%{scopes: ["read:reports"], admin: true}
|
||||
when action in [:list_reports, :report_show]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:reports"]}
|
||||
%{scopes: ["write:reports"], admin: true}
|
||||
when action in [:report_update_state, :report_respond]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:statuses"]} when action == :list_user_statuses
|
||||
%{scopes: ["read:statuses"], admin: true}
|
||||
when action == :list_user_statuses
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:statuses"]}
|
||||
%{scopes: ["write:statuses"], admin: true}
|
||||
when action in [:status_update, :status_delete]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read"]}
|
||||
%{scopes: ["read"], admin: true}
|
||||
when action in [:config_show, :migrate_to_db, :migrate_from_db, :list_log]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write"]}
|
||||
%{scopes: ["write"], admin: true}
|
||||
when action in [:relay_follow, :relay_unfollow, :config_update]
|
||||
)
|
||||
|
||||
@ -647,11 +649,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||
end
|
||||
|
||||
def list_grouped_reports(conn, _params) do
|
||||
reports = Utils.get_reported_activities()
|
||||
statuses = Utils.get_reported_activities()
|
||||
|
||||
conn
|
||||
|> put_view(ReportView)
|
||||
|> render("index_grouped.json", Utils.get_reports_grouped_by_status(reports))
|
||||
|> render("index_grouped.json", Utils.get_reports_grouped_by_status(statuses))
|
||||
end
|
||||
|
||||
def report_show(conn, %{"id" => id}) do
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
defmodule Pleroma.Web.AdminAPI.ReportView do
|
||||
use Pleroma.Web, :view
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.AdminAPI.Report
|
||||
@ -45,10 +46,16 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
|
||||
def render("index_grouped.json", %{groups: groups}) do
|
||||
reports =
|
||||
Enum.map(groups, fn group ->
|
||||
status =
|
||||
case group.status do
|
||||
%Activity{} = activity -> StatusView.render("show.json", %{activity: activity})
|
||||
_ -> group.status
|
||||
end
|
||||
|
||||
%{
|
||||
date: group[:date],
|
||||
account: group[:account],
|
||||
status: group[:status],
|
||||
status: Map.put_new(status, "deleted", false),
|
||||
actors: Enum.map(group[:actors], &merge_account_views/1),
|
||||
reports:
|
||||
group[:reports]
|
||||
|
@ -20,7 +20,7 @@ defmodule Pleroma.Web.ChatChannel do
|
||||
def handle_in("new_msg", %{"text" => text}, %{assigns: %{user_name: user_name}} = socket) do
|
||||
text = String.trim(text)
|
||||
|
||||
if String.length(text) > 0 do
|
||||
if String.length(text) in 1..Pleroma.Config.get([:instance, :chat_limit]) do
|
||||
author = User.get_cached_by_nickname(user_name)
|
||||
author = Pleroma.Web.MastodonAPI.AccountView.render("show.json", user: author)
|
||||
message = ChatChannelState.add_message(%{text: text, author: author})
|
||||
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.ThreadMute
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
@ -32,7 +33,7 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
def unfollow(follower, unfollowed) do
|
||||
with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed),
|
||||
{:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed),
|
||||
{:ok, _unfollowed} <- User.unsubscribe(follower, unfollowed) do
|
||||
{:ok, _subscription} <- User.unsubscribe(follower, unfollowed) do
|
||||
{:ok, follower}
|
||||
end
|
||||
end
|
||||
@ -420,15 +421,11 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
|
||||
defp set_visibility(activity, _), do: {:ok, activity}
|
||||
|
||||
def hide_reblogs(user, %{ap_id: ap_id} = _muted) do
|
||||
if ap_id not in user.muted_reblogs do
|
||||
User.add_reblog_mute(user, ap_id)
|
||||
end
|
||||
def hide_reblogs(%User{} = user, %User{} = target) do
|
||||
UserRelationship.create_reblog_mute(user, target)
|
||||
end
|
||||
|
||||
def show_reblogs(user, %{ap_id: ap_id} = _muted) do
|
||||
if ap_id in user.muted_reblogs do
|
||||
User.remove_reblog_mute(user, ap_id)
|
||||
end
|
||||
def show_reblogs(%User{} = user, %User{} = target) do
|
||||
UserRelationship.delete_reblog_mute(user, target)
|
||||
end
|
||||
end
|
||||
|
@ -451,6 +451,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||
recipients ++ to
|
||||
end
|
||||
|
||||
def maybe_notify_to_recipients(recipients, _), do: recipients
|
||||
|
||||
def maybe_notify_mentioned_recipients(
|
||||
recipients,
|
||||
%Activity{data: %{"to" => _to, "type" => type} = data} = activity
|
||||
@ -492,7 +494,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||
with %User{} = user <- User.get_cached_by_ap_id(actor) do
|
||||
subscriber_ids =
|
||||
user
|
||||
|> User.subscribers()
|
||||
|> User.subscriber_users()
|
||||
|> Enum.filter(&Visibility.visible_for_user?(activity, &1))
|
||||
|> Enum.map(& &1.ap_id)
|
||||
|
||||
@ -502,6 +504,17 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||
|
||||
def maybe_notify_subscribers(recipients, _), do: recipients
|
||||
|
||||
def maybe_notify_followers(recipients, %Activity{data: %{"type" => "Move"}} = activity) do
|
||||
with %User{} = user <- User.get_cached_by_ap_id(activity.actor) do
|
||||
user
|
||||
|> User.get_followers()
|
||||
|> Enum.map(& &1.ap_id)
|
||||
|> Enum.concat(recipients)
|
||||
end
|
||||
end
|
||||
|
||||
def maybe_notify_followers(recipients, _), do: recipients
|
||||
|
||||
def maybe_extract_mentions(%{"tag" => tag}) do
|
||||
tag
|
||||
|> Enum.filter(fn x -> is_map(x) && x["type"] == "Mention" end)
|
||||
|
@ -61,14 +61,7 @@ defmodule Pleroma.Web.Endpoint do
|
||||
plug(Plug.RequestId)
|
||||
plug(Plug.Logger)
|
||||
|
||||
plug(
|
||||
Plug.Parsers,
|
||||
parsers: [:urlencoded, :multipart, :json],
|
||||
pass: ["*/*"],
|
||||
json_decoder: Jason,
|
||||
length: Pleroma.Config.get([:instance, :upload_limit]),
|
||||
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
|
||||
)
|
||||
plug(Pleroma.Plugs.Parsers)
|
||||
|
||||
plug(Plug.MethodOverride)
|
||||
plug(Plug.Head)
|
||||
|
@ -152,6 +152,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
:hide_favorites,
|
||||
:show_role,
|
||||
:skip_thread_containment,
|
||||
:allow_following_move,
|
||||
:discoverable
|
||||
]
|
||||
|> Enum.reduce(%{}, fn key, acc ->
|
||||
@ -187,6 +188,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
{:ok, Map.merge(user.pleroma_settings_store, value)}
|
||||
end)
|
||||
|> add_if_present(params, "default_scope", :default_scope)
|
||||
|> add_if_present(params, "actor_type", :actor_type)
|
||||
|
||||
emojis_text = (user_params["display_name"] || "") <> (user_params["note"] || "")
|
||||
|
||||
@ -248,7 +250,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
@doc "GET /api/v1/accounts/:id/statuses"
|
||||
def statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user) do
|
||||
params = Map.put(params, "tag", params["tagged"])
|
||||
params =
|
||||
params
|
||||
|> Map.put("tag", params["tagged"])
|
||||
|> Map.delete("godmode")
|
||||
|
||||
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
||||
|
||||
conn
|
||||
@ -323,7 +329,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
def mute(%{assigns: %{user: muter, account: muted}} = conn, params) do
|
||||
notifications? = params |> Map.get("notifications", true) |> truthy_param?()
|
||||
|
||||
with {:ok, muter} <- User.mute(muter, muted, notifications?) do
|
||||
with {:ok, _user_relationships} <- User.mute(muter, muted, notifications?) do
|
||||
render(conn, "relationship.json", user: muter, target: muted)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
@ -332,7 +338,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
|
||||
@doc "POST /api/v1/accounts/:id/unmute"
|
||||
def unmute(%{assigns: %{user: muter, account: muted}} = conn, _params) do
|
||||
with {:ok, muter} <- User.unmute(muter, muted) do
|
||||
with {:ok, _user_relationships} <- User.unmute(muter, muted) do
|
||||
render(conn, "relationship.json", user: muter, target: muted)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
@ -341,7 +347,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
|
||||
@doc "POST /api/v1/accounts/:id/block"
|
||||
def block(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do
|
||||
with {:ok, blocker} <- User.block(blocker, blocked),
|
||||
with {:ok, _user_block} <- User.block(blocker, blocked),
|
||||
{:ok, _activity} <- ActivityPub.block(blocker, blocked) do
|
||||
render(conn, "relationship.json", user: blocker, target: blocked)
|
||||
else
|
||||
@ -351,7 +357,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
|
||||
@doc "POST /api/v1/accounts/:id/unblock"
|
||||
def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do
|
||||
with {:ok, blocker} <- User.unblock(blocker, blocked),
|
||||
with {:ok, _user_block} <- User.unblock(blocker, blocked),
|
||||
{:ok, _activity} <- ActivityPub.unblock(blocker, blocked) do
|
||||
render(conn, "relationship.json", user: blocker, target: blocked)
|
||||
else
|
||||
@ -373,12 +379,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
|
||||
@doc "GET /api/v1/mutes"
|
||||
def mutes(%{assigns: %{user: user}} = conn, _) do
|
||||
render(conn, "index.json", users: User.muted_users(user), for: user, as: :user)
|
||||
users = User.muted_users(user, _restrict_deactivated = true)
|
||||
render(conn, "index.json", users: users, for: user, as: :user)
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/blocks"
|
||||
def blocks(%{assigns: %{user: user}} = conn, _) do
|
||||
render(conn, "index.json", users: User.blocked_users(user), for: user, as: :user)
|
||||
users = User.blocked_users(user, _restrict_deactivated = true)
|
||||
render(conn, "index.json", users: users, for: user, as: :user)
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/endorsements"
|
||||
|
@ -24,19 +24,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
||||
|
||||
with {:ok, follower, _followed, _} <- result do
|
||||
options = cast_params(params)
|
||||
|
||||
case reblogs_visibility(options[:reblogs], result) do
|
||||
{:ok, follower} -> {:ok, follower}
|
||||
_ -> {:ok, follower}
|
||||
end
|
||||
set_reblogs_visibility(options[:reblogs], result)
|
||||
{:ok, follower}
|
||||
end
|
||||
end
|
||||
|
||||
defp reblogs_visibility(false, {:ok, follower, followed, _}) do
|
||||
defp set_reblogs_visibility(false, {:ok, follower, followed, _}) do
|
||||
CommonAPI.hide_reblogs(follower, followed)
|
||||
end
|
||||
|
||||
defp reblogs_visibility(_, {:ok, follower, followed, _}) do
|
||||
defp set_reblogs_visibility(_, {:ok, follower, followed, _}) do
|
||||
CommonAPI.show_reblogs(follower, followed)
|
||||
end
|
||||
|
||||
@ -73,7 +70,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
||||
exclude_types: {:array, :string},
|
||||
exclude_visibilities: {:array, :string},
|
||||
reblogs: :boolean,
|
||||
with_muted: :boolean
|
||||
with_muted: :boolean,
|
||||
with_move: :boolean
|
||||
}
|
||||
|
||||
changeset = cast({%{}, param_types}, params, Map.keys(param_types))
|
||||
|
@ -50,8 +50,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||
id: to_string(target.id),
|
||||
following: User.following?(user, target),
|
||||
followed_by: User.following?(target, user),
|
||||
blocking: User.blocks_ap_id?(user, target),
|
||||
blocked_by: User.blocks_ap_id?(target, user),
|
||||
blocking: User.blocks_user?(user, target),
|
||||
blocked_by: User.blocks_user?(target, user),
|
||||
muting: User.mutes?(user, target),
|
||||
muting_notifications: User.muted_notifications?(user, target),
|
||||
subscribing: User.subscribed_to?(user, target),
|
||||
@ -86,7 +86,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||
0
|
||||
end
|
||||
|
||||
bot = (user.source_data["type"] || "Person") in ["Application", "Service"]
|
||||
bot = user.actor_type in ["Application", "Service"]
|
||||
|
||||
emojis =
|
||||
(user.source_data["tag"] || [])
|
||||
@ -137,7 +137,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||
sensitive: false,
|
||||
fields: user.raw_fields,
|
||||
pleroma: %{
|
||||
discoverable: user.discoverable
|
||||
discoverable: user.discoverable,
|
||||
actor_type: user.actor_type
|
||||
}
|
||||
},
|
||||
|
||||
@ -162,6 +163,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||
|> maybe_put_chat_token(user, opts[:for], opts)
|
||||
|> maybe_put_activation_status(user, opts[:for])
|
||||
|> maybe_put_follow_requests_count(user, opts[:for])
|
||||
|> maybe_put_allow_following_move(user, opts[:for])
|
||||
|> maybe_put_unread_conversation_count(user, opts[:for])
|
||||
end
|
||||
|
||||
@ -238,6 +240,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||
|
||||
defp maybe_put_notification_settings(data, _, _), do: data
|
||||
|
||||
defp maybe_put_allow_following_move(data, %User{id: user_id} = user, %User{id: user_id}) do
|
||||
Kernel.put_in(data, [:pleroma, :allow_following_move], user.allow_following_move)
|
||||
end
|
||||
|
||||
defp maybe_put_allow_following_move(data, _, _), do: data
|
||||
|
||||
defp maybe_put_activation_status(data, user, %User{is_admin: true}) do
|
||||
Kernel.put_in(data, [:pleroma, :deactivated], user.deactivated)
|
||||
end
|
||||
|
@ -37,32 +37,24 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
||||
}
|
||||
|
||||
case mastodon_type do
|
||||
"mention" ->
|
||||
response
|
||||
|> Map.merge(%{
|
||||
status: StatusView.render("show.json", %{activity: activity, for: user})
|
||||
})
|
||||
|
||||
"favourite" ->
|
||||
response
|
||||
|> Map.merge(%{
|
||||
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
||||
})
|
||||
|
||||
"reblog" ->
|
||||
response
|
||||
|> Map.merge(%{
|
||||
status: StatusView.render("show.json", %{activity: parent_activity, for: user})
|
||||
})
|
||||
|
||||
"follow" ->
|
||||
response
|
||||
|
||||
_ ->
|
||||
nil
|
||||
"mention" -> put_status(response, activity, user)
|
||||
"favourite" -> put_status(response, parent_activity, user)
|
||||
"reblog" -> put_status(response, parent_activity, user)
|
||||
"move" -> put_target(response, activity, user)
|
||||
"follow" -> response
|
||||
_ -> nil
|
||||
end
|
||||
else
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp put_status(response, activity, user) do
|
||||
Map.put(response, :status, StatusView.render("show.json", %{activity: activity, for: user}))
|
||||
end
|
||||
|
||||
defp put_target(response, activity, user) do
|
||||
target = User.get_cached_by_ap_id(activity.data["target"])
|
||||
Map.put(response, :target, AccountView.render("show.json", %{user: target, for: user}))
|
||||
end
|
||||
end
|
||||
|
@ -222,7 +222,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
||||
{:user_active, true} <- {:user_active, !user.deactivated},
|
||||
{:password_reset_pending, false} <-
|
||||
{:password_reset_pending, user.password_reset_pending},
|
||||
{:ok, scopes} <- validate_scopes(app, params),
|
||||
{:ok, scopes} <- validate_scopes(app, params, user),
|
||||
{:ok, auth} <- Authorization.create_authorization(app, user, scopes),
|
||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||
json(conn, Token.Response.build(user, token))
|
||||
@ -471,7 +471,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
||||
{:get_user, (user && {:ok, user}) || Authenticator.get_user(conn)},
|
||||
%App{} = app <- Repo.get_by(App, client_id: client_id),
|
||||
true <- redirect_uri in String.split(app.redirect_uris),
|
||||
{:ok, scopes} <- validate_scopes(app, auth_attrs),
|
||||
{:ok, scopes} <- validate_scopes(app, auth_attrs, user),
|
||||
{:auth_active, true} <- {:auth_active, User.auth_active?(user)} do
|
||||
Authorization.create_authorization(app, user, scopes)
|
||||
end
|
||||
@ -487,12 +487,12 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
||||
defp put_session_registration_id(%Plug.Conn{} = conn, registration_id),
|
||||
do: put_session(conn, :registration_id, registration_id)
|
||||
|
||||
@spec validate_scopes(App.t(), map()) ::
|
||||
@spec validate_scopes(App.t(), map(), User.t()) ::
|
||||
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
||||
defp validate_scopes(app, params) do
|
||||
defp validate_scopes(%App{} = app, params, %User{} = user) do
|
||||
params
|
||||
|> Scopes.fetch_scopes(app.scopes)
|
||||
|> Scopes.validate(app.scopes)
|
||||
|> Scopes.validate(app.scopes, user)
|
||||
end
|
||||
|
||||
def default_redirect_uri(%App{} = app) do
|
||||
|
@ -7,6 +7,9 @@ defmodule Pleroma.Web.OAuth.Scopes do
|
||||
Functions for dealing with scopes.
|
||||
"""
|
||||
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.User
|
||||
|
||||
@doc """
|
||||
Fetch scopes from request params.
|
||||
|
||||
@ -53,15 +56,36 @@ defmodule Pleroma.Web.OAuth.Scopes do
|
||||
@doc """
|
||||
Validates scopes.
|
||||
"""
|
||||
@spec validate(list() | nil, list()) ::
|
||||
@spec validate(list() | nil, list(), User.t()) ::
|
||||
{:ok, list()} | {:error, :missing_scopes | :unsupported_scopes}
|
||||
def validate([], _app_scopes), do: {:error, :missing_scopes}
|
||||
def validate(nil, _app_scopes), do: {:error, :missing_scopes}
|
||||
def validate(blank_scopes, _app_scopes, _user) when blank_scopes in [nil, []],
|
||||
do: {:error, :missing_scopes}
|
||||
|
||||
def validate(scopes, app_scopes) do
|
||||
case Pleroma.Plugs.OAuthScopesPlug.filter_descendants(scopes, app_scopes) do
|
||||
def validate(scopes, app_scopes, %User{} = user) do
|
||||
with {:ok, _} <- ensure_scopes_support(scopes, app_scopes),
|
||||
{:ok, scopes} <- authorize_admin_scopes(scopes, app_scopes, user) do
|
||||
{:ok, scopes}
|
||||
end
|
||||
end
|
||||
|
||||
defp ensure_scopes_support(scopes, app_scopes) do
|
||||
case OAuthScopesPlug.filter_descendants(scopes, app_scopes) do
|
||||
^scopes -> {:ok, scopes}
|
||||
_ -> {:error, :unsupported_scopes}
|
||||
end
|
||||
end
|
||||
|
||||
defp authorize_admin_scopes(scopes, app_scopes, %User{} = user) do
|
||||
if user.is_admin || !contains_admin_scopes?(scopes) || !contains_admin_scopes?(app_scopes) do
|
||||
{:ok, scopes}
|
||||
else
|
||||
{:error, :unsupported_scopes}
|
||||
end
|
||||
end
|
||||
|
||||
def contains_admin_scopes?(scopes) do
|
||||
scopes
|
||||
|> OAuthScopesPlug.filter_descendants(["admin"])
|
||||
|> Enum.any?()
|
||||
end
|
||||
end
|
||||
|
@ -11,11 +11,6 @@ defmodule Pleroma.Web.OAuth.Token.CleanWorker do
|
||||
@ten_seconds 10_000
|
||||
@one_day 86_400_000
|
||||
|
||||
@interval Pleroma.Config.get(
|
||||
[:oauth2, :clean_expired_tokens_interval],
|
||||
@one_day
|
||||
)
|
||||
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Workers.BackgroundWorker
|
||||
|
||||
@ -29,8 +24,9 @@ defmodule Pleroma.Web.OAuth.Token.CleanWorker do
|
||||
@doc false
|
||||
def handle_info(:perform, state) do
|
||||
BackgroundWorker.enqueue("clean_expired_tokens", %{})
|
||||
interval = Pleroma.Config.get([:oauth2, :clean_expired_tokens_interval], @one_day)
|
||||
|
||||
Process.send_after(self(), :perform, @interval)
|
||||
Process.send_after(self(), :perform, interval)
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
|
@ -144,7 +144,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||
|
||||
@doc "POST /api/v1/pleroma/accounts/:id/subscribe"
|
||||
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
|
||||
with {:ok, subscription_target} <- User.subscribe(user, subscription_target) do
|
||||
with {:ok, _subscription} <- User.subscribe(user, subscription_target) do
|
||||
render(conn, "relationship.json", user: user, target: subscription_target)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
@ -153,7 +153,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||
|
||||
@doc "POST /api/v1/pleroma/accounts/:id/unsubscribe"
|
||||
def unsubscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
|
||||
with {:ok, subscription_target} <- User.unsubscribe(user, subscription_target) do
|
||||
with {:ok, _subscription} <- User.unsubscribe(user, subscription_target) do
|
||||
render(conn, "relationship.json", user: user, target: subscription_target)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write"]}
|
||||
%{scopes: ["write"], admin: true}
|
||||
when action in [
|
||||
:create,
|
||||
:delete,
|
||||
|
@ -16,14 +16,14 @@ defmodule Pleroma.Web.Push.Impl do
|
||||
require Logger
|
||||
import Ecto.Query
|
||||
|
||||
@types ["Create", "Follow", "Announce", "Like"]
|
||||
@types ["Create", "Follow", "Announce", "Like", "Move"]
|
||||
|
||||
@doc "Performs sending notifications for user subscriptions"
|
||||
@spec perform(Notification.t()) :: list(any) | :error
|
||||
def perform(
|
||||
%{
|
||||
activity: %{data: %{"type" => activity_type}, id: activity_id} = activity,
|
||||
user_id: user_id
|
||||
activity: %{data: %{"type" => activity_type}} = activity,
|
||||
user: %User{id: user_id}
|
||||
} = notif
|
||||
)
|
||||
when activity_type in @types do
|
||||
@ -39,18 +39,17 @@ defmodule Pleroma.Web.Push.Impl do
|
||||
for subscription <- fetch_subsriptions(user_id),
|
||||
get_in(subscription.data, ["alerts", type]) do
|
||||
%{
|
||||
title: format_title(notif),
|
||||
access_token: subscription.token.token,
|
||||
body: format_body(notif, actor, object),
|
||||
notification_id: notif.id,
|
||||
notification_type: type,
|
||||
icon: avatar_url,
|
||||
preferred_locale: "en",
|
||||
pleroma: %{
|
||||
activity_id: activity_id,
|
||||
activity_id: notif.activity.id,
|
||||
direct_conversation_id: direct_conversation_id
|
||||
}
|
||||
}
|
||||
|> Map.merge(build_content(notif, actor, object))
|
||||
|> Jason.encode!()
|
||||
|> push_message(build_sub(subscription), gcm_api_key, subscription)
|
||||
end
|
||||
@ -100,6 +99,24 @@ defmodule Pleroma.Web.Push.Impl do
|
||||
}
|
||||
end
|
||||
|
||||
def build_content(
|
||||
%{
|
||||
activity: %{data: %{"directMessage" => true}},
|
||||
user: %{notification_settings: %{privacy_option: true}}
|
||||
},
|
||||
actor,
|
||||
_
|
||||
) do
|
||||
%{title: "New Direct Message", body: "@#{actor.nickname}"}
|
||||
end
|
||||
|
||||
def build_content(notif, actor, object) do
|
||||
%{
|
||||
title: format_title(notif),
|
||||
body: format_body(notif, actor, object)
|
||||
}
|
||||
end
|
||||
|
||||
def format_body(
|
||||
%{activity: %{data: %{"type" => "Create"}}},
|
||||
actor,
|
||||
|
@ -129,16 +129,17 @@ defmodule Pleroma.Web.Streamer.Worker do
|
||||
end
|
||||
|
||||
defp should_send?(%User{} = user, %Activity{} = item) do
|
||||
blocks = user.blocks || []
|
||||
mutes = user.mutes || []
|
||||
reblog_mutes = user.muted_reblogs || []
|
||||
recipient_blocks = MapSet.new(blocks ++ mutes)
|
||||
%{block: blocked_ap_ids, mute: muted_ap_ids, reblog_mute: reblog_muted_ap_ids} =
|
||||
User.outgoing_relations_ap_ids(user, [:block, :mute, :reblog_mute])
|
||||
|
||||
recipient_blocks = MapSet.new(blocked_ap_ids ++ muted_ap_ids)
|
||||
recipients = MapSet.new(item.recipients)
|
||||
domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks)
|
||||
|
||||
with parent <- Object.normalize(item) || item,
|
||||
true <- Enum.all?([blocks, mutes, reblog_mutes], &(item.actor not in &1)),
|
||||
true <- Enum.all?([blocks, mutes], &(parent.data["actor"] not in &1)),
|
||||
true <-
|
||||
Enum.all?([blocked_ap_ids, muted_ap_ids, reblog_muted_ap_ids], &(item.actor not in &1)),
|
||||
true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(parent.data["actor"] not in &1)),
|
||||
true <- MapSet.disjoint?(recipients, recipient_blocks),
|
||||
%{host: item_host} <- URI.parse(item.actor),
|
||||
%{host: parent_host} <- URI.parse(parent.data["actor"]),
|
||||
|
@ -71,4 +71,11 @@ defmodule Pleroma.Workers.BackgroundWorker do
|
||||
activity = Activity.get_by_id(activity_id)
|
||||
Pleroma.Web.RichMedia.Helpers.perform(:fetch, activity)
|
||||
end
|
||||
|
||||
def perform(%{"op" => "move_following", "origin_id" => origin_id, "target_id" => target_id}, _) do
|
||||
origin = User.get_cached_by_id(origin_id)
|
||||
target = User.get_cached_by_id(target_id)
|
||||
|
||||
Pleroma.FollowingRelationship.move_following(origin, target)
|
||||
end
|
||||
end
|
||||
|
@ -13,7 +13,7 @@ defmodule Pleroma.Workers.WebPusherWorker do
|
||||
notification =
|
||||
Notification
|
||||
|> Repo.get(notification_id)
|
||||
|> Repo.preload([:activity])
|
||||
|> Repo.preload([:activity, :user])
|
||||
|
||||
Pleroma.Web.Push.Impl.perform(notification)
|
||||
end
|
||||
|
3
mix.exs
3
mix.exs
@ -100,9 +100,10 @@ defmodule Pleroma.Mixfile do
|
||||
{:plug_cowboy, "~> 2.0"},
|
||||
{:phoenix_pubsub, "~> 1.1"},
|
||||
{:phoenix_ecto, "~> 4.0"},
|
||||
{:ecto_enum, "~> 1.4"},
|
||||
{:ecto_sql, "~> 3.2"},
|
||||
{:postgrex, ">= 0.13.5"},
|
||||
{:oban, "~> 0.8.1"},
|
||||
{:oban, "~> 0.12.0"},
|
||||
{:quantum, "~> 2.3"},
|
||||
{:gettext, "~> 0.15"},
|
||||
{:comeonin, "~> 4.1.1"},
|
||||
|
11
mix.lock
11
mix.lock
@ -23,8 +23,9 @@
|
||||
"decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"},
|
||||
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"},
|
||||
"earmark": {:hex, :earmark, "1.4.2", "3aa0bd23bc4c61cf2f1e5d752d1bb470560a6f8539974f767a38923bb20e1d7f", [:mix], [], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "3.2.3", "51274df79862845b388733fddcf6f107d0c8c86e27abe7131fa98f8d30761bda", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.2.0", "751cea597e8deb616084894dd75cbabfdbe7255ff01e8c058ca13f0353a3921b", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.2.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "3.2.5", "76c864b77948a479e18e69cc1d0f0f4ee7cced1148ffe6a093ff91eba644f0b5", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.2.2", "d10845bc147b9f61ef485cbf0973c0a337237199bd9bd30dd9542db00aadc26b", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.2.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.2.0 or ~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"esshd": {:hex, :esshd, "0.1.0", "6f93a2062adb43637edad0ea7357db2702a4b80dd9683482fe00f5134e97f4c1", [:mix], [], "hexpm"},
|
||||
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm"},
|
||||
"ex2ms": {:hex, :ex2ms, "1.5.0", "19e27f9212be9a96093fed8cdfbef0a2b56c21237196d26760f11dfcfae58e97", [:mix], [], "hexpm"},
|
||||
@ -38,7 +39,7 @@
|
||||
"fast_html": {:hex, :fast_html, "0.99.4", "d80812664f0429607e1d880fba0ef04da87a2e4fa596701bcaae17953535695c", [:make, :mix], [], "hexpm"},
|
||||
"fast_sanitize": {:hex, :fast_sanitize, "0.1.4", "6c2e7203ca2f8275527a3021ba6e9d5d4ee213a47dc214a97c128737c9e56df1", [:mix], [{:fast_html, "~> 0.99", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"floki": {:hex, :floki, "0.23.0", "956ab6dba828c96e732454809fb0bd8d43ce0979b75f34de6322e73d4c917829", [:mix], [{:html_entities, "~> 0.4.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"floki": {:hex, :floki, "0.23.1", "e100306ce7d8841d70a559748e5091542e2cfc67ffb3ade92b89a8435034dab1", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm"},
|
||||
"gen_stage": {:hex, :gen_stage, "0.14.3", "d0c66f1c87faa301c1a85a809a3ee9097a4264b2edf7644bf5c123237ef732bf", [:mix], [], "hexpm"},
|
||||
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
|
||||
@ -67,7 +68,7 @@
|
||||
"myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "0.5.1", "c90796ecee0289dbb5ad16d3ad06f957b0cd1199769641c961cfe0b97db190e0", [:mix], [], "hexpm"},
|
||||
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
|
||||
"oban": {:hex, :oban, "0.8.1", "4bbf62eb1829f856d69aeb5069ac7036afe07db8221a17de2a9169cc7a58a318", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"oban": {:hex, :oban, "0.12.0", "5477d5ab4a5a201c0b6c89764040ebfc5d2c71c488a36f378016ce5990838f0f", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
|
||||
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "hexpm"},
|
||||
"phoenix": {:hex, :phoenix, "1.4.10", "619e4a545505f562cd294df52294372d012823f4fd9d34a6657a8b242898c255", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
@ -97,7 +98,7 @@
|
||||
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm"},
|
||||
"swoosh": {:hex, :swoosh, "0.23.5", "bfd9404bbf5069b1be2ffd317923ce57e58b332e25dbca2a35dedd7820dfee5a", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"syslog": {:git, "https://github.com/Vagabond/erlang-syslog.git", "4a6c6f2c996483e86c1320e9553f91d337bcb6aa", [tag: "1.0.5"]},
|
||||
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm"},
|
||||
"tesla": {:hex, :tesla, "1.3.0", "f35d72f029e608f9cdc6f6d6fcc7c66cf6d6512a70cfef9206b21b8bd0203a30", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 0.4", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.3", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
@ -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,17 @@
|
||||
defmodule Pleroma.Repo.Migrations.CreateUserRelationships do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create_if_not_exists table(:user_relationships) do
|
||||
add(:source_id, references(:users, type: :uuid, on_delete: :delete_all))
|
||||
add(:target_id, references(:users, type: :uuid, on_delete: :delete_all))
|
||||
add(:relationship_type, :integer, null: false)
|
||||
|
||||
timestamps(updated_at: false)
|
||||
end
|
||||
|
||||
create_if_not_exists(
|
||||
unique_index(:user_relationships, [:source_id, :relationship_type, :target_id])
|
||||
)
|
||||
end
|
||||
end
|
@ -0,0 +1,68 @@
|
||||
defmodule Pleroma.Repo.Migrations.DataMigrationPopulateUserRelationships do
|
||||
use Ecto.Migration
|
||||
|
||||
alias Ecto.Adapters.SQL
|
||||
alias Pleroma.Repo
|
||||
|
||||
require Logger
|
||||
|
||||
def up do
|
||||
Enum.each(
|
||||
[blocks: 1, mutes: 2, muted_reblogs: 3, muted_notifications: 4, subscribers: 5],
|
||||
fn {field, relationship_type_code} ->
|
||||
migrate(field, relationship_type_code)
|
||||
|
||||
if field == :subscribers do
|
||||
drop_if_exists(index(:users, [:subscribers]))
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
def down, do: :noop
|
||||
|
||||
defp migrate(field, relationship_type_code) do
|
||||
Logger.info("Processing users.#{field}...")
|
||||
|
||||
{:ok, %{rows: field_rows}} =
|
||||
SQL.query(Repo, "SELECT id, #{field} FROM users WHERE #{field} != '{}'")
|
||||
|
||||
target_ap_ids =
|
||||
Enum.flat_map(
|
||||
field_rows,
|
||||
fn [_, ap_ids] -> ap_ids end
|
||||
)
|
||||
|> Enum.uniq()
|
||||
|
||||
# Selecting ids of all targets at once in order to reduce the number of SELECT queries
|
||||
{:ok, %{rows: target_ap_id_id}} =
|
||||
SQL.query(Repo, "SELECT ap_id, id FROM users WHERE ap_id = ANY($1)", [target_ap_ids])
|
||||
|
||||
target_id_by_ap_id = Enum.into(target_ap_id_id, %{}, fn [k, v] -> {k, v} end)
|
||||
|
||||
Enum.each(
|
||||
field_rows,
|
||||
fn [source_id, target_ap_ids] ->
|
||||
source_uuid = Ecto.UUID.cast!(source_id)
|
||||
|
||||
for target_ap_id <- target_ap_ids do
|
||||
target_id = target_id_by_ap_id[target_ap_id]
|
||||
|
||||
with {:ok, target_uuid} <- target_id && Ecto.UUID.cast(target_id) do
|
||||
execute("""
|
||||
INSERT INTO user_relationships(
|
||||
source_id, target_id, relationship_type, inserted_at
|
||||
)
|
||||
VALUES(
|
||||
'#{source_uuid}'::uuid, '#{target_uuid}'::uuid, #{relationship_type_code}, now()
|
||||
)
|
||||
ON CONFLICT (source_id, relationship_type, target_id) DO NOTHING
|
||||
""")
|
||||
else
|
||||
_ -> Logger.warn("Unresolved #{field} reference: (#{source_uuid}, #{target_id})")
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
@ -0,0 +1,9 @@
|
||||
defmodule Pleroma.Repo.Migrations.AddActivitypubActorType do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table("users") do
|
||||
add(:actor_type, :string, null: false, default: "Person")
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,9 @@
|
||||
defmodule Pleroma.Repo.Migrations.RemoveInfoFromUsers do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:users) do
|
||||
remove(:info, :map, default: %{})
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,53 @@
|
||||
defmodule Pleroma.Repo.Migrations.FixMissingFollowingCount do
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
"""
|
||||
UPDATE
|
||||
users
|
||||
SET
|
||||
following_count = sub.count
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
users.id AS sub_id
|
||||
,COUNT (following_relationships.id)
|
||||
FROM
|
||||
following_relationships
|
||||
,users
|
||||
WHERE
|
||||
users.id = following_relationships.follower_id
|
||||
AND following_relationships.state = 'accept'
|
||||
GROUP BY
|
||||
users.id
|
||||
) AS sub
|
||||
WHERE
|
||||
users.id = sub.sub_id
|
||||
AND users.local = TRUE
|
||||
;
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
UPDATE
|
||||
users
|
||||
SET
|
||||
following_count = 0
|
||||
WHERE
|
||||
following_count IS NULL
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
execute("ALTER TABLE users
|
||||
ALTER COLUMN following_count SET DEFAULT 0,
|
||||
ALTER COLUMN following_count SET NOT NULL
|
||||
")
|
||||
end
|
||||
|
||||
def down do
|
||||
execute("ALTER TABLE users
|
||||
ALTER COLUMN following_count DROP DEFAULT,
|
||||
ALTER COLUMN following_count DROP NOT NULL
|
||||
")
|
||||
end
|
||||
end
|
93
priv/scrubbers/default.ex
Normal file
93
priv/scrubbers/default.ex
Normal file
@ -0,0 +1,93 @@
|
||||
defmodule Pleroma.HTML.Scrubber.Default do
|
||||
@doc "The default HTML scrubbing policy: no "
|
||||
|
||||
require FastSanitize.Sanitizer.Meta
|
||||
alias FastSanitize.Sanitizer.Meta
|
||||
|
||||
# credo:disable-for-previous-line
|
||||
# No idea how to fix this one…
|
||||
|
||||
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
|
||||
|
||||
Meta.strip_comments()
|
||||
|
||||
Meta.allow_tag_with_uri_attributes(:a, ["href", "data-user", "data-tag"], @valid_schemes)
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:a, "class", [
|
||||
"hashtag",
|
||||
"u-url",
|
||||
"mention",
|
||||
"u-url mention",
|
||||
"mention u-url"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:a, "rel", [
|
||||
"tag",
|
||||
"nofollow",
|
||||
"noopener",
|
||||
"noreferrer",
|
||||
"ugc"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:a, ["name", "title"])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:abbr, ["title"])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:b, [])
|
||||
Meta.allow_tag_with_these_attributes(:blockquote, [])
|
||||
Meta.allow_tag_with_these_attributes(:br, [])
|
||||
Meta.allow_tag_with_these_attributes(:code, [])
|
||||
Meta.allow_tag_with_these_attributes(:del, [])
|
||||
Meta.allow_tag_with_these_attributes(:em, [])
|
||||
Meta.allow_tag_with_these_attributes(:i, [])
|
||||
Meta.allow_tag_with_these_attributes(:li, [])
|
||||
Meta.allow_tag_with_these_attributes(:ol, [])
|
||||
Meta.allow_tag_with_these_attributes(:p, [])
|
||||
Meta.allow_tag_with_these_attributes(:pre, [])
|
||||
Meta.allow_tag_with_these_attributes(:strong, [])
|
||||
Meta.allow_tag_with_these_attributes(:sub, [])
|
||||
Meta.allow_tag_with_these_attributes(:sup, [])
|
||||
Meta.allow_tag_with_these_attributes(:u, [])
|
||||
Meta.allow_tag_with_these_attributes(:ul, [])
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card"])
|
||||
Meta.allow_tag_with_these_attributes(:span, [])
|
||||
|
||||
@allow_inline_images Pleroma.Config.get([:markup, :allow_inline_images])
|
||||
|
||||
if @allow_inline_images do
|
||||
# restrict img tags to http/https only, because of MediaProxy.
|
||||
Meta.allow_tag_with_uri_attributes(:img, ["src"], ["http", "https"])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:img, [
|
||||
"width",
|
||||
"height",
|
||||
"class",
|
||||
"title",
|
||||
"alt"
|
||||
])
|
||||
end
|
||||
|
||||
if Pleroma.Config.get([:markup, :allow_tables]) do
|
||||
Meta.allow_tag_with_these_attributes(:table, [])
|
||||
Meta.allow_tag_with_these_attributes(:tbody, [])
|
||||
Meta.allow_tag_with_these_attributes(:td, [])
|
||||
Meta.allow_tag_with_these_attributes(:th, [])
|
||||
Meta.allow_tag_with_these_attributes(:thead, [])
|
||||
Meta.allow_tag_with_these_attributes(:tr, [])
|
||||
end
|
||||
|
||||
if Pleroma.Config.get([:markup, :allow_headings]) do
|
||||
Meta.allow_tag_with_these_attributes(:h1, [])
|
||||
Meta.allow_tag_with_these_attributes(:h2, [])
|
||||
Meta.allow_tag_with_these_attributes(:h3, [])
|
||||
Meta.allow_tag_with_these_attributes(:h4, [])
|
||||
Meta.allow_tag_with_these_attributes(:h5, [])
|
||||
end
|
||||
|
||||
if Pleroma.Config.get([:markup, :allow_fonts]) do
|
||||
Meta.allow_tag_with_these_attributes(:font, ["face"])
|
||||
end
|
||||
|
||||
Meta.strip_everything_not_covered()
|
||||
end
|
27
priv/scrubbers/links_only.ex
Normal file
27
priv/scrubbers/links_only.ex
Normal file
@ -0,0 +1,27 @@
|
||||
defmodule Pleroma.HTML.Scrubber.LinksOnly do
|
||||
@moduledoc """
|
||||
An HTML scrubbing policy which limits to links only.
|
||||
"""
|
||||
|
||||
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
|
||||
|
||||
require FastSanitize.Sanitizer.Meta
|
||||
alias FastSanitize.Sanitizer.Meta
|
||||
|
||||
Meta.strip_comments()
|
||||
|
||||
# links
|
||||
Meta.allow_tag_with_uri_attributes(:a, ["href"], @valid_schemes)
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:a, "rel", [
|
||||
"tag",
|
||||
"nofollow",
|
||||
"noopener",
|
||||
"noreferrer",
|
||||
"me",
|
||||
"ugc"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:a, ["name", "title"])
|
||||
Meta.strip_everything_not_covered()
|
||||
end
|
32
priv/scrubbers/media_proxy.ex
Normal file
32
priv/scrubbers/media_proxy.ex
Normal file
@ -0,0 +1,32 @@
|
||||
defmodule Pleroma.HTML.Transform.MediaProxy do
|
||||
@moduledoc "Transforms inline image URIs to use MediaProxy."
|
||||
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
def before_scrub(html), do: html
|
||||
|
||||
def scrub_attribute(:img, {"src", "http" <> target}) do
|
||||
media_url =
|
||||
("http" <> target)
|
||||
|> MediaProxy.url()
|
||||
|
||||
{"src", media_url}
|
||||
end
|
||||
|
||||
def scrub_attribute(_tag, attribute), do: attribute
|
||||
|
||||
def scrub({:img, attributes, children}) do
|
||||
attributes =
|
||||
attributes
|
||||
|> Enum.map(fn attr -> scrub_attribute(:img, attr) end)
|
||||
|> Enum.reject(&is_nil(&1))
|
||||
|
||||
{:img, attributes, children}
|
||||
end
|
||||
|
||||
def scrub({:comment, _text, _children}), do: ""
|
||||
|
||||
def scrub({tag, attributes, children}), do: {tag, attributes, children}
|
||||
def scrub({_tag, children}), do: children
|
||||
def scrub(text), do: text
|
||||
end
|
57
priv/scrubbers/twitter_text.ex
Normal file
57
priv/scrubbers/twitter_text.ex
Normal file
@ -0,0 +1,57 @@
|
||||
defmodule Pleroma.HTML.Scrubber.TwitterText do
|
||||
@moduledoc """
|
||||
An HTML scrubbing policy which limits to twitter-style text. Only
|
||||
paragraphs, breaks and links are allowed through the filter.
|
||||
"""
|
||||
|
||||
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
|
||||
|
||||
require FastSanitize.Sanitizer.Meta
|
||||
alias FastSanitize.Sanitizer.Meta
|
||||
|
||||
Meta.strip_comments()
|
||||
|
||||
# links
|
||||
Meta.allow_tag_with_uri_attributes(:a, ["href", "data-user", "data-tag"], @valid_schemes)
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:a, "class", [
|
||||
"hashtag",
|
||||
"u-url",
|
||||
"mention",
|
||||
"u-url mention",
|
||||
"mention u-url"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_this_attribute_values(:a, "rel", [
|
||||
"tag",
|
||||
"nofollow",
|
||||
"noopener",
|
||||
"noreferrer"
|
||||
])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:a, ["name", "title"])
|
||||
|
||||
# paragraphs and linebreaks
|
||||
Meta.allow_tag_with_these_attributes(:br, [])
|
||||
Meta.allow_tag_with_these_attributes(:p, [])
|
||||
|
||||
# microformats
|
||||
Meta.allow_tag_with_this_attribute_values(:span, "class", ["h-card"])
|
||||
Meta.allow_tag_with_these_attributes(:span, [])
|
||||
|
||||
# allow inline images for custom emoji
|
||||
if Pleroma.Config.get([:markup, :allow_inline_images]) do
|
||||
# restrict img tags to http/https only, because of MediaProxy.
|
||||
Meta.allow_tag_with_uri_attributes(:img, ["src"], ["http", "https"])
|
||||
|
||||
Meta.allow_tag_with_these_attributes(:img, [
|
||||
"width",
|
||||
"height",
|
||||
"class",
|
||||
"title",
|
||||
"alt"
|
||||
])
|
||||
end
|
||||
|
||||
Meta.strip_everything_not_covered()
|
||||
end
|
@ -29,7 +29,11 @@
|
||||
"@id": "litepub:oauthRegistrationEndpoint",
|
||||
"@type": "@id"
|
||||
},
|
||||
"EmojiReaction": "litepub:EmojiReaction"
|
||||
"EmojiReaction": "litepub:EmojiReaction",
|
||||
"alsoKnownAs": {
|
||||
"@id": "as:alsoKnownAs",
|
||||
"@type": "@id"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -252,7 +252,7 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||
|
||||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 4
|
||||
|
||||
{:ok, blocker} = User.block(blocker, blocked)
|
||||
{:ok, _user_relationship} = User.block(blocker, blocked)
|
||||
|
||||
# The conversations with the blocked user are marked as read
|
||||
assert [%{read: true}, %{read: true}, %{read: true}, %{read: false}] =
|
||||
@ -274,7 +274,7 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||
blocked = insert(:user)
|
||||
third_user = insert(:user)
|
||||
|
||||
{:ok, blocker} = User.block(blocker, blocked)
|
||||
{:ok, _user_relationship} = User.block(blocker, blocked)
|
||||
|
||||
# When the blocked user is the author
|
||||
{:ok, _direct1} =
|
||||
@ -311,7 +311,7 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||
"visibility" => "direct"
|
||||
})
|
||||
|
||||
{:ok, blocker} = User.block(blocker, blocked)
|
||||
{:ok, _user_relationship} = User.block(blocker, blocked)
|
||||
assert [%{read: true}] = Participation.for_user(blocker)
|
||||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
|
||||
|
||||
|
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",
|
||||
"conversation": "ostatus:conversation",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"Emoji": "toot:Emoji"
|
||||
"Emoji": "toot:Emoji",
|
||||
"alsoKnownAs": {
|
||||
"@id": "as:alsoKnownAs",
|
||||
"@type": "@id"
|
||||
}
|
||||
}],
|
||||
"id": "http://mastodon.example.org/users/admin",
|
||||
"type": "Person",
|
||||
@ -50,5 +54,6 @@
|
||||
"type": "Image",
|
||||
"mediaType": "image/png",
|
||||
"url": "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"
|
||||
}
|
||||
},
|
||||
"alsoKnownAs": ["http://example.org/users/foo"]
|
||||
}
|
||||
|
19
test/fixtures/users_mock/friendica_followers.json
vendored
Normal file
19
test/fixtures/users_mock/friendica_followers.json
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||
"dfrn": "http://purl.org/macgirvin/dfrn/1.0/",
|
||||
"diaspora": "https://diasporafoundation.org/ns/",
|
||||
"litepub": "http://litepub.social/ns#",
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"sensitive": "as:sensitive",
|
||||
"Hashtag": "as:Hashtag",
|
||||
"directMessage": "litepub:directMessage"
|
||||
}
|
||||
],
|
||||
"id": "http://localhost:8080/followers/fuser3",
|
||||
"type": "OrderedCollection",
|
||||
"totalItems": 296
|
||||
}
|
19
test/fixtures/users_mock/friendica_following.json
vendored
Normal file
19
test/fixtures/users_mock/friendica_following.json
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"vcard": "http://www.w3.org/2006/vcard/ns#",
|
||||
"dfrn": "http://purl.org/macgirvin/dfrn/1.0/",
|
||||
"diaspora": "https://diasporafoundation.org/ns/",
|
||||
"litepub": "http://litepub.social/ns#",
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"sensitive": "as:sensitive",
|
||||
"Hashtag": "as:Hashtag",
|
||||
"directMessage": "litepub:directMessage"
|
||||
}
|
||||
],
|
||||
"id": "http://localhost:8080/following/fuser3",
|
||||
"type": "OrderedCollection",
|
||||
"totalItems": 32
|
||||
}
|
@ -228,5 +228,16 @@ defmodule Pleroma.HTMLTest do
|
||||
|
||||
assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
|
||||
end
|
||||
|
||||
test "does not crash when there is an HTML entity in a link" do
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(user, %{"status" => "\"http://cofe.com/?boomer=ok&foo=bar\""})
|
||||
|
||||
object = Object.normalize(activity)
|
||||
|
||||
assert {:ok, nil} = HTML.extract_first_external_url(object, object.data["content"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -93,7 +93,7 @@ defmodule Pleroma.NotificationTest do
|
||||
activity = insert(:note_activity)
|
||||
author = User.get_cached_by_ap_id(activity.data["actor"])
|
||||
user = insert(:user)
|
||||
{:ok, user} = User.block(user, author)
|
||||
{:ok, _user_relationship} = User.block(user, author)
|
||||
|
||||
assert Notification.create_notification(activity, user)
|
||||
end
|
||||
@ -112,7 +112,7 @@ defmodule Pleroma.NotificationTest do
|
||||
muter = insert(:user)
|
||||
muted = insert(:user)
|
||||
|
||||
{:ok, muter} = User.mute(muter, muted, false)
|
||||
{:ok, _user_relationships} = User.mute(muter, muted, false)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"})
|
||||
|
||||
@ -136,7 +136,10 @@ defmodule Pleroma.NotificationTest do
|
||||
|
||||
test "it disables notifications from followers" do
|
||||
follower = insert(:user)
|
||||
followed = insert(:user, notification_settings: %{"followers" => false})
|
||||
|
||||
followed =
|
||||
insert(:user, notification_settings: %Pleroma.User.NotificationSetting{followers: false})
|
||||
|
||||
User.follow(follower, followed)
|
||||
{:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
|
||||
refute Notification.create_notification(activity, followed)
|
||||
@ -144,13 +147,20 @@ defmodule Pleroma.NotificationTest do
|
||||
|
||||
test "it disables notifications from non-followers" do
|
||||
follower = insert(:user)
|
||||
followed = insert(:user, notification_settings: %{"non_followers" => false})
|
||||
|
||||
followed =
|
||||
insert(:user,
|
||||
notification_settings: %Pleroma.User.NotificationSetting{non_followers: false}
|
||||
)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
|
||||
refute Notification.create_notification(activity, followed)
|
||||
end
|
||||
|
||||
test "it disables notifications from people the user follows" do
|
||||
follower = insert(:user, notification_settings: %{"follows" => false})
|
||||
follower =
|
||||
insert(:user, notification_settings: %Pleroma.User.NotificationSetting{follows: false})
|
||||
|
||||
followed = insert(:user)
|
||||
User.follow(follower, followed)
|
||||
follower = Repo.get(User, follower.id)
|
||||
@ -159,7 +169,9 @@ defmodule Pleroma.NotificationTest do
|
||||
end
|
||||
|
||||
test "it disables notifications from people the user does not follow" do
|
||||
follower = insert(:user, notification_settings: %{"non_follows" => false})
|
||||
follower =
|
||||
insert(:user, notification_settings: %Pleroma.User.NotificationSetting{non_follows: false})
|
||||
|
||||
followed = insert(:user)
|
||||
{:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"})
|
||||
refute Notification.create_notification(activity, follower)
|
||||
@ -630,13 +642,46 @@ defmodule Pleroma.NotificationTest do
|
||||
|
||||
assert Enum.empty?(Notification.for_user(local_user))
|
||||
end
|
||||
|
||||
test "move activity generates a notification" do
|
||||
%{ap_id: old_ap_id} = old_user = insert(:user)
|
||||
%{ap_id: new_ap_id} = new_user = insert(:user, also_known_as: [old_ap_id])
|
||||
follower = insert(:user)
|
||||
other_follower = insert(:user, %{allow_following_move: false})
|
||||
|
||||
User.follow(follower, old_user)
|
||||
User.follow(other_follower, old_user)
|
||||
|
||||
Pleroma.Web.ActivityPub.ActivityPub.move(old_user, new_user)
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
assert [] = Notification.for_user(follower)
|
||||
|
||||
assert [
|
||||
%{
|
||||
activity: %{
|
||||
data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
|
||||
}
|
||||
}
|
||||
] = Notification.for_user(follower, %{with_move: true})
|
||||
|
||||
assert [] = Notification.for_user(other_follower)
|
||||
|
||||
assert [
|
||||
%{
|
||||
activity: %{
|
||||
data: %{"type" => "Move", "actor" => ^old_ap_id, "target" => ^new_ap_id}
|
||||
}
|
||||
}
|
||||
] = Notification.for_user(other_follower, %{with_move: true})
|
||||
end
|
||||
end
|
||||
|
||||
describe "for_user" do
|
||||
test "it returns notifications for muted user without notifications" do
|
||||
user = insert(:user)
|
||||
muted = insert(:user)
|
||||
{:ok, user} = User.mute(user, muted, false)
|
||||
{:ok, _user_relationships} = User.mute(user, muted, false)
|
||||
|
||||
{:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"})
|
||||
|
||||
@ -646,7 +691,7 @@ defmodule Pleroma.NotificationTest do
|
||||
test "it doesn't return notifications for muted user with notifications" do
|
||||
user = insert(:user)
|
||||
muted = insert(:user)
|
||||
{:ok, user} = User.mute(user, muted)
|
||||
{:ok, _user_relationships} = User.mute(user, muted)
|
||||
|
||||
{:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"})
|
||||
|
||||
@ -656,7 +701,7 @@ defmodule Pleroma.NotificationTest do
|
||||
test "it doesn't return notifications for blocked user" do
|
||||
user = insert(:user)
|
||||
blocked = insert(:user)
|
||||
{:ok, user} = User.block(user, blocked)
|
||||
{:ok, _user_relationship} = User.block(user, blocked)
|
||||
|
||||
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
|
||||
|
||||
@ -686,7 +731,7 @@ defmodule Pleroma.NotificationTest do
|
||||
test "it returns notifications from a muted user when with_muted is set" do
|
||||
user = insert(:user)
|
||||
muted = insert(:user)
|
||||
{:ok, user} = User.mute(user, muted)
|
||||
{:ok, _user_relationships} = User.mute(user, muted)
|
||||
|
||||
{:ok, _activity} = CommonAPI.post(muted, %{"status" => "hey @#{user.nickname}"})
|
||||
|
||||
@ -696,7 +741,7 @@ defmodule Pleroma.NotificationTest do
|
||||
test "it doesn't return notifications from a blocked user when with_muted is set" do
|
||||
user = insert(:user)
|
||||
blocked = insert(:user)
|
||||
{:ok, user} = User.block(user, blocked)
|
||||
{:ok, _user_relationship} = User.block(user, blocked)
|
||||
|
||||
{:ok, _activity} = CommonAPI.post(blocked, %{"status" => "hey @#{user.nickname}"})
|
||||
|
||||
|
@ -8,36 +8,116 @@ defmodule Pleroma.Plugs.UserIsAdminPlugTest do
|
||||
alias Pleroma.Plugs.UserIsAdminPlug
|
||||
import Pleroma.Factory
|
||||
|
||||
test "accepts a user that is admin" do
|
||||
user = insert(:user, is_admin: true)
|
||||
describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
|
||||
clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
|
||||
Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], false)
|
||||
end
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
test "accepts a user that is an admin" do
|
||||
user = insert(:user, is_admin: true)
|
||||
|
||||
ret_conn =
|
||||
conn
|
||||
|> UserIsAdminPlug.call(%{})
|
||||
conn = assign(build_conn(), :user, user)
|
||||
|
||||
assert conn == ret_conn
|
||||
ret_conn = UserIsAdminPlug.call(conn, %{})
|
||||
|
||||
assert conn == ret_conn
|
||||
end
|
||||
|
||||
test "denies a user that isn't an admin" do
|
||||
user = insert(:user)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> UserIsAdminPlug.call(%{})
|
||||
|
||||
assert conn.status == 403
|
||||
end
|
||||
|
||||
test "denies when a user isn't set" do
|
||||
conn = UserIsAdminPlug.call(build_conn(), %{})
|
||||
|
||||
assert conn.status == 403
|
||||
end
|
||||
end
|
||||
|
||||
test "denies a user that isn't admin" do
|
||||
user = insert(:user)
|
||||
describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
|
||||
clear_config([:auth, :enforce_oauth_admin_scope_usage]) do
|
||||
Pleroma.Config.put([:auth, :enforce_oauth_admin_scope_usage], true)
|
||||
end
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> UserIsAdminPlug.call(%{})
|
||||
setup do
|
||||
admin_user = insert(:user, is_admin: true)
|
||||
non_admin_user = insert(:user, is_admin: false)
|
||||
blank_user = nil
|
||||
|
||||
assert conn.status == 403
|
||||
end
|
||||
{:ok, %{users: [admin_user, non_admin_user, blank_user]}}
|
||||
end
|
||||
|
||||
test "denies when a user isn't set" do
|
||||
conn =
|
||||
build_conn()
|
||||
|> UserIsAdminPlug.call(%{})
|
||||
test "if token has any of admin scopes, accepts a user that is an admin", %{conn: conn} do
|
||||
user = insert(:user, is_admin: true)
|
||||
token = insert(:oauth_token, user: user, scopes: ["admin:something"])
|
||||
|
||||
assert conn.status == 403
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> assign(:token, token)
|
||||
|
||||
ret_conn = UserIsAdminPlug.call(conn, %{})
|
||||
|
||||
assert conn == ret_conn
|
||||
end
|
||||
|
||||
test "if token has any of admin scopes, denies a user that isn't an admin", %{conn: conn} do
|
||||
user = insert(:user, is_admin: false)
|
||||
token = insert(:oauth_token, user: user, scopes: ["admin:something"])
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> assign(:token, token)
|
||||
|> UserIsAdminPlug.call(%{})
|
||||
|
||||
assert conn.status == 403
|
||||
end
|
||||
|
||||
test "if token has any of admin scopes, denies when a user isn't set", %{conn: conn} do
|
||||
token = insert(:oauth_token, scopes: ["admin:something"])
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, nil)
|
||||
|> assign(:token, token)
|
||||
|> UserIsAdminPlug.call(%{})
|
||||
|
||||
assert conn.status == 403
|
||||
end
|
||||
|
||||
test "if token lacks admin scopes, denies users regardless of is_admin flag",
|
||||
%{users: users} do
|
||||
for user <- users do
|
||||
token = insert(:oauth_token, user: user)
|
||||
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> assign(:token, token)
|
||||
|> UserIsAdminPlug.call(%{})
|
||||
|
||||
assert conn.status == 403
|
||||
end
|
||||
end
|
||||
|
||||
test "if token is missing, denies users regardless of is_admin flag", %{users: users} do
|
||||
for user <- users do
|
||||
conn =
|
||||
build_conn()
|
||||
|> assign(:user, user)
|
||||
|> assign(:token, nil)
|
||||
|> UserIsAdminPlug.call(%{})
|
||||
|
||||
assert conn.status == 403
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -10,7 +10,8 @@ defmodule Pleroma.Builders.UserBuilder do
|
||||
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
|
||||
bio: "A tester.",
|
||||
ap_id: "some id",
|
||||
last_digest_emailed_at: NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second)
|
||||
last_digest_emailed_at: NaiveDateTime.truncate(NaiveDateTime.utc_now(), :second),
|
||||
notification_settings: %Pleroma.User.NotificationSetting{}
|
||||
}
|
||||
|
||||
Map.merge(user, data)
|
||||
|
@ -23,6 +23,7 @@ defmodule Pleroma.Web.ChannelCase do
|
||||
quote do
|
||||
# Import conveniences for testing with channels
|
||||
use Phoenix.ChannelTest
|
||||
use Pleroma.Tests.Helpers
|
||||
|
||||
# The default endpoint for testing
|
||||
@endpoint Pleroma.Web.Endpoint
|
||||
|
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,8 +31,8 @@ defmodule Pleroma.Factory do
|
||||
nickname: sequence(:nickname, &"nick#{&1}"),
|
||||
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
|
||||
bio: sequence(:bio, &"Tester Number #{&1}"),
|
||||
info: %{},
|
||||
last_digest_emailed_at: NaiveDateTime.utc_now()
|
||||
last_digest_emailed_at: NaiveDateTime.utc_now(),
|
||||
notification_settings: %Pleroma.User.NotificationSetting{}
|
||||
}
|
||||
|
||||
%{
|
||||
@ -43,6 +43,18 @@ defmodule Pleroma.Factory do
|
||||
}
|
||||
end
|
||||
|
||||
def user_relationship_factory(attrs \\ %{}) do
|
||||
source = attrs[:source] || insert(:user)
|
||||
target = attrs[:target] || insert(:user)
|
||||
relationship_type = attrs[:relationship_type] || :block
|
||||
|
||||
%Pleroma.UserRelationship{
|
||||
source_id: source.id,
|
||||
target_id: target.id,
|
||||
relationship_type: relationship_type
|
||||
}
|
||||
end
|
||||
|
||||
def note_factory(attrs \\ %{}) do
|
||||
text = sequence(:text, &"This is :moominmamma: note #{&1}")
|
||||
|
||||
|
@ -75,6 +75,23 @@ defmodule Pleroma.Tests.Helpers do
|
||||
|> Poison.decode!()
|
||||
end
|
||||
|
||||
def stringify_keys(nil), do: nil
|
||||
|
||||
def stringify_keys(key) when key in [true, false], do: key
|
||||
def stringify_keys(key) when is_atom(key), do: Atom.to_string(key)
|
||||
|
||||
def stringify_keys(map) when is_map(map) do
|
||||
map
|
||||
|> Enum.map(fn {k, v} -> {stringify_keys(k), stringify_keys(v)} end)
|
||||
|> Enum.into(%{})
|
||||
end
|
||||
|
||||
def stringify_keys([head | rest] = list) when is_list(list) do
|
||||
[stringify_keys(head) | stringify_keys(rest)]
|
||||
end
|
||||
|
||||
def stringify_keys(key), do: key
|
||||
|
||||
defmacro guards_config(config_path) do
|
||||
quote do
|
||||
initial_setting = Pleroma.Config.get(config_path)
|
||||
|
@ -1035,6 +1035,22 @@ defmodule HttpRequestMock do
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://localhost:8080/followers/fuser3", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/users_mock/friendica_followers.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://localhost:8080/following/fuser3", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: File.read!("test/fixtures/users_mock/friendica_following.json")
|
||||
}}
|
||||
end
|
||||
|
||||
def get("http://localhost:4001/users/fuser2/followers", _, _, _) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
|
@ -63,4 +63,84 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
||||
assert file =~ "config :pleroma, :setting_first,"
|
||||
assert file =~ "config :pleroma, :setting_second,"
|
||||
end
|
||||
|
||||
test "load a settings with large values and pass to file", %{temp_file: temp_file} do
|
||||
Config.create(%{
|
||||
group: "pleroma",
|
||||
key: ":instance",
|
||||
value: [
|
||||
name: "Pleroma",
|
||||
email: "example@example.com",
|
||||
notify_email: "noreply@example.com",
|
||||
description: "A Pleroma instance, an alternative fediverse server",
|
||||
limit: 5_000,
|
||||
chat_limit: 5_000,
|
||||
remote_limit: 100_000,
|
||||
upload_limit: 16_000_000,
|
||||
avatar_upload_limit: 2_000_000,
|
||||
background_upload_limit: 4_000_000,
|
||||
banner_upload_limit: 4_000_000,
|
||||
poll_limits: %{
|
||||
max_options: 20,
|
||||
max_option_chars: 200,
|
||||
min_expiration: 0,
|
||||
max_expiration: 365 * 24 * 60 * 60
|
||||
},
|
||||
registrations_open: true,
|
||||
federating: true,
|
||||
federation_incoming_replies_max_depth: 100,
|
||||
federation_reachability_timeout_days: 7,
|
||||
federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],
|
||||
allow_relay: true,
|
||||
rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
|
||||
public: true,
|
||||
quarantined_instances: [],
|
||||
managed_config: true,
|
||||
static_dir: "instance/static/",
|
||||
allowed_post_formats: ["text/plain", "text/html", "text/markdown", "text/bbcode"],
|
||||
mrf_transparency: true,
|
||||
mrf_transparency_exclusions: [],
|
||||
autofollowed_nicknames: [],
|
||||
max_pinned_statuses: 1,
|
||||
no_attachment_links: true,
|
||||
welcome_user_nickname: nil,
|
||||
welcome_message: nil,
|
||||
max_report_comment_size: 1000,
|
||||
safe_dm_mentions: false,
|
||||
healthcheck: false,
|
||||
remote_post_retention_days: 90,
|
||||
skip_thread_containment: true,
|
||||
limit_to_local_content: :unauthenticated,
|
||||
dynamic_configuration: false,
|
||||
user_bio_length: 5000,
|
||||
user_name_length: 100,
|
||||
max_account_fields: 10,
|
||||
max_remote_account_fields: 20,
|
||||
account_field_name_length: 512,
|
||||
account_field_value_length: 2048,
|
||||
external_user_synchronization: true,
|
||||
extended_nickname_format: true,
|
||||
multi_factor_authentication: [
|
||||
totp: [
|
||||
# digits 6 or 8
|
||||
digits: 6,
|
||||
period: 30
|
||||
],
|
||||
backup_codes: [
|
||||
number: 2,
|
||||
length: 6
|
||||
]
|
||||
]
|
||||
]
|
||||
})
|
||||
|
||||
Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "temp", "true"])
|
||||
|
||||
assert Repo.all(Config) == []
|
||||
assert File.exists?(temp_file)
|
||||
{:ok, file} = File.read(temp_file)
|
||||
|
||||
assert file ==
|
||||
"use Mix.Config\n\nconfig :pleroma, :instance,\n name: \"Pleroma\",\n email: \"example@example.com\",\n notify_email: \"noreply@example.com\",\n description: \"A Pleroma instance, an alternative fediverse server\",\n limit: 5000,\n chat_limit: 5000,\n remote_limit: 100_000,\n upload_limit: 16_000_000,\n avatar_upload_limit: 2_000_000,\n background_upload_limit: 4_000_000,\n banner_upload_limit: 4_000_000,\n poll_limits: %{\n max_expiration: 31_536_000,\n max_option_chars: 200,\n max_options: 20,\n min_expiration: 0\n },\n registrations_open: true,\n federating: true,\n federation_incoming_replies_max_depth: 100,\n federation_reachability_timeout_days: 7,\n federation_publisher_modules: [Pleroma.Web.ActivityPub.Publisher],\n allow_relay: true,\n rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,\n public: true,\n quarantined_instances: [],\n managed_config: true,\n static_dir: \"instance/static/\",\n allowed_post_formats: [\"text/plain\", \"text/html\", \"text/markdown\", \"text/bbcode\"],\n mrf_transparency: true,\n mrf_transparency_exclusions: [],\n autofollowed_nicknames: [],\n max_pinned_statuses: 1,\n no_attachment_links: true,\n welcome_user_nickname: nil,\n welcome_message: nil,\n max_report_comment_size: 1000,\n safe_dm_mentions: false,\n healthcheck: false,\n remote_post_retention_days: 90,\n skip_thread_containment: true,\n limit_to_local_content: :unauthenticated,\n dynamic_configuration: false,\n user_bio_length: 5000,\n user_name_length: 100,\n max_account_fields: 10,\n max_remote_account_fields: 20,\n account_field_name_length: 512,\n account_field_value_length: 2048,\n external_user_synchronization: true,\n extended_nickname_format: true,\n multi_factor_authentication: [\n totp: [digits: 6, period: 30],\n backup_codes: [number: 2, length: 6]\n ]\n"
|
||||
end
|
||||
end
|
||||
|
@ -3,7 +3,8 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
os_exclude = if :os.type() == {:unix, :darwin}, do: [skip_on_mac: true], else: []
|
||||
ExUnit.start(exclude: os_exclude)
|
||||
ExUnit.start(exclude: [:federated | os_exclude])
|
||||
|
||||
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)
|
||||
Mox.defmock(Pleroma.ReverseProxy.ClientMock, for: Pleroma.ReverseProxy.Client)
|
||||
{:ok, _} = Application.ensure_all_started(:ex_machina)
|
||||
|
21
test/user/notification_setting_test.exs
Normal file
21
test/user/notification_setting_test.exs
Normal file
@ -0,0 +1,21 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.User.NotificationSettingTest do
|
||||
use Pleroma.DataCase
|
||||
|
||||
alias Pleroma.User.NotificationSetting
|
||||
|
||||
describe "changeset/2" do
|
||||
test "sets valid privacy option" do
|
||||
changeset =
|
||||
NotificationSetting.changeset(
|
||||
%NotificationSetting{},
|
||||
%{"privacy_option" => true}
|
||||
)
|
||||
|
||||
assert %Ecto.Changeset{valid?: true} = changeset
|
||||
end
|
||||
end
|
||||
end
|
130
test/user_relationship_test.exs
Normal file
130
test/user_relationship_test.exs
Normal file
@ -0,0 +1,130 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.UserRelationshipTest do
|
||||
alias Pleroma.UserRelationship
|
||||
|
||||
use Pleroma.DataCase
|
||||
|
||||
import Pleroma.Factory
|
||||
|
||||
describe "*_exists?/2" do
|
||||
setup do
|
||||
{:ok, users: insert_list(2, :user)}
|
||||
end
|
||||
|
||||
test "returns false if record doesn't exist", %{users: [user1, user2]} do
|
||||
refute UserRelationship.block_exists?(user1, user2)
|
||||
refute UserRelationship.mute_exists?(user1, user2)
|
||||
refute UserRelationship.notification_mute_exists?(user1, user2)
|
||||
refute UserRelationship.reblog_mute_exists?(user1, user2)
|
||||
refute UserRelationship.inverse_subscription_exists?(user1, user2)
|
||||
end
|
||||
|
||||
test "returns true if record exists", %{users: [user1, user2]} do
|
||||
for relationship_type <- [
|
||||
:block,
|
||||
:mute,
|
||||
:notification_mute,
|
||||
:reblog_mute,
|
||||
:inverse_subscription
|
||||
] do
|
||||
insert(:user_relationship,
|
||||
source: user1,
|
||||
target: user2,
|
||||
relationship_type: relationship_type
|
||||
)
|
||||
end
|
||||
|
||||
assert UserRelationship.block_exists?(user1, user2)
|
||||
assert UserRelationship.mute_exists?(user1, user2)
|
||||
assert UserRelationship.notification_mute_exists?(user1, user2)
|
||||
assert UserRelationship.reblog_mute_exists?(user1, user2)
|
||||
assert UserRelationship.inverse_subscription_exists?(user1, user2)
|
||||
end
|
||||
end
|
||||
|
||||
describe "create_*/2" do
|
||||
setup do
|
||||
{:ok, users: insert_list(2, :user)}
|
||||
end
|
||||
|
||||
test "creates user relationship record if it doesn't exist", %{users: [user1, user2]} do
|
||||
for relationship_type <- [
|
||||
:block,
|
||||
:mute,
|
||||
:notification_mute,
|
||||
:reblog_mute,
|
||||
:inverse_subscription
|
||||
] do
|
||||
insert(:user_relationship,
|
||||
source: user1,
|
||||
target: user2,
|
||||
relationship_type: relationship_type
|
||||
)
|
||||
end
|
||||
|
||||
UserRelationship.create_block(user1, user2)
|
||||
UserRelationship.create_mute(user1, user2)
|
||||
UserRelationship.create_notification_mute(user1, user2)
|
||||
UserRelationship.create_reblog_mute(user1, user2)
|
||||
UserRelationship.create_inverse_subscription(user1, user2)
|
||||
|
||||
assert UserRelationship.block_exists?(user1, user2)
|
||||
assert UserRelationship.mute_exists?(user1, user2)
|
||||
assert UserRelationship.notification_mute_exists?(user1, user2)
|
||||
assert UserRelationship.reblog_mute_exists?(user1, user2)
|
||||
assert UserRelationship.inverse_subscription_exists?(user1, user2)
|
||||
end
|
||||
|
||||
test "if record already exists, returns it", %{users: [user1, user2]} do
|
||||
user_block = UserRelationship.create_block(user1, user2)
|
||||
assert user_block == UserRelationship.create_block(user1, user2)
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete_*/2" do
|
||||
setup do
|
||||
{:ok, users: insert_list(2, :user)}
|
||||
end
|
||||
|
||||
test "deletes user relationship record if it exists", %{users: [user1, user2]} do
|
||||
for relationship_type <- [
|
||||
:block,
|
||||
:mute,
|
||||
:notification_mute,
|
||||
:reblog_mute,
|
||||
:inverse_subscription
|
||||
] do
|
||||
insert(:user_relationship,
|
||||
source: user1,
|
||||
target: user2,
|
||||
relationship_type: relationship_type
|
||||
)
|
||||
end
|
||||
|
||||
assert {:ok, %UserRelationship{}} = UserRelationship.delete_block(user1, user2)
|
||||
assert {:ok, %UserRelationship{}} = UserRelationship.delete_mute(user1, user2)
|
||||
assert {:ok, %UserRelationship{}} = UserRelationship.delete_notification_mute(user1, user2)
|
||||
assert {:ok, %UserRelationship{}} = UserRelationship.delete_reblog_mute(user1, user2)
|
||||
|
||||
assert {:ok, %UserRelationship{}} =
|
||||
UserRelationship.delete_inverse_subscription(user1, user2)
|
||||
|
||||
refute UserRelationship.block_exists?(user1, user2)
|
||||
refute UserRelationship.mute_exists?(user1, user2)
|
||||
refute UserRelationship.notification_mute_exists?(user1, user2)
|
||||
refute UserRelationship.reblog_mute_exists?(user1, user2)
|
||||
refute UserRelationship.inverse_subscription_exists?(user1, user2)
|
||||
end
|
||||
|
||||
test "if record does not exist, returns {:ok, nil}", %{users: [user1, user2]} do
|
||||
assert {:ok, nil} = UserRelationship.delete_block(user1, user2)
|
||||
assert {:ok, nil} = UserRelationship.delete_mute(user1, user2)
|
||||
assert {:ok, nil} = UserRelationship.delete_notification_mute(user1, user2)
|
||||
assert {:ok, nil} = UserRelationship.delete_reblog_mute(user1, user2)
|
||||
assert {:ok, nil} = UserRelationship.delete_inverse_subscription(user1, user2)
|
||||
end
|
||||
end
|
||||
end
|
@ -174,6 +174,7 @@ defmodule Pleroma.UserSearchTest do
|
||||
|> Map.put(:search_rank, nil)
|
||||
|> Map.put(:search_type, nil)
|
||||
|> Map.put(:last_digest_emailed_at, nil)
|
||||
|> Map.put(:notification_settings, nil)
|
||||
|
||||
assert user == expected
|
||||
end
|
||||
|
@ -44,6 +44,56 @@ defmodule Pleroma.UserTest do
|
||||
end
|
||||
end
|
||||
|
||||
describe "AP ID user relationships" do
|
||||
setup do
|
||||
{:ok, user: insert(:user)}
|
||||
end
|
||||
|
||||
test "outgoing_relations_ap_ids/1", %{user: user} do
|
||||
rel_types = [:block, :mute, :notification_mute, :reblog_mute, :inverse_subscription]
|
||||
|
||||
ap_ids_by_rel =
|
||||
Enum.into(
|
||||
rel_types,
|
||||
%{},
|
||||
fn rel_type ->
|
||||
rel_records =
|
||||
insert_list(2, :user_relationship, %{source: user, relationship_type: rel_type})
|
||||
|
||||
ap_ids = Enum.map(rel_records, fn rr -> Repo.preload(rr, :target).target.ap_id end)
|
||||
{rel_type, Enum.sort(ap_ids)}
|
||||
end
|
||||
)
|
||||
|
||||
assert ap_ids_by_rel[:block] == Enum.sort(User.blocked_users_ap_ids(user))
|
||||
assert ap_ids_by_rel[:block] == Enum.sort(Enum.map(User.blocked_users(user), & &1.ap_id))
|
||||
|
||||
assert ap_ids_by_rel[:mute] == Enum.sort(User.muted_users_ap_ids(user))
|
||||
assert ap_ids_by_rel[:mute] == Enum.sort(Enum.map(User.muted_users(user), & &1.ap_id))
|
||||
|
||||
assert ap_ids_by_rel[:notification_mute] ==
|
||||
Enum.sort(User.notification_muted_users_ap_ids(user))
|
||||
|
||||
assert ap_ids_by_rel[:notification_mute] ==
|
||||
Enum.sort(Enum.map(User.notification_muted_users(user), & &1.ap_id))
|
||||
|
||||
assert ap_ids_by_rel[:reblog_mute] == Enum.sort(User.reblog_muted_users_ap_ids(user))
|
||||
|
||||
assert ap_ids_by_rel[:reblog_mute] ==
|
||||
Enum.sort(Enum.map(User.reblog_muted_users(user), & &1.ap_id))
|
||||
|
||||
assert ap_ids_by_rel[:inverse_subscription] == Enum.sort(User.subscriber_users_ap_ids(user))
|
||||
|
||||
assert ap_ids_by_rel[:inverse_subscription] ==
|
||||
Enum.sort(Enum.map(User.subscriber_users(user), & &1.ap_id))
|
||||
|
||||
outgoing_relations_ap_ids = User.outgoing_relations_ap_ids(user, rel_types)
|
||||
|
||||
assert ap_ids_by_rel ==
|
||||
Enum.into(outgoing_relations_ap_ids, %{}, fn {k, v} -> {k, Enum.sort(v)} end)
|
||||
end
|
||||
end
|
||||
|
||||
describe "when tags are nil" do
|
||||
test "tagging a user" do
|
||||
user = insert(:user, %{tags: nil})
|
||||
@ -119,7 +169,7 @@ defmodule Pleroma.UserTest do
|
||||
CommonAPI.follow(follower, followed)
|
||||
assert [_activity] = User.get_follow_requests(followed)
|
||||
|
||||
{:ok, _follower} = User.block(followed, follower)
|
||||
{:ok, _user_relationship} = User.block(followed, follower)
|
||||
assert [] = User.get_follow_requests(followed)
|
||||
end
|
||||
|
||||
@ -132,8 +182,8 @@ defmodule Pleroma.UserTest do
|
||||
not_followed = insert(:user)
|
||||
reverse_blocked = insert(:user)
|
||||
|
||||
{:ok, user} = User.block(user, blocked)
|
||||
{:ok, reverse_blocked} = User.block(reverse_blocked, user)
|
||||
{:ok, _user_relationship} = User.block(user, blocked)
|
||||
{:ok, _user_relationship} = User.block(reverse_blocked, user)
|
||||
|
||||
{:ok, user} = User.follow(user, followed_zero)
|
||||
|
||||
@ -186,7 +236,7 @@ defmodule Pleroma.UserTest do
|
||||
blocker = insert(:user)
|
||||
blockee = insert(:user)
|
||||
|
||||
{:ok, blocker} = User.block(blocker, blockee)
|
||||
{:ok, _user_relationship} = User.block(blocker, blockee)
|
||||
|
||||
{:error, _} = User.follow(blockee, blocker)
|
||||
end
|
||||
@ -195,7 +245,7 @@ defmodule Pleroma.UserTest do
|
||||
blocker = insert(:user)
|
||||
blocked = insert(:user)
|
||||
|
||||
{:ok, blocker} = User.block(blocker, blocked)
|
||||
{:ok, _user_relationship} = User.block(blocker, blocked)
|
||||
|
||||
{:error, _} = User.subscribe(blocked, blocker)
|
||||
end
|
||||
@ -367,18 +417,6 @@ defmodule Pleroma.UserTest do
|
||||
|
||||
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
|
||||
end
|
||||
|
||||
test "it ensures info is not nil" do
|
||||
changeset = User.register_changeset(%User{}, @full_user_data)
|
||||
|
||||
assert changeset.valid?
|
||||
|
||||
{:ok, user} =
|
||||
changeset
|
||||
|> Repo.insert()
|
||||
|
||||
refute is_nil(user.info)
|
||||
end
|
||||
end
|
||||
|
||||
describe "user registration, with :account_activation_required" do
|
||||
@ -432,8 +470,7 @@ defmodule Pleroma.UserTest do
|
||||
:user,
|
||||
local: false,
|
||||
nickname: "admin@mastodon.example.org",
|
||||
ap_id: ap_id,
|
||||
info: %{}
|
||||
ap_id: ap_id
|
||||
)
|
||||
|
||||
{:ok, fetched_user} = User.get_or_fetch(ap_id)
|
||||
@ -494,8 +531,7 @@ defmodule Pleroma.UserTest do
|
||||
local: false,
|
||||
nickname: "admin@mastodon.example.org",
|
||||
ap_id: "http://mastodon.example.org/users/admin",
|
||||
last_refreshed_at: a_week_ago,
|
||||
info: %{}
|
||||
last_refreshed_at: a_week_ago
|
||||
)
|
||||
|
||||
assert orig_user.last_refreshed_at == a_week_ago
|
||||
@ -536,7 +572,6 @@ defmodule Pleroma.UserTest do
|
||||
name: "Someone",
|
||||
nickname: "a@b.de",
|
||||
ap_id: "http...",
|
||||
info: %{some: "info"},
|
||||
avatar: %{some: "avatar"}
|
||||
}
|
||||
|
||||
@ -693,7 +728,7 @@ defmodule Pleroma.UserTest do
|
||||
refute User.mutes?(user, muted_user)
|
||||
refute User.muted_notifications?(user, muted_user)
|
||||
|
||||
{:ok, user} = User.mute(user, muted_user)
|
||||
{:ok, _user_relationships} = User.mute(user, muted_user)
|
||||
|
||||
assert User.mutes?(user, muted_user)
|
||||
assert User.muted_notifications?(user, muted_user)
|
||||
@ -703,8 +738,8 @@ defmodule Pleroma.UserTest do
|
||||
user = insert(:user)
|
||||
muted_user = insert(:user)
|
||||
|
||||
{:ok, user} = User.mute(user, muted_user)
|
||||
{:ok, user} = User.unmute(user, muted_user)
|
||||
{:ok, _user_relationships} = User.mute(user, muted_user)
|
||||
{:ok, _user_mute} = User.unmute(user, muted_user)
|
||||
|
||||
refute User.mutes?(user, muted_user)
|
||||
refute User.muted_notifications?(user, muted_user)
|
||||
@ -717,7 +752,7 @@ defmodule Pleroma.UserTest do
|
||||
refute User.mutes?(user, muted_user)
|
||||
refute User.muted_notifications?(user, muted_user)
|
||||
|
||||
{:ok, user} = User.mute(user, muted_user, false)
|
||||
{:ok, _user_relationships} = User.mute(user, muted_user, false)
|
||||
|
||||
assert User.mutes?(user, muted_user)
|
||||
refute User.muted_notifications?(user, muted_user)
|
||||
@ -731,7 +766,7 @@ defmodule Pleroma.UserTest do
|
||||
|
||||
refute User.blocks?(user, blocked_user)
|
||||
|
||||
{:ok, user} = User.block(user, blocked_user)
|
||||
{:ok, _user_relationship} = User.block(user, blocked_user)
|
||||
|
||||
assert User.blocks?(user, blocked_user)
|
||||
end
|
||||
@ -740,8 +775,8 @@ defmodule Pleroma.UserTest do
|
||||
user = insert(:user)
|
||||
blocked_user = insert(:user)
|
||||
|
||||
{:ok, user} = User.block(user, blocked_user)
|
||||
{:ok, user} = User.unblock(user, blocked_user)
|
||||
{:ok, _user_relationship} = User.block(user, blocked_user)
|
||||
{:ok, _user_block} = User.unblock(user, blocked_user)
|
||||
|
||||
refute User.blocks?(user, blocked_user)
|
||||
end
|
||||
@ -756,7 +791,7 @@ defmodule Pleroma.UserTest do
|
||||
assert User.following?(blocker, blocked)
|
||||
assert User.following?(blocked, blocker)
|
||||
|
||||
{:ok, blocker} = User.block(blocker, blocked)
|
||||
{:ok, _user_relationship} = User.block(blocker, blocked)
|
||||
blocked = User.get_cached_by_id(blocked.id)
|
||||
|
||||
assert User.blocks?(blocker, blocked)
|
||||
@ -774,7 +809,7 @@ defmodule Pleroma.UserTest do
|
||||
assert User.following?(blocker, blocked)
|
||||
refute User.following?(blocked, blocker)
|
||||
|
||||
{:ok, blocker} = User.block(blocker, blocked)
|
||||
{:ok, _user_relationship} = User.block(blocker, blocked)
|
||||
blocked = User.get_cached_by_id(blocked.id)
|
||||
|
||||
assert User.blocks?(blocker, blocked)
|
||||
@ -792,7 +827,7 @@ defmodule Pleroma.UserTest do
|
||||
refute User.following?(blocker, blocked)
|
||||
assert User.following?(blocked, blocker)
|
||||
|
||||
{:ok, blocker} = User.block(blocker, blocked)
|
||||
{:ok, _user_relationship} = User.block(blocker, blocked)
|
||||
blocked = User.get_cached_by_id(blocked.id)
|
||||
|
||||
assert User.blocks?(blocker, blocked)
|
||||
@ -805,12 +840,12 @@ defmodule Pleroma.UserTest do
|
||||
blocker = insert(:user)
|
||||
blocked = insert(:user)
|
||||
|
||||
{:ok, blocker} = User.subscribe(blocked, blocker)
|
||||
{:ok, _subscription} = User.subscribe(blocked, blocker)
|
||||
|
||||
assert User.subscribed_to?(blocked, blocker)
|
||||
refute User.subscribed_to?(blocker, blocked)
|
||||
|
||||
{:ok, blocker} = User.block(blocker, blocked)
|
||||
{:ok, _user_relationship} = User.block(blocker, blocked)
|
||||
|
||||
assert User.blocks?(blocker, blocked)
|
||||
refute User.subscribed_to?(blocker, blocked)
|
||||
@ -1153,8 +1188,7 @@ defmodule Pleroma.UserTest do
|
||||
ap_id: user.ap_id,
|
||||
name: user.name,
|
||||
nickname: user.nickname,
|
||||
bio: String.duplicate("h", current_max_length + 1),
|
||||
info: %{}
|
||||
bio: String.duplicate("h", current_max_length + 1)
|
||||
}
|
||||
|
||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||
@ -1167,8 +1201,7 @@ defmodule Pleroma.UserTest do
|
||||
data = %{
|
||||
ap_id: user.ap_id,
|
||||
name: String.duplicate("h", current_max_length + 1),
|
||||
nickname: user.nickname,
|
||||
info: %{}
|
||||
nickname: user.nickname
|
||||
}
|
||||
|
||||
assert {:ok, %User{}} = User.insert_or_update_user(data)
|
||||
@ -1351,7 +1384,8 @@ defmodule Pleroma.UserTest do
|
||||
{:ok, _follower2} = User.follow(follower2, user)
|
||||
{:ok, _follower3} = User.follow(follower3, user)
|
||||
|
||||
{:ok, user} = User.block(user, follower)
|
||||
{:ok, _user_relationship} = User.block(user, follower)
|
||||
user = refresh_record(user)
|
||||
|
||||
assert user.follower_count == 2
|
||||
end
|
||||
|
@ -298,7 +298,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||
assert json_response(conn1, :ok)
|
||||
assert Enum.any?(conn1.resp_headers, &(&1 == {"x-cache", "MISS from Pleroma"}))
|
||||
|
||||
Activity.delete_by_ap_id(activity.object.data["id"])
|
||||
Activity.delete_all_by_object_ap_id(activity.object.data["id"])
|
||||
|
||||
conn2 =
|
||||
conn
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user