Merge remote-tracking branch 'origin/develop' into shigusegubu
This commit is contained in:
commit
8d82808d7a
632 changed files with 7519 additions and 1314 deletions
|
|
@ -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
|
||||
|
|
|
|||
311
CHANGELOG.md
311
CHANGELOG.md
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
Add new activity actor/type index. Greatly speeds up retrieval of rare types (like "Listen")
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix 'Create a user' description in admin api docs
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix AssignAppUser migration OOM
|
||||
|
|
@ -1 +0,0 @@
|
|||
- Fix building "captcha" library with OpenBSD make
|
||||
|
|
@ -1 +0,0 @@
|
|||
Docs: Restore DB schema before data to avoid long restore times
|
||||
|
|
@ -1 +0,0 @@
|
|||
Return 404 with a better error message instead of 400 when receiving an activity for a deactivated user
|
||||
|
|
@ -1 +0,0 @@
|
|||
Use JSON for DeepL API requests
|
||||
|
|
@ -1 +0,0 @@
|
|||
Deleting an instance queues individual jobs for each user that needs to be deleted from the server.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Support Dislike activity, as sent by Mitra and Friendica, by changing it into a thumbs-down EmojiReact
|
||||
|
|
@ -1 +0,0 @@
|
|||
Elixir 1.18: Fixed warnings and new deprecations
|
||||
|
|
@ -1 +0,0 @@
|
|||
Added a way to upload new packs from a URL or ZIP file via Admin API
|
||||
|
|
@ -1 +0,0 @@
|
|||
Support Mitra-style emoji likes.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix endorsement state display in relationship view
|
||||
|
|
@ -1 +0,0 @@
|
|||
Add `duration` to the block endpoint, which makes block expire
|
||||
|
|
@ -1 +0,0 @@
|
|||
Expose markup configuration in InstanceView
|
||||
|
|
@ -1 +0,0 @@
|
|||
Set PATH in the FreeBSD rc script to avoid failures starting the service
|
||||
|
|
@ -1 +0,0 @@
|
|||
Improved performance of status search queries using the default GIN index
|
||||
|
|
@ -1 +0,0 @@
|
|||
Update Cowboy, Gun, and Plug family of dependencies
|
||||
|
|
@ -1 +0,0 @@
|
|||
Hashtag searches return real results based on words in your query
|
||||
|
|
@ -1 +0,0 @@
|
|||
Implement language detection with fastText
|
||||
|
|
@ -1 +0,0 @@
|
|||
Added MRF.QuietReply which prevents replies to public posts from being published to the timelines
|
||||
|
|
@ -1 +0,0 @@
|
|||
Oban Notifier was changed to Oban.Notifiers.PG for performance and scalability benefits
|
||||
|
|
@ -1 +0,0 @@
|
|||
Updated relayd/httpd config files to be on par with nginx
|
||||
|
|
@ -1 +0,0 @@
|
|||
replaced depracated flags and functions, renamed service to fit other service files
|
||||
|
|
@ -1 +0,0 @@
|
|||
Updated Postgrex library to 0.20.0
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
Improved the logic of how we determine if a server is unreachable.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Relax alsoKnownAs requirements to just URI, not necessarily HTTP(S)
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix release builds
|
||||
|
|
@ -1 +0,0 @@
|
|||
Change scrobble external link param name to use snake case
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
Change SMTP example to use the Mua adapter that works with OTP>25
|
||||
|
|
@ -1 +0,0 @@
|
|||
Updated Tesla to 1.15.3
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
Allow Terms of Service panel behaviour to be configurable
|
||||
|
|
@ -1 +0,0 @@
|
|||
Support translation providers (DeepL, LibreTranslate)
|
||||
|
|
@ -1 +0,0 @@
|
|||
Truncate the length of Rich Media title and description fields
|
||||
|
|
@ -1 +0,0 @@
|
|||
Fix HTTP client making invalid requests due to no percent encoding processing or validation.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Enforce an exact domain match for WebFinger resolution
|
||||
|
|
@ -1 +0,0 @@
|
|||
Don't require an Accept header for WebFinger queries and default to JSON.
|
||||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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`:
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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`)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
#}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
# }
|
||||
# }
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ defmodule Pleroma.Constants do
|
|||
"pleroma_internal",
|
||||
"generator",
|
||||
"rules",
|
||||
"language"
|
||||
"language",
|
||||
"voters"
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
109
lib/pleroma/language/translation/mozhi.ex
Normal file
109
lib/pleroma/language/translation/mozhi.ex
Normal 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
|
||||
129
lib/pleroma/language/translation/translate_locally.ex
Normal file
129
lib/pleroma/language/translation/translate_locally.ex
Normal 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
|
||||
|
|
@ -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}")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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: []
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ->
|
||||
|
|
|
|||
14
lib/pleroma/signature/api.ex
Normal file
14
lib/pleroma/signature/api.ex
Normal 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
|
||||
|
|
@ -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}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
142
lib/pleroma/utils/uri_encoding.ex
Normal file
142
lib/pleroma/utils/uri_encoding.ex
Normal 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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
11
lib/pleroma/web/activity_pub/transmogrifier/api.ex
Normal file
11
lib/pleroma/web/activity_pub/transmogrifier/api.ex
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
59
lib/pleroma/web/activity_pub/views/collection_view_helper.ex
Normal file
59
lib/pleroma/web/activity_pub/views/collection_view_helper.ex
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
%{
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue