Merge branch 'develop' into 'from/develop/tusooa/emit-move'

# Conflicts:
#   CHANGELOG.md
#   test/pleroma/user_test.exs
This commit is contained in:
tusooa 2022-07-31 21:32:49 +00:00
commit c80096522c
1458 changed files with 31249 additions and 2288 deletions

View File

@ -1,4 +1,4 @@
image: elixir:1.9.4 image: git.pleroma.social:5050/pleroma/pleroma/ci-base
variables: &global_variables variables: &global_variables
POSTGRES_DB: pleroma_test POSTGRES_DB: pleroma_test
@ -26,12 +26,7 @@ stages:
before_script: before_script:
- echo $MIX_ENV - echo $MIX_ENV
- rm -rf _build/*/lib/pleroma - rm -rf _build/*/lib/pleroma
- apt-get update && apt-get install -y cmake
- mix local.hex --force
- mix local.rebar --force
- mix deps.get - mix deps.get
- apt-get -qq update
- apt-get install -y libmagic-dev
after_script: after_script:
- rm -rf _build/*/lib/pleroma - rm -rf _build/*/lib/pleroma
@ -39,7 +34,8 @@ after_script:
build: build:
stage: build stage: build
only: only:
changes: changes: &build_changes_policy
- ".gitlab-ci.yml"
- "**/*.ex" - "**/*.ex"
- "**/*.exs" - "**/*.exs"
- "mix.lock" - "mix.lock"
@ -50,6 +46,7 @@ spec-build:
stage: test stage: test
only: only:
changes: changes:
- ".gitlab-ci.yml"
- "lib/pleroma/web/api_spec/**/*.ex" - "lib/pleroma/web/api_spec/**/*.ex"
- "lib/pleroma/web/api_spec.ex" - "lib/pleroma/web/api_spec.ex"
artifacts: artifacts:
@ -64,7 +61,7 @@ benchmark:
variables: variables:
MIX_ENV: benchmark MIX_ENV: benchmark
services: services:
- name: postgres:9.6 - name: postgres:9.6-alpine
alias: postgres alias: postgres
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
script: script:
@ -75,20 +72,16 @@ benchmark:
unit-testing: unit-testing:
stage: test stage: test
only: only:
changes: changes: *build_changes_policy
- "**/*.ex"
- "**/*.exs"
- "mix.lock"
cache: &testing_cache_policy cache: &testing_cache_policy
<<: *global_cache_policy <<: *global_cache_policy
policy: pull policy: pull
services: services:
- name: postgres:13 - name: postgres:13-alpine
alias: postgres alias: postgres
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
script: script:
- apt-get update && apt-get install -y libimage-exiftool-perl ffmpeg
- mix ecto.create - mix ecto.create
- mix ecto.migrate - mix ecto.migrate
- mix coveralls --preload-modules - mix coveralls --preload-modules
@ -96,17 +89,15 @@ unit-testing:
unit-testing-erratic: unit-testing-erratic:
stage: test stage: test
retry: 2 retry: 2
allow_failure: true
only: only:
changes: changes: *build_changes_policy
- "**/*.ex"
- "**/*.exs"
- "mix.lock"
cache: &testing_cache_policy cache: &testing_cache_policy
<<: *global_cache_policy <<: *global_cache_policy
policy: pull policy: pull
services: services:
- name: postgres:13 - name: postgres:13-alpine
alias: postgres alias: postgres
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
script: script:
@ -133,10 +124,7 @@ unit-testing-erratic:
unit-testing-rum: unit-testing-rum:
stage: test stage: test
only: only:
changes: changes: *build_changes_policy
- "**/*.ex"
- "**/*.exs"
- "mix.lock"
cache: *testing_cache_policy cache: *testing_cache_policy
services: services:
- name: minibikini/postgres-with-rum:12 - name: minibikini/postgres-with-rum:12
@ -146,46 +134,42 @@ unit-testing-rum:
<<: *global_variables <<: *global_variables
RUM_ENABLED: "true" RUM_ENABLED: "true"
script: script:
- apt-get update && apt-get install -y libimage-exiftool-perl ffmpeg
- mix ecto.create - mix ecto.create
- mix ecto.migrate - mix ecto.migrate
- "mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/" - "mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/"
- mix test --preload-modules - mix test --preload-modules
lint: lint:
image: elixir:1.12 image: &current_elixir elixir:1.12-alpine
stage: test stage: test
only: only:
changes: changes: *build_changes_policy
- "**/*.ex"
- "**/*.exs"
- "mix.lock"
cache: *testing_cache_policy cache: *testing_cache_policy
before_script: &current_bfr_script
- apk update
- apk add build-base cmake file-dev git openssl
- mix local.hex --force
- mix local.rebar --force
- mix deps.get
script: script:
- mix format --check-formatted - mix format --check-formatted
analysis: analysis:
stage: test stage: test
only: only:
changes: changes: *build_changes_policy
- "**/*.ex"
- "**/*.exs"
- "mix.lock"
cache: *testing_cache_policy cache: *testing_cache_policy
script: script:
- mix credo --strict --only=warnings,todo,fixme,consistency,readability - mix credo --strict --only=warnings,todo,fixme,consistency,readability
cycles: cycles:
image: *current_elixir
stage: test stage: test
image: elixir:1.11
only: only:
changes: changes: *build_changes_policy
- "**/*.ex"
- "**/*.exs"
- "mix.lock"
cache: {} cache: {}
before_script: *current_bfr_script
script: script:
- mix deps.get
- mix compile - mix compile
- mix xref graph --format cycles --label compile | awk '{print $0} END{exit ($0 != "No cycles found")}' - mix xref graph --format cycles --label compile | awk '{print $0} END{exit ($0 != "No cycles found")}'
@ -305,7 +289,7 @@ amd64-musl:
cache: *release-cache cache: *release-cache
variables: *release-variables variables: *release-variables
before_script: &before-release-musl before_script: &before-release-musl
- apk add git gcc g++ musl-dev make cmake file-dev - apk add git build-base cmake file-dev openssl
- echo "import Mix.Config" > config/prod.secret.exs - echo "import Mix.Config" > config/prod.secret.exs
- mix local.hex --force - mix local.hex --force
- mix local.rebar --force - mix local.rebar --force

View File

@ -12,21 +12,61 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed ### Changed
- Allow users to remove their emails if instance does not need email to register - Allow users to remove their emails if instance does not need email to register
- Uploadfilter `Pleroma.Upload.Filter.Exiftool` has been renamed to `Pleroma.Upload.Filter.Exiftool.StripLocation`
### Added ### Added
- `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object - `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object
- Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney. - Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney.
- `ForceMentionsInPostContent` MRF policy
- AdminAPI: allow moderators to manage reports, users, invites, and custom emojis - AdminAPI: allow moderators to manage reports, users, invites, and custom emojis
- AdminAPI: restrict moderators to access sensitive data: change user credentials, get password reset token, read private statuses and chats, etc - AdminAPI: restrict moderators to access sensitive data: change user credentials, get password reset token, read private statuses and chats, etc
- PleromaAPI: Add remote follow API endpoint at `POST /api/v1/pleroma/remote_interaction`
- MastoAPI: Add `GET /api/v1/accounts/lookup`
- MastoAPI: Profile Directory support
- MastoAPI: Support v2 Suggestions (handpicked accounts only)
- Ability to log slow Ecto queries by configuring `:pleroma, :telemetry, :slow_queries_logging`
- Added Phoenix LiveDashboard at `/phoenix/live_dashboard`
- Added `/manifest.json` for progressive web apps.
- MastoAPI: Support for `birthday` and `show_birthday` field in `/api/v1/accounts/update_credentials`.
- Configuration: Add `birthday_required` and `birthday_min_age` settings to provide a way to require users to enter their birth date.
- PleromaAPI: Add `GET /api/v1/pleroma/birthdays` API endpoint
- Make backend-rendered pages translatable. This includes emails. Pages returned as a HTTP response are translated using the language specified in the `userLanguage` cookie, or the `Accept-Language` header. Emails are translated using the `language` field when registering. This language can be changed by `PATCH /api/v1/accounts/update_credentials` with the `language` field.
- Uploadfilter `Pleroma.Upload.Filter.Exiftool.ReadDescription` returns description values to the FE so they can pre fill the image description field
- Added move account API - Added move account API
### Fixed ### Fixed
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
- Handle Reject for already-accepted Follows properly - Handle Reject for already-accepted Follows properly
- Display OpenGraph data on alternative notice routes. - Display OpenGraph data on alternative notice routes.
- Fix replies count for remote replies
- Fixed hashtags disappearing from the end of lines when Markdown is enabled
- ChatAPI: Add link headers
- Limited number of search results to 40 to prevent DoS attacks
- ActivityPub: fixed federation of attachment dimensions
- Fixed benchmarks
- Elixir 1.13 support
- Fixed crash when pinned_objects is nil
- Fixed slow timelines when there are a lot of deactivated users
- Fixed account deletion API
### Removed ### Removed
## 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
### Upgrade notes
1. Restart Pleroma
## 2.4.1 - 2021-08-29 ## 2.4.1 - 2021-08-29
### Changed ### Changed
@ -60,6 +100,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Improved Twittercard and OpenGraph meta tag generation including thumbnails and image dimension metadata when available. - Improved Twittercard and OpenGraph meta tag generation including thumbnails and image dimension metadata when available.
- AdminAPI: sort users so the newest are at the top. - AdminAPI: sort users so the newest are at the top.
- ActivityPub Client-to-Server(C2S): Limitation on the type of Activity/Object are lifted as they are now passed through ObjectValidators - ActivityPub Client-to-Server(C2S): Limitation on the type of Activity/Object are lifted as they are now passed through ObjectValidators
- MRF (`AntiFollowbotPolicy`): Bot accounts are now also considered followbots. Users can still allow bots to follow them by first following the bot.
### Added ### Added

View File

@ -1,11 +1,11 @@
Unless otherwise stated this repository is copyright © 2017-2021 Unless otherwise stated this repository is copyright © 2017-2022
Pleroma Authors <https://pleroma.social/>, and is distributed under Pleroma Authors <https://pleroma.social/>, and is distributed under
The GNU Affero General Public License Version 3, you should have received a The GNU Affero General Public License Version 3, you should have received a
copy of the license file as AGPL-3. copy of the license file as AGPL-3.
--- ---
Files inside docs directory are copyright © 2021 Pleroma Authors Files inside docs directory are copyright © 2022 Pleroma Authors
<https://pleroma.social/>, and are distributed under the Creative Commons <https://pleroma.social/>, and are distributed under the Creative Commons
Attribution 4.0 International license, you should have received Attribution 4.0 International license, you should have received
a copy of the license file as CC-BY-4.0. a copy of the license file as CC-BY-4.0.
@ -30,7 +30,7 @@ priv/static/images/pleroma-fox-tan-shy.png
--- ---
The following files are copyright © 2017-2020 Pleroma Authors The following files are copyright © 2017-2022 Pleroma Authors
<https://pleroma.social/>, and are distributed under the Creative Commons <https://pleroma.social/>, and are distributed under the Creative Commons
Attribution-ShareAlike 4.0 International license, you should have received Attribution-ShareAlike 4.0 International license, you should have received
a copy of the license file as CC-BY-SA-4.0. a copy of the license file as CC-BY-SA-4.0.

7
ci/Dockerfile Normal file
View File

@ -0,0 +1,7 @@
FROM elixir:1.9.4
RUN apt-get update &&\
apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\
mix local.hex --force &&\
mix local.rebar --force

1
ci/build_and_push.sh Executable file
View File

@ -0,0 +1 @@
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:latest --push .

View File

@ -149,8 +149,6 @@ config :pleroma, Pleroma.Web.Endpoint,
] ]
# Configures Elixir's Logger # Configures Elixir's Logger
config :logger, truncate: 65536
config :logger, :console, config :logger, :console,
level: :debug, level: :debug,
format: "\n$time $metadata[$level] $message\n", format: "\n$time $metadata[$level] $message\n",
@ -189,6 +187,7 @@ config :pleroma, :instance,
email: "example@example.com", email: "example@example.com",
notify_email: "noreply@example.com", notify_email: "noreply@example.com",
description: "Pleroma: An efficient and flexible fediverse server", description: "Pleroma: An efficient and flexible fediverse server",
short_description: "",
background_image: "/images/city.jpg", background_image: "/images/city.jpg",
instance_thumbnail: "/instance/thumbnail.jpeg", instance_thumbnail: "/instance/thumbnail.jpeg",
limit: 5_000, limit: 5_000,
@ -258,7 +257,11 @@ config :pleroma, :instance,
show_reactions: true, show_reactions: true,
password_reset_token_validity: 60 * 60 * 24, password_reset_token_validity: 60 * 60 * 24,
profile_directory: true, profile_directory: true,
privileged_staff: false privileged_staff: false,
max_endorsed_users: 20,
birthday_required: false,
birthday_min_age: 0,
max_media_attachments: 1_000
config :pleroma, :welcome, config :pleroma, :welcome,
direct_message: [ direct_message: [
@ -856,13 +859,6 @@ config :pleroma, ConcurrentLimiter, [
{Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, [max_running: 5, max_waiting: 5]} {Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, [max_running: 5, max_waiting: 5]}
] ]
config :pleroma, :telemetry,
slow_queries_logging: [
enabled: false,
min_duration: 500_000,
exclude_sources: [nil, "oban_jobs"]
]
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs" import_config "#{Mix.env()}.exs"

View File

@ -536,6 +536,15 @@ config :pleroma, :config_description, [
"Very cool instance" "Very cool instance"
] ]
}, },
%{
key: :short_description,
type: :string,
description:
"Shorter version of instance description. It can be seen on `/api/v1/instance`",
suggestions: [
"Cool instance"
]
},
%{ %{
key: :limit, key: :limit,
type: :integer, type: :integer,
@ -552,6 +561,14 @@ config :pleroma, :config_description, [
100_000 100_000
] ]
}, },
%{
key: :max_media_attachments,
type: :integer,
description: "Maximum number of post media attachments",
suggestions: [
1_000_000
]
},
%{ %{
key: :upload_limit, key: :upload_limit,
type: :integer, type: :integer,
@ -742,6 +759,16 @@ config :pleroma, :config_description, [
3 3
] ]
}, },
%{
key: :max_endorsed_users,
type: :integer,
description: "The maximum number of recommended accounts. 0 will disable the feature.",
suggestions: [
0,
1,
3
]
},
%{ %{
key: :autofollowed_nicknames, key: :autofollowed_nicknames,
type: {:list, :string}, type: {:list, :string},
@ -947,6 +974,17 @@ config :pleroma, :config_description, [
type: :boolean, type: :boolean,
description: description:
"Let moderators access sensitive data (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)" "Let moderators access sensitive data (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)"
},
%{
key: :birthday_required,
type: :boolean,
description: "Require users to enter their birthday."
},
%{
key: :birthday_min_age,
type: :integer,
description:
"Minimum required age for users to create account. Only used if birthday is required."
} }
] ]
}, },
@ -1691,6 +1729,11 @@ config :pleroma, :config_description, [
type: :boolean, type: :boolean,
description: "Sign object fetches with HTTP signatures" description: "Sign object fetches with HTTP signatures"
}, },
%{
key: :authorized_fetch_mode,
type: :boolean,
description: "Require HTTP signatures for AP fetches"
},
%{ %{
key: :note_replies_output_limit, key: :note_replies_output_limit,
type: :integer, type: :integer,
@ -2697,7 +2740,7 @@ config :pleroma, :config_description, [
key: :versions, key: :versions,
type: {:list, :atom}, type: {:list, :atom},
description: "List of TLS version to use", description: "List of TLS version to use",
suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"] suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2", ":tlsv1.3"]
} }
] ]
} }

View File

@ -37,7 +37,8 @@ If any of the options are left unspecified, you will be prompted interactively.
- `--static-dir <path>` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.) - `--static-dir <path>` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)
- `--listen-ip <ip>` - the ip the app should listen to, defaults to 127.0.0.1 - `--listen-ip <ip>` - the ip the app should listen to, defaults to 127.0.0.1
- `--listen-port <port>` - the port the app should listen to, defaults to 4000 - `--listen-port <port>` - the port the app should listen to, defaults to 4000
- `--strip-uploads <Y|N>` - use ExifTool to strip uploads of sensitive location data - `--strip-uploads-location <Y|N>` - use ExifTool to strip uploads of sensitive location data
- `--read-uploads-description <Y|N>` - use ExifTool to read image descriptions from uploads
- `--anonymize-uploads <Y|N>` - randomize uploaded filenames - `--anonymize-uploads <Y|N>` - randomize uploaded filenames
- `--dedupe-uploads <Y|N>` - store files based on their hash to reduce data storage requirements if duplicates are uploaded with different filenames - `--dedupe-uploads <Y|N>` - store files based on their hash to reduce data storage requirements if duplicates are uploaded with different filenames
- `--skip-release-env` - skip generation the release environment file - `--skip-release-env` - skip generation the release environment file

View File

@ -17,11 +17,11 @@ su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate"
## For from source installations (using git) ## For from source installations (using git)
1. Go to the working directory of Pleroma (default is `/opt/pleroma`) 1. Go to the working directory of Pleroma (default is `/opt/pleroma`)
2. Run `git pull`. This pulls the latest changes from upstream. 2. Run `git pull` [^1]. This pulls the latest changes from upstream.
3. Run `mix deps.get` [^1]. This pulls in any new dependencies. 3. Run `mix deps.get` [^1]. This pulls in any new dependencies.
4. Stop the Pleroma service. 4. Stop the Pleroma service.
5. Run `mix ecto.migrate` [^1] [^2]. This task performs database migrations, if there were any. 5. Run `mix ecto.migrate` [^1] [^2]. This task performs database migrations, if there were any.
6. Start the Pleroma service. 6. Start the Pleroma service.
[^1]: Depending on which install guide you followed (for example on Debian/Ubuntu), you want to run `mix` tasks as `pleroma` user by adding `sudo -Hu pleroma` before the command. [^1]: Depending on which install guide you followed (for example on Debian/Ubuntu), you want to run `git` and `mix` tasks as `pleroma` user by adding `sudo -Hu pleroma` before the command.
[^2]: Prefix with `MIX_ENV=prod` to run it using the production config file. [^2]: Prefix with `MIX_ENV=prod` to run it using the production config file.

View File

@ -116,3 +116,9 @@ Feel free to contact us to be added to this list!
- Contact: [@r@freesoftwareextremist.com](https://freesoftwareextremist.com/users/r) - Contact: [@r@freesoftwareextremist.com](https://freesoftwareextremist.com/users/r)
- Features: Does not requires JavaScript - Features: Does not requires JavaScript
- Features: MastoAPI - Features: MastoAPI
### Glitch-lily
- Source Code: <https://lily.kazv.moe/infra/glitch-lily>
- Contact: [@tusooa@kazv.moe](https://kazv.moe/users/tusooa)
- Features: MastoAPI
- Based on [glitch-soc](https://github.com/glitch-soc/mastodon) frontend

View File

@ -18,6 +18,7 @@ To add configuration to your config file, you can copy it from the base config.
* `email`: Email used to reach an Administrator/Moderator of the instance. * `email`: Email used to reach an Administrator/Moderator of the instance.
* `notify_email`: Email used for notifications. * `notify_email`: Email used for notifications.
* `description`: The instances description, can be seen in nodeinfo and ``/api/v1/instance``. * `description`: The instances description, can be seen in nodeinfo and ``/api/v1/instance``.
* `short_description`: Shorter version of instance description, can be seen on ``/api/v1/instance``.
* `limit`: Posts character limit (CW/Subject included in the counter). * `limit`: Posts character limit (CW/Subject included in the counter).
* `description_limit`: The character limit for image descriptions. * `description_limit`: The character limit for image descriptions.
* `remote_limit`: Hard character limit beyond which remote posts will be dropped. * `remote_limit`: Hard character limit beyond which remote posts will be dropped.
@ -125,6 +126,9 @@ To add configuration to your config file, you can copy it from the base config.
* `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.Workers.PurgeExpiredActivity` to be enabled for processing the scheduled delections. * `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.Workers.PurgeExpiredActivity` to be enabled for processing the scheduled delections.
* `Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy`: Makes all bot posts to disappear from public timelines. * `Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy`: Makes all bot posts to disappear from public timelines.
* `Pleroma.Web.ActivityPub.MRF.FollowBotPolicy`: Automatically follows newly discovered users from the specified bot account. Local accounts, locked accounts, and users with "#nobot" in their bio are respected and excluded from being followed. * `Pleroma.Web.ActivityPub.MRF.FollowBotPolicy`: Automatically follows newly discovered users from the specified bot account. Local accounts, locked accounts, and users with "#nobot" in their bio are respected and excluded from being followed.
* `Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy`: Drops follow requests from followbots. Users can still allow bots to follow them by first following the bot.
* `Pleroma.Web.ActivityPub.MRF.KeywordPolicy`: Rejects or removes from the federated timeline or replaces keywords. (See [`:mrf_keyword`](#mrf_keyword)).
* `Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent`: Forces every mentioned user to be reflected in the post content.
* `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo). * `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
* `transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value. * `transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
@ -624,12 +628,18 @@ This filter replaces the filename (not the path) of an upload. For complete obfu
No specific configuration. No specific configuration.
#### Pleroma.Upload.Filter.Exiftool #### Pleroma.Upload.Filter.Exiftool.StripLocation
This filter only strips the GPS and location metadata with Exiftool leaving color profiles and attributes intact. This filter only strips the GPS and location metadata with Exiftool leaving color profiles and attributes intact.
No specific configuration. No specific configuration.
#### Pleroma.Upload.Filter.Exiftool.ReadDescription
This filter reads the ImageDescription and iptc:Caption-Abstract fields with Exiftool so clients can prefill the media description field.
No specific configuration.
#### Pleroma.Upload.Filter.Mogrify #### Pleroma.Upload.Filter.Mogrify
* `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"implode", "1"}]`. * `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"implode", "1"}]`.

View File

@ -1636,3 +1636,117 @@ Returns the content of the document
"error": "Could not install frontend" "error": "Could not install frontend"
} }
``` ```
## `GET /api/v1/pleroma/admin/announcements`
### List announcements
- Params: `offset`, `limit`
- Response: JSON, list of announcements
```json
[
{
"id": "AHDp0GBdRn1EPN5HN2",
"content": "some content",
"starts_at": null,
"ends_at": null,
"all_day": false,
"published_at": "2022-03-09T02:13:05",
"reactions": [],
"statuses": [],
"tags": [],
"emojis": [],
"updated_at": "2022-03-09T02:13:05"
}
]
```
Note that this differs from the Mastodon API variant: Mastodon API only returns *active* announcements, while this returns all.
## `GET /api/v1/pleroma/admin/announcements/:id`
### Display one announcement
- Response: JSON, one announcement
```json
{
"id": "AHDp0GBdRn1EPN5HN2",
"content": "some content",
"starts_at": null,
"ends_at": null,
"all_day": false,
"published_at": "2022-03-09T02:13:05",
"reactions": [],
"statuses": [],
"tags": [],
"emojis": [],
"updated_at": "2022-03-09T02:13:05"
}
```
## `POST /api/v1/pleroma/admin/announcements`
### Create an announcement
- Params:
- `content`: string, required, announcement content
- `starts_at`: datetime, optional, default to null, the time when the announcement will become active (displayed to users); if it is null, the announcement will be active immediately
- `ends_at`: datetime, optional, default to null, the time when the announcement will become inactive (no longer displayed to users); if it is null, the announcement will be active until an admin deletes it
- `all_day`: boolean, optional, default to false, tells the client whether to only display dates for `starts_at` and `ends_at`
- Response: JSON, created announcement
```json
{
"id": "AHDp0GBdRn1EPN5HN2",
"content": "some content",
"starts_at": null,
"ends_at": null,
"all_day": false,
"published_at": "2022-03-09T02:13:05",
"reactions": [],
"statuses": [],
"tags": [],
"emojis": [],
"updated_at": "2022-03-09T02:13:05"
}
```
## `PATCH /api/v1/pleroma/admin/announcements/:id`
### Change an announcement
- Params: same as `POST /api/v1/pleroma/admin/announcements`, except no param is required.
- Updates the announcement according to params. Missing params are kept as-is.
- Response: JSON, updated announcement
```json
{
"id": "AHDp0GBdRn1EPN5HN2",
"content": "some content",
"starts_at": null,
"ends_at": null,
"all_day": false,
"published_at": "2022-03-09T02:13:05",
"reactions": [],
"statuses": [],
"tags": [],
"emojis": [],
"updated_at": "2022-03-09T02:13:05"
}
```
## `DELETE /api/v1/pleroma/admin/announcements/:id`
### Delete an announcement
- Response: JSON, empty object
```json
{}
```

View File

@ -241,6 +241,7 @@ Additional parameters can be added to the JSON body/Form data:
- `discoverable` - if true, external services (search bots) etc. are allowed to index / list the account (regardless of this setting, user will still appear in regular search results). - `discoverable` - if true, external services (search bots) etc. are allowed to index / list the account (regardless of this setting, user will still appear in regular search results).
- `actor_type` - the type of this account. - `actor_type` - the type of this account.
- `accepts_chat_messages` - if false, this account will reject all chat messages. - `accepts_chat_messages` - if false, this account will reject all chat messages.
- `language` - user's preferred language for receiving emails (digest, confirmation, etc.)
All images (avatar, banner and background) can be reset to the default by sending an empty string ("") instead of a file. All images (avatar, banner and background) can be reset to the default by sending an empty string ("") instead of a file.
@ -292,6 +293,7 @@ Has these additional parameters (which are the same as in Pleroma-API):
- `captcha_token`: optional, contains provider-specific captcha token - `captcha_token`: optional, contains provider-specific captcha token
- `captcha_answer_data`: optional, contains provider-specific captcha data - `captcha_answer_data`: optional, contains provider-specific captcha data
- `token`: invite token required when the registrations aren't public. - `token`: invite token required when the registrations aren't public.
- `language`: optional, user's preferred language for receiving emails (digest, confirmation, etc.), default to the language set in the `userLanguage` cookies or `Accept-Language` header.
## Instance ## Instance
@ -377,12 +379,6 @@ Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer feat
- `GET /api/v1/identity_proofs`: Returns an empty array, `[]` - `GET /api/v1/identity_proofs`: Returns an empty array, `[]`
### Endorsements
*Added in Mastodon 2.5.0*
- `GET /api/v1/endorsements`: Returns an empty array, `[]`
### Featured tags ### Featured tags
*Added in Mastodon 3.0.0* *Added in Mastodon 3.0.0*

View File

@ -37,7 +37,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
``` ```
* Note: Same data as Mastodon APIs `/api/v1/custom_emojis` but in a different format * Note: Same data as Mastodon APIs `/api/v1/custom_emojis` but in a different format
## `/api/v1/pleroma/follow_import` ## `/api/pleroma/follow_import`
### Imports your follows, for example from a Mastodon CSV file. ### Imports your follows, for example from a Mastodon CSV file.
* Method: `POST` * Method: `POST`
* Authentication: required * Authentication: required
@ -46,7 +46,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
* Response: HTTP 200 on success, 500 on error * Response: HTTP 200 on success, 500 on error
* Note: Users that can't be followed are silently skipped. * Note: Users that can't be followed are silently skipped.
## `/api/v1/pleroma/blocks_import` ## `/api/pleroma/blocks_import`
### Imports your blocks. ### Imports your blocks.
* Method: `POST` * Method: `POST`
* Authentication: required * Authentication: required
@ -54,7 +54,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
* `list`: STRING or FILE containing a whitespace-separated list of accounts to block * `list`: STRING or FILE containing a whitespace-separated list of accounts to block
* Response: HTTP 200 on success, 500 on error * Response: HTTP 200 on success, 500 on error
## `/api/v1/pleroma/mutes_import` ## `/api/pleroma/mutes_import`
### Imports your mutes. ### Imports your mutes.
* Method: `POST` * Method: `POST`
* Authentication: required * Authentication: required
@ -70,7 +70,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
* Response: Provider specific JSON, the only guaranteed parameter is `type` * Response: Provider specific JSON, the only guaranteed parameter is `type`
* Example response: `{"type": "kocaptcha", "token": "whatever", "url": "https://captcha.kotobank.ch/endpoint", "seconds_valid": 300}` * Example response: `{"type": "kocaptcha", "token": "whatever", "url": "https://captcha.kotobank.ch/endpoint", "seconds_valid": 300}`
## `/api/v1/pleroma/delete_account` ## `/api/pleroma/delete_account`
### Delete an account ### Delete an account
* Method `POST` * Method `POST`
* Authentication: required * Authentication: required
@ -79,7 +79,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
* Response: JSON. Returns `{"status": "success"}` if the deletion was successful, `{"error": "[error message]"}` otherwise * Response: JSON. Returns `{"status": "success"}` if the deletion was successful, `{"error": "[error message]"}` otherwise
* Example response: `{"error": "Invalid password."}` * Example response: `{"error": "Invalid password."}`
## `/api/v1/pleroma/disable_account` ## `/api/pleroma/disable_account`
### Disable an account ### Disable an account
* Method `POST` * Method `POST`
* Authentication: required * Authentication: required
@ -88,21 +88,22 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
* Response: JSON. Returns `{"status": "success"}` if the account was successfully disabled, `{"error": "[error message]"}` otherwise * Response: JSON. Returns `{"status": "success"}` if the account was successfully disabled, `{"error": "[error message]"}` otherwise
* Example response: `{"error": "Invalid password."}` * Example response: `{"error": "Invalid password."}`
## `/api/v1/pleroma/accounts/mfa` ## `/api/pleroma/accounts/mfa`
#### Gets current MFA settings #### Gets current MFA settings
* method: `GET` * method: `GET`
* Authentication: required * Authentication: required
* OAuth scope: `read:security` * OAuth scope: `read:security`
* Response: JSON. Returns `{"enabled": "false", "totp": false }` * Response: JSON. Returns `{"settings": {"enabled": "false", "totp": false }}`
* Note: `enabled` is whether multi-factor auth is enabled for the user in general, while `totp` is one type of MFA.
## `/api/v1/pleroma/accounts/mfa/setup/totp` ## `/api/pleroma/accounts/mfa/setup/totp`
#### Pre-setup the MFA/TOTP method #### Pre-setup the MFA/TOTP method
* method: `GET` * method: `GET`
* Authentication: required * Authentication: required
* OAuth scope: `write:security` * OAuth scope: `write:security`
* Response: JSON. Returns `{"key": [secret_key], "provisioning_uri": "[qr code uri]" }` when successful, otherwise returns HTTP 422 `{"error": "error_msg"}` * Response: JSON. Returns `{"key": [secret_key], "provisioning_uri": "[qr code uri]" }` when successful, otherwise returns HTTP 422 `{"error": "error_msg"}`
## `/api/v1/pleroma/accounts/mfa/confirm/totp` ## `/api/pleroma/accounts/mfa/confirm/totp`
#### Confirms & enables MFA/TOTP support for user account. #### Confirms & enables MFA/TOTP support for user account.
* method: `POST` * method: `POST`
* Authentication: required * Authentication: required
@ -113,7 +114,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
* Response: JSON. Returns `{}` if the enable was successful, HTTP 422 `{"error": "[error message]"}` otherwise * Response: JSON. Returns `{}` if the enable was successful, HTTP 422 `{"error": "[error message]"}` otherwise
## `/api/v1/pleroma/accounts/mfa/totp` ## `/api/pleroma/accounts/mfa/totp`
#### Disables MFA/TOTP method for user account. #### Disables MFA/TOTP method for user account.
* method: `DELETE` * method: `DELETE`
* Authentication: required * Authentication: required
@ -123,7 +124,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
* Response: JSON. Returns `{}` if the disable was successful, HTTP 422 `{"error": "[error message]"}` otherwise * Response: JSON. Returns `{}` if the disable was successful, HTTP 422 `{"error": "[error message]"}` otherwise
* Example response: `{"error": "Invalid password."}` * Example response: `{"error": "Invalid password."}`
## `/api/v1/pleroma/accounts/mfa/backup_codes` ## `/api/pleroma/accounts/mfa/backup_codes`
#### Generstes backup codes MFA for user account. #### Generstes backup codes MFA for user account.
* method: `GET` * method: `GET`
* Authentication: required * Authentication: required
@ -331,7 +332,7 @@ See [Admin-API](admin_api.md)
} }
``` ```
## `/api/v1/pleroma/change_email` ## `/api/pleroma/change_email`
### Change account email ### Change account email
* Method `POST` * Method `POST`
* Authentication: required * Authentication: required
@ -689,3 +690,38 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
"url": "https://example.com/media/backups/archive-foobar-20200910T161803-QUhx6VYDRQ2wfV0SdA2Pfj_2CLM_ATUlw-D5l5TJf4Q.zip" "url": "https://example.com/media/backups/archive-foobar-20200910T161803-QUhx6VYDRQ2wfV0SdA2Pfj_2CLM_ATUlw-D5l5TJf4Q.zip"
}] }]
``` ```
## `GET /api/oauth_tokens`
### Retrieve a list of active sessions for the user
* Method: `GET`
* Authentication: required
* Params: none
* Response: JSON
* Example response:
```json
[
{
"app_name": "Pleroma FE",
"id": 9275,
"valid_until": "2121-11-24T15:51:08.234234"
},
{
"app_name": "Patron",
"id": 8805,
"valid_until": "2121-10-26T18:09:59.857150"
},
{
"app_name": "Soapbox FE",
"id": 9727,
"valid_until": "2121-12-25T16:52:39.692877"
}
]
```
## `DELETE /api/oauth_tokens/:id`
### Revoke a user session by its ID
* Method: `DELETE`
* Authentication: required
* Params: none
* Response: HTTP 200 on success, 500 on error

View File

@ -0,0 +1,9 @@
# Setting up a Gitlab-runner
When you push changes, a pipeline will start some automated jobs. These are done with so called [runners](https://docs.gitlab.com/runner/), services that run somewhere on a server and run these automated jobs. These jobs typically run tests and should pass. If not, you probably need to fix something.
Generally, Pleroma provides a runner, so you don't need to set up your own. However, if for whatever reason you want to set up your own, here's some high level instructions.
1. We use docker to run the jobs, so you should install that. For Debian, you need to allow non-free packages in the [source list](https://wiki.debian.org/SourcesList). Then you can install docker with `apt install docker-compose`.
2. You can [install](https://docs.gitlab.com/runner/install/index.html) and [configure](https://docs.gitlab.com/runner/register/index.html) a Gitlab-runner. It's probably easiest to install from the packages, but there are other options as well.
3. When registering the runner, you'll need some values. You can find them in the project under your own name. Choose "Settings", "CI/CD", and then expand "Runners". For executor you can choose "docker". For default image, you can use the image used in <https://git.pleroma.social/pleroma/pleroma/-/blob/develop/.gitlab-ci.yml#L1> (although it shouldn't matter much).

View File

@ -1,9 +1,9 @@
# Optional software packages needed for specific functionality # Optional software packages needed for specific functionality
For specific Pleroma functionality (which is disabled by default) some or all of the below packages are required: For specific Pleroma functionality (which is disabled by default) some or all of the below packages are required:
* `ImageMagic` * `ImageMagic`
* `ffmpeg` * `ffmpeg`
* `exiftool` * `exiftool`
Please refer to documentation in `docs/installation` on how to install them on specific OS. Please refer to documentation in `docs/installation` on how to install them on specific OS.
@ -14,19 +14,20 @@ Note: the packages are not required with the current default settings of Pleroma
`ImageMagick` is a set of tools to create, edit, compose, or convert bitmap images. `ImageMagick` is a set of tools to create, edit, compose, or convert bitmap images.
It is required for the following Pleroma features: It is required for the following Pleroma features:
* `Pleroma.Upload.Filters.Mogrify`, `Pleroma.Upload.Filters.Mogrifun` upload filters (related config: `Plaroma.Upload/filters` in `config/config.exs`) * `Pleroma.Upload.Filters.Mogrify`, `Pleroma.Upload.Filters.Mogrifun` upload filters (related config: `Plaroma.Upload/filters` in `config/config.exs`)
* Media preview proxy for still images (related config: `media_preview_proxy/enabled` in `config/config.exs`) * Media preview proxy for still images (related config: `media_preview_proxy/enabled` in `config/config.exs`)
## `ffmpeg` ## `ffmpeg`
`ffmpeg` is software to record, convert and stream audio and video. `ffmpeg` is software to record, convert and stream audio and video.
It is required for the following Pleroma features: It is required for the following Pleroma features:
* Media preview proxy for videos (related config: `media_preview_proxy/enabled` in `config/config.exs`) * Media preview proxy for videos (related config: `media_preview_proxy/enabled` in `config/config.exs`)
## `exiftool` ## `exiftool`
`exiftool` is media files metadata reader/writer. `exiftool` is media files metadata reader/writer.
It is required for the following Pleroma features: It is required for the following Pleroma features:
* `Pleroma.Upload.Filters.Exiftool` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) * `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`)

View File

@ -5,34 +5,13 @@
# 2. Copy this section into your Caddyfile and restart Caddy. # 2. Copy this section into your Caddyfile and restart Caddy.
example.tld { example.tld {
log /var/log/caddy/pleroma_access.log log {
errors /var/log/caddy/pleroma_error.log output file /var/log/caddy/pleroma.log
}
gzip encode gzip
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only # this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only
# and `localhost.` resolves to [::0] on some systems: see issue #930 # and `localhost.` resolves to [::0] on some systems: see issue #930
proxy / 127.0.0.1:4000 { reverse_proxy 127.0.0.1:4000
websocket
transparent
}
tls {
# Remove the rest of the lines in here, if you want to support older devices
key_type p256
ciphers ECDHE-ECDSA-WITH-CHACHA20-POLY1305 ECDHE-RSA-WITH-CHACHA20-POLY1305 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-GCM-SHA256
}
# If you do not want to use the mediaproxy function, remove these lines.
# To use this directive, you need the http.cache plugin for Caddy.
cache {
match_path /media
default_max_age 720m
}
cache {
match_path /proxy
default_max_age 720m
}
# Stop removing lines here.
} }

View File

@ -81,6 +81,19 @@ server {
proxy_pass http://phoenix; proxy_pass http://phoenix;
} }
# Uncomment this if you want notice compatibility routes for frontends like Soapbox.
# location ~ ^/@[^/]+/([^/]+)$ {
# proxy_pass http://phoenix/notice/$1;
# }
#
# location ~ ^/@[^/]+/posts/([^/]+)$ {
# proxy_pass http://phoenix/notice/$1;
# }
#
# location ~ ^/[^/]+/status/([^/]+)$ {
# proxy_pass http://phoenix/notice/$1;
# }
location ~ ^/(media|proxy) { location ~ ^/(media|proxy) {
proxy_cache pleroma_media_cache; proxy_cache pleroma_media_cache;
slice 1m; slice 1m;

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Pleroma do defmodule Mix.Pleroma do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.App do defmodule Mix.Tasks.Pleroma.App do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Benchmark do defmodule Mix.Tasks.Pleroma.Benchmark do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Config do defmodule Mix.Tasks.Pleroma.Config do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.CountStatuses do defmodule Mix.Tasks.Pleroma.CountStatuses do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Database do defmodule Mix.Tasks.Pleroma.Database do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Digest do defmodule Mix.Tasks.Pleroma.Digest do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Docs do defmodule Mix.Tasks.Pleroma.Docs do

View File

@ -1,6 +1,6 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-onl # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Ecto do defmodule Mix.Tasks.Pleroma.Ecto do
@doc """ @doc """

View File

@ -1,6 +1,6 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-onl # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Ecto.Migrate do defmodule Mix.Tasks.Pleroma.Ecto.Migrate do
use Mix.Task use Mix.Task

View File

@ -1,6 +1,6 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-onl # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Ecto.Rollback do defmodule Mix.Tasks.Pleroma.Ecto.Rollback do
use Mix.Task use Mix.Task

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Email do defmodule Mix.Tasks.Pleroma.Email do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Emoji do defmodule Mix.Tasks.Pleroma.Emoji do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Frontend do defmodule Mix.Tasks.Pleroma.Frontend do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Instance do defmodule Mix.Tasks.Pleroma.Instance do
@ -34,7 +34,8 @@ defmodule Mix.Tasks.Pleroma.Instance do
static_dir: :string, static_dir: :string,
listen_ip: :string, listen_ip: :string,
listen_port: :string, listen_port: :string,
strip_uploads: :string, strip_uploads_location: :string,
read_uploads_description: :string,
anonymize_uploads: :string, anonymize_uploads: :string,
dedupe_uploads: :string dedupe_uploads: :string
], ],
@ -161,7 +162,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
) )
|> Path.expand() |> Path.expand()
{strip_uploads_message, strip_uploads_default} = {strip_uploads_location_message, strip_uploads_location_default} =
if Pleroma.Utils.command_available?("exiftool") do if Pleroma.Utils.command_available?("exiftool") do
{"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as installed. (y/n)", {"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as installed. (y/n)",
"y"} "y"}
@ -170,12 +171,29 @@ defmodule Mix.Tasks.Pleroma.Instance do
"n"} "n"}
end end
strip_uploads = strip_uploads_location =
get_option( get_option(
options, options,
:strip_uploads, :strip_uploads_location,
strip_uploads_message, strip_uploads_location_message,
strip_uploads_default strip_uploads_location_default
) === "y"
{read_uploads_description_message, read_uploads_description_default} =
if Pleroma.Utils.command_available?("exiftool") do
{"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as installed. (y/n)",
"y"}
else
{"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as not installed, please install it if you answer yes. (y/n)",
"n"}
end
read_uploads_description =
get_option(
options,
:read_uploads_description,
read_uploads_description_message,
read_uploads_description_default
) === "y" ) === "y"
anonymize_uploads = anonymize_uploads =
@ -229,7 +247,8 @@ defmodule Mix.Tasks.Pleroma.Instance do
listen_port: listen_port, listen_port: listen_port,
upload_filters: upload_filters:
upload_filters(%{ upload_filters(%{
strip: strip_uploads, strip_location: strip_uploads_location,
read_description: read_uploads_description,
anonymize: anonymize_uploads, anonymize: anonymize_uploads,
dedupe: dedupe_uploads dedupe: dedupe_uploads
}) })
@ -297,12 +316,19 @@ defmodule Mix.Tasks.Pleroma.Instance do
defp upload_filters(filters) when is_map(filters) do defp upload_filters(filters) when is_map(filters) do
enabled_filters = enabled_filters =
if filters.strip do if filters.strip_location do
[Pleroma.Upload.Filter.Exiftool] [Pleroma.Upload.Filter.Exiftool.StripLocation]
else else
[] []
end end
enabled_filters =
if filters.read_description do
enabled_filters ++ [Pleroma.Upload.Filter.Exiftool.ReadDescription]
else
enabled_filters
end
enabled_filters = enabled_filters =
if filters.anonymize do if filters.anonymize do
enabled_filters ++ [Pleroma.Upload.Filter.AnonymizeFilename] enabled_filters ++ [Pleroma.Upload.Filter.AnonymizeFilename]

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.NotificationSettings do defmodule Mix.Tasks.Pleroma.NotificationSettings do

View File

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.OpenapiSpec do defmodule Mix.Tasks.Pleroma.OpenapiSpec do
def run([path]) do def run([path]) do
# Load Pleroma application to get version info # Load Pleroma application to get version info

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.RefreshCounterCache do defmodule Mix.Tasks.Pleroma.RefreshCounterCache do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Relay do defmodule Mix.Tasks.Pleroma.Relay do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.RobotsTxt do defmodule Mix.Tasks.Pleroma.RobotsTxt do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Uploads do defmodule Mix.Tasks.Pleroma.Uploads do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.User do defmodule Mix.Tasks.Pleroma.User do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Phoenix.Transports.WebSocket.Raw do defmodule Phoenix.Transports.WebSocket.Raw do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity do defmodule Pleroma.Activity do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity.HTML do defmodule Pleroma.Activity.HTML do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity.Ir.Topics do defmodule Pleroma.Activity.Ir.Topics do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity.Queries do defmodule Pleroma.Activity.Queries do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity.Search do defmodule Pleroma.Activity.Search do

160
lib/pleroma/announcement.ex Normal file
View File

@ -0,0 +1,160 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Announcement do
use Ecto.Schema
import Ecto.Changeset, only: [cast: 3, validate_required: 2]
import Ecto.Query
alias Pleroma.AnnouncementReadRelationship
alias Pleroma.Repo
@type t :: %__MODULE__{}
@primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true}
schema "announcements" do
field(:data, :map)
field(:starts_at, :utc_datetime)
field(:ends_at, :utc_datetime)
field(:rendered, :map)
timestamps(type: :utc_datetime)
end
def change(struct, params \\ %{}) do
struct
|> cast(validate_params(struct, params), [:data, :starts_at, :ends_at, :rendered])
|> validate_required([:data])
end
defp validate_params(struct, params) do
base_data =
%{
"content" => "",
"all_day" => false
}
|> Map.merge((struct && struct.data) || %{})
merged_data =
Map.merge(base_data, params.data)
|> Map.take(["content", "all_day"])
params
|> Map.merge(%{data: merged_data})
|> add_rendered_properties()
end
def add_rendered_properties(params) do
{content_html, _, _} =
Pleroma.Web.CommonAPI.Utils.format_input(params.data["content"], "text/plain",
mentions_format: :full
)
rendered = %{
"content" => content_html
}
params
|> Map.put(:rendered, rendered)
end
def add(params) do
changeset = change(%__MODULE__{}, params)
Repo.insert(changeset)
end
def update(announcement, params) do
changeset = change(announcement, params)
Repo.update(changeset)
end
def list_all do
__MODULE__
|> Repo.all()
end
def list_paginated(%{limit: limited_number, offset: offset_number}) do
__MODULE__
|> limit(^limited_number)
|> offset(^offset_number)
|> Repo.all()
end
def get_by_id(id) do
Repo.get_by(__MODULE__, id: id)
end
def delete_by_id(id) do
with announcement when not is_nil(announcement) <- get_by_id(id),
{:ok, _} <- Repo.delete(announcement) do
:ok
else
_ ->
:error
end
end
def read_by?(announcement, user) do
AnnouncementReadRelationship.exists?(user, announcement)
end
def mark_read_by(announcement, user) do
AnnouncementReadRelationship.mark_read(user, announcement)
end
def render_json(announcement, opts \\ []) do
extra_params =
case Keyword.fetch(opts, :for) do
{:ok, user} when not is_nil(user) ->
%{read: read_by?(announcement, user)}
_ ->
%{}
end
admin_extra_params =
case Keyword.fetch(opts, :admin) do
{:ok, true} ->
%{pleroma: %{raw_content: announcement.data["content"]}}
_ ->
%{}
end
base = %{
id: announcement.id,
content: announcement.rendered["content"],
starts_at: announcement.starts_at,
ends_at: announcement.ends_at,
all_day: announcement.data["all_day"],
published_at: announcement.inserted_at,
updated_at: announcement.updated_at,
mentions: [],
statuses: [],
tags: [],
emojis: [],
reactions: []
}
base
|> Map.merge(extra_params)
|> Map.merge(admin_extra_params)
end
# "visible" means:
# starts_at < time < ends_at
def list_all_visible_when(time) do
__MODULE__
|> where([a], is_nil(a.starts_at) or a.starts_at < ^time)
|> where([a], is_nil(a.ends_at) or a.ends_at > ^time)
|> Repo.all()
end
def list_all_visible do
list_all_visible_when(DateTime.now("Etc/UTC") |> elem(1))
end
end

View File

@ -0,0 +1,55 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.AnnouncementReadRelationship do
use Ecto.Schema
import Ecto.Changeset
alias FlakeId.Ecto.CompatType
alias Pleroma.Announcement
alias Pleroma.Repo
alias Pleroma.User
@type t :: %__MODULE__{}
schema "announcement_read_relationships" do
belongs_to(:user, User, type: CompatType)
belongs_to(:announcement, Announcement, type: CompatType)
timestamps(updated_at: false)
end
def mark_read(user, announcement) do
%__MODULE__{}
|> cast(%{user_id: user.id, announcement_id: announcement.id}, [:user_id, :announcement_id])
|> validate_required([:user_id, :announcement_id])
|> foreign_key_constraint(:user_id)
|> foreign_key_constraint(:announcement_id)
|> unique_constraint([:user_id, :announcement_id])
|> Repo.insert()
end
def mark_unread(user, announcement) do
with relationship <- get(user, announcement),
{:exists, true} <- {:exists, not is_nil(relationship)},
{:ok, _} <- Repo.delete(relationship) do
:ok
else
{:exists, false} ->
:ok
_ ->
:error
end
end
def get(user, announcement) do
Repo.get_by(__MODULE__, user_id: user.id, announcement_id: announcement.id)
end
def exists?(user, announcement) do
not is_nil(get(user, announcement))
end
end

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Application do defmodule Pleroma.Application do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ApplicationRequirements do defmodule Pleroma.ApplicationRequirements do
@ -164,7 +164,8 @@ defmodule Pleroma.ApplicationRequirements do
defp check_system_commands!(:ok) do defp check_system_commands!(:ok) do
filter_commands_statuses = [ filter_commands_statuses = [
check_filter(Pleroma.Upload.Filter.Exiftool, "exiftool"), check_filter(Pleroma.Upload.Filter.Exiftool.StripLocation, "exiftool"),
check_filter(Pleroma.Upload.Filter.Exiftool.ReadDescription, "exiftool"),
check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"), check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"),
check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"), check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"),
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "mogrify"), check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "mogrify"),

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.BBS.Authenticator do defmodule Pleroma.BBS.Authenticator do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.BBS.Handler do defmodule Pleroma.BBS.Handler do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Bookmark do defmodule Pleroma.Bookmark do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Caching do defmodule Pleroma.Caching do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha do defmodule Pleroma.Captcha do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha.Kocaptcha do defmodule Pleroma.Captcha.Kocaptcha do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha.Native do defmodule Pleroma.Captcha.Native do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha.Service do defmodule Pleroma.Captcha.Service do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Chat do defmodule Pleroma.Chat do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Chat.MessageReference do defmodule Pleroma.Chat.MessageReference do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Clippy do defmodule Pleroma.Clippy do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config do defmodule Pleroma.Config do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.DeprecationWarnings do defmodule Pleroma.Config.DeprecationWarnings do
@ -20,6 +20,43 @@ defmodule Pleroma.Config.DeprecationWarnings do
"\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"} "\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"}
] ]
def check_exiftool_filter do
filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, [])
if Pleroma.Upload.Filter.Exiftool in filters do
Logger.warn("""
!!!DEPRECATION WARNING!!!
Your config is using Exiftool as a filter instead of Exiftool.StripLocation. This should work for now, but you are advised to change to the new configuration to prevent possible issues later:
```
config :pleroma, Pleroma.Upload,
filters: [Pleroma.Upload.Filter.Exiftool]
```
Is now
```
config :pleroma, Pleroma.Upload,
filters: [Pleroma.Upload.Filter.Exiftool.StripLocation]
```
""")
new_config =
filters
|> Enum.map(fn
Pleroma.Upload.Filter.Exiftool -> Pleroma.Upload.Filter.Exiftool.StripLocation
filter -> filter
end)
Config.put([Pleroma.Upload, :filters], new_config)
:error
else
:ok
end
end
def check_simple_policy_tuples do def check_simple_policy_tuples do
has_strings = has_strings =
Config.get([:mrf_simple]) Config.get([:mrf_simple])
@ -180,7 +217,8 @@ defmodule Pleroma.Config.DeprecationWarnings do
check_old_chat_shoutbox(), check_old_chat_shoutbox(),
check_quarantined_instances_tuples(), check_quarantined_instances_tuples(),
check_transparency_exclusions_tuples(), check_transparency_exclusions_tuples(),
check_simple_policy_tuples() check_simple_policy_tuples(),
check_exiftool_filter()
] ]
|> Enum.reduce(:ok, fn |> Enum.reduce(:ok, fn
:ok, :ok -> :ok :ok, :ok -> :ok

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Getting do defmodule Pleroma.Config.Getting do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Helpers do defmodule Pleroma.Config.Helpers do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Holder do defmodule Pleroma.Config.Holder do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Loader do defmodule Pleroma.Config.Loader do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Oban do defmodule Pleroma.Config.Oban do

View File

@ -1,3 +1,7 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.ReleaseRuntimeProvider do defmodule Pleroma.Config.ReleaseRuntimeProvider do
@moduledoc """ @moduledoc """
Imports runtime config and `{env}.exported_from_db.secret.exs` for releases. Imports runtime config and `{env}.exported_from_db.secret.exs` for releases.

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.TransferTask do defmodule Pleroma.Config.TransferTask do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ConfigDB do defmodule Pleroma.ConfigDB do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Constants do defmodule Pleroma.Constants do
@ -27,4 +27,10 @@ defmodule Pleroma.Constants do
do: do:
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css) ~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css)
) )
# basic regex, just there to weed out potential mistakes
# https://datatracker.ietf.org/doc/html/rfc2045#section-5.1
const(mime_regex,
do: ~r/^[^[:cntrl:] ()<>@,;:\\"\/\[\]?=]+\/[^[:cntrl:] ()<>@,;:\\"\/\[\]?=]+(; .*)?$/
)
end end

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Conversation do defmodule Pleroma.Conversation do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Conversation.Participation do defmodule Pleroma.Conversation.Participation do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Conversation.Participation.RecipientShip do defmodule Pleroma.Conversation.Participation.RecipientShip do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.CounterCache do defmodule Pleroma.CounterCache do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.DataMigration do defmodule Pleroma.DataMigration do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Delivery do defmodule Pleroma.Delivery do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Docs.Generator do defmodule Pleroma.Docs.Generator do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Docs.JSON do defmodule Pleroma.Docs.JSON do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Docs.Markdown do defmodule Pleroma.Docs.Markdown do

View File

@ -0,0 +1,10 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Docs.Translator do
require Pleroma.Docs.Translator.Compiler
require Pleroma.Web.Gettext
@before_compile Pleroma.Docs.Translator.Compiler
end

View File

@ -0,0 +1,119 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Docs.Translator.Compiler do
@external_resource "config/description.exs"
@raw_config Pleroma.Config.Loader.read("config/description.exs")
@raw_descriptions @raw_config[:pleroma][:config_description]
defmacro __before_compile__(_env) do
strings =
__MODULE__.descriptions()
|> __MODULE__.extract_strings()
quote do
def placeholder do
unquote do
Enum.map(
strings,
fn {path, type, string} ->
ctxt = msgctxt_for(path, type)
quote do
Pleroma.Web.Gettext.dpgettext_noop(
"config_descriptions",
unquote(ctxt),
unquote(string)
)
end
end
)
end
end
end
end
def descriptions do
Pleroma.Web.ActivityPub.MRF.config_descriptions()
|> Enum.reduce(@raw_descriptions, fn description, acc -> [description | acc] end)
|> Pleroma.Docs.Generator.convert_to_strings()
end
def extract_strings(descriptions) do
descriptions
|> Enum.reduce(%{strings: [], path: []}, &process_item/2)
|> Map.get(:strings)
end
defp process_item(entity, acc) do
current_level =
acc
|> process_desc(entity)
|> process_label(entity)
process_children(entity, current_level)
end
defp process_desc(acc, %{description: desc} = item) do
%{
strings: [{acc.path ++ [key_for(item)], "description", desc} | acc.strings],
path: acc.path
}
end
defp process_desc(acc, _) do
acc
end
defp process_label(acc, %{label: label} = item) do
%{
strings: [{acc.path ++ [key_for(item)], "label", label} | acc.strings],
path: acc.path
}
end
defp process_label(acc, _) do
acc
end
defp process_children(%{children: children} = item, acc) do
current_level = Map.put(acc, :path, acc.path ++ [key_for(item)])
children
|> Enum.reduce(current_level, &process_item/2)
|> Map.put(:path, acc.path)
end
defp process_children(_, acc) do
acc
end
def msgctxt_for(path, type) do
"config #{type} at #{Enum.join(path, " > ")}"
end
defp convert_group({_, group}) do
group
end
defp convert_group(group) do
group
end
def key_for(%{group: group, key: key}) do
"#{convert_group(group)}-#{key}"
end
def key_for(%{group: group}) do
convert_group(group)
end
def key_for(%{key: key}) do
key
end
def key_for(_) do
nil
end
end

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
import EctoEnum import EctoEnum
@ -10,7 +10,8 @@ defenum(Pleroma.UserRelationship.Type,
reblog_mute: 3, reblog_mute: 3,
notification_mute: 4, notification_mute: 4,
inverse_subscription: 5, inverse_subscription: 5,
suggestion_dismiss: 6 suggestion_dismiss: 6,
endorsement: 7
) )
defenum(Pleroma.FollowingRelationship.State, defenum(Pleroma.FollowingRelationship.State,

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime do defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.DateTime do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Emoji do defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Emoji do

View File

@ -0,0 +1,25 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.MIME do
use Ecto.Type
require Pleroma.Constants
def type, do: :string
def cast(mime) when is_binary(mime) do
if mime =~ Pleroma.Constants.mime_regex() do
{:ok, mime}
else
{:ok, "application/octet-stream"}
end
end
def cast(_), do: :error
def dump(data), do: {:ok, data}
def load(data), do: {:ok, data}
end

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID do defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients do defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Recipients do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText do defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.SafeText do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Uri do defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Uri do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.Config.Atom do defmodule Pleroma.EctoType.Config.Atom do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.Config.BinaryValue do defmodule Pleroma.EctoType.Config.BinaryValue do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.AdminEmail do defmodule Pleroma.Emails.AdminEmail do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.Mailer do defmodule Pleroma.Emails.Mailer do

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.NewUsersDigestEmail do defmodule Pleroma.Emails.NewUsersDigestEmail do

View File

@ -1,13 +1,16 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.UserEmail do defmodule Pleroma.Emails.UserEmail do
@moduledoc "User emails" @moduledoc "User emails"
require Pleroma.Web.Gettext
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.Endpoint alias Pleroma.Web.Endpoint
alias Pleroma.Web.Gettext
alias Pleroma.Web.Router alias Pleroma.Web.Router
import Swoosh.Email import Swoosh.Email
@ -27,29 +30,75 @@ defmodule Pleroma.Emails.UserEmail do
@spec welcome(User.t(), map()) :: Swoosh.Email.t() @spec welcome(User.t(), map()) :: Swoosh.Email.t()
def welcome(user, opts \\ %{}) do def welcome(user, opts \\ %{}) do
new() Gettext.with_locale_or_default user.language do
|> to(recipient(user)) new()
|> from(Map.get(opts, :sender, sender())) |> to(recipient(user))
|> subject(Map.get(opts, :subject, "Welcome to #{instance_name()}!")) |> from(Map.get(opts, :sender, sender()))
|> html_body(Map.get(opts, :html, "Welcome to #{instance_name()}!")) |> subject(
|> text_body(Map.get(opts, :text, "Welcome to #{instance_name()}!")) Map.get(
opts,
:subject,
Gettext.dpgettext(
"static_pages",
"welcome email subject",
"Welcome to %{instance_name}!",
instance_name: instance_name()
)
)
)
|> html_body(
Map.get(
opts,
:html,
Gettext.dpgettext(
"static_pages",
"welcome email html body",
"Welcome to %{instance_name}!",
instance_name: instance_name()
)
)
)
|> text_body(
Map.get(
opts,
:text,
Gettext.dpgettext(
"static_pages",
"welcome email text body",
"Welcome to %{instance_name}!",
instance_name: instance_name()
)
)
)
end
end end
def password_reset_email(user, token) when is_binary(token) do def password_reset_email(user, token) when is_binary(token) do
password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token) Gettext.with_locale_or_default user.language do
password_reset_url = Router.Helpers.reset_password_url(Endpoint, :reset, token)
html_body = """ html_body =
<h3>Reset your password at #{instance_name()}</h3> Gettext.dpgettext(
<p>Someone has requested password change for your account at #{instance_name()}.</p> "static_pages",
<p>If it was you, visit the following link to proceed: <a href="#{password_reset_url}">reset password</a>.</p> "password reset email body",
<p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p> """
""" <h3>Reset your password at %{instance_name}</h3>
<p>Someone has requested password change for your account at %{instance_name}.</p>
<p>If it was you, visit the following link to proceed: <a href="%{password_reset_url}">reset password</a>.</p>
<p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p>
""",
instance_name: instance_name(),
password_reset_url: password_reset_url
)
new() new()
|> to(recipient(user)) |> to(recipient(user))
|> from(sender()) |> from(sender())
|> subject("Password reset") |> subject(
|> html_body(html_body) Gettext.dpgettext("static_pages", "password reset email subject", "Password reset")
)
|> html_body(html_body)
end
end end
def user_invitation_email( def user_invitation_email(
@ -58,73 +107,136 @@ defmodule Pleroma.Emails.UserEmail do
to_email, to_email,
to_name \\ nil to_name \\ nil
) do ) do
registration_url = Gettext.with_locale_or_default user.language do
Router.Helpers.redirect_url( registration_url =
Endpoint, Router.Helpers.redirect_url(
:registration_page, Endpoint,
user_invite_token.token :registration_page,
user_invite_token.token
)
html_body =
Gettext.dpgettext(
"static_pages",
"user invitation email body",
"""
<h3>You are invited to %{instance_name}</h3>
<p>%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.</p>
<p>Click the following link to register: <a href="%{registration_url}">accept invitation</a>.</p>
""",
instance_name: instance_name(),
inviter_name: user.name,
registration_url: registration_url
)
new()
|> to(recipient(to_email, to_name))
|> from(sender())
|> subject(
Gettext.dpgettext(
"static_pages",
"user invitation email subject",
"Invitation to %{instance_name}",
instance_name: instance_name()
)
) )
|> html_body(html_body)
html_body = """ end
<h3>You are invited to #{instance_name()}</h3>
<p>#{user.name} invites you to join #{instance_name()}, an instance of Pleroma federated social networking platform.</p>
<p>Click the following link to register: <a href="#{registration_url}">accept invitation</a>.</p>
"""
new()
|> to(recipient(to_email, to_name))
|> from(sender())
|> subject("Invitation to #{instance_name()}")
|> html_body(html_body)
end end
def account_confirmation_email(user) do def account_confirmation_email(user) do
confirmation_url = Gettext.with_locale_or_default user.language do
Router.Helpers.confirm_email_url( confirmation_url =
Endpoint, Router.Helpers.confirm_email_url(
:confirm_email, Endpoint,
user.id, :confirm_email,
to_string(user.confirmation_token) user.id,
to_string(user.confirmation_token)
)
html_body =
Gettext.dpgettext(
"static_pages",
"confirmation email body",
"""
<h3>Thank you for registering on %{instance_name}</h3>
<p>Email confirmation is required to activate the account.</p>
<p>Please click the following link to <a href="%{confirmation_url}">activate your account</a>.</p>
""",
instance_name: instance_name(),
confirmation_url: confirmation_url
)
new()
|> to(recipient(user))
|> from(sender())
|> subject(
Gettext.dpgettext(
"static_pages",
"confirmation email subject",
"%{instance_name} account confirmation",
instance_name: instance_name()
)
) )
|> html_body(html_body)
html_body = """ end
<h3>Thank you for registering on #{instance_name()}</h3>
<p>Email confirmation is required to activate the account.</p>
<p>Please click the following link to <a href="#{confirmation_url}">activate your account</a>.</p>
"""
new()
|> to(recipient(user))
|> from(sender())
|> subject("#{instance_name()} account confirmation")
|> html_body(html_body)
end end
def approval_pending_email(user) do def approval_pending_email(user) do
html_body = """ Gettext.with_locale_or_default user.language do
<h3>Awaiting Approval</h3> html_body =
<p>Your account at #{instance_name()} is being reviewed by staff. You will receive another email once your account is approved.</p> Gettext.dpgettext(
""" "static_pages",
"approval pending email body",
"""
<h3>Awaiting Approval</h3>
<p>Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.</p>
""",
instance_name: instance_name()
)
new() new()
|> to(recipient(user)) |> to(recipient(user))
|> from(sender()) |> from(sender())
|> subject("Your account is awaiting approval") |> subject(
|> html_body(html_body) Gettext.dpgettext(
"static_pages",
"approval pending email subject",
"Your account is awaiting approval"
)
)
|> html_body(html_body)
end
end end
def successful_registration_email(user) do def successful_registration_email(user) do
html_body = """ Gettext.with_locale_or_default user.language do
<h3>Hello @#{user.nickname},</h3> html_body =
<p>Your account at #{instance_name()} has been registered successfully.</p> Gettext.dpgettext(
<p>No further action is required to activate your account.</p> "static_pages",
""" "successful registration email body",
"""
<h3>Hello @%{nickname},</h3>
<p>Your account at %{instance_name} has been registered successfully.</p>
<p>No further action is required to activate your account.</p>
""",
nickname: user.nickname,
instance_name: instance_name()
)
new() new()
|> to(recipient(user)) |> to(recipient(user))
|> from(sender()) |> from(sender())
|> subject("Account registered on #{instance_name()}") |> subject(
|> html_body(html_body) Gettext.dpgettext(
"static_pages",
"successful registration email subject",
"Account registered on %{instance_name}",
instance_name: instance_name()
)
)
|> html_body(html_body)
end
end end
@doc """ @doc """
@ -134,69 +246,78 @@ defmodule Pleroma.Emails.UserEmail do
""" """
@spec digest_email(User.t()) :: Swoosh.Email.t() | nil @spec digest_email(User.t()) :: Swoosh.Email.t() | nil
def digest_email(user) do def digest_email(user) do
notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at) Gettext.with_locale_or_default user.language do
notifications = Pleroma.Notification.for_user_since(user, user.last_digest_emailed_at)
mentions = mentions =
notifications notifications
|> Enum.filter(&(&1.activity.data["type"] == "Create")) |> Enum.filter(&(&1.activity.data["type"] == "Create"))
|> Enum.map(fn notification -> |> Enum.map(fn notification ->
object = Pleroma.Object.normalize(notification.activity, fetch: false) object = Pleroma.Object.normalize(notification.activity, fetch: false)
if not is_nil(object) do if not is_nil(object) do
object = update_in(object.data["content"], &format_links/1) object = update_in(object.data["content"], &format_links/1)
%{ %{
data: notification, data: notification,
object: object, object: object,
from: User.get_by_ap_id(notification.activity.actor) from: User.get_by_ap_id(notification.activity.actor)
} }
end end
end) end)
|> Enum.filter(& &1) |> Enum.filter(& &1)
followers = followers =
notifications notifications
|> Enum.filter(&(&1.activity.data["type"] == "Follow")) |> Enum.filter(&(&1.activity.data["type"] == "Follow"))
|> Enum.map(fn notification -> |> Enum.map(fn notification ->
from = User.get_by_ap_id(notification.activity.actor) from = User.get_by_ap_id(notification.activity.actor)
if not is_nil(from) do if not is_nil(from) do
%{ %{
data: notification, data: notification,
object: Pleroma.Object.normalize(notification.activity, fetch: false), object: Pleroma.Object.normalize(notification.activity, fetch: false),
from: User.get_by_ap_id(notification.activity.actor) from: User.get_by_ap_id(notification.activity.actor)
} }
end end
end) end)
|> Enum.filter(& &1) |> Enum.filter(& &1)
unless Enum.empty?(mentions) do unless Enum.empty?(mentions) do
styling = Config.get([__MODULE__, :styling]) styling = Config.get([__MODULE__, :styling])
logo = Config.get([__MODULE__, :logo]) logo = Config.get([__MODULE__, :logo])
html_data = %{ html_data = %{
instance: instance_name(), instance: instance_name(),
user: user, user: user,
mentions: mentions, mentions: mentions,
followers: followers, followers: followers,
unsubscribe_link: unsubscribe_url(user, "digest"), unsubscribe_link: unsubscribe_url(user, "digest"),
styling: styling styling: styling
} }
logo_path = logo_path =
if is_nil(logo) do if is_nil(logo) do
Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg") Path.join(:code.priv_dir(:pleroma), "static/static/logo.svg")
else else
Path.join(Config.get([:instance, :static_dir]), logo) Path.join(Config.get([:instance, :static_dir]), logo)
end end
new() new()
|> to(recipient(user)) |> to(recipient(user))
|> from(sender()) |> from(sender())
|> subject("Your digest from #{instance_name()}") |> subject(
|> put_layout(false) Gettext.dpgettext(
|> render_body("digest.html", html_data) "static_pages",
|> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline)) "digest email subject",
"Your digest from %{instance_name}",
instance_name: instance_name()
)
)
|> put_layout(false)
|> render_body("digest.html", html_data)
|> attachment(Swoosh.Attachment.new(logo_path, filename: "logo.svg", type: :inline))
end
end end
end end
@ -226,27 +347,47 @@ defmodule Pleroma.Emails.UserEmail do
def backup_is_ready_email(backup, admin_user_id \\ nil) do def backup_is_ready_email(backup, admin_user_id \\ nil) do
%{user: user} = Pleroma.Repo.preload(backup, :user) %{user: user} = Pleroma.Repo.preload(backup, :user)
download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
html_body = Gettext.with_locale_or_default user.language do
if is_nil(admin_user_id) do download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
"""
<p>You requested a full backup of your Pleroma account. It's ready for download:</p>
<p><a href="#{download_url}">#{download_url}</a></p>
"""
else
admin = Pleroma.Repo.get(User, admin_user_id)
""" html_body =
<p>Admin @#{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:</p> if is_nil(admin_user_id) do
<p><a href="#{download_url}">#{download_url}</a></p> Gettext.dpgettext(
""" "static_pages",
end "account archive email body - self-requested",
"""
<p>You requested a full backup of your Pleroma account. It's ready for download:</p>
<p><a href="%{download_url}">%{download_url}</a></p>
""",
download_url: download_url
)
else
admin = Pleroma.Repo.get(User, admin_user_id)
new() Gettext.dpgettext(
|> to(recipient(user)) "static_pages",
|> from(sender()) "account archive email body - admin requested",
|> subject("Your account archive is ready") """
|> html_body(html_body) <p>Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:</p>
<p><a href="%{download_url}">%{download_url}</a></p>
""",
admin_nickname: admin.nickname,
download_url: download_url
)
end
new()
|> to(recipient(user))
|> from(sender())
|> subject(
Gettext.dpgettext(
"static_pages",
"account archive email subject",
"Your account archive is ready"
)
)
|> html_body(html_body)
end
end end
end end

View File

@ -1,11 +1,11 @@
# emoji-test.txt # emoji-test.txt
# Date: 2020-09-12, 22:19:50 GMT # Date: 2021-08-26, 17:22:23 GMT
# © 2020 Unicode®, Inc. # © 2021 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html # For terms of use, see http://www.unicode.org/terms_of_use.html
# #
# Emoji Keyboard/Display Test Data for UTS #51 # Emoji Keyboard/Display Test Data for UTS #51
# Version: 13.1 # Version: 14.0
# #
# For documentation and usage, see http://www.unicode.org/reports/tr51 # For documentation and usage, see http://www.unicode.org/reports/tr51
# #
@ -43,6 +43,7 @@
1F602 ; fully-qualified # 😂 E0.6 face with tears of joy 1F602 ; fully-qualified # 😂 E0.6 face with tears of joy
1F642 ; fully-qualified # 🙂 E1.0 slightly smiling face 1F642 ; fully-qualified # 🙂 E1.0 slightly smiling face
1F643 ; fully-qualified # 🙃 E1.0 upside-down face 1F643 ; fully-qualified # 🙃 E1.0 upside-down face
1FAE0 ; fully-qualified # 🫠 E14.0 melting face
1F609 ; fully-qualified # 😉 E0.6 winking face 1F609 ; fully-qualified # 😉 E0.6 winking face
1F60A ; fully-qualified # 😊 E0.6 smiling face with smiling eyes 1F60A ; fully-qualified # 😊 E0.6 smiling face with smiling eyes
1F607 ; fully-qualified # 😇 E1.0 smiling face with halo 1F607 ; fully-qualified # 😇 E1.0 smiling face with halo
@ -68,10 +69,13 @@
1F911 ; fully-qualified # 🤑 E1.0 money-mouth face 1F911 ; fully-qualified # 🤑 E1.0 money-mouth face
# subgroup: face-hand # subgroup: face-hand
1F917 ; fully-qualified # 🤗 E1.0 hugging face 1F917 ; fully-qualified # 🤗 E1.0 smiling face with open hands
1F92D ; fully-qualified # 🤭 E5.0 face with hand over mouth 1F92D ; fully-qualified # 🤭 E5.0 face with hand over mouth
1FAE2 ; fully-qualified # 🫢 E14.0 face with open eyes and hand over mouth
1FAE3 ; fully-qualified # 🫣 E14.0 face with peeking eye
1F92B ; fully-qualified # 🤫 E5.0 shushing face 1F92B ; fully-qualified # 🤫 E5.0 shushing face
1F914 ; fully-qualified # 🤔 E1.0 thinking face 1F914 ; fully-qualified # 🤔 E1.0 thinking face
1FAE1 ; fully-qualified # 🫡 E14.0 saluting face
# subgroup: face-neutral-skeptical # subgroup: face-neutral-skeptical
1F910 ; fully-qualified # 🤐 E1.0 zipper-mouth face 1F910 ; fully-qualified # 🤐 E1.0 zipper-mouth face
@ -79,6 +83,7 @@
1F610 ; fully-qualified # 😐 E0.7 neutral face 1F610 ; fully-qualified # 😐 E0.7 neutral face
1F611 ; fully-qualified # 😑 E1.0 expressionless face 1F611 ; fully-qualified # 😑 E1.0 expressionless face
1F636 ; fully-qualified # 😶 E1.0 face without mouth 1F636 ; fully-qualified # 😶 E1.0 face without mouth
1FAE5 ; fully-qualified # 🫥 E14.0 dotted line face
1F636 200D 1F32B FE0F ; fully-qualified # 😶‍🌫️ E13.1 face in clouds 1F636 200D 1F32B FE0F ; fully-qualified # 😶‍🌫️ E13.1 face in clouds
1F636 200D 1F32B ; minimally-qualified # 😶‍🌫 E13.1 face in clouds 1F636 200D 1F32B ; minimally-qualified # 😶‍🌫 E13.1 face in clouds
1F60F ; fully-qualified # 😏 E0.6 smirking face 1F60F ; fully-qualified # 😏 E0.6 smirking face
@ -105,7 +110,7 @@
1F975 ; fully-qualified # 🥵 E11.0 hot face 1F975 ; fully-qualified # 🥵 E11.0 hot face
1F976 ; fully-qualified # 🥶 E11.0 cold face 1F976 ; fully-qualified # 🥶 E11.0 cold face
1F974 ; fully-qualified # 🥴 E11.0 woozy face 1F974 ; fully-qualified # 🥴 E11.0 woozy face
1F635 ; fully-qualified # 😵 E0.6 knocked-out face 1F635 ; fully-qualified # 😵 E0.6 face with crossed-out eyes
1F635 200D 1F4AB ; fully-qualified # 😵‍💫 E13.1 face with spiral eyes 1F635 200D 1F4AB ; fully-qualified # 😵‍💫 E13.1 face with spiral eyes
1F92F ; fully-qualified # 🤯 E5.0 exploding head 1F92F ; fully-qualified # 🤯 E5.0 exploding head
@ -121,6 +126,7 @@
# subgroup: face-concerned # subgroup: face-concerned
1F615 ; fully-qualified # 😕 E1.0 confused face 1F615 ; fully-qualified # 😕 E1.0 confused face
1FAE4 ; fully-qualified # 🫤 E14.0 face with diagonal mouth
1F61F ; fully-qualified # 😟 E1.0 worried face 1F61F ; fully-qualified # 😟 E1.0 worried face
1F641 ; fully-qualified # 🙁 E1.0 slightly frowning face 1F641 ; fully-qualified # 🙁 E1.0 slightly frowning face
2639 FE0F ; fully-qualified # ☹️ E0.7 frowning face 2639 FE0F ; fully-qualified # ☹️ E0.7 frowning face
@ -130,6 +136,7 @@
1F632 ; fully-qualified # 😲 E0.6 astonished face 1F632 ; fully-qualified # 😲 E0.6 astonished face
1F633 ; fully-qualified # 😳 E0.6 flushed face 1F633 ; fully-qualified # 😳 E0.6 flushed face
1F97A ; fully-qualified # 🥺 E11.0 pleading face 1F97A ; fully-qualified # 🥺 E11.0 pleading face
1F979 ; fully-qualified # 🥹 E14.0 face holding back tears
1F626 ; fully-qualified # 😦 E1.0 frowning face with open mouth 1F626 ; fully-qualified # 😦 E1.0 frowning face with open mouth
1F627 ; fully-qualified # 😧 E1.0 anguished face 1F627 ; fully-qualified # 😧 E1.0 anguished face
1F628 ; fully-qualified # 😨 E0.6 fearful face 1F628 ; fully-qualified # 😨 E0.6 fearful face
@ -232,8 +239,8 @@
1F4AD ; fully-qualified # 💭 E1.0 thought balloon 1F4AD ; fully-qualified # 💭 E1.0 thought balloon
1F4A4 ; fully-qualified # 💤 E0.6 zzz 1F4A4 ; fully-qualified # 💤 E0.6 zzz
# Smileys & Emotion subtotal: 170 # Smileys & Emotion subtotal: 177
# Smileys & Emotion subtotal: 170 w/o modifiers # Smileys & Emotion subtotal: 177 w/o modifiers
# group: People & Body # group: People & Body
@ -269,6 +276,30 @@
1F596 1F3FD ; fully-qualified # 🖖🏽 E1.0 vulcan salute: medium skin tone 1F596 1F3FD ; fully-qualified # 🖖🏽 E1.0 vulcan salute: medium skin tone
1F596 1F3FE ; fully-qualified # 🖖🏾 E1.0 vulcan salute: medium-dark skin tone 1F596 1F3FE ; fully-qualified # 🖖🏾 E1.0 vulcan salute: medium-dark skin tone
1F596 1F3FF ; fully-qualified # 🖖🏿 E1.0 vulcan salute: dark skin tone 1F596 1F3FF ; fully-qualified # 🖖🏿 E1.0 vulcan salute: dark skin tone
1FAF1 ; fully-qualified # 🫱 E14.0 rightwards hand
1FAF1 1F3FB ; fully-qualified # 🫱🏻 E14.0 rightwards hand: light skin tone
1FAF1 1F3FC ; fully-qualified # 🫱🏼 E14.0 rightwards hand: medium-light skin tone
1FAF1 1F3FD ; fully-qualified # 🫱🏽 E14.0 rightwards hand: medium skin tone
1FAF1 1F3FE ; fully-qualified # 🫱🏾 E14.0 rightwards hand: medium-dark skin tone
1FAF1 1F3FF ; fully-qualified # 🫱🏿 E14.0 rightwards hand: dark skin tone
1FAF2 ; fully-qualified # 🫲 E14.0 leftwards hand
1FAF2 1F3FB ; fully-qualified # 🫲🏻 E14.0 leftwards hand: light skin tone
1FAF2 1F3FC ; fully-qualified # 🫲🏼 E14.0 leftwards hand: medium-light skin tone
1FAF2 1F3FD ; fully-qualified # 🫲🏽 E14.0 leftwards hand: medium skin tone
1FAF2 1F3FE ; fully-qualified # 🫲🏾 E14.0 leftwards hand: medium-dark skin tone
1FAF2 1F3FF ; fully-qualified # 🫲🏿 E14.0 leftwards hand: dark skin tone
1FAF3 ; fully-qualified # 🫳 E14.0 palm down hand
1FAF3 1F3FB ; fully-qualified # 🫳🏻 E14.0 palm down hand: light skin tone
1FAF3 1F3FC ; fully-qualified # 🫳🏼 E14.0 palm down hand: medium-light skin tone
1FAF3 1F3FD ; fully-qualified # 🫳🏽 E14.0 palm down hand: medium skin tone
1FAF3 1F3FE ; fully-qualified # 🫳🏾 E14.0 palm down hand: medium-dark skin tone
1FAF3 1F3FF ; fully-qualified # 🫳🏿 E14.0 palm down hand: dark skin tone
1FAF4 ; fully-qualified # 🫴 E14.0 palm up hand
1FAF4 1F3FB ; fully-qualified # 🫴🏻 E14.0 palm up hand: light skin tone
1FAF4 1F3FC ; fully-qualified # 🫴🏼 E14.0 palm up hand: medium-light skin tone
1FAF4 1F3FD ; fully-qualified # 🫴🏽 E14.0 palm up hand: medium skin tone
1FAF4 1F3FE ; fully-qualified # 🫴🏾 E14.0 palm up hand: medium-dark skin tone
1FAF4 1F3FF ; fully-qualified # 🫴🏿 E14.0 palm up hand: dark skin tone
# subgroup: hand-fingers-partial # subgroup: hand-fingers-partial
1F44C ; fully-qualified # 👌 E0.6 OK hand 1F44C ; fully-qualified # 👌 E0.6 OK hand
@ -302,6 +333,12 @@
1F91E 1F3FD ; fully-qualified # 🤞🏽 E3.0 crossed fingers: medium skin tone 1F91E 1F3FD ; fully-qualified # 🤞🏽 E3.0 crossed fingers: medium skin tone
1F91E 1F3FE ; fully-qualified # 🤞🏾 E3.0 crossed fingers: medium-dark skin tone 1F91E 1F3FE ; fully-qualified # 🤞🏾 E3.0 crossed fingers: medium-dark skin tone
1F91E 1F3FF ; fully-qualified # 🤞🏿 E3.0 crossed fingers: dark skin tone 1F91E 1F3FF ; fully-qualified # 🤞🏿 E3.0 crossed fingers: dark skin tone
1FAF0 ; fully-qualified # 🫰 E14.0 hand with index finger and thumb crossed
1FAF0 1F3FB ; fully-qualified # 🫰🏻 E14.0 hand with index finger and thumb crossed: light skin tone
1FAF0 1F3FC ; fully-qualified # 🫰🏼 E14.0 hand with index finger and thumb crossed: medium-light skin tone
1FAF0 1F3FD ; fully-qualified # 🫰🏽 E14.0 hand with index finger and thumb crossed: medium skin tone
1FAF0 1F3FE ; fully-qualified # 🫰🏾 E14.0 hand with index finger and thumb crossed: medium-dark skin tone
1FAF0 1F3FF ; fully-qualified # 🫰🏿 E14.0 hand with index finger and thumb crossed: dark skin tone
1F91F ; fully-qualified # 🤟 E5.0 love-you gesture 1F91F ; fully-qualified # 🤟 E5.0 love-you gesture
1F91F 1F3FB ; fully-qualified # 🤟🏻 E5.0 love-you gesture: light skin tone 1F91F 1F3FB ; fully-qualified # 🤟🏻 E5.0 love-you gesture: light skin tone
1F91F 1F3FC ; fully-qualified # 🤟🏼 E5.0 love-you gesture: medium-light skin tone 1F91F 1F3FC ; fully-qualified # 🤟🏼 E5.0 love-you gesture: medium-light skin tone
@ -359,6 +396,12 @@
261D 1F3FD ; fully-qualified # ☝🏽 E1.0 index pointing up: medium skin tone 261D 1F3FD ; fully-qualified # ☝🏽 E1.0 index pointing up: medium skin tone
261D 1F3FE ; fully-qualified # ☝🏾 E1.0 index pointing up: medium-dark skin tone 261D 1F3FE ; fully-qualified # ☝🏾 E1.0 index pointing up: medium-dark skin tone
261D 1F3FF ; fully-qualified # ☝🏿 E1.0 index pointing up: dark skin tone 261D 1F3FF ; fully-qualified # ☝🏿 E1.0 index pointing up: dark skin tone
1FAF5 ; fully-qualified # 🫵 E14.0 index pointing at the viewer
1FAF5 1F3FB ; fully-qualified # 🫵🏻 E14.0 index pointing at the viewer: light skin tone
1FAF5 1F3FC ; fully-qualified # 🫵🏼 E14.0 index pointing at the viewer: medium-light skin tone
1FAF5 1F3FD ; fully-qualified # 🫵🏽 E14.0 index pointing at the viewer: medium skin tone
1FAF5 1F3FE ; fully-qualified # 🫵🏾 E14.0 index pointing at the viewer: medium-dark skin tone
1FAF5 1F3FF ; fully-qualified # 🫵🏿 E14.0 index pointing at the viewer: dark skin tone
# subgroup: hand-fingers-closed # subgroup: hand-fingers-closed
1F44D ; fully-qualified # 👍 E0.6 thumbs up 1F44D ; fully-qualified # 👍 E0.6 thumbs up
@ -411,6 +454,12 @@
1F64C 1F3FD ; fully-qualified # 🙌🏽 E1.0 raising hands: medium skin tone 1F64C 1F3FD ; fully-qualified # 🙌🏽 E1.0 raising hands: medium skin tone
1F64C 1F3FE ; fully-qualified # 🙌🏾 E1.0 raising hands: medium-dark skin tone 1F64C 1F3FE ; fully-qualified # 🙌🏾 E1.0 raising hands: medium-dark skin tone
1F64C 1F3FF ; fully-qualified # 🙌🏿 E1.0 raising hands: dark skin tone 1F64C 1F3FF ; fully-qualified # 🙌🏿 E1.0 raising hands: dark skin tone
1FAF6 ; fully-qualified # 🫶 E14.0 heart hands
1FAF6 1F3FB ; fully-qualified # 🫶🏻 E14.0 heart hands: light skin tone
1FAF6 1F3FC ; fully-qualified # 🫶🏼 E14.0 heart hands: medium-light skin tone
1FAF6 1F3FD ; fully-qualified # 🫶🏽 E14.0 heart hands: medium skin tone
1FAF6 1F3FE ; fully-qualified # 🫶🏾 E14.0 heart hands: medium-dark skin tone
1FAF6 1F3FF ; fully-qualified # 🫶🏿 E14.0 heart hands: dark skin tone
1F450 ; fully-qualified # 👐 E0.6 open hands 1F450 ; fully-qualified # 👐 E0.6 open hands
1F450 1F3FB ; fully-qualified # 👐🏻 E1.0 open hands: light skin tone 1F450 1F3FB ; fully-qualified # 👐🏻 E1.0 open hands: light skin tone
1F450 1F3FC ; fully-qualified # 👐🏼 E1.0 open hands: medium-light skin tone 1F450 1F3FC ; fully-qualified # 👐🏼 E1.0 open hands: medium-light skin tone
@ -424,6 +473,31 @@
1F932 1F3FE ; fully-qualified # 🤲🏾 E5.0 palms up together: medium-dark skin tone 1F932 1F3FE ; fully-qualified # 🤲🏾 E5.0 palms up together: medium-dark skin tone
1F932 1F3FF ; fully-qualified # 🤲🏿 E5.0 palms up together: dark skin tone 1F932 1F3FF ; fully-qualified # 🤲🏿 E5.0 palms up together: dark skin tone
1F91D ; fully-qualified # 🤝 E3.0 handshake 1F91D ; fully-qualified # 🤝 E3.0 handshake
1F91D 1F3FB ; fully-qualified # 🤝🏻 E3.0 handshake: light skin tone
1F91D 1F3FC ; fully-qualified # 🤝🏼 E3.0 handshake: medium-light skin tone
1F91D 1F3FD ; fully-qualified # 🤝🏽 E3.0 handshake: medium skin tone
1F91D 1F3FE ; fully-qualified # 🤝🏾 E3.0 handshake: medium-dark skin tone
1F91D 1F3FF ; fully-qualified # 🤝🏿 E3.0 handshake: dark skin tone
1FAF1 1F3FB 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏻‍🫲🏼 E14.0 handshake: light skin tone, medium-light skin tone
1FAF1 1F3FB 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏻‍🫲🏽 E14.0 handshake: light skin tone, medium skin tone
1FAF1 1F3FB 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏻‍🫲🏾 E14.0 handshake: light skin tone, medium-dark skin tone
1FAF1 1F3FB 200D 1FAF2 1F3FF ; fully-qualified # 🫱🏻‍🫲🏿 E14.0 handshake: light skin tone, dark skin tone
1FAF1 1F3FC 200D 1FAF2 1F3FB ; fully-qualified # 🫱🏼‍🫲🏻 E14.0 handshake: medium-light skin tone, light skin tone
1FAF1 1F3FC 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏼‍🫲🏽 E14.0 handshake: medium-light skin tone, medium skin tone
1FAF1 1F3FC 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏼‍🫲🏾 E14.0 handshake: medium-light skin tone, medium-dark skin tone
1FAF1 1F3FC 200D 1FAF2 1F3FF ; fully-qualified # 🫱🏼‍🫲🏿 E14.0 handshake: medium-light skin tone, dark skin tone
1FAF1 1F3FD 200D 1FAF2 1F3FB ; fully-qualified # 🫱🏽‍🫲🏻 E14.0 handshake: medium skin tone, light skin tone
1FAF1 1F3FD 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏽‍🫲🏼 E14.0 handshake: medium skin tone, medium-light skin tone
1FAF1 1F3FD 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏽‍🫲🏾 E14.0 handshake: medium skin tone, medium-dark skin tone
1FAF1 1F3FD 200D 1FAF2 1F3FF ; fully-qualified # 🫱🏽‍🫲🏿 E14.0 handshake: medium skin tone, dark skin tone
1FAF1 1F3FE 200D 1FAF2 1F3FB ; fully-qualified # 🫱🏾‍🫲🏻 E14.0 handshake: medium-dark skin tone, light skin tone
1FAF1 1F3FE 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏾‍🫲🏼 E14.0 handshake: medium-dark skin tone, medium-light skin tone
1FAF1 1F3FE 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏾‍🫲🏽 E14.0 handshake: medium-dark skin tone, medium skin tone
1FAF1 1F3FE 200D 1FAF2 1F3FF ; fully-qualified # 🫱🏾‍🫲🏿 E14.0 handshake: medium-dark skin tone, dark skin tone
1FAF1 1F3FF 200D 1FAF2 1F3FB ; fully-qualified # 🫱🏿‍🫲🏻 E14.0 handshake: dark skin tone, light skin tone
1FAF1 1F3FF 200D 1FAF2 1F3FC ; fully-qualified # 🫱🏿‍🫲🏼 E14.0 handshake: dark skin tone, medium-light skin tone
1FAF1 1F3FF 200D 1FAF2 1F3FD ; fully-qualified # 🫱🏿‍🫲🏽 E14.0 handshake: dark skin tone, medium skin tone
1FAF1 1F3FF 200D 1FAF2 1F3FE ; fully-qualified # 🫱🏿‍🫲🏾 E14.0 handshake: dark skin tone, medium-dark skin tone
1F64F ; fully-qualified # 🙏 E0.6 folded hands 1F64F ; fully-qualified # 🙏 E0.6 folded hands
1F64F 1F3FB ; fully-qualified # 🙏🏻 E1.0 folded hands: light skin tone 1F64F 1F3FB ; fully-qualified # 🙏🏻 E1.0 folded hands: light skin tone
1F64F 1F3FC ; fully-qualified # 🙏🏼 E1.0 folded hands: medium-light skin tone 1F64F 1F3FC ; fully-qualified # 🙏🏼 E1.0 folded hands: medium-light skin tone
@ -501,6 +575,7 @@
1F441 ; unqualified # 👁 E0.7 eye 1F441 ; unqualified # 👁 E0.7 eye
1F445 ; fully-qualified # 👅 E0.6 tongue 1F445 ; fully-qualified # 👅 E0.6 tongue
1F444 ; fully-qualified # 👄 E0.6 mouth 1F444 ; fully-qualified # 👄 E0.6 mouth
1FAE6 ; fully-qualified # 🫦 E14.0 biting lip
# subgroup: person # subgroup: person
1F476 ; fully-qualified # 👶 E0.6 baby 1F476 ; fully-qualified # 👶 E0.6 baby
@ -1472,6 +1547,12 @@
1F477 1F3FE 200D 2640 ; minimally-qualified # 👷🏾‍♀ E4.0 woman construction worker: medium-dark skin tone 1F477 1F3FE 200D 2640 ; minimally-qualified # 👷🏾‍♀ E4.0 woman construction worker: medium-dark skin tone
1F477 1F3FF 200D 2640 FE0F ; fully-qualified # 👷🏿‍♀️ E4.0 woman construction worker: dark skin tone 1F477 1F3FF 200D 2640 FE0F ; fully-qualified # 👷🏿‍♀️ E4.0 woman construction worker: dark skin tone
1F477 1F3FF 200D 2640 ; minimally-qualified # 👷🏿‍♀ E4.0 woman construction worker: dark skin tone 1F477 1F3FF 200D 2640 ; minimally-qualified # 👷🏿‍♀ E4.0 woman construction worker: dark skin tone
1FAC5 ; fully-qualified # 🫅 E14.0 person with crown
1FAC5 1F3FB ; fully-qualified # 🫅🏻 E14.0 person with crown: light skin tone
1FAC5 1F3FC ; fully-qualified # 🫅🏼 E14.0 person with crown: medium-light skin tone
1FAC5 1F3FD ; fully-qualified # 🫅🏽 E14.0 person with crown: medium skin tone
1FAC5 1F3FE ; fully-qualified # 🫅🏾 E14.0 person with crown: medium-dark skin tone
1FAC5 1F3FF ; fully-qualified # 🫅🏿 E14.0 person with crown: dark skin tone
1F934 ; fully-qualified # 🤴 E3.0 prince 1F934 ; fully-qualified # 🤴 E3.0 prince
1F934 1F3FB ; fully-qualified # 🤴🏻 E3.0 prince: light skin tone 1F934 1F3FB ; fully-qualified # 🤴🏻 E3.0 prince: light skin tone
1F934 1F3FC ; fully-qualified # 🤴🏼 E3.0 prince: medium-light skin tone 1F934 1F3FC ; fully-qualified # 🤴🏼 E3.0 prince: medium-light skin tone
@ -1592,6 +1673,18 @@
1F930 1F3FD ; fully-qualified # 🤰🏽 E3.0 pregnant woman: medium skin tone 1F930 1F3FD ; fully-qualified # 🤰🏽 E3.0 pregnant woman: medium skin tone
1F930 1F3FE ; fully-qualified # 🤰🏾 E3.0 pregnant woman: medium-dark skin tone 1F930 1F3FE ; fully-qualified # 🤰🏾 E3.0 pregnant woman: medium-dark skin tone
1F930 1F3FF ; fully-qualified # 🤰🏿 E3.0 pregnant woman: dark skin tone 1F930 1F3FF ; fully-qualified # 🤰🏿 E3.0 pregnant woman: dark skin tone
1FAC3 ; fully-qualified # 🫃 E14.0 pregnant man
1FAC3 1F3FB ; fully-qualified # 🫃🏻 E14.0 pregnant man: light skin tone
1FAC3 1F3FC ; fully-qualified # 🫃🏼 E14.0 pregnant man: medium-light skin tone
1FAC3 1F3FD ; fully-qualified # 🫃🏽 E14.0 pregnant man: medium skin tone
1FAC3 1F3FE ; fully-qualified # 🫃🏾 E14.0 pregnant man: medium-dark skin tone
1FAC3 1F3FF ; fully-qualified # 🫃🏿 E14.0 pregnant man: dark skin tone
1FAC4 ; fully-qualified # 🫄 E14.0 pregnant person
1FAC4 1F3FB ; fully-qualified # 🫄🏻 E14.0 pregnant person: light skin tone
1FAC4 1F3FC ; fully-qualified # 🫄🏼 E14.0 pregnant person: medium-light skin tone
1FAC4 1F3FD ; fully-qualified # 🫄🏽 E14.0 pregnant person: medium skin tone
1FAC4 1F3FE ; fully-qualified # 🫄🏾 E14.0 pregnant person: medium-dark skin tone
1FAC4 1F3FF ; fully-qualified # 🫄🏿 E14.0 pregnant person: dark skin tone
1F931 ; fully-qualified # 🤱 E5.0 breast-feeding 1F931 ; fully-qualified # 🤱 E5.0 breast-feeding
1F931 1F3FB ; fully-qualified # 🤱🏻 E5.0 breast-feeding: light skin tone 1F931 1F3FB ; fully-qualified # 🤱🏻 E5.0 breast-feeding: light skin tone
1F931 1F3FC ; fully-qualified # 🤱🏼 E5.0 breast-feeding: medium-light skin tone 1F931 1F3FC ; fully-qualified # 🤱🏼 E5.0 breast-feeding: medium-light skin tone
@ -1862,6 +1955,7 @@
1F9DF 200D 2642 ; minimally-qualified # 🧟‍♂ E5.0 man zombie 1F9DF 200D 2642 ; minimally-qualified # 🧟‍♂ E5.0 man zombie
1F9DF 200D 2640 FE0F ; fully-qualified # 🧟‍♀️ E5.0 woman zombie 1F9DF 200D 2640 FE0F ; fully-qualified # 🧟‍♀️ E5.0 woman zombie
1F9DF 200D 2640 ; minimally-qualified # 🧟‍♀ E5.0 woman zombie 1F9DF 200D 2640 ; minimally-qualified # 🧟‍♀ E5.0 woman zombie
1F9CC ; fully-qualified # 🧌 E14.0 troll
# subgroup: person-activity # subgroup: person-activity
1F486 ; fully-qualified # 💆 E0.6 person getting massage 1F486 ; fully-qualified # 💆 E0.6 person getting massage
@ -3168,8 +3262,8 @@
1FAC2 ; fully-qualified # 🫂 E13.0 people hugging 1FAC2 ; fully-qualified # 🫂 E13.0 people hugging
1F463 ; fully-qualified # 👣 E0.6 footprints 1F463 ; fully-qualified # 👣 E0.6 footprints
# People & Body subtotal: 2899 # People & Body subtotal: 2986
# People & Body subtotal: 494 w/o modifiers # People & Body subtotal: 506 w/o modifiers
# group: Component # group: Component
@ -3304,6 +3398,7 @@
1F988 ; fully-qualified # 🦈 E3.0 shark 1F988 ; fully-qualified # 🦈 E3.0 shark
1F419 ; fully-qualified # 🐙 E0.6 octopus 1F419 ; fully-qualified # 🐙 E0.6 octopus
1F41A ; fully-qualified # 🐚 E0.6 spiral shell 1F41A ; fully-qualified # 🐚 E0.6 spiral shell
1FAB8 ; fully-qualified # 🪸 E14.0 coral
# subgroup: animal-bug # subgroup: animal-bug
1F40C ; fully-qualified # 🐌 E0.6 snail 1F40C ; fully-qualified # 🐌 E0.6 snail
@ -3329,6 +3424,7 @@
1F490 ; fully-qualified # 💐 E0.6 bouquet 1F490 ; fully-qualified # 💐 E0.6 bouquet
1F338 ; fully-qualified # 🌸 E0.6 cherry blossom 1F338 ; fully-qualified # 🌸 E0.6 cherry blossom
1F4AE ; fully-qualified # 💮 E0.6 white flower 1F4AE ; fully-qualified # 💮 E0.6 white flower
1FAB7 ; fully-qualified # 🪷 E14.0 lotus
1F3F5 FE0F ; fully-qualified # 🏵️ E0.7 rosette 1F3F5 FE0F ; fully-qualified # 🏵️ E0.7 rosette
1F3F5 ; unqualified # 🏵 E0.7 rosette 1F3F5 ; unqualified # 🏵 E0.7 rosette
1F339 ; fully-qualified # 🌹 E0.6 rose 1F339 ; fully-qualified # 🌹 E0.6 rose
@ -3353,9 +3449,11 @@
1F341 ; fully-qualified # 🍁 E0.6 maple leaf 1F341 ; fully-qualified # 🍁 E0.6 maple leaf
1F342 ; fully-qualified # 🍂 E0.6 fallen leaf 1F342 ; fully-qualified # 🍂 E0.6 fallen leaf
1F343 ; fully-qualified # 🍃 E0.6 leaf fluttering in wind 1F343 ; fully-qualified # 🍃 E0.6 leaf fluttering in wind
1FAB9 ; fully-qualified # 🪹 E14.0 empty nest
1FABA ; fully-qualified # 🪺 E14.0 nest with eggs
# Animals & Nature subtotal: 147 # Animals & Nature subtotal: 151
# Animals & Nature subtotal: 147 w/o modifiers # Animals & Nature subtotal: 151 w/o modifiers
# group: Food & Drink # group: Food & Drink
@ -3396,6 +3494,7 @@
1F9C5 ; fully-qualified # 🧅 E12.0 onion 1F9C5 ; fully-qualified # 🧅 E12.0 onion
1F344 ; fully-qualified # 🍄 E0.6 mushroom 1F344 ; fully-qualified # 🍄 E0.6 mushroom
1F95C ; fully-qualified # 🥜 E3.0 peanuts 1F95C ; fully-qualified # 🥜 E3.0 peanuts
1FAD8 ; fully-qualified # 🫘 E14.0 beans
1F330 ; fully-qualified # 🌰 E0.6 chestnut 1F330 ; fully-qualified # 🌰 E0.6 chestnut
# subgroup: food-prepared # subgroup: food-prepared
@ -3491,6 +3590,7 @@
1F37B ; fully-qualified # 🍻 E0.6 clinking beer mugs 1F37B ; fully-qualified # 🍻 E0.6 clinking beer mugs
1F942 ; fully-qualified # 🥂 E3.0 clinking glasses 1F942 ; fully-qualified # 🥂 E3.0 clinking glasses
1F943 ; fully-qualified # 🥃 E3.0 tumbler glass 1F943 ; fully-qualified # 🥃 E3.0 tumbler glass
1FAD7 ; fully-qualified # 🫗 E14.0 pouring liquid
1F964 ; fully-qualified # 🥤 E5.0 cup with straw 1F964 ; fully-qualified # 🥤 E5.0 cup with straw
1F9CB ; fully-qualified # 🧋 E13.0 bubble tea 1F9CB ; fully-qualified # 🧋 E13.0 bubble tea
1F9C3 ; fully-qualified # 🧃 E12.0 beverage box 1F9C3 ; fully-qualified # 🧃 E12.0 beverage box
@ -3504,10 +3604,11 @@
1F374 ; fully-qualified # 🍴 E0.6 fork and knife 1F374 ; fully-qualified # 🍴 E0.6 fork and knife
1F944 ; fully-qualified # 🥄 E3.0 spoon 1F944 ; fully-qualified # 🥄 E3.0 spoon
1F52A ; fully-qualified # 🔪 E0.6 kitchen knife 1F52A ; fully-qualified # 🔪 E0.6 kitchen knife
1FAD9 ; fully-qualified # 🫙 E14.0 jar
1F3FA ; fully-qualified # 🏺 E1.0 amphora 1F3FA ; fully-qualified # 🏺 E1.0 amphora
# Food & Drink subtotal: 131 # Food & Drink subtotal: 134
# Food & Drink subtotal: 131 w/o modifiers # Food & Drink subtotal: 134 w/o modifiers
# group: Travel & Places # group: Travel & Places
@ -3597,6 +3698,7 @@
2668 FE0F ; fully-qualified # ♨️ E0.6 hot springs 2668 FE0F ; fully-qualified # ♨️ E0.6 hot springs
2668 ; unqualified # ♨ E0.6 hot springs 2668 ; unqualified # ♨ E0.6 hot springs
1F3A0 ; fully-qualified # 🎠 E0.6 carousel horse 1F3A0 ; fully-qualified # 🎠 E0.6 carousel horse
1F6DD ; fully-qualified # 🛝 E14.0 playground slide
1F3A1 ; fully-qualified # 🎡 E0.6 ferris wheel 1F3A1 ; fully-qualified # 🎡 E0.6 ferris wheel
1F3A2 ; fully-qualified # 🎢 E0.6 roller coaster 1F3A2 ; fully-qualified # 🎢 E0.6 roller coaster
1F488 ; fully-qualified # 💈 E0.6 barber pole 1F488 ; fully-qualified # 💈 E0.6 barber pole
@ -3652,6 +3754,7 @@
1F6E2 FE0F ; fully-qualified # 🛢️ E0.7 oil drum 1F6E2 FE0F ; fully-qualified # 🛢️ E0.7 oil drum
1F6E2 ; unqualified # 🛢 E0.7 oil drum 1F6E2 ; unqualified # 🛢 E0.7 oil drum
26FD ; fully-qualified # ⛽ E0.6 fuel pump 26FD ; fully-qualified # ⛽ E0.6 fuel pump
1F6DE ; fully-qualified # 🛞 E14.0 wheel
1F6A8 ; fully-qualified # 🚨 E0.6 police car light 1F6A8 ; fully-qualified # 🚨 E0.6 police car light
1F6A5 ; fully-qualified # 🚥 E0.6 horizontal traffic light 1F6A5 ; fully-qualified # 🚥 E0.6 horizontal traffic light
1F6A6 ; fully-qualified # 🚦 E1.0 vertical traffic light 1F6A6 ; fully-qualified # 🚦 E1.0 vertical traffic light
@ -3660,6 +3763,7 @@
# subgroup: transport-water # subgroup: transport-water
2693 ; fully-qualified # ⚓ E0.6 anchor 2693 ; fully-qualified # ⚓ E0.6 anchor
1F6DF ; fully-qualified # 🛟 E14.0 ring buoy
26F5 ; fully-qualified # ⛵ E0.6 sailboat 26F5 ; fully-qualified # ⛵ E0.6 sailboat
1F6F6 ; fully-qualified # 🛶 E3.0 canoe 1F6F6 ; fully-qualified # 🛶 E3.0 canoe
1F6A4 ; fully-qualified # 🚤 E0.6 speedboat 1F6A4 ; fully-qualified # 🚤 E0.6 speedboat
@ -3797,8 +3901,8 @@
1F4A7 ; fully-qualified # 💧 E0.6 droplet 1F4A7 ; fully-qualified # 💧 E0.6 droplet
1F30A ; fully-qualified # 🌊 E0.6 water wave 1F30A ; fully-qualified # 🌊 E0.6 water wave
# Travel & Places subtotal: 264 # Travel & Places subtotal: 267
# Travel & Places subtotal: 264 w/o modifiers # Travel & Places subtotal: 267 w/o modifiers
# group: Activities # group: Activities
@ -3874,6 +3978,7 @@
1F52E ; fully-qualified # 🔮 E0.6 crystal ball 1F52E ; fully-qualified # 🔮 E0.6 crystal ball
1FA84 ; fully-qualified # 🪄 E13.0 magic wand 1FA84 ; fully-qualified # 🪄 E13.0 magic wand
1F9FF ; fully-qualified # 🧿 E11.0 nazar amulet 1F9FF ; fully-qualified # 🧿 E11.0 nazar amulet
1FAAC ; fully-qualified # 🪬 E14.0 hamsa
1F3AE ; fully-qualified # 🎮 E0.6 video game 1F3AE ; fully-qualified # 🎮 E0.6 video game
1F579 FE0F ; fully-qualified # 🕹️ E0.7 joystick 1F579 FE0F ; fully-qualified # 🕹️ E0.7 joystick
1F579 ; unqualified # 🕹 E0.7 joystick 1F579 ; unqualified # 🕹 E0.7 joystick
@ -3882,6 +3987,7 @@
1F9E9 ; fully-qualified # 🧩 E11.0 puzzle piece 1F9E9 ; fully-qualified # 🧩 E11.0 puzzle piece
1F9F8 ; fully-qualified # 🧸 E11.0 teddy bear 1F9F8 ; fully-qualified # 🧸 E11.0 teddy bear
1FA85 ; fully-qualified # 🪅 E13.0 piñata 1FA85 ; fully-qualified # 🪅 E13.0 piñata
1FAA9 ; fully-qualified # 🪩 E14.0 mirror ball
1FA86 ; fully-qualified # 🪆 E13.0 nesting dolls 1FA86 ; fully-qualified # 🪆 E13.0 nesting dolls
2660 FE0F ; fully-qualified # ♠️ E0.6 spade suit 2660 FE0F ; fully-qualified # ♠️ E0.6 spade suit
2660 ; unqualified # ♠ E0.6 spade suit 2660 ; unqualified # ♠ E0.6 spade suit
@ -3907,8 +4013,8 @@
1F9F6 ; fully-qualified # 🧶 E11.0 yarn 1F9F6 ; fully-qualified # 🧶 E11.0 yarn
1FAA2 ; fully-qualified # 🪢 E13.0 knot 1FAA2 ; fully-qualified # 🪢 E13.0 knot
# Activities subtotal: 95 # Activities subtotal: 97
# Activities subtotal: 95 w/o modifiers # Activities subtotal: 97 w/o modifiers
# group: Objects # group: Objects
@ -4009,6 +4115,7 @@
# subgroup: computer # subgroup: computer
1F50B ; fully-qualified # 🔋 E0.6 battery 1F50B ; fully-qualified # 🔋 E0.6 battery
1FAAB ; fully-qualified # 🪫 E14.0 low battery
1F50C ; fully-qualified # 🔌 E0.6 electric plug 1F50C ; fully-qualified # 🔌 E0.6 electric plug
1F4BB ; fully-qualified # 💻 E0.6 laptop 1F4BB ; fully-qualified # 💻 E0.6 laptop
1F5A5 FE0F ; fully-qualified # 🖥️ E0.7 desktop computer 1F5A5 FE0F ; fully-qualified # 🖥️ E0.7 desktop computer
@ -4207,7 +4314,9 @@
1FA78 ; fully-qualified # 🩸 E12.0 drop of blood 1FA78 ; fully-qualified # 🩸 E12.0 drop of blood
1F48A ; fully-qualified # 💊 E0.6 pill 1F48A ; fully-qualified # 💊 E0.6 pill
1FA79 ; fully-qualified # 🩹 E12.0 adhesive bandage 1FA79 ; fully-qualified # 🩹 E12.0 adhesive bandage
1FA7C ; fully-qualified # 🩼 E14.0 crutch
1FA7A ; fully-qualified # 🩺 E12.0 stethoscope 1FA7A ; fully-qualified # 🩺 E12.0 stethoscope
1FA7B ; fully-qualified # 🩻 E14.0 x-ray
# subgroup: household # subgroup: household
1F6AA ; fully-qualified # 🚪 E0.6 door 1F6AA ; fully-qualified # 🚪 E0.6 door
@ -4232,6 +4341,7 @@
1F9FB ; fully-qualified # 🧻 E11.0 roll of paper 1F9FB ; fully-qualified # 🧻 E11.0 roll of paper
1FAA3 ; fully-qualified # 🪣 E13.0 bucket 1FAA3 ; fully-qualified # 🪣 E13.0 bucket
1F9FC ; fully-qualified # 🧼 E11.0 soap 1F9FC ; fully-qualified # 🧼 E11.0 soap
1FAE7 ; fully-qualified # 🫧 E14.0 bubbles
1FAA5 ; fully-qualified # 🪥 E13.0 toothbrush 1FAA5 ; fully-qualified # 🪥 E13.0 toothbrush
1F9FD ; fully-qualified # 🧽 E11.0 sponge 1F9FD ; fully-qualified # 🧽 E11.0 sponge
1F9EF ; fully-qualified # 🧯 E11.0 fire extinguisher 1F9EF ; fully-qualified # 🧯 E11.0 fire extinguisher
@ -4246,9 +4356,10 @@
26B1 ; unqualified # ⚱ E1.0 funeral urn 26B1 ; unqualified # ⚱ E1.0 funeral urn
1F5FF ; fully-qualified # 🗿 E0.6 moai 1F5FF ; fully-qualified # 🗿 E0.6 moai
1FAA7 ; fully-qualified # 🪧 E13.0 placard 1FAA7 ; fully-qualified # 🪧 E13.0 placard
1FAAA ; fully-qualified # 🪪 E14.0 identification card
# Objects subtotal: 299 # Objects subtotal: 304
# Objects subtotal: 299 w/o modifiers # Objects subtotal: 304 w/o modifiers
# group: Symbols # group: Symbols
@ -4409,6 +4520,7 @@
2795 ; fully-qualified # E0.6 plus 2795 ; fully-qualified # E0.6 plus
2796 ; fully-qualified # E0.6 minus 2796 ; fully-qualified # E0.6 minus
2797 ; fully-qualified # ➗ E0.6 divide 2797 ; fully-qualified # ➗ E0.6 divide
1F7F0 ; fully-qualified # 🟰 E14.0 heavy equals sign
267E FE0F ; fully-qualified # ♾️ E11.0 infinity 267E FE0F ; fully-qualified # ♾️ E11.0 infinity
267E ; unqualified # ♾ E11.0 infinity 267E ; unqualified # ♾ E11.0 infinity
@ -4581,8 +4693,8 @@
1F533 ; fully-qualified # 🔳 E0.6 white square button 1F533 ; fully-qualified # 🔳 E0.6 white square button
1F532 ; fully-qualified # 🔲 E0.6 black square button 1F532 ; fully-qualified # 🔲 E0.6 black square button
# Symbols subtotal: 301 # Symbols subtotal: 302
# Symbols subtotal: 301 w/o modifiers # Symbols subtotal: 302 w/o modifiers
# group: Flags # group: Flags
@ -4871,7 +4983,7 @@
# Flags subtotal: 275 w/o modifiers # Flags subtotal: 275 w/o modifiers
# Status Counts # Status Counts
# fully-qualified : 3512 # fully-qualified : 3624
# minimally-qualified : 817 # minimally-qualified : 817
# unqualified : 252 # unqualified : 252
# component : 9 # component : 9

View File

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server # Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emoji do defmodule Pleroma.Emoji do
@ -9,6 +9,7 @@ defmodule Pleroma.Emoji do
""" """
use GenServer use GenServer
alias Pleroma.Emoji.Combinations
alias Pleroma.Emoji.Loader alias Pleroma.Emoji.Loader
require Logger require Logger
@ -137,4 +138,17 @@ defmodule Pleroma.Emoji do
end end
def is_unicode_emoji?(_), do: false def is_unicode_emoji?(_), do: false
emoji_qualification_map =
emojis
|> Enum.filter(&String.contains?(&1, "\uFE0F"))
|> Combinations.variate_emoji_qualification()
for {qualified, unqualified_list} <- emoji_qualification_map do
for unqualified <- unqualified_list do
def fully_qualify_emoji(unquote(unqualified)), do: unquote(qualified)
end
end
def fully_qualify_emoji(emoji), do: emoji
end end

View File

@ -0,0 +1,45 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emoji.Combinations do
# FE0F is the emoji variation sequence. It is used for fully-qualifying
# emoji, and that includes emoji combinations.
# This code generates combinations per emoji: for each FE0F, all possible
# combinations of the character being removed or staying will be generated.
# This is made as an attempt to find all partially-qualified and unqualified
# versions of a fully-qualified emoji.
# I have found *no cases* for which this would be a problem, after browsing
# the entire emoji list in emoji-test.txt. This is safe, and, sadly, most
# likely sane too.
defp qualification_combinations(codepoints) do
qualification_combinations([[]], codepoints)
end
defp qualification_combinations(acc, []), do: acc
defp qualification_combinations(acc, ["\uFE0F" | tail]) do
acc
|> Enum.flat_map(fn x -> [x, x ++ ["\uFE0F"]] end)
|> qualification_combinations(tail)
end
defp qualification_combinations(acc, [codepoint | tail]) do
acc
|> Enum.map(&Kernel.++(&1, [codepoint]))
|> qualification_combinations(tail)
end
def variate_emoji_qualification(emoji) when is_binary(emoji) do
emoji
|> String.codepoints()
|> qualification_combinations()
|> Enum.map(&List.to_string/1)
end
def variate_emoji_qualification(emoji) when is_list(emoji) do
emoji
|> Enum.map(fn emoji -> {emoji, variate_emoji_qualification(emoji)} end)
end
end

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