Merge remote-tracking branch 'origin/develop' into shigusegubu

This commit is contained in:
Henry Jameson 2026-01-03 02:01:33 +02:00
commit 8d82808d7a
632 changed files with 7519 additions and 1314 deletions

View file

@ -1,4 +1,4 @@
image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.14.5-otp-25 image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.15.8-otp-26
variables: &global_variables variables: &global_variables
# Only used for the release # Only used for the release
@ -16,9 +16,15 @@ workflow:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "develop" - if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_COMMIT_BRANCH == "stable" - if: $CI_COMMIT_BRANCH == "stable"
- if: $CI_PIPELINE_SOURCE == "web"
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never when: never
# Default artifacts configuration
.default_artifacts: &default_artifacts
expire_in: 30 days
cache: &global_cache_policy cache: &global_cache_policy
key: $CI_JOB_IMAGE-$CI_COMMIT_SHORT_SHA key: $CI_JOB_IMAGE-$CI_COMMIT_SHORT_SHA
paths: paths:
@ -56,6 +62,7 @@ check-changelog:
before_script: '' before_script: ''
after_script: '' after_script: ''
cache: {} cache: {}
artifacts: *default_artifacts
script: script:
- apk add git - apk add git
- sh ./tools/check-changelog - sh ./tools/check-changelog
@ -71,8 +78,9 @@ check-changelog:
.using-ci-base: .using-ci-base:
tags: tags:
- amd64 - amd64
artifacts: *default_artifacts
build-1.14.5-otp-25: build-1.15.8-otp-26:
extends: extends:
- .build_changes_policy - .build_changes_policy
- .using-ci-base - .using-ci-base
@ -101,8 +109,12 @@ spec-build:
artifacts: artifacts:
paths: paths:
- spec.json - spec.json
reports:
dotenv: build.env
expire_in: 42 years
script: script:
- mix pleroma.openapi_spec spec.json - mix pleroma.openapi_spec spec.json
- echo "SPEC_BUILD_JOB_ID=$CI_JOB_ID" >> build.env
benchmark: benchmark:
extends: extends:
@ -120,7 +132,7 @@ benchmark:
- mix ecto.migrate - mix ecto.migrate
- mix pleroma.load_testing - mix pleroma.load_testing
unit-testing-1.14.5-otp-25: unit-testing-1.15.8-otp-26:
extends: extends:
- .build_changes_policy - .build_changes_policy
- .using-ci-base - .using-ci-base
@ -153,6 +165,7 @@ unit-testing-1.14.5-otp-25:
- su testuser -c "HOME=/home/testuser mix pleroma.test_runner --cover --preload-modules" - su testuser -c "HOME=/home/testuser mix pleroma.test_runner --cover --preload-modules"
coverage: '/^Line total: ([^ ]*%)$/' coverage: '/^Line total: ([^ ]*%)$/'
artifacts: artifacts:
expire_in: 30 days
reports: reports:
coverage_report: coverage_report:
coverage_format: cobertura coverage_format: cobertura
@ -171,6 +184,7 @@ unit-testing-1.18.3-otp-27:
formatting-1.15: formatting-1.15:
extends: .build_changes_policy extends: .build_changes_policy
artifacts: *default_artifacts
image: &formatting_elixir elixir:1.15-alpine image: &formatting_elixir elixir:1.15-alpine
stage: lint stage: lint
cache: *testing_cache_policy cache: *testing_cache_policy
@ -185,6 +199,7 @@ formatting-1.15:
cycles-1.15: cycles-1.15:
extends: .build_changes_policy extends: .build_changes_policy
artifacts: *default_artifacts
image: *formatting_elixir image: *formatting_elixir
stage: lint stage: lint
cache: {} cache: {}
@ -208,7 +223,7 @@ dialyzer:
- .using-ci-base - .using-ci-base
stage: lint stage: lint
allow_failure: true allow_failure: true
when: manual when: manual
cache: *testing_cache_policy cache: *testing_cache_policy
tags: tags:
- feld - feld
@ -217,15 +232,14 @@ dialyzer:
docs-deploy: docs-deploy:
stage: deploy stage: deploy
cache: *testing_cache_policy trigger:
image: alpine:latest project: pleroma/docs
branch: master
strategy: depend
only: only:
- stable@pleroma/pleroma - stable@pleroma/pleroma
- develop@pleroma/pleroma - develop@pleroma/pleroma
before_script:
- apk add curl
script:
- curl --fail-with-body -X POST -F"token=$DOCS_PIPELINE_TRIGGER" -F'ref=master' -F"variables[BRANCH]=$CI_COMMIT_REF_NAME" https://git.pleroma.social/api/v4/projects/673/trigger/pipeline
review_app: review_app:
image: alpine:3.9 image: alpine:3.9
stage: deploy stage: deploy
@ -241,6 +255,7 @@ review_app:
except: except:
- master - master
- develop - develop
artifacts: *default_artifacts
script: script:
- echo "$CI_ENVIRONMENT_SLUG" - echo "$CI_ENVIRONMENT_SLUG"
- mkdir -p ~/.ssh - mkdir -p ~/.ssh
@ -257,21 +272,19 @@ review_app:
spec-deploy: spec-deploy:
stage: deploy stage: deploy
artifacts: trigger:
paths: project: pleroma/api-docs
- spec.json branch: master
strategy: depend
only: only:
- develop@pleroma/pleroma - develop@pleroma/pleroma
image: alpine:latest variables:
before_script: SPEC_BUILD_JOB_ID: $SPEC_BUILD_JOB_ID
- apk add curl
script:
- curl --fail-with-body -X POST -F"token=$API_DOCS_PIPELINE_TRIGGER" -F'ref=master' -F"variables[BRANCH]=$CI_COMMIT_REF_NAME" -F"variables[JOB_REF]=$CI_JOB_ID" https://git.pleroma.social/api/v4/projects/1130/trigger/pipeline
stop_review_app: stop_review_app:
image: alpine:3.9 image: alpine:3.9
stage: deploy stage: deploy
artifacts: *default_artifacts
before_script: before_script:
- apk update && apk add openssh-client git - apk update && apk add openssh-client git
when: manual when: manual
@ -290,7 +303,7 @@ stop_review_app:
amd64: amd64:
stage: release stage: release
image: image:
name: hexpm/elixir-amd64:1.17.3-erlang-26.2.5.6-ubuntu-focal-20241011 name: hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716
only: &release-only only: &release-only
- stable@pleroma/pleroma - stable@pleroma/pleroma
- develop@pleroma/pleroma - develop@pleroma/pleroma
@ -317,7 +330,7 @@ amd64:
VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS
DEBIAN_FRONTEND: noninteractive DEBIAN_FRONTEND: noninteractive
before_script: &before-release before_script: &before-release
- apt-get update && apt-get install -y cmake libmagic-dev libvips-dev erlang-dev git - apt-get update && apt-get install -y cmake libmagic-dev libvips-dev erlang-dev git build-essential
- echo "import Config" > config/prod.secret.exs - echo "import Config" > config/prod.secret.exs
- mix local.hex --force - mix local.hex --force
- mix local.rebar --force - mix local.rebar --force
@ -333,7 +346,7 @@ amd64-musl:
artifacts: *release-artifacts artifacts: *release-artifacts
only: *release-only only: *release-only
image: image:
name: hexpm/elixir-amd64:1.17.3-erlang-26.2.5.6-alpine-3.17.9 name: hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-alpine-3.22.1
tags: tags:
- amd64 - amd64
cache: *release-cache cache: *release-cache
@ -377,7 +390,7 @@ arm64:
tags: tags:
- arm - arm
image: image:
name: hexpm/elixir-arm64:1.17.3-erlang-26.2.5.6-ubuntu-focal-20241011 name: hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716
cache: *release-cache cache: *release-cache
variables: *release-variables variables: *release-variables
before_script: *before-release before_script: *before-release
@ -390,7 +403,7 @@ arm64-musl:
tags: tags:
- arm - arm
image: image:
name: hexpm/elixir-arm64:1.17.3-erlang-26.2.5.6-alpine-3.17.9 name: hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-alpine-3.22.1
cache: *release-cache cache: *release-cache
variables: *release-variables variables: *release-variables
before_script: *before-release-musl before_script: *before-release-musl

View file

@ -4,9 +4,113 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2.10
### Security
- Admin API: Fixed self-revocation vulnerability where admins could accidentally revoke their own admin status via the single-user permission endpoint
- Fix bypass of the restrict unauthenticated setting by requesting local Activities
### Changed
- Add new activity actor/type index. Greatly speeds up retrieval of rare types (like "Listen")
- Use separate schemas for muted/blocked accounts lists
- Docs: Restore DB schema before data to avoid long restore times
- Return 404 with a better error message instead of 400 when receiving an activity for a deactivated user
- Deleting an instance queues individual jobs for each user that needs to be deleted from the server.
- Update Dockerfile to use Elixir 1.17.3, Erlang 26.2.5.6, and Alpine 3.17.9 to match CI release builds
- Docs RUM index: Add OTP install command, update index size expectation and recommend VACUUM FULL
- Support new Mastodon API for endorsed accounts
- Allow FediIndex crawler bot by default
- Update Cowboy, Gun, and Plug family of dependencies
- Hashtag searches return real results based on words in your query
- Support `quoted_status_id` parameter in post creation request
- Use Mastodon-compatible route for quotes list and param for quotes count
- Updated the example Nginx configuration
- Oban Notifier was changed to Oban.Notifiers.PG for performance and scalability benefits
- Updated relayd/httpd config files to be on par with nginx
- Order favourites and reblogs list from newest to oldest
- Update Pleroma-FE to 2.9.2
- Updated Postgrex library to 0.20.0
- Improved the logic of how we determine if a server is unreachable.
- Relax alsoKnownAs requirements to just URI, not necessarily HTTP(S)
- Redirect /users/:nickname.rss to /users/:nickname/feed.rss instead of .atom
- Add `write:scrobbles` and `read:scrobbles` scope for scrobbling
- Change scrobble external link param name to use snake case
- Allow "invisible" and "ellipsis" classes for span tags to match Mastodon behavior
- Change SMTP example to use the Mua adapter that works with OTP>25
- Updated Tesla to 1.15.3
- Truncate the length of Rich Media title and description fields
- Don't require an Accept header for WebFinger queries and default to JSON.
### Added
- Support Dislike activity, as sent by Mitra and Friendica, by changing it into a thumbs-down EmojiReact
- Support Mitra-style emoji likes.
- Added a way to upload new packs from a URL or ZIP file via Admin API
- Add `duration` to the block endpoint, which makes block expire
- Expose markup configuration in InstanceView
- Allow filtering users with `accepts_chat_messages` capability
- Add `timelines_access` to InstanceView
- Implement language detection with fastText
- Added MRF.QuietReply which prevents replies to public posts from being published to the timelines
- Oban.Plugins.Lazarus to help recover stuck jobs from an unclean shutdown of Pleroma
- Add /api/v1/pleroma/outgoing_follow_requests
- Allow users to select preferred frontend
- Provide full replies collection in ActivityPub objects
- Allow anonymizing reports sent to remote servers
- Add only_reblogs parameter to account statuses API for filtering to show only reblogs/reposts
- Allow setting custom user-agent for fetching rich media content
- Scrubber: Allow `quote-inline` class in <p> tags used by Mastodon quotes
- Add `base_urls` to the /api/v1/instance pleroma metadata which provides information about the base URLs for media_proxy and uploads when configured
- Stream marker updates
- Allow Terms of Service panel behaviour to be configurable
- Support translation providers (DeepL, LibreTranslate)
- Support Mozhi translation provider
- Support translateLocally translation provider
### Fixed
- AP C2S: Reject interactions with statuses not visible to Actor
- Fix AssignAppUser migration OOM
- Fix fetching public keys with authorized fetch enabled
- Fix building "captcha" library with OpenBSD make
- Use JSON for DeepL API requests
- Elixir 1.18: Fixed warnings and new deprecations
- Fix endorsement state display in relationship view
- Fix publisher when publishing to a list of users
- Fix reports being rejected when the activity had an empty CC or TO field (instead of not having them at all)
- Set PATH in the FreeBSD rc script to avoid failures starting the service
- Improved performance of status search queries using the default GIN index
- Use end-of-string in regex for local `get_by_nickname`
- Respect restrict_unauthenticated in /api/v1/accounts/lookup
- MastodonAPI: Reject interactions with statuses not visible to user
- Fix ModerationLog FunctionClauseError for unknown actions
- MRF InlineQuotePolicy: Don't inline quoted post URL in Mastodon quote posts
- Fix NodeInfo content-type
- Add Actor images normalization from array of urls to string
- Add `update` to @notification_types
- replaced depracated flags and functions, renamed service to fit other service files
- Allow to pin/unpip chats
- Fix federation issue where Public visibility information in cc field was lost when sent to remote servers, causing posts to appear with inconsistent visibility across instances
- OpenBSD relayd: Fix IPv6 example
- Fix release builds
- `remote_url` links to unproxied URL
- Send push notifications for statuses from subscribed accounts
- Backport [Elixir PR 14242](https://github.com/elixir-lang/elixir/pull/14242) fixing racy mkdir and lack of error handling of parent directory creation
- Transmogrifier: convert "as:Public" to full w3 URL
- Update voters count in remote polls when refreshing
- Fix sometimes incorrect URI percent encoding
- Fix HTTP client making invalid requests due to no percent encoding processing or validation.
- ObjectView: Do not leak unsanitized internal representation of non-Create/non-Undo Activities on fetches
- Fix WebFinger for split-domain setups
- Enforce an exact domain match for WebFinger resolution
- MastodonAPI: Fix misattribution of statuses when fetched via non-Announce Activity ID
## 2.9.1 ## 2.9.1
### Security ### Security
- Fix authorization checks for C2S Update activities to prevent unauthorized modifications of other users' content. - Fix authorization checks for C2S Update activities to prevent unauthorized modifications of other users' content.
- Fix content-type spoofing vulnerability that could allow users to upload ActivityPub objects as attachments - Fix content-type spoofing vulnerability that could allow users to upload ActivityPub objects as attachments
- Reject cross-domain redirects when fetching ActivityPub objects to prevent bypassing domain-based security controls. - Reject cross-domain redirects when fetching ActivityPub objects to prevent bypassing domain-based security controls.
@ -16,27 +120,33 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Validate Content-Type headers when fetching remote ActivityPub objects to prevent spoofing attacks. - Validate Content-Type headers when fetching remote ActivityPub objects to prevent spoofing attacks.
### Changed ### Changed
- Include `pl-fe` in available frontends - Include `pl-fe` in available frontends
### Fixed ### Fixed
- Remove trailing ` from end of line 75 which caused issues copy-pasting - Remove trailing ` from end of line 75 which caused issues copy-pasting
## 2.9.0 ## 2.9.0
### Security ### Security
- Require HTTP signatures (if enabled) for routes used by both C2S and S2S AP API - Require HTTP signatures (if enabled) for routes used by both C2S and S2S AP API
- Fix several spoofing vectors - Fix several spoofing vectors
### Changed ### Changed
- Performance: Use 301 (permanent) redirect instead of 302 (temporary) when redirecting small images in media proxy. This allows browsers to cache the redirect response.
- Performance: Use 301 (permanent) redirect instead of 302 (temporary) when redirecting small images in media proxy. This allows browsers to cache the redirect response.
### Added ### Added
- Include "published" in actor view - Include "published" in actor view
- Link to exported outbox/followers/following collections in backup actor.json - Link to exported outbox/followers/following collections in backup actor.json
- Hashtag following - Hashtag following
- Allow to specify post language - Allow to specify post language
### Fixed ### Fixed
- Verify a local Update sent through AP C2S so users can only update their own objects - Verify a local Update sent through AP C2S so users can only update their own objects
- Fix Mastodon incoming edits with inlined "likes" - Fix Mastodon incoming edits with inlined "likes"
- Allow incoming "Listen" activities - Allow incoming "Listen" activities
@ -46,11 +156,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix blurhash generation crashes - Fix blurhash generation crashes
### Removed ### Removed
- Retire MRFs DNSRBL, FODirectReply, and QuietReply - Retire MRFs DNSRBL, FODirectReply, and QuietReply
## 2.8.0 ## 2.8.0
### Changed ### Changed
- Metadata: Do not include .atom feed links for remote accounts - Metadata: Do not include .atom feed links for remote accounts
- Bumped `fast_html` to v2.3.0, which notably allows to use system-installed lexbor with passing `WITH_SYSTEM_LEXBOR=1` environment variable at build-time - Bumped `fast_html` to v2.3.0, which notably allows to use system-installed lexbor with passing `WITH_SYSTEM_LEXBOR=1` environment variable at build-time
- Dedupe upload filter now uses a three-level sharding directory structure - Dedupe upload filter now uses a three-level sharding directory structure
@ -71,6 +183,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Worker configuration is no longer available. This only affects custom max_retries values for a couple Oban queues. - Worker configuration is no longer available. This only affects custom max_retries values for a couple Oban queues.
### Added ### Added
- Add metadata provider for ActivityPub alternate links - Add metadata provider for ActivityPub alternate links
- Added support for argon2 passwords and their conversion for migration from Akkoma fork to upstream. - Added support for argon2 passwords and their conversion for migration from Akkoma fork to upstream.
- Respect :restrict_unauthenticated for hashtag rss/atom feeds - Respect :restrict_unauthenticated for hashtag rss/atom feeds
@ -88,6 +201,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Include session scopes in TokenView - Include session scopes in TokenView
### Fixed ### Fixed
- Verify a local Update sent through AP C2S so users can only update their own objects - Verify a local Update sent through AP C2S so users can only update their own objects
- Fixed malformed follow requests that cause them to appear stuck pending due to the recipient being unable to process them. - Fixed malformed follow requests that cause them to appear stuck pending due to the recipient being unable to process them.
- Fix incoming Block activities being rejected - Fix incoming Block activities being rejected
@ -105,14 +219,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Make vapid_config return empty array, fixing preloading for instances without push notifications configured - Make vapid_config return empty array, fixing preloading for instances without push notifications configured
### Removed ### Removed
- Remove stub for /api/v1/accounts/:id/identity_proofs (deprecated by Mastodon 3.5.0) - Remove stub for /api/v1/accounts/:id/identity_proofs (deprecated by Mastodon 3.5.0)
## 2.7.1 ## 2.7.1
### Changed ### Changed
- Accept `application/activity+json` for requests to `/.well-known/nodeinfo` - Accept `application/activity+json` for requests to `/.well-known/nodeinfo`
### Fixed ### Fixed
- Truncate remote user fields, avoids them getting rejected - Truncate remote user fields, avoids them getting rejected
- Improve the `FollowValidator` to successfully incoming activities with an errant `cc` field. - Improve the `FollowValidator` to successfully incoming activities with an errant `cc` field.
- Resolved edge case where the API can report you are following a user but the relationship is not fully established. - Resolved edge case where the API can report you are following a user but the relationship is not fully established.
@ -122,16 +239,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2.7.0 ## 2.7.0
### Security ### Security
- HTTP Security: By default, don't allow unsafe-eval. The setting needs to be changed to allow Flash emulation. - HTTP Security: By default, don't allow unsafe-eval. The setting needs to be changed to allow Flash emulation.
- Fix webfinger spoofing. - Fix webfinger spoofing.
- Use proper workers for fetching pins instead of an ad-hoc task, fixing a potential fetch loop - Use proper workers for fetching pins instead of an ad-hoc task, fixing a potential fetch loop
### Changed ### Changed
- Update to Phoenix 1.7 - Update to Phoenix 1.7
- Elixir Logger configuration is now longer permitted through AdminFE and ConfigDB - Elixir Logger configuration is now longer permitted through AdminFE and ConfigDB
- Refactor the user backups code and improve test coverage - Refactor the user backups code and improve test coverage
- Invalid activities delivered to the inbox will be rejected with a 400 Bad Request - Invalid activities delivered to the inbox will be rejected with a 400 Bad Request
- Support Bandit as an alternative to Cowboy for the HTTP server. - Support Bandit as an alternative to Cowboy for the HTTP server.
- Update Bandit to 1.5.2 - Update Bandit to 1.5.2
- Replace eblurhash with rinpatch_blurhash. This also removes a dependency on ImageMagick. - Replace eblurhash with rinpatch_blurhash. This also removes a dependency on ImageMagick.
- Elixir 1.13 is the minimum required version. - Elixir 1.13 is the minimum required version.
@ -170,6 +289,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Refactor the Mastodon /api/v1/streaming websocket handler to use Phoenix.Socket.Transport - Refactor the Mastodon /api/v1/streaming websocket handler to use Phoenix.Socket.Transport
### Added ### Added
- Uploader: Add support for uploading attachments using IPFS - Uploader: Add support for uploading attachments using IPFS
- Add NSFW-detecting MRF - Add NSFW-detecting MRF
- Add DNSRBL MRF - Add DNSRBL MRF
@ -215,6 +335,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Support honk-style attachment summaries as alt-text. - Support honk-style attachment summaries as alt-text.
### Fixed ### Fixed
- Fix Emoji object IDs not always being valid - Fix Emoji object IDs not always being valid
- Remove checking ImageMagick's commands for Pleroma.Upload.Filter.AnalyzeMetadata - Remove checking ImageMagick's commands for Pleroma.Upload.Filter.AnalyzeMetadata
- Ensure that StripLocation actually removes everything resembling GPS data from PNGs - Ensure that StripLocation actually removes everything resembling GPS data from PNGs
@ -257,7 +378,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix Optimistic Inbox for failed signatures - Fix Optimistic Inbox for failed signatures
- MediaProxy Preview failures prevented when encountering certain video files - MediaProxy Preview failures prevented when encountering certain video files
- pleroma_ctl: Use realpath(1) instead of readlink(1) - pleroma_ctl: Use realpath(1) instead of readlink(1)
- ReceiverWorker: Make sure non-{:ok, _} is returned as {:error, …} - ReceiverWorker: Make sure non-{:ok, \_} is returned as {:error, …}
- Harden Rich Media parsing against very slow or malicious URLs - Harden Rich Media parsing against very slow or malicious URLs
- Rich Media Preview cache eviction when the activity is updated. - Rich Media Preview cache eviction when the activity is updated.
- Parsing of RichMedia TTLs for Amazon URLs when query parameters are nil - Parsing of RichMedia TTLs for Amazon URLs when query parameters are nil
@ -269,32 +390,41 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix validate_webfinger when running a different domain for Webfinger - Fix validate_webfinger when running a different domain for Webfinger
### Removed ### Removed
- Mastodon API: Remove deprecated GET /api/v1/statuses/:id/card endpoint https://github.com/mastodon/mastodon/pull/11213 - Mastodon API: Remove deprecated GET /api/v1/statuses/:id/card endpoint https://github.com/mastodon/mastodon/pull/11213
- Removed support for multiple federator modules as we only support ActivityPub - Removed support for multiple federator modules as we only support ActivityPub
## 2.6.2 ## 2.6.2
### Security ### Security
- MRF StealEmojiPolicy: Sanitize shortcodes (thanks to Hazel K for the report - MRF StealEmojiPolicy: Sanitize shortcodes (thanks to Hazel K for the report
## 2.6.1 ## 2.6.1
### Changed ### Changed
- - Document maximum supported version of Erlang & Elixir - - Document maximum supported version of Erlang & Elixir
### Added ### Added
- [docs] add frontends management documentation - [docs] add frontends management documentation
### Fixed ### Fixed
- TwitterAPI: Return proper error when healthcheck is disabled - TwitterAPI: Return proper error when healthcheck is disabled
- Fix eblurhash and elixir-captcha not using system cflags - Fix eblurhash and elixir-captcha not using system cflags
## 2.6.0 ## 2.6.0
### Security ### Security
- Preload: Make generated JSON html-safe. It already was html safe because it only consists of config data that is base64 encoded, but this will keep it safe it that ever changes. - Preload: Make generated JSON html-safe. It already was html safe because it only consists of config data that is base64 encoded, but this will keep it safe it that ever changes.
- CommonAPI: Prevent users from accessing media of other users by creating a status with reused attachment ID - CommonAPI: Prevent users from accessing media of other users by creating a status with reused attachment ID
- Disable XML entity resolution completely to fix a dos vulnerability - Disable XML entity resolution completely to fix a dos vulnerability
### Added ### Added
- Support for Image activities, namely from Hubzilla - Support for Image activities, namely from Hubzilla
- Add OAuth scope descriptions - Add OAuth scope descriptions
- Allow lang attribute in status text - Allow lang attribute in status text
@ -305,6 +435,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Add unified streaming endpoint - Add unified streaming endpoint
### Fixed ### Fixed
- rel="me" was missing its cache - rel="me" was missing its cache
- MediaProxy responses now return a sandbox CSP header - MediaProxy responses now return a sandbox CSP header
- Filter context activities using Visibility.visible_for_user? - Filter context activities using Visibility.visible_for_user?
@ -326,6 +457,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Show more informative errors when profile exceeds char limits - Show more informative errors when profile exceeds char limits
### Removed ### Removed
- BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact) - BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact)
- remove BBS/SSH feature, replaced by an external bridge. - remove BBS/SSH feature, replaced by an external bridge.
- Remove a few unused indexes. - Remove a few unused indexes.
@ -335,56 +467,67 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2.5.4 ## 2.5.4
## Security ## Security
- Fix XML External Entity (XXE) loading vulnerability allowing to fetch arbitrary files from the server's filesystem - Fix XML External Entity (XXE) loading vulnerability allowing to fetch arbitrary files from the server's filesystem
## 2.5.3 ## 2.5.3
### Security ### Security
- Emoji pack loader sanitizes pack names - Emoji pack loader sanitizes pack names
- Reduced permissions of config files and directories, distros requiring greater permissions like group-read need to pre-create the directories - Reduced permissions of config files and directories, distros requiring greater permissions like group-read need to pre-create the directories
## 2.5.5 ## 2.5.5
## Security ## Security
- Prevent users from accessing media of other users by creating a status with reused attachment ID - Prevent users from accessing media of other users by creating a status with reused attachment ID
## 2.5.4 ## 2.5.4
## Security ## Security
- Fix XML External Entity (XXE) loading vulnerability allowing to fetch arbitrary files from the server's filesystem - Fix XML External Entity (XXE) loading vulnerability allowing to fetch arbitrary files from the server's filesystem
## 2.5.3 ## 2.5.3
### Security ### Security
- Emoji pack loader sanitizes pack names - Emoji pack loader sanitizes pack names
- Reduced permissions of config files and directories, distros requiring greater permissions like group-read need to pre-create the directories - Reduced permissions of config files and directories, distros requiring greater permissions like group-read need to pre-create the directories
## 2.5.2 ## 2.5.2
### Security ### Security
- `/proxy` endpoint now sets a Content-Security-Policy (sandbox) - `/proxy` endpoint now sets a Content-Security-Policy (sandbox)
- WebSocket endpoint now respects unauthenticated restrictions for streams of public posts - WebSocket endpoint now respects unauthenticated restrictions for streams of public posts
- OEmbed HTML tags are now filtered - OEmbed HTML tags are now filtered
### Changed ### Changed
- docs: Be more explicit about the level of compatibility of OTP releases - docs: Be more explicit about the level of compatibility of OTP releases
- Set default background worker timeout to 15 minutes - Set default background worker timeout to 15 minutes
### Fixed ### Fixed
- Atom/RSS formatting (HTML truncation, published, missing summary) - Atom/RSS formatting (HTML truncation, published, missing summary)
- Remove `static_fe` pipeline for `/users/:nickname/feed` - Remove `static_fe` pipeline for `/users/:nickname/feed`
- Stop oban from retrying if validating errors occur when processing incoming data - Stop oban from retrying if validating errors occur when processing incoming data
- Make sure object refetching as used by already received polls follows MRF rules - Make sure object refetching as used by already received polls follows MRF rules
### Removed ### Removed
- BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact) - BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact)
## 2.5.1 ## 2.5.1
### Added ### Added
- Allow customizing instance languages - Allow customizing instance languages
### Fixed ### Fixed
- Security: uploading HTTP endpoint can no longer create directories in the upload dir (internal APIs, like backup, still can do it.) - Security: uploading HTTP endpoint can no longer create directories in the upload dir (internal APIs, like backup, still can do it.)
- ~ character in urls in Markdown posts are handled properly - ~ character in urls in Markdown posts are handled properly
- Exiftool upload filter will now ignore SVG files - Exiftool upload filter will now ignore SVG files
@ -405,6 +548,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Quack, the logging backend that pushes to Slack channels - Quack, the logging backend that pushes to Slack channels
### Changed ### Changed
- **Breaking:** Elixir >=1.11 is now required (was >= 1.9) - **Breaking:** Elixir >=1.11 is now required (was >= 1.9)
- Allow users to remove their emails if instance does not need email to register - Allow users to remove their emails if instance does not need email to register
- Uploadfilter `Pleroma.Upload.Filter.Exiftool` has been renamed to `Pleroma.Upload.Filter.Exiftool.StripLocation` - Uploadfilter `Pleroma.Upload.Filter.Exiftool` has been renamed to `Pleroma.Upload.Filter.Exiftool.StripLocation`
@ -415,6 +559,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- CSP now includes wasm-unsafe-eval - CSP now includes wasm-unsafe-eval
### Added ### Added
- `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object - `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object
- Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney. - Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney.
- `ForceMentionsInPostContent` MRF policy - `ForceMentionsInPostContent` MRF policy
@ -436,6 +581,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Possibility to discover users like `user@example.org`, while Pleroma is working on `pleroma.example.org`. Additional configuration required. - Possibility to discover users like `user@example.org`, while Pleroma is working on `pleroma.example.org`. Additional configuration required.
### Fixed ### Fixed
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
- Handle Reject for already-accepted Follows properly - Handle Reject for already-accepted Follows properly
- Display OpenGraph data on alternative notice routes. - Display OpenGraph data on alternative notice routes.
@ -458,6 +604,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2.4.5 - 2022-11-27 ## 2.4.5 - 2022-11-27
## Fixed ## Fixed
- Image `class` attributes not being scrubbed, allowing to exploit frontend special classes [!3792](https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3792) - Image `class` attributes not being scrubbed, allowing to exploit frontend special classes [!3792](https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3792)
- Delete report notifs when demoting from superuser [!3642](https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3642) - Delete report notifs when demoting from superuser [!3642](https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3642)
- Validate `mediaType` only by it's format rather than using a list [!3597](https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3597) - Validate `mediaType` only by it's format rather than using a list [!3597](https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3597)
@ -472,17 +619,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2.4.4 - 2022-08-19 ## 2.4.4 - 2022-08-19
### Security ### Security
- Streaming API sessions will now properly disconnect if the corresponding token is revoked - Streaming API sessions will now properly disconnect if the corresponding token is revoked
## 2.4.3 - 2022-05-06 ## 2.4.3 - 2022-05-06
### Security ### Security
- Private `/objects/` and `/activities/` leaking if cached by authenticated user - Private `/objects/` and `/activities/` leaking if cached by authenticated user
- SweetXML library DTD bomb - SweetXML library DTD bomb
## 2.4.2 - 2022-01-10 ## 2.4.2 - 2022-01-10
### Fixed ### Fixed
- Federation issues caused by HTTP pool checkout timeouts - Federation issues caused by HTTP pool checkout timeouts
- Compatibility with Elixir 1.13 - Compatibility with Elixir 1.13
@ -493,12 +643,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2.4.1 - 2021-08-29 ## 2.4.1 - 2021-08-29
### Changed ### Changed
- Make `mix pleroma.database set_text_search_config` run concurrently and indefinitely - Make `mix pleroma.database set_text_search_config` run concurrently and indefinitely
### Added ### Added
- AdminAPI: Missing configuration description for StealEmojiPolicy - AdminAPI: Missing configuration description for StealEmojiPolicy
### Fixed ### Fixed
- MastodonAPI: Stream out Create activities - MastodonAPI: Stream out Create activities
- MRF ObjectAgePolicy: Fix pattern matching on "published" - MRF ObjectAgePolicy: Fix pattern matching on "published"
- TwitterAPI: Make `change_password` and `change_email` require params on body instead of query - TwitterAPI: Make `change_password` and `change_email` require params on body instead of query
@ -537,6 +690,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Pinned posts federation - Pinned posts federation
### Fixed ### Fixed
- Don't crash so hard when email settings are invalid. - Don't crash so hard when email settings are invalid.
- Checking activated Upload Filters for required commands. - Checking activated Upload Filters for required commands.
- Remote users can no longer reappear after being deleted. - Remote users can no longer reappear after being deleted.
@ -554,6 +708,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fixed some Markdown issues, including trailing slash in links. - Fixed some Markdown issues, including trailing slash in links.
### Removed ### Removed
- **Breaking**: Remove deprecated `/api/qvitter/statuses/notifications/read` (replaced by `/api/v1/pleroma/notifications/read`) - **Breaking**: Remove deprecated `/api/qvitter/statuses/notifications/read` (replaced by `/api/v1/pleroma/notifications/read`)
## [2.3.0] - 2021-03-01 ## [2.3.0] - 2021-03-01
@ -672,6 +827,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [2.2.1] - 2020-12-22 ## [2.2.1] - 2020-12-22
### Changed ### Changed
- Updated Pleroma FE - Updated Pleroma FE
### Fixed ### Fixed
@ -724,7 +880,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- **Breaking:** `Pleroma.Workers.Cron.ClearOauthTokenWorker` setting from Oban `:crontab` (moved to scheduled jobs). - **Breaking:** `Pleroma.Workers.Cron.ClearOauthTokenWorker` setting from Oban `:crontab` (moved to scheduled jobs).
- **Breaking:** `Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker` setting from Oban `:crontab` (moved to scheduled jobs). - **Breaking:** `Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker` setting from Oban `:crontab` (moved to scheduled jobs).
- Removed `:managed_config` option. In practice, it was accidentally removed with 2.0.0 release when frontends were - Removed `:managed_config` option. In practice, it was accidentally removed with 2.0.0 release when frontends were
switched to a new configuration mechanism, however it was not officially removed until now. switched to a new configuration mechanism, however it was not officially removed until now.
### Added ### Added
@ -755,8 +911,10 @@ switched to a new configuration mechanism, however it was not officially removed
1. Install libmagic and development headers (`libmagic-dev` on Ubuntu/Debian, `file-dev` on Alpine Linux) 1. Install libmagic and development headers (`libmagic-dev` on Ubuntu/Debian, `file-dev` on Alpine Linux)
2. Run database migrations (inside Pleroma directory): 2. Run database migrations (inside Pleroma directory):
- OTP: `./bin/pleroma_ctl migrate`
- From Source: `mix ecto.migrate` - OTP: `./bin/pleroma_ctl migrate`
- From Source: `mix ecto.migrate`
3. Restart Pleroma 3. Restart Pleroma
## [2.1.2] - 2020-09-17 ## [2.1.2] - 2020-09-17
@ -784,6 +942,7 @@ switched to a new configuration mechanism, however it was not officially removed
## [2.1.1] - 2020-09-08 ## [2.1.1] - 2020-09-08
### Security ### Security
- Fix possible DoS in Mastodon API user search due to an error in match clauses, leading to an infinite recursion and subsequent OOM with certain inputs. - Fix possible DoS in Mastodon API user search due to an error in match clauses, leading to an infinite recursion and subsequent OOM with certain inputs.
- Fix metadata leak for accounts and statuses on private instances. - Fix metadata leak for accounts and statuses on private instances.
- Fix possible DoS in Admin API search using an atom leak vulnerability. Authentication with admin rights was required to exploit. - Fix possible DoS in Admin API search using an atom leak vulnerability. Authentication with admin rights was required to exploit.
@ -794,6 +953,7 @@ switched to a new configuration mechanism, however it was not officially removed
- Improved error message when cmake is not available at build stage. - Improved error message when cmake is not available at build stage.
### Added ### Added
- Rich media failure tracking (along with `:failure_backoff` option). - Rich media failure tracking (along with `:failure_backoff` option).
<details> <details>
@ -803,6 +963,7 @@ switched to a new configuration mechanism, however it was not officially removed
</details> </details>
### Fixed ### Fixed
- Default HTTP adapter not respecting pool setting, leading to possible OOM. - Default HTTP adapter not respecting pool setting, leading to possible OOM.
- Fixed uploading webp images when the Exiftool Upload Filter is enabled by skipping them - Fixed uploading webp images when the Exiftool Upload Filter is enabled by skipping them
- Mastodon API: Search parameter `following` now correctly returns the followings rather than the followers - Mastodon API: Search parameter `following` now correctly returns the followings rather than the followers
@ -908,6 +1069,7 @@ switched to a new configuration mechanism, however it was not officially removed
</details> </details>
### Fixed ### Fixed
- Fix list pagination and other list issues. - Fix list pagination and other list issues.
- Support pagination in conversations API - Support pagination in conversations API
- **Breaking**: SimplePolicy `:reject` and `:accept` allow deletions again - **Breaking**: SimplePolicy `:reject` and `:accept` allow deletions again
@ -928,9 +1090,11 @@ switched to a new configuration mechanism, however it was not officially removed
## [2.0.7] - 2020-06-13 ## [2.0.7] - 2020-06-13
### Security ### Security
- Fix potential DoSes exploiting atom leaks in rich media parser and the `UserAllowListPolicy` MRF policy - Fix potential DoSes exploiting atom leaks in rich media parser and the `UserAllowListPolicy` MRF policy
### Fixed ### Fixed
- CSP: not allowing images/media from every host when mediaproxy is disabled - CSP: not allowing images/media from every host when mediaproxy is disabled
- CSP: not adding mediaproxy base url to image/media hosts - CSP: not adding mediaproxy base url to image/media hosts
- StaticFE missing the CSS file - StaticFE missing the CSS file
@ -942,28 +1106,36 @@ switched to a new configuration mechanism, however it was not officially removed
## [2.0.6] - 2020-06-09 ## [2.0.6] - 2020-06-09
### Security ### Security
- CSP: harden `image-src` and `media-src` when MediaProxy is used - CSP: harden `image-src` and `media-src` when MediaProxy is used
### Fixed ### Fixed
- AP C2S: Fix pagination in inbox/outbox - AP C2S: Fix pagination in inbox/outbox
- Various compilation errors on OTP 23 - Various compilation errors on OTP 23
- Mastodon API streaming: Repeats from muted threads not being filtered - Mastodon API streaming: Repeats from muted threads not being filtered
### Changed ### Changed
- Various database performance improvements - Various database performance improvements
### Upgrade notes ### Upgrade notes
1. Run database migrations (inside Pleroma directory): 1. Run database migrations (inside Pleroma directory):
- OTP: `./bin/pleroma_ctl migrate`
- From Source: `mix ecto.migrate` - OTP: `./bin/pleroma_ctl migrate`
- From Source: `mix ecto.migrate`
2. Restart Pleroma 2. Restart Pleroma
## [2.0.5] - 2020-05-13 ## [2.0.5] - 2020-05-13
### Security ### Security
- Fix possible private status leaks in Mastodon Streaming API - Fix possible private status leaks in Mastodon Streaming API
### Fixed ### Fixed
- Crashes when trying to block a user if block federation is disabled - Crashes when trying to block a user if block federation is disabled
- Not being able to start the instance without `erlang-eldap` installed - Not being able to start the instance without `erlang-eldap` installed
- Users with bios over the limit getting rejected - Users with bios over the limit getting rejected
@ -976,9 +1148,11 @@ switched to a new configuration mechanism, however it was not officially removed
## [2.0.4] - 2020-05-10 ## [2.0.4] - 2020-05-10
### Security ### Security
- AP C2S: Fix a potential DoS by creating nonsensical objects that break timelines - AP C2S: Fix a potential DoS by creating nonsensical objects that break timelines
### Fixed ### Fixed
- Peertube user lookups not working - Peertube user lookups not working
- `InsertSkeletonsForDeletedUsers` migration failing on some instances - `InsertSkeletonsForDeletedUsers` migration failing on some instances
- Healthcheck reporting the number of memory currently used, rather than allocated in total - Healthcheck reporting the number of memory currently used, rather than allocated in total
@ -990,6 +1164,7 @@ switched to a new configuration mechanism, however it was not officially removed
#### Apache only #### Apache only
1. Remove the following line from your config: 1. Remove the following line from your config:
``` ```
SSLCertificateFile /etc/letsencrypt/live/${servername}/cert.pem SSLCertificateFile /etc/letsencrypt/live/${servername}/cert.pem
``` ```
@ -1001,11 +1176,13 @@ switched to a new configuration mechanism, however it was not officially removed
## [2.0.3] - 2020-05-02 ## [2.0.3] - 2020-05-02
### Security ### Security
- Disallow re-registration of previously deleted users, which allowed viewing direct messages addressed to them - Disallow re-registration of previously deleted users, which allowed viewing direct messages addressed to them
- Mastodon API: Fix `POST /api/v1/follow_requests/:id/authorize` allowing to force a follow from a local user even if they didn't request to follow - Mastodon API: Fix `POST /api/v1/follow_requests/:id/authorize` allowing to force a follow from a local user even if they didn't request to follow
- CSP: Sandbox uploads - CSP: Sandbox uploads
### Fixed ### Fixed
- Notifications from blocked domains - Notifications from blocked domains
- Potential federation issues with Mastodon versions before 3.0.0 - Potential federation issues with Mastodon versions before 3.0.0
- HTTP Basic Authentication permissions issue - HTTP Basic Authentication permissions issue
@ -1016,6 +1193,7 @@ switched to a new configuration mechanism, however it was not officially removed
- `blob:` urls not being allowed by CSP - `blob:` urls not being allowed by CSP
### Added ### Added
- NodeInfo: ObjectAgePolicy settings to the `federation` list. - NodeInfo: ObjectAgePolicy settings to the `federation` list.
- Follow request notifications - Follow request notifications
<details> <details>
@ -1027,19 +1205,24 @@ switched to a new configuration mechanism, however it was not officially removed
1. Restart Pleroma 1. Restart Pleroma
2. Run database migrations (inside Pleroma directory): 2. Run database migrations (inside Pleroma directory):
- OTP: `./bin/pleroma_ctl migrate`
- From Source: `mix ecto.migrate`
3. Reset status visibility counters (inside Pleroma directory):
- OTP: `./bin/pleroma_ctl refresh_counter_cache`
- From Source: `mix pleroma.refresh_counter_cache`
- OTP: `./bin/pleroma_ctl migrate`
- From Source: `mix ecto.migrate`
3. Reset status visibility counters (inside Pleroma directory):
- OTP: `./bin/pleroma_ctl refresh_counter_cache`
- From Source: `mix pleroma.refresh_counter_cache`
## [2.0.2] - 2020-04-08 ## [2.0.2] - 2020-04-08
### Added ### Added
- Support for Funkwhale's `Audio` activity - Support for Funkwhale's `Audio` activity
- Admin API: `PATCH /api/pleroma/admin/users/:nickname/update_credentials` - Admin API: `PATCH /api/pleroma/admin/users/:nickname/update_credentials`
### Fixed ### Fixed
- Blocked/muted users still generating push notifications - Blocked/muted users still generating push notifications
- Input textbox for bio ignoring newlines - Input textbox for bio ignoring newlines
- OTP: Inability to use PostgreSQL databases with SSL - OTP: Inability to use PostgreSQL databases with SSL
@ -1047,13 +1230,17 @@ switched to a new configuration mechanism, however it was not officially removed
- Incorrect URL for Funkwhale channels - Incorrect URL for Funkwhale channels
### Upgrade notes ### Upgrade notes
1. Restart Pleroma 1. Restart Pleroma
## [2.0.1] - 2020-03-15 ## [2.0.1] - 2020-03-15
### Security ### Security
- Static-FE: Fix remote posts not being sanitized - Static-FE: Fix remote posts not being sanitized
### Fixed ### Fixed
- Rate limiter crashes when there is no explicitly specified ip in the config - Rate limiter crashes when there is no explicitly specified ip in the config
- 500 errors when no `Accept` header is present if Static-FE is enabled - 500 errors when no `Accept` header is present if Static-FE is enabled
- Instance panel not being updated immediately due to wrong `Cache-Control` headers - Instance panel not being updated immediately due to wrong `Cache-Control` headers
@ -1064,24 +1251,33 @@ switched to a new configuration mechanism, however it was not officially removed
- Mastodon Streaming API: hashtag timelines not working - Mastodon Streaming API: hashtag timelines not working
### Changed ### Changed
- BBCode and Markdown formatters will no longer return any `\n` and only use `<br/>` for newlines - BBCode and Markdown formatters will no longer return any `\n` and only use `<br/>` for newlines
- Mastodon API: Allow registration without email if email verification is not enabled - Mastodon API: Allow registration without email if email verification is not enabled
### Upgrade notes ### Upgrade notes
#### Nginx only #### Nginx only
1. Remove `proxy_ignore_headers Cache-Control;` and `proxy_hide_header Cache-Control;` from your config. 1. Remove `proxy_ignore_headers Cache-Control;` and `proxy_hide_header Cache-Control;` from your config.
#### Everyone #### Everyone
1. Run database migrations (inside Pleroma directory): 1. Run database migrations (inside Pleroma directory):
- OTP: `./bin/pleroma_ctl migrate`
- From Source: `mix ecto.migrate` - OTP: `./bin/pleroma_ctl migrate`
- From Source: `mix ecto.migrate`
2. Restart Pleroma 2. Restart Pleroma
## [2.0.0] - 2019-03-08 ## [2.0.0] - 2019-03-08
### Security ### Security
- Mastodon API: Fix being able to request enormous amount of statuses in timelines leading to DoS. Now limited to 40 per request. - Mastodon API: Fix being able to request enormous amount of statuses in timelines leading to DoS. Now limited to 40 per request.
### Removed ### Removed
- **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media` - **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media`
- **Breaking**: OStatus protocol support - **Breaking**: OStatus protocol support
- **Breaking**: MDII uploader - **Breaking**: MDII uploader
@ -1093,6 +1289,7 @@ switched to a new configuration mechanism, however it was not officially removed
</details> </details>
### Changed ### Changed
- **Breaking:** Pleroma won't start if it detects unapplied migrations - **Breaking:** Pleroma won't start if it detects unapplied migrations
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7) - **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
- **Breaking:** `Pleroma.Plugs.RemoteIp` and `:rate_limiter` enabled by default. Please ensure your reverse proxy forwards the real IP! - **Breaking:** `Pleroma.Plugs.RemoteIp` and `:rate_limiter` enabled by default. Please ensure your reverse proxy forwards the real IP!
@ -1142,6 +1339,7 @@ switched to a new configuration mechanism, however it was not officially removed
</details> </details>
### Added ### Added
- `:chat_limit` option to limit chat characters. - `:chat_limit` option to limit chat characters.
- `cleanup_attachments` option to remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances. - `cleanup_attachments` option to remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances.
- Refreshing poll results for remote polls - Refreshing poll results for remote polls
@ -1209,6 +1407,7 @@ switched to a new configuration mechanism, however it was not officially removed
</details> </details>
### Fixed ### Fixed
- Report emails now include functional links to profiles of remote user accounts - Report emails now include functional links to profiles of remote user accounts
- Not being able to log in to some third-party apps when logged in to MastoFE - Not being able to log in to some third-party apps when logged in to MastoFE
- MRF: `Delete` activities being exempt from MRF policies - MRF: `Delete` activities being exempt from MRF policies
@ -1228,7 +1427,9 @@ switched to a new configuration mechanism, however it was not officially removed
</details> </details>
## [1.1.9] - 2020-02-10 ## [1.1.9] - 2020-02-10
### Fixed ### Fixed
- OTP: Inability to set the upload limit (again) - OTP: Inability to set the upload limit (again)
- Not being able to pin polls - Not being able to pin polls
- Streaming API: incorrect handling of reblog mutes - Streaming API: incorrect handling of reblog mutes
@ -1236,98 +1437,132 @@ switched to a new configuration mechanism, however it was not officially removed
- OpenGraph provider: html entities in descriptions - OpenGraph provider: html entities in descriptions
## [1.1.8] - 2020-01-10 ## [1.1.8] - 2020-01-10
### Fixed ### Fixed
- Captcha generation issues - Captcha generation issues
- Returned Kocaptcha endpoint to configuration - Returned Kocaptcha endpoint to configuration
- Captcha validity is now 5 minutes - Captcha validity is now 5 minutes
## [1.1.7] - 2019-12-13 ## [1.1.7] - 2019-12-13
### Fixed ### Fixed
- OTP: Inability to set the upload limit - OTP: Inability to set the upload limit
- OTP: Inability to override node name/distribution type to run 2 Pleroma instances on the same machine - OTP: Inability to override node name/distribution type to run 2 Pleroma instances on the same machine
### Added ### Added
- Integrated captcha provider - Integrated captcha provider
### Changed ### Changed
- Captcha enabled by default - Captcha enabled by default
- Default Captcha provider changed from `Pleroma.Captcha.Kocaptcha` to `Pleroma.Captcha.Native` - Default Captcha provider changed from `Pleroma.Captcha.Kocaptcha` to `Pleroma.Captcha.Native`
- Better `Cache-Control` header for static content - Better `Cache-Control` header for static content
### Bundled Pleroma-FE Changes ### Bundled Pleroma-FE Changes
#### Added #### Added
- Icons in the navigation panel - Icons in the navigation panel
#### Fixed #### Fixed
- Improved support unauthenticated view of private instances - Improved support unauthenticated view of private instances
#### Removed #### Removed
- Whitespace hack on empty post content - Whitespace hack on empty post content
## [1.1.6] - 2019-11-19 ## [1.1.6] - 2019-11-19
### Fixed ### Fixed
- Not being able to log into to third party apps when the browser is logged into mastofe - Not being able to log into to third party apps when the browser is logged into mastofe
- Email confirmation not being required even when enabled - Email confirmation not being required even when enabled
- Mastodon API: conversations API crashing when one status is malformed - Mastodon API: conversations API crashing when one status is malformed
### Bundled Pleroma-FE Changes ### Bundled Pleroma-FE Changes
#### Added #### Added
- About page - About page
- Meme arrows - Meme arrows
#### Fixed #### Fixed
- Image modal not closing unless clicked outside of image - Image modal not closing unless clicked outside of image
- Attachment upload spinner not being centered - Attachment upload spinner not being centered
- Showing follow counters being 0 when they are actually hidden - Showing follow counters being 0 when they are actually hidden
## [1.1.5] - 2019-11-09 ## [1.1.5] - 2019-11-09
### Fixed ### Fixed
- Polls having different numbers in timelines/notifications/poll api endpoints due to cache desyncronization - Polls having different numbers in timelines/notifications/poll api endpoints due to cache desyncronization
- Pleroma API: OAuth token endpoint not being found when ".json" suffix is appended - Pleroma API: OAuth token endpoint not being found when ".json" suffix is appended
### Changed ### Changed
- Frontend bundle updated to [044c9ad0](https://git.pleroma.social/pleroma/pleroma-fe/commit/044c9ad0562af059dd961d50961a3880fca9c642) - Frontend bundle updated to [044c9ad0](https://git.pleroma.social/pleroma/pleroma-fe/commit/044c9ad0562af059dd961d50961a3880fca9c642)
## [1.1.4] - 2019-11-01 ## [1.1.4] - 2019-11-01
### Fixed ### Fixed
- Added a migration that fills up empty user.info fields to prevent breakage after previous unsafe migrations. - Added a migration that fills up empty user.info fields to prevent breakage after previous unsafe migrations.
- Failure to migrate from pre-1.0.0 versions - Failure to migrate from pre-1.0.0 versions
- Mastodon API: Notification stream not including follow notifications - Mastodon API: Notification stream not including follow notifications
## [1.1.3] - 2019-10-25 ## [1.1.3] - 2019-10-25
### Fixed ### Fixed
- Blocked users showing up in notifications collapsed as if they were muted - Blocked users showing up in notifications collapsed as if they were muted
- `pleroma_ctl` not working on Debian's default shell - `pleroma_ctl` not working on Debian's default shell
## [1.1.2] - 2019-10-18 ## [1.1.2] - 2019-10-18
### Fixed ### Fixed
- `pleroma_ctl` trying to connect to a running instance when generating the config, which of course doesn't exist. - `pleroma_ctl` trying to connect to a running instance when generating the config, which of course doesn't exist.
## [1.1.1] - 2019-10-18 ## [1.1.1] - 2019-10-18
### Fixed ### Fixed
- One of the migrations between 1.0.0 and 1.1.0 wiping user info of the relay user because of unexpected behavior of postgresql's `jsonb_set`, resulting in inability to post in the default configuration. If you were affected, please run the following query in postgres console, the relay user will be recreated automatically: - One of the migrations between 1.0.0 and 1.1.0 wiping user info of the relay user because of unexpected behavior of postgresql's `jsonb_set`, resulting in inability to post in the default configuration. If you were affected, please run the following query in postgres console, the relay user will be recreated automatically:
``` ```
delete from users where ap_id = 'https://your.instance.hostname/relay'; delete from users where ap_id = 'https://your.instance.hostname/relay';
``` ```
- Bad user search matches - Bad user search matches
## [1.1.0] - 2019-10-14 ## [1.1.0] - 2019-10-14
**Breaking:** The stable branch has been changed from `master` to `stable`. If you want to keep using 1.0, the `release/1.0` branch will receive security updates for 6 months after 1.1 release. **Breaking:** The stable branch has been changed from `master` to `stable`. If you want to keep using 1.0, the `release/1.0` branch will receive security updates for 6 months after 1.1 release.
**OTP Note:** `pleroma_ctl` in 1.0 defaults to `master` and doesn't support specifying arbitrary branches, making `./pleroma_ctl update` fail. To fix this, fetch a version of `pleroma_ctl` from 1.1 using the command below and proceed with the update normally: **OTP Note:** `pleroma_ctl` in 1.0 defaults to `master` and doesn't support specifying arbitrary branches, making `./pleroma_ctl update` fail. To fix this, fetch a version of `pleroma_ctl` from 1.1 using the command below and proceed with the update normally:
``` ```
curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/develop/rel/files/bin/pleroma_ctl' curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/develop/rel/files/bin/pleroma_ctl'
``` ```
### Security ### Security
- Mastodon API: respect post privacy in `/api/v1/statuses/:id/{favourited,reblogged}_by` - Mastodon API: respect post privacy in `/api/v1/statuses/:id/{favourited,reblogged}_by`
### Removed ### Removed
- **Breaking:** GNU Social API with Qvitter extensions support - **Breaking:** GNU Social API with Qvitter extensions support
- Emoji: Remove longfox emojis. - Emoji: Remove longfox emojis.
- Remove `Reply-To` header from report emails for admins. - Remove `Reply-To` header from report emails for admins.
- ActivityPub: The `/objects/:uuid/likes` endpoint. - ActivityPub: The `/objects/:uuid/likes` endpoint.
### Changed ### Changed
- **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config - **Breaking:** Configuration: A setting to explicitly disable the mailer was added, defaulting to true, if you are using a mailer add `config :pleroma, Pleroma.Emails.Mailer, enabled: true` to your config
- **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired - **Breaking:** Configuration: `/media/` is now removed when `base_url` is configured, append `/media/` to your `base_url` config to keep the old behaviour if desired
- **Breaking:** `/api/pleroma/notifications/read` is moved to `/api/v1/pleroma/notifications/read` and now supports `max_id` and responds with Mastodon API entities. - **Breaking:** `/api/pleroma/notifications/read` is moved to `/api/v1/pleroma/notifications/read` and now supports `max_id` and responds with Mastodon API entities.
@ -1341,10 +1576,11 @@ curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/devel
- Mastodon API: `pleroma.thread_muted` key in the Status entity - Mastodon API: `pleroma.thread_muted` key in the Status entity
- AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private statuses) - AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private statuses)
- Improve digest email template - Improve digest email template
Pagination: (optional) return `total` alongside with `items` when paginating Pagination: (optional) return `total` alongside with `items` when paginating
- The `Pleroma.FlakeId` module has been replaced with the `flake_id` library. - The `Pleroma.FlakeId` module has been replaced with the `flake_id` library.
### Fixed ### Fixed
- Following from Osada - Following from Osada
- Favorites timeline doing database-intensive queries - Favorites timeline doing database-intensive queries
- Metadata rendering errors resulting in the entire page being inaccessible - Metadata rendering errors resulting in the entire page being inaccessible
@ -1377,6 +1613,7 @@ curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/devel
- Reverse Proxy limiting `max_body_length` was incorrectly defined and only checked `Content-Length` headers which may not be sufficient in some circumstances - Reverse Proxy limiting `max_body_length` was incorrectly defined and only checked `Content-Length` headers which may not be sufficient in some circumstances
### Added ### Added
- Expiring/ephemeral activities. All activities can have expires_at value set, which controls when they should be deleted automatically. - Expiring/ephemeral activities. All activities can have expires_at value set, which controls when they should be deleted automatically.
- Mastodon API: in post_status, the expires_in parameter lets you set the number of seconds until an activity expires. It must be at least one hour. - Mastodon API: in post_status, the expires_in parameter lets you set the number of seconds until an activity expires. It must be at least one hour.
- Mastodon API: all status JSON responses contain a `pleroma.expires_at` item which states when an activity will expire. The value is only shown to the user who created the activity. To everyone else it's empty. - Mastodon API: all status JSON responses contain a `pleroma.expires_at` item which states when an activity will expire. The value is only shown to the user who created the activity. To everyone else it's empty.
@ -1420,24 +1657,33 @@ curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/devel
- Reverse Proxy: Do not retry failed requests to limit pressure on the peer - Reverse Proxy: Do not retry failed requests to limit pressure on the peer
### Changed ### Changed
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
- Admin API: changed json structure for saving config settings. - Admin API: changed json structure for saving config settings.
- RichMedia: parsers and their order are configured in `rich_media` config. - RichMedia: parsers and their order are configured in `rich_media` config.
- RichMedia: add the rich media ttl based on image expiration time. - RichMedia: add the rich media ttl based on image expiration time.
## [1.0.7] - 2019-09-26 ## [1.0.7] - 2019-09-26
### Fixed ### Fixed
- Broken federation on Erlang 22 (previous versions of hackney http client were using an option that got deprecated) - Broken federation on Erlang 22 (previous versions of hackney http client were using an option that got deprecated)
### Changed ### Changed
- ActivityPub: The first page in inboxes/outboxes is no longer embedded. - ActivityPub: The first page in inboxes/outboxes is no longer embedded.
## [1.0.6] - 2019-08-14 ## [1.0.6] - 2019-08-14
### Fixed ### Fixed
- MRF: fix use of unserializable keyword lists in describe() implementations - MRF: fix use of unserializable keyword lists in describe() implementations
- ActivityPub S2S: POST requests are now signed with `(request-target)` pseudo-header. - ActivityPub S2S: POST requests are now signed with `(request-target)` pseudo-header.
## [1.0.5] - 2019-08-13 ## [1.0.5] - 2019-08-13
### Fixed ### Fixed
- Mastodon API: follower/following counters not being nullified, when `hide_follows`/`hide_followers` is set - Mastodon API: follower/following counters not being nullified, when `hide_follows`/`hide_followers` is set
- Mastodon API: `muted` in the Status entity, using author's account to determine if the thread was muted - Mastodon API: `muted` in the Status entity, using author's account to determine if the thread was muted
- Mastodon API: return the actual profile URL in the Account entity's `url` property when appropriate - Mastodon API: return the actual profile URL in the Account entity's `url` property when appropriate
@ -1448,6 +1694,7 @@ curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/devel
- Fix internal server error when using the healthcheck API. - Fix internal server error when using the healthcheck API.
### Added ### Added
- **Breaking:** MRF describe API, which adds support for exposing configuration information about MRF policies to NodeInfo. - **Breaking:** MRF describe API, which adds support for exposing configuration information about MRF policies to NodeInfo.
Custom modules will need to be updated by adding, at the very least, `def describe, do: {:ok, %{}}` to the MRF policy modules. Custom modules will need to be updated by adding, at the very least, `def describe, do: {:ok, %{}}` to the MRF policy modules.
- Relays: Added a task to list relay subscriptions. - Relays: Added a task to list relay subscriptions.
@ -1459,21 +1706,28 @@ curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/devel
- Configuration: `federation_incoming_replies_max_depth` option - Configuration: `federation_incoming_replies_max_depth` option
### Removed ### Removed
- Federation: Remove `likes` from objects. - Federation: Remove `likes` from objects.
- **Breaking:** ActivityPub: The `accept_blocks` configuration setting. - **Breaking:** ActivityPub: The `accept_blocks` configuration setting.
## [1.0.4] - 2019-08-01 ## [1.0.4] - 2019-08-01
### Fixed ### Fixed
- Invalid SemVer version generation, when the current branch does not have commits ahead of tag/checked out on a tag - Invalid SemVer version generation, when the current branch does not have commits ahead of tag/checked out on a tag
## [1.0.3] - 2019-07-31 ## [1.0.3] - 2019-07-31
### Security ### Security
- OStatus: eliminate the possibility of a protocol downgrade attack. - OStatus: eliminate the possibility of a protocol downgrade attack.
- OStatus: prevent following locked accounts, bypassing the approval process. - OStatus: prevent following locked accounts, bypassing the approval process.
- TwitterAPI: use CommonAPI to handle remote follows instead of OStatus. - TwitterAPI: use CommonAPI to handle remote follows instead of OStatus.
## [1.0.2] - 2019-07-28 ## [1.0.2] - 2019-07-28
### Fixed ### Fixed
- Not being able to pin unlisted posts - Not being able to pin unlisted posts
- Mastodon API: represent poll IDs as strings - Mastodon API: represent poll IDs as strings
- MediaProxy: fix matching filenames - MediaProxy: fix matching filenames
@ -1484,19 +1738,25 @@ curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/devel
- ActivityPub S2S: remote user deletions now work the same as local user deletions. - ActivityPub S2S: remote user deletions now work the same as local user deletions.
### Changed ### Changed
- Configuration: OpenGraph and TwitterCard providers enabled by default - Configuration: OpenGraph and TwitterCard providers enabled by default
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
## [1.0.1] - 2019-07-14 ## [1.0.1] - 2019-07-14
### Security ### Security
- OStatus: fix an object spoofing vulnerability. - OStatus: fix an object spoofing vulnerability.
## [1.0.0] - 2019-06-29 ## [1.0.0] - 2019-06-29
### Security ### Security
- Mastodon API: Fix display names not being sanitized - Mastodon API: Fix display names not being sanitized
- Rich media: Do not crawl private IP ranges - Rich media: Do not crawl private IP ranges
### Added ### Added
- Digest email for inactive users - Digest email for inactive users
- Add a generic settings store for frontends / clients to use. - Add a generic settings store for frontends / clients to use.
- Explicit addressing option for posting. - Explicit addressing option for posting.
@ -1561,6 +1821,7 @@ curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/devel
- Configuration: default syslog tag "Pleroma" is now lowercased to "pleroma" - Configuration: default syslog tag "Pleroma" is now lowercased to "pleroma"
### Changed ### Changed
- **Breaking:** bind to 127.0.0.1 instead of 0.0.0.0 by default - **Breaking:** bind to 127.0.0.1 instead of 0.0.0.0 by default
- **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer - **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer
- Thread containment / test for complete visibility will be skipped by default. - Thread containment / test for complete visibility will be skipped by default.
@ -1602,6 +1863,7 @@ curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/devel
- Rich Media: crawl only https URLs. - Rich Media: crawl only https URLs.
### Fixed ### Fixed
- Follow requests don't get 'stuck' anymore. - Follow requests don't get 'stuck' anymore.
- Added an FTS index on objects. Running `vacuum analyze` and setting a larger `work_mem` is recommended. - Added an FTS index on objects. Running `vacuum analyze` and setting a larger `work_mem` is recommended.
- Followers counter not being updated when a follower is blocked - Followers counter not being updated when a follower is blocked
@ -1637,31 +1899,48 @@ curl -Lo ./bin/pleroma_ctl 'https://git.pleroma.social/pleroma/pleroma/raw/devel
- MRF: Simple policy now properly delists imported or relayed statuses - MRF: Simple policy now properly delists imported or relayed statuses
## Removed ## Removed
- Configuration: `config :pleroma, :fe` in favor of the more flexible `config :pleroma, :frontend_configurations` - Configuration: `config :pleroma, :fe` in favor of the more flexible `config :pleroma, :frontend_configurations`
## [0.9.99999] - 2019-05-31 ## [0.9.99999] - 2019-05-31
### Security ### Security
- Mastodon API: Fix lists leaking private posts - Mastodon API: Fix lists leaking private posts
## [0.9.9999] - 2019-04-05 ## [0.9.9999] - 2019-04-05
### Security ### Security
- Mastodon API: Fix content warnings skipping HTML sanitization - Mastodon API: Fix content warnings skipping HTML sanitization
## [0.9.999] - 2019-03-13 ## [0.9.999] - 2019-03-13
Frontend changes only. Frontend changes only.
### Added ### Added
- Added floating action button for posting status on mobile - Added floating action button for posting status on mobile
### Changed ### Changed
- Changed user-settings icon to a pencil - Changed user-settings icon to a pencil
### Fixed ### Fixed
- Keyboard shortcuts activating when typing a message - Keyboard shortcuts activating when typing a message
- Gaps when scrolling down on a timeline after showing new - Gaps when scrolling down on a timeline after showing new
## [0.9.99] - 2019-03-08 ## [0.9.99] - 2019-03-08
### Changed ### Changed
- Update the frontend to the 0.9.99 tag - Update the frontend to the 0.9.99 tag
### Fixed ### Fixed
- Sign the date header in federation to fix Mastodon federation. - Sign the date header in federation to fix Mastodon federation.
## [0.9.9] - 2019-02-22 ## [0.9.9] - 2019-02-22
This is our first stable release. This is our first stable release.

View file

@ -1,10 +1,10 @@
# https://hub.docker.com/r/hexpm/elixir/tags # https://hub.docker.com/r/hexpm/elixir/tags
ARG ELIXIR_IMG=hexpm/elixir ARG ELIXIR_IMG=hexpm/elixir
ARG ELIXIR_VER=1.14.5 ARG ELIXIR_VER=1.17.3
ARG ERLANG_VER=25.3.2.14 ARG ERLANG_VER=26.2.5.6
ARG ALPINE_VER=3.17.9 ARG ALPINE_VER=3.17.9
FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} as build FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} AS build
COPY . . COPY . .
@ -15,6 +15,7 @@ RUN apk add git gcc g++ musl-dev make cmake file-dev vips-dev &&\
echo "import Config" > config/prod.secret.exs &&\ echo "import Config" > config/prod.secret.exs &&\
mix local.hex --force &&\ mix local.hex --force &&\
mix local.rebar --force &&\ mix local.rebar --force &&\
mix deps.clean --all &&\
mix deps.get --only prod &&\ mix deps.get --only prod &&\
mkdir release &&\ mkdir release &&\
mix release --path release mix release --path release

View file

@ -1 +0,0 @@
Add new activity actor/type index. Greatly speeds up retrieval of rare types (like "Listen")

View file

@ -1 +0,0 @@
Fix 'Create a user' description in admin api docs

View file

@ -1 +0,0 @@
Fix AssignAppUser migration OOM

View file

@ -1 +0,0 @@
- Fix building "captcha" library with OpenBSD make

View file

@ -1 +0,0 @@
Docs: Restore DB schema before data to avoid long restore times

View file

@ -1 +0,0 @@
Return 404 with a better error message instead of 400 when receiving an activity for a deactivated user

View file

@ -1 +0,0 @@
Use JSON for DeepL API requests

View file

@ -1 +0,0 @@
Deleting an instance queues individual jobs for each user that needs to be deleted from the server.

View file

@ -1 +0,0 @@
Support Dislike activity, as sent by Mitra and Friendica, by changing it into a thumbs-down EmojiReact

View file

@ -1 +0,0 @@
Elixir 1.18: Fixed warnings and new deprecations

View file

@ -1 +0,0 @@
Added a way to upload new packs from a URL or ZIP file via Admin API

View file

@ -1 +0,0 @@
Support Mitra-style emoji likes.

View file

@ -1 +0,0 @@
Fix endorsement state display in relationship view

View file

@ -1 +0,0 @@
Add `duration` to the block endpoint, which makes block expire

View file

@ -1 +0,0 @@
Expose markup configuration in InstanceView

View file

@ -1 +0,0 @@
Set PATH in the FreeBSD rc script to avoid failures starting the service

View file

@ -1 +0,0 @@
Improved performance of status search queries using the default GIN index

View file

@ -1 +0,0 @@
Update Cowboy, Gun, and Plug family of dependencies

View file

@ -1 +0,0 @@
Hashtag searches return real results based on words in your query

View file

@ -1 +0,0 @@
Implement language detection with fastText

View file

@ -1 +0,0 @@
Added MRF.QuietReply which prevents replies to public posts from being published to the timelines

View file

@ -1 +0,0 @@
Oban Notifier was changed to Oban.Notifiers.PG for performance and scalability benefits

View file

@ -1 +0,0 @@
Updated relayd/httpd config files to be on par with nginx

View file

@ -1 +0,0 @@
replaced depracated flags and functions, renamed service to fit other service files

View file

@ -1 +0,0 @@
Updated Postgrex library to 0.20.0

View file

@ -1 +0,0 @@
Fix federation issue where Public visibility information in cc field was lost when sent to remote servers, causing posts to appear with inconsistent visibility across instances

View file

@ -1 +0,0 @@
Improved the logic of how we determine if a server is unreachable.

View file

@ -1 +0,0 @@
Relax alsoKnownAs requirements to just URI, not necessarily HTTP(S)

View file

@ -1 +0,0 @@
Fix release builds

View file

@ -1 +0,0 @@
Change scrobble external link param name to use snake case

View file

@ -1 +0,0 @@
Add `base_urls` to the /api/v1/instance pleroma metadata which provides information about the base URLs for media_proxy and uploads when configured

View file

@ -1 +0,0 @@
Change SMTP example to use the Mua adapter that works with OTP>25

View file

@ -1 +0,0 @@
Updated Tesla to 1.15.3

View file

@ -1 +0,0 @@
Backport [Elixir PR 14242](https://github.com/elixir-lang/elixir/pull/14242) fixing racy mkdir and lack of error handling of parent directory creation

View file

@ -1 +0,0 @@
Allow Terms of Service panel behaviour to be configurable

View file

@ -1 +0,0 @@
Support translation providers (DeepL, LibreTranslate)

View file

@ -1 +0,0 @@
Truncate the length of Rich Media title and description fields

View file

View file

@ -1 +0,0 @@
Fix HTTP client making invalid requests due to no percent encoding processing or validation.

View file

@ -1 +0,0 @@
Enforce an exact domain match for WebFinger resolution

View file

@ -1 +0,0 @@
Don't require an Accept header for WebFinger queries and default to JSON.

View file

@ -362,7 +362,9 @@ config :pleroma, :activitypub,
note_replies_output_limit: 5, note_replies_output_limit: 5,
sign_object_fetches: true, sign_object_fetches: true,
authorized_fetch_mode: false, authorized_fetch_mode: false,
client_api_enabled: false client_api_enabled: false,
anonymize_reporter: false,
anonymize_reporter_local_nickname: ""
config :pleroma, :streamer, config :pleroma, :streamer,
workers: 3, workers: 3,
@ -613,7 +615,7 @@ config :pleroma, Oban,
search_indexing: [limit: 10, paused: true], search_indexing: [limit: 10, paused: true],
slow: 5 slow: 5
], ],
plugins: [{Oban.Plugins.Pruner, max_age: 900}], plugins: [Oban.Plugins.Lazarus, {Oban.Plugins.Pruner, max_age: 900}],
crontab: [ crontab: [
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker}, {"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}, {"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker},

View file

@ -1797,6 +1797,23 @@ config :pleroma, :config_description, [
key: :client_api_enabled, key: :client_api_enabled,
type: :boolean, type: :boolean,
description: "Allow client to server ActivityPub interactions" description: "Allow client to server ActivityPub interactions"
},
%{
key: :anonymize_reporter,
type: :boolean,
label: "Anonymize local reports",
description:
"If true, replace local reporters with the designated local user for the copy to be sent to remote servers"
},
%{
key: :anonymize_reporter_local_nickname,
type: :string,
label: "Anonymized reporter",
description:
"The nickname of the designated local user that replaces the actual reporter in the copy to be sent to remote servers",
suggestions: [
"lain"
]
} }
] ]
}, },
@ -2114,6 +2131,11 @@ config :pleroma, :config_description, [
description: description:
"Amount of milliseconds after which the HTTP request is forcibly terminated.", "Amount of milliseconds after which the HTTP request is forcibly terminated.",
suggestions: [5_000] suggestions: [5_000]
},
%{
key: :user_agent,
type: :string,
description: "Custom User-Agent header to be used when fetching rich media content."
} }
] ]
}, },
@ -3311,6 +3333,12 @@ config :pleroma, :config_description, [
description: description:
"A map containing available frontends and parameters for their installation.", "A map containing available frontends and parameters for their installation.",
children: frontend_options children: frontend_options
},
%{
key: :pickable,
type: {:list, :string},
description:
"A list containing all frontends users can pick as their preference, format is :name/:ref, e.g pleroma-fe/stable."
} }
] ]
}, },
@ -3517,9 +3545,7 @@ config :pleroma, :config_description, [
%{ %{
key: :provider, key: :provider,
type: :module, type: :module,
suggestions: [ suggestions: {:list_behaviour_implementations, Pleroma.Language.LanguageDetector.Provider}
Pleroma.Language.LanguageDetector.Fasttext
]
}, },
%{ %{
group: {:subgroup, Pleroma.Language.LanguageDetector.Fasttext}, group: {:subgroup, Pleroma.Language.LanguageDetector.Fasttext},
@ -3539,10 +3565,7 @@ config :pleroma, :config_description, [
%{ %{
key: :provider, key: :provider,
type: :module, type: :module,
suggestions: [ suggestions: {:list_behaviour_implementations, Pleroma.Language.Translation.Provider}
Pleroma.Language.Translation.Deepl,
Pleroma.Language.Translation.Libretranslate
]
}, },
%{ %{
group: {:subgroup, Pleroma.Language.Translation.Deepl}, group: {:subgroup, Pleroma.Language.Translation.Deepl},
@ -3571,6 +3594,27 @@ config :pleroma, :config_description, [
label: "LibreTranslate API Key", label: "LibreTranslate API Key",
type: :string, type: :string,
suggestions: ["YOUR_API_KEY"] suggestions: ["YOUR_API_KEY"]
},
%{
group: {:subgroup, Pleroma.Language.Translation.TranslateLocally},
key: :intermediary_language,
label:
"translateLocally intermediary language (used when direct source->target model is not available)",
type: :string,
suggestions: ["en"]
},
%{
group: {:subgroup, Pleroma.Language.Translation.Mozhi},
key: :base_url,
label: "Mozhi instance URL",
type: :string
},
%{
group: {:subgroup, Pleroma.Language.Translation.Mozhi},
key: :engine,
label: "Engine used for Mozhi",
type: :string,
suggestions: ["libretranslate"]
} }
] ]
} }

View file

@ -170,6 +170,10 @@ config :pleroma, Pleroma.Upload.Filter.Mogrify, config_impl: Pleroma.StaticStubb
config :pleroma, Pleroma.Upload.Filter.Mogrify, mogrify_impl: Pleroma.MogrifyMock config :pleroma, Pleroma.Upload.Filter.Mogrify, mogrify_impl: Pleroma.MogrifyMock
config :pleroma, Pleroma.Signature, http_signatures_impl: Pleroma.StubbedHTTPSignaturesMock config :pleroma, Pleroma.Signature, http_signatures_impl: Pleroma.StubbedHTTPSignaturesMock
config :pleroma, Pleroma.Web.ActivityPub.Publisher, signature_impl: Pleroma.SignatureMock
config :pleroma, Pleroma.Web.ActivityPub.Publisher,
transmogrifier_impl: Pleroma.Web.ActivityPub.TransmogrifierMock
peer_module = peer_module =
if String.to_integer(System.otp_release()) >= 25 do if String.to_integer(System.otp_release()) >= 25 do

View file

@ -28,6 +28,7 @@ Feel free to contact us to be added to this list!
### AndStatus ### AndStatus
- Homepage: <http://andstatus.org/> - Homepage: <http://andstatus.org/>
- Source Code: <https://github.com/andstatus/andstatus/> - Source Code: <https://github.com/andstatus/andstatus/>
- Contact: [@AndStatus@mastodon.social](https://mastodon.social/@AndStatus)
- Platforms: Android - Platforms: Android
- Features: MastoAPI, ActivityPub (Client-to-Server) - Features: MastoAPI, ActivityPub (Client-to-Server)
@ -40,8 +41,8 @@ Feel free to contact us to be added to this list!
### Fedilab ### Fedilab
- Homepage: <https://fedilab.app/> - Homepage: <https://fedilab.app/>
- Source Code: <https://framagit.org/tom79/fedilab/> - Source Code: <https://codeberg.org/tom79/Fedilab>
- Contact: [@fedilab@framapiaf.org](https://framapiaf.org/users/fedilab) - Contact: [@apps@toot.fedilab.app](https://toot.fedilab.app/@apps)
- Platforms: Android - Platforms: Android
- Features: MastoAPI, Streaming Ready, Moderation, Text Formatting - Features: MastoAPI, Streaming Ready, Moderation, Text Formatting
@ -51,8 +52,8 @@ Feel free to contact us to be added to this list!
- Features: MastoAPI, No Streaming - Features: MastoAPI, No Streaming
### Husky ### Husky
- Source code: <https://git.mentality.rip/FWGS/Husky> - Source code: <https://github.com/captainepoch/husky>
- Contact: [@Husky@enigmatic.observer](https://enigmatic.observer/users/Husky) - Contact: [@husky@stereophonic.space](https://stereophonic.space/users/husky)
- Platforms: Android - Platforms: Android
- Features: MastoAPI, No Streaming, Emoji Reactions, Text Formatting, FE Stickers - Features: MastoAPI, No Streaming, Emoji Reactions, Text Formatting, FE Stickers
@ -65,7 +66,7 @@ Feel free to contact us to be added to this list!
### Tusky ### Tusky
- Homepage: <https://tuskyapp.github.io/> - Homepage: <https://tuskyapp.github.io/>
- Source Code: <https://github.com/tuskyapp/Tusky> - Source Code: <https://github.com/tuskyapp/Tusky>
- Contact: [@ConnyDuck@mastodon.social](https://mastodon.social/users/ConnyDuck) - Contact: [@Tusky@mastodon.social](https://mastodon.social/@Tusky)
- Platforms: Android - Platforms: Android
- Features: MastoAPI, No Streaming - Features: MastoAPI, No Streaming
@ -76,10 +77,10 @@ Feel free to contact us to be added to this list!
- Platform: Android - Platform: Android
- Features: MastoAPI, No Streaming - Features: MastoAPI, No Streaming
### Indigenous ### IndiePass
- Homepage: <https://indigenous.realize.be/> - Homepage: <https://indiepass.app/>
- Source Code: <https://github.com/swentel/indigenous-android/> - Source Code: <https://github.com/IndiePass/indiepass-android>
- Contact: [@swentel@realize.be](https://realize.be) - Contact: [@marksuth@mastodon.social](https://mastodon.social/@marksuth)
- Platforms: Android - Platforms: Android
- Features: MastoAPI, No Streaming - Features: MastoAPI, No Streaming

View file

@ -904,21 +904,31 @@ config :logger, :console,
### RUM indexing for full text search ### RUM indexing for full text search
!!! warning
It is recommended to use PostgreSQL v11 or newer. We have seen some minor issues with lower PostgreSQL versions.
* `rum_enabled`: If RUM indexes should be used. Defaults to `false`. * `rum_enabled`: If RUM indexes should be used. Defaults to `false`.
RUM indexes are an alternative indexing scheme that is not included in PostgreSQL by default. While they may eventually be mainlined, for now they have to be installed as a PostgreSQL extension from https://github.com/postgrespro/rum. RUM indexes are an alternative indexing scheme that is not included in PostgreSQL by default. While they may eventually be mainlined, for now they have to be installed as a PostgreSQL extension from [https://github.com/postgrespro/rum](https://github.com/postgrespro/rum).
Their advantage over the standard GIN indexes is that they allow efficient ordering of search results by timestamp, which makes search queries a lot faster on larger servers, by one or two orders of magnitude. They take up around 3 times as much space as GIN indexes. Their advantage over the standard GIN indexes is that they allow efficient ordering of search results by timestamp, which makes search queries a lot faster on larger servers, by one or two orders of magnitude. They take up around 3-4 times as much space as GIN indexes.
To enable them, both the `rum_enabled` flag has to be set and the following special migration has to be run: To enable them, both the `rum_enabled` flag has to be set and the following special migration has to be run:
`mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/` * Source install:
- Stop Pleroma
- `mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/`
* OTP install:
- Stop Pleroma
- `pleroma_ctl migrate --migrations-path priv/repo/optional_migrations/rum_indexing/`
This will probably take a long time. This will probably take a long time.
!!! note
It is recommended to `VACUUM FULL` the objects table after the migration has completed, to do that run:
```
# sudo -Hu postgres vacuumdb --full --analyze -t objects <pleroma DB name>
```
Now you can start Pleroma back up.
## Alternative client protocols ## Alternative client protocols
### BBS / SSH access ### BBS / SSH access

View file

@ -16,7 +16,9 @@ location /proxy {
``` ```
Also add the following on top of the configuration, outside of the `server` block: Also add the following on top of the configuration, outside of the `server` block:
``` ```
proxy_cache_path /tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=10g inactive=720m use_temp_path=off; # Note: The cache directory must exist and be writable by nginx.
# If nginx runs in a chroot, create it inside the chroot.
proxy_cache_path /var/tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=10g inactive=720m use_temp_path=off;
``` ```
If you came here from one of the installation guides, take a look at the example configuration `/installation/pleroma.nginx`, where this part is already included. If you came here from one of the installation guides, take a look at the example configuration `/installation/pleroma.nginx`, where this part is already included.

View file

@ -66,9 +66,9 @@ Returned data:
"username": "somenick", "username": "somenick",
... ...
}, },
"id" : "1", "id": "1",
"unread" : 2, "unread": 2,
"last_message" : {...}, // The last message in that chat "last_message": {...}, // The last message in that chat
"updated_at": "2020-04-21T15:11:46.000Z" "updated_at": "2020-04-21T15:11:46.000Z"
} }
``` ```
@ -93,8 +93,8 @@ Returned data:
"username": "somenick", "username": "somenick",
... ...
}, },
"id" : "1", "id": "1",
"unread" : 0, "unread": 0,
"updated_at": "2020-04-21T15:11:46.000Z" "updated_at": "2020-04-21T15:11:46.000Z"
} }
``` ```
@ -111,7 +111,7 @@ The modified chat message
### Getting a list of Chats ### Getting a list of Chats
`GET /api/v1/pleroma/chats` `GET /api/v2/pleroma/chats`
This will return a list of chats that you have been involved in, sorted by their This will return a list of chats that you have been involved in, sorted by their
last update (so new chats will be at the top). last update (so new chats will be at the top).
@ -119,6 +119,7 @@ last update (so new chats will be at the top).
Parameters: Parameters:
- with_muted: Include chats from muted users (boolean). - with_muted: Include chats from muted users (boolean).
- pinned: Include only pinned chats (boolean).
Returned data: Returned data:
@ -130,16 +131,16 @@ Returned data:
"username": "somenick", "username": "somenick",
... ...
}, },
"id" : "1", "id": "1",
"unread" : 2, "unread": 2,
"last_message" : {...}, // The last message in that chat "last_message": {...}, // The last message in that chat
"updated_at": "2020-04-21T15:11:46.000Z" "updated_at": "2020-04-21T15:11:46.000Z"
} }
] ]
``` ```
The recipient of messages that are sent to this chat is given by their AP ID. The recipient of messages that are sent to this chat is given by their AP ID.
No pagination is implemented for now. The usual pagination options are implemented.
### Getting the messages for a Chat ### Getting the messages for a Chat
@ -226,6 +227,32 @@ Deleting a chat message for given Chat id works like this:
Returned data is the deleted message. Returned data is the deleted message.
### Pinning a chat
Pinning a chat works like this:
`POST /api/v1/pleroma/chats/:id/pin`
Returned data:
```json
{
"account": {
"id": "someflakeid",
"username": "somenick",
...
},
"id": "1",
"unread": 0,
"updated_at": "2020-04-21T15:11:46.000Z",
"pinned": true,
}
```
To unpin a pinned chat, use:
`POST /api/v1/pleroma/chats/:id/unpin`
### Notifications ### Notifications
There's a new `pleroma:chat_mention` notification, which has this form. It is not given out in the notifications endpoint by default, you need to explicitly request it with `include_types[]=pleroma:chat_mention`: There's a new `pleroma:chat_mention` notification, which has this form. It is not given out in the notifications endpoint by default, you need to explicitly request it with `include_types[]=pleroma:chat_mention`:

View file

@ -39,11 +39,13 @@ Has these additional fields under the `pleroma` object:
- `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. - `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. - `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. - `pinned_at`: a datetime (iso8601) when status was pinned, `null` otherwise.
- `quotes_count`: the count of status quotes.
- `non_anonymous`: true if the source post specifies the poll results are not anonymous. Currently only implemented by Smithereen.
- `bookmark_folder`: the ID of the folder bookmark is stored within (if any). - `bookmark_folder`: the ID of the folder bookmark is stored within (if any).
- `list_id`: the ID of the list the post is addressed to (if any, only returned to author). - `list_id`: the ID of the list the post is addressed to (if any, only returned to author).
Has these additional fields under the `poll.pleroma` object:
- `non_anonymous`: true if the source post specifies the poll results are not anonymous. Currently only implemented by Smithereen.
The `GET /api/v1/statuses/:id/source` endpoint additionally has the following attributes: The `GET /api/v1/statuses/:id/source` endpoint additionally has the following attributes:
- `content_type`: The content type of the status source. - `content_type`: The content type of the status source.
@ -88,6 +90,7 @@ The `id` parameter can also be the `nickname` of the user. This only works in th
- `only_media`: include only statuses with media attached - `only_media`: include only statuses with media attached
- `with_muted`: include statuses/reactions from muted accounts - `with_muted`: include statuses/reactions from muted accounts
- `exclude_reblogs`: exclude reblogs - `exclude_reblogs`: exclude reblogs
- `only_reblogs`: include only reblogs
- `exclude_replies`: exclude replies - `exclude_replies`: exclude replies
- `exclude_visibilities`: exclude visibilities - `exclude_visibilities`: exclude visibilities
@ -97,6 +100,9 @@ Endpoints which accept `with_relationships` parameter:
- `/api/v1/accounts/:id/followers` - `/api/v1/accounts/:id/followers`
- `/api/v1/accounts/:id/following` - `/api/v1/accounts/:id/following`
- `/api/v1/mutes` - `/api/v1/mutes`
- `/api/v1/blocks`
- `/api/v1/search`
- `/api/v2/search`
Has these additional fields under the `pleroma` object: Has these additional fields under the `pleroma` object:

View file

@ -684,6 +684,7 @@ Audio scrobbling in Pleroma is **deprecated**.
### Creates a new Listen activity for an account ### Creates a new Listen activity for an account
* Method `POST` * Method `POST`
* Authentication: required * Authentication: required
* OAuth scope: `write:scrobbles`
* Params: * Params:
* `title`: the title of the media playing * `title`: the title of the media playing
* `album`: the album of the media playing [optional] * `album`: the album of the media playing [optional]

View file

@ -14,7 +14,7 @@ Note: This article is potentially outdated because at this time we may not have
- PostgreSQL 11.0以上 (Ubuntu16.04では9.5しか提供されていないので,[](https://www.postgresql.org/download/linux/ubuntu/)こちらから新しいバージョンを入手してください) - PostgreSQL 11.0以上 (Ubuntu16.04では9.5しか提供されていないので,[](https://www.postgresql.org/download/linux/ubuntu/)こちらから新しいバージョンを入手してください)
- `postgresql-contrib` 11.0以上 (同上) - `postgresql-contrib` 11.0以上 (同上)
- Elixir 1.14 以上 ([Debianのリポジトリからインストールしないこと ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください) - Elixir 1.15 以上 ([Debianのリポジトリからインストールしないこと ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください)
- `erlang-dev` - `erlang-dev`
- `erlang-nox` - `erlang-nox`
- `git` - `git`

View file

@ -1,7 +1,7 @@
## Required dependencies ## Required dependencies
* PostgreSQL >=11.0 * PostgreSQL >=11.0
* Elixir >=1.14.0 <1.19 * Elixir >=1.15.0 <1.19
* Erlang OTP >=23.0.0 (supported: <28) * Erlang OTP >=23.0.0 (supported: <28)
* git * git
* file / libmagic * file / libmagic

View file

@ -16,7 +16,7 @@ Note: the packages are not required with the current default settings of Pleroma
It is required for the following Pleroma features: It is required for the following Pleroma features:
* `Pleroma.Upload.Filters.Mogrify`, `Pleroma.Upload.Filters.Mogrifun` upload filters (related config: `Plaroma.Upload/filters` in `config/config.exs`) * `Pleroma.Upload.Filters.Mogrify`, `Pleroma.Upload.Filters.Mogrifun` upload filters (related config: `Pleroma.Upload/filters` in `config/config.exs`)
* Media preview proxy for still images (related config: `media_preview_proxy/enabled` in `config/config.exs`) * Media preview proxy for still images (related config: `media_preview_proxy/enabled` in `config/config.exs`)
## `ffmpeg` ## `ffmpeg`
@ -33,5 +33,5 @@ It is required for the following Pleroma features:
It is required for the following Pleroma features: It is required for the following Pleroma features:
* `Pleroma.Upload.Filters.Exiftool.StripLocation` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) * `Pleroma.Upload.Filters.Exiftool.StripLocation` upload filter (related config: `Pleroma.Upload/filters` in `config/config.exs`)
* `Pleroma.Upload.Filters.Exiftool.ReadDescription` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) * `Pleroma.Upload.Filters.Exiftool.ReadDescription` upload filter (related config: `Pleroma.Upload/filters` in `config/config.exs`)

View file

@ -56,7 +56,6 @@ http protocol pleroma { # Protocol for upstream Pleroma server
relay wwwtls { relay wwwtls {
listen on $ext_inet port https tls # Comment to disable listening on IPv4 listen on $ext_inet port https tls # Comment to disable listening on IPv4
#listen on $ext_inet6 port https tls # Comment to disable listening on IPv6
protocol pleroma protocol pleroma
@ -66,3 +65,16 @@ relay wwwtls {
# Example: # Example:
#forward to <httpd_server> port 8080 #forward to <httpd_server> port 8080
} }
# Uncomment relay block to enable IPv6
#relay wwwtls6 {
# listen on $ext_inet6 port https tls
# protocol pleroma
# forward to <pleroma_server> port 4000 check tcp timeout 500 # Adjust timeout accordingly when relayd returns 502 while Pleroma is running without problems.
# # When serving multiple services, add the forwards here.
# # Example:
# #forward to <httpd_server> port 8080
#}

View file

@ -6,7 +6,9 @@
# 3. Copy this file to /etc/nginx/sites-available/ and then add a symlink to it # 3. Copy this file to /etc/nginx/sites-available/ and then add a symlink to it
# in /etc/nginx/sites-enabled/ and run 'nginx -s reload' or restart nginx. # in /etc/nginx/sites-enabled/ and run 'nginx -s reload' or restart nginx.
proxy_cache_path /tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=10g # Note: The cache directory must exist and be writable by nginx.
# If nginx runs in a chroot, create it inside the chroot.
proxy_cache_path /var/tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=10g
inactive=720m use_temp_path=off; inactive=720m use_temp_path=off;
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only # this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
@ -41,8 +43,21 @@ ssl_session_cache shared:ssl_session_cache:10m;
server { server {
server_name example.tld; server_name example.tld;
listen 443 ssl http2; listen 443 ssl;
listen [::]:443 ssl http2; listen [::]:443 ssl;
http2 on;
# Optional HTTP/3 support
# Note: requires you open UDP port 443
#
# listen 443 quic reuseport;
# listen [::]:443 quic reuseport;
# http3 on;
# quic_retry on;
# ssl_early_data on;
# quic_gso on;
# add_header Alt-Svc 'h3=":443"; ma=86400';
ssl_session_timeout 1d; ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off; ssl_session_tickets off;
@ -67,8 +82,14 @@ server {
gzip_http_version 1.1; gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/activity+json application/atom+xml; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/activity+json application/atom+xml;
# the nginx default is 1m, not enough for large media uploads # Nginx media upload limitation
# Ensure that this value matches or exceeds your Pleroma upload limit:
#
# config :pleroma, :instance,
# upload_limit: 16_000_000
#
client_max_body_size 16m; client_max_body_size 16m;
ignore_invalid_headers off; ignore_invalid_headers off;
proxy_http_version 1.1; proxy_http_version 1.1;
@ -94,7 +115,9 @@ server {
# proxy_pass http://phoenix/notice/$1; # proxy_pass http://phoenix/notice/$1;
# } # }
location ~ ^/(media|proxy) { # Remove this location if you choose to use a dedicated subdomain
# for mediaproxy
location /proxy {
proxy_cache pleroma_media_cache; proxy_cache pleroma_media_cache;
slice 1m; slice 1m;
proxy_cache_key $host$uri$is_args$args$slice_range; proxy_cache_key $host$uri$is_args$args$slice_range;
@ -106,4 +129,95 @@ server {
chunked_transfer_encoding on; chunked_transfer_encoding on;
proxy_pass http://phoenix; proxy_pass http://phoenix;
} }
# Nginx can serve the local file uploads directly reducing work for
# the backend. Make sure to change this to a "deny all" if you use
# a dedicated subdomain. It will break access to uploads that have already
# federated if you are converting an existing installation, so weigh the risks
# carefully.
#
# location /media/ {
# alias /var/lib/pleroma/uploads/; # <-- make sure this is correct for your deployment
# allow all;
# add_header X-Content-Type-Options "nosniff";
# add_header Content-Security-Policy "sandbox";
# }
} }
# It is strongly recommended that you host your media and the mediaproxy on a dedicated subdomain for security reasons.
# The following Pleroma settings will be required to enable this capability:
#
# config :pleroma, :media_proxy,
# base_url: "https://media.example.tld/"
#
# # Assuming default media upload deployment (e.g., not S3 which will require a different domain anyway) --
# config :pleroma, Pleroma.Upload,
# base_url: "https://media.example.tld/media/",
#
# config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
#
# And then uncomment and configure the following server.
# Make sure your certificate was issued to support both domains or use a dedicated certificate:
#
# server {
# server_name media.example.tld;
#
# listen 443 ssl;
# listen [::]:443 ssl;
# http2 on;
#
# # Optional HTTP/3 support
# # Note: requires you open UDP port 443
# #
# # listen 443 quic reuseport;
# # listen [::]:443 quic reuseport;
# # http3 on;
# # quic_retry on;
# # ssl_early_data on;
# # quic_gso on;
# # add_header Alt-Svc 'h3=":443"; ma=86400';
#
# ssl_session_timeout 1d;
# ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
# ssl_session_tickets off;
#
# ssl_trusted_certificate /etc/letsencrypt/live/example.tld/chain.pem;
# ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
#
# ssl_protocols TLSv1.2 TLSv1.3;
# ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
# ssl_prefer_server_ciphers off;
# # In case of an old server with an OpenSSL version of 1.0.2 or below,
# # leave only prime256v1 or comment out the following line.
# ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
# ssl_stapling on;
# ssl_stapling_verify on;
#
# proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "upgrade";
# proxy_set_header Host $http_host;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#
# location /media/ { # <-- make sure this path matches your Pleroma.Upload :base_url
# alias /var/lib/pleroma/uploads/; # <-- make sure this is correct for your deployment
# allow all;
# add_header X-Content-Type-Options "nosniff";
# add_header Content-Security-Policy "sandbox";
# }
#
# location /proxy {
# proxy_cache pleroma_media_cache;
# slice 1m;
# proxy_cache_key $host$uri$is_args$args$slice_range;
# proxy_set_header Range $slice_range;
# proxy_cache_valid 200 206 301 304 1h;
# proxy_cache_lock on;
# proxy_ignore_client_abort on;
# proxy_buffering on;
# chunked_transfer_encoding on;
# proxy_pass http://phoenix;
# }
# }

View file

@ -7,7 +7,7 @@ defmodule Pleroma.Activity.Queries do
Contains queries for Activity. Contains queries for Activity.
""" """
import Ecto.Query, only: [from: 2, where: 3] import Ecto.Query, only: [from: 2]
@type query :: Ecto.Queryable.t() | Pleroma.Activity.t() @type query :: Ecto.Queryable.t() | Pleroma.Activity.t()
@ -70,22 +70,6 @@ defmodule Pleroma.Activity.Queries do
) )
end end
@spec by_object_in_reply_to_id(query, String.t(), keyword()) :: query
def by_object_in_reply_to_id(query, in_reply_to_id, opts \\ []) do
query =
if opts[:skip_preloading] do
Activity.with_joined_object(query)
else
Activity.with_preloaded_object(query)
end
where(
query,
[activity, object: o],
fragment("(?)->>'inReplyTo' = ?", o.data, ^to_string(in_reply_to_id))
)
end
@spec by_type(query, String.t()) :: query @spec by_type(query, String.t()) :: query
def by_type(query \\ Activity, activity_type) do def by_type(query \\ Activity, activity_type) do
from( from(

View file

@ -25,6 +25,8 @@ defmodule Pleroma.Chat do
belongs_to(:user, User, type: FlakeId.Ecto.CompatType) belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
field(:recipient, :string) field(:recipient, :string)
field(:pinned, :boolean)
timestamps() timestamps()
end end
@ -94,4 +96,16 @@ defmodule Pleroma.Chat do
order_by: [desc: c.updated_at] order_by: [desc: c.updated_at]
) )
end end
def pin(%__MODULE__{} = chat) do
chat
|> cast(%{pinned: true}, [:pinned])
|> Repo.update()
end
def unpin(%__MODULE__{} = chat) do
chat
|> cast(%{pinned: false}, [:pinned])
|> Repo.update()
end
end end

View file

@ -21,7 +21,8 @@ defmodule Pleroma.Constants do
"pleroma_internal", "pleroma_internal",
"generator", "generator",
"rules", "rules",
"language" "language",
"voters"
] ]
) )

View file

@ -157,6 +157,16 @@ defmodule Pleroma.FollowingRelationship do
|> Repo.all() |> Repo.all()
end end
def get_outgoing_follow_requests(%User{id: id}) do
__MODULE__
|> join(:inner, [r], f in assoc(r, :following))
|> where([r], r.state == ^:follow_pending)
|> where([r], r.follower_id == ^id)
|> where([r, f], f.is_active == true)
|> select([r, f], f)
|> Repo.all()
end
def following?(%User{id: follower_id}, %User{id: followed_id}) do def following?(%User{id: follower_id}, %User{id: followed_id}) do
__MODULE__ __MODULE__
|> where(follower_id: ^follower_id, following_id: ^followed_id, state: ^:follow_accept) |> where(follower_id: ^follower_id, following_id: ^followed_id, state: ^:follow_accept)

View file

@ -131,31 +131,4 @@ defmodule Pleroma.HTTP do
defp default_middleware, defp default_middleware,
do: [Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.EncodeUrl] do: [Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.EncodeUrl]
def encode_url(url) when is_binary(url) do
URI.parse(url)
|> then(fn parsed ->
path = encode_path(parsed.path)
query = encode_query(parsed.query)
%{parsed | path: path, query: query}
end)
|> URI.to_string()
end
defp encode_path(nil), do: nil
defp encode_path(path) when is_binary(path) do
path
|> URI.decode()
|> URI.encode()
end
defp encode_query(nil), do: nil
defp encode_query(query) when is_binary(query) do
query
|> URI.decode_query()
|> URI.encode_query()
end
end end

View file

@ -16,7 +16,12 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do
config_opts = Pleroma.Config.get([:http, :adapter], []) config_opts = Pleroma.Config.get([:http, :adapter], [])
url_encoding =
Keyword.new()
|> Keyword.put(:path_encode_fun, fn path -> path end)
@defaults @defaults
|> Keyword.merge(url_encoding)
|> Keyword.merge(config_opts) |> Keyword.merge(config_opts)
|> Keyword.merge(connection_opts) |> Keyword.merge(connection_opts)
|> add_scheme_opts(uri) |> add_scheme_opts(uri)

View file

@ -0,0 +1,109 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Language.Translation.Mozhi do
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
alias Pleroma.Language.Translation.Provider
use Provider
@behaviour Provider
@name "Mozhi"
@impl Provider
def configured?, do: not_empty_string(base_url()) and not_empty_string(engine())
@impl Provider
def translate(content, source_language, target_language) do
endpoint =
base_url()
|> URI.merge("/api/translate")
|> URI.to_string()
case Pleroma.HTTP.get(
endpoint <>
"?" <>
URI.encode_query(%{
engine: engine(),
text: content,
from: source_language,
to: target_language
}),
[{"Accept", "application/json"}]
) do
{:ok, %{status: 200} = res} ->
%{
"translated-text" => content,
"source_language" => source_language
} = Jason.decode!(res.body)
{:ok,
%{
content: content,
detected_source_language: source_language,
provider: @name
}}
_ ->
{:error, :internal_server_error}
end
end
@impl Provider
def supported_languages(type) when type in [:source, :target] do
path =
case type do
:source -> "/api/source_languages"
:target -> "/api/target_languages"
end
endpoint =
base_url()
|> URI.merge(path)
|> URI.to_string()
case Pleroma.HTTP.get(
endpoint <>
"?" <>
URI.encode_query(%{
engine: engine()
}),
[{"Accept", "application/json"}]
) do
{:ok, %{status: 200} = res} ->
languages =
Jason.decode!(res.body)
|> Enum.map(fn %{"Id" => language} -> language end)
{:ok, languages}
_ ->
{:error, :internal_server_error}
end
end
@impl Provider
def languages_matrix do
with {:ok, source_languages} <- supported_languages(:source),
{:ok, target_languages} <- supported_languages(:target) do
{:ok,
Map.new(source_languages, fn language -> {language, target_languages -- [language]} end)}
else
{:error, error} -> {:error, error}
end
end
@impl Provider
def name, do: @name
defp base_url do
Pleroma.Config.get([__MODULE__, :base_url])
end
defp engine do
Pleroma.Config.get([__MODULE__, :engine])
end
end

View file

@ -0,0 +1,129 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Language.Translation.TranslateLocally do
alias Pleroma.Language.Translation.Provider
use Provider
@behaviour Provider
@name "translateLocally"
@impl Provider
def missing_dependencies do
if Pleroma.Utils.command_available?("translateLocally") do
[]
else
["translateLocally"]
end
end
@impl Provider
def configured?, do: is_map(models())
@impl Provider
def translate(content, source_language, target_language) do
model =
models()
|> Map.get(source_language, %{})
|> Map.get(target_language)
models =
if model do
[model]
else
[
models()
|> Map.get(source_language, %{})
|> Map.get(intermediary_language()),
models()
|> Map.get(intermediary_language(), %{})
|> Map.get(target_language)
]
end
translated_content =
Enum.reduce(models, content, fn model, content ->
text_path = Path.join(System.tmp_dir!(), "translateLocally-#{Ecto.UUID.generate()}")
File.write(text_path, content)
translated_content =
case System.cmd("translateLocally", ["-m", model, "-i", text_path, "--html"]) do
{content, _} -> content
_ -> nil
end
File.rm(text_path)
translated_content
end)
{:ok,
%{
content: translated_content,
detected_source_language: source_language,
provider: @name
}}
end
@impl Provider
def supported_languages(:source) do
languages =
languages_matrix()
|> elem(1)
|> Map.keys()
{:ok, languages}
end
@impl Provider
def supported_languages(:target) do
languages =
languages_matrix()
|> elem(1)
|> Map.values()
|> List.flatten()
|> Enum.uniq()
{:ok, languages}
end
@impl Provider
def languages_matrix do
languages =
models()
|> Map.to_list()
|> Enum.map(fn {key, value} -> {key, Map.keys(value)} end)
|> Enum.into(%{})
matrix =
if intermediary_language() do
languages
|> Map.to_list()
|> Enum.map(fn {key, value} ->
with_intermediary =
(((value ++ languages[intermediary_language()])
|> Enum.uniq()) --
[key])
|> Enum.sort()
{key, with_intermediary}
end)
|> Enum.into(%{})
else
languages
end
{:ok, matrix}
end
@impl Provider
def name, do: @name
defp models, do: Pleroma.Config.get([__MODULE__, :models])
defp intermediary_language, do: Pleroma.Config.get([__MODULE__, :intermediary_language])
end

View file

@ -575,6 +575,12 @@ defmodule Pleroma.ModerationLog do
"@#{actor_nickname} requested account backup for @#{user_nickname}" "@#{actor_nickname} requested account backup for @#{user_nickname}"
end end
def get_log_entry_message(%ModerationLog{data: data}) do
actor_name = get_in(data, ["actor", "nickname"]) || "unknown"
action = data["action"] || "unknown"
"@#{actor_name} performed action #{action}"
end
defp nicknames_to_string(nicknames) do defp nicknames_to_string(nicknames) do
nicknames nicknames
|> Enum.map(&"@#{&1}") |> Enum.map(&"@#{&1}")

View file

@ -74,6 +74,7 @@ defmodule Pleroma.Notification do
reblog reblog
poll poll
status status
update
} }
def changeset(%Notification{} = notification, attrs) do def changeset(%Notification{} = notification, attrs) do
@ -281,10 +282,15 @@ defmodule Pleroma.Notification do
select: n.id select: n.id
) )
Multi.new() {:ok, %{marker: marker}} =
|> Multi.update_all(:ids, query, set: [seen: true, updated_at: NaiveDateTime.utc_now()]) Multi.new()
|> Marker.multi_set_last_read_id(user, "notifications") |> Multi.update_all(:ids, query, set: [seen: true, updated_at: NaiveDateTime.utc_now()])
|> Repo.transaction() |> Marker.multi_set_last_read_id(user, "notifications")
|> Repo.transaction()
Streamer.stream(["user", "user:notification"], marker)
{:ok, %{marker: marker}}
end end
@spec read_one(User.t(), String.t()) :: @spec read_one(User.t(), String.t()) ::
@ -525,9 +531,7 @@ defmodule Pleroma.Notification do
%Activity{data: %{"type" => "Create"}} = activity, %Activity{data: %{"type" => "Create"}} = activity,
local_only local_only
) do ) do
notification_enabled_ap_ids = notification_enabled_ap_ids = Utils.get_notified_subscribers(activity)
[]
|> Utils.maybe_notify_subscribers(activity)
potential_receivers = potential_receivers =
User.get_users_from_set(notification_enabled_ap_ids, local_only: local_only) User.get_users_from_set(notification_enabled_ap_ids, local_only: local_only)

View file

@ -126,7 +126,7 @@ defmodule Pleroma.Object do
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}") Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
end end
def normalize(_, options \\ [fetch: false, id_only: false]) def normalize(_, options \\ [fetch: false])
# If we pass an Activity to Object.normalize(), we can try to use the preloaded object. # If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
# Use this whenever possible, especially when walking graphs in an O(N) loop! # Use this whenever possible, especially when walking graphs in an O(N) loop!
@ -155,9 +155,6 @@ defmodule Pleroma.Object do
def normalize(ap_id, options) when is_binary(ap_id) do def normalize(ap_id, options) when is_binary(ap_id) do
cond do cond do
Keyword.get(options, :id_only) ->
ap_id
Keyword.get(options, :fetch) -> Keyword.get(options, :fetch) ->
case Fetcher.fetch_object_from_id(ap_id, options) do case Fetcher.fetch_object_from_id(ap_id, options) do
{:ok, object} -> object {:ok, object} -> object
@ -401,28 +398,6 @@ defmodule Pleroma.Object do
String.starts_with?(id, Pleroma.Web.Endpoint.url() <> "/") String.starts_with?(id, Pleroma.Web.Endpoint.url() <> "/")
end end
def replies(object, opts \\ []) do
object = Object.normalize(object, fetch: false)
query =
Object
|> where(
[o],
fragment("(?)->>'inReplyTo' = ?", o.data, ^object.data["id"])
)
|> order_by([o], asc: o.id)
if opts[:self_only] do
actor = object.data["actor"]
where(query, [o], fragment("(?)->>'actor' = ?", o.data, ^actor))
else
query
end
end
def self_replies(object, opts \\ []),
do: replies(object, Keyword.put(opts, :self_only, true))
def tags(%Object{data: %{"tag" => tags}}) when is_list(tags), do: tags def tags(%Object{data: %{"tag" => tags}}) when is_list(tags), do: tags
def tags(_), do: [] def tags(_), do: []

View file

@ -5,6 +5,7 @@
defmodule Pleroma.Object.Updater do defmodule Pleroma.Object.Updater do
require Pleroma.Constants require Pleroma.Constants
alias Pleroma.Maps
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Repo alias Pleroma.Repo
@ -115,6 +116,7 @@ defmodule Pleroma.Object.Updater do
# Choices are the same, but counts are different # Choices are the same, but counts are different
to_be_updated to_be_updated
|> Map.put(key, updated_object[key]) |> Map.put(key, updated_object[key])
|> Maps.put_if_present("votersCount", updated_object["votersCount"])
else else
# Choices (or vote type) have changed, do not allow this # Choices (or vote type) have changed, do not allow this
_ -> to_be_updated _ -> to_be_updated

View file

@ -95,13 +95,30 @@ defmodule Pleroma.Pagination do
offset: :integer, offset: :integer,
limit: :integer, limit: :integer,
skip_extra_order: :boolean, skip_extra_order: :boolean,
skip_order: :boolean skip_order: :boolean,
order_asc: :boolean
} }
changeset = cast({%{}, param_types}, params, Map.keys(param_types)) changeset = cast({%{}, param_types}, params, Map.keys(param_types))
changeset.changes changeset.changes
end end
defp order_statement(query, table_binding, :asc) do
order_by(
query,
[{u, table_position(query, table_binding)}],
fragment("? asc nulls last", u.id)
)
end
defp order_statement(query, table_binding, :desc) do
order_by(
query,
[{u, table_position(query, table_binding)}],
fragment("? desc nulls last", u.id)
)
end
defp restrict(query, :min_id, %{min_id: min_id}, table_binding) do defp restrict(query, :min_id, %{min_id: min_id}, table_binding) do
where(query, [{q, table_position(query, table_binding)}], q.id > ^min_id) where(query, [{q, table_position(query, table_binding)}], q.id > ^min_id)
end end
@ -119,19 +136,16 @@ defmodule Pleroma.Pagination do
defp restrict(%{order_bys: [_ | _]} = query, :order, %{skip_extra_order: true}, _), do: query defp restrict(%{order_bys: [_ | _]} = query, :order, %{skip_extra_order: true}, _), do: query
defp restrict(query, :order, %{min_id: _}, table_binding) do defp restrict(query, :order, %{min_id: _}, table_binding) do
order_by( order_statement(query, table_binding, :asc)
query,
[{u, table_position(query, table_binding)}],
fragment("? asc nulls last", u.id)
)
end end
defp restrict(query, :order, _options, table_binding) do defp restrict(query, :order, %{max_id: _}, table_binding) do
order_by( order_statement(query, table_binding, :desc)
query, end
[{u, table_position(query, table_binding)}],
fragment("? desc nulls last", u.id) defp restrict(query, :order, options, table_binding) do
) dir = if options[:order_asc], do: :asc, else: :desc
order_statement(query, table_binding, dir)
end end
defp restrict(query, :offset, %{offset: offset}, _table_binding) do defp restrict(query, :offset, %{offset: offset}, _table_binding) do
@ -151,11 +165,9 @@ defmodule Pleroma.Pagination do
defp restrict(query, _, _, _), do: query defp restrict(query, _, _, _), do: query
defp enforce_order(result, %{min_id: _}) do defp enforce_order(result, %{min_id: _, order_asc: true}), do: result
result defp enforce_order(result, %{min_id: _}), do: Enum.reverse(result)
|> Enum.reverse() defp enforce_order(result, %{max_id: _, order_asc: true}), do: Enum.reverse(result)
end
defp enforce_order(result, _), do: result defp enforce_order(result, _), do: result
defp table_position(%Ecto.Query{} = query, binding_name) do defp table_position(%Ecto.Query{} = query, binding_name) do

View file

@ -3,6 +3,8 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReverseProxy do defmodule Pleroma.ReverseProxy do
alias Pleroma.Utils.URIEncoding
@range_headers ~w(range if-range) @range_headers ~w(range if-range)
@keep_req_headers ~w(accept accept-encoding cache-control if-modified-since) ++ @keep_req_headers ~w(accept accept-encoding cache-control if-modified-since) ++
~w(if-unmodified-since if-none-match) ++ @range_headers ~w(if-unmodified-since if-none-match) ++ @range_headers
@ -155,11 +157,12 @@ defmodule Pleroma.ReverseProxy do
end end
defp request(method, url, headers, opts) do defp request(method, url, headers, opts) do
Logger.debug("#{__MODULE__} #{method} #{url} #{inspect(headers)}")
method = method |> String.downcase() |> String.to_existing_atom() method = method |> String.downcase() |> String.to_existing_atom()
url = maybe_encode_url(url) url = maybe_encode_url(url)
Logger.debug("#{__MODULE__} #{method} #{url} #{inspect(headers)}")
case client().request(method, url, headers, "", opts) do case client().request(method, url, headers, "", opts) do
{:ok, code, headers, client} when code in @valid_resp_codes -> {:ok, code, headers, client} when code in @valid_resp_codes ->
{:ok, code, downcase_headers(headers), client} {:ok, code, downcase_headers(headers), client}
@ -459,9 +462,9 @@ defmodule Pleroma.ReverseProxy do
# Also do it for test environment # Also do it for test environment
defp maybe_encode_url(url) do defp maybe_encode_url(url) do
case Application.get_env(:tesla, :adapter) do case Application.get_env(:tesla, :adapter) do
Tesla.Adapter.Hackney -> Pleroma.HTTP.encode_url(url) Tesla.Adapter.Hackney -> URIEncoding.encode_url(url)
{Tesla.Adapter.Finch, _} -> Pleroma.HTTP.encode_url(url) {Tesla.Adapter.Finch, _} -> URIEncoding.encode_url(url)
Tesla.Mock -> Pleroma.HTTP.encode_url(url) Tesla.Mock -> URIEncoding.encode_url(url)
_ -> url _ -> url
end end
end end

View file

@ -7,6 +7,11 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
@impl true @impl true
def request(method, url, headers, body, opts \\ []) do def request(method, url, headers, body, opts \\ []) do
opts =
Keyword.put_new(opts, :path_encode_fun, fn path ->
path
end)
:hackney.request(method, url, headers, body, opts) :hackney.request(method, url, headers, body, opts)
end end

View file

@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Signature do defmodule Pleroma.Signature do
@behaviour Pleroma.Signature.API
@behaviour HTTPSignatures.Adapter @behaviour HTTPSignatures.Adapter
alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.EctoType.ActivityPub.ObjectValidators
@ -53,7 +54,7 @@ defmodule Pleroma.Signature do
def fetch_public_key(conn) do def fetch_public_key(conn) do
with {:ok, actor_id} <- get_actor_id(conn), with {:ok, actor_id} <- get_actor_id(conn),
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do {:ok, public_key} <- User.get_or_fetch_public_key_for_ap_id(actor_id) do
{:ok, public_key} {:ok, public_key}
else else
e -> e ->

View file

@ -0,0 +1,14 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Signature.API do
@moduledoc """
Behaviour for signing requests and producing HTTP Date headers.
This is used to allow tests to replace the signing implementation with Mox.
"""
@callback sign(user :: Pleroma.User.t(), headers :: map()) :: String.t()
@callback signed_date() :: String.t()
end

View file

@ -17,7 +17,7 @@ defmodule Pleroma.Tesla.Middleware.EncodeUrl do
@impl Tesla.Middleware @impl Tesla.Middleware
def call(%Tesla.Env{url: url} = env, next, _) do def call(%Tesla.Env{url: url} = env, next, _) do
url = Pleroma.HTTP.encode_url(url) url = Pleroma.Utils.URIEncoding.encode_url(url)
env = %{env | url: url} env = %{env | url: url}

View file

@ -35,6 +35,7 @@ defmodule Pleroma.Upload do
""" """
alias Ecto.UUID alias Ecto.UUID
alias Pleroma.Maps alias Pleroma.Maps
alias Pleroma.Utils.URIEncoding
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
require Logger require Logger
@ -230,11 +231,18 @@ defmodule Pleroma.Upload do
tmp_path tmp_path
end end
# Encoding the whole path here is fine since the path is in a
# UUID/<file name> form.
# The file at this point isn't %-encoded, so the path shouldn't
# be decoded first like Pleroma.Utils.URIEncoding.encode_url/1 does.
defp url_from_spec(%__MODULE__{name: name}, base_url, {:file, path}) do defp url_from_spec(%__MODULE__{name: name}, base_url, {:file, path}) do
encode_opts = [bypass_decode: true, bypass_parse: true]
path = path =
URI.encode(path, &char_unescaped?/1) <> URIEncoding.encode_url(path, encode_opts) <>
if Pleroma.Config.get([__MODULE__, :link_name], false) do if Pleroma.Config.get([__MODULE__, :link_name], false) do
"?name=#{URI.encode(name, &char_unescaped?/1)}" enum = %{name: name}
"?#{URI.encode_query(enum)}"
else else
"" ""
end end

View file

@ -233,8 +233,8 @@ defmodule Pleroma.User do
for {_relationship_type, [{_outgoing_relation, outgoing_relation_target}, _]} <- for {_relationship_type, [{_outgoing_relation, outgoing_relation_target}, _]} <-
@user_relationships_config do @user_relationships_config do
# `def blocked_users_relation/2`, `def muted_users_relation/2`, # `def blocked_users_relation/2`, `def muted_users_relation/2`,
# `def reblog_muted_users_relation/2`, `def notification_muted_users/2`, # `def reblog_muted_users_relation/2`, `def notification_muted_users_relation/2`,
# `def subscriber_users/2`, `def endorsed_users_relation/2` # `def subscriber_users_relation/2`, `def endorsed_users_relation/2`
def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
target_users_query = assoc(user, unquote(outgoing_relation_target)) target_users_query = assoc(user, unquote(outgoing_relation_target))
@ -288,6 +288,7 @@ defmodule Pleroma.User do
defdelegate following?(follower, followed), to: FollowingRelationship defdelegate following?(follower, followed), to: FollowingRelationship
defdelegate following_ap_ids(user), to: FollowingRelationship defdelegate following_ap_ids(user), to: FollowingRelationship
defdelegate get_follow_requests(user), to: FollowingRelationship defdelegate get_follow_requests(user), to: FollowingRelationship
defdelegate get_outgoing_follow_requests(user), to: FollowingRelationship
defdelegate search(query, opts \\ []), to: User.Search defdelegate search(query, opts \\ []), to: User.Search
@doc """ @doc """
@ -801,13 +802,6 @@ defmodule Pleroma.User do
when is_nil(password) do when is_nil(password) do
params = Map.put_new(params, :accepts_chat_messages, true) params = Map.put_new(params, :accepts_chat_messages, true)
params =
if Map.has_key?(params, :email) do
Map.put_new(params, :email, params[:email])
else
params
end
struct struct
|> cast(params, [ |> cast(params, [
:name, :name,
@ -1364,7 +1358,7 @@ defmodule Pleroma.User do
@spec get_by_nickname(String.t()) :: User.t() | nil @spec get_by_nickname(String.t()) :: User.t() | nil
def get_by_nickname(nickname) do def get_by_nickname(nickname) do
Repo.get_by(User, nickname: nickname) || Repo.get_by(User, nickname: nickname) ||
if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()})i, nickname) do if Regex.match?(~r(@#{Pleroma.Web.Endpoint.host()}$)i, nickname) do
Repo.get_by(User, nickname: local_nickname(nickname)) Repo.get_by(User, nickname: local_nickname(nickname))
end end
end end
@ -2314,6 +2308,15 @@ defmodule Pleroma.User do
def public_key(_), do: {:error, "key not found"} def public_key(_), do: {:error, "key not found"}
def get_or_fetch_public_key_for_ap_id(ap_id) do
with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id),
{:ok, public_key} <- public_key(user) do
{:ok, public_key}
else
_ -> :error
end
end
def get_public_key_for_ap_id(ap_id) do def get_public_key_for_ap_id(ap_id) do
with %User{} = user <- get_cached_by_ap_id(ap_id), with %User{} = user <- get_cached_by_ap_id(ap_id),
{:ok, public_key} <- public_key(user) do {:ok, public_key} <- public_key(user) do

View file

@ -16,6 +16,7 @@ defmodule Pleroma.User.Search do
following = Keyword.get(opts, :following, false) following = Keyword.get(opts, :following, false)
result_limit = Keyword.get(opts, :limit, @limit) result_limit = Keyword.get(opts, :limit, @limit)
offset = Keyword.get(opts, :offset, 0) offset = Keyword.get(opts, :offset, 0)
capabilities = Keyword.get(opts, :capabilities, [])
for_user = Keyword.get(opts, :for_user) for_user = Keyword.get(opts, :for_user)
@ -32,7 +33,7 @@ defmodule Pleroma.User.Search do
results = results =
query_string query_string
|> search_query(for_user, following, top_user_ids) |> search_query(for_user, following, top_user_ids, capabilities)
|> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset) |> Pagination.fetch_paginated(%{"offset" => offset, "limit" => result_limit}, :offset)
results results
@ -80,7 +81,7 @@ defmodule Pleroma.User.Search do
end end
end end
defp search_query(query_string, for_user, following, top_user_ids) do defp search_query(query_string, for_user, following, top_user_ids, capabilities) do
for_user for_user
|> base_query(following) |> base_query(following)
|> filter_blocked_user(for_user) |> filter_blocked_user(for_user)
@ -94,6 +95,7 @@ defmodule Pleroma.User.Search do
|> subquery() |> subquery()
|> order_by(desc: :search_rank) |> order_by(desc: :search_rank)
|> maybe_restrict_local(for_user) |> maybe_restrict_local(for_user)
|> maybe_restrict_accepting_chat_messages(capabilities)
|> filter_deactivated_users() |> filter_deactivated_users()
end end
@ -214,6 +216,14 @@ defmodule Pleroma.User.Search do
end end
end end
defp maybe_restrict_accepting_chat_messages(query, capabilities) do
if "accepts_chat_messages" in capabilities do
from(q in query, where: q.accepts_chat_messages == true)
else
query
end
end
defp limit, do: Pleroma.Config.get([:instance, :limit_to_local_content], :unauthenticated) defp limit, do: Pleroma.Config.get([:instance, :limit_to_local_content], :unauthenticated)
defp restrict_local(q), do: where(q, [u], u.local == true) defp restrict_local(q), do: where(q, [u], u.local == true)

View file

@ -0,0 +1,142 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2025 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Utils.URIEncoding do
@moduledoc """
Utility functions for dealing with URI encoding of paths and queries
with support for query-encoding quirks.
"""
require Pleroma.Constants
# We don't always want to decode the path first, like is the case in
# Pleroma.Upload.url_from_spec/3.
@doc """
Wraps URI encoding/decoding functions from Elixir's standard library to fix usually unintended side-effects.
Supports two URL processing options in the optional 2nd argument with the default being `false`:
* `bypass_parse` - Bypasses `URI.parse` stage, useful when it's not desirable to parse to URL first
before encoding it. Supports only encoding as the Path segment of a URI.
* `bypass_decode` - Bypasses `URI.decode` stage for the Path segment of a URI. Used when a URL
has to be double %-encoded for internal reasons.
Options must be specified as a Keyword with tuples with booleans, otherwise
`{:error, :invalid_opts}` is returned. Example:
`encode_url(url, [bypass_parse: true, bypass_decode: true])`
"""
@spec encode_url(String.t(), Keyword.t()) :: String.t() | {:error, :invalid_opts}
def encode_url(url, opts \\ []) when is_binary(url) and is_list(opts) do
bypass_parse = Keyword.get(opts, :bypass_parse, false)
bypass_decode = Keyword.get(opts, :bypass_decode, false)
with true <- is_boolean(bypass_parse),
true <- is_boolean(bypass_decode) do
cond do
bypass_parse ->
encode_path(url, bypass_decode)
true ->
URI.parse(url)
|> then(fn parsed ->
path = encode_path(parsed.path, bypass_decode)
query = encode_query(parsed.query, parsed.host)
%{parsed | path: path, query: query}
end)
|> URI.to_string()
end
else
_ -> {:error, :invalid_opts}
end
end
defp encode_path(nil, _bypass_decode), do: nil
# URI.encode/2 deliberately does not encode all chars that are forbidden
# in the path component of a URI. It only encodes chars that are forbidden
# in the whole URI. A predicate in the 2nd argument is used to fix that here.
# URI.encode/2 uses the predicate function to determine whether each byte
# (in an integer representation) should be encoded or not.
defp encode_path(path, bypass_decode) when is_binary(path) do
path =
cond do
bypass_decode ->
path
true ->
URI.decode(path)
end
path
|> URI.encode(fn byte ->
URI.char_unreserved?(byte) ||
Enum.any?(
Pleroma.Constants.uri_path_allowed_reserved_chars(),
fn char ->
char == byte
end
)
end)
end
# Order of kv pairs in query is not preserved when using URI.decode_query.
# URI.query_decoder/2 returns a stream which so far appears to not change order.
# Immediately switch to a list to prevent breakage for sites that expect
# the order of query keys to be always the same.
defp encode_query(query, host) when is_binary(query) do
query
|> URI.query_decoder()
|> Enum.to_list()
|> do_encode_query(host)
end
defp encode_query(nil, _), do: nil
# Always uses www_form encoding.
# Taken from Elixir's URI module.
defp do_encode_query(enumerable, host) do
Enum.map_join(enumerable, "&", &maybe_apply_query_quirk(&1, host))
end
# https://git.pleroma.social/pleroma/pleroma/-/issues/1055
defp maybe_apply_query_quirk({key, value}, "i.guim.co.uk" = _host) do
case key do
"precrop" ->
query_encode_kv_pair({key, value}, ~c":,")
key ->
query_encode_kv_pair({key, value})
end
end
defp maybe_apply_query_quirk({key, value}, _), do: query_encode_kv_pair({key, value})
# Taken from Elixir's URI module and modified to support quirks.
defp query_encode_kv_pair({key, value}, rules \\ []) when is_list(rules) do
cond do
length(rules) > 0 ->
# URI.encode_query/2 does not appear to follow spec and encodes all parts
# of our URI path Constant. This appears to work outside of edge-cases
# like The Guardian Rich Media Cards, keeping behavior same as with
# URI.encode_query/2 unless otherwise specified via rules.
(URI.encode_www_form(Kernel.to_string(key)) <>
"=" <>
URI.encode(value, fn byte ->
URI.char_unreserved?(byte) ||
Enum.any?(
rules,
fn char ->
char == byte
end
)
end))
|> String.replace("%20", "+")
true ->
URI.encode_www_form(Kernel.to_string(key)) <>
"=" <> URI.encode_www_form(Kernel.to_string(value))
end
end
end

View file

@ -414,10 +414,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
with flag_data <- make_flag_data(params, additional), with flag_data <- make_flag_data(params, additional),
{:ok, activity} <- insert(flag_data, local), {:ok, activity} <- insert(flag_data, local),
{:ok, stripped_activity} <- strip_report_status_data(activity),
_ <- notify_and_stream(activity), _ <- notify_and_stream(activity),
:ok <- :ok <- maybe_federate(activity) do
maybe_federate(stripped_activity) do
User.all_users_with_privilege(:reports_manage_reports) User.all_users_with_privilege(:reports_manage_reports)
|> Enum.filter(fn user -> user.ap_id != actor end) |> Enum.filter(fn user -> user.ap_id != actor end)
|> Enum.filter(fn user -> not is_nil(user.email) end) |> Enum.filter(fn user -> not is_nil(user.email) end)
@ -501,6 +499,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Repo.all() |> Repo.all()
end end
def fetch_objects_for_replies_collection(parent_ap_id, opts \\ %{}) do
opts =
opts
|> Map.put(:order_asc, true)
|> Map.put(:id_type, :integer)
from(o in Object,
where:
fragment("?->>'inReplyTo' = ?", o.data, ^parent_ap_id) and
fragment(
"(?->'to' \\? ?::text OR ?->'cc' \\? ?::text)",
o.data,
^Pleroma.Constants.as_public(),
o.data,
^Pleroma.Constants.as_public()
) and
fragment("?->>'type' <> 'Answer'", o.data),
select: %{id: o.id, ap_id: fragment("?->>'id'", o.data)}
)
|> Pagination.fetch_paginated(opts, :keyset)
end
@spec fetch_latest_direct_activity_id_for_context(String.t(), keyword() | map()) :: @spec fetch_latest_direct_activity_id_for_context(String.t(), keyword() | map()) ::
Ecto.UUID.t() | nil Ecto.UUID.t() | nil
def fetch_latest_direct_activity_id_for_context(context, opts \\ %{}) do def fetch_latest_direct_activity_id_for_context(context, opts \\ %{}) do
@ -1065,6 +1085,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data)) from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
end end
defp restrict_reblogs(query, %{only_reblogs: true}) do
from(activity in query, where: fragment("?->>'type' = 'Announce'", activity.data))
end
defp restrict_reblogs(query, _), do: query defp restrict_reblogs(query, _), do: query
defp restrict_muted(query, %{with_muted: true}), do: query defp restrict_muted(query, %{with_muted: true}), do: query
@ -1567,7 +1591,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp get_actor_url(_url), do: nil defp get_actor_url(_url), do: nil
defp normalize_image(%{"url" => url} = data) do defp normalize_image(%{"url" => url} = data) when is_binary(url) do
%{ %{
"type" => "Image", "type" => "Image",
"url" => [%{"href" => url}] "url" => [%{"href" => url}]
@ -1575,6 +1599,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> maybe_put_description(data) |> maybe_put_description(data)
end end
defp normalize_image(%{"url" => urls}) when is_list(urls) do
url = urls |> List.first()
%{"url" => url}
|> normalize_image()
end
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image() defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
defp normalize_image(_), do: nil defp normalize_image(_), do: nil

View file

@ -31,6 +31,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
@federating_only_actions [:internal_fetch, :relay, :relay_following, :relay_followers] @federating_only_actions [:internal_fetch, :relay, :relay_following, :relay_followers]
@object_replies_known_param_keys ["page", "min_id", "max_id", "since_id", "limit"]
plug(FederatingPlug when action in @federating_only_actions) plug(FederatingPlug when action in @federating_only_actions)
plug( plug(
@ -95,6 +97,36 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
end end
end end
def object_replies(%{assigns: assigns, query_params: params} = conn, _all_params) do
object_ap_id = conn.path_info |> Enum.reverse() |> tl() |> Enum.reverse()
object_ap_id = Endpoint.url() <> "/" <> Enum.join(object_ap_id, "/")
# Most other API params are converted to atoms by OpenAPISpex 3.x
# and therefore helper functions assume atoms. For consistency,
# also convert our params to atoms here.
params =
params
|> Map.take(@object_replies_known_param_keys)
|> Enum.into(%{}, fn {k, v} -> {String.to_existing_atom(k), v} end)
|> Map.put(:object_ap_id, object_ap_id)
|> Map.put(:order_asc, true)
|> Map.put(:conn, conn)
with %Object{} = object <- Object.get_cached_by_ap_id(object_ap_id),
user <- Map.get(assigns, :user, nil),
{_, true} <- {:visible?, Visibility.visible_for_user?(object, user)} do
conn
|> maybe_skip_cache(user)
|> set_cache_ttl_for(object)
|> put_resp_content_type("application/activity+json")
|> put_view(ObjectView)
|> render("object_replies.json", render_params: params)
else
{:visible?, false} -> {:error, :not_found}
nil -> {:error, :not_found}
end
end
def track_object_fetch(conn, nil), do: conn def track_object_fetch(conn, nil), do: conn
def track_object_fetch(conn, object_id) do def track_object_fetch(conn, object_id) do
@ -257,8 +289,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> put_view(UserView) |> put_view(UserView)
|> render("activity_collection_page.json", %{ |> render("activity_collection_page.json", %{
activities: activities, activities: activities,
pagination: ControllerHelper.get_pagination_fields(conn, activities), pagination: ControllerHelper.get_pagination_fields(conn, activities)
iri: "#{user.ap_id}/outbox"
}) })
end end
end end
@ -404,8 +435,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> put_view(UserView) |> put_view(UserView)
|> render("activity_collection_page.json", %{ |> render("activity_collection_page.json", %{
activities: activities, activities: activities,
pagination: ControllerHelper.get_pagination_fields(conn, activities), pagination: ControllerHelper.get_pagination_fields(conn, activities)
iri: "#{user.ap_id}/inbox"
}) })
end end
@ -482,6 +512,42 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
{:ok, activity} {:ok, activity}
end end
# We currently lack a Flag ObjectValidator since both CommonAPI and Transmogrifier
# both send it straight to ActivityPub.flag and C2S currently has to go through
# the normal pipeline which requires an ObjectValidator.
# TODO: Add a Flag Activity ObjectValidator
defp check_allowed_action(_, %{"type" => "Flag"}) do
{:error, "Flag activities aren't currently supported in C2S"}
end
# It would respond with 201 and silently fail with:
# Could not decode featured collection at fetch #{user.ap_id} \
# {:error, "Trying to fetch local resource"}
defp check_allowed_action(%{ap_id: ap_id}, %{"type" => "Update", "object" => %{"id" => ap_id}}),
do: {:error, "Updating profile is not currently supported in C2S"}
defp check_allowed_action(_, activity), do: {:ok, activity}
defp validate_visibility(%User{} = user, %{"type" => type, "object" => object} = activity) do
with {_, %Object{} = normalized_object} <-
{:normalize, Object.normalize(object, fetch: false)},
{_, true} <- {:visibility, Visibility.visible_for_user?(normalized_object, user)} do
{:ok, activity}
else
{:normalize, _} ->
if type in ["Create", "Listen"] do
# Creating new object via C2S; user is local and authenticated
# via the :authenticate Plug pipeline.
{:ok, activity}
else
{:error, "No such object found"}
end
{:visibility, _} ->
{:forbidden, "You can't interact with this object"}
end
end
def update_outbox( def update_outbox(
%{assigns: %{user: %User{nickname: nickname, ap_id: actor} = user}} = conn, %{assigns: %{user: %User{nickname: nickname, ap_id: actor} = user}} = conn,
%{"nickname" => nickname} = params %{"nickname" => nickname} = params
@ -493,6 +559,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> Map.put("actor", actor) |> Map.put("actor", actor)
with {:ok, params} <- fix_user_message(user, params), with {:ok, params} <- fix_user_message(user, params),
{:ok, params} <- check_allowed_action(user, params),
{:ok, params} <- validate_visibility(user, params),
{:ok, activity, _} <- Pipeline.common_pipeline(params, local: true), {:ok, activity, _} <- Pipeline.common_pipeline(params, local: true),
%Activity{data: activity_data} <- Activity.normalize(activity) do %Activity{data: activity_data} <- Activity.normalize(activity) do
conn conn

View file

@ -18,6 +18,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
content =~ quote_url -> true content =~ quote_url -> true
# Does the content already have a .quote-inline span? # Does the content already have a .quote-inline span?
content =~ "<span class=\"quote-inline\">" -> true content =~ "<span class=\"quote-inline\">" -> true
# Does the content already have a .quote-inline p? (Mastodon)
content =~ "<p class=\"quote-inline\">" -> true
# No inline quote found # No inline quote found
true -> false true -> false
end end

View file

@ -56,20 +56,24 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
defp fix_tag(%{"tag" => tag} = data) when is_map(tag), do: Map.put(data, "tag", [tag]) defp fix_tag(%{"tag" => tag} = data) when is_map(tag), do: Map.put(data, "tag", [tag])
defp fix_tag(data), do: Map.drop(data, ["tag"]) defp fix_tag(data), do: Map.drop(data, ["tag"])
# legacy internal *oma format
defp fix_replies(%{"replies" => replies} = data) when is_list(replies), do: data
defp fix_replies(%{"replies" => %{"first" => %{"items" => replies}}} = data) defp fix_replies(%{"replies" => %{"first" => %{"items" => replies}}} = data)
when is_list(replies), when is_list(replies),
do: Map.put(data, "replies", replies) do: Map.put(data, "replies", replies)
defp fix_replies(%{"replies" => %{"first" => %{"orderedItems" => replies}}} = data)
when is_list(replies),
do: Map.put(data, "replies", replies)
defp fix_replies(%{"replies" => %{"items" => replies}} = data) when is_list(replies), defp fix_replies(%{"replies" => %{"items" => replies}} = data) when is_list(replies),
do: Map.put(data, "replies", replies) do: Map.put(data, "replies", replies)
# TODO: Pleroma does not have any support for Collections at the moment. defp fix_replies(%{"replies" => %{"orderedItems" => replies}} = data) when is_list(replies),
# If the `replies` field is not something the ObjectID validator can handle, do: Map.put(data, "replies", replies)
# the activity/object would be rejected, which is bad behavior.
defp fix_replies(%{"replies" => replies} = data) when not is_list(replies),
do: Map.drop(data, ["replies"])
defp fix_replies(data), do: data defp fix_replies(data), do: Map.delete(data, "replies")
def fix_attachments(%{"attachment" => attachment} = data) when is_map(attachment), def fix_attachments(%{"attachment" => attachment} = data) when is_map(attachment),
do: Map.put(data, "attachment", [attachment]) do: Map.put(data, "attachment", [attachment])

View file

@ -20,7 +20,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
require Pleroma.Constants require Pleroma.Constants
def cast_and_filter_recipients(message, field, follower_collection, field_fallback \\ []) do def cast_and_filter_recipients(message, field, follower_collection, field_fallback \\ []) do
{:ok, data} = ObjectValidators.Recipients.cast(message[field] || field_fallback) # Fix as:Public/Public before ObjectID casting drops it, but keep `field_fallback`
# semantics (only used when the field is missing).
recipients =
%{field => message[field] || field_fallback}
|> Transmogrifier.fix_addressing_list(field)
|> Transmogrifier.fix_addressing_public(field)
|> Map.fetch!(field)
{:ok, data} = ObjectValidators.Recipients.cast(recipients)
data = data =
Enum.reject(data, fn x -> Enum.reject(data, fn x ->

View file

@ -13,7 +13,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.Publisher.Prepared alias Pleroma.Web.ActivityPub.Publisher.Prepared
alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Workers.PublisherWorker alias Pleroma.Workers.PublisherWorker
require Pleroma.Constants require Pleroma.Constants
@ -26,6 +25,18 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
ActivityPub outgoing federation module. ActivityPub outgoing federation module.
""" """
@signature_impl Application.compile_env(
:pleroma,
[__MODULE__, :signature_impl],
Pleroma.Signature
)
@transmogrifier_impl Application.compile_env(
:pleroma,
[__MODULE__, :transmogrifier_impl],
Pleroma.Web.ActivityPub.Transmogrifier
)
@doc """ @doc """
Enqueue publishing a single activity. Enqueue publishing a single activity.
""" """
@ -68,7 +79,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
Determine if an activity can be represented by running it through Transmogrifier. Determine if an activity can be represented by running it through Transmogrifier.
""" """
def representable?(%Activity{} = activity) do def representable?(%Activity{} = activity) do
with {:ok, _data} <- Transmogrifier.prepare_outgoing(activity.data) do with {:ok, _data} <- @transmogrifier_impl.prepare_outgoing(activity.data) do
true true
else else
_e -> _e ->
@ -91,7 +102,17 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
Logger.debug("Federating #{ap_id} to #{inbox}") Logger.debug("Federating #{ap_id} to #{inbox}")
uri = %{path: path} = URI.parse(inbox) uri = %{path: path} = URI.parse(inbox)
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data) {:ok, data} = @transmogrifier_impl.prepare_outgoing(activity.data)
{actor, data} =
with {_, false} <- {:actor_changed?, data["actor"] != activity.data["actor"]} do
{actor, data}
else
{:actor_changed?, true} ->
# If prepare_outgoing changes the actor, re-get it from the db
new_actor = User.get_cached_by_ap_id(data["actor"])
{new_actor, data}
end
param_cc = Map.get(params, :cc, []) param_cc = Map.get(params, :cc, [])
@ -115,10 +136,10 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64()) digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
date = Pleroma.Signature.signed_date() date = @signature_impl.signed_date()
signature = signature =
Pleroma.Signature.sign(actor, %{ @signature_impl.sign(actor, %{
"(request-target)": "post #{path}", "(request-target)": "post #{path}",
host: signature_host(uri), host: signature_host(uri),
"content-length": byte_size(json), "content-length": byte_size(json),
@ -310,17 +331,21 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
Repo.checkout(fn -> Repo.checkout(fn ->
Enum.each([priority_inboxes, other_inboxes], fn inboxes -> Enum.each([priority_inboxes, other_inboxes], fn inboxes ->
Enum.each(inboxes, fn inbox -> Enum.each(inboxes, fn inbox ->
%User{ap_id: ap_id} = Enum.find(recipients, fn actor -> actor.inbox == inbox end) {%User{ap_id: ap_id}, priority} =
get_user_with_priority(inbox, priority_recipients, recipients)
# Get all the recipients on the same host and add them to cc. Otherwise, a remote # Get all the recipients on the same host and add them to cc. Otherwise, a remote
# instance would only accept a first message for the first recipient and ignore the rest. # instance would only accept a first message for the first recipient and ignore the rest.
cc = get_cc_ap_ids(ap_id, recipients) cc = get_cc_ap_ids(ap_id, recipients)
__MODULE__.enqueue_one(%{ __MODULE__.enqueue_one(
inbox: inbox, %{
cc: cc, inbox: inbox,
activity_id: activity.id cc: cc,
}) activity_id: activity.id
},
priority: priority
)
end) end)
end) end)
end) end)
@ -382,4 +407,15 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
end end
def gather_nodeinfo_protocol_names, do: ["activitypub"] def gather_nodeinfo_protocol_names, do: ["activitypub"]
defp get_user_with_priority(inbox, priority_recipients, recipients) do
[{priority_recipients, 0}, {recipients, 1}]
|> Enum.find_value(fn {recipients, priority} ->
with %User{} = user <- Enum.find(recipients, fn actor -> actor.inbox == inbox end) do
{user, priority}
else
_ -> nil
end
end)
end
end end

View file

@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
@moduledoc """ @moduledoc """
A module to handle coding from internal to wire ActivityPub and back. A module to handle coding from internal to wire ActivityPub and back.
""" """
@behaviour Pleroma.Web.ActivityPub.Transmogrifier.API
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Maps alias Pleroma.Maps
@ -22,7 +23,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Federator alias Pleroma.Web.Federator
import Ecto.Query
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1] import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
require Pleroma.Constants require Pleroma.Constants
@ -103,6 +103,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
end end
@doc """
Bovine compatibility
https://codeberg.org/bovine/bovine/issues/53
"""
def fix_addressing_public(map, field) do
addrs = Map.get(map, field, []) |> List.wrap()
Map.put(
map,
field,
Enum.map(addrs, fn
"Public" -> Pleroma.Constants.as_public()
"as:Public" -> Pleroma.Constants.as_public()
x -> x
end)
)
end
# if directMessage flag is set to true, leave the addressing alone # if directMessage flag is set to true, leave the addressing alone
def fix_explicit_addressing(%{"directMessage" => true} = object, _follower_collection), def fix_explicit_addressing(%{"directMessage" => true} = object, _follower_collection),
do: object do: object
@ -160,6 +178,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> fix_addressing_list("cc") |> fix_addressing_list("cc")
|> fix_addressing_list("bto") |> fix_addressing_list("bto")
|> fix_addressing_list("bcc") |> fix_addressing_list("bcc")
|> fix_addressing_public("to")
|> fix_addressing_public("cc")
|> fix_addressing_public("bto")
|> fix_addressing_public("bcc")
|> fix_explicit_addressing(follower_collection) |> fix_explicit_addressing(follower_collection)
|> fix_implicit_addressing(follower_collection) |> fix_implicit_addressing(follower_collection)
end end
@ -739,48 +761,26 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def set_quote_url(obj), do: obj def set_quote_url(obj), do: obj
@doc """ @doc """
Serialized Mastodon-compatible `replies` collection containing _self-replies_. Inline first page of the `replies` collection,
Based on Mastodon's ActivityPub::NoteSerializer#replies. containing any replies in chronological order.
""" """
def set_replies(obj_data) do def set_replies(%{"type" => type} = obj_data)
replies_uris = when type in Pleroma.Constants.status_object_types() do
with limit when limit > 0 <- with obj_ap_id when is_binary(obj_ap_id) <- obj_data["id"],
Pleroma.Config.get([:activitypub, :note_replies_output_limit], 0), limit when limit > 0 <-
%Object{} = object <- Object.get_cached_by_ap_id(obj_data["id"]) do Pleroma.Config.get([:activitypub, :note_replies_output_limit], 0),
object collection <-
|> Object.self_replies() Pleroma.Web.ActivityPub.ObjectView.render("object_replies.json", %{
|> select([o], fragment("?->>'id'", o.data)) render_params: %{object_ap_id: obj_data["id"], limit: limit, skip_ap_ctx: true}
|> limit(^limit) }) do
|> Repo.all() Map.put(obj_data, "replies", collection)
else else
_ -> [] 0 -> Map.put(obj_data, "replies", obj_data["id"] <> "/replies")
end _ -> obj_data
end
set_replies(obj_data, replies_uris)
end end
defp set_replies(obj, []) do def set_replies(obj_data), do: obj_data
obj
end
defp set_replies(obj, replies_uris) do
replies_collection = %{
"type" => "Collection",
"items" => replies_uris
}
Map.merge(obj, %{"replies" => replies_collection})
end
def replies(%{"replies" => %{"first" => %{"items" => items}}}) when not is_nil(items) do
items
end
def replies(%{"replies" => %{"items" => items}}) when not is_nil(items) do
items
end
def replies(_), do: []
# Prepares the object of an outgoing create activity. # Prepares the object of an outgoing create activity.
def prepare_object(object) do def prepare_object(object) do
@ -850,6 +850,27 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, data} {:ok, data}
end end
def prepare_outgoing(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data)
when objtype in Pleroma.Constants.actor_types() do
object =
object
|> maybe_fix_user_object()
|> strip_internal_fields()
data =
data
|> Map.put("object", object)
|> strip_internal_fields()
|> Map.merge(Utils.make_json_ld_header(object))
|> Map.delete("bcc")
{:ok, data}
end
def prepare_outgoing(%{"type" => "Update", "object" => %{}} = data) do
raise "Requested to serve an Update for non-updateable object type: #{inspect(data)}"
end
def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do
object = object =
object_id object_id
@ -909,6 +930,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
end end
def prepare_outgoing(%{"type" => "Flag"} = data) do
with {:ok, stripped_activity} <- Utils.strip_report_status_data(data),
stripped_activity <- Utils.maybe_anonymize_reporter(stripped_activity),
stripped_activity <- Map.merge(stripped_activity, Utils.make_json_ld_header()) do
{:ok, stripped_activity}
end
end
def prepare_outgoing(%{"type" => _type} = data) do def prepare_outgoing(%{"type" => _type} = data) do
data = data =
data data

View file

@ -0,0 +1,11 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Transmogrifier.API do
@moduledoc """
Behaviour for the subset of Transmogrifier used by Publisher.
"""
@callback prepare_outgoing(map()) :: {:ok, map()} | {:error, term()}
end

View file

@ -82,7 +82,11 @@ defmodule Pleroma.Web.ActivityPub.Utils do
def unaddressed_message?(params), def unaddressed_message?(params),
do: do:
[params["to"], params["cc"], params["bto"], params["bcc"]] [params["to"], params["cc"], params["bto"], params["bcc"]]
|> Enum.all?(&is_nil(&1)) |> Enum.all?(fn
nil -> true
[] -> true
_ -> false
end)
@spec recipient_in_message(User.t(), User.t(), map()) :: boolean() @spec recipient_in_message(User.t(), User.t(), map()) :: boolean()
def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params), def recipient_in_message(%User{ap_id: ap_id} = recipient, %User{} = actor, params),
@ -859,8 +863,14 @@ defmodule Pleroma.Web.ActivityPub.Utils do
def update_report_state(_, _), do: {:error, "Unsupported state"} def update_report_state(_, _), do: {:error, "Unsupported state"}
def strip_report_status_data(activity) do def strip_report_status_data(%Activity{} = activity) do
[actor | reported_activities] = activity.data["object"] with {:ok, new_data} <- strip_report_status_data(activity.data) do
{:ok, %{activity | data: new_data}}
end
end
def strip_report_status_data(data) do
[actor | reported_activities] = data["object"]
stripped_activities = stripped_activities =
Enum.reduce(reported_activities, [], fn act, acc -> Enum.reduce(reported_activities, [], fn act, acc ->
@ -870,9 +880,36 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end end
end) end)
new_data = put_in(activity.data, ["object"], [actor | stripped_activities]) new_data = put_in(data, ["object"], [actor | stripped_activities])
{:ok, %{activity | data: new_data}} {:ok, new_data}
end
def get_anonymized_reporter do
with true <- Pleroma.Config.get([:activitypub, :anonymize_reporter]),
nickname when is_binary(nickname) <-
Pleroma.Config.get([:activitypub, :anonymize_reporter_local_nickname]),
%User{ap_id: ap_id, local: true} <- User.get_cached_by_nickname(nickname) do
ap_id
else
_ -> nil
end
end
def maybe_anonymize_reporter(%Activity{data: data} = activity) do
new_data = maybe_anonymize_reporter(data)
%Activity{activity | actor: new_data["actor"], data: new_data}
end
def maybe_anonymize_reporter(activity) do
ap_id = get_anonymized_reporter()
if is_binary(ap_id) do
activity
|> Map.put("actor", ap_id)
else
activity
end
end end
def update_activity_visibility(activity, visibility) when visibility in @valid_visibilities do def update_activity_visibility(activity, visibility) when visibility in @valid_visibilities do

View file

@ -0,0 +1,59 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# Copyright © 2025 Akkoma Authors <https://akkoma.dev/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.CollectionViewHelper do
alias Pleroma.Web.ActivityPub.Utils
def collection_page_offset(collection, iri, page, show_items \\ true, total \\ nil) do
offset = (page - 1) * 10
items = Enum.slice(collection, offset, 10)
items = Enum.map(items, fn user -> user.ap_id end)
total = total || length(collection)
map = %{
"id" => "#{iri}?page=#{page}",
"type" => "OrderedCollectionPage",
"partOf" => iri,
"totalItems" => total,
"orderedItems" => if(show_items, do: items, else: [])
}
if offset + 10 < total do
Map.put(map, "next", "#{iri}?page=#{page + 1}")
else
map
end
end
defp maybe_omit_next(pagination, _items, nil), do: pagination
defp maybe_omit_next(pagination, items, limit) when is_binary(limit) do
case Integer.parse(limit) do
{limit, ""} -> maybe_omit_next(pagination, items, limit)
_ -> maybe_omit_next(pagination, items, nil)
end
end
defp maybe_omit_next(pagination, items, limit) when is_number(limit) do
if Enum.count(items) < limit, do: Map.delete(pagination, "next"), else: pagination
end
def collection_page_keyset(
display_items,
pagination,
limit \\ nil,
skip_ap_context \\ false
) do
%{
"type" => "OrderedCollectionPage",
"orderedItems" => display_items
}
|> Map.merge(pagination)
|> maybe_omit_next(display_items, limit)
|> then(fn m ->
if skip_ap_context, do: m, else: Map.merge(m, Utils.make_json_ld_header())
end)
end
end

View file

@ -6,7 +6,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
use Pleroma.Web, :view use Pleroma.Web, :view
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.CollectionViewHelper
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ControllerHelper
def render("object.json", %{object: %Object{} = object}) do def render("object.json", %{object: %Object{} = object}) do
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header(object.data) base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header(object.data)
@ -15,26 +18,94 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
Map.merge(base, additional) Map.merge(base, additional)
end end
def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} = activity}) def render("object.json", %{object: %Activity{} = activity}) do
when activity_type in ["Create", "Listen"] do {:ok, ap_data} = Transmogrifier.prepare_outgoing(activity.data)
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header(activity.data) ap_data
object = Object.normalize(activity, fetch: false)
additional =
Transmogrifier.prepare_object(activity.data)
|> Map.put("object", Transmogrifier.prepare_object(object.data))
Map.merge(base, additional)
end end
def render("object.json", %{object: %Activity{} = activity}) do def render("object_replies.json", %{
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header(activity.data) conn: conn,
object_id = Object.normalize(activity, id_only: true) render_params: %{object_ap_id: object_ap_id, page: "true"} = params
}) do
params = Map.put_new(params, :limit, 40)
additional = items = ActivityPub.fetch_objects_for_replies_collection(object_ap_id, params)
Transmogrifier.prepare_object(activity.data) display_items = map_reply_collection_items(items)
|> Map.put("object", object_id)
Map.merge(base, additional) pagination = ControllerHelper.get_pagination_fields(conn, items, %{}, :asc)
CollectionViewHelper.collection_page_keyset(display_items, pagination, params[:limit])
end
def render(
"object_replies.json",
%{
render_params: %{object_ap_id: object_ap_id} = params
} = opts
) do
params =
params
|> Map.drop([:max_id, :min_id, :since_id, :object_ap_id])
|> Map.put_new(:limit, 40)
|> Map.put(:total, true)
%{total: total, items: items} =
ActivityPub.fetch_objects_for_replies_collection(object_ap_id, params)
display_items = map_reply_collection_items(items)
first_pagination = reply_collection_first_pagination(items, opts)
col_ap =
%{
"id" => object_ap_id <> "/replies",
"type" => "OrderedCollection",
"totalItems" => total
}
col_ap =
if total > 0 do
first_page =
CollectionViewHelper.collection_page_keyset(
display_items,
first_pagination,
params[:limit],
true
)
Map.put(col_ap, "first", first_page)
else
col_ap
end
if params[:skip_ap_ctx] do
col_ap
else
Map.merge(col_ap, Pleroma.Web.ActivityPub.Utils.make_json_ld_header())
end
end
defp map_reply_collection_items(items), do: Enum.map(items, fn %{ap_id: ap_id} -> ap_id end)
defp reply_collection_first_pagination(items, %{conn: %Plug.Conn{} = conn}) do
pagination = ControllerHelper.get_pagination_fields(conn, items, %{"page" => true}, :asc)
Map.put(pagination, "id", Phoenix.Controller.current_url(conn, %{"page" => true}))
end
defp reply_collection_first_pagination(items, %{render_params: %{object_ap_id: object_ap_id}}) do
%{
"id" => object_ap_id <> "/replies?page=true",
"partOf" => object_ap_id <> "/replies"
}
|> then(fn m ->
case items do
[] ->
m
i ->
next_id = object_ap_id <> "/replies?page=true&min_id=#{List.last(i)[:id]}"
Map.put(m, "next", next_id)
end
end)
end end
end end

View file

@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.ActivityPub.CollectionViewHelper
alias Pleroma.Web.ActivityPub.ObjectView alias Pleroma.Web.ActivityPub.ObjectView
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
@ -164,7 +165,13 @@ defmodule Pleroma.Web.ActivityPub.UserView do
0 0
end end
collection(following, "#{user.ap_id}/following", page, showing_items, total) CollectionViewHelper.collection_page_offset(
following,
"#{user.ap_id}/following",
page,
showing_items,
total
)
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end
@ -189,7 +196,12 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"totalItems" => total, "totalItems" => total,
"first" => "first" =>
if showing_items do if showing_items do
collection(following, "#{user.ap_id}/following", 1, !user.hide_follows) CollectionViewHelper.collection_page_offset(
following,
"#{user.ap_id}/following",
1,
!user.hide_follows
)
else else
"#{user.ap_id}/following?page=1" "#{user.ap_id}/following?page=1"
end end
@ -212,7 +224,13 @@ defmodule Pleroma.Web.ActivityPub.UserView do
0 0
end end
collection(followers, "#{user.ap_id}/followers", page, showing_items, total) CollectionViewHelper.collection_page_offset(
followers,
"#{user.ap_id}/followers",
page,
showing_items,
total
)
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end
@ -236,7 +254,12 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"type" => "OrderedCollection", "type" => "OrderedCollection",
"first" => "first" =>
if showing_items do if showing_items do
collection(followers, "#{user.ap_id}/followers", 1, showing_items, total) CollectionViewHelper.collection_page_offset(
followers,
"#{user.ap_id}/followers",
1,
showing_items
)
else else
"#{user.ap_id}/followers?page=1" "#{user.ap_id}/followers?page=1"
end end
@ -256,7 +279,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
def render("activity_collection_page.json", %{ def render("activity_collection_page.json", %{
activities: activities, activities: activities,
iri: iri,
pagination: pagination pagination: pagination
}) do }) do
collection = collection =
@ -265,13 +287,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
data data
end) end)
%{ CollectionViewHelper.collection_page_keyset(collection, pagination)
"type" => "OrderedCollectionPage",
"partOf" => iri,
"orderedItems" => collection
}
|> Map.merge(Utils.make_json_ld_header())
|> Map.merge(pagination)
end end
def render("featured.json", %{ def render("featured.json", %{
@ -299,27 +315,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
Map.put(map, "totalItems", total) Map.put(map, "totalItems", total)
end end
def collection(collection, iri, page, show_items \\ true, total \\ nil) do
offset = (page - 1) * 10
items = Enum.slice(collection, offset, 10)
items = Enum.map(items, fn user -> user.ap_id end)
total = total || length(collection)
map = %{
"id" => "#{iri}?page=#{page}",
"type" => "OrderedCollectionPage",
"partOf" => iri,
"totalItems" => total,
"orderedItems" => if(show_items, do: items, else: [])
}
if offset < total do
Map.put(map, "next", "#{iri}?page=#{page + 1}")
else
map
end
end
defp maybe_make_image(func, description, key, user) do defp maybe_make_image(func, description, key, user) do
if image = func.(user, no_default: true) do if image = func.(user, no_default: true) do
%{ %{

View file

@ -73,6 +73,22 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|> Pleroma.List.member?(user) |> Pleroma.List.member?(user)
end end
def visible_for_user?(%Activity{object: %Object{} = object} = activity, nil) do
activity_visibility? = restrict_unauthenticated_access?(activity)
activity_public? = public?(activity) and not local_public?(activity)
object_visibility? = restrict_unauthenticated_access?(object)
object_public? = public?(object) and not local_public?(object)
# Activity could be local, but object might not (Announce/Like)
cond do
activity_visibility? or object_visibility? ->
false
true ->
activity_public? and object_public?
end
end
def visible_for_user?(%{__struct__: module} = message, nil) def visible_for_user?(%{__struct__: module} = message, nil)
when module in [Activity, Object] do when module in [Activity, Object] do
if restrict_unauthenticated_access?(message), if restrict_unauthenticated_access?(message),

Some files were not shown because too many files have changed in this diff Show more