Merge branch 'develop' into stable

This commit is contained in:
FloatingGhost 2022-10-08 12:12:52 +01:00
commit 5e7be063c7
50 changed files with 1229 additions and 207 deletions

View file

@ -14,6 +14,14 @@ variables:
- stable
- refs/tags/v*
- refs/tags/stable-*
- &on-stable
when:
event:
- push
- tag
branch:
- stable
- refs/tags/stable-*
- &on-point-release
when:
event:
@ -110,6 +118,8 @@ pipeline:
- export SOURCE=akkoma-ubuntu-jammy.zip
- export DEST=scaleway:akkoma-updates/$${CI_COMMIT_TAG:-"$CI_COMMIT_BRANCH"}/akkoma-ubuntu-jammy.zip
- /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:
image: hexpm/elixir:1.13.4-erlang-24.3.4.5-debian-bullseye-20220801
@ -142,7 +152,7 @@ pipeline:
# Canonical amd64-musl
musl:
image: hexpm/elixir:1.13.4-erlang-24.3.4.5-alpine-3.15.6
<<: *on-release
<<: *on-stable
environment:
MIX_ENV: prod
commands:
@ -157,7 +167,7 @@ pipeline:
release-musl:
image: akkoma/releaser
<<: *on-release
<<: *on-stable
secrets: *scw-secrets
commands:
- export SOURCE=akkoma-amd64-musl.zip
@ -172,8 +182,12 @@ pipeline:
- SCW_DEFAULT_ORGANIZATION_ID
environment:
CI: "true"
image: akkoma/docs-builder
image: python:3.10-slim
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
- cd docs
- pip install -r requirements.txt

View file

@ -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/).
## 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
### Added
@ -18,6 +33,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed
- 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
- Enable remote users to interact with posts
### Fixed
- 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.
- ubuntu-jammy is built for... well, ubuntu 22 (LTS)
- amd64-musl is built for alpine 3.16
- Enable remote users to interact with posts
### Fixed
- Updated mastoFE path, for the newer version

View file

@ -48,6 +48,7 @@
config :pleroma, Pleroma.Repo,
telemetry_event: [Pleroma.Repo.Instrumenter],
queue_target: 20_000,
migration_lock: nil
config :pleroma, Pleroma.Captcha,
@ -260,7 +261,8 @@
password_reset_token_validity: 60 * 60 * 24,
profile_directory: true,
privileged_staff: false,
local_bubble: []
local_bubble: [],
max_frontend_settings_json_chars: 100_000
config :pleroma, :welcome,
direct_message: [
@ -752,9 +754,9 @@
},
"soapbox-fe" => %{
"name" => "soapbox-fe",
"git" => "https://gitlab.com/soapbox-pub/soapbox-fe",
"git" => "https://gitlab.com/soapbox-pub/soapbox",
"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",
"build_dir" => "static"
},

View file

@ -14,6 +14,10 @@ su akkoma -s $SHELL -lc "./bin/pleroma_ctl update"
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)
1. Go to the working directory of Akkoma (default is `/opt/akkoma`)

View file

@ -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
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`
```json

View file

@ -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.
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/
HiddenServicePort 80 127.0.0.1:8099
HiddenServiceVersion 3 # Remove if Tor version is below 0.3 ( tor --version )
HTTPTunnelPort 9080
```
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`
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,
run the following:

View file

@ -141,8 +141,7 @@ You then need to set the URL and authentication credentials if relevant.
### 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
have to do it one time, but it might take a while, depending on the amount of posts your instance has seen.
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.
The sequence of actions is as follows:

View file

@ -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.
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?
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.
Remember, what you see is only the frontend part of Mastodon, the backend is still Akkoma.

View file

@ -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:
| distribution | flavour |
| ------------- | ------------ |
| debian stable | amd64 |
| ubuntu focal | amd64 |
| ubuntu jammy | ubuntu-jammy |
| alpine | amd64-musl |
| distribution | flavour | available branches |
| ------------- | ------------------ | ------------------- |
| debian stable | amd64 | develop, stable |
| ubuntu focal | amd64 | develop, stable |
| ubuntu jammy | amd64-ubuntu-jammy | develop, stable |
| alpine | amd64-musl | stable |
Other similar distributions will _probably_ work, but if it is not listed above, there is no official
support.

View 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

View file

@ -1,13 +1,13 @@
# emoji-test.txt
# Date: 2021-08-26, 17:22:23 GMT
# © 2021 Unicode®, Inc.
# Date: 2022-08-12, 20:24:39 GMT
# © 2022 Unicode®, Inc.
# 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
# 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.
# Format: code points; status # emoji name
@ -92,6 +92,7 @@
1F62C ; fully-qualified # 😬 E1.0 grimacing face
1F62E 200D 1F4A8 ; fully-qualified # 😮‍💨 E13.1 face exhaling
1F925 ; fully-qualified # 🤥 E3.0 lying face
1FAE8 ; fully-qualified # 🫨 E15.0 shaking face
# subgroup: face-sleepy
1F60C ; fully-qualified # 😌 E0.6 relieved face
@ -155,7 +156,7 @@
# subgroup: face-negative
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
1F92C ; fully-qualified # 🤬 E5.0 face with symbols on mouth
1F608 ; fully-qualified # 😈 E1.0 smiling face with horns
@ -190,8 +191,7 @@
1F649 ; fully-qualified # 🙉 E0.6 hear-no-evil monkey
1F64A ; fully-qualified # 🙊 E0.6 speak-no-evil monkey
# subgroup: emotion
1F48B ; fully-qualified # 💋 E0.6 kiss mark
# subgroup: heart
1F48C ; fully-qualified # 💌 E0.6 love letter
1F498 ; fully-qualified # 💘 E0.6 heart with arrow
1F49D ; fully-qualified # 💝 E0.6 heart with ribbon
@ -210,14 +210,20 @@
2764 200D 1FA79 ; unqualified # ❤‍🩹 E13.1 mending heart
2764 FE0F ; fully-qualified # ❤️ E0.6 red heart
2764 ; unqualified # ❤ E0.6 red heart
1FA77 ; fully-qualified # 🩷 E15.0 pink heart
1F9E1 ; fully-qualified # 🧡 E5.0 orange heart
1F49B ; fully-qualified # 💛 E0.6 yellow heart
1F49A ; fully-qualified # 💚 E0.6 green heart
1F499 ; fully-qualified # 💙 E0.6 blue heart
1FA75 ; fully-qualified # 🩵 E15.0 light blue heart
1F49C ; fully-qualified # 💜 E0.6 purple heart
1F90E ; fully-qualified # 🤎 E12.0 brown heart
1F5A4 ; fully-qualified # 🖤 E3.0 black heart
1FA76 ; fully-qualified # 🩶 E15.0 grey heart
1F90D ; fully-qualified # 🤍 E12.0 white heart
# subgroup: emotion
1F48B ; fully-qualified # 💋 E0.6 kiss mark
1F4AF ; fully-qualified # 💯 E0.6 hundred points
1F4A2 ; fully-qualified # 💢 E0.6 anger symbol
1F4A5 ; fully-qualified # 💥 E0.6 collision
@ -226,21 +232,20 @@
1F4A8 ; fully-qualified # 💨 E0.6 dashing away
1F573 FE0F ; fully-qualified # 🕳️ E0.7 hole
1F573 ; unqualified # 🕳 E0.7 hole
1F4A3 ; fully-qualified # 💣 E0.6 bomb
1F4AC ; fully-qualified # 💬 E0.6 speech balloon
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 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
1F5E8 FE0F ; fully-qualified # 🗨️ E2.0 left speech bubble
1F5E8 ; unqualified # 🗨 E2.0 left speech bubble
1F5EF FE0F ; fully-qualified # 🗯️ E0.7 right anger bubble
1F5EF ; unqualified # 🗯 E0.7 right anger bubble
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: 177 w/o modifiers
# Smileys & Emotion subtotal: 180
# Smileys & Emotion subtotal: 180 w/o modifiers
# group: People & Body
@ -300,6 +305,18 @@
1FAF4 1F3FD ; fully-qualified # 🫴🏽 E14.0 palm up hand: medium skin tone
1FAF4 1F3FE ; fully-qualified # 🫴🏾 E14.0 palm up hand: medium-dark skin tone
1FAF4 1F3FF ; fully-qualified # 🫴🏿 E14.0 palm up hand: dark skin tone
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
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 1F3FF ; fully-qualified # 🤲🏿 E5.0 palms up together: dark skin tone
1F91D ; fully-qualified # 🤝 E3.0 handshake
1F91D 1F3FB ; fully-qualified # 🤝🏻 E3.0 handshake: light skin tone
1F91D 1F3FC ; fully-qualified # 🤝🏼 E3.0 handshake: medium-light skin tone
1F91D 1F3FD ; fully-qualified # 🤝🏽 E3.0 handshake: medium skin tone
1F91D 1F3FE ; fully-qualified # 🤝🏾 E3.0 handshake: medium-dark skin tone
1F91D 1F3FF ; fully-qualified # 🤝🏿 E3.0 handshake: dark skin tone
1F91D 1F3FB ; fully-qualified # 🤝🏻 E14.0 handshake: light skin tone
1F91D 1F3FC ; fully-qualified # 🤝🏼 E14.0 handshake: medium-light skin tone
1F91D 1F3FD ; fully-qualified # 🤝🏽 E14.0 handshake: medium skin tone
1F91D 1F3FE ; fully-qualified # 🤝🏾 E14.0 handshake: medium-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 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
@ -1455,7 +1472,7 @@
1F575 1F3FF ; fully-qualified # 🕵🏿 E2.0 detective: dark skin tone
1F575 FE0F 200D 2642 FE0F ; fully-qualified # 🕵️‍♂️ 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 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
@ -1469,7 +1486,7 @@
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 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 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
@ -2302,7 +2319,7 @@
1F3CC 1F3FF ; fully-qualified # 🏌🏿 E4.0 person golfing: dark skin tone
1F3CC FE0F 200D 2642 FE0F ; fully-qualified # 🏌️‍♂️ 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 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
@ -2316,7 +2333,7 @@
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 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 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
@ -2427,7 +2444,7 @@
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 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 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
@ -2441,7 +2458,7 @@
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 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 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
@ -2462,7 +2479,7 @@
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 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 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
@ -2476,7 +2493,7 @@
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 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 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
@ -3262,8 +3279,8 @@
1FAC2 ; fully-qualified # 🫂 E13.0 people hugging
1F463 ; fully-qualified # 👣 E0.6 footprints
# People & Body subtotal: 2986
# People & Body subtotal: 506 w/o modifiers
# People & Body subtotal: 2998
# People & Body subtotal: 508 w/o modifiers
# group: Component
@ -3306,6 +3323,8 @@
1F405 ; fully-qualified # 🐅 E1.0 tiger
1F406 ; fully-qualified # 🐆 E1.0 leopard
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
1F984 ; fully-qualified # 🦄 E1.0 unicorn
1F993 ; fully-qualified # 🦓 E5.0 zebra
@ -3373,6 +3392,9 @@
1F9A9 ; fully-qualified # 🦩 E12.0 flamingo
1F99A ; fully-qualified # 🦚 E11.0 peacock
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
1F438 ; fully-qualified # 🐸 E0.6 frog
@ -3399,6 +3421,7 @@
1F419 ; fully-qualified # 🐙 E0.6 octopus
1F41A ; fully-qualified # 🐚 E0.6 spiral shell
1FAB8 ; fully-qualified # 🪸 E14.0 coral
1FABC ; fully-qualified # 🪼 E15.0 jellyfish
# subgroup: animal-bug
1F40C ; fully-qualified # 🐌 E0.6 snail
@ -3433,6 +3456,7 @@
1F33B ; fully-qualified # 🌻 E0.6 sunflower
1F33C ; fully-qualified # 🌼 E0.6 blossom
1F337 ; fully-qualified # 🌷 E0.6 tulip
1FABB ; fully-qualified # 🪻 E15.0 hyacinth
# subgroup: plant-other
1F331 ; fully-qualified # 🌱 E0.6 seedling
@ -3451,9 +3475,10 @@
1F343 ; fully-qualified # 🍃 E0.6 leaf fluttering in wind
1FAB9 ; fully-qualified # 🪹 E14.0 empty nest
1FABA ; fully-qualified # 🪺 E14.0 nest with eggs
1F344 ; fully-qualified # 🍄 E0.6 mushroom
# Animals & Nature subtotal: 151
# Animals & Nature subtotal: 151 w/o modifiers
# Animals & Nature subtotal: 159
# Animals & Nature subtotal: 159 w/o modifiers
# group: Food & Drink
@ -3492,10 +3517,11 @@
1F966 ; fully-qualified # 🥦 E5.0 broccoli
1F9C4 ; fully-qualified # 🧄 E12.0 garlic
1F9C5 ; fully-qualified # 🧅 E12.0 onion
1F344 ; fully-qualified # 🍄 E0.6 mushroom
1F95C ; fully-qualified # 🥜 E3.0 peanuts
1FAD8 ; fully-qualified # 🫘 E14.0 beans
1F330 ; fully-qualified # 🌰 E0.6 chestnut
1FADA ; fully-qualified # 🫚 E15.0 ginger root
1FADB ; fully-qualified # 🫛 E15.0 pea pod
# subgroup: food-prepared
1F35E ; fully-qualified # 🍞 E0.6 bread
@ -3607,8 +3633,8 @@
1FAD9 ; fully-qualified # 🫙 E14.0 jar
1F3FA ; fully-qualified # 🏺 E1.0 amphora
# Food & Drink subtotal: 134
# Food & Drink subtotal: 134 w/o modifiers
# Food & Drink subtotal: 135
# Food & Drink subtotal: 135 w/o modifiers
# group: Travel & Places
@ -3974,11 +4000,10 @@
1F3AF ; fully-qualified # 🎯 E0.6 bullseye
1FA80 ; fully-qualified # 🪀 E12.0 yo-yo
1FA81 ; fully-qualified # 🪁 E12.0 kite
1F52B ; fully-qualified # 🔫 E0.6 water pistol
1F3B1 ; fully-qualified # 🎱 E0.6 pool 8 ball
1F52E ; fully-qualified # 🔮 E0.6 crystal ball
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
1F579 FE0F ; fully-qualified # 🕹️ E0.7 joystick
1F579 ; unqualified # 🕹 E0.7 joystick
@ -4013,8 +4038,8 @@
1F9F6 ; fully-qualified # 🧶 E11.0 yarn
1FAA2 ; fully-qualified # 🪢 E13.0 knot
# Activities subtotal: 97
# Activities subtotal: 97 w/o modifiers
# Activities subtotal: 96
# Activities subtotal: 96 w/o modifiers
# group: Objects
@ -4040,6 +4065,7 @@
1FA73 ; fully-qualified # 🩳 E12.0 shorts
1F459 ; fully-qualified # 👙 E0.6 bikini
1F45A ; fully-qualified # 👚 E0.6 womans clothes
1FAAD ; fully-qualified # 🪭 E15.0 folding hand fan
1F45B ; fully-qualified # 👛 E0.6 purse
1F45C ; fully-qualified # 👜 E0.6 handbag
1F45D ; fully-qualified # 👝 E0.6 clutch bag
@ -4055,6 +4081,7 @@
1F461 ; fully-qualified # 👡 E0.6 womans sandal
1FA70 ; fully-qualified # 🩰 E12.0 ballet shoes
1F462 ; fully-qualified # 👢 E0.6 womans boot
1FAAE ; fully-qualified # 🪮 E15.0 hair pick
1F451 ; fully-qualified # 👑 E0.6 crown
1F452 ; fully-qualified # 👒 E0.6 womans hat
1F3A9 ; fully-qualified # 🎩 E0.6 top hat
@ -4103,6 +4130,8 @@
1FA95 ; fully-qualified # 🪕 E12.0 banjo
1F941 ; fully-qualified # 🥁 E3.0 drum
1FA98 ; fully-qualified # 🪘 E13.0 long drum
1FA87 ; fully-qualified # 🪇 E15.0 maracas
1FA88 ; fully-qualified # 🪈 E15.0 flute
# subgroup: phone
1F4F1 ; fully-qualified # 📱 E0.6 mobile phone
@ -4275,7 +4304,7 @@
1F5E1 ; unqualified # 🗡 E0.7 dagger
2694 FE0F ; fully-qualified # ⚔️ 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
1F3F9 ; fully-qualified # 🏹 E1.0 bow and arrow
1F6E1 FE0F ; fully-qualified # 🛡️ E0.7 shield
@ -4354,12 +4383,14 @@
1FAA6 ; fully-qualified # 🪦 E13.0 headstone
26B1 FE0F ; fully-qualified # ⚱️ 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
1FAA7 ; fully-qualified # 🪧 E13.0 placard
1FAAA ; fully-qualified # 🪪 E14.0 identification card
# Objects subtotal: 304
# Objects subtotal: 304 w/o modifiers
# Objects subtotal: 310
# Objects subtotal: 310 w/o modifiers
# group: Symbols
@ -4455,6 +4486,7 @@
262E ; unqualified # ☮ E1.0 peace symbol
1F54E ; fully-qualified # 🕎 E1.0 menorah
1F52F ; fully-qualified # 🔯 E0.6 dotted six-pointed star
1FAAF ; fully-qualified # 🪯 E15.0 khanda
# subgroup: zodiac
2648 ; fully-qualified # ♈ E0.6 Aries
@ -4503,6 +4535,7 @@
1F505 ; fully-qualified # 🔅 E1.0 dim button
1F506 ; fully-qualified # 🔆 E1.0 bright button
1F4F6 ; fully-qualified # 📶 E0.6 antenna bars
1F6DC ; fully-qualified # 🛜 E15.0 wireless
1F4F3 ; fully-qualified # 📳 E0.6 vibration mode
1F4F4 ; fully-qualified # 📴 E0.6 mobile phone off
@ -4693,8 +4726,8 @@
1F533 ; fully-qualified # 🔳 E0.6 white square button
1F532 ; fully-qualified # 🔲 E0.6 black square button
# Symbols subtotal: 302
# Symbols subtotal: 302 w/o modifiers
# Symbols subtotal: 304
# Symbols subtotal: 304 w/o modifiers
# group: Flags
@ -4709,7 +4742,7 @@
1F3F3 200D 1F308 ; unqualified # 🏳‍🌈 E4.0 rainbow flag
1F3F3 FE0F 200D 26A7 FE0F ; fully-qualified # 🏳️‍⚧️ 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
1F3F4 200D 2620 FE0F ; fully-qualified # 🏴‍☠️ E11.0 pirate flag
1F3F4 200D 2620 ; minimally-qualified # 🏴‍☠ E11.0 pirate flag
@ -4983,9 +5016,9 @@
# Flags subtotal: 275 w/o modifiers
# Status Counts
# fully-qualified : 3624
# minimally-qualified : 817
# unqualified : 252
# fully-qualified : 3655
# minimally-qualified : 827
# unqualified : 242
# component : 9
#EOF

View file

@ -145,7 +145,7 @@ defp warn_on_no_object_preloaded(ap_id) do
Logger.debug("Backtrace: #{inspect(Process.info(:erlang.self(), :current_stacktrace))}")
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.
# Use this whenever possible, especially when walking graphs in an O(N) loop!
@ -173,10 +173,15 @@ def normalize(%Activity{data: %{"object" => ap_id}}, options) do
def normalize(%{"id" => ap_id}, options), do: normalize(ap_id, options)
def normalize(ap_id, options) when is_binary(ap_id) do
if Keyword.get(options, :fetch) do
Fetcher.fetch_object_from_id!(ap_id, options)
else
get_cached_by_ap_id(ap_id)
cond do
Keyword.get(options, :id_only) ->
ap_id
Keyword.get(options, :fetch) ->
Fetcher.fetch_object_from_id!(ap_id, options)
true ->
get_cached_by_ap_id(ap_id)
end
end

View file

@ -153,7 +153,7 @@ def add_to_index(activity) do
)
with {:ok, res} <- result,
true <- Map.has_key?(res, "uid") do
true <- Map.has_key?(res, "taskUid") do
# Do nothing
else
_ ->

View file

@ -66,9 +66,8 @@ def refetch_public_key(conn) do
end
end
def sign(%User{} = user, headers) do
with {:ok, %{keys: keys}} <- User.ensure_keys_present(user),
{:ok, private_key, _} <- Keys.keys_from_pem(keys) do
def sign(%User{keys: keys} = user, headers) do
with {:ok, private_key, _} <- Keys.keys_from_pem(keys) do
HTTPSignatures.sign(private_key, user.ap_id <> "#main-key", headers)
end
end

View file

@ -165,6 +165,8 @@ defmodule Pleroma.User do
has_many(:outgoing_relationships, UserRelationship, foreign_key: :source_id)
has_many(:incoming_relationships, UserRelationship, foreign_key: :target_id)
has_many(:frontend_profiles, Pleroma.Akkoma.FrontendSettingsProfile)
for {relationship_type,
[
{outgoing_relation, outgoing_relation_target},
@ -681,9 +683,9 @@ def register_changeset_ldap(struct, params = %{password: password})
|> validate_exclusion(:nickname, Config.get([User, :restricted_nicknames]))
|> validate_format(:nickname, local_nickname_regex())
|> put_ap_id()
|> put_keys()
|> unique_constraint(:ap_id)
|> put_following_and_follower_and_featured_address()
|> put_private_key()
end
def register_changeset(struct, params \\ %{}, opts \\ []) do
@ -741,10 +743,10 @@ def register_changeset(struct, params \\ %{}, opts \\ []) do
|> validate_length(:registration_reason, max: reason_limit)
|> maybe_validate_required_email(opts[:external])
|> put_password_hash
|> put_keys()
|> put_ap_id()
|> unique_constraint(:ap_id)
|> put_following_and_follower_and_featured_address()
|> put_private_key()
end
def maybe_validate_required_email(changeset, true), do: changeset
@ -757,11 +759,6 @@ def maybe_validate_required_email(changeset, _) do
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
ap_id = ap_id(%User{nickname: get_field(changeset, :nickname)})
put_change(changeset, :ap_id, ap_id)
@ -779,6 +776,11 @@ def put_following_and_follower_and_featured_address(changeset) do
|> put_change(:featured_address, featured)
end
defp put_private_key(changeset) do
{:ok, pem} = Keys.generate_rsa_pem()
put_change(changeset, :keys, pem)
end
defp autofollow_users(user) do
candidates = Config.get([:instance, :autofollowed_nicknames])
@ -1955,6 +1957,7 @@ defp create_service_actor(uri, nickname) do
follower_address: uri <> "/followers"
}
|> change
|> put_private_key()
|> unique_constraint(:nickname)
|> Repo.insert()
|> set_cache()
@ -1987,7 +1990,8 @@ def ap_enabled?(_), do: false
@doc "Gets or fetch a user by uri or nickname."
@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)
# wait a period of time and return newest version of the User structs
@ -2220,17 +2224,6 @@ def get_mascot(%{mascot: mascot}) when is_nil(mascot) do
}
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
from(u in User,
where: u.nickname in ^nicknames,

View file

@ -94,6 +94,7 @@ defp search_query(query_string, for_user, following, top_user_ids) do
|> subquery()
|> order_by(desc: :search_rank)
|> maybe_restrict_local(for_user)
|> filter_deactivated_users()
end
defp select_top_users(query, top_user_ids) do
@ -166,6 +167,10 @@ defp filter_internal_users(query) do
from(q in query, where: q.actor_type != "Application")
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
query
|> join(:left, [u], b in Pleroma.UserRelationship,

View file

@ -66,8 +66,7 @@ defp relay_active?(conn, _) do
end
def user(conn, %{"nickname" => nickname}) do
with %User{local: true} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- User.ensure_keys_present(user) do
with %User{local: true} = user <- User.get_cached_by_nickname(nickname) do
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
@ -174,7 +173,6 @@ def relay_following(conn, _params) do
def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
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, (for_user && for_user == user) || !user.hide_follows} do
{page, _} = Integer.parse(page)
@ -192,8 +190,7 @@ def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "p
end
def following(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do
with %User{} = user <- User.get_cached_by_nickname(nickname),
{user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
with %User{} = user <- User.get_cached_by_nickname(nickname) do
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
@ -213,7 +210,6 @@ def relay_followers(conn, _params) do
def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "page" => page}) do
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, (for_user && for_user == user) || !user.hide_followers} do
{page, _} = Integer.parse(page)
@ -231,8 +227,7 @@ def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname, "p
end
def followers(%{assigns: %{user: for_user}} = conn, %{"nickname" => nickname}) do
with %User{} = user <- User.get_cached_by_nickname(nickname),
{user, for_user} <- ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
with %User{} = user <- User.get_cached_by_nickname(nickname) do
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
@ -245,8 +240,7 @@ def outbox(
%{"nickname" => nickname, "page" => page?} = params
)
when page? in [true, "true"] do
with %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- User.ensure_keys_present(user) do
with %User{} = user <- User.get_cached_by_nickname(nickname) do
# "include_poll_votes" is a hack because postgres generates inefficient
# queries when filtering by 'Answer', poll votes will be hidden by the
# visibility filter in this case anyway
@ -270,8 +264,7 @@ def outbox(
end
def outbox(conn, %{"nickname" => nickname}) do
with %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- User.ensure_keys_present(user) do
with %User{} = user <- User.get_cached_by_nickname(nickname) do
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
@ -328,14 +321,10 @@ defp post_inbox_relayed_create(conn, params) do
end
defp represent_service_actor(%User{} = user, conn) do
with {:ok, user} <- User.ensure_keys_present(user) do
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
|> render("user.json", %{user: user})
else
nil -> {:error, :not_found}
end
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
|> render("user.json", %{user: user})
end
defp represent_service_actor(nil, _), do: {:error, :not_found}
@ -388,12 +377,10 @@ def read_inbox(
def read_inbox(%{assigns: %{user: %User{nickname: nickname} = user}} = conn, %{
"nickname" => nickname
}) do
with {:ok, user} <- User.ensure_keys_present(user) do
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
|> render("activity_collection.json", %{iri: "#{user.ap_id}/inbox"})
end
conn
|> put_resp_content_type("application/activity+json")
|> put_view(UserView)
|> render("activity_collection.json", %{iri: "#{user.ap_id}/inbox"})
end
def read_inbox(%{assigns: %{user: %User{nickname: as_nickname}}} = conn, %{
@ -530,19 +517,6 @@ defp set_requester_reachable(%Plug.Conn{} = conn, _) do
conn
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
with {:ok, object} <-
ActivityPub.upload(

View file

@ -29,11 +29,11 @@ def render("object.json", %{object: %Activity{data: %{"type" => activity_type}}
def render("object.json", %{object: %Activity{} = activity}) do
base = Pleroma.Web.ActivityPub.Utils.make_json_ld_header()
object = Object.normalize(activity, fetch: false)
object_id = Object.normalize(activity, id_only: true)
additional =
Transmogrifier.prepare_object(activity.data)
|> Map.put("object", object.data["id"])
|> Map.put("object", object_id)
Map.merge(base, additional)
end

View file

@ -34,7 +34,6 @@ def render("endpoints.json", %{user: %User{local: true} = _user}) do
def render("endpoints.json", _), do: %{}
def render("service.json", %{user: user}) do
{:ok, user} = User.ensure_keys_present(user)
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key])
@ -71,7 +70,6 @@ def render("user.json", %{user: %User{nickname: "internal." <> _} = user}),
do: render("service.json", %{user: user}) |> Map.put("preferredUsername", user.nickname)
def render("user.json", %{user: user}) do
{:ok, user} = User.ensure_keys_present(user)
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key])

View file

@ -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

View file

@ -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

View file

@ -16,7 +16,7 @@ def index_operation do
%Operation{
tags: ["Backups"],
summary: "List backups",
security: [%{"oAuth" => ["read:account"]}],
security: [%{"oAuth" => ["read:backups"]}],
operationId: "PleromaAPI.BackupController.index",
responses: %{
200 =>
@ -37,7 +37,7 @@ def create_operation do
%Operation{
tags: ["Backups"],
summary: "Create a backup",
security: [%{"oAuth" => ["read:account"]}],
security: [%{"oAuth" => ["read:backups"]}],
operationId: "PleromaAPI.BackupController.create",
responses: %{
200 =>

View file

@ -69,10 +69,8 @@ def perform(:publish_one, module, params) do
def perform(:publish, activity) do
Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end)
with %User{} = actor <- User.get_cached_by_ap_id(activity.data["actor"]),
{:ok, actor} <- User.ensure_keys_present(actor) do
Publisher.publish(actor, activity)
end
%User{} = actor = User.get_cached_by_ap_id(activity.data["actor"])
Publisher.publish(actor, activity)
end
def perform(:incoming_ap_doc, params) do

View file

@ -8,8 +8,8 @@ defmodule Pleroma.Web.Metadata.Utils do
alias Pleroma.Formatter
alias Pleroma.HTML
def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
content
defp scrub_html_and_truncate_object_field(field, object) do
field
# html content comes from DB already encoded, decode first and scrub after
|> HtmlEntities.decode()
|> String.replace(~r/<br\s?\/?>/, " ")
@ -19,6 +19,17 @@ def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
|> Formatter.truncate()
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
content
|> scrub_html

View file

@ -9,7 +9,7 @@ defmodule Pleroma.Web.PleromaAPI.BackupController do
alias Pleroma.Web.Plugs.OAuthScopesPlug
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)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaBackupOperation

View file

@ -47,15 +47,17 @@ def call(conn, _) do
#
@spec fetch_user_and_token(String.t()) :: {:ok, User.t(), Token.t()} | nil
defp fetch_user_and_token(token) do
query =
token_query =
from(t in Token,
where: t.token == ^token,
join: user in assoc(t, :user),
preload: [user: user]
where: t.token == ^token
)
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}
else
_ -> nil
end
end

View file

@ -466,6 +466,26 @@ defmodule Pleroma.Web.Router do
scope "/api/v1/akkoma", Pleroma.Web.AkkomaAPI do
pipe_through(:authenticated_api)
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
scope "/api/v1", Pleroma.Web.MastodonAPI do

View file

@ -69,8 +69,6 @@ defp gather_aliases(%User{} = user) do
end
def represent_user(user, "JSON") do
{:ok, user} = User.ensure_keys_present(user)
%{
"subject" => "acct:#{user.nickname}@#{domain()}",
"aliases" => gather_aliases(user),
@ -79,8 +77,6 @@ def represent_user(user, "JSON") do
end
def represent_user(user, "XML") do
{:ok, user} = User.ensure_keys_present(user)
aliases =
user
|> gather_aliases()

View file

@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
def project do
[
app: :pleroma,
version: version("3.2.0"),
version: version("3.3.0"),
elixir: "~> 1.12",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(),

View file

@ -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

View file

@ -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

View file

@ -1,9 +1,15 @@
defmodule Pleroma.Repo.Migrations.EnsureMastofeSettings do
use Ecto.Migration
def change do
def up do
alter table(:users) do
add_if_not_exists(:mastofe_settings, :map)
end
end
def down do
alter table(:users) do
remove_if_exists(:mastofe_settings, :map)
end
end
end

Binary file not shown.

View file

@ -2,28 +2,24 @@
# XXX: This should be removed when elixir's releases get custom command support
detect_flavour() {
arch="$(uname -m)"
if [ "$arch" = "x86_64" ]; then
arch="amd64"
elif [ "$arch" = "aarch64" ]; then
arch="arm64"
else
echo "Unsupported arch: $arch" >&2
exit 1
fi
arch="amd64"
# Special cases
if grep -qe "VERSION_CODENAME=jammy" /etc/os-release; then
echo "$arch-ubuntu-jammy"
else
if getconf GNU_LIBC_VERSION >/dev/null; then
libc_postfix=""
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
if getconf GNU_LIBC_VERSION >/dev/null; then
libc_postfix=""
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"
echo "$arch$libc_postfix"
fi
}
detect_branch() {

27
test/fixtures/rsa_keys/key_1.pem vendored Normal file
View 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
View 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
View 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
View 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
View 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-----

View 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

View file

@ -47,7 +47,7 @@ test "indexes a local post on creation" do
Jason.decode!(body)
)
json(%{updateId: 1})
json(%{taskUid: 1})
end)
{:ok, activity} =
@ -100,11 +100,11 @@ test "deletes posts from index when deleted locally" do
Jason.decode!(body)
)
json(%{updateId: 1})
json(%{taskUid: 1})
%{method: :delete, url: "http://127.0.0.1:7700/indexes/objects/documents/" <> id} ->
assert String.length(id) > 1
json(%{updateId: 2})
json(%{taskUid: 2})
end)
{:ok, activity} =

View file

@ -65,6 +65,14 @@ test "excludes invisible users from results" do
assert found_user.id == user.id
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
test "includes non-discoverable users in results" do
insert(:user, %{nickname: "john 3000", is_discoverable: false})

View file

@ -620,15 +620,15 @@ test "it blocks blacklisted email domains" do
assert changeset.valid?
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)
assert changeset.valid?
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 is_binary(changeset.changes[:keys])
assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
end
@ -737,6 +737,13 @@ test "gets an existing user by ap_id" do
freshed_user = refresh_record(user)
assert freshed_user == fetched_user
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
describe "get_or_fetch/1 remote users with tld, while BE is runned on subdomain" do
@ -2130,21 +2137,6 @@ test "Only includes users with no read notifications" do
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
test "it returns a list of AP ids for a given set of nicknames" do
user = insert(:user)

View file

@ -81,4 +81,18 @@ test "renders an announce activity" do
assert result["object"] == object.data["id"]
assert result["type"] == "Announce"
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

View file

@ -12,7 +12,6 @@ defmodule Pleroma.Web.ActivityPub.UserViewTest do
test "Renders a user, including the public key" do
user = insert(:user)
{:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user})
@ -55,7 +54,6 @@ test "Renders with emoji tags" do
test "Does not add an avatar image if the user hasn't set one" do
user = insert(:user)
{:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user})
refute result["icon"]
@ -67,8 +65,6 @@ test "Does not add an avatar image if the user hasn't set one" do
banner: %{"url" => [%{"href" => "https://somebanner"}]}
)
{:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user})
assert result["icon"]["url"] == "https://someurl"
assert result["image"]["url"] == "https://somebanner"
@ -89,7 +85,6 @@ test "renders AKAs" do
describe "endpoints" do
test "local users have a usable endpoints structure" do
user = insert(:user)
{:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user})
@ -105,7 +100,6 @@ test "local users have a usable endpoints structure" do
test "remote users have an empty endpoints structure" do
user = insert(:user, local: false)
{:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user})
@ -115,7 +109,6 @@ test "remote users have an empty endpoints structure" do
test "instance users do not expose oAuth endpoints" do
user = insert(:user, nickname: nil, local: true)
{:ok, user} = User.ensure_keys_present(user)
result = UserView.render("user.json", %{user: user})

View file

@ -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

View file

@ -39,6 +39,7 @@ test "it uses summary twittercard if post has no attachment" do
"actor" => user.ap_id,
"tag" => [],
"id" => "https://pleroma.gov/objects/whatever",
"summary" => "",
"content" => "pleroma in a nutshell"
}
})
@ -54,6 +55,36 @@ test "it uses summary twittercard if post has no attachment" do
] == result
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
clear_config([Pleroma.Web.Metadata, :unfurl_nsfw], false)
user = insert(:user, name: "Jimmy Hendriks", bio: "born 19 March 1994")
@ -65,6 +96,7 @@ test "it renders avatar not attachment if post is nsfw and unfurl_nsfw is disabl
"actor" => user.ap_id,
"tag" => [],
"id" => "https://pleroma.gov/objects/whatever",
"summary" => "",
"content" => "pleroma in a nutshell",
"sensitive" => true,
"attachment" => [
@ -109,6 +141,7 @@ test "it renders supported types of attachments and skips unknown types" do
"actor" => user.ap_id,
"tag" => [],
"id" => "https://pleroma.gov/objects/whatever",
"summary" => "",
"content" => "pleroma in a nutshell",
"attachment" => [
%{

View file

@ -8,7 +8,7 @@ defmodule Pleroma.Web.Metadata.UtilsTest do
alias Pleroma.Web.Metadata.Utils
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)
note =
@ -16,6 +16,7 @@ test "it returns text without encode HTML" do
data: %{
"actor" => user.ap_id,
"id" => "https://pleroma.gov/objects/whatever",
"summary" => nil,
"content" => "Pleroma's really cool!"
}
})
@ -23,6 +24,39 @@ test "it returns text without encode HTML" do
assert Utils.scrub_html_and_truncate(note) == "Pleroma's really cool!"
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
user = insert(:user)

View file

@ -11,7 +11,7 @@ defmodule Pleroma.Web.PleromaAPI.BackupControllerTest do
setup do
clear_config([Pleroma.Upload, :uploader])
clear_config([Backup, :limit_days])
oauth_access(["read:accounts"])
oauth_access(["read:backups"])
end
test "GET /api/v1/pleroma/backups", %{user: user, conn: conn} do
@ -85,7 +85,7 @@ test "POST /api/v1/pleroma/backups", %{user: _user, conn: conn} do
test "Backup without email address" do
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)

View file

@ -10,6 +10,15 @@ defmodule Pleroma.Factory do
alias Pleroma.Object
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
conversation = insert(:conversation)
user = insert(:user)
@ -28,6 +37,8 @@ def conversation_factory do
end
def user_factory(attrs \\ %{}) do
pem = Enum.random(@rsa_keys)
user = %User{
name: sequence(:name, &"Test テスト User #{&1}"),
email: sequence(:email, &"user#{&1}@example.com"),
@ -39,7 +50,8 @@ def user_factory(attrs \\ %{}) do
last_refreshed_at: NaiveDateTime.utc_now(),
notification_settings: %Pleroma.User.NotificationSetting{},
multi_factor_authentication_settings: %Pleroma.MFA.Settings{},
ap_enabled: true
ap_enabled: true,
keys: pem
}
urls =
@ -651,4 +663,15 @@ def announcement_factory(params \\ %{}) do
|> Map.merge(params)
|> Pleroma.Announcement.add_rendered_properties()
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