Merge branch 'develop' into feld-2168-media-preview-proxy
This commit is contained in:
commit
479578b148
|
@ -194,7 +194,7 @@ amd64:
|
||||||
variables: &release-variables
|
variables: &release-variables
|
||||||
MIX_ENV: prod
|
MIX_ENV: prod
|
||||||
before_script: &before-release
|
before_script: &before-release
|
||||||
- apt install cmake -y
|
- apt-get update && apt-get install -y cmake
|
||||||
- echo "import Mix.Config" > config/prod.secret.exs
|
- echo "import Mix.Config" > config/prod.secret.exs
|
||||||
- mix local.hex --force
|
- mix local.hex --force
|
||||||
- mix local.rebar --force
|
- mix local.rebar --force
|
||||||
|
|
22
CHANGELOG.md
22
CHANGELOG.md
|
@ -6,18 +6,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
## [unreleased]
|
## [unreleased]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- **Breaking:** The default descriptions on uploads are now empty. The old behavior (filename as default) can be configured, see the cheat sheet.
|
||||||
- **Breaking:** Added the ObjectAgePolicy to the default set of MRFs. This will delist and strip the follower collection of any message received that is older than 7 days. This will stop users from seeing very old messages in the timelines. The messages can still be viewed on the user's page and in conversations. They also still trigger notifications.
|
- **Breaking:** Added the ObjectAgePolicy to the default set of MRFs. This will delist and strip the follower collection of any message received that is older than 7 days. This will stop users from seeing very old messages in the timelines. The messages can still be viewed on the user's page and in conversations. They also still trigger notifications.
|
||||||
- **Breaking:** Elixir >=1.9 is now required (was >= 1.8)
|
- **Breaking:** Elixir >=1.9 is now required (was >= 1.8)
|
||||||
- **Breaking:** Configuration: `:auto_linker, :opts` moved to `:pleroma, Pleroma.Formatter`. Old config namespace is deprecated.
|
- **Breaking:** Configuration: `:auto_linker, :opts` moved to `:pleroma, Pleroma.Formatter`. Old config namespace is deprecated.
|
||||||
|
- **Breaking:** Configuration: `:instance, welcome_user_nickname` moved to `:welcome, :direct_message, :sender_nickname`, `:instance, :welcome_message` moved to `:welcome, :direct_message, :message`. Old config namespace is deprecated.
|
||||||
|
- **Breaking:** LDAP: Fallback to local database authentication has been removed for security reasons and lack of a mechanism to ensure the passwords are synchronized when LDAP passwords are updated.
|
||||||
|
- **Breaking** Changed defaults for `:restrict_unauthenticated` so that when `:instance, :public` is set to `false` then all `:restrict_unauthenticated` items be effectively set to `true`. If you'd like to allow unauthenticated access to specific API endpoints on a private instance, please explicitly set `:restrict_unauthenticated` to non-default value in `config/prod.secret.exs`.
|
||||||
- In Conversations, return only direct messages as `last_status`
|
- In Conversations, return only direct messages as `last_status`
|
||||||
- Using the `only_media` filter on timelines will now exclude reblog media
|
- Using the `only_media` filter on timelines will now exclude reblog media
|
||||||
- MFR policy to set global expiration for all local Create activities
|
- MFR policy to set global expiration for all local Create activities
|
||||||
- OGP rich media parser merged with TwitterCard
|
- OGP rich media parser merged with TwitterCard
|
||||||
- Configuration: `:instance, rewrite_policy` moved to `:mrf, policies`, `:instance, :mrf_transparency` moved to `:mrf, :transparency`, `:instance, :mrf_transparency_exclusions` moved to `:mrf, :transparency_exclusions`. Old config namespace is deprecated.
|
- Configuration: `:instance, rewrite_policy` moved to `:mrf, policies`, `:instance, :mrf_transparency` moved to `:mrf, :transparency`, `:instance, :mrf_transparency_exclusions` moved to `:mrf, :transparency_exclusions`. Old config namespace is deprecated.
|
||||||
- Configuration: `:media_proxy, whitelist` format changed to host with scheme (e.g. `http://example.com` instead of `example.com`). Domain format is deprecated.
|
- Configuration: `:media_proxy, whitelist` format changed to host with scheme (e.g. `http://example.com` instead of `example.com`). Domain format is deprecated.
|
||||||
- **Breaking:** Configuration: `:instance, welcome_user_nickname` moved to `:welcome, :direct_message, :sender_nickname`, `:instance, :welcome_message` moved to `:welcome, :direct_message, :message`. Old config namespace is deprecated.
|
|
||||||
- **Breaking:** LDAP: Fallback to local database authentication has been removed for security reasons and lack of a mechanism to ensure the passwords are synchronized when LDAP passwords are updated.
|
|
||||||
- **Breaking** Changed defaults for `:restrict_unauthenticated` so that when `:instance, :public` is set to `false` then all `:restrict_unauthenticated` items be effectively set to `true`. If you'd like to allow unauthenticated access to specific API endpoints on a private instance, please explicitly set `:restrict_unauthenticated` to non-default value in `config/prod.secret.exs`.
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
|
@ -25,33 +27,36 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- **Breaking:** Pleroma API: The routes to update avatar, banner and background have been removed.
|
- **Breaking:** Pleroma API: The routes to update avatar, banner and background have been removed.
|
||||||
- **Breaking:** Image description length is limited now.
|
- **Breaking:** Image description length is limited now.
|
||||||
- **Breaking:** Emoji API: changed methods and renamed routes.
|
- **Breaking:** Emoji API: changed methods and renamed routes.
|
||||||
|
- **Breaking:** Notification Settings API for suppressing notifications has been simplified down to `block_from_strangers`.
|
||||||
|
- **Breaking:** Notification Settings API option for hiding push notification contents has been renamed to `hide_notification_contents`.
|
||||||
- MastodonAPI: Allow removal of avatar, banner and background.
|
- MastodonAPI: Allow removal of avatar, banner and background.
|
||||||
- Streaming: Repeats of a user's posts will no longer be pushed to the user's stream.
|
- Streaming: Repeats of a user's posts will no longer be pushed to the user's stream.
|
||||||
- Mastodon API: Added `pleroma.metadata.fields_limits` to /api/v1/instance
|
- Mastodon API: Added `pleroma.metadata.fields_limits` to /api/v1/instance
|
||||||
- Mastodon API: On deletion, returns the original post text.
|
- Mastodon API: On deletion, returns the original post text.
|
||||||
- Mastodon API: Add `pleroma.unread_count` to the Marker entity.
|
- Mastodon API: Add `pleroma.unread_count` to the Marker entity.
|
||||||
- **Breaking:** Notification Settings API for suppressing notifications
|
|
||||||
has been simplified down to `block_from_strangers`.
|
|
||||||
- **Breaking:** Notification Settings API option for hiding push notification
|
|
||||||
contents has been renamed to `hide_notification_contents`
|
|
||||||
- Mastodon API: Added `pleroma.metadata.post_formats` to /api/v1/instance
|
- Mastodon API: Added `pleroma.metadata.post_formats` to /api/v1/instance
|
||||||
- Mastodon API (legacy): Allow query parameters for `/api/v1/domain_blocks`, e.g. `/api/v1/domain_blocks?domain=badposters.zone`
|
- Mastodon API (legacy): Allow query parameters for `/api/v1/domain_blocks`, e.g. `/api/v1/domain_blocks?domain=badposters.zone`
|
||||||
- Pleroma API: `/api/pleroma/captcha` responses now include `seconds_valid` with an integer value.
|
- Pleroma API: `/api/pleroma/captcha` responses now include `seconds_valid` with an integer value.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Admin API Changes</summary>
|
<summary>Admin API Changes</summary>
|
||||||
|
|
||||||
|
- **Breaking** Changed relay `/api/pleroma/admin/relay` endpoints response format.
|
||||||
- Status visibility stats: now can return stats per instance.
|
- Status visibility stats: now can return stats per instance.
|
||||||
|
|
||||||
- Mix task to refresh counter cache (`mix pleroma.refresh_counter_cache`)
|
- Mix task to refresh counter cache (`mix pleroma.refresh_counter_cache`)
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- **Breaking:** removed `with_move` parameter from notifications timeline.
|
- **Breaking:** removed `with_move` parameter from notifications timeline.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Frontends: Add mix task to install frontends.
|
||||||
|
- Frontends: Add configurable frontends for primary and admin fe.
|
||||||
- Configuration: Added a blacklist for email servers.
|
- Configuration: Added a blacklist for email servers.
|
||||||
- Chats: Added `accepts_chat_messages` field to user, exposed in APIs and federation.
|
- Chats: Added `accepts_chat_messages` field to user, exposed in APIs and federation.
|
||||||
- Chats: Added support for federated chats. For details, see the docs.
|
- Chats: Added support for federated chats. For details, see the docs.
|
||||||
|
@ -107,6 +112,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Emoji Packs could not be listed when instance was set to `public: false`
|
- Emoji Packs could not be listed when instance was set to `public: false`
|
||||||
- Fix whole_word always returning false on filter get requests
|
- Fix whole_word always returning false on filter get requests
|
||||||
- Migrations not working on OTP releases if the database was connected over ssl
|
- Migrations not working on OTP releases if the database was connected over ssl
|
||||||
|
- Fix relay following
|
||||||
|
|
||||||
## [Unreleased (patch)]
|
## [Unreleased (patch)]
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,8 @@
|
||||||
pool: :upload
|
pool: :upload
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
filename_display_max_length: 30
|
filename_display_max_length: 30,
|
||||||
|
default_description: nil
|
||||||
|
|
||||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
||||||
|
|
||||||
|
@ -682,7 +683,50 @@
|
||||||
# With no frontend configuration, the bundled files from the `static` directory will
|
# With no frontend configuration, the bundled files from the `static` directory will
|
||||||
# be used.
|
# be used.
|
||||||
#
|
#
|
||||||
# config :pleroma, :frontends, primary: %{"name" => "pleroma", "ref" => "develop"}
|
# config :pleroma, :frontends,
|
||||||
|
# primary: %{"name" => "pleroma-fe", "ref" => "develop"},
|
||||||
|
# admin: %{"name" => "admin-fe", "ref" => "stable"},
|
||||||
|
# available: %{...}
|
||||||
|
|
||||||
|
config :pleroma, :frontends,
|
||||||
|
available: %{
|
||||||
|
"kenoma" => %{
|
||||||
|
"name" => "kenoma",
|
||||||
|
"git" => "https://git.pleroma.social/lambadalambda/kenoma",
|
||||||
|
"build_url" =>
|
||||||
|
"https://git.pleroma.social/lambadalambda/kenoma/-/jobs/artifacts/${ref}/download?job=build",
|
||||||
|
"ref" => "master"
|
||||||
|
},
|
||||||
|
"pleroma-fe" => %{
|
||||||
|
"name" => "pleroma-fe",
|
||||||
|
"git" => "https://git.pleroma.social/pleroma/pleroma-fe",
|
||||||
|
"build_url" =>
|
||||||
|
"https://git.pleroma.social/pleroma/pleroma-fe/-/jobs/artifacts/${ref}/download?job=build",
|
||||||
|
"ref" => "develop"
|
||||||
|
},
|
||||||
|
"fedi-fe" => %{
|
||||||
|
"name" => "fedi-fe",
|
||||||
|
"git" => "https://git.pleroma.social/pleroma/fedi-fe",
|
||||||
|
"build_url" =>
|
||||||
|
"https://git.pleroma.social/pleroma/fedi-fe/-/jobs/artifacts/${ref}/download?job=build",
|
||||||
|
"ref" => "master"
|
||||||
|
},
|
||||||
|
"admin-fe" => %{
|
||||||
|
"name" => "admin-fe",
|
||||||
|
"git" => "https://git.pleroma.social/pleroma/admin-fe",
|
||||||
|
"build_url" =>
|
||||||
|
"https://git.pleroma.social/pleroma/admin-fe/-/jobs/artifacts/${ref}/download?job=build",
|
||||||
|
"ref" => "develop"
|
||||||
|
},
|
||||||
|
"soapbox-fe" => %{
|
||||||
|
"name" => "soapbox-fe",
|
||||||
|
"git" => "https://gitlab.com/soapbox-pub/soapbox-fe",
|
||||||
|
"build_url" =>
|
||||||
|
"https://gitlab.com/soapbox-pub/soapbox-fe/-/jobs/artifacts/${ref}/download?job=build-production",
|
||||||
|
"ref" => "v1.0.0",
|
||||||
|
"build_dir" => "static"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
config :pleroma, :web_cache_ttl,
|
config :pleroma, :web_cache_ttl,
|
||||||
activity_pub: nil,
|
activity_pub: nil,
|
||||||
|
|
|
@ -12,6 +12,55 @@
|
||||||
compress: false
|
compress: false
|
||||||
]
|
]
|
||||||
|
|
||||||
|
installed_frontend_options = [
|
||||||
|
%{
|
||||||
|
key: "name",
|
||||||
|
label: "Name",
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"Name of the installed frontend. Valid config must include both `Name` and `Reference` values."
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: "ref",
|
||||||
|
label: "Reference",
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"Reference of the installed frontend to be used. Valid config must include both `Name` and `Reference` values."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
frontend_options = [
|
||||||
|
%{
|
||||||
|
key: "name",
|
||||||
|
label: "Name",
|
||||||
|
type: :string,
|
||||||
|
description: "Name of the frontend."
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: "ref",
|
||||||
|
label: "Reference",
|
||||||
|
type: :string,
|
||||||
|
description: "Reference of the frontend to be used."
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: "git",
|
||||||
|
type: :string,
|
||||||
|
description: "URL of the git repository of the frontend"
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: "build_url",
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"Either an url to a zip file containing the frontend or a template to build it by inserting the `ref`. The string `${ref}` will be replaced by the configured `ref`.",
|
||||||
|
example: "https://some.url/builds/${ref}.zip"
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: "build_dir",
|
||||||
|
type: :string,
|
||||||
|
description: "The directory inside the zip file "
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
config :pleroma, :config_description, [
|
config :pleroma, :config_description, [
|
||||||
%{
|
%{
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
|
@ -3609,21 +3658,40 @@
|
||||||
key: :primary,
|
key: :primary,
|
||||||
type: :map,
|
type: :map,
|
||||||
description: "Primary frontend, the one that is served for all pages by default",
|
description: "Primary frontend, the one that is served for all pages by default",
|
||||||
|
children: installed_frontend_options
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :admin,
|
||||||
|
type: :map,
|
||||||
|
description: "Admin frontend",
|
||||||
|
children: installed_frontend_options
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :available,
|
||||||
|
type: :map,
|
||||||
|
description:
|
||||||
|
"A map containing available frontends and parameters for their installation.",
|
||||||
children: [
|
children: [
|
||||||
%{
|
frontend_options
|
||||||
key: "name",
|
]
|
||||||
label: "Name",
|
}
|
||||||
type: :string,
|
]
|
||||||
description:
|
},
|
||||||
"Name of the installed primary frontend. Valid config must include both `Name` and `Reference` values."
|
%{
|
||||||
},
|
group: :pleroma,
|
||||||
%{
|
key: Pleroma.Web.Preload,
|
||||||
key: "ref",
|
type: :group,
|
||||||
label: "Reference",
|
description: "Preload-related settings",
|
||||||
type: :string,
|
children: [
|
||||||
description:
|
%{
|
||||||
"Reference of the installed primary frontend to be used. Valid config must include both `Name` and `Reference` values."
|
key: :providers,
|
||||||
}
|
type: {:list, :module},
|
||||||
|
description: "List of preload providers to enable",
|
||||||
|
suggestions: [
|
||||||
|
Pleroma.Web.Preload.Providers.Instance,
|
||||||
|
Pleroma.Web.Preload.Providers.User,
|
||||||
|
Pleroma.Web.Preload.Providers.Timelines,
|
||||||
|
Pleroma.Web.Preload.Providers.StatusNet
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -21,7 +21,10 @@
|
||||||
|
|
||||||
config :pleroma, :auth, oauth_consumer_strategies: []
|
config :pleroma, :auth, oauth_consumer_strategies: []
|
||||||
|
|
||||||
config :pleroma, Pleroma.Upload, filters: [], link_name: false
|
config :pleroma, Pleroma.Upload,
|
||||||
|
filters: [],
|
||||||
|
link_name: false,
|
||||||
|
default_description: :filename
|
||||||
|
|
||||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
|
||||||
|
|
||||||
|
|
|
@ -313,31 +313,53 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- On failure: `Not found`
|
- On failure: `Not found`
|
||||||
- On success: JSON array of user's latest statuses
|
- On success: JSON array of user's latest statuses
|
||||||
|
|
||||||
|
## `GET /api/pleroma/admin/relay`
|
||||||
|
|
||||||
|
### List Relays
|
||||||
|
|
||||||
|
Params: none
|
||||||
|
Response:
|
||||||
|
|
||||||
|
* On success: JSON array of relays
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{"actor": "https://example.com/relay", "followed_back": true},
|
||||||
|
{"actor": "https://example2.com/relay", "followed_back": false}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## `POST /api/pleroma/admin/relay`
|
## `POST /api/pleroma/admin/relay`
|
||||||
|
|
||||||
### Follow a Relay
|
### Follow a Relay
|
||||||
|
|
||||||
- Params:
|
Params:
|
||||||
- `relay_url`
|
|
||||||
- Response:
|
* `relay_url`
|
||||||
- On success: URL of the followed relay
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
* On success: relay json object
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"actor": "https://example.com/relay", "followed_back": true}
|
||||||
|
```
|
||||||
|
|
||||||
## `DELETE /api/pleroma/admin/relay`
|
## `DELETE /api/pleroma/admin/relay`
|
||||||
|
|
||||||
### Unfollow a Relay
|
### Unfollow a Relay
|
||||||
|
|
||||||
- Params:
|
Params:
|
||||||
- `relay_url`
|
|
||||||
- Response:
|
|
||||||
- On success: URL of the unfollowed relay
|
|
||||||
|
|
||||||
## `GET /api/pleroma/admin/relay`
|
* `relay_url`
|
||||||
|
|
||||||
### List Relays
|
Response:
|
||||||
|
|
||||||
- Params: none
|
* On success: URL of the unfollowed relay
|
||||||
- Response:
|
|
||||||
- On success: JSON array of relays
|
```json
|
||||||
|
{"https://example.com/relay"}
|
||||||
|
```
|
||||||
|
|
||||||
## `POST /api/pleroma/admin/users/invite_token`
|
## `POST /api/pleroma/admin/users/invite_token`
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Managing frontends
|
||||||
|
|
||||||
|
`mix pleroma.frontend install <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>]`
|
||||||
|
|
||||||
|
Frontend can be installed either from local zip file, or automatically downloaded from the web.
|
||||||
|
|
||||||
|
You can give all the options directly on the command like, but missing information will be filled out by looking at the data configured under `frontends.available` in the config files.
|
||||||
|
|
||||||
|
Currently known `<frontend>` values are:
|
||||||
|
- [admin-fe](https://git.pleroma.social/pleroma/admin-fe)
|
||||||
|
- [kenoma](http://git.pleroma.social/lambadalambda/kenoma)
|
||||||
|
- [pleroma-fe](http://git.pleroma.social/pleroma/pleroma-fe)
|
||||||
|
- [fedi-fe](https://git.pleroma.social/pleroma/fedi-fe)
|
||||||
|
- [soapbox-fe](https://gitlab.com/soapbox-pub/soapbox-fe)
|
||||||
|
|
||||||
|
You can still install frontends that are not configured, see below.
|
||||||
|
|
||||||
|
## Example installations for a known frontend
|
||||||
|
|
||||||
|
For a frontend configured under the `available` key, it's enough to install it by name.
|
||||||
|
|
||||||
|
```sh tab="OTP"
|
||||||
|
./bin/pleroma_ctl frontend install pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh tab="From Source"
|
||||||
|
mix pleroma.frontend install pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
This will download the latest build for the the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`).
|
||||||
|
|
||||||
|
You can override any of the details. To install a pleroma build from a different url, you could do this:
|
||||||
|
|
||||||
|
```sh tab="OPT"
|
||||||
|
./bin/pleroma_ctl frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh tab="From Source"
|
||||||
|
mix pleroma.frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
Similarly, you can also install from a local zip file.
|
||||||
|
|
||||||
|
```sh tab="OTP"
|
||||||
|
./bin/pleroma_ctl frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh tab="From Source"
|
||||||
|
mix pleroma.frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
The resulting frontend will always be installed into a folder of this template: `${instance_static}/frontends/${name}/${ref}`
|
||||||
|
|
||||||
|
Careful: This folder will be completely replaced on installation
|
||||||
|
|
||||||
|
## Example installation for an unknown frontend
|
||||||
|
|
||||||
|
The installation process is the same, but you will have to give all the needed options on the commond line. For example:
|
||||||
|
|
||||||
|
```sh tab="OTP"
|
||||||
|
./bin/pleroma_ctl frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
```sh tab="From Source"
|
||||||
|
mix pleroma.frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don't have a zip file but just want to install a frontend from a local path, you can simply copy the files over a folder of this template: `${instance_static}/frontends/${name}/${ref}`
|
||||||
|
|
|
@ -6,11 +6,11 @@ Feel free to contact us to be added to this list!
|
||||||
### Roma for Desktop
|
### Roma for Desktop
|
||||||
- Homepage: <https://www.pleroma.com/#desktopApp>
|
- Homepage: <https://www.pleroma.com/#desktopApp>
|
||||||
- Source Code: <https://github.com/roma-apps/roma-desktop>
|
- Source Code: <https://github.com/roma-apps/roma-desktop>
|
||||||
- Platforms: Windows, Mac, (Linux?)
|
- Platforms: Windows, Mac, Linux
|
||||||
- Features: Streaming Ready
|
- Features: Streaming Ready
|
||||||
|
|
||||||
### Social
|
### Social
|
||||||
- Source Code: <https://gitlab.gnome.org/BrainBlasted/Social>
|
- Source Code: <https://gitlab.gnome.org/World/Social>
|
||||||
- Contact: [@brainblasted@social.libre.fi](https://social.libre.fi/users/brainblasted)
|
- Contact: [@brainblasted@social.libre.fi](https://social.libre.fi/users/brainblasted)
|
||||||
- Platforms: Linux (GNOME)
|
- Platforms: Linux (GNOME)
|
||||||
- Note(2019-01-28): Not at a pre-alpha stage yet
|
- Note(2019-01-28): Not at a pre-alpha stage yet
|
||||||
|
@ -35,7 +35,7 @@ Feel free to contact us to be added to this list!
|
||||||
- Source Code: <https://framagit.org/tom79/fedilab/>
|
- Source Code: <https://framagit.org/tom79/fedilab/>
|
||||||
- Contact: [@fedilab@framapiaf.org](https://framapiaf.org/users/fedilab)
|
- Contact: [@fedilab@framapiaf.org](https://framapiaf.org/users/fedilab)
|
||||||
- Platforms: Android
|
- Platforms: Android
|
||||||
- Features: Streaming Ready, Moderation, Text Formatting
|
- Features: Streaming Ready, Moderation, Text Formatting
|
||||||
|
|
||||||
### Kyclos
|
### Kyclos
|
||||||
- Source Code: <https://git.pleroma.social/pleroma/harbour-kyclos>
|
- Source Code: <https://git.pleroma.social/pleroma/harbour-kyclos>
|
||||||
|
@ -48,16 +48,9 @@ Feel free to contact us to be added to this list!
|
||||||
- Platforms: Android
|
- Platforms: Android
|
||||||
- Features: No Streaming, Emoji Reactions, Text Formatting, FE Stickers
|
- Features: No Streaming, Emoji Reactions, Text Formatting, FE Stickers
|
||||||
|
|
||||||
### Nekonium
|
|
||||||
- Homepage: [F-Droid Repository](https://repo.gdgd.jp.net/), [Google Play](https://play.google.com/store/apps/details?id=com.apps.nekonium), [Amazon](https://www.amazon.co.jp/dp/B076FXPRBC/)
|
|
||||||
- Source: <https://gogs.gdgd.jp.net/lin/nekonium>
|
|
||||||
- Contact: [@lin@pleroma.gdgd.jp.net](https://pleroma.gdgd.jp.net/users/lin)
|
|
||||||
- Platforms: Android
|
|
||||||
- Features: Streaming Ready
|
|
||||||
|
|
||||||
### Fedi
|
### Fedi
|
||||||
- Homepage: <https://www.fediapp.com/>
|
- Homepage: <https://www.fediapp.com/>
|
||||||
- Source Code: Proprietary, but free
|
- Source Code: Proprietary, but gratis
|
||||||
- Platforms: iOS, Android
|
- Platforms: iOS, Android
|
||||||
- Features: Pleroma-specific features like Reactions
|
- Features: Pleroma-specific features like Reactions
|
||||||
|
|
||||||
|
@ -70,9 +63,9 @@ Feel free to contact us to be added to this list!
|
||||||
|
|
||||||
### Twidere
|
### Twidere
|
||||||
- Homepage: <https://twidere.mariotaku.org/>
|
- Homepage: <https://twidere.mariotaku.org/>
|
||||||
- Source Code: <https://github.com/TwidereProject/Twidere-Android/>, <https://github.com/TwidereProject/Twidere-iOS/>
|
- Source Code: <https://github.com/TwidereProject/Twidere-Android/>
|
||||||
- Contact: <me@mariotaku.org>
|
- Contact: <me@mariotaku.org>
|
||||||
- Platform: Android, iOS
|
- Platform: Android
|
||||||
- Features: No Streaming
|
- Features: No Streaming
|
||||||
|
|
||||||
### Indigenous
|
### Indigenous
|
||||||
|
@ -89,11 +82,6 @@ Feel free to contact us to be added to this list!
|
||||||
- Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc)
|
- Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc)
|
||||||
- Features: No Streaming
|
- Features: No Streaming
|
||||||
|
|
||||||
### Feather
|
|
||||||
- Source Code: <https://github.com/kaniini/feather>
|
|
||||||
- Contact: [@kaniini@pleroma.site](https://pleroma.site/kaniini)
|
|
||||||
- Features: No Streaming
|
|
||||||
|
|
||||||
### Halcyon
|
### Halcyon
|
||||||
- Source Code: <https://notabug.org/halcyon-suite/halcyon>
|
- Source Code: <https://notabug.org/halcyon-suite/halcyon>
|
||||||
- Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon)
|
- Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon)
|
||||||
|
@ -107,6 +95,15 @@ Feel free to contact us to be added to this list!
|
||||||
- Features: No Streaming
|
- Features: No Streaming
|
||||||
|
|
||||||
### Sengi
|
### Sengi
|
||||||
|
- Homepage: <https://nicolasconstant.github.io/sengi/>
|
||||||
- Source Code: <https://github.com/NicolasConstant/sengi>
|
- Source Code: <https://github.com/NicolasConstant/sengi>
|
||||||
- Contact: [@sengi_app@mastodon.social](https://mastodon.social/users/sengi_app)
|
- Contact: [@sengi_app@mastodon.social](https://mastodon.social/users/sengi_app)
|
||||||
- Note(2019-01-28): The development is currently in a early stage.
|
|
||||||
|
### DashFE
|
||||||
|
- Source Code: <https://notabug.org/daisuke/DashboardFE>
|
||||||
|
- Contact: [@dashfe@stereophonic.space](https://stereophonic.space/users/dashfe)
|
||||||
|
|
||||||
|
### BloatFE
|
||||||
|
- Source Code: <https://git.freesoftwareextremist.com/bloat/>
|
||||||
|
- Contact: [@r@freesoftwareextremist.com](https://freesoftwareextremist.com/users/r)
|
||||||
|
- Features: Does not requires JavaScript
|
||||||
|
|
|
@ -552,6 +552,7 @@ the source code is here: [kocaptcha](https://github.com/koto-bank/kocaptcha). Th
|
||||||
* `proxy_remote`: If you're using a remote uploader, Pleroma will proxy media requests instead of redirecting to it.
|
* `proxy_remote`: If you're using a remote uploader, Pleroma will proxy media requests instead of redirecting to it.
|
||||||
* `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation.
|
* `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation.
|
||||||
* `filename_display_max_length`: Set max length of a filename to display. 0 = no limit. Default: 30.
|
* `filename_display_max_length`: Set max length of a filename to display. 0 = no limit. Default: 30.
|
||||||
|
* `default_description`: Sets which default description an image has if none is set explicitly. Options: nil (default) - Don't set a default, :filename - use the filename of the file, a string (e.g. "attachment") - Use this string
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
`strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.
|
`strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.
|
||||||
|
@ -1069,11 +1070,11 @@ Control favicons for instances.
|
||||||
|
|
||||||
Frontends in Pleroma are swappable - you can specify which one to use here.
|
Frontends in Pleroma are swappable - you can specify which one to use here.
|
||||||
|
|
||||||
For now, you can set a frontend with the key `primary` and the options of `name` and `ref`. This will then make Pleroma serve the frontend from a folder constructed by concatenating the instance static path, `frontends` and the name and ref.
|
You can set a frontends for the key `primary` and `admin` and the options of `name` and `ref`. This will then make Pleroma serve the frontend from a folder constructed by concatenating the instance static path, `frontends` and the name and ref.
|
||||||
|
|
||||||
The key `primary` refers to the frontend that will be served by default for general requests. In the future, other frontends like the admin frontend will also be configurable here.
|
The key `primary` refers to the frontend that will be served by default for general requests. The key `admin` refers to the frontend that will be served at the `/pleroma/admin` path.
|
||||||
|
|
||||||
If you don't set anything here, the bundled frontend will be used.
|
If you don't set anything here, the bundled frontends will be used.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
@ -1082,6 +1083,10 @@ config :pleroma, :frontends,
|
||||||
primary: %{
|
primary: %{
|
||||||
"name" => "pleroma",
|
"name" => "pleroma",
|
||||||
"ref" => "stable"
|
"ref" => "stable"
|
||||||
|
},
|
||||||
|
admin: %{
|
||||||
|
"name" => "admin",
|
||||||
|
"ref" => "develop"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,27 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# REQUIRE: DAEMON postgresql
|
# $FreeBSD$
|
||||||
# PROVIDE: pleroma
|
# PROVIDE: pleroma
|
||||||
|
# REQUIRE: DAEMON postgresql
|
||||||
|
# KEYWORD: shutdown
|
||||||
|
|
||||||
# sudo -u pleroma MIX_ENV=prod elixir --erl \"-detached\" -S mix phx.server
|
# sudo -u pleroma MIX_ENV=prod elixir --erl \"-detached\" -S mix phx.server
|
||||||
|
|
||||||
. /etc/rc.subr
|
. /etc/rc.subr
|
||||||
|
|
||||||
name="pleroma"
|
name=pleroma
|
||||||
|
rcvar=pleroma_enable
|
||||||
|
|
||||||
desc="Pleroma Social Media Platform"
|
desc="Pleroma Social Media Platform"
|
||||||
rcvar=${name}_enable
|
|
||||||
command="/usr/local/bin/elixir"
|
|
||||||
command_args="--erl \"-detached\" -S /usr/local/bin/mix phx.server"
|
|
||||||
pidfile="/dev/null"
|
|
||||||
|
|
||||||
pleroma_user="pleroma"
|
|
||||||
pleroma_home="/home/pleroma"
|
|
||||||
pleroma_chdir="${pleroma_home}/pleroma"
|
|
||||||
pleroma_env="HOME=${pleroma_home} MIX_ENV=prod"
|
|
||||||
|
|
||||||
check_pidfile()
|
|
||||||
{
|
|
||||||
pid=$(pgrep beam.smp$)
|
|
||||||
echo -n "${pid}"
|
|
||||||
}
|
|
||||||
|
|
||||||
load_rc_config ${name}
|
load_rc_config ${name}
|
||||||
|
|
||||||
|
: ${pleroma_user:=pleroma}
|
||||||
|
: ${pleroma_home:=$(getent passwd ${pleroma_user} | awk -F: '{print $6}')}
|
||||||
|
: ${pleroma_chdir:="${pleroma_home}/pleroma"}
|
||||||
|
: ${pleroma_env:="HOME=${pleroma_home} MIX_ENV=prod"}
|
||||||
|
|
||||||
|
command=/usr/local/bin/elixir
|
||||||
|
command_args="--erl \"-detached\" -S /usr/local/bin/mix phx.server"
|
||||||
|
procname="*beam.smp"
|
||||||
|
|
||||||
run_rc_command "$1"
|
run_rc_command "$1"
|
||||||
|
|
|
@ -14,7 +14,7 @@ defmodule Mix.Pleroma do
|
||||||
:swoosh,
|
:swoosh,
|
||||||
:timex
|
:timex
|
||||||
]
|
]
|
||||||
@cachex_children ["object", "user"]
|
@cachex_children ["object", "user", "scrubber"]
|
||||||
@doc "Common functions to be reused in mix tasks"
|
@doc "Common functions to be reused in mix tasks"
|
||||||
def start_pleroma do
|
def start_pleroma do
|
||||||
Pleroma.Config.Holder.save_default()
|
Pleroma.Config.Holder.save_default()
|
||||||
|
|
|
@ -15,7 +15,7 @@ def run(["ls-packs" | args]) do
|
||||||
{options, [], []} = parse_global_opts(args)
|
{options, [], []} = parse_global_opts(args)
|
||||||
|
|
||||||
url_or_path = options[:manifest] || default_manifest()
|
url_or_path = options[:manifest] || default_manifest()
|
||||||
manifest = fetch_and_decode(url_or_path)
|
manifest = fetch_and_decode!(url_or_path)
|
||||||
|
|
||||||
Enum.each(manifest, fn {name, info} ->
|
Enum.each(manifest, fn {name, info} ->
|
||||||
to_print = [
|
to_print = [
|
||||||
|
@ -42,7 +42,7 @@ def run(["get-packs" | args]) do
|
||||||
|
|
||||||
url_or_path = options[:manifest] || default_manifest()
|
url_or_path = options[:manifest] || default_manifest()
|
||||||
|
|
||||||
manifest = fetch_and_decode(url_or_path)
|
manifest = fetch_and_decode!(url_or_path)
|
||||||
|
|
||||||
for pack_name <- pack_names do
|
for pack_name <- pack_names do
|
||||||
if Map.has_key?(manifest, pack_name) do
|
if Map.has_key?(manifest, pack_name) do
|
||||||
|
@ -92,7 +92,7 @@ def run(["get-packs" | args]) do
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
files = fetch_and_decode(files_loc)
|
files = fetch_and_decode!(files_loc)
|
||||||
|
|
||||||
IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
|
IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
|
||||||
|
|
||||||
|
@ -243,9 +243,11 @@ def run(["reload"]) do
|
||||||
IO.puts("Emoji packs have been reloaded.")
|
IO.puts("Emoji packs have been reloaded.")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fetch_and_decode(from) do
|
defp fetch_and_decode!(from) do
|
||||||
with {:ok, json} <- fetch(from) do
|
with {:ok, json} <- fetch(from) do
|
||||||
Jason.decode!(json)
|
Jason.decode!(json)
|
||||||
|
else
|
||||||
|
{:error, error} -> raise "#{from} cannot be fetched. Error: #{error} occur."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Pleroma.Frontend do
|
||||||
|
use Mix.Task
|
||||||
|
|
||||||
|
import Mix.Pleroma
|
||||||
|
|
||||||
|
@shortdoc "Manages bundled Pleroma frontends"
|
||||||
|
|
||||||
|
@moduledoc File.read!("docs/administration/CLI_tasks/frontend.md")
|
||||||
|
|
||||||
|
def run(["install", "none" | _args]) do
|
||||||
|
shell_info("Skipping frontend installation because none was requested")
|
||||||
|
"none"
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["install", frontend | args]) do
|
||||||
|
log_level = Logger.level()
|
||||||
|
Logger.configure(level: :warn)
|
||||||
|
start_pleroma()
|
||||||
|
|
||||||
|
{options, [], []} =
|
||||||
|
OptionParser.parse(
|
||||||
|
args,
|
||||||
|
strict: [
|
||||||
|
ref: :string,
|
||||||
|
static_dir: :string,
|
||||||
|
build_url: :string,
|
||||||
|
build_dir: :string,
|
||||||
|
file: :string
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
instance_static_dir =
|
||||||
|
with nil <- options[:static_dir] do
|
||||||
|
Pleroma.Config.get!([:instance, :static_dir])
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd_frontend_info = %{
|
||||||
|
"name" => frontend,
|
||||||
|
"ref" => options[:ref],
|
||||||
|
"build_url" => options[:build_url],
|
||||||
|
"build_dir" => options[:build_dir]
|
||||||
|
}
|
||||||
|
|
||||||
|
config_frontend_info = Pleroma.Config.get([:frontends, :available, frontend], %{})
|
||||||
|
|
||||||
|
frontend_info =
|
||||||
|
Map.merge(config_frontend_info, cmd_frontend_info, fn _key, config, cmd ->
|
||||||
|
# This only overrides things that are actually set
|
||||||
|
cmd || config
|
||||||
|
end)
|
||||||
|
|
||||||
|
ref = frontend_info["ref"]
|
||||||
|
|
||||||
|
unless ref do
|
||||||
|
raise "No ref given or configured"
|
||||||
|
end
|
||||||
|
|
||||||
|
dest =
|
||||||
|
Path.join([
|
||||||
|
instance_static_dir,
|
||||||
|
"frontends",
|
||||||
|
frontend,
|
||||||
|
ref
|
||||||
|
])
|
||||||
|
|
||||||
|
fe_label = "#{frontend} (#{ref})"
|
||||||
|
|
||||||
|
tmp_dir = Path.join(dest, "tmp")
|
||||||
|
|
||||||
|
with {_, :ok} <-
|
||||||
|
{:download_or_unzip, download_or_unzip(frontend_info, tmp_dir, options[:file])},
|
||||||
|
shell_info("Installing #{fe_label} to #{dest}"),
|
||||||
|
:ok <- install_frontend(frontend_info, tmp_dir, dest) do
|
||||||
|
File.rm_rf!(tmp_dir)
|
||||||
|
shell_info("Frontend #{fe_label} installed to #{dest}")
|
||||||
|
|
||||||
|
Logger.configure(level: log_level)
|
||||||
|
else
|
||||||
|
{:download_or_unzip, _} ->
|
||||||
|
shell_info("Could not download or unzip the frontend")
|
||||||
|
|
||||||
|
_e ->
|
||||||
|
shell_info("Could not install the frontend")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp download_or_unzip(frontend_info, temp_dir, file) do
|
||||||
|
if file do
|
||||||
|
with {:ok, zip} <- File.read(Path.expand(file)) do
|
||||||
|
unzip(zip, temp_dir)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
download_build(frontend_info, temp_dir)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unzip(zip, dest) do
|
||||||
|
with {:ok, unzipped} <- :zip.unzip(zip, [:memory]) do
|
||||||
|
File.rm_rf!(dest)
|
||||||
|
File.mkdir_p!(dest)
|
||||||
|
|
||||||
|
Enum.each(unzipped, fn {filename, data} ->
|
||||||
|
path = filename
|
||||||
|
|
||||||
|
new_file_path = Path.join(dest, path)
|
||||||
|
|
||||||
|
new_file_path
|
||||||
|
|> Path.dirname()
|
||||||
|
|> File.mkdir_p!()
|
||||||
|
|
||||||
|
File.write!(new_file_path, data)
|
||||||
|
end)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp download_build(frontend_info, dest) do
|
||||||
|
shell_info("Downloading pre-built bundle for #{frontend_info["name"]}")
|
||||||
|
url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"])
|
||||||
|
|
||||||
|
with {:ok, %{status: 200, body: zip_body}} <-
|
||||||
|
Pleroma.HTTP.get(url, [], timeout: 120_000, recv_timeout: 120_000) do
|
||||||
|
unzip(zip_body, dest)
|
||||||
|
else
|
||||||
|
e -> {:error, e}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp install_frontend(frontend_info, source, dest) do
|
||||||
|
from = frontend_info["build_dir"] || "dist"
|
||||||
|
File.mkdir_p!(dest)
|
||||||
|
File.cp_r!(Path.join([source, from]), dest)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
|
@ -35,10 +35,16 @@ def run(["unfollow", target]) do
|
||||||
def run(["list"]) do
|
def run(["list"]) do
|
||||||
start_pleroma()
|
start_pleroma()
|
||||||
|
|
||||||
with {:ok, list} <- Relay.list(true) do
|
with {:ok, list} <- Relay.list() do
|
||||||
list |> Enum.each(&shell_info(&1))
|
Enum.each(list, &print_relay_url/1)
|
||||||
else
|
else
|
||||||
{:error, e} -> shell_error("Error while fetching relay subscription list: #{inspect(e)}")
|
{:error, e} -> shell_error("Error while fetching relay subscription list: #{inspect(e)}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp print_relay_url(%{followed_back: false} = relay) do
|
||||||
|
shell_info("#{relay.actor} - no Accept received (relay didn't follow back)")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp print_relay_url(relay), do: shell_info(relay.actor)
|
||||||
end
|
end
|
||||||
|
|
|
@ -107,25 +107,34 @@ def digest_email(user) do
|
||||||
|> Enum.filter(&(&1.activity.data["type"] == "Create"))
|
|> Enum.filter(&(&1.activity.data["type"] == "Create"))
|
||||||
|> Enum.map(fn notification ->
|
|> Enum.map(fn notification ->
|
||||||
object = Pleroma.Object.normalize(notification.activity)
|
object = Pleroma.Object.normalize(notification.activity)
|
||||||
object = update_in(object.data["content"], &format_links/1)
|
|
||||||
|
|
||||||
%{
|
if not is_nil(object) do
|
||||||
data: notification,
|
object = update_in(object.data["content"], &format_links/1)
|
||||||
object: object,
|
|
||||||
from: User.get_by_ap_id(notification.activity.actor)
|
%{
|
||||||
}
|
data: notification,
|
||||||
|
object: object,
|
||||||
|
from: User.get_by_ap_id(notification.activity.actor)
|
||||||
|
}
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|> Enum.filter(& &1)
|
||||||
|
|
||||||
followers =
|
followers =
|
||||||
notifications
|
notifications
|
||||||
|> Enum.filter(&(&1.activity.data["type"] == "Follow"))
|
|> Enum.filter(&(&1.activity.data["type"] == "Follow"))
|
||||||
|> Enum.map(fn notification ->
|
|> Enum.map(fn notification ->
|
||||||
%{
|
from = User.get_by_ap_id(notification.activity.actor)
|
||||||
data: notification,
|
|
||||||
object: Pleroma.Object.normalize(notification.activity),
|
if not is_nil(from) do
|
||||||
from: User.get_by_ap_id(notification.activity.actor)
|
%{
|
||||||
}
|
data: notification,
|
||||||
|
object: Pleroma.Object.normalize(notification.activity),
|
||||||
|
from: User.get_by_ap_id(notification.activity.actor)
|
||||||
|
}
|
||||||
|
end
|
||||||
end)
|
end)
|
||||||
|
|> Enum.filter(& &1)
|
||||||
|
|
||||||
unless Enum.empty?(mentions) do
|
unless Enum.empty?(mentions) do
|
||||||
styling = Config.get([__MODULE__, :styling])
|
styling = Config.get([__MODULE__, :styling])
|
||||||
|
|
|
@ -264,4 +264,12 @@ defp validate_following_id_follower_id_inequality(%Changeset{} = changeset) do
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec following_ap_ids(User.t()) :: [String.t()]
|
||||||
|
def following_ap_ids(%User{} = user) do
|
||||||
|
user
|
||||||
|
|> following_query()
|
||||||
|
|> select([r, u], u.ap_id)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,6 +30,7 @@ def init(opts) do
|
||||||
opts
|
opts
|
||||||
|> Keyword.put(:from, "__unconfigured_frontend_static_plug")
|
|> Keyword.put(:from, "__unconfigured_frontend_static_plug")
|
||||||
|> Plug.Static.init()
|
|> Plug.Static.init()
|
||||||
|
|> Map.put(:frontend_type, opts[:frontend_type])
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(conn, opts) do
|
def call(conn, opts) do
|
||||||
|
|
|
@ -56,6 +56,15 @@ defmodule Pleroma.Upload do
|
||||||
}
|
}
|
||||||
defstruct [:id, :name, :tempfile, :content_type, :path]
|
defstruct [:id, :name, :tempfile, :content_type, :path]
|
||||||
|
|
||||||
|
defp get_description(opts, upload) do
|
||||||
|
case {opts[:description], Pleroma.Config.get([Pleroma.Upload, :default_description])} do
|
||||||
|
{description, _} when is_binary(description) -> description
|
||||||
|
{_, :filename} -> upload.name
|
||||||
|
{_, str} when is_binary(str) -> str
|
||||||
|
_ -> ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()}
|
@spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()}
|
||||||
def store(upload, opts \\ []) do
|
def store(upload, opts \\ []) do
|
||||||
opts = get_opts(opts)
|
opts = get_opts(opts)
|
||||||
|
@ -63,7 +72,7 @@ def store(upload, opts \\ []) do
|
||||||
with {:ok, upload} <- prepare_upload(upload, opts),
|
with {:ok, upload} <- prepare_upload(upload, opts),
|
||||||
upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
|
upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
|
||||||
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
|
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
|
||||||
description = Map.get(opts, :description) || upload.name,
|
description = get_description(opts, upload),
|
||||||
{_, true} <-
|
{_, true} <-
|
||||||
{:description_limit,
|
{:description_limit,
|
||||||
String.length(description) <= Pleroma.Config.get([:instance, :description_limit])},
|
String.length(description) <= Pleroma.Config.get([:instance, :description_limit])},
|
||||||
|
|
|
@ -247,6 +247,13 @@ def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defdelegate following_count(user), to: FollowingRelationship
|
||||||
|
defdelegate following(user), to: FollowingRelationship
|
||||||
|
defdelegate following?(follower, followed), to: FollowingRelationship
|
||||||
|
defdelegate following_ap_ids(user), to: FollowingRelationship
|
||||||
|
defdelegate get_follow_requests(user), to: FollowingRelationship
|
||||||
|
defdelegate search(query, opts \\ []), to: User.Search
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Dumps Flake Id to SQL-compatible format (16-byte UUID).
|
Dumps Flake Id to SQL-compatible format (16-byte UUID).
|
||||||
E.g. "9pQtDGXuq4p3VlcJEm" -> <<0, 0, 1, 110, 179, 218, 42, 92, 213, 41, 44, 227, 95, 213, 0, 0>>
|
E.g. "9pQtDGXuq4p3VlcJEm" -> <<0, 0, 1, 110, 179, 218, 42, 92, 213, 41, 44, 227, 95, 213, 0, 0>>
|
||||||
|
@ -372,8 +379,6 @@ def restrict_deactivated(query) do
|
||||||
from(u in query, where: u.deactivated != ^true)
|
from(u in query, where: u.deactivated != ^true)
|
||||||
end
|
end
|
||||||
|
|
||||||
defdelegate following_count(user), to: FollowingRelationship
|
|
||||||
|
|
||||||
defp truncate_fields_param(params) do
|
defp truncate_fields_param(params) do
|
||||||
if Map.has_key?(params, :fields) do
|
if Map.has_key?(params, :fields) do
|
||||||
Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1))
|
Map.put(params, :fields, Enum.map(params[:fields], &truncate_field/1))
|
||||||
|
@ -868,8 +873,6 @@ def follow_all(follower, followeds) do
|
||||||
set_cache(follower)
|
set_cache(follower)
|
||||||
end
|
end
|
||||||
|
|
||||||
defdelegate following(user), to: FollowingRelationship
|
|
||||||
|
|
||||||
def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do
|
def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do
|
||||||
deny_follow_blocked = Config.get([:user, :deny_follow_blocked])
|
deny_follow_blocked = Config.get([:user, :deny_follow_blocked])
|
||||||
|
|
||||||
|
@ -923,8 +926,6 @@ defp do_unfollow(%User{} = follower, %User{} = followed) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defdelegate following?(follower, followed), to: FollowingRelationship
|
|
||||||
|
|
||||||
@doc "Returns follow state as Pleroma.FollowingRelationship.State value"
|
@doc "Returns follow state as Pleroma.FollowingRelationship.State value"
|
||||||
def get_follow_state(%User{} = follower, %User{} = following) do
|
def get_follow_state(%User{} = follower, %User{} = following) do
|
||||||
following_relationship = FollowingRelationship.get(follower, following)
|
following_relationship = FollowingRelationship.get(follower, following)
|
||||||
|
@ -1189,8 +1190,6 @@ def get_friends_ids(user, page \\ nil) do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
defdelegate get_follow_requests(user), to: FollowingRelationship
|
|
||||||
|
|
||||||
def increase_note_count(%User{} = user) do
|
def increase_note_count(%User{} = user) do
|
||||||
User
|
User
|
||||||
|> where(id: ^user.id)
|
|> where(id: ^user.id)
|
||||||
|
@ -2163,8 +2162,6 @@ def get_ap_ids_by_nicknames(nicknames) do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
defdelegate search(query, opts \\ []), to: User.Search
|
|
||||||
|
|
||||||
defp put_password_hash(
|
defp put_password_hash(
|
||||||
%Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset
|
%Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset
|
||||||
) do
|
) do
|
||||||
|
|
|
@ -85,7 +85,7 @@ defp increase_replies_count_if_reply(%{
|
||||||
|
|
||||||
defp increase_replies_count_if_reply(_create_data), do: :noop
|
defp increase_replies_count_if_reply(_create_data), do: :noop
|
||||||
|
|
||||||
@object_types ["ChatMessage", "Question", "Answer"]
|
@object_types ~w[ChatMessage Question Answer Audio Event]
|
||||||
@spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
|
@spec persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
|
||||||
def persist(%{"type" => type} = object, meta) when type in @object_types do
|
def persist(%{"type" => type} = object, meta) when type in @object_types do
|
||||||
with {:ok, object} <- Object.create(object) do
|
with {:ok, object} <- Object.create(object) do
|
||||||
|
@ -1344,9 +1344,8 @@ def fetch_and_prepare_user_from_ap_id(ap_id) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def maybe_handle_clashing_nickname(data) do
|
def maybe_handle_clashing_nickname(data) do
|
||||||
nickname = data[:nickname]
|
with nickname when is_binary(nickname) <- data[:nickname],
|
||||||
|
%User{} = old_user <- User.get_by_nickname(nickname),
|
||||||
with %User{} = old_user <- User.get_by_nickname(nickname),
|
|
||||||
{_, false} <- {:ap_id_comparison, data[:ap_id] == old_user.ap_id} do
|
{_, false} <- {:ap_id_comparison, data[:ap_id] == old_user.ap_id} do
|
||||||
Logger.info(
|
Logger.info(
|
||||||
"Found an old user for #{nickname}, the old ap id is #{old_user.ap_id}, new one is #{
|
"Found an old user for #{nickname}, the old ap id is #{old_user.ap_id}, new one is #{
|
||||||
|
@ -1360,7 +1359,7 @@ def maybe_handle_clashing_nickname(data) do
|
||||||
else
|
else
|
||||||
{:ap_id_comparison, true} ->
|
{:ap_id_comparison, true} ->
|
||||||
Logger.info(
|
Logger.info(
|
||||||
"Found an old user for #{nickname}, but the ap id #{data[:ap_id]} is the same as the new user. Race condition? Not changing anything."
|
"Found an old user for #{data[:nickname]}, but the ap id #{data[:ap_id]} is the same as the new user. Race condition? Not changing anything."
|
||||||
)
|
)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
|
|
|
@ -215,7 +215,7 @@ def announce(actor, object, options \\ []) do
|
||||||
|
|
||||||
to =
|
to =
|
||||||
cond do
|
cond do
|
||||||
actor.ap_id == Relay.relay_ap_id() ->
|
actor.ap_id == Relay.ap_id() ->
|
||||||
[actor.follower_address]
|
[actor.follower_address]
|
||||||
|
|
||||||
public? ->
|
public? ->
|
||||||
|
|
|
@ -23,6 +23,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.DeleteValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.EventValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.FollowValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.LikeValidator
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator
|
alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator
|
||||||
|
@ -43,6 +44,16 @@ def validate(%{"type" => type} = object, meta)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate(%{"type" => "Event"} = object, meta) do
|
||||||
|
with {:ok, object} <-
|
||||||
|
object
|
||||||
|
|> EventValidator.cast_and_validate()
|
||||||
|
|> Ecto.Changeset.apply_action(:insert) do
|
||||||
|
object = stringify_keys(object)
|
||||||
|
{:ok, object, meta}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def validate(%{"type" => "Follow"} = object, meta) do
|
def validate(%{"type" => "Follow"} = object, meta) do
|
||||||
with {:ok, object} <-
|
with {:ok, object} <-
|
||||||
object
|
object
|
||||||
|
@ -187,7 +198,7 @@ def validate(
|
||||||
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
|
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
|
||||||
meta
|
meta
|
||||||
)
|
)
|
||||||
when objtype in ~w[Question Answer Audio] do
|
when objtype in ~w[Question Answer Audio Event] do
|
||||||
with {:ok, object_data} <- cast_and_apply(object),
|
with {:ok, object_data} <- cast_and_apply(object),
|
||||||
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
|
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
|
||||||
{:ok, create_activity} <-
|
{:ok, create_activity} <-
|
||||||
|
@ -225,6 +236,10 @@ def cast_and_apply(%{"type" => "Audio"} = object) do
|
||||||
AudioValidator.cast_and_apply(object)
|
AudioValidator.cast_and_apply(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cast_and_apply(%{"type" => "Event"} = object) do
|
||||||
|
EventValidator.cast_and_apply(object)
|
||||||
|
end
|
||||||
|
|
||||||
def cast_and_apply(o), do: {:error, {:validator_not_set, o}}
|
def cast_and_apply(o), do: {:error, {:validator_not_set, o}}
|
||||||
|
|
||||||
# is_struct/1 isn't present in Elixir 1.8.x
|
# is_struct/1 isn't present in Elixir 1.8.x
|
||||||
|
|
|
@ -41,34 +41,34 @@ def changeset(struct, data) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_media_type(data) do
|
def fix_media_type(data) do
|
||||||
data =
|
data = Map.put_new(data, "mediaType", data["mimeType"])
|
||||||
data
|
|
||||||
|> Map.put_new("mediaType", data["mimeType"])
|
|
||||||
|
|
||||||
if MIME.valid?(data["mediaType"]) do
|
if MIME.valid?(data["mediaType"]) do
|
||||||
data
|
data
|
||||||
else
|
else
|
||||||
data
|
Map.put(data, "mediaType", "application/octet-stream")
|
||||||
|> Map.put("mediaType", "application/octet-stream")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_url(data) do
|
defp handle_href(href, mediaType) do
|
||||||
case data["url"] do
|
[
|
||||||
url when is_binary(url) ->
|
%{
|
||||||
data
|
"href" => href,
|
||||||
|> Map.put(
|
"type" => "Link",
|
||||||
"url",
|
"mediaType" => mediaType
|
||||||
[
|
}
|
||||||
%{
|
]
|
||||||
"href" => url,
|
end
|
||||||
"type" => "Link",
|
|
||||||
"mediaType" => data["mediaType"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
_ ->
|
defp fix_url(data) do
|
||||||
|
cond do
|
||||||
|
is_binary(data["url"]) ->
|
||||||
|
Map.put(data, "url", handle_href(data["url"], data["mediaType"]))
|
||||||
|
|
||||||
|
is_binary(data["href"]) and data["url"] == nil ->
|
||||||
|
Map.put(data, "url", handle_href(data["href"], data["mediaType"]))
|
||||||
|
|
||||||
|
true ->
|
||||||
data
|
data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -41,7 +41,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do
|
||||||
field(:like_count, :integer, default: 0)
|
field(:like_count, :integer, default: 0)
|
||||||
field(:announcement_count, :integer, default: 0)
|
field(:announcement_count, :integer, default: 0)
|
||||||
field(:inReplyTo, :string)
|
field(:inReplyTo, :string)
|
||||||
field(:uri, ObjectValidators.Uri)
|
field(:url, ObjectValidators.Uri)
|
||||||
# short identifier for PleromaFE to group statuses by context
|
# short identifier for PleromaFE to group statuses by context
|
||||||
field(:context_id, :integer)
|
field(:context_id, :integer)
|
||||||
|
|
||||||
|
@ -66,10 +66,24 @@ def cast_data(data) do
|
||||||
|> changeset(data)
|
|> changeset(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp fix_url(%{"url" => url} = data) when is_list(url) do
|
||||||
|
attachment =
|
||||||
|
Enum.find(url, fn x -> is_map(x) and String.starts_with?(x["mimeType"], "audio/") end)
|
||||||
|
|
||||||
|
link_element = Enum.find(url, fn x -> is_map(x) and x["mimeType"] == "text/html" end)
|
||||||
|
|
||||||
|
data
|
||||||
|
|> Map.put("attachment", [attachment])
|
||||||
|
|> Map.put("url", link_element["href"])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fix_url(data), do: data
|
||||||
|
|
||||||
defp fix(data) do
|
defp fix(data) do
|
||||||
data
|
data
|
||||||
|> CommonFixes.fix_defaults()
|
|> CommonFixes.fix_defaults()
|
||||||
|> CommonFixes.fix_attribution()
|
|> CommonFixes.fix_attribution()
|
||||||
|
|> fix_url()
|
||||||
end
|
end
|
||||||
|
|
||||||
def changeset(struct, data) do
|
def changeset(struct, data) do
|
||||||
|
@ -83,7 +97,7 @@ def changeset(struct, data) do
|
||||||
def validate_data(data_cng) do
|
def validate_data(data_cng) do
|
||||||
data_cng
|
data_cng
|
||||||
|> validate_inclusion(:type, ["Audio"])
|
|> validate_inclusion(:type, ["Audio"])
|
||||||
|> validate_required([:id, :actor, :attributedTo, :type, :context])
|
|> validate_required([:id, :actor, :attributedTo, :type, :context, :attachment])
|
||||||
|> CommonValidations.validate_any_presence([:cc, :to])
|
|> CommonValidations.validate_any_presence([:cc, :to])
|
||||||
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|
||||||
|> CommonValidations.validate_actor_presence()
|
|> CommonValidations.validate_actor_presence()
|
||||||
|
|
|
@ -61,9 +61,20 @@ defp fix_context(data, meta) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp fix_addressing(data, meta) do
|
||||||
|
if object = meta[:object_data] do
|
||||||
|
data
|
||||||
|
|> Map.put_new("to", object["to"] || [])
|
||||||
|
|> Map.put_new("cc", object["cc"] || [])
|
||||||
|
else
|
||||||
|
data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp fix(data, meta) do
|
defp fix(data, meta) do
|
||||||
data
|
data
|
||||||
|> fix_context(meta)
|
|> fix_context(meta)
|
||||||
|
|> fix_addressing(meta)
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_data(cng, meta \\ []) do
|
def validate_data(cng, meta \\ []) do
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
@primary_key false
|
||||||
|
@derive Jason.Encoder
|
||||||
|
|
||||||
|
# Extends from NoteValidator
|
||||||
|
embedded_schema do
|
||||||
|
field(:id, ObjectValidators.ObjectID, primary_key: true)
|
||||||
|
field(:to, ObjectValidators.Recipients, default: [])
|
||||||
|
field(:cc, ObjectValidators.Recipients, default: [])
|
||||||
|
field(:bto, ObjectValidators.Recipients, default: [])
|
||||||
|
field(:bcc, ObjectValidators.Recipients, default: [])
|
||||||
|
# TODO: Write type
|
||||||
|
field(:tag, {:array, :map}, default: [])
|
||||||
|
field(:type, :string)
|
||||||
|
|
||||||
|
field(:name, :string)
|
||||||
|
field(:summary, :string)
|
||||||
|
field(:content, :string)
|
||||||
|
|
||||||
|
field(:context, :string)
|
||||||
|
# short identifier for PleromaFE to group statuses by context
|
||||||
|
field(:context_id, :integer)
|
||||||
|
|
||||||
|
# TODO: Remove actor on objects
|
||||||
|
field(:actor, ObjectValidators.ObjectID)
|
||||||
|
|
||||||
|
field(:attributedTo, ObjectValidators.ObjectID)
|
||||||
|
field(:published, ObjectValidators.DateTime)
|
||||||
|
# TODO: Write type
|
||||||
|
field(:emoji, :map, default: %{})
|
||||||
|
field(:sensitive, :boolean, default: false)
|
||||||
|
embeds_many(:attachment, AttachmentValidator)
|
||||||
|
field(:replies_count, :integer, default: 0)
|
||||||
|
field(:like_count, :integer, default: 0)
|
||||||
|
field(:announcement_count, :integer, default: 0)
|
||||||
|
field(:inReplyTo, ObjectValidators.ObjectID)
|
||||||
|
field(:url, ObjectValidators.Uri)
|
||||||
|
|
||||||
|
field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
|
field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_and_apply(data) do
|
||||||
|
data
|
||||||
|
|> cast_data
|
||||||
|
|> apply_action(:insert)
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_and_validate(data) do
|
||||||
|
data
|
||||||
|
|> cast_data()
|
||||||
|
|> validate_data()
|
||||||
|
end
|
||||||
|
|
||||||
|
def cast_data(data) do
|
||||||
|
%__MODULE__{}
|
||||||
|
|> changeset(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fix(data) do
|
||||||
|
data
|
||||||
|
|> CommonFixes.fix_defaults()
|
||||||
|
|> CommonFixes.fix_attribution()
|
||||||
|
end
|
||||||
|
|
||||||
|
def changeset(struct, data) do
|
||||||
|
data = fix(data)
|
||||||
|
|
||||||
|
struct
|
||||||
|
|> cast(data, __schema__(:fields) -- [:attachment])
|
||||||
|
|> cast_embed(:attachment)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_data(data_cng) do
|
||||||
|
data_cng
|
||||||
|
|> validate_inclusion(:type, ["Event"])
|
||||||
|
|> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id])
|
||||||
|
|> CommonValidations.validate_any_presence([:cc, :to])
|
||||||
|
|> CommonValidations.validate_fields_match([:actor, :attributedTo])
|
||||||
|
|> CommonValidations.validate_actor_presence()
|
||||||
|
|> CommonValidations.validate_host_match()
|
||||||
|
end
|
||||||
|
end
|
|
@ -20,11 +20,17 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
|
||||||
# TODO: Write type
|
# TODO: Write type
|
||||||
field(:tag, {:array, :map}, default: [])
|
field(:tag, {:array, :map}, default: [])
|
||||||
field(:type, :string)
|
field(:type, :string)
|
||||||
|
|
||||||
|
field(:name, :string)
|
||||||
|
field(:summary, :string)
|
||||||
field(:content, :string)
|
field(:content, :string)
|
||||||
|
|
||||||
field(:context, :string)
|
field(:context, :string)
|
||||||
|
# short identifier for PleromaFE to group statuses by context
|
||||||
|
field(:context_id, :integer)
|
||||||
|
|
||||||
field(:actor, ObjectValidators.ObjectID)
|
field(:actor, ObjectValidators.ObjectID)
|
||||||
field(:attributedTo, ObjectValidators.ObjectID)
|
field(:attributedTo, ObjectValidators.ObjectID)
|
||||||
field(:summary, :string)
|
|
||||||
field(:published, ObjectValidators.DateTime)
|
field(:published, ObjectValidators.DateTime)
|
||||||
# TODO: Write type
|
# TODO: Write type
|
||||||
field(:emoji, :map, default: %{})
|
field(:emoji, :map, default: %{})
|
||||||
|
@ -35,13 +41,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
|
||||||
field(:like_count, :integer, default: 0)
|
field(:like_count, :integer, default: 0)
|
||||||
field(:announcement_count, :integer, default: 0)
|
field(:announcement_count, :integer, default: 0)
|
||||||
field(:inReplyTo, ObjectValidators.ObjectID)
|
field(:inReplyTo, ObjectValidators.ObjectID)
|
||||||
field(:uri, ObjectValidators.Uri)
|
field(:url, ObjectValidators.Uri)
|
||||||
|
|
||||||
field(:likes, {:array, :string}, default: [])
|
field(:likes, {:array, :string}, default: [])
|
||||||
field(:announcements, {:array, :string}, default: [])
|
field(:announcements, {:array, :string}, default: [])
|
||||||
|
|
||||||
# see if needed
|
|
||||||
field(:context_id, :string)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast_and_validate(data) do
|
def cast_and_validate(data) do
|
||||||
|
|
|
@ -43,7 +43,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
|
||||||
field(:like_count, :integer, default: 0)
|
field(:like_count, :integer, default: 0)
|
||||||
field(:announcement_count, :integer, default: 0)
|
field(:announcement_count, :integer, default: 0)
|
||||||
field(:inReplyTo, ObjectValidators.ObjectID)
|
field(:inReplyTo, ObjectValidators.ObjectID)
|
||||||
field(:uri, ObjectValidators.Uri)
|
field(:url, ObjectValidators.Uri)
|
||||||
# short identifier for PleromaFE to group statuses by context
|
# short identifier for PleromaFE to group statuses by context
|
||||||
field(:context_id, :integer)
|
field(:context_id, :integer)
|
||||||
|
|
||||||
|
|
|
@ -10,19 +10,13 @@ defmodule Pleroma.Web.ActivityPub.Relay do
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@relay_nickname "relay"
|
@nickname "relay"
|
||||||
|
|
||||||
def get_actor do
|
@spec ap_id() :: String.t()
|
||||||
actor =
|
def ap_id, do: "#{Pleroma.Web.Endpoint.url()}/#{@nickname}"
|
||||||
relay_ap_id()
|
|
||||||
|> User.get_or_create_service_actor_by_ap_id(@relay_nickname)
|
|
||||||
|
|
||||||
actor
|
@spec get_actor() :: User.t() | nil
|
||||||
end
|
def get_actor, do: User.get_or_create_service_actor_by_ap_id(ap_id(), @nickname)
|
||||||
|
|
||||||
def relay_ap_id do
|
|
||||||
"#{Pleroma.Web.Endpoint.url()}/relay"
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec follow(String.t()) :: {:ok, Activity.t()} | {:error, any()}
|
@spec follow(String.t()) :: {:ok, Activity.t()} | {:error, any()}
|
||||||
def follow(target_instance) do
|
def follow(target_instance) do
|
||||||
|
@ -61,34 +55,38 @@ def publish(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||||
|
|
||||||
def publish(_), do: {:error, "Not implemented"}
|
def publish(_), do: {:error, "Not implemented"}
|
||||||
|
|
||||||
@spec list(boolean()) :: {:ok, [String.t()]} | {:error, any()}
|
@spec list() :: {:ok, [%{actor: String.t(), followed_back: boolean()}]} | {:error, any()}
|
||||||
def list(with_not_accepted \\ false) do
|
def list do
|
||||||
with %User{} = user <- get_actor() do
|
with %User{} = user <- get_actor() do
|
||||||
accepted =
|
accepted =
|
||||||
user
|
user
|
||||||
|> User.following()
|
|> following()
|
||||||
|> Enum.map(fn entry -> URI.parse(entry).host end)
|
|> Enum.map(fn actor -> %{actor: actor, followed_back: true} end)
|
||||||
|
|
||||||
|
without_accept =
|
||||||
|
user
|
||||||
|
|> Pleroma.Activity.following_requests_for_actor()
|
||||||
|
|> Enum.map(fn activity -> %{actor: activity.data["object"], followed_back: false} end)
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|
|
||||||
list =
|
{:ok, accepted ++ without_accept}
|
||||||
if with_not_accepted do
|
|
||||||
without_accept =
|
|
||||||
user
|
|
||||||
|> Pleroma.Activity.following_requests_for_actor()
|
|
||||||
|> Enum.map(fn a -> URI.parse(a.data["object"]).host <> " (no Accept received)" end)
|
|
||||||
|> Enum.uniq()
|
|
||||||
|
|
||||||
accepted ++ without_accept
|
|
||||||
else
|
|
||||||
accepted
|
|
||||||
end
|
|
||||||
|
|
||||||
{:ok, list}
|
|
||||||
else
|
else
|
||||||
error -> format_error(error)
|
error -> format_error(error)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec following() :: [String.t()]
|
||||||
|
def following do
|
||||||
|
get_actor()
|
||||||
|
|> following()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp following(user) do
|
||||||
|
user
|
||||||
|
|> User.following_ap_ids()
|
||||||
|
|> Enum.uniq()
|
||||||
|
end
|
||||||
|
|
||||||
defp format_error({:error, error}), do: format_error(error)
|
defp format_error({:error, error}), do: format_error(error)
|
||||||
|
|
||||||
defp format_error(error) do
|
defp format_error(error) do
|
||||||
|
|
|
@ -341,7 +341,7 @@ def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_object_creation(%{"type" => objtype} = object, meta)
|
def handle_object_creation(%{"type" => objtype} = object, meta)
|
||||||
when objtype in ~w[Audio Question] do
|
when objtype in ~w[Audio Question Event] do
|
||||||
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
|
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
|
||||||
{:ok, object, meta}
|
{:ok, object, meta}
|
||||||
end
|
end
|
||||||
|
|
|
@ -276,13 +276,12 @@ def fix_url(%{"url" => url} = object) when is_map(url) do
|
||||||
Map.put(object, "url", url["href"])
|
Map.put(object, "url", url["href"])
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_url(%{"type" => object_type, "url" => url} = object)
|
def fix_url(%{"type" => "Video", "url" => url} = object) when is_list(url) do
|
||||||
when object_type in ["Video", "Audio"] and is_list(url) do
|
|
||||||
attachment =
|
attachment =
|
||||||
Enum.find(url, fn x ->
|
Enum.find(url, fn x ->
|
||||||
media_type = x["mediaType"] || x["mimeType"] || ""
|
media_type = x["mediaType"] || x["mimeType"] || ""
|
||||||
|
|
||||||
is_map(x) and String.starts_with?(media_type, ["audio/", "video/"])
|
is_map(x) and String.starts_with?(media_type, "video/")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
link_element =
|
link_element =
|
||||||
|
@ -461,7 +460,7 @@ def handle_incoming(
|
||||||
%{"type" => "Create", "object" => %{"type" => objtype} = object} = data,
|
%{"type" => "Create", "object" => %{"type" => objtype} = object} = data,
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
when objtype in ~w{Article Event Note Video Page} do
|
when objtype in ~w{Article Note Video Page} do
|
||||||
actor = Containment.get_actor(data)
|
actor = Containment.get_actor(data)
|
||||||
|
|
||||||
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
|
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
|
||||||
|
@ -555,7 +554,7 @@ def handle_incoming(
|
||||||
%{"type" => "Create", "object" => %{"type" => objtype}} = data,
|
%{"type" => "Create", "object" => %{"type" => objtype}} = data,
|
||||||
_options
|
_options
|
||||||
)
|
)
|
||||||
when objtype in ~w{Question Answer ChatMessage Audio} do
|
when objtype in ~w{Question Answer ChatMessage Audio Event} do
|
||||||
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
|
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
|
||||||
{:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
|
{:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
|
|
@ -39,7 +39,7 @@ def follow(%{assigns: %{user: admin}, body_params: %{relay_url: target}} = conn,
|
||||||
target: target
|
target: target
|
||||||
})
|
})
|
||||||
|
|
||||||
json(conn, target)
|
json(conn, %{actor: target, followed_back: target in Relay.following()})
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -79,7 +79,8 @@ def render("show.json", %{user: user}) do
|
||||||
"confirmation_pending" => user.confirmation_pending,
|
"confirmation_pending" => user.confirmation_pending,
|
||||||
"approval_pending" => user.approval_pending,
|
"approval_pending" => user.approval_pending,
|
||||||
"url" => user.uri || user.ap_id,
|
"url" => user.uri || user.ap_id,
|
||||||
"registration_reason" => user.registration_reason
|
"registration_reason" => user.registration_reason,
|
||||||
|
"actor_type" => user.actor_type
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,7 @@ def index_operation do
|
||||||
properties: %{
|
properties: %{
|
||||||
relays: %Schema{
|
relays: %Schema{
|
||||||
type: :array,
|
type: :array,
|
||||||
items: %Schema{type: :string},
|
items: relay()
|
||||||
example: ["lain.com", "mstdn.io"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -43,19 +42,9 @@ def follow_operation do
|
||||||
operationId: "AdminAPI.RelayController.follow",
|
operationId: "AdminAPI.RelayController.follow",
|
||||||
security: [%{"oAuth" => ["write:follows"]}],
|
security: [%{"oAuth" => ["write:follows"]}],
|
||||||
parameters: admin_api_params(),
|
parameters: admin_api_params(),
|
||||||
requestBody:
|
requestBody: request_body("Parameters", relay_url()),
|
||||||
request_body("Parameters", %Schema{
|
|
||||||
type: :object,
|
|
||||||
properties: %{
|
|
||||||
relay_url: %Schema{type: :string, format: :uri}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 => Operation.response("Status", "application/json", relay())
|
||||||
Operation.response("Status", "application/json", %Schema{
|
|
||||||
type: :string,
|
|
||||||
example: "http://mastodon.example.org/users/admin"
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -67,13 +56,7 @@ def unfollow_operation do
|
||||||
operationId: "AdminAPI.RelayController.unfollow",
|
operationId: "AdminAPI.RelayController.unfollow",
|
||||||
security: [%{"oAuth" => ["write:follows"]}],
|
security: [%{"oAuth" => ["write:follows"]}],
|
||||||
parameters: admin_api_params(),
|
parameters: admin_api_params(),
|
||||||
requestBody:
|
requestBody: request_body("Parameters", relay_url()),
|
||||||
request_body("Parameters", %Schema{
|
|
||||||
type: :object,
|
|
||||||
properties: %{
|
|
||||||
relay_url: %Schema{type: :string, format: :uri}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
responses: %{
|
responses: %{
|
||||||
200 =>
|
200 =>
|
||||||
Operation.response("Status", "application/json", %Schema{
|
Operation.response("Status", "application/json", %Schema{
|
||||||
|
@ -83,4 +66,29 @@ def unfollow_operation do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp relay do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
actor: %Schema{
|
||||||
|
type: :string,
|
||||||
|
example: "https://example.com/relay"
|
||||||
|
},
|
||||||
|
followed_back: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description: "Is relay followed back by this actor?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp relay_url do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
relay_url: %Schema{type: :string, format: :uri}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,6 +39,18 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
plug(Plug.Static.IndexHtml, at: "/pleroma/admin/")
|
||||||
|
|
||||||
|
plug(Pleroma.Plugs.FrontendStatic,
|
||||||
|
at: "/pleroma/admin",
|
||||||
|
frontend_type: :admin,
|
||||||
|
gzip: true,
|
||||||
|
cache_control_for_etags: @static_cache_control,
|
||||||
|
headers: %{
|
||||||
|
"cache-control" => @static_cache_control
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Serve at "/" the static files from "priv/static" directory.
|
# Serve at "/" the static files from "priv/static" directory.
|
||||||
#
|
#
|
||||||
# You should set gzip to true if you are running phoenix.digest
|
# You should set gzip to true if you are running phoenix.digest
|
||||||
|
@ -56,8 +68,6 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(Plug.Static.IndexHtml, at: "/pleroma/admin/")
|
|
||||||
|
|
||||||
plug(Plug.Static,
|
plug(Plug.Static,
|
||||||
at: "/pleroma/admin/",
|
at: "/pleroma/admin/",
|
||||||
from: {:pleroma, "priv/static/adminfe/"}
|
from: {:pleroma, "priv/static/adminfe/"}
|
||||||
|
|
|
@ -474,23 +474,10 @@ def get_reply_to(%{data: %{"object" => _object}} = activity, _) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_content(%{data: %{"type" => object_type}} = object)
|
def render_content(%{data: %{"name" => name}} = object) when not is_nil(name) and name != "" do
|
||||||
when object_type in ["Video", "Event", "Audio"] do
|
url = object.data["url"] || object.data["id"]
|
||||||
with name when not is_nil(name) and name != "" <- object.data["name"] do
|
|
||||||
"<p><a href=\"#{object.data["id"]}\">#{name}</a></p>#{object.data["content"]}"
|
|
||||||
else
|
|
||||||
_ -> object.data["content"] || ""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_content(%{data: %{"type" => object_type}} = object)
|
"<p><a href=\"#{url}\">#{name}</a></p>#{object.data["content"]}"
|
||||||
when object_type in ["Article", "Page"] do
|
|
||||||
with summary when not is_nil(summary) and summary != "" <- object.data["name"],
|
|
||||||
url when is_bitstring(url) <- object.data["url"] do
|
|
||||||
"<p><a href=\"#{url}\">#{summary}</a></p>#{object.data["content"]}"
|
|
||||||
else
|
|
||||||
_ -> object.data["content"] || ""
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_content(object), do: object.data["content"] || ""
|
def render_content(object), do: object.data["content"] || ""
|
||||||
|
|
|
@ -14,10 +14,10 @@ defmodule Pleroma.Emails.MailerTest do
|
||||||
subject: "Pleroma test email",
|
subject: "Pleroma test email",
|
||||||
to: [{"Test User", "user1@example.com"}]
|
to: [{"Test User", "user1@example.com"}]
|
||||||
}
|
}
|
||||||
setup do: clear_config([Pleroma.Emails.Mailer, :enabled])
|
setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true)
|
||||||
|
|
||||||
test "not send email when mailer is disabled" do
|
test "not send email when mailer is disabled" do
|
||||||
Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false)
|
clear_config([Pleroma.Emails.Mailer, :enabled], false)
|
||||||
Mailer.deliver(@email)
|
Mailer.deliver(@email)
|
||||||
:timer.sleep(100)
|
:timer.sleep(100)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
this is a text file
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
"https://funkwhale.audio/ns",
|
||||||
|
{
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"Hashtag": "as:Hashtag"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "Create",
|
||||||
|
"id": "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871/activity",
|
||||||
|
"actor": "https://channels.tests.funkwhale.audio/federation/actors/compositions",
|
||||||
|
"object": {
|
||||||
|
"id": "https://channels.tests.funkwhale.audio/federation/music/uploads/42342395-0208-4fee-a38d-259a6dae0871",
|
||||||
|
"type": "Audio",
|
||||||
|
"name": "Compositions - Test Audio for Pleroma",
|
||||||
|
"attributedTo": "https://channels.tests.funkwhale.audio/federation/actors/compositions",
|
||||||
|
"published": "2020-03-11T10:01:52.714918+00:00",
|
||||||
|
"to": "https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"url": [
|
||||||
|
{
|
||||||
|
"type": "Link",
|
||||||
|
"mimeType": "audio/ogg",
|
||||||
|
"href": "https://channels.tests.funkwhale.audio/api/v1/listen/3901e5d8-0445-49d5-9711-e096cf32e515/?upload=42342395-0208-4fee-a38d-259a6dae0871&download=false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Link",
|
||||||
|
"mimeType": "text/html",
|
||||||
|
"href": "https://channels.tests.funkwhale.audio/library/tracks/74"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"content": "<p>This is a test Audio for Pleroma.</p>",
|
||||||
|
"mediaType": "text/html",
|
||||||
|
"tag": [
|
||||||
|
{
|
||||||
|
"type": "Hashtag",
|
||||||
|
"name": "#funkwhale"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Hashtag",
|
||||||
|
"name": "#test"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Hashtag",
|
||||||
|
"name": "#tests"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"summary": "#funkwhale #test #tests",
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
{
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.FrontendStaticPlugTest do
|
defmodule Pleroma.Web.FrontendStaticPlugTest do
|
||||||
|
alias Pleroma.Plugs.FrontendStatic
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
@dir "test/tmp/instance_static"
|
@dir "test/tmp/instance_static"
|
||||||
|
@ -14,6 +15,18 @@ defmodule Pleroma.Web.FrontendStaticPlugTest do
|
||||||
|
|
||||||
setup do: clear_config([:instance, :static_dir], @dir)
|
setup do: clear_config([:instance, :static_dir], @dir)
|
||||||
|
|
||||||
|
test "init will give a static plug config + the frontend type" do
|
||||||
|
opts =
|
||||||
|
[
|
||||||
|
at: "/admin",
|
||||||
|
frontend_type: :admin
|
||||||
|
]
|
||||||
|
|> FrontendStatic.init()
|
||||||
|
|
||||||
|
assert opts[:at] == ["admin"]
|
||||||
|
assert opts[:frontend_type] == :admin
|
||||||
|
end
|
||||||
|
|
||||||
test "overrides existing static files", %{conn: conn} do
|
test "overrides existing static files", %{conn: conn} do
|
||||||
name = "pelmora"
|
name = "pelmora"
|
||||||
ref = "uguu"
|
ref = "uguu"
|
||||||
|
@ -27,4 +40,18 @@ test "overrides existing static files", %{conn: conn} do
|
||||||
index = get(conn, "/")
|
index = get(conn, "/")
|
||||||
assert html_response(index, 200) == "from frontend plug"
|
assert html_response(index, 200) == "from frontend plug"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "overrides existing static files for the `pleroma/admin` path", %{conn: conn} do
|
||||||
|
name = "pelmora"
|
||||||
|
ref = "uguu"
|
||||||
|
|
||||||
|
clear_config([:frontends, :admin], %{"name" => name, "ref" => ref})
|
||||||
|
path = "#{@dir}/frontends/#{name}/#{ref}"
|
||||||
|
|
||||||
|
File.mkdir_p!(path)
|
||||||
|
File.write!("#{path}/index.html", "from frontend plug")
|
||||||
|
|
||||||
|
index = get(conn, "/pleroma/admin/")
|
||||||
|
assert html_response(index, 200) == "from frontend plug"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,6 +17,8 @@ defmodule Mix.Tasks.Pleroma.DigestTest do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true)
|
||||||
|
|
||||||
describe "pleroma.digest test" do
|
describe "pleroma.digest test" do
|
||||||
test "Sends digest to the given user" do
|
test "Sends digest to the given user" do
|
||||||
user1 = insert(:user)
|
user1 = insert(:user)
|
||||||
|
|
|
@ -16,6 +16,8 @@ defmodule Mix.Tasks.Pleroma.EmailTest do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
setup do: clear_config([Pleroma.Emails.Mailer, :enabled], true)
|
||||||
|
|
||||||
describe "pleroma.email test" do
|
describe "pleroma.email test" do
|
||||||
test "Sends test email with no given address" do
|
test "Sends test email with no given address" do
|
||||||
mail_to = Config.get([:instance, :email])
|
mail_to = Config.get([:instance, :email])
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.FrontendTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
alias Mix.Tasks.Pleroma.Frontend
|
||||||
|
|
||||||
|
import ExUnit.CaptureIO, only: [capture_io: 1]
|
||||||
|
|
||||||
|
@dir "test/frontend_static_test"
|
||||||
|
|
||||||
|
setup do
|
||||||
|
File.mkdir_p!(@dir)
|
||||||
|
clear_config([:instance, :static_dir], @dir)
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
File.rm_rf(@dir)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it downloads and unzips a known frontend" do
|
||||||
|
clear_config([:frontends, :available], %{
|
||||||
|
"pleroma" => %{
|
||||||
|
"ref" => "fantasy",
|
||||||
|
"name" => "pleroma",
|
||||||
|
"build_url" => "http://gensokyo.2hu/builds/${ref}"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/builds/fantasy"} ->
|
||||||
|
%Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend_dist.zip")}
|
||||||
|
end)
|
||||||
|
|
||||||
|
capture_io(fn ->
|
||||||
|
Frontend.run(["install", "pleroma"])
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert File.exists?(Path.join([@dir, "frontends", "pleroma", "fantasy", "test.txt"]))
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it also works given a file" do
|
||||||
|
clear_config([:frontends, :available], %{
|
||||||
|
"pleroma" => %{
|
||||||
|
"ref" => "fantasy",
|
||||||
|
"name" => "pleroma",
|
||||||
|
"build_dir" => ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
capture_io(fn ->
|
||||||
|
Frontend.run(["install", "pleroma", "--file", "test/fixtures/tesla_mock/frontend.zip"])
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert File.exists?(Path.join([@dir, "frontends", "pleroma", "fantasy", "test.txt"]))
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it downloads and unzips unknown frontends" do
|
||||||
|
Tesla.Mock.mock(fn %{url: "http://gensokyo.2hu/madeup.zip"} ->
|
||||||
|
%Tesla.Env{status: 200, body: File.read!("test/fixtures/tesla_mock/frontend.zip")}
|
||||||
|
end)
|
||||||
|
|
||||||
|
capture_io(fn ->
|
||||||
|
Frontend.run([
|
||||||
|
"install",
|
||||||
|
"unknown",
|
||||||
|
"--ref",
|
||||||
|
"baka",
|
||||||
|
"--build-url",
|
||||||
|
"http://gensokyo.2hu/madeup.zip",
|
||||||
|
"--build-dir",
|
||||||
|
""
|
||||||
|
])
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert File.exists?(Path.join([@dir, "frontends", "unknown", "baka", "test.txt"]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -42,7 +42,11 @@ test "relay is followed" do
|
||||||
assert activity.data["object"] == target_user.ap_id
|
assert activity.data["object"] == target_user.ap_id
|
||||||
|
|
||||||
:ok = Mix.Tasks.Pleroma.Relay.run(["list"])
|
:ok = Mix.Tasks.Pleroma.Relay.run(["list"])
|
||||||
assert_receive {:mix_shell, :info, ["mastodon.example.org (no Accept received)"]}
|
|
||||||
|
assert_receive {:mix_shell, :info,
|
||||||
|
[
|
||||||
|
"http://mastodon.example.org/users/admin - no Accept received (relay didn't follow back)"
|
||||||
|
]}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -95,8 +99,8 @@ test "Prints relay subscription list" do
|
||||||
|
|
||||||
:ok = Mix.Tasks.Pleroma.Relay.run(["list"])
|
:ok = Mix.Tasks.Pleroma.Relay.run(["list"])
|
||||||
|
|
||||||
assert_receive {:mix_shell, :info, ["mstdn.io"]}
|
assert_receive {:mix_shell, :info, ["https://mstdn.io/users/mayuutann"]}
|
||||||
assert_receive {:mix_shell, :info, ["mastodon.example.org"]}
|
assert_receive {:mix_shell, :info, ["http://mastodon.example.org/users/admin"]}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -533,7 +533,7 @@ test "accept follow activity", %{conn: conn} do
|
||||||
end)
|
end)
|
||||||
|
|
||||||
:ok = Mix.Tasks.Pleroma.Relay.run(["list"])
|
:ok = Mix.Tasks.Pleroma.Relay.run(["list"])
|
||||||
assert_receive {:mix_shell, :info, ["relay.mastodon.host"]}
|
assert_receive {:mix_shell, :info, ["https://relay.mastodon.host/actor"]}
|
||||||
end
|
end
|
||||||
|
|
||||||
@tag capture_log: true
|
@tag capture_log: true
|
||||||
|
|
|
@ -990,13 +990,39 @@ test "returns reblogs for users for whom reblogs have not been muted" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "uploading files" do
|
describe "uploading files" do
|
||||||
test "copies the file to the configured folder" do
|
setup do
|
||||||
file = %Plug.Upload{
|
test_file = %Plug.Upload{
|
||||||
content_type: "image/jpg",
|
content_type: "image/jpg",
|
||||||
path: Path.absname("test/fixtures/image.jpg"),
|
path: Path.absname("test/fixtures/image.jpg"),
|
||||||
filename: "an_image.jpg"
|
filename: "an_image.jpg"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%{test_file: test_file}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "sets a description if given", %{test_file: file} do
|
||||||
|
{:ok, %Object{} = object} = ActivityPub.upload(file, description: "a cool file")
|
||||||
|
assert object.data["name"] == "a cool file"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it sets the default description depending on the configuration", %{test_file: file} do
|
||||||
|
clear_config([Pleroma.Upload, :default_description])
|
||||||
|
|
||||||
|
Pleroma.Config.put([Pleroma.Upload, :default_description], nil)
|
||||||
|
{:ok, %Object{} = object} = ActivityPub.upload(file)
|
||||||
|
assert object.data["name"] == ""
|
||||||
|
|
||||||
|
Pleroma.Config.put([Pleroma.Upload, :default_description], :filename)
|
||||||
|
{:ok, %Object{} = object} = ActivityPub.upload(file)
|
||||||
|
assert object.data["name"] == "an_image.jpg"
|
||||||
|
|
||||||
|
Pleroma.Config.put([Pleroma.Upload, :default_description], "unnamed attachment")
|
||||||
|
{:ok, %Object{} = object} = ActivityPub.upload(file)
|
||||||
|
assert object.data["name"] == "unnamed attachment"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "copies the file to the configured folder", %{test_file: file} do
|
||||||
|
clear_config([Pleroma.Upload, :default_description], :filename)
|
||||||
{:ok, %Object{} = object} = ActivityPub.upload(file)
|
{:ok, %Object{} = object} = ActivityPub.upload(file)
|
||||||
assert object.data["name"] == "an_image.jpg"
|
assert object.data["name"] == "an_image.jpg"
|
||||||
end
|
end
|
||||||
|
|
|
@ -42,4 +42,42 @@ test "it works for incoming listens" do
|
||||||
assert object.data["album"] == "lain radio"
|
assert object.data["album"] == "lain radio"
|
||||||
assert object.data["length"] == 180_000
|
assert object.data["length"] == 180_000
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "Funkwhale Audio object" do
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{url: "https://channels.tests.funkwhale.audio/federation/actors/compositions"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/tesla_mock/funkwhale_channel.json")
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
data = File.read!("test/fixtures/tesla_mock/funkwhale_create_audio.json") |> Poison.decode!()
|
||||||
|
|
||||||
|
{:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert object = Object.normalize(activity, false)
|
||||||
|
|
||||||
|
assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
|
||||||
|
assert object.data["cc"] == []
|
||||||
|
|
||||||
|
assert object.data["url"] == "https://channels.tests.funkwhale.audio/library/tracks/74"
|
||||||
|
|
||||||
|
assert object.data["attachment"] == [
|
||||||
|
%{
|
||||||
|
"mediaType" => "audio/ogg",
|
||||||
|
"type" => "Link",
|
||||||
|
"name" => nil,
|
||||||
|
"url" => [
|
||||||
|
%{
|
||||||
|
"href" =>
|
||||||
|
"https://channels.tests.funkwhale.audio/api/v1/listen/3901e5d8-0445-49d5-9711-e096cf32e515/?upload=42342395-0208-4fee-a38d-259a6dae0871&download=false",
|
||||||
|
"mediaType" => "audio/ogg",
|
||||||
|
"type" => "Link"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.Transmogrifier.EventHandlingTest do
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
alias Pleroma.Object.Fetcher
|
||||||
|
|
||||||
|
test "Mobilizon Event object" do
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{url: "https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/tesla_mock/mobilizon.org-event.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
%{url: "https://mobilizon.org/@tcit"} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/tesla_mock/mobilizon.org-user.json")
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert {:ok, object} =
|
||||||
|
Fetcher.fetch_object_from_id(
|
||||||
|
"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert object.data["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
assert object.data["cc"] == []
|
||||||
|
|
||||||
|
assert object.data["url"] ==
|
||||||
|
"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
|
||||||
|
|
||||||
|
assert object.data["published"] == "2019-12-17T11:33:56Z"
|
||||||
|
assert object.data["name"] == "Mobilizon Launching Party"
|
||||||
|
end
|
||||||
|
end
|
|
@ -24,6 +24,8 @@ test "Mastodon Question activity" do
|
||||||
|
|
||||||
object = Object.normalize(activity, false)
|
object = Object.normalize(activity, false)
|
||||||
|
|
||||||
|
assert object.data["url"] == "https://mastodon.sdf.org/@rinpatch/102070944809637304"
|
||||||
|
|
||||||
assert object.data["closed"] == "2019-05-11T09:03:36Z"
|
assert object.data["closed"] == "2019-05-11T09:03:36Z"
|
||||||
|
|
||||||
assert object.data["context"] == activity.data["context"]
|
assert object.data["context"] == activity.data["context"]
|
||||||
|
|
|
@ -381,7 +381,8 @@ test "Show", %{conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert expected == json_response(conn, 200)
|
assert expected == json_response(conn, 200)
|
||||||
|
@ -663,7 +664,8 @@ test "renders users array for the first page", %{conn: conn, admin: admin} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => admin.ap_id,
|
"url" => admin.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
"deactivated" => user.deactivated,
|
"deactivated" => user.deactivated,
|
||||||
|
@ -677,7 +679,8 @@ test "renders users array for the first page", %{conn: conn, admin: admin} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
"deactivated" => user2.deactivated,
|
"deactivated" => user2.deactivated,
|
||||||
|
@ -691,7 +694,8 @@ test "renders users array for the first page", %{conn: conn, admin: admin} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => true,
|
"approval_pending" => true,
|
||||||
"url" => user2.ap_id,
|
"url" => user2.ap_id,
|
||||||
"registration_reason" => "I'm a chill dude"
|
"registration_reason" => "I'm a chill dude",
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|> Enum.sort_by(& &1["nickname"])
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
@ -766,7 +770,8 @@ test "regular search", %{conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -794,7 +799,8 @@ test "search by domain", %{conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -822,7 +828,8 @@ test "search by full nickname", %{conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -850,7 +857,8 @@ test "search by display name", %{conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -878,7 +886,8 @@ test "search by email", %{conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -906,7 +915,8 @@ test "regular search with page size", %{conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -929,7 +939,8 @@ test "regular search with page size", %{conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user2.ap_id,
|
"url" => user2.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -964,7 +975,8 @@ test "only local users" do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -992,7 +1004,8 @@ test "only local users with no query", %{conn: conn, admin: old_admin} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
"deactivated" => admin.deactivated,
|
"deactivated" => admin.deactivated,
|
||||||
|
@ -1006,7 +1019,8 @@ test "only local users with no query", %{conn: conn, admin: old_admin} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => admin.ap_id,
|
"url" => admin.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
"deactivated" => false,
|
"deactivated" => false,
|
||||||
|
@ -1020,7 +1034,8 @@ test "only local users with no query", %{conn: conn, admin: old_admin} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => old_admin.ap_id,
|
"url" => old_admin.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|> Enum.sort_by(& &1["nickname"])
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
@ -1058,7 +1073,8 @@ test "only unapproved users", %{conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => true,
|
"approval_pending" => true,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => "Plz let me in!"
|
"registration_reason" => "Plz let me in!",
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|> Enum.sort_by(& &1["nickname"])
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
@ -1091,7 +1107,8 @@ test "load only admins", %{conn: conn, admin: admin} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => admin.ap_id,
|
"url" => admin.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
"deactivated" => false,
|
"deactivated" => false,
|
||||||
|
@ -1105,7 +1122,8 @@ test "load only admins", %{conn: conn, admin: admin} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => second_admin.ap_id,
|
"url" => second_admin.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|> Enum.sort_by(& &1["nickname"])
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
@ -1140,7 +1158,8 @@ test "load only moderators", %{conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => moderator.ap_id,
|
"url" => moderator.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1168,7 +1187,8 @@ test "load users with tags list", %{conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user1.ap_id,
|
"url" => user1.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
"deactivated" => false,
|
"deactivated" => false,
|
||||||
|
@ -1182,7 +1202,8 @@ test "load users with tags list", %{conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user2.ap_id,
|
"url" => user2.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|> Enum.sort_by(& &1["nickname"])
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
@ -1245,7 +1266,8 @@ test "it works with multiple filters" do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1272,7 +1294,8 @@ test "it omits relay user", %{admin: admin, conn: conn} do
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => admin.ap_id,
|
"url" => admin.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1357,7 +1380,8 @@ test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admi
|
||||||
"confirmation_pending" => false,
|
"confirmation_pending" => false,
|
||||||
"approval_pending" => false,
|
"approval_pending" => false,
|
||||||
"url" => user.ap_id,
|
"url" => user.ap_id,
|
||||||
"registration_reason" => nil
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
}
|
}
|
||||||
|
|
||||||
log_entry = Repo.one(ModerationLog)
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
|
@ -39,8 +39,10 @@ test "POST /relay", %{conn: conn, admin: admin} do
|
||||||
relay_url: "http://mastodon.example.org/users/admin"
|
relay_url: "http://mastodon.example.org/users/admin"
|
||||||
})
|
})
|
||||||
|
|
||||||
assert json_response_and_validate_schema(conn, 200) ==
|
assert json_response_and_validate_schema(conn, 200) == %{
|
||||||
"http://mastodon.example.org/users/admin"
|
"actor" => "http://mastodon.example.org/users/admin",
|
||||||
|
"followed_back" => false
|
||||||
|
}
|
||||||
|
|
||||||
log_entry = Repo.one(ModerationLog)
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
@ -59,8 +61,13 @@ test "GET /relay", %{conn: conn} do
|
||||||
|
|
||||||
conn = get(conn, "/api/pleroma/admin/relay")
|
conn = get(conn, "/api/pleroma/admin/relay")
|
||||||
|
|
||||||
assert json_response_and_validate_schema(conn, 200)["relays"] --
|
assert json_response_and_validate_schema(conn, 200)["relays"] == [
|
||||||
["mastodon.example.org", "mstdn.io"] == []
|
%{
|
||||||
|
"actor" => "http://mastodon.example.org/users/admin",
|
||||||
|
"followed_back" => true
|
||||||
|
},
|
||||||
|
%{"actor" => "https://mstdn.io/users/mayuutann", "followed_back" => true}
|
||||||
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "DELETE /relay", %{conn: conn, admin: admin} do
|
test "DELETE /relay", %{conn: conn, admin: admin} do
|
||||||
|
|
|
@ -517,6 +517,12 @@ test "a Mobilizon event" do
|
||||||
represented = StatusView.render("show.json", %{for: user, activity: activity})
|
represented = StatusView.render("show.json", %{for: user, activity: activity})
|
||||||
|
|
||||||
assert represented[:id] == to_string(activity.id)
|
assert represented[:id] == to_string(activity.id)
|
||||||
|
|
||||||
|
assert represented[:url] ==
|
||||||
|
"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39"
|
||||||
|
|
||||||
|
assert represented[:content] ==
|
||||||
|
"<p><a href=\"https://mobilizon.org/events/252d5816-00a3-4a89-a66f-15bf65c33e39\">Mobilizon Launching Party</a></p><p>Mobilizon is now federated! 🎉</p><p></p><p>You can view this event from other instances if they are subscribed to mobilizon.org, and soon directly from Mastodon and Pleroma. It is possible that you may see some comments from other instances, including Mastodon ones, just below.</p><p></p><p>With a Mobilizon account on an instance, you may <strong>participate</strong> at events from other instances and <strong>add comments</strong> on events.</p><p></p><p>Of course, it's still <u>a work in progress</u>: if reports made from an instance on events and comments can be federated, you can't block people right now, and moderators actions are rather limited, but this <strong>will definitely get fixed over time</strong> until first stable version next year.</p><p></p><p>Anyway, if you want to come up with some feedback, head over to our forum or - if you feel you have technical skills and are familiar with it - on our Gitlab repository.</p><p></p><p>Also, to people that want to set Mobilizon themselves even though we really don't advise to do that for now, we have a little documentation but it's quite the early days and you'll probably need some help. No worries, you can chat with us on our Forum or though our Matrix channel.</p><p></p><p>Check our website for more informations and follow us on Twitter or Mastodon.</p>"
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "build_tags/1" do
|
describe "build_tags/1" do
|
||||||
|
|
Loading…
Reference in New Issue