Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into simplePolicy_reasons_for_instance_specific_policies
This commit is contained in:
commit
95d5589df6
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@
|
||||
/db
|
||||
/deps
|
||||
/*.ez
|
||||
/test/instance
|
||||
/test/uploads
|
||||
/.elixir_ls
|
||||
/test/fixtures/DSCN0010_tmp.jpg
|
||||
|
45
CHANGELOG.md
45
CHANGELOG.md
@ -10,24 +10,28 @@ 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.
|
||||
- **Breaking** Besides only the instance, entries for simple_policy, transparency_exclusions and quarantined_instances now contain a reason as well.
|
||||
- **Breaking** Besides only the instance, entries for simple_policy, transparency_exclusions and
|
||||
- Admin API: Reports now ordered by newest
|
||||
|
||||
### Added
|
||||
|
||||
- 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`
|
||||
- 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.
|
||||
- Ability to set ActivityPub aliases for follower migration.
|
||||
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
@ -35,13 +39,15 @@ 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
|
||||
- Streaming API: Add follow relationships updates
|
||||
- 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.
|
||||
- Creating incorrect IPv4 address-style HTTP links when encountering certain numbers.
|
||||
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
@ -51,24 +57,35 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## Unreleased (Patch)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix ability to update Pleroma Chat push notifications with PUT /api/v1/push/subscription and alert type pleroma:chat_mention
|
||||
- Emoji Reaction activity filtering from blocked and muted accounts.
|
||||
- StealEmojiPolicy creates dir for emojis, if it doesn't exist.
|
||||
|
||||
## [2.2.1] - 2020-12-22
|
||||
|
||||
### Changed
|
||||
- Updated Pleroma FE
|
||||
|
||||
### Fixed
|
||||
|
||||
- Config generation: rename `Pleroma.Upload.Filter.ExifTool` to `Pleroma.Upload.Filter.Exiftool`.
|
||||
- Search: RUM index search speed has been fixed.
|
||||
- S3 Uploads with Elixir 1.11.
|
||||
- Emoji Reaction activity filtering from blocked and muted accounts.
|
||||
- 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.
|
||||
- Search: RUM index search speed has been fixed.
|
||||
- Rich Media Previews sometimes showed the wrong preview due to a bug following redirects.
|
||||
- Fixes for the autolinker.
|
||||
- Forwarded reports duplication from Pleroma instances.
|
||||
|
||||
<details>
|
||||
<summary>API</summary>
|
||||
- Statuses were not displayed for Mastodon forwarded reports.
|
||||
- <details>
|
||||
<summary>API</summary>
|
||||
- Statuses were not displayed for Mastodon forwarded reports.
|
||||
</details>
|
||||
|
||||
</details>
|
||||
### Upgrade notes
|
||||
|
||||
1. Restart Pleroma
|
||||
|
||||
## [2.2.0] - 2020-11-12
|
||||
|
||||
@ -106,7 +123,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.
|
||||
|
||||
|
@ -47,7 +47,6 @@ use Mix.Config
|
||||
config :pleroma, ecto_repos: [Pleroma.Repo]
|
||||
|
||||
config :pleroma, Pleroma.Repo,
|
||||
types: Pleroma.PostgresTypes,
|
||||
telemetry_event: [Pleroma.Repo.Instrumenter],
|
||||
migration_lock: nil
|
||||
|
||||
@ -64,14 +63,6 @@ config :pleroma, Pleroma.Upload,
|
||||
filters: [Pleroma.Upload.Filter.Dedupe],
|
||||
link_name: false,
|
||||
proxy_remote: false,
|
||||
proxy_opts: [
|
||||
redirect_on_failure: false,
|
||||
max_body_length: 25 * 1_048_576,
|
||||
http: [
|
||||
follow_redirect: true,
|
||||
pool: :upload
|
||||
]
|
||||
],
|
||||
filename_display_max_length: 30,
|
||||
default_description: nil
|
||||
|
||||
@ -306,7 +297,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 +334,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 +639,7 @@ config :pleroma, :email_notifications,
|
||||
}
|
||||
|
||||
config :pleroma, :oauth2,
|
||||
token_expires_in: 3600 * 24 * 30,
|
||||
token_expires_in: 3600 * 24 * 365 * 100,
|
||||
issue_new_refresh_token: true,
|
||||
clean_expired_tokens: false
|
||||
|
||||
|
@ -101,74 +101,10 @@ config :pleroma, :config_description, [
|
||||
%{
|
||||
key: :proxy_remote,
|
||||
type: :boolean,
|
||||
description:
|
||||
"If enabled, requests to media stored using a remote uploader will be proxied instead of being redirected"
|
||||
},
|
||||
%{
|
||||
key: :proxy_opts,
|
||||
label: "Proxy Options",
|
||||
type: :keyword,
|
||||
description: "Options for Pleroma.ReverseProxy",
|
||||
suggestions: [
|
||||
redirect_on_failure: false,
|
||||
max_body_length: 25 * 1_048_576,
|
||||
http: [
|
||||
follow_redirect: true,
|
||||
pool: :media
|
||||
]
|
||||
],
|
||||
children: [
|
||||
%{
|
||||
key: :redirect_on_failure,
|
||||
type: :boolean,
|
||||
description:
|
||||
"Redirects the client to the real remote URL if there's any HTTP errors. " <>
|
||||
"Any error during body processing will not be redirected as the response is chunked."
|
||||
},
|
||||
%{
|
||||
key: :max_body_length,
|
||||
type: :integer,
|
||||
description:
|
||||
"Limits the content length to be approximately the " <>
|
||||
"specified length. It is validated with the `content-length` header and also verified when proxying."
|
||||
},
|
||||
%{
|
||||
key: :http,
|
||||
label: "HTTP",
|
||||
type: :keyword,
|
||||
description: "HTTP options",
|
||||
children: [
|
||||
%{
|
||||
key: :adapter,
|
||||
type: :keyword,
|
||||
description: "Adapter specific options",
|
||||
children: [
|
||||
%{
|
||||
key: :ssl_options,
|
||||
type: :keyword,
|
||||
label: "SSL Options",
|
||||
description: "SSL options for HTTP adapter",
|
||||
children: [
|
||||
%{
|
||||
key: :versions,
|
||||
type: {:list, :atom},
|
||||
description: "List of TLS versions to use",
|
||||
suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :proxy_url,
|
||||
label: "Proxy URL",
|
||||
type: [:string, :tuple],
|
||||
description: "Proxy URL",
|
||||
suggestions: ["127.0.0.1:8123", {:socks5, :localhost, 9050}]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
description: """
|
||||
Proxy requests to the remote uploader.\n
|
||||
Useful if media upload endpoint is not internet accessible.
|
||||
"""
|
||||
},
|
||||
%{
|
||||
key: :filename_display_max_length,
|
||||
@ -1256,7 +1192,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,
|
||||
@ -1342,7 +1278,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,
|
||||
@ -1552,7 +1488,7 @@ config :pleroma, :config_description, [
|
||||
%{
|
||||
key: :enabled,
|
||||
type: :boolean,
|
||||
description: "Enables proxying of remote media to the instance's proxy"
|
||||
description: "Enables proxying of remote media via the instance's proxy"
|
||||
},
|
||||
%{
|
||||
key: :base_url,
|
||||
@ -1589,80 +1525,41 @@ config :pleroma, :config_description, [
|
||||
},
|
||||
%{
|
||||
key: :proxy_opts,
|
||||
label: "Proxy Options",
|
||||
label: "Advanced MediaProxy Options",
|
||||
type: :keyword,
|
||||
description: "Options for Pleroma.ReverseProxy",
|
||||
description: "Internal Pleroma.ReverseProxy settings",
|
||||
suggestions: [
|
||||
redirect_on_failure: false,
|
||||
max_body_length: 25 * 1_048_576,
|
||||
max_read_duration: 30_000,
|
||||
http: [
|
||||
follow_redirect: true,
|
||||
pool: :media
|
||||
]
|
||||
max_read_duration: 30_000
|
||||
],
|
||||
children: [
|
||||
%{
|
||||
key: :redirect_on_failure,
|
||||
type: :boolean,
|
||||
description:
|
||||
"Redirects the client to the real remote URL if there's any HTTP errors. " <>
|
||||
"Any error during body processing will not be redirected as the response is chunked."
|
||||
description: """
|
||||
Redirects the client to the origin server upon encountering HTTP errors.\n
|
||||
Note that files larger than Max Body Length will trigger an error. (e.g., Peertube videos)\n\n
|
||||
**WARNING:** This setting will allow larger files to be accessed, but exposes the\n
|
||||
IP addresses of your users to the other servers, bypassing the MediaProxy.
|
||||
"""
|
||||
},
|
||||
%{
|
||||
key: :max_body_length,
|
||||
type: :integer,
|
||||
description:
|
||||
"Limits the content length to be approximately the " <>
|
||||
"specified length. It is validated with the `content-length` header and also verified when proxying."
|
||||
description: "Maximum file size allowed through the Pleroma MediaProxy cache."
|
||||
},
|
||||
%{
|
||||
key: :max_read_duration,
|
||||
type: :integer,
|
||||
description: "Timeout (in milliseconds) of GET request to remote URI."
|
||||
},
|
||||
%{
|
||||
key: :http,
|
||||
label: "HTTP",
|
||||
type: :keyword,
|
||||
description: "HTTP options",
|
||||
children: [
|
||||
%{
|
||||
key: :adapter,
|
||||
type: :keyword,
|
||||
description: "Adapter specific options",
|
||||
children: [
|
||||
%{
|
||||
key: :ssl_options,
|
||||
type: :keyword,
|
||||
label: "SSL Options",
|
||||
description: "SSL options for HTTP adapter",
|
||||
children: [
|
||||
%{
|
||||
key: :versions,
|
||||
type: {:list, :atom},
|
||||
description: "List of TLS version to use",
|
||||
suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :proxy_url,
|
||||
label: "Proxy URL",
|
||||
type: [:string, :tuple],
|
||||
description: "Proxy URL",
|
||||
suggestions: ["127.0.0.1:8123", {:socks5, :localhost, 9050}]
|
||||
}
|
||||
]
|
||||
description: "Timeout (in milliseconds) of GET request to the remote URI."
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :whitelist,
|
||||
type: {:list, :string},
|
||||
description: "List of hosts with scheme to bypass the mediaproxy",
|
||||
description: "List of hosts with scheme to bypass the MediaProxy",
|
||||
suggestions: ["http://example.com"]
|
||||
}
|
||||
]
|
||||
@ -1955,14 +1852,8 @@ config :pleroma, :config_description, [
|
||||
group: :pleroma,
|
||||
key: Oban,
|
||||
type: :group,
|
||||
description: """
|
||||
[Oban](https://github.com/sorentwo/oban) asynchronous job processor configuration.
|
||||
|
||||
Note: if you are running PostgreSQL in [`silent_mode`](https://postgresqlco.nf/en/doc/param/silent_mode?version=9.1),
|
||||
it's advised to set [`log_destination`](https://postgresqlco.nf/en/doc/param/log_destination?version=9.1) to `syslog`,
|
||||
otherwise `postmaster.log` file may grow because of "you don't own a lock of type ShareLock" warnings
|
||||
(see https://github.com/sorentwo/oban/issues/52).
|
||||
""",
|
||||
description:
|
||||
"[Oban](https://github.com/sorentwo/oban) asynchronous job processor configuration.",
|
||||
children: [
|
||||
%{
|
||||
key: :log,
|
||||
|
@ -1,2 +1,4 @@
|
||||
firefox, /emoji/Firefox.gif, Gif,Fun
|
||||
blank, /emoji/blank.png, Fun
|
||||
dinosaur, /emoji/dino walking.gif, Gif
|
||||
external_emoji, https://example.com/emoji.png
|
||||
|
@ -47,7 +47,10 @@ config :pleroma, Pleroma.Repo,
|
||||
password: "postgres",
|
||||
database: "pleroma_test",
|
||||
hostname: System.get_env("DB_HOST") || "localhost",
|
||||
pool: Ecto.Adapters.SQL.Sandbox
|
||||
pool: Ecto.Adapters.SQL.Sandbox,
|
||||
pool_size: 50
|
||||
|
||||
config :pleroma, :dangerzone, override_repo_pool_size: true
|
||||
|
||||
# Reduce hash rounds for testing
|
||||
config :pbkdf2_elixir, rounds: 1
|
||||
@ -121,6 +124,20 @@ config :tzdata, :autoupdate, :disabled
|
||||
|
||||
config :pleroma, :mrf, policies: []
|
||||
|
||||
config :pleroma, :pipeline,
|
||||
object_validator: Pleroma.Web.ActivityPub.ObjectValidatorMock,
|
||||
mrf: Pleroma.Web.ActivityPub.MRFMock,
|
||||
activity_pub: Pleroma.Web.ActivityPub.ActivityPubMock,
|
||||
side_effects: Pleroma.Web.ActivityPub.SideEffectsMock,
|
||||
federator: Pleroma.Web.FederatorMock,
|
||||
config: Pleroma.ConfigMock
|
||||
|
||||
config :pleroma, :cachex, provider: Pleroma.CachexMock
|
||||
|
||||
config :pleroma, :side_effects,
|
||||
ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock,
|
||||
logger: Pleroma.LoggerMock
|
||||
|
||||
if File.exists?("./config/test.secret.exs") do
|
||||
import_config "test.secret.exs"
|
||||
else
|
||||
|
@ -1123,6 +1123,7 @@ Loads json generated from `config/descriptions.exs`.
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1234,
|
||||
"data": {
|
||||
"actor": {
|
||||
"id": 1,
|
||||
|
@ -206,6 +206,7 @@ Additional parameters can be added to the JSON body/Form data:
|
||||
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
|
||||
- `skip_thread_containment` - if true, skip filtering out broken threads
|
||||
- `allow_following_move` - if true, allows automatically follow moved following accounts
|
||||
- `also_known_as` - array of ActivityPub IDs, needed for following move
|
||||
- `pleroma_background_image` - sets the background image of the user. Can be set to "" (an empty string) to reset.
|
||||
- `discoverable` - if true, external services (search bots) etc. are allowed to index / list the account (regardless of this setting, user will still appear in regular search results).
|
||||
- `actor_type` - the type of this account.
|
||||
|
@ -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
|
||||
|
@ -9,29 +9,32 @@ static_dir="instance/static"
|
||||
# project_branch="pleroma"
|
||||
# static_dir="priv/static"
|
||||
|
||||
if [[ ! -d "${static_dir}" ]]
|
||||
if [ ! -d "${static_dir}" ]
|
||||
then
|
||||
echo "Error: ${static_dir} directory is missing, are you sure you are running this script at the root of pleroma’s repository?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
last_modified="$(curl -s -I 'https://git.pleroma.social/api/v4/projects/'${project_id}'/jobs/artifacts/'${project_branch}'/download?job=build' | grep '^Last-Modified:' | cut -d: -f2-)"
|
||||
last_modified="$(curl --fail -s -I 'https://git.pleroma.social/api/v4/projects/'${project_id}'/jobs/artifacts/'${project_branch}'/download?job=build' | grep '^Last-Modified:' | cut -d: -f2-)"
|
||||
|
||||
echo "branch:${project_branch}"
|
||||
echo "Last-Modified:${last_modified}"
|
||||
|
||||
artifact="mastofe.zip"
|
||||
|
||||
if [[ -e mastofe.timestamp ]] && [[ "${last_modified}" != "" ]]
|
||||
if [ "${last_modified}x" = "x" ]
|
||||
then
|
||||
if [[ "$(cat mastofe.timestamp)" == "${last_modified}" ]]
|
||||
then
|
||||
echo "MastoFE is up-to-date, exiting…"
|
||||
exit 0
|
||||
fi
|
||||
echo "ERROR: Couldn't get the modification date of the latest build archive, maybe it expired, exiting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
curl -c - "https://git.pleroma.social/api/v4/projects/${project_id}/jobs/artifacts/${project_branch}/download?job=build" -o "${artifact}" || exit
|
||||
if [ -e mastofe.timestamp ] && [ "$(cat mastofe.timestamp)" = "${last_modified}" ]
|
||||
then
|
||||
echo "MastoFE is up-to-date, exiting..."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
curl --fail -c - "https://git.pleroma.social/api/v4/projects/${project_id}/jobs/artifacts/${project_branch}/download?job=build" -o "${artifact}" || exit
|
||||
|
||||
# TODO: Update the emoji as well
|
||||
rm -fr "${static_dir}/sw.js" "${static_dir}/packs" || exit
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -24,6 +24,8 @@ defmodule Pleroma.Activity do
|
||||
|
||||
@primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
schema "activities" do
|
||||
field(:data, :map)
|
||||
field(:local, :boolean, default: true)
|
||||
@ -272,7 +274,7 @@ defmodule Pleroma.Activity do
|
||||
defp get_in_reply_to_activity_from_object(_), do: nil
|
||||
|
||||
def get_in_reply_to_activity(%Activity{} = activity) do
|
||||
get_in_reply_to_activity_from_object(Object.normalize(activity))
|
||||
get_in_reply_to_activity_from_object(Object.normalize(activity, fetch: false))
|
||||
end
|
||||
|
||||
def normalize(obj) when is_map(obj), do: get_by_ap_id_with_object(obj["id"])
|
||||
@ -298,7 +300,7 @@ defmodule Pleroma.Activity do
|
||||
|
||||
defp purge_web_resp_cache(%Activity{} = activity) do
|
||||
%{path: path} = URI.parse(activity.data["id"])
|
||||
Cachex.del(:web_resp_cache, path)
|
||||
@cachex.del(:web_resp_cache, path)
|
||||
activity
|
||||
end
|
||||
|
||||
|
@ -8,7 +8,7 @@ defmodule Pleroma.Activity.Ir.Topics do
|
||||
|
||||
def get_activity_topics(activity) do
|
||||
activity
|
||||
|> Object.normalize()
|
||||
|> Object.normalize(fetch: false)
|
||||
|> generate_topics(activity)
|
||||
|> List.flatten()
|
||||
end
|
||||
|
19
lib/pleroma/caching.ex
Normal file
19
lib/pleroma/caching.ex
Normal file
@ -0,0 +1,19 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Caching do
|
||||
@callback get!(Cachex.cache(), any()) :: any()
|
||||
@callback get(Cachex.cache(), any()) :: {atom(), any()}
|
||||
@callback put(Cachex.cache(), any(), any(), Keyword.t()) :: {Cachex.status(), boolean()}
|
||||
@callback put(Cachex.cache(), any(), any()) :: {Cachex.status(), boolean()}
|
||||
@callback fetch!(Cachex.cache(), any(), function() | nil) :: any()
|
||||
# @callback del(Cachex.cache(), any(), Keyword.t()) :: {Cachex.status(), boolean()}
|
||||
@callback del(Cachex.cache(), any()) :: {Cachex.status(), boolean()}
|
||||
@callback stream!(Cachex.cache(), any()) :: Enumerable.t()
|
||||
@callback expire_at(Cachex.cache(), binary(), number()) :: {Cachex.status(), boolean()}
|
||||
@callback exists?(Cachex.cache(), any()) :: {Cachex.status(), boolean()}
|
||||
@callback execute!(Cachex.cache(), function()) :: any()
|
||||
@callback get_and_update(Cachex.cache(), any(), function()) ::
|
||||
{:commit | :ignore, any()}
|
||||
end
|
@ -7,6 +7,8 @@ defmodule Pleroma.Captcha do
|
||||
alias Plug.Crypto.KeyGenerator
|
||||
alias Plug.Crypto.MessageEncryptor
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
@doc """
|
||||
Ask the configured captcha service for a new captcha
|
||||
"""
|
||||
@ -86,7 +88,7 @@ defmodule Pleroma.Captcha do
|
||||
end
|
||||
|
||||
defp validate_usage(token) do
|
||||
if is_nil(Cachex.get!(:used_captcha_cache, token)) do
|
||||
if is_nil(@cachex.get!(:used_captcha_cache, token)) do
|
||||
:ok
|
||||
else
|
||||
{:error, :already_used}
|
||||
@ -95,7 +97,7 @@ defmodule Pleroma.Captcha do
|
||||
|
||||
defp mark_captcha_as_used(token) do
|
||||
ttl = seconds_valid() |> :timer.seconds()
|
||||
Cachex.put(:used_captcha_cache, token, true, ttl: ttl)
|
||||
@cachex.put(:used_captcha_cache, token, true, ttl: ttl)
|
||||
end
|
||||
|
||||
defp method, do: Pleroma.Config.get!([__MODULE__, :method])
|
||||
|
@ -3,14 +3,18 @@
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Config do
|
||||
@behaviour Pleroma.Config.Getting
|
||||
defmodule Error do
|
||||
defexception [:message]
|
||||
end
|
||||
|
||||
@impl true
|
||||
def get(key), do: get(key, nil)
|
||||
|
||||
@impl true
|
||||
def get([key], default), do: get(key, default)
|
||||
|
||||
@impl true
|
||||
def get([_ | _] = path, default) do
|
||||
case fetch(path) do
|
||||
{:ok, value} -> value
|
||||
@ -18,6 +22,7 @@ defmodule Pleroma.Config do
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def get(key, default) do
|
||||
Application.get_env(:pleroma, key, default)
|
||||
end
|
||||
|
8
lib/pleroma/config/getting.ex
Normal file
8
lib/pleroma/config/getting.ex
Normal file
@ -0,0 +1,8 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Config.Getting do
|
||||
@callback get(any()) :: any()
|
||||
@callback get(any(), any()) :: any()
|
||||
end
|
@ -5,6 +5,7 @@
|
||||
defmodule Pleroma.Conversation do
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Conversation.Participation.RecipientShip
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
use Ecto.Schema
|
||||
@ -58,7 +59,7 @@ defmodule Pleroma.Conversation do
|
||||
def create_or_bump_for(activity, opts \\ []) do
|
||||
with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity),
|
||||
"Create" <- activity.data["type"],
|
||||
object <- Pleroma.Object.normalize(activity),
|
||||
%Object{} = object <- Object.normalize(activity, fetch: false),
|
||||
true <- object.data["type"] in ["Note", "Question"],
|
||||
ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do
|
||||
{:ok, conversation} = create_for_ap_id(ap_id)
|
||||
|
@ -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
|
||||
@ -106,7 +119,7 @@ defmodule Pleroma.Emails.UserEmail do
|
||||
notifications
|
||||
|> Enum.filter(&(&1.activity.data["type"] == "Create"))
|
||||
|> Enum.map(fn notification ->
|
||||
object = Pleroma.Object.normalize(notification.activity)
|
||||
object = Pleroma.Object.normalize(notification.activity, fetch: false)
|
||||
|
||||
if not is_nil(object) do
|
||||
object = update_in(object.data["content"], &format_links/1)
|
||||
@ -129,7 +142,7 @@ defmodule Pleroma.Emails.UserEmail do
|
||||
if not is_nil(from) do
|
||||
%{
|
||||
data: notification,
|
||||
object: Pleroma.Object.normalize(notification.activity),
|
||||
object: Pleroma.Object.normalize(notification.activity, fetch: false),
|
||||
from: User.get_by_ap_id(notification.activity.actor)
|
||||
}
|
||||
end
|
||||
@ -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
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
defmodule Pleroma.Emoji.Formatter do
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
def emojify(text) do
|
||||
@ -43,7 +44,7 @@ defmodule Pleroma.Emoji.Formatter do
|
||||
Emoji.get_all()
|
||||
|> Enum.filter(fn {emoji, %Emoji{}} -> String.contains?(text, ":#{emoji}:") end)
|
||||
|> Enum.reduce(%{}, fn {name, %Emoji{file: file}}, acc ->
|
||||
Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}")
|
||||
Map.put(acc, name, to_string(URI.merge(Web.base_url(), file)))
|
||||
end)
|
||||
end
|
||||
|
||||
|
@ -20,16 +20,18 @@ defmodule Pleroma.Emoji.Pack do
|
||||
name: String.t()
|
||||
}
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
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 +64,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 +95,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 +283,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 +305,6 @@ defmodule Pleroma.Emoji.Pack do
|
||||
|> length()
|
||||
|
||||
{:ok, Map.put(pack, :files_count, files_count)}
|
||||
else
|
||||
{:error, :not_found}
|
||||
end
|
||||
end
|
||||
|
||||
@ -415,7 +417,7 @@ defmodule Pleroma.Emoji.Pack do
|
||||
ttl_per_file = Pleroma.Config.get!([:emoji, :shared_pack_cache_seconds_per_file])
|
||||
overall_ttl = :timer.seconds(ttl_per_file * Enum.count(files))
|
||||
|
||||
Cachex.put!(
|
||||
@cachex.put(
|
||||
:emoji_packs_cache,
|
||||
pack.name,
|
||||
# if pack.json MD5 changes, the cache is not valid anymore
|
||||
@ -434,10 +436,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 +500,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 +527,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
|
||||
|
||||
@ -606,7 +620,7 @@ defmodule Pleroma.Emoji.Pack do
|
||||
defp fetch_archive(pack) do
|
||||
hash = :crypto.hash(:md5, File.read!(pack.pack_file))
|
||||
|
||||
case Cachex.get!(:emoji_packs_cache, pack.name) do
|
||||
case @cachex.get!(:emoji_packs_cache, pack.name) do
|
||||
%{hash: ^hash, pack_data: archive} -> archive
|
||||
_ -> create_archive_and_cache(pack, hash)
|
||||
end
|
||||
|
@ -76,7 +76,7 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
|
||||
|> Enum.map(fn activity ->
|
||||
user = User.get_cached_by_ap_id(activity.data["actor"])
|
||||
|
||||
object = Object.normalize(activity)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
like_count = object.data["like_count"] || 0
|
||||
announcement_count = object.data["announcement_count"] || 0
|
||||
|
||||
|
@ -6,6 +6,8 @@ defmodule Pleroma.HTML do
|
||||
# Scrubbers are compiled on boot so they can be configured in OTP releases
|
||||
# @on_load :compile_scrubbers
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
def compile_scrubbers do
|
||||
dir = Path.join(:code.priv_dir(:pleroma), "scrubbers")
|
||||
|
||||
@ -56,8 +58,8 @@ defmodule Pleroma.HTML do
|
||||
) do
|
||||
key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}"
|
||||
|
||||
Cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||
object = Pleroma.Object.normalize(activity)
|
||||
@cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||
object = Pleroma.Object.normalize(activity, fetch: false)
|
||||
ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false, callback)
|
||||
end)
|
||||
end
|
||||
@ -105,7 +107,7 @@ defmodule Pleroma.HTML do
|
||||
unless object.data["fake"] do
|
||||
key = "URL|#{object.id}"
|
||||
|
||||
Cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||
@cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||
{:commit, {:ok, extract_first_external_url(content)}}
|
||||
end)
|
||||
else
|
||||
|
@ -2,8 +2,6 @@
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
Postgrex.Types.define(
|
||||
Pleroma.PostgresTypes,
|
||||
[] ++ Ecto.Adapters.Postgres.extensions(),
|
||||
json: Jason
|
||||
)
|
||||
defmodule Pleroma.Logging do
|
||||
@callback error(String.t()) :: any()
|
||||
end
|
@ -358,7 +358,7 @@ defmodule Pleroma.Notification do
|
||||
def create_notifications(activity, options \\ [])
|
||||
|
||||
def create_notifications(%Activity{data: %{"to" => _, "type" => "Create"}} = activity, options) do
|
||||
object = Object.normalize(activity, false)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
if object && object.data["type"] == "Answer" do
|
||||
{:ok, []}
|
||||
@ -625,7 +625,7 @@ defmodule Pleroma.Notification do
|
||||
def skip?(:filtered, %{data: %{"type" => type}}, _) when type in ["Follow", "Move"], do: false
|
||||
|
||||
def skip?(:filtered, activity, user) do
|
||||
object = Object.normalize(activity)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
cond do
|
||||
is_nil(object) ->
|
||||
|
@ -23,6 +23,8 @@ defmodule Pleroma.Object do
|
||||
|
||||
@derive {Jason.Encoder, only: [:data]}
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
schema "objects" do
|
||||
field(:data, :map)
|
||||
|
||||
@ -106,39 +108,42 @@ defmodule Pleroma.Object do
|
||||
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
|
||||
end
|
||||
|
||||
def normalize(_, fetch_remote \\ true, options \\ [])
|
||||
def normalize(_, options \\ [fetch: false])
|
||||
|
||||
# If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
|
||||
# Use this whenever possible, especially when walking graphs in an O(N) loop!
|
||||
def normalize(%Object{} = object, _, _), do: object
|
||||
def normalize(%Activity{object: %Object{} = object}, _, _), do: object
|
||||
def normalize(%Object{} = object, _), do: object
|
||||
def normalize(%Activity{object: %Object{} = object}, _), do: object
|
||||
|
||||
# A hack for fake activities
|
||||
def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}, _, _) do
|
||||
def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}, _) do
|
||||
%Object{id: "pleroma:fake_object_id", data: data}
|
||||
end
|
||||
|
||||
# No preloaded object
|
||||
def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}, fetch_remote, _) do
|
||||
def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}, options) do
|
||||
warn_on_no_object_preloaded(ap_id)
|
||||
normalize(ap_id, fetch_remote)
|
||||
normalize(ap_id, options)
|
||||
end
|
||||
|
||||
# No preloaded object
|
||||
def normalize(%Activity{data: %{"object" => ap_id}}, fetch_remote, _) do
|
||||
def normalize(%Activity{data: %{"object" => ap_id}}, options) do
|
||||
warn_on_no_object_preloaded(ap_id)
|
||||
normalize(ap_id, fetch_remote)
|
||||
normalize(ap_id, options)
|
||||
end
|
||||
|
||||
# Old way, try fetching the object through cache.
|
||||
def normalize(%{"id" => ap_id}, fetch_remote, _), do: normalize(ap_id, fetch_remote)
|
||||
def normalize(ap_id, false, _) when is_binary(ap_id), do: get_cached_by_ap_id(ap_id)
|
||||
def normalize(%{"id" => ap_id}, options), do: normalize(ap_id, options)
|
||||
|
||||
def normalize(ap_id, true, options) when is_binary(ap_id) do
|
||||
Fetcher.fetch_object_from_id!(ap_id, options)
|
||||
def normalize(ap_id, options) when is_binary(ap_id) do
|
||||
if Keyword.get(options, :fetch) do
|
||||
Fetcher.fetch_object_from_id!(ap_id, options)
|
||||
else
|
||||
get_cached_by_ap_id(ap_id)
|
||||
end
|
||||
end
|
||||
|
||||
def normalize(_, _, _), do: nil
|
||||
def normalize(_, _), do: nil
|
||||
|
||||
# Owned objects can only be accessed by their owner
|
||||
def authorize_access(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}) do
|
||||
@ -156,9 +161,9 @@ defmodule Pleroma.Object do
|
||||
def get_cached_by_ap_id(ap_id) do
|
||||
key = "object:#{ap_id}"
|
||||
|
||||
with {:ok, nil} <- Cachex.get(:object_cache, key),
|
||||
with {:ok, nil} <- @cachex.get(:object_cache, key),
|
||||
object when not is_nil(object) <- get_by_ap_id(ap_id),
|
||||
{:ok, true} <- Cachex.put(:object_cache, key, object) do
|
||||
{:ok, true} <- @cachex.put(:object_cache, key, object) do
|
||||
object
|
||||
else
|
||||
{:ok, object} -> object
|
||||
@ -216,13 +221,13 @@ defmodule Pleroma.Object do
|
||||
end
|
||||
|
||||
def invalid_object_cache(%Object{data: %{"id" => id}}) do
|
||||
with {:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do
|
||||
Cachex.del(:web_resp_cache, URI.parse(id).path)
|
||||
with {:ok, true} <- @cachex.del(:object_cache, "object:#{id}") do
|
||||
@cachex.del(:web_resp_cache, URI.parse(id).path)
|
||||
end
|
||||
end
|
||||
|
||||
def set_cache(%Object{data: %{"id" => ap_id}} = object) do
|
||||
Cachex.put(:object_cache, "object:#{ap_id}", object)
|
||||
@cachex.put(:object_cache, "object:#{ap_id}", object)
|
||||
{:ok, object}
|
||||
end
|
||||
|
||||
@ -283,7 +288,7 @@ defmodule Pleroma.Object do
|
||||
end
|
||||
|
||||
def increase_vote_count(ap_id, name, actor) do
|
||||
with %Object{} = object <- Object.normalize(ap_id),
|
||||
with %Object{} = object <- Object.normalize(ap_id, fetch: false),
|
||||
"Question" <- object.data["type"] do
|
||||
key = if poll_is_multiple?(object), do: "anyOf", else: "oneOf"
|
||||
|
||||
@ -324,7 +329,7 @@ defmodule Pleroma.Object do
|
||||
end
|
||||
|
||||
def replies(object, opts \\ []) do
|
||||
object = Object.normalize(object)
|
||||
object = Object.normalize(object, fetch: false)
|
||||
|
||||
query =
|
||||
Object
|
||||
|
@ -83,13 +83,13 @@ defmodule Pleroma.Object.Fetcher do
|
||||
with {_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
|
||||
{_, true} <- {:allowed_depth, Federator.allowed_thread_distance?(options[:depth])},
|
||||
{_, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)},
|
||||
{_, nil} <- {:normalize, Object.normalize(data, false)},
|
||||
{_, nil} <- {:normalize, Object.normalize(data, fetch: false)},
|
||||
params <- prepare_activity_params(data),
|
||||
{_, :ok} <- {:containment, Containment.contain_origin(id, params)},
|
||||
{_, {:ok, activity}} <-
|
||||
{:transmogrifier, Transmogrifier.handle_incoming(params, options)},
|
||||
{_, _data, %Object{} = object} <-
|
||||
{:object, data, Object.normalize(activity, false)} do
|
||||
{:object, data, Object.normalize(activity, fetch: false)} do
|
||||
{:ok, object}
|
||||
else
|
||||
{:allowed_depth, false} ->
|
||||
|
@ -17,6 +17,8 @@ defmodule Pleroma.ReverseProxy do
|
||||
@failed_request_ttl :timer.seconds(60)
|
||||
@methods ~w(GET HEAD)
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
def max_read_duration_default, do: @max_read_duration
|
||||
def default_cache_control_header, do: @default_cache_control_header
|
||||
|
||||
@ -107,7 +109,7 @@ defmodule Pleroma.ReverseProxy do
|
||||
opts
|
||||
end
|
||||
|
||||
with {:ok, nil} <- Cachex.get(:failed_proxy_url_cache, url),
|
||||
with {:ok, nil} <- @cachex.get(:failed_proxy_url_cache, url),
|
||||
{:ok, code, headers, client} <- request(method, url, req_headers, client_opts),
|
||||
:ok <-
|
||||
header_length_constraint(
|
||||
@ -427,6 +429,6 @@ defmodule Pleroma.ReverseProxy do
|
||||
nil
|
||||
end
|
||||
|
||||
Cachex.put(:failed_proxy_url_cache, url, true, ttl: ttl)
|
||||
@cachex.put(:failed_proxy_url_cache, url, true, ttl: ttl)
|
||||
end
|
||||
end
|
||||
|
@ -81,6 +81,8 @@ defmodule Pleroma.User do
|
||||
]
|
||||
]
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
schema "users" do
|
||||
field(:bio, :string, default: "")
|
||||
field(:raw_bio, :string)
|
||||
@ -140,7 +142,7 @@ defmodule Pleroma.User do
|
||||
field(:allow_following_move, :boolean, default: true)
|
||||
field(:skip_thread_containment, :boolean, default: false)
|
||||
field(:actor_type, :string, default: "Person")
|
||||
field(:also_known_as, {:array, :string}, default: [])
|
||||
field(:also_known_as, {:array, ObjectValidators.ObjectID}, default: [])
|
||||
field(:inbox, :string)
|
||||
field(:shared_inbox, :string)
|
||||
field(:accepts_chat_messages, :boolean, default: nil)
|
||||
@ -246,13 +248,13 @@ defmodule Pleroma.User do
|
||||
end
|
||||
|
||||
def cached_blocked_users_ap_ids(user) do
|
||||
Cachex.fetch!(:user_cache, "blocked_users_ap_ids:#{user.ap_id}", fn _ ->
|
||||
@cachex.fetch!(:user_cache, "blocked_users_ap_ids:#{user.ap_id}", fn _ ->
|
||||
blocked_users_ap_ids(user)
|
||||
end)
|
||||
end
|
||||
|
||||
def cached_muted_users_ap_ids(user) do
|
||||
Cachex.fetch!(:user_cache, "muted_users_ap_ids:#{user.ap_id}", fn _ ->
|
||||
@cachex.fetch!(:user_cache, "muted_users_ap_ids:#{user.ap_id}", fn _ ->
|
||||
muted_users_ap_ids(user)
|
||||
end)
|
||||
end
|
||||
@ -513,6 +515,7 @@ defmodule Pleroma.User do
|
||||
:hide_follows_count,
|
||||
:hide_favorites,
|
||||
:allow_following_move,
|
||||
:also_known_as,
|
||||
:background,
|
||||
:show_role,
|
||||
:skip_thread_containment,
|
||||
@ -521,7 +524,6 @@ defmodule Pleroma.User do
|
||||
:pleroma_settings_store,
|
||||
:is_discoverable,
|
||||
:actor_type,
|
||||
:also_known_as,
|
||||
:accepts_chat_messages
|
||||
]
|
||||
)
|
||||
@ -806,18 +808,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)
|
||||
@ -1016,9 +1050,9 @@ defmodule Pleroma.User do
|
||||
def set_cache({:error, err}), do: {:error, err}
|
||||
|
||||
def set_cache(%User{} = user) do
|
||||
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
|
||||
Cachex.put(:user_cache, "nickname:#{user.nickname}", user)
|
||||
Cachex.put(:user_cache, "friends_ap_ids:#{user.nickname}", get_user_friends_ap_ids(user))
|
||||
@cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
|
||||
@cachex.put(:user_cache, "nickname:#{user.nickname}", user)
|
||||
@cachex.put(:user_cache, "friends_ap_ids:#{user.nickname}", get_user_friends_ap_ids(user))
|
||||
{:ok, user}
|
||||
end
|
||||
|
||||
@ -1041,26 +1075,26 @@ defmodule Pleroma.User do
|
||||
|
||||
@spec get_cached_user_friends_ap_ids(User.t()) :: [String.t()]
|
||||
def get_cached_user_friends_ap_ids(user) do
|
||||
Cachex.fetch!(:user_cache, "friends_ap_ids:#{user.ap_id}", fn _ ->
|
||||
@cachex.fetch!(:user_cache, "friends_ap_ids:#{user.ap_id}", fn _ ->
|
||||
get_user_friends_ap_ids(user)
|
||||
end)
|
||||
end
|
||||
|
||||
def invalidate_cache(user) do
|
||||
Cachex.del(:user_cache, "ap_id:#{user.ap_id}")
|
||||
Cachex.del(:user_cache, "nickname:#{user.nickname}")
|
||||
Cachex.del(:user_cache, "friends_ap_ids:#{user.ap_id}")
|
||||
Cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
|
||||
Cachex.del(:user_cache, "muted_users_ap_ids:#{user.ap_id}")
|
||||
@cachex.del(:user_cache, "ap_id:#{user.ap_id}")
|
||||
@cachex.del(:user_cache, "nickname:#{user.nickname}")
|
||||
@cachex.del(:user_cache, "friends_ap_ids:#{user.ap_id}")
|
||||
@cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
|
||||
@cachex.del(:user_cache, "muted_users_ap_ids:#{user.ap_id}")
|
||||
end
|
||||
|
||||
@spec get_cached_by_ap_id(String.t()) :: User.t() | nil
|
||||
def get_cached_by_ap_id(ap_id) do
|
||||
key = "ap_id:#{ap_id}"
|
||||
|
||||
with {:ok, nil} <- Cachex.get(:user_cache, key),
|
||||
with {:ok, nil} <- @cachex.get(:user_cache, key),
|
||||
user when not is_nil(user) <- get_by_ap_id(ap_id),
|
||||
{:ok, true} <- Cachex.put(:user_cache, key, user) do
|
||||
{:ok, true} <- @cachex.put(:user_cache, key, user) do
|
||||
user
|
||||
else
|
||||
{:ok, user} -> user
|
||||
@ -1072,11 +1106,11 @@ defmodule Pleroma.User do
|
||||
key = "id:#{id}"
|
||||
|
||||
ap_id =
|
||||
Cachex.fetch!(:user_cache, key, fn _ ->
|
||||
@cachex.fetch!(:user_cache, key, fn _ ->
|
||||
user = get_by_id(id)
|
||||
|
||||
if user do
|
||||
Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
|
||||
@cachex.put(:user_cache, "ap_id:#{user.ap_id}", user)
|
||||
{:commit, user.ap_id}
|
||||
else
|
||||
{:ignore, ""}
|
||||
@ -1089,7 +1123,7 @@ defmodule Pleroma.User do
|
||||
def get_cached_by_nickname(nickname) do
|
||||
key = "nickname:#{nickname}"
|
||||
|
||||
Cachex.fetch!(:user_cache, key, fn ->
|
||||
@cachex.fetch!(:user_cache, key, fn _ ->
|
||||
case get_or_fetch_by_nickname(nickname) do
|
||||
{:ok, user} -> {:commit, user}
|
||||
{:error, _error} -> {:ignore, nil}
|
||||
@ -1358,7 +1392,7 @@ defmodule Pleroma.User do
|
||||
)
|
||||
end
|
||||
|
||||
Cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}")
|
||||
@cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}")
|
||||
|
||||
{:ok, Enum.filter([user_mute, user_notification_mute], & &1)}
|
||||
end
|
||||
@ -1368,7 +1402,7 @@ defmodule Pleroma.User do
|
||||
with {:ok, user_mute} <- UserRelationship.delete_mute(muter, mutee),
|
||||
{:ok, user_notification_mute} <-
|
||||
UserRelationship.delete_notification_mute(muter, mutee) do
|
||||
Cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}")
|
||||
@cachex.del(:user_cache, "muted_users_ap_ids:#{muter.ap_id}")
|
||||
{:ok, [user_mute, user_notification_mute]}
|
||||
end
|
||||
end
|
||||
@ -1590,11 +1624,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}, [])
|
||||
@ -2081,18 +2138,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
|
||||
@ -2365,7 +2410,7 @@ defmodule Pleroma.User do
|
||||
{:ok, UserRelationship.t()} | {:error, Ecto.Changeset.t()}
|
||||
defp add_to_block(%User{} = user, %User{} = blocked) do
|
||||
with {:ok, relationship} <- UserRelationship.create_block(user, blocked) do
|
||||
Cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
|
||||
@cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
|
||||
{:ok, relationship}
|
||||
end
|
||||
end
|
||||
@ -2374,7 +2419,7 @@ defmodule Pleroma.User do
|
||||
{:ok, UserRelationship.t()} | {:ok, nil} | {:error, Ecto.Changeset.t()}
|
||||
defp remove_from_block(%User{} = user, %User{} = blocked) do
|
||||
with {:ok, relationship} <- UserRelationship.delete_block(user, blocked) do
|
||||
Cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
|
||||
@cachex.del(:user_cache, "blocked_users_ap_ids:#{user.ap_id}")
|
||||
{:ok, relationship}
|
||||
end
|
||||
end
|
||||
|
@ -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
|
||||
|
@ -32,6 +32,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
require Logger
|
||||
require Pleroma.Constants
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.ActivityPub.Persisting
|
||||
@behaviour Pleroma.Web.ActivityPub.ActivityPub.Streaming
|
||||
|
||||
defp get_recipients(%{"type" => "Create"} = data) do
|
||||
to = Map.get(data, "to", [])
|
||||
cc = Map.get(data, "cc", [])
|
||||
@ -85,13 +88,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
defp increase_replies_count_if_reply(_create_data), do: :noop
|
||||
|
||||
@object_types ~w[ChatMessage Question Answer Audio Video Event Article]
|
||||
@spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
|
||||
@impl true
|
||||
def persist(%{"type" => type} = object, meta) when type in @object_types do
|
||||
with {:ok, object} <- Object.create(object) do
|
||||
{:ok, object, meta}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def persist(object, meta) do
|
||||
with local <- Keyword.fetch!(meta, :local),
|
||||
{recipients, _, _} <- get_recipients(object),
|
||||
@ -221,6 +225,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
Streamer.stream("participation", participations)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def stream_out_participations(%Object{data: %{"context" => context}}, user) do
|
||||
with %Conversation{} = conversation <- Conversation.get_for_ap_id(context) do
|
||||
conversation = Repo.preload(conversation, :participations)
|
||||
@ -237,8 +242,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def stream_out_participations(_, _), do: :noop
|
||||
|
||||
@impl true
|
||||
def stream_out(%Activity{data: %{"type" => data_type}} = activity)
|
||||
when data_type in ["Create", "Announce", "Delete"] do
|
||||
activity
|
||||
@ -246,6 +253,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|> Streamer.stream(activity)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def stream_out(_activity) do
|
||||
:noop
|
||||
end
|
||||
@ -600,12 +608,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
|> Map.put(:muting_user, reading_user)
|
||||
end
|
||||
|
||||
pagination_type = Map.get(params, :pagination_type) || :keyset
|
||||
|
||||
%{
|
||||
godmode: params[:godmode],
|
||||
reading_user: reading_user
|
||||
}
|
||||
|> user_activities_recipients()
|
||||
|> fetch_activities(params)
|
||||
|> fetch_activities(params, pagination_type)
|
||||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
|
7
lib/pleroma/web/activity_pub/activity_pub/persisting.ex
Normal file
7
lib/pleroma/web/activity_pub/activity_pub/persisting.ex
Normal file
@ -0,0 +1,7 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ActivityPub.Persisting do
|
||||
@callback persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
|
||||
end
|
12
lib/pleroma/web/activity_pub/activity_pub/streaming.ex
Normal file
12
lib/pleroma/web/activity_pub/activity_pub/streaming.ex
Normal file
@ -0,0 +1,12 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ActivityPub.Streaming do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
|
||||
@callback stream_out(Activity.t()) :: any()
|
||||
@callback stream_out_participations(Object.t(), User.t()) :: any()
|
||||
end
|
@ -128,7 +128,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||
end
|
||||
|
||||
defp maybe_set_tracking_data(conn, %Activity{data: %{"type" => "Create"}} = activity) do
|
||||
object_id = Object.normalize(activity).id
|
||||
object_id = Object.normalize(activity, fetch: false).id
|
||||
assign(conn, :tracking_fun_data, object_id)
|
||||
end
|
||||
|
||||
@ -434,7 +434,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||
end
|
||||
|
||||
defp handle_user_activity(%User{} = user, %{"type" => "Delete"} = params) do
|
||||
with %Object{} = object <- Object.normalize(params["object"]),
|
||||
with %Object{} = object <- Object.normalize(params["object"], fetch: false),
|
||||
true <- user.is_moderator || user.ap_id == object.data["actor"],
|
||||
{:ok, delete_data, _} <- Builder.delete(user, object.data["id"]),
|
||||
{:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do
|
||||
@ -445,7 +445,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||
end
|
||||
|
||||
defp handle_user_activity(%User{} = user, %{"type" => "Like"} = params) do
|
||||
with %Object{} = object <- Object.normalize(params["object"]),
|
||||
with %Object{} = object <- Object.normalize(params["object"], fetch: false),
|
||||
{_, {:ok, like_object, meta}} <- {:build_object, Builder.like(user, object)},
|
||||
{_, {:ok, %Activity{} = activity, _meta}} <-
|
||||
{:common_pipeline,
|
||||
|
@ -80,7 +80,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
||||
|
||||
@spec delete(User.t(), String.t()) :: {:ok, map(), keyword()}
|
||||
def delete(actor, object_id) do
|
||||
object = Object.normalize(object_id, false)
|
||||
object = Object.normalize(object_id, fetch: false)
|
||||
|
||||
user = !object && User.get_cached_by_ap_id(object_id)
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
defmodule Pleroma.Web.ActivityPub.MRF do
|
||||
require Logger
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.PipelineFiltering
|
||||
|
||||
@mrf_config_descriptions [
|
||||
%{
|
||||
group: :pleroma,
|
||||
@ -72,6 +74,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
||||
|
||||
def filter(%{} = object), do: get_policies() |> filter(object)
|
||||
|
||||
@impl true
|
||||
def pipeline_filter(%{} = message, meta) do
|
||||
object = meta[:object_data]
|
||||
ap_id = message["object"]
|
||||
|
@ -31,7 +31,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
|
||||
when is_map(child_object) do
|
||||
child =
|
||||
child_object["inReplyTo"]
|
||||
|> Object.normalize(child_object["inReplyTo"])
|
||||
|> Object.normalize(fetch: false)
|
||||
|> filter_by_summary(child_object)
|
||||
|
||||
object = Map.put(object, "object", child)
|
||||
|
7
lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex
Normal file
7
lib/pleroma/web/activity_pub/mrf/pipeline_filtering.ex
Normal file
@ -0,0 +1,7 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.PipelineFiltering do
|
||||
@callback pipeline_filter(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
|
||||
end
|
@ -10,73 +10,75 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
|
||||
@moduledoc "Detect new emojis by their shortcode and steals them"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
defp remote_host?(host), do: host != Config.get([Pleroma.Web.Endpoint, :url, :host])
|
||||
|
||||
defp accept_host?(host), do: host in Config.get([:mrf_steal_emoji, :hosts], [])
|
||||
|
||||
defp steal_emoji({shortcode, url}) do
|
||||
defp steal_emoji({shortcode, url}, emoji_dir_path) do
|
||||
url = Pleroma.Web.MediaProxy.url(url)
|
||||
{:ok, response} = Pleroma.HTTP.get(url)
|
||||
size_limit = Config.get([:mrf_steal_emoji, :size_limit], 50_000)
|
||||
|
||||
if byte_size(response.body) <= size_limit do
|
||||
emoji_dir_path =
|
||||
Config.get(
|
||||
[:mrf_steal_emoji, :path],
|
||||
Path.join(Config.get([:instance, :static_dir]), "emoji/stolen")
|
||||
with {:ok, %{status: status} = response} when status in 200..299 <- Pleroma.HTTP.get(url) do
|
||||
size_limit = Config.get([:mrf_steal_emoji, :size_limit], 50_000)
|
||||
|
||||
if byte_size(response.body) <= size_limit do
|
||||
extension =
|
||||
url
|
||||
|> URI.parse()
|
||||
|> Map.get(:path)
|
||||
|> Path.basename()
|
||||
|> Path.extname()
|
||||
|
||||
file_path = Path.join(emoji_dir_path, shortcode <> (extension || ".png"))
|
||||
|
||||
case File.write(file_path, response.body) do
|
||||
:ok ->
|
||||
shortcode
|
||||
|
||||
e ->
|
||||
Logger.warn("MRF.StealEmojiPolicy: Failed to write to #{file_path}: #{inspect(e)}")
|
||||
nil
|
||||
end
|
||||
else
|
||||
Logger.debug(
|
||||
"MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{
|
||||
size_limit
|
||||
} B)"
|
||||
)
|
||||
|
||||
extension =
|
||||
url
|
||||
|> URI.parse()
|
||||
|> Map.get(:path)
|
||||
|> Path.basename()
|
||||
|> Path.extname()
|
||||
|
||||
file_path = Path.join([emoji_dir_path, shortcode <> (extension || ".png")])
|
||||
|
||||
try do
|
||||
:ok = File.write(file_path, response.body)
|
||||
|
||||
shortcode
|
||||
rescue
|
||||
e ->
|
||||
Logger.warn("MRF.StealEmojiPolicy: Failed to write to #{file_path}: #{inspect(e)}")
|
||||
nil
|
||||
nil
|
||||
end
|
||||
else
|
||||
Logger.debug(
|
||||
"MRF.StealEmojiPolicy: :#{shortcode}: at #{url} (#{byte_size(response.body)} B) over size limit (#{
|
||||
size_limit
|
||||
} B)"
|
||||
)
|
||||
|
||||
nil
|
||||
e ->
|
||||
Logger.warn("MRF.StealEmojiPolicy: Failed to fetch #{url}: #{inspect(e)}")
|
||||
nil
|
||||
end
|
||||
rescue
|
||||
e ->
|
||||
Logger.warn("MRF.StealEmojiPolicy: Failed to fetch #{url}: #{inspect(e)}")
|
||||
nil
|
||||
end
|
||||
|
||||
@impl true
|
||||
def filter(%{"object" => %{"emoji" => foreign_emojis, "actor" => actor}} = message) do
|
||||
host = URI.parse(actor).host
|
||||
|
||||
if remote_host?(host) and accept_host?(host) do
|
||||
if host != Pleroma.Web.Endpoint.host() and accept_host?(host) do
|
||||
installed_emoji = Pleroma.Emoji.get_all() |> Enum.map(fn {k, _} -> k end)
|
||||
|
||||
emoji_dir_path =
|
||||
Config.get(
|
||||
[:mrf_steal_emoji, :path],
|
||||
Path.join(Config.get([:instance, :static_dir]), "emoji/stolen")
|
||||
)
|
||||
|
||||
File.mkdir_p(emoji_dir_path)
|
||||
|
||||
new_emojis =
|
||||
foreign_emojis
|
||||
|> Enum.filter(fn {shortcode, _url} -> shortcode not in installed_emoji end)
|
||||
|> Enum.reject(fn {shortcode, _url} -> shortcode in installed_emoji end)
|
||||
|> Enum.filter(fn {shortcode, _url} ->
|
||||
reject_emoji? =
|
||||
Config.get([:mrf_steal_emoji, :rejected_shortcodes], [])
|
||||
[:mrf_steal_emoji, :rejected_shortcodes]
|
||||
|> Config.get([])
|
||||
|> Enum.find(false, fn regex -> String.match?(shortcode, regex) end)
|
||||
|
||||
!reject_emoji?
|
||||
end)
|
||||
|> Enum.map(&steal_emoji(&1))
|
||||
|> Enum.map(&steal_emoji(&1, emoji_dir_path))
|
||||
|> Enum.filter(& &1)
|
||||
|
||||
if !Enum.empty?(new_emojis) do
|
||||
|
@ -9,6 +9,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||
the system.
|
||||
"""
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.Object
|
||||
@ -32,7 +34,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator
|
||||
|
||||
@spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
|
||||
@impl true
|
||||
def validate(object, meta)
|
||||
|
||||
def validate(%{"type" => type} = object, meta)
|
||||
@ -286,7 +288,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||
|
||||
def fetch_actor_and_object(object) do
|
||||
fetch_actor(object)
|
||||
Object.normalize(object["object"], true)
|
||||
Object.normalize(object["object"], fetch: true)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
@ -0,0 +1,7 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidator.Validating do
|
||||
@callback validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
|
||||
end
|
@ -14,12 +14,19 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Federator
|
||||
|
||||
@side_effects Config.get([:pipeline, :side_effects], SideEffects)
|
||||
@federator Config.get([:pipeline, :federator], Federator)
|
||||
@object_validator Config.get([:pipeline, :object_validator], ObjectValidator)
|
||||
@mrf Config.get([:pipeline, :mrf], MRF)
|
||||
@activity_pub Config.get([:pipeline, :activity_pub], ActivityPub)
|
||||
@config Config.get([:pipeline, :config], Config)
|
||||
|
||||
@spec common_pipeline(map(), keyword()) ::
|
||||
{:ok, Activity.t() | Object.t(), keyword()} | {:error, any()}
|
||||
def common_pipeline(object, meta) do
|
||||
case Repo.transaction(fn -> do_common_pipeline(object, meta) end) do
|
||||
{:ok, {:ok, activity, meta}} ->
|
||||
SideEffects.handle_after_transaction(meta)
|
||||
@side_effects.handle_after_transaction(meta)
|
||||
{:ok, activity, meta}
|
||||
|
||||
{:ok, value} ->
|
||||
@ -35,13 +42,13 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
|
||||
|
||||
def do_common_pipeline(object, meta) do
|
||||
with {_, {:ok, validated_object, meta}} <-
|
||||
{:validate_object, ObjectValidator.validate(object, meta)},
|
||||
{:validate_object, @object_validator.validate(object, meta)},
|
||||
{_, {:ok, mrfd_object, meta}} <-
|
||||
{:mrf_object, MRF.pipeline_filter(validated_object, meta)},
|
||||
{:mrf_object, @mrf.pipeline_filter(validated_object, meta)},
|
||||
{_, {:ok, activity, meta}} <-
|
||||
{:persist_object, ActivityPub.persist(mrfd_object, meta)},
|
||||
{:persist_object, @activity_pub.persist(mrfd_object, meta)},
|
||||
{_, {:ok, activity, meta}} <-
|
||||
{:execute_side_effects, SideEffects.handle(activity, meta)},
|
||||
{:execute_side_effects, @side_effects.handle(activity, meta)},
|
||||
{_, {:ok, _}} <- {:federation, maybe_federate(activity, meta)} do
|
||||
{:ok, activity, meta}
|
||||
else
|
||||
@ -54,7 +61,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
|
||||
|
||||
defp maybe_federate(%Activity{} = activity, meta) do
|
||||
with {:ok, local} <- Keyword.fetch(meta, :local) do
|
||||
do_not_federate = meta[:do_not_federate] || !Config.get([:instance, :federating])
|
||||
do_not_federate = meta[:do_not_federate] || !@config.get([:instance, :federating])
|
||||
|
||||
if !do_not_federate and local and not Visibility.is_local_public?(activity) do
|
||||
activity =
|
||||
@ -64,7 +71,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
|
||||
activity
|
||||
end
|
||||
|
||||
Federator.publish(activity)
|
||||
@federator.publish(activity)
|
||||
{:ok, :federated}
|
||||
else
|
||||
{:ok, :not_federated}
|
||||
|
@ -130,7 +130,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
||||
|
||||
fetchers =
|
||||
with %Activity{data: %{"type" => "Delete"}} <- activity,
|
||||
%Object{id: object_id} <- Object.normalize(activity),
|
||||
%Object{id: object_id} <- Object.normalize(activity, fetch: false),
|
||||
fetchers <- User.get_delivered_users_by_object_id(object_id),
|
||||
_ <- Delivery.delete_all_by_object_id(object_id) do
|
||||
fetchers
|
||||
|
@ -27,11 +27,19 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
|
||||
require Logger
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
@ap_streamer Pleroma.Config.get([:side_effects, :ap_streamer], ActivityPub)
|
||||
@logger Pleroma.Config.get([:side_effects, :logger], Logger)
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.SideEffects.Handling
|
||||
|
||||
@impl true
|
||||
def handle(object, meta \\ [])
|
||||
|
||||
# Task this handles
|
||||
# - Follows
|
||||
# - Sends a notification
|
||||
@impl true
|
||||
def handle(
|
||||
%{
|
||||
data: %{
|
||||
@ -59,6 +67,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
# - Rejects all existing follow activities for this person
|
||||
# - Updates the follow state
|
||||
# - Dismisses notification
|
||||
@impl true
|
||||
def handle(
|
||||
%{
|
||||
data: %{
|
||||
@ -85,6 +94,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
# - Follows if possible
|
||||
# - Sends a notification
|
||||
# - Generates accept or reject if appropriate
|
||||
@impl true
|
||||
def handle(
|
||||
%{
|
||||
data: %{
|
||||
@ -126,6 +136,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
|
||||
# Tasks this handles:
|
||||
# - Unfollow and block
|
||||
@impl true
|
||||
def handle(
|
||||
%{data: %{"type" => "Block", "object" => blocked_user, "actor" => blocking_user}} =
|
||||
object,
|
||||
@ -144,6 +155,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
#
|
||||
# For a local user, we also get a changeset with the full information, so we
|
||||
# can update non-federating, non-activitypub settings as well.
|
||||
@impl true
|
||||
def handle(%{data: %{"type" => "Update", "object" => updated_object}} = object, meta) do
|
||||
if changeset = Keyword.get(meta, :user_update_changeset) do
|
||||
changeset
|
||||
@ -162,6 +174,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
# Tasks this handles:
|
||||
# - Add like to object
|
||||
# - Set up notification
|
||||
@impl true
|
||||
def handle(%{data: %{"type" => "Like"}} = object, meta) do
|
||||
liked_object = Object.get_by_ap_id(object.data["object"])
|
||||
Utils.add_like_to_object(object, liked_object)
|
||||
@ -179,6 +192,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
# - Increase replies count
|
||||
# - Set up ActivityExpiration
|
||||
# - Set up notifications
|
||||
@impl true
|
||||
def handle(%{data: %{"type" => "Create"}} = activity, meta) do
|
||||
with {:ok, object, meta} <- handle_object_creation(meta[:object_data], meta),
|
||||
%User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||
@ -207,6 +221,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
# - Add announce to object
|
||||
# - Set up notification
|
||||
# - Stream out the announce
|
||||
@impl true
|
||||
def handle(%{data: %{"type" => "Announce"}} = object, meta) do
|
||||
announced_object = Object.get_by_ap_id(object.data["object"])
|
||||
user = User.get_cached_by_ap_id(object.data["actor"])
|
||||
@ -224,6 +239,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
{:ok, object, meta}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle(%{data: %{"type" => "Undo", "object" => undone_object}} = object, meta) do
|
||||
with undone_object <- Activity.get_by_ap_id(undone_object),
|
||||
:ok <- handle_undoing(undone_object) do
|
||||
@ -234,6 +250,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
# Tasks this handles:
|
||||
# - Add reaction to object
|
||||
# - Set up notification
|
||||
@impl true
|
||||
def handle(%{data: %{"type" => "EmojiReact"}} = object, meta) do
|
||||
reacted_object = Object.get_by_ap_id(object.data["object"])
|
||||
Utils.add_emoji_reaction_to_object(object, reacted_object)
|
||||
@ -250,9 +267,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
# - Reduce the user note count
|
||||
# - Reduce the reply count
|
||||
# - Stream out the activity
|
||||
@impl true
|
||||
def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object, meta) do
|
||||
deleted_object =
|
||||
Object.normalize(deleted_object, false) ||
|
||||
Object.normalize(deleted_object, fetch: false) ||
|
||||
User.get_cached_by_ap_id(deleted_object)
|
||||
|
||||
result =
|
||||
@ -271,12 +289,12 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
|
||||
MessageReference.delete_for_object(deleted_object)
|
||||
|
||||
ActivityPub.stream_out(object)
|
||||
ActivityPub.stream_out_participations(deleted_object, user)
|
||||
@ap_streamer.stream_out(object)
|
||||
@ap_streamer.stream_out_participations(deleted_object, user)
|
||||
:ok
|
||||
else
|
||||
{:actor, _} ->
|
||||
Logger.error("The object doesn't have an actor: #{inspect(deleted_object)}")
|
||||
@logger.error("The object doesn't have an actor: #{inspect(deleted_object)}")
|
||||
:no_object_actor
|
||||
end
|
||||
|
||||
@ -295,6 +313,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
end
|
||||
|
||||
# Nothing to do
|
||||
@impl true
|
||||
def handle(object, meta) do
|
||||
{:ok, object, meta}
|
||||
end
|
||||
@ -312,7 +331,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
{:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
|
||||
{:ok, cm_ref} = MessageReference.create(chat, object, user.ap_id != actor.ap_id)
|
||||
|
||||
Cachex.put(
|
||||
@cachex.put(
|
||||
:chat_message_id_idempotency_key_cache,
|
||||
cm_ref.id,
|
||||
meta[:idempotency_key]
|
||||
@ -439,6 +458,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||
|> Keyword.put(:notifications, notifications ++ existing)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_after_transaction(meta) do
|
||||
meta
|
||||
|> send_notifications()
|
||||
|
8
lib/pleroma/web/activity_pub/side_effects/handling.ex
Normal file
8
lib/pleroma/web/activity_pub/side_effects/handling.ex
Normal file
@ -0,0 +1,8 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.SideEffects.Handling do
|
||||
@callback handle(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
|
||||
@callback handle_after_transaction(map()) :: map()
|
||||
end
|
@ -653,7 +653,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
|
||||
@spec get_obj_helper(String.t(), Keyword.t()) :: {:ok, Object.t()} | nil
|
||||
def get_obj_helper(id, options \\ []) do
|
||||
case Object.normalize(id, true, options) do
|
||||
options = Keyword.put(options, :fetch, true)
|
||||
|
||||
case Object.normalize(id, options) do
|
||||
%Object{} = object -> {:ok, object}
|
||||
_ -> nil
|
||||
end
|
||||
@ -672,7 +674,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
"actor" => attributed_to,
|
||||
"object" => data
|
||||
}) do
|
||||
{:ok, Object.normalize(activity)}
|
||||
{:ok, Object.normalize(activity, fetch: false)}
|
||||
else
|
||||
_ -> get_obj_helper(object_id)
|
||||
end
|
||||
@ -763,7 +765,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
when activity_type in ["Create", "Listen"] do
|
||||
object =
|
||||
object_id
|
||||
|> Object.normalize()
|
||||
|> Object.normalize(fetch: false)
|
||||
|> Map.get(:data)
|
||||
|> prepare_object
|
||||
|
||||
@ -779,7 +781,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do
|
||||
object =
|
||||
object_id
|
||||
|> Object.normalize()
|
||||
|> Object.normalize(fetch: false)
|
||||
|
||||
data =
|
||||
if Visibility.is_private?(object) && object.data["actor"] == ap_id do
|
||||
@ -919,7 +921,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
|
||||
defp build_emoji_tag({name, url}) do
|
||||
%{
|
||||
"icon" => %{"url" => url, "type" => "Image"},
|
||||
"icon" => %{"url" => "#{URI.encode(url)}", "type" => "Image"},
|
||||
"name" => ":" <> name <> ":",
|
||||
"type" => "Emoji",
|
||||
"updated" => "1970-01-01T00:00:00Z",
|
||||
|
@ -18,7 +18,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
|
||||
def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} = activity})
|
||||
when activity_type in ["Create", "Listen"] do
|
||||
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
||||
object = Object.normalize(activity)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
additional =
|
||||
Transmogrifier.prepare_object(activity.data)
|
||||
@ -29,7 +29,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
|
||||
|
||||
def render("object.json", %{object: %Activity{} = activity}) do
|
||||
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
||||
object = Object.normalize(activity)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
additional =
|
||||
Transmogrifier.prepare_object(activity.data)
|
||||
|
@ -112,7 +112,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||
"tag" => emoji_tags,
|
||||
# Note: key name is indeed "discoverable" (not an error)
|
||||
"discoverable" => user.is_discoverable,
|
||||
"capabilities" => capabilities
|
||||
"capabilities" => capabilities,
|
||||
"alsoKnownAs" => user.also_known_as
|
||||
}
|
||||
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|
||||
|> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
|
||||
|
@ -103,13 +103,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||
godmode = params["godmode"] == "true" || params["godmode"] == true
|
||||
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
|
||||
{_, page_size} = page_params(params)
|
||||
{page, page_size} = page_params(params)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_user_activities(user, nil, %{
|
||||
limit: page_size,
|
||||
offset: (page - 1) * page_size,
|
||||
godmode: godmode,
|
||||
exclude_reblogs: not with_reblogs
|
||||
exclude_reblogs: not with_reblogs,
|
||||
pagination_type: :offset
|
||||
})
|
||||
|
||||
conn
|
||||
@ -415,7 +417,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"})
|
||||
|
||||
|
@ -9,6 +9,8 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do
|
||||
alias Pleroma.Web.MediaProxy
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(
|
||||
@ -38,7 +40,7 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do
|
||||
|
||||
defp fetch_entries(params) do
|
||||
MediaProxy.cache_table()
|
||||
|> Cachex.stream!(Cachex.Query.create(true, :key))
|
||||
|> @cachex.stream!(Cachex.Query.create(true, :key))
|
||||
|> filter_entries(params[:query])
|
||||
end
|
||||
|
||||
|
@ -69,6 +69,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
||||
|
||||
%{
|
||||
"id" => user.id,
|
||||
"email" => user.email,
|
||||
"avatar" => avatar,
|
||||
"nickname" => user.nickname,
|
||||
"display_name" => display_name,
|
||||
|
@ -21,6 +21,7 @@ defmodule Pleroma.Web.AdminAPI.ModerationLogView do
|
||||
|> DateTime.to_unix()
|
||||
|
||||
%{
|
||||
id: log_entry.id,
|
||||
data: log_entry.data,
|
||||
time: time,
|
||||
message: ModerationLog.get_log_entry_message(log_entry)
|
||||
|
@ -19,8 +19,7 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
|
||||
reports:
|
||||
reports[:items]
|
||||
|> Enum.map(&Report.extract_report_info/1)
|
||||
|> Enum.map(&render(__MODULE__, "show.json", &1))
|
||||
|> Enum.reverse(),
|
||||
|> Enum.map(&render(__MODULE__, "show.json", &1)),
|
||||
total: reports[:total]
|
||||
}
|
||||
end
|
||||
|
@ -614,6 +614,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
||||
nullable: true,
|
||||
description: "Allows automatically follow moved following accounts"
|
||||
},
|
||||
also_known_as: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{type: :string},
|
||||
nullable: true,
|
||||
description: "List of alternate ActivityPub IDs"
|
||||
},
|
||||
pleroma_background_image: %Schema{
|
||||
type: :string,
|
||||
nullable: true,
|
||||
@ -644,6 +650,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
||||
pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
|
||||
skip_thread_containment: false,
|
||||
allow_following_move: false,
|
||||
also_known_as: ["https://foo.bar/users/foo"],
|
||||
discoverable: false,
|
||||
actor_type: "Person"
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -40,6 +40,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
||||
pleroma: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
ap_id: %Schema{type: :string},
|
||||
also_known_as: %Schema{type: :array, items: %Schema{type: :string}},
|
||||
allow_following_move: %Schema{
|
||||
type: :boolean,
|
||||
description: "whether the user allows automatically follow moved following accounts"
|
||||
|
@ -142,7 +142,7 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
with {_, %Activity{data: %{"object" => _, "type" => "Create"}} = activity} <-
|
||||
{:find_activity, Activity.get_by_id(activity_id)},
|
||||
{_, %Object{} = object, _} <-
|
||||
{:find_object, Object.normalize(activity, false), activity},
|
||||
{:find_object, Object.normalize(activity, fetch: false), activity},
|
||||
true <- User.superuser?(user) || user.ap_id == object.data["actor"],
|
||||
{:ok, delete_data, _} <- Builder.delete(user, object.data["id"]),
|
||||
{:ok, delete, _} <- Pipeline.common_pipeline(delete_data, local: true) do
|
||||
@ -173,7 +173,7 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
|
||||
def repeat(id, user, params \\ %{}) do
|
||||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
|
||||
object = %Object{} <- Object.normalize(activity, false),
|
||||
object = %Object{} <- Object.normalize(activity, fetch: false),
|
||||
{_, nil} <- {:existing_announce, Utils.get_existing_announce(user.ap_id, object)},
|
||||
public = public_announce?(object, params),
|
||||
{:ok, announce, _} <- Builder.announce(user, object, public: public),
|
||||
@ -191,7 +191,7 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
def unrepeat(id, user) do
|
||||
with {_, %Activity{data: %{"type" => "Create"}} = activity} <-
|
||||
{:find_activity, Activity.get_by_id(id)},
|
||||
%Object{} = note <- Object.normalize(activity, false),
|
||||
%Object{} = note <- Object.normalize(activity, fetch: false),
|
||||
%Activity{} = announce <- Utils.get_existing_announce(user.ap_id, note),
|
||||
{:ok, undo, _} <- Builder.undo(user, announce),
|
||||
{:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do
|
||||
@ -253,7 +253,7 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
def unfavorite(id, user) do
|
||||
with {_, %Activity{data: %{"type" => "Create"}} = activity} <-
|
||||
{:find_activity, Activity.get_by_id(id)},
|
||||
%Object{} = note <- Object.normalize(activity, false),
|
||||
%Object{} = note <- Object.normalize(activity, fetch: false),
|
||||
%Activity{} = like <- Utils.get_existing_like(user.ap_id, note),
|
||||
{:ok, undo, _} <- Builder.undo(user, like),
|
||||
{:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do
|
||||
@ -266,7 +266,7 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
|
||||
def react_with_emoji(id, user, emoji) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||
object <- Object.normalize(activity),
|
||||
object <- Object.normalize(activity, fetch: false),
|
||||
{:ok, emoji_react, _} <- Builder.emoji_react(user, object, emoji),
|
||||
{:ok, activity, _} <- Pipeline.common_pipeline(emoji_react, local: true) do
|
||||
{:ok, activity}
|
||||
@ -377,7 +377,7 @@ defmodule Pleroma.Web.CommonAPI do
|
||||
def get_replied_to_visibility(nil), do: nil
|
||||
|
||||
def get_replied_to_visibility(activity) do
|
||||
with %Object{} = object <- Object.normalize(activity) do
|
||||
with %Object{} = object <- Object.normalize(activity, fetch: false) do
|
||||
Visibility.get_visibility(object)
|
||||
end
|
||||
end
|
||||
|
@ -319,7 +319,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||
defp add_in_reply_to(object, nil), do: object
|
||||
|
||||
defp add_in_reply_to(object, in_reply_to) do
|
||||
with %Object{} = in_reply_to_object <- Object.normalize(in_reply_to) do
|
||||
with %Object{} = in_reply_to_object <- Object.normalize(in_reply_to, fetch: false) do
|
||||
Map.put(object, "inReplyTo", in_reply_to_object.data["id"])
|
||||
else
|
||||
_ -> object
|
||||
@ -399,7 +399,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
||||
%Activity{data: %{"to" => _to, "type" => type} = data} = activity
|
||||
)
|
||||
when type == "Create" do
|
||||
object = Object.normalize(activity, false)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
object_data =
|
||||
cond do
|
||||
|
@ -31,7 +31,7 @@ defmodule Pleroma.Web.EmbedController do
|
||||
end
|
||||
|
||||
defp get_counts(%Activity{} = activity) do
|
||||
%Object{data: data} = Object.normalize(activity)
|
||||
%Object{data: data} = Object.normalize(activity, fetch: false)
|
||||
|
||||
%{
|
||||
likes: Map.get(data, "like_count", 0),
|
||||
|
@ -15,6 +15,8 @@ defmodule Pleroma.Web.Federator do
|
||||
|
||||
require Logger
|
||||
|
||||
@behaviour Pleroma.Web.Federator.Publishing
|
||||
|
||||
@doc """
|
||||
Returns `true` if the distance to target object does not exceed max configured value.
|
||||
Serves to prevent fetching of very long threads, especially useful on smaller instances.
|
||||
@ -39,10 +41,12 @@ defmodule Pleroma.Web.Federator do
|
||||
ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params})
|
||||
end
|
||||
|
||||
@impl true
|
||||
def publish(%{id: "pleroma:fakeid"} = activity) do
|
||||
perform(:publish, activity)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def publish(activity) do
|
||||
PublisherWorker.enqueue("publish", %{"activity_id" => activity.id})
|
||||
end
|
||||
|
7
lib/pleroma/web/federator/publishing.ex
Normal file
7
lib/pleroma/web/federator/publishing.ex
Normal file
@ -0,0 +1,7 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Federator.Publishing do
|
||||
@callback publish(map()) :: any()
|
||||
end
|
@ -23,7 +23,7 @@ defmodule Pleroma.Web.Feed.FeedView do
|
||||
def pub_date(%DateTime{} = date), do: Timex.format!(date, "{RFC822}")
|
||||
|
||||
def prepare_activity(activity, opts \\ []) do
|
||||
object = Object.normalize(activity)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
actor =
|
||||
if opts[:actor] do
|
||||
@ -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}"
|
||||
|
@ -184,6 +184,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
:show_role,
|
||||
:skip_thread_containment,
|
||||
:allow_following_move,
|
||||
:also_known_as,
|
||||
:accepts_chat_messages
|
||||
]
|
||||
|> Enum.reduce(%{}, fn key, acc ->
|
||||
@ -207,6 +208,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||
if bot, do: {:ok, "Service"}, else: {:ok, "Person"}
|
||||
end)
|
||||
|> Maps.put_if_present(:actor_type, params[:actor_type])
|
||||
|> Maps.put_if_present(:also_known_as, params[:also_known_as])
|
||||
# Note: param name is indeed :locked (not an error)
|
||||
|> Maps.put_if_present(:is_locked, params[:locked])
|
||||
# Note: param name is indeed :discoverable (not an error)
|
||||
|
@ -26,6 +26,8 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PollOperation
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
@doc "GET /api/v1/polls/:id"
|
||||
def show(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
|
||||
@ -55,7 +57,7 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
|
||||
defp get_cached_vote_or_vote(user, object, choices) do
|
||||
idempotency_key = "polls:#{user.id}:#{object.data["id"]}"
|
||||
|
||||
Cachex.fetch!(:idempotency_cache, idempotency_key, fn ->
|
||||
@cachex.fetch!(:idempotency_cache, idempotency_key, fn _ ->
|
||||
case CommonAPI.vote(user, object, choices) do
|
||||
{:error, _message} = res -> {:ignore, res}
|
||||
res -> {:commit, res}
|
||||
|
@ -318,7 +318,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||
with true <- Pleroma.Config.get([:instance, :show_reactions]),
|
||||
%Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
{:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
|
||||
%Object{data: %{"likes" => likes}} <- Object.normalize(activity) do
|
||||
%Object{data: %{"likes" => likes}} <- Object.normalize(activity, fetch: false) do
|
||||
users =
|
||||
User
|
||||
|> Ecto.Query.where([u], u.ap_id in ^likes)
|
||||
@ -339,7 +339,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(id),
|
||||
{:visible, true} <- {:visible, Visibility.visible_for_user?(activity, user)},
|
||||
%Object{data: %{"announcements" => announces, "id" => ap_id}} <-
|
||||
Object.normalize(activity) do
|
||||
Object.normalize(activity, fetch: false) do
|
||||
announces =
|
||||
"Announce"
|
||||
|> Activity.Queries.by_type()
|
||||
|
@ -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"
|
||||
|
||||
@ -269,6 +265,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||
# Pleroma extension
|
||||
pleroma: %{
|
||||
ap_id: user.ap_id,
|
||||
also_known_as: user.also_known_as,
|
||||
confirmation_pending: user.confirmation_pending,
|
||||
tags: user.tags,
|
||||
hide_followers_count: user.hide_followers_count,
|
||||
|
@ -139,7 +139,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
||||
end
|
||||
|
||||
defp put_chat_message(response, activity, reading_user, opts) do
|
||||
object = Object.normalize(activity)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
author = User.get_cached_by_ap_id(object.data["actor"])
|
||||
chat = Pleroma.Chat.get(reading_user.id, author.ap_id)
|
||||
cm_ref = MessageReference.for_chat_and_object(chat, object)
|
||||
|
@ -41,7 +41,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
activities
|
||||
|> Enum.map(fn
|
||||
%{data: %{"type" => "Create"}} = activity ->
|
||||
object = Object.normalize(activity)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
object && object.data["inReplyTo"] != "" && object.data["inReplyTo"]
|
||||
|
||||
_ ->
|
||||
@ -51,7 +51,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
|> Activity.create_by_object_ap_id_with_object()
|
||||
|> Repo.all()
|
||||
|> Enum.reduce(%{}, fn activity, acc ->
|
||||
object = Object.normalize(activity)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
if object, do: Map.put(acc, object.data["id"], activity), else: acc
|
||||
end)
|
||||
end
|
||||
@ -65,7 +65,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
defp get_context_id(_), do: nil
|
||||
|
||||
defp reblogged?(activity, user) do
|
||||
object = Object.normalize(activity) || %{}
|
||||
object = Object.normalize(activity, fetch: false) || %{}
|
||||
present?(user && user.ap_id in (object.data["announcements"] || []))
|
||||
end
|
||||
|
||||
@ -84,7 +84,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
parent_activities =
|
||||
activities
|
||||
|> Enum.filter(&(&1.data["type"] == "Announce" && &1.data["object"]))
|
||||
|> Enum.map(&Object.normalize(&1).data["id"])
|
||||
|> Enum.map(&Object.normalize(&1, fetch: false).data["id"])
|
||||
|> Activity.create_by_object_ap_id()
|
||||
|> Activity.with_preloaded_object(:left)
|
||||
|> Activity.with_preloaded_bookmark(reading_user)
|
||||
@ -124,7 +124,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
) do
|
||||
user = CommonAPI.get_user(activity.data["actor"])
|
||||
created_at = Utils.to_masto_date(activity.data["published"])
|
||||
activity_object = Object.normalize(activity)
|
||||
activity_object = Object.normalize(activity, fetch: false)
|
||||
|
||||
reblogged_parent_activity =
|
||||
if opts[:parent_activities] do
|
||||
@ -193,7 +193,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
end
|
||||
|
||||
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
||||
object = Object.normalize(activity)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
user = CommonAPI.get_user(activity.data["actor"])
|
||||
user_follower_address = user.follower_address
|
||||
@ -451,7 +451,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
end
|
||||
|
||||
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
|
||||
object = Object.normalize(activity)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
with nil <- replied_to_activities[object.data["inReplyTo"]] do
|
||||
# If user didn't participate in the thread
|
||||
@ -460,7 +460,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||
end
|
||||
|
||||
def get_reply_to(%{data: %{"object" => _object}} = activity, _) do
|
||||
object = Object.normalize(activity)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
if object.data["inReplyTo"] && object.data["inReplyTo"] != "" do
|
||||
Activity.get_create_by_object_ap_id(object.data["inReplyTo"])
|
||||
|
@ -12,29 +12,31 @@ defmodule Pleroma.Web.MediaProxy do
|
||||
@base64_opts [padding: false]
|
||||
@cache_table :banned_urls_cache
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
def cache_table, do: @cache_table
|
||||
|
||||
@spec in_banned_urls(String.t()) :: boolean()
|
||||
def in_banned_urls(url), do: elem(Cachex.exists?(@cache_table, url(url)), 1)
|
||||
def in_banned_urls(url), do: elem(@cachex.exists?(@cache_table, url(url)), 1)
|
||||
|
||||
def remove_from_banned_urls(urls) when is_list(urls) do
|
||||
Cachex.execute!(@cache_table, fn cache ->
|
||||
Enum.each(Invalidation.prepare_urls(urls), &Cachex.del(cache, &1))
|
||||
@cachex.execute!(@cache_table, fn cache ->
|
||||
Enum.each(Invalidation.prepare_urls(urls), &@cachex.del(cache, &1))
|
||||
end)
|
||||
end
|
||||
|
||||
def remove_from_banned_urls(url) when is_binary(url) do
|
||||
Cachex.del(@cache_table, url(url))
|
||||
@cachex.del(@cache_table, url(url))
|
||||
end
|
||||
|
||||
def put_in_banned_urls(urls) when is_list(urls) do
|
||||
Cachex.execute!(@cache_table, fn cache ->
|
||||
Enum.each(Invalidation.prepare_urls(urls), &Cachex.put(cache, &1, true))
|
||||
@cachex.execute!(@cache_table, fn cache ->
|
||||
Enum.each(Invalidation.prepare_urls(urls), &@cachex.put(cache, &1, true))
|
||||
end)
|
||||
end
|
||||
|
||||
def put_in_banned_urls(url) when is_binary(url) do
|
||||
Cachex.put(@cache_table, url(url), true)
|
||||
@cachex.put(@cache_table, url(url), true)
|
||||
end
|
||||
|
||||
def url(url) when is_nil(url) or url == "", do: nil
|
||||
|
@ -74,14 +74,14 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
||||
cond do
|
||||
format in ["json", "activity+json"] ->
|
||||
if activity.local do
|
||||
%{data: %{"id" => redirect_url}} = Object.normalize(activity)
|
||||
%{data: %{"id" => redirect_url}} = Object.normalize(activity, fetch: false)
|
||||
redirect(conn, external: redirect_url)
|
||||
else
|
||||
{:error, :not_found}
|
||||
end
|
||||
|
||||
activity.data["type"] == "Create" ->
|
||||
%Object{} = object = Object.normalize(activity)
|
||||
%Object{} = object = Object.normalize(activity, fetch: false)
|
||||
|
||||
RedirectController.redirector_with_meta(
|
||||
conn,
|
||||
@ -112,7 +112,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
||||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
|
||||
true <- Visibility.is_public?(activity),
|
||||
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
|
||||
%Object{} = object <- Object.normalize(activity),
|
||||
%Object{} = object <- Object.normalize(activity, fetch: false),
|
||||
%{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
|
||||
true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
|
||||
conn
|
||||
|
@ -82,7 +82,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
||||
media_id: params[:media_id],
|
||||
idempotency_key: idempotency_key(conn)
|
||||
),
|
||||
message <- Object.normalize(activity, false),
|
||||
message <- Object.normalize(activity, fetch: false),
|
||||
cm_ref <- MessageReference.for_chat_and_object(chat, message) do
|
||||
conn
|
||||
|> put_view(MessageReferenceView)
|
||||
|
@ -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
|
||||
|
@ -29,7 +29,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionController do
|
||||
with true <- Pleroma.Config.get([:instance, :show_reactions]),
|
||||
%Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
|
||||
%Object{data: %{"reactions" => reactions}} when is_list(reactions) <-
|
||||
Object.normalize(activity) do
|
||||
Object.normalize(activity, fetch: false) do
|
||||
reactions =
|
||||
reactions
|
||||
|> filter(params)
|
||||
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.PleromaAPI.BackupView do
|
||||
|
||||
def render("show.json", %{backup: %Backup{} = backup}) do
|
||||
%{
|
||||
id: backup.id,
|
||||
content_type: backup.content_type,
|
||||
url: download_url(backup),
|
||||
file_size: backup.file_size,
|
||||
|
@ -10,6 +10,8 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
|
||||
alias Pleroma.Web.CommonAPI.Utils
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
def render(
|
||||
"show.json",
|
||||
%{
|
||||
@ -51,7 +53,7 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
|
||||
end
|
||||
|
||||
defp put_idempotency_key(data) do
|
||||
with {:ok, idempotency_key} <- Cachex.get(:chat_message_id_idempotency_key_cache, data.id) do
|
||||
with {:ok, idempotency_key} <- @cachex.get(:chat_message_id_idempotency_key_cache, data.id) do
|
||||
data
|
||||
|> Maps.put_if_present(:idempotency_key, idempotency_key)
|
||||
else
|
||||
|
@ -15,7 +15,7 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleView do
|
||||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
|
||||
def render("show.json", %{activity: %Activity{data: %{"type" => "Listen"}} = activity} = opts) do
|
||||
object = Object.normalize(activity)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
|
||||
user = CommonAPI.get_user(activity.data["actor"])
|
||||
created_at = Utils.to_masto_date(activity.data["published"])
|
||||
|
@ -41,6 +41,8 @@ defmodule Pleroma.Web.Plugs.Cache do
|
||||
|
||||
@defaults %{ttl: nil, query_params: true}
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
@impl true
|
||||
def init([]), do: @defaults
|
||||
|
||||
@ -53,7 +55,7 @@ defmodule Pleroma.Web.Plugs.Cache do
|
||||
def call(%{method: "GET"} = conn, opts) do
|
||||
key = cache_key(conn, opts)
|
||||
|
||||
case Cachex.get(:web_resp_cache, key) do
|
||||
case @cachex.get(:web_resp_cache, key) do
|
||||
{:ok, nil} ->
|
||||
cache_resp(conn, opts)
|
||||
|
||||
@ -97,11 +99,11 @@ defmodule Pleroma.Web.Plugs.Cache do
|
||||
|
||||
conn =
|
||||
unless opts[:tracking_fun] do
|
||||
Cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl)
|
||||
@cachex.put(:web_resp_cache, key, {content_type, body}, ttl: ttl)
|
||||
conn
|
||||
else
|
||||
tracking_fun_data = Map.get(conn.assigns, :tracking_fun_data, nil)
|
||||
Cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl)
|
||||
@cachex.put(:web_resp_cache, key, {content_type, body, tracking_fun_data}, ttl: ttl)
|
||||
|
||||
opts.tracking_fun.(conn, tracking_fun_data)
|
||||
end
|
||||
|
@ -8,6 +8,8 @@ defmodule Pleroma.Web.Plugs.IdempotencyPlug do
|
||||
|
||||
@behaviour Plug
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
@impl true
|
||||
def init(opts), do: opts
|
||||
|
||||
@ -25,7 +27,7 @@ defmodule Pleroma.Web.Plugs.IdempotencyPlug do
|
||||
def call(conn, _), do: conn
|
||||
|
||||
def process_request(conn, key) do
|
||||
case Cachex.get(:idempotency_cache, key) do
|
||||
case @cachex.get(:idempotency_cache, key) do
|
||||
{:ok, nil} ->
|
||||
cache_resposnse(conn, key)
|
||||
|
||||
@ -43,7 +45,7 @@ defmodule Pleroma.Web.Plugs.IdempotencyPlug do
|
||||
content_type = get_content_type(conn)
|
||||
|
||||
record = {request_id, content_type, conn.status, conn.resp_body}
|
||||
{:ok, _} = Cachex.put(:idempotency_cache, key, record)
|
||||
{:ok, _} = @cachex.put(:idempotency_cache, key, record)
|
||||
|
||||
conn
|
||||
|> put_resp_header("idempotency-key", key)
|
||||
|
@ -72,6 +72,8 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
|
||||
|
||||
require Logger
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
@doc false
|
||||
def init(plug_opts) do
|
||||
plug_opts
|
||||
@ -124,7 +126,7 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
|
||||
key_name = make_key_name(action_settings)
|
||||
limit = get_limits(action_settings)
|
||||
|
||||
case Cachex.get(bucket_name, key_name) do
|
||||
case @cachex.get(bucket_name, key_name) do
|
||||
{:error, :no_cache} ->
|
||||
@inspect_bucket_not_found
|
||||
|
||||
@ -157,7 +159,7 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
|
||||
key_name = make_key_name(action_settings)
|
||||
limit = get_limits(action_settings)
|
||||
|
||||
case Cachex.get_and_update(bucket_name, key_name, &increment_value(&1, limit)) do
|
||||
case @cachex.get_and_update(bucket_name, key_name, &increment_value(&1, limit)) do
|
||||
{:commit, value} ->
|
||||
{:ok, value}
|
||||
|
||||
|
@ -87,8 +87,15 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do
|
||||
end
|
||||
|
||||
defp get_media(conn, {:url, url}, true, _) do
|
||||
proxy_opts = [
|
||||
http: [
|
||||
follow_redirect: true,
|
||||
pool: :upload
|
||||
]
|
||||
]
|
||||
|
||||
conn
|
||||
|> Pleroma.ReverseProxy.call(url, Pleroma.Config.get([Pleroma.Upload, :proxy_opts], []))
|
||||
|> Pleroma.ReverseProxy.call(url, proxy_opts)
|
||||
end
|
||||
|
||||
defp get_media(conn, {:url, url}, _, _) do
|
||||
|
@ -32,7 +32,7 @@ defmodule Pleroma.Web.Push.Impl do
|
||||
mastodon_type = notification.type
|
||||
gcm_api_key = Application.get_env(:web_push_encryption, :gcm_api_key)
|
||||
avatar_url = User.avatar_url(actor)
|
||||
object = Object.normalize(activity, false)
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
user = User.get_cached_by_id(user_id)
|
||||
direct_conversation_id = Activity.direct_conversation_id(activity, user)
|
||||
|
||||
|
@ -12,8 +12,9 @@ defmodule Pleroma.Web.RelMe do
|
||||
if Pleroma.Config.get(:env) == :test do
|
||||
def parse(url) when is_binary(url), do: parse_url(url)
|
||||
else
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
def parse(url) when is_binary(url) do
|
||||
Cachex.fetch!(:rel_me_cache, url, fn _ ->
|
||||
@cachex.fetch!(:rel_me_cache, url, fn _ ->
|
||||
{:commit, parse_url(url)}
|
||||
end)
|
||||
rescue
|
||||
|
@ -69,7 +69,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
|
||||
|
||||
def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||
with true <- Config.get([:rich_media, :enabled]),
|
||||
%Object{} = object <- Object.normalize(activity) do
|
||||
%Object{} = object <- Object.normalize(activity, fetch: false) do
|
||||
fetch_data_for_object(object)
|
||||
else
|
||||
_ -> %{}
|
||||
|
@ -5,6 +5,8 @@
|
||||
defmodule Pleroma.Web.RichMedia.Parser do
|
||||
require Logger
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
defp parsers do
|
||||
Pleroma.Config.get([:rich_media, :parsers])
|
||||
end
|
||||
@ -24,7 +26,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
||||
end
|
||||
|
||||
defp get_cached_or_parse(url) do
|
||||
case Cachex.fetch(:rich_media_cache, url, fn ->
|
||||
case @cachex.fetch(:rich_media_cache, url, fn ->
|
||||
case parse_url(url) do
|
||||
{:ok, _} = res ->
|
||||
{:commit, res}
|
||||
@ -64,7 +66,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
||||
|
||||
defp set_error_ttl(url, _reason) do
|
||||
ttl = Pleroma.Config.get([:rich_media, :failure_backoff], 60_000)
|
||||
Cachex.expire(:rich_media_cache, url, ttl)
|
||||
@cachex.expire(:rich_media_cache, url, ttl)
|
||||
:ok
|
||||
end
|
||||
|
||||
@ -106,7 +108,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
||||
{:ok, ttl} when is_number(ttl) ->
|
||||
ttl = ttl * 1000
|
||||
|
||||
case Cachex.expire_at(:rich_media_cache, url, ttl) do
|
||||
case @cachex.expire_at(:rich_media_cache, url, ttl) do
|
||||
{:ok, true} -> {:ok, ttl}
|
||||
{:ok, false} -> {:error, :no_key}
|
||||
end
|
||||
|
@ -122,7 +122,7 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
||||
end
|
||||
|
||||
defp get_counts(%Activity{} = activity) do
|
||||
%Object{data: data} = Object.normalize(activity)
|
||||
%Object{data: data} = Object.normalize(activity, fetch: false)
|
||||
|
||||
%{
|
||||
likes: data["like_count"] || 0,
|
||||
|
@ -151,7 +151,7 @@ defmodule Pleroma.Web.Streamer do
|
||||
recipients = MapSet.new(item.recipients)
|
||||
domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.domain_blocks)
|
||||
|
||||
with parent <- Object.normalize(item) || item,
|
||||
with parent <- Object.normalize(item, fetch: false) || item,
|
||||
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 <-
|
||||
|
@ -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]-->
|
||||
|
@ -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) <-
|
||||
|
@ -58,12 +58,16 @@ defmodule Pleroma.Web.WebFinger do
|
||||
] ++ Publisher.gather_webfinger_links(user)
|
||||
end
|
||||
|
||||
defp gather_aliases(%User{} = user) do
|
||||
[user.ap_id | user.also_known_as]
|
||||
end
|
||||
|
||||
def represent_user(user, "JSON") do
|
||||
{:ok, user} = User.ensure_keys_present(user)
|
||||
|
||||
%{
|
||||
"subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}",
|
||||
"aliases" => [user.ap_id],
|
||||
"aliases" => gather_aliases(user),
|
||||
"links" => gather_links(user)
|
||||
}
|
||||
end
|
||||
@ -71,6 +75,11 @@ defmodule Pleroma.Web.WebFinger do
|
||||
def represent_user(user, "XML") do
|
||||
{:ok, user} = User.ensure_keys_present(user)
|
||||
|
||||
aliases =
|
||||
user
|
||||
|> gather_aliases()
|
||||
|> Enum.map(&{:Alias, &1})
|
||||
|
||||
links =
|
||||
gather_links(user)
|
||||
|> Enum.map(fn link -> {:Link, link} end)
|
||||
@ -79,9 +88,8 @@ defmodule Pleroma.Web.WebFinger do
|
||||
:XRD,
|
||||
%{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"},
|
||||
[
|
||||
{:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"},
|
||||
{:Alias, user.ap_id}
|
||||
] ++ links
|
||||
{:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"}
|
||||
] ++ aliases ++ links
|
||||
}
|
||||
|> XmlBuilder.to_doc()
|
||||
end
|
||||
@ -116,6 +124,9 @@ defmodule Pleroma.Web.WebFinger do
|
||||
{"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} ->
|
||||
Map.put(data, "ap_id", link["href"])
|
||||
|
||||
{nil, "http://ostatus.org/schema/1.0/subscribe"} ->
|
||||
Map.put(data, "subscribe_address", link["template"])
|
||||
|
||||
_ ->
|
||||
Logger.debug("Unhandled type: #{inspect(link["type"])}")
|
||||
data
|
||||
|
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"),
|
||||
@ -147,8 +147,8 @@ defmodule Pleroma.Mixfile do
|
||||
{:earmark, "1.4.3"},
|
||||
{:bbcode_pleroma, "~> 0.2.0"},
|
||||
{:crypt,
|
||||
git: "https://github.com/msantos/crypt.git",
|
||||
ref: "f63a705f92c26955977ee62a313012e309a4d77a"},
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/crypt.git",
|
||||
ref: "cf2aa3f11632e8b0634810a15b3e612c7526f6a3"},
|
||||
{:cors_plug, "~> 2.0"},
|
||||
{:web_push_encryption, "~> 0.3"},
|
||||
{:swoosh, "~> 1.0"},
|
||||
@ -158,7 +158,7 @@ defmodule Pleroma.Mixfile do
|
||||
{:floki, "~> 0.27"},
|
||||
{:timex, "~> 3.6"},
|
||||
{:ueberauth, "~> 0.4"},
|
||||
{:linkify, "~> 0.4.0"},
|
||||
{:linkify, "~> 0.4.1"},
|
||||
{:http_signatures, "~> 0.1.0"},
|
||||
{:telemetry, "~> 0.3"},
|
||||
{:poolboy, "~> 1.5"},
|
||||
@ -211,7 +211,7 @@ defmodule Pleroma.Mixfile do
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/hackney.git",
|
||||
ref: "7d7119f0651515d6d7669c78393fd90950a3ec6e",
|
||||
override: true},
|
||||
{:mox, "~> 0.5", only: :test},
|
||||
{:mox, "~> 1.0", only: :test},
|
||||
{:websocket_client, git: "https://github.com/jeremyong/websocket_client.git", only: :test}
|
||||
] ++ oauth_deps()
|
||||
end
|
||||
|
8
mix.lock
8
mix.lock
@ -22,7 +22,7 @@
|
||||
"cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"},
|
||||
"credo": {:hex, :credo, "1.4.1", "16392f1edd2cdb1de9fe4004f5ab0ae612c92e230433968eab00aafd976282fc", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "155f8a2989ad77504de5d8291fa0d41320fdcaa6a1030472e9967f285f8c7692"},
|
||||
"crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"crypt": {:git, "https://github.com/msantos/crypt.git", "f63a705f92c26955977ee62a313012e309a4d77a", [ref: "f63a705f92c26955977ee62a313012e309a4d77a"]},
|
||||
"crypt": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/crypt.git", "cf2aa3f11632e8b0634810a15b3e612c7526f6a3", [ref: "cf2aa3f11632e8b0634810a15b3e612c7526f6a3"]},
|
||||
"custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"},
|
||||
"db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"},
|
||||
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
|
||||
@ -33,7 +33,7 @@
|
||||
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.4.5", "30161f81b167d561a9a2df4329c10ae05ff36eca7ccc84628f2c8b9fa1e43323", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "31990c6a3579b36a3c0841d34a94c275e727de8b84f58509da5f1b2032c98ac2"},
|
||||
"eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.6.1", "8faa29a5597faba999aeeb72bbb9c91694ef8068f0131192fb199f98d32994ef", [:mix], [], "hexpm", "35d33270680f8d839a4003c3e9f43afb595310a592405a00afc12de4c7f55a18"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
|
||||
"esshd": {:hex, :esshd, "0.1.1", "d4dd4c46698093a40a56afecce8a46e246eb35463c457c246dacba2e056f31b5", [:mix], [], "hexpm", "d73e341e3009d390aa36387dc8862860bf9f874c94d9fd92ade2926376f49981"},
|
||||
"eternal": {:hex, :eternal, "1.2.1", "d5b6b2499ba876c57be2581b5b999ee9bdf861c647401066d3eeed111d096bc4", [:mix], [], "hexpm", "b14f1dc204321429479c569cfbe8fb287541184ed040956c8862cb7a677b8406"},
|
||||
"ex2ms": {:hex, :ex2ms, "1.5.0", "19e27f9212be9a96093fed8cdfbef0a2b56c21237196d26760f11dfcfae58e97", [:mix], [], "hexpm"},
|
||||
@ -65,7 +65,7 @@
|
||||
"jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"},
|
||||
"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"},
|
||||
"linkify": {:hex, :linkify, "0.4.1", "f881eb3429ae88010cf736e6fb3eed406c187bcdd544902ec937496636b7c7b3", [:mix], [], "hexpm", "ce98693f54ae9ace59f2f7a8aed3de2ef311381a8ce7794804bd75484c371dda"},
|
||||
"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"},
|
||||
@ -76,7 +76,7 @@
|
||||
"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"},
|
||||
"mox": {:hex, :mox, "0.5.2", "55a0a5ba9ccc671518d068c8dddd20eeb436909ea79d1799e2209df7eaa98b6c", [:mix], [], "hexpm", "df4310628cd628ee181df93f50ddfd07be3e5ecc30232d3b6aadf30bdfe6092b"},
|
||||
"mox": {:hex, :mox, "1.0.0", "4b3c7005173f47ff30641ba044eb0fe67287743eec9bd9545e37f3002b0a9f8b", [:mix], [], "hexpm", "201b0a20b7abdaaab083e9cf97884950f8a30a1350a1da403b3145e213c6f4df"},
|
||||
"myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"},
|
||||
"nimble_pool": {:hex, :nimble_pool, "0.1.0", "ffa9d5be27eee2b00b0c634eb649aa27f97b39186fec3c493716c2a33e784ec6", [:mix], [], "hexpm", "343a1eaa620ddcf3430a83f39f2af499fe2370390d4f785cd475b4df5acaf3f9"},
|
||||
|
141
priv/gettext/en/LC_MESSAGES/posix_errors.po
Normal file
141
priv/gettext/en/LC_MESSAGES/posix_errors.po
Normal file
@ -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"
|
149
priv/gettext/posix_errors.pot
Normal file
149
priv/gettext/posix_errors.pot
Normal file
@ -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 ""
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user