Merge remote-tracking branch 'origin/develop' into shigusegubu
* origin/develop: (252 commits) mix.exs: 2.4.50 Update PleromaFE Bundle (2.4.0) CHANGELOG.md: Fix instances of 2020 being actually 2021 Release 2.4.0 Update AdminFE bundle Visibility: check Tombstone objects in visible_for_user?/2 CHANGELOG: fixed JSON error rendering Add activity+json to Phoenix :format_encoders Fixes ErrorView rendering CHANGELOG: AdminAPI return date with users AdminAPI: add date to users CHANGELOG: AdminAPI users sort AdminAPI: sort user results by ID descending Moderators: reorganize :admin_api pipeline in Router Moderators: add UserIsStaffPlug AP C2S: Remove restrictions and make it go through pipeline Update Linkify to fix crash on posts with a URL we failed to parse correctly fix the fucking list timelines on mastofe/soapbox-fe Deletions: change User.purge/1 to defp, add CHANGELOG entry Deletions: don't purge keys so Delete/Undo activities can be signed Deletions: allow deactivated users to be deleted ...
This commit is contained in:
commit
78c21efa1e
562 changed files with 6099 additions and 2580 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -28,6 +28,7 @@ erl_crash.dump
|
|||
# variables.
|
||||
/config/*.secret.exs
|
||||
/config/generated_config.exs
|
||||
/config/runtime.exs
|
||||
/config/*.env
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ variables: &global_variables
|
|||
MIX_ENV: test
|
||||
|
||||
cache: &global_cache_policy
|
||||
key: ${CI_COMMIT_REF_SLUG}
|
||||
key:
|
||||
files:
|
||||
- mix.lock
|
||||
paths:
|
||||
- deps
|
||||
- _build
|
||||
|
|
@ -22,6 +24,8 @@ stages:
|
|||
- docker
|
||||
|
||||
before_script:
|
||||
- echo $MIX_ENV
|
||||
- rm -rf _build/*/lib/pleroma
|
||||
- apt-get update && apt-get install -y cmake
|
||||
- mix local.hex --force
|
||||
- mix local.rebar --force
|
||||
|
|
@ -29,13 +33,25 @@ before_script:
|
|||
- apt-get -qq update
|
||||
- apt-get install -y libmagic-dev
|
||||
|
||||
after_script:
|
||||
- rm -rf _build/*/lib/pleroma
|
||||
|
||||
build:
|
||||
stage: build
|
||||
only:
|
||||
changes:
|
||||
- "**/*.ex"
|
||||
- "**/*.exs"
|
||||
- "mix.lock"
|
||||
script:
|
||||
- mix compile --force
|
||||
|
||||
spec-build:
|
||||
stage: test
|
||||
only:
|
||||
changes:
|
||||
- "lib/pleroma/web/api_spec/**/*.ex"
|
||||
- "lib/pleroma/web/api_spec.ex"
|
||||
artifacts:
|
||||
paths:
|
||||
- spec.json
|
||||
|
|
@ -58,6 +74,11 @@ benchmark:
|
|||
|
||||
unit-testing:
|
||||
stage: test
|
||||
only:
|
||||
changes:
|
||||
- "**/*.ex"
|
||||
- "**/*.exs"
|
||||
- "mix.lock"
|
||||
retry: 2
|
||||
cache: &testing_cache_policy
|
||||
<<: *global_cache_policy
|
||||
|
|
@ -91,6 +112,11 @@ unit-testing:
|
|||
|
||||
unit-testing-rum:
|
||||
stage: test
|
||||
only:
|
||||
changes:
|
||||
- "**/*.ex"
|
||||
- "**/*.exs"
|
||||
- "mix.lock"
|
||||
retry: 2
|
||||
cache: *testing_cache_policy
|
||||
services:
|
||||
|
|
@ -109,16 +135,40 @@ unit-testing-rum:
|
|||
|
||||
lint:
|
||||
stage: test
|
||||
only:
|
||||
changes:
|
||||
- "**/*.ex"
|
||||
- "**/*.exs"
|
||||
- "mix.lock"
|
||||
cache: *testing_cache_policy
|
||||
script:
|
||||
- mix format --check-formatted
|
||||
|
||||
analysis:
|
||||
stage: test
|
||||
only:
|
||||
changes:
|
||||
- "**/*.ex"
|
||||
- "**/*.exs"
|
||||
- "mix.lock"
|
||||
cache: *testing_cache_policy
|
||||
script:
|
||||
- mix credo --strict --only=warnings,todo,fixme,consistency,readability
|
||||
|
||||
cycles:
|
||||
stage: test
|
||||
image: elixir:1.11
|
||||
only:
|
||||
changes:
|
||||
- "**/*.ex"
|
||||
- "**/*.exs"
|
||||
- "mix.lock"
|
||||
cache: {}
|
||||
script:
|
||||
- mix deps.get
|
||||
- mix compile
|
||||
- mix xref graph --format cycles --label compile | awk '{print $0} END{exit ($0 != "No cycles found")}'
|
||||
|
||||
docs-deploy:
|
||||
stage: deploy
|
||||
cache: *testing_cache_policy
|
||||
|
|
|
|||
45
CHANGELOG.md
45
CHANGELOG.md
|
|
@ -8,19 +8,54 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
|
||||
### Changed
|
||||
|
||||
### Added
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
## 2.4.0 - 2021-08-xx
|
||||
|
||||
### Changed
|
||||
|
||||
- **Breaking:** Configuration: `:chat, enabled` moved to `:shout, enabled` and `:instance, chat_limit` moved to `:shout, limit`
|
||||
- Support for Erlang/OTP 24
|
||||
- The `application` metadata returned with statuses is no longer hardcoded. Apps that want to display these details will now have valid data for new posts after this change.
|
||||
- HTTPSecurityPlug now sends a response header to opt out of Google's FLoC (Federated Learning of Cohorts) targeted advertising.
|
||||
- Email address is now returned if requesting user is the owner of the user account so it can be exposed in client and FE user settings UIs.
|
||||
- Improved Twittercard and OpenGraph meta tag generation including thumbnails and image dimension metadata when available.
|
||||
- AdminAPI: sort users so the newest are at the top.
|
||||
- ActivityPub Client-to-Server(C2S): Limitation on the type of Activity/Object are lifted as they are now passed through ObjectValidators
|
||||
|
||||
### Added
|
||||
|
||||
- MRF (`FollowBotPolicy`): New MRF Policy which makes a designated local Bot account attempt to follow all users in public Notes received by your instance. Users who require approving follower requests or have #nobot in their profile are excluded.
|
||||
|
||||
## Unreleased (Patch)
|
||||
- Return OAuth token `id` (primary key) in POST `/oauth/token`.
|
||||
- AdminAPI: return `created_at` date with users.
|
||||
- `AnalyzeMetadata` upload filter for extracting image/video attachment dimensions and generating blurhashes for images. Blurhashes for videos are not generated at this time.
|
||||
- Attachment dimensions and blurhashes are federated when available.
|
||||
- Pinned posts federation
|
||||
|
||||
### Fixed
|
||||
|
||||
- Don't crash so hard when email settings are invalid.
|
||||
- Checking activated Upload Filters for required commands.
|
||||
- Remote users can no longer reappear after being deleted.
|
||||
- Deactivated users may now be deleted.
|
||||
- Mix task `pleroma.database prune_objects`
|
||||
- Fixed rendering of JSON errors on ActivityPub endpoints.
|
||||
- Linkify: Parsing crash with URLs ending in unbalanced closed paren, no path separator, and no query parameters
|
||||
- Try to save exported ConfigDB settings (migrate_from_db) in the system temp directory if default location is not writable.
|
||||
- Uploading custom instance thumbnail via AdminAPI/AdminFE generated invalid URL to the image
|
||||
- Applying ConcurrentLimiter settings via AdminAPI
|
||||
- User login failures if their `notification_settings` were in a NULL state.
|
||||
- Mix task `pleroma.user delete_activities` query transaction timeout is now :infinity
|
||||
- MRF (`SimplePolicy`): Embedded objects are now checked. If any embedded object would be rejected, its parent is rejected. This fixes Announces leaking posts from blocked domains.
|
||||
- Fixed some Markdown issues, including trailing slash in links.
|
||||
|
||||
## [2.3.0] - 2020-03-01
|
||||
### Removed
|
||||
- **Breaking**: Remove deprecated `/api/qvitter/statuses/notifications/read` (replaced by `/api/v1/pleroma/notifications/read`)
|
||||
|
||||
## [2.3.0] - 2021-03-01
|
||||
|
||||
### Security
|
||||
|
||||
|
|
@ -121,7 +156,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Mastodon API: Support for expires_in/expires_at in the Filters.
|
||||
</details>
|
||||
|
||||
## [2.2.2] - 2020-01-18
|
||||
## [2.2.2] - 2021-01-18
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ ARG DATA=/var/lib/pleroma
|
|||
|
||||
RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
|
||||
apk update &&\
|
||||
apk add exiftool imagemagick libmagic ncurses postgresql-client &&\
|
||||
apk add exiftool ffmpeg imagemagick libmagic ncurses postgresql-client &&\
|
||||
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
|
||||
mkdir -p ${DATA}/uploads &&\
|
||||
mkdir -p ${DATA}/static &&\
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ Currently Pleroma is not packaged by any OS/Distros, but if you want to package
|
|||
### Docker
|
||||
While we don’t provide docker files, other people have written very good ones. Take a look at <https://github.com/angristan/docker-pleroma> or <https://glitch.sh/sn0w/pleroma-docker>.
|
||||
|
||||
### Raspberry Pi
|
||||
Community maintained Raspberry Pi image that you can flash and run Pleroma on your Raspberry Pi. Available here <https://github.com/guysoft/PleromaPi>.
|
||||
|
||||
### Compilation Troubleshooting
|
||||
If you ever encounter compilation issues during the updating of Pleroma, you can try these commands and see if they fix things:
|
||||
|
||||
|
|
@ -50,5 +53,5 @@ If you are not developing Pleroma, it is better to use the OTP release, which co
|
|||
- Latest Git revision: <https://docs-develop.pleroma.social>
|
||||
|
||||
## Community Channels
|
||||
* IRC: **#pleroma** and **#pleroma-dev** on freenode, webchat is available at <https://irc.pleroma.social>
|
||||
* Matrix: <https://matrix.to/#/#freenode_#pleroma:matrix.org> and <https://matrix.to/#/#freenode_#pleroma-dev:matrix.org>
|
||||
* IRC: **#pleroma** and **#pleroma-dev** on libera.chat, webchat is available at <https://irc.pleroma.social>
|
||||
* Matrix: [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) and [#pleroma-dev:libera.chat](https://matrix.to/#/#pleroma-dev:libera.chat)
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ defmodule Pleroma.LoadTesting.Activities do
|
|||
"url" => [
|
||||
%{
|
||||
"href" =>
|
||||
"#{Pleroma.Web.base_url()}/media/b1b873552422a07bf53af01f3c231c841db4dfc42c35efde681abaf0f2a4eab7.jpg",
|
||||
"#{Pleroma.Web.Endpoint.url()}/media/b1b873552422a07bf53af01f3c231c841db4dfc42c35efde681abaf0f2a4eab7.jpg",
|
||||
"mediaType" => "image/jpeg",
|
||||
"type" => "Link"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use Mix.Config
|
||||
import Config
|
||||
|
||||
# We don't run a server during test. If one is required,
|
||||
# you can enable the server option below.
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
#
|
||||
# This configuration file is loaded before any dependency and
|
||||
# is restricted to this project.
|
||||
use Mix.Config
|
||||
import Config
|
||||
|
||||
# General application configuration
|
||||
config :pleroma, ecto_repos: [Pleroma.Repo]
|
||||
|
|
@ -190,7 +190,6 @@ config :pleroma, :instance,
|
|||
instance_thumbnail: "/instance/thumbnail.jpeg",
|
||||
limit: 5_000,
|
||||
description_limit: 5_000,
|
||||
chat_limit: 5_000,
|
||||
remote_limit: 100_000,
|
||||
upload_limit: 200_000_000,
|
||||
avatar_upload_limit: 2_000_000,
|
||||
|
|
@ -471,9 +470,11 @@ config :pleroma, :media_preview_proxy,
|
|||
image_quality: 85,
|
||||
min_content_length: 100 * 1024
|
||||
|
||||
config :pleroma, :chat, enabled: false
|
||||
config :pleroma, :shout,
|
||||
enabled: false,
|
||||
limit: 5_000
|
||||
|
||||
config :phoenix, :format_encoders, json: Jason
|
||||
config :phoenix, :format_encoders, json: Jason, "activity+json": Jason
|
||||
|
||||
config :phoenix, :json_library, Jason
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use Mix.Config
|
||||
import Config
|
||||
|
||||
websocket_config = [
|
||||
path: "/websocket",
|
||||
|
|
@ -544,14 +544,6 @@ config :pleroma, :config_description, [
|
|||
5_000
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :chat_limit,
|
||||
type: :integer,
|
||||
description: "Character limit of the instance chat messages",
|
||||
suggestions: [
|
||||
5_000
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :remote_limit,
|
||||
type: :integer,
|
||||
|
|
@ -682,7 +674,8 @@ config :pleroma, :config_description, [
|
|||
%{
|
||||
key: :allow_relay,
|
||||
type: :boolean,
|
||||
description: "Enable Pleroma's Relay, which makes it possible to follow a whole instance"
|
||||
description:
|
||||
"Permits remote instances to subscribe to all public posts of your instance. (Important!) This may increase the visibility of your instance."
|
||||
},
|
||||
%{
|
||||
key: :public,
|
||||
|
|
@ -1182,7 +1175,6 @@ config :pleroma, :config_description, [
|
|||
alwaysShowSubjectInput: true,
|
||||
background: "/static/aurora_borealis.jpg",
|
||||
collapseMessageWithSubject: false,
|
||||
disableChat: false,
|
||||
greentext: false,
|
||||
hideFilteredStatuses: false,
|
||||
hideMutedPosts: false,
|
||||
|
|
@ -1229,12 +1221,6 @@ config :pleroma, :config_description, [
|
|||
description:
|
||||
"When a message has a subject (aka Content Warning), collapse it by default"
|
||||
},
|
||||
%{
|
||||
key: :disableChat,
|
||||
label: "PleromaFE Chat",
|
||||
type: :boolean,
|
||||
description: "Disables PleromaFE Chat component"
|
||||
},
|
||||
%{
|
||||
key: :greentext,
|
||||
label: "Greentext",
|
||||
|
|
@ -2652,13 +2638,22 @@ config :pleroma, :config_description, [
|
|||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :chat,
|
||||
key: :shout,
|
||||
type: :group,
|
||||
description: "Pleroma chat settings",
|
||||
description: "Pleroma shout settings",
|
||||
children: [
|
||||
%{
|
||||
key: :enabled,
|
||||
type: :boolean
|
||||
type: :boolean,
|
||||
description: "Enables the backend Shoutbox chat feature."
|
||||
},
|
||||
%{
|
||||
key: :limit,
|
||||
type: :integer,
|
||||
description: "Shout message character limit.",
|
||||
suggestions: [
|
||||
5_000
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use Mix.Config
|
||||
import Config
|
||||
|
||||
# For development, we disable any cache and enable
|
||||
# debugging and code reloading.
|
||||
|
|
@ -54,6 +54,10 @@ config :pleroma, Pleroma.Repo,
|
|||
|
||||
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: true
|
||||
|
||||
# Reduce recompilation time
|
||||
# https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects
|
||||
config :phoenix, :plug_init_mode, :runtime
|
||||
|
||||
if File.exists?("./config/dev.secret.exs") do
|
||||
import_config "dev.secret.exs"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use Mix.Config
|
||||
import Config
|
||||
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
http: [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use Mix.Config
|
||||
import Config
|
||||
|
||||
# For production, we often load configuration from external
|
||||
# sources, such as your system environment. For this reason,
|
||||
|
|
@ -63,7 +63,12 @@ config :logger, :ex_syslogger, level: :info
|
|||
|
||||
# Finally import the config/prod.secret.exs
|
||||
# which should be versioned separately.
|
||||
import_config "prod.secret.exs"
|
||||
if File.exists?("./config/prod.secret.exs") do
|
||||
import_config "prod.secret.exs"
|
||||
else
|
||||
"`config/prod.secret.exs` not found. You may want to create one by running `mix pleroma.instance gen`"
|
||||
|> IO.warn([])
|
||||
end
|
||||
|
||||
if File.exists?("./config/prod.exported_from_db.secret.exs"),
|
||||
do: import_config("prod.exported_from_db.secret.exs")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use Mix.Config
|
||||
import Config
|
||||
|
||||
# We don't run a server during test. If one is required,
|
||||
# you can enable the server option below.
|
||||
|
|
@ -133,6 +133,10 @@ config :pleroma, :side_effects,
|
|||
ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock,
|
||||
logger: Pleroma.LoggerMock
|
||||
|
||||
# Reduce recompilation time
|
||||
# https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects
|
||||
config :phoenix, :plug_init_mode, :runtime
|
||||
|
||||
if File.exists?("./config/test.secret.exs") do
|
||||
import_config "test.secret.exs"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ For from source installations Pleroma configuration works by first importing the
|
|||
|
||||
To add configuration to your config file, you can copy it from the base config. The latest version of it can be viewed [here](https://git.pleroma.social/pleroma/pleroma/blob/develop/config/config.exs). You can also use this file if you don't know how an option is supposed to be formatted.
|
||||
|
||||
## :chat
|
||||
## :shout
|
||||
|
||||
* `enabled` - Enables the backend chat. Defaults to `true`.
|
||||
* `enabled` - Enables the backend Shoutbox chat feature. Defaults to `true`.
|
||||
* `limit` - Shout character limit. Defaults to `5_000`
|
||||
|
||||
## :instance
|
||||
* `name`: The instance’s name.
|
||||
|
|
@ -19,7 +20,6 @@ To add configuration to your config file, you can copy it from the base config.
|
|||
* `description`: The instance’s description, can be seen in nodeinfo and ``/api/v1/instance``.
|
||||
* `limit`: Posts character limit (CW/Subject included in the counter).
|
||||
* `description_limit`: The character limit for image descriptions.
|
||||
* `chat_limit`: Character limit of the instance chat messages.
|
||||
* `remote_limit`: Hard character limit beyond which remote posts will be dropped.
|
||||
* `upload_limit`: File size limit of uploads (except for avatar, background, banner).
|
||||
* `avatar_upload_limit`: File size limit of user’s profile avatars.
|
||||
|
|
@ -37,7 +37,7 @@ To add configuration to your config file, you can copy it from the base config.
|
|||
* `federating`: Enable federation with other instances.
|
||||
* `federation_incoming_replies_max_depth`: Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes.
|
||||
* `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
|
||||
* `allow_relay`: Enable Pleroma’s Relay, which makes it possible to follow a whole instance.
|
||||
* `allow_relay`: Permits remote instances to subscribe to all public posts of your instance. This may increase the visibility of your instance.
|
||||
* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. Note that there is a dependent setting restricting or allowing unauthenticated access to specific resources, see `restrict_unauthenticated` for more details.
|
||||
* `quarantined_instances`: List of ActivityPub instances where private (DMs, followers-only) activities will not be send.
|
||||
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ For example, here is a sample policy module which rewrites all messages to "new
|
|||
```elixir
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.RewritePolicy do
|
||||
@moduledoc "MRF policy which rewrites all Notes to have 'new message content'."
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
# Catch messages which contain Note objects with actual data to filter.
|
||||
# Capture the object as `object`, the message content as `content` and the
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ Has these additional fields under the `pleroma` object:
|
|||
- `thread_muted`: true if the thread the post belongs to is muted
|
||||
- `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint.
|
||||
- `parent_visible`: If the parent of this post is visible to the user or not.
|
||||
- `pinned_at`: a datetime (iso8601) when status was pinned, `null` otherwise.
|
||||
|
||||
## Scheduled statuses
|
||||
|
||||
|
|
@ -255,9 +256,29 @@ This information is returned in the `/api/v1/accounts/verify_credentials` endpoi
|
|||
|
||||
*Pleroma supports refreshing tokens.*
|
||||
|
||||
`POST /oauth/token`
|
||||
### POST `/oauth/token`
|
||||
|
||||
Post here request with `grant_type=refresh_token` to obtain new access token. Returns an access token.
|
||||
You can obtain access tokens for a user in a few additional ways.
|
||||
|
||||
#### Refreshing a token
|
||||
|
||||
To obtain a new access token from a refresh token, pass `grant_type=refresh_token` with the following extra parameters:
|
||||
|
||||
- `refresh_token`: The refresh token.
|
||||
|
||||
#### Getting a token with a password
|
||||
|
||||
To obtain a token from a user's password, pass `grant_type=password` with the following extra parameters:
|
||||
|
||||
- `username`: Username to authenticate.
|
||||
- `password`: The user's password.
|
||||
|
||||
#### Response body
|
||||
|
||||
Additional fields are returned in the response:
|
||||
|
||||
- `id`: The primary key of this token in Pleroma's database.
|
||||
- `me` (user tokens only): The ActivityPub ID of the user who owns the token.
|
||||
|
||||
## Account Registration
|
||||
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ See [Admin-API](admin_api.md)
|
|||
* Note: Behaves exactly the same as `POST /api/v1/upload`.
|
||||
Can only accept images - any attempt to upload non-image files will be met with `HTTP 415 Unsupported Media Type`.
|
||||
|
||||
## `/api/v1/pleroma/notification_settings`
|
||||
## `/api/pleroma/notification_settings`
|
||||
### Updates user notification settings
|
||||
* Method `PUT`
|
||||
* Authentication: required
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ The default front-end used by Pleroma is Pleroma-FE. You can find more informati
|
|||
|
||||
### Mastodon interface
|
||||
If the Pleroma interface isn't your thing, or you're just trying something new but you want to keep using the familiar Mastodon interface, we got that too!
|
||||
Just add a "/web" after your instance url (e.g. <https://pleroma.soycaf.com/web>) and you'll end on the Mastodon web interface, but with a Pleroma backend! MAGIC!
|
||||
Just add a "/web" after your instance url (e.g. <https://pleroma.soykaf.com/web>) and you'll end on the Mastodon web interface, but with a Pleroma backend! MAGIC!
|
||||
The Mastodon interface is from the Glitch-soc fork. For more information on the Mastodon interface you can check the [Mastodon](https://docs.joinmastodon.org/) and [Glitch-soc](https://glitch-soc.github.io/docs/) documentation.
|
||||
|
||||
Remember, what you see is only the frontend part of Mastodon, the backend is still Pleroma.
|
||||
|
|
|
|||
|
|
@ -5,25 +5,7 @@ This guide is a step-by-step installation guide for Alpine Linux. The instructio
|
|||
|
||||
It assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.linode.com/docs/tools-reference/custom-kernels-distros/install-alpine-linux-on-your-linode/#configuration). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su -l <username> -s $SHELL -c 'command'` instead.
|
||||
|
||||
### Required packages
|
||||
|
||||
* `postgresql`
|
||||
* `elixir`
|
||||
* `erlang`
|
||||
* `erlang-parsetools`
|
||||
* `erlang-xmerl`
|
||||
* `git`
|
||||
* `file-dev`
|
||||
* Development Tools
|
||||
* `cmake`
|
||||
|
||||
#### Optional packages used in this guide
|
||||
|
||||
* `nginx` (preferred, example configs for other reverse proxies can be found in the repo)
|
||||
* `certbot` (or any other ACME client for Let’s Encrypt certificates)
|
||||
* `ImageMagick`
|
||||
* `ffmpeg`
|
||||
* `exiftool`
|
||||
{! backend/installation/generic_dependencies.include !}
|
||||
|
||||
### Prepare the system
|
||||
|
||||
|
|
@ -117,7 +99,7 @@ cd /opt/pleroma
|
|||
sudo -Hu pleroma mix deps.get
|
||||
```
|
||||
|
||||
* Generate the configuration: `sudo -Hu pleroma mix pleroma.instance gen`
|
||||
* Generate the configuration: `sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen`
|
||||
* Answer with `yes` if it asks you to install `rebar3`.
|
||||
* This may take some time, because parts of pleroma get compiled first.
|
||||
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
||||
|
|
@ -240,4 +222,4 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
|
|||
|
||||
## Questions
|
||||
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ cd /opt/pleroma
|
|||
sudo -Hu pleroma mix deps.get
|
||||
```
|
||||
|
||||
* Generate the configuration: `sudo -Hu pleroma mix pleroma.instance gen`
|
||||
* Generate the configuration: `sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen`
|
||||
* Answer with `yes` if it asks you to install `rebar3`.
|
||||
* This may take some time, because parts of pleroma get compiled first.
|
||||
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
||||
|
|
@ -215,4 +215,4 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
|
|||
|
||||
## Questions
|
||||
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||
|
|
|
|||
|
|
@ -1,27 +1,9 @@
|
|||
# Installing on Debian Based Distributions
|
||||
## Installation
|
||||
|
||||
This guide will assume you are on Debian Stretch. This guide should also work with Ubuntu 16.04 and 18.04. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-add-delete-and-grant-sudo-privileges-to-users-on-a-debian-vps). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
|
||||
This guide will assume you are on Debian 11 (“bullseye”) or later. This guide should also work with Ubuntu 18.04 (“Bionic Beaver”) and later. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-add-delete-and-grant-sudo-privileges-to-users-on-a-debian-vps). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
|
||||
|
||||
### Required packages
|
||||
|
||||
* `postgresql` (9.6+, Ubuntu 16.04 comes with 9.5, you can get a newer version from [here](https://www.postgresql.org/download/linux/ubuntu/))
|
||||
* `postgresql-contrib` (9.6+, same situtation as above)
|
||||
* `elixir` (1.8+, Follow the guide to install from the Erlang Solutions repo or use [asdf](https://github.com/asdf-vm/asdf) as the pleroma user)
|
||||
* `erlang-dev`
|
||||
* `erlang-nox`
|
||||
* `libmagic-dev`
|
||||
* `git`
|
||||
* `build-essential`
|
||||
* `cmake`
|
||||
|
||||
#### Optional packages used in this guide
|
||||
|
||||
* `nginx` (preferred, example configs for other reverse proxies can be found in the repo)
|
||||
* `certbot` (or any other ACME client for Let’s Encrypt certificates)
|
||||
* `ImageMagick`
|
||||
* `ffmpeg`
|
||||
* `exiftool`
|
||||
{! backend/installation/generic_dependencies.include !}
|
||||
|
||||
### Prepare the system
|
||||
|
||||
|
|
@ -40,20 +22,14 @@ sudo apt install git build-essential postgresql postgresql-contrib cmake libmagi
|
|||
|
||||
### Install Elixir and Erlang
|
||||
|
||||
* Download and add the Erlang repository:
|
||||
|
||||
```shell
|
||||
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb
|
||||
sudo dpkg -i /tmp/erlang-solutions_2.0_all.deb
|
||||
```
|
||||
|
||||
* Install Elixir and Erlang:
|
||||
* Install Elixir and Erlang (you might need to use backports or [asdf](https://github.com/asdf-vm/asdf) on old systems):
|
||||
|
||||
```shell
|
||||
sudo apt update
|
||||
sudo apt install elixir erlang-dev erlang-nox
|
||||
```
|
||||
|
||||
|
||||
### Optional packages: [`docs/installation/optional/media_graphics_packages.md`](../installation/optional/media_graphics_packages.md)
|
||||
|
||||
```shell
|
||||
|
|
@ -90,7 +66,7 @@ cd /opt/pleroma
|
|||
sudo -Hu pleroma mix deps.get
|
||||
```
|
||||
|
||||
* Generate the configuration: `sudo -Hu pleroma mix pleroma.instance gen`
|
||||
* Generate the configuration: `sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen`
|
||||
* Answer with `yes` if it asks you to install `rebar3`.
|
||||
* This may take some time, because parts of pleroma get compiled first.
|
||||
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
||||
|
|
@ -202,4 +178,4 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
|
|||
|
||||
## Questions
|
||||
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ sudo -Hu pleroma mix deps.get
|
|||
|
||||
* コンフィギュレーションを生成します。
|
||||
```
|
||||
sudo -Hu pleroma mix pleroma.instance gen
|
||||
sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen
|
||||
```
|
||||
* rebar3をインストールしてもよいか聞かれたら、yesを入力してください。
|
||||
* このときにpleromaの一部がコンパイルされるため、この処理には時間がかかります。
|
||||
|
|
@ -103,7 +103,7 @@ sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs}
|
|||
|
||||
* 先程のコマンドで、すでに `config/setup_db.psql` というファイルが作られています。このファイルをもとに、データベースを作成します。
|
||||
```
|
||||
sudo -Hu pleroma mix pleroma.instance gen
|
||||
sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen
|
||||
```
|
||||
|
||||
* そして、データベースのマイグレーションを実行します。
|
||||
|
|
@ -191,5 +191,5 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
|
|||
|
||||
インストールについて質問がある、もしくは、うまくいかないときは、以下のところで質問できます。
|
||||
|
||||
* [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org)
|
||||
* **Freenode** の **#pleroma** IRCチャンネル
|
||||
* [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat)
|
||||
* **libera.chat** の **#pleroma** IRCチャンネル
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
This document was written for FreeBSD 12.1, but should be work on future releases.
|
||||
|
||||
## Required software
|
||||
{! backend/installation/generic_dependencies.include !}
|
||||
|
||||
## Installing software used in this guide
|
||||
|
||||
This assumes the target system has `pkg(8)`.
|
||||
|
||||
|
|
@ -54,7 +56,7 @@ Configure Pleroma. Note that you need a domain name at this point:
|
|||
```
|
||||
$ cd /home/pleroma/pleroma
|
||||
$ mix deps.get # Enter "y" when asked to install Hex
|
||||
$ mix pleroma.instance gen # You will be asked a few questions here.
|
||||
$ MIX_ENV=prod mix pleroma.instance gen # You will be asked a few questions here.
|
||||
$ cp config/generated_config.exs config/prod.secret.exs
|
||||
```
|
||||
|
||||
|
|
@ -213,4 +215,4 @@ incorrect timestamps. You should have ntpd running.
|
|||
|
||||
## Questions
|
||||
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||
|
|
|
|||
16
docs/installation/generic_dependencies.include
Normal file
16
docs/installation/generic_dependencies.include
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
## Required dependencies
|
||||
|
||||
* PostgreSQL 9.6+
|
||||
* Elixir 1.9+
|
||||
* Erlang OTP 22.2+
|
||||
* git
|
||||
* file / libmagic
|
||||
* gcc (clang might also work)
|
||||
* GNU make
|
||||
* CMake
|
||||
|
||||
## Optionnal dependencies
|
||||
|
||||
* ImageMagick
|
||||
* FFmpeg
|
||||
* exiftool
|
||||
|
|
@ -3,9 +3,7 @@
|
|||
|
||||
This guide will assume that you have administrative rights, either as root or a user with [sudo permissions](https://wiki.gentoo.org/wiki/Sudo). Lines that begin with `#` indicate that they should be run as the superuser. Lines using `$` should be run as the indicated user, e.g. `pleroma$` should be run as the `pleroma` user.
|
||||
|
||||
### Configuring your hostname (optional)
|
||||
|
||||
If you would like your prompt to permanently include your host/domain, change `/etc/conf.d/hostname` to your hostname. You can reboot or use the `hostname` command to make immediate changes.
|
||||
{! backend/installation/generic_dependencies.include !}
|
||||
|
||||
### Your make.conf, package.use, and USE flags
|
||||
|
||||
|
|
@ -135,7 +133,7 @@ pleroma$ mix deps.get
|
|||
* Generate the configuration:
|
||||
|
||||
```shell
|
||||
pleroma$ mix pleroma.instance gen
|
||||
pleroma$ MIX_ENV=prod mix pleroma.instance gen
|
||||
```
|
||||
|
||||
* Answer with `yes` if it asks you to install `rebar3`.
|
||||
|
|
@ -298,4 +296,4 @@ If you opted to allow sudo for the `pleroma` user but would like to remove the a
|
|||
|
||||
## Questions
|
||||
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
# Installing on NetBSD
|
||||
|
||||
## Required software
|
||||
{! backend/installation/generic_dependencies.include !}
|
||||
|
||||
## Installing software used in this guide
|
||||
|
||||
pkgin should have been installed by the NetBSD installer if you selected
|
||||
the right options. If it isn't installed, install it using pkg_add.
|
||||
|
|
@ -71,7 +73,7 @@ Configure Pleroma. Note that you need a domain name at this point:
|
|||
```
|
||||
$ cd /home/pleroma/pleroma
|
||||
$ mix deps.get
|
||||
$ mix pleroma.instance gen # You will be asked a few questions here.
|
||||
$ MIX_ENV=prod mix pleroma.instance gen # You will be asked a few questions here.
|
||||
```
|
||||
|
||||
Since Postgres is configured, we can now initialize the database. There should
|
||||
|
|
@ -193,8 +195,6 @@ Run `# /etc/rc.d/pleroma start` to start Pleroma.
|
|||
|
||||
Restart nginx with `# /etc/rc.d/nginx restart` and you should be up and running.
|
||||
|
||||
If you need further help, contact niaa on freenode.
|
||||
|
||||
Make sure your time is in sync, or other instances will receive your posts with
|
||||
incorrect timestamps. You should have ntpd running.
|
||||
|
||||
|
|
@ -208,4 +208,4 @@ incorrect timestamps. You should have ntpd running.
|
|||
|
||||
## Questions
|
||||
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||
|
|
|
|||
|
|
@ -4,19 +4,11 @@ This guide describes the installation and configuration of pleroma (and the requ
|
|||
|
||||
For any additional information regarding commands and configuration files mentioned here, check the man pages [online](https://man.openbsd.org/) or directly on your server with the man command.
|
||||
|
||||
{! backend/installation/generic_dependencies.include !}
|
||||
|
||||
### Preparing the system
|
||||
#### Required software
|
||||
|
||||
The following packages need to be installed:
|
||||
|
||||
* elixir
|
||||
* gmake
|
||||
* git
|
||||
* postgresql-server
|
||||
* postgresql-contrib
|
||||
* cmake
|
||||
* ffmpeg
|
||||
* ImageMagick
|
||||
|
||||
To install them, run the following command (with doas or as root):
|
||||
|
||||
```
|
||||
|
|
@ -239,7 +231,7 @@ Enter a shell as \_pleroma (as root `su _pleroma -`) and enter pleroma's install
|
|||
Then follow the main installation guide:
|
||||
|
||||
* run `mix deps.get`
|
||||
* run `mix pleroma.instance gen` and enter your instance's information when asked
|
||||
* run `MIX_ENV=prod mix pleroma.instance gen` and enter your instance's information when asked
|
||||
* copy config/generated\_config.exs to config/prod.secret.exs. The default values should be sufficient but you should edit it and check that everything seems OK.
|
||||
* exit your current shell back to a root one and run `psql -U postgres -f /home/_pleroma/pleroma/config/setup_db.psql` to setup the database.
|
||||
* return to a \_pleroma shell into pleroma's installation directory (`su _pleroma -;cd ~/pleroma`) and run `MIX_ENV=prod mix ecto.migrate`
|
||||
|
|
@ -264,4 +256,4 @@ LC_ALL=en_US.UTF-8 MIX_ENV=prod mix pleroma.user new <username> <your@emailaddre
|
|||
|
||||
## Questions
|
||||
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ suositeltavaa tehdä komennon `doas` avulla, katso `doas (1)` ja `doas.conf (5)`
|
|||
Tästä eteenpäin oletuksena on, että domain "esimerkki.com" osoittaa
|
||||
serverin IP-osoitteeseen.
|
||||
|
||||
Jos asennuksen kanssa on ongelmia, IRC-kanava #pleroma Freenodessa tai
|
||||
Matrix-kanava #freenode_#pleroma:matrix.org ovat hyviä paikkoja löytää apua
|
||||
Jos asennuksen kanssa on ongelmia, IRC-kanava #pleroma Libera.chat tai
|
||||
Matrix-kanava #pleroma:libera.chat ovat hyviä paikkoja löytää apua
|
||||
(englanniksi), `/msg eal kukkuu` jos haluat välttämättä puhua härmää.
|
||||
|
||||
Asenna tarvittava ohjelmisto:
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ Other than things bundled in the OTP release Pleroma depends on:
|
|||
|
||||
=== "Alpine"
|
||||
```
|
||||
echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories
|
||||
awk 'NR==2' /etc/apk/repositories | sed 's/main/community/' | tee -a /etc/apk/repositories
|
||||
apk update
|
||||
apk add curl unzip ncurses postgresql postgresql-contrib nginx certbot file-dev
|
||||
```
|
||||
|
|
@ -50,7 +50,6 @@ Per [`docs/installation/optional/media_graphics_packages.md`](optional/media_gra
|
|||
|
||||
=== "Alpine"
|
||||
```
|
||||
echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories
|
||||
apk update
|
||||
apk add imagemagick ffmpeg exiftool
|
||||
```
|
||||
|
|
@ -232,7 +231,7 @@ At this point if you open your (sub)domain in a browser you should see a 502 err
|
|||
|
||||
If everything worked, you should see Pleroma-FE when visiting your domain. If that didn't happen, try reviewing the installation steps, starting Pleroma in the foreground and seeing if there are any errrors.
|
||||
|
||||
Still doesn't work? Feel free to contact us on [#pleroma on freenode](https://irc.pleroma.social) or via matrix at <https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org>, you can also [file an issue on our Gitlab](https://git.pleroma.social/pleroma/pleroma-support/issues/new)
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC, you can also [file an issue on our Gitlab](https://git.pleroma.social/pleroma/pleroma-support/issues/new).
|
||||
|
||||
## Post installation
|
||||
|
||||
|
|
@ -290,7 +289,7 @@ nginx -t
|
|||
|
||||
## Create your first user and set as admin
|
||||
```sh
|
||||
cd /opt/pleroma/bin
|
||||
cd /opt/pleroma
|
||||
su pleroma -s $SHELL -lc "./bin/pleroma_ctl user new joeuser joeuser@sld.tld --admin"
|
||||
```
|
||||
This will create an account withe the username of 'joeuser' with the email address of joeuser@sld.tld, and set that user's account as an admin. This will result in a link that you can paste into the browser, which logs you in and enables you to set the password.
|
||||
|
|
@ -301,4 +300,4 @@ This will create an account withe the username of 'joeuser' with the email addre
|
|||
|
||||
## Questions
|
||||
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
||||
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC, you can also [file an issue on our Gitlab](https://git.pleroma.social/pleroma/pleroma-support/issues/new).
|
||||
|
|
|
|||
|
|
@ -96,6 +96,15 @@ defmodule Mix.Tasks.Pleroma.Database do
|
|||
)
|
||||
|> Repo.delete_all(timeout: :infinity)
|
||||
|
||||
prune_hashtags_query = """
|
||||
DELETE FROM hashtags AS ht
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM hashtags_objects hto
|
||||
WHERE ht.id = hto.hashtag_id)
|
||||
"""
|
||||
|
||||
Repo.query(prune_hashtags_query)
|
||||
|
||||
if Keyword.get(options, :vacuum) do
|
||||
Maintenance.vacuum("full")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -184,40 +184,48 @@ defmodule Pleroma.Activity do
|
|||
|> Repo.one()
|
||||
end
|
||||
|
||||
@spec get_by_id(String.t()) :: Activity.t() | nil
|
||||
def get_by_id(id) do
|
||||
case FlakeId.flake_id?(id) do
|
||||
true ->
|
||||
Activity
|
||||
|> where([a], a.id == ^id)
|
||||
|> restrict_deactivated_users()
|
||||
|> Repo.one()
|
||||
@doc """
|
||||
Gets activity by ID, doesn't load activities from deactivated actors by default.
|
||||
"""
|
||||
@spec get_by_id(String.t(), keyword()) :: t() | nil
|
||||
def get_by_id(id, opts \\ [filter: [:restrict_deactivated]]), do: get_by_id_with_opts(id, opts)
|
||||
|
||||
_ ->
|
||||
nil
|
||||
@spec get_by_id_with_user_actor(String.t()) :: t() | nil
|
||||
def get_by_id_with_user_actor(id), do: get_by_id_with_opts(id, preload: [:user_actor])
|
||||
|
||||
@spec get_by_id_with_object(String.t()) :: t() | nil
|
||||
def get_by_id_with_object(id), do: get_by_id_with_opts(id, preload: [:object])
|
||||
|
||||
defp get_by_id_with_opts(id, opts) do
|
||||
if FlakeId.flake_id?(id) do
|
||||
query = Queries.by_id(id)
|
||||
|
||||
with_filters_query =
|
||||
if is_list(opts[:filter]) do
|
||||
Enum.reduce(opts[:filter], query, fn
|
||||
{:type, type}, acc -> Queries.by_type(acc, type)
|
||||
:restrict_deactivated, acc -> restrict_deactivated_users(acc)
|
||||
_, acc -> acc
|
||||
end)
|
||||
else
|
||||
query
|
||||
end
|
||||
|
||||
with_preloads_query =
|
||||
if is_list(opts[:preload]) do
|
||||
Enum.reduce(opts[:preload], with_filters_query, fn
|
||||
:user_actor, acc -> with_preloaded_user_actor(acc)
|
||||
:object, acc -> with_preloaded_object(acc)
|
||||
_, acc -> acc
|
||||
end)
|
||||
else
|
||||
with_filters_query
|
||||
end
|
||||
|
||||
Repo.one(with_preloads_query)
|
||||
end
|
||||
end
|
||||
|
||||
def get_by_id_with_user_actor(id) do
|
||||
case FlakeId.flake_id?(id) do
|
||||
true ->
|
||||
Activity
|
||||
|> where([a], a.id == ^id)
|
||||
|> with_preloaded_user_actor()
|
||||
|> Repo.one()
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def get_by_id_with_object(id) do
|
||||
Activity
|
||||
|> where(id: ^id)
|
||||
|> with_preloaded_object()
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
def all_by_ids_with_object(ids) do
|
||||
Activity
|
||||
|> where([a], a.id in ^ids)
|
||||
|
|
@ -269,6 +277,11 @@ defmodule Pleroma.Activity do
|
|||
|
||||
def get_create_by_object_ap_id_with_object(_), do: nil
|
||||
|
||||
@spec create_by_id_with_object(String.t()) :: t() | nil
|
||||
def create_by_id_with_object(id) do
|
||||
get_by_id_with_opts(id, preload: [:object], filter: [type: "Create"])
|
||||
end
|
||||
|
||||
defp get_in_reply_to_activity_from_object(%Object{data: %{"inReplyTo" => ap_id}}) do
|
||||
get_create_by_object_ap_id_with_object(ap_id)
|
||||
end
|
||||
|
|
@ -279,7 +292,8 @@ defmodule Pleroma.Activity do
|
|||
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"])
|
||||
def normalize(%Activity{data: %{"id" => ap_id}}), do: get_by_ap_id_with_object(ap_id)
|
||||
def normalize(%{"id" => ap_id}), do: get_by_ap_id_with_object(ap_id)
|
||||
def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id_with_object(ap_id)
|
||||
def normalize(_), do: nil
|
||||
|
||||
|
|
@ -300,13 +314,15 @@ defmodule Pleroma.Activity do
|
|||
|
||||
def delete_all_by_object_ap_id(_), do: nil
|
||||
|
||||
defp purge_web_resp_cache(%Activity{} = activity) do
|
||||
%{path: path} = URI.parse(activity.data["id"])
|
||||
@cachex.del(:web_resp_cache, path)
|
||||
defp purge_web_resp_cache(%Activity{data: %{"id" => id}} = activity) when is_binary(id) do
|
||||
with %{path: path} <- URI.parse(id) do
|
||||
@cachex.del(:web_resp_cache, path)
|
||||
end
|
||||
|
||||
activity
|
||||
end
|
||||
|
||||
defp purge_web_resp_cache(nil), do: nil
|
||||
defp purge_web_resp_cache(activity), do: activity
|
||||
|
||||
def follow_accepted?(
|
||||
%Activity{data: %{"type" => "Follow", "object" => followed_ap_id}} = activity
|
||||
|
|
@ -368,12 +384,6 @@ defmodule Pleroma.Activity do
|
|||
end
|
||||
end
|
||||
|
||||
@spec pinned_by_actor?(Activity.t()) :: boolean()
|
||||
def pinned_by_actor?(%Activity{} = activity) do
|
||||
actor = user_actor(activity)
|
||||
activity.id in actor.pinned_activities
|
||||
end
|
||||
|
||||
@spec get_by_object_ap_id_with_object(String.t()) :: t() | nil
|
||||
def get_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do
|
||||
ap_id
|
||||
|
|
@ -384,4 +394,13 @@ defmodule Pleroma.Activity do
|
|||
end
|
||||
|
||||
def get_by_object_ap_id_with_object(_), do: nil
|
||||
|
||||
@spec add_by_params_query(String.t(), String.t(), String.t()) :: Ecto.Query.t()
|
||||
def add_by_params_query(object_id, actor, target) do
|
||||
object_id
|
||||
|> Queries.by_object_id()
|
||||
|> Queries.by_type("Add")
|
||||
|> Queries.by_actor(actor)
|
||||
|> where([a], fragment("?->>'target' = ?", a.data, ^target))
|
||||
end
|
||||
end
|
||||
|
|
|
|||
45
lib/pleroma/activity/html.ex
Normal file
45
lib/pleroma/activity/html.ex
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Activity.HTML do
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.Object
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
def get_cached_scrubbed_html_for_activity(
|
||||
content,
|
||||
scrubbers,
|
||||
activity,
|
||||
key \\ "",
|
||||
callback \\ fn x -> x end
|
||||
) do
|
||||
key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}"
|
||||
|
||||
@cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||
object = Object.normalize(activity, fetch: false)
|
||||
HTML.ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false, callback)
|
||||
end)
|
||||
end
|
||||
|
||||
def get_cached_stripped_html_for_activity(content, activity, key) do
|
||||
get_cached_scrubbed_html_for_activity(
|
||||
content,
|
||||
FastSanitize.Sanitizer.StripTags,
|
||||
activity,
|
||||
key,
|
||||
&HtmlEntities.decode/1
|
||||
)
|
||||
end
|
||||
|
||||
defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do
|
||||
generate_scrubber_signature([scrubber])
|
||||
end
|
||||
|
||||
defp generate_scrubber_signature(scrubbers) do
|
||||
Enum.reduce(scrubbers, "", fn scrubber, signature ->
|
||||
"#{signature}#{to_string(scrubber)}"
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
@ -14,6 +14,11 @@ defmodule Pleroma.Activity.Queries do
|
|||
alias Pleroma.Activity
|
||||
alias Pleroma.User
|
||||
|
||||
@spec by_id(query(), String.t()) :: query()
|
||||
def by_id(query \\ Activity, id) do
|
||||
from(a in query, where: a.id == ^id)
|
||||
end
|
||||
|
||||
@spec by_ap_id(query, String.t()) :: query
|
||||
def by_ap_id(query \\ Activity, ap_id) do
|
||||
from(
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ defmodule Pleroma.Application do
|
|||
if Process.whereis(Pleroma.Web.Endpoint) do
|
||||
case Config.get([:http, :user_agent], :default) do
|
||||
:default ->
|
||||
info = "#{Pleroma.Web.base_url()} <#{Config.get([:instance, :email], "")}>"
|
||||
info = "#{Pleroma.Web.Endpoint.url()} <#{Config.get([:instance, :email], "")}>"
|
||||
named_version() <> "; " <> info
|
||||
|
||||
custom ->
|
||||
|
|
@ -102,7 +102,7 @@ defmodule Pleroma.Application do
|
|||
] ++
|
||||
task_children(@mix_env) ++
|
||||
dont_run_in_test(@mix_env) ++
|
||||
chat_child(chat_enabled?()) ++
|
||||
shout_child(shout_enabled?()) ++
|
||||
[Pleroma.Gopher.Server]
|
||||
|
||||
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
|
||||
|
|
@ -216,7 +216,7 @@ defmodule Pleroma.Application do
|
|||
type: :worker
|
||||
}
|
||||
|
||||
defp chat_enabled?, do: Config.get([:chat, :enabled])
|
||||
defp shout_enabled?, do: Config.get([:shout, :enabled])
|
||||
|
||||
defp dont_run_in_test(env) when env in [:test, :benchmark], do: []
|
||||
|
||||
|
|
@ -237,14 +237,14 @@ defmodule Pleroma.Application do
|
|||
]
|
||||
end
|
||||
|
||||
defp chat_child(true) do
|
||||
defp shout_child(true) do
|
||||
[
|
||||
Pleroma.Web.ChatChannel.ChatChannelState,
|
||||
Pleroma.Web.ShoutChannel.ShoutChannelState,
|
||||
{Phoenix.PubSub, [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]}
|
||||
]
|
||||
end
|
||||
|
||||
defp chat_child(_), do: []
|
||||
defp shout_child(_), do: []
|
||||
|
||||
defp task_children(:test) do
|
||||
[
|
||||
|
|
|
|||
|
|
@ -34,15 +34,16 @@ defmodule Pleroma.ApplicationRequirements do
|
|||
defp check_welcome_message_config!(:ok) do
|
||||
if Pleroma.Config.get([:welcome, :email, :enabled], false) and
|
||||
not Pleroma.Emails.Mailer.enabled?() do
|
||||
Logger.error("""
|
||||
To send welcome email do you need to enable mail.
|
||||
\nconfig :pleroma, Pleroma.Emails.Mailer, enabled: true
|
||||
""")
|
||||
Logger.warn("""
|
||||
To send welcome emails, you need to enable the mailer.
|
||||
Welcome emails will NOT be sent with the current config.
|
||||
|
||||
{:error, "The mail disabled."}
|
||||
else
|
||||
:ok
|
||||
Enable the mailer:
|
||||
config :pleroma, Pleroma.Emails.Mailer, enabled: true
|
||||
""")
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp check_welcome_message_config!(result), do: result
|
||||
|
|
@ -51,18 +52,21 @@ defmodule Pleroma.ApplicationRequirements do
|
|||
#
|
||||
def check_confirmation_accounts!(:ok) do
|
||||
if Pleroma.Config.get([:instance, :account_activation_required]) &&
|
||||
not Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) do
|
||||
Logger.error(
|
||||
"Account activation enabled, but no Mailer settings enabled.\n" <>
|
||||
"Please set config :pleroma, :instance, account_activation_required: false\n" <>
|
||||
"Otherwise setup and enable Mailer."
|
||||
)
|
||||
not Pleroma.Emails.Mailer.enabled?() do
|
||||
Logger.warn("""
|
||||
Account activation is required, but the mailer is disabled.
|
||||
Users will NOT be able to confirm their accounts with this config.
|
||||
Either disable account activation or enable the mailer.
|
||||
|
||||
{:error,
|
||||
"Account activation enabled, but Mailer is disabled. Cannot send confirmation emails."}
|
||||
else
|
||||
:ok
|
||||
Disable account activation:
|
||||
config :pleroma, :instance, account_activation_required: false
|
||||
|
||||
Enable the mailer:
|
||||
config :pleroma, Pleroma.Emails.Mailer, enabled: true
|
||||
""")
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def check_confirmation_accounts!(result), do: result
|
||||
|
|
@ -160,9 +164,12 @@ defmodule Pleroma.ApplicationRequirements do
|
|||
|
||||
defp check_system_commands!(:ok) do
|
||||
filter_commands_statuses = [
|
||||
check_filter(Pleroma.Upload.Filters.Exiftool, "exiftool"),
|
||||
check_filter(Pleroma.Upload.Filters.Mogrify, "mogrify"),
|
||||
check_filter(Pleroma.Upload.Filters.Mogrifun, "mogrify")
|
||||
check_filter(Pleroma.Upload.Filter.Exiftool, "exiftool"),
|
||||
check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"),
|
||||
check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"),
|
||||
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "mogrify"),
|
||||
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "convert"),
|
||||
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "ffprobe")
|
||||
]
|
||||
|
||||
preview_proxy_commands_status =
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
:ok <- check_gun_pool_options(),
|
||||
:ok <- check_activity_expiration_config(),
|
||||
:ok <- check_remote_ip_plug_name(),
|
||||
:ok <- check_uploders_s3_public_endpoint() do
|
||||
:ok <- check_uploders_s3_public_endpoint(),
|
||||
:ok <- check_old_chat_shoutbox() do
|
||||
:ok
|
||||
else
|
||||
_ ->
|
||||
|
|
@ -215,4 +216,27 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
@spec check_old_chat_shoutbox() :: :ok | nil
|
||||
def check_old_chat_shoutbox do
|
||||
instance_config = Pleroma.Config.get([:instance])
|
||||
chat_config = Pleroma.Config.get([:chat]) || []
|
||||
|
||||
use_old_config =
|
||||
Keyword.has_key?(instance_config, :chat_limit) or
|
||||
Keyword.has_key?(chat_config, :enabled)
|
||||
|
||||
if use_old_config do
|
||||
Logger.error("""
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using the old namespace for the Shoutbox configuration. You need to convert to the new namespace. e.g.,
|
||||
\n* `config :pleroma, :chat, enabled` and `config :pleroma, :instance, chat_limit` are now equal to:
|
||||
\n* `config :pleroma, :shout, enabled` and `config :pleroma, :shout, limit`
|
||||
""")
|
||||
|
||||
:error
|
||||
else
|
||||
:ok
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Config.Loader do
|
||||
# These modules are only being used as keys here (for equality check),
|
||||
# so it's okay to use `Module.concat/1` to have the compiler ignore them.
|
||||
@reject_keys [
|
||||
Pleroma.Repo,
|
||||
Pleroma.Web.Endpoint,
|
||||
Module.concat(["Pleroma.Repo"]),
|
||||
Module.concat(["Pleroma.Web.Endpoint"]),
|
||||
:env,
|
||||
:configurable_from_database,
|
||||
:database,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
defmodule Pleroma.Config.ReleaseRuntimeProvider do
|
||||
@moduledoc """
|
||||
Imports `runtime.exs` and `{env}.exported_from_db.secret.exs` for elixir releases.
|
||||
Imports runtime config and `{env}.exported_from_db.secret.exs` for releases.
|
||||
"""
|
||||
@behaviour Config.Provider
|
||||
|
||||
|
|
@ -8,10 +8,11 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do
|
|||
def init(opts), do: opts
|
||||
|
||||
@impl true
|
||||
def load(config, _opts) do
|
||||
def load(config, opts) do
|
||||
with_defaults = Config.Reader.merge(config, Pleroma.Config.Holder.release_defaults())
|
||||
|
||||
config_path = System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs"
|
||||
config_path =
|
||||
opts[:config_path] || System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs"
|
||||
|
||||
with_runtime_config =
|
||||
if File.exists?(config_path) do
|
||||
|
|
@ -24,7 +25,7 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do
|
|||
warning = [
|
||||
IO.ANSI.red(),
|
||||
IO.ANSI.bright(),
|
||||
"!!! #{config_path} not found! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file",
|
||||
"!!! Config path is not declared! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file",
|
||||
IO.ANSI.reset()
|
||||
]
|
||||
|
||||
|
|
@ -33,13 +34,14 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do
|
|||
end
|
||||
|
||||
exported_config_path =
|
||||
config_path
|
||||
|> Path.dirname()
|
||||
|> Path.join("prod.exported_from_db.secret.exs")
|
||||
opts[:exported_config_path] ||
|
||||
config_path
|
||||
|> Path.dirname()
|
||||
|> Path.join("#{Pleroma.Config.get(:env)}.exported_from_db.secret.exs")
|
||||
|
||||
with_exported =
|
||||
if File.exists?(exported_config_path) do
|
||||
exported_config = Config.Reader.read!(with_runtime_config)
|
||||
exported_config = Config.Reader.read!(exported_config_path)
|
||||
Config.Reader.merge(with_runtime_config, exported_config)
|
||||
else
|
||||
with_runtime_config
|
||||
|
|
|
|||
|
|
@ -13,23 +13,25 @@ defmodule Pleroma.Config.TransferTask do
|
|||
|
||||
@type env() :: :test | :benchmark | :dev | :prod
|
||||
|
||||
@reboot_time_keys [
|
||||
{:pleroma, :hackney_pools},
|
||||
{:pleroma, :chat},
|
||||
{:pleroma, Oban},
|
||||
{:pleroma, :rate_limit},
|
||||
{:pleroma, :markup},
|
||||
{:pleroma, :streamer},
|
||||
{:pleroma, :pools},
|
||||
{:pleroma, :connections_pool}
|
||||
]
|
||||
defp reboot_time_keys,
|
||||
do: [
|
||||
{:pleroma, :hackney_pools},
|
||||
{:pleroma, :shout},
|
||||
{:pleroma, Oban},
|
||||
{:pleroma, :rate_limit},
|
||||
{:pleroma, :markup},
|
||||
{:pleroma, :streamer},
|
||||
{:pleroma, :pools},
|
||||
{:pleroma, :connections_pool}
|
||||
]
|
||||
|
||||
@reboot_time_subkeys [
|
||||
{:pleroma, Pleroma.Captcha, [:seconds_valid]},
|
||||
{:pleroma, Pleroma.Upload, [:proxy_remote]},
|
||||
{:pleroma, :instance, [:upload_limit]},
|
||||
{:pleroma, :gopher, [:enabled]}
|
||||
]
|
||||
defp reboot_time_subkeys,
|
||||
do: [
|
||||
{:pleroma, Pleroma.Captcha, [:seconds_valid]},
|
||||
{:pleroma, Pleroma.Upload, [:proxy_remote]},
|
||||
{:pleroma, :instance, [:upload_limit]},
|
||||
{:pleroma, :gopher, [:enabled]}
|
||||
]
|
||||
|
||||
def start_link(restart_pleroma? \\ true) do
|
||||
load_and_update_env([], restart_pleroma?)
|
||||
|
|
@ -165,12 +167,12 @@ defmodule Pleroma.Config.TransferTask do
|
|||
end
|
||||
|
||||
defp group_and_key_need_reboot?(group, key) do
|
||||
Enum.any?(@reboot_time_keys, fn {g, k} -> g == group and k == key end)
|
||||
Enum.any?(reboot_time_keys(), fn {g, k} -> g == group and k == key end)
|
||||
end
|
||||
|
||||
defp group_and_subkey_need_reboot?(group, key, value) do
|
||||
Keyword.keyword?(value) and
|
||||
Enum.any?(@reboot_time_subkeys, fn {g, k, subkeys} ->
|
||||
Enum.any?(reboot_time_subkeys(), fn {g, k, subkeys} ->
|
||||
g == group and k == key and
|
||||
Enum.any?(Keyword.keys(value), &(&1 in subkeys))
|
||||
end)
|
||||
|
|
|
|||
|
|
@ -387,6 +387,6 @@ defmodule Pleroma.ConfigDB do
|
|||
@spec module_name?(String.t()) :: boolean()
|
||||
def module_name?(string) do
|
||||
Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Quack|Ueberauth|Swoosh)\./, string) or
|
||||
string in ["Oban", "Ueberauth", "ExSyslogger"]
|
||||
string in ["Oban", "Ueberauth", "ExSyslogger", "ConcurrentLimiter"]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -27,6 +27,4 @@ defmodule Pleroma.Constants do
|
|||
do:
|
||||
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css)
|
||||
)
|
||||
|
||||
def as_local_public, do: Pleroma.Web.base_url() <> "/#Public"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,256 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
#
|
||||
# This file is derived from Earmark, under the following copyright:
|
||||
# Copyright © 2014 Dave Thomas, The Pragmatic Programmers
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Upstream: https://github.com/pragdave/earmark/blob/master/lib/earmark/html_renderer.ex
|
||||
defmodule Pleroma.EarmarkRenderer do
|
||||
@moduledoc false
|
||||
|
||||
alias Earmark.Block
|
||||
alias Earmark.Context
|
||||
alias Earmark.HtmlRenderer
|
||||
alias Earmark.Options
|
||||
|
||||
import Earmark.Inline, only: [convert: 3]
|
||||
import Earmark.Helpers.HtmlHelpers
|
||||
import Earmark.Message, only: [add_messages_from: 2, get_messages: 1, set_messages: 2]
|
||||
import Earmark.Context, only: [append: 2, set_value: 2]
|
||||
import Earmark.Options, only: [get_mapper: 1]
|
||||
|
||||
@doc false
|
||||
def render(blocks, %Context{options: %Options{}} = context) do
|
||||
messages = get_messages(context)
|
||||
|
||||
{contexts, html} =
|
||||
get_mapper(context.options).(
|
||||
blocks,
|
||||
&render_block(&1, put_in(context.options.messages, []))
|
||||
)
|
||||
|> Enum.unzip()
|
||||
|
||||
all_messages =
|
||||
contexts
|
||||
|> Enum.reduce(messages, fn ctx, messages1 -> messages1 ++ get_messages(ctx) end)
|
||||
|
||||
{put_in(context.options.messages, all_messages), html |> IO.iodata_to_binary()}
|
||||
end
|
||||
|
||||
#############
|
||||
# Paragraph #
|
||||
#############
|
||||
defp render_block(%Block.Para{lnb: lnb, lines: lines, attrs: attrs}, context) do
|
||||
lines = convert(lines, lnb, context)
|
||||
add_attrs(lines, "<p>#{lines.value}</p>", attrs, [], lnb)
|
||||
end
|
||||
|
||||
########
|
||||
# Html #
|
||||
########
|
||||
defp render_block(%Block.Html{html: html}, context) do
|
||||
{context, html}
|
||||
end
|
||||
|
||||
defp render_block(%Block.HtmlComment{lines: lines}, context) do
|
||||
{context, lines}
|
||||
end
|
||||
|
||||
defp render_block(%Block.HtmlOneline{html: html}, context) do
|
||||
{context, html}
|
||||
end
|
||||
|
||||
#########
|
||||
# Ruler #
|
||||
#########
|
||||
defp render_block(%Block.Ruler{lnb: lnb, attrs: attrs}, context) do
|
||||
add_attrs(context, "<hr />", attrs, [], lnb)
|
||||
end
|
||||
|
||||
###########
|
||||
# Heading #
|
||||
###########
|
||||
defp render_block(
|
||||
%Block.Heading{lnb: lnb, level: level, content: content, attrs: attrs},
|
||||
context
|
||||
) do
|
||||
converted = convert(content, lnb, context)
|
||||
html = "<h#{level}>#{converted.value}</h#{level}>"
|
||||
add_attrs(converted, html, attrs, [], lnb)
|
||||
end
|
||||
|
||||
##############
|
||||
# Blockquote #
|
||||
##############
|
||||
|
||||
defp render_block(%Block.BlockQuote{lnb: lnb, blocks: blocks, attrs: attrs}, context) do
|
||||
{context1, body} = render(blocks, context)
|
||||
html = "<blockquote>#{body}</blockquote>"
|
||||
add_attrs(context1, html, attrs, [], lnb)
|
||||
end
|
||||
|
||||
#########
|
||||
# Table #
|
||||
#########
|
||||
|
||||
defp render_block(
|
||||
%Block.Table{lnb: lnb, header: header, rows: rows, alignments: aligns, attrs: attrs},
|
||||
context
|
||||
) do
|
||||
{context1, html} = add_attrs(context, "<table>", attrs, [], lnb)
|
||||
context2 = set_value(context1, html)
|
||||
|
||||
context3 =
|
||||
if header do
|
||||
append(add_trs(append(context2, "<thead>"), [header], "th", aligns, lnb), "</thead>")
|
||||
else
|
||||
# Maybe an error, needed append(context, html)
|
||||
context2
|
||||
end
|
||||
|
||||
context4 = append(add_trs(append(context3, "<tbody>"), rows, "td", aligns, lnb), "</tbody>")
|
||||
|
||||
{context4, [context4.value, "</table>"]}
|
||||
end
|
||||
|
||||
########
|
||||
# Code #
|
||||
########
|
||||
|
||||
defp render_block(
|
||||
%Block.Code{lnb: lnb, language: language, attrs: attrs} = block,
|
||||
%Context{options: options} = context
|
||||
) do
|
||||
class =
|
||||
if language, do: ~s{ class="#{code_classes(language, options.code_class_prefix)}"}, else: ""
|
||||
|
||||
tag = ~s[<pre><code#{class}>]
|
||||
lines = options.render_code.(block)
|
||||
html = ~s[#{tag}#{lines}</code></pre>]
|
||||
add_attrs(context, html, attrs, [], lnb)
|
||||
end
|
||||
|
||||
#########
|
||||
# Lists #
|
||||
#########
|
||||
|
||||
defp render_block(
|
||||
%Block.List{lnb: lnb, type: type, blocks: items, attrs: attrs, start: start},
|
||||
context
|
||||
) do
|
||||
{context1, content} = render(items, context)
|
||||
html = "<#{type}#{start}>#{content}</#{type}>"
|
||||
add_attrs(context1, html, attrs, [], lnb)
|
||||
end
|
||||
|
||||
# format a single paragraph list item, and remove the para tags
|
||||
defp render_block(
|
||||
%Block.ListItem{lnb: lnb, blocks: blocks, spaced: false, attrs: attrs},
|
||||
context
|
||||
)
|
||||
when length(blocks) == 1 do
|
||||
{context1, content} = render(blocks, context)
|
||||
content = Regex.replace(~r{</?p>}, content, "")
|
||||
html = "<li>#{content}</li>"
|
||||
add_attrs(context1, html, attrs, [], lnb)
|
||||
end
|
||||
|
||||
# format a spaced list item
|
||||
defp render_block(%Block.ListItem{lnb: lnb, blocks: blocks, attrs: attrs}, context) do
|
||||
{context1, content} = render(blocks, context)
|
||||
html = "<li>#{content}</li>"
|
||||
add_attrs(context1, html, attrs, [], lnb)
|
||||
end
|
||||
|
||||
##################
|
||||
# Footnote Block #
|
||||
##################
|
||||
|
||||
defp render_block(%Block.FnList{blocks: footnotes}, context) do
|
||||
items =
|
||||
Enum.map(footnotes, fn note ->
|
||||
blocks = append_footnote_link(note)
|
||||
%Block.ListItem{attrs: "#fn:#{note.number}", type: :ol, blocks: blocks}
|
||||
end)
|
||||
|
||||
{context1, html} = render_block(%Block.List{type: :ol, blocks: items}, context)
|
||||
{context1, Enum.join([~s[<div class="footnotes">], "<hr />", html, "</div>"])}
|
||||
end
|
||||
|
||||
#######################################
|
||||
# Isolated IALs are rendered as paras #
|
||||
#######################################
|
||||
|
||||
defp render_block(%Block.Ial{verbatim: verbatim}, context) do
|
||||
{context, "<p>{:#{verbatim}}</p>"}
|
||||
end
|
||||
|
||||
####################
|
||||
# IDDef is ignored #
|
||||
####################
|
||||
|
||||
defp render_block(%Block.IdDef{}, context), do: {context, ""}
|
||||
|
||||
#####################################
|
||||
# And here are the inline renderers #
|
||||
#####################################
|
||||
|
||||
defdelegate br, to: HtmlRenderer
|
||||
defdelegate codespan(text), to: HtmlRenderer
|
||||
defdelegate em(text), to: HtmlRenderer
|
||||
defdelegate strong(text), to: HtmlRenderer
|
||||
defdelegate strikethrough(text), to: HtmlRenderer
|
||||
|
||||
defdelegate link(url, text), to: HtmlRenderer
|
||||
defdelegate link(url, text, title), to: HtmlRenderer
|
||||
|
||||
defdelegate image(path, alt, title), to: HtmlRenderer
|
||||
|
||||
defdelegate footnote_link(ref, backref, number), to: HtmlRenderer
|
||||
|
||||
# Table rows
|
||||
defp add_trs(context, rows, tag, aligns, lnb) do
|
||||
numbered_rows =
|
||||
rows
|
||||
|> Enum.zip(Stream.iterate(lnb, &(&1 + 1)))
|
||||
|
||||
numbered_rows
|
||||
|> Enum.reduce(context, fn {row, lnb}, ctx ->
|
||||
append(add_tds(append(ctx, "<tr>"), row, tag, aligns, lnb), "</tr>")
|
||||
end)
|
||||
end
|
||||
|
||||
defp add_tds(context, row, tag, aligns, lnb) do
|
||||
Enum.reduce(1..length(row), context, add_td_fn(row, tag, aligns, lnb))
|
||||
end
|
||||
|
||||
defp add_td_fn(row, tag, aligns, lnb) do
|
||||
fn n, ctx ->
|
||||
style =
|
||||
case Enum.at(aligns, n - 1, :default) do
|
||||
:default -> ""
|
||||
align -> " style=\"text-align: #{align}\""
|
||||
end
|
||||
|
||||
col = Enum.at(row, n - 1)
|
||||
converted = convert(col, lnb, set_messages(ctx, []))
|
||||
append(add_messages_from(ctx, converted), "<#{tag}#{style}>#{converted.value}</#{tag}>")
|
||||
end
|
||||
end
|
||||
|
||||
###############################
|
||||
# Append Footnote Return Link #
|
||||
###############################
|
||||
|
||||
defdelegate append_footnote_link(note), to: HtmlRenderer
|
||||
defdelegate append_footnote_link(note, fnlink), to: HtmlRenderer
|
||||
|
||||
defdelegate render_code(lines), to: HtmlRenderer
|
||||
|
||||
defp code_classes(language, prefix) do
|
||||
["" | String.split(prefix || "")]
|
||||
|> Enum.map(fn pfx -> "#{pfx}#{language}" end)
|
||||
|> Enum.join(" ")
|
||||
end
|
||||
end
|
||||
|
|
@ -13,21 +13,33 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients do
|
|||
cast([object])
|
||||
end
|
||||
|
||||
def cast(data) when is_list(data) do
|
||||
data
|
||||
|> Enum.reduce_while({:ok, []}, fn element, {:ok, list} ->
|
||||
case ObjectID.cast(element) do
|
||||
{:ok, id} ->
|
||||
{:cont, {:ok, [id | list]}}
|
||||
|
||||
_ ->
|
||||
{:halt, :error}
|
||||
end
|
||||
end)
|
||||
def cast(object) when is_map(object) do
|
||||
case ObjectID.cast(object) do
|
||||
{:ok, data} -> {:ok, [data]}
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
|
||||
def cast(_) do
|
||||
:error
|
||||
def cast(data) when is_list(data) do
|
||||
data =
|
||||
data
|
||||
|> Enum.reduce_while([], fn element, list ->
|
||||
case ObjectID.cast(element) do
|
||||
{:ok, id} ->
|
||||
{:cont, [id | list]}
|
||||
|
||||
_ ->
|
||||
{:cont, list}
|
||||
end
|
||||
end)
|
||||
|> Enum.sort()
|
||||
|> Enum.uniq()
|
||||
|
||||
{:ok, data}
|
||||
end
|
||||
|
||||
def cast(data) do
|
||||
{:error, data}
|
||||
end
|
||||
|
||||
def dump(data) do
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ defmodule Pleroma.Emails.AdminEmail do
|
|||
#{comment_html}
|
||||
#{statuses_html}
|
||||
<p>
|
||||
<a href="#{Pleroma.Web.base_url()}/pleroma/admin/#/reports/index">View Reports in AdminFE</a>
|
||||
<a href="#{Pleroma.Web.Endpoint.url()}/pleroma/admin/#/reports/index">View Reports in AdminFE</a>
|
||||
"""
|
||||
|
||||
new()
|
||||
|
|
@ -87,7 +87,7 @@ defmodule Pleroma.Emails.AdminEmail do
|
|||
html_body = """
|
||||
<p>New account for review: <a href="#{account.ap_id}">@#{account.nickname}</a></p>
|
||||
<blockquote>#{HTML.strip_tags(account.registration_reason)}</blockquote>
|
||||
<a href="#{Pleroma.Web.base_url()}/pleroma/admin/#/users/#{account.id}/">Visit AdminFE</a>
|
||||
<a href="#{Pleroma.Web.Endpoint.url()}/pleroma/admin/#/users/#{account.id}/">Visit AdminFE</a>
|
||||
"""
|
||||
|
||||
new()
|
||||
|
|
|
|||
|
|
@ -5,15 +5,22 @@
|
|||
defmodule Pleroma.Emails.UserEmail do
|
||||
@moduledoc "User emails"
|
||||
|
||||
use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email}
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.Router
|
||||
|
||||
import Swoosh.Email
|
||||
import Phoenix.Swoosh, except: [render_body: 3]
|
||||
import Pleroma.Config.Helpers, only: [instance_name: 0, sender: 0]
|
||||
|
||||
def render_body(email, template, assigns \\ %{}) do
|
||||
email
|
||||
|> put_new_layout({Pleroma.Web.LayoutView, :email})
|
||||
|> put_new_view(Pleroma.Web.EmailView)
|
||||
|> Phoenix.Swoosh.render_body(template, assigns)
|
||||
end
|
||||
|
||||
defp recipient(email, nil), do: email
|
||||
defp recipient(email, name), do: {name, email}
|
||||
defp recipient(%User{} = user), do: recipient(user.email, user.name)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
defmodule Pleroma.Emoji.Formatter do
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
def emojify(text) do
|
||||
|
|
@ -44,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, to_string(URI.merge(Web.base_url(), file)))
|
||||
Map.put(acc, name, to_string(URI.merge(Endpoint.url(), file)))
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ defmodule Pleroma.Formatter do
|
|||
|
||||
def hashtag_handler("#" <> tag = tag_text, _buffer, _opts, acc) do
|
||||
tag = String.downcase(tag)
|
||||
url = "#{Pleroma.Web.base_url()}/tag/#{tag}"
|
||||
url = "#{Pleroma.Web.Endpoint.url()}/tag/#{tag}"
|
||||
|
||||
link =
|
||||
Phoenix.HTML.Tag.content_tag(:a, tag_text,
|
||||
|
|
@ -121,6 +121,10 @@ defmodule Pleroma.Formatter do
|
|||
end
|
||||
end
|
||||
|
||||
def markdown_to_html(text) do
|
||||
Earmark.as_html!(text, %Earmark.Options{compact_output: true})
|
||||
end
|
||||
|
||||
def html_escape({text, mentions, hashtags}, type) do
|
||||
{html_escape(text, type), mentions, hashtags}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@ defmodule Pleroma.Gun do
|
|||
@callback await(pid(), reference()) :: {:response, :fin, 200, []}
|
||||
@callback set_owner(pid(), pid()) :: :ok
|
||||
|
||||
@api Pleroma.Config.get([Pleroma.Gun], Pleroma.Gun.API)
|
||||
|
||||
defp api, do: @api
|
||||
defp api, do: Pleroma.Config.get([Pleroma.Gun], Pleroma.Gun.API)
|
||||
|
||||
def open(host, port, opts), do: api().open(host, port, opts)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
|
||||
use GenServer, restart: :temporary
|
||||
|
||||
@registry Pleroma.Gun.ConnectionPool
|
||||
defp registry, do: Pleroma.Gun.ConnectionPool
|
||||
|
||||
def start_monitor do
|
||||
pid =
|
||||
case :gen_server.start(__MODULE__, [], name: {:via, Registry, {@registry, "reclaimer"}}) do
|
||||
case :gen_server.start(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do
|
||||
{:ok, pid} ->
|
||||
pid
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
|
|||
# {worker_pid, crf, last_reference} end)
|
||||
unused_conns =
|
||||
Registry.select(
|
||||
@registry,
|
||||
registry(),
|
||||
[
|
||||
{{:_, :"$1", {:_, :"$2", :"$3", :"$4"}}, [{:==, :"$2", []}], [{{:"$1", :"$3", :"$4"}}]}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
|||
alias Pleroma.Gun
|
||||
use GenServer, restart: :temporary
|
||||
|
||||
@registry Pleroma.Gun.ConnectionPool
|
||||
defp registry, do: Pleroma.Gun.ConnectionPool
|
||||
|
||||
def start_link([key | _] = opts) do
|
||||
GenServer.start_link(__MODULE__, opts, name: {:via, Registry, {@registry, key}})
|
||||
GenServer.start_link(__MODULE__, opts, name: {:via, Registry, {registry(), key}})
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
@ -24,7 +24,7 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
|||
time = :erlang.monotonic_time(:millisecond)
|
||||
|
||||
{_, _} =
|
||||
Registry.update_value(@registry, key, fn _ ->
|
||||
Registry.update_value(registry(), key, fn _ ->
|
||||
{conn_pid, [client_pid], 1, time}
|
||||
end)
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
|||
time = :erlang.monotonic_time(:millisecond)
|
||||
|
||||
{{conn_pid, used_by, _, _}, _} =
|
||||
Registry.update_value(@registry, key, fn {conn_pid, used_by, crf, last_reference} ->
|
||||
Registry.update_value(registry(), key, fn {conn_pid, used_by, crf, last_reference} ->
|
||||
{conn_pid, [client_pid | used_by], crf(time - last_reference, crf), time}
|
||||
end)
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
|||
@impl true
|
||||
def handle_call(:remove_client, {client_pid, _}, %{key: key} = state) do
|
||||
{{_conn_pid, used_by, _crf, _last_reference}, _} =
|
||||
Registry.update_value(@registry, key, fn {conn_pid, used_by, crf, last_reference} ->
|
||||
Registry.update_value(registry(), key, fn {conn_pid, used_by, crf, last_reference} ->
|
||||
{conn_pid, List.delete(used_by, client_pid), crf, last_reference}
|
||||
end)
|
||||
|
||||
|
|
|
|||
|
|
@ -49,31 +49,6 @@ defmodule Pleroma.HTML do
|
|||
def filter_tags(html), do: filter_tags(html, nil)
|
||||
def strip_tags(html), do: filter_tags(html, FastSanitize.Sanitizer.StripTags)
|
||||
|
||||
def get_cached_scrubbed_html_for_activity(
|
||||
content,
|
||||
scrubbers,
|
||||
activity,
|
||||
key \\ "",
|
||||
callback \\ fn x -> x end
|
||||
) do
|
||||
key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}"
|
||||
|
||||
@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
|
||||
|
||||
def get_cached_stripped_html_for_activity(content, activity, key) do
|
||||
get_cached_scrubbed_html_for_activity(
|
||||
content,
|
||||
FastSanitize.Sanitizer.StripTags,
|
||||
activity,
|
||||
key,
|
||||
&HtmlEntities.decode/1
|
||||
)
|
||||
end
|
||||
|
||||
def ensure_scrubbed_html(
|
||||
content,
|
||||
scrubbers,
|
||||
|
|
@ -92,16 +67,6 @@ defmodule Pleroma.HTML do
|
|||
end
|
||||
end
|
||||
|
||||
defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do
|
||||
generate_scrubber_signature([scrubber])
|
||||
end
|
||||
|
||||
defp generate_scrubber_signature(scrubbers) do
|
||||
Enum.reduce(scrubbers, "", fn scrubber, signature ->
|
||||
"#{signature}#{to_string(scrubber)}"
|
||||
end)
|
||||
end
|
||||
|
||||
def extract_first_external_url_from_object(%{data: %{"content" => content}} = object)
|
||||
when is_binary(content) do
|
||||
unless object.data["fake"] do
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
|
|||
Config.get([:pools, pool, :recv_timeout], default)
|
||||
end
|
||||
|
||||
@prefix Pleroma.Gun.ConnectionPool
|
||||
def limiter_setup do
|
||||
prefix = Pleroma.Gun.ConnectionPool
|
||||
wait = Config.get([:connections_pool, :connection_acquisition_wait])
|
||||
retries = Config.get([:connections_pool, :connection_acquisition_retries])
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
|
|||
max_waiting = Keyword.get(opts, :max_waiting, 10)
|
||||
|
||||
result =
|
||||
ConcurrentLimiter.new(:"#{@prefix}.#{name}", max_running, max_waiting,
|
||||
ConcurrentLimiter.new(:"#{prefix}.#{name}", max_running, max_waiting,
|
||||
wait: wait,
|
||||
max_retries: retries
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
defmodule Pleroma.HTTP.WebPush do
|
||||
@moduledoc false
|
||||
|
||||
def post(url, payload, headers) do
|
||||
def post(url, payload, headers, options \\ []) do
|
||||
list_headers = Map.to_list(headers)
|
||||
Pleroma.HTTP.post(url, payload, list_headers)
|
||||
Pleroma.HTTP.post(url, payload, list_headers, options)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,13 +5,18 @@
|
|||
defmodule Pleroma.Instances do
|
||||
@moduledoc "Instances context."
|
||||
|
||||
@adapter Pleroma.Instances.Instance
|
||||
alias Pleroma.Instances.Instance
|
||||
|
||||
defdelegate filter_reachable(urls_or_hosts), to: @adapter
|
||||
defdelegate reachable?(url_or_host), to: @adapter
|
||||
defdelegate set_reachable(url_or_host), to: @adapter
|
||||
defdelegate set_unreachable(url_or_host, unreachable_since \\ nil), to: @adapter
|
||||
defdelegate get_consistently_unreachable(), to: @adapter
|
||||
def filter_reachable(urls_or_hosts), do: Instance.filter_reachable(urls_or_hosts)
|
||||
|
||||
def reachable?(url_or_host), do: Instance.reachable?(url_or_host)
|
||||
|
||||
def set_reachable(url_or_host), do: Instance.set_reachable(url_or_host)
|
||||
|
||||
def set_unreachable(url_or_host, unreachable_since \\ nil),
|
||||
do: Instance.set_unreachable(url_or_host, unreachable_since)
|
||||
|
||||
def get_consistently_unreachable, do: Instance.get_consistently_unreachable()
|
||||
|
||||
def set_consistently_unreachable(url_or_host),
|
||||
do: set_unreachable(url_or_host, reachability_datetime_threshold())
|
||||
|
|
|
|||
|
|
@ -12,4 +12,10 @@ defmodule Pleroma.Maps do
|
|||
_ -> map
|
||||
end
|
||||
end
|
||||
|
||||
def safe_put_in(data, keys, value) when is_map(data) and is_list(keys) do
|
||||
Kernel.put_in(data, keys, value)
|
||||
rescue
|
||||
_ -> data
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -366,7 +366,7 @@ defmodule Pleroma.Object do
|
|||
end
|
||||
|
||||
def local?(%Object{data: %{"id" => id}}) do
|
||||
String.starts_with?(id, Pleroma.Web.base_url() <> "/")
|
||||
String.starts_with?(id, Pleroma.Web.Endpoint.url() <> "/")
|
||||
end
|
||||
|
||||
def replies(object, opts \\ []) do
|
||||
|
|
|
|||
|
|
@ -71,6 +71,14 @@ defmodule Pleroma.Object.Containment do
|
|||
compare_uris(id_uri, other_uri)
|
||||
end
|
||||
|
||||
# Mastodon pin activities don't have an id, so we check the object field, which will be pinned.
|
||||
def contain_origin_from_id(id, %{"object" => object}) when is_binary(object) do
|
||||
id_uri = URI.parse(id)
|
||||
object_uri = URI.parse(object)
|
||||
|
||||
compare_uris(id_uri, object_uri)
|
||||
end
|
||||
|
||||
def contain_origin_from_id(_id, _data), do: :error
|
||||
|
||||
def contain_child(%{"object" => %{"id" => id, "attributedTo" => _} = object}),
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Object.Fetcher do
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.Maps
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Containment
|
||||
alias Pleroma.Repo
|
||||
|
|
@ -101,6 +102,9 @@ defmodule Pleroma.Object.Fetcher do
|
|||
{:transmogrifier, {:error, {:reject, e}}} ->
|
||||
{:reject, e}
|
||||
|
||||
{:transmogrifier, {:reject, e}} ->
|
||||
{:reject, e}
|
||||
|
||||
{:transmogrifier, _} = e ->
|
||||
{:error, e}
|
||||
|
||||
|
|
@ -124,12 +128,14 @@ defmodule Pleroma.Object.Fetcher do
|
|||
defp prepare_activity_params(data) do
|
||||
%{
|
||||
"type" => "Create",
|
||||
"to" => data["to"] || [],
|
||||
"cc" => data["cc"] || [],
|
||||
# Should we seriously keep this attributedTo thing?
|
||||
"actor" => data["actor"] || data["attributedTo"],
|
||||
"object" => data
|
||||
}
|
||||
|> Maps.put_if_present("to", data["to"])
|
||||
|> Maps.put_if_present("cc", data["cc"])
|
||||
|> Maps.put_if_present("bto", data["bto"])
|
||||
|> Maps.put_if_present("bcc", data["bcc"])
|
||||
end
|
||||
|
||||
def fetch_object_from_id!(id, options \\ []) do
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ defmodule Pleroma.Repo do
|
|||
adapter: Ecto.Adapters.Postgres,
|
||||
migration_timestamps: [type: :naive_datetime_usec]
|
||||
|
||||
use Ecto.Explain
|
||||
|
||||
import Ecto.Query
|
||||
require Logger
|
||||
|
||||
|
|
|
|||
|
|
@ -411,7 +411,7 @@ defmodule Pleroma.ReverseProxy do
|
|||
{:ok, :no_duration_limit, :no_duration_limit}
|
||||
end
|
||||
|
||||
defp client, do: Pleroma.ReverseProxy.Client
|
||||
defp client, do: Pleroma.ReverseProxy.Client.Wrapper
|
||||
|
||||
defp track_failed_url(url, error, opts) do
|
||||
ttl =
|
||||
|
|
|
|||
|
|
@ -17,22 +17,4 @@ defmodule Pleroma.ReverseProxy.Client do
|
|||
@callback stream_body(map()) :: {:ok, binary(), map()} | :done | {:error, atom() | String.t()}
|
||||
|
||||
@callback close(reference() | pid() | map()) :: :ok
|
||||
|
||||
def request(method, url, headers, body \\ "", opts \\ []) do
|
||||
client().request(method, url, headers, body, opts)
|
||||
end
|
||||
|
||||
def stream_body(ref), do: client().stream_body(ref)
|
||||
|
||||
def close(ref), do: client().close(ref)
|
||||
|
||||
defp client do
|
||||
:tesla
|
||||
|> Application.get_env(:adapter)
|
||||
|> client()
|
||||
end
|
||||
|
||||
defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
|
||||
defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
|
||||
defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
|
||||
end
|
||||
|
|
|
|||
29
lib/pleroma/reverse_proxy/client/wrapper.ex
Normal file
29
lib/pleroma/reverse_proxy/client/wrapper.ex
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ReverseProxy.Client.Wrapper do
|
||||
@moduledoc "Meta-client that calls the appropriate client from the config."
|
||||
@behaviour Pleroma.ReverseProxy.Client
|
||||
|
||||
@impl true
|
||||
def request(method, url, headers, body \\ "", opts \\ []) do
|
||||
client().request(method, url, headers, body, opts)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def stream_body(ref), do: client().stream_body(ref)
|
||||
|
||||
@impl true
|
||||
def close(ref), do: client().close(ref)
|
||||
|
||||
defp client do
|
||||
:tesla
|
||||
|> Application.get_env(:adapter)
|
||||
|> client()
|
||||
end
|
||||
|
||||
defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
|
||||
defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
|
||||
defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
|
||||
end
|
||||
|
|
@ -9,7 +9,6 @@ defmodule Pleroma.Tests.AuthTestController do
|
|||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
# Serves only with proper OAuth token (:api and :authenticated_api)
|
||||
|
|
@ -47,10 +46,7 @@ defmodule Pleroma.Tests.AuthTestController do
|
|||
# Via :authenticated_api, serves if token is present and has requested scopes
|
||||
#
|
||||
# Suggested use: as :fallback_oauth_check but open with nil :user for :api on private instances
|
||||
plug(
|
||||
:skip_plug,
|
||||
EnsurePublicOrAuthenticatedPlug when action == :fallback_oauth_skip_publicity_check
|
||||
)
|
||||
plug(:skip_public_check when action == :fallback_oauth_skip_publicity_check)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
|
|
@ -62,11 +58,7 @@ defmodule Pleroma.Tests.AuthTestController do
|
|||
# Via :authenticated_api, serves if :user is set (regardless of token presence and its scopes)
|
||||
#
|
||||
# Suggested use: making an :api endpoint always accessible (e.g. email confirmation endpoint)
|
||||
plug(
|
||||
:skip_plug,
|
||||
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug]
|
||||
when action == :skip_oauth_skip_publicity_check
|
||||
)
|
||||
plug(:skip_auth when action == :skip_oauth_skip_publicity_check)
|
||||
|
||||
# Via :authenticated_api, always fails with 403 (endpoint is insecure)
|
||||
# Via :api, drops :user if present and serves if public (private instance rejects on no user)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ defmodule Pleroma.Upload do
|
|||
is once created permanent and changing it (especially in uploaders) is probably a bad idea!
|
||||
* `:tempfile` - path to the temporary file. Prefer in-place changes on the file rather than changing the
|
||||
path as the temporary file is also tracked by `Plug.Upload{}` and automatically deleted once the request is over.
|
||||
* `:width` - width of the media in pixels
|
||||
* `:height` - height of the media in pixels
|
||||
* `:blurhash` - string hash of the image encoded with the blurhash algorithm (https://blurha.sh/)
|
||||
|
||||
Related behaviors:
|
||||
|
||||
|
|
@ -32,6 +35,7 @@ defmodule Pleroma.Upload do
|
|||
"""
|
||||
alias Ecto.UUID
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Maps
|
||||
require Logger
|
||||
|
||||
@type source ::
|
||||
|
|
@ -53,9 +57,12 @@ defmodule Pleroma.Upload do
|
|||
name: String.t(),
|
||||
tempfile: String.t(),
|
||||
content_type: String.t(),
|
||||
width: integer(),
|
||||
height: integer(),
|
||||
blurhash: String.t(),
|
||||
path: String.t()
|
||||
}
|
||||
defstruct [:id, :name, :tempfile, :content_type, :path]
|
||||
defstruct [:id, :name, :tempfile, :content_type, :width, :height, :blurhash, :path]
|
||||
|
||||
defp get_description(opts, upload) do
|
||||
case {opts[:description], Pleroma.Config.get([Pleroma.Upload, :default_description])} do
|
||||
|
|
@ -89,9 +96,12 @@ defmodule Pleroma.Upload do
|
|||
"mediaType" => upload.content_type,
|
||||
"href" => url_from_spec(upload, opts.base_url, url_spec)
|
||||
}
|
||||
|> Maps.put_if_present("width", upload.width)
|
||||
|> Maps.put_if_present("height", upload.height)
|
||||
],
|
||||
"name" => description
|
||||
}}
|
||||
}
|
||||
|> Maps.put_if_present("blurhash", upload.blurhash)}
|
||||
else
|
||||
{:description_limit, _} ->
|
||||
{:error, :description_too_long}
|
||||
|
|
@ -225,7 +235,7 @@ defmodule Pleroma.Upload do
|
|||
|
||||
case uploader do
|
||||
Pleroma.Uploaders.Local ->
|
||||
upload_base_url || Pleroma.Web.base_url() <> "/media/"
|
||||
upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/"
|
||||
|
||||
Pleroma.Uploaders.S3 ->
|
||||
bucket = Config.get([Pleroma.Uploaders.S3, :bucket])
|
||||
|
|
@ -251,7 +261,7 @@ defmodule Pleroma.Upload do
|
|||
end
|
||||
|
||||
_ ->
|
||||
public_endpoint || upload_base_url || Pleroma.Web.base_url() <> "/media/"
|
||||
public_endpoint || upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ defmodule Pleroma.Upload.Filter do
|
|||
|
||||
require Logger
|
||||
|
||||
@callback filter(Pleroma.Upload.t()) ::
|
||||
@callback filter(upload :: struct()) ::
|
||||
{:ok, :filtered}
|
||||
| {:ok, :noop}
|
||||
| {:ok, :filtered, Pleroma.Upload.t()}
|
||||
| {:ok, :filtered, upload :: struct()}
|
||||
| {:error, any()}
|
||||
|
||||
@spec filter([module()], Pleroma.Upload.t()) :: {:ok, Pleroma.Upload.t()} | {:error, any()}
|
||||
@spec filter([module()], upload :: struct()) :: {:ok, upload :: struct()} | {:error, any()}
|
||||
|
||||
def filter([], upload) do
|
||||
{:ok, upload}
|
||||
|
|
|
|||
83
lib/pleroma/upload/filter/analyze_metadata.ex
Normal file
83
lib/pleroma/upload/filter/analyze_metadata.ex
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Upload.Filter.AnalyzeMetadata do
|
||||
@moduledoc """
|
||||
Extracts metadata about the upload, such as width/height
|
||||
"""
|
||||
require Logger
|
||||
|
||||
@behaviour Pleroma.Upload.Filter
|
||||
|
||||
@spec filter(Pleroma.Upload.t()) ::
|
||||
{:ok, :filtered, Pleroma.Upload.t()} | {:ok, :noop} | {:error, String.t()}
|
||||
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _} = upload) do
|
||||
try do
|
||||
image =
|
||||
file
|
||||
|> Mogrify.open()
|
||||
|> Mogrify.verbose()
|
||||
|
||||
upload =
|
||||
upload
|
||||
|> Map.put(:width, image.width)
|
||||
|> Map.put(:height, image.height)
|
||||
|> Map.put(:blurhash, get_blurhash(file))
|
||||
|
||||
{:ok, :filtered, upload}
|
||||
rescue
|
||||
e in ErlangError ->
|
||||
Logger.warn("#{__MODULE__}: #{inspect(e)}")
|
||||
{:ok, :noop}
|
||||
end
|
||||
end
|
||||
|
||||
def filter(%Pleroma.Upload{tempfile: file, content_type: "video" <> _} = upload) do
|
||||
try do
|
||||
result = media_dimensions(file)
|
||||
|
||||
upload =
|
||||
upload
|
||||
|> Map.put(:width, result.width)
|
||||
|> Map.put(:height, result.height)
|
||||
|
||||
{:ok, :filtered, upload}
|
||||
rescue
|
||||
e in ErlangError ->
|
||||
Logger.warn("#{__MODULE__}: #{inspect(e)}")
|
||||
{:ok, :noop}
|
||||
end
|
||||
end
|
||||
|
||||
def filter(_), do: {:ok, :noop}
|
||||
|
||||
defp get_blurhash(file) do
|
||||
with {:ok, blurhash} <- :eblurhash.magick(file) do
|
||||
blurhash
|
||||
else
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp media_dimensions(file) do
|
||||
with executable when is_binary(executable) <- System.find_executable("ffprobe"),
|
||||
args = [
|
||||
"-v",
|
||||
"error",
|
||||
"-show_entries",
|
||||
"stream=width,height",
|
||||
"-of",
|
||||
"csv=p=0:s=x",
|
||||
file
|
||||
],
|
||||
{result, 0} <- System.cmd(executable, args),
|
||||
[width, height] <-
|
||||
String.split(String.trim(result), "x") |> Enum.map(&String.to_integer(&1)) do
|
||||
%{width: width, height: height}
|
||||
else
|
||||
nil -> {:error, {:ffprobe, :command_not_found}}
|
||||
{:error, _} = error -> error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -35,7 +35,7 @@ defmodule Pleroma.Uploaders.Uploader do
|
|||
|
||||
"""
|
||||
@type file_spec :: {:file | :url, String.t()}
|
||||
@callback put_file(Pleroma.Upload.t()) ::
|
||||
@callback put_file(upload :: struct()) ::
|
||||
:ok | {:ok, file_spec()} | {:error, String.t()} | :wait_callback
|
||||
|
||||
@callback delete_file(file :: String.t()) :: :ok | {:error, String.t()}
|
||||
|
|
@ -46,7 +46,7 @@ defmodule Pleroma.Uploaders.Uploader do
|
|||
| {:error, Plug.Conn.t(), String.t()}
|
||||
@optional_callbacks http_callback: 2
|
||||
|
||||
@spec put_file(module(), Pleroma.Upload.t()) :: {:ok, file_spec()} | {:error, String.t()}
|
||||
@spec put_file(module(), upload :: struct()) :: {:ok, file_spec()} | {:error, String.t()}
|
||||
def put_file(uploader, upload) do
|
||||
case uploader.put_file(upload) do
|
||||
:ok -> {:ok, {:file, upload.path}}
|
||||
|
|
|
|||
|
|
@ -27,13 +27,13 @@ defmodule Pleroma.User do
|
|||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.OAuth
|
||||
alias Pleroma.Web.RelMe
|
||||
alias Pleroma.Workers.BackgroundWorker
|
||||
|
|
@ -99,6 +99,7 @@ defmodule Pleroma.User do
|
|||
field(:local, :boolean, default: true)
|
||||
field(:follower_address, :string)
|
||||
field(:following_address, :string)
|
||||
field(:featured_address, :string)
|
||||
field(:search_rank, :float, virtual: true)
|
||||
field(:search_type, :integer, virtual: true)
|
||||
field(:tags, {:array, :string}, default: [])
|
||||
|
|
@ -130,7 +131,6 @@ defmodule Pleroma.User do
|
|||
field(:hide_followers, :boolean, default: false)
|
||||
field(:hide_follows, :boolean, default: false)
|
||||
field(:hide_favorites, :boolean, default: true)
|
||||
field(:pinned_activities, {:array, :string}, default: [])
|
||||
field(:email_notifications, :map, default: %{"digest" => false})
|
||||
field(:mascot, :map, default: nil)
|
||||
field(:emoji, :map, default: %{})
|
||||
|
|
@ -148,6 +148,7 @@ defmodule Pleroma.User do
|
|||
field(:accepts_chat_messages, :boolean, default: nil)
|
||||
field(:last_active_at, :naive_datetime)
|
||||
field(:disclose_client, :boolean, default: true)
|
||||
field(:pinned_objects, :map, default: %{})
|
||||
|
||||
embeds_one(
|
||||
:notification_settings,
|
||||
|
|
@ -359,7 +360,7 @@ defmodule Pleroma.User do
|
|||
|
||||
_ ->
|
||||
unless options[:no_default] do
|
||||
Config.get([:assets, :default_user_avatar], "#{Web.base_url()}/images/avi.png")
|
||||
Config.get([:assets, :default_user_avatar], "#{Endpoint.url()}/images/avi.png")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -367,13 +368,15 @@ defmodule Pleroma.User do
|
|||
def banner_url(user, options \\ []) do
|
||||
case user.banner do
|
||||
%{"url" => [%{"href" => href} | _]} -> href
|
||||
_ -> !options[:no_default] && "#{Web.base_url()}/images/banner.png"
|
||||
_ -> !options[:no_default] && "#{Endpoint.url()}/images/banner.png"
|
||||
end
|
||||
end
|
||||
|
||||
# Should probably be renamed or removed
|
||||
def ap_id(%User{nickname: nickname}), do: "#{Web.base_url()}/users/#{nickname}"
|
||||
@spec ap_id(User.t()) :: String.t()
|
||||
def ap_id(%User{nickname: nickname}), do: "#{Endpoint.url()}/users/#{nickname}"
|
||||
|
||||
@spec ap_followers(User.t()) :: String.t()
|
||||
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
|
||||
def ap_followers(%User{} = user), do: "#{ap_id(user)}/followers"
|
||||
|
||||
|
|
@ -381,6 +384,11 @@ defmodule Pleroma.User do
|
|||
def ap_following(%User{following_address: fa}) when is_binary(fa), do: fa
|
||||
def ap_following(%User{} = user), do: "#{ap_id(user)}/following"
|
||||
|
||||
@spec ap_featured_collection(User.t()) :: String.t()
|
||||
def ap_featured_collection(%User{featured_address: fa}) when is_binary(fa), do: fa
|
||||
|
||||
def ap_featured_collection(%User{} = user), do: "#{ap_id(user)}/collections/featured"
|
||||
|
||||
defp truncate_fields_param(params) do
|
||||
if Map.has_key?(params, :fields) do
|
||||
Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1))
|
||||
|
|
@ -443,6 +451,7 @@ defmodule Pleroma.User do
|
|||
:uri,
|
||||
:follower_address,
|
||||
:following_address,
|
||||
:featured_address,
|
||||
:hide_followers,
|
||||
:hide_follows,
|
||||
:hide_followers_count,
|
||||
|
|
@ -454,7 +463,8 @@ defmodule Pleroma.User do
|
|||
:invisible,
|
||||
:actor_type,
|
||||
:also_known_as,
|
||||
:accepts_chat_messages
|
||||
:accepts_chat_messages,
|
||||
:pinned_objects
|
||||
]
|
||||
)
|
||||
|> cast(params, [:name], empty_values: [])
|
||||
|
|
@ -686,7 +696,7 @@ defmodule Pleroma.User do
|
|||
|> validate_format(:nickname, local_nickname_regex())
|
||||
|> put_ap_id()
|
||||
|> unique_constraint(:ap_id)
|
||||
|> put_following_and_follower_address()
|
||||
|> put_following_and_follower_and_featured_address()
|
||||
end
|
||||
|
||||
def register_changeset(struct, params \\ %{}, opts \\ []) do
|
||||
|
|
@ -747,7 +757,7 @@ defmodule Pleroma.User do
|
|||
|> put_password_hash
|
||||
|> put_ap_id()
|
||||
|> unique_constraint(:ap_id)
|
||||
|> put_following_and_follower_address()
|
||||
|> put_following_and_follower_and_featured_address()
|
||||
end
|
||||
|
||||
def maybe_validate_required_email(changeset, true), do: changeset
|
||||
|
|
@ -765,11 +775,16 @@ defmodule Pleroma.User do
|
|||
put_change(changeset, :ap_id, ap_id)
|
||||
end
|
||||
|
||||
defp put_following_and_follower_address(changeset) do
|
||||
followers = ap_followers(%User{nickname: get_field(changeset, :nickname)})
|
||||
defp put_following_and_follower_and_featured_address(changeset) do
|
||||
user = %User{nickname: get_field(changeset, :nickname)}
|
||||
followers = ap_followers(user)
|
||||
following = ap_following(user)
|
||||
featured = ap_featured_collection(user)
|
||||
|
||||
changeset
|
||||
|> put_change(:follower_address, followers)
|
||||
|> put_change(:following_address, following)
|
||||
|> put_change(:featured_address, featured)
|
||||
end
|
||||
|
||||
defp autofollow_users(user) do
|
||||
|
|
@ -1680,8 +1695,6 @@ defmodule Pleroma.User do
|
|||
email: nil,
|
||||
name: nil,
|
||||
password_hash: nil,
|
||||
keys: nil,
|
||||
public_key: nil,
|
||||
avatar: %{},
|
||||
tags: [],
|
||||
last_refreshed_at: nil,
|
||||
|
|
@ -1692,9 +1705,7 @@ defmodule Pleroma.User do
|
|||
follower_count: 0,
|
||||
following_count: 0,
|
||||
is_locked: false,
|
||||
is_confirmed: true,
|
||||
password_reset_pending: false,
|
||||
is_approved: true,
|
||||
registration_reason: nil,
|
||||
confirmation_token: nil,
|
||||
domain_blocks: [],
|
||||
|
|
@ -1710,45 +1721,53 @@ defmodule Pleroma.User do
|
|||
raw_fields: [],
|
||||
is_discoverable: false,
|
||||
also_known_as: []
|
||||
# id: preserved
|
||||
# ap_id: preserved
|
||||
# nickname: preserved
|
||||
})
|
||||
end
|
||||
|
||||
# Purge doesn't delete the user from the database.
|
||||
# It just nulls all its fields and deactivates it.
|
||||
# See `User.purge_user_changeset/1` above.
|
||||
defp purge(%User{} = user) do
|
||||
user
|
||||
|> purge_user_changeset()
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def delete(users) when is_list(users) do
|
||||
for user <- users, do: delete(user)
|
||||
end
|
||||
|
||||
def delete(%User{} = user) do
|
||||
# Purge the user immediately
|
||||
purge(user)
|
||||
BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
|
||||
end
|
||||
|
||||
defp delete_and_invalidate_cache(%User{} = user) do
|
||||
# *Actually* delete the user from the DB
|
||||
defp delete_from_db(%User{} = user) do
|
||||
invalidate_cache(user)
|
||||
Repo.delete(user)
|
||||
end
|
||||
|
||||
defp delete_or_deactivate(%User{local: false} = user), do: delete_and_invalidate_cache(user)
|
||||
# If the user never finalized their account, it's safe to delete them.
|
||||
defp maybe_delete_from_db(%User{local: true, is_confirmed: false} = user),
|
||||
do: delete_from_db(user)
|
||||
|
||||
defp delete_or_deactivate(%User{local: true} = user) do
|
||||
status = account_status(user)
|
||||
defp maybe_delete_from_db(%User{local: true, is_approved: false} = user),
|
||||
do: delete_from_db(user)
|
||||
|
||||
case status do
|
||||
:confirmation_pending ->
|
||||
delete_and_invalidate_cache(user)
|
||||
|
||||
:approval_pending ->
|
||||
delete_and_invalidate_cache(user)
|
||||
|
||||
_ ->
|
||||
user
|
||||
|> purge_user_changeset()
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
end
|
||||
defp maybe_delete_from_db(user), do: {:ok, user}
|
||||
|
||||
def perform(:force_password_reset, user), do: force_password_reset(user)
|
||||
|
||||
@spec perform(atom(), User.t()) :: {:ok, User.t()}
|
||||
def perform(:delete, %User{} = user) do
|
||||
# Purge the user again, in case perform/2 is called directly
|
||||
purge(user)
|
||||
|
||||
# Remove all relationships
|
||||
user
|
||||
|> get_followers()
|
||||
|
|
@ -1766,10 +1785,9 @@ defmodule Pleroma.User do
|
|||
|
||||
delete_user_activities(user)
|
||||
delete_notifications_from_user_activities(user)
|
||||
|
||||
delete_outgoing_pending_follow_requests(user)
|
||||
|
||||
delete_or_deactivate(user)
|
||||
maybe_delete_from_db(user)
|
||||
end
|
||||
|
||||
def perform(:set_activation_async, user, status), do: set_activation(user, status)
|
||||
|
|
@ -2343,45 +2361,35 @@ defmodule Pleroma.User do
|
|||
cast(user, %{is_approved: approved?}, [:is_approved])
|
||||
end
|
||||
|
||||
def add_pinnned_activity(user, %Pleroma.Activity{id: id}) do
|
||||
if id not in user.pinned_activities do
|
||||
max_pinned_statuses = Config.get([:instance, :max_pinned_statuses], 0)
|
||||
params = %{pinned_activities: user.pinned_activities ++ [id]}
|
||||
|
||||
# if pinned activity was scheduled for deletion, we remove job
|
||||
if expiration = Pleroma.Workers.PurgeExpiredActivity.get_expiration(id) do
|
||||
Oban.cancel_job(expiration.id)
|
||||
end
|
||||
@spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()}
|
||||
def add_pinned_object_id(%User{} = user, object_id) do
|
||||
if !user.pinned_objects[object_id] do
|
||||
params = %{pinned_objects: Map.put(user.pinned_objects, object_id, NaiveDateTime.utc_now())}
|
||||
|
||||
user
|
||||
|> cast(params, [:pinned_activities])
|
||||
|> validate_length(:pinned_activities,
|
||||
max: max_pinned_statuses,
|
||||
message: "You have already pinned the maximum number of statuses"
|
||||
)
|
||||
|> cast(params, [:pinned_objects])
|
||||
|> validate_change(:pinned_objects, fn :pinned_objects, pinned_objects ->
|
||||
max_pinned_statuses = Config.get([:instance, :max_pinned_statuses], 0)
|
||||
|
||||
if Enum.count(pinned_objects) <= max_pinned_statuses do
|
||||
[]
|
||||
else
|
||||
[pinned_objects: "You have already pinned the maximum number of statuses"]
|
||||
end
|
||||
end)
|
||||
else
|
||||
change(user)
|
||||
end
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def remove_pinnned_activity(user, %Pleroma.Activity{id: id, data: data}) do
|
||||
params = %{pinned_activities: List.delete(user.pinned_activities, id)}
|
||||
|
||||
# if pinned activity was scheduled for deletion, we reschedule it for deletion
|
||||
if data["expires_at"] do
|
||||
# MRF.ActivityExpirationPolicy used UTC timestamps for expires_at in original implementation
|
||||
{:ok, expires_at} =
|
||||
data["expires_at"] |> Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime.cast()
|
||||
|
||||
Pleroma.Workers.PurgeExpiredActivity.enqueue(%{
|
||||
activity_id: id,
|
||||
expires_at: expires_at
|
||||
})
|
||||
end
|
||||
|
||||
@spec remove_pinned_object_id(User.t(), String.t()) :: {:ok, t()} | {:error, term()}
|
||||
def remove_pinned_object_id(%User{} = user, object_id) do
|
||||
user
|
||||
|> cast(params, [:pinned_activities])
|
||||
|> cast(
|
||||
%{pinned_objects: Map.delete(user.pinned_objects, object_id)},
|
||||
[:pinned_objects]
|
||||
)
|
||||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ defmodule Pleroma.User.Query do
|
|||
- e.g. Pleroma.User.Query.build(%{ap_id: ["http://ap_id1", "http://ap_id2"]})
|
||||
"""
|
||||
import Ecto.Query
|
||||
import Pleroma.Web.AdminAPI.Search, only: [not_empty_string: 1]
|
||||
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
|
||||
|
||||
alias Pleroma.FollowingRelationship
|
||||
alias Pleroma.User
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ defmodule Pleroma.Utils do
|
|||
eperm epipe erange erofs espipe esrch estale etxtbsy exdev
|
||||
)a
|
||||
|
||||
@repo_timeout Pleroma.Config.get([Pleroma.Repo, :timeout], 15_000)
|
||||
|
||||
def compile_dir(dir) when is_binary(dir) do
|
||||
dir
|
||||
|> File.ls!()
|
||||
|
|
@ -63,4 +65,21 @@ defmodule Pleroma.Utils do
|
|||
end
|
||||
|
||||
def posix_error_message(_), do: ""
|
||||
|
||||
@doc """
|
||||
Returns [timeout: integer] suitable for passing as an option to Repo functions.
|
||||
|
||||
This function detects if the execution was triggered from IEx shell, Mix task, or
|
||||
./bin/pleroma_ctl and sets the timeout to :infinity, else returns the default timeout value.
|
||||
"""
|
||||
@spec query_timeout() :: [timeout: integer]
|
||||
def query_timeout do
|
||||
{parent, _, _, _} = Process.info(self(), :current_stacktrace) |> elem(1) |> Enum.fetch!(2)
|
||||
|
||||
cond do
|
||||
parent |> to_string |> String.starts_with?("Elixir.Mix.Task") -> [timeout: :infinity]
|
||||
parent == :erl_eval -> [timeout: :infinity]
|
||||
true -> [timeout: @repo_timeout]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -35,9 +35,10 @@ defmodule Pleroma.Web do
|
|||
import Plug.Conn
|
||||
|
||||
import Pleroma.Web.Gettext
|
||||
import Pleroma.Web.Router.Helpers
|
||||
import Pleroma.Web.TranslationHelpers
|
||||
|
||||
alias Pleroma.Web.Router.Helpers, as: Routes
|
||||
|
||||
plug(:set_put_layout)
|
||||
|
||||
defp set_put_layout(conn, _) do
|
||||
|
|
@ -61,6 +62,14 @@ defmodule Pleroma.Web do
|
|||
)
|
||||
end
|
||||
|
||||
defp skip_auth(conn, _) do
|
||||
skip_plug(conn, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug])
|
||||
end
|
||||
|
||||
defp skip_public_check(conn, _) do
|
||||
skip_plug(conn, EnsurePublicOrAuthenticatedPlug)
|
||||
end
|
||||
|
||||
# Executed just before actual controller action, invokes before-action hooks (callbacks)
|
||||
defp action(conn, params) do
|
||||
with %{halted: false} = conn <-
|
||||
|
|
@ -131,7 +140,8 @@ defmodule Pleroma.Web do
|
|||
|
||||
import Pleroma.Web.ErrorHelpers
|
||||
import Pleroma.Web.Gettext
|
||||
import Pleroma.Web.Router.Helpers
|
||||
|
||||
alias Pleroma.Web.Router.Helpers, as: Routes
|
||||
|
||||
require Logger
|
||||
|
||||
|
|
@ -229,20 +239,4 @@ defmodule Pleroma.Web do
|
|||
defmacro __using__(which) when is_atom(which) do
|
||||
apply(__MODULE__, which, [])
|
||||
end
|
||||
|
||||
def base_url do
|
||||
Pleroma.Web.Endpoint.url()
|
||||
end
|
||||
|
||||
# TODO: Change to Phoenix.Router.routes/1 for Phoenix 1.6.0+
|
||||
def get_api_routes do
|
||||
Pleroma.Web.Router.__routes__()
|
||||
|> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end)
|
||||
|> Enum.map(fn r ->
|
||||
r.path
|
||||
|> String.split("/", trim: true)
|
||||
|> List.first()
|
||||
end)
|
||||
|> Enum.uniq()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -53,15 +53,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
{recipients, to, cc}
|
||||
end
|
||||
|
||||
defp check_actor_is_active(nil), do: true
|
||||
defp check_actor_can_insert(%{"type" => "Delete"}), do: true
|
||||
defp check_actor_can_insert(%{"type" => "Undo"}), do: true
|
||||
|
||||
defp check_actor_is_active(actor) when is_binary(actor) do
|
||||
defp check_actor_can_insert(%{"actor" => actor}) when is_binary(actor) do
|
||||
case User.get_cached_by_ap_id(actor) do
|
||||
%User{is_active: true} -> true
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
defp check_actor_can_insert(_), do: true
|
||||
|
||||
defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(content) do
|
||||
limit = Config.get([:instance, :remote_limit])
|
||||
String.length(content) <= limit
|
||||
|
|
@ -88,7 +91,7 @@ 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]
|
||||
@object_types ~w[ChatMessage Question Answer Audio Video Event Article Note Page]
|
||||
@impl true
|
||||
def persist(%{"type" => type} = object, meta) when type in @object_types do
|
||||
with {:ok, object} <- Object.create(object) do
|
||||
|
|
@ -117,7 +120,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
|
||||
with nil <- Activity.normalize(map),
|
||||
map <- lazy_put_activity_defaults(map, fake),
|
||||
{_, true} <- {:actor_check, bypass_actor_check || check_actor_is_active(map["actor"])},
|
||||
{_, true} <- {:actor_check, bypass_actor_check || check_actor_can_insert(map)},
|
||||
{_, true} <- {:remote_limit_pass, check_remote_limit(map)},
|
||||
{:ok, map} <- MRF.filter(map),
|
||||
{recipients, _, _} = get_recipients(map),
|
||||
|
|
@ -630,7 +633,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> Map.put(:type, ["Create", "Announce"])
|
||||
|> Map.put(:user, reading_user)
|
||||
|> Map.put(:actor_id, user.ap_id)
|
||||
|> Map.put(:pinned_activity_ids, user.pinned_activities)
|
||||
|> Map.put(:pinned_object_ids, Map.keys(user.pinned_objects))
|
||||
|
||||
params =
|
||||
if User.blocks?(reading_user, user) do
|
||||
|
|
@ -1075,8 +1078,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_unlisted(query, _), do: query
|
||||
|
||||
defp restrict_pinned(query, %{pinned: true, pinned_activity_ids: ids}) do
|
||||
from(activity in query, where: activity.id in ^ids)
|
||||
defp restrict_pinned(query, %{pinned: true, pinned_object_ids: ids}) do
|
||||
from(
|
||||
[activity, object: o] in query,
|
||||
where:
|
||||
fragment(
|
||||
"(?)->>'type' = 'Create' and coalesce((?)->'object'->>'id', (?)->>'object') = any (?)",
|
||||
activity.data,
|
||||
activity.data,
|
||||
activity.data,
|
||||
^ids
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict_pinned(query, _), do: query
|
||||
|
|
@ -1419,6 +1432,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
invisible = data["invisible"] || false
|
||||
actor_type = data["type"] || "Person"
|
||||
|
||||
featured_address = data["featured"]
|
||||
{:ok, pinned_objects} = fetch_and_prepare_featured_from_ap_id(featured_address)
|
||||
|
||||
public_key =
|
||||
if is_map(data["publicKey"]) && is_binary(data["publicKey"]["publicKeyPem"]) do
|
||||
data["publicKey"]["publicKeyPem"]
|
||||
|
|
@ -1447,13 +1463,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
name: data["name"],
|
||||
follower_address: data["followers"],
|
||||
following_address: data["following"],
|
||||
featured_address: featured_address,
|
||||
bio: data["summary"] || "",
|
||||
actor_type: actor_type,
|
||||
also_known_as: Map.get(data, "alsoKnownAs", []),
|
||||
public_key: public_key,
|
||||
inbox: data["inbox"],
|
||||
shared_inbox: shared_inbox,
|
||||
accepts_chat_messages: accepts_chat_messages
|
||||
accepts_chat_messages: accepts_chat_messages,
|
||||
pinned_objects: pinned_objects
|
||||
}
|
||||
|
||||
# nickname can be nil because of virtual actors
|
||||
|
|
@ -1591,6 +1609,41 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
end
|
||||
|
||||
def pin_data_from_featured_collection(%{
|
||||
"type" => type,
|
||||
"orderedItems" => objects
|
||||
})
|
||||
when type in ["OrderedCollection", "Collection"] do
|
||||
Map.new(objects, fn %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()} end)
|
||||
end
|
||||
|
||||
def fetch_and_prepare_featured_from_ap_id(nil) do
|
||||
{:ok, %{}}
|
||||
end
|
||||
|
||||
def fetch_and_prepare_featured_from_ap_id(ap_id) do
|
||||
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do
|
||||
{:ok, pin_data_from_featured_collection(data)}
|
||||
else
|
||||
e ->
|
||||
Logger.error("Could not decode featured collection at fetch #{ap_id}, #{inspect(e)}")
|
||||
{:ok, %{}}
|
||||
end
|
||||
end
|
||||
|
||||
def pinned_fetch_task(nil), do: nil
|
||||
|
||||
def pinned_fetch_task(%{pinned_objects: pins}) do
|
||||
if Enum.all?(pins, fn {ap_id, _} ->
|
||||
Object.get_cached_by_ap_id(ap_id) ||
|
||||
match?({:ok, _object}, Fetcher.fetch_object_from_id(ap_id))
|
||||
end) do
|
||||
:ok
|
||||
else
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
def make_user_from_ap_id(ap_id) do
|
||||
user = User.get_cached_by_ap_id(ap_id)
|
||||
|
||||
|
|
@ -1598,6 +1651,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
Transmogrifier.upgrade_user_from_ap_id(ap_id)
|
||||
else
|
||||
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
|
||||
{:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end)
|
||||
|
||||
if user do
|
||||
user
|
||||
|> User.remote_user_changeset(data)
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.ActivityPub.Persisting do
|
||||
@callback persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
|
||||
@callback persist(map(), keyword()) :: {:ok, struct()}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,10 +3,6 @@
|
|||
# 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()
|
||||
@callback stream_out(struct()) :: any()
|
||||
@callback stream_out_participations(struct(), struct()) :: any()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
alias Pleroma.Object.Fetcher
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||
alias Pleroma.Web.ActivityPub.ObjectView
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
|
|
@ -403,83 +402,90 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|> json(err)
|
||||
end
|
||||
|
||||
defp handle_user_activity(
|
||||
%User{} = user,
|
||||
%{"type" => "Create", "object" => %{"type" => "Note"} = object} = params
|
||||
) do
|
||||
content = if is_binary(object["content"]), do: object["content"], else: ""
|
||||
name = if is_binary(object["name"]), do: object["name"], else: ""
|
||||
summary = if is_binary(object["summary"]), do: object["summary"], else: ""
|
||||
length = String.length(content <> name <> summary)
|
||||
defp fix_user_message(%User{ap_id: actor}, %{"type" => "Create", "object" => object} = activity)
|
||||
when is_map(object) do
|
||||
length =
|
||||
[object["content"], object["summary"], object["name"]]
|
||||
|> Enum.filter(&is_binary(&1))
|
||||
|> Enum.join("")
|
||||
|> String.length()
|
||||
|
||||
if length > Pleroma.Config.get([:instance, :limit]) do
|
||||
{:error, dgettext("errors", "Note is over the character limit")}
|
||||
else
|
||||
limit = Pleroma.Config.get([:instance, :limit])
|
||||
|
||||
if length < limit do
|
||||
object =
|
||||
object
|
||||
|> Map.merge(Map.take(params, ["to", "cc"]))
|
||||
|> Map.put("attributedTo", user.ap_id)
|
||||
|> Transmogrifier.fix_object()
|
||||
|> Transmogrifier.strip_internal_fields()
|
||||
|> Map.put("attributedTo", actor)
|
||||
|> Map.put("actor", actor)
|
||||
|> Map.put("id", Utils.generate_object_id())
|
||||
|
||||
ActivityPub.create(%{
|
||||
to: params["to"],
|
||||
actor: user,
|
||||
context: object["context"],
|
||||
object: object,
|
||||
additional: Map.take(params, ["cc"])
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_user_activity(%User{} = user, %{"type" => "Delete"} = params) do
|
||||
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
|
||||
{:ok, delete}
|
||||
{:ok, Map.put(activity, "object", object)}
|
||||
else
|
||||
_ -> {:error, dgettext("errors", "Can't delete object")}
|
||||
{:error,
|
||||
dgettext(
|
||||
"errors",
|
||||
"Character limit (%{limit} characters) exceeded, contains %{length} characters",
|
||||
limit: limit,
|
||||
length: length
|
||||
)}
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_user_activity(%User{} = user, %{"type" => "Like"} = params) do
|
||||
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,
|
||||
Pipeline.common_pipeline(like_object, Keyword.put(meta, :local, true))} do
|
||||
defp fix_user_message(
|
||||
%User{ap_id: actor} = user,
|
||||
%{"type" => "Delete", "object" => object} = activity
|
||||
) do
|
||||
with {_, %Object{data: object_data}} <- {:normalize, Object.normalize(object, fetch: false)},
|
||||
{_, true} <- {:permission, user.is_moderator || actor == object_data["actor"]} do
|
||||
{:ok, activity}
|
||||
else
|
||||
_ -> {:error, dgettext("errors", "Can't like object")}
|
||||
{:normalize, _} ->
|
||||
{:error, "No such object found"}
|
||||
|
||||
{:permission, _} ->
|
||||
{:forbidden, "You can't delete this object"}
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_user_activity(_, _) do
|
||||
{:error, dgettext("errors", "Unhandled activity type")}
|
||||
defp fix_user_message(%User{}, activity) do
|
||||
{:ok, activity}
|
||||
end
|
||||
|
||||
def update_outbox(
|
||||
%{assigns: %{user: %User{nickname: nickname} = user}} = conn,
|
||||
%{assigns: %{user: %User{nickname: nickname, ap_id: actor} = user}} = conn,
|
||||
%{"nickname" => nickname} = params
|
||||
) do
|
||||
actor = user.ap_id
|
||||
|
||||
params =
|
||||
params
|
||||
|> Map.drop(["id"])
|
||||
|> Map.drop(["nickname"])
|
||||
|> Map.put("id", Utils.generate_activity_id())
|
||||
|> Map.put("actor", actor)
|
||||
|> Transmogrifier.fix_addressing()
|
||||
|
||||
with {:ok, %Activity{} = activity} <- handle_user_activity(user, params) do
|
||||
with {:ok, params} <- fix_user_message(user, params),
|
||||
{:ok, activity, _} <- Pipeline.common_pipeline(params, local: true),
|
||||
%Activity{data: activity_data} <- Activity.normalize(activity) do
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> put_resp_header("location", activity.data["id"])
|
||||
|> json(activity.data)
|
||||
|> put_resp_header("location", activity_data["id"])
|
||||
|> json(activity_data)
|
||||
else
|
||||
{:forbidden, message} ->
|
||||
conn
|
||||
|> put_status(:forbidden)
|
||||
|> json(message)
|
||||
|
||||
{:error, message} ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(message)
|
||||
|
||||
e ->
|
||||
Logger.warn(fn -> "AP C2S: #{inspect(e)}" end)
|
||||
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json("Bad Request")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -543,4 +549,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|> json(object.data)
|
||||
end
|
||||
end
|
||||
|
||||
def pinned(conn, %{"nickname" => nickname}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname(nickname) do
|
||||
conn
|
||||
|> put_resp_header("content-type", "application/activity+json")
|
||||
|> json(UserView.render("featured.json", %{user: user}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
[actor.follower_address]
|
||||
|
||||
public? and Visibility.is_local_public?(object) ->
|
||||
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_local_public()]
|
||||
[actor.follower_address, object.data["actor"], Utils.as_local_public()]
|
||||
|
||||
public? ->
|
||||
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()]
|
||||
|
|
@ -273,4 +273,36 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
"context" => object.data["context"]
|
||||
}, []}
|
||||
end
|
||||
|
||||
@spec pin(User.t(), Object.t()) :: {:ok, map(), keyword()}
|
||||
def pin(%User{} = user, object) do
|
||||
{:ok,
|
||||
%{
|
||||
"id" => Utils.generate_activity_id(),
|
||||
"target" => pinned_url(user.nickname),
|
||||
"object" => object.data["id"],
|
||||
"actor" => user.ap_id,
|
||||
"type" => "Add",
|
||||
"to" => [Pleroma.Constants.as_public()],
|
||||
"cc" => [user.follower_address]
|
||||
}, []}
|
||||
end
|
||||
|
||||
@spec unpin(User.t(), Object.t()) :: {:ok, map, keyword()}
|
||||
def unpin(%User{} = user, object) do
|
||||
{:ok,
|
||||
%{
|
||||
"id" => Utils.generate_activity_id(),
|
||||
"target" => pinned_url(user.nickname),
|
||||
"object" => object.data["id"],
|
||||
"actor" => user.ap_id,
|
||||
"type" => "Remove",
|
||||
"to" => [Pleroma.Constants.as_public()],
|
||||
"cc" => [user.follower_address]
|
||||
}, []}
|
||||
end
|
||||
|
||||
defp pinned_url(nickname) when is_binary(nickname) do
|
||||
Pleroma.Web.Router.Helpers.activity_pub_url(Pleroma.Web.Endpoint, :pinned, nickname)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -51,17 +51,6 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
|
||||
@required_description_keys [:key, :related_policy]
|
||||
|
||||
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
|
||||
@callback describe() :: {:ok | :error, Map.t()}
|
||||
@callback config_description() :: %{
|
||||
optional(:children) => [map()],
|
||||
key: atom(),
|
||||
related_policy: String.t(),
|
||||
label: String.t(),
|
||||
description: String.t()
|
||||
}
|
||||
@optional_callbacks config_description: 0
|
||||
|
||||
def filter(policies, %{} = message) do
|
||||
policies
|
||||
|> Enum.reduce({:ok, message}, fn
|
||||
|
|
@ -142,7 +131,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
def describe, do: get_policies() |> describe()
|
||||
|
||||
def config_descriptions do
|
||||
Pleroma.Web.ActivityPub.MRF
|
||||
Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|> Pleroma.Docs.Generator.list_behaviour_implementations()
|
||||
|> config_descriptions()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do
|
||||
@moduledoc "Adds expiration to all local Create activities"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(activity) do
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
|
|||
|
||||
@moduledoc "Prevent followbots from following with a bit of heuristic"
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
# XXX: this should become User.normalize_by_ap_id() or similar, really.
|
||||
defp normalize_by_ap_id(%{"id" => id}), do: User.get_cached_by_ap_id(id)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
|
||||
alias Pleroma.User
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
require Logger
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
|
||||
require Logger
|
||||
@moduledoc "Drop and log everything received"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
|
|||
alias Pleroma.Object
|
||||
|
||||
@moduledoc "Ensure a re: is prepended on replies to a post with a Subject"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do
|
||||
alias Pleroma.User
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
@moduledoc "Remove bot posts from federated timeline"
|
||||
|
||||
require Pleroma.Constants
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
|
|||
Note: This MRF Policy is always enabled, if you want to disable it you have to set empty lists.
|
||||
"""
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp check_reject(message, hashtags) do
|
||||
if Enum.any?(Config.get([:mrf_hashtag, :reject]), fn match -> match in hashtags end) do
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
|
|||
|
||||
@moduledoc "Block messages with too much mentions (configurable)"
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp delist_message(message, threshold) when threshold > 0 do
|
||||
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
|
|||
|
||||
@moduledoc "Reject or Word-Replace messages with a keyword or regex"
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
defp string_matches?(string, _) when not is_binary(string) do
|
||||
false
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
|
||||
@moduledoc "Preloads any attachments in the MediaProxy cache by prefetching them"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
alias Pleroma.HTTP
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.MentionPolicy do
|
||||
@moduledoc "Block messages which mention a user"
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Create"} = message) do
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
|
||||
@moduledoc "Filter local activities which have no content"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.Endpoint
|
||||
|
||||
@impl true
|
||||
def filter(%{"actor" => actor} = object) do
|
||||
|
|
@ -24,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
|
|||
def filter(object), do: {:ok, object}
|
||||
|
||||
defp is_local?(actor) do
|
||||
if actor |> String.starts_with?("#{Web.base_url()}") do
|
||||
if actor |> String.starts_with?("#{Endpoint.url()}") do
|
||||
true
|
||||
else
|
||||
false
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
|
||||
@moduledoc "Does nothing (lets the messages go through unmodified)"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(object) do
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
|
||||
@moduledoc "Ensure no content placeholder is present (such as the dot from mastodon)"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do
|
|||
@moduledoc "Scrub configured hypertext markup"
|
||||
alias Pleroma.HTML
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Create", "object" => child_object} = object) do
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
|
|||
require Pleroma.Constants
|
||||
|
||||
@moduledoc "Filter activities depending on their age"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp check_date(%{"object" => %{"published" => published}} = message) do
|
||||
with %DateTime{} = now <- DateTime.utc_now(),
|
||||
|
|
|
|||
16
lib/pleroma/web/activity_pub/mrf/policy.ex
Normal file
16
lib/pleroma/web/activity_pub/mrf/policy.ex
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.Policy do
|
||||
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
|
||||
@callback describe() :: {:ok | :error, Map.t()}
|
||||
@callback config_description() :: %{
|
||||
optional(:children) => [map()],
|
||||
key: atom(),
|
||||
related_policy: String.t(),
|
||||
label: String.t(),
|
||||
description: String.t()
|
||||
}
|
||||
@optional_callbacks config_description: 0
|
||||
end
|
||||
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
|
|||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
||||
@moduledoc "Filter activities depending on their origin instance"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.FollowingRelationship
|
||||
|
|
@ -177,6 +177,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
|
||||
defp check_banner_removal(_actor_info, object), do: {:ok, object}
|
||||
|
||||
defp check_object(%{"object" => object} = activity) do
|
||||
with {:ok, _object} <- filter(object) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
defp check_object(object), do: {:ok, object}
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Delete", "actor" => actor} = object) do
|
||||
%{host: actor_host} = URI.parse(actor)
|
||||
|
|
@ -202,7 +210,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
{:ok, object} <- check_media_nsfw(actor_info, object),
|
||||
{:ok, object} <- check_ftl_removal(actor_info, object),
|
||||
{:ok, object} <- check_followers_only(actor_info, object),
|
||||
{:ok, object} <- check_report_removal(actor_info, object) do
|
||||
{:ok, object} <- check_report_removal(actor_info, object),
|
||||
{:ok, object} <- check_object(object) do
|
||||
{:ok, object}
|
||||
else
|
||||
{:reject, nil} -> {:reject, "[SimplePolicy]"}
|
||||
|
|
@ -227,6 +236,19 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
|||
end
|
||||
end
|
||||
|
||||
def filter(object) when is_binary(object) do
|
||||
uri = URI.parse(object)
|
||||
|
||||
with {:ok, object} <- check_accept(uri, object),
|
||||
{:ok, object} <- check_reject(uri, object) do
|
||||
{:ok, object}
|
||||
else
|
||||
{:reject, nil} -> {:reject, "[SimplePolicy]"}
|
||||
{:reject, _} = e -> e
|
||||
_ -> {:reject, "[SimplePolicy]"}
|
||||
end
|
||||
end
|
||||
|
||||
def filter(object), do: {:ok, object}
|
||||
|
||||
@impl true
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
|
|||
alias Pleroma.Config
|
||||
|
||||
@moduledoc "Detect new emojis by their shortcode and steals them"
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp accept_host?(host), do: host in Config.get([:mrf_steal_emoji, :hosts], [])
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
|
|||
|
||||
require Logger
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
defp lookup_subchain(actor) do
|
||||
with matches <- Config.get([:mrf_subchain, :match_actor]),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do
|
||||
alias Pleroma.User
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
@moduledoc """
|
||||
Apply policies based on user tags
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue