Merge branch 'develop' into stable
This commit is contained in:
commit
5e7be063c7
50 changed files with 1232 additions and 208 deletions
|
@ -14,6 +14,14 @@ variables:
|
||||||
- stable
|
- stable
|
||||||
- refs/tags/v*
|
- refs/tags/v*
|
||||||
- refs/tags/stable-*
|
- refs/tags/stable-*
|
||||||
|
- &on-stable
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
branch:
|
||||||
|
- stable
|
||||||
|
- refs/tags/stable-*
|
||||||
- &on-point-release
|
- &on-point-release
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
|
@ -110,6 +118,8 @@ pipeline:
|
||||||
- export SOURCE=akkoma-ubuntu-jammy.zip
|
- export SOURCE=akkoma-ubuntu-jammy.zip
|
||||||
- export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-ubuntu-jammy.zip
|
- export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-ubuntu-jammy.zip
|
||||||
- /bin/sh /entrypoint.sh
|
- /bin/sh /entrypoint.sh
|
||||||
|
- export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-amd64-ubuntu-jammy.zip
|
||||||
|
- /bin/sh /entrypoint.sh
|
||||||
|
|
||||||
debian-bullseye:
|
debian-bullseye:
|
||||||
image: hexpm/elixir:1.13.4-erlang-24.3.4.5-debian-bullseye-20220801
|
image: hexpm/elixir:1.13.4-erlang-24.3.4.5-debian-bullseye-20220801
|
||||||
|
@ -142,7 +152,7 @@ pipeline:
|
||||||
# Canonical amd64-musl
|
# Canonical amd64-musl
|
||||||
musl:
|
musl:
|
||||||
image: hexpm/elixir:1.13.4-erlang-24.3.4.5-alpine-3.15.6
|
image: hexpm/elixir:1.13.4-erlang-24.3.4.5-alpine-3.15.6
|
||||||
<<: *on-release
|
<<: *on-stable
|
||||||
environment:
|
environment:
|
||||||
MIX_ENV: prod
|
MIX_ENV: prod
|
||||||
commands:
|
commands:
|
||||||
|
@ -157,7 +167,7 @@ pipeline:
|
||||||
|
|
||||||
release-musl:
|
release-musl:
|
||||||
image: akkoma/releaser
|
image: akkoma/releaser
|
||||||
<<: *on-release
|
<<: *on-stable
|
||||||
secrets: *scw-secrets
|
secrets: *scw-secrets
|
||||||
commands:
|
commands:
|
||||||
- export SOURCE=akkoma-amd64-musl.zip
|
- export SOURCE=akkoma-amd64-musl.zip
|
||||||
|
@ -172,8 +182,12 @@ pipeline:
|
||||||
- SCW_DEFAULT_ORGANIZATION_ID
|
- SCW_DEFAULT_ORGANIZATION_ID
|
||||||
environment:
|
environment:
|
||||||
CI: "true"
|
CI: "true"
|
||||||
image: akkoma/docs-builder
|
image: python:3.10-slim
|
||||||
commands:
|
commands:
|
||||||
|
- apt-get update && apt-get install -y rclone wget git zip
|
||||||
|
- wget https://github.com/scaleway/scaleway-cli/releases/download/v2.5.1/scaleway-cli_2.5.1_linux_amd64
|
||||||
|
- mv scaleway-cli_2.5.1_linux_amd64 scaleway-cli
|
||||||
|
- chmod +x scaleway-cli
|
||||||
- ./scaleway-cli object config install type=rclone
|
- ./scaleway-cli object config install type=rclone
|
||||||
- cd docs
|
- cd docs
|
||||||
- pip install -r requirements.txt
|
- pip install -r requirements.txt
|
||||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
## 2022.10
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Ability to sync frontend profiles between clients, with a name attached
|
||||||
|
- Status card generation will now use the media summary if it is available
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Emoji updated to latest 15.0 draft
|
||||||
|
- **Breaking**: `/api/v1/pleroma/backups` endpoints now requires `read:backups` scope instead of `read:accounts`
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- OAuthPlug no longer joins with the database every call and uses the user cache
|
||||||
|
- Undo activities no longer try to look up by ID, and render correctly
|
||||||
|
- prevent false-errors from meilisearch
|
||||||
|
|
||||||
## 2022.09
|
## 2022.09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -18,6 +33,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
### Changed
|
### Changed
|
||||||
- MFM parsing is now done on the backend by a modified version of ilja's parser -> https://akkoma.dev/AkkomaGang/mfm-parser
|
- MFM parsing is now done on the backend by a modified version of ilja's parser -> https://akkoma.dev/AkkomaGang/mfm-parser
|
||||||
- InlineQuotePolicy is now on by default
|
- InlineQuotePolicy is now on by default
|
||||||
|
- Enable remote users to interact with posts
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Compatibility with latest meilisearch
|
- Compatibility with latest meilisearch
|
||||||
|
@ -49,7 +65,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- amd64 is built for debian stable. Compatible with ubuntu 20.
|
- amd64 is built for debian stable. Compatible with ubuntu 20.
|
||||||
- ubuntu-jammy is built for... well, ubuntu 22 (LTS)
|
- ubuntu-jammy is built for... well, ubuntu 22 (LTS)
|
||||||
- amd64-musl is built for alpine 3.16
|
- amd64-musl is built for alpine 3.16
|
||||||
- Enable remote users to interact with posts
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Updated mastoFE path, for the newer version
|
- Updated mastoFE path, for the newer version
|
||||||
|
|
|
@ -48,6 +48,7 @@ config :pleroma, ecto_repos: [Pleroma.Repo]
|
||||||
|
|
||||||
config :pleroma, Pleroma.Repo,
|
config :pleroma, Pleroma.Repo,
|
||||||
telemetry_event: [Pleroma.Repo.Instrumenter],
|
telemetry_event: [Pleroma.Repo.Instrumenter],
|
||||||
|
queue_target: 20_000,
|
||||||
migration_lock: nil
|
migration_lock: nil
|
||||||
|
|
||||||
config :pleroma, Pleroma.Captcha,
|
config :pleroma, Pleroma.Captcha,
|
||||||
|
@ -260,7 +261,8 @@ config :pleroma, :instance,
|
||||||
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,
|
||||||
local_bubble: []
|
local_bubble: [],
|
||||||
|
max_frontend_settings_json_chars: 100_000
|
||||||
|
|
||||||
config :pleroma, :welcome,
|
config :pleroma, :welcome,
|
||||||
direct_message: [
|
direct_message: [
|
||||||
|
@ -752,9 +754,9 @@ config :pleroma, :frontends,
|
||||||
},
|
},
|
||||||
"soapbox-fe" => %{
|
"soapbox-fe" => %{
|
||||||
"name" => "soapbox-fe",
|
"name" => "soapbox-fe",
|
||||||
"git" => "https://gitlab.com/soapbox-pub/soapbox-fe",
|
"git" => "https://gitlab.com/soapbox-pub/soapbox",
|
||||||
"build_url" =>
|
"build_url" =>
|
||||||
"https://gitlab.com/soapbox-pub/soapbox-fe/-/jobs/artifacts/${ref}/download?job=build-production",
|
"https://gitlab.com/soapbox-pub/soapbox/-/jobs/artifacts/${ref}/download?job=build-production",
|
||||||
"ref" => "v2.0.0",
|
"ref" => "v2.0.0",
|
||||||
"build_dir" => "static"
|
"build_dir" => "static"
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,6 +14,10 @@ su akkoma -s $SHELL -lc "./bin/pleroma_ctl update"
|
||||||
su akkoma -s $SHELL -lc "./bin/pleroma_ctl migrate"
|
su akkoma -s $SHELL -lc "./bin/pleroma_ctl migrate"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you selected an alternate flavour on installation,
|
||||||
|
you _may_ need to specify `--flavour`, in the same way as
|
||||||
|
[when installing](../../installation/otp_en#detecting-flavour).
|
||||||
|
|
||||||
## For from source installations (using git)
|
## For from source installations (using git)
|
||||||
|
|
||||||
1. Go to the working directory of Akkoma (default is `/opt/akkoma`)
|
1. Go to the working directory of Akkoma (default is `/opt/akkoma`)
|
||||||
|
|
|
@ -21,7 +21,7 @@ This will only save the theme for you personally. To make it available to the wh
|
||||||
|
|
||||||
### Upload the theme to the server
|
### Upload the theme to the server
|
||||||
|
|
||||||
Themes can be found in the [static directory](static_dir.md). Create `STATIC-DIR/static/themes/` if needed and copy your theme there. Next you need to add an entry for your theme to `STATIC-DIR/static/styles.json`. If you use a from source installation, you'll first need to copy the file from `priv/static/static/styles.json`.
|
Themes can be found in the [static directory](static_dir.md). Create `STATIC-DIR/static/themes/` if needed and copy your theme there. Next you need to add an entry for your theme to `STATIC-DIR/static/styles.json`. If you use a from source installation, you'll first need to copy the file from `STATIC-DIR/frontends/pleroma-fe/REF/static/styles.json` (where `REF` is `stable` or `develop` depending on which ref you decided to install).
|
||||||
|
|
||||||
Example of `styles.json` where we add our own `my-awesome-theme.json`
|
Example of `styles.json` where we add our own `my-awesome-theme.json`
|
||||||
```json
|
```json
|
||||||
|
|
|
@ -14,11 +14,12 @@ apt -yq install tor
|
||||||
|
|
||||||
**WARNING:** Onion instances not using a Tor version supporting V3 addresses will not be able to federate with you.
|
**WARNING:** Onion instances not using a Tor version supporting V3 addresses will not be able to federate with you.
|
||||||
|
|
||||||
Create the hidden service for your Akkoma instance in `/etc/tor/torrc`:
|
Create the hidden service for your Akkoma instance in `/etc/tor/torrc`, with an HTTP tunnel:
|
||||||
```
|
```
|
||||||
HiddenServiceDir /var/lib/tor/akkoma_hidden_service/
|
HiddenServiceDir /var/lib/tor/akkoma_hidden_service/
|
||||||
HiddenServicePort 80 127.0.0.1:8099
|
HiddenServicePort 80 127.0.0.1:8099
|
||||||
HiddenServiceVersion 3 # Remove if Tor version is below 0.3 ( tor --version )
|
HiddenServiceVersion 3 # Remove if Tor version is below 0.3 ( tor --version )
|
||||||
|
HTTPTunnelPort 9080
|
||||||
```
|
```
|
||||||
Restart Tor to generate an adress:
|
Restart Tor to generate an adress:
|
||||||
```
|
```
|
||||||
|
@ -35,7 +36,7 @@ Next, edit your Akkoma config.
|
||||||
If running in prod, navigate to your Akkoma directory, edit `config/prod.secret.exs`
|
If running in prod, navigate to your Akkoma directory, edit `config/prod.secret.exs`
|
||||||
and append this line:
|
and append this line:
|
||||||
```
|
```
|
||||||
config :pleroma, :http, proxy_url: {:socks5, :localhost, 9050}
|
config :pleroma, :http, proxy_url: "http://localhost:9080"
|
||||||
```
|
```
|
||||||
In your Akkoma directory, assuming you're running prod,
|
In your Akkoma directory, assuming you're running prod,
|
||||||
run the following:
|
run the following:
|
||||||
|
|
|
@ -141,8 +141,7 @@ You then need to set the URL and authentication credentials if relevant.
|
||||||
|
|
||||||
### Initial indexing
|
### Initial indexing
|
||||||
|
|
||||||
After setting up the configuration, you'll want to index all of your already existsing posts. Only public posts are indexed. You'll only
|
After setting up the configuration, you'll want to index all of your already existsing posts. You'll only have to do it one time, but it might take a while, depending on the amount of posts your instance has seen.
|
||||||
have to do it one time, but it might take a while, depending on the amount of posts your instance has seen.
|
|
||||||
|
|
||||||
The sequence of actions is as follows:
|
The sequence of actions is as follows:
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,20 @@ It actually consists of two components: a backend, named simply Akkoma, and a us
|
||||||
It's part of what we call the fediverse, a federated network of instances which speak common protocols and can communicate with each other.
|
It's part of what we call the fediverse, a federated network of instances which speak common protocols and can communicate with each other.
|
||||||
One account on an instance is enough to talk to the entire fediverse!
|
One account on an instance is enough to talk to the entire fediverse!
|
||||||
|
|
||||||
|
## Community Channels
|
||||||
|
|
||||||
|
### IRC
|
||||||
|
|
||||||
|
For support or general questions, pop over to #akkoma and #akkoma-dev at [irc.akkoma.dev](https://irc.akkoma.dev) (port 6697, SSL)
|
||||||
|
|
||||||
|
### Discourse
|
||||||
|
|
||||||
|
For more general meta-discussion, for example discussion of potential future features, head on over to [meta.akkoma.dev](https://meta.akkoma.dev)
|
||||||
|
|
||||||
|
### Dev diaries and release notifications
|
||||||
|
|
||||||
|
will be posted via [@akkoma@ihba](https://ihatebeinga.live/users/akkoma)
|
||||||
|
|
||||||
## How can I use it?
|
## How can I use it?
|
||||||
|
|
||||||
Akkoma instances are already widely deployed, a list can be found at <https://the-federation.info/pleroma> and <https://fediverse.network/pleroma>.
|
Akkoma instances are already widely deployed, a list can be found at <https://the-federation.info/pleroma> and <https://fediverse.network/pleroma>.
|
||||||
|
@ -26,3 +40,4 @@ Just add a "/web" after your instance url (e.g. <https://pleroma.soykaf.com/web>
|
||||||
The Mastodon interface is from the Glitch-soc fork. For more information on the Mastodon interface you can check the [Mastodon](https://docs.joinmastodon.org/) and [Glitch-soc](https://glitch-soc.github.io/docs/) documentation.
|
The Mastodon interface is from the Glitch-soc fork. For more information on the Mastodon interface you can check the [Mastodon](https://docs.joinmastodon.org/) and [Glitch-soc](https://glitch-soc.github.io/docs/) documentation.
|
||||||
|
|
||||||
Remember, what you see is only the frontend part of Mastodon, the backend is still Akkoma.
|
Remember, what you see is only the frontend part of Mastodon, the backend is still Akkoma.
|
||||||
|
|
||||||
|
|
|
@ -19,12 +19,12 @@ This is a little more complex than it used to be (thanks ubuntu)
|
||||||
|
|
||||||
Use the following mapping to figure out your flavour:
|
Use the following mapping to figure out your flavour:
|
||||||
|
|
||||||
| distribution | flavour |
|
| distribution | flavour | available branches |
|
||||||
| ------------- | ------------ |
|
| ------------- | ------------------ | ------------------- |
|
||||||
| debian stable | amd64 |
|
| debian stable | amd64 | develop, stable |
|
||||||
| ubuntu focal | amd64 |
|
| ubuntu focal | amd64 | develop, stable |
|
||||||
| ubuntu jammy | ubuntu-jammy |
|
| ubuntu jammy | amd64-ubuntu-jammy | develop, stable |
|
||||||
| alpine | amd64-musl |
|
| alpine | amd64-musl | stable |
|
||||||
|
|
||||||
Other similar distributions will _probably_ work, but if it is not listed above, there is no official
|
Other similar distributions will _probably_ work, but if it is not listed above, there is no official
|
||||||
support.
|
support.
|
||||||
|
|
100
lib/pleroma/akkoma/frontend_setting_profile.ex
Normal file
100
lib/pleroma/akkoma/frontend_setting_profile.ex
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
defmodule Pleroma.Akkoma.FrontendSettingsProfile do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@primary_key false
|
||||||
|
schema "user_frontend_setting_profiles" do
|
||||||
|
belongs_to(:user, Pleroma.User, primary_key: true, type: FlakeId.Ecto.CompatType)
|
||||||
|
field(:frontend_name, :string, primary_key: true)
|
||||||
|
field(:profile_name, :string, primary_key: true)
|
||||||
|
field(:settings, :map)
|
||||||
|
field(:version, :integer)
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
def changeset(%__MODULE__{} = struct, attrs) do
|
||||||
|
struct
|
||||||
|
|> cast(attrs, [:user_id, :frontend_name, :profile_name, :settings, :version])
|
||||||
|
|> validate_required([:user_id, :frontend_name, :profile_name, :settings, :version])
|
||||||
|
|> validate_length(:frontend_name, min: 1, max: 255)
|
||||||
|
|> validate_length(:profile_name, min: 1, max: 255)
|
||||||
|
|> validate_version(struct)
|
||||||
|
|> validate_number(:version, greater_than: 0)
|
||||||
|
|> validate_settings_length(Config.get([:instance, :max_frontend_settings_json_chars]))
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_or_update(%User{} = user, frontend_name, profile_name, settings, version) do
|
||||||
|
struct =
|
||||||
|
case get_by_user_and_frontend_name_and_profile_name(user, frontend_name, profile_name) do
|
||||||
|
nil ->
|
||||||
|
%__MODULE__{}
|
||||||
|
|
||||||
|
%__MODULE__{} = profile ->
|
||||||
|
profile
|
||||||
|
end
|
||||||
|
|
||||||
|
struct
|
||||||
|
|> changeset(%{
|
||||||
|
user_id: user.id,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: settings,
|
||||||
|
version: version
|
||||||
|
})
|
||||||
|
|> Repo.insert_or_update()
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_all_by_user_and_frontend_name(%User{id: user_id}, frontend_name) do
|
||||||
|
Repo.all(
|
||||||
|
from(p in __MODULE__, where: p.user_id == ^user_id and p.frontend_name == ^frontend_name)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_by_user_and_frontend_name_and_profile_name(
|
||||||
|
%User{id: user_id},
|
||||||
|
frontend_name,
|
||||||
|
profile_name
|
||||||
|
) do
|
||||||
|
Repo.one(
|
||||||
|
from(p in __MODULE__,
|
||||||
|
where:
|
||||||
|
p.user_id == ^user_id and p.frontend_name == ^frontend_name and
|
||||||
|
p.profile_name == ^profile_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_profile(profile) do
|
||||||
|
Repo.delete(profile)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_settings_length(
|
||||||
|
%Ecto.Changeset{changes: %{settings: settings}} = changeset,
|
||||||
|
max_length
|
||||||
|
) do
|
||||||
|
settings_json = Jason.encode!(settings)
|
||||||
|
|
||||||
|
if String.length(settings_json) > max_length do
|
||||||
|
add_error(changeset, :settings, "is too long")
|
||||||
|
else
|
||||||
|
changeset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_version(changeset, %{version: nil}), do: changeset
|
||||||
|
|
||||||
|
defp validate_version(%Ecto.Changeset{changes: %{version: version}} = changeset, %{
|
||||||
|
version: prev_version
|
||||||
|
}) do
|
||||||
|
if version != prev_version + 1 do
|
||||||
|
add_error(changeset, :version, "must be incremented by 1")
|
||||||
|
else
|
||||||
|
changeset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,13 +1,13 @@
|
||||||
# emoji-test.txt
|
# emoji-test.txt
|
||||||
# Date: 2021-08-26, 17:22:23 GMT
|
# Date: 2022-08-12, 20:24:39 GMT
|
||||||
# © 2021 Unicode®, Inc.
|
# © 2022 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 https://www.unicode.org/terms_of_use.html
|
||||||
#
|
#
|
||||||
# Emoji Keyboard/Display Test Data for UTS #51
|
# Emoji Keyboard/Display Test Data for UTS #51
|
||||||
# Version: 14.0
|
# Version: 15.0
|
||||||
#
|
#
|
||||||
# For documentation and usage, see http://www.unicode.org/reports/tr51
|
# For documentation and usage, see https://www.unicode.org/reports/tr51
|
||||||
#
|
#
|
||||||
# This file provides data for testing which emoji forms should be in keyboards and which should also be displayed/processed.
|
# This file provides data for testing which emoji forms should be in keyboards and which should also be displayed/processed.
|
||||||
# Format: code points; status # emoji name
|
# Format: code points; status # emoji name
|
||||||
|
@ -92,6 +92,7 @@
|
||||||
1F62C ; fully-qualified # 😬 E1.0 grimacing face
|
1F62C ; fully-qualified # 😬 E1.0 grimacing face
|
||||||
1F62E 200D 1F4A8 ; fully-qualified # 😮💨 E13.1 face exhaling
|
1F62E 200D 1F4A8 ; fully-qualified # 😮💨 E13.1 face exhaling
|
||||||
1F925 ; fully-qualified # 🤥 E3.0 lying face
|
1F925 ; fully-qualified # 🤥 E3.0 lying face
|
||||||
|
1FAE8 ; fully-qualified # 🫨 E15.0 shaking face
|
||||||
|
|
||||||
# subgroup: face-sleepy
|
# subgroup: face-sleepy
|
||||||
1F60C ; fully-qualified # 😌 E0.6 relieved face
|
1F60C ; fully-qualified # 😌 E0.6 relieved face
|
||||||
|
@ -155,7 +156,7 @@
|
||||||
|
|
||||||
# subgroup: face-negative
|
# subgroup: face-negative
|
||||||
1F624 ; fully-qualified # 😤 E0.6 face with steam from nose
|
1F624 ; fully-qualified # 😤 E0.6 face with steam from nose
|
||||||
1F621 ; fully-qualified # 😡 E0.6 pouting face
|
1F621 ; fully-qualified # 😡 E0.6 enraged face
|
||||||
1F620 ; fully-qualified # 😠 E0.6 angry face
|
1F620 ; fully-qualified # 😠 E0.6 angry face
|
||||||
1F92C ; fully-qualified # 🤬 E5.0 face with symbols on mouth
|
1F92C ; fully-qualified # 🤬 E5.0 face with symbols on mouth
|
||||||
1F608 ; fully-qualified # 😈 E1.0 smiling face with horns
|
1F608 ; fully-qualified # 😈 E1.0 smiling face with horns
|
||||||
|
@ -190,8 +191,7 @@
|
||||||
1F649 ; fully-qualified # 🙉 E0.6 hear-no-evil monkey
|
1F649 ; fully-qualified # 🙉 E0.6 hear-no-evil monkey
|
||||||
1F64A ; fully-qualified # 🙊 E0.6 speak-no-evil monkey
|
1F64A ; fully-qualified # 🙊 E0.6 speak-no-evil monkey
|
||||||
|
|
||||||
# subgroup: emotion
|
# subgroup: heart
|
||||||
1F48B ; fully-qualified # 💋 E0.6 kiss mark
|
|
||||||
1F48C ; fully-qualified # 💌 E0.6 love letter
|
1F48C ; fully-qualified # 💌 E0.6 love letter
|
||||||
1F498 ; fully-qualified # 💘 E0.6 heart with arrow
|
1F498 ; fully-qualified # 💘 E0.6 heart with arrow
|
||||||
1F49D ; fully-qualified # 💝 E0.6 heart with ribbon
|
1F49D ; fully-qualified # 💝 E0.6 heart with ribbon
|
||||||
|
@ -210,14 +210,20 @@
|
||||||
2764 200D 1FA79 ; unqualified # ❤🩹 E13.1 mending heart
|
2764 200D 1FA79 ; unqualified # ❤🩹 E13.1 mending heart
|
||||||
2764 FE0F ; fully-qualified # ❤️ E0.6 red heart
|
2764 FE0F ; fully-qualified # ❤️ E0.6 red heart
|
||||||
2764 ; unqualified # ❤ E0.6 red heart
|
2764 ; unqualified # ❤ E0.6 red heart
|
||||||
|
1FA77 ; fully-qualified # 🩷 E15.0 pink heart
|
||||||
1F9E1 ; fully-qualified # 🧡 E5.0 orange heart
|
1F9E1 ; fully-qualified # 🧡 E5.0 orange heart
|
||||||
1F49B ; fully-qualified # 💛 E0.6 yellow heart
|
1F49B ; fully-qualified # 💛 E0.6 yellow heart
|
||||||
1F49A ; fully-qualified # 💚 E0.6 green heart
|
1F49A ; fully-qualified # 💚 E0.6 green heart
|
||||||
1F499 ; fully-qualified # 💙 E0.6 blue heart
|
1F499 ; fully-qualified # 💙 E0.6 blue heart
|
||||||
|
1FA75 ; fully-qualified # 🩵 E15.0 light blue heart
|
||||||
1F49C ; fully-qualified # 💜 E0.6 purple heart
|
1F49C ; fully-qualified # 💜 E0.6 purple heart
|
||||||
1F90E ; fully-qualified # 🤎 E12.0 brown heart
|
1F90E ; fully-qualified # 🤎 E12.0 brown heart
|
||||||
1F5A4 ; fully-qualified # 🖤 E3.0 black heart
|
1F5A4 ; fully-qualified # 🖤 E3.0 black heart
|
||||||
|
1FA76 ; fully-qualified # 🩶 E15.0 grey heart
|
||||||
1F90D ; fully-qualified # 🤍 E12.0 white heart
|
1F90D ; fully-qualified # 🤍 E12.0 white heart
|
||||||
|
|
||||||
|
# subgroup: emotion
|
||||||
|
1F48B ; fully-qualified # 💋 E0.6 kiss mark
|
||||||
1F4AF ; fully-qualified # 💯 E0.6 hundred points
|
1F4AF ; fully-qualified # 💯 E0.6 hundred points
|
||||||
1F4A2 ; fully-qualified # 💢 E0.6 anger symbol
|
1F4A2 ; fully-qualified # 💢 E0.6 anger symbol
|
||||||
1F4A5 ; fully-qualified # 💥 E0.6 collision
|
1F4A5 ; fully-qualified # 💥 E0.6 collision
|
||||||
|
@ -226,21 +232,20 @@
|
||||||
1F4A8 ; fully-qualified # 💨 E0.6 dashing away
|
1F4A8 ; fully-qualified # 💨 E0.6 dashing away
|
||||||
1F573 FE0F ; fully-qualified # 🕳️ E0.7 hole
|
1F573 FE0F ; fully-qualified # 🕳️ E0.7 hole
|
||||||
1F573 ; unqualified # 🕳 E0.7 hole
|
1F573 ; unqualified # 🕳 E0.7 hole
|
||||||
1F4A3 ; fully-qualified # 💣 E0.6 bomb
|
|
||||||
1F4AC ; fully-qualified # 💬 E0.6 speech balloon
|
1F4AC ; fully-qualified # 💬 E0.6 speech balloon
|
||||||
1F441 FE0F 200D 1F5E8 FE0F ; fully-qualified # 👁️🗨️ E2.0 eye in speech bubble
|
1F441 FE0F 200D 1F5E8 FE0F ; fully-qualified # 👁️🗨️ E2.0 eye in speech bubble
|
||||||
1F441 200D 1F5E8 FE0F ; unqualified # 👁🗨️ E2.0 eye in speech bubble
|
1F441 200D 1F5E8 FE0F ; unqualified # 👁🗨️ E2.0 eye in speech bubble
|
||||||
1F441 FE0F 200D 1F5E8 ; unqualified # 👁️🗨 E2.0 eye in speech bubble
|
1F441 FE0F 200D 1F5E8 ; minimally-qualified # 👁️🗨 E2.0 eye in speech bubble
|
||||||
1F441 200D 1F5E8 ; unqualified # 👁🗨 E2.0 eye in speech bubble
|
1F441 200D 1F5E8 ; unqualified # 👁🗨 E2.0 eye in speech bubble
|
||||||
1F5E8 FE0F ; fully-qualified # 🗨️ E2.0 left speech bubble
|
1F5E8 FE0F ; fully-qualified # 🗨️ E2.0 left speech bubble
|
||||||
1F5E8 ; unqualified # 🗨 E2.0 left speech bubble
|
1F5E8 ; unqualified # 🗨 E2.0 left speech bubble
|
||||||
1F5EF FE0F ; fully-qualified # 🗯️ E0.7 right anger bubble
|
1F5EF FE0F ; fully-qualified # 🗯️ E0.7 right anger bubble
|
||||||
1F5EF ; unqualified # 🗯 E0.7 right anger bubble
|
1F5EF ; unqualified # 🗯 E0.7 right anger bubble
|
||||||
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: 177
|
# Smileys & Emotion subtotal: 180
|
||||||
# Smileys & Emotion subtotal: 177 w/o modifiers
|
# Smileys & Emotion subtotal: 180 w/o modifiers
|
||||||
|
|
||||||
# group: People & Body
|
# group: People & Body
|
||||||
|
|
||||||
|
@ -300,6 +305,18 @@
|
||||||
1FAF4 1F3FD ; fully-qualified # 🫴🏽 E14.0 palm up hand: medium 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 1F3FE ; fully-qualified # 🫴🏾 E14.0 palm up hand: medium-dark skin tone
|
||||||
1FAF4 1F3FF ; fully-qualified # 🫴🏿 E14.0 palm up hand: dark skin tone
|
1FAF4 1F3FF ; fully-qualified # 🫴🏿 E14.0 palm up hand: dark skin tone
|
||||||
|
1FAF7 ; fully-qualified # 🫷 E15.0 leftwards pushing hand
|
||||||
|
1FAF7 1F3FB ; fully-qualified # 🫷🏻 E15.0 leftwards pushing hand: light skin tone
|
||||||
|
1FAF7 1F3FC ; fully-qualified # 🫷🏼 E15.0 leftwards pushing hand: medium-light skin tone
|
||||||
|
1FAF7 1F3FD ; fully-qualified # 🫷🏽 E15.0 leftwards pushing hand: medium skin tone
|
||||||
|
1FAF7 1F3FE ; fully-qualified # 🫷🏾 E15.0 leftwards pushing hand: medium-dark skin tone
|
||||||
|
1FAF7 1F3FF ; fully-qualified # 🫷🏿 E15.0 leftwards pushing hand: dark skin tone
|
||||||
|
1FAF8 ; fully-qualified # 🫸 E15.0 rightwards pushing hand
|
||||||
|
1FAF8 1F3FB ; fully-qualified # 🫸🏻 E15.0 rightwards pushing hand: light skin tone
|
||||||
|
1FAF8 1F3FC ; fully-qualified # 🫸🏼 E15.0 rightwards pushing hand: medium-light skin tone
|
||||||
|
1FAF8 1F3FD ; fully-qualified # 🫸🏽 E15.0 rightwards pushing hand: medium skin tone
|
||||||
|
1FAF8 1F3FE ; fully-qualified # 🫸🏾 E15.0 rightwards pushing hand: medium-dark skin tone
|
||||||
|
1FAF8 1F3FF ; fully-qualified # 🫸🏿 E15.0 rightwards pushing 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
|
||||||
|
@ -473,11 +490,11 @@
|
||||||
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 1F3FB ; fully-qualified # 🤝🏻 E14.0 handshake: light skin tone
|
||||||
1F91D 1F3FC ; fully-qualified # 🤝🏼 E3.0 handshake: medium-light skin tone
|
1F91D 1F3FC ; fully-qualified # 🤝🏼 E14.0 handshake: medium-light skin tone
|
||||||
1F91D 1F3FD ; fully-qualified # 🤝🏽 E3.0 handshake: medium skin tone
|
1F91D 1F3FD ; fully-qualified # 🤝🏽 E14.0 handshake: medium skin tone
|
||||||
1F91D 1F3FE ; fully-qualified # 🤝🏾 E3.0 handshake: medium-dark skin tone
|
1F91D 1F3FE ; fully-qualified # 🤝🏾 E14.0 handshake: medium-dark skin tone
|
||||||
1F91D 1F3FF ; fully-qualified # 🤝🏿 E3.0 handshake: dark skin tone
|
1F91D 1F3FF ; fully-qualified # 🤝🏿 E14.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 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 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 1F3FE ; fully-qualified # 🫱🏻🫲🏾 E14.0 handshake: light skin tone, medium-dark skin tone
|
||||||
|
@ -1455,7 +1472,7 @@
|
||||||
1F575 1F3FF ; fully-qualified # 🕵🏿 E2.0 detective: dark skin tone
|
1F575 1F3FF ; fully-qualified # 🕵🏿 E2.0 detective: dark skin tone
|
||||||
1F575 FE0F 200D 2642 FE0F ; fully-qualified # 🕵️♂️ E4.0 man detective
|
1F575 FE0F 200D 2642 FE0F ; fully-qualified # 🕵️♂️ E4.0 man detective
|
||||||
1F575 200D 2642 FE0F ; unqualified # 🕵♂️ E4.0 man detective
|
1F575 200D 2642 FE0F ; unqualified # 🕵♂️ E4.0 man detective
|
||||||
1F575 FE0F 200D 2642 ; unqualified # 🕵️♂ E4.0 man detective
|
1F575 FE0F 200D 2642 ; minimally-qualified # 🕵️♂ E4.0 man detective
|
||||||
1F575 200D 2642 ; unqualified # 🕵♂ E4.0 man detective
|
1F575 200D 2642 ; unqualified # 🕵♂ E4.0 man detective
|
||||||
1F575 1F3FB 200D 2642 FE0F ; fully-qualified # 🕵🏻♂️ E4.0 man detective: light skin tone
|
1F575 1F3FB 200D 2642 FE0F ; fully-qualified # 🕵🏻♂️ E4.0 man detective: light skin tone
|
||||||
1F575 1F3FB 200D 2642 ; minimally-qualified # 🕵🏻♂ E4.0 man detective: light skin tone
|
1F575 1F3FB 200D 2642 ; minimally-qualified # 🕵🏻♂ E4.0 man detective: light skin tone
|
||||||
|
@ -1469,7 +1486,7 @@
|
||||||
1F575 1F3FF 200D 2642 ; minimally-qualified # 🕵🏿♂ E4.0 man detective: dark skin tone
|
1F575 1F3FF 200D 2642 ; minimally-qualified # 🕵🏿♂ E4.0 man detective: dark skin tone
|
||||||
1F575 FE0F 200D 2640 FE0F ; fully-qualified # 🕵️♀️ E4.0 woman detective
|
1F575 FE0F 200D 2640 FE0F ; fully-qualified # 🕵️♀️ E4.0 woman detective
|
||||||
1F575 200D 2640 FE0F ; unqualified # 🕵♀️ E4.0 woman detective
|
1F575 200D 2640 FE0F ; unqualified # 🕵♀️ E4.0 woman detective
|
||||||
1F575 FE0F 200D 2640 ; unqualified # 🕵️♀ E4.0 woman detective
|
1F575 FE0F 200D 2640 ; minimally-qualified # 🕵️♀ E4.0 woman detective
|
||||||
1F575 200D 2640 ; unqualified # 🕵♀ E4.0 woman detective
|
1F575 200D 2640 ; unqualified # 🕵♀ E4.0 woman detective
|
||||||
1F575 1F3FB 200D 2640 FE0F ; fully-qualified # 🕵🏻♀️ E4.0 woman detective: light skin tone
|
1F575 1F3FB 200D 2640 FE0F ; fully-qualified # 🕵🏻♀️ E4.0 woman detective: light skin tone
|
||||||
1F575 1F3FB 200D 2640 ; minimally-qualified # 🕵🏻♀ E4.0 woman detective: light skin tone
|
1F575 1F3FB 200D 2640 ; minimally-qualified # 🕵🏻♀ E4.0 woman detective: light skin tone
|
||||||
|
@ -2302,7 +2319,7 @@
|
||||||
1F3CC 1F3FF ; fully-qualified # 🏌🏿 E4.0 person golfing: dark skin tone
|
1F3CC 1F3FF ; fully-qualified # 🏌🏿 E4.0 person golfing: dark skin tone
|
||||||
1F3CC FE0F 200D 2642 FE0F ; fully-qualified # 🏌️♂️ E4.0 man golfing
|
1F3CC FE0F 200D 2642 FE0F ; fully-qualified # 🏌️♂️ E4.0 man golfing
|
||||||
1F3CC 200D 2642 FE0F ; unqualified # 🏌♂️ E4.0 man golfing
|
1F3CC 200D 2642 FE0F ; unqualified # 🏌♂️ E4.0 man golfing
|
||||||
1F3CC FE0F 200D 2642 ; unqualified # 🏌️♂ E4.0 man golfing
|
1F3CC FE0F 200D 2642 ; minimally-qualified # 🏌️♂ E4.0 man golfing
|
||||||
1F3CC 200D 2642 ; unqualified # 🏌♂ E4.0 man golfing
|
1F3CC 200D 2642 ; unqualified # 🏌♂ E4.0 man golfing
|
||||||
1F3CC 1F3FB 200D 2642 FE0F ; fully-qualified # 🏌🏻♂️ E4.0 man golfing: light skin tone
|
1F3CC 1F3FB 200D 2642 FE0F ; fully-qualified # 🏌🏻♂️ E4.0 man golfing: light skin tone
|
||||||
1F3CC 1F3FB 200D 2642 ; minimally-qualified # 🏌🏻♂ E4.0 man golfing: light skin tone
|
1F3CC 1F3FB 200D 2642 ; minimally-qualified # 🏌🏻♂ E4.0 man golfing: light skin tone
|
||||||
|
@ -2316,7 +2333,7 @@
|
||||||
1F3CC 1F3FF 200D 2642 ; minimally-qualified # 🏌🏿♂ E4.0 man golfing: dark skin tone
|
1F3CC 1F3FF 200D 2642 ; minimally-qualified # 🏌🏿♂ E4.0 man golfing: dark skin tone
|
||||||
1F3CC FE0F 200D 2640 FE0F ; fully-qualified # 🏌️♀️ E4.0 woman golfing
|
1F3CC FE0F 200D 2640 FE0F ; fully-qualified # 🏌️♀️ E4.0 woman golfing
|
||||||
1F3CC 200D 2640 FE0F ; unqualified # 🏌♀️ E4.0 woman golfing
|
1F3CC 200D 2640 FE0F ; unqualified # 🏌♀️ E4.0 woman golfing
|
||||||
1F3CC FE0F 200D 2640 ; unqualified # 🏌️♀ E4.0 woman golfing
|
1F3CC FE0F 200D 2640 ; minimally-qualified # 🏌️♀ E4.0 woman golfing
|
||||||
1F3CC 200D 2640 ; unqualified # 🏌♀ E4.0 woman golfing
|
1F3CC 200D 2640 ; unqualified # 🏌♀ E4.0 woman golfing
|
||||||
1F3CC 1F3FB 200D 2640 FE0F ; fully-qualified # 🏌🏻♀️ E4.0 woman golfing: light skin tone
|
1F3CC 1F3FB 200D 2640 FE0F ; fully-qualified # 🏌🏻♀️ E4.0 woman golfing: light skin tone
|
||||||
1F3CC 1F3FB 200D 2640 ; minimally-qualified # 🏌🏻♀ E4.0 woman golfing: light skin tone
|
1F3CC 1F3FB 200D 2640 ; minimally-qualified # 🏌🏻♀ E4.0 woman golfing: light skin tone
|
||||||
|
@ -2427,7 +2444,7 @@
|
||||||
26F9 1F3FF ; fully-qualified # ⛹🏿 E2.0 person bouncing ball: dark skin tone
|
26F9 1F3FF ; fully-qualified # ⛹🏿 E2.0 person bouncing ball: dark skin tone
|
||||||
26F9 FE0F 200D 2642 FE0F ; fully-qualified # ⛹️♂️ E4.0 man bouncing ball
|
26F9 FE0F 200D 2642 FE0F ; fully-qualified # ⛹️♂️ E4.0 man bouncing ball
|
||||||
26F9 200D 2642 FE0F ; unqualified # ⛹♂️ E4.0 man bouncing ball
|
26F9 200D 2642 FE0F ; unqualified # ⛹♂️ E4.0 man bouncing ball
|
||||||
26F9 FE0F 200D 2642 ; unqualified # ⛹️♂ E4.0 man bouncing ball
|
26F9 FE0F 200D 2642 ; minimally-qualified # ⛹️♂ E4.0 man bouncing ball
|
||||||
26F9 200D 2642 ; unqualified # ⛹♂ E4.0 man bouncing ball
|
26F9 200D 2642 ; unqualified # ⛹♂ E4.0 man bouncing ball
|
||||||
26F9 1F3FB 200D 2642 FE0F ; fully-qualified # ⛹🏻♂️ E4.0 man bouncing ball: light skin tone
|
26F9 1F3FB 200D 2642 FE0F ; fully-qualified # ⛹🏻♂️ E4.0 man bouncing ball: light skin tone
|
||||||
26F9 1F3FB 200D 2642 ; minimally-qualified # ⛹🏻♂ E4.0 man bouncing ball: light skin tone
|
26F9 1F3FB 200D 2642 ; minimally-qualified # ⛹🏻♂ E4.0 man bouncing ball: light skin tone
|
||||||
|
@ -2441,7 +2458,7 @@
|
||||||
26F9 1F3FF 200D 2642 ; minimally-qualified # ⛹🏿♂ E4.0 man bouncing ball: dark skin tone
|
26F9 1F3FF 200D 2642 ; minimally-qualified # ⛹🏿♂ E4.0 man bouncing ball: dark skin tone
|
||||||
26F9 FE0F 200D 2640 FE0F ; fully-qualified # ⛹️♀️ E4.0 woman bouncing ball
|
26F9 FE0F 200D 2640 FE0F ; fully-qualified # ⛹️♀️ E4.0 woman bouncing ball
|
||||||
26F9 200D 2640 FE0F ; unqualified # ⛹♀️ E4.0 woman bouncing ball
|
26F9 200D 2640 FE0F ; unqualified # ⛹♀️ E4.0 woman bouncing ball
|
||||||
26F9 FE0F 200D 2640 ; unqualified # ⛹️♀ E4.0 woman bouncing ball
|
26F9 FE0F 200D 2640 ; minimally-qualified # ⛹️♀ E4.0 woman bouncing ball
|
||||||
26F9 200D 2640 ; unqualified # ⛹♀ E4.0 woman bouncing ball
|
26F9 200D 2640 ; unqualified # ⛹♀ E4.0 woman bouncing ball
|
||||||
26F9 1F3FB 200D 2640 FE0F ; fully-qualified # ⛹🏻♀️ E4.0 woman bouncing ball: light skin tone
|
26F9 1F3FB 200D 2640 FE0F ; fully-qualified # ⛹🏻♀️ E4.0 woman bouncing ball: light skin tone
|
||||||
26F9 1F3FB 200D 2640 ; minimally-qualified # ⛹🏻♀ E4.0 woman bouncing ball: light skin tone
|
26F9 1F3FB 200D 2640 ; minimally-qualified # ⛹🏻♀ E4.0 woman bouncing ball: light skin tone
|
||||||
|
@ -2462,7 +2479,7 @@
|
||||||
1F3CB 1F3FF ; fully-qualified # 🏋🏿 E2.0 person lifting weights: dark skin tone
|
1F3CB 1F3FF ; fully-qualified # 🏋🏿 E2.0 person lifting weights: dark skin tone
|
||||||
1F3CB FE0F 200D 2642 FE0F ; fully-qualified # 🏋️♂️ E4.0 man lifting weights
|
1F3CB FE0F 200D 2642 FE0F ; fully-qualified # 🏋️♂️ E4.0 man lifting weights
|
||||||
1F3CB 200D 2642 FE0F ; unqualified # 🏋♂️ E4.0 man lifting weights
|
1F3CB 200D 2642 FE0F ; unqualified # 🏋♂️ E4.0 man lifting weights
|
||||||
1F3CB FE0F 200D 2642 ; unqualified # 🏋️♂ E4.0 man lifting weights
|
1F3CB FE0F 200D 2642 ; minimally-qualified # 🏋️♂ E4.0 man lifting weights
|
||||||
1F3CB 200D 2642 ; unqualified # 🏋♂ E4.0 man lifting weights
|
1F3CB 200D 2642 ; unqualified # 🏋♂ E4.0 man lifting weights
|
||||||
1F3CB 1F3FB 200D 2642 FE0F ; fully-qualified # 🏋🏻♂️ E4.0 man lifting weights: light skin tone
|
1F3CB 1F3FB 200D 2642 FE0F ; fully-qualified # 🏋🏻♂️ E4.0 man lifting weights: light skin tone
|
||||||
1F3CB 1F3FB 200D 2642 ; minimally-qualified # 🏋🏻♂ E4.0 man lifting weights: light skin tone
|
1F3CB 1F3FB 200D 2642 ; minimally-qualified # 🏋🏻♂ E4.0 man lifting weights: light skin tone
|
||||||
|
@ -2476,7 +2493,7 @@
|
||||||
1F3CB 1F3FF 200D 2642 ; minimally-qualified # 🏋🏿♂ E4.0 man lifting weights: dark skin tone
|
1F3CB 1F3FF 200D 2642 ; minimally-qualified # 🏋🏿♂ E4.0 man lifting weights: dark skin tone
|
||||||
1F3CB FE0F 200D 2640 FE0F ; fully-qualified # 🏋️♀️ E4.0 woman lifting weights
|
1F3CB FE0F 200D 2640 FE0F ; fully-qualified # 🏋️♀️ E4.0 woman lifting weights
|
||||||
1F3CB 200D 2640 FE0F ; unqualified # 🏋♀️ E4.0 woman lifting weights
|
1F3CB 200D 2640 FE0F ; unqualified # 🏋♀️ E4.0 woman lifting weights
|
||||||
1F3CB FE0F 200D 2640 ; unqualified # 🏋️♀ E4.0 woman lifting weights
|
1F3CB FE0F 200D 2640 ; minimally-qualified # 🏋️♀ E4.0 woman lifting weights
|
||||||
1F3CB 200D 2640 ; unqualified # 🏋♀ E4.0 woman lifting weights
|
1F3CB 200D 2640 ; unqualified # 🏋♀ E4.0 woman lifting weights
|
||||||
1F3CB 1F3FB 200D 2640 FE0F ; fully-qualified # 🏋🏻♀️ E4.0 woman lifting weights: light skin tone
|
1F3CB 1F3FB 200D 2640 FE0F ; fully-qualified # 🏋🏻♀️ E4.0 woman lifting weights: light skin tone
|
||||||
1F3CB 1F3FB 200D 2640 ; minimally-qualified # 🏋🏻♀ E4.0 woman lifting weights: light skin tone
|
1F3CB 1F3FB 200D 2640 ; minimally-qualified # 🏋🏻♀ E4.0 woman lifting weights: light skin tone
|
||||||
|
@ -3262,8 +3279,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: 2986
|
# People & Body subtotal: 2998
|
||||||
# People & Body subtotal: 506 w/o modifiers
|
# People & Body subtotal: 508 w/o modifiers
|
||||||
|
|
||||||
# group: Component
|
# group: Component
|
||||||
|
|
||||||
|
@ -3306,6 +3323,8 @@
|
||||||
1F405 ; fully-qualified # 🐅 E1.0 tiger
|
1F405 ; fully-qualified # 🐅 E1.0 tiger
|
||||||
1F406 ; fully-qualified # 🐆 E1.0 leopard
|
1F406 ; fully-qualified # 🐆 E1.0 leopard
|
||||||
1F434 ; fully-qualified # 🐴 E0.6 horse face
|
1F434 ; fully-qualified # 🐴 E0.6 horse face
|
||||||
|
1FACE ; fully-qualified # 🫎 E15.0 moose
|
||||||
|
1FACF ; fully-qualified # 🫏 E15.0 donkey
|
||||||
1F40E ; fully-qualified # 🐎 E0.6 horse
|
1F40E ; fully-qualified # 🐎 E0.6 horse
|
||||||
1F984 ; fully-qualified # 🦄 E1.0 unicorn
|
1F984 ; fully-qualified # 🦄 E1.0 unicorn
|
||||||
1F993 ; fully-qualified # 🦓 E5.0 zebra
|
1F993 ; fully-qualified # 🦓 E5.0 zebra
|
||||||
|
@ -3373,6 +3392,9 @@
|
||||||
1F9A9 ; fully-qualified # 🦩 E12.0 flamingo
|
1F9A9 ; fully-qualified # 🦩 E12.0 flamingo
|
||||||
1F99A ; fully-qualified # 🦚 E11.0 peacock
|
1F99A ; fully-qualified # 🦚 E11.0 peacock
|
||||||
1F99C ; fully-qualified # 🦜 E11.0 parrot
|
1F99C ; fully-qualified # 🦜 E11.0 parrot
|
||||||
|
1FABD ; fully-qualified # 🪽 E15.0 wing
|
||||||
|
1F426 200D 2B1B ; fully-qualified # 🐦⬛ E15.0 black bird
|
||||||
|
1FABF ; fully-qualified # 🪿 E15.0 goose
|
||||||
|
|
||||||
# subgroup: animal-amphibian
|
# subgroup: animal-amphibian
|
||||||
1F438 ; fully-qualified # 🐸 E0.6 frog
|
1F438 ; fully-qualified # 🐸 E0.6 frog
|
||||||
|
@ -3399,6 +3421,7 @@
|
||||||
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
|
1FAB8 ; fully-qualified # 🪸 E14.0 coral
|
||||||
|
1FABC ; fully-qualified # 🪼 E15.0 jellyfish
|
||||||
|
|
||||||
# subgroup: animal-bug
|
# subgroup: animal-bug
|
||||||
1F40C ; fully-qualified # 🐌 E0.6 snail
|
1F40C ; fully-qualified # 🐌 E0.6 snail
|
||||||
|
@ -3433,6 +3456,7 @@
|
||||||
1F33B ; fully-qualified # 🌻 E0.6 sunflower
|
1F33B ; fully-qualified # 🌻 E0.6 sunflower
|
||||||
1F33C ; fully-qualified # 🌼 E0.6 blossom
|
1F33C ; fully-qualified # 🌼 E0.6 blossom
|
||||||
1F337 ; fully-qualified # 🌷 E0.6 tulip
|
1F337 ; fully-qualified # 🌷 E0.6 tulip
|
||||||
|
1FABB ; fully-qualified # 🪻 E15.0 hyacinth
|
||||||
|
|
||||||
# subgroup: plant-other
|
# subgroup: plant-other
|
||||||
1F331 ; fully-qualified # 🌱 E0.6 seedling
|
1F331 ; fully-qualified # 🌱 E0.6 seedling
|
||||||
|
@ -3451,9 +3475,10 @@
|
||||||
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
|
1FAB9 ; fully-qualified # 🪹 E14.0 empty nest
|
||||||
1FABA ; fully-qualified # 🪺 E14.0 nest with eggs
|
1FABA ; fully-qualified # 🪺 E14.0 nest with eggs
|
||||||
|
1F344 ; fully-qualified # 🍄 E0.6 mushroom
|
||||||
|
|
||||||
# Animals & Nature subtotal: 151
|
# Animals & Nature subtotal: 159
|
||||||
# Animals & Nature subtotal: 151 w/o modifiers
|
# Animals & Nature subtotal: 159 w/o modifiers
|
||||||
|
|
||||||
# group: Food & Drink
|
# group: Food & Drink
|
||||||
|
|
||||||
|
@ -3492,10 +3517,11 @@
|
||||||
1F966 ; fully-qualified # 🥦 E5.0 broccoli
|
1F966 ; fully-qualified # 🥦 E5.0 broccoli
|
||||||
1F9C4 ; fully-qualified # 🧄 E12.0 garlic
|
1F9C4 ; fully-qualified # 🧄 E12.0 garlic
|
||||||
1F9C5 ; fully-qualified # 🧅 E12.0 onion
|
1F9C5 ; fully-qualified # 🧅 E12.0 onion
|
||||||
1F344 ; fully-qualified # 🍄 E0.6 mushroom
|
|
||||||
1F95C ; fully-qualified # 🥜 E3.0 peanuts
|
1F95C ; fully-qualified # 🥜 E3.0 peanuts
|
||||||
1FAD8 ; fully-qualified # 🫘 E14.0 beans
|
1FAD8 ; fully-qualified # 🫘 E14.0 beans
|
||||||
1F330 ; fully-qualified # 🌰 E0.6 chestnut
|
1F330 ; fully-qualified # 🌰 E0.6 chestnut
|
||||||
|
1FADA ; fully-qualified # 🫚 E15.0 ginger root
|
||||||
|
1FADB ; fully-qualified # 🫛 E15.0 pea pod
|
||||||
|
|
||||||
# subgroup: food-prepared
|
# subgroup: food-prepared
|
||||||
1F35E ; fully-qualified # 🍞 E0.6 bread
|
1F35E ; fully-qualified # 🍞 E0.6 bread
|
||||||
|
@ -3607,8 +3633,8 @@
|
||||||
1FAD9 ; fully-qualified # 🫙 E14.0 jar
|
1FAD9 ; fully-qualified # 🫙 E14.0 jar
|
||||||
1F3FA ; fully-qualified # 🏺 E1.0 amphora
|
1F3FA ; fully-qualified # 🏺 E1.0 amphora
|
||||||
|
|
||||||
# Food & Drink subtotal: 134
|
# Food & Drink subtotal: 135
|
||||||
# Food & Drink subtotal: 134 w/o modifiers
|
# Food & Drink subtotal: 135 w/o modifiers
|
||||||
|
|
||||||
# group: Travel & Places
|
# group: Travel & Places
|
||||||
|
|
||||||
|
@ -3974,11 +4000,10 @@
|
||||||
1F3AF ; fully-qualified # 🎯 E0.6 bullseye
|
1F3AF ; fully-qualified # 🎯 E0.6 bullseye
|
||||||
1FA80 ; fully-qualified # 🪀 E12.0 yo-yo
|
1FA80 ; fully-qualified # 🪀 E12.0 yo-yo
|
||||||
1FA81 ; fully-qualified # 🪁 E12.0 kite
|
1FA81 ; fully-qualified # 🪁 E12.0 kite
|
||||||
|
1F52B ; fully-qualified # 🔫 E0.6 water pistol
|
||||||
1F3B1 ; fully-qualified # 🎱 E0.6 pool 8 ball
|
1F3B1 ; fully-qualified # 🎱 E0.6 pool 8 ball
|
||||||
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
|
|
||||||
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
|
||||||
|
@ -4013,8 +4038,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: 97
|
# Activities subtotal: 96
|
||||||
# Activities subtotal: 97 w/o modifiers
|
# Activities subtotal: 96 w/o modifiers
|
||||||
|
|
||||||
# group: Objects
|
# group: Objects
|
||||||
|
|
||||||
|
@ -4040,6 +4065,7 @@
|
||||||
1FA73 ; fully-qualified # 🩳 E12.0 shorts
|
1FA73 ; fully-qualified # 🩳 E12.0 shorts
|
||||||
1F459 ; fully-qualified # 👙 E0.6 bikini
|
1F459 ; fully-qualified # 👙 E0.6 bikini
|
||||||
1F45A ; fully-qualified # 👚 E0.6 woman’s clothes
|
1F45A ; fully-qualified # 👚 E0.6 woman’s clothes
|
||||||
|
1FAAD ; fully-qualified # 🪭 E15.0 folding hand fan
|
||||||
1F45B ; fully-qualified # 👛 E0.6 purse
|
1F45B ; fully-qualified # 👛 E0.6 purse
|
||||||
1F45C ; fully-qualified # 👜 E0.6 handbag
|
1F45C ; fully-qualified # 👜 E0.6 handbag
|
||||||
1F45D ; fully-qualified # 👝 E0.6 clutch bag
|
1F45D ; fully-qualified # 👝 E0.6 clutch bag
|
||||||
|
@ -4055,6 +4081,7 @@
|
||||||
1F461 ; fully-qualified # 👡 E0.6 woman’s sandal
|
1F461 ; fully-qualified # 👡 E0.6 woman’s sandal
|
||||||
1FA70 ; fully-qualified # 🩰 E12.0 ballet shoes
|
1FA70 ; fully-qualified # 🩰 E12.0 ballet shoes
|
||||||
1F462 ; fully-qualified # 👢 E0.6 woman’s boot
|
1F462 ; fully-qualified # 👢 E0.6 woman’s boot
|
||||||
|
1FAAE ; fully-qualified # 🪮 E15.0 hair pick
|
||||||
1F451 ; fully-qualified # 👑 E0.6 crown
|
1F451 ; fully-qualified # 👑 E0.6 crown
|
||||||
1F452 ; fully-qualified # 👒 E0.6 woman’s hat
|
1F452 ; fully-qualified # 👒 E0.6 woman’s hat
|
||||||
1F3A9 ; fully-qualified # 🎩 E0.6 top hat
|
1F3A9 ; fully-qualified # 🎩 E0.6 top hat
|
||||||
|
@ -4103,6 +4130,8 @@
|
||||||
1FA95 ; fully-qualified # 🪕 E12.0 banjo
|
1FA95 ; fully-qualified # 🪕 E12.0 banjo
|
||||||
1F941 ; fully-qualified # 🥁 E3.0 drum
|
1F941 ; fully-qualified # 🥁 E3.0 drum
|
||||||
1FA98 ; fully-qualified # 🪘 E13.0 long drum
|
1FA98 ; fully-qualified # 🪘 E13.0 long drum
|
||||||
|
1FA87 ; fully-qualified # 🪇 E15.0 maracas
|
||||||
|
1FA88 ; fully-qualified # 🪈 E15.0 flute
|
||||||
|
|
||||||
# subgroup: phone
|
# subgroup: phone
|
||||||
1F4F1 ; fully-qualified # 📱 E0.6 mobile phone
|
1F4F1 ; fully-qualified # 📱 E0.6 mobile phone
|
||||||
|
@ -4275,7 +4304,7 @@
|
||||||
1F5E1 ; unqualified # 🗡 E0.7 dagger
|
1F5E1 ; unqualified # 🗡 E0.7 dagger
|
||||||
2694 FE0F ; fully-qualified # ⚔️ E1.0 crossed swords
|
2694 FE0F ; fully-qualified # ⚔️ E1.0 crossed swords
|
||||||
2694 ; unqualified # ⚔ E1.0 crossed swords
|
2694 ; unqualified # ⚔ E1.0 crossed swords
|
||||||
1F52B ; fully-qualified # 🔫 E0.6 water pistol
|
1F4A3 ; fully-qualified # 💣 E0.6 bomb
|
||||||
1FA83 ; fully-qualified # 🪃 E13.0 boomerang
|
1FA83 ; fully-qualified # 🪃 E13.0 boomerang
|
||||||
1F3F9 ; fully-qualified # 🏹 E1.0 bow and arrow
|
1F3F9 ; fully-qualified # 🏹 E1.0 bow and arrow
|
||||||
1F6E1 FE0F ; fully-qualified # 🛡️ E0.7 shield
|
1F6E1 FE0F ; fully-qualified # 🛡️ E0.7 shield
|
||||||
|
@ -4354,12 +4383,14 @@
|
||||||
1FAA6 ; fully-qualified # 🪦 E13.0 headstone
|
1FAA6 ; fully-qualified # 🪦 E13.0 headstone
|
||||||
26B1 FE0F ; fully-qualified # ⚱️ E1.0 funeral urn
|
26B1 FE0F ; fully-qualified # ⚱️ E1.0 funeral urn
|
||||||
26B1 ; unqualified # ⚱ E1.0 funeral urn
|
26B1 ; unqualified # ⚱ E1.0 funeral urn
|
||||||
|
1F9FF ; fully-qualified # 🧿 E11.0 nazar amulet
|
||||||
|
1FAAC ; fully-qualified # 🪬 E14.0 hamsa
|
||||||
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
|
1FAAA ; fully-qualified # 🪪 E14.0 identification card
|
||||||
|
|
||||||
# Objects subtotal: 304
|
# Objects subtotal: 310
|
||||||
# Objects subtotal: 304 w/o modifiers
|
# Objects subtotal: 310 w/o modifiers
|
||||||
|
|
||||||
# group: Symbols
|
# group: Symbols
|
||||||
|
|
||||||
|
@ -4455,6 +4486,7 @@
|
||||||
262E ; unqualified # ☮ E1.0 peace symbol
|
262E ; unqualified # ☮ E1.0 peace symbol
|
||||||
1F54E ; fully-qualified # 🕎 E1.0 menorah
|
1F54E ; fully-qualified # 🕎 E1.0 menorah
|
||||||
1F52F ; fully-qualified # 🔯 E0.6 dotted six-pointed star
|
1F52F ; fully-qualified # 🔯 E0.6 dotted six-pointed star
|
||||||
|
1FAAF ; fully-qualified # 🪯 E15.0 khanda
|
||||||
|
|
||||||
# subgroup: zodiac
|
# subgroup: zodiac
|
||||||
2648 ; fully-qualified # ♈ E0.6 Aries
|
2648 ; fully-qualified # ♈ E0.6 Aries
|
||||||
|
@ -4503,6 +4535,7 @@
|
||||||
1F505 ; fully-qualified # 🔅 E1.0 dim button
|
1F505 ; fully-qualified # 🔅 E1.0 dim button
|
||||||
1F506 ; fully-qualified # 🔆 E1.0 bright button
|
1F506 ; fully-qualified # 🔆 E1.0 bright button
|
||||||
1F4F6 ; fully-qualified # 📶 E0.6 antenna bars
|
1F4F6 ; fully-qualified # 📶 E0.6 antenna bars
|
||||||
|
1F6DC ; fully-qualified # 🛜 E15.0 wireless
|
||||||
1F4F3 ; fully-qualified # 📳 E0.6 vibration mode
|
1F4F3 ; fully-qualified # 📳 E0.6 vibration mode
|
||||||
1F4F4 ; fully-qualified # 📴 E0.6 mobile phone off
|
1F4F4 ; fully-qualified # 📴 E0.6 mobile phone off
|
||||||
|
|
||||||
|
@ -4693,8 +4726,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: 302
|
# Symbols subtotal: 304
|
||||||
# Symbols subtotal: 302 w/o modifiers
|
# Symbols subtotal: 304 w/o modifiers
|
||||||
|
|
||||||
# group: Flags
|
# group: Flags
|
||||||
|
|
||||||
|
@ -4709,7 +4742,7 @@
|
||||||
1F3F3 200D 1F308 ; unqualified # 🏳🌈 E4.0 rainbow flag
|
1F3F3 200D 1F308 ; unqualified # 🏳🌈 E4.0 rainbow flag
|
||||||
1F3F3 FE0F 200D 26A7 FE0F ; fully-qualified # 🏳️⚧️ E13.0 transgender flag
|
1F3F3 FE0F 200D 26A7 FE0F ; fully-qualified # 🏳️⚧️ E13.0 transgender flag
|
||||||
1F3F3 200D 26A7 FE0F ; unqualified # 🏳⚧️ E13.0 transgender flag
|
1F3F3 200D 26A7 FE0F ; unqualified # 🏳⚧️ E13.0 transgender flag
|
||||||
1F3F3 FE0F 200D 26A7 ; unqualified # 🏳️⚧ E13.0 transgender flag
|
1F3F3 FE0F 200D 26A7 ; minimally-qualified # 🏳️⚧ E13.0 transgender flag
|
||||||
1F3F3 200D 26A7 ; unqualified # 🏳⚧ E13.0 transgender flag
|
1F3F3 200D 26A7 ; unqualified # 🏳⚧ E13.0 transgender flag
|
||||||
1F3F4 200D 2620 FE0F ; fully-qualified # 🏴☠️ E11.0 pirate flag
|
1F3F4 200D 2620 FE0F ; fully-qualified # 🏴☠️ E11.0 pirate flag
|
||||||
1F3F4 200D 2620 ; minimally-qualified # 🏴☠ E11.0 pirate flag
|
1F3F4 200D 2620 ; minimally-qualified # 🏴☠ E11.0 pirate flag
|
||||||
|
@ -4983,9 +5016,9 @@
|
||||||
# Flags subtotal: 275 w/o modifiers
|
# Flags subtotal: 275 w/o modifiers
|
||||||
|
|
||||||
# Status Counts
|
# Status Counts
|
||||||
# fully-qualified : 3624
|
# fully-qualified : 3655
|
||||||
# minimally-qualified : 817
|
# minimally-qualified : 827
|
||||||
# unqualified : 252
|
# unqualified : 242
|
||||||
# component : 9
|
# component : 9
|
||||||
|
|
||||||
#EOF
|
#EOF
|
||||||
|
|
|
@ -145,7 +145,7 @@ defmodule Pleroma.Object do
|
||||||
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
|
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def normalize(_, options \\ [fetch: false])
|
def normalize(_, options \\ [fetch: false, id_only: false])
|
||||||
|
|
||||||
# If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
|
# If we pass an Activity to Object.normalize(), we can try to use the preloaded object.
|
||||||
# Use this whenever possible, especially when walking graphs in an O(N) loop!
|
# Use this whenever possible, especially when walking graphs in an O(N) loop!
|
||||||
|
@ -173,10 +173,15 @@ defmodule Pleroma.Object do
|
||||||
def normalize(%{"id" => ap_id}, options), do: normalize(ap_id, options)
|
def normalize(%{"id" => ap_id}, options), do: normalize(ap_id, options)
|
||||||
|
|
||||||
def normalize(ap_id, options) when is_binary(ap_id) do
|
def normalize(ap_id, options) when is_binary(ap_id) do
|
||||||
if Keyword.get(options, :fetch) do
|
cond do
|
||||||
Fetcher.fetch_object_from_id!(ap_id, options)
|
Keyword.get(options, :id_only) ->
|
||||||
else
|
ap_id
|
||||||
get_cached_by_ap_id(ap_id)
|
|
||||||
|
Keyword.get(options, :fetch) ->
|
||||||
|
Fetcher.fetch_object_from_id!(ap_id, options)
|
||||||
|
|
||||||
|
true ->
|
||||||
|
get_cached_by_ap_id(ap_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -153,7 +153,7 @@ defmodule Pleroma.Search.Meilisearch do
|
||||||
)
|
)
|
||||||
|
|
||||||
with {:ok, res} <- result,
|
with {:ok, res} <- result,
|
||||||
true <- Map.has_key?(res, "uid") do
|
true <- Map.has_key?(res, "taskUid") do
|
||||||
# Do nothing
|
# Do nothing
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
|
|
@ -66,9 +66,8 @@ defmodule Pleroma.Signature do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def sign(%User{} = user, headers) do
|
def sign(%User{keys: keys} = user, headers) do
|
||||||
with {:ok, %{keys: keys}} <- User.ensure_keys_present(user),
|
with {:ok, private_key, _} <- Keys.keys_from_pem(keys) do
|
||||||
{:ok, private_key, _} <- Keys.keys_from_pem(keys) do
|
|
||||||
HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)
|
HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -165,6 +165,8 @@ defmodule Pleroma.User do
|
||||||
has_many(:outgoing_relationships, UserRelationship, foreign_key: :source_id)
|
has_many(:outgoing_relationships, UserRelationship, foreign_key: :source_id)
|
||||||
has_many(:incoming_relationships, UserRelationship, foreign_key: :target_id)
|
has_many(:incoming_relationships, UserRelationship, foreign_key: :target_id)
|
||||||
|
|
||||||
|
has_many(:frontend_profiles, Pleroma.Akkoma.FrontendSettingsProfile)
|
||||||
|
|
||||||
for {relationship_type,
|
for {relationship_type,
|
||||||
[
|
[
|
||||||
{outgoing_relation, outgoing_relation_target},
|
{outgoing_relation, outgoing_relation_target},
|
||||||
|
@ -681,9 +683,9 @@ defmodule Pleroma.User do
|
||||||
|> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
|
|> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
|
||||||
|> validate_format(:nickname, local_nickname_regex())
|
|> validate_format(:nickname, local_nickname_regex())
|
||||||
|> put_ap_id()
|
|> put_ap_id()
|
||||||
|> put_keys()
|
|
||||||
|> unique_constraint(:ap_id)
|
|> unique_constraint(:ap_id)
|
||||||
|> put_following_and_follower_and_featured_address()
|
|> put_following_and_follower_and_featured_address()
|
||||||
|
|> put_private_key()
|
||||||
end
|
end
|
||||||
|
|
||||||
def register_changeset(struct, params \\ %{}, opts \\ []) do
|
def register_changeset(struct, params \\ %{}, opts \\ []) do
|
||||||
|
@ -741,10 +743,10 @@ defmodule Pleroma.User do
|
||||||
|> validate_length(:registration_reason, max: reason_limit)
|
|> validate_length(:registration_reason, max: reason_limit)
|
||||||
|> maybe_validate_required_email(opts[:external])
|
|> maybe_validate_required_email(opts[:external])
|
||||||
|> put_password_hash
|
|> put_password_hash
|
||||||
|> put_keys()
|
|
||||||
|> put_ap_id()
|
|> put_ap_id()
|
||||||
|> unique_constraint(:ap_id)
|
|> unique_constraint(:ap_id)
|
||||||
|> put_following_and_follower_and_featured_address()
|
|> put_following_and_follower_and_featured_address()
|
||||||
|
|> put_private_key()
|
||||||
end
|
end
|
||||||
|
|
||||||
def maybe_validate_required_email(changeset, true), do: changeset
|
def maybe_validate_required_email(changeset, true), do: changeset
|
||||||
|
@ -757,11 +759,6 @@ defmodule Pleroma.User do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def put_keys(changeset) do
|
|
||||||
{:ok, pem} = Keys.generate_rsa_pem()
|
|
||||||
put_change(changeset, :keys, pem)
|
|
||||||
end
|
|
||||||
|
|
||||||
def put_ap_id(changeset) do
|
def put_ap_id(changeset) do
|
||||||
ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)})
|
ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)})
|
||||||
put_change(changeset, :ap_id, ap_id)
|
put_change(changeset, :ap_id, ap_id)
|
||||||
|
@ -779,6 +776,11 @@ defmodule Pleroma.User do
|
||||||
|> put_change(:featured_address, featured)
|
|> put_change(:featured_address, featured)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp put_private_key(changeset) do
|
||||||
|
{:ok, pem} = Keys.generate_rsa_pem()
|
||||||
|
put_change(changeset, :keys, pem)
|
||||||
|
end
|
||||||
|
|
||||||
defp autofollow_users(user) do
|
defp autofollow_users(user) do
|
||||||
candidates = Config.get([:instance, :autofollowed_nicknames])
|
candidates = Config.get([:instance, :autofollowed_nicknames])
|
||||||
|
|
||||||
|
@ -1955,6 +1957,7 @@ defmodule Pleroma.User do
|
||||||
follower_address: uri <> "/followers"
|
follower_address: uri <> "/followers"
|
||||||
}
|
}
|
||||||
|> change
|
|> change
|
||||||
|
|> put_private_key()
|
||||||
|> unique_constraint(:nickname)
|
|> unique_constraint(:nickname)
|
||||||
|> Repo.insert()
|
|> Repo.insert()
|
||||||
|> set_cache()
|
|> set_cache()
|
||||||
|
@ -1987,7 +1990,8 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
@doc "Gets or fetch a user by uri or nickname."
|
@doc "Gets or fetch a user by uri or nickname."
|
||||||
@spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()}
|
@spec get_or_fetch(String.t()) :: {:ok, User.t()} | {:error, String.t()}
|
||||||
def get_or_fetch("http" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
|
def get_or_fetch("http://" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
|
||||||
|
def get_or_fetch("https://" <> _host = uri), do: get_or_fetch_by_ap_id(uri)
|
||||||
def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname)
|
def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname)
|
||||||
|
|
||||||
# wait a period of time and return newest version of the User structs
|
# wait a period of time and return newest version of the User structs
|
||||||
|
@ -2220,17 +2224,6 @@ defmodule Pleroma.User do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_keys_present(%{keys: keys} = user) when not is_nil(keys), do: {:ok, user}
|
|
||||||
|
|
||||||
def ensure_keys_present(%User{} = user) do
|
|
||||||
with {:ok, pem} <- Keys.generate_rsa_pem() do
|
|
||||||
user
|
|
||||||
|> cast(%{keys: pem}, [:keys])
|
|
||||||
|> validate_required([:keys])
|
|
||||||
|> update_and_set_cache()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_ap_ids_by_nicknames(nicknames) do
|
def get_ap_ids_by_nicknames(nicknames) do
|
||||||
from(u in User,
|
from(u in User,
|
||||||
where: u.nickname in ^nicknames,
|
where: u.nickname in ^nicknames,
|
||||||
|
|
|
@ -94,6 +94,7 @@ defmodule Pleroma.User.Search do
|
||||||
|> subquery()
|
|> subquery()
|
||||||
|> order_by(desc: :search_rank)
|
|> order_by(desc: :search_rank)
|
||||||
|> maybe_restrict_local(for_user)
|
|> maybe_restrict_local(for_user)
|
||||||
|
|> filter_deactivated_users()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp select_top_users(query, top_user_ids) do
|
defp select_top_users(query, top_user_ids) do
|
||||||
|
@ -166,6 +167,10 @@ defmodule Pleroma.User.Search do
|
||||||
from(q in query, where: q.actor_type != "Application")
|
from(q in query, where: q.actor_type != "Application")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp filter_deactivated_users(query) do
|
||||||
|
from(q in query, where: q.is_active == true)
|
||||||
|
end
|
||||||
|
|
||||||
defp filter_blocked_user(query, %User{} = blocker) do
|
defp filter_blocked_user(query, %User{} = blocker) do
|
||||||
query
|
query
|
||||||
|> join(:left, [u], b in Pleroma.UserRelationship,
|
|> join(:left, [u], b in Pleroma.UserRelationship,
|
||||||
|
|
|
@ -66,8 +66,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
end
|
end
|
||||||
|
|
||||||
def user(conn, %{"nickname" => nickname}) do
|
def user(conn, %{"nickname" => nickname}) do
|
||||||
with %User{local: true} = user <- User.get_cached_by_nickname(nickname),
|
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
{:ok, user} <- User.ensure_keys_present(user) do
|
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("application/activity+json")
|
|> put_resp_content_type("application/activity+json")
|
||||||
|> put_view(UserView)
|
|> put_view(UserView)
|
||||||
|
@ -174,7 +173,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
|
|
||||||
def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
|
def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||||
{user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
|
|
||||||
{:show_follows, true} <-
|
{:show_follows, true} <-
|
||||||
{:show_follows, (for_user && for_user == user) || !user.hide_follows} do
|
{:show_follows, (for_user && for_user == user) || !user.hide_follows} do
|
||||||
{page, _} = Integer.parse(page)
|
{page, _} = Integer.parse(page)
|
||||||
|
@ -192,8 +190,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
end
|
end
|
||||||
|
|
||||||
def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do
|
def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
{user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
|
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("application/activity+json")
|
|> put_resp_content_type("application/activity+json")
|
||||||
|> put_view(UserView)
|
|> put_view(UserView)
|
||||||
|
@ -213,7 +210,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
|
|
||||||
def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
|
def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
||||||
{user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user),
|
|
||||||
{:show_followers, true} <-
|
{:show_followers, true} <-
|
||||||
{:show_followers, (for_user && for_user == user) || !user.hide_followers} do
|
{:show_followers, (for_user && for_user == user) || !user.hide_followers} do
|
||||||
{page, _} = Integer.parse(page)
|
{page, _} = Integer.parse(page)
|
||||||
|
@ -231,8 +227,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
end
|
end
|
||||||
|
|
||||||
def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do
|
def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
{user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
|
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("application/activity+json")
|
|> put_resp_content_type("application/activity+json")
|
||||||
|> put_view(UserView)
|
|> put_view(UserView)
|
||||||
|
@ -245,8 +240,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
%{"nickname" => nickname, "page" => page?} = params
|
%{"nickname" => nickname, "page" => page?} = params
|
||||||
)
|
)
|
||||||
when page? in [true, "true"] do
|
when page? in [true, "true"] do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
{:ok, user} <- User.ensure_keys_present(user) do
|
|
||||||
# "include_poll_votes" is a hack because postgres generates inefficient
|
# "include_poll_votes" is a hack because postgres generates inefficient
|
||||||
# queries when filtering by 'Answer', poll votes will be hidden by the
|
# queries when filtering by 'Answer', poll votes will be hidden by the
|
||||||
# visibility filter in this case anyway
|
# visibility filter in this case anyway
|
||||||
|
@ -270,8 +264,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
end
|
end
|
||||||
|
|
||||||
def outbox(conn, %{"nickname" => nickname}) do
|
def outbox(conn, %{"nickname" => nickname}) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname(nickname),
|
with %User{} = user <- User.get_cached_by_nickname(nickname) do
|
||||||
{:ok, user} <- User.ensure_keys_present(user) do
|
|
||||||
conn
|
conn
|
||||||
|> put_resp_content_type("application/activity+json")
|
|> put_resp_content_type("application/activity+json")
|
||||||
|> put_view(UserView)
|
|> put_view(UserView)
|
||||||
|
@ -328,14 +321,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp represent_service_actor(%User{} = user, conn) do
|
defp represent_service_actor(%User{} = user, conn) do
|
||||||
with {:ok, user} <- User.ensure_keys_present(user) do
|
conn
|
||||||
conn
|
|> put_resp_content_type("application/activity+json")
|
||||||
|> put_resp_content_type("application/activity+json")
|
|> put_view(UserView)
|
||||||
|> put_view(UserView)
|
|> render("user.json", %{user: user})
|
||||||
|> render("user.json", %{user: user})
|
|
||||||
else
|
|
||||||
nil -> {:error, :not_found}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp represent_service_actor(nil, _), do: {:error, :not_found}
|
defp represent_service_actor(nil, _), do: {:error, :not_found}
|
||||||
|
@ -388,12 +377,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
def read_inbox(%{assigns: %{user: %User{nickname: nickname} = user}} = conn, %{
|
def read_inbox(%{assigns: %{user: %User{nickname: nickname} = user}} = conn, %{
|
||||||
"nickname" => nickname
|
"nickname" => nickname
|
||||||
}) do
|
}) do
|
||||||
with {:ok, user} <- User.ensure_keys_present(user) do
|
conn
|
||||||
conn
|
|> put_resp_content_type("application/activity+json")
|
||||||
|> put_resp_content_type("application/activity+json")
|
|> put_view(UserView)
|
||||||
|> put_view(UserView)
|
|> render("activity_collection.json", %{iri: "#{user.ap_id}/inbox"})
|
||||||
|> render("activity_collection.json", %{iri: "#{user.ap_id}/inbox"})
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_inbox(%{assigns: %{user: %User{nickname: as_nickname}}} = conn, %{
|
def read_inbox(%{assigns: %{user: %User{nickname: as_nickname}}} = conn, %{
|
||||||
|
@ -530,19 +517,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
|
|
||||||
defp ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
|
|
||||||
{:ok, new_user} = User.ensure_keys_present(user)
|
|
||||||
|
|
||||||
for_user =
|
|
||||||
if new_user != user and match?(%User{}, for_user) do
|
|
||||||
User.get_cached_by_nickname(for_user.nickname)
|
|
||||||
else
|
|
||||||
for_user
|
|
||||||
end
|
|
||||||
|
|
||||||
{new_user, for_user}
|
|
||||||
end
|
|
||||||
|
|
||||||
def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
|
def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
|
||||||
with {:ok, object} <-
|
with {:ok, object} <-
|
||||||
ActivityPub.upload(
|
ActivityPub.upload(
|
||||||
|
|
|
@ -29,11 +29,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
|
||||||
|
|
||||||
def render("object.json", %{object: %Activity{} = activity}) do
|
def render("object.json", %{object: %Activity{} = activity}) do
|
||||||
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
|
||||||
object = Object.normalize(activity, fetch: false)
|
object_id = Object.normalize(activity, id_only: true)
|
||||||
|
|
||||||
additional =
|
additional =
|
||||||
Transmogrifier.prepare_object(activity.data)
|
Transmogrifier.prepare_object(activity.data)
|
||||||
|> Map.put("object", object.data["id"])
|
|> Map.put("object", object_id)
|
||||||
|
|
||||||
Map.merge(base, additional)
|
Map.merge(base, additional)
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
def render("endpoints.json", _), do: %{}
|
def render("endpoints.json", _), do: %{}
|
||||||
|
|
||||||
def render("service.json", %{user: user}) do
|
def render("service.json", %{user: user}) do
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
||||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||||
public_key = :public_key.pem_encode([public_key])
|
public_key = :public_key.pem_encode([public_key])
|
||||||
|
@ -71,7 +70,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
do: render("service.json", %{user: user}) |> Map.put("preferredUsername", user.nickname)
|
do: render("service.json", %{user: user}) |> Map.put("preferredUsername", user.nickname)
|
||||||
|
|
||||||
def render("user.json", %{user: user}) do
|
def render("user.json", %{user: user}) do
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
||||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||||
public_key = :public_key.pem_encode([public_key])
|
public_key = :public_key.pem_encode([public_key])
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
defmodule Pleroma.Web.AkkomaAPI.FrontendSettingsController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.Akkoma.FrontendSettingsProfile
|
||||||
|
|
||||||
|
@unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{@unauthenticated_access | scopes: ["read:accounts"]}
|
||||||
|
when action in [
|
||||||
|
:list_profiles,
|
||||||
|
:get_profile
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{@unauthenticated_access | scopes: ["write:accounts"]}
|
||||||
|
when action in [
|
||||||
|
:update_profile,
|
||||||
|
:delete_profile
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.FrontendSettingsOperation
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
|
@doc "GET /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name"
|
||||||
|
def get_profile(conn, %{frontend_name: frontend_name, profile_name: profile_name}) do
|
||||||
|
with %FrontendSettingsProfile{} = profile <-
|
||||||
|
FrontendSettingsProfile.get_by_user_and_frontend_name_and_profile_name(
|
||||||
|
conn.assigns.user,
|
||||||
|
frontend_name,
|
||||||
|
profile_name
|
||||||
|
) do
|
||||||
|
conn
|
||||||
|
|> json(%{
|
||||||
|
settings: profile.settings,
|
||||||
|
version: profile.version
|
||||||
|
})
|
||||||
|
else
|
||||||
|
nil -> {:error, :not_found}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "GET /api/v1/akkoma/frontend_settings/:frontend_name"
|
||||||
|
def list_profiles(conn, %{frontend_name: frontend_name}) do
|
||||||
|
with profiles <-
|
||||||
|
FrontendSettingsProfile.get_all_by_user_and_frontend_name(
|
||||||
|
conn.assigns.user,
|
||||||
|
frontend_name
|
||||||
|
),
|
||||||
|
data <-
|
||||||
|
Enum.map(profiles, fn profile ->
|
||||||
|
%{name: profile.profile_name, version: profile.version}
|
||||||
|
end) do
|
||||||
|
json(conn, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "DELETE /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name"
|
||||||
|
def delete_profile(conn, %{frontend_name: frontend_name, profile_name: profile_name}) do
|
||||||
|
with %FrontendSettingsProfile{} = profile <-
|
||||||
|
FrontendSettingsProfile.get_by_user_and_frontend_name_and_profile_name(
|
||||||
|
conn.assigns.user,
|
||||||
|
frontend_name,
|
||||||
|
profile_name
|
||||||
|
),
|
||||||
|
{:ok, _} <- FrontendSettingsProfile.delete_profile(profile) do
|
||||||
|
json(conn, %{deleted: "ok"})
|
||||||
|
else
|
||||||
|
nil -> {:error, :not_found}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "PUT /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name"
|
||||||
|
def update_profile(%{body_params: %{settings: settings, version: version}} = conn, %{
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name
|
||||||
|
}) do
|
||||||
|
with {:ok, profile} <-
|
||||||
|
FrontendSettingsProfile.create_or_update(
|
||||||
|
conn.assigns.user,
|
||||||
|
frontend_name,
|
||||||
|
profile_name,
|
||||||
|
settings,
|
||||||
|
version
|
||||||
|
) do
|
||||||
|
conn
|
||||||
|
|> json(profile.settings)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,133 @@
|
||||||
|
defmodule Pleroma.Web.ApiSpec.FrontendSettingsOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
@spec open_api_operation(atom) :: Operation.t()
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec list_profiles_operation() :: Operation.t()
|
||||||
|
def list_profiles_operation() do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Retrieve frontend setting profiles"],
|
||||||
|
summary: "Frontend Settings Profiles",
|
||||||
|
description: "List frontend setting profiles",
|
||||||
|
operationId: "AkkomaAPI.FrontendSettingsController.list_profiles",
|
||||||
|
parameters: [frontend_name_param()],
|
||||||
|
security: [%{"oAuth" => ["read:accounts"]}],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Profiles", "application/json", %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
name: %Schema{type: :string},
|
||||||
|
version: %Schema{type: :integer}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_profile_operation() :: Operation.t()
|
||||||
|
def get_profile_operation() do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Retrieve frontend setting profile"],
|
||||||
|
summary: "Frontend Settings Profile",
|
||||||
|
description: "Get frontend setting profile",
|
||||||
|
operationId: "AkkomaAPI.FrontendSettingsController.get_profile",
|
||||||
|
security: [%{"oAuth" => ["read:accounts"]}],
|
||||||
|
parameters: [frontend_name_param(), profile_name_param()],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Profile", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
"version" => %Schema{type: :integer},
|
||||||
|
"settings" => %Schema{type: :object, additionalProperties: true}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
404 => Operation.response("Not Found", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec delete_profile_operation() :: Operation.t()
|
||||||
|
def delete_profile_operation() do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Delete frontend setting profile"],
|
||||||
|
summary: "Delete frontend Settings Profile",
|
||||||
|
description: "Delete frontend setting profile",
|
||||||
|
operationId: "AkkomaAPI.FrontendSettingsController.delete_profile",
|
||||||
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
|
parameters: [frontend_name_param(), profile_name_param()],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Empty", "application/json", %Schema{type: :object}),
|
||||||
|
404 => Operation.response("Not Found", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec update_profile_operation() :: Operation.t()
|
||||||
|
def update_profile_operation() do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Update frontend setting profile"],
|
||||||
|
summary: "Frontend Settings Profile",
|
||||||
|
description: "Update frontend setting profile",
|
||||||
|
operationId: "AkkomaAPI.FrontendSettingsController.update_profile_operation",
|
||||||
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
|
parameters: [frontend_name_param(), profile_name_param()],
|
||||||
|
requestBody: profile_body_param(),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Settings", "application/json", %Schema{type: :object}),
|
||||||
|
422 => Operation.response("Invalid", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def frontend_name_param do
|
||||||
|
Operation.parameter(:frontend_name, :path, :string, "Frontend name",
|
||||||
|
example: "pleroma-fe",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def profile_name_param do
|
||||||
|
Operation.parameter(:profile_name, :path, :string, "Profile name",
|
||||||
|
example: "mobile",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def profile_body_param do
|
||||||
|
request_body(
|
||||||
|
"Settings",
|
||||||
|
%Schema{
|
||||||
|
title: "Frontend Setting Profile",
|
||||||
|
type: :object,
|
||||||
|
required: [:version, :settings],
|
||||||
|
properties: %{
|
||||||
|
version: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: "Version of the profile, must increment by 1 each time",
|
||||||
|
example: 1
|
||||||
|
},
|
||||||
|
settings: %Schema{
|
||||||
|
type: :object,
|
||||||
|
description: "Settings of the profile",
|
||||||
|
example: %{
|
||||||
|
theme: "dark",
|
||||||
|
locale: "en"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -16,7 +16,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Backups"],
|
tags: ["Backups"],
|
||||||
summary: "List backups",
|
summary: "List backups",
|
||||||
security: [%{"oAuth" => ["read:account"]}],
|
security: [%{"oAuth" => ["read:backups"]}],
|
||||||
operationId: "PleromaAPI.BackupController.index",
|
operationId: "PleromaAPI.BackupController.index",
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
@ -37,7 +37,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Backups"],
|
tags: ["Backups"],
|
||||||
summary: "Create a backup",
|
summary: "Create a backup",
|
||||||
security: [%{"oAuth" => ["read:account"]}],
|
security: [%{"oAuth" => ["read:backups"]}],
|
||||||
operationId: "PleromaAPI.BackupController.create",
|
operationId: "PleromaAPI.BackupController.create",
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
|
|
|
@ -69,10 +69,8 @@ defmodule Pleroma.Web.Federator do
|
||||||
def perform(:publish, activity) do
|
def perform(:publish, activity) do
|
||||||
Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end)
|
Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end)
|
||||||
|
|
||||||
with %User{} = actor <- User.get_cached_by_ap_id(activity.data["actor"]),
|
%User{} = actor = User.get_cached_by_ap_id(activity.data["actor"])
|
||||||
{:ok, actor} <- User.ensure_keys_present(actor) do
|
Publisher.publish(actor, activity)
|
||||||
Publisher.publish(actor, activity)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(:incoming_ap_doc, params) do
|
def perform(:incoming_ap_doc, params) do
|
||||||
|
|
|
@ -8,8 +8,8 @@ defmodule Pleroma.Web.Metadata.Utils do
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
|
|
||||||
def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
|
defp scrub_html_and_truncate_object_field(field, object) do
|
||||||
content
|
field
|
||||||
# html content comes from DB already encoded, decode first and scrub after
|
# html content comes from DB already encoded, decode first and scrub after
|
||||||
|> HtmlEntities.decode()
|
|> HtmlEntities.decode()
|
||||||
|> String.replace(~r/<br\s?\/?>/, " ")
|
|> String.replace(~r/<br\s?\/?>/, " ")
|
||||||
|
@ -19,6 +19,17 @@ defmodule Pleroma.Web.Metadata.Utils do
|
||||||
|> Formatter.truncate()
|
|> Formatter.truncate()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def scrub_html_and_truncate(%{data: %{"summary" => summary}} = object)
|
||||||
|
when is_binary(summary) and summary != "" do
|
||||||
|
summary
|
||||||
|
|> scrub_html_and_truncate_object_field(object)
|
||||||
|
end
|
||||||
|
|
||||||
|
def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
|
||||||
|
content
|
||||||
|
|> scrub_html_and_truncate_object_field(object)
|
||||||
|
end
|
||||||
|
|
||||||
def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) do
|
def scrub_html_and_truncate(content, max_length \\ 200) when is_binary(content) do
|
||||||
content
|
content
|
||||||
|> scrub_html
|
|> scrub_html
|
||||||
|
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.PleromaAPI.BackupController do
|
||||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action in [:index, :create])
|
plug(OAuthScopesPlug, %{scopes: ["read:backups"]} when action in [:index, :create])
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaBackupOperation
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaBackupOperation
|
||||||
|
|
|
@ -47,15 +47,17 @@ defmodule Pleroma.Web.Plugs.OAuthPlug do
|
||||||
#
|
#
|
||||||
@spec fetch_user_and_token(String.t()) :: {:ok, User.t(), Token.t()} | nil
|
@spec fetch_user_and_token(String.t()) :: {:ok, User.t(), Token.t()} | nil
|
||||||
defp fetch_user_and_token(token) do
|
defp fetch_user_and_token(token) do
|
||||||
query =
|
token_query =
|
||||||
from(t in Token,
|
from(t in Token,
|
||||||
where: t.token == ^token,
|
where: t.token == ^token
|
||||||
join: user in assoc(t, :user),
|
|
||||||
preload: [user: user]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with %Token{user: user} = token_record <- Repo.one(query) do
|
with %Token{user_id: user_id} = token_record <- Repo.one(token_query),
|
||||||
|
false <- is_nil(user_id),
|
||||||
|
%User{} = user <- User.get_cached_by_id(user_id) do
|
||||||
{:ok, user, token_record}
|
{:ok, user, token_record}
|
||||||
|
else
|
||||||
|
_ -> nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -466,6 +466,26 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api/v1/akkoma", Pleroma.Web.AkkomaAPI do
|
scope "/api/v1/akkoma", Pleroma.Web.AkkomaAPI do
|
||||||
pipe_through(:authenticated_api)
|
pipe_through(:authenticated_api)
|
||||||
get("/translation/languages", TranslationController, :languages)
|
get("/translation/languages", TranslationController, :languages)
|
||||||
|
|
||||||
|
get("/frontend_settings/:frontend_name", FrontendSettingsController, :list_profiles)
|
||||||
|
|
||||||
|
get(
|
||||||
|
"/frontend_settings/:frontend_name/:profile_name",
|
||||||
|
FrontendSettingsController,
|
||||||
|
:get_profile
|
||||||
|
)
|
||||||
|
|
||||||
|
put(
|
||||||
|
"/frontend_settings/:frontend_name/:profile_name",
|
||||||
|
FrontendSettingsController,
|
||||||
|
:update_profile
|
||||||
|
)
|
||||||
|
|
||||||
|
delete(
|
||||||
|
"/frontend_settings/:frontend_name/:profile_name",
|
||||||
|
FrontendSettingsController,
|
||||||
|
:delete_profile
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||||
|
|
|
@ -69,8 +69,6 @@ defmodule Pleroma.Web.WebFinger do
|
||||||
end
|
end
|
||||||
|
|
||||||
def represent_user(user, "JSON") do
|
def represent_user(user, "JSON") do
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"subject" => "acct:#{user.nickname}@#{domain()}",
|
"subject" => "acct:#{user.nickname}@#{domain()}",
|
||||||
"aliases" => gather_aliases(user),
|
"aliases" => gather_aliases(user),
|
||||||
|
@ -79,8 +77,6 @@ defmodule Pleroma.Web.WebFinger do
|
||||||
end
|
end
|
||||||
|
|
||||||
def represent_user(user, "XML") do
|
def represent_user(user, "XML") do
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
|
|
||||||
aliases =
|
aliases =
|
||||||
user
|
user
|
||||||
|> gather_aliases()
|
|> gather_aliases()
|
||||||
|
|
2
mix.exs
2
mix.exs
|
@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
app: :pleroma,
|
app: :pleroma,
|
||||||
version: version("3.2.0"),
|
version: version("3.3.0"),
|
||||||
elixir: "~> 1.12",
|
elixir: "~> 1.12",
|
||||||
elixirc_paths: elixirc_paths(Mix.env()),
|
elixirc_paths: elixirc_paths(Mix.env()),
|
||||||
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Repo.Migrations.GenerateUnsetUserKeys do
|
||||||
|
use Ecto.Migration
|
||||||
|
import Ecto.Query
|
||||||
|
alias Pleroma.Keys
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
def change do
|
||||||
|
query =
|
||||||
|
from(u in User,
|
||||||
|
where: u.local == true,
|
||||||
|
where: is_nil(u.keys),
|
||||||
|
select: u
|
||||||
|
)
|
||||||
|
|
||||||
|
Repo.stream(query)
|
||||||
|
|> Enum.each(fn user ->
|
||||||
|
with {:ok, pem} <- Keys.generate_rsa_pem() do
|
||||||
|
Ecto.Changeset.cast(user, %{keys: pem}, [:keys])
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,29 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddUserFrontendProfiles do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def up do
|
||||||
|
create_if_not_exists table("user_frontend_setting_profiles", primary_key: false) do
|
||||||
|
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all), primary_key: true)
|
||||||
|
add(:frontend_name, :string, primary_key: true)
|
||||||
|
add(:profile_name, :string, primary_key: true)
|
||||||
|
add(:version, :integer)
|
||||||
|
add(:settings, :map)
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create_if_not_exists(index(:user_frontend_setting_profiles, [:user_id, :frontend_name]))
|
||||||
|
|
||||||
|
create_if_not_exists(
|
||||||
|
unique_index(:user_frontend_setting_profiles, [:user_id, :frontend_name, :profile_name])
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
drop_if_exists(table("user_frontend_setting_profiles"))
|
||||||
|
drop_if_exists(index(:user_frontend_setting_profiles, [:user_id, :frontend_name]))
|
||||||
|
|
||||||
|
drop_if_exists(
|
||||||
|
unique_index(:user_frontend_setting_profiles, [:user_id, :frontend_name, :profile_name])
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,9 +1,15 @@
|
||||||
defmodule Pleroma.Repo.Migrations.EnsureMastofeSettings do
|
defmodule Pleroma.Repo.Migrations.EnsureMastofeSettings do
|
||||||
use Ecto.Migration
|
use Ecto.Migration
|
||||||
|
|
||||||
def change do
|
def up do
|
||||||
alter table(:users) do
|
alter table(:users) do
|
||||||
add_if_not_exists(:mastofe_settings, :map)
|
add_if_not_exists(:mastofe_settings, :map)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
alter table(:users) do
|
||||||
|
remove_if_exists(:mastofe_settings, :map)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -51,6 +51,7 @@ body {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin: 35px auto;
|
margin: 35px auto;
|
||||||
box-shadow: 0 1px 4px 0px rgba(0, 0, 0, 0.5);
|
box-shadow: 0 1px 4px 0px rgba(0, 0, 0, 0.5);
|
||||||
|
padding: 0em 1em 0em 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container__content {
|
.container__content {
|
||||||
|
@ -86,7 +87,6 @@ form {
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
box-sizing: content-box;
|
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
@ -97,6 +97,8 @@ input {
|
||||||
transition-duration: 0.35s;
|
transition-duration: 0.35s;
|
||||||
border-bottom: 2px solid #2a384a;
|
border-bottom: 2px solid #2a384a;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
width: inherit;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.scopes-input {
|
.scopes-input {
|
||||||
|
|
|
@ -2,28 +2,24 @@
|
||||||
# XXX: This should be removed when elixir's releases get custom command support
|
# XXX: This should be removed when elixir's releases get custom command support
|
||||||
|
|
||||||
detect_flavour() {
|
detect_flavour() {
|
||||||
arch="$(uname -m)"
|
arch="amd64"
|
||||||
if [ "$arch" = "x86_64" ]; then
|
# Special cases
|
||||||
arch="amd64"
|
if grep -qe "VERSION_CODENAME=jammy" /etc/os-release; then
|
||||||
elif [ "$arch" = "aarch64" ]; then
|
echo "$arch-ubuntu-jammy"
|
||||||
arch="arm64"
|
else
|
||||||
else
|
if getconf GNU_LIBC_VERSION >/dev/null; then
|
||||||
echo "Unsupported arch: $arch" >&2
|
libc_postfix=""
|
||||||
exit 1
|
elif [ "$(ldd 2>&1 | head -c 9)" = "musl libc" ]; then
|
||||||
fi
|
libc_postfix="-musl"
|
||||||
|
elif [ "$(find /lib/libc.musl* | wc -l)" ]; then
|
||||||
|
libc_postfix="-musl"
|
||||||
|
else
|
||||||
|
echo "Unsupported libc" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
if getconf GNU_LIBC_VERSION >/dev/null; then
|
echo "$arch$libc_postfix"
|
||||||
libc_postfix=""
|
fi
|
||||||
elif [ "$(ldd 2>&1 | head -c 9)" = "musl libc" ]; then
|
|
||||||
libc_postfix="-musl"
|
|
||||||
elif [ "$(find /lib/libc.musl* | wc -l)" ]; then
|
|
||||||
libc_postfix="-musl"
|
|
||||||
else
|
|
||||||
echo "Unsupported libc" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$arch$libc_postfix"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
detect_branch() {
|
detect_branch() {
|
||||||
|
|
27
test/fixtures/rsa_keys/key_1.pem
vendored
Normal file
27
test/fixtures/rsa_keys/key_1.pem
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEA2gdPJM5bWarGZ6QujfQ296l1yEQohS5fdtnxYQc+RXuS1gqZ
|
||||||
|
R/jVGHG25o4tmwyCLClyREU1CBTOCQBsg+BSehXlxNR9fiB4KaVQW9MMNa2vhHuG
|
||||||
|
f7HLdILiC+SPPTV1Bi8LCpxJowiSpnFPP4BDDeRKib7nOxll9Ln9gEpUueKKabsQ
|
||||||
|
EQKCmEJYhIz/8g5R0Qz+6VjASdejDjTEdZbr/rwyldRRjIklyeZ3lBzB/c8/51wn
|
||||||
|
HT2Dt0r9NiapxYC3oNhbE2A+4FU9pZTqS8yc3KqWZAy74snaRO9QQSednKlOJpXP
|
||||||
|
V3vwWo5CxuSNLttV7zRcrqeYOkIVNF4dQ/bHzQIDAQABAoIBADTCfglnEj4BkF92
|
||||||
|
IHnjdgW6cTEUJUYNMba+CKY1LYF85Mx85hi/gzmWEu95yllxznJHWUpiAPJCrpUJ
|
||||||
|
EDldaDf44pAd53xE+S8CvQ5rZNH8hLOnfKWb7aL1JSRBm9PxAq+LZL2dkkgsg+hZ
|
||||||
|
FRdFv3Q2IT9x/dyUSdLNyyVnV1dfoya/7zOFc7+TwqlofznzrlBgNoAe8Lb4AN/q
|
||||||
|
itormPxskqATiq11XtP4F6eQ556eRgHCBxmktx/rRDl6f9G9dvjRQOA2qZlHQdFq
|
||||||
|
kjOZsrvItL46LdVoLPOdCYG+3HFeKoDUR1NNXEkt66eqmEhLY4MgzGUT1wqXWk7N
|
||||||
|
XowZc9UCgYEA+L5h4PhANiY5Kd+PkRI8zTlJMv8hFqLK17Q0p9eL+mAyOgXjH9so
|
||||||
|
QutJf4wU+h6ESDxH+1tCjCN307uUqT7YnT2zHf3b6GcmA+t6ewxfxOY2nJ82HENq
|
||||||
|
hK1aodnPTvRRRqCGfrx9qUHRTarTzi+2u86zH+KoMHSiuzn4VpQhg4MCgYEA4GOL
|
||||||
|
1tLR9+hyfYuMFo2CtQjp3KpJeGNKEqc33vFD05xJQX+m5THamBv8vzdVlVrMh/7j
|
||||||
|
iV85mlA7HaaP+r5DGwtonw9bqY76lYRgJJprsS5lHcRnXsDmU4Ne8RdB3dHNsT5P
|
||||||
|
n4P6v8y4jaT638iJ/qLt4e8itOBlZwS//VIglm8CgYEA7KXD3RKRlHK9A7drkOs2
|
||||||
|
6VBM8bWEN1LdhGYvilcpFyUZ49XiBVatcS0EGdKdym/qDgc7vElQgJ7ly4y0nGfs
|
||||||
|
EXy3whrYcrxfkG8hcZuOKXeUEWHvSuhgmKWMilr8PfN2t6jVDBIrwzGY/Tk+lPUT
|
||||||
|
9o1qITW0KZVtlI5MU6JOWB0CgYAHwwnETZibxbuoIhqfcRezYXKNgop2EqEuUgB5
|
||||||
|
wsjA2igijuLcDMRt/JHan3RjbTekAKooR1X7w4i39toGJ2y008kzr1lRXTPH1kNp
|
||||||
|
ILpW767pv7B/s5aEDwhKuK47mRVPa0Nf1jXnSpKbu7g943b6ivJFnXsK3LRFQwHN
|
||||||
|
JnkgGwKBgGUleQVd2GPr1dkqLVOF/s2aNB/+h2b1WFWwq0YTnW81OLwAcUVE4p58
|
||||||
|
3GQgz8PCsWbNdTb9yFY5fq0fXgi0+T54FEoZWH09DrOepA433llAwI6sq7egrFdr
|
||||||
|
kKQttZMzs6ST9q/IOF4wgqSnBjjTC06vKSkNAlXJz+LMvIRMeBr0
|
||||||
|
-----END RSA PRIVATE KEY-----
|
27
test/fixtures/rsa_keys/key_2.pem
vendored
Normal file
27
test/fixtures/rsa_keys/key_2.pem
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpQIBAAKCAQEAwu0VqVGRVDW09V3zZ0+08K9HMKivIzIInO0xim3jbfVcg8r1
|
||||||
|
sR7vNLorYAB6TDDlXYAWKx1OxUMZusbOigrpQd+5wy8VdCogDD7qk4bbZ+NjXkuD
|
||||||
|
ETzrQsGWUXe+IdeH8L0Zh0bGjbarCuA0qAeY1TEteGl+Qwo2dsrBUH7yKmWO6Mz9
|
||||||
|
XfPshrIDOGo4QNyVfEBNGq2K9eRrQUHeAPcM2/qu4ZAZRK+VCifDZrF8ZNpoAsnS
|
||||||
|
R2mJDhOBUMvI/ZaxOc2ry4EzwcS4uBaM2wONkGWDaqO6jNAQflaX7vtzOAeJB7Dt
|
||||||
|
VKXUUcZAGN7uI3c2mG5IKGMhTYUtUdrzmqmtZwIDAQABAoIBAQCHBJfTf3dt4AGn
|
||||||
|
T9twfSp06MQj9UPS2i5THI0LONCm8qSReX0zoZzJZgbzaYFM0zWczUMNvDA6vR7O
|
||||||
|
XDTmM2acxW4zv6JZo3Ata0sqwuepDz1eLGnt/8dppxQK/ClL4bH8088h/6k6sgPJ
|
||||||
|
9cEjfpejXHwFgvT9VM6i/BBpRHVTXWuJqwpDtg+bleQNN3L3RapluDd7BGiKoCwQ
|
||||||
|
cCTKd+lxTu9gVJkbRTI/Jn3kV+rnedYxHTxVp5cU1qIabsJWBcdDz25mRHupxQsn
|
||||||
|
JbQR4+ZnRLeAsC6WJZtEJz2KjXgBaYroHbGZY3KcGW95ILqiCJoJJugbW1eABKnN
|
||||||
|
Q5k8XVspAoGBAPzGJBZuX3c0quorhMIpREmGq2vS6VCQwLhH5qayYYH1LiPDfpdq
|
||||||
|
69lOROxZodzLxBgTf5z/a5kBF+eNKvOqfZJeRTxmllxxO1MuJQuRLi/b7BHHLuyN
|
||||||
|
Eea+YwtehA0T0CbD2hydefARNDruor2BLvt/kt6qEoIFiPauTsMfXP39AoGBAMVp
|
||||||
|
8argtnB+vsk5Z7rpQ4b9gF5QxfNbA0Hpg5wUUdYrUjFr50KWt1iowj6AOVp/EYgr
|
||||||
|
xRfvOQdYODDH7R5cjgMbwvtpHo39Zwq7ewaiT1sJXnpGmCDVh+pdTHePC5OOXnxN
|
||||||
|
0USK3M4KjltjVqJo7xPPElgJvCejudD47mtHMaQzAoGBAIFQ/PVc0goyL55NVUXf
|
||||||
|
xse21cv7wtEsvOuKHT361FegD1LMmN7uHGq32BryYBSNSmzmzMqNAYbtQEV9uxOd
|
||||||
|
jVBsWg9kjFgOtcMAQIOCapahdExEEoWCRj49+H3AhN4L3Nl4KQWqqs9efdIIc8lv
|
||||||
|
ZZHU2lZ/u6g5HLDWzASW7wQhAoGAdERPRrqN+HdNWinrA9Q6JxjKL8IWs5rYsksb
|
||||||
|
biMxh5eAEwdf7oHhfd/2duUB4mCQLMjKjawgxEia33AAIS+VnBMPpQ5mJm4l79Y3
|
||||||
|
QNL7Nbyw3gcRtdTM9aT5Ujj3MnJZB5C1PU8jeF4TNZOuBH0UwW/ld+BT5myxFXhm
|
||||||
|
wtvtSq0CgYEA19b0/7il4Em6uiLOmYUuqaUoFhUPqzjaS6OM/lRAw12coWv/8/1P
|
||||||
|
cwaNZHNMW9Me/bNH3zcOTz0lxnYp2BeRehjFYVPRuS1GU7uwqKtlL2wCPptTfAhN
|
||||||
|
aJWIplzUCTg786u+sdNZ0umWRuCLoUpsKTgP/yt4RglzEcfxAuBDljk=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
27
test/fixtures/rsa_keys/key_3.pem
vendored
Normal file
27
test/fixtures/rsa_keys/key_3.pem
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpQIBAAKCAQEA0GvzqZ3r78GLa7guGn+palKRLGru4D4jnriHgfrUAJrdLyZ5
|
||||||
|
9d0zAA4qnS2L6YAMoPPBhBUtIV5e2sn1+rwTClWU3dm3FyBAeqdeIBKN+04AyrUc
|
||||||
|
HXYaZtOPJXCTeytzoSQE359Tq6+xwgoHlUWSWxQF51/z/PDQcUvqFjJqAtdiDchd
|
||||||
|
3CiFRtdjegyxXGnqvPmBix+vEjDytcVydfch+R1Twf6f5EL7a1jFVWNGcratYBEl
|
||||||
|
nqOWKI2fBu/WA8QlrcVW5zmtZo9aJ6IrFddQgQTxPk/mEHgCzv8tbCRI9TxiXeYH
|
||||||
|
YqxZFYBW40xbZQwGRjaYHJlIRYp9+TOynW9OZQIDAQABAoIBAQC97cIMDbdVsyAk
|
||||||
|
N6D70N5H35ofygqJGtdG6o3B6xuKuZVaREvbu4mgQUigF0Nqs5/OhJMSlGGeCOuT
|
||||||
|
oXug1Abd4gNY7++jCWb43tAtlfsAyaJ7FvPZ/SguEBhgW+hp07z5WWN/jSeoSuFI
|
||||||
|
G++xHcczbFm88XncRG8O78kQFTz5/DlQYkFXfbqpuS3BqxnrACpDCUfrUwZNYFIp
|
||||||
|
CUNq21jdifhHwlS0K3PX8A5HdOYeVnVHaE78LGE4oJVHwcokELv+PYqarWZq/a6L
|
||||||
|
vKU3yn2+4pj2WO490iGQaRKVM35vrtjdVxiWEIUiFc3Jg5fKZA3wuHXoF1N1DpPO
|
||||||
|
BO6Att55AoGBAP/nC2szmDcnU5Sh8LDeQbL+FpSBwOmFnmel5uqbjKnDzf9emPQu
|
||||||
|
NFUls1N9OGgyUq08TnmcY/7wLZzcu7Y9XOUURuYtx9nGRs4RmE2VEBhK1r7CkDIx
|
||||||
|
oOb+NtdqnPtQASAxCHszoGCFxpuV7UVoo2SRgc+M4ceX128arvBUtvdrAoGBANCA
|
||||||
|
RuO3eelkXaJoCeogEUVWXZ6QmPeYzbMD4vg2DM0ynUbReyuEIIhn+SR7tehlj5ie
|
||||||
|
4T3ixVdur6k+YUdiFhUYgXaHBJWHoHl1lrU3ZON8n7AeEk9ft6gg4L07ouj78UMZ
|
||||||
|
sArJIlU5mLnW02zbV9XryU39dIgpQREqC0bIOtVvAoGBAORv1JKq6Rt7ALJy6VCJ
|
||||||
|
5y4ogfGp7pLHk8NEpuERYDz/rLllMbbwNAk6cV17L8pb+c/pQMhwohcnQiCALxUc
|
||||||
|
q/tW4X+CqJ+vzu8PZ90Bzu9Qh2iceGpGQTNTBZPA+UeigI7DFqYcTPM9GDE1YiyO
|
||||||
|
nyUcezvSsI4i7s6gjD+/7+DnAoGABm3+QaV1z/m1XX3B2IN2pOG971bcML54kW2s
|
||||||
|
QSVBjc5ixT1OhBAGBM7YAwUBnhILtJQptAPbPBAAwMJYs5/VuH7R9zrArG/LRhOX
|
||||||
|
Oy1jIhTEw+SZgfMcscWZyJwfMPob/Yq8QAjl0yT8jbaPPIsjEUi9I3eOcWh8RjA6
|
||||||
|
ussP7WcCgYEAm3yvJR9z6QGoQQwtDbwjyZPYOSgK9wFS/65aupi6cm/Qk2N1YaLY
|
||||||
|
q2amNrzNsIc9vQwYGEHUwogn4MieHk96V7m2f0Hx9EHCMwizU9EiS6oyiLVowTG6
|
||||||
|
YsBgSzcpnt0Vkgil4CQks5uQoan0tubEUQ5DI79lLnb02n4o46iAYK0=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
27
test/fixtures/rsa_keys/key_4.pem
vendored
Normal file
27
test/fixtures/rsa_keys/key_4.pem
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpQIBAAKCAQEAw6MLRbP/henX2JxwdMkQlskKghBoMyUPu9kZpUQ9yYfIm9I4
|
||||||
|
a3gEfzef75jKLOSf+BkZulvEUGjC+VnkpV3s+OZCSq81Ykv5PHuTqbj8Cn/dEt/g
|
||||||
|
lBXxPcOBKWqa+1cDX6QVIVJsBihLB/1b64H3U96Yu9+knmXvT1Az5MFA2KtSq7HJ
|
||||||
|
O+GJNn0EMI7xwPz/atUGlMLrhzwS4UDpw9CAaRPojplJYl4K1JMCFTgTt3hJILXZ
|
||||||
|
tw1MKTeeyWzNiuQRBQJuCnqfvsBYsasIlHWfqIL/uBzcGHHCIK5ZW9luntJXyLVj
|
||||||
|
zzaF7etIJk1uddM2wnqOOaVyqbssZXGt7Tb9IQIDAQABAoIBAH5QJRUKFK8Xvp9C
|
||||||
|
0nD06NsSTtCPW1e6VCBLGf3Uw7f9DY9d+cOZp/2jooYGNnMp4gdD3ZKvcV8hZNGu
|
||||||
|
Mqx6qmhB8wdZfLRMrU1Z1Is+vqzgxZJMLiouyKXCNwDQreQd2DXGMUZkew62sUsl
|
||||||
|
UFYMge4KyL50tUr4Mb0Z4YePJxk804tcqgw0n+D0lR7ZKhSqoQpoMqEiO+27Yw7E
|
||||||
|
Txj/MKH8f/ZJ6LBLRISOdBOrxonHqqeYWchczykCwojOZc3bIlWZGhg727dFTHDC
|
||||||
|
yrj3/zsZ2hy+TQsucCFY0RljIbacmHvrF/VqfhTIhg98H0F27V/jiPGsdKhptyst
|
||||||
|
E9iQVMkCgYEA42ge4H2Wl42sRh61GOrOgzzr0WZS54bF5skMxiGGnLwnb82rwUBt
|
||||||
|
xw94PRORJbV9l+2fkxbfiW0uzornfN8OBHSB64Pcjzzbl5Qm+eaDOiuTLtakYOWQ
|
||||||
|
/ipGqw8iE4J9iRteZCo8GnMxWbTkYCporTlFDTeYguXmwR4yCXtlCbMCgYEA3DxM
|
||||||
|
7R5HMUWRe64ucdekMh742McS8q/X5jdN9iFGy0M8P1WTyspSlaPDXgjaO4XqpRqg
|
||||||
|
djkL993kCDvOAiDl6Tpdiu1iFcOaRLb19Tj1pm8sKdk6X4d10U9lFri4NVYCmvVi
|
||||||
|
yOahUYFK/k5bA+1o+KU9Pi82H36H3WNeF4evC9sCgYEAs1zNdc04uQKiTZAs0KFr
|
||||||
|
DzI+4aOuYjT35ObQr3mD/h2dkV6MSNmzfF1kPfAv/KkgjXN7+H0DBRbb40bF/MTF
|
||||||
|
/peSXZtcnJGote7Bqzu4Z2o1Ja1ga5jF+uKHaKZ//xleQIUYtzJkw4v18cZulrb8
|
||||||
|
ZxyTrTAbl6sTjWBuoPH1qGcCgYEAsQNahR9X81dKJpGKTQAYvhw8wOfI5/zD2ArN
|
||||||
|
g62dXBRPYUxkPJM/q3xzs6oD1eG+BjQPktYpM3FKLf/7haRxhnLd6qL/uiR8Ywx3
|
||||||
|
RkEg2EP0yDIMA+o5nSFmS8vuaxgVgf0HCBiuwnbcEuhhqRdxzp/pSIjjxI6LnzqV
|
||||||
|
zu3EmQ8CgYEAhq8Uhvw+79tK7q2PCjDbiucA0n/4a3aguuvRoEh7F93Pf6VGZmT+
|
||||||
|
Yld54Cd4P5ATI3r5YdD+JBuvgNMOTVPCaD/WpjbJKnrpNEXtXRQD6LzAXZDNk0sF
|
||||||
|
IO9i4gjhBolRykWn10khoPdxw/34FWBP5SxU1JYk75NQXvI3TD+5xbU=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
27
test/fixtures/rsa_keys/key_5.pem
vendored
Normal file
27
test/fixtures/rsa_keys/key_5.pem
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpgIBAAKCAQEA0jdKtMkgqnEGO3dn4OKxtggfFDzv+ddXToO0cdPXkUgPajCo
|
||||||
|
UGPunz+A1KmkAmLY0Vwk0tkOmKK8GFHek/5zQ+1N2FHBi19fbwlJk7hzh5OiYRhu
|
||||||
|
YZi0d6LsqEMKhDk6NqIeiFmOe2YHgklVvZV0hebvHlHLgzDhYrDltSPe33UZa3MS
|
||||||
|
g2Knf4WQAjLOo2BAb+oyj/UNXeAqaMGcOr6/kAHPcODW2EGhF3H3umFLv7t/Kq5i
|
||||||
|
WPBgarbCGPR5qq9SW5ZIjS3Sz0dl105Grw8wU23CC/2IBZ5vNiu+bkmLEoh/KpX2
|
||||||
|
YBILoLmwtVX0Qxc15CrpOi12p+/4pLR8kuEowQIDAQABAoIBAQDMDQ3AJMdHisSQ
|
||||||
|
7pvvyDzWRFXesDQE4YmG1gNOxmImTLthyW9n8UjMXbjxNOXVxxtNRdMcs8MeWECa
|
||||||
|
nsWeBEzgr7VzeBCV9/LL9kjsUgwamyzwcOWcaL0ssAJmZgUMSfx+0akvkzbiAyzg
|
||||||
|
w8ytZSihXYPYe28/ni/5O1sOFI6feenOnJ9NSmVUA24c9TTJGNQs7XRUMZ8f9wt6
|
||||||
|
KwRmYeNDKyqH7NvLmmKoDp6m7bMDQxWArVTAoRWTVApnj35iLQtmSi8DBdw6xSzQ
|
||||||
|
fKpUe/B4iQmMNxUW7KmolOvCIS5wcYZJE+/j7xshA2GGnOpx4aC+N+w2GSX4Bz/q
|
||||||
|
OnYSpGUBAoGBAOwnSeg17xlZqmd86qdiCxg0hRtAjwrd7btYq6nkK+t9woXgcV99
|
||||||
|
FBS3nLbk/SIdXCW8vHFJTmld60j2q2kdestYBdHznwNZJ4Ee8JhamzcC64wY7O0x
|
||||||
|
RameO/6uoKS4C3VF+Zc9CCPfZOqYujkGvSqbTjFZWuFtDp0GHDk+qEIRAoGBAOPh
|
||||||
|
+PCB2QkGgiujSPmuCT5PTuNylAug3D4ZdMRKpQb9Rnzlia1Rpdrihq+PvB2vwa+S
|
||||||
|
mB6dgb0E7M2AyEMVu5buris0mVpRdmEeLCXR8mYJ48kOslIGArEStXDetfbRaXdK
|
||||||
|
7vf4APq2d78AQYldU2fYlo754Dh/3MZIguzpqMuxAoGBAIDJqG/AQiYkFV+c62ff
|
||||||
|
e0d3FQRYv+ngQE9Eu1HKwv0Jt7VFQu8din8F56yC013wfxmBhY+Ot/mUo8VF6RNJ
|
||||||
|
ZXdSCNKINzcfPwEW+4VLHIzyxbzAty1gCqrHRdbOK4PJb05EnCqTuUW/Bg0+v4hs
|
||||||
|
GWwMCKe3IG4CCM8vzuKVPjPRAoGBANYCQtJDb3q9ZQPsTb1FxyKAQprx4Lzm7c9Y
|
||||||
|
AsPRQhhFRaxHuLtPQU5FjK1VdBoBFAl5x2iBDPVhqa348pml0E0Xi/PBav9aH61n
|
||||||
|
M5i1CUrwoL4SEj9bq61133XHgeXwlnZUpgW0H99T+zMh32pMfea5jfNqETueQMzq
|
||||||
|
DiLF8SKRAoGBAOFlU0kRZmAx3Y4rhygp1ydPBt5+zfDaGINRWEN7QWjhX2QQan3C
|
||||||
|
SnXZlP3POXLessKxdCpBDq/RqVQhLea6KJMfP3F0YbohfWHt96WjiriJ0d0ZYVhu
|
||||||
|
34aUM2UGGG0Kia9OVvftESBaXk02vrY9zU3LAVAv0eLgIADm1kpj85v7
|
||||||
|
-----END RSA PRIVATE KEY-----
|
196
test/pleroma/akkoma/frontend_setting_profile_test.exs
Normal file
196
test/pleroma/akkoma/frontend_setting_profile_test.exs
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
defmodule Pleroma.Akkoma.FrontendSettingsProfileTest do
|
||||||
|
use Pleroma.DataCase, async: true
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
alias Pleroma.Akkoma.FrontendSettingsProfile
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "changeset/2" do
|
||||||
|
test "valid" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = "test"
|
||||||
|
settings = %{"test" => "test"}
|
||||||
|
struct = %FrontendSettingsProfile{}
|
||||||
|
|
||||||
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: settings,
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{valid?: true} = FrontendSettingsProfile.changeset(struct, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when settings is too long" do
|
||||||
|
clear_config([:instance, :max_frontend_settings_json_chars], 10)
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = "test"
|
||||||
|
settings = %{"verylong" => "verylongoops"}
|
||||||
|
struct = %FrontendSettingsProfile{}
|
||||||
|
|
||||||
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: settings,
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{valid?: false, errors: [settings: {"is too long", _}]} =
|
||||||
|
FrontendSettingsProfile.changeset(struct, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when frontend name is too short" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = ""
|
||||||
|
profile_name = "test"
|
||||||
|
settings = %{"test" => "test"}
|
||||||
|
struct = %FrontendSettingsProfile{}
|
||||||
|
|
||||||
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: settings,
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{valid?: false, errors: [frontend_name: {"can't be blank", _}]} =
|
||||||
|
FrontendSettingsProfile.changeset(struct, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when profile name is too short" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = ""
|
||||||
|
settings = %{"test" => "test"}
|
||||||
|
struct = %FrontendSettingsProfile{}
|
||||||
|
|
||||||
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: settings,
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{valid?: false, errors: [profile_name: {"can't be blank", _}]} =
|
||||||
|
FrontendSettingsProfile.changeset(struct, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when version is negative" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = "test"
|
||||||
|
settings = %{"test" => "test"}
|
||||||
|
struct = %FrontendSettingsProfile{}
|
||||||
|
|
||||||
|
attrs = %{
|
||||||
|
user_id: user.id,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: settings,
|
||||||
|
version: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{valid?: false, errors: [version: {"must be greater than %{number}", _}]} =
|
||||||
|
FrontendSettingsProfile.changeset(struct, attrs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "create_or_update/2" do
|
||||||
|
test "it should create a new record" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = "test"
|
||||||
|
settings = %{"test" => "test"}
|
||||||
|
|
||||||
|
assert {:ok, %FrontendSettingsProfile{}} =
|
||||||
|
FrontendSettingsProfile.create_or_update(
|
||||||
|
user,
|
||||||
|
frontend_name,
|
||||||
|
profile_name,
|
||||||
|
settings,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it should update a record" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = "test"
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
settings = %{"test" => "test2"}
|
||||||
|
|
||||||
|
assert {:ok, %FrontendSettingsProfile{settings: ^settings}} =
|
||||||
|
FrontendSettingsProfile.create_or_update(
|
||||||
|
user,
|
||||||
|
frontend_name,
|
||||||
|
profile_name,
|
||||||
|
settings,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "get_all_by_user_and_frontend_name/2" do
|
||||||
|
test "it should return all records" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: "profileA",
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: "profileB",
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
assert [%FrontendSettingsProfile{profile_name: "profileA"}, %{profile_name: "profileB"}] =
|
||||||
|
FrontendSettingsProfile.get_all_by_user_and_frontend_name(user, frontend_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "get_by_user_and_frontend_name_and_profile_name/3" do
|
||||||
|
test "it should return a record" do
|
||||||
|
user = insert(:user)
|
||||||
|
frontend_name = "test"
|
||||||
|
profile_name = "profileA"
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: frontend_name,
|
||||||
|
profile_name: profile_name,
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
assert %FrontendSettingsProfile{profile_name: "profileA"} =
|
||||||
|
FrontendSettingsProfile.get_by_user_and_frontend_name_and_profile_name(
|
||||||
|
user,
|
||||||
|
frontend_name,
|
||||||
|
profile_name
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -47,7 +47,7 @@ defmodule Pleroma.Search.MeilisearchTest do
|
||||||
Jason.decode!(body)
|
Jason.decode!(body)
|
||||||
)
|
)
|
||||||
|
|
||||||
json(%{updateId: 1})
|
json(%{taskUid: 1})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
|
@ -100,11 +100,11 @@ defmodule Pleroma.Search.MeilisearchTest do
|
||||||
Jason.decode!(body)
|
Jason.decode!(body)
|
||||||
)
|
)
|
||||||
|
|
||||||
json(%{updateId: 1})
|
json(%{taskUid: 1})
|
||||||
|
|
||||||
%{method: :delete, url: "http://127.0.0.1:7700/indexes/objects/documents/" <> id} ->
|
%{method: :delete, url: "http://127.0.0.1:7700/indexes/objects/documents/" <> id} ->
|
||||||
assert String.length(id) > 1
|
assert String.length(id) > 1
|
||||||
json(%{updateId: 2})
|
json(%{taskUid: 2})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
|
|
|
@ -65,6 +65,14 @@ defmodule Pleroma.UserSearchTest do
|
||||||
assert found_user.id == user.id
|
assert found_user.id == user.id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "excludes deactivated users from results" do
|
||||||
|
user = insert(:user, %{nickname: "john t1000"})
|
||||||
|
insert(:user, %{is_active: false, nickname: "john t800"})
|
||||||
|
|
||||||
|
[found_user] = User.search("john")
|
||||||
|
assert found_user.id == user.id
|
||||||
|
end
|
||||||
|
|
||||||
# Note: as in Mastodon, `is_discoverable` doesn't anyhow relate to user searchability
|
# Note: as in Mastodon, `is_discoverable` doesn't anyhow relate to user searchability
|
||||||
test "includes non-discoverable users in results" do
|
test "includes non-discoverable users in results" do
|
||||||
insert(:user, %{nickname: "john 3000", is_discoverable: false})
|
insert(:user, %{nickname: "john 3000", is_discoverable: false})
|
||||||
|
|
|
@ -620,15 +620,15 @@ defmodule Pleroma.UserTest do
|
||||||
assert changeset.valid?
|
assert changeset.valid?
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it sets the password_hash, ap_id and PEM key" do
|
test "it sets the password_hash, ap_id, private key and followers collection address" do
|
||||||
changeset = User.register_changeset(%User{}, @full_user_data)
|
changeset = User.register_changeset(%User{}, @full_user_data)
|
||||||
|
|
||||||
assert changeset.valid?
|
assert changeset.valid?
|
||||||
|
|
||||||
assert is_binary(changeset.changes[:password_hash])
|
assert is_binary(changeset.changes[:password_hash])
|
||||||
|
assert is_binary(changeset.changes[:keys])
|
||||||
assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname})
|
assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname})
|
||||||
assert is_binary(changeset.changes[:keys])
|
assert is_binary(changeset.changes[:keys])
|
||||||
|
|
||||||
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
|
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -737,6 +737,13 @@ defmodule Pleroma.UserTest do
|
||||||
freshed_user = refresh_record(user)
|
freshed_user = refresh_record(user)
|
||||||
assert freshed_user == fetched_user
|
assert freshed_user == fetched_user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "gets an existing user by nickname starting with http" do
|
||||||
|
user = insert(:user, nickname: "httpssome")
|
||||||
|
{:ok, fetched_user} = User.get_or_fetch("httpssome")
|
||||||
|
|
||||||
|
assert user == fetched_user
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "get_or_fetch/1 remote users with tld, while BE is runned on subdomain" do
|
describe "get_or_fetch/1 remote users with tld, while BE is runned on subdomain" do
|
||||||
|
@ -2130,21 +2137,6 @@ defmodule Pleroma.UserTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "ensure_keys_present" do
|
|
||||||
test "it creates keys for a user and stores them in info" do
|
|
||||||
user = insert(:user)
|
|
||||||
refute is_binary(user.keys)
|
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
assert is_binary(user.keys)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "it doesn't create keys if there already are some" do
|
|
||||||
user = insert(:user, keys: "xxx")
|
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
assert user.keys == "xxx"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "get_ap_ids_by_nicknames" do
|
describe "get_ap_ids_by_nicknames" do
|
||||||
test "it returns a list of AP ids for a given set of nicknames" do
|
test "it returns a list of AP ids for a given set of nicknames" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
|
@ -81,4 +81,18 @@ defmodule Pleroma.Web.ActivityPub.ObjectViewTest do
|
||||||
assert result["object"] == object.data["id"]
|
assert result["object"] == object.data["id"]
|
||||||
assert result["type"] == "Announce"
|
assert result["type"] == "Announce"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "renders an undo announce activity" do
|
||||||
|
note = insert(:note_activity)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, announce} = CommonAPI.repeat(note.id, user)
|
||||||
|
{:ok, undo} = CommonAPI.unrepeat(note.id, user)
|
||||||
|
|
||||||
|
result = ObjectView.render("object.json", %{object: undo})
|
||||||
|
|
||||||
|
assert result["id"] == undo.data["id"]
|
||||||
|
assert result["object"] == announce.data["id"]
|
||||||
|
assert result["type"] == "Undo"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
||||||
|
|
||||||
test "Renders a user, including the public key" do
|
test "Renders a user, including the public key" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
|
|
||||||
result = UserView.render("user.json", %{user: user})
|
result = UserView.render("user.json", %{user: user})
|
||||||
|
|
||||||
|
@ -55,7 +54,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
||||||
|
|
||||||
test "Does not add an avatar image if the user hasn't set one" do
|
test "Does not add an avatar image if the user hasn't set one" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
|
|
||||||
result = UserView.render("user.json", %{user: user})
|
result = UserView.render("user.json", %{user: user})
|
||||||
refute result["icon"]
|
refute result["icon"]
|
||||||
|
@ -67,8 +65,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
||||||
banner: %{"url" => [%{"href" => "https://somebanner"}]}
|
banner: %{"url" => [%{"href" => "https://somebanner"}]}
|
||||||
)
|
)
|
||||||
|
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
|
|
||||||
result = UserView.render("user.json", %{user: user})
|
result = UserView.render("user.json", %{user: user})
|
||||||
assert result["icon"]["url"] == "https://someurl"
|
assert result["icon"]["url"] == "https://someurl"
|
||||||
assert result["image"]["url"] == "https://somebanner"
|
assert result["image"]["url"] == "https://somebanner"
|
||||||
|
@ -89,7 +85,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
||||||
describe "endpoints" do
|
describe "endpoints" do
|
||||||
test "local users have a usable endpoints structure" do
|
test "local users have a usable endpoints structure" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
|
|
||||||
result = UserView.render("user.json", %{user: user})
|
result = UserView.render("user.json", %{user: user})
|
||||||
|
|
||||||
|
@ -105,7 +100,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
||||||
|
|
||||||
test "remote users have an empty endpoints structure" do
|
test "remote users have an empty endpoints structure" do
|
||||||
user = insert(:user, local: false)
|
user = insert(:user, local: false)
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
|
|
||||||
result = UserView.render("user.json", %{user: user})
|
result = UserView.render("user.json", %{user: user})
|
||||||
|
|
||||||
|
@ -115,7 +109,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
|
||||||
|
|
||||||
test "instance users do not expose oAuth endpoints" do
|
test "instance users do not expose oAuth endpoints" do
|
||||||
user = insert(:user, nickname: nil, local: true)
|
user = insert(:user, nickname: nil, local: true)
|
||||||
{:ok, user} = User.ensure_keys_present(user)
|
|
||||||
|
|
||||||
result = UserView.render("user.json", %{user: user})
|
result = UserView.render("user.json", %{user: user})
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
defmodule Pleroma.Web.AkkomaAPI.FrontendSettingsControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
alias Pleroma.Akkoma.FrontendSettingsProfile
|
||||||
|
|
||||||
|
describe "GET /api/v1/akkoma/frontend_settings/:frontend_name" do
|
||||||
|
test "it returns a list of profiles" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["read"])
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile, user: user, frontend_name: "test", profile_name: "test1")
|
||||||
|
insert(:frontend_setting_profile, user: user, frontend_name: "test", profile_name: "test2")
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/akkoma/frontend_settings/test")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response == [
|
||||||
|
%{"name" => "test1", "version" => 1},
|
||||||
|
%{"name" => "test2", "version" => 1}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name" do
|
||||||
|
test "it returns 404 if not found" do
|
||||||
|
%{conn: conn} = oauth_access(["read"])
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/akkoma/frontend_settings/unknown_frontend/unknown_profile")
|
||||||
|
|> json_response_and_validate_schema(404)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns 200 if found" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["read"])
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: "test",
|
||||||
|
profile_name: "test1",
|
||||||
|
settings: %{"test" => "test"}
|
||||||
|
)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/akkoma/frontend_settings/test/test1")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response == %{"settings" => %{"test" => "test"}, "version" => 1}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "PUT /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name" do
|
||||||
|
test "puts a config" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["write"])
|
||||||
|
settings = %{"test" => "test2"}
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/v1/akkoma/frontend_settings/test/test1", %{
|
||||||
|
"settings" => settings,
|
||||||
|
"version" => 1
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response == settings
|
||||||
|
|
||||||
|
assert %FrontendSettingsProfile{settings: ^settings} =
|
||||||
|
FrontendSettingsProfile.get_by_user_and_frontend_name_and_profile_name(
|
||||||
|
user,
|
||||||
|
"test",
|
||||||
|
"test1"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "refuses to overwrite a newer config" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["write"])
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: "test",
|
||||||
|
profile_name: "test1",
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 2
|
||||||
|
)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put("/api/v1/akkoma/frontend_settings/test/test1", %{
|
||||||
|
"settings" => %{"test" => "test2"},
|
||||||
|
"version" => 1
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(422)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE /api/v1/akkoma/frontend_settings/:frontend_name/:profile_name" do
|
||||||
|
test "deletes a config" do
|
||||||
|
%{conn: conn, user: user} = oauth_access(["write"])
|
||||||
|
|
||||||
|
insert(:frontend_setting_profile,
|
||||||
|
user: user,
|
||||||
|
frontend_name: "test",
|
||||||
|
profile_name: "test1",
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 2
|
||||||
|
)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> delete("/api/v1/akkoma/frontend_settings/test/test1")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert FrontendSettingsProfile.get_by_user_and_frontend_name_and_profile_name(
|
||||||
|
user,
|
||||||
|
"test",
|
||||||
|
"test1"
|
||||||
|
) == nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -39,6 +39,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
|
||||||
"actor" => user.ap_id,
|
"actor" => user.ap_id,
|
||||||
"tag" => [],
|
"tag" => [],
|
||||||
"id" => "https://pleroma.gov/objects/whatever",
|
"id" => "https://pleroma.gov/objects/whatever",
|
||||||
|
"summary" => "",
|
||||||
"content" => "pleroma in a nutshell"
|
"content" => "pleroma in a nutshell"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -54,6 +55,36 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
|
||||||
] == result
|
] == result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it uses summary as description if post has one" do
|
||||||
|
user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "HI"})
|
||||||
|
|
||||||
|
note =
|
||||||
|
insert(:note, %{
|
||||||
|
data: %{
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"tag" => [],
|
||||||
|
"id" => "https://pleroma.gov/objects/whatever",
|
||||||
|
"summary" => "Public service announcement on caffeine consumption",
|
||||||
|
"content" => "cofe"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
result = TwitterCard.build_tags(%{object: note, user: user, activity_id: activity.id})
|
||||||
|
|
||||||
|
assert [
|
||||||
|
{:meta, [property: "twitter:title", content: Utils.user_name_string(user)], []},
|
||||||
|
{:meta,
|
||||||
|
[
|
||||||
|
property: "twitter:description",
|
||||||
|
content: "Public service announcement on caffeine consumption"
|
||||||
|
], []},
|
||||||
|
{:meta, [property: "twitter:image", content: "http://localhost:4001/images/avi.png"],
|
||||||
|
[]},
|
||||||
|
{:meta, [property: "twitter:card", content: "summary"], []}
|
||||||
|
] == result
|
||||||
|
end
|
||||||
|
|
||||||
test "it renders avatar not attachment if post is nsfw and unfurl_nsfw is disabled" do
|
test "it renders avatar not attachment if post is nsfw and unfurl_nsfw is disabled" do
|
||||||
clear_config([Pleroma.Web.Metadata, :unfurl_nsfw], false)
|
clear_config([Pleroma.Web.Metadata, :unfurl_nsfw], false)
|
||||||
user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
|
user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
|
||||||
|
@ -65,6 +96,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
|
||||||
"actor" => user.ap_id,
|
"actor" => user.ap_id,
|
||||||
"tag" => [],
|
"tag" => [],
|
||||||
"id" => "https://pleroma.gov/objects/whatever",
|
"id" => "https://pleroma.gov/objects/whatever",
|
||||||
|
"summary" => "",
|
||||||
"content" => "pleroma in a nutshell",
|
"content" => "pleroma in a nutshell",
|
||||||
"sensitive" => true,
|
"sensitive" => true,
|
||||||
"attachment" => [
|
"attachment" => [
|
||||||
|
@ -109,6 +141,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCardTest do
|
||||||
"actor" => user.ap_id,
|
"actor" => user.ap_id,
|
||||||
"tag" => [],
|
"tag" => [],
|
||||||
"id" => "https://pleroma.gov/objects/whatever",
|
"id" => "https://pleroma.gov/objects/whatever",
|
||||||
|
"summary" => "",
|
||||||
"content" => "pleroma in a nutshell",
|
"content" => "pleroma in a nutshell",
|
||||||
"attachment" => [
|
"attachment" => [
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.Metadata.UtilsTest do
|
||||||
alias Pleroma.Web.Metadata.Utils
|
alias Pleroma.Web.Metadata.Utils
|
||||||
|
|
||||||
describe "scrub_html_and_truncate/1" do
|
describe "scrub_html_and_truncate/1" do
|
||||||
test "it returns text without encode HTML" do
|
test "it returns content text without encode HTML if summary is nil" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
note =
|
note =
|
||||||
|
@ -16,6 +16,7 @@ defmodule Pleroma.Web.Metadata.UtilsTest do
|
||||||
data: %{
|
data: %{
|
||||||
"actor" => user.ap_id,
|
"actor" => user.ap_id,
|
||||||
"id" => "https://pleroma.gov/objects/whatever",
|
"id" => "https://pleroma.gov/objects/whatever",
|
||||||
|
"summary" => nil,
|
||||||
"content" => "Pleroma's really cool!"
|
"content" => "Pleroma's really cool!"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -23,6 +24,39 @@ defmodule Pleroma.Web.Metadata.UtilsTest do
|
||||||
assert Utils.scrub_html_and_truncate(note) == "Pleroma's really cool!"
|
assert Utils.scrub_html_and_truncate(note) == "Pleroma's really cool!"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it returns context text without encode HTML if summary is empty" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
note =
|
||||||
|
insert(:note, %{
|
||||||
|
data: %{
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"id" => "https://pleroma.gov/objects/whatever",
|
||||||
|
"summary" => "",
|
||||||
|
"content" => "Pleroma's really cool!"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
assert Utils.scrub_html_and_truncate(note) == "Pleroma's really cool!"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns summary text without encode HTML if summary is filled" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
note =
|
||||||
|
insert(:note, %{
|
||||||
|
data: %{
|
||||||
|
"actor" => user.ap_id,
|
||||||
|
"id" => "https://pleroma.gov/objects/whatever",
|
||||||
|
"summary" => "Public service announcement on caffeine consumption",
|
||||||
|
"content" => "cofe"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
assert Utils.scrub_html_and_truncate(note) ==
|
||||||
|
"Public service announcement on caffeine consumption"
|
||||||
|
end
|
||||||
|
|
||||||
test "it does not return old content after editing" do
|
test "it does not return old content after editing" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ defmodule Pleroma.Web.PleromaAPI.BackupControllerTest do
|
||||||
setup do
|
setup do
|
||||||
clear_config([Pleroma.Upload, :uploader])
|
clear_config([Pleroma.Upload, :uploader])
|
||||||
clear_config([Backup, :limit_days])
|
clear_config([Backup, :limit_days])
|
||||||
oauth_access(["read:accounts"])
|
oauth_access(["read:backups"])
|
||||||
end
|
end
|
||||||
|
|
||||||
test "GET /api/v1/pleroma/backups", %{user: user, conn: conn} do
|
test "GET /api/v1/pleroma/backups", %{user: user, conn: conn} do
|
||||||
|
@ -85,7 +85,7 @@ defmodule Pleroma.Web.PleromaAPI.BackupControllerTest do
|
||||||
|
|
||||||
test "Backup without email address" do
|
test "Backup without email address" do
|
||||||
user = Pleroma.Factory.insert(:user, email: nil)
|
user = Pleroma.Factory.insert(:user, email: nil)
|
||||||
%{conn: conn} = oauth_access(["read:accounts"], user: user)
|
%{conn: conn} = oauth_access(["read:backups"], user: user)
|
||||||
|
|
||||||
assert is_nil(user.email)
|
assert is_nil(user.email)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,15 @@ defmodule Pleroma.Factory do
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@rsa_keys [
|
||||||
|
"test/fixtures/rsa_keys/key_1.pem",
|
||||||
|
"test/fixtures/rsa_keys/key_2.pem",
|
||||||
|
"test/fixtures/rsa_keys/key_3.pem",
|
||||||
|
"test/fixtures/rsa_keys/key_4.pem",
|
||||||
|
"test/fixtures/rsa_keys/key_5.pem"
|
||||||
|
]
|
||||||
|
|> Enum.map(&File.read!/1)
|
||||||
|
|
||||||
def participation_factory do
|
def participation_factory do
|
||||||
conversation = insert(:conversation)
|
conversation = insert(:conversation)
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
@ -28,6 +37,8 @@ defmodule Pleroma.Factory do
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_factory(attrs \\ %{}) do
|
def user_factory(attrs \\ %{}) do
|
||||||
|
pem = Enum.random(@rsa_keys)
|
||||||
|
|
||||||
user = %User{
|
user = %User{
|
||||||
name: sequence(:name, &"Test テスト User #{&1}"),
|
name: sequence(:name, &"Test テスト User #{&1}"),
|
||||||
email: sequence(:email, &"user#{&1}@example.com"),
|
email: sequence(:email, &"user#{&1}@example.com"),
|
||||||
|
@ -39,7 +50,8 @@ defmodule Pleroma.Factory do
|
||||||
last_refreshed_at: NaiveDateTime.utc_now(),
|
last_refreshed_at: NaiveDateTime.utc_now(),
|
||||||
notification_settings: %Pleroma.User.NotificationSetting{},
|
notification_settings: %Pleroma.User.NotificationSetting{},
|
||||||
multi_factor_authentication_settings: %Pleroma.MFA.Settings{},
|
multi_factor_authentication_settings: %Pleroma.MFA.Settings{},
|
||||||
ap_enabled: true
|
ap_enabled: true,
|
||||||
|
keys: pem
|
||||||
}
|
}
|
||||||
|
|
||||||
urls =
|
urls =
|
||||||
|
@ -651,4 +663,15 @@ defmodule Pleroma.Factory do
|
||||||
|> Map.merge(params)
|
|> Map.merge(params)
|
||||||
|> Pleroma.Announcement.add_rendered_properties()
|
|> Pleroma.Announcement.add_rendered_properties()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def frontend_setting_profile_factory(params \\ %{}) do
|
||||||
|
%Pleroma.Akkoma.FrontendSettingsProfile{
|
||||||
|
user: build(:user),
|
||||||
|
frontend_name: "akkoma-fe",
|
||||||
|
profile_name: "default",
|
||||||
|
settings: %{"test" => "test"},
|
||||||
|
version: 1
|
||||||
|
}
|
||||||
|
|> Map.merge(params)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue