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
# Only used for the release
@ -16,9 +16,15 @@ workflow:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "develop"
- if: $CI_COMMIT_BRANCH == "stable"
- if: $CI_PIPELINE_SOURCE == "web"
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never
# Default artifacts configuration
.default_artifacts: &default_artifacts
expire_in: 30 days
cache: &global_cache_policy
key: $CI_JOB_IMAGE-$CI_COMMIT_SHORT_SHA
paths:
@ -56,6 +62,7 @@ check-changelog:
before_script: ''
after_script: ''
cache: {}
artifacts: *default_artifacts
script:
- apk add git
- sh ./tools/check-changelog
@ -71,8 +78,9 @@ check-changelog:
.using-ci-base:
tags:
- amd64
artifacts: *default_artifacts
build-1.14.5-otp-25:
build-1.15.8-otp-26:
extends:
- .build_changes_policy
- .using-ci-base
@ -101,8 +109,12 @@ spec-build:
artifacts:
paths:
- spec.json
reports:
dotenv: build.env
expire_in: 42 years
script:
- mix pleroma.openapi_spec spec.json
- echo "SPEC_BUILD_JOB_ID=$CI_JOB_ID" >> build.env
benchmark:
extends:
@ -120,7 +132,7 @@ benchmark:
- mix ecto.migrate
- mix pleroma.load_testing
unit-testing-1.14.5-otp-25:
unit-testing-1.15.8-otp-26:
extends:
- .build_changes_policy
- .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"
coverage: '/^Line total: ([^ ]*%)$/'
artifacts:
expire_in: 30 days
reports:
coverage_report:
coverage_format: cobertura
@ -171,6 +184,7 @@ unit-testing-1.18.3-otp-27:
formatting-1.15:
extends: .build_changes_policy
artifacts: *default_artifacts
image: &formatting_elixir elixir:1.15-alpine
stage: lint
cache: *testing_cache_policy
@ -185,6 +199,7 @@ formatting-1.15:
cycles-1.15:
extends: .build_changes_policy
artifacts: *default_artifacts
image: *formatting_elixir
stage: lint
cache: {}
@ -208,7 +223,7 @@ dialyzer:
- .using-ci-base
stage: lint
allow_failure: true
when: manual
when: manual
cache: *testing_cache_policy
tags:
- feld
@ -217,15 +232,14 @@ dialyzer:
docs-deploy:
stage: deploy
cache: *testing_cache_policy
image: alpine:latest
trigger:
project: pleroma/docs
branch: master
strategy: depend
only:
- stable@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:
image: alpine:3.9
stage: deploy
@ -241,6 +255,7 @@ review_app:
except:
- master
- develop
artifacts: *default_artifacts
script:
- echo "$CI_ENVIRONMENT_SLUG"
- mkdir -p ~/.ssh
@ -257,21 +272,19 @@ review_app:
spec-deploy:
stage: deploy
artifacts:
paths:
- spec.json
trigger:
project: pleroma/api-docs
branch: master
strategy: depend
only:
- develop@pleroma/pleroma
image: alpine:latest
before_script:
- 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
variables:
SPEC_BUILD_JOB_ID: $SPEC_BUILD_JOB_ID
stop_review_app:
image: alpine:3.9
stage: deploy
artifacts: *default_artifacts
before_script:
- apk update && apk add openssh-client git
when: manual
@ -290,7 +303,7 @@ stop_review_app:
amd64:
stage: release
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
- stable@pleroma/pleroma
- develop@pleroma/pleroma
@ -317,7 +330,7 @@ amd64:
VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS
DEBIAN_FRONTEND: noninteractive
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
- mix local.hex --force
- mix local.rebar --force
@ -333,7 +346,7 @@ amd64-musl:
artifacts: *release-artifacts
only: *release-only
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:
- amd64
cache: *release-cache
@ -377,7 +390,7 @@ arm64:
tags:
- arm
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
variables: *release-variables
before_script: *before-release
@ -390,7 +403,7 @@ arm64-musl:
tags:
- arm
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
variables: *release-variables
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/).
## 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
### Security
- 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
- 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.
### Changed
- Include `pl-fe` in available frontends
### Fixed
- Remove trailing ` from end of line 75 which caused issues copy-pasting
## 2.9.0
### Security
- Require HTTP signatures (if enabled) for routes used by both C2S and S2S AP API
- Fix several spoofing vectors
### 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
- Include "published" in actor view
- Link to exported outbox/followers/following collections in backup actor.json
- Hashtag following
- Allow to specify post language
### Fixed
- Verify a local Update sent through AP C2S so users can only update their own objects
- Fix Mastodon incoming edits with inlined "likes"
- 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
### Removed
- Retire MRFs DNSRBL, FODirectReply, and QuietReply
## 2.8.0
### Changed
- 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
- 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.
### Added
- Add metadata provider for ActivityPub alternate links
- Added support for argon2 passwords and their conversion for migration from Akkoma fork to upstream.
- 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
### Fixed
- 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.
- 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
### Removed
- Remove stub for /api/v1/accounts/:id/identity_proofs (deprecated by Mastodon 3.5.0)
## 2.7.1
### Changed
- Accept `application/activity+json` for requests to `/.well-known/nodeinfo`
### Fixed
- Truncate remote user fields, avoids them getting rejected
- 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.
@ -122,16 +239,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2.7.0
### Security
- HTTP Security: By default, don't allow unsafe-eval. The setting needs to be changed to allow Flash emulation.
- Fix webfinger spoofing.
- Use proper workers for fetching pins instead of an ad-hoc task, fixing a potential fetch loop
### Changed
- Update to Phoenix 1.7
- Elixir Logger configuration is now longer permitted through AdminFE and ConfigDB
- Refactor the user backups code and improve test coverage
- 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
- Replace eblurhash with rinpatch_blurhash. This also removes a dependency on ImageMagick.
- 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
### Added
- Uploader: Add support for uploading attachments using IPFS
- Add NSFW-detecting 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.
### Fixed
- Fix Emoji object IDs not always being valid
- Remove checking ImageMagick's commands for Pleroma.Upload.Filter.AnalyzeMetadata
- 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
- MediaProxy Preview failures prevented when encountering certain video files
- 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
- Rich Media Preview cache eviction when the activity is updated.
- 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
### Removed
- 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
## 2.6.2
### Security
- MRF StealEmojiPolicy: Sanitize shortcodes (thanks to Hazel K for the report
## 2.6.1
### Changed
- - Document maximum supported version of Erlang & Elixir
### Added
- [docs] add frontends management documentation
### Fixed
- TwitterAPI: Return proper error when healthcheck is disabled
- Fix eblurhash and elixir-captcha not using system cflags
## 2.6.0
### 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.
- 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
### Added
- Support for Image activities, namely from Hubzilla
- Add OAuth scope descriptions
- 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
### Fixed
- rel="me" was missing its cache
- MediaProxy responses now return a sandbox CSP header
- 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
### Removed
- BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact)
- remove BBS/SSH feature, replaced by an external bridge.
- 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
## Security
- Fix XML External Entity (XXE) loading vulnerability allowing to fetch arbitrary files from the server's filesystem
## 2.5.3
### Security
- 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
## 2.5.5
## Security
- Prevent users from accessing media of other users by creating a status with reused attachment ID
## 2.5.4
## Security
- Fix XML External Entity (XXE) loading vulnerability allowing to fetch arbitrary files from the server's filesystem
## 2.5.3
### Security
- 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
## 2.5.2
### Security
- `/proxy` endpoint now sets a Content-Security-Policy (sandbox)
- WebSocket endpoint now respects unauthenticated restrictions for streams of public posts
- OEmbed HTML tags are now filtered
### Changed
- docs: Be more explicit about the level of compatibility of OTP releases
- Set default background worker timeout to 15 minutes
### Fixed
- Atom/RSS formatting (HTML truncation, published, missing summary)
- Remove `static_fe` pipeline for `/users/:nickname/feed`
- 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
### Removed
- BREAKING: Support for passwords generated with `crypt(3)` (Gnu Social migration artifact)
## 2.5.1
### Added
- Allow customizing instance languages
### Fixed
- 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
- 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
### Changed
- **Breaking:** Elixir >=1.11 is now required (was >= 1.9)
- 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`
@ -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
### Added
- `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.
- `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.
### Fixed
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
- Handle Reject for already-accepted Follows properly
- 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
## Fixed
- 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)
- 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
### Security
- Streaming API sessions will now properly disconnect if the corresponding token is revoked
## 2.4.3 - 2022-05-06
### Security
- Private `/objects/` and `/activities/` leaking if cached by authenticated user
- SweetXML library DTD bomb
## 2.4.2 - 2022-01-10
### Fixed
- Federation issues caused by HTTP pool checkout timeouts
- 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
### Changed
- Make `mix pleroma.database set_text_search_config` run concurrently and indefinitely
### Added
- AdminAPI: Missing configuration description for StealEmojiPolicy
### Fixed
- MastodonAPI: Stream out Create activities
- MRF ObjectAgePolicy: Fix pattern matching on "published"
- 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
### Fixed
- Don't crash so hard when email settings are invalid.
- Checking activated Upload Filters for required commands.
- Remote users can no longer reappear after being deleted.
@ -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.
### Removed
- **Breaking**: Remove deprecated `/api/qvitter/statuses/notifications/read` (replaced by `/api/v1/pleroma/notifications/read`)
## [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
### Changed
- Updated Pleroma FE
### 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.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
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
@ -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)
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
## [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
### 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 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.
@ -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.
### Added
- Rich media failure tracking (along with `:failure_backoff` option).
<details>
@ -803,6 +963,7 @@ switched to a new configuration mechanism, however it was not officially removed
</details>
### Fixed
- 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
- 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>
### Fixed
- Fix list pagination and other list issues.
- Support pagination in conversations API
- **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
### Security
- Fix potential DoSes exploiting atom leaks in rich media parser and the `UserAllowListPolicy` MRF policy
### Fixed
- CSP: not allowing images/media from every host when mediaproxy is disabled
- CSP: not adding mediaproxy base url to image/media hosts
- 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
### Security
- CSP: harden `image-src` and `media-src` when MediaProxy is used
### Fixed
- AP C2S: Fix pagination in inbox/outbox
- Various compilation errors on OTP 23
- Mastodon API streaming: Repeats from muted threads not being filtered
### Changed
- Various database performance improvements
### Upgrade notes
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.0.5] - 2020-05-13
### Security
- Fix possible private status leaks in Mastodon Streaming API
### Fixed
- Crashes when trying to block a user if block federation is disabled
- Not being able to start the instance without `erlang-eldap` installed
- 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
### Security
- AP C2S: Fix a potential DoS by creating nonsensical objects that break timelines
### Fixed
- Peertube user lookups not working
- `InsertSkeletonsForDeletedUsers` migration failing on some instances
- 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
1. Remove the following line from your config:
```
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
### Security
- 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
- CSP: Sandbox uploads
### Fixed
- Notifications from blocked domains
- Potential federation issues with Mastodon versions before 3.0.0
- 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
### Added
- NodeInfo: ObjectAgePolicy settings to the `federation` list.
- Follow request notifications
<details>
@ -1027,19 +1205,24 @@ switched to a new configuration mechanism, however it was not officially removed
1. Restart Pleroma
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
### Added
- Support for Funkwhale's `Audio` activity
- Admin API: `PATCH /api/pleroma/admin/users/:nickname/update_credentials`
### Fixed
- Blocked/muted users still generating push notifications
- Input textbox for bio ignoring newlines
- 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
### Upgrade notes
1. Restart Pleroma
## [2.0.1] - 2020-03-15
### Security
- Static-FE: Fix remote posts not being sanitized
### Fixed
- 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
- 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
### Changed
- 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
### Upgrade notes
#### Nginx only
1. Remove `proxy_ignore_headers Cache-Control;` and `proxy_hide_header Cache-Control;` from your config.
#### Everyone
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.0.0] - 2019-03-08
### Security
- Mastodon API: Fix being able to request enormous amount of statuses in timelines leading to DoS. Now limited to 40 per request.
### Removed
- **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media`
- **Breaking**: OStatus protocol support
- **Breaking**: MDII uploader
@ -1093,6 +1289,7 @@ switched to a new configuration mechanism, however it was not officially removed
</details>
### Changed
- **Breaking:** Pleroma won't start if it detects unapplied migrations
- **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!
@ -1142,6 +1339,7 @@ switched to a new configuration mechanism, however it was not officially removed
</details>
### Added
- `: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.
- Refreshing poll results for remote polls
@ -1209,6 +1407,7 @@ switched to a new configuration mechanism, however it was not officially removed
</details>
### Fixed
- Report emails now include functional links to profiles of remote user accounts
- Not being able to log in to some third-party apps when logged in to MastoFE
- MRF: `Delete` activities being exempt from MRF policies
@ -1228,7 +1427,9 @@ switched to a new configuration mechanism, however it was not officially removed
</details>
## [1.1.9] - 2020-02-10
### Fixed
- OTP: Inability to set the upload limit (again)
- Not being able to pin polls
- 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
## [1.1.8] - 2020-01-10
### Fixed
- Captcha generation issues
- Returned Kocaptcha endpoint to configuration
- Captcha validity is now 5 minutes
## [1.1.7] - 2019-12-13
### Fixed
- OTP: Inability to set the upload limit
- OTP: Inability to override node name/distribution type to run 2 Pleroma instances on the same machine
### Added
- Integrated captcha provider
### Changed
- Captcha enabled by default
- Default Captcha provider changed from `Pleroma.Captcha.Kocaptcha` to `Pleroma.Captcha.Native`
- Better `Cache-Control` header for static content
### Bundled Pleroma-FE Changes
#### Added
- Icons in the navigation panel
#### Fixed
- Improved support unauthenticated view of private instances
#### Removed
- Whitespace hack on empty post content
## [1.1.6] - 2019-11-19
### Fixed
- 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
- Mastodon API: conversations API crashing when one status is malformed
### Bundled Pleroma-FE Changes
#### Added
- About page
- Meme arrows
#### Fixed
- Image modal not closing unless clicked outside of image
- Attachment upload spinner not being centered
- Showing follow counters being 0 when they are actually hidden
## [1.1.5] - 2019-11-09
### Fixed
- 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
### Changed
- Frontend bundle updated to [044c9ad0](https://git.pleroma.social/pleroma/pleroma-fe/commit/044c9ad0562af059dd961d50961a3880fca9c642)
## [1.1.4] - 2019-11-01
### Fixed
- 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
- Mastodon API: Notification stream not including follow notifications
## [1.1.3] - 2019-10-25
### Fixed
- Blocked users showing up in notifications collapsed as if they were muted
- `pleroma_ctl` not working on Debian's default shell
## [1.1.2] - 2019-10-18
### Fixed
- `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
### 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:
```
delete from users where ap_id = 'https://your.instance.hostname/relay';
```
- Bad user search matches
## [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.
**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'
```
### Security
- Mastodon API: respect post privacy in `/api/v1/statuses/:id/{favourited,reblogged}_by`
### Removed
- **Breaking:** GNU Social API with Qvitter extensions support
- Emoji: Remove longfox emojis.
- Remove `Reply-To` header from report emails for admins.
- ActivityPub: The `/objects/:uuid/likes` endpoint.
### 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: `/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.
@ -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
- AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private statuses)
- 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.
### Fixed
- Following from Osada
- Favorites timeline doing database-intensive queries
- 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
### Added
- 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: 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
### Changed
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
- Admin API: changed json structure for saving config settings.
- RichMedia: parsers and their order are configured in `rich_media` config.
- RichMedia: add the rich media ttl based on image expiration time.
## [1.0.7] - 2019-09-26
### Fixed
- Broken federation on Erlang 22 (previous versions of hackney http client were using an option that got deprecated)
### Changed
- ActivityPub: The first page in inboxes/outboxes is no longer embedded.
## [1.0.6] - 2019-08-14
### Fixed
- MRF: fix use of unserializable keyword lists in describe() implementations
- ActivityPub S2S: POST requests are now signed with `(request-target)` pseudo-header.
## [1.0.5] - 2019-08-13
### Fixed
- 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: 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.
### Added
- **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.
- 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
### Removed
- Federation: Remove `likes` from objects.
- **Breaking:** ActivityPub: The `accept_blocks` configuration setting.
## [1.0.4] - 2019-08-01
### Fixed
- 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
### Security
- OStatus: eliminate the possibility of a protocol downgrade attack.
- OStatus: prevent following locked accounts, bypassing the approval process.
- TwitterAPI: use CommonAPI to handle remote follows instead of OStatus.
## [1.0.2] - 2019-07-28
### Fixed
- Not being able to pin unlisted posts
- Mastodon API: represent poll IDs as strings
- 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.
### Changed
- Configuration: OpenGraph and TwitterCard providers enabled by default
- Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text
## [1.0.1] - 2019-07-14
### Security
- OStatus: fix an object spoofing vulnerability.
## [1.0.0] - 2019-06-29
### Security
- Mastodon API: Fix display names not being sanitized
- Rich media: Do not crawl private IP ranges
### Added
- Digest email for inactive users
- Add a generic settings store for frontends / clients to use.
- 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"
### Changed
- **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
- 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.
### Fixed
- Follow requests don't get 'stuck' anymore.
- 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
@ -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
## Removed
- Configuration: `config :pleroma, :fe` in favor of the more flexible `config :pleroma, :frontend_configurations`
## [0.9.99999] - 2019-05-31
### Security
- Mastodon API: Fix lists leaking private posts
## [0.9.9999] - 2019-04-05
### Security
- Mastodon API: Fix content warnings skipping HTML sanitization
## [0.9.999] - 2019-03-13
Frontend changes only.
### Added
- Added floating action button for posting status on mobile
### Changed
- Changed user-settings icon to a pencil
### Fixed
- Keyboard shortcuts activating when typing a message
- Gaps when scrolling down on a timeline after showing new
## [0.9.99] - 2019-03-08
### Changed
- Update the frontend to the 0.9.99 tag
### Fixed
- Sign the date header in federation to fix Mastodon federation.
## [0.9.9] - 2019-02-22
This is our first stable release.

View file

@ -1,10 +1,10 @@
# https://hub.docker.com/r/hexpm/elixir/tags
ARG ELIXIR_IMG=hexpm/elixir
ARG ELIXIR_VER=1.14.5
ARG ERLANG_VER=25.3.2.14
ARG ELIXIR_VER=1.17.3
ARG ERLANG_VER=26.2.5.6
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 . .
@ -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 &&\
mix local.hex --force &&\
mix local.rebar --force &&\
mix deps.clean --all &&\
mix deps.get --only prod &&\
mkdir 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,
sign_object_fetches: true,
authorized_fetch_mode: false,
client_api_enabled: false
client_api_enabled: false,
anonymize_reporter: false,
anonymize_reporter_local_nickname: ""
config :pleroma, :streamer,
workers: 3,
@ -613,7 +615,7 @@ config :pleroma, Oban,
search_indexing: [limit: 10, paused: true],
slow: 5
],
plugins: [{Oban.Plugins.Pruner, max_age: 900}],
plugins: [Oban.Plugins.Lazarus, {Oban.Plugins.Pruner, max_age: 900}],
crontab: [
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker},

View file

@ -1797,6 +1797,23 @@ config :pleroma, :config_description, [
key: :client_api_enabled,
type: :boolean,
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:
"Amount of milliseconds after which the HTTP request is forcibly terminated.",
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:
"A map containing available frontends and parameters for their installation.",
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,
type: :module,
suggestions: [
Pleroma.Language.LanguageDetector.Fasttext
]
suggestions: {:list_behaviour_implementations, Pleroma.Language.LanguageDetector.Provider}
},
%{
group: {:subgroup, Pleroma.Language.LanguageDetector.Fasttext},
@ -3539,10 +3565,7 @@ config :pleroma, :config_description, [
%{
key: :provider,
type: :module,
suggestions: [
Pleroma.Language.Translation.Deepl,
Pleroma.Language.Translation.Libretranslate
]
suggestions: {:list_behaviour_implementations, Pleroma.Language.Translation.Provider}
},
%{
group: {:subgroup, Pleroma.Language.Translation.Deepl},
@ -3571,6 +3594,27 @@ config :pleroma, :config_description, [
label: "LibreTranslate API Key",
type: :string,
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.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 =
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
- Homepage: <http://andstatus.org/>
- Source Code: <https://github.com/andstatus/andstatus/>
- Contact: [@AndStatus@mastodon.social](https://mastodon.social/@AndStatus)
- Platforms: Android
- Features: MastoAPI, ActivityPub (Client-to-Server)
@ -40,8 +41,8 @@ Feel free to contact us to be added to this list!
### Fedilab
- Homepage: <https://fedilab.app/>
- Source Code: <https://framagit.org/tom79/fedilab/>
- Contact: [@fedilab@framapiaf.org](https://framapiaf.org/users/fedilab)
- Source Code: <https://codeberg.org/tom79/Fedilab>
- Contact: [@apps@toot.fedilab.app](https://toot.fedilab.app/@apps)
- Platforms: Android
- 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
### Husky
- Source code: <https://git.mentality.rip/FWGS/Husky>
- Contact: [@Husky@enigmatic.observer](https://enigmatic.observer/users/Husky)
- Source code: <https://github.com/captainepoch/husky>
- Contact: [@husky@stereophonic.space](https://stereophonic.space/users/husky)
- Platforms: Android
- 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
- Homepage: <https://tuskyapp.github.io/>
- 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
- Features: MastoAPI, No Streaming
@ -76,10 +77,10 @@ Feel free to contact us to be added to this list!
- Platform: Android
- Features: MastoAPI, No Streaming
### Indigenous
- Homepage: <https://indigenous.realize.be/>
- Source Code: <https://github.com/swentel/indigenous-android/>
- Contact: [@swentel@realize.be](https://realize.be)
### IndiePass
- Homepage: <https://indiepass.app/>
- Source Code: <https://github.com/IndiePass/indiepass-android>
- Contact: [@marksuth@mastodon.social](https://mastodon.social/@marksuth)
- Platforms: Android
- Features: MastoAPI, No Streaming

View file

@ -904,21 +904,31 @@ config :logger, :console,
### 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 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:
`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.
!!! 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
### 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:
```
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.

View file

@ -66,9 +66,9 @@ Returned data:
"username": "somenick",
...
},
"id" : "1",
"unread" : 2,
"last_message" : {...}, // The last message in that chat
"id": "1",
"unread": 2,
"last_message": {...}, // The last message in that chat
"updated_at": "2020-04-21T15:11:46.000Z"
}
```
@ -93,8 +93,8 @@ Returned data:
"username": "somenick",
...
},
"id" : "1",
"unread" : 0,
"id": "1",
"unread": 0,
"updated_at": "2020-04-21T15:11:46.000Z"
}
```
@ -111,7 +111,7 @@ The modified chat message
### 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
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:
- with_muted: Include chats from muted users (boolean).
- pinned: Include only pinned chats (boolean).
Returned data:
@ -130,16 +131,16 @@ Returned data:
"username": "somenick",
...
},
"id" : "1",
"unread" : 2,
"last_message" : {...}, // The last message in that chat
"id": "1",
"unread": 2,
"last_message": {...}, // The last message in that chat
"updated_at": "2020-04-21T15:11:46.000Z"
}
]
```
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
@ -226,6 +227,32 @@ Deleting a chat message for given Chat id works like this:
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
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.
- `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.
- `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).
- `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:
- `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
- `with_muted`: include statuses/reactions from muted accounts
- `exclude_reblogs`: exclude reblogs
- `only_reblogs`: include only reblogs
- `exclude_replies`: exclude replies
- `exclude_visibilities`: exclude visibilities
@ -97,6 +100,9 @@ Endpoints which accept `with_relationships` parameter:
- `/api/v1/accounts/:id/followers`
- `/api/v1/accounts/:id/following`
- `/api/v1/mutes`
- `/api/v1/blocks`
- `/api/v1/search`
- `/api/v2/search`
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
* Method `POST`
* Authentication: required
* OAuth scope: `write:scrobbles`
* Params:
* `title`: the title of the media playing
* `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-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-nox`
- `git`

View file

@ -1,7 +1,7 @@
## Required dependencies
* PostgreSQL >=11.0
* Elixir >=1.14.0 <1.19
* Elixir >=1.15.0 <1.19
* Erlang OTP >=23.0.0 (supported: <28)
* git
* 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:
* `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`)
## `ffmpeg`
@ -33,5 +33,5 @@ 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.ReadDescription` 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: `Pleroma.Upload/filters` in `config/config.exs`)

View file

@ -56,7 +56,6 @@ http protocol pleroma { # Protocol for upstream Pleroma server
relay wwwtls {
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
@ -66,3 +65,16 @@ relay wwwtls {
# Example:
#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
# 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;
# 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_name example.tld;
listen 443 ssl http2;
listen [::]:443 ssl http2;
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;
@ -67,8 +82,14 @@ server {
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;
# 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;
ignore_invalid_headers off;
proxy_http_version 1.1;
@ -94,7 +115,9 @@ server {
# 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;
slice 1m;
proxy_cache_key $host$uri$is_args$args$slice_range;
@ -106,4 +129,95 @@ server {
chunked_transfer_encoding on;
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.
"""
import Ecto.Query, only: [from: 2, where: 3]
import Ecto.Query, only: [from: 2]
@type query :: Ecto.Queryable.t() | Pleroma.Activity.t()
@ -70,22 +70,6 @@ defmodule Pleroma.Activity.Queries do
)
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
def by_type(query \\ Activity, activity_type) do
from(

View file

@ -25,6 +25,8 @@ defmodule Pleroma.Chat do
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
field(:recipient, :string)
field(:pinned, :boolean)
timestamps()
end
@ -94,4 +96,16 @@ defmodule Pleroma.Chat do
order_by: [desc: c.updated_at]
)
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

View file

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

View file

@ -157,6 +157,16 @@ defmodule Pleroma.FollowingRelationship do
|> Repo.all()
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
__MODULE__
|> 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,
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

View file

@ -16,7 +16,12 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do
config_opts = Pleroma.Config.get([:http, :adapter], [])
url_encoding =
Keyword.new()
|> Keyword.put(:path_encode_fun, fn path -> path end)
@defaults
|> Keyword.merge(url_encoding)
|> Keyword.merge(config_opts)
|> Keyword.merge(connection_opts)
|> 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}"
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
nicknames
|> Enum.map(&"@#{&1}")

View file

@ -74,6 +74,7 @@ defmodule Pleroma.Notification do
reblog
poll
status
update
}
def changeset(%Notification{} = notification, attrs) do
@ -281,10 +282,15 @@ defmodule Pleroma.Notification do
select: n.id
)
Multi.new()
|> Multi.update_all(:ids, query, set: [seen: true, updated_at: NaiveDateTime.utc_now()])
|> Marker.multi_set_last_read_id(user, "notifications")
|> Repo.transaction()
{:ok, %{marker: marker}} =
Multi.new()
|> Multi.update_all(:ids, query, set: [seen: true, updated_at: NaiveDateTime.utc_now()])
|> Marker.multi_set_last_read_id(user, "notifications")
|> Repo.transaction()
Streamer.stream(["user", "user:notification"], marker)
{:ok, %{marker: marker}}
end
@spec read_one(User.t(), String.t()) ::
@ -525,9 +531,7 @@ defmodule Pleroma.Notification do
%Activity{data: %{"type" => "Create"}} = activity,
local_only
) do
notification_enabled_ap_ids =
[]
|> Utils.maybe_notify_subscribers(activity)
notification_enabled_ap_ids = Utils.get_notified_subscribers(activity)
potential_receivers =
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))}")
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.
# 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
cond do
Keyword.get(options, :id_only) ->
ap_id
Keyword.get(options, :fetch) ->
case Fetcher.fetch_object_from_id(ap_id, options) do
{:ok, object} -> object
@ -401,28 +398,6 @@ defmodule Pleroma.Object do
String.starts_with?(id, Pleroma.Web.Endpoint.url() <> "/")
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(_), do: []

View file

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

View file

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

View file

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

View file

@ -7,6 +7,11 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
@impl true
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)
end

View file

@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Signature do
@behaviour Pleroma.Signature.API
@behaviour HTTPSignatures.Adapter
alias Pleroma.EctoType.ActivityPub.ObjectValidators
@ -53,7 +54,7 @@ defmodule Pleroma.Signature do
def fetch_public_key(conn) do
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}
else
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
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}

View file

@ -35,6 +35,7 @@ defmodule Pleroma.Upload do
"""
alias Ecto.UUID
alias Pleroma.Maps
alias Pleroma.Utils.URIEncoding
alias Pleroma.Web.ActivityPub.Utils
require Logger
@ -230,11 +231,18 @@ defmodule Pleroma.Upload do
tmp_path
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
encode_opts = [bypass_decode: true, bypass_parse: true]
path =
URI.encode(path, &char_unescaped?/1) <>
URIEncoding.encode_url(path, encode_opts) <>
if Pleroma.Config.get([__MODULE__, :link_name], false) do
"?name=#{URI.encode(name, &char_unescaped?/1)}"
enum = %{name: name}
"?#{URI.encode_query(enum)}"
else
""
end

View file

@ -233,8 +233,8 @@ defmodule Pleroma.User do
for {_relationship_type, [{_outgoing_relation, outgoing_relation_target}, _]} <-
@user_relationships_config do
# `def blocked_users_relation/2`, `def muted_users_relation/2`,
# `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
# `def subscriber_users/2`, `def endorsed_users_relation/2`
# `def reblog_muted_users_relation/2`, `def notification_muted_users_relation/2`,
# `def subscriber_users_relation/2`, `def endorsed_users_relation/2`
def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
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_ap_ids(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
@doc """
@ -801,13 +802,6 @@ defmodule Pleroma.User do
when is_nil(password) do
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
|> cast(params, [
:name,
@ -1364,7 +1358,7 @@ defmodule Pleroma.User do
@spec get_by_nickname(String.t()) :: User.t() | nil
def get_by_nickname(nickname) do
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))
end
end
@ -2314,6 +2308,15 @@ defmodule Pleroma.User do
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
with %User{} = user <- get_cached_by_ap_id(ap_id),
{:ok, public_key} <- public_key(user) do

View file

@ -16,6 +16,7 @@ defmodule Pleroma.User.Search do
following = Keyword.get(opts, :following, false)
result_limit = Keyword.get(opts, :limit, @limit)
offset = Keyword.get(opts, :offset, 0)
capabilities = Keyword.get(opts, :capabilities, [])
for_user = Keyword.get(opts, :for_user)
@ -32,7 +33,7 @@ defmodule Pleroma.User.Search do
results =
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)
results
@ -80,7 +81,7 @@ defmodule Pleroma.User.Search do
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
|> base_query(following)
|> filter_blocked_user(for_user)
@ -94,6 +95,7 @@ defmodule Pleroma.User.Search do
|> subquery()
|> order_by(desc: :search_rank)
|> maybe_restrict_local(for_user)
|> maybe_restrict_accepting_chat_messages(capabilities)
|> filter_deactivated_users()
end
@ -214,6 +216,14 @@ defmodule Pleroma.User.Search do
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 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),
{:ok, activity} <- insert(flag_data, local),
{:ok, stripped_activity} <- strip_report_status_data(activity),
_ <- notify_and_stream(activity),
:ok <-
maybe_federate(stripped_activity) do
:ok <- maybe_federate(activity) do
User.all_users_with_privilege(:reports_manage_reports)
|> Enum.filter(fn user -> user.ap_id != actor end)
|> Enum.filter(fn user -> not is_nil(user.email) end)
@ -501,6 +499,28 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Repo.all()
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()) ::
Ecto.UUID.t() | nil
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))
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_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 normalize_image(%{"url" => url} = data) do
defp normalize_image(%{"url" => url} = data) when is_binary(url) do
%{
"type" => "Image",
"url" => [%{"href" => url}]
@ -1575,6 +1599,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> maybe_put_description(data)
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(_), do: nil

View file

@ -31,6 +31,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
@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(
@ -95,6 +97,36 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
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, object_id) do
@ -257,8 +289,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> put_view(UserView)
|> render("activity_collection_page.json", %{
activities: activities,
pagination: ControllerHelper.get_pagination_fields(conn, activities),
iri: "#{user.ap_id}/outbox"
pagination: ControllerHelper.get_pagination_fields(conn, activities)
})
end
end
@ -404,8 +435,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> put_view(UserView)
|> render("activity_collection_page.json", %{
activities: activities,
pagination: ControllerHelper.get_pagination_fields(conn, activities),
iri: "#{user.ap_id}/inbox"
pagination: ControllerHelper.get_pagination_fields(conn, activities)
})
end
@ -482,6 +512,42 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
{:ok, activity}
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(
%{assigns: %{user: %User{nickname: nickname, ap_id: actor} = user}} = conn,
%{"nickname" => nickname} = params
@ -493,6 +559,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|> Map.put("actor", actor)
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),
%Activity{data: activity_data} <- Activity.normalize(activity) do
conn

View file

@ -18,6 +18,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
content =~ quote_url -> true
# Does the content already have a .quote-inline span?
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
true -> false
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(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)
when is_list(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),
do: Map.put(data, "replies", replies)
# TODO: Pleroma does not have any support for Collections at the moment.
# If the `replies` field is not something the ObjectID validator can handle,
# 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(%{"replies" => %{"orderedItems" => replies}} = data) when is_list(replies),
do: Map.put(data, "replies", 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),
do: Map.put(data, "attachment", [attachment])

View file

@ -20,7 +20,15 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
require Pleroma.Constants
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 =
Enum.reject(data, fn x ->

View file

@ -13,7 +13,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Publisher.Prepared
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Workers.PublisherWorker
require Pleroma.Constants
@ -26,6 +25,18 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
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 """
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.
"""
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
else
_e ->
@ -91,7 +102,17 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
Logger.debug("Federating #{ap_id} to #{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, [])
@ -115,10 +136,10 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
date = Pleroma.Signature.signed_date()
date = @signature_impl.signed_date()
signature =
Pleroma.Signature.sign(actor, %{
@signature_impl.sign(actor, %{
"(request-target)": "post #{path}",
host: signature_host(uri),
"content-length": byte_size(json),
@ -310,17 +331,21 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
Repo.checkout(fn ->
Enum.each([priority_inboxes, other_inboxes], fn inboxes ->
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
# instance would only accept a first message for the first recipient and ignore the rest.
cc = get_cc_ap_ids(ap_id, recipients)
__MODULE__.enqueue_one(%{
inbox: inbox,
cc: cc,
activity_id: activity.id
})
__MODULE__.enqueue_one(
%{
inbox: inbox,
cc: cc,
activity_id: activity.id
},
priority: priority
)
end)
end)
end)
@ -382,4 +407,15 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
end
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

View file

@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
@moduledoc """
A module to handle coding from internal to wire ActivityPub and back.
"""
@behaviour Pleroma.Web.ActivityPub.Transmogrifier.API
alias Pleroma.Activity
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Maps
@ -22,7 +23,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.Federator
import Ecto.Query
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
require Pleroma.Constants
@ -103,6 +103,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
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
def fix_explicit_addressing(%{"directMessage" => true} = object, _follower_collection),
do: object
@ -160,6 +178,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> fix_addressing_list("cc")
|> fix_addressing_list("bto")
|> 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_implicit_addressing(follower_collection)
end
@ -739,48 +761,26 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def set_quote_url(obj), do: obj
@doc """
Serialized Mastodon-compatible `replies` collection containing _self-replies_.
Based on Mastodon's ActivityPub::NoteSerializer#replies.
Inline first page of the `replies` collection,
containing any replies in chronological order.
"""
def set_replies(obj_data) do
replies_uris =
with limit when limit > 0 <-
Pleroma.Config.get([:activitypub, :note_replies_output_limit], 0),
%Object{} = object <- Object.get_cached_by_ap_id(obj_data["id"]) do
object
|> Object.self_replies()
|> select([o], fragment("?->>'id'", o.data))
|> limit(^limit)
|> Repo.all()
else
_ -> []
end
set_replies(obj_data, replies_uris)
def set_replies(%{"type" => type} = obj_data)
when type in Pleroma.Constants.status_object_types() do
with obj_ap_id when is_binary(obj_ap_id) <- obj_data["id"],
limit when limit > 0 <-
Pleroma.Config.get([:activitypub, :note_replies_output_limit], 0),
collection <-
Pleroma.Web.ActivityPub.ObjectView.render("object_replies.json", %{
render_params: %{object_ap_id: obj_data["id"], limit: limit, skip_ap_ctx: true}
}) do
Map.put(obj_data, "replies", collection)
else
0 -> Map.put(obj_data, "replies", obj_data["id"] <> "/replies")
_ -> obj_data
end
end
defp set_replies(obj, []) do
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: []
def set_replies(obj_data), do: obj_data
# Prepares the object of an outgoing create activity.
def prepare_object(object) do
@ -850,6 +850,27 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, data}
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
object =
object_id
@ -909,6 +930,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
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
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),
do:
[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()
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 strip_report_status_data(activity) do
[actor | reported_activities] = activity.data["object"]
def strip_report_status_data(%Activity{} = activity) do
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 =
Enum.reduce(reported_activities, [], fn act, acc ->
@ -870,9 +880,36 @@ defmodule Pleroma.Web.ActivityPub.Utils do
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
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
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.CollectionViewHelper
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ControllerHelper
def render("object.json", %{object: %Object{} = object}) do
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)
end
def render("object.json", %{object: %Activity{data: %{"type" => activity_type}} = activity})
when activity_type in ["Create", "Listen"] do
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header(activity.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)
def render("object.json", %{object: %Activity{} = activity}) do
{:ok, ap_data} = Transmogrifier.prepare_outgoing(activity.data)
ap_data
end
def render("object.json", %{object: %Activity{} = activity}) do
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header(activity.data)
object_id = Object.normalize(activity, id_only: true)
def render("object_replies.json", %{
conn: conn,
render_params: %{object_ap_id: object_ap_id, page: "true"} = params
}) do
params = Map.put_new(params, :limit, 40)
additional =
Transmogrifier.prepare_object(activity.data)
|> Map.put("object", object_id)
items = ActivityPub.fetch_objects_for_replies_collection(object_ap_id, params)
display_items = map_reply_collection_items(items)
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

View file

@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.CollectionViewHelper
alias Pleroma.Web.ActivityPub.ObjectView
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
@ -164,7 +165,13 @@ defmodule Pleroma.Web.ActivityPub.UserView do
0
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())
end
@ -189,7 +196,12 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"totalItems" => total,
"first" =>
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
"#{user.ap_id}/following?page=1"
end
@ -212,7 +224,13 @@ defmodule Pleroma.Web.ActivityPub.UserView do
0
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())
end
@ -236,7 +254,12 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"type" => "OrderedCollection",
"first" =>
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
"#{user.ap_id}/followers?page=1"
end
@ -256,7 +279,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
def render("activity_collection_page.json", %{
activities: activities,
iri: iri,
pagination: pagination
}) do
collection =
@ -265,13 +287,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
data
end)
%{
"type" => "OrderedCollectionPage",
"partOf" => iri,
"orderedItems" => collection
}
|> Map.merge(Utils.make_json_ld_header())
|> Map.merge(pagination)
CollectionViewHelper.collection_page_keyset(collection, pagination)
end
def render("featured.json", %{
@ -299,27 +315,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
Map.put(map, "totalItems", total)
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
if image = func.(user, no_default: true) do
%{

View file

@ -73,6 +73,22 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|> Pleroma.List.member?(user)
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)
when module in [Activity, Object] do
if restrict_unauthenticated_access?(message),

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