Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into emr_develop
This commit is contained in:
commit
b7247af696
|
@ -1,8 +1,10 @@
|
|||
*.ex diff=elixir
|
||||
*.exs diff=elixir
|
||||
# At the time of writing all js/css files included
|
||||
# in the repo are minified bundles, and we don't want
|
||||
# to search/diff those as text files.
|
||||
|
||||
priv/static/instance/static.css diff=css
|
||||
|
||||
# Most of js/css files included in the repo are minified bundles,
|
||||
# and we don't want to search/diff those as text files.
|
||||
*.js binary
|
||||
*.js.map binary
|
||||
*.css binary
|
||||
|
|
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -10,6 +10,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
- Polls now always return a `voters_count`, even if they are single-choice.
|
||||
- Admin Emails: The ap id is used as the user link in emails now.
|
||||
- Improved registration workflow for email confirmation and account approval modes.
|
||||
- **Breaking:** Changed `mix pleroma.user toggle_confirmed` to `mix pleroma.user confirm`
|
||||
- Search: When using Postgres 11+, Pleroma will use the `websearch_to_tsvector` function to parse search queries.
|
||||
- Emoji: Support the full Unicode 13.1 set of Emoji for reactions, plus regional indicators.
|
||||
|
||||
|
@ -17,13 +19,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
- Reports now generate notifications for admins and mods.
|
||||
- Experimental websocket-based federation between Pleroma instances.
|
||||
- Support for local-only statuses
|
||||
- Support for local-only statuses.
|
||||
- Support pagination of blocks and mutes.
|
||||
- Account backup.
|
||||
- Configuration: Add `:instance, autofollowing_nicknames` setting to provide a way to make accounts automatically follow new users that register on the local Pleroma instance.
|
||||
- Ability to view remote timelines, with ex. `/api/v1/timelines/public?instance=lain.com` and streams `public:remote` and `public:remote:media`.
|
||||
- The site title is now injected as a `title` tag like preloads or metadata.
|
||||
- Password reset tokens now are not accepted after a certain age.
|
||||
- Mix tasks to help with displaying and removing ConfigDB entries. See `mix pleroma.config`.
|
||||
- OAuth form improvements: users are remembered by their cookie, the CSS is overridable by the admin, and the style has been improved.
|
||||
- OAuth improvements and fixes: more secure session-based authentication (by token that could be revoked anytime), ability to revoke belonging OAuth token from any client etc.
|
||||
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
|
@ -31,13 +36,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Pleroma API: Add `idempotency_key` to the chat message entity that can be used for optimistic message sending.
|
||||
- Pleroma API: (`GET /api/v1/pleroma/federation_status`) Add a way to get a list of unreachable instances.
|
||||
- Mastodon API: User and conversation mutes can now auto-expire if `expires_in` parameter was given while adding the mute.
|
||||
- Admin API: An endpoint to manage frontends
|
||||
|
||||
- Admin API: An endpoint to manage frontends.
|
||||
- Streaming API: Add follow relationships updates.
|
||||
</details>
|
||||
|
||||
### Fixed
|
||||
|
||||
- Users with `is_discoverable` field set to false (default value) will appear in in-service search results but be hidden from external services (search bots etc.).
|
||||
- Streaming API: Posts and notifications are not dropped, when CLI task is executing.
|
||||
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
|
@ -58,6 +64,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Mix task pleroma.user delete_activities for source installations.
|
||||
- Fix ability to update Pleroma Chat push notifications with PUT /api/v1/push/subscription and alert type pleroma:chat_mention
|
||||
- Forwarded reports duplication from Pleroma instances.
|
||||
- Rich Media Previews sometimes showed the wrong preview due to a bug following redirects.
|
||||
|
||||
<details>
|
||||
<summary>API</summary>
|
||||
|
@ -101,7 +108,7 @@ switched to a new configuration mechanism, however it was not officially removed
|
|||
|
||||
- Media preview proxy (requires `ffmpeg` and `ImageMagick` to be installed and media proxy to be enabled; see `:media_preview_proxy` config for more details).
|
||||
- Mix tasks for controlling user account confirmation status in bulk (`mix pleroma.user confirm_all` and `mix pleroma.user unconfirm_all`)
|
||||
- Mix task for sending confirmation emails to all unconfirmed users (`mix pleroma.email send_confirmation_mails`)
|
||||
- Mix task for sending confirmation emails to all unconfirmed users (`mix pleroma.email resend_confirmation_emails`)
|
||||
- Mix task option for force-unfollowing relays
|
||||
- App metrics: ability to restrict access to specified IP whitelist.
|
||||
|
||||
|
|
|
@ -109,8 +109,8 @@ defmodule Pleroma.LoadTesting.Users do
|
|||
end
|
||||
|
||||
def make_friends(%User{} = main_user, %User{} = user) do
|
||||
{:ok, _} = User.follow(main_user, user)
|
||||
{:ok, _} = User.follow(user, main_user)
|
||||
{:ok, _, _} = User.follow(main_user, user)
|
||||
{:ok, _, _} = User.follow(user, main_user)
|
||||
end
|
||||
|
||||
@spec get_users(User.t(), keyword()) :: [User.t()]
|
||||
|
|
|
@ -50,7 +50,7 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Timelines do
|
|||
)
|
||||
|
||||
users
|
||||
|> Enum.each(fn {:ok, follower} -> Pleroma.User.follow(follower, user) end)
|
||||
|> Enum.each(fn {:ok, follower, user} -> Pleroma.User.follow(follower, user) end)
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
|
|
|
@ -306,7 +306,7 @@ config :pleroma, :frontend_configurations,
|
|||
hideSitename: false,
|
||||
hideUserStats: false,
|
||||
loginMethod: "password",
|
||||
logo: "/static/logo.png",
|
||||
logo: "/static/logo.svg",
|
||||
logoMargin: ".1em",
|
||||
logoMask: true,
|
||||
minimalScopesMode: false,
|
||||
|
@ -343,8 +343,8 @@ config :pleroma, :assets,
|
|||
config :pleroma, :manifest,
|
||||
icons: [
|
||||
%{
|
||||
src: "/static/logo.png",
|
||||
type: "image/png"
|
||||
src: "/static/logo.svg",
|
||||
type: "image/svg+xml"
|
||||
}
|
||||
],
|
||||
theme_color: "#282c37",
|
||||
|
@ -648,7 +648,7 @@ config :pleroma, :email_notifications,
|
|||
}
|
||||
|
||||
config :pleroma, :oauth2,
|
||||
token_expires_in: 600,
|
||||
token_expires_in: 3600 * 24 * 365 * 100,
|
||||
issue_new_refresh_token: true,
|
||||
clean_expired_tokens: false
|
||||
|
||||
|
|
|
@ -1254,7 +1254,7 @@ config :pleroma, :config_description, [
|
|||
hideSitename: false,
|
||||
hideUserStats: false,
|
||||
loginMethod: "password",
|
||||
logo: "/static/logo.png",
|
||||
logo: "/static/logo.svg",
|
||||
logoMargin: ".1em",
|
||||
logoMask: true,
|
||||
minimalScopesMode: false,
|
||||
|
@ -1340,7 +1340,7 @@ config :pleroma, :config_description, [
|
|||
key: :logo,
|
||||
type: {:string, :image},
|
||||
description: "URL of the logo, defaults to Pleroma's logo",
|
||||
suggestions: ["/static/logo.png"]
|
||||
suggestions: ["/static/logo.svg"]
|
||||
},
|
||||
%{
|
||||
key: :logoMargin,
|
||||
|
@ -2540,7 +2540,7 @@ config :pleroma, :config_description, [
|
|||
key: :token_expires_in,
|
||||
type: :integer,
|
||||
description: "The lifetime in seconds of the access token",
|
||||
suggestions: [600]
|
||||
suggestions: [2_592_000]
|
||||
},
|
||||
%{
|
||||
key: :issue_new_refresh_token,
|
||||
|
|
|
@ -4,7 +4,7 @@ A Pleroma instance can be identified by "<Mastodon version> (compatible; Pleroma
|
|||
|
||||
## Flake IDs
|
||||
|
||||
Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are lexically sortable strings
|
||||
Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However, just like Mastodon's ids, they are lexically sortable strings
|
||||
|
||||
## Timelines
|
||||
|
||||
|
@ -26,8 +26,8 @@ Has these additional fields under the `pleroma` object:
|
|||
- `conversation_id`: the ID of the AP context the status is associated with (if any)
|
||||
- `direct_conversation_id`: the ID of the Mastodon direct message conversation the status is associated with (if any)
|
||||
- `in_reply_to_account_acct`: the `acct` property of User entity for replied user (if any)
|
||||
- `content`: a map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
||||
- `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
||||
- `content`: a map consisting of alternate representations of the `content` property with the key being its mimetype. Currently, the only alternate representation supported is `text/plain`
|
||||
- `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being its mimetype. Currently, the only alternate representation supported is `text/plain`
|
||||
- `expires_at`: a datetime (iso8601) that states when the post will expire (be deleted automatically), or empty if the post won't expire
|
||||
- `thread_muted`: true if the thread the post belongs to is muted
|
||||
- `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint.
|
||||
|
@ -170,9 +170,9 @@ Returns on success: 200 OK `{}`
|
|||
|
||||
Additional parameters can be added to the JSON body/Form data:
|
||||
|
||||
- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
|
||||
- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entity would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
|
||||
- `content_type`: string, contain the MIME type of the status, it is transformed into HTML by the backend. You can get the list of the supported MIME types with the nodeinfo endpoint.
|
||||
- `to`: A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for for post visibility are not affected by this and will still apply.
|
||||
- `to`: A list of nicknames (like `lain@soykaf.club` or `lain` on the local server) that will be used to determine who is going to be addressed by this post. Using this will disable the implicit addressing by mentioned names in the `status` body, only the people in the `to` list will be addressed. The normal rules for post visibility are not affected by this and will still apply.
|
||||
- `visibility`: string, besides standard MastoAPI values (`direct`, `private`, `unlisted`, `local` or `public`) it can be used to address a List by setting it to `list:LIST_ID`.
|
||||
- `expires_in`: The number of seconds the posted activity should expire in. When a posted activity expires it will be deleted from the server, and a delete request for it will be federated. This needs to be longer than an hour.
|
||||
- `in_reply_to_conversation_id`: Will reply to a given conversation, addressing only the people who are part of the recipient set of that conversation. Sets the visibility to `direct`.
|
||||
|
@ -279,10 +279,27 @@ Has these additional fields under the `pleroma` object:
|
|||
|
||||
## Streaming
|
||||
|
||||
### Chats
|
||||
|
||||
There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
|
||||
|
||||
### Remote timelines
|
||||
|
||||
For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`.
|
||||
|
||||
### Follow relationships updates
|
||||
|
||||
Pleroma streams follow relationships updates as `pleroma:follow_relationships_update` events to the `user` stream.
|
||||
|
||||
The message payload consist of:
|
||||
|
||||
- `state`: a relationship state, one of `follow_pending`, `follow_accept` or `follow_reject`.
|
||||
|
||||
- `follower` and `following` maps with following fields:
|
||||
- `id`: user ID
|
||||
- `follower_count`: follower count
|
||||
- `following_count`: following count
|
||||
|
||||
## User muting and thread muting
|
||||
|
||||
Both user muting and thread muting can be done for only a certain time by adding an `expires_in` parameter to the API calls and giving the expiration time in seconds.
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
config :pleroma, configurable_from_database: false
|
||||
```
|
||||
|
||||
To delete transfered settings from database optional flag `-d` can be used. `<env>` is `prod` by default.
|
||||
To delete transferred settings from database optional flag `-d` can be used. `<env>` is `prod` by default.
|
||||
|
||||
=== "OTP"
|
||||
```sh
|
||||
|
@ -43,3 +43,111 @@ To delete transfered settings from database optional flag `-d` can be used. `<en
|
|||
```sh
|
||||
mix pleroma.config migrate_from_db [--env=<env>] [-d]
|
||||
```
|
||||
|
||||
## Dump all of the config settings defined in the database
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config dump
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config dump
|
||||
```
|
||||
|
||||
## List individual configuration groups in the database
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config groups
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config groups
|
||||
```
|
||||
|
||||
## Dump the saved configuration values for a specific group or key
|
||||
|
||||
e.g., this shows all the settings under `config :pleroma`
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config dump pleroma
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config dump pleroma
|
||||
```
|
||||
|
||||
To get values under a specific key:
|
||||
|
||||
e.g., this shows all the settings under `config :pleroma, :instance`
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config dump pleroma instance
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config dump pleroma instance
|
||||
```
|
||||
|
||||
## Delete the saved configuration values for a specific group or key
|
||||
|
||||
e.g., this deletes all the settings under `config :tesla`
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config delete [--force] tesla
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config delete [--force] tesla
|
||||
```
|
||||
|
||||
To delete values under a specific key:
|
||||
|
||||
e.g., this deletes all the settings under `config :phoenix, :stacktrace_depth`
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config delete [--force] phoenix stacktrace_depth
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config delete [--force] phoenix stacktrace_depth
|
||||
```
|
||||
|
||||
## Remove all settings from the database
|
||||
|
||||
This forcibly removes all saved values in the database.
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config [--force] reset
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config [--force] reset
|
||||
```
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
mix pleroma.email test [--to <destination email address>]
|
||||
```
|
||||
|
||||
|
||||
Example:
|
||||
Example:
|
||||
|
||||
=== "OTP"
|
||||
|
||||
|
@ -36,11 +35,11 @@ Example:
|
|||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl email send_confirmation_mails
|
||||
./bin/pleroma_ctl email resend_confirmation_emails
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.email send_confirmation_mails
|
||||
mix pleroma.email resend_confirmation_emails
|
||||
```
|
||||
|
|
|
@ -264,13 +264,13 @@
|
|||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user toggle_confirmed <nickname>
|
||||
./bin/pleroma_ctl user confirm <nickname>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user toggle_confirmed <nickname>
|
||||
mix pleroma.user confirm <nickname>
|
||||
```
|
||||
|
||||
## Set confirmation status for all regular active users
|
||||
|
|
|
@ -5,50 +5,37 @@ The configuration of Pleroma has traditionally been managed with a config file,
|
|||
|
||||
## Migration to database config
|
||||
|
||||
1. Run the mix task to migrate to the database. You'll receive some debugging output and a few messages informing you of what happened.
|
||||
1. Run the mix task to migrate to the database.
|
||||
|
||||
**Source:**
|
||||
|
||||
|
||||
```
|
||||
$ mix pleroma.config migrate_to_db
|
||||
```
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
**OTP:**
|
||||
|
||||
|
||||
*Note: OTP users need Pleroma to be running for `pleroma_ctl` commands to work*
|
||||
|
||||
|
||||
```
|
||||
$ ./bin/pleroma_ctl config migrate_to_db
|
||||
```
|
||||
|
||||
```
|
||||
10:04:34.155 [debug] QUERY OK source="config" db=1.6ms decode=2.0ms queue=33.5ms idle=0.0ms
|
||||
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
|
||||
Migrating settings from file: /home/pleroma/config/dev.secret.exs
|
||||
|
||||
10:04:34.240 [debug] QUERY OK db=4.5ms queue=0.3ms idle=92.2ms
|
||||
TRUNCATE config; []
|
||||
|
||||
10:04:34.244 [debug] QUERY OK db=2.8ms queue=0.3ms idle=97.2ms
|
||||
ALTER SEQUENCE config_id_seq RESTART; []
|
||||
|
||||
10:04:34.256 [debug] QUERY OK source="config" db=0.8ms queue=1.4ms idle=109.8ms
|
||||
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 WHERE ((c0."group" = $1) AND (c0."key" = $2)) [":pleroma", ":instance"]
|
||||
|
||||
10:04:34.292 [debug] QUERY OK db=2.6ms queue=1.7ms idle=137.7ms
|
||||
INSERT INTO "config" ("group","key","value","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5) RETURNING "id" [":pleroma", ":instance", <<131, 108, 0, 0, 0, 1, 104, 2, 100, 0, 4, 110, 97, 109, 101, 109, 0, 0, 0, 7, 66, 108, 101, 114, 111, 109, 97, 106>>, ~N[2020-07-12 15:04:34], ~N[2020-07-12 15:04:34]]
|
||||
|
||||
Settings for key instance migrated.
|
||||
Settings for group :pleroma migrated.
|
||||
```
|
||||
|
||||
|
||||
2. It is recommended to backup your config file now.
|
||||
|
||||
```
|
||||
cp config/dev.secret.exs config/dev.secret.exs.orig
|
||||
```
|
||||
|
||||
|
||||
3. Edit your Pleroma config to enable database configuration:
|
||||
|
||||
```
|
||||
|
@ -76,17 +63,17 @@ The configuration of Pleroma has traditionally been managed with a config file,
|
|||
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
url: [host: "cool.pleroma.site", scheme: "https", port: 443]
|
||||
|
||||
|
||||
config :pleroma, Pleroma.Repo,
|
||||
adapter: Ecto.Adapters.Postgres,
|
||||
username: "pleroma",
|
||||
password: "MySecretPassword",
|
||||
database: "pleroma_prod",
|
||||
hostname: "localhost"
|
||||
|
||||
|
||||
config :pleroma, configurable_from_database: true
|
||||
```
|
||||
|
||||
|
||||
5. Restart your instance and you can now access the Settings tab in AdminFE.
|
||||
|
||||
|
||||
|
@ -95,15 +82,15 @@ The configuration of Pleroma has traditionally been managed with a config file,
|
|||
1. Run the mix task to migrate back from the database. You'll receive some debugging output and a few messages informing you of what happened.
|
||||
|
||||
**Source:**
|
||||
|
||||
|
||||
```
|
||||
$ mix pleroma.config migrate_from_db
|
||||
```
|
||||
|
||||
|
||||
or
|
||||
|
||||
|
||||
**OTP:**
|
||||
|
||||
|
||||
```
|
||||
$ ./bin/pleroma_ctl config migrate_from_db
|
||||
```
|
||||
|
@ -111,7 +98,7 @@ The configuration of Pleroma has traditionally been managed with a config file,
|
|||
```
|
||||
10:26:30.593 [debug] QUERY OK source="config" db=9.8ms decode=1.2ms queue=26.0ms idle=0.0ms
|
||||
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
|
||||
|
||||
|
||||
10:26:30.659 [debug] QUERY OK source="config" db=1.1ms idle=80.7ms
|
||||
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
|
||||
Database configuration settings have been saved to config/dev.exported_from_db.secret.exs
|
||||
|
@ -124,30 +111,45 @@ The configuration of Pleroma has traditionally been managed with a config file,
|
|||
## Debugging
|
||||
|
||||
### Clearing database config
|
||||
You can clear the database config by truncating the `config` table in the database. e.g.,
|
||||
You can clear the database config with the following command:
|
||||
|
||||
```
|
||||
psql -d pleroma_dev
|
||||
pleroma_dev=# TRUNCATE config;
|
||||
TRUNCATE TABLE
|
||||
```
|
||||
**Source:**
|
||||
|
||||
```
|
||||
$ mix pleroma.config reset
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
**OTP:**
|
||||
|
||||
```
|
||||
$ ./bin/pleroma_ctl config reset
|
||||
```
|
||||
|
||||
Additionally, every time you migrate the configuration to the database the config table is automatically truncated to ensure a clean migration.
|
||||
|
||||
### Manually removing a setting
|
||||
If you encounter a situation where the server cannot run properly because of an invalid setting in the database and this is preventing you from accessing AdminFE, you can manually remove the offending setting if you know which one it is.
|
||||
|
||||
e.g., here is an example showing a minimal configuration in the database. Only the `config :pleroma, :instance` settings are in the table:
|
||||
e.g., here is an example showing a the removal of the `config :pleroma, :instance` settings:
|
||||
|
||||
```
|
||||
psql -d pleroma_dev
|
||||
pleroma_dev=# select * from config;
|
||||
id | key | value | inserted_at | updated_at | group
|
||||
----+-----------+------------------------------------------------------------+---------------------+---------------------+----------
|
||||
1 | :instance | \x836c0000000168026400046e616d656d00000007426c65726f6d616a | 2020-07-12 15:33:29 | 2020-07-12 15:33:29 | :pleroma
|
||||
(1 row)
|
||||
pleroma_dev=# delete from config where key = ':instance' and group = ':pleroma';
|
||||
DELETE 1
|
||||
```
|
||||
**Source:**
|
||||
|
||||
```
|
||||
$ mix pleroma.config delete pleroma instance
|
||||
Are you sure you want to continue? [n] y
|
||||
config :pleroma, :instance deleted from the ConfigDB.
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
**OTP:**
|
||||
|
||||
```
|
||||
$ ./bin/pleroma_ctl config delete pleroma instance
|
||||
Are you sure you want to continue? [n] y
|
||||
config :pleroma, :instance deleted from the ConfigDB.
|
||||
```
|
||||
|
||||
Now the `config :pleroma, :instance` settings have been removed from the database.
|
||||
|
|
|
@ -88,3 +88,8 @@ config :pleroma, :frontend_configurations,
|
|||
Note the extra `static` folder for the terms-of-service.html
|
||||
|
||||
Terms of Service will be shown to all users on the registration page. It's the best place where to write down the rules for your instance. You can modify the rules by adding and changing `$static_dir/static/terms-of-service.html`.
|
||||
|
||||
|
||||
## Styling rendered pages
|
||||
|
||||
To overwrite the CSS stylesheet of the OAuth form and other static pages, you can upload your own CSS file to `instance/static/static.css`. This will completely replace the CSS used by those pages, so it might be a good idea to copy the one from `priv/static/instance/static.css` and make your changes.
|
||||
|
|
|
@ -14,9 +14,9 @@ This document contains notes and guidelines for Pleroma developers.
|
|||
|
||||
For `:api` pipeline routes, it'll be verified whether `OAuthScopesPlug` was called or explicitly skipped, and if it was not then auth information will be dropped for request. Then `EnsurePublicOrAuthenticatedPlug` will be called to ensure that either the instance is not private or user is authenticated (unless explicitly skipped). Such automated checks help to prevent human errors and result in higher security / privacy for users.
|
||||
|
||||
## [HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization)
|
||||
## Non-OAuth authentication
|
||||
|
||||
* With HTTP Basic Auth, OAuth scopes check is _not_ performed for any action (since password is provided during the auth, requester is able to obtain a token with full permissions anyways). `Pleroma.Web.Plugs.AuthenticationPlug` and `Pleroma.Web.Plugs.LegacyAuthenticationPlug` both call `Pleroma.Web.Plugs.OAuthScopesPlug.skip_plug(conn)` when password is provided.
|
||||
* With non-OAuth authentication ([HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) or HTTP header- or params-provided auth), OAuth scopes check is _not_ performed for any action (since password is provided during the auth, requester is able to obtain a token with full permissions anyways); auth plugs invoke `Pleroma.Helpers.AuthHelper.skip_oauth(conn)` in this case.
|
||||
|
||||
## Auth-related configuration, OAuth consumer mode etc.
|
||||
|
||||
|
|
|
@ -12,7 +12,8 @@ defmodule Mix.Pleroma do
|
|||
:cachex,
|
||||
:flake_id,
|
||||
:swoosh,
|
||||
:timex
|
||||
:timex,
|
||||
:fast_html
|
||||
]
|
||||
@cachex_children ["object", "user", "scrubber", "web_resp"]
|
||||
@doc "Common functions to be reused in mix tasks"
|
||||
|
@ -37,12 +38,23 @@ defmodule Mix.Pleroma do
|
|||
|
||||
Enum.each(apps, &Application.ensure_all_started/1)
|
||||
|
||||
oban_config = [
|
||||
crontab: [],
|
||||
repo: Pleroma.Repo,
|
||||
log: false,
|
||||
queues: [],
|
||||
plugins: []
|
||||
]
|
||||
|
||||
children =
|
||||
[
|
||||
Pleroma.Repo,
|
||||
Pleroma.Emoji,
|
||||
{Pleroma.Config.TransferTask, false},
|
||||
Pleroma.Web.Endpoint,
|
||||
{Oban, Pleroma.Config.get(Oban)}
|
||||
{Oban, oban_config},
|
||||
{Majic.Pool,
|
||||
[name: Pleroma.MajicPool, pool_size: Pleroma.Config.get([:majic_pool, :size], 2)]}
|
||||
] ++
|
||||
http_children(adapter)
|
||||
|
||||
|
@ -98,12 +110,6 @@ defmodule Mix.Pleroma do
|
|||
end
|
||||
end
|
||||
|
||||
def shell_yes?(message) do
|
||||
if mix_shell?(),
|
||||
do: Mix.shell().yes?("Continue?"),
|
||||
else: shell_prompt(message, "Continue?") in ~w(Yn Y y)
|
||||
end
|
||||
|
||||
def shell_info(message) do
|
||||
if mix_shell?(),
|
||||
do: Mix.shell().info(message),
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Mix.Tasks.Pleroma.Config do
|
||||
use Mix.Task
|
||||
|
||||
import Ecto.Query
|
||||
import Mix.Pleroma
|
||||
|
||||
alias Pleroma.ConfigDB
|
||||
|
@ -14,26 +15,199 @@ defmodule Mix.Tasks.Pleroma.Config do
|
|||
@moduledoc File.read!("docs/administration/CLI_tasks/config.md")
|
||||
|
||||
def run(["migrate_to_db"]) do
|
||||
start_pleroma()
|
||||
migrate_to_db()
|
||||
check_configdb(fn ->
|
||||
start_pleroma()
|
||||
migrate_to_db()
|
||||
end)
|
||||
end
|
||||
|
||||
def run(["migrate_from_db" | options]) do
|
||||
check_configdb(fn ->
|
||||
start_pleroma()
|
||||
|
||||
{opts, _} =
|
||||
OptionParser.parse!(options,
|
||||
strict: [env: :string, delete: :boolean],
|
||||
aliases: [d: :delete]
|
||||
)
|
||||
|
||||
migrate_from_db(opts)
|
||||
end)
|
||||
end
|
||||
|
||||
def run(["dump"]) do
|
||||
check_configdb(fn ->
|
||||
start_pleroma()
|
||||
|
||||
header = config_header()
|
||||
|
||||
settings =
|
||||
ConfigDB
|
||||
|> Repo.all()
|
||||
|> Enum.sort()
|
||||
|
||||
unless settings == [] do
|
||||
shell_info("#{header}")
|
||||
|
||||
Enum.each(settings, &dump(&1))
|
||||
else
|
||||
shell_error("No settings in ConfigDB.")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def run(["dump", group, key]) do
|
||||
check_configdb(fn ->
|
||||
start_pleroma()
|
||||
|
||||
group = maybe_atomize(group)
|
||||
key = maybe_atomize(key)
|
||||
|
||||
group
|
||||
|> ConfigDB.get_by_group_and_key(key)
|
||||
|> dump()
|
||||
end)
|
||||
end
|
||||
|
||||
def run(["dump", group]) do
|
||||
check_configdb(fn ->
|
||||
start_pleroma()
|
||||
|
||||
group = maybe_atomize(group)
|
||||
|
||||
dump_group(group)
|
||||
end)
|
||||
end
|
||||
|
||||
def run(["groups"]) do
|
||||
check_configdb(fn ->
|
||||
start_pleroma()
|
||||
|
||||
groups =
|
||||
ConfigDB
|
||||
|> distinct([c], true)
|
||||
|> select([c], c.group)
|
||||
|> Repo.all()
|
||||
|
||||
if length(groups) > 0 do
|
||||
shell_info("The following configuration groups are set in ConfigDB:\r\n")
|
||||
groups |> Enum.each(fn x -> shell_info("- #{x}") end)
|
||||
shell_info("\r\n")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def run(["reset", "--force"]) do
|
||||
check_configdb(fn ->
|
||||
start_pleroma()
|
||||
truncatedb()
|
||||
shell_info("The ConfigDB settings have been removed from the database.")
|
||||
end)
|
||||
end
|
||||
|
||||
def run(["reset"]) do
|
||||
check_configdb(fn ->
|
||||
start_pleroma()
|
||||
|
||||
shell_info("The following settings will be permanently removed:")
|
||||
|
||||
ConfigDB
|
||||
|> Repo.all()
|
||||
|> Enum.sort()
|
||||
|> Enum.each(&dump(&1))
|
||||
|
||||
shell_error("\nTHIS CANNOT BE UNDONE!")
|
||||
|
||||
if shell_prompt("Are you sure you want to continue?", "n") in ~w(Yn Y y) do
|
||||
truncatedb()
|
||||
|
||||
shell_info("The ConfigDB settings have been removed from the database.")
|
||||
else
|
||||
shell_error("No changes made.")
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def run(["delete", "--force", group, key]) do
|
||||
start_pleroma()
|
||||
|
||||
{opts, _} =
|
||||
OptionParser.parse!(options,
|
||||
strict: [env: :string, delete: :boolean],
|
||||
aliases: [d: :delete]
|
||||
)
|
||||
group = maybe_atomize(group)
|
||||
key = maybe_atomize(key)
|
||||
|
||||
migrate_from_db(opts)
|
||||
with true <- key_exists?(group, key) do
|
||||
shell_info("The following settings will be removed from ConfigDB:\n")
|
||||
|
||||
group
|
||||
|> ConfigDB.get_by_group_and_key(key)
|
||||
|> dump()
|
||||
|
||||
delete_key(group, key)
|
||||
else
|
||||
_ ->
|
||||
shell_error("No settings in ConfigDB for #{inspect(group)}, #{inspect(key)}. Aborting.")
|
||||
end
|
||||
end
|
||||
|
||||
def run(["delete", "--force", group]) do
|
||||
start_pleroma()
|
||||
|
||||
group = maybe_atomize(group)
|
||||
|
||||
with true <- group_exists?(group) do
|
||||
shell_info("The following settings will be removed from ConfigDB:\n")
|
||||
dump_group(group)
|
||||
delete_group(group)
|
||||
else
|
||||
_ -> shell_error("No settings in ConfigDB for #{inspect(group)}. Aborting.")
|
||||
end
|
||||
end
|
||||
|
||||
def run(["delete", group, key]) do
|
||||
start_pleroma()
|
||||
|
||||
group = maybe_atomize(group)
|
||||
key = maybe_atomize(key)
|
||||
|
||||
with true <- key_exists?(group, key) do
|
||||
shell_info("The following settings will be removed from ConfigDB:\n")
|
||||
|
||||
group
|
||||
|> ConfigDB.get_by_group_and_key(key)
|
||||
|> dump()
|
||||
|
||||
if shell_prompt("Are you sure you want to continue?", "n") in ~w(Yn Y y) do
|
||||
delete_key(group, key)
|
||||
else
|
||||
shell_error("No changes made.")
|
||||
end
|
||||
else
|
||||
_ ->
|
||||
shell_error("No settings in ConfigDB for #{inspect(group)}, #{inspect(key)}. Aborting.")
|
||||
end
|
||||
end
|
||||
|
||||
def run(["delete", group]) do
|
||||
start_pleroma()
|
||||
|
||||
group = maybe_atomize(group)
|
||||
|
||||
with true <- group_exists?(group) do
|
||||
shell_info("The following settings will be removed from ConfigDB:\n")
|
||||
dump_group(group)
|
||||
|
||||
if shell_prompt("Are you sure you want to continue?", "n") in ~w(Yn Y y) do
|
||||
delete_group(group)
|
||||
else
|
||||
shell_error("No changes made.")
|
||||
end
|
||||
else
|
||||
_ -> shell_error("No settings in ConfigDB for #{inspect(group)}. Aborting.")
|
||||
end
|
||||
end
|
||||
|
||||
@spec migrate_to_db(Path.t() | nil) :: any()
|
||||
def migrate_to_db(file_path \\ nil) do
|
||||
with true <- Pleroma.Config.get([:configurable_from_database]),
|
||||
:ok <- Pleroma.Config.DeprecationWarnings.warn() do
|
||||
with :ok <- Pleroma.Config.DeprecationWarnings.warn() do
|
||||
config_file =
|
||||
if file_path do
|
||||
file_path
|
||||
|
@ -47,16 +221,15 @@ defmodule Mix.Tasks.Pleroma.Config do
|
|||
|
||||
do_migrate_to_db(config_file)
|
||||
else
|
||||
:error -> deprecation_error()
|
||||
_ -> migration_error()
|
||||
_ ->
|
||||
shell_error("Migration is not allowed until all deprecation warnings have been resolved.")
|
||||
end
|
||||
end
|
||||
|
||||
defp do_migrate_to_db(config_file) do
|
||||
if File.exists?(config_file) do
|
||||
shell_info("Migrating settings from file: #{Path.expand(config_file)}")
|
||||
Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
|
||||
Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
|
||||
truncatedb()
|
||||
|
||||
custom_config =
|
||||
config_file
|
||||
|
@ -80,52 +253,38 @@ defmodule Mix.Tasks.Pleroma.Config do
|
|||
shell_info("Settings for key #{key} migrated.")
|
||||
end)
|
||||
|
||||
shell_info("Settings for group :#{group} migrated.")
|
||||
shell_info("Settings for group #{inspect(group)} migrated.")
|
||||
end
|
||||
|
||||
defp migrate_from_db(opts) do
|
||||
if Pleroma.Config.get([:configurable_from_database]) do
|
||||
env = opts[:env] || Pleroma.Config.get(:env)
|
||||
env = opts[:env] || Pleroma.Config.get(:env)
|
||||
|
||||
config_path =
|
||||
if Pleroma.Config.get(:release) do
|
||||
:config_path
|
||||
|> Pleroma.Config.get()
|
||||
|> Path.dirname()
|
||||
else
|
||||
"config"
|
||||
end
|
||||
|> Path.join("#{env}.exported_from_db.secret.exs")
|
||||
config_path =
|
||||
if Pleroma.Config.get(:release) do
|
||||
:config_path
|
||||
|> Pleroma.Config.get()
|
||||
|> Path.dirname()
|
||||
else
|
||||
"config"
|
||||
end
|
||||
|> Path.join("#{env}.exported_from_db.secret.exs")
|
||||
|
||||
file = File.open!(config_path, [:write, :utf8])
|
||||
file = File.open!(config_path, [:write, :utf8])
|
||||
|
||||
IO.write(file, config_header())
|
||||
IO.write(file, config_header())
|
||||
|
||||
ConfigDB
|
||||
|> Repo.all()
|
||||
|> Enum.each(&write_and_delete(&1, file, opts[:delete]))
|
||||
ConfigDB
|
||||
|> Repo.all()
|
||||
|> Enum.each(&write_and_delete(&1, file, opts[:delete]))
|
||||
|
||||
:ok = File.close(file)
|
||||
System.cmd("mix", ["format", config_path])
|
||||
:ok = File.close(file)
|
||||
System.cmd("mix", ["format", config_path])
|
||||
|
||||
shell_info(
|
||||
"Database configuration settings have been exported to config/#{env}.exported_from_db.secret.exs"
|
||||
)
|
||||
else
|
||||
migration_error()
|
||||
end
|
||||
end
|
||||
|
||||
defp migration_error do
|
||||
shell_error(
|
||||
"Migration is not allowed in config. You can change this behavior by setting `config :pleroma, configurable_from_database: true`"
|
||||
shell_info(
|
||||
"Database configuration settings have been exported to config/#{env}.exported_from_db.secret.exs"
|
||||
)
|
||||
end
|
||||
|
||||
defp deprecation_error do
|
||||
shell_error("Migration is not allowed until all deprecation warnings have been resolved.")
|
||||
end
|
||||
|
||||
if Code.ensure_loaded?(Config.Reader) do
|
||||
defp config_header, do: "import Config\r\n\r\n"
|
||||
defp read_file(config_file), do: Config.Reader.read_imports!(config_file)
|
||||
|
@ -150,8 +309,80 @@ defmodule Mix.Tasks.Pleroma.Config do
|
|||
|
||||
defp delete(config, true) do
|
||||
{:ok, _} = Repo.delete(config)
|
||||
shell_info("#{config.key} deleted from DB.")
|
||||
|
||||
shell_info(
|
||||
"config #{inspect(config.group)}, #{inspect(config.key)} was deleted from the ConfigDB."
|
||||
)
|
||||
end
|
||||
|
||||
defp delete(_config, _), do: :ok
|
||||
|
||||
defp dump(%ConfigDB{} = config) do
|
||||
value = inspect(config.value, limit: :infinity)
|
||||
|
||||
shell_info("config #{inspect(config.group)}, #{inspect(config.key)}, #{value}\r\n\r\n")
|
||||
end
|
||||
|
||||
defp dump(_), do: :noop
|
||||
|
||||
defp dump_group(group) when is_atom(group) do
|
||||
group
|
||||
|> ConfigDB.get_all_by_group()
|
||||
|> Enum.each(&dump/1)
|
||||
end
|
||||
|
||||
defp group_exists?(group) do
|
||||
group
|
||||
|> ConfigDB.get_all_by_group()
|
||||
|> Enum.any?()
|
||||
end
|
||||
|
||||
defp key_exists?(group, key) do
|
||||
group
|
||||
|> ConfigDB.get_by_group_and_key(key)
|
||||
|> is_nil
|
||||
|> Kernel.!()
|
||||
end
|
||||
|
||||
defp maybe_atomize(arg) when is_atom(arg), do: arg
|
||||
|
||||
defp maybe_atomize(":" <> arg), do: maybe_atomize(arg)
|
||||
|
||||
defp maybe_atomize(arg) when is_binary(arg) do
|
||||
if ConfigDB.module_name?(arg) do
|
||||
String.to_existing_atom("Elixir." <> arg)
|
||||
else
|
||||
String.to_atom(arg)
|
||||
end
|
||||
end
|
||||
|
||||
defp check_configdb(callback) do
|
||||
with true <- Pleroma.Config.get([:configurable_from_database]) do
|
||||
callback.()
|
||||
else
|
||||
_ ->
|
||||
shell_error(
|
||||
"ConfigDB not enabled. Please check the value of :configurable_from_database in your configuration."
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp delete_key(group, key) do
|
||||
check_configdb(fn ->
|
||||
ConfigDB.delete(%{group: group, key: key})
|
||||
end)
|
||||
end
|
||||
|
||||
defp delete_group(group) do
|
||||
check_configdb(fn ->
|
||||
group
|
||||
|> ConfigDB.get_all_by_group()
|
||||
|> Enum.each(&ConfigDB.delete/1)
|
||||
end)
|
||||
end
|
||||
|
||||
defp truncatedb do
|
||||
Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
|
||||
Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,9 +48,15 @@ defmodule Mix.Tasks.Pleroma.Database do
|
|||
def run(["update_users_following_followers_counts"]) do
|
||||
start_pleroma()
|
||||
|
||||
User
|
||||
|> Repo.all()
|
||||
|> Enum.each(&User.update_follower_count/1)
|
||||
Repo.transaction(
|
||||
fn ->
|
||||
from(u in User, select: u)
|
||||
|> Repo.stream()
|
||||
|> Stream.each(&User.update_follower_count/1)
|
||||
|> Stream.run()
|
||||
end,
|
||||
timeout: :infinity
|
||||
)
|
||||
end
|
||||
|
||||
def run(["prune_objects" | args]) do
|
||||
|
|
|
@ -161,12 +161,21 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
)
|
||||
|> Path.expand()
|
||||
|
||||
{strip_uploads_message, strip_uploads_default} =
|
||||
if Pleroma.Utils.command_available?("exiftool") do
|
||||
{"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as installed. (y/n)",
|
||||
"y"}
|
||||
else
|
||||
{"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as not installed, please install it if you answer yes. (y/n)",
|
||||
"n"}
|
||||
end
|
||||
|
||||
strip_uploads =
|
||||
get_option(
|
||||
options,
|
||||
:strip_uploads,
|
||||
"Do you want to strip location (GPS) data from uploaded images? (y/n)",
|
||||
"y"
|
||||
strip_uploads_message,
|
||||
strip_uploads_default
|
||||
) === "y"
|
||||
|
||||
anonymize_uploads =
|
||||
|
@ -253,7 +262,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
else
|
||||
shell_error(
|
||||
"The task would have overwritten the following files:\n" <>
|
||||
(Enum.map(paths, &"- #{&1}\n") |> Enum.join("")) <>
|
||||
(Enum.map(will_overwrite, &"- #{&1}\n") |> Enum.join("")) <>
|
||||
"Rerun with `--force` to overwrite them."
|
||||
)
|
||||
end
|
||||
|
|
|
@ -60,7 +60,7 @@ defmodule Mix.Tasks.Pleroma.User do
|
|||
- admin: #{if(admin?, do: "true", else: "false")}
|
||||
""")
|
||||
|
||||
proceed? = assume_yes? or shell_yes?("Continue?")
|
||||
proceed? = assume_yes? or shell_prompt("Continue?", "n") in ~w(Yn Y y)
|
||||
|
||||
if proceed? do
|
||||
start_pleroma()
|
||||
|
@ -345,11 +345,11 @@ defmodule Mix.Tasks.Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
def run(["toggle_confirmed", nickname]) do
|
||||
def run(["confirm", nickname]) do
|
||||
start_pleroma()
|
||||
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname) do
|
||||
{:ok, user} = User.toggle_confirmation(user)
|
||||
{:ok, user} = User.confirm(user)
|
||||
|
||||
message = if user.confirmation_pending, do: "needs", else: "doesn't need"
|
||||
|
||||
|
|
|
@ -194,6 +194,19 @@ defmodule Pleroma.Activity do
|
|||
end
|
||||
end
|
||||
|
||||
def get_by_id_with_user_actor(id) do
|
||||
case FlakeId.flake_id?(id) do
|
||||
true ->
|
||||
Activity
|
||||
|> where([a], a.id == ^id)
|
||||
|> with_preloaded_user_actor()
|
||||
|> Repo.one()
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def get_by_id_with_object(id) do
|
||||
Activity
|
||||
|> where(id: ^id)
|
||||
|
|
|
@ -24,6 +24,7 @@ defmodule Pleroma.ApplicationRequirements do
|
|||
|> check_migrations_applied!()
|
||||
|> check_welcome_message_config!()
|
||||
|> check_rum!()
|
||||
|> check_repo_pool_size!()
|
||||
|> handle_result()
|
||||
end
|
||||
|
||||
|
@ -188,6 +189,30 @@ defmodule Pleroma.ApplicationRequirements do
|
|||
|
||||
defp check_system_commands!(result), do: result
|
||||
|
||||
defp check_repo_pool_size!(:ok) do
|
||||
if Pleroma.Config.get([Pleroma.Repo, :pool_size], 10) != 10 and
|
||||
not Pleroma.Config.get([:dangerzone, :override_repo_pool_size], false) do
|
||||
Logger.error("""
|
||||
!!!CONFIG WARNING!!!
|
||||
|
||||
The database pool size has been altered from the recommended value of 10.
|
||||
|
||||
Please revert or ensure your database is tuned appropriately and then set
|
||||
`config :pleroma, :dangerzone, override_repo_pool_size: true`.
|
||||
|
||||
If you are experiencing database timeouts, please check the "Optimizing
|
||||
your PostgreSQL performance" section in the documentation. If you still
|
||||
encounter issues after that, please open an issue on the tracker.
|
||||
""")
|
||||
|
||||
{:error, "Repo.pool_size different than recommended value."}
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defp check_repo_pool_size!(result), do: result
|
||||
|
||||
defp check_filter(filter, command_required) do
|
||||
filters = Config.get([Pleroma.Upload, :filters])
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.ConfigDB do
|
|||
use Ecto.Schema
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query, only: [select: 3]
|
||||
import Ecto.Query, only: [select: 3, from: 2]
|
||||
import Pleroma.Web.Gettext
|
||||
|
||||
alias __MODULE__
|
||||
|
@ -41,8 +41,18 @@ defmodule Pleroma.ConfigDB do
|
|||
end)
|
||||
end
|
||||
|
||||
@spec get_all_by_group(atom() | String.t()) :: [t()]
|
||||
def get_all_by_group(group) do
|
||||
from(c in ConfigDB, where: c.group == ^group) |> Repo.all()
|
||||
end
|
||||
|
||||
@spec get_by_group_and_key(atom() | String.t(), atom() | String.t()) :: t() | nil
|
||||
def get_by_group_and_key(group, key) do
|
||||
get_by_params(%{group: group, key: key})
|
||||
end
|
||||
|
||||
@spec get_by_params(map()) :: ConfigDB.t() | nil
|
||||
def get_by_params(params), do: Repo.get_by(ConfigDB, params)
|
||||
def get_by_params(%{group: _, key: _} = params), do: Repo.get_by(ConfigDB, params)
|
||||
|
||||
@spec changeset(ConfigDB.t(), map()) :: Changeset.t()
|
||||
def changeset(config, params \\ %{}) do
|
||||
|
|
|
@ -93,6 +93,19 @@ defmodule Pleroma.Emails.UserEmail do
|
|||
|> html_body(html_body)
|
||||
end
|
||||
|
||||
def approval_pending_email(user) do
|
||||
html_body = """
|
||||
<h3>Awaiting Approval</h3>
|
||||
<p>Your account at #{instance_name()} is being reviewed by staff. You will receive another email once your account is approved.</p>
|
||||
"""
|
||||
|
||||
new()
|
||||
|> to(recipient(user))
|
||||
|> from(sender())
|
||||
|> subject("Your account is awaiting approval")
|
||||
|> html_body(html_body)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Email used in digest email notifications
|
||||
Includes Mentions and New Followers data
|
||||
|
@ -151,7 +164,7 @@ defmodule Pleroma.Emails.UserEmail do
|
|||
|
||||
logo_path =
|
||||
if is_nil(logo) do
|
||||
Path.join(:code.priv_dir(:pleroma), "static/static/logo.png")
|
||||
Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg")
|
||||
else
|
||||
Path.join(Config.get([:instance, :static_dir]), logo)
|
||||
end
|
||||
|
@ -162,7 +175,7 @@ defmodule Pleroma.Emails.UserEmail do
|
|||
|> subject("Your digest from #{instance_name()}")
|
||||
|> put_layout(false)
|
||||
|> render_body("digest.html", html_data)
|
||||
|> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.png", type: :inline))
|
||||
|> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -22,14 +22,14 @@ defmodule Pleroma.Emoji.Pack do
|
|||
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.Emoji.Pack
|
||||
alias Pleroma.Utils
|
||||
|
||||
@spec create(String.t()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values}
|
||||
def create(name) do
|
||||
with :ok <- validate_not_empty([name]),
|
||||
dir <- Path.join(emoji_path(), name),
|
||||
:ok <- File.mkdir(dir) do
|
||||
%__MODULE__{pack_file: Path.join(dir, "pack.json")}
|
||||
|> save_pack()
|
||||
save_pack(%__MODULE__{pack_file: Path.join(dir, "pack.json")})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -62,10 +62,9 @@ defmodule Pleroma.Emoji.Pack do
|
|||
@spec delete(String.t()) ::
|
||||
{:ok, [binary()]} | {:error, File.posix(), binary()} | {:error, :empty_values}
|
||||
def delete(name) do
|
||||
with :ok <- validate_not_empty([name]) do
|
||||
emoji_path()
|
||||
|> Path.join(name)
|
||||
|> File.rm_rf()
|
||||
with :ok <- validate_not_empty([name]),
|
||||
pack_path <- Path.join(emoji_path(), name) do
|
||||
File.rm_rf(pack_path)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -94,7 +93,7 @@ defmodule Pleroma.Emoji.Pack do
|
|||
def add_file(%Pack{} = pack, _, _, %Plug.Upload{content_type: "application/zip"} = file) do
|
||||
with {:ok, zip_files} <- :zip.table(to_charlist(file.path)),
|
||||
[_ | _] = emojies <- unpack_zip_emojies(zip_files),
|
||||
{:ok, tmp_dir} <- Pleroma.Utils.tmp_dir("emoji") do
|
||||
{:ok, tmp_dir} <- Utils.tmp_dir("emoji") do
|
||||
try do
|
||||
{:ok, _emoji_files} =
|
||||
:zip.unzip(
|
||||
|
@ -282,18 +281,21 @@ defmodule Pleroma.Emoji.Pack do
|
|||
end
|
||||
end
|
||||
|
||||
@spec load_pack(String.t()) :: {:ok, t()} | {:error, :not_found}
|
||||
@spec load_pack(String.t()) :: {:ok, t()} | {:error, :file.posix()}
|
||||
def load_pack(name) do
|
||||
pack_file = Path.join([emoji_path(), name, "pack.json"])
|
||||
|
||||
if File.exists?(pack_file) do
|
||||
with {:ok, _} <- File.stat(pack_file),
|
||||
{:ok, pack_data} <- File.read(pack_file) do
|
||||
pack =
|
||||
pack_file
|
||||
|> File.read!()
|
||||
|> from_json()
|
||||
|> Map.put(:pack_file, pack_file)
|
||||
|> Map.put(:path, Path.dirname(pack_file))
|
||||
|> Map.put(:name, name)
|
||||
from_json(
|
||||
pack_data,
|
||||
%{
|
||||
pack_file: pack_file,
|
||||
path: Path.dirname(pack_file),
|
||||
name: name
|
||||
}
|
||||
)
|
||||
|
||||
files_count =
|
||||
pack.files
|
||||
|
@ -301,8 +303,6 @@ defmodule Pleroma.Emoji.Pack do
|
|||
|> length()
|
||||
|
||||
{:ok, Map.put(pack, :files_count, files_count)}
|
||||
else
|
||||
{:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -434,10 +434,17 @@ defmodule Pleroma.Emoji.Pack do
|
|||
end
|
||||
end
|
||||
|
||||
defp from_json(json) do
|
||||
defp from_json(json, attrs) do
|
||||
map = Jason.decode!(json)
|
||||
|
||||
struct(__MODULE__, %{files: map["files"], pack: map["pack"]})
|
||||
pack_attrs =
|
||||
attrs
|
||||
|> Map.merge(%{
|
||||
files: map["files"],
|
||||
pack: map["pack"]
|
||||
})
|
||||
|
||||
struct(__MODULE__, pack_attrs)
|
||||
end
|
||||
|
||||
defp validate_shareable_packs_available(uri) do
|
||||
|
@ -491,10 +498,10 @@ defmodule Pleroma.Emoji.Pack do
|
|||
end
|
||||
|
||||
defp create_subdirs(file_path) do
|
||||
if String.contains?(file_path, "/") do
|
||||
file_path
|
||||
|> Path.dirname()
|
||||
|> File.mkdir_p!()
|
||||
with true <- String.contains?(file_path, "/"),
|
||||
path <- Path.dirname(file_path),
|
||||
false <- File.exists?(path) do
|
||||
File.mkdir_p!(path)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -518,10 +525,15 @@ defmodule Pleroma.Emoji.Pack do
|
|||
|
||||
defp get_filename(pack, shortcode) do
|
||||
with %{^shortcode => filename} when is_binary(filename) <- pack.files,
|
||||
true <- pack.path |> Path.join(filename) |> File.exists?() do
|
||||
file_path <- Path.join(pack.path, filename),
|
||||
{:ok, _} <- File.stat(file_path) do
|
||||
{:ok, filename}
|
||||
else
|
||||
_ -> {:error, :doesnt_exist}
|
||||
{:error, _} = error ->
|
||||
error
|
||||
|
||||
_ ->
|
||||
{:error, :doesnt_exist}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -62,23 +62,47 @@ defmodule Pleroma.FollowingRelationship do
|
|||
follow(follower, following, state)
|
||||
|
||||
following_relationship ->
|
||||
following_relationship
|
||||
|> cast(%{state: state}, [:state])
|
||||
|> validate_required([:state])
|
||||
|> Repo.update()
|
||||
with {:ok, _following_relationship} <-
|
||||
following_relationship
|
||||
|> cast(%{state: state}, [:state])
|
||||
|> validate_required([:state])
|
||||
|> Repo.update() do
|
||||
after_update(state, follower, following)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def follow(%User{} = follower, %User{} = following, state \\ :follow_accept) do
|
||||
%__MODULE__{}
|
||||
|> changeset(%{follower: follower, following: following, state: state})
|
||||
|> Repo.insert(on_conflict: :nothing)
|
||||
with {:ok, _following_relationship} <-
|
||||
%__MODULE__{}
|
||||
|> changeset(%{follower: follower, following: following, state: state})
|
||||
|> Repo.insert(on_conflict: :nothing) do
|
||||
after_update(state, follower, following)
|
||||
end
|
||||
end
|
||||
|
||||
def unfollow(%User{} = follower, %User{} = following) do
|
||||
case get(follower, following) do
|
||||
%__MODULE__{} = following_relationship -> Repo.delete(following_relationship)
|
||||
_ -> {:ok, nil}
|
||||
%__MODULE__{} = following_relationship ->
|
||||
with {:ok, _following_relationship} <- Repo.delete(following_relationship) do
|
||||
after_update(:unfollow, follower, following)
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:ok, nil}
|
||||
end
|
||||
end
|
||||
|
||||
defp after_update(state, %User{} = follower, %User{} = following) do
|
||||
with {:ok, following} <- User.update_follower_count(following),
|
||||
{:ok, follower} <- User.update_following_count(follower) do
|
||||
Pleroma.Web.Streamer.stream("follow_relationship", %{
|
||||
state: state,
|
||||
following: following,
|
||||
follower: follower
|
||||
})
|
||||
|
||||
{:ok, follower, following}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Helpers.AuthHelper do
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Plug.Conn
|
||||
|
||||
import Plug.Conn
|
||||
|
||||
@oauth_token_session_key :oauth_token
|
||||
|
||||
@doc """
|
||||
Skips OAuth permissions (scopes) checks, assigns nil `:token`.
|
||||
Intended to be used with explicit authentication and only when OAuth token cannot be determined.
|
||||
"""
|
||||
def skip_oauth(conn) do
|
||||
conn
|
||||
|> assign(:token, nil)
|
||||
|> OAuthScopesPlug.skip_plug()
|
||||
end
|
||||
|
||||
@doc "Drops authentication info from connection"
|
||||
def drop_auth_info(conn) do
|
||||
# To simplify debugging, setting a private variable on `conn` if auth info is dropped
|
||||
conn
|
||||
|> assign(:user, nil)
|
||||
|> assign(:token, nil)
|
||||
|> put_private(:authentication_ignored, true)
|
||||
end
|
||||
|
||||
@doc "Gets OAuth token string from session"
|
||||
def get_session_token(%Conn{} = conn) do
|
||||
get_session(conn, @oauth_token_session_key)
|
||||
end
|
||||
|
||||
@doc "Updates OAuth token string in session"
|
||||
def put_session_token(%Conn{} = conn, token) when is_binary(token) do
|
||||
put_session(conn, @oauth_token_session_key, token)
|
||||
end
|
||||
|
||||
@doc "Deletes OAuth token string from session"
|
||||
def delete_session_token(%Conn{} = conn) do
|
||||
delete_session(conn, @oauth_token_session_key)
|
||||
end
|
||||
end
|
|
@ -77,7 +77,7 @@ defmodule Pleroma.Instances.Instance do
|
|||
)
|
||||
end
|
||||
|
||||
def reachable?(_), do: true
|
||||
def reachable?(url_or_host) when is_binary(url_or_host), do: true
|
||||
|
||||
def set_reachable(url_or_host) when is_binary(url_or_host) do
|
||||
with host <- host(url_or_host),
|
||||
|
@ -166,7 +166,8 @@ defmodule Pleroma.Instances.Instance do
|
|||
|
||||
defp scrape_favicon(%URI{} = instance_uri) do
|
||||
try do
|
||||
with {:ok, %Tesla.Env{body: html}} <-
|
||||
with {_, true} <- {:reachable, reachable?(instance_uri.host)},
|
||||
{:ok, %Tesla.Env{body: html}} <-
|
||||
Pleroma.HTTP.get(to_string(instance_uri), [{"accept", "text/html"}], pool: :media),
|
||||
{_, [favicon_rel | _]} when is_binary(favicon_rel) <-
|
||||
{:parse,
|
||||
|
@ -175,7 +176,15 @@ defmodule Pleroma.Instances.Instance do
|
|||
{:merge, URI.merge(instance_uri, favicon_rel) |> to_string()} do
|
||||
favicon
|
||||
else
|
||||
_ -> nil
|
||||
{:reachable, false} ->
|
||||
Logger.debug(
|
||||
"Instance.scrape_favicon(\"#{to_string(instance_uri)}\") ignored unreachable host"
|
||||
)
|
||||
|
||||
nil
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
rescue
|
||||
e ->
|
||||
|
|
|
@ -12,6 +12,26 @@ defmodule Pleroma.ModerationLog do
|
|||
|
||||
import Ecto.Query
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
@type log_subject :: Activity.t() | User.t() | list(User.t())
|
||||
@type log_params :: %{
|
||||
required(:actor) => User.t(),
|
||||
required(:action) => String.t(),
|
||||
optional(:subject) => log_subject(),
|
||||
optional(:subject_actor) => User.t(),
|
||||
optional(:subject_id) => String.t(),
|
||||
optional(:subjects) => list(User.t()),
|
||||
optional(:permission) => String.t(),
|
||||
optional(:text) => String.t(),
|
||||
optional(:sensitive) => String.t(),
|
||||
optional(:visibility) => String.t(),
|
||||
optional(:followed) => User.t(),
|
||||
optional(:follower) => User.t(),
|
||||
optional(:nicknames) => list(String.t()),
|
||||
optional(:tags) => list(String.t()),
|
||||
optional(:target) => String.t()
|
||||
}
|
||||
|
||||
schema "moderation_log" do
|
||||
field(:data, :map)
|
||||
|
||||
|
@ -90,203 +110,105 @@ defmodule Pleroma.ModerationLog do
|
|||
parsed_datetime
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, subject: [User], action: String.t(), permission: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
subject: subjects,
|
||||
action: action,
|
||||
permission: permission
|
||||
}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"subject" => user_to_map(subjects),
|
||||
"action" => action,
|
||||
"permission" => permission,
|
||||
"message" => ""
|
||||
}
|
||||
defp prepare_log_data(%{actor: actor, action: action} = attrs) do
|
||||
%{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => action,
|
||||
"message" => ""
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
|> Pleroma.Maps.put_if_present("subject_actor", user_to_map(attrs[:subject_actor]))
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, subject: User, action: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "report_update",
|
||||
subject: %Activity{data: %{"type" => "Flag"}} = subject
|
||||
}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => "report_update",
|
||||
"subject" => report_to_map(subject),
|
||||
"message" => ""
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
defp prepare_log_data(attrs), do: attrs
|
||||
|
||||
@spec insert_log(log_params()) :: {:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{actor: %User{}, subject: subjects, permission: permission} = attrs) do
|
||||
data =
|
||||
attrs
|
||||
|> prepare_log_data
|
||||
|> Map.merge(%{"subject" => user_to_map(subjects), "permission" => permission})
|
||||
|
||||
insert_log_entry_with_message(%ModerationLog{data: data})
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, subject: Activity, action: String.t(), text: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "report_note",
|
||||
subject: %Activity{} = subject,
|
||||
text: text
|
||||
}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => "report_note",
|
||||
"subject" => report_to_map(subject),
|
||||
"text" => text
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
def insert_log(%{actor: %User{}, action: action, subject: %Activity{} = subject} = attrs)
|
||||
when action in ["report_note_delete", "report_update", "report_note"] do
|
||||
data =
|
||||
attrs
|
||||
|> prepare_log_data
|
||||
|> Pleroma.Maps.put_if_present("text", attrs[:text])
|
||||
|> Map.merge(%{"subject" => report_to_map(subject)})
|
||||
|
||||
insert_log_entry_with_message(%ModerationLog{data: data})
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, subject: Activity, action: String.t(), text: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "report_note_delete",
|
||||
subject: %Activity{} = subject,
|
||||
text: text
|
||||
}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => "report_note_delete",
|
||||
"subject" => report_to_map(subject),
|
||||
"text" => text
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
end
|
||||
|
||||
@spec insert_log(%{
|
||||
actor: User,
|
||||
subject: Activity,
|
||||
action: String.t(),
|
||||
sensitive: String.t(),
|
||||
visibility: String.t()
|
||||
}) :: {:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "status_update",
|
||||
subject: %Activity{} = subject,
|
||||
sensitive: sensitive,
|
||||
visibility: visibility
|
||||
}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => "status_update",
|
||||
def insert_log(
|
||||
%{
|
||||
actor: %User{},
|
||||
action: action,
|
||||
subject: %Activity{} = subject,
|
||||
sensitive: sensitive,
|
||||
visibility: visibility
|
||||
} = attrs
|
||||
)
|
||||
when action == "status_update" do
|
||||
data =
|
||||
attrs
|
||||
|> prepare_log_data
|
||||
|> Map.merge(%{
|
||||
"subject" => status_to_map(subject),
|
||||
"sensitive" => sensitive,
|
||||
"visibility" => visibility,
|
||||
"message" => ""
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
"visibility" => visibility
|
||||
})
|
||||
|
||||
insert_log_entry_with_message(%ModerationLog{data: data})
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, action: String.t(), subject_id: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: "status_delete",
|
||||
subject_id: subject_id
|
||||
}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => "status_delete",
|
||||
"subject_id" => subject_id,
|
||||
"message" => ""
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
def insert_log(%{actor: %User{}, action: action, subject_id: subject_id} = attrs)
|
||||
when action == "status_delete" do
|
||||
data =
|
||||
attrs
|
||||
|> prepare_log_data
|
||||
|> Map.merge(%{"subject_id" => subject_id})
|
||||
|
||||
insert_log_entry_with_message(%ModerationLog{data: data})
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, subject: User, action: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{actor: %User{} = actor, subject: subject, action: action}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => action,
|
||||
"subject" => user_to_map(subject),
|
||||
"message" => ""
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
def insert_log(%{actor: %User{}, subject: subject, action: _action} = attrs) do
|
||||
data =
|
||||
attrs
|
||||
|> prepare_log_data
|
||||
|> Map.merge(%{"subject" => user_to_map(subject)})
|
||||
|
||||
insert_log_entry_with_message(%ModerationLog{data: data})
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, subjects: [User], action: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{actor: %User{} = actor, subjects: subjects, action: action}) do
|
||||
subjects = Enum.map(subjects, &user_to_map/1)
|
||||
def insert_log(%{actor: %User{}, subjects: subjects, action: _action} = attrs) do
|
||||
data =
|
||||
attrs
|
||||
|> prepare_log_data
|
||||
|> Map.merge(%{"subjects" => user_to_map(subjects)})
|
||||
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => action,
|
||||
"subjects" => subjects,
|
||||
"message" => ""
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
insert_log_entry_with_message(%ModerationLog{data: data})
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, action: String.t(), followed: User, follower: User}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
followed: %User{} = followed,
|
||||
follower: %User{} = follower,
|
||||
action: "follow"
|
||||
}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => "follow",
|
||||
"followed" => user_to_map(followed),
|
||||
"follower" => user_to_map(follower),
|
||||
"message" => ""
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
def insert_log(
|
||||
%{
|
||||
actor: %User{},
|
||||
followed: %User{} = followed,
|
||||
follower: %User{} = follower,
|
||||
action: action
|
||||
} = attrs
|
||||
)
|
||||
when action in ["unfollow", "follow"] do
|
||||
data =
|
||||
attrs
|
||||
|> prepare_log_data
|
||||
|> Map.merge(%{"followed" => user_to_map(followed), "follower" => user_to_map(follower)})
|
||||
|
||||
insert_log_entry_with_message(%ModerationLog{data: data})
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, action: String.t(), followed: User, follower: User}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
followed: %User{} = followed,
|
||||
follower: %User{} = follower,
|
||||
action: "unfollow"
|
||||
}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => "unfollow",
|
||||
"followed" => user_to_map(followed),
|
||||
"follower" => user_to_map(follower),
|
||||
"message" => ""
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
end
|
||||
|
||||
@spec insert_log(%{
|
||||
actor: User,
|
||||
action: String.t(),
|
||||
nicknames: [String.t()],
|
||||
tags: [String.t()]
|
||||
}) :: {:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
nicknames: nicknames,
|
||||
|
@ -305,27 +227,16 @@ defmodule Pleroma.ModerationLog do
|
|||
|> insert_log_entry_with_message()
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, action: String.t(), target: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{
|
||||
actor: %User{} = actor,
|
||||
action: action,
|
||||
target: target
|
||||
})
|
||||
def insert_log(%{actor: %User{}, action: action, target: target} = attrs)
|
||||
when action in ["relay_follow", "relay_unfollow"] do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => user_to_map(actor),
|
||||
"action" => action,
|
||||
"target" => target,
|
||||
"message" => ""
|
||||
}
|
||||
}
|
||||
|> insert_log_entry_with_message()
|
||||
data =
|
||||
attrs
|
||||
|> prepare_log_data
|
||||
|> Map.merge(%{"target" => target})
|
||||
|
||||
insert_log_entry_with_message(%ModerationLog{data: data})
|
||||
end
|
||||
|
||||
@spec insert_log(%{actor: User, action: String.t(), subject_id: String.t()}) ::
|
||||
{:ok, ModerationLog} | {:error, any}
|
||||
def insert_log(%{actor: %User{} = actor, action: "chat_message_delete", subject_id: subject_id}) do
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
|
@ -345,32 +256,27 @@ defmodule Pleroma.ModerationLog do
|
|||
end
|
||||
|
||||
defp user_to_map(users) when is_list(users) do
|
||||
users |> Enum.map(&user_to_map/1)
|
||||
Enum.map(users, &user_to_map/1)
|
||||
end
|
||||
|
||||
defp user_to_map(%User{} = user) do
|
||||
user
|
||||
|> Map.from_struct()
|
||||
|> Map.take([:id, :nickname])
|
||||
|> Map.new(fn {k, v} -> {Atom.to_string(k), v} end)
|
||||
|> Map.put("type", "user")
|
||||
end
|
||||
|
||||
defp user_to_map(_), do: nil
|
||||
|
||||
defp report_to_map(%Activity{} = report) do
|
||||
%{
|
||||
"type" => "report",
|
||||
"id" => report.id,
|
||||
"state" => report.data["state"]
|
||||
}
|
||||
%{"type" => "report", "id" => report.id, "state" => report.data["state"]}
|
||||
end
|
||||
|
||||
defp status_to_map(%Activity{} = status) do
|
||||
%{
|
||||
"type" => "status",
|
||||
"id" => status.id
|
||||
}
|
||||
%{"type" => "status", "id" => status.id}
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog.t()) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -382,7 +288,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} made @#{follower_nickname} #{action} @#{followed_nickname}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -393,7 +298,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} deleted users: #{users_to_nicknames_string(subjects)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -404,7 +308,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} created users: #{users_to_nicknames_string(subjects)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -415,7 +318,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} activated users: #{users_to_nicknames_string(users)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -426,7 +328,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} deactivated users: #{users_to_nicknames_string(users)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -437,7 +338,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} approved users: #{users_to_nicknames_string(users)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -451,7 +351,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} added tags: #{tags_string} to users: #{nicknames_to_string(nicknames)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -465,7 +364,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} removed tags: #{tags_string} from users: #{nicknames_to_string(nicknames)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -477,7 +375,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} made #{users_to_nicknames_string(users)} #{permission}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -489,7 +386,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} revoked #{permission} role from #{users_to_nicknames_string(users)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -500,7 +396,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} followed relay: #{target}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -511,42 +406,48 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} unfollowed relay: #{target}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "report_update",
|
||||
"subject" => %{"id" => subject_id, "state" => state, "type" => "report"}
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} updated report ##{subject_id} with '#{state}' state"
|
||||
def get_log_entry_message(
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "report_update",
|
||||
"subject" => %{"id" => subject_id, "state" => state, "type" => "report"}
|
||||
}
|
||||
} = log
|
||||
) do
|
||||
"@#{actor_nickname} updated report ##{subject_id}" <>
|
||||
subject_actor_nickname(log, " (on user ", ")") <>
|
||||
" with '#{state}' state"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "report_note",
|
||||
"subject" => %{"id" => subject_id, "type" => "report"},
|
||||
"text" => text
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} added note '#{text}' to report ##{subject_id}"
|
||||
def get_log_entry_message(
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "report_note",
|
||||
"subject" => %{"id" => subject_id, "type" => "report"},
|
||||
"text" => text
|
||||
}
|
||||
} = log
|
||||
) do
|
||||
"@#{actor_nickname} added note '#{text}' to report ##{subject_id}" <>
|
||||
subject_actor_nickname(log, " on user ")
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "report_note_delete",
|
||||
"subject" => %{"id" => subject_id, "type" => "report"},
|
||||
"text" => text
|
||||
}
|
||||
}) do
|
||||
"@#{actor_nickname} deleted note '#{text}' from report ##{subject_id}"
|
||||
def get_log_entry_message(
|
||||
%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
"action" => "report_note_delete",
|
||||
"subject" => %{"id" => subject_id, "type" => "report"},
|
||||
"text" => text
|
||||
}
|
||||
} = log
|
||||
) do
|
||||
"@#{actor_nickname} deleted note '#{text}' from report ##{subject_id}" <>
|
||||
subject_actor_nickname(log, " on user ")
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -559,7 +460,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} updated status ##{subject_id}, set visibility: '#{visibility}'"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -572,7 +472,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} updated status ##{subject_id}, set sensitive: '#{sensitive}'"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -587,7 +486,6 @@ defmodule Pleroma.ModerationLog do
|
|||
}'"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -598,7 +496,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} deleted status ##{subject_id}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -609,7 +506,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} forced password reset for users: #{users_to_nicknames_string(subjects)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -620,7 +516,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} confirmed email for users: #{users_to_nicknames_string(subjects)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -633,7 +528,6 @@ defmodule Pleroma.ModerationLog do
|
|||
}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -644,7 +538,6 @@ defmodule Pleroma.ModerationLog do
|
|||
"@#{actor_nickname} updated users: #{users_to_nicknames_string(subjects)}"
|
||||
end
|
||||
|
||||
@spec get_log_entry_message(ModerationLog) :: String.t()
|
||||
def get_log_entry_message(%ModerationLog{
|
||||
data: %{
|
||||
"actor" => %{"nickname" => actor_nickname},
|
||||
|
@ -676,4 +569,16 @@ defmodule Pleroma.ModerationLog do
|
|||
|> Enum.map(&"@#{&1["nickname"]}")
|
||||
|> Enum.join(", ")
|
||||
end
|
||||
|
||||
defp subject_actor_nickname(%ModerationLog{data: data}, prefix_msg, postfix_msg \\ "") do
|
||||
case data do
|
||||
%{"subject_actor" => %{"nickname" => subject_actor}} ->
|
||||
[prefix_msg, "@#{subject_actor}", postfix_msg]
|
||||
|> Enum.reject(&(&1 == ""))
|
||||
|> Enum.join()
|
||||
|
||||
_ ->
|
||||
""
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -806,18 +806,50 @@ defmodule Pleroma.User do
|
|||
end
|
||||
end
|
||||
|
||||
def post_register_action(%User{} = user) do
|
||||
def post_register_action(%User{confirmation_pending: true} = user) do
|
||||
with {:ok, _} <- try_send_confirmation_email(user) do
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
def post_register_action(%User{approval_pending: true} = user) do
|
||||
with {:ok, _} <- send_user_approval_email(user),
|
||||
{:ok, _} <- send_admin_approval_emails(user) do
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
def post_register_action(%User{approval_pending: false, confirmation_pending: false} = user) do
|
||||
with {:ok, user} <- autofollow_users(user),
|
||||
{:ok, _} <- autofollowing_users(user),
|
||||
{:ok, user} <- set_cache(user),
|
||||
{:ok, _} <- send_welcome_email(user),
|
||||
{:ok, _} <- send_welcome_message(user),
|
||||
{:ok, _} <- send_welcome_chat_message(user),
|
||||
{:ok, _} <- try_send_confirmation_email(user) do
|
||||
{:ok, _} <- send_welcome_chat_message(user) do
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
defp send_user_approval_email(user) do
|
||||
user
|
||||
|> Pleroma.Emails.UserEmail.approval_pending_email()
|
||||
|> Pleroma.Emails.Mailer.deliver_async()
|
||||
|
||||
{:ok, :enqueued}
|
||||
end
|
||||
|
||||
defp send_admin_approval_emails(user) do
|
||||
all_superusers()
|
||||
|> Enum.filter(fn user -> not is_nil(user.email) end)
|
||||
|> Enum.each(fn superuser ->
|
||||
superuser
|
||||
|> Pleroma.Emails.AdminEmail.new_unapproved_registration(user)
|
||||
|> Pleroma.Emails.Mailer.deliver_async()
|
||||
end)
|
||||
|
||||
{:ok, :enqueued}
|
||||
end
|
||||
|
||||
def send_welcome_message(user) do
|
||||
if User.WelcomeMessage.enabled?() do
|
||||
User.WelcomeMessage.post_message(user)
|
||||
|
@ -894,7 +926,7 @@ defmodule Pleroma.User do
|
|||
if not ap_enabled?(followed) do
|
||||
follow(follower, followed)
|
||||
else
|
||||
{:ok, follower}
|
||||
{:ok, follower, followed}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -920,11 +952,6 @@ defmodule Pleroma.User do
|
|||
|
||||
true ->
|
||||
FollowingRelationship.follow(follower, followed, state)
|
||||
|
||||
{:ok, _} = update_follower_count(followed)
|
||||
|
||||
follower
|
||||
|> update_following_count()
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -948,11 +975,6 @@ defmodule Pleroma.User do
|
|||
case get_follow_state(follower, followed) do
|
||||
state when state in [:follow_pending, :follow_accept] ->
|
||||
FollowingRelationship.unfollow(follower, followed)
|
||||
{:ok, followed} = update_follower_count(followed)
|
||||
|
||||
{:ok, follower} = update_following_count(follower)
|
||||
|
||||
{:ok, follower, followed}
|
||||
|
||||
nil ->
|
||||
{:error, "Not subscribed!"}
|
||||
|
@ -1600,11 +1622,34 @@ defmodule Pleroma.User do
|
|||
end)
|
||||
end
|
||||
|
||||
def approve(%User{} = user) do
|
||||
change(user, approval_pending: false)
|
||||
|> update_and_set_cache()
|
||||
def approve(%User{approval_pending: true} = user) do
|
||||
with chg <- change(user, approval_pending: false),
|
||||
{:ok, user} <- update_and_set_cache(chg) do
|
||||
post_register_action(user)
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
def approve(%User{} = user), do: {:ok, user}
|
||||
|
||||
def confirm(users) when is_list(users) do
|
||||
Repo.transaction(fn ->
|
||||
Enum.map(users, fn user ->
|
||||
with {:ok, user} <- confirm(user), do: user
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
def confirm(%User{confirmation_pending: true} = user) do
|
||||
with chg <- confirmation_changeset(user, need_confirmation: false),
|
||||
{:ok, user} <- update_and_set_cache(chg) do
|
||||
post_register_action(user)
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
def confirm(%User{} = user), do: {:ok, user}
|
||||
|
||||
def update_notification_settings(%User{} = user, settings) do
|
||||
user
|
||||
|> cast(%{notification_settings: settings}, [])
|
||||
|
@ -2091,18 +2136,6 @@ defmodule Pleroma.User do
|
|||
updated_user
|
||||
end
|
||||
|
||||
@spec toggle_confirmation(User.t()) :: {:ok, User.t()} | {:error, Changeset.t()}
|
||||
def toggle_confirmation(%User{} = user) do
|
||||
user
|
||||
|> confirmation_changeset(need_confirmation: !user.confirmation_pending)
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
@spec toggle_confirmation([User.t()]) :: [{:ok, User.t()} | {:error, Changeset.t()}]
|
||||
def toggle_confirmation(users) do
|
||||
Enum.map(users, &toggle_confirmation/1)
|
||||
end
|
||||
|
||||
@spec need_confirmation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()}
|
||||
def need_confirmation(%User{} = user, bool) do
|
||||
user
|
||||
|
@ -2418,4 +2451,8 @@ defmodule Pleroma.User do
|
|||
|> Map.put(:bio, HTML.filter_tags(user.bio, filter))
|
||||
|> Map.put(:fields, fields)
|
||||
end
|
||||
|
||||
def get_host(%User{ap_id: ap_id} = _user) do
|
||||
URI.parse(ap_id).host
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,7 +45,7 @@ defmodule Pleroma.User.Import do
|
|||
identifiers,
|
||||
fn identifier ->
|
||||
with {:ok, %User{} = followed} <- User.get_or_fetch(identifier),
|
||||
{:ok, follower} <- User.maybe_direct_follow(follower, followed),
|
||||
{:ok, follower, followed} <- User.maybe_direct_follow(follower, followed),
|
||||
{:ok, _, _, _} <- CommonAPI.follow(follower, followed) do
|
||||
followed
|
||||
else
|
||||
|
|
|
@ -3,6 +3,14 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Utils do
|
||||
@posix_error_codes ~w(
|
||||
eacces eagain ebadf ebadmsg ebusy edeadlk edeadlock edquot eexist efault
|
||||
efbig eftype eintr einval eio eisdir eloop emfile emlink emultihop
|
||||
enametoolong enfile enobufs enodev enolck enolink enoent enomem enospc
|
||||
enosr enostr enosys enotblk enotdir enotsup enxio eopnotsupp eoverflow
|
||||
eperm epipe erange erofs espipe esrch estale etxtbsy exdev
|
||||
)a
|
||||
|
||||
def compile_dir(dir) when is_binary(dir) do
|
||||
dir
|
||||
|> File.ls!()
|
||||
|
@ -44,4 +52,12 @@ defmodule Pleroma.Utils do
|
|||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
@spec posix_error_message(atom()) :: binary()
|
||||
def posix_error_message(code) when code in @posix_error_codes do
|
||||
error_message = Gettext.dgettext(Pleroma.Web.Gettext, "posix_errors", "#{code}")
|
||||
"(POSIX error: #{error_message})"
|
||||
end
|
||||
|
||||
def posix_error_message(_), do: ""
|
||||
end
|
||||
|
|
|
@ -20,6 +20,7 @@ defmodule Pleroma.Web do
|
|||
below.
|
||||
"""
|
||||
|
||||
alias Pleroma.Helpers.AuthHelper
|
||||
alias Pleroma.Web.Plugs.EnsureAuthenticatedPlug
|
||||
alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug
|
||||
|
@ -75,7 +76,7 @@ defmodule Pleroma.Web do
|
|||
defp maybe_drop_authentication_if_oauth_check_ignored(conn) do
|
||||
if PlugHelper.plug_called?(conn, ExpectPublicOrAuthenticatedCheckPlug) and
|
||||
not PlugHelper.plug_called_or_skipped?(conn, OAuthScopesPlug) do
|
||||
OAuthScopesPlug.drop_auth_info(conn)
|
||||
AuthHelper.drop_auth_info(conn)
|
||||
else
|
||||
conn
|
||||
end
|
||||
|
|
|
@ -47,10 +47,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
%User{} = followed <- User.get_cached_by_ap_id(actor),
|
||||
%User{} = follower <- User.get_cached_by_ap_id(follower_id),
|
||||
{:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"),
|
||||
{:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept) do
|
||||
{:ok, _follower, followed} <-
|
||||
FollowingRelationship.update(follower, followed, :follow_accept) do
|
||||
Notification.update_notification_type(followed, follow_activity)
|
||||
User.update_follower_count(followed)
|
||||
User.update_following_count(follower)
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
|
@ -99,7 +98,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
) do
|
||||
with %User{} = follower <- User.get_cached_by_ap_id(following_user),
|
||||
%User{} = followed <- User.get_cached_by_ap_id(followed_user),
|
||||
{_, {:ok, _}, _, _} <-
|
||||
{_, {:ok, _, _}, _, _} <-
|
||||
{:following, User.follow(follower, followed, :follow_pending), follower, followed} do
|
||||
if followed.local && !followed.is_locked do
|
||||
{:ok, accept_data, _} = Builder.accept(followed, object)
|
||||
|
|
|
@ -415,7 +415,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||
|
||||
User.toggle_confirmation(users)
|
||||
User.confirm(users)
|
||||
|
||||
ModerationLog.insert_log(%{actor: admin, subject: users, action: "confirm_email"})
|
||||
|
||||
|
|
|
@ -50,10 +50,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
|
|||
Enum.map(reports, fn report ->
|
||||
case CommonAPI.update_report_state(report.id, report.state) do
|
||||
{:ok, activity} ->
|
||||
report = Activity.get_by_id_with_user_actor(activity.id)
|
||||
|
||||
ModerationLog.insert_log(%{
|
||||
action: "report_update",
|
||||
actor: admin,
|
||||
subject: activity
|
||||
subject: activity,
|
||||
subject_actor: report.user_actor
|
||||
})
|
||||
|
||||
activity
|
||||
|
@ -73,11 +76,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
|
|||
def notes_create(%{assigns: %{user: user}, body_params: %{content: content}} = conn, %{
|
||||
id: report_id
|
||||
}) do
|
||||
with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
|
||||
with {:ok, _} <- ReportNote.create(user.id, report_id, content),
|
||||
report <- Activity.get_by_id_with_user_actor(report_id) do
|
||||
ModerationLog.insert_log(%{
|
||||
action: "report_note",
|
||||
actor: user,
|
||||
subject: Activity.get_by_id(report_id),
|
||||
subject: report,
|
||||
subject_actor: report.user_actor,
|
||||
text: content
|
||||
})
|
||||
|
||||
|
@ -91,11 +96,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
|
|||
id: note_id,
|
||||
report_id: report_id
|
||||
}) do
|
||||
with {:ok, note} <- ReportNote.destroy(note_id) do
|
||||
with {:ok, note} <- ReportNote.destroy(note_id),
|
||||
report <- Activity.get_by_id_with_user_actor(report_id) do
|
||||
ModerationLog.insert_log(%{
|
||||
action: "report_note_delete",
|
||||
actor: user,
|
||||
subject: Activity.get_by_id(report_id),
|
||||
subject: report,
|
||||
subject_actor: report.user_actor,
|
||||
text: note.content
|
||||
})
|
||||
|
||||
|
|
|
@ -27,7 +27,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiFileOperation do
|
|||
422 => Operation.response("Unprocessable Entity", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError),
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||
409 => Operation.response("Conflict", "application/json", ApiError)
|
||||
409 => Operation.response("Conflict", "application/json", ApiError),
|
||||
500 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -169,7 +169,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do
|
|||
responses: %{
|
||||
200 => ok_response(),
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
404 => Operation.response("Not Found", "application/json", ApiError),
|
||||
500 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
@ -184,7 +185,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do
|
|||
parameters: [name_param()],
|
||||
responses: %{
|
||||
200 => Operation.response("Metadata", "application/json", metadata()),
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||
500 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -51,7 +51,7 @@ defmodule Pleroma.Web.Feed.FeedView do
|
|||
def feed_logo do
|
||||
case Pleroma.Config.get([:feed, :logo]) do
|
||||
nil ->
|
||||
"#{Pleroma.Web.base_url()}/static/logo.png"
|
||||
"#{Pleroma.Web.base_url()}/static/logo.svg"
|
||||
|
||||
logo ->
|
||||
"#{Pleroma.Web.base_url()}#{logo}"
|
||||
|
|
|
@ -6,6 +6,8 @@ defmodule Pleroma.Web.MastoFEController do
|
|||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.MastodonAPI.AuthController
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
|
@ -26,27 +28,27 @@ defmodule Pleroma.Web.MastoFEController do
|
|||
)
|
||||
|
||||
@doc "GET /web/*path"
|
||||
def index(%{assigns: %{user: user, token: token}} = conn, _params)
|
||||
when not is_nil(user) and not is_nil(token) do
|
||||
conn
|
||||
|> put_layout(false)
|
||||
|> render("index.html",
|
||||
token: token.token,
|
||||
user: user,
|
||||
custom_emojis: Pleroma.Emoji.get_all()
|
||||
)
|
||||
end
|
||||
|
||||
def index(conn, _params) do
|
||||
conn
|
||||
|> put_session(:return_to, conn.request_path)
|
||||
|> redirect(to: "/web/login")
|
||||
with %{assigns: %{user: %User{} = user, token: %Token{app_id: token_app_id} = token}} <- conn,
|
||||
{:ok, %{id: ^token_app_id}} <- AuthController.local_mastofe_app() do
|
||||
conn
|
||||
|> put_layout(false)
|
||||
|> render("index.html",
|
||||
token: token.token,
|
||||
user: user,
|
||||
custom_emojis: Pleroma.Emoji.get_all()
|
||||
)
|
||||
else
|
||||
_ ->
|
||||
conn
|
||||
|> put_session(:return_to, conn.request_path)
|
||||
|> redirect(to: "/web/login")
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /web/manifest.json"
|
||||
def manifest(conn, _params) do
|
||||
conn
|
||||
|> render("manifest.json")
|
||||
render(conn, "manifest.json")
|
||||
end
|
||||
|
||||
@doc "PUT /api/web/settings: Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere"
|
||||
|
|
|
@ -25,7 +25,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
alias Pleroma.Web.MastodonAPI.MastodonAPIController
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.OAuth.OAuthController
|
||||
alias Pleroma.Web.OAuth.OAuthView
|
||||
alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.Plugs.RateLimiter
|
||||
|
@ -103,7 +102,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
{:ok, user} <- TwitterAPI.register_user(params),
|
||||
{_, {:ok, token}} <-
|
||||
{:login, OAuthController.login(user, app, app.scopes)} do
|
||||
json(conn, OAuthView.render("token.json", %{user: user, token: token}))
|
||||
OAuthController.after_token_exchange(conn, %{user: user, token: token})
|
||||
else
|
||||
{:login, {:account_status, :confirmation_pending}} ->
|
||||
json_response(conn, :ok, %{
|
||||
|
|
|
@ -7,10 +7,13 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
|||
|
||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||
|
||||
alias Pleroma.Helpers.AuthHelper
|
||||
alias Pleroma.Helpers.UriHelper
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.OAuth.Authorization
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.OAuth.Token.Strategy.Revoke, as: RevokeToken
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
@ -20,24 +23,35 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
|||
@local_mastodon_name "Mastodon-Local"
|
||||
|
||||
@doc "GET /web/login"
|
||||
def login(%{assigns: %{user: %User{}}} = conn, _params) do
|
||||
redirect(conn, to: local_mastodon_root_path(conn))
|
||||
end
|
||||
|
||||
# Local Mastodon FE login init action
|
||||
def login(conn, %{"code" => auth_token}) do
|
||||
with {:ok, app} <- get_or_make_app(),
|
||||
# Local Mastodon FE login callback action
|
||||
def login(conn, %{"code" => auth_token} = params) do
|
||||
with {:ok, app} <- local_mastofe_app(),
|
||||
{:ok, auth} <- Authorization.get_by_token(app, auth_token),
|
||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||
{:ok, oauth_token} <- Token.exchange_token(app, auth) do
|
||||
redirect_to =
|
||||
conn
|
||||
|> local_mastodon_post_login_path()
|
||||
|> UriHelper.modify_uri_params(%{"access_token" => oauth_token.token})
|
||||
|
||||
conn
|
||||
|> put_session(:oauth_token, token.token)
|
||||
|> redirect(to: local_mastodon_root_path(conn))
|
||||
|> AuthHelper.put_session_token(oauth_token.token)
|
||||
|> redirect(to: redirect_to)
|
||||
else
|
||||
_ -> redirect_to_oauth_form(conn, params)
|
||||
end
|
||||
end
|
||||
|
||||
# Local Mastodon FE callback action
|
||||
def login(conn, _) do
|
||||
with {:ok, app} <- get_or_make_app() do
|
||||
def login(conn, params) do
|
||||
with %{assigns: %{user: %User{}, token: %Token{app_id: app_id}}} <- conn,
|
||||
{:ok, %{id: ^app_id}} <- local_mastofe_app() do
|
||||
redirect(conn, to: local_mastodon_post_login_path(conn))
|
||||
else
|
||||
_ -> redirect_to_oauth_form(conn, params)
|
||||
end
|
||||
end
|
||||
|
||||
defp redirect_to_oauth_form(conn, _params) do
|
||||
with {:ok, app} <- local_mastofe_app() do
|
||||
path =
|
||||
o_auth_path(conn, :authorize,
|
||||
response_type: "code",
|
||||
|
@ -52,9 +66,16 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
|||
|
||||
@doc "DELETE /auth/sign_out"
|
||||
def logout(conn, _) do
|
||||
conn
|
||||
|> clear_session
|
||||
|> redirect(to: "/")
|
||||
conn =
|
||||
with %{assigns: %{token: %Token{} = oauth_token}} <- conn,
|
||||
session_token = AuthHelper.get_session_token(conn),
|
||||
{:ok, %Token{token: ^session_token}} <- RevokeToken.revoke(oauth_token) do
|
||||
AuthHelper.delete_session_token(conn)
|
||||
else
|
||||
_ -> conn
|
||||
end
|
||||
|
||||
redirect(conn, to: "/")
|
||||
end
|
||||
|
||||
@doc "POST /auth/password"
|
||||
|
@ -66,7 +87,7 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
|||
json_response(conn, :no_content, "")
|
||||
end
|
||||
|
||||
defp local_mastodon_root_path(conn) do
|
||||
defp local_mastodon_post_login_path(conn) do
|
||||
case get_session(conn, :return_to) do
|
||||
nil ->
|
||||
masto_fe_path(conn, :index, ["getting-started"])
|
||||
|
@ -77,9 +98,11 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
|||
end
|
||||
end
|
||||
|
||||
@spec get_or_make_app() :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
||||
defp get_or_make_app do
|
||||
%{client_name: @local_mastodon_name, redirect_uris: "."}
|
||||
|> App.get_or_make(["read", "write", "follow", "push", "admin"])
|
||||
@spec local_mastofe_app() :: {:ok, App.t()} | {:error, Ecto.Changeset.t()}
|
||||
def local_mastofe_app do
|
||||
App.get_or_make(
|
||||
%{client_name: @local_mastodon_name, redirect_uris: "."},
|
||||
["read", "write", "follow", "push", "admin"]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -187,18 +187,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
header_static = User.banner_url(user) |> MediaProxy.preview_url(static: true)
|
||||
|
||||
following_count =
|
||||
if !user.hide_follows_count or !user.hide_follows or opts[:for] == user do
|
||||
user.following_count || 0
|
||||
else
|
||||
0
|
||||
end
|
||||
if !user.hide_follows_count or !user.hide_follows or opts[:for] == user,
|
||||
do: user.following_count,
|
||||
else: 0
|
||||
|
||||
followers_count =
|
||||
if !user.hide_followers_count or !user.hide_followers or opts[:for] == user do
|
||||
user.follower_count || 0
|
||||
else
|
||||
0
|
||||
end
|
||||
if !user.hide_followers_count or !user.hide_followers or opts[:for] == user,
|
||||
do: user.follower_count,
|
||||
else: 0
|
||||
|
||||
bot = user.actor_type == "Service"
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.App
|
||||
alias Pleroma.Web.OAuth.Authorization
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
@ -53,7 +54,8 @@ defmodule Pleroma.Web.OAuth.Authorization do
|
|||
end
|
||||
|
||||
defp add_lifetime(changeset) do
|
||||
put_change(changeset, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10))
|
||||
lifespan = Token.lifespan()
|
||||
put_change(changeset, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), lifespan))
|
||||
end
|
||||
|
||||
@spec use_changeset(Authtorizatiton.t(), map()) :: Changeset.t()
|
||||
|
|
|
@ -13,7 +13,6 @@ defmodule Pleroma.Web.OAuth.MFAController do
|
|||
alias Pleroma.Web.Auth.TOTPAuthenticator
|
||||
alias Pleroma.Web.OAuth.MFAView, as: View
|
||||
alias Pleroma.Web.OAuth.OAuthController
|
||||
alias Pleroma.Web.OAuth.OAuthView
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
plug(:fetch_session when action in [:show, :verify])
|
||||
|
@ -75,7 +74,7 @@ defmodule Pleroma.Web.OAuth.MFAController do
|
|||
{:ok, %{user: user, authorization: auth}} <- MFA.Token.validate(mfa_token),
|
||||
{:ok, _} <- validates_challenge(user, params),
|
||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||
json(conn, OAuthView.render("token.json", %{user: user, token: token}))
|
||||
OAuthController.after_token_exchange(conn, %{user: user, token: token})
|
||||
else
|
||||
_error ->
|
||||
conn
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.OAuth.OAuthController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Helpers.AuthHelper
|
||||
alias Pleroma.Helpers.UriHelper
|
||||
alias Pleroma.Maps
|
||||
alias Pleroma.MFA
|
||||
|
@ -79,6 +80,13 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
available_scopes = (app && app.scopes) || []
|
||||
scopes = Scopes.fetch_scopes(params, available_scopes)
|
||||
|
||||
user =
|
||||
with %{assigns: %{user: %User{} = user}} <- conn do
|
||||
user
|
||||
else
|
||||
_ -> nil
|
||||
end
|
||||
|
||||
scopes =
|
||||
if scopes == [] do
|
||||
available_scopes
|
||||
|
@ -88,6 +96,8 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
|
||||
# Note: `params` might differ from `conn.params`; use `@params` not `@conn.params` in template
|
||||
render(conn, Authenticator.auth_template(), %{
|
||||
user: user,
|
||||
app: app && Map.delete(app, :client_secret),
|
||||
response_type: params["response_type"],
|
||||
client_id: params["client_id"],
|
||||
available_scopes: available_scopes,
|
||||
|
@ -131,11 +141,13 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
end
|
||||
end
|
||||
|
||||
def create_authorization(
|
||||
%Plug.Conn{} = conn,
|
||||
%{"authorization" => _} = params,
|
||||
opts \\ []
|
||||
) do
|
||||
def create_authorization(_, _, opts \\ [])
|
||||
|
||||
def create_authorization(%Plug.Conn{assigns: %{user: %User{} = user}} = conn, params, []) do
|
||||
create_authorization(conn, params, user: user)
|
||||
end
|
||||
|
||||
def create_authorization(%Plug.Conn{} = conn, %{"authorization" => _} = params, opts) do
|
||||
with {:ok, auth, user} <- do_create_authorization(conn, params, opts[:user]),
|
||||
{:mfa_required, _, _, false} <- {:mfa_required, user, auth, MFA.require?(user)} do
|
||||
after_create_authorization(conn, auth, params)
|
||||
|
@ -248,7 +260,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
||||
{:ok, %{user: user} = token} <- Token.get_by_refresh_token(app, token),
|
||||
{:ok, token} <- RefreshToken.grant(token) do
|
||||
json(conn, OAuthView.render("token.json", %{user: user, token: token}))
|
||||
after_token_exchange(conn, %{user: user, token: token})
|
||||
else
|
||||
_error -> render_invalid_credentials_error(conn)
|
||||
end
|
||||
|
@ -260,7 +272,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
{:ok, auth} <- Authorization.get_by_token(app, fixed_token),
|
||||
%User{} = user <- User.get_cached_by_id(auth.user_id),
|
||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||
json(conn, OAuthView.render("token.json", %{user: user, token: token}))
|
||||
after_token_exchange(conn, %{user: user, token: token})
|
||||
else
|
||||
error ->
|
||||
handle_token_exchange_error(conn, error)
|
||||
|
@ -275,7 +287,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
{:ok, app} <- Token.Utils.fetch_app(conn),
|
||||
requested_scopes <- Scopes.fetch_scopes(params, app.scopes),
|
||||
{:ok, token} <- login(user, app, requested_scopes) do
|
||||
json(conn, OAuthView.render("token.json", %{user: user, token: token}))
|
||||
after_token_exchange(conn, %{user: user, token: token})
|
||||
else
|
||||
error ->
|
||||
handle_token_exchange_error(conn, error)
|
||||
|
@ -298,7 +310,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
||||
{:ok, auth} <- Authorization.create_authorization(app, %User{}),
|
||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||
json(conn, OAuthView.render("token.json", %{token: token}))
|
||||
after_token_exchange(conn, %{token: token})
|
||||
else
|
||||
_error ->
|
||||
handle_token_exchange_error(conn, :invalid_credentails)
|
||||
|
@ -308,6 +320,12 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
# Bad request
|
||||
def token_exchange(%Plug.Conn{} = conn, params), do: bad_request(conn, params)
|
||||
|
||||
def after_token_exchange(%Plug.Conn{} = conn, %{token: token} = view_params) do
|
||||
conn
|
||||
|> AuthHelper.put_session_token(token.token)
|
||||
|> json(OAuthView.render("token.json", view_params))
|
||||
end
|
||||
|
||||
defp handle_token_exchange_error(%Plug.Conn{} = conn, {:mfa_required, user, auth, _}) do
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|
@ -361,9 +379,17 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
render_invalid_credentials_error(conn)
|
||||
end
|
||||
|
||||
def token_revoke(%Plug.Conn{} = conn, %{"token" => _token} = params) do
|
||||
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
||||
{:ok, _token} <- RevokeToken.revoke(app, params) do
|
||||
def token_revoke(%Plug.Conn{} = conn, %{"token" => token}) do
|
||||
with {:ok, %Token{} = oauth_token} <- Token.get_by_token(token),
|
||||
{:ok, oauth_token} <- RevokeToken.revoke(oauth_token) do
|
||||
conn =
|
||||
with session_token = AuthHelper.get_session_token(conn),
|
||||
%Token{token: ^session_token} <- oauth_token do
|
||||
AuthHelper.delete_session_token(conn)
|
||||
else
|
||||
_ -> conn
|
||||
end
|
||||
|
||||
json(conn, %{})
|
||||
else
|
||||
_error ->
|
||||
|
|
|
@ -13,7 +13,7 @@ defmodule Pleroma.Web.OAuth.OAuthView do
|
|||
token_type: "Bearer",
|
||||
access_token: token.token,
|
||||
refresh_token: token.refresh_token,
|
||||
expires_in: expires_in(),
|
||||
expires_in: NaiveDateTime.diff(token.valid_until, NaiveDateTime.utc_now()),
|
||||
scope: Enum.join(token.scopes, " "),
|
||||
created_at: Utils.format_created_at(token)
|
||||
}
|
||||
|
@ -25,6 +25,4 @@ defmodule Pleroma.Web.OAuth.OAuthView do
|
|||
response
|
||||
end
|
||||
end
|
||||
|
||||
defp expires_in, do: Pleroma.Config.get([:oauth2, :token_expires_in], 600)
|
||||
end
|
||||
|
|
|
@ -27,6 +27,18 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
timestamps()
|
||||
end
|
||||
|
||||
def lifespan do
|
||||
Pleroma.Config.get!([:oauth2, :token_expires_in])
|
||||
end
|
||||
|
||||
@doc "Gets token by unique access token"
|
||||
@spec get_by_token(String.t()) :: {:ok, t()} | {:error, :not_found}
|
||||
def get_by_token(token) do
|
||||
token
|
||||
|> Query.get_by_token()
|
||||
|> Repo.find_resource()
|
||||
end
|
||||
|
||||
@doc "Gets token for app by access token"
|
||||
@spec get_by_token(App.t(), String.t()) :: {:ok, t()} | {:error, :not_found}
|
||||
def get_by_token(%App{id: app_id} = _app, token) do
|
||||
|
@ -75,11 +87,11 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
end
|
||||
|
||||
defp put_valid_until(changeset, attrs) do
|
||||
expires_in =
|
||||
Map.get(attrs, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), expires_in()))
|
||||
valid_until =
|
||||
Map.get(attrs, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), lifespan()))
|
||||
|
||||
changeset
|
||||
|> change(%{valid_until: expires_in})
|
||||
|> change(%{valid_until: valid_until})
|
||||
|> validate_required([:valid_until])
|
||||
end
|
||||
|
||||
|
@ -130,6 +142,4 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
end
|
||||
|
||||
def is_expired?(_), do: false
|
||||
|
||||
defp expires_in, do: Pleroma.Config.get([:oauth2, :token_expires_in], 600)
|
||||
end
|
||||
|
|
|
@ -42,7 +42,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do
|
|||
|> json(%{error: "pack name, shortcode or filename cannot be empty"})
|
||||
|
||||
{:error, _} = error ->
|
||||
handle_error(conn, error, %{pack_name: pack_name})
|
||||
handle_error(conn, error, %{
|
||||
pack_name: pack_name,
|
||||
message: "Unexpected error occurred while adding file to pack."
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -69,7 +72,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do
|
|||
|> json(%{error: "new_shortcode or new_filename cannot be empty"})
|
||||
|
||||
{:error, _} = error ->
|
||||
handle_error(conn, error, %{pack_name: pack_name, code: shortcode})
|
||||
handle_error(conn, error, %{
|
||||
pack_name: pack_name,
|
||||
code: shortcode,
|
||||
message: "Unexpected error occurred while updating."
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -84,7 +91,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do
|
|||
|> json(%{error: "pack name or shortcode cannot be empty"})
|
||||
|
||||
{:error, _} = error ->
|
||||
handle_error(conn, error, %{pack_name: pack_name, code: shortcode})
|
||||
handle_error(conn, error, %{
|
||||
pack_name: pack_name,
|
||||
code: shortcode,
|
||||
message: "Unexpected error occurred while deleting emoji file."
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -94,18 +105,24 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do
|
|||
|> json(%{error: "Emoji \"#{emoji_code}\" does not exist"})
|
||||
end
|
||||
|
||||
defp handle_error(conn, {:error, :not_found}, %{pack_name: pack_name}) do
|
||||
defp handle_error(conn, {:error, :enoent}, %{pack_name: pack_name}) do
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "pack \"#{pack_name}\" is not found"})
|
||||
end
|
||||
|
||||
defp handle_error(conn, {:error, _}, _) do
|
||||
render_error(
|
||||
conn,
|
||||
:internal_server_error,
|
||||
"Unexpected error occurred while adding file to pack."
|
||||
)
|
||||
defp handle_error(conn, {:error, error}, opts) do
|
||||
message =
|
||||
[
|
||||
Map.get(opts, :message, "Unexpected error occurred."),
|
||||
Pleroma.Utils.posix_error_message(error)
|
||||
]
|
||||
|> Enum.join(" ")
|
||||
|> String.trim()
|
||||
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(%{error: message})
|
||||
end
|
||||
|
||||
defp get_filename(%Plug.Upload{filename: filename}), do: filename
|
||||
|
|
|
@ -71,7 +71,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|
|||
with {:ok, pack} <- Pack.show(name: name, page: page, page_size: page_size) do
|
||||
json(conn, pack)
|
||||
else
|
||||
{:error, :not_found} ->
|
||||
{:error, :enoent} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "Pack #{name} does not exist"})
|
||||
|
@ -80,6 +80,17 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|
|||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{error: "pack name cannot be empty"})
|
||||
|
||||
{:error, error} ->
|
||||
error_message =
|
||||
add_posix_error(
|
||||
"Failed to get the contents of the `#{name}` pack.",
|
||||
error
|
||||
)
|
||||
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(%{error: error_message})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -95,7 +106,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|
|||
"Pack #{name} cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing"
|
||||
})
|
||||
|
||||
{:error, :not_found} ->
|
||||
{:error, :enoent} ->
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(%{error: "Pack #{name} does not exist"})
|
||||
|
@ -116,10 +127,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|
|||
|> put_status(:internal_server_error)
|
||||
|> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"})
|
||||
|
||||
{:error, e} ->
|
||||
{:error, error} ->
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(%{error: e})
|
||||
|> json(%{error: error})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -139,12 +150,16 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|
|||
|> put_status(:bad_request)
|
||||
|> json(%{error: "pack name cannot be empty"})
|
||||
|
||||
{:error, _} ->
|
||||
render_error(
|
||||
conn,
|
||||
:internal_server_error,
|
||||
"Unexpected error occurred while creating pack."
|
||||
)
|
||||
{:error, error} ->
|
||||
error_message =
|
||||
add_posix_error(
|
||||
"Unexpected error occurred while creating pack.",
|
||||
error
|
||||
)
|
||||
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(%{error: error_message})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -164,10 +179,12 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|
|||
|> put_status(:bad_request)
|
||||
|> json(%{error: "pack name cannot be empty"})
|
||||
|
||||
{:error, _, _} ->
|
||||
{:error, error, _} ->
|
||||
error_message = add_posix_error("Couldn't delete the `#{name}` pack", error)
|
||||
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(%{error: "Couldn't delete the pack #{name}"})
|
||||
|> json(%{error: error_message})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -180,12 +197,16 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|
|||
|> put_status(:bad_request)
|
||||
|> json(%{error: "The fallback archive does not have all files specified in pack.json"})
|
||||
|
||||
{:error, _} ->
|
||||
render_error(
|
||||
conn,
|
||||
:internal_server_error,
|
||||
"Unexpected error occurred while updating pack metadata."
|
||||
)
|
||||
{:error, error} ->
|
||||
error_message =
|
||||
add_posix_error(
|
||||
"Unexpected error occurred while updating pack metadata.",
|
||||
error
|
||||
)
|
||||
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(%{error: error_message})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -204,4 +225,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|
|||
|> json(%{error: "Error accessing emoji pack directory"})
|
||||
end
|
||||
end
|
||||
|
||||
defp add_posix_error(msg, error) do
|
||||
[msg, Pleroma.Utils.posix_error_message(error)]
|
||||
|> Enum.join(" ")
|
||||
|> String.trim()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,21 +5,14 @@
|
|||
defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlug do
|
||||
import Plug.Conn
|
||||
|
||||
alias Pleroma.Helpers.AuthHelper
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.Plugs.RateLimiter
|
||||
|
||||
def init(options) do
|
||||
options
|
||||
end
|
||||
|
||||
def secret_token do
|
||||
case Pleroma.Config.get(:admin_token) do
|
||||
blank when blank in [nil, ""] -> nil
|
||||
token -> token
|
||||
end
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||
|
||||
def call(conn, _) do
|
||||
|
@ -30,7 +23,7 @@ defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlug do
|
|||
end
|
||||
end
|
||||
|
||||
def authenticate(%{params: %{"admin_token" => admin_token}} = conn) do
|
||||
defp authenticate(%{params: %{"admin_token" => admin_token}} = conn) do
|
||||
if admin_token == secret_token() do
|
||||
assign_admin_user(conn)
|
||||
else
|
||||
|
@ -38,7 +31,7 @@ defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlug do
|
|||
end
|
||||
end
|
||||
|
||||
def authenticate(conn) do
|
||||
defp authenticate(conn) do
|
||||
token = secret_token()
|
||||
|
||||
case get_req_header(conn, "x-admin-token") do
|
||||
|
@ -48,10 +41,17 @@ defmodule Pleroma.Web.Plugs.AdminSecretAuthenticationPlug do
|
|||
end
|
||||
end
|
||||
|
||||
defp secret_token do
|
||||
case Pleroma.Config.get(:admin_token) do
|
||||
blank when blank in [nil, ""] -> nil
|
||||
token -> token
|
||||
end
|
||||
end
|
||||
|
||||
defp assign_admin_user(conn) do
|
||||
conn
|
||||
|> assign(:user, %User{is_admin: true})
|
||||
|> OAuthScopesPlug.skip_plug()
|
||||
|> AuthHelper.skip_oauth()
|
||||
end
|
||||
|
||||
defp handle_bad_token(conn) do
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.AuthenticationPlug do
|
||||
@moduledoc "Password authentication plug."
|
||||
|
||||
alias Pleroma.Helpers.AuthHelper
|
||||
alias Pleroma.User
|
||||
|
||||
import Plug.Conn
|
||||
|
@ -11,6 +14,30 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
|
|||
|
||||
def init(options), do: options
|
||||
|
||||
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||
|
||||
def call(
|
||||
%{
|
||||
assigns: %{
|
||||
auth_user: %{password_hash: password_hash} = auth_user,
|
||||
auth_credentials: %{password: password}
|
||||
}
|
||||
} = conn,
|
||||
_
|
||||
) do
|
||||
if checkpw(password, password_hash) do
|
||||
{:ok, auth_user} = maybe_update_password(auth_user, password)
|
||||
|
||||
conn
|
||||
|> assign(:user, auth_user)
|
||||
|> AuthHelper.skip_oauth()
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
def call(conn, _), do: conn
|
||||
|
||||
def checkpw(password, "$6" <> _ = password_hash) do
|
||||
:crypt.crypt(password, password_hash) == password_hash
|
||||
end
|
||||
|
@ -40,40 +67,6 @@ defmodule Pleroma.Web.Plugs.AuthenticationPlug do
|
|||
def maybe_update_password(user, _), do: {:ok, user}
|
||||
|
||||
defp do_update_password(user, password) do
|
||||
user
|
||||
|> User.password_update_changeset(%{
|
||||
"password" => password,
|
||||
"password_confirmation" => password
|
||||
})
|
||||
|> Pleroma.Repo.update()
|
||||
User.reset_password(user, %{password: password, password_confirmation: password})
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||
|
||||
def call(
|
||||
%{
|
||||
assigns: %{
|
||||
auth_user: %{password_hash: password_hash} = auth_user,
|
||||
auth_credentials: %{password: password}
|
||||
}
|
||||
} = conn,
|
||||
_
|
||||
) do
|
||||
if checkpw(password, password_hash) do
|
||||
{:ok, auth_user} = maybe_update_password(auth_user, password)
|
||||
|
||||
conn
|
||||
|> assign(:user, auth_user)
|
||||
|> Pleroma.Web.Plugs.OAuthScopesPlug.skip_plug()
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
def call(%{assigns: %{auth_credentials: %{password: _}}} = conn, _) do
|
||||
Pbkdf2.no_user_verify()
|
||||
conn
|
||||
end
|
||||
|
||||
def call(conn, _), do: conn
|
||||
end
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.BasicAuthDecoderPlug do
|
||||
@moduledoc """
|
||||
Decodes HTTP Basic Auth information and assigns `:auth_credentials`.
|
||||
|
||||
NOTE: no checks are performed at this step, auth_credentials/username could be easily faked.
|
||||
"""
|
||||
|
||||
import Plug.Conn
|
||||
|
||||
def init(options) do
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.EnsureUserKeyPlug do
|
||||
import Plug.Conn
|
||||
|
||||
def init(opts) do
|
||||
opts
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: _}} = conn, _), do: conn
|
||||
|
||||
def call(conn, _) do
|
||||
conn
|
||||
|> assign(:user, nil)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,36 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug do
|
||||
import Plug.Conn
|
||||
|
||||
alias Pleroma.Helpers.AuthHelper
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
@moduledoc "Ensures presence and consistency of :user and :token assigns."
|
||||
|
||||
def init(opts) do
|
||||
opts
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{id: user_id}} = assigns} = conn, _) do
|
||||
with %Token{user_id: ^user_id} <- assigns[:token] do
|
||||
conn
|
||||
else
|
||||
%Token{} ->
|
||||
# A safety net for abnormal (unexpected) scenario: :token belongs to another user
|
||||
AuthHelper.drop_auth_info(conn)
|
||||
|
||||
_ ->
|
||||
assign(conn, :token, nil)
|
||||
end
|
||||
end
|
||||
|
||||
def call(conn, _) do
|
||||
conn
|
||||
|> assign(:user, nil)
|
||||
|> assign(:token, nil)
|
||||
end
|
||||
end
|
|
@ -1,41 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.LegacyAuthenticationPlug do
|
||||
import Plug.Conn
|
||||
|
||||
alias Pleroma.User
|
||||
|
||||
def init(options) do
|
||||
options
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||
|
||||
def call(
|
||||
%{
|
||||
assigns: %{
|
||||
auth_user: %{password_hash: "$6$" <> _ = password_hash} = auth_user,
|
||||
auth_credentials: %{password: password}
|
||||
}
|
||||
} = conn,
|
||||
_
|
||||
) do
|
||||
with ^password_hash <- :crypt.crypt(password, password_hash),
|
||||
{:ok, user} <-
|
||||
User.reset_password(auth_user, %{password: password, password_confirmation: password}) do
|
||||
conn
|
||||
|> assign(:auth_user, user)
|
||||
|> assign(:user, user)
|
||||
|> Pleroma.Web.Plugs.OAuthScopesPlug.skip_plug()
|
||||
else
|
||||
_ ->
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
def call(conn, _) do
|
||||
conn
|
||||
end
|
||||
end
|
|
@ -3,6 +3,7 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
|
||||
alias Pleroma.Helpers.AuthHelper
|
||||
alias Pleroma.Signature
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
|
@ -12,6 +13,47 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
|
|||
|
||||
def init(options), do: options
|
||||
|
||||
def call(%{assigns: %{user: %User{}}} = conn, _opts), do: conn
|
||||
|
||||
# if this has payload make sure it is signed by the same actor that made it
|
||||
def call(%{assigns: %{valid_signature: true}, params: %{"actor" => actor}} = conn, _opts) do
|
||||
with actor_id <- Utils.get_ap_id(actor),
|
||||
{:user, %User{} = user} <- {:user, user_from_key_id(conn)},
|
||||
{:user_match, true} <- {:user_match, user.ap_id == actor_id} do
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> AuthHelper.skip_oauth()
|
||||
else
|
||||
{:user_match, false} ->
|
||||
Logger.debug("Failed to map identity from signature (payload actor mismatch)")
|
||||
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{inspect(actor)}")
|
||||
assign(conn, :valid_signature, false)
|
||||
|
||||
# remove me once testsuite uses mapped capabilities instead of what we do now
|
||||
{:user, nil} ->
|
||||
Logger.debug("Failed to map identity from signature (lookup failure)")
|
||||
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}")
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
# no payload, probably a signed fetch
|
||||
def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
|
||||
with %User{} = user <- user_from_key_id(conn) do
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> AuthHelper.skip_oauth()
|
||||
else
|
||||
_ ->
|
||||
Logger.debug("Failed to map identity from signature (no payload actor mismatch)")
|
||||
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}")
|
||||
assign(conn, :valid_signature, false)
|
||||
end
|
||||
end
|
||||
|
||||
# no signature at all
|
||||
def call(conn, _opts), do: conn
|
||||
|
||||
defp key_id_from_conn(conn) do
|
||||
with %{"keyId" => key_id} <- HTTPSignatures.signature_for_conn(conn),
|
||||
{:ok, ap_id} <- Signature.key_id_to_actor_id(key_id) do
|
||||
|
@ -31,41 +73,4 @@ defmodule Pleroma.Web.Plugs.MappedSignatureToIdentityPlug do
|
|||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: _}} = conn, _opts), do: conn
|
||||
|
||||
# if this has payload make sure it is signed by the same actor that made it
|
||||
def call(%{assigns: %{valid_signature: true}, params: %{"actor" => actor}} = conn, _opts) do
|
||||
with actor_id <- Utils.get_ap_id(actor),
|
||||
{:user, %User{} = user} <- {:user, user_from_key_id(conn)},
|
||||
{:user_match, true} <- {:user_match, user.ap_id == actor_id} do
|
||||
assign(conn, :user, user)
|
||||
else
|
||||
{:user_match, false} ->
|
||||
Logger.debug("Failed to map identity from signature (payload actor mismatch)")
|
||||
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{inspect(actor)}")
|
||||
assign(conn, :valid_signature, false)
|
||||
|
||||
# remove me once testsuite uses mapped capabilities instead of what we do now
|
||||
{:user, nil} ->
|
||||
Logger.debug("Failed to map identity from signature (lookup failure)")
|
||||
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}, actor=#{actor}")
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
# no payload, probably a signed fetch
|
||||
def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
|
||||
with %User{} = user <- user_from_key_id(conn) do
|
||||
assign(conn, :user, user)
|
||||
else
|
||||
_ ->
|
||||
Logger.debug("Failed to map identity from signature (no payload actor mismatch)")
|
||||
Logger.debug("key_id=#{inspect(key_id_from_conn(conn))}")
|
||||
assign(conn, :valid_signature, false)
|
||||
end
|
||||
end
|
||||
|
||||
# no signature at all
|
||||
def call(conn, _opts), do: conn
|
||||
end
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.OAuthPlug do
|
||||
@moduledoc "Performs OAuth authentication by token from params / headers / cookies."
|
||||
|
||||
import Plug.Conn
|
||||
import Ecto.Query
|
||||
|
||||
alias Pleroma.Helpers.AuthHelper
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.App
|
||||
|
@ -17,45 +20,26 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
|
|||
|
||||
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||
|
||||
def call(%{params: %{"access_token" => access_token}} = conn, _) do
|
||||
with {:ok, user, token_record} <- fetch_user_and_token(access_token) do
|
||||
conn
|
||||
|> assign(:token, token_record)
|
||||
|> assign(:user, user)
|
||||
else
|
||||
_ ->
|
||||
# token found, but maybe only with app
|
||||
with {:ok, app, token_record} <- fetch_app_and_token(access_token) do
|
||||
conn
|
||||
|> assign(:token, token_record)
|
||||
|> assign(:app, app)
|
||||
else
|
||||
_ -> conn
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def call(conn, _) do
|
||||
case fetch_token_str(conn) do
|
||||
{:ok, token} ->
|
||||
with {:ok, user, token_record} <- fetch_user_and_token(token) do
|
||||
conn
|
||||
|> assign(:token, token_record)
|
||||
|> assign(:user, user)
|
||||
else
|
||||
_ ->
|
||||
# token found, but maybe only with app
|
||||
with {:ok, app, token_record} <- fetch_app_and_token(token) do
|
||||
conn
|
||||
|> assign(:token, token_record)
|
||||
|> assign(:app, app)
|
||||
else
|
||||
_ -> conn
|
||||
end
|
||||
end
|
||||
|
||||
_ ->
|
||||
with {:ok, token_str} <- fetch_token_str(conn) do
|
||||
with {:ok, user, user_token} <- fetch_user_and_token(token_str),
|
||||
false <- Token.is_expired?(user_token) do
|
||||
conn
|
||||
|> assign(:token, user_token)
|
||||
|> assign(:user, user)
|
||||
else
|
||||
_ ->
|
||||
with {:ok, app, app_token} <- fetch_app_and_token(token_str),
|
||||
false <- Token.is_expired?(app_token) do
|
||||
conn
|
||||
|> assign(:token, app_token)
|
||||
|> assign(:app, app)
|
||||
else
|
||||
_ -> conn
|
||||
end
|
||||
end
|
||||
else
|
||||
_ -> conn
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -70,7 +54,6 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
|
|||
preload: [user: user]
|
||||
)
|
||||
|
||||
# credo:disable-for-next-line Credo.Check.Readability.MaxLineLength
|
||||
with %Token{user: user} = token_record <- Repo.one(query) do
|
||||
{:ok, user, token_record}
|
||||
end
|
||||
|
@ -86,29 +69,23 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
|
|||
end
|
||||
end
|
||||
|
||||
# Gets token from session by :oauth_token key
|
||||
# Gets token string from conn (in params / headers / session)
|
||||
#
|
||||
@spec fetch_token_from_session(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
|
||||
defp fetch_token_from_session(conn) do
|
||||
case get_session(conn, :oauth_token) do
|
||||
nil -> :no_token_found
|
||||
token -> {:ok, token}
|
||||
end
|
||||
@spec fetch_token_str(Plug.Conn.t() | list(String.t())) :: :no_token_found | {:ok, String.t()}
|
||||
defp fetch_token_str(%Plug.Conn{params: %{"access_token" => access_token}} = _conn) do
|
||||
{:ok, access_token}
|
||||
end
|
||||
|
||||
# Gets token from headers
|
||||
#
|
||||
@spec fetch_token_str(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
|
||||
defp fetch_token_str(%Plug.Conn{} = conn) do
|
||||
headers = get_req_header(conn, "authorization")
|
||||
|
||||
with :no_token_found <- fetch_token_str(headers),
|
||||
do: fetch_token_from_session(conn)
|
||||
with {:ok, token} <- fetch_token_str(headers) do
|
||||
{:ok, token}
|
||||
else
|
||||
_ -> fetch_token_from_session(conn)
|
||||
end
|
||||
end
|
||||
|
||||
@spec fetch_token_str(Keyword.t()) :: :no_token_found | {:ok, String.t()}
|
||||
defp fetch_token_str([]), do: :no_token_found
|
||||
|
||||
defp fetch_token_str([token | tail]) do
|
||||
trimmed_token = String.trim(token)
|
||||
|
||||
|
@ -117,4 +94,14 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
|
|||
_ -> fetch_token_str(tail)
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_token_str([]), do: :no_token_found
|
||||
|
||||
@spec fetch_token_from_session(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
|
||||
defp fetch_token_from_session(conn) do
|
||||
case AuthHelper.get_session_token(conn) do
|
||||
nil -> :no_token_found
|
||||
token -> {:ok, token}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.Plugs.OAuthScopesPlug do
|
|||
import Pleroma.Web.Gettext
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Helpers.AuthHelper
|
||||
|
||||
use Pleroma.Web, :plug
|
||||
|
||||
|
@ -28,7 +29,7 @@ defmodule Pleroma.Web.Plugs.OAuthScopesPlug do
|
|||
conn
|
||||
|
||||
options[:fallback] == :proceed_unauthenticated ->
|
||||
drop_auth_info(conn)
|
||||
AuthHelper.drop_auth_info(conn)
|
||||
|
||||
true ->
|
||||
missing_scopes = scopes -- matched_scopes
|
||||
|
@ -44,15 +45,6 @@ defmodule Pleroma.Web.Plugs.OAuthScopesPlug do
|
|||
end
|
||||
end
|
||||
|
||||
@doc "Drops authentication info from connection"
|
||||
def drop_auth_info(conn) do
|
||||
# To simplify debugging, setting a private variable on `conn` if auth info is dropped
|
||||
conn
|
||||
|> put_private(:authentication_ignored, true)
|
||||
|> assign(:user, nil)
|
||||
|> assign(:token, nil)
|
||||
end
|
||||
|
||||
@doc "Keeps those of `scopes` which are descendants of `supported_scopes`"
|
||||
def filter_descendants(scopes, supported_scopes) do
|
||||
Enum.filter(
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.SessionAuthenticationPlug do
|
||||
import Plug.Conn
|
||||
|
||||
def init(options) do
|
||||
options
|
||||
end
|
||||
|
||||
def call(conn, _) do
|
||||
with saved_user_id <- get_session(conn, :user_id),
|
||||
%{auth_user: %{id: ^saved_user_id}} <- conn.assigns do
|
||||
conn
|
||||
|> assign(:user, conn.assigns.auth_user)
|
||||
else
|
||||
_ -> conn
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,16 +3,15 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.SetUserSessionIdPlug do
|
||||
import Plug.Conn
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Helpers.AuthHelper
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
def init(opts) do
|
||||
opts
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{id: id}}} = conn, _) do
|
||||
conn
|
||||
|> put_session(:user_id, id)
|
||||
def call(%{assigns: %{token: %Token{} = oauth_token}} = conn, _) do
|
||||
AuthHelper.put_session_token(conn, oauth_token.token)
|
||||
end
|
||||
|
||||
def call(conn, _), do: conn
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.UserEnabledPlug do
|
||||
import Plug.Conn
|
||||
alias Pleroma.Helpers.AuthHelper
|
||||
alias Pleroma.User
|
||||
|
||||
def init(options) do
|
||||
|
@ -11,9 +11,10 @@ defmodule Pleroma.Web.Plugs.UserEnabledPlug do
|
|||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{} = user}} = conn, _) do
|
||||
case User.account_status(user) do
|
||||
:active -> conn
|
||||
_ -> assign(conn, :user, nil)
|
||||
if User.account_status(user) == :active do
|
||||
conn
|
||||
else
|
||||
AuthHelper.drop_auth_info(conn)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.UserFetcherPlug do
|
||||
@moduledoc """
|
||||
Assigns `:auth_user` basing on `:auth_credentials`.
|
||||
|
||||
NOTE: no checks are performed at this step, auth_credentials/username could be easily faked.
|
||||
"""
|
||||
|
||||
alias Pleroma.User
|
||||
import Plug.Conn
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ defmodule Pleroma.Web.Router do
|
|||
plug(:fetch_session)
|
||||
plug(Pleroma.Web.Plugs.OAuthPlug)
|
||||
plug(Pleroma.Web.Plugs.UserEnabledPlug)
|
||||
plug(Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug)
|
||||
end
|
||||
|
||||
pipeline :expect_authentication do
|
||||
|
@ -48,15 +49,13 @@ defmodule Pleroma.Web.Router do
|
|||
plug(Pleroma.Web.Plugs.OAuthPlug)
|
||||
plug(Pleroma.Web.Plugs.BasicAuthDecoderPlug)
|
||||
plug(Pleroma.Web.Plugs.UserFetcherPlug)
|
||||
plug(Pleroma.Web.Plugs.SessionAuthenticationPlug)
|
||||
plug(Pleroma.Web.Plugs.LegacyAuthenticationPlug)
|
||||
plug(Pleroma.Web.Plugs.AuthenticationPlug)
|
||||
end
|
||||
|
||||
pipeline :after_auth do
|
||||
plug(Pleroma.Web.Plugs.UserEnabledPlug)
|
||||
plug(Pleroma.Web.Plugs.SetUserSessionIdPlug)
|
||||
plug(Pleroma.Web.Plugs.EnsureUserKeyPlug)
|
||||
plug(Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug)
|
||||
end
|
||||
|
||||
pipeline :base_api do
|
||||
|
@ -100,7 +99,7 @@ defmodule Pleroma.Web.Router do
|
|||
pipeline :pleroma_html do
|
||||
plug(:browser)
|
||||
plug(:authenticate)
|
||||
plug(Pleroma.Web.Plugs.EnsureUserKeyPlug)
|
||||
plug(Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug)
|
||||
end
|
||||
|
||||
pipeline :well_known do
|
||||
|
@ -292,7 +291,6 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
post("/main/ostatus", UtilController, :remote_subscribe)
|
||||
get("/ostatus_subscribe", RemoteFollowController, :follow)
|
||||
|
||||
post("/ostatus_subscribe", RemoteFollowController, :do_follow)
|
||||
end
|
||||
|
||||
|
@ -321,20 +319,26 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
|
||||
scope "/oauth", Pleroma.Web.OAuth do
|
||||
scope [] do
|
||||
pipe_through(:oauth)
|
||||
get("/authorize", OAuthController, :authorize)
|
||||
end
|
||||
|
||||
post("/authorize", OAuthController, :create_authorization)
|
||||
post("/token", OAuthController, :token_exchange)
|
||||
post("/revoke", OAuthController, :token_revoke)
|
||||
get("/registration_details", OAuthController, :registration_details)
|
||||
|
||||
post("/mfa/challenge", MFAController, :challenge)
|
||||
post("/mfa/verify", MFAController, :verify, as: :mfa_verify)
|
||||
get("/mfa", MFAController, :show)
|
||||
|
||||
scope [] do
|
||||
pipe_through(:oauth)
|
||||
|
||||
get("/authorize", OAuthController, :authorize)
|
||||
post("/authorize", OAuthController, :create_authorization)
|
||||
end
|
||||
|
||||
scope [] do
|
||||
pipe_through(:fetch_session)
|
||||
|
||||
post("/token", OAuthController, :token_exchange)
|
||||
post("/revoke", OAuthController, :token_revoke)
|
||||
post("/mfa/challenge", MFAController, :challenge)
|
||||
end
|
||||
|
||||
scope [] do
|
||||
pipe_through(:browser)
|
||||
|
||||
|
|
|
@ -36,9 +36,8 @@ defmodule Pleroma.Web.Streamer do
|
|||
) ::
|
||||
{:ok, topic :: String.t()} | {:error, :bad_topic} | {:error, :unauthorized}
|
||||
def get_topic_and_add_socket(stream, user, oauth_token, params \\ %{}) do
|
||||
case get_topic(stream, user, oauth_token, params) do
|
||||
{:ok, topic} -> add_socket(topic, user)
|
||||
error -> error
|
||||
with {:ok, topic} <- get_topic(stream, user, oauth_token, params) do
|
||||
add_socket(topic, user)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -70,10 +69,10 @@ defmodule Pleroma.Web.Streamer do
|
|||
def get_topic(
|
||||
stream,
|
||||
%User{id: user_id} = user,
|
||||
%Token{user_id: token_user_id} = oauth_token,
|
||||
%Token{user_id: user_id} = oauth_token,
|
||||
_params
|
||||
)
|
||||
when stream in @user_streams and user_id == token_user_id do
|
||||
when stream in @user_streams do
|
||||
# Note: "read" works for all user streams (not mentioning it since it's an ancestor scope)
|
||||
required_scopes =
|
||||
if stream == "user:notification" do
|
||||
|
@ -97,10 +96,9 @@ defmodule Pleroma.Web.Streamer do
|
|||
def get_topic(
|
||||
"list",
|
||||
%User{id: user_id} = user,
|
||||
%Token{user_id: token_user_id} = oauth_token,
|
||||
%Token{user_id: user_id} = oauth_token,
|
||||
%{"list" => id}
|
||||
)
|
||||
when user_id == token_user_id do
|
||||
) do
|
||||
cond do
|
||||
OAuthScopesPlug.filter_descendants(["read", "read:lists"], oauth_token.scopes) == [] ->
|
||||
{:error, :unauthorized}
|
||||
|
@ -137,16 +135,10 @@ defmodule Pleroma.Web.Streamer do
|
|||
|
||||
def stream(topics, items) do
|
||||
if should_env_send?() do
|
||||
List.wrap(topics)
|
||||
|> Enum.each(fn topic ->
|
||||
List.wrap(items)
|
||||
|> Enum.each(fn item ->
|
||||
spawn(fn -> do_stream(topic, item) end)
|
||||
end)
|
||||
end)
|
||||
for topic <- List.wrap(topics), item <- List.wrap(items) do
|
||||
spawn(fn -> do_stream(topic, item) end)
|
||||
end
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def filtered_by_user?(user, item, streamed_type \\ :activity)
|
||||
|
@ -160,8 +152,7 @@ defmodule Pleroma.Web.Streamer do
|
|||
domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks)
|
||||
|
||||
with parent <- Object.normalize(item) || item,
|
||||
true <-
|
||||
Enum.all?([blocked_ap_ids, muted_ap_ids], &(item.actor not in &1)),
|
||||
true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(item.actor not in &1)),
|
||||
true <- item.data["type"] != "Announce" || item.actor not in reblog_muted_ap_ids,
|
||||
true <-
|
||||
!(streamed_type == :activity && item.data["type"] == "Announce" &&
|
||||
|
@ -195,6 +186,19 @@ defmodule Pleroma.Web.Streamer do
|
|||
end)
|
||||
end
|
||||
|
||||
defp do_stream("follow_relationship", item) do
|
||||
text = StreamerView.render("follow_relationships_update.json", item)
|
||||
user_topic = "user:#{item.follower.id}"
|
||||
|
||||
Logger.debug("Trying to push follow relationship update to #{user_topic}\n\n")
|
||||
|
||||
Registry.dispatch(@registry, user_topic, fn list ->
|
||||
Enum.each(list, fn {pid, _auth} ->
|
||||
send(pid, {:text, text})
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
defp do_stream("participation", participation) do
|
||||
user_topic = "direct:#{participation.user_id}"
|
||||
Logger.debug("Trying to push a conversation participation to #{user_topic}\n\n")
|
||||
|
|
|
@ -126,7 +126,7 @@
|
|||
<div align="center" class="img-container center"
|
||||
style="padding-right: 0px;padding-left: 0px;">
|
||||
<!--[if mso]><table width="100%" cellpadding="0" cellspacing="0" border="0"><tr style="line-height:0px"><td style="padding-right: 0px;padding-left: 0px;" align="center"><![endif]--><img
|
||||
align="center" alt="Image" border="0" class="center" src="cid:logo.png"
|
||||
align="center" alt="Image" border="0" class="center" src="cid:logo.svg"
|
||||
style="text-decoration: none; -ms-interpolation-mode: bicubic; border: 0; height: 80px; width: auto; max-height: 80px; display: block;"
|
||||
title="Image" height="80" />
|
||||
<!--[if mso]></td></tr></table><![endif]-->
|
||||
|
|
|
@ -1,233 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui" />
|
||||
<title>
|
||||
<%= Pleroma.Config.get([:instance, :name]) %>
|
||||
</title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #121a24;
|
||||
font-family: sans-serif;
|
||||
color: #b9b9ba;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 420px;
|
||||
padding: 20px;
|
||||
background-color: #182230;
|
||||
border-radius: 4px;
|
||||
margin: auto;
|
||||
margin-top: 10vh;
|
||||
box-shadow: 0 1px 4px 0px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: #b9b9ba;
|
||||
font-weight: normal;
|
||||
font-size: 18px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #d8a070;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.input {
|
||||
text-align: left;
|
||||
color: #89898a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
input {
|
||||
box-sizing: content-box;
|
||||
padding: 10px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
background-color: #121a24;
|
||||
color: #b9b9ba;
|
||||
border: 0;
|
||||
transition-property: border-bottom;
|
||||
transition-duration: 0.35s;
|
||||
border-bottom: 2px solid #2a384a;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.scopes-input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: 1em;
|
||||
text-align: left;
|
||||
color: #89898a;
|
||||
}
|
||||
|
||||
.scopes-input label:first-child {
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
.scopes {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
text-align: left;
|
||||
color: #b9b9ba;
|
||||
}
|
||||
|
||||
.scope {
|
||||
display: flex;
|
||||
flex-basis: 100%;
|
||||
height: 2em;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.scope:before {
|
||||
color: #b9b9ba;
|
||||
content: "✔\fe0e";
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
[type="checkbox"] + label {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[type="checkbox"] + label:before {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
color: white;
|
||||
background-color: #121a24;
|
||||
border: 4px solid #121a24;
|
||||
box-shadow: 0px 0px 1px 0 #d8a070;
|
||||
box-sizing: border-box;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
margin-right: 1.0em;
|
||||
content: "";
|
||||
transition-property: background-color;
|
||||
transition-duration: 0.35s;
|
||||
color: #121a24;
|
||||
margin-bottom: -0.2em;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
[type="checkbox"]:checked + label:before {
|
||||
background-color: #d8a070;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
border-bottom: 2px solid #d8a070;
|
||||
}
|
||||
|
||||
button {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
background-color: #1c2a3a;
|
||||
color: #b9b9ba;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
text-transform: uppercase;
|
||||
font-size: 16px;
|
||||
box-shadow: 0px 0px 2px 0px black,
|
||||
0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset,
|
||||
0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 0px 0px 1px #d8a070,
|
||||
0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset,
|
||||
0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
background-color: #931014;
|
||||
border: 1px solid #a06060;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #7d796a;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@media all and (max-width: 440px) {
|
||||
.container {
|
||||
margin-top: 0
|
||||
}
|
||||
|
||||
.scope {
|
||||
flex-basis: 0%;
|
||||
}
|
||||
|
||||
.scope:before {
|
||||
content: "";
|
||||
margin-left: 0em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.scope:first-child:before {
|
||||
margin-left: 1em;
|
||||
content: "✔\fe0e";
|
||||
}
|
||||
|
||||
.scope:after {
|
||||
content: ",";
|
||||
}
|
||||
|
||||
.scope:last-child:after {
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
.form-row {
|
||||
display: flex;
|
||||
}
|
||||
.form-row > label {
|
||||
text-align: left;
|
||||
line-height: 47px;
|
||||
flex: 1;
|
||||
}
|
||||
.form-row > input {
|
||||
flex: 2;
|
||||
}
|
||||
</style>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,minimal-ui">
|
||||
<title><%= Pleroma.Config.get([:instance, :name]) %></title>
|
||||
<link rel="stylesheet" href="/instance/static.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="instance-header">
|
||||
<a class="instance-header__content" href="/">
|
||||
<img class="instance-header__thumbnail" src="<%= Pleroma.Config.get([:instance, :instance_thumbnail]) %>">
|
||||
<h1 class="instance-header__title"><%= Pleroma.Config.get([:instance, :name]) %></h1>
|
||||
</a>
|
||||
</div>
|
||||
<div class="container">
|
||||
<h1><%= Pleroma.Config.get([:instance, :name]) %></h1>
|
||||
<%= @inner_content %>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -5,32 +5,55 @@
|
|||
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
|
||||
<% end %>
|
||||
|
||||
<h2>OAuth Authorization</h2>
|
||||
<%= form_for @conn, o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %>
|
||||
|
||||
<%= if @params["registration"] in ["true", true] do %>
|
||||
<h3>This is the first time you visit! Please enter your Pleroma handle.</h3>
|
||||
<p>Choose carefully! You won't be able to change this later. You will be able to change your display name, though.</p>
|
||||
<div class="input">
|
||||
<%= label f, :nickname, "Pleroma Handle" %>
|
||||
<%= text_input f, :nickname, placeholder: "lain" %>
|
||||
<%= if @user do %>
|
||||
<div class="account-header">
|
||||
<div class="account-header__banner" style="background-image: url('<%= Pleroma.User.banner_url(@user) %>')"></div>
|
||||
<div class="account-header__avatar" style="background-image: url('<%= Pleroma.User.avatar_url(@user) %>')"></div>
|
||||
<div class="account-header__meta">
|
||||
<div class="account-header__display-name"><%= @user.name %></div>
|
||||
<div class="account-header__nickname">@<%= @user.nickname %>@<%= Pleroma.User.get_host(@user) %></div>
|
||||
</div>
|
||||
</div>
|
||||
<%= hidden_input f, :name, value: @params["name"] %>
|
||||
<%= hidden_input f, :password, value: @params["password"] %>
|
||||
<br>
|
||||
<% else %>
|
||||
<div class="input">
|
||||
<%= label f, :name, "Username" %>
|
||||
<%= text_input f, :name %>
|
||||
</div>
|
||||
<div class="input">
|
||||
<%= label f, :password, "Password" %>
|
||||
<%= password_input f, :password %>
|
||||
</div>
|
||||
<%= submit "Log In" %>
|
||||
<%= render @view_module, "_scopes.html", Map.merge(assigns, %{form: f}) %>
|
||||
<% end %>
|
||||
|
||||
<div class="container__content">
|
||||
<%= if @app do %>
|
||||
<p>Application <strong><%= @app.client_name %></strong> is requesting access to your account.</p>
|
||||
<%= render @view_module, "_scopes.html", Map.merge(assigns, %{form: f}) %>
|
||||
<% end %>
|
||||
|
||||
<%= if @user do %>
|
||||
<div class="actions">
|
||||
<a class="button button--cancel" href="/">Cancel</a>
|
||||
<%= submit "Approve", class: "button--approve" %>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= if @params["registration"] in ["true", true] do %>
|
||||
<h3>This is the first time you visit! Please enter your Pleroma handle.</h3>
|
||||
<p>Choose carefully! You won't be able to change this later. You will be able to change your display name, though.</p>
|
||||
<div class="input">
|
||||
<%= label f, :nickname, "Pleroma Handle" %>
|
||||
<%= text_input f, :nickname, placeholder: "lain" %>
|
||||
</div>
|
||||
<%= hidden_input f, :name, value: @params["name"] %>
|
||||
<%= hidden_input f, :password, value: @params["password"] %>
|
||||
<br>
|
||||
<% else %>
|
||||
<div class="input">
|
||||
<%= label f, :name, "Username" %>
|
||||
<%= text_input f, :name %>
|
||||
</div>
|
||||
<div class="input">
|
||||
<%= label f, :password, "Password" %>
|
||||
<%= password_input f, :password %>
|
||||
</div>
|
||||
<%= submit "Log In" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= hidden_input f, :client_id, value: @client_id %>
|
||||
<%= hidden_input f, :response_type, value: @response_type %>
|
||||
<%= hidden_input f, :redirect_uri, value: @redirect_uri %>
|
||||
|
@ -40,4 +63,3 @@
|
|||
<%= if Pleroma.Config.oauth_consumer_enabled?() do %>
|
||||
<%= render @view_module, Pleroma.Web.Auth.Authenticator.oauth_consumer_template(), assigns %>
|
||||
<% end %>
|
||||
|
||||
|
|
|
@ -31,10 +31,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
|
||||
with %User{} = user <- User.get_cached_by_id(uid),
|
||||
true <- user.local and user.confirmation_pending and user.confirmation_token == token,
|
||||
{:ok, _} <-
|
||||
user
|
||||
|> User.confirmation_changeset(need_confirmation: false)
|
||||
|> User.update_and_set_cache() do
|
||||
{:ok, _} <- User.confirm(user) do
|
||||
redirect(conn, to: "/")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,7 +45,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
|
||||
case User.register(changeset) do
|
||||
{:ok, user} ->
|
||||
maybe_notify_admins(user)
|
||||
{:ok, user}
|
||||
|
||||
{:error, changeset} ->
|
||||
|
@ -58,18 +57,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
end
|
||||
end
|
||||
|
||||
defp maybe_notify_admins(%User{} = account) do
|
||||
if Pleroma.Config.get([:instance, :account_approval_required]) do
|
||||
User.all_superusers()
|
||||
|> Enum.filter(fn user -> not is_nil(user.email) end)
|
||||
|> Enum.each(fn superuser ->
|
||||
superuser
|
||||
|> Pleroma.Emails.AdminEmail.new_unapproved_registration(account)
|
||||
|> Pleroma.Emails.Mailer.deliver_async()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
def password_reset(nickname_or_email) do
|
||||
with true <- is_binary(nickname_or_email),
|
||||
%User{local: true, email: email, deactivated: false} = user when is_binary(email) <-
|
||||
|
|
|
@ -74,6 +74,28 @@ defmodule Pleroma.Web.StreamerView do
|
|||
|> Jason.encode!()
|
||||
end
|
||||
|
||||
def render("follow_relationships_update.json", item) do
|
||||
%{
|
||||
event: "pleroma:follow_relationships_update",
|
||||
payload:
|
||||
%{
|
||||
state: item.state,
|
||||
follower: %{
|
||||
id: item.follower.id,
|
||||
follower_count: item.follower.follower_count,
|
||||
following_count: item.follower.following_count
|
||||
},
|
||||
following: %{
|
||||
id: item.following.id,
|
||||
follower_count: item.following.follower_count,
|
||||
following_count: item.following.following_count
|
||||
}
|
||||
}
|
||||
|> Jason.encode!()
|
||||
}
|
||||
|> Jason.encode!()
|
||||
end
|
||||
|
||||
def render("conversation.json", %Participation{} = participation) do
|
||||
%{
|
||||
event: "conversation",
|
||||
|
|
10
mix.exs
10
mix.exs
|
@ -22,7 +22,7 @@ defmodule Pleroma.Mixfile do
|
|||
docs: [
|
||||
source_url_pattern:
|
||||
"https://git.pleroma.social/pleroma/pleroma/blob/develop/%{path}#L%{line}",
|
||||
logo: "priv/static/static/logo.png",
|
||||
logo: "priv/static/images/logo.png",
|
||||
extras: ["README.md", "CHANGELOG.md"] ++ Path.wildcard("docs/**/*.md"),
|
||||
groups_for_extras: [
|
||||
"Installation manuals": Path.wildcard("docs/installation/*.md"),
|
||||
|
@ -194,7 +194,8 @@ defmodule Pleroma.Mixfile do
|
|||
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
|
||||
{:restarter, path: "./restarter"},
|
||||
{:majic,
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", branch: "develop"},
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/majic.git",
|
||||
ref: "4c692e544b28d1f5e543fb8a44be090f8cd96f80"},
|
||||
{:open_api_spex,
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git",
|
||||
ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"},
|
||||
|
@ -206,7 +207,10 @@ defmodule Pleroma.Mixfile do
|
|||
{:mock, "~> 0.3.5", only: :test},
|
||||
# temporary downgrade for excoveralls, hackney until hackney max_connections bug will be fixed
|
||||
{:excoveralls, "0.12.3", only: :test},
|
||||
{:hackney, "1.15.2", override: true},
|
||||
{:hackney,
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/hackney.git",
|
||||
ref: "7d7119f0651515d6d7669c78393fd90950a3ec6e",
|
||||
override: true},
|
||||
{:mox, "~> 0.5", only: :test},
|
||||
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test}
|
||||
] ++ oauth_deps()
|
||||
|
|
18
mix.lock
18
mix.lock
|
@ -11,7 +11,7 @@
|
|||
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
|
||||
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]},
|
||||
"castore": {:hex, :castore, "0.1.7", "1ca19eee705cde48c9e809e37fdd0730510752cc397745e550f6065a56a701e9", [:mix], [], "hexpm", "a2ae2c13d40e9c308387f1aceb14786dca019ebc2a11484fb2a9f797ea0aa0d8"},
|
||||
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"},
|
||||
"certifi": {:git, "https://github.com/certifi/erlang-certifi", "e08b12e8993502240c25b78563993776f87ecd2a", [tag: "2.5.1"]},
|
||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||
"comeonin": {:hex, :comeonin, "5.3.1", "7fe612b739c78c9c1a75186ef2d322ce4d25032d119823269d0aa1e2f1e20025", [:mix], [], "hexpm", "d6222483060c17f0977fad1b7401ef0c5863c985a64352755f366aee3799c245"},
|
||||
"concurrent_limiter": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/concurrent_limiter.git", "d81be41024569330f296fc472e24198d7499ba78", [ref: "d81be41024569330f296fc472e24198d7499ba78"]},
|
||||
|
@ -53,12 +53,12 @@
|
|||
"gen_state_machine": {:hex, :gen_state_machine, "2.0.5", "9ac15ec6e66acac994cc442dcc2c6f9796cf380ec4b08267223014be1c728a95", [:mix], [], "hexpm"},
|
||||
"gettext": {:hex, :gettext, "0.18.0", "406d6b9e0e3278162c2ae1de0a60270452c553536772167e2d701f028116f870", [:mix], [], "hexpm", "c3f850be6367ebe1a08616c2158affe4a23231c70391050bf359d5f92f66a571"},
|
||||
"gun": {:git, "https://github.com/ninenines/gun.git", "921c47146b2d9567eac7e9a4d2ccc60fffd4f327", [ref: "921c47146b2d9567eac7e9a4d2ccc60fffd4f327"]},
|
||||
"hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e0100f8ef7d1124222c11ad362c857d3df7cb5f4204054f9f0f4a728666591fc"},
|
||||
"hackney": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/hackney.git", "7d7119f0651515d6d7669c78393fd90950a3ec6e", [ref: "7d7119f0651515d6d7669c78393fd90950a3ec6e"]},
|
||||
"html_entities": {:hex, :html_entities, "0.5.1", "1c9715058b42c35a2ab65edc5b36d0ea66dd083767bef6e3edb57870ef556549", [:mix], [], "hexpm", "30efab070904eb897ff05cd52fa61c1025d7f8ef3a9ca250bc4e6513d16c32de"},
|
||||
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.3.0", "f005ad692b717691203f940c686208aa3d8ffd9dd4bb3699240096a51fa9564e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"http_signatures": {:hex, :http_signatures, "0.1.0", "4e4b501a936dbf4cb5222597038a89ea10781776770d2e185849fa829686b34c", [:mix], [], "hexpm", "f8a7b3731e3fd17d38fa6e343fcad7b03d6874a3b0a108c8568a71ed9c2cf824"},
|
||||
"httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"},
|
||||
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"},
|
||||
"idna": {:git, "https://github.com/benoitc/erlang-idna", "6cff72747821110169ecfac871b0c69e5064afff", [tag: "6.0.0"]},
|
||||
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"},
|
||||
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
|
||||
"joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"},
|
||||
|
@ -66,13 +66,13 @@
|
|||
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
|
||||
"libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
|
||||
"linkify": {:hex, :linkify, "0.4.0", "7845b6ac33050a41acaf9318923ce6e7f3854418be9a5f22184de103f7a68ff9", [:mix], [], "hexpm", "a0ceb4c78591fecccf1d99fecc10c13dba75a307c663c80e28af9e2cdd9776ee"},
|
||||
"majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", "4c692e544b28d1f5e543fb8a44be090f8cd96f80", [branch: "develop"]},
|
||||
"majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", "4c692e544b28d1f5e543fb8a44be090f8cd96f80", [ref: "4c692e544b28d1f5e543fb8a44be090f8cd96f80"]},
|
||||
"makeup": {:hex, :makeup, "1.0.3", "e339e2f766d12e7260e6672dd4047405963c5ec99661abdc432e6ec67d29ef95", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "2e9b4996d11832947731f7608fed7ad2f9443011b3b479ae288011265cdd3dad"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
||||
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||
"metrics": {:git, "https://github.com/benoitc/erlang-metrics", "c6eb4dcf29f9e907539915e2ab996f40c2ec7e8e", [tag: "1.0.1"]},
|
||||
"mime": {:hex, :mime, "1.4.0", "5066f14944b470286146047d2f73518cf5cca82f8e4815cf35d196b58cf07c47", [:mix], [], "hexpm", "75fa42c4228ea9a23f70f123c74ba7cece6a03b1fd474fe13f6a7a85c6ea4ff6"},
|
||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||
"mimerl": {:git, "https://github.com/benoitc/mimerl", "5a1b22a8fada5b3b40438da00a6923cb87a42bbc", [tag: "1.2.0"]},
|
||||
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
|
||||
"mock": {:hex, :mock, "0.3.5", "feb81f52b8dcf0a0d65001d2fec459f6b6a8c22562d94a965862f6cc066b5431", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "6fae404799408300f863550392635d8f7e3da6b71abdd5c393faf41b131c8728"},
|
||||
"mogrify": {:hex, :mogrify, "0.7.4", "9b2496dde44b1ce12676f85d7dc531900939e6367bc537c7243a1b089435b32d", [:mix], [], "hexpm", "50d79e337fba6bc95bfbef918058c90f50b17eed9537771e61d4619488f099c3"},
|
||||
|
@ -84,7 +84,7 @@
|
|||
"oban": {:hex, :oban, "2.1.0", "034144686f7e76a102b5d67731f098d98a9e4a52b07c25ad580a01f83a7f1cf5", [:mix], [{:ecto_sql, ">= 3.4.3", [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", "c6f067fa3b308ed9e0e6beb2b34277c9c4e48bf95338edabd8f4a757a26e04c2"},
|
||||
"open_api_spex": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git", "f296ac0924ba3cf79c7a588c4c252889df4c2edd", [ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"]},
|
||||
"p1_utils": {:hex, :p1_utils, "1.0.18", "3fe224de5b2e190d730a3c5da9d6e8540c96484cf4b4692921d1e28f0c32b01c", [:rebar3], [], "hexpm", "1fc8773a71a15553b179c986b22fbeead19b28fe486c332d4929700ffeb71f88"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
|
||||
"parse_trans": {:git, "https://github.com/uwiger/parse_trans.git", "76abb347c3c1d00fb0ccf9e4b43e22b3d2288484", [tag: "3.3.0"]},
|
||||
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "1.2.1", "9cbe354b58121075bd20eb83076900a3832324b7dd171a6895fab57b6bb2752c", [:mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}], "hexpm", "d3b40a4a4630f0b442f19eca891fcfeeee4c40871936fed2f68e1c4faa30481f"},
|
||||
"phoenix": {:hex, :phoenix, "1.5.6", "8298cdb4e0f943242ba8410780a6a69cbbe972fef199b341a36898dd751bdd66", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "0dc4d39af1306b6aa5122729b0a95ca779e42c708c6fe7abbb3d336d5379e956"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"},
|
||||
|
@ -110,7 +110,7 @@
|
|||
"recon": {:hex, :recon, "2.5.1", "430ffa60685ac1efdfb1fe4c97b8767c92d0d92e6e7c3e8621559ba77598678a", [:mix, :rebar3], [], "hexpm", "5721c6b6d50122d8f68cccac712caa1231f97894bab779eff5ff0f886cb44648"},
|
||||
"remote_ip": {:git, "https://git.pleroma.social/pleroma/remote_ip.git", "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8", [ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"]},
|
||||
"sleeplocks": {:hex, :sleeplocks, "1.1.1", "3d462a0639a6ef36cc75d6038b7393ae537ab394641beb59830a1b8271faeed3", [:rebar3], [], "hexpm", "84ee37aeff4d0d92b290fff986d6a95ac5eedf9b383fadfd1d88e9b84a1c02e1"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"},
|
||||
"ssl_verify_fun": {:git, "https://github.com/deadtrickster/ssl_verify_fun.erl", "c5718226b0b9f3d1a38ef6ca3c3b4c75f53dda92", [tag: "1.1.4"]},
|
||||
"sweet_xml": {:hex, :sweet_xml, "0.6.6", "fc3e91ec5dd7c787b6195757fbcf0abc670cee1e4172687b45183032221b66b8", [:mix], [], "hexpm", "2e1ec458f892ffa81f9f8386e3f35a1af6db7a7a37748a64478f13163a1f3573"},
|
||||
"swoosh": {:hex, :swoosh, "1.0.6", "6765e334c67dacabe721f0d701c7e5a6f06e4595c90df6f91e73ebd54d555833", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {: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", "7c50ef78e4acfd1cbd4907dc1fa87b5540675a6be9dc979d04890f49d7ec1830"},
|
||||
"syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
|
||||
|
@ -120,7 +120,7 @@
|
|||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
||||
"tzdata": {:hex, :tzdata, "1.0.4", "a3baa4709ea8dba552dca165af6ae97c624a2d6ac14bd265165eaa8e8af94af6", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "b02637db3df1fd66dd2d3c4f194a81633d0e4b44308d36c1b2fdfd1e4e6f169b"},
|
||||
"ueberauth": {:hex, :ueberauth, "0.6.3", "d42ace28b870e8072cf30e32e385579c57b9cc96ec74fa1f30f30da9c14f3cc0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "afc293d8a1140d6591b53e3eaf415ca92842cb1d32fad3c450c6f045f7f91b60"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"},
|
||||
"unicode_util_compat": {:git, "https://github.com/benoitc/unicode_util_compat.git", "38d7bc105f51159e8ea3279c40121db9db1e652f", [tag: "0.3.1"]},
|
||||
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},
|
||||
"web_push_encryption": {:hex, :web_push_encryption, "0.3.0", "598b5135e696fd1404dc8d0d7c0fa2c027244a4e5d5e5a98ba267f14fdeaabc8", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "f10bdd1afe527ede694749fb77a2f22f146a51b054c7fa541c9fd920fba7c875"},
|
||||
"websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []},
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
## This file is a PO Template file.
|
||||
msgid "eperm"
|
||||
msgstr "Operation not permitted"
|
||||
|
||||
msgid "eacces"
|
||||
msgstr "Permission denied"
|
||||
|
||||
msgid "eagain"
|
||||
msgstr "Resource temporarily unavailable"
|
||||
|
||||
msgid "ebadf"
|
||||
msgstr "Bad file descriptor"
|
||||
|
||||
msgid "ebadmsg"
|
||||
msgstr "Bad message"
|
||||
|
||||
msgid "ebusy"
|
||||
msgstr "Device or resource busy"
|
||||
|
||||
msgid "edeadlk"
|
||||
msgstr "Resource deadlock avoided"
|
||||
|
||||
msgid "edeadlock"
|
||||
msgstr "Resource deadlock avoided"
|
||||
|
||||
msgid "edquot"
|
||||
msgstr "Disk quota exceeded"
|
||||
|
||||
msgid "eexist"
|
||||
msgstr "File exists"
|
||||
|
||||
msgid "efault"
|
||||
msgstr "Bad address"
|
||||
|
||||
msgid "efbig"
|
||||
msgstr "File is too large"
|
||||
|
||||
msgid "eftype"
|
||||
msgstr "Inappropriate file type or format"
|
||||
|
||||
msgid "eintr"
|
||||
msgstr "Interrupted system call"
|
||||
|
||||
msgid "einval"
|
||||
msgstr "Invalid argument"
|
||||
|
||||
msgid "eio"
|
||||
msgstr "Input/output error"
|
||||
|
||||
msgid "eisdir"
|
||||
msgstr "Illegal operation on a directory"
|
||||
|
||||
msgid "eloop"
|
||||
msgstr "Too many levels of symbolic links"
|
||||
|
||||
msgid "emfile"
|
||||
msgstr "Too many open files"
|
||||
|
||||
msgid "emlink"
|
||||
msgstr "Too many links"
|
||||
|
||||
msgid "emultihop"
|
||||
msgstr "Multihop attempted"
|
||||
|
||||
msgid "enametoolong"
|
||||
msgstr "File name is too long"
|
||||
|
||||
msgid "enfile"
|
||||
msgstr "Too many open files in system"
|
||||
|
||||
msgid "enobufs"
|
||||
msgstr "No buffer space available"
|
||||
|
||||
msgid "enodev"
|
||||
msgstr "No such device"
|
||||
|
||||
msgid "enolck"
|
||||
msgstr "No locks available"
|
||||
|
||||
msgid "enolink"
|
||||
msgstr "Link has been severed"
|
||||
|
||||
msgid "enoent"
|
||||
msgstr "No such file or directory"
|
||||
|
||||
msgid "enomem"
|
||||
msgstr "Cannot allocate memory"
|
||||
|
||||
msgid "enospc"
|
||||
msgstr "No space left on device"
|
||||
|
||||
msgid "enosr"
|
||||
msgstr "Out of streams resources"
|
||||
|
||||
msgid "enostr"
|
||||
msgstr "Device is not a stream"
|
||||
|
||||
msgid "enosys"
|
||||
msgstr "Function not implemented"
|
||||
|
||||
msgid "enotblk"
|
||||
msgstr "Block device required"
|
||||
|
||||
msgid "enotdir"
|
||||
msgstr "Not a directory"
|
||||
|
||||
msgid "enotsup"
|
||||
msgstr "Operation not supported"
|
||||
|
||||
msgid "enxio"
|
||||
msgstr "No such device or address"
|
||||
|
||||
msgid "eopnotsupp"
|
||||
msgstr "Operation not supported"
|
||||
|
||||
msgid "eoverflow"
|
||||
msgstr "Value too large for defined data type"
|
||||
|
||||
msgid "epipe"
|
||||
msgstr "Broken pipe"
|
||||
|
||||
msgid "erange"
|
||||
msgstr "Numerical result out of range"
|
||||
|
||||
msgid "erofs"
|
||||
msgstr "Read-only file system"
|
||||
|
||||
msgid "espipe"
|
||||
msgstr "Illegal seek"
|
||||
|
||||
msgid "esrch"
|
||||
msgstr "No such process"
|
||||
|
||||
msgid "estale"
|
||||
msgstr "Stale file handle"
|
||||
|
||||
msgid "etxtbsy"
|
||||
msgstr "Text file busy"
|
||||
|
||||
msgid "exdev"
|
||||
msgstr "Invalid cross-device link"
|
|
@ -0,0 +1,149 @@
|
|||
## This file is a PO Template file.
|
||||
##
|
||||
## `msgid`s here are often extracted from source code.
|
||||
## Add new translations manually only if they're dynamic
|
||||
## translations that can't be statically extracted.
|
||||
##
|
||||
## Run `mix gettext.extract` to bring this file up to
|
||||
## date. Leave `msgstr`s empty as changing them here as no
|
||||
## effect: edit them in PO (`.po`) files instead.
|
||||
msgid "eperm"
|
||||
msgstr ""
|
||||
|
||||
msgid "eacces"
|
||||
msgstr ""
|
||||
|
||||
msgid "eagain"
|
||||
msgstr ""
|
||||
|
||||
msgid "ebadf"
|
||||
msgstr ""
|
||||
|
||||
msgid "ebadmsg"
|
||||
msgstr ""
|
||||
|
||||
msgid "ebusy"
|
||||
msgstr ""
|
||||
|
||||
msgid "edeadlk"
|
||||
msgstr ""
|
||||
|
||||
msgid "edeadlock"
|
||||
msgstr ""
|
||||
|
||||
msgid "edquot"
|
||||
msgstr ""
|
||||
|
||||
msgid "eexist"
|
||||
msgstr ""
|
||||
|
||||
msgid "efault"
|
||||
msgstr ""
|
||||
|
||||
msgid "efbig"
|
||||
msgstr ""
|
||||
|
||||
msgid "eftype"
|
||||
msgstr ""
|
||||
|
||||
msgid "eintr"
|
||||
msgstr ""
|
||||
|
||||
msgid "einval"
|
||||
msgstr ""
|
||||
|
||||
msgid "eio"
|
||||
msgstr ""
|
||||
|
||||
msgid "eisdir"
|
||||
msgstr ""
|
||||
|
||||
msgid "eloop"
|
||||
msgstr ""
|
||||
|
||||
msgid "emfile"
|
||||
msgstr ""
|
||||
|
||||
msgid "emlink"
|
||||
msgstr ""
|
||||
|
||||
msgid "emultihop"
|
||||
msgstr ""
|
||||
|
||||
msgid "enametoolong"
|
||||
msgstr ""
|
||||
|
||||
msgid "enfile"
|
||||
msgstr ""
|
||||
|
||||
msgid "enobufs"
|
||||
msgstr ""
|
||||
|
||||
msgid "enodev"
|
||||
msgstr ""
|
||||
|
||||
msgid "enolck"
|
||||
msgstr ""
|
||||
|
||||
msgid "enolink"
|
||||
msgstr ""
|
||||
|
||||
msgid "enoent"
|
||||
msgstr ""
|
||||
|
||||
msgid "enomem"
|
||||
msgstr ""
|
||||
|
||||
msgid "enospc"
|
||||
msgstr ""
|
||||
|
||||
msgid "enosr"
|
||||
msgstr ""
|
||||
|
||||
msgid "enostr"
|
||||
msgstr ""
|
||||
|
||||
msgid "enosys"
|
||||
msgstr ""
|
||||
|
||||
msgid "enotblk"
|
||||
msgstr ""
|
||||
|
||||
msgid "enotdir"
|
||||
msgstr ""
|
||||
|
||||
msgid "enotsup"
|
||||
msgstr ""
|
||||
|
||||
msgid "enxio"
|
||||
msgstr ""
|
||||
|
||||
msgid "eopnotsupp"
|
||||
msgstr ""
|
||||
|
||||
msgid "eoverflow"
|
||||
msgstr ""
|
||||
|
||||
msgid "epipe"
|
||||
msgstr ""
|
||||
|
||||
msgid "erange"
|
||||
msgstr ""
|
||||
|
||||
msgid "erofs"
|
||||
msgstr ""
|
||||
|
||||
msgid "espipe"
|
||||
msgstr ""
|
||||
|
||||
msgid "esrch"
|
||||
msgstr ""
|
||||
|
||||
msgid "estale"
|
||||
msgstr ""
|
||||
|
||||
msgid "etxtbsy"
|
||||
msgstr ""
|
||||
|
||||
msgid "exdev"
|
||||
msgstr ""
|
|
@ -0,0 +1,599 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-10 16:09+0000\n"
|
||||
"PO-Revision-Date: 2020-12-11 00:56+0000\n"
|
||||
"Last-Translator: ZEN <xinit.info@gmail.com>\n"
|
||||
"Language-Team: Ukrainian <https://translate.pleroma.social/projects/pleroma/"
|
||||
"pleroma/uk/>\n"
|
||||
"Language: uk\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<="
|
||||
"4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 4.0.4\n"
|
||||
|
||||
## This file is a PO Template file.
|
||||
##
|
||||
## `msgid`s here are often extracted from source code.
|
||||
## Add new translations manually only if they're dynamic
|
||||
## translations that can't be statically extracted.
|
||||
##
|
||||
## Run `mix gettext.extract` to bring this file up to
|
||||
## date. Leave `msgstr`s empty as changing them here as no
|
||||
## effect: edit them in PO (`.po`) files instead.
|
||||
## From Ecto.Changeset.cast/4
|
||||
msgid "can't be blank"
|
||||
msgstr "не може бути пустим"
|
||||
|
||||
## From Ecto.Changeset.unique_constraint/3
|
||||
msgid "has already been taken"
|
||||
msgstr "вже зайнято"
|
||||
|
||||
## From Ecto.Changeset.put_change/3
|
||||
msgid "is invalid"
|
||||
msgstr "недійсний"
|
||||
|
||||
## From Ecto.Changeset.validate_format/3
|
||||
msgid "has invalid format"
|
||||
msgstr "має недійсний формат"
|
||||
|
||||
## From Ecto.Changeset.validate_subset/3
|
||||
msgid "has an invalid entry"
|
||||
msgstr "має недійсний запис"
|
||||
|
||||
## From Ecto.Changeset.validate_exclusion/3
|
||||
msgid "is reserved"
|
||||
msgstr "зарезервовано"
|
||||
|
||||
## From Ecto.Changeset.validate_confirmation/3
|
||||
msgid "does not match confirmation"
|
||||
msgstr "не збігається з підтвердженням"
|
||||
|
||||
## From Ecto.Changeset.no_assoc_constraint/3
|
||||
msgid "is still associated with this entry"
|
||||
msgstr "все ще пов'язаний з цим записом"
|
||||
|
||||
msgid "are still associated with this entry"
|
||||
msgstr "все ще пов'язані з цим записом"
|
||||
|
||||
## From Ecto.Changeset.validate_length/3
|
||||
msgid "should be %{count} character(s)"
|
||||
msgid_plural "should be %{count} character(s)"
|
||||
msgstr[0] "повинен містити %{count} символ"
|
||||
msgstr[1] "повинен містити %{count} символи"
|
||||
msgstr[2] "повинен містити %{count} символів"
|
||||
|
||||
msgid "should have %{count} item(s)"
|
||||
msgid_plural "should have %{count} item(s)"
|
||||
msgstr[0] "повинен містити %{count} елемент"
|
||||
msgstr[1] "повинен містити %{count} елементи"
|
||||
msgstr[2] "повинен містити %{count} елементів"
|
||||
|
||||
msgid "should be at least %{count} character(s)"
|
||||
msgid_plural "should be at least %{count} character(s)"
|
||||
msgstr[0] "повинен містити хоча б %{count} символ"
|
||||
msgstr[1] "повинен містити хоча б %{count} символи"
|
||||
msgstr[2] "повинен містити хоча б %{count} символів"
|
||||
|
||||
msgid "should have at least %{count} item(s)"
|
||||
msgid_plural "should have at least %{count} item(s)"
|
||||
msgstr[0] "повинен містити хоча б %{count} елемент"
|
||||
msgstr[1] "повинен містити хоча б %{count} елементи"
|
||||
msgstr[2] "повинен містити хоча б %{count} елементів"
|
||||
|
||||
msgid "should be at most %{count} character(s)"
|
||||
msgid_plural "should be at most %{count} character(s)"
|
||||
msgstr[0] "повинен бути не більше %{count} символу"
|
||||
msgstr[1] "повинен бути не більше %{count} символів"
|
||||
msgstr[2] "повинен бути не більше %{count} символів"
|
||||
|
||||
msgid "should have at most %{count} item(s)"
|
||||
msgid_plural "should have at most %{count} item(s)"
|
||||
msgstr[0] "повинен містити не більше %{count} елемента"
|
||||
msgstr[1] "повинен містити не більше %{count} елементів"
|
||||
msgstr[2] "повинен містити не більше %{count} елементів"
|
||||
|
||||
## From Ecto.Changeset.validate_number/3
|
||||
msgid "must be less than %{number}"
|
||||
msgstr "повинен мати значення менше ніж %{number}"
|
||||
|
||||
msgid "must be greater than %{number}"
|
||||
msgstr "повинен мати значення більше ніж %{number}"
|
||||
|
||||
msgid "must be less than or equal to %{number}"
|
||||
msgstr "повинен мати значення менше або рівне %{number}"
|
||||
|
||||
msgid "must be greater than or equal to %{number}"
|
||||
msgstr "повинен мати значення більше або рівне %{number}"
|
||||
|
||||
msgid "must be equal to %{number}"
|
||||
msgstr "повинен мати лише значення, рівне %{number}"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:505
|
||||
#, elixir-format
|
||||
msgid "Account not found"
|
||||
msgstr "Обліковий запис не знайдено"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:339
|
||||
#, elixir-format
|
||||
msgid "Already voted"
|
||||
msgstr "Вже проголосовано"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:359
|
||||
#, elixir-format
|
||||
msgid "Bad request"
|
||||
msgstr "Невірний запит"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:426
|
||||
#, elixir-format
|
||||
msgid "Can't delete object"
|
||||
msgstr "Виникла помилка при видаленні об'єкту"
|
||||
|
||||
#: lib/pleroma/web/controller_helper.ex:105
|
||||
#: lib/pleroma/web/controller_helper.ex:111
|
||||
#, elixir-format
|
||||
msgid "Can't display this activity"
|
||||
msgstr "Не вдається відобразити цю активність"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:285
|
||||
#, elixir-format
|
||||
msgid "Can't find user"
|
||||
msgstr "Користувача не знайдено"
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:61
|
||||
#, elixir-format
|
||||
msgid "Can't get favorites"
|
||||
msgstr "Не вдається отримати вподобання"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:438
|
||||
#, elixir-format
|
||||
msgid "Can't like object"
|
||||
msgstr "Не вдається вподобати об’єкт"
|
||||
|
||||
#: lib/pleroma/web/common_api/utils.ex:563
|
||||
#, elixir-format
|
||||
msgid "Cannot post an empty status without attachments"
|
||||
msgstr "Не вдається опублікувати порожнє повідомлення без вкладень"
|
||||
|
||||
#: lib/pleroma/web/common_api/utils.ex:511
|
||||
#, elixir-format
|
||||
msgid "Comment must be up to %{max_size} characters"
|
||||
msgstr "Коментар може містити не більше %{max_size} символів"
|
||||
|
||||
#: lib/pleroma/config/config_db.ex:191
|
||||
#, elixir-format
|
||||
msgid "Config with params %{params} not found"
|
||||
msgstr "Конфігурація з параметрами %{params} не знайдена"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:181
|
||||
#: lib/pleroma/web/common_api/common_api.ex:185
|
||||
#, elixir-format
|
||||
msgid "Could not delete"
|
||||
msgstr "Не можу видалити"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:231
|
||||
#, elixir-format
|
||||
msgid "Could not favorite"
|
||||
msgstr "Не вдалося додати до вподобаного"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:453
|
||||
#, elixir-format
|
||||
msgid "Could not pin"
|
||||
msgstr "Не вдалося закріпити"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:278
|
||||
#, elixir-format
|
||||
msgid "Could not unfavorite"
|
||||
msgstr "Не вдалося видалити з вподобаного"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:463
|
||||
#, elixir-format
|
||||
msgid "Could not unpin"
|
||||
msgstr "Не вдалося відкріпити"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:216
|
||||
#, elixir-format
|
||||
msgid "Could not unrepeat"
|
||||
msgstr "Не вдалося скасувати поширення"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:512
|
||||
#: lib/pleroma/web/common_api/common_api.ex:521
|
||||
#, elixir-format
|
||||
msgid "Could not update state"
|
||||
msgstr "Не вдалося оновити стан"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:207
|
||||
#, elixir-format
|
||||
msgid "Error."
|
||||
msgstr "Помилка."
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:106
|
||||
#, elixir-format
|
||||
msgid "Invalid CAPTCHA"
|
||||
msgstr "Невірна CAPTCHA"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:116
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:568
|
||||
#, elixir-format
|
||||
msgid "Invalid credentials"
|
||||
msgstr "Неправильні дані автентифікації"
|
||||
|
||||
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:38
|
||||
#, elixir-format
|
||||
msgid "Invalid credentials."
|
||||
msgstr "Неправильні дані автентифікації."
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:355
|
||||
#, elixir-format
|
||||
msgid "Invalid indices"
|
||||
msgstr "Неправильні індекси"
|
||||
|
||||
#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:29
|
||||
#, elixir-format
|
||||
msgid "Invalid parameters"
|
||||
msgstr "Неправильні параметри"
|
||||
|
||||
#: lib/pleroma/web/common_api/utils.ex:414
|
||||
#, elixir-format
|
||||
msgid "Invalid password."
|
||||
msgstr "Неправильний пароль."
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:220
|
||||
#, elixir-format
|
||||
msgid "Invalid request"
|
||||
msgstr "Невірний запит"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:109
|
||||
#, elixir-format
|
||||
msgid "Kocaptcha service unavailable"
|
||||
msgstr "Сервіс Kocaptcha недоступний"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:112
|
||||
#, elixir-format
|
||||
msgid "Missing parameters"
|
||||
msgstr "Відсутні параметри"
|
||||
|
||||
#: lib/pleroma/web/common_api/utils.ex:547
|
||||
#, elixir-format
|
||||
msgid "No such conversation"
|
||||
msgstr "Немає такої розмови"
|
||||
|
||||
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:388
|
||||
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:414 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:456
|
||||
#, elixir-format
|
||||
msgid "No such permission_group"
|
||||
msgstr "Не існує такої групи повноважень"
|
||||
|
||||
#: lib/pleroma/plugs/uploaded_media.ex:84
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:486 lib/pleroma/web/admin_api/controllers/fallback_controller.ex:11
|
||||
#: lib/pleroma/web/feed/user_controller.ex:71 lib/pleroma/web/ostatus/ostatus_controller.ex:143
|
||||
#, elixir-format
|
||||
msgid "Not found"
|
||||
msgstr "Не знайдено"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:331
|
||||
#, elixir-format
|
||||
msgid "Poll's author can't vote"
|
||||
msgstr "Автор опитування не може голосувати"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20
|
||||
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:37 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:49
|
||||
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:50 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:306
|
||||
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71
|
||||
#, elixir-format
|
||||
msgid "Record not found"
|
||||
msgstr "Запис не знайдено"
|
||||
|
||||
#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:35
|
||||
#: lib/pleroma/web/feed/user_controller.ex:77 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:36
|
||||
#: lib/pleroma/web/ostatus/ostatus_controller.ex:149
|
||||
#, elixir-format
|
||||
msgid "Something went wrong"
|
||||
msgstr "Щось зламалося"
|
||||
|
||||
#: lib/pleroma/web/common_api/activity_draft.ex:107
|
||||
#, elixir-format
|
||||
msgid "The message visibility must be direct"
|
||||
msgstr "Видимість у повідомлення повинна бути `Приватний`"
|
||||
|
||||
#: lib/pleroma/web/common_api/utils.ex:573
|
||||
#, elixir-format
|
||||
msgid "The status is over the character limit"
|
||||
msgstr "Цей статус перевищує ліміт символів"
|
||||
|
||||
#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31
|
||||
#, elixir-format
|
||||
msgid "This resource requires authentication."
|
||||
msgstr "Цей ресурс вимагає автентифікації."
|
||||
|
||||
#: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206
|
||||
#, elixir-format
|
||||
msgid "Throttled"
|
||||
msgstr "Обмежено. Перевищено ліміт запитів."
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:356
|
||||
#, elixir-format
|
||||
msgid "Too many choices"
|
||||
msgstr "Забагато варіантів вибору"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:443
|
||||
#, elixir-format
|
||||
msgid "Unhandled activity type"
|
||||
msgstr "Непідтримуваний тип активності"
|
||||
|
||||
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:485
|
||||
#, elixir-format
|
||||
msgid "You can't revoke your own admin status."
|
||||
msgstr "Ви не можете позбавити самого себе статусу адміністратора."
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:221
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:308
|
||||
#, elixir-format
|
||||
msgid "Your account is currently disabled"
|
||||
msgstr "Ваш обліковий запис наразі вимкнено"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:183
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:331
|
||||
#, elixir-format
|
||||
msgid "Your login is missing a confirmed e-mail address"
|
||||
msgstr "Ваша електрона адреса не підтверджена"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:390
|
||||
#, elixir-format
|
||||
msgid "can't read inbox of %{nickname} as %{as_nickname}"
|
||||
msgstr ""
|
||||
"Не вдається прочитати \"Вхідні\" повідомлення %{nickname} як %{as_nickname}"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:473
|
||||
#, elixir-format
|
||||
msgid "can't update outbox of %{nickname} as %{as_nickname}"
|
||||
msgstr ""
|
||||
"Не вдається оновити \"Вихідні\" повідомлення %{nickname} як %{as_nickname}"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:471
|
||||
#, elixir-format
|
||||
msgid "conversation is already muted"
|
||||
msgstr "Розмова вже заглушена"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:314
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:492
|
||||
#, elixir-format
|
||||
msgid "error"
|
||||
msgstr "помилка"
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:32
|
||||
#, elixir-format
|
||||
msgid "mascots can only be images"
|
||||
msgstr "талісманами можуть бути лише зображення"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:62
|
||||
#, elixir-format
|
||||
msgid "not found"
|
||||
msgstr "не знайдено"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:394
|
||||
#, elixir-format
|
||||
msgid "Bad OAuth request."
|
||||
msgstr "Невірний запит OAuth."
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:115
|
||||
#, elixir-format
|
||||
msgid "CAPTCHA already used"
|
||||
msgstr "CAPTCHA вже використана"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:112
|
||||
#, elixir-format
|
||||
msgid "CAPTCHA expired"
|
||||
msgstr "Термін дії CAPTCHA закінчився"
|
||||
|
||||
#: lib/pleroma/plugs/uploaded_media.ex:57
|
||||
#, elixir-format
|
||||
msgid "Failed"
|
||||
msgstr "Не вдалося"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:410
|
||||
#, elixir-format
|
||||
msgid "Failed to authenticate: %{message}."
|
||||
msgstr "Помилка автентифікації: %{message}."
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:441
|
||||
#, elixir-format
|
||||
msgid "Failed to set up user account."
|
||||
msgstr "Не вдалося створити обліковий запис."
|
||||
|
||||
#: lib/pleroma/plugs/oauth_scopes_plug.ex:38
|
||||
#, elixir-format
|
||||
msgid "Insufficient permissions: %{permissions}."
|
||||
msgstr "Недостатньо прав: %{permissions}."
|
||||
|
||||
#: lib/pleroma/plugs/uploaded_media.ex:104
|
||||
#, elixir-format
|
||||
msgid "Internal Error"
|
||||
msgstr "Внутрішня помилка"
|
||||
|
||||
#: lib/pleroma/web/oauth/fallback_controller.ex:22
|
||||
#: lib/pleroma/web/oauth/fallback_controller.ex:29
|
||||
#, elixir-format
|
||||
msgid "Invalid Username/Password"
|
||||
msgstr "Неправильне ім'я користувача або пароль"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:118
|
||||
#, elixir-format
|
||||
msgid "Invalid answer data"
|
||||
msgstr "Неправильна відповідь"
|
||||
|
||||
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:33
|
||||
#, elixir-format
|
||||
msgid "Nodeinfo schema version not handled"
|
||||
msgstr "Версія схеми Nodeinfo не враховується"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:172
|
||||
#, elixir-format
|
||||
msgid "This action is outside the authorized scopes"
|
||||
msgstr "Ця дія виходить за рамки доступних повноважень"
|
||||
|
||||
#: lib/pleroma/web/oauth/fallback_controller.ex:14
|
||||
#, elixir-format
|
||||
msgid "Unknown error, please check the details and try again."
|
||||
msgstr "Невідома помилка. Перевірте деталі та повторіть спробу."
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:119
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:158
|
||||
#, elixir-format
|
||||
msgid "Unlisted redirect_uri."
|
||||
msgstr "Невідомий redirect_uri."
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:390
|
||||
#, elixir-format
|
||||
msgid "Unsupported OAuth provider: %{provider}."
|
||||
msgstr "Непідтримуваний постачальник послуг OAuth: %{provider}."
|
||||
|
||||
#: lib/pleroma/uploaders/uploader.ex:72
|
||||
#, elixir-format
|
||||
msgid "Uploader callback timeout"
|
||||
msgstr "Тайм-аут при завантаженні"
|
||||
|
||||
#: lib/pleroma/web/uploader_controller.ex:23
|
||||
#, elixir-format
|
||||
msgid "bad request"
|
||||
msgstr "невірний запит"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:103
|
||||
#, elixir-format
|
||||
msgid "CAPTCHA Error"
|
||||
msgstr "Помилка CAPTCHA"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:290
|
||||
#, elixir-format
|
||||
msgid "Could not add reaction emoji"
|
||||
msgstr "Не вдалося додати емодзі для реакції"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:301
|
||||
#, elixir-format
|
||||
msgid "Could not remove reaction emoji"
|
||||
msgstr "Не вдалося видалити реакцію"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:129
|
||||
#, elixir-format
|
||||
msgid "Invalid CAPTCHA (Missing parameter: %{name})"
|
||||
msgstr "Недійсна CAPTCHA (Відсутній параметр: %{name})"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:92
|
||||
#, elixir-format
|
||||
msgid "List not found"
|
||||
msgstr "Список не знайдено"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:123
|
||||
#, elixir-format
|
||||
msgid "Missing parameter: %{name}"
|
||||
msgstr "Відсутній параметр: %{name}"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:210
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:321
|
||||
#, elixir-format
|
||||
msgid "Password reset is required"
|
||||
msgstr "Потрібно скинути пароль"
|
||||
|
||||
#: lib/pleroma/tests/auth_test_controller.ex:9
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6
|
||||
#: lib/pleroma/web/admin_api/controllers/config_controller.ex:6 lib/pleroma/web/admin_api/controllers/fallback_controller.ex:6
|
||||
#: lib/pleroma/web/admin_api/controllers/invite_controller.ex:6 lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex:6
|
||||
#: lib/pleroma/web/admin_api/controllers/oauth_app_controller.ex:6 lib/pleroma/web/admin_api/controllers/relay_controller.ex:6
|
||||
#: lib/pleroma/web/admin_api/controllers/report_controller.ex:6 lib/pleroma/web/admin_api/controllers/status_controller.ex:6
|
||||
#: lib/pleroma/web/controller_helper.ex:6 lib/pleroma/web/embed_controller.ex:6
|
||||
#: lib/pleroma/web/fallback_redirect_controller.ex:6 lib/pleroma/web/feed/tag_controller.ex:6
|
||||
#: lib/pleroma/web/feed/user_controller.ex:6 lib/pleroma/web/mailer/subscription_controller.ex:2
|
||||
#: lib/pleroma/web/masto_fe_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/account_controller.ex:6
|
||||
#: lib/pleroma/web/mastodon_api/controllers/app_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/auth_controller.ex:6
|
||||
#: lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex:6
|
||||
#: lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:6
|
||||
#: lib/pleroma/web/mastodon_api/controllers/filter_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:6
|
||||
#: lib/pleroma/web/mastodon_api/controllers/instance_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/list_controller.ex:6
|
||||
#: lib/pleroma/web/mastodon_api/controllers/marker_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex:14
|
||||
#: lib/pleroma/web/mastodon_api/controllers/media_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/notification_controller.ex:6
|
||||
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/report_controller.ex:8
|
||||
#: lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/search_controller.ex:6
|
||||
#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:7
|
||||
#: lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:6
|
||||
#: lib/pleroma/web/media_proxy/media_proxy_controller.ex:6 lib/pleroma/web/mongooseim/mongoose_im_controller.ex:6
|
||||
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:6 lib/pleroma/web/oauth/fallback_controller.ex:6
|
||||
#: lib/pleroma/web/oauth/mfa_controller.ex:10 lib/pleroma/web/oauth/oauth_controller.ex:6
|
||||
#: lib/pleroma/web/ostatus/ostatus_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/account_controller.ex:6
|
||||
#: lib/pleroma/web/pleroma_api/controllers/chat_controller.ex:5 lib/pleroma/web/pleroma_api/controllers/conversation_controller.ex:6
|
||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:2 lib/pleroma/web/pleroma_api/controllers/emoji_reaction_controller.ex:6
|
||||
#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/notification_controller.ex:6
|
||||
#: lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex:6
|
||||
#: lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex:7 lib/pleroma/web/static_fe/static_fe_controller.ex:6
|
||||
#: lib/pleroma/web/twitter_api/controllers/password_controller.ex:10 lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex:6
|
||||
#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:6 lib/pleroma/web/twitter_api/twitter_api_controller.ex:6
|
||||
#: lib/pleroma/web/uploader_controller.ex:6 lib/pleroma/web/web_finger/web_finger_controller.ex:6
|
||||
#, elixir-format
|
||||
msgid "Security violation: OAuth scopes check was neither handled nor explicitly skipped."
|
||||
msgstr ""
|
||||
"Порушення безпеки: перевірка обсягу OAuth не була оброблена, ні явно "
|
||||
"пропущена."
|
||||
|
||||
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:28
|
||||
#, elixir-format
|
||||
msgid "Two-factor authentication enabled, you must use a access token."
|
||||
msgstr ""
|
||||
"Двофакторна автентифікація ввімкнена, ви повинні використовувати ключ "
|
||||
"доступу."
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:210
|
||||
#, elixir-format
|
||||
msgid "Unexpected error occurred while adding file to pack."
|
||||
msgstr "Несподівана помилка при додаванні файлу в пакет."
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:138
|
||||
#, elixir-format
|
||||
msgid "Unexpected error occurred while creating pack."
|
||||
msgstr "Несподівана помилка під час створення пакета."
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:278
|
||||
#, elixir-format
|
||||
msgid "Unexpected error occurred while removing file from pack."
|
||||
msgstr "Під час видалення файлу з пакета сталася несподівана помилка."
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:250
|
||||
#, elixir-format
|
||||
msgid "Unexpected error occurred while updating file in pack."
|
||||
msgstr "Під час оновлення файлу в пакеті сталася несподівана помилка."
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:179
|
||||
#, elixir-format
|
||||
msgid "Unexpected error occurred while updating pack metadata."
|
||||
msgstr "Під час оновлення метаданих пакета сталася несподівана помилка."
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
||||
#, elixir-format
|
||||
msgid "Web push subscription is disabled on this Pleroma instance"
|
||||
msgstr "Web push-сповіщення вимкнені на цьому інстансі Pleroma"
|
||||
|
||||
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:451
|
||||
#, elixir-format
|
||||
msgid "You can't revoke your own admin/moderator status."
|
||||
msgstr "Ви не можете позбавити самого себе статусу адміністратора/модератора."
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:126
|
||||
#, elixir-format
|
||||
msgid "authorization required for timeline view"
|
||||
msgstr "необхідно ввійти в систему для перегляду стрічки повідомлень"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:24
|
||||
#, elixir-format
|
||||
msgid "Access denied"
|
||||
msgstr "Доступ заборонено"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:282
|
||||
#, elixir-format
|
||||
msgid "This API requires an authenticated user"
|
||||
msgstr "Цей API вимагає автентифікованого користувача"
|
||||
|
||||
#: lib/pleroma/plugs/user_is_admin_plug.ex:21
|
||||
#, elixir-format
|
||||
msgid "User is not an admin."
|
||||
msgstr "Користувач не є адміністратором."
|
|
@ -3,7 +3,7 @@ msgstr ""
|
|||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-09-20 13:18+0000\n"
|
||||
"PO-Revision-Date: 2020-10-22 18:25+0000\n"
|
||||
"PO-Revision-Date: 2020-12-14 06:00+0000\n"
|
||||
"Last-Translator: shironeko <shironeko@tesaguri.club>\n"
|
||||
"Language-Team: Chinese (Simplified) <https://translate.pleroma.social/"
|
||||
"projects/pleroma/pleroma/zh_Hans/>\n"
|
||||
|
@ -146,9 +146,9 @@ msgid "Cannot post an empty status without attachments"
|
|||
msgstr "无法发送空白且不包含附件的状态"
|
||||
|
||||
#: lib/pleroma/web/common_api/utils.ex:511
|
||||
#, elixir-format
|
||||
#, elixir-format, fuzzy
|
||||
msgid "Comment must be up to %{max_size} characters"
|
||||
msgstr ""
|
||||
msgstr "评论最多可使用 %{max_size} 字符"
|
||||
|
||||
#: lib/pleroma/config/config_db.ex:191
|
||||
#, elixir-format
|
||||
|
@ -250,21 +250,21 @@ msgstr "没有该对话"
|
|||
|
||||
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:388
|
||||
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:414 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:456
|
||||
#, elixir-format
|
||||
#, elixir-format, fuzzy
|
||||
msgid "No such permission_group"
|
||||
msgstr ""
|
||||
msgstr "没有该权限组"
|
||||
|
||||
#: lib/pleroma/plugs/uploaded_media.ex:84
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:486 lib/pleroma/web/admin_api/controllers/fallback_controller.ex:11
|
||||
#: lib/pleroma/web/feed/user_controller.ex:71 lib/pleroma/web/ostatus/ostatus_controller.ex:143
|
||||
#, elixir-format
|
||||
msgid "Not found"
|
||||
msgstr ""
|
||||
msgstr "未找到"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:331
|
||||
#, elixir-format
|
||||
msgid "Poll's author can't vote"
|
||||
msgstr ""
|
||||
msgstr "投票的发起者不能投票"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20
|
||||
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:37 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:49
|
||||
|
@ -272,39 +272,39 @@ msgstr ""
|
|||
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71
|
||||
#, elixir-format
|
||||
msgid "Record not found"
|
||||
msgstr ""
|
||||
msgstr "未找到该记录"
|
||||
|
||||
#: lib/pleroma/web/admin_api/controllers/fallback_controller.ex:35
|
||||
#: lib/pleroma/web/feed/user_controller.ex:77 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:36
|
||||
#: lib/pleroma/web/ostatus/ostatus_controller.ex:149
|
||||
#, elixir-format
|
||||
msgid "Something went wrong"
|
||||
msgstr ""
|
||||
msgstr "发生了一些错误"
|
||||
|
||||
#: lib/pleroma/web/common_api/activity_draft.ex:107
|
||||
#, elixir-format
|
||||
msgid "The message visibility must be direct"
|
||||
msgstr ""
|
||||
msgstr "该消息必须为私信"
|
||||
|
||||
#: lib/pleroma/web/common_api/utils.ex:573
|
||||
#, elixir-format
|
||||
msgid "The status is over the character limit"
|
||||
msgstr ""
|
||||
msgstr "状态超过了字符数限制"
|
||||
|
||||
#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31
|
||||
#, elixir-format
|
||||
msgid "This resource requires authentication."
|
||||
msgstr ""
|
||||
msgstr "该资源需要认证。"
|
||||
|
||||
#: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206
|
||||
#, elixir-format
|
||||
#, elixir-format, fuzzy
|
||||
msgid "Throttled"
|
||||
msgstr ""
|
||||
msgstr "节流了"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:356
|
||||
#, elixir-format
|
||||
msgid "Too many choices"
|
||||
msgstr ""
|
||||
msgstr "太多选项"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:443
|
||||
#, elixir-format
|
||||
|
@ -314,101 +314,101 @@ msgstr ""
|
|||
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:485
|
||||
#, elixir-format
|
||||
msgid "You can't revoke your own admin status."
|
||||
msgstr ""
|
||||
msgstr "您不能撤消自己的管理员权限。"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:221
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:308
|
||||
#, elixir-format
|
||||
msgid "Your account is currently disabled"
|
||||
msgstr ""
|
||||
msgstr "您的账户已被禁用"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:183
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:331
|
||||
#, elixir-format
|
||||
msgid "Your login is missing a confirmed e-mail address"
|
||||
msgstr ""
|
||||
msgstr "您的账户缺少已认证的 e-mail 地址"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:390
|
||||
#, elixir-format
|
||||
msgid "can't read inbox of %{nickname} as %{as_nickname}"
|
||||
msgstr ""
|
||||
msgstr "无法以 %{as_nickname} 读取 %{nickname} 的收件箱"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:473
|
||||
#, elixir-format
|
||||
msgid "can't update outbox of %{nickname} as %{as_nickname}"
|
||||
msgstr ""
|
||||
msgstr "无法以 %{as_nickname} 更新 %{nickname} 的出件箱"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:471
|
||||
#, elixir-format
|
||||
msgid "conversation is already muted"
|
||||
msgstr ""
|
||||
msgstr "对话已经被静音"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:314
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:492
|
||||
#, elixir-format
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
msgstr "错误"
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:32
|
||||
#, elixir-format
|
||||
msgid "mascots can only be images"
|
||||
msgstr ""
|
||||
msgstr "吉祥物只能是图片"
|
||||
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:62
|
||||
#, elixir-format
|
||||
msgid "not found"
|
||||
msgstr ""
|
||||
msgstr "未找到"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:394
|
||||
#, elixir-format
|
||||
msgid "Bad OAuth request."
|
||||
msgstr ""
|
||||
msgstr "错误的 OAuth 请求。"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:115
|
||||
#, elixir-format
|
||||
msgid "CAPTCHA already used"
|
||||
msgstr ""
|
||||
msgstr "验证码已被使用"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:112
|
||||
#, elixir-format
|
||||
msgid "CAPTCHA expired"
|
||||
msgstr ""
|
||||
msgstr "验证码已过期"
|
||||
|
||||
#: lib/pleroma/plugs/uploaded_media.ex:57
|
||||
#, elixir-format
|
||||
msgid "Failed"
|
||||
msgstr ""
|
||||
msgstr "失败"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:410
|
||||
#, elixir-format
|
||||
#, elixir-format, fuzzy
|
||||
msgid "Failed to authenticate: %{message}."
|
||||
msgstr ""
|
||||
msgstr "认证失败:%{message}。"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:441
|
||||
#, elixir-format
|
||||
msgid "Failed to set up user account."
|
||||
msgstr ""
|
||||
msgstr "建立用户帐号失败。"
|
||||
|
||||
#: lib/pleroma/plugs/oauth_scopes_plug.ex:38
|
||||
#, elixir-format
|
||||
msgid "Insufficient permissions: %{permissions}."
|
||||
msgstr ""
|
||||
msgstr "权限不足:%{permissions}。"
|
||||
|
||||
#: lib/pleroma/plugs/uploaded_media.ex:104
|
||||
#, elixir-format
|
||||
msgid "Internal Error"
|
||||
msgstr ""
|
||||
msgstr "内部错误"
|
||||
|
||||
#: lib/pleroma/web/oauth/fallback_controller.ex:22
|
||||
#: lib/pleroma/web/oauth/fallback_controller.ex:29
|
||||
#, elixir-format
|
||||
msgid "Invalid Username/Password"
|
||||
msgstr ""
|
||||
msgstr "无效的用户名/密码"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:118
|
||||
#, elixir-format
|
||||
#, elixir-format, fuzzy
|
||||
msgid "Invalid answer data"
|
||||
msgstr ""
|
||||
msgstr "无效的回答数据"
|
||||
|
||||
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:33
|
||||
#, elixir-format
|
||||
|
@ -418,12 +418,12 @@ msgstr ""
|
|||
#: lib/pleroma/web/oauth/oauth_controller.ex:172
|
||||
#, elixir-format
|
||||
msgid "This action is outside the authorized scopes"
|
||||
msgstr ""
|
||||
msgstr "此操作在许可范围以外"
|
||||
|
||||
#: lib/pleroma/web/oauth/fallback_controller.ex:14
|
||||
#, elixir-format
|
||||
msgid "Unknown error, please check the details and try again."
|
||||
msgstr ""
|
||||
msgstr "未知错误,请检查并重试。"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:119
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:158
|
||||
|
@ -434,53 +434,53 @@ msgstr ""
|
|||
#: lib/pleroma/web/oauth/oauth_controller.ex:390
|
||||
#, elixir-format
|
||||
msgid "Unsupported OAuth provider: %{provider}."
|
||||
msgstr ""
|
||||
msgstr "不支持的 OAuth 提供者:%{provider}。"
|
||||
|
||||
#: lib/pleroma/uploaders/uploader.ex:72
|
||||
#, elixir-format
|
||||
#, elixir-format, fuzzy
|
||||
msgid "Uploader callback timeout"
|
||||
msgstr ""
|
||||
msgstr "上传回复超时"
|
||||
|
||||
#: lib/pleroma/web/uploader_controller.ex:23
|
||||
#, elixir-format
|
||||
msgid "bad request"
|
||||
msgstr ""
|
||||
msgstr "错误的请求"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:103
|
||||
#, elixir-format
|
||||
msgid "CAPTCHA Error"
|
||||
msgstr ""
|
||||
msgstr "验证码错误"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:290
|
||||
#, elixir-format
|
||||
#, elixir-format, fuzzy
|
||||
msgid "Could not add reaction emoji"
|
||||
msgstr ""
|
||||
msgstr "无法添加表情反应"
|
||||
|
||||
#: lib/pleroma/web/common_api/common_api.ex:301
|
||||
#, elixir-format
|
||||
msgid "Could not remove reaction emoji"
|
||||
msgstr ""
|
||||
msgstr "无法移除表情反应"
|
||||
|
||||
#: lib/pleroma/web/twitter_api/twitter_api.ex:129
|
||||
#, elixir-format
|
||||
msgid "Invalid CAPTCHA (Missing parameter: %{name})"
|
||||
msgstr ""
|
||||
msgstr "无效的验证码(缺少参数:%{name})"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:92
|
||||
#, elixir-format
|
||||
msgid "List not found"
|
||||
msgstr ""
|
||||
msgstr "未找到列表"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:123
|
||||
#, elixir-format
|
||||
msgid "Missing parameter: %{name}"
|
||||
msgstr ""
|
||||
msgstr "缺少参数:%{name}"
|
||||
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:210
|
||||
#: lib/pleroma/web/oauth/oauth_controller.ex:321
|
||||
#, elixir-format
|
||||
msgid "Password reset is required"
|
||||
msgstr ""
|
||||
msgstr "需要重置密码"
|
||||
|
||||
#: lib/pleroma/tests/auth_test_controller.ex:9
|
||||
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:6
|
||||
|
@ -520,61 +520,61 @@ msgid "Security violation: OAuth scopes check was neither handled nor explicitly
|
|||
msgstr ""
|
||||
|
||||
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:28
|
||||
#, elixir-format
|
||||
#, elixir-format, fuzzy
|
||||
msgid "Two-factor authentication enabled, you must use a access token."
|
||||
msgstr ""
|
||||
msgstr "已启用两因素验证,您需要使用访问令牌。"
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:210
|
||||
#, elixir-format
|
||||
msgid "Unexpected error occurred while adding file to pack."
|
||||
msgstr ""
|
||||
msgstr "向表情包添加文件时发生了没有预料到的错误。"
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:138
|
||||
#, elixir-format
|
||||
msgid "Unexpected error occurred while creating pack."
|
||||
msgstr ""
|
||||
msgstr "创建表情包时发生了没有预料到的错误。"
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:278
|
||||
#, elixir-format
|
||||
msgid "Unexpected error occurred while removing file from pack."
|
||||
msgstr ""
|
||||
msgstr "从表情包移除文件时发生了没有预料到的错误。"
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:250
|
||||
#, elixir-format
|
||||
msgid "Unexpected error occurred while updating file in pack."
|
||||
msgstr ""
|
||||
msgstr "更新表情包内的文件时发生了没有预料到的错误。"
|
||||
|
||||
#: lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex:179
|
||||
#, elixir-format
|
||||
msgid "Unexpected error occurred while updating pack metadata."
|
||||
msgstr ""
|
||||
msgstr "更新表情包元数据时发生了没有预料到的错误。"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
||||
#, elixir-format
|
||||
#, elixir-format, fuzzy
|
||||
msgid "Web push subscription is disabled on this Pleroma instance"
|
||||
msgstr ""
|
||||
msgstr "此 Pleroma 实例禁用了网页推送订阅"
|
||||
|
||||
#: lib/pleroma/web/admin_api/controllers/admin_api_controller.ex:451
|
||||
#, elixir-format
|
||||
msgid "You can't revoke your own admin/moderator status."
|
||||
msgstr ""
|
||||
msgstr "您不能撤消自己的管理员权限。"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:126
|
||||
#, elixir-format
|
||||
msgid "authorization required for timeline view"
|
||||
msgstr ""
|
||||
msgstr "浏览时间线需要认证"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:24
|
||||
#, elixir-format
|
||||
msgid "Access denied"
|
||||
msgstr ""
|
||||
msgstr "拒绝访问"
|
||||
|
||||
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:282
|
||||
#, elixir-format
|
||||
msgid "This API requires an authenticated user"
|
||||
msgstr ""
|
||||
msgstr "此 API 需要已认证的用户"
|
||||
|
||||
#: lib/pleroma/plugs/user_is_admin_plug.ex:21
|
||||
#, elixir-format
|
||||
msgid "User is not an admin."
|
||||
msgstr ""
|
||||
msgstr "该用户不是管理员。"
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
defmodule Pleroma.Repo.Migrations.DataMigrationProlongOAuthTokensValidUntil do
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
expires_in = Pleroma.Config.get!([:oauth2, :token_expires_in])
|
||||
valid_until = NaiveDateTime.add(NaiveDateTime.utc_now(), expires_in, :second)
|
||||
execute("update oauth_tokens set valid_until = '#{valid_until}'")
|
||||
end
|
||||
|
||||
def down do
|
||||
:noop
|
||||
end
|
||||
end
|
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,296 @@
|
|||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--brand-color: #d8a070;
|
||||
--background-color: #121a24;
|
||||
--foreground-color: #182230;
|
||||
--primary-text-color: #b9b9ba;
|
||||
--muted-text-color: #89898a;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--background-color);
|
||||
font-family: sans-serif;
|
||||
color: var(--primary-text-color);
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.instance-header {
|
||||
height: 60px;
|
||||
padding: 10px;
|
||||
background: var(--foreground-color);
|
||||
box-shadow: 0 1px 4px 0px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.instance-header__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.instance-header__thumbnail {
|
||||
max-width: 40px;
|
||||
border-radius: 4px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.instance-header__title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 400px;
|
||||
background-color: var(--foreground-color);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin: 35px auto;
|
||||
box-shadow: 0 1px 4px 0px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.container__content {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: var(--primary-text-color);
|
||||
font-weight: normal;
|
||||
font-size: 18px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--brand-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.input {
|
||||
color: var(--muted-text-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
input {
|
||||
box-sizing: content-box;
|
||||
padding: 10px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
background-color: var(--background-color);
|
||||
color: var(--primary-text-color);
|
||||
border: 0;
|
||||
transition-property: border-bottom;
|
||||
transition-duration: 0.35s;
|
||||
border-bottom: 2px solid #2a384a;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.scopes-input {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 1em 0;
|
||||
color: var(--muted-text-color);
|
||||
}
|
||||
|
||||
.scopes-input label:first-child {
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
.scopes {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
.scope {
|
||||
display: flex;
|
||||
flex-basis: 100%;
|
||||
height: 2em;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.scope:before {
|
||||
color: var(--primary-text-color);
|
||||
content: "✔\fe0e";
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
[type="checkbox"] + label {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[type="checkbox"] + label:before {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
color: white;
|
||||
background-color: var(--background-color);
|
||||
border: 4px solid var(--background-color);
|
||||
box-shadow: 0px 0px 1px 0 var(--brand-color);
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
margin-right: 1.0em;
|
||||
content: "";
|
||||
transition-property: background-color;
|
||||
transition-duration: 0.35s;
|
||||
color: var(--background-color);
|
||||
margin-bottom: -0.2em;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
[type="checkbox"]:checked + label:before {
|
||||
background-color: var(--brand-color);
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
border-bottom: 2px solid var(--brand-color);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.actions button,
|
||||
.actions a.button {
|
||||
width: auto;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
a.button,
|
||||
button {
|
||||
width: 100%;
|
||||
background-color: #1c2a3a;
|
||||
color: var(--primary-text-color);
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
padding: 10px 16px;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
text-transform: uppercase;
|
||||
font-size: 16px;
|
||||
box-shadow: 0px 0px 2px 0px black,
|
||||
0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset,
|
||||
0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
|
||||
}
|
||||
|
||||
a.button:hover,
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0px 0px 0px 1px var(--brand-color),
|
||||
0px 1px 0px 0px rgba(255, 255, 255, 0.2) inset,
|
||||
0px -1px 0px 0px rgba(0, 0, 0, 0.2) inset;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
width: 100%;
|
||||
background-color: #931014;
|
||||
border: 1px solid #a06060;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #7d796a;
|
||||
padding: 10px;
|
||||
margin-top: 20px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.account-header__banner {
|
||||
width: 100%;
|
||||
height: 112px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.account-header__avatar {
|
||||
width: 94px;
|
||||
height: 94px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
margin: -47px 10px 0;
|
||||
border: 6px solid var(--foreground-color);
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.account-header__meta {
|
||||
padding: 6px 20px 17px;
|
||||
}
|
||||
|
||||
.account-header__display-name {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.account-header__nickname {
|
||||
font-size: 14px;
|
||||
color: var(--muted-text-color);
|
||||
}
|
||||
|
||||
@media all and (max-width: 420px) {
|
||||
.container {
|
||||
margin: 0 auto;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.scope {
|
||||
flex-basis: 0%;
|
||||
}
|
||||
|
||||
.scope:before {
|
||||
content: "";
|
||||
margin-left: 0em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.scope:first-child:before {
|
||||
margin-left: 1em;
|
||||
content: "✔\fe0e";
|
||||
}
|
||||
|
||||
.scope:after {
|
||||
content: ",";
|
||||
}
|
||||
|
||||
.scope:last-child:after {
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
.form-row {
|
||||
display: flex;
|
||||
}
|
||||
.form-row > label {
|
||||
line-height: 47px;
|
||||
flex: 1;
|
||||
}
|
||||
.form-row > input {
|
||||
flex: 2;
|
||||
}
|
|
@ -7,6 +7,7 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
|||
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Mix.Tasks.Pleroma.Config, as: MixTask
|
||||
alias Pleroma.ConfigDB
|
||||
alias Pleroma.Repo
|
||||
|
||||
|
@ -22,30 +23,41 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
|||
:ok
|
||||
end
|
||||
|
||||
setup_all do: clear_config(:configurable_from_database, true)
|
||||
defp config_records do
|
||||
ConfigDB
|
||||
|> Repo.all()
|
||||
|> Enum.sort()
|
||||
end
|
||||
|
||||
defp insert_config_record(group, key, value) do
|
||||
insert(:config,
|
||||
group: group,
|
||||
key: key,
|
||||
value: value
|
||||
)
|
||||
end
|
||||
|
||||
test "error if file with custom settings doesn't exist" do
|
||||
Mix.Tasks.Pleroma.Config.migrate_to_db("config/not_existance_config_file.exs")
|
||||
MixTask.migrate_to_db("config/non_existent_config_file.exs")
|
||||
|
||||
assert_receive {:mix_shell, :info,
|
||||
[
|
||||
"To migrate settings, you must define custom settings in config/not_existance_config_file.exs."
|
||||
]},
|
||||
15
|
||||
msg =
|
||||
"To migrate settings, you must define custom settings in config/non_existent_config_file.exs."
|
||||
|
||||
assert_receive {:mix_shell, :info, [^msg]}, 15
|
||||
end
|
||||
|
||||
describe "migrate_to_db/1" do
|
||||
setup do
|
||||
initial = Application.get_env(:quack, :level)
|
||||
on_exit(fn -> Application.put_env(:quack, :level, initial) end)
|
||||
clear_config(:configurable_from_database, true)
|
||||
clear_config([:quack, :level])
|
||||
end
|
||||
|
||||
@tag capture_log: true
|
||||
test "config migration refused when deprecated settings are found" do
|
||||
clear_config([:media_proxy, :whitelist], ["domain_without_scheme.com"])
|
||||
assert Repo.all(ConfigDB) == []
|
||||
assert config_records() == []
|
||||
|
||||
Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs")
|
||||
MixTask.migrate_to_db("test/fixtures/config/temp.secret.exs")
|
||||
|
||||
assert_received {:mix_shell, :error, [message]}
|
||||
|
||||
|
@ -54,9 +66,9 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
|||
end
|
||||
|
||||
test "filtered settings are migrated to db" do
|
||||
assert Repo.all(ConfigDB) == []
|
||||
assert config_records() == []
|
||||
|
||||
Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs")
|
||||
MixTask.migrate_to_db("test/fixtures/config/temp.secret.exs")
|
||||
|
||||
config1 = ConfigDB.get_by_params(%{group: ":pleroma", key: ":first_setting"})
|
||||
config2 = ConfigDB.get_by_params(%{group: ":pleroma", key: ":second_setting"})
|
||||
|
@ -71,18 +83,19 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
|||
end
|
||||
|
||||
test "config table is truncated before migration" do
|
||||
insert(:config, key: :first_setting, value: [key: "value", key2: ["Activity"]])
|
||||
assert Repo.aggregate(ConfigDB, :count, :id) == 1
|
||||
insert_config_record(:pleroma, :first_setting, key: "value", key2: ["Activity"])
|
||||
assert length(config_records()) == 1
|
||||
|
||||
Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs")
|
||||
MixTask.migrate_to_db("test/fixtures/config/temp.secret.exs")
|
||||
|
||||
config = ConfigDB.get_by_params(%{group: ":pleroma", key: ":first_setting"})
|
||||
assert config.value == [key: "value", key2: [Repo]]
|
||||
end
|
||||
end
|
||||
|
||||
describe "with deletion temp file" do
|
||||
describe "with deletion of temp file" do
|
||||
setup do
|
||||
clear_config(:configurable_from_database, true)
|
||||
temp_file = "config/temp.exported_from_db.secret.exs"
|
||||
|
||||
on_exit(fn ->
|
||||
|
@ -93,13 +106,13 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
|||
end
|
||||
|
||||
test "settings are migrated to file and deleted from db", %{temp_file: temp_file} do
|
||||
insert(:config, key: :setting_first, value: [key: "value", key2: ["Activity"]])
|
||||
insert(:config, key: :setting_second, value: [key: "value2", key2: [Repo]])
|
||||
insert(:config, group: :quack, key: :level, value: :info)
|
||||
insert_config_record(:pleroma, :setting_first, key: "value", key2: ["Activity"])
|
||||
insert_config_record(:pleroma, :setting_second, key: "value2", key2: [Repo])
|
||||
insert_config_record(:quack, :level, :info)
|
||||
|
||||
Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "--env", "temp", "-d"])
|
||||
MixTask.run(["migrate_from_db", "--env", "temp", "-d"])
|
||||
|
||||
assert Repo.all(ConfigDB) == []
|
||||
assert config_records() == []
|
||||
|
||||
file = File.read!(temp_file)
|
||||
assert file =~ "config :pleroma, :setting_first,"
|
||||
|
@ -169,9 +182,9 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
|||
]
|
||||
)
|
||||
|
||||
Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "--env", "temp", "-d"])
|
||||
MixTask.run(["migrate_from_db", "--env", "temp", "-d"])
|
||||
|
||||
assert Repo.all(ConfigDB) == []
|
||||
assert config_records() == []
|
||||
assert File.exists?(temp_file)
|
||||
{:ok, file} = File.read(temp_file)
|
||||
|
||||
|
@ -186,4 +199,114 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
|
|||
"#{header}\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 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 autofollowed_nicknames: [],\n max_pinned_statuses: 1,\n attachment_links: false,\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 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
|
||||
|
||||
describe "operations on database config" do
|
||||
setup do: clear_config(:configurable_from_database, true)
|
||||
|
||||
test "dumping a specific group" do
|
||||
insert_config_record(:pleroma, :instance, name: "Pleroma Test")
|
||||
|
||||
insert_config_record(:web_push_encryption, :vapid_details,
|
||||
subject: "mailto:administrator@example.com",
|
||||
public_key:
|
||||
"BOsPL-_KjNnjj_RMvLeR3dTOrcndi4TbMR0cu56gLGfGaT5m1gXxSfRHOcC4Dd78ycQL1gdhtx13qgKHmTM5xAI",
|
||||
private_key: "Ism6FNdS31nLCA94EfVbJbDdJXCxAZ8cZiB1JQPN_t4"
|
||||
)
|
||||
|
||||
MixTask.run(["dump", "pleroma"])
|
||||
|
||||
assert_receive {:mix_shell, :info,
|
||||
["config :pleroma, :instance, [name: \"Pleroma Test\"]\r\n\r\n"]}
|
||||
|
||||
refute_receive {
|
||||
:mix_shell,
|
||||
:info,
|
||||
[
|
||||
"config :web_push_encryption, :vapid_details, [subject: \"mailto:administrator@example.com\", public_key: \"BOsPL-_KjNnjj_RMvLeR3dTOrcndi4TbMR0cu56gLGfGaT5m1gXxSfRHOcC4Dd78ycQL1gdhtx13qgKHmTM5xAI\", private_key: \"Ism6FNdS31nLCA94EfVbJbDdJXCxAZ8cZiB1JQPN_t4\"]\r\n\r\n"
|
||||
]
|
||||
}
|
||||
|
||||
# Ensure operations work when using atom syntax
|
||||
MixTask.run(["dump", ":pleroma"])
|
||||
|
||||
assert_receive {:mix_shell, :info,
|
||||
["config :pleroma, :instance, [name: \"Pleroma Test\"]\r\n\r\n"]}
|
||||
end
|
||||
|
||||
test "dumping a specific key in a group" do
|
||||
insert_config_record(:pleroma, :instance, name: "Pleroma Test")
|
||||
insert_config_record(:pleroma, Pleroma.Captcha, enabled: false)
|
||||
|
||||
MixTask.run(["dump", "pleroma", "Pleroma.Captcha"])
|
||||
|
||||
refute_receive {:mix_shell, :info,
|
||||
["config :pleroma, :instance, [name: \"Pleroma Test\"]\r\n\r\n"]}
|
||||
|
||||
assert_receive {:mix_shell, :info,
|
||||
["config :pleroma, Pleroma.Captcha, [enabled: false]\r\n\r\n"]}
|
||||
end
|
||||
|
||||
test "dumps all configuration successfully" do
|
||||
insert_config_record(:pleroma, :instance, name: "Pleroma Test")
|
||||
insert_config_record(:pleroma, Pleroma.Captcha, enabled: false)
|
||||
|
||||
MixTask.run(["dump"])
|
||||
|
||||
assert_receive {:mix_shell, :info,
|
||||
["config :pleroma, :instance, [name: \"Pleroma Test\"]\r\n\r\n"]}
|
||||
|
||||
assert_receive {:mix_shell, :info,
|
||||
["config :pleroma, Pleroma.Captcha, [enabled: false]\r\n\r\n"]}
|
||||
end
|
||||
end
|
||||
|
||||
describe "when configdb disabled" do
|
||||
test "refuses to dump" do
|
||||
clear_config(:configurable_from_database, false)
|
||||
|
||||
insert_config_record(:pleroma, :instance, name: "Pleroma Test")
|
||||
|
||||
MixTask.run(["dump"])
|
||||
|
||||
msg =
|
||||
"ConfigDB not enabled. Please check the value of :configurable_from_database in your configuration."
|
||||
|
||||
assert_receive {:mix_shell, :error, [^msg]}
|
||||
end
|
||||
end
|
||||
|
||||
describe "destructive operations" do
|
||||
setup do: clear_config(:configurable_from_database, true)
|
||||
|
||||
setup do
|
||||
insert_config_record(:pleroma, :instance, name: "Pleroma Test")
|
||||
insert_config_record(:pleroma, Pleroma.Captcha, enabled: false)
|
||||
insert_config_record(:pleroma2, :key2, z: 1)
|
||||
|
||||
assert length(config_records()) == 3
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
test "deletes group of settings" do
|
||||
MixTask.run(["delete", "--force", "pleroma"])
|
||||
|
||||
assert [%ConfigDB{group: :pleroma2, key: :key2}] = config_records()
|
||||
end
|
||||
|
||||
test "deletes specified key" do
|
||||
MixTask.run(["delete", "--force", "pleroma", "Pleroma.Captcha"])
|
||||
|
||||
assert [
|
||||
%ConfigDB{group: :pleroma, key: :instance},
|
||||
%ConfigDB{group: :pleroma2, key: :key2}
|
||||
] = config_records()
|
||||
end
|
||||
|
||||
test "resets entire config" do
|
||||
MixTask.run(["reset", "--force"])
|
||||
|
||||
assert config_records() == []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -73,7 +73,7 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do
|
|||
describe "running update_users_following_followers_counts" do
|
||||
test "following and followers count are updated" do
|
||||
[user, user2] = insert_pair(:user)
|
||||
{:ok, %User{} = user} = User.follow(user, user2)
|
||||
{:ok, %User{} = user, _user2} = User.follow(user, user2)
|
||||
|
||||
following = User.following(user)
|
||||
|
||||
|
@ -87,7 +87,8 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do
|
|||
|
||||
assert user.follower_count == 3
|
||||
|
||||
assert :ok == Mix.Tasks.Pleroma.Database.run(["update_users_following_followers_counts"])
|
||||
assert {:ok, :ok} ==
|
||||
Mix.Tasks.Pleroma.Database.run(["update_users_following_followers_counts"])
|
||||
|
||||
user = User.get_by_id(user.id)
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do
|
|||
unsaved = build(:user)
|
||||
|
||||
# prepare to answer yes
|
||||
send(self(), {:mix_shell_input, :yes?, true})
|
||||
send(self(), {:mix_shell_input, :prompt, "Y"})
|
||||
|
||||
Mix.Tasks.Pleroma.User.run([
|
||||
"new",
|
||||
|
@ -55,7 +55,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do
|
|||
assert_received {:mix_shell, :info, [message]}
|
||||
assert message =~ "user will be created"
|
||||
|
||||
assert_received {:mix_shell, :yes?, [message]}
|
||||
assert_received {:mix_shell, :prompt, [message]}
|
||||
assert message =~ "Continue"
|
||||
|
||||
assert_received {:mix_shell, :info, [message]}
|
||||
|
@ -73,14 +73,14 @@ defmodule Mix.Tasks.Pleroma.UserTest do
|
|||
unsaved = build(:user)
|
||||
|
||||
# prepare to answer no
|
||||
send(self(), {:mix_shell_input, :yes?, false})
|
||||
send(self(), {:mix_shell_input, :prompt, "N"})
|
||||
|
||||
Mix.Tasks.Pleroma.User.run(["new", unsaved.nickname, unsaved.email])
|
||||
|
||||
assert_received {:mix_shell, :info, [message]}
|
||||
assert message =~ "user will be created"
|
||||
|
||||
assert_received {:mix_shell, :yes?, [message]}
|
||||
assert_received {:mix_shell, :prompt, [message]}
|
||||
assert message =~ "Continue"
|
||||
|
||||
assert_received {:mix_shell, :info, [message]}
|
||||
|
@ -462,24 +462,24 @@ defmodule Mix.Tasks.Pleroma.UserTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "running toggle_confirmed" do
|
||||
describe "running confirm" do
|
||||
test "user is confirmed" do
|
||||
%{id: id, nickname: nickname} = insert(:user, confirmation_pending: false)
|
||||
|
||||
assert :ok = Mix.Tasks.Pleroma.User.run(["toggle_confirmed", nickname])
|
||||
assert :ok = Mix.Tasks.Pleroma.User.run(["confirm", nickname])
|
||||
assert_received {:mix_shell, :info, [message]}
|
||||
assert message == "#{nickname} needs confirmation."
|
||||
assert message == "#{nickname} doesn't need confirmation."
|
||||
|
||||
user = Repo.get(User, id)
|
||||
assert user.confirmation_pending
|
||||
assert user.confirmation_token
|
||||
refute user.confirmation_pending
|
||||
refute user.confirmation_token
|
||||
end
|
||||
|
||||
test "user is not confirmed" do
|
||||
%{id: id, nickname: nickname} =
|
||||
insert(:user, confirmation_pending: true, confirmation_token: "some token")
|
||||
|
||||
assert :ok = Mix.Tasks.Pleroma.User.run(["toggle_confirmed", nickname])
|
||||
assert :ok = Mix.Tasks.Pleroma.User.run(["confirm", nickname])
|
||||
assert_received {:mix_shell, :info, [message]}
|
||||
assert message == "#{nickname} doesn't need confirmation."
|
||||
|
||||
|
@ -489,7 +489,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do
|
|||
end
|
||||
|
||||
test "it prints an error message when user is not exist" do
|
||||
Mix.Tasks.Pleroma.User.run(["toggle_confirmed", "foo"])
|
||||
Mix.Tasks.Pleroma.User.run(["confirm", "foo"])
|
||||
|
||||
assert_received {:mix_shell, :error, [message]}
|
||||
assert message =~ "No local user"
|
||||
|
@ -503,7 +503,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do
|
|||
moot = insert(:user, nickname: "moot")
|
||||
kawen = insert(:user, nickname: "kawen", name: "fediverse expert moon")
|
||||
|
||||
{:ok, user} = User.follow(user, moon)
|
||||
{:ok, user, moon} = User.follow(user, moon)
|
||||
|
||||
assert [moon.id, kawen.id] == User.Search.search("moon") |> Enum.map(& &1.id)
|
||||
|
||||
|
|
|
@ -197,6 +197,13 @@ defmodule Pleroma.ActivityTest do
|
|||
assert [%{id: ^id1, object: %Object{}}, %{id: ^id2, object: %Object{}}] = activities
|
||||
end
|
||||
|
||||
test "get_by_id_with_user_actor/1" do
|
||||
user = insert(:user)
|
||||
activity = insert(:note_activity, note: insert(:note, user: user))
|
||||
|
||||
assert Activity.get_by_id_with_user_actor(activity.id).user_actor == user
|
||||
end
|
||||
|
||||
test "get_by_id_with_object/1" do
|
||||
%{id: id} = insert(:note_activity)
|
||||
|
||||
|
|
|
@ -12,6 +12,25 @@ defmodule Pleroma.ApplicationRequirementsTest do
|
|||
alias Pleroma.Config
|
||||
alias Pleroma.Repo
|
||||
|
||||
describe "check_repo_pool_size!/1" do
|
||||
test "raises if the pool size is unexpected" do
|
||||
clear_config([Pleroma.Repo, :pool_size], 11)
|
||||
|
||||
assert_raise Pleroma.ApplicationRequirements.VerifyError,
|
||||
"Repo.pool_size different than recommended value.",
|
||||
fn ->
|
||||
capture_log(&Pleroma.ApplicationRequirements.verify!/0)
|
||||
end
|
||||
end
|
||||
|
||||
test "doesn't raise if the pool size is unexpected but the respective flag is set" do
|
||||
clear_config([Pleroma.Repo, :pool_size], 11)
|
||||
clear_config([:dangerzone, :override_repo_pool_size], true)
|
||||
|
||||
assert Pleroma.ApplicationRequirements.verify!() == :ok
|
||||
end
|
||||
end
|
||||
|
||||
describe "check_welcome_message_config!/1" do
|
||||
setup do: clear_config([:welcome])
|
||||
setup do: clear_config([Pleroma.Emails.Mailer])
|
||||
|
|
|
@ -19,7 +19,7 @@ defmodule Pleroma.BBS.HandlerTest do
|
|||
user = insert(:user)
|
||||
followed = insert(:user)
|
||||
|
||||
{:ok, user} = User.follow(user, followed)
|
||||
{:ok, user, followed} = User.follow(user, followed)
|
||||
|
||||
{:ok, _first} = CommonAPI.post(user, %{status: "hey"})
|
||||
{:ok, _second} = CommonAPI.post(followed, %{status: "hello"})
|
||||
|
|
|
@ -45,4 +45,15 @@ defmodule Pleroma.Emails.UserEmailTest do
|
|||
assert email.html_body =~
|
||||
Router.Helpers.confirm_email_url(Endpoint, :confirm_email, user.id, "conf-token")
|
||||
end
|
||||
|
||||
test "build approval pending email" do
|
||||
config = Pleroma.Config.get(:instance)
|
||||
user = insert(:user)
|
||||
email = UserEmail.approval_pending_email(user)
|
||||
|
||||
assert email.from == {config[:name], config[:notify_email]}
|
||||
assert email.to == [{user.name, user.email}]
|
||||
assert email.subject == "Your account is awaiting approval"
|
||||
assert email.html_body =~ "Awaiting Approval"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Instances.InstanceTest do
|
||||
alias Pleroma.Instances
|
||||
alias Pleroma.Instances.Instance
|
||||
alias Pleroma.Repo
|
||||
|
||||
|
@ -148,5 +149,13 @@ defmodule Pleroma.Instances.InstanceTest do
|
|||
)
|
||||
end) =~ "Instance.scrape_favicon(\"https://no-favicon.example.org/\") error: "
|
||||
end
|
||||
|
||||
test "Doesn't scrapes unreachable instances" do
|
||||
instance = insert(:instance, unreachable_since: Instances.reachability_datetime_threshold())
|
||||
url = "https://" <> instance.host
|
||||
|
||||
assert capture_log(fn -> assert nil == Instance.get_or_update_favicon(URI.parse(url)) end) =~
|
||||
"Instance.scrape_favicon(\"#{url}\") ignored unreachable host"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,9 +32,9 @@ defmodule Pleroma.InstancesTest do
|
|||
assert Instances.reachable?(URI.parse(url).host)
|
||||
end
|
||||
|
||||
test "returns true on non-binary input" do
|
||||
assert Instances.reachable?(nil)
|
||||
assert Instances.reachable?(1)
|
||||
test "raises FunctionClauseError exception on non-binary input" do
|
||||
assert_raise FunctionClauseError, fn -> Instances.reachable?(nil) end
|
||||
assert_raise FunctionClauseError, fn -> Instances.reachable?(1) end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -182,11 +182,14 @@ defmodule Pleroma.ModerationLogTest do
|
|||
end
|
||||
|
||||
test "logging report update", %{moderator: moderator} do
|
||||
user = insert(:user)
|
||||
|
||||
report = %Activity{
|
||||
id: "9m9I1F4p8ftrTP6QTI",
|
||||
data: %{
|
||||
"type" => "Flag",
|
||||
"state" => "resolved"
|
||||
"state" => "resolved",
|
||||
"actor" => user.ap_id
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,35 +197,48 @@ defmodule Pleroma.ModerationLogTest do
|
|||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
action: "report_update",
|
||||
subject: report
|
||||
subject: report,
|
||||
subject_actor: user
|
||||
})
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
|
||||
assert log.data["message"] ==
|
||||
"@#{moderator.nickname} updated report ##{report.id} with 'resolved' state"
|
||||
"@#{moderator.nickname} updated report ##{report.id} (on user @#{user.nickname}) with 'resolved' state"
|
||||
end
|
||||
|
||||
test "logging report response", %{moderator: moderator} do
|
||||
user = insert(:user)
|
||||
|
||||
report = %Activity{
|
||||
id: "9m9I1F4p8ftrTP6QTI",
|
||||
data: %{
|
||||
"type" => "Note"
|
||||
"type" => "Note",
|
||||
"actor" => user.ap_id
|
||||
}
|
||||
}
|
||||
|
||||
{:ok, _} =
|
||||
ModerationLog.insert_log(%{
|
||||
actor: moderator,
|
||||
action: "report_note",
|
||||
subject: report,
|
||||
text: "look at this"
|
||||
})
|
||||
attrs = %{
|
||||
actor: moderator,
|
||||
action: "report_note",
|
||||
subject: report,
|
||||
text: "look at this"
|
||||
}
|
||||
|
||||
log = Repo.one(ModerationLog)
|
||||
{:ok, log1} = ModerationLog.insert_log(attrs)
|
||||
log = Repo.get(ModerationLog, log1.id)
|
||||
|
||||
assert log.data["message"] ==
|
||||
"@#{moderator.nickname} added note 'look at this' to report ##{report.id}"
|
||||
|
||||
{:ok, log2} = ModerationLog.insert_log(Map.merge(attrs, %{subject_actor: user}))
|
||||
|
||||
log = Repo.get(ModerationLog, log2.id)
|
||||
|
||||
assert log.data["message"] ==
|
||||
"@#{moderator.nickname} added note 'look at this' to report ##{report.id} on user @#{
|
||||
user.nickname
|
||||
}"
|
||||
end
|
||||
|
||||
test "logging status sensitivity update", %{moderator: moderator} do
|
||||
|
|
|
@ -779,7 +779,7 @@ defmodule Pleroma.NotificationTest do
|
|||
other_user = insert(:user)
|
||||
|
||||
{:ok, other_user} = User.block_domain(other_user, blocked_domain)
|
||||
{:ok, other_user} = User.follow(other_user, user)
|
||||
{:ok, other_user, user} = User.follow(other_user, user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey @#{other_user.nickname}!"})
|
||||
|
||||
|
@ -1070,7 +1070,7 @@ defmodule Pleroma.NotificationTest do
|
|||
blocked = insert(:user, ap_id: "http://some-domain.com")
|
||||
|
||||
{:ok, user} = User.block_domain(user, "some-domain.com")
|
||||
{:ok, _} = User.follow(user, blocked)
|
||||
{:ok, _, _} = User.follow(user, blocked)
|
||||
|
||||
{:ok, _activity} = CommonAPI.post(blocked, %{status: "hey @#{user.nickname}"})
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ defmodule Pleroma.User.ImportTest do
|
|||
|
||||
assert {:ok, result} = ObanHelpers.perform(job)
|
||||
assert is_list(result)
|
||||
assert result == [user2, user3]
|
||||
assert result == [refresh_record(user2), refresh_record(user3)]
|
||||
assert User.following?(user1, user2)
|
||||
assert User.following?(user1, user3)
|
||||
end
|
||||
|
|
|
@ -151,8 +151,8 @@ defmodule Pleroma.UserSearchTest do
|
|||
follower = insert(:user, %{name: "Doe"})
|
||||
friend = insert(:user, %{name: "Doe"})
|
||||
|
||||
{:ok, follower} = User.follow(follower, u1)
|
||||
{:ok, u1} = User.follow(u1, friend)
|
||||
{:ok, follower, u1} = User.follow(follower, u1)
|
||||
{:ok, u1, friend} = User.follow(u1, friend)
|
||||
|
||||
assert [friend.id, follower.id, u2.id] --
|
||||
Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == []
|
||||
|
@ -165,9 +165,9 @@ defmodule Pleroma.UserSearchTest do
|
|||
following_jimi = insert(:user, %{name: "Lizz Wright"})
|
||||
follower_lizz = insert(:user, %{name: "Jimi"})
|
||||
|
||||
{:ok, lizz} = User.follow(lizz, following_lizz)
|
||||
{:ok, _jimi} = User.follow(jimi, following_jimi)
|
||||
{:ok, _follower_lizz} = User.follow(follower_lizz, lizz)
|
||||
{:ok, lizz, following_lizz} = User.follow(lizz, following_lizz)
|
||||
{:ok, _jimi, _following_jimi} = User.follow(jimi, following_jimi)
|
||||
{:ok, _follower_lizz, _lizz} = User.follow(follower_lizz, lizz)
|
||||
|
||||
assert Enum.map(User.search("jimi", following: true, for_user: lizz), & &1.id) == [
|
||||
following_lizz.id
|
||||
|
|
|
@ -233,7 +233,7 @@ defmodule Pleroma.UserTest do
|
|||
{:ok, _user_relationship} = User.block(user, blocked)
|
||||
{:ok, _user_relationship} = User.block(reverse_blocked, user)
|
||||
|
||||
{:ok, user} = User.follow(user, followed_zero)
|
||||
{:ok, user, followed_zero} = User.follow(user, followed_zero)
|
||||
|
||||
{:ok, user} = User.follow_all(user, [followed_one, followed_two, blocked, reverse_blocked])
|
||||
|
||||
|
@ -262,7 +262,7 @@ defmodule Pleroma.UserTest do
|
|||
user = insert(:user)
|
||||
followed = insert(:user)
|
||||
|
||||
{:ok, user} = User.follow(user, followed)
|
||||
{:ok, user, followed} = User.follow(user, followed)
|
||||
|
||||
user = User.get_cached_by_id(user.id)
|
||||
followed = User.get_cached_by_ap_id(followed.ap_id)
|
||||
|
@ -302,7 +302,7 @@ defmodule Pleroma.UserTest do
|
|||
follower = insert(:user, is_locked: true)
|
||||
followed = insert(:user, is_locked: true)
|
||||
|
||||
{:ok, follower} = User.maybe_direct_follow(follower, followed)
|
||||
{:ok, follower, followed} = User.maybe_direct_follow(follower, followed)
|
||||
|
||||
refute User.following?(follower, followed)
|
||||
end
|
||||
|
@ -330,7 +330,7 @@ defmodule Pleroma.UserTest do
|
|||
following_address: "http://localhost:4001/users/fuser2/following"
|
||||
})
|
||||
|
||||
{:ok, user} = User.follow(user, followed, :follow_accept)
|
||||
{:ok, user, followed} = User.follow(user, followed, :follow_accept)
|
||||
|
||||
{:ok, user, _activity} = User.unfollow(user, followed)
|
||||
|
||||
|
@ -343,7 +343,7 @@ defmodule Pleroma.UserTest do
|
|||
followed = insert(:user)
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, user} = User.follow(user, followed, :follow_accept)
|
||||
{:ok, user, followed} = User.follow(user, followed, :follow_accept)
|
||||
|
||||
assert User.following(user) == [user.follower_address, followed.follower_address]
|
||||
|
||||
|
@ -535,6 +535,22 @@ defmodule Pleroma.UserTest do
|
|||
|> assert_email_sent()
|
||||
end
|
||||
|
||||
test "sends a pending approval email" do
|
||||
clear_config([:instance, :account_approval_required], true)
|
||||
|
||||
{:ok, user} =
|
||||
User.register_changeset(%User{}, @full_user_data)
|
||||
|> User.register()
|
||||
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
assert_email_sent(
|
||||
from: Pleroma.Config.Helpers.sender(),
|
||||
to: {user.name, user.email},
|
||||
subject: "Your account is awaiting approval"
|
||||
)
|
||||
end
|
||||
|
||||
test "it requires an email, name, nickname and password, bio is optional when account_activation_required is enabled" do
|
||||
Pleroma.Config.put([:instance, :account_activation_required], true)
|
||||
|
||||
|
@ -911,8 +927,8 @@ defmodule Pleroma.UserTest do
|
|||
follower_two = insert(:user)
|
||||
not_follower = insert(:user)
|
||||
|
||||
{:ok, follower_one} = User.follow(follower_one, user)
|
||||
{:ok, follower_two} = User.follow(follower_two, user)
|
||||
{:ok, follower_one, user} = User.follow(follower_one, user)
|
||||
{:ok, follower_two, user} = User.follow(follower_two, user)
|
||||
|
||||
res = User.get_followers(user)
|
||||
|
||||
|
@ -927,8 +943,8 @@ defmodule Pleroma.UserTest do
|
|||
followed_two = insert(:user)
|
||||
not_followed = insert(:user)
|
||||
|
||||
{:ok, user} = User.follow(user, followed_one)
|
||||
{:ok, user} = User.follow(user, followed_two)
|
||||
{:ok, user, followed_one} = User.follow(user, followed_one)
|
||||
{:ok, user, followed_two} = User.follow(user, followed_two)
|
||||
|
||||
res = User.get_friends(user)
|
||||
|
||||
|
@ -1098,8 +1114,8 @@ defmodule Pleroma.UserTest do
|
|||
blocker = insert(:user)
|
||||
blocked = insert(:user)
|
||||
|
||||
{:ok, blocker} = User.follow(blocker, blocked)
|
||||
{:ok, blocked} = User.follow(blocked, blocker)
|
||||
{:ok, blocker, blocked} = User.follow(blocker, blocked)
|
||||
{:ok, blocked, blocker} = User.follow(blocked, blocker)
|
||||
|
||||
assert User.following?(blocker, blocked)
|
||||
assert User.following?(blocked, blocker)
|
||||
|
@ -1117,7 +1133,7 @@ defmodule Pleroma.UserTest do
|
|||
blocker = insert(:user)
|
||||
blocked = insert(:user)
|
||||
|
||||
{:ok, blocker} = User.follow(blocker, blocked)
|
||||
{:ok, blocker, blocked} = User.follow(blocker, blocked)
|
||||
|
||||
assert User.following?(blocker, blocked)
|
||||
refute User.following?(blocked, blocker)
|
||||
|
@ -1135,7 +1151,7 @@ defmodule Pleroma.UserTest do
|
|||
blocker = insert(:user)
|
||||
blocked = insert(:user)
|
||||
|
||||
{:ok, blocked} = User.follow(blocked, blocker)
|
||||
{:ok, blocked, blocker} = User.follow(blocked, blocker)
|
||||
|
||||
refute User.following?(blocker, blocked)
|
||||
assert User.following?(blocked, blocker)
|
||||
|
@ -1233,7 +1249,7 @@ defmodule Pleroma.UserTest do
|
|||
good_eggo = insert(:user, %{ap_id: "https://meanies.social/user/cuteposter"})
|
||||
|
||||
{:ok, user} = User.block_domain(user, "meanies.social")
|
||||
{:ok, user} = User.follow(user, good_eggo)
|
||||
{:ok, user, good_eggo} = User.follow(user, good_eggo)
|
||||
|
||||
refute User.blocks?(user, good_eggo)
|
||||
end
|
||||
|
@ -1267,8 +1283,8 @@ defmodule Pleroma.UserTest do
|
|||
assert Enum.map([actor, addressed], & &1.ap_id) --
|
||||
Enum.map(User.get_recipients_from_activity(activity), & &1.ap_id) == []
|
||||
|
||||
{:ok, user} = User.follow(user, actor)
|
||||
{:ok, _user_two} = User.follow(user_two, actor)
|
||||
{:ok, user, actor} = User.follow(user, actor)
|
||||
{:ok, _user_two, _actor} = User.follow(user_two, actor)
|
||||
recipients = User.get_recipients_from_activity(activity)
|
||||
assert length(recipients) == 3
|
||||
assert user in recipients
|
||||
|
@ -1289,8 +1305,8 @@ defmodule Pleroma.UserTest do
|
|||
assert Enum.map([actor, addressed], & &1.ap_id) --
|
||||
Enum.map(User.get_recipients_from_activity(activity), & &1.ap_id) == []
|
||||
|
||||
{:ok, _actor} = User.follow(actor, user)
|
||||
{:ok, _actor} = User.follow(actor, user_two)
|
||||
{:ok, _actor, _user} = User.follow(actor, user)
|
||||
{:ok, _actor, _user_two} = User.follow(actor, user_two)
|
||||
recipients = User.get_recipients_from_activity(activity)
|
||||
assert length(recipients) == 2
|
||||
assert addressed in recipients
|
||||
|
@ -1311,7 +1327,7 @@ defmodule Pleroma.UserTest do
|
|||
user = insert(:user)
|
||||
user2 = insert(:user)
|
||||
|
||||
{:ok, user} = User.follow(user, user2)
|
||||
{:ok, user, user2} = User.follow(user, user2)
|
||||
{:ok, _user} = User.deactivate(user)
|
||||
|
||||
user2 = User.get_cached_by_id(user2.id)
|
||||
|
@ -1324,7 +1340,7 @@ defmodule Pleroma.UserTest do
|
|||
user = insert(:user)
|
||||
user2 = insert(:user)
|
||||
|
||||
{:ok, user2} = User.follow(user2, user)
|
||||
{:ok, user2, user} = User.follow(user2, user)
|
||||
assert user2.following_count == 1
|
||||
assert User.following_count(user2) == 1
|
||||
|
||||
|
@ -1342,7 +1358,7 @@ defmodule Pleroma.UserTest do
|
|||
user = insert(:user)
|
||||
user2 = insert(:user)
|
||||
|
||||
{:ok, user2} = User.follow(user2, user)
|
||||
{:ok, user2, user} = User.follow(user2, user)
|
||||
|
||||
{:ok, activity} = CommonAPI.post(user, %{status: "hey @#{user2.nickname}"})
|
||||
|
||||
|
@ -1393,6 +1409,98 @@ defmodule Pleroma.UserTest do
|
|||
assert false == user.approval_pending
|
||||
end)
|
||||
end
|
||||
|
||||
test "it sends welcome email if it is set" do
|
||||
clear_config([:welcome, :email, :enabled], true)
|
||||
clear_config([:welcome, :email, :sender], "tester@test.me")
|
||||
|
||||
user = insert(:user, approval_pending: true)
|
||||
welcome_user = insert(:user, email: "tester@test.me")
|
||||
instance_name = Pleroma.Config.get([:instance, :name])
|
||||
|
||||
User.approve(user)
|
||||
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
assert_email_sent(
|
||||
from: {instance_name, welcome_user.email},
|
||||
to: {user.name, user.email},
|
||||
html_body: "Welcome to #{instance_name}"
|
||||
)
|
||||
end
|
||||
|
||||
test "approving an approved user does not trigger post-register actions" do
|
||||
clear_config([:welcome, :email, :enabled], true)
|
||||
|
||||
user = insert(:user, approval_pending: false)
|
||||
User.approve(user)
|
||||
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
assert_no_email_sent()
|
||||
end
|
||||
end
|
||||
|
||||
describe "confirm" do
|
||||
test "confirms a user" do
|
||||
user = insert(:user, confirmation_pending: true)
|
||||
assert true == user.confirmation_pending
|
||||
{:ok, user} = User.confirm(user)
|
||||
assert false == user.confirmation_pending
|
||||
end
|
||||
|
||||
test "confirms a list of users" do
|
||||
unconfirmed_users = [
|
||||
insert(:user, confirmation_pending: true),
|
||||
insert(:user, confirmation_pending: true),
|
||||
insert(:user, confirmation_pending: true)
|
||||
]
|
||||
|
||||
{:ok, users} = User.confirm(unconfirmed_users)
|
||||
|
||||
assert Enum.count(users) == 3
|
||||
|
||||
Enum.each(users, fn user ->
|
||||
assert false == user.confirmation_pending
|
||||
end)
|
||||
end
|
||||
|
||||
test "sends approval emails when `approval_pending: true`" do
|
||||
admin = insert(:user, is_admin: true)
|
||||
user = insert(:user, confirmation_pending: true, approval_pending: true)
|
||||
User.confirm(user)
|
||||
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
user_email = Pleroma.Emails.UserEmail.approval_pending_email(user)
|
||||
admin_email = Pleroma.Emails.AdminEmail.new_unapproved_registration(admin, user)
|
||||
|
||||
notify_email = Pleroma.Config.get([:instance, :notify_email])
|
||||
instance_name = Pleroma.Config.get([:instance, :name])
|
||||
|
||||
# User approval email
|
||||
assert_email_sent(
|
||||
from: {instance_name, notify_email},
|
||||
to: {user.name, user.email},
|
||||
html_body: user_email.html_body
|
||||
)
|
||||
|
||||
# Admin email
|
||||
assert_email_sent(
|
||||
from: {instance_name, notify_email},
|
||||
to: {admin.name, admin.email},
|
||||
html_body: admin_email.html_body
|
||||
)
|
||||
end
|
||||
|
||||
test "confirming a confirmed user does not trigger post-register actions" do
|
||||
user = insert(:user, confirmation_pending: false, approval_pending: true)
|
||||
User.confirm(user)
|
||||
|
||||
ObanHelpers.perform_all()
|
||||
|
||||
assert_no_email_sent()
|
||||
end
|
||||
end
|
||||
|
||||
describe "delete" do
|
||||
|
@ -1415,10 +1523,10 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
test "it deactivates a user, all follow relationships and all activities", %{user: user} do
|
||||
follower = insert(:user)
|
||||
{:ok, follower} = User.follow(follower, user)
|
||||
{:ok, follower, user} = User.follow(follower, user)
|
||||
|
||||
locked_user = insert(:user, name: "locked", is_locked: true)
|
||||
{:ok, _} = User.follow(user, locked_user, :follow_pending)
|
||||
{:ok, _, _} = User.follow(user, locked_user, :follow_pending)
|
||||
|
||||
object = insert(:note, user: user)
|
||||
activity = insert(:note_activity, user: user, note: object)
|
||||
|
@ -1776,9 +1884,9 @@ defmodule Pleroma.UserTest do
|
|||
follower2 = insert(:user)
|
||||
follower3 = insert(:user)
|
||||
|
||||
{:ok, follower} = User.follow(follower, user)
|
||||
{:ok, _follower2} = User.follow(follower2, user)
|
||||
{:ok, _follower3} = User.follow(follower3, user)
|
||||
{:ok, follower, user} = User.follow(follower, user)
|
||||
{:ok, _follower2, _user} = User.follow(follower2, user)
|
||||
{:ok, _follower3, _user} = User.follow(follower3, user)
|
||||
|
||||
{:ok, _user_relationship} = User.block(user, follower)
|
||||
user = refresh_record(user)
|
||||
|
@ -1889,24 +1997,6 @@ defmodule Pleroma.UserTest do
|
|||
end
|
||||
end
|
||||
|
||||
describe "toggle_confirmation/1" do
|
||||
test "if user is confirmed" do
|
||||
user = insert(:user, confirmation_pending: false)
|
||||
{:ok, user} = User.toggle_confirmation(user)
|
||||
|
||||
assert user.confirmation_pending
|
||||
assert user.confirmation_token
|
||||
end
|
||||
|
||||
test "if user is unconfirmed" do
|
||||
user = insert(:user, confirmation_pending: true, confirmation_token: "some token")
|
||||
{:ok, user} = User.toggle_confirmation(user)
|
||||
|
||||
refute user.confirmation_pending
|
||||
refute user.confirmation_token
|
||||
end
|
||||
end
|
||||
|
||||
describe "ensure_keys_present" do
|
||||
test "it creates keys for a user and stores them in info" do
|
||||
user = insert(:user)
|
||||
|
@ -2019,8 +2109,7 @@ defmodule Pleroma.UserTest do
|
|||
assert other_user.following_count == 0
|
||||
assert other_user.follower_count == 0
|
||||
|
||||
{:ok, user} = Pleroma.User.follow(user, other_user)
|
||||
other_user = Pleroma.User.get_by_id(other_user.id)
|
||||
{:ok, user, other_user} = Pleroma.User.follow(user, other_user)
|
||||
|
||||
assert user.following_count == 1
|
||||
assert other_user.follower_count == 1
|
||||
|
@ -2043,8 +2132,7 @@ defmodule Pleroma.UserTest do
|
|||
assert other_user.follower_count == 0
|
||||
|
||||
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
||||
{:ok, _user} = User.follow(user, other_user)
|
||||
other_user = User.get_by_id(other_user.id)
|
||||
{:ok, _user, other_user} = User.follow(user, other_user)
|
||||
|
||||
assert other_user.follower_count == 437
|
||||
end
|
||||
|
@ -2066,7 +2154,7 @@ defmodule Pleroma.UserTest do
|
|||
assert other_user.follower_count == 0
|
||||
|
||||
Pleroma.Config.put([:instance, :external_user_synchronization], true)
|
||||
{:ok, other_user} = User.follow(other_user, user)
|
||||
{:ok, other_user, _user} = User.follow(other_user, user)
|
||||
|
||||
assert other_user.following_count == 152
|
||||
end
|
||||
|
@ -2178,4 +2266,9 @@ defmodule Pleroma.UserTest do
|
|||
|
||||
assert User.avatar_url(user, no_default: true) == nil
|
||||
end
|
||||
|
||||
test "get_host/1" do
|
||||
user = insert(:user, ap_id: "https://lain.com/users/lain", nickname: "lain")
|
||||
assert User.get_host(user) == "lain.com"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -675,7 +675,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
|||
recipient = insert(:user)
|
||||
actor = insert(:user, %{ap_id: "http://mastodon.example.org/users/actor"})
|
||||
|
||||
{:ok, recipient} = User.follow(recipient, actor)
|
||||
{:ok, recipient, actor} = User.follow(recipient, actor)
|
||||
|
||||
object =
|
||||
data["object"]
|
||||
|
|
|
@ -726,7 +726,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
domain_user = insert(:user, %{ap_id: "https://#{domain}/@pundit"})
|
||||
blocker = insert(:user)
|
||||
|
||||
{:ok, blocker} = User.follow(blocker, domain_user)
|
||||
{:ok, blocker, domain_user} = User.follow(blocker, domain_user)
|
||||
{:ok, blocker} = User.block_domain(blocker, domain)
|
||||
|
||||
assert User.following?(blocker, domain_user)
|
||||
|
@ -853,7 +853,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
user = insert(:user)
|
||||
booster = insert(:user)
|
||||
|
||||
{:ok, user} = User.follow(user, booster)
|
||||
{:ok, user, booster} = User.follow(user, booster)
|
||||
|
||||
{:ok, announce} = CommonAPI.repeat(activity_three.id, booster)
|
||||
|
||||
|
@ -1158,13 +1158,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
user2 = insert(:user)
|
||||
user3 = insert(:user)
|
||||
|
||||
{:ok, user1} = User.follow(user1, user3)
|
||||
{:ok, user1, user3} = User.follow(user1, user3)
|
||||
assert User.following?(user1, user3)
|
||||
|
||||
{:ok, user2} = User.follow(user2, user3)
|
||||
{:ok, user2, user3} = User.follow(user2, user3)
|
||||
assert User.following?(user2, user3)
|
||||
|
||||
{:ok, user3} = User.follow(user3, user2)
|
||||
{:ok, user3, user2} = User.follow(user3, user2)
|
||||
assert User.following?(user3, user2)
|
||||
|
||||
{:ok, public_activity} = CommonAPI.post(user3, %{status: "hi 1"})
|
||||
|
@ -1931,13 +1931,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
|
||||
defp public_messages(_) do
|
||||
[u1, u2, u3, u4] = insert_list(4, :user)
|
||||
{:ok, u1} = User.follow(u1, u2)
|
||||
{:ok, u2} = User.follow(u2, u1)
|
||||
{:ok, u1} = User.follow(u1, u4)
|
||||
{:ok, u4} = User.follow(u4, u1)
|
||||
{:ok, u1, u2} = User.follow(u1, u2)
|
||||
{:ok, u2, u1} = User.follow(u2, u1)
|
||||
{:ok, u1, u4} = User.follow(u1, u4)
|
||||
{:ok, u4, u1} = User.follow(u4, u1)
|
||||
|
||||
{:ok, u2} = User.follow(u2, u3)
|
||||
{:ok, u3} = User.follow(u3, u2)
|
||||
{:ok, u2, u3} = User.follow(u2, u3)
|
||||
{:ok, u3, u2} = User.follow(u3, u2)
|
||||
|
||||
{:ok, a1} = CommonAPI.post(u1, %{status: "Status"})
|
||||
|
||||
|
@ -2030,15 +2030,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
|||
|
||||
defp private_messages(_) do
|
||||
[u1, u2, u3, u4] = insert_list(4, :user)
|
||||
{:ok, u1} = User.follow(u1, u2)
|
||||
{:ok, u2} = User.follow(u2, u1)
|
||||
{:ok, u1} = User.follow(u1, u3)
|
||||
{:ok, u3} = User.follow(u3, u1)
|
||||
{:ok, u1} = User.follow(u1, u4)
|
||||
{:ok, u4} = User.follow(u4, u1)
|
||||
{:ok, u1, u2} = User.follow(u1, u2)
|
||||
{:ok, u2, u1} = User.follow(u2, u1)
|
||||
{:ok, u1, u3} = User.follow(u1, u3)
|
||||
{:ok, u3, u1} = User.follow(u3, u1)
|
||||
{:ok, u1, u4} = User.follow(u1, u4)
|
||||
{:ok, u4, u1} = User.follow(u4, u1)
|
||||
|
||||
{:ok, u2} = User.follow(u2, u3)
|
||||
{:ok, u3} = User.follow(u3, u2)
|
||||
{:ok, u2, u3} = User.follow(u2, u3)
|
||||
{:ok, u3, u2} = User.follow(u3, u2)
|
||||
|
||||
{:ok, a1} = CommonAPI.post(u1, %{status: "Status", visibility: "private"})
|
||||
|
||||
|
|
|
@ -281,8 +281,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do
|
|||
actor = insert(:user, follower_address: follower.ap_id)
|
||||
user = insert(:user)
|
||||
|
||||
{:ok, _follower_one} = Pleroma.User.follow(follower, actor)
|
||||
actor = refresh_record(actor)
|
||||
{:ok, follower, actor} = Pleroma.User.follow(follower, actor)
|
||||
|
||||
note_activity =
|
||||
insert(:note_activity,
|
||||
|
|
|
@ -15,7 +15,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AcceptHandlingTest do
|
|||
follower = insert(:user)
|
||||
followed = insert(:user)
|
||||
|
||||
{:ok, follower} = User.follow(follower, followed)
|
||||
{:ok, follower, followed} = User.follow(follower, followed)
|
||||
assert User.following?(follower, followed) == true
|
||||
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
|
||||
|
|
|
@ -40,8 +40,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.BlockHandlingTest do
|
|||
|> Map.put("object", blocked.ap_id)
|
||||
|> Map.put("actor", blocker.ap_id)
|
||||
|
||||
{:ok, blocker} = User.follow(blocker, blocked)
|
||||
{:ok, blocked} = User.follow(blocked, blocker)
|
||||
{:ok, blocker, blocked} = User.follow(blocker, blocked)
|
||||
{:ok, blocked, blocker} = User.follow(blocked, blocker)
|
||||
|
||||
assert User.following?(blocker, blocked)
|
||||
assert User.following?(blocked, blocker)
|
||||
|
|
|
@ -35,7 +35,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.RejectHandlingTest do
|
|||
follower = insert(:user)
|
||||
followed = insert(:user, is_locked: true)
|
||||
|
||||
{:ok, follower} = User.follow(follower, followed)
|
||||
{:ok, follower, followed} = User.follow(follower, followed)
|
||||
{:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed)
|
||||
|
||||
assert User.following?(follower, followed) == true
|
||||
|
|
|
@ -15,7 +15,7 @@ defmodule Pleroma.Web.ActivityPub.VisibilityTest do
|
|||
mentioned = insert(:user)
|
||||
following = insert(:user)
|
||||
unrelated = insert(:user)
|
||||
{:ok, following} = Pleroma.User.follow(following, user)
|
||||
{:ok, following, user} = Pleroma.User.follow(following, user)
|
||||
{:ok, list} = Pleroma.List.create("foo", user)
|
||||
|
||||
Pleroma.List.follow(list, unrelated)
|
||||
|
|
|
@ -941,7 +941,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|
||||
describe "/api/pleroma/admin/stats" do
|
||||
test "status visibility count", %{conn: conn} do
|
||||
admin = insert(:user, is_admin: true)
|
||||
user = insert(:user)
|
||||
CommonAPI.post(user, %{visibility: "public", status: "hey"})
|
||||
CommonAPI.post(user, %{visibility: "unlisted", status: "hey"})
|
||||
|
@ -949,7 +948,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|
||||
response =
|
||||
conn
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/stats")
|
||||
|> json_response(200)
|
||||
|
||||
|
@ -958,7 +956,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
end
|
||||
|
||||
test "by instance", %{conn: conn} do
|
||||
admin = insert(:user, is_admin: true)
|
||||
user1 = insert(:user)
|
||||
instance2 = "instance2.tld"
|
||||
user2 = insert(:user, %{ap_id: "https://#{instance2}/@actor"})
|
||||
|
@ -969,7 +966,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
|
|||
|
||||
response =
|
||||
conn
|
||||
|> assign(:user, admin)
|
||||
|> get("/api/pleroma/admin/stats", instance: instance2)
|
||||
|> json_response(200)
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue