Merge remote-tracking branch 'remotes/origin/develop' into 2168-media-preview-proxy
This commit is contained in:
commit
6fd4f58ead
@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- **Breaking:** removed `with_move` parameter from notifications timeline.
|
- **Breaking:** removed `with_move` parameter from notifications timeline.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
- Instance: Add `background_image` to configuration and `/api/v1/instance`
|
||||||
- Instance: Extend `/api/v1/instance` with Pleroma-specific information.
|
- Instance: Extend `/api/v1/instance` with Pleroma-specific information.
|
||||||
- NodeInfo: `pleroma:api/v1/notifications:include_types_filter` to the `features` list.
|
- NodeInfo: `pleroma:api/v1/notifications:include_types_filter` to the `features` list.
|
||||||
- NodeInfo: `pleroma_emoji_reactions` to the `features` list.
|
- NodeInfo: `pleroma_emoji_reactions` to the `features` list.
|
||||||
|
@ -123,7 +123,7 @@ defmodule Pleroma.LoadTesting.Activities do
|
|||||||
Enum.each(1..activity_count, fn _ ->
|
Enum.each(1..activity_count, fn _ ->
|
||||||
random = :rand.uniform()
|
random = :rand.uniform()
|
||||||
i = Enum.find_index(intervals, fn {lower, upper} -> lower <= random && upper > random end)
|
i = Enum.find_index(intervals, fn {lower, upper} -> lower <= random && upper > random end)
|
||||||
CommonAPI.post(Enum.random(users), %{"status" => "a post with the tag #tag_#{i}"})
|
CommonAPI.post(Enum.random(users), %{status: "a post with the tag #tag_#{i}"})
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -137,8 +137,8 @@ defmodule Pleroma.LoadTesting.Activities do
|
|||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
CommonAPI.post(user, %{
|
CommonAPI.post(user, %{
|
||||||
"status" => "Start of #{visibility} long thread",
|
status: "Start of #{visibility} long thread",
|
||||||
"visibility" => visibility
|
visibility: visibility
|
||||||
})
|
})
|
||||||
|
|
||||||
Agent.update(:benchmark_state, fn state ->
|
Agent.update(:benchmark_state, fn state ->
|
||||||
@ -186,7 +186,7 @@ defmodule Pleroma.LoadTesting.Activities do
|
|||||||
{:ok, _activity} =
|
{:ok, _activity} =
|
||||||
group
|
group
|
||||||
|> get_actor(user, friends, non_friends)
|
|> get_actor(user, friends, non_friends)
|
||||||
|> CommonAPI.post(%{"status" => "Simple status", "visibility" => visibility})
|
|> CommonAPI.post(%{status: "Simple status", visibility: visibility})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("emoji", visibility, group, user, friends, non_friends, _opts) do
|
defp insert_activity("emoji", visibility, group, user, friends, non_friends, _opts) do
|
||||||
@ -194,8 +194,8 @@ defmodule Pleroma.LoadTesting.Activities do
|
|||||||
group
|
group
|
||||||
|> get_actor(user, friends, non_friends)
|
|> get_actor(user, friends, non_friends)
|
||||||
|> CommonAPI.post(%{
|
|> CommonAPI.post(%{
|
||||||
"status" => "Simple status with emoji :firefox:",
|
status: "Simple status with emoji :firefox:",
|
||||||
"visibility" => visibility
|
visibility: visibility
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -213,8 +213,8 @@ defmodule Pleroma.LoadTesting.Activities do
|
|||||||
group
|
group
|
||||||
|> get_actor(user, friends, non_friends)
|
|> get_actor(user, friends, non_friends)
|
||||||
|> CommonAPI.post(%{
|
|> CommonAPI.post(%{
|
||||||
"status" => Enum.join(user_mentions, ", ") <> " simple status with mentions",
|
status: Enum.join(user_mentions, ", ") <> " simple status with mentions",
|
||||||
"visibility" => visibility
|
visibility: visibility
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -236,8 +236,8 @@ defmodule Pleroma.LoadTesting.Activities do
|
|||||||
group
|
group
|
||||||
|> get_actor(user, friends, non_friends)
|
|> get_actor(user, friends, non_friends)
|
||||||
|> CommonAPI.post(%{
|
|> CommonAPI.post(%{
|
||||||
"status" => mentions <> " hell thread status",
|
status: mentions <> " hell thread status",
|
||||||
"visibility" => visibility
|
visibility: visibility
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -262,9 +262,9 @@ defmodule Pleroma.LoadTesting.Activities do
|
|||||||
|
|
||||||
{:ok, _activity} =
|
{:ok, _activity} =
|
||||||
CommonAPI.post(actor, %{
|
CommonAPI.post(actor, %{
|
||||||
"status" => "Post with attachment",
|
status: "Post with attachment",
|
||||||
"visibility" => visibility,
|
visibility: visibility,
|
||||||
"media_ids" => [object.id]
|
media_ids: [object.id]
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -272,7 +272,7 @@ defmodule Pleroma.LoadTesting.Activities do
|
|||||||
{:ok, _activity} =
|
{:ok, _activity} =
|
||||||
group
|
group
|
||||||
|> get_actor(user, friends, non_friends)
|
|> get_actor(user, friends, non_friends)
|
||||||
|> CommonAPI.post(%{"status" => "Status with #tag", "visibility" => visibility})
|
|> CommonAPI.post(%{status: "Status with #tag", visibility: visibility})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_activity("like", visibility, group, user, friends, non_friends, opts) do
|
defp insert_activity("like", visibility, group, user, friends, non_friends, opts) do
|
||||||
@ -312,8 +312,7 @@ defmodule Pleroma.LoadTesting.Activities do
|
|||||||
actor = get_actor(group, user, friends, non_friends)
|
actor = get_actor(group, user, friends, non_friends)
|
||||||
tasks = get_reply_tasks(visibility, group)
|
tasks = get_reply_tasks(visibility, group)
|
||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} = CommonAPI.post(user, %{status: "Simple status", visibility: visibility})
|
||||||
CommonAPI.post(user, %{"status" => "Simple status", "visibility" => visibility})
|
|
||||||
|
|
||||||
acc = {activity.id, ["@" <> actor.nickname, "reply to status"]}
|
acc = {activity.id, ["@" <> actor.nickname, "reply to status"]}
|
||||||
insert_replies(tasks, visibility, user, friends, non_friends, acc)
|
insert_replies(tasks, visibility, user, friends, non_friends, acc)
|
||||||
@ -336,8 +335,8 @@ defmodule Pleroma.LoadTesting.Activities do
|
|||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
CommonAPI.post(actor, %{
|
CommonAPI.post(actor, %{
|
||||||
"status" => Enum.join(data, ", ") <> "simple status",
|
status: Enum.join(data, ", ") <> "simple status",
|
||||||
"visibility" => "direct"
|
visibility: "direct"
|
||||||
})
|
})
|
||||||
|
|
||||||
acc = {activity.id, ["@" <> user.nickname | data] ++ ["reply to status"]}
|
acc = {activity.id, ["@" <> user.nickname | data] ++ ["reply to status"]}
|
||||||
@ -527,9 +526,9 @@ defmodule Pleroma.LoadTesting.Activities do
|
|||||||
defp insert_reply(actor, data, activity_id, visibility) do
|
defp insert_reply(actor, data, activity_id, visibility) do
|
||||||
{:ok, reply} =
|
{:ok, reply} =
|
||||||
CommonAPI.post(actor, %{
|
CommonAPI.post(actor, %{
|
||||||
"status" => Enum.join(data, ", "),
|
status: Enum.join(data, ", "),
|
||||||
"visibility" => visibility,
|
visibility: visibility,
|
||||||
"in_reply_to_status_id" => activity_id
|
in_reply_to_status_id: activity_id
|
||||||
})
|
})
|
||||||
|
|
||||||
{reply.id, ["@" <> actor.nickname | data]}
|
{reply.id, ["@" <> actor.nickname | data]}
|
||||||
|
@ -387,56 +387,47 @@ defmodule Pleroma.LoadTesting.Fetcher do
|
|||||||
|
|
||||||
favourites = ActivityPub.fetch_favourites(user)
|
favourites = ActivityPub.fetch_favourites(user)
|
||||||
|
|
||||||
output_relationships =
|
|
||||||
!!Pleroma.Config.get([:extensions, :output_relationships_in_statuses_by_default])
|
|
||||||
|
|
||||||
Benchee.run(
|
Benchee.run(
|
||||||
%{
|
%{
|
||||||
"Rendering home timeline" => fn ->
|
"Rendering home timeline" => fn ->
|
||||||
StatusView.render("index.json", %{
|
StatusView.render("index.json", %{
|
||||||
activities: home_activities,
|
activities: home_activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: !output_relationships
|
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
"Rendering direct timeline" => fn ->
|
"Rendering direct timeline" => fn ->
|
||||||
StatusView.render("index.json", %{
|
StatusView.render("index.json", %{
|
||||||
activities: direct_activities,
|
activities: direct_activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: !output_relationships
|
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
"Rendering public timeline" => fn ->
|
"Rendering public timeline" => fn ->
|
||||||
StatusView.render("index.json", %{
|
StatusView.render("index.json", %{
|
||||||
activities: public_activities,
|
activities: public_activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: !output_relationships
|
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
"Rendering tag timeline" => fn ->
|
"Rendering tag timeline" => fn ->
|
||||||
StatusView.render("index.json", %{
|
StatusView.render("index.json", %{
|
||||||
activities: tag_activities,
|
activities: tag_activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: !output_relationships
|
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
"Rendering notifications" => fn ->
|
"Rendering notifications" => fn ->
|
||||||
Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
|
Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
|
||||||
notifications: notifications,
|
notifications: notifications,
|
||||||
for: user,
|
for: user
|
||||||
skip_relationships: !output_relationships
|
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
"Rendering favourites timeline" => fn ->
|
"Rendering favourites timeline" => fn ->
|
||||||
StatusView.render("index.json", %{
|
StatusView.render("index.json", %{
|
||||||
activities: favourites,
|
activities: favourites,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: !output_relationships
|
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
@ -183,6 +183,7 @@ config :pleroma, :instance,
|
|||||||
email: "example@example.com",
|
email: "example@example.com",
|
||||||
notify_email: "noreply@example.com",
|
notify_email: "noreply@example.com",
|
||||||
description: "A Pleroma instance, an alternative fediverse server",
|
description: "A Pleroma instance, an alternative fediverse server",
|
||||||
|
background_image: "/images/city.jpg",
|
||||||
limit: 5_000,
|
limit: 5_000,
|
||||||
chat_limit: 5_000,
|
chat_limit: 5_000,
|
||||||
remote_limit: 100_000,
|
remote_limit: 100_000,
|
||||||
@ -251,8 +252,6 @@ config :pleroma, :instance,
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
config :pleroma, :extensions, output_relationships_in_statuses_by_default: true
|
|
||||||
|
|
||||||
config :pleroma, :feed,
|
config :pleroma, :feed,
|
||||||
post_title: %{
|
post_title: %{
|
||||||
max_length: 100,
|
max_length: 100,
|
||||||
@ -378,6 +377,10 @@ config :pleroma, :rich_media,
|
|||||||
|
|
||||||
config :pleroma, :media_proxy,
|
config :pleroma, :media_proxy,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
invalidation: [
|
||||||
|
enabled: false,
|
||||||
|
provider: Pleroma.Web.MediaProxy.Invalidation.Script
|
||||||
|
],
|
||||||
proxy_opts: [
|
proxy_opts: [
|
||||||
redirect_on_failure: false,
|
redirect_on_failure: false,
|
||||||
max_body_length: 25 * 1_048_576,
|
max_body_length: 25 * 1_048_576,
|
||||||
|
@ -679,15 +679,6 @@ config :pleroma, :config_description, [
|
|||||||
7
|
7
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
%{
|
|
||||||
key: :federation_publisher_modules,
|
|
||||||
type: {:list, :module},
|
|
||||||
description:
|
|
||||||
"List of modules for federation publishing. Module names are shortened (removed leading `Pleroma.Web.` part), but on adding custom module you need to use full name.",
|
|
||||||
suggestions: [
|
|
||||||
Pleroma.Web.ActivityPub.Publisher
|
|
||||||
]
|
|
||||||
},
|
|
||||||
%{
|
%{
|
||||||
key: :allow_relay,
|
key: :allow_relay,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
@ -1902,12 +1893,6 @@ config :pleroma, :config_description, [
|
|||||||
(see https://github.com/sorentwo/oban/issues/52).
|
(see https://github.com/sorentwo/oban/issues/52).
|
||||||
""",
|
""",
|
||||||
children: [
|
children: [
|
||||||
%{
|
|
||||||
key: :repo,
|
|
||||||
type: :module,
|
|
||||||
description: "Application's Ecto repo",
|
|
||||||
suggestions: [Pleroma.Repo]
|
|
||||||
},
|
|
||||||
%{
|
%{
|
||||||
key: :verbose,
|
key: :verbose,
|
||||||
type: {:dropdown, :atom},
|
type: {:dropdown, :atom},
|
||||||
@ -2682,18 +2667,6 @@ config :pleroma, :config_description, [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
%{
|
|
||||||
group: :http_signatures,
|
|
||||||
type: :group,
|
|
||||||
description: "HTTP Signatures settings",
|
|
||||||
children: [
|
|
||||||
%{
|
|
||||||
key: :adapter,
|
|
||||||
type: :module,
|
|
||||||
suggestions: [Pleroma.Signature]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
%{
|
%{
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
key: :http,
|
key: :http,
|
||||||
|
@ -216,6 +216,7 @@ Has theses additional parameters (which are the same as in Pleroma-API):
|
|||||||
- `avatar_upload_limit`: The same for avatars
|
- `avatar_upload_limit`: The same for avatars
|
||||||
- `background_upload_limit`: The same for backgrounds
|
- `background_upload_limit`: The same for backgrounds
|
||||||
- `banner_upload_limit`: The same for banners
|
- `banner_upload_limit`: The same for banners
|
||||||
|
- `background_image`: A background image that frontends can use
|
||||||
- `pleroma.metadata.features`: A list of supported features
|
- `pleroma.metadata.features`: A list of supported features
|
||||||
- `pleroma.metadata.federation`: The federation restrictions of this instance
|
- `pleroma.metadata.federation`: The federation restrictions of this instance
|
||||||
- `vapid_public_key`: The public key needed for push messages
|
- `vapid_public_key`: The public key needed for push messages
|
||||||
|
@ -265,7 +265,7 @@ See [Admin-API](admin_api.md)
|
|||||||
* Method `PUT`
|
* Method `PUT`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
* Params:
|
* Params:
|
||||||
* `image`: Multipart image
|
* `file`: Multipart image
|
||||||
* Response: JSON. Returns a mastodon media attachment entity
|
* Response: JSON. Returns a mastodon media attachment entity
|
||||||
when successful, otherwise returns HTTP 415 `{"error": "error_msg"}`
|
when successful, otherwise returns HTTP 415 `{"error": "error_msg"}`
|
||||||
* Example response:
|
* Example response:
|
||||||
@ -426,7 +426,7 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa
|
|||||||
* Authentication: required
|
* Authentication: required
|
||||||
* Params:
|
* Params:
|
||||||
* `file`: file needs to be uploaded with the multipart request or link to remote file.
|
* `file`: file needs to be uploaded with the multipart request or link to remote file.
|
||||||
* `shortcode`: (*optional*) shortcode for new emoji, must be uniq for all emoji. If not sended, shortcode will be taken from original filename.
|
* `shortcode`: (*optional*) shortcode for new emoji, must be unique for all emoji. If not sended, shortcode will be taken from original filename.
|
||||||
* `filename`: (*optional*) new emoji file name. If not specified will be taken from original filename.
|
* `filename`: (*optional*) new emoji file name. If not specified will be taken from original filename.
|
||||||
* Response: JSON, list of files for updated pack (hashmap -> shortcode => filename) with status 200, either error status with error message.
|
* Response: JSON, list of files for updated pack (hashmap -> shortcode => filename) with status 200, either error status with error message.
|
||||||
|
|
||||||
|
@ -249,6 +249,40 @@ This section describe PWA manifest instance-specific values. Currently this opti
|
|||||||
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.
|
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.
|
||||||
* `proxy_opts`: All options defined in `Pleroma.ReverseProxy` documentation, defaults to `[max_body_length: (25*1_048_576)]`.
|
* `proxy_opts`: All options defined in `Pleroma.ReverseProxy` documentation, defaults to `[max_body_length: (25*1_048_576)]`.
|
||||||
* `whitelist`: List of domains to bypass the mediaproxy
|
* `whitelist`: List of domains to bypass the mediaproxy
|
||||||
|
* `invalidation`: options for remove media from cache after delete object:
|
||||||
|
* `enabled`: Enables purge cache
|
||||||
|
* `provider`: Which one of the [purge cache strategy](#purge-cache-strategy) to use.
|
||||||
|
|
||||||
|
### Purge cache strategy
|
||||||
|
|
||||||
|
#### Pleroma.Web.MediaProxy.Invalidation.Script
|
||||||
|
|
||||||
|
This strategy allow perform external bash script to purge cache.
|
||||||
|
Urls of attachments pass to script as arguments.
|
||||||
|
|
||||||
|
* `script_path`: path to external script.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```elixir
|
||||||
|
config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Script,
|
||||||
|
script_path: "./installation/nginx-cache-purge.example"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pleroma.Web.MediaProxy.Invalidation.Http
|
||||||
|
|
||||||
|
This strategy allow perform custom http request to purge cache.
|
||||||
|
|
||||||
|
* `method`: http method. default is `purge`
|
||||||
|
* `headers`: http headers. default is empty
|
||||||
|
* `options`: request options. default is empty
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```elixir
|
||||||
|
config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Http,
|
||||||
|
method: :purge,
|
||||||
|
headers: [],
|
||||||
|
options: []
|
||||||
|
```
|
||||||
|
|
||||||
## Link previews
|
## Link previews
|
||||||
|
|
||||||
@ -619,24 +653,6 @@ config :pleroma, :workers,
|
|||||||
* `enabled: false` corresponds to `config :pleroma, :workers, retries: [federator_outgoing: 1]`
|
* `enabled: false` corresponds to `config :pleroma, :workers, retries: [federator_outgoing: 1]`
|
||||||
* deprecated options: `max_jobs`, `initial_timeout`
|
* deprecated options: `max_jobs`, `initial_timeout`
|
||||||
|
|
||||||
### Pleroma.Scheduler
|
|
||||||
|
|
||||||
Configuration for [Quantum](https://github.com/quantum-elixir/quantum-core) jobs scheduler.
|
|
||||||
|
|
||||||
See [Quantum readme](https://github.com/quantum-elixir/quantum-core#usage) for the list of supported options.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```elixir
|
|
||||||
config :pleroma, Pleroma.Scheduler,
|
|
||||||
global: true,
|
|
||||||
overlap: true,
|
|
||||||
timezone: :utc,
|
|
||||||
jobs: [{"0 */6 * * * *", {Pleroma.Web.Websub, :refresh_subscriptions, []}}]
|
|
||||||
```
|
|
||||||
|
|
||||||
The above example defines a single job which invokes `Pleroma.Web.Websub.refresh_subscriptions()` every 6 hours ("0 */6 * * * *", [crontab format](https://en.wikipedia.org/wiki/Cron)).
|
|
||||||
|
|
||||||
## :web_push_encryption, :vapid_details
|
## :web_push_encryption, :vapid_details
|
||||||
|
|
||||||
Web Push Notifications configuration. You can use the mix task `mix web_push.gen.keypair` to generate it.
|
Web Push Notifications configuration. You can use the mix task `mix web_push.gen.keypair` to generate it.
|
||||||
|
38
docs/configuration/storing_remote_media.md
Normal file
38
docs/configuration/storing_remote_media.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Storing Remote Media
|
||||||
|
|
||||||
|
Pleroma does not store remote/federated media by default. The best way to achieve this is to change Nginx to keep its reverse proxy cache
|
||||||
|
for a year and to activate the `MediaProxyWarmingPolicy` MRF policy in Pleroma which will automatically fetch all media through the proxy
|
||||||
|
as soon as the post is received by your instance.
|
||||||
|
|
||||||
|
## Nginx
|
||||||
|
|
||||||
|
```
|
||||||
|
proxy_cache_path /long/term/storage/path/pleroma-media-cache levels=1:2
|
||||||
|
keys_zone=pleroma_media_cache:10m inactive=1y use_temp_path=off;
|
||||||
|
|
||||||
|
location ~ ^/(media|proxy) {
|
||||||
|
proxy_cache pleroma_media_cache;
|
||||||
|
slice 1m;
|
||||||
|
proxy_cache_key $host$uri$is_args$args$slice_range;
|
||||||
|
proxy_set_header Range $slice_range;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_cache_valid 206 301 302 304 1h;
|
||||||
|
proxy_cache_valid 200 1y;
|
||||||
|
proxy_cache_use_stale error timeout invalid_header updating;
|
||||||
|
proxy_ignore_client_abort on;
|
||||||
|
proxy_buffering on;
|
||||||
|
chunked_transfer_encoding on;
|
||||||
|
proxy_ignore_headers Cache-Control Expires;
|
||||||
|
proxy_hide_header Cache-Control Expires;
|
||||||
|
proxy_pass http://127.0.0.1:4000;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pleroma
|
||||||
|
|
||||||
|
Add to your `prod.secret.exs`:
|
||||||
|
|
||||||
|
```
|
||||||
|
config :pleroma, :instance,
|
||||||
|
rewrite_policy: [Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy]
|
||||||
|
```
|
40
installation/nginx-cache-purge.sh.example
Executable file
40
installation/nginx-cache-purge.sh.example
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# A simple shell script to delete a media from the Nginx cache.
|
||||||
|
|
||||||
|
SCRIPTNAME=${0##*/}
|
||||||
|
|
||||||
|
# NGINX cache directory
|
||||||
|
CACHE_DIRECTORY="/tmp/pleroma-media-cache"
|
||||||
|
|
||||||
|
## Return the files where the items are cached.
|
||||||
|
## $1 - the filename, can be a pattern .
|
||||||
|
## $2 - the cache directory.
|
||||||
|
## $3 - (optional) the number of parallel processes to run for grep.
|
||||||
|
get_cache_files() {
|
||||||
|
local max_parallel=${3-16}
|
||||||
|
find $2 -maxdepth 2 -type d | xargs -P $max_parallel -n 1 grep -E Rl "^KEY:.*$1" | sort -u
|
||||||
|
}
|
||||||
|
|
||||||
|
## Removes an item from the given cache zone.
|
||||||
|
## $1 - the filename, can be a pattern .
|
||||||
|
## $2 - the cache directory.
|
||||||
|
purge_item() {
|
||||||
|
for f in $(get_cache_files $1 $2); do
|
||||||
|
echo "found file: $f"
|
||||||
|
[ -f $f ] || continue
|
||||||
|
echo "Deleting $f from $2."
|
||||||
|
rm $f
|
||||||
|
done
|
||||||
|
} # purge_item
|
||||||
|
|
||||||
|
purge() {
|
||||||
|
for url in "$@"
|
||||||
|
do
|
||||||
|
echo "$SCRIPTNAME delete \`$url\` from cache ($CACHE_DIRECTORY)"
|
||||||
|
purge_item $url $CACHE_DIRECTORY
|
||||||
|
done
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
purge $1
|
@ -67,8 +67,7 @@ defmodule Mix.Tasks.Pleroma.Benchmark do
|
|||||||
Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
|
Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: true
|
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
@ -16,162 +16,78 @@ defmodule Pleroma.Emoji.Pack do
|
|||||||
|
|
||||||
alias Pleroma.Emoji
|
alias Pleroma.Emoji
|
||||||
|
|
||||||
@spec emoji_path() :: Path.t()
|
|
||||||
def emoji_path do
|
|
||||||
static = Pleroma.Config.get!([:instance, :static_dir])
|
|
||||||
Path.join(static, "emoji")
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec create(String.t()) :: :ok | {:error, File.posix()} | {:error, :empty_values}
|
@spec create(String.t()) :: :ok | {:error, File.posix()} | {:error, :empty_values}
|
||||||
def create(name) when byte_size(name) > 0 do
|
def create(name) do
|
||||||
dir = Path.join(emoji_path(), name)
|
with :ok <- validate_not_empty([name]),
|
||||||
|
dir <- Path.join(emoji_path(), name),
|
||||||
with :ok <- File.mkdir(dir) do
|
:ok <- File.mkdir(dir) do
|
||||||
%__MODULE__{
|
%__MODULE__{pack_file: Path.join(dir, "pack.json")}
|
||||||
pack_file: Path.join(dir, "pack.json")
|
|
||||||
}
|
|
||||||
|> save_pack()
|
|> save_pack()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(_), do: {:error, :empty_values}
|
@spec show(String.t()) :: {:ok, t()} | {:error, atom()}
|
||||||
|
def show(name) do
|
||||||
@spec show(String.t()) :: {:ok, t()} | {:loaded, nil} | {:error, :empty_values}
|
with :ok <- validate_not_empty([name]),
|
||||||
def show(name) when byte_size(name) > 0 do
|
{:ok, pack} <- load_pack(name) do
|
||||||
with {_, %__MODULE__{} = pack} <- {:loaded, load_pack(name)},
|
{:ok, validate_pack(pack)}
|
||||||
{_, pack} <- validate_pack(pack) do
|
|
||||||
{:ok, pack}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def show(_), do: {:error, :empty_values}
|
|
||||||
|
|
||||||
@spec delete(String.t()) ::
|
@spec delete(String.t()) ::
|
||||||
{:ok, [binary()]} | {:error, File.posix(), binary()} | {:error, :empty_values}
|
{:ok, [binary()]} | {:error, File.posix(), binary()} | {:error, :empty_values}
|
||||||
def delete(name) when byte_size(name) > 0 do
|
def delete(name) do
|
||||||
emoji_path()
|
with :ok <- validate_not_empty([name]) do
|
||||||
|> Path.join(name)
|
emoji_path()
|
||||||
|> File.rm_rf()
|
|> Path.join(name)
|
||||||
end
|
|> File.rm_rf()
|
||||||
|
|
||||||
def delete(_), do: {:error, :empty_values}
|
|
||||||
|
|
||||||
@spec add_file(String.t(), String.t(), Path.t(), Plug.Upload.t() | String.t()) ::
|
|
||||||
{:ok, t()} | {:error, File.posix()} | {:error, :empty_values}
|
|
||||||
def add_file(name, shortcode, filename, file)
|
|
||||||
when byte_size(name) > 0 and byte_size(shortcode) > 0 and byte_size(filename) > 0 do
|
|
||||||
with {_, nil} <- {:exists, Emoji.get(shortcode)},
|
|
||||||
{_, %__MODULE__{} = pack} <- {:loaded, load_pack(name)} do
|
|
||||||
file_path = Path.join(pack.path, filename)
|
|
||||||
|
|
||||||
create_subdirs(file_path)
|
|
||||||
|
|
||||||
case file do
|
|
||||||
%Plug.Upload{path: upload_path} ->
|
|
||||||
# Copy the uploaded file from the temporary directory
|
|
||||||
File.copy!(upload_path, file_path)
|
|
||||||
|
|
||||||
url when is_binary(url) ->
|
|
||||||
# Download and write the file
|
|
||||||
file_contents = Tesla.get!(url).body
|
|
||||||
File.write!(file_path, file_contents)
|
|
||||||
end
|
|
||||||
|
|
||||||
files = Map.put(pack.files, shortcode, filename)
|
|
||||||
|
|
||||||
updated_pack = %{pack | files: files}
|
|
||||||
|
|
||||||
case save_pack(updated_pack) do
|
|
||||||
:ok ->
|
|
||||||
Emoji.reload()
|
|
||||||
{:ok, updated_pack}
|
|
||||||
|
|
||||||
e ->
|
|
||||||
e
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_file(_, _, _, _), do: {:error, :empty_values}
|
@spec add_file(String.t(), String.t(), Path.t(), Plug.Upload.t() | String.t()) ::
|
||||||
|
{:ok, t()} | {:error, File.posix() | atom()}
|
||||||
defp create_subdirs(file_path) do
|
def add_file(name, shortcode, filename, file) do
|
||||||
if String.contains?(file_path, "/") do
|
with :ok <- validate_not_empty([name, shortcode, filename]),
|
||||||
file_path
|
:ok <- validate_emoji_not_exists(shortcode),
|
||||||
|> Path.dirname()
|
{:ok, pack} <- load_pack(name),
|
||||||
|> File.mkdir_p!()
|
:ok <- save_file(file, pack, filename),
|
||||||
|
{:ok, updated_pack} <- pack |> put_emoji(shortcode, filename) |> save_pack() do
|
||||||
|
Emoji.reload()
|
||||||
|
{:ok, updated_pack}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec delete_file(String.t(), String.t()) ::
|
@spec delete_file(String.t(), String.t()) ::
|
||||||
{:ok, t()} | {:error, File.posix()} | {:error, :empty_values}
|
{:ok, t()} | {:error, File.posix() | atom()}
|
||||||
def delete_file(name, shortcode) when byte_size(name) > 0 and byte_size(shortcode) > 0 do
|
def delete_file(name, shortcode) do
|
||||||
with {_, %__MODULE__{} = pack} <- {:loaded, load_pack(name)},
|
with :ok <- validate_not_empty([name, shortcode]),
|
||||||
{_, {filename, files}} when not is_nil(filename) <-
|
{:ok, pack} <- load_pack(name),
|
||||||
{:exists, Map.pop(pack.files, shortcode)},
|
:ok <- remove_file(pack, shortcode),
|
||||||
emoji <- Path.join(pack.path, filename),
|
{:ok, updated_pack} <- pack |> delete_emoji(shortcode) |> save_pack() do
|
||||||
{_, true} <- {:exists, File.exists?(emoji)} do
|
Emoji.reload()
|
||||||
emoji_dir = Path.dirname(emoji)
|
{:ok, updated_pack}
|
||||||
|
|
||||||
File.rm!(emoji)
|
|
||||||
|
|
||||||
if String.contains?(filename, "/") and File.ls!(emoji_dir) == [] do
|
|
||||||
File.rmdir!(emoji_dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
updated_pack = %{pack | files: files}
|
|
||||||
|
|
||||||
case save_pack(updated_pack) do
|
|
||||||
:ok ->
|
|
||||||
Emoji.reload()
|
|
||||||
{:ok, updated_pack}
|
|
||||||
|
|
||||||
e ->
|
|
||||||
e
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_file(_, _), do: {:error, :empty_values}
|
|
||||||
|
|
||||||
@spec update_file(String.t(), String.t(), String.t(), String.t(), boolean()) ::
|
@spec update_file(String.t(), String.t(), String.t(), String.t(), boolean()) ::
|
||||||
{:ok, t()} | {:error, File.posix()} | {:error, :empty_values}
|
{:ok, t()} | {:error, File.posix() | atom()}
|
||||||
def update_file(name, shortcode, new_shortcode, new_filename, force)
|
def update_file(name, shortcode, new_shortcode, new_filename, force) do
|
||||||
when byte_size(name) > 0 and byte_size(shortcode) > 0 and byte_size(new_shortcode) > 0 and
|
with :ok <- validate_not_empty([name, shortcode, new_shortcode, new_filename]),
|
||||||
byte_size(new_filename) > 0 do
|
{:ok, pack} <- load_pack(name),
|
||||||
with {_, %__MODULE__{} = pack} <- {:loaded, load_pack(name)},
|
{:ok, filename} <- get_filename(pack, shortcode),
|
||||||
{_, {filename, files}} when not is_nil(filename) <-
|
:ok <- validate_emoji_not_exists(new_shortcode, force),
|
||||||
{:exists, Map.pop(pack.files, shortcode)},
|
:ok <- rename_file(pack, filename, new_filename),
|
||||||
{_, true} <- {:not_used, force or is_nil(Emoji.get(new_shortcode))} do
|
{:ok, updated_pack} <-
|
||||||
old_path = Path.join(pack.path, filename)
|
pack
|
||||||
old_dir = Path.dirname(old_path)
|
|> delete_emoji(shortcode)
|
||||||
new_path = Path.join(pack.path, new_filename)
|
|> put_emoji(new_shortcode, new_filename)
|
||||||
|
|> save_pack() do
|
||||||
create_subdirs(new_path)
|
Emoji.reload()
|
||||||
|
{:ok, updated_pack}
|
||||||
:ok = File.rename(old_path, new_path)
|
|
||||||
|
|
||||||
if String.contains?(filename, "/") and File.ls!(old_dir) == [] do
|
|
||||||
File.rmdir!(old_dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
files = Map.put(files, new_shortcode, new_filename)
|
|
||||||
|
|
||||||
updated_pack = %{pack | files: files}
|
|
||||||
|
|
||||||
case save_pack(updated_pack) do
|
|
||||||
:ok ->
|
|
||||||
Emoji.reload()
|
|
||||||
{:ok, updated_pack}
|
|
||||||
|
|
||||||
e ->
|
|
||||||
e
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_file(_, _, _, _, _), do: {:error, :empty_values}
|
@spec import_from_filesystem() :: {:ok, [String.t()]} | {:error, File.posix() | atom()}
|
||||||
|
|
||||||
@spec import_from_filesystem() :: {:ok, [String.t()]} | {:error, atom()}
|
|
||||||
def import_from_filesystem do
|
def import_from_filesystem do
|
||||||
emoji_path = emoji_path()
|
emoji_path = emoji_path()
|
||||||
|
|
||||||
@ -184,7 +100,7 @@ defmodule Pleroma.Emoji.Pack do
|
|||||||
File.dir?(path) and File.exists?(Path.join(path, "pack.json"))
|
File.dir?(path) and File.exists?(Path.join(path, "pack.json"))
|
||||||
end)
|
end)
|
||||||
|> Enum.map(&write_pack_contents/1)
|
|> Enum.map(&write_pack_contents/1)
|
||||||
|> Enum.filter(& &1)
|
|> Enum.reject(&is_nil/1)
|
||||||
|
|
||||||
{:ok, names}
|
{:ok, names}
|
||||||
else
|
else
|
||||||
@ -193,6 +109,117 @@ defmodule Pleroma.Emoji.Pack do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec list_remote(String.t()) :: {:ok, map()} | {:error, atom()}
|
||||||
|
def list_remote(url) do
|
||||||
|
uri = url |> String.trim() |> URI.parse()
|
||||||
|
|
||||||
|
with :ok <- validate_shareable_packs_available(uri) do
|
||||||
|
uri
|
||||||
|
|> URI.merge("/api/pleroma/emoji/packs")
|
||||||
|
|> http_get()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec list_local() :: {:ok, map()}
|
||||||
|
def list_local do
|
||||||
|
with {:ok, results} <- list_packs_dir() do
|
||||||
|
packs =
|
||||||
|
results
|
||||||
|
|> Enum.map(fn name ->
|
||||||
|
case load_pack(name) do
|
||||||
|
{:ok, pack} -> pack
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> Enum.reject(&is_nil/1)
|
||||||
|
|> Map.new(fn pack -> {pack.name, validate_pack(pack)} end)
|
||||||
|
|
||||||
|
{:ok, packs}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_archive(String.t()) :: {:ok, binary()} | {:error, atom()}
|
||||||
|
def get_archive(name) do
|
||||||
|
with {:ok, pack} <- load_pack(name),
|
||||||
|
:ok <- validate_downloadable(pack) do
|
||||||
|
{:ok, fetch_archive(pack)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec download(String.t(), String.t(), String.t()) :: :ok | {:error, atom()}
|
||||||
|
def download(name, url, as) do
|
||||||
|
uri = url |> String.trim() |> URI.parse()
|
||||||
|
|
||||||
|
with :ok <- validate_shareable_packs_available(uri),
|
||||||
|
{:ok, remote_pack} <- uri |> URI.merge("/api/pleroma/emoji/packs/#{name}") |> http_get(),
|
||||||
|
{:ok, %{sha: sha, url: url} = pack_info} <- fetch_pack_info(remote_pack, uri, name),
|
||||||
|
{:ok, archive} <- download_archive(url, sha),
|
||||||
|
pack <- copy_as(remote_pack, as || name),
|
||||||
|
{:ok, _} = unzip(archive, pack_info, remote_pack, pack) do
|
||||||
|
# Fallback can't contain a pack.json file, since that would cause the fallback-src-sha256
|
||||||
|
# in it to depend on itself
|
||||||
|
if pack_info[:fallback] do
|
||||||
|
save_pack(pack)
|
||||||
|
else
|
||||||
|
{:ok, pack}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec save_metadata(map(), t()) :: {:ok, t()} | {:error, File.posix()}
|
||||||
|
def save_metadata(metadata, %__MODULE__{} = pack) do
|
||||||
|
pack
|
||||||
|
|> Map.put(:pack, metadata)
|
||||||
|
|> save_pack()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec update_metadata(String.t(), map()) :: {:ok, t()} | {:error, File.posix()}
|
||||||
|
def update_metadata(name, data) do
|
||||||
|
with {:ok, pack} <- load_pack(name) do
|
||||||
|
if fallback_sha_changed?(pack, data) do
|
||||||
|
update_sha_and_save_metadata(pack, data)
|
||||||
|
else
|
||||||
|
save_metadata(data, pack)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec load_pack(String.t()) :: {:ok, t()} | {:error, :not_found}
|
||||||
|
def load_pack(name) do
|
||||||
|
pack_file = Path.join([emoji_path(), name, "pack.json"])
|
||||||
|
|
||||||
|
if File.exists?(pack_file) do
|
||||||
|
pack =
|
||||||
|
pack_file
|
||||||
|
|> File.read!()
|
||||||
|
|> from_json()
|
||||||
|
|> Map.put(:pack_file, pack_file)
|
||||||
|
|> Map.put(:path, Path.dirname(pack_file))
|
||||||
|
|> Map.put(:name, name)
|
||||||
|
|
||||||
|
{:ok, pack}
|
||||||
|
else
|
||||||
|
{:error, :not_found}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec emoji_path() :: Path.t()
|
||||||
|
defp emoji_path do
|
||||||
|
[:instance, :static_dir]
|
||||||
|
|> Pleroma.Config.get!()
|
||||||
|
|> Path.join("emoji")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_emoji_not_exists(shortcode, force \\ false)
|
||||||
|
defp validate_emoji_not_exists(_shortcode, true), do: :ok
|
||||||
|
|
||||||
|
defp validate_emoji_not_exists(shortcode, _) do
|
||||||
|
case Emoji.get(shortcode) do
|
||||||
|
nil -> :ok
|
||||||
|
_ -> {:error, :already_exists}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp write_pack_contents(path) do
|
defp write_pack_contents(path) do
|
||||||
pack = %__MODULE__{
|
pack = %__MODULE__{
|
||||||
files: files_from_path(path),
|
files: files_from_path(path),
|
||||||
@ -201,7 +228,7 @@ defmodule Pleroma.Emoji.Pack do
|
|||||||
}
|
}
|
||||||
|
|
||||||
case save_pack(pack) do
|
case save_pack(pack) do
|
||||||
:ok -> Path.basename(path)
|
{:ok, _pack} -> Path.basename(path)
|
||||||
_ -> nil
|
_ -> nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -216,7 +243,8 @@ defmodule Pleroma.Emoji.Pack do
|
|||||||
# FIXME: Copy-pasted from Pleroma.Emoji/load_from_file_stream/2
|
# FIXME: Copy-pasted from Pleroma.Emoji/load_from_file_stream/2
|
||||||
|
|
||||||
# Create a map of shortcodes to filenames from emoji.txt
|
# Create a map of shortcodes to filenames from emoji.txt
|
||||||
File.read!(txt_path)
|
txt_path
|
||||||
|
|> File.read!()
|
||||||
|> String.split("\n")
|
|> String.split("\n")
|
||||||
|> Enum.map(&String.trim/1)
|
|> Enum.map(&String.trim/1)
|
||||||
|> Enum.map(fn line ->
|
|> Enum.map(fn line ->
|
||||||
@ -226,21 +254,18 @@ defmodule Pleroma.Emoji.Pack do
|
|||||||
[name, file | _] ->
|
[name, file | _] ->
|
||||||
file_dir_name = Path.dirname(file)
|
file_dir_name = Path.dirname(file)
|
||||||
|
|
||||||
file =
|
if String.ends_with?(path, file_dir_name) do
|
||||||
if String.ends_with?(path, file_dir_name) do
|
{name, Path.basename(file)}
|
||||||
Path.basename(file)
|
else
|
||||||
else
|
{name, file}
|
||||||
file
|
end
|
||||||
end
|
|
||||||
|
|
||||||
{name, file}
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|> Enum.filter(& &1)
|
|> Enum.reject(&is_nil/1)
|
||||||
|> Enum.into(%{})
|
|> Map.new()
|
||||||
else
|
else
|
||||||
# If there's no emoji.txt, assume all files
|
# If there's no emoji.txt, assume all files
|
||||||
# that are of certain extensions from the config are emojis and import them all
|
# that are of certain extensions from the config are emojis and import them all
|
||||||
@ -249,60 +274,20 @@ defmodule Pleroma.Emoji.Pack do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec list_remote(String.t()) :: {:ok, map()}
|
|
||||||
def list_remote(url) do
|
|
||||||
uri =
|
|
||||||
url
|
|
||||||
|> String.trim()
|
|
||||||
|> URI.parse()
|
|
||||||
|
|
||||||
with {_, true} <- {:shareable, shareable_packs_available?(uri)} do
|
|
||||||
packs =
|
|
||||||
uri
|
|
||||||
|> URI.merge("/api/pleroma/emoji/packs")
|
|
||||||
|> to_string()
|
|
||||||
|> Tesla.get!()
|
|
||||||
|> Map.get(:body)
|
|
||||||
|> Jason.decode!()
|
|
||||||
|
|
||||||
{:ok, packs}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec list_local() :: {:ok, map()}
|
|
||||||
def list_local do
|
|
||||||
emoji_path = emoji_path()
|
|
||||||
|
|
||||||
# Create the directory first if it does not exist. This is probably the first request made
|
|
||||||
# with the API so it should be sufficient
|
|
||||||
with {:create_dir, :ok} <- {:create_dir, File.mkdir_p(emoji_path)},
|
|
||||||
{:ls, {:ok, results}} <- {:ls, File.ls(emoji_path)} do
|
|
||||||
packs =
|
|
||||||
results
|
|
||||||
|> Enum.map(&load_pack/1)
|
|
||||||
|> Enum.filter(& &1)
|
|
||||||
|> Enum.map(&validate_pack/1)
|
|
||||||
|> Map.new()
|
|
||||||
|
|
||||||
{:ok, packs}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp validate_pack(pack) do
|
defp validate_pack(pack) do
|
||||||
if downloadable?(pack) do
|
info =
|
||||||
archive = fetch_archive(pack)
|
if downloadable?(pack) do
|
||||||
archive_sha = :crypto.hash(:sha256, archive) |> Base.encode16()
|
archive = fetch_archive(pack)
|
||||||
|
archive_sha = :crypto.hash(:sha256, archive) |> Base.encode16()
|
||||||
|
|
||||||
info =
|
|
||||||
pack.pack
|
pack.pack
|
||||||
|> Map.put("can-download", true)
|
|> Map.put("can-download", true)
|
||||||
|> Map.put("download-sha256", archive_sha)
|
|> Map.put("download-sha256", archive_sha)
|
||||||
|
else
|
||||||
|
Map.put(pack.pack, "can-download", false)
|
||||||
|
end
|
||||||
|
|
||||||
{pack.name, Map.put(pack, :pack, info)}
|
Map.put(pack, :pack, info)
|
||||||
else
|
|
||||||
info = Map.put(pack.pack, "can-download", false)
|
|
||||||
{pack.name, Map.put(pack, :pack, info)}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp downloadable?(pack) do
|
defp downloadable?(pack) do
|
||||||
@ -315,26 +300,6 @@ defmodule Pleroma.Emoji.Pack do
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_archive(String.t()) :: {:ok, binary()}
|
|
||||||
def get_archive(name) do
|
|
||||||
with {_, %__MODULE__{} = pack} <- {:exists?, load_pack(name)},
|
|
||||||
{_, true} <- {:can_download?, downloadable?(pack)} do
|
|
||||||
{:ok, fetch_archive(pack)}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp fetch_archive(pack) do
|
|
||||||
hash = :crypto.hash(:md5, File.read!(pack.pack_file))
|
|
||||||
|
|
||||||
case Cachex.get!(:emoji_packs_cache, pack.name) do
|
|
||||||
%{hash: ^hash, pack_data: archive} ->
|
|
||||||
archive
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
create_archive_and_cache(pack, hash)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp create_archive_and_cache(pack, hash) do
|
defp create_archive_and_cache(pack, hash) do
|
||||||
files = ['pack.json' | Enum.map(pack.files, fn {_, file} -> to_charlist(file) end)]
|
files = ['pack.json' | Enum.map(pack.files, fn {_, file} -> to_charlist(file) end)]
|
||||||
|
|
||||||
@ -356,152 +321,221 @@ defmodule Pleroma.Emoji.Pack do
|
|||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec download(String.t(), String.t(), String.t()) :: :ok
|
defp save_pack(pack) do
|
||||||
def download(name, url, as) do
|
with {:ok, json} <- Jason.encode(pack, pretty: true),
|
||||||
uri =
|
:ok <- File.write(pack.pack_file, json) do
|
||||||
url
|
|
||||||
|> String.trim()
|
|
||||||
|> URI.parse()
|
|
||||||
|
|
||||||
with {_, true} <- {:shareable, shareable_packs_available?(uri)} do
|
|
||||||
remote_pack =
|
|
||||||
uri
|
|
||||||
|> URI.merge("/api/pleroma/emoji/packs/#{name}")
|
|
||||||
|> to_string()
|
|
||||||
|> Tesla.get!()
|
|
||||||
|> Map.get(:body)
|
|
||||||
|> Jason.decode!()
|
|
||||||
|
|
||||||
result =
|
|
||||||
case remote_pack["pack"] do
|
|
||||||
%{"share-files" => true, "can-download" => true, "download-sha256" => sha} ->
|
|
||||||
{:ok,
|
|
||||||
%{
|
|
||||||
sha: sha,
|
|
||||||
url: URI.merge(uri, "/api/pleroma/emoji/packs/#{name}/archive") |> to_string()
|
|
||||||
}}
|
|
||||||
|
|
||||||
%{"fallback-src" => src, "fallback-src-sha256" => sha} when is_binary(src) ->
|
|
||||||
{:ok,
|
|
||||||
%{
|
|
||||||
sha: sha,
|
|
||||||
url: src,
|
|
||||||
fallback: true
|
|
||||||
}}
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
{:error,
|
|
||||||
"The pack was not set as shared and there is no fallback src to download from"}
|
|
||||||
end
|
|
||||||
|
|
||||||
with {:ok, %{sha: sha, url: url} = pinfo} <- result,
|
|
||||||
%{body: archive} <- Tesla.get!(url),
|
|
||||||
{_, true} <- {:checksum, Base.decode16!(sha) == :crypto.hash(:sha256, archive)} do
|
|
||||||
local_name = as || name
|
|
||||||
|
|
||||||
path = Path.join(emoji_path(), local_name)
|
|
||||||
|
|
||||||
pack = %__MODULE__{
|
|
||||||
name: local_name,
|
|
||||||
path: path,
|
|
||||||
files: remote_pack["files"],
|
|
||||||
pack_file: Path.join(path, "pack.json")
|
|
||||||
}
|
|
||||||
|
|
||||||
File.mkdir_p!(pack.path)
|
|
||||||
|
|
||||||
files = Enum.map(remote_pack["files"], fn {_, path} -> to_charlist(path) end)
|
|
||||||
# Fallback cannot contain a pack.json file
|
|
||||||
files = if pinfo[:fallback], do: files, else: ['pack.json' | files]
|
|
||||||
|
|
||||||
{:ok, _} = :zip.unzip(archive, cwd: to_charlist(pack.path), file_list: files)
|
|
||||||
|
|
||||||
# Fallback can't contain a pack.json file, since that would cause the fallback-src-sha256
|
|
||||||
# in it to depend on itself
|
|
||||||
if pinfo[:fallback] do
|
|
||||||
save_pack(pack)
|
|
||||||
end
|
|
||||||
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp save_pack(pack), do: File.write(pack.pack_file, Jason.encode!(pack, pretty: true))
|
|
||||||
|
|
||||||
@spec save_metadata(map(), t()) :: {:ok, t()} | {:error, File.posix()}
|
|
||||||
def save_metadata(metadata, %__MODULE__{} = pack) do
|
|
||||||
pack = Map.put(pack, :pack, metadata)
|
|
||||||
|
|
||||||
with :ok <- save_pack(pack) do
|
|
||||||
{:ok, pack}
|
{:ok, pack}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec update_metadata(String.t(), map()) :: {:ok, t()} | {:error, File.posix()}
|
|
||||||
def update_metadata(name, data) do
|
|
||||||
pack = load_pack(name)
|
|
||||||
|
|
||||||
fb_sha_changed? =
|
|
||||||
not is_nil(data["fallback-src"]) and data["fallback-src"] != pack.pack["fallback-src"]
|
|
||||||
|
|
||||||
with {_, true} <- {:update?, fb_sha_changed?},
|
|
||||||
{:ok, %{body: zip}} <- Tesla.get(data["fallback-src"]),
|
|
||||||
{:ok, f_list} <- :zip.unzip(zip, [:memory]),
|
|
||||||
{_, true} <- {:has_all_files?, has_all_files?(pack.files, f_list)} do
|
|
||||||
fallback_sha = :crypto.hash(:sha256, zip) |> Base.encode16()
|
|
||||||
|
|
||||||
data
|
|
||||||
|> Map.put("fallback-src-sha256", fallback_sha)
|
|
||||||
|> save_metadata(pack)
|
|
||||||
else
|
|
||||||
{:update?, _} -> save_metadata(data, pack)
|
|
||||||
e -> e
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if all files from the pack.json are in the archive
|
|
||||||
defp has_all_files?(files, f_list) do
|
|
||||||
Enum.all?(files, fn {_, from_manifest} ->
|
|
||||||
List.keyfind(f_list, to_charlist(from_manifest), 0)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
@spec load_pack(String.t()) :: t() | nil
|
|
||||||
def load_pack(name) do
|
|
||||||
pack_file = Path.join([emoji_path(), name, "pack.json"])
|
|
||||||
|
|
||||||
if File.exists?(pack_file) do
|
|
||||||
pack_file
|
|
||||||
|> File.read!()
|
|
||||||
|> from_json()
|
|
||||||
|> Map.put(:pack_file, pack_file)
|
|
||||||
|> Map.put(:path, Path.dirname(pack_file))
|
|
||||||
|> Map.put(:name, name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp from_json(json) do
|
defp from_json(json) do
|
||||||
map = Jason.decode!(json)
|
map = Jason.decode!(json)
|
||||||
|
|
||||||
struct(__MODULE__, %{files: map["files"], pack: map["pack"]})
|
struct(__MODULE__, %{files: map["files"], pack: map["pack"]})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp shareable_packs_available?(uri) do
|
defp validate_shareable_packs_available(uri) do
|
||||||
uri
|
with {:ok, %{"links" => links}} <- uri |> URI.merge("/.well-known/nodeinfo") |> http_get(),
|
||||||
|> URI.merge("/.well-known/nodeinfo")
|
# Get the actual nodeinfo address and fetch it
|
||||||
|> to_string()
|
{:ok, %{"metadata" => %{"features" => features}}} <-
|
||||||
|> Tesla.get!()
|
links |> List.last() |> Map.get("href") |> http_get() do
|
||||||
|> Map.get(:body)
|
if Enum.member?(features, "shareable_emoji_packs") do
|
||||||
|> Jason.decode!()
|
:ok
|
||||||
|> Map.get("links")
|
else
|
||||||
|> List.last()
|
{:error, :not_shareable}
|
||||||
|> Map.get("href")
|
end
|
||||||
# Get the actual nodeinfo address and fetch it
|
end
|
||||||
|> Tesla.get!()
|
end
|
||||||
|> Map.get(:body)
|
|
||||||
|> Jason.decode!()
|
defp validate_not_empty(list) do
|
||||||
|> get_in(["metadata", "features"])
|
if Enum.all?(list, fn i -> is_binary(i) and i != "" end) do
|
||||||
|> Enum.member?("shareable_emoji_packs")
|
:ok
|
||||||
|
else
|
||||||
|
{:error, :empty_values}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp save_file(file, pack, filename) do
|
||||||
|
file_path = Path.join(pack.path, filename)
|
||||||
|
create_subdirs(file_path)
|
||||||
|
|
||||||
|
case file do
|
||||||
|
%Plug.Upload{path: upload_path} ->
|
||||||
|
# Copy the uploaded file from the temporary directory
|
||||||
|
with {:ok, _} <- File.copy(upload_path, file_path), do: :ok
|
||||||
|
|
||||||
|
url when is_binary(url) ->
|
||||||
|
# Download and write the file
|
||||||
|
file_contents = Tesla.get!(url).body
|
||||||
|
File.write(file_path, file_contents)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp put_emoji(pack, shortcode, filename) do
|
||||||
|
files = Map.put(pack.files, shortcode, filename)
|
||||||
|
%{pack | files: files}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp delete_emoji(pack, shortcode) do
|
||||||
|
files = Map.delete(pack.files, shortcode)
|
||||||
|
%{pack | files: files}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp rename_file(pack, filename, new_filename) do
|
||||||
|
old_path = Path.join(pack.path, filename)
|
||||||
|
new_path = Path.join(pack.path, new_filename)
|
||||||
|
create_subdirs(new_path)
|
||||||
|
|
||||||
|
with :ok <- File.rename(old_path, new_path) do
|
||||||
|
remove_dir_if_empty(old_path, filename)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp create_subdirs(file_path) do
|
||||||
|
if String.contains?(file_path, "/") do
|
||||||
|
file_path
|
||||||
|
|> Path.dirname()
|
||||||
|
|> File.mkdir_p!()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp remove_file(pack, shortcode) do
|
||||||
|
with {:ok, filename} <- get_filename(pack, shortcode),
|
||||||
|
emoji <- Path.join(pack.path, filename),
|
||||||
|
:ok <- File.rm(emoji) do
|
||||||
|
remove_dir_if_empty(emoji, filename)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp remove_dir_if_empty(emoji, filename) do
|
||||||
|
dir = Path.dirname(emoji)
|
||||||
|
|
||||||
|
if String.contains?(filename, "/") and File.ls!(dir) == [] do
|
||||||
|
File.rmdir!(dir)
|
||||||
|
else
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_filename(pack, shortcode) do
|
||||||
|
with %{^shortcode => filename} when is_binary(filename) <- pack.files,
|
||||||
|
true <- pack.path |> Path.join(filename) |> File.exists?() do
|
||||||
|
{:ok, filename}
|
||||||
|
else
|
||||||
|
_ -> {:error, :doesnt_exist}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp http_get(%URI{} = url), do: url |> to_string() |> http_get()
|
||||||
|
|
||||||
|
defp http_get(url) do
|
||||||
|
with {:ok, %{body: body}} <- url |> Pleroma.HTTP.get() do
|
||||||
|
Jason.decode(body)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp list_packs_dir do
|
||||||
|
emoji_path = emoji_path()
|
||||||
|
# Create the directory first if it does not exist. This is probably the first request made
|
||||||
|
# with the API so it should be sufficient
|
||||||
|
with {:create_dir, :ok} <- {:create_dir, File.mkdir_p(emoji_path)},
|
||||||
|
{:ls, {:ok, results}} <- {:ls, File.ls(emoji_path)} do
|
||||||
|
{:ok, results}
|
||||||
|
else
|
||||||
|
{:create_dir, {:error, e}} -> {:error, :create_dir, e}
|
||||||
|
{:ls, {:error, e}} -> {:error, :ls, e}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_downloadable(pack) do
|
||||||
|
if downloadable?(pack), do: :ok, else: {:error, :cant_download}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp copy_as(remote_pack, local_name) do
|
||||||
|
path = Path.join(emoji_path(), local_name)
|
||||||
|
|
||||||
|
%__MODULE__{
|
||||||
|
name: local_name,
|
||||||
|
path: path,
|
||||||
|
files: remote_pack["files"],
|
||||||
|
pack_file: Path.join(path, "pack.json")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp unzip(archive, pack_info, remote_pack, local_pack) do
|
||||||
|
with :ok <- File.mkdir_p!(local_pack.path) do
|
||||||
|
files = Enum.map(remote_pack["files"], fn {_, path} -> to_charlist(path) end)
|
||||||
|
# Fallback cannot contain a pack.json file
|
||||||
|
files = if pack_info[:fallback], do: files, else: ['pack.json' | files]
|
||||||
|
|
||||||
|
:zip.unzip(archive, cwd: to_charlist(local_pack.path), file_list: files)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_pack_info(remote_pack, uri, name) do
|
||||||
|
case remote_pack["pack"] do
|
||||||
|
%{"share-files" => true, "can-download" => true, "download-sha256" => sha} ->
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
sha: sha,
|
||||||
|
url: URI.merge(uri, "/api/pleroma/emoji/packs/#{name}/archive") |> to_string()
|
||||||
|
}}
|
||||||
|
|
||||||
|
%{"fallback-src" => src, "fallback-src-sha256" => sha} when is_binary(src) ->
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
sha: sha,
|
||||||
|
url: src,
|
||||||
|
fallback: true
|
||||||
|
}}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:error, "The pack was not set as shared and there is no fallback src to download from"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp download_archive(url, sha) do
|
||||||
|
with {:ok, %{body: archive}} <- Tesla.get(url) do
|
||||||
|
if Base.decode16!(sha) == :crypto.hash(:sha256, archive) do
|
||||||
|
{:ok, archive}
|
||||||
|
else
|
||||||
|
{:error, :imvalid_checksum}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_archive(pack) do
|
||||||
|
hash = :crypto.hash(:md5, File.read!(pack.pack_file))
|
||||||
|
|
||||||
|
case Cachex.get!(:emoji_packs_cache, pack.name) do
|
||||||
|
%{hash: ^hash, pack_data: archive} -> archive
|
||||||
|
_ -> create_archive_and_cache(pack, hash)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fallback_sha_changed?(pack, data) do
|
||||||
|
is_binary(data[:"fallback-src"]) and data[:"fallback-src"] != pack.pack["fallback-src"]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_sha_and_save_metadata(pack, data) do
|
||||||
|
with {:ok, %{body: zip}} <- Tesla.get(data[:"fallback-src"]),
|
||||||
|
:ok <- validate_has_all_files(pack, zip) do
|
||||||
|
fallback_sha = :sha256 |> :crypto.hash(zip) |> Base.encode16()
|
||||||
|
|
||||||
|
data
|
||||||
|
|> Map.put("fallback-src-sha256", fallback_sha)
|
||||||
|
|> save_metadata(pack)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_has_all_files(pack, zip) do
|
||||||
|
with {:ok, f_list} <- :zip.unzip(zip, [:memory]) do
|
||||||
|
# Check if all files from the pack.json are in the archive
|
||||||
|
pack.files
|
||||||
|
|> Enum.all?(fn {_, from_manifest} ->
|
||||||
|
List.keyfind(f_list, to_charlist(from_manifest), 0)
|
||||||
|
end)
|
||||||
|
|> if(do: :ok, else: {:error, :incomplete})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.MFA do
|
defmodule Pleroma.MFA do
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.MFA.BackupCodes do
|
defmodule Pleroma.MFA.BackupCodes do
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.MFA.Changeset do
|
defmodule Pleroma.MFA.Changeset do
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.MFA.Settings do
|
defmodule Pleroma.MFA.Settings do
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.MFA.Token do
|
defmodule Pleroma.MFA.Token do
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.MFA.TOTP do
|
defmodule Pleroma.MFA.TOTP do
|
||||||
|
@ -9,11 +9,13 @@ defmodule Pleroma.Object do
|
|||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Config
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Object.Fetcher
|
alias Pleroma.Object.Fetcher
|
||||||
alias Pleroma.ObjectTombstone
|
alias Pleroma.ObjectTombstone
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Workers.AttachmentsCleanupWorker
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@ -138,12 +140,17 @@ defmodule Pleroma.Object do
|
|||||||
|
|
||||||
def normalize(_, _, _), do: nil
|
def normalize(_, _, _), do: nil
|
||||||
|
|
||||||
# Owned objects can only be mutated by their owner
|
# Owned objects can only be accessed by their owner
|
||||||
def authorize_mutation(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}),
|
def authorize_access(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}) do
|
||||||
do: actor == ap_id
|
if actor == ap_id do
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
{:error, :forbidden}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Legacy objects can be mutated by anybody
|
# Legacy objects can be accessed by anybody
|
||||||
def authorize_mutation(%Object{}, %User{}), do: true
|
def authorize_access(%Object{}, %User{}), do: :ok
|
||||||
|
|
||||||
@spec get_cached_by_ap_id(String.t()) :: Object.t() | nil
|
@spec get_cached_by_ap_id(String.t()) :: Object.t() | nil
|
||||||
def get_cached_by_ap_id(ap_id) do
|
def get_cached_by_ap_id(ap_id) do
|
||||||
@ -183,27 +190,37 @@ defmodule Pleroma.Object do
|
|||||||
def delete(%Object{data: %{"id" => id}} = object) do
|
def delete(%Object{data: %{"id" => id}} = object) do
|
||||||
with {:ok, _obj} = swap_object_with_tombstone(object),
|
with {:ok, _obj} = swap_object_with_tombstone(object),
|
||||||
deleted_activity = Activity.delete_all_by_object_ap_id(id),
|
deleted_activity = Activity.delete_all_by_object_ap_id(id),
|
||||||
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}"),
|
{:ok, _} <- invalid_object_cache(object) do
|
||||||
{:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path) do
|
cleanup_attachments(
|
||||||
with true <- Pleroma.Config.get([:instance, :cleanup_attachments]) do
|
Config.get([:instance, :cleanup_attachments]),
|
||||||
{:ok, _} =
|
%{"object" => object}
|
||||||
Pleroma.Workers.AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{
|
)
|
||||||
"object" => object
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
{:ok, object, deleted_activity}
|
{:ok, object, deleted_activity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def prune(%Object{data: %{"id" => id}} = object) do
|
@spec cleanup_attachments(boolean(), %{required(:object) => map()}) ::
|
||||||
|
{:ok, Oban.Job.t() | nil}
|
||||||
|
def cleanup_attachments(true, %{"object" => _} = params) do
|
||||||
|
AttachmentsCleanupWorker.enqueue("cleanup_attachments", params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def cleanup_attachments(_, _), do: {:ok, nil}
|
||||||
|
|
||||||
|
def prune(%Object{data: %{"id" => _id}} = object) do
|
||||||
with {:ok, object} <- Repo.delete(object),
|
with {:ok, object} <- Repo.delete(object),
|
||||||
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}"),
|
{:ok, _} <- invalid_object_cache(object) do
|
||||||
{:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path) do
|
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def invalid_object_cache(%Object{data: %{"id" => id}}) do
|
||||||
|
with {:ok, true} <- Cachex.del(:object_cache, "object:#{id}") do
|
||||||
|
Cachex.del(:web_resp_cache, URI.parse(id).path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def set_cache(%Object{data: %{"id" => ap_id}} = object) do
|
def set_cache(%Object{data: %{"id" => ap_id}} = object) do
|
||||||
Cachex.put(:object_cache, "object:#{ap_id}", object)
|
Cachex.put(:object_cache, "object:#{ap_id}", object)
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
|
@ -30,6 +30,25 @@ defmodule Pleroma.Plugs.AuthenticationPlug do
|
|||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def maybe_update_password(%User{password_hash: "$2" <> _} = user, password) do
|
||||||
|
do_update_password(user, password)
|
||||||
|
end
|
||||||
|
|
||||||
|
def maybe_update_password(%User{password_hash: "$6" <> _} = user, password) do
|
||||||
|
do_update_password(user, password)
|
||||||
|
end
|
||||||
|
|
||||||
|
def maybe_update_password(user, _), do: {:ok, user}
|
||||||
|
|
||||||
|
defp do_update_password(user, password) do
|
||||||
|
user
|
||||||
|
|> User.password_update_changeset(%{
|
||||||
|
"password" => password,
|
||||||
|
"password_confirmation" => password
|
||||||
|
})
|
||||||
|
|> Pleroma.Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||||
|
|
||||||
def call(
|
def call(
|
||||||
@ -42,6 +61,8 @@ defmodule Pleroma.Plugs.AuthenticationPlug do
|
|||||||
_
|
_
|
||||||
) do
|
) do
|
||||||
if checkpw(password, password_hash) do
|
if checkpw(password, password_hash) do
|
||||||
|
{:ok, auth_user} = maybe_update_password(auth_user, password)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> assign(:user, auth_user)
|
|> assign(:user, auth_user)
|
||||||
|> OAuthScopesPlug.skip_plug()
|
|> OAuthScopesPlug.skip_plug()
|
||||||
|
@ -1204,7 +1204,9 @@ defmodule Pleroma.User do
|
|||||||
def get_recipients_from_activity(%Activity{recipients: to, actor: actor}) do
|
def get_recipients_from_activity(%Activity{recipients: to, actor: actor}) do
|
||||||
to = [actor | to]
|
to = [actor | to]
|
||||||
|
|
||||||
User.Query.build(%{recipients_from_activity: to, local: true, deactivated: false})
|
query = User.Query.build(%{recipients_from_activity: to, local: true, deactivated: false})
|
||||||
|
|
||||||
|
query
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1430,6 +1432,25 @@ defmodule Pleroma.User do
|
|||||||
BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
|
BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp delete_and_invalidate_cache(%User{} = user) do
|
||||||
|
invalidate_cache(user)
|
||||||
|
Repo.delete(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp delete_or_deactivate(%User{local: false} = user), do: delete_and_invalidate_cache(user)
|
||||||
|
|
||||||
|
defp delete_or_deactivate(%User{local: true} = user) do
|
||||||
|
status = account_status(user)
|
||||||
|
|
||||||
|
if status == :confirmation_pending do
|
||||||
|
delete_and_invalidate_cache(user)
|
||||||
|
else
|
||||||
|
user
|
||||||
|
|> change(%{deactivated: true, email: nil})
|
||||||
|
|> update_and_set_cache()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def perform(:force_password_reset, user), do: force_password_reset(user)
|
def perform(:force_password_reset, user), do: force_password_reset(user)
|
||||||
|
|
||||||
@spec perform(atom(), User.t()) :: {:ok, User.t()}
|
@spec perform(atom(), User.t()) :: {:ok, User.t()}
|
||||||
@ -1451,14 +1472,7 @@ defmodule Pleroma.User do
|
|||||||
|
|
||||||
delete_user_activities(user)
|
delete_user_activities(user)
|
||||||
|
|
||||||
if user.local do
|
delete_or_deactivate(user)
|
||||||
user
|
|
||||||
|> change(%{deactivated: true, email: nil})
|
|
||||||
|> update_and_set_cache()
|
|
||||||
else
|
|
||||||
invalidate_cache(user)
|
|
||||||
Repo.delete(user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(:deactivate_async, user, status), do: deactivate(user, status)
|
def perform(:deactivate_async, user, status), do: deactivate(user, status)
|
||||||
|
@ -167,20 +167,18 @@ defmodule Pleroma.User.Query do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp compose_query({:recipients_from_activity, to}, query) do
|
defp compose_query({:recipients_from_activity, to}, query) do
|
||||||
query
|
following_query =
|
||||||
|> join(:left, [u], r in FollowingRelationship,
|
from(u in User,
|
||||||
as: :relationships,
|
join: f in FollowingRelationship,
|
||||||
on: r.follower_id == u.id
|
on: u.id == f.following_id,
|
||||||
|
where: f.state == ^:follow_accept,
|
||||||
|
where: u.follower_address in ^to,
|
||||||
|
select: f.follower_id
|
||||||
|
)
|
||||||
|
|
||||||
|
from(u in query,
|
||||||
|
where: u.ap_id in ^to or u.id in subquery(following_query)
|
||||||
)
|
)
|
||||||
|> join(:left, [relationships: r], f in User,
|
|
||||||
as: :following,
|
|
||||||
on: f.id == r.following_id
|
|
||||||
)
|
|
||||||
|> where(
|
|
||||||
[u, following: f, relationships: r],
|
|
||||||
u.ap_id in ^to or (f.follower_address in ^to and r.state == ^:follow_accept)
|
|
||||||
)
|
|
||||||
|> distinct(true)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp compose_query({:order_by, key}, query) do
|
defp compose_query({:order_by, key}, query) do
|
||||||
|
@ -87,6 +87,22 @@ defmodule Pleroma.UserRelationship do
|
|||||||
source_to_target_rel_types \\ nil,
|
source_to_target_rel_types \\ nil,
|
||||||
target_to_source_rel_types \\ nil
|
target_to_source_rel_types \\ nil
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def dictionary(
|
||||||
|
_source_users,
|
||||||
|
_target_users,
|
||||||
|
[] = _source_to_target_rel_types,
|
||||||
|
[] = _target_to_source_rel_types
|
||||||
|
) do
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
def dictionary(
|
||||||
|
source_users,
|
||||||
|
target_users,
|
||||||
|
source_to_target_rel_types,
|
||||||
|
target_to_source_rel_types
|
||||||
|
)
|
||||||
when is_list(source_users) and is_list(target_users) do
|
when is_list(source_users) and is_list(target_users) do
|
||||||
source_user_ids = User.binary_id(source_users)
|
source_user_ids = User.binary_id(source_users)
|
||||||
target_user_ids = User.binary_id(target_users)
|
target_user_ids = User.binary_id(target_users)
|
||||||
@ -138,11 +154,16 @@ defmodule Pleroma.UserRelationship do
|
|||||||
|
|
||||||
def view_relationships_option(%User{} = reading_user, actors, opts) do
|
def view_relationships_option(%User{} = reading_user, actors, opts) do
|
||||||
{source_to_target_rel_types, target_to_source_rel_types} =
|
{source_to_target_rel_types, target_to_source_rel_types} =
|
||||||
if opts[:source_mutes_only] do
|
case opts[:subset] do
|
||||||
# This option is used for rendering statuses (FE needs `muted` flag for each one anyways)
|
:source_mutes ->
|
||||||
{[:mute], []}
|
# Used for statuses rendering (FE needs `muted` flag for each status when statuses load)
|
||||||
else
|
{[:mute], []}
|
||||||
{[:block, :mute, :notification_mute, :reblog_mute], [:block, :inverse_subscription]}
|
|
||||||
|
nil ->
|
||||||
|
{[:block, :mute, :notification_mute, :reblog_mute], [:block, :inverse_subscription]}
|
||||||
|
|
||||||
|
unknown ->
|
||||||
|
raise "Unsupported :subset option value: #{inspect(unknown)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
user_relationships =
|
user_relationships =
|
||||||
@ -153,7 +174,17 @@ defmodule Pleroma.UserRelationship do
|
|||||||
target_to_source_rel_types
|
target_to_source_rel_types
|
||||||
)
|
)
|
||||||
|
|
||||||
following_relationships = FollowingRelationship.all_between_user_sets([reading_user], actors)
|
following_relationships =
|
||||||
|
case opts[:subset] do
|
||||||
|
:source_mutes ->
|
||||||
|
[]
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
FollowingRelationship.all_between_user_sets([reading_user], actors)
|
||||||
|
|
||||||
|
unknown ->
|
||||||
|
raise "Unsupported :subset option value: #{inspect(unknown)}"
|
||||||
|
end
|
||||||
|
|
||||||
%{user_relationships: user_relationships, following_relationships: following_relationships}
|
%{user_relationships: user_relationships, following_relationships: following_relationships}
|
||||||
end
|
end
|
||||||
|
@ -22,6 +22,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||||||
alias Pleroma.Web.ActivityPub.Pipeline
|
alias Pleroma.Web.ActivityPub.Pipeline
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.Web.AdminAPI
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
alias Pleroma.Web.AdminAPI.ConfigView
|
alias Pleroma.Web.AdminAPI.ConfigView
|
||||||
alias Pleroma.Web.AdminAPI.ModerationLogView
|
alias Pleroma.Web.AdminAPI.ModerationLogView
|
||||||
@ -30,8 +31,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||||||
alias Pleroma.Web.AdminAPI.Search
|
alias Pleroma.Web.AdminAPI.Search
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
|
alias Pleroma.Web.MastodonAPI
|
||||||
alias Pleroma.Web.MastodonAPI.AppView
|
alias Pleroma.Web.MastodonAPI.AppView
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
|
||||||
alias Pleroma.Web.OAuth.App
|
alias Pleroma.Web.OAuth.App
|
||||||
alias Pleroma.Web.Router
|
alias Pleroma.Web.Router
|
||||||
|
|
||||||
@ -280,8 +281,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||||||
})
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(Pleroma.Web.AdminAPI.StatusView)
|
|> put_view(AdminAPI.StatusView)
|
||||||
|> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
|
|> render("index.json", %{activities: activities, as: :activity})
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
||||||
@ -299,8 +300,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||||||
})
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(MastodonAPI.StatusView)
|
||||||
|> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
|
|> render("index.json", %{activities: activities, as: :activity})
|
||||||
else
|
else
|
||||||
_ -> {:error, :not_found}
|
_ -> {:error, :not_found}
|
||||||
end
|
end
|
||||||
@ -829,14 +830,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||||||
})
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(Pleroma.Web.AdminAPI.StatusView)
|
|> put_view(AdminAPI.StatusView)
|
||||||
|> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
|
|> render("index.json", %{activities: activities, as: :activity})
|
||||||
end
|
end
|
||||||
|
|
||||||
def status_show(conn, %{"id" => id}) do
|
def status_show(conn, %{"id" => id}) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id(id) do
|
with %Activity{} = activity <- Activity.get_by_id(id) do
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(MastodonAPI.StatusView)
|
||||||
|> render("show.json", %{activity: activity})
|
|> render("show.json", %{activity: activity})
|
||||||
else
|
else
|
||||||
_ -> errors(conn, {:error, :not_found})
|
_ -> errors(conn, {:error, :not_found})
|
||||||
@ -861,7 +862,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||||||
})
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(MastodonAPI.StatusView)
|
||||||
|> render("show.json", %{activity: activity})
|
|> render("show.json", %{activity: activity})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -6,7 +6,9 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.AdminAPI
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
|
alias Pleroma.Web.MastodonAPI
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
def render("index.json", %{users: users, count: count, page_size: page_size}) do
|
def render("index.json", %{users: users, count: count, page_size: page_size}) do
|
||||||
@ -119,6 +121,13 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def merge_account_views(%User{} = user) do
|
||||||
|
MastodonAPI.AccountView.render("show.json", %{user: user})
|
||||||
|
|> Map.merge(AdminAPI.AccountView.render("show.json", %{user: user}))
|
||||||
|
end
|
||||||
|
|
||||||
|
def merge_account_views(_), do: %{}
|
||||||
|
|
||||||
defp parse_error([]), do: ""
|
defp parse_error([]), do: ""
|
||||||
|
|
||||||
defp parse_error(errors) do
|
defp parse_error(errors) do
|
||||||
|
@ -7,10 +7,13 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
|
|||||||
|
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.AdminAPI
|
||||||
alias Pleroma.Web.AdminAPI.Report
|
alias Pleroma.Web.AdminAPI.Report
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
|
||||||
|
defdelegate merge_account_views(user), to: AdminAPI.AccountView
|
||||||
|
|
||||||
def render("index.json", %{reports: reports}) do
|
def render("index.json", %{reports: reports}) do
|
||||||
%{
|
%{
|
||||||
reports:
|
reports:
|
||||||
@ -41,8 +44,7 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
|
|||||||
statuses:
|
statuses:
|
||||||
StatusView.render("index.json", %{
|
StatusView.render("index.json", %{
|
||||||
activities: statuses,
|
activities: statuses,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: false
|
|
||||||
}),
|
}),
|
||||||
state: report.data["state"],
|
state: report.data["state"],
|
||||||
notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes})
|
notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes})
|
||||||
@ -70,11 +72,4 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
|
|||||||
created_at: Utils.to_masto_date(inserted_at)
|
created_at: Utils.to_masto_date(inserted_at)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp merge_account_views(%User{} = user) do
|
|
||||||
Pleroma.Web.MastodonAPI.AccountView.render("show.json", %{user: user})
|
|
||||||
|> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}))
|
|
||||||
end
|
|
||||||
|
|
||||||
defp merge_account_views(_), do: %{}
|
|
||||||
end
|
end
|
||||||
|
@ -7,24 +7,19 @@ defmodule Pleroma.Web.AdminAPI.StatusView do
|
|||||||
|
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
|
||||||
alias Pleroma.User
|
alias Pleroma.Web.AdminAPI
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI
|
||||||
|
|
||||||
|
defdelegate merge_account_views(user), to: AdminAPI.AccountView
|
||||||
|
|
||||||
def render("index.json", opts) do
|
def render("index.json", opts) do
|
||||||
safe_render_many(opts.activities, __MODULE__, "show.json", opts)
|
safe_render_many(opts.activities, __MODULE__, "show.json", opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
||||||
user = StatusView.get_user(activity.data["actor"])
|
user = MastodonAPI.StatusView.get_user(activity.data["actor"])
|
||||||
|
|
||||||
StatusView.render("show.json", opts)
|
MastodonAPI.StatusView.render("show.json", opts)
|
||||||
|> Map.merge(%{account: merge_account_views(user)})
|
|> Map.merge(%{account: merge_account_views(user)})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp merge_account_views(%User{} = user) do
|
|
||||||
Pleroma.Web.MastodonAPI.AccountView.render("show.json", %{user: user})
|
|
||||||
|> Map.merge(Pleroma.Web.AdminAPI.AccountView.render("show.json", %{user: user}))
|
|
||||||
end
|
|
||||||
|
|
||||||
defp merge_account_views(_), do: %{}
|
|
||||||
end
|
end
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
defmodule Pleroma.Web.ApiSpec.Helpers do
|
defmodule Pleroma.Web.ApiSpec.Helpers do
|
||||||
alias OpenApiSpex.Operation
|
alias OpenApiSpex.Operation
|
||||||
alias OpenApiSpex.Schema
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||||
|
|
||||||
def request_body(description, schema_ref, opts \\ []) do
|
def request_body(description, schema_ref, opts \\ []) do
|
||||||
media_types = ["application/json", "multipart/form-data", "application/x-www-form-urlencoded"]
|
media_types = ["application/json", "multipart/form-data", "application/x-www-form-urlencoded"]
|
||||||
@ -47,6 +48,15 @@ defmodule Pleroma.Web.ApiSpec.Helpers do
|
|||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def with_relationships_param do
|
||||||
|
Operation.parameter(
|
||||||
|
:with_relationships,
|
||||||
|
:query,
|
||||||
|
BooleanLike,
|
||||||
|
"Embed relationships into accounts."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def empty_object_response do
|
def empty_object_response do
|
||||||
Operation.response("Empty object", "application/json", %Schema{type: :object, example: %{}})
|
Operation.response("Empty object", "application/json", %Schema{type: :object, example: %{}})
|
||||||
end
|
end
|
||||||
|
@ -155,8 +155,10 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||||||
security: [%{"oAuth" => ["read:accounts"]}],
|
security: [%{"oAuth" => ["read:accounts"]}],
|
||||||
description:
|
description:
|
||||||
"Accounts which follow the given account, if network is not hidden by the account owner.",
|
"Accounts which follow the given account, if network is not hidden by the account owner.",
|
||||||
parameters:
|
parameters: [
|
||||||
[%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}] ++ pagination_params(),
|
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
|
||||||
|
with_relationships_param() | pagination_params()
|
||||||
|
],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
||||||
}
|
}
|
||||||
@ -171,8 +173,10 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||||||
security: [%{"oAuth" => ["read:accounts"]}],
|
security: [%{"oAuth" => ["read:accounts"]}],
|
||||||
description:
|
description:
|
||||||
"Accounts which the given account is following, if network is not hidden by the account owner.",
|
"Accounts which the given account is following, if network is not hidden by the account owner.",
|
||||||
parameters:
|
parameters: [
|
||||||
[%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}] ++ pagination_params(),
|
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
|
||||||
|
with_relationships_param() | pagination_params()
|
||||||
|
],
|
||||||
responses: %{200 => Operation.response("Accounts", "application/json", array_of_accounts())}
|
responses: %{200 => Operation.response("Accounts", "application/json", array_of_accounts())}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@ -389,7 +393,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||||||
format: :password
|
format: :password
|
||||||
},
|
},
|
||||||
agreement: %Schema{
|
agreement: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
description:
|
description:
|
||||||
"Whether the user agrees to the local rules, terms, and policies. These should be presented to the user in order to allow them to consent before setting this parameter to TRUE."
|
"Whether the user agrees to the local rules, terms, and policies. These should be presented to the user in order to allow them to consent before setting this parameter to TRUE."
|
||||||
},
|
},
|
||||||
@ -459,7 +463,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
bot: %Schema{
|
bot: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Whether the account has a bot flag."
|
description: "Whether the account has a bot flag."
|
||||||
},
|
},
|
||||||
@ -482,7 +486,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||||||
format: :binary
|
format: :binary
|
||||||
},
|
},
|
||||||
locked: %Schema{
|
locked: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Whether manual approval of follow requests is required."
|
description: "Whether manual approval of follow requests is required."
|
||||||
},
|
},
|
||||||
@ -506,37 +510,37 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||||||
|
|
||||||
# Pleroma-specific fields
|
# Pleroma-specific fields
|
||||||
no_rich_text: %Schema{
|
no_rich_text: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "html tags are stripped from all statuses requested from the API"
|
description: "html tags are stripped from all statuses requested from the API"
|
||||||
},
|
},
|
||||||
hide_followers: %Schema{
|
hide_followers: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "user's followers will be hidden"
|
description: "user's followers will be hidden"
|
||||||
},
|
},
|
||||||
hide_follows: %Schema{
|
hide_follows: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "user's follows will be hidden"
|
description: "user's follows will be hidden"
|
||||||
},
|
},
|
||||||
hide_followers_count: %Schema{
|
hide_followers_count: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "user's follower count will be hidden"
|
description: "user's follower count will be hidden"
|
||||||
},
|
},
|
||||||
hide_follows_count: %Schema{
|
hide_follows_count: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "user's follow count will be hidden"
|
description: "user's follow count will be hidden"
|
||||||
},
|
},
|
||||||
hide_favorites: %Schema{
|
hide_favorites: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "user's favorites timeline will be hidden"
|
description: "user's favorites timeline will be hidden"
|
||||||
},
|
},
|
||||||
show_role: %Schema{
|
show_role: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "user's role (e.g admin, moderator) will be exposed to anyone in the
|
description: "user's role (e.g admin, moderator) will be exposed to anyone in the
|
||||||
API"
|
API"
|
||||||
@ -548,12 +552,12 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||||||
description: "Opaque user settings to be saved on the backend."
|
description: "Opaque user settings to be saved on the backend."
|
||||||
},
|
},
|
||||||
skip_thread_containment: %Schema{
|
skip_thread_containment: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Skip filtering out broken threads"
|
description: "Skip filtering out broken threads"
|
||||||
},
|
},
|
||||||
allow_following_move: %Schema{
|
allow_following_move: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Allows automatically follow moved following accounts"
|
description: "Allows automatically follow moved following accounts"
|
||||||
},
|
},
|
||||||
@ -564,7 +568,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||||||
format: :binary
|
format: :binary
|
||||||
},
|
},
|
||||||
discoverable: %Schema{
|
discoverable: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description:
|
description:
|
||||||
"Discovery of this account in search results and other services is allowed."
|
"Discovery of this account in search results and other services is allowed."
|
||||||
@ -674,7 +678,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
notifications: %Schema{
|
notifications: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Mute notifications in addition to statuses? Defaults to true.",
|
description: "Mute notifications in addition to statuses? Defaults to true.",
|
||||||
default: true
|
default: true
|
||||||
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.FilterOperation do
|
|||||||
alias OpenApiSpex.Operation
|
alias OpenApiSpex.Operation
|
||||||
alias OpenApiSpex.Schema
|
alias OpenApiSpex.Schema
|
||||||
alias Pleroma.Web.ApiSpec.Helpers
|
alias Pleroma.Web.ApiSpec.Helpers
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||||
|
|
||||||
def open_api_operation(action) do
|
def open_api_operation(action) do
|
||||||
operation = String.to_existing_atom("#{action}_operation")
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
@ -171,7 +172,7 @@ defmodule Pleroma.Web.ApiSpec.FilterOperation do
|
|||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
irreversible: %Schema{
|
irreversible: %Schema{
|
||||||
type: :bolean,
|
allOf: [BooleanLike],
|
||||||
description:
|
description:
|
||||||
"Should the server irreversibly drop matching entities from home and notifications?",
|
"Should the server irreversibly drop matching entities from home and notifications?",
|
||||||
default: false
|
default: false
|
||||||
@ -199,13 +200,13 @@ defmodule Pleroma.Web.ApiSpec.FilterOperation do
|
|||||||
"Array of enumerable strings `home`, `notifications`, `public`, `thread`. At least one context must be specified."
|
"Array of enumerable strings `home`, `notifications`, `public`, `thread`. At least one context must be specified."
|
||||||
},
|
},
|
||||||
irreversible: %Schema{
|
irreversible: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description:
|
description:
|
||||||
"Should the server irreversibly drop matching entities from home and notifications?"
|
"Should the server irreversibly drop matching entities from home and notifications?"
|
||||||
},
|
},
|
||||||
whole_word: %Schema{
|
whole_word: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Consider word boundaries?",
|
description: "Consider word boundaries?",
|
||||||
default: true
|
default: true
|
||||||
|
@ -125,11 +125,17 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do
|
|||||||
},
|
},
|
||||||
avatar_upload_limit: %Schema{type: :integer, description: "The title of the website"},
|
avatar_upload_limit: %Schema{type: :integer, description: "The title of the website"},
|
||||||
background_upload_limit: %Schema{type: :integer, description: "The title of the website"},
|
background_upload_limit: %Schema{type: :integer, description: "The title of the website"},
|
||||||
banner_upload_limit: %Schema{type: :integer, description: "The title of the website"}
|
banner_upload_limit: %Schema{type: :integer, description: "The title of the website"},
|
||||||
|
background_image: %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :uri,
|
||||||
|
description: "The background image for the website"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
example: %{
|
example: %{
|
||||||
"avatar_upload_limit" => 2_000_000,
|
"avatar_upload_limit" => 2_000_000,
|
||||||
"background_upload_limit" => 4_000_000,
|
"background_upload_limit" => 4_000_000,
|
||||||
|
"background_image" => "/static/image.png",
|
||||||
"banner_upload_limit" => 4_000_000,
|
"banner_upload_limit" => 4_000_000,
|
||||||
"description" => "A Pleroma instance, an alternative fediverse server",
|
"description" => "A Pleroma instance, an alternative fediverse server",
|
||||||
"email" => "lain@lain.com",
|
"email" => "lain@lain.com",
|
||||||
|
132
lib/pleroma/web/api_spec/operations/media_operation.ex
Normal file
132
lib/pleroma/web/api_spec/operations/media_operation.ex
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.MediaOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Helpers
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Attachment
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["media"],
|
||||||
|
summary: "Upload media as attachment",
|
||||||
|
description: "Creates an attachment to be used with a new status.",
|
||||||
|
operationId: "MediaController.create",
|
||||||
|
security: [%{"oAuth" => ["write:media"]}],
|
||||||
|
requestBody: Helpers.request_body("Parameters", create_request()),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Media", "application/json", Attachment),
|
||||||
|
401 => Operation.response("Media", "application/json", ApiError),
|
||||||
|
422 => Operation.response("Media", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp create_request do
|
||||||
|
%Schema{
|
||||||
|
title: "MediaCreateRequest",
|
||||||
|
description: "POST body for creating an attachment",
|
||||||
|
type: :object,
|
||||||
|
required: [:file],
|
||||||
|
properties: %{
|
||||||
|
file: %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :binary,
|
||||||
|
description: "The file to be attached, using multipart form data."
|
||||||
|
},
|
||||||
|
description: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "A plain-text description of the media, for accessibility purposes."
|
||||||
|
},
|
||||||
|
focus: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["media"],
|
||||||
|
summary: "Upload media as attachment",
|
||||||
|
description: "Creates an attachment to be used with a new status.",
|
||||||
|
operationId: "MediaController.update",
|
||||||
|
security: [%{"oAuth" => ["write:media"]}],
|
||||||
|
parameters: [id_param()],
|
||||||
|
requestBody: Helpers.request_body("Parameters", update_request()),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Media", "application/json", Attachment),
|
||||||
|
400 => Operation.response("Media", "application/json", ApiError),
|
||||||
|
401 => Operation.response("Media", "application/json", ApiError),
|
||||||
|
422 => Operation.response("Media", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_request do
|
||||||
|
%Schema{
|
||||||
|
title: "MediaUpdateRequest",
|
||||||
|
description: "POST body for updating an attachment",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
file: %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :binary,
|
||||||
|
description: "The file to be attached, using multipart form data."
|
||||||
|
},
|
||||||
|
description: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "A plain-text description of the media, for accessibility purposes."
|
||||||
|
},
|
||||||
|
focus: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["media"],
|
||||||
|
summary: "Show Uploaded media attachment",
|
||||||
|
operationId: "MediaController.show",
|
||||||
|
parameters: [id_param()],
|
||||||
|
security: [%{"oAuth" => ["read:media"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Media", "application/json", Attachment),
|
||||||
|
401 => Operation.response("Media", "application/json", ApiError),
|
||||||
|
422 => Operation.response("Media", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create2_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["media"],
|
||||||
|
summary: "Upload media as attachment",
|
||||||
|
description: "Creates an attachment to be used with a new status.",
|
||||||
|
operationId: "MediaController.create2",
|
||||||
|
security: [%{"oAuth" => ["write:media"]}],
|
||||||
|
requestBody: Helpers.request_body("Parameters", create_request()),
|
||||||
|
responses: %{
|
||||||
|
202 => Operation.response("Media", "application/json", Attachment),
|
||||||
|
422 => Operation.response("Media", "application/json", ApiError),
|
||||||
|
500 => Operation.response("Media", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp id_param do
|
||||||
|
Operation.parameter(:id, :path, :string, "The ID of the Attachment entity")
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,390 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.PleromaEmojiPackOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emoji Packs"],
|
||||||
|
summary: "Make request to another instance for emoji packs list",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
parameters: [url_param()],
|
||||||
|
operationId: "PleromaAPI.EmojiPackController.remote",
|
||||||
|
responses: %{
|
||||||
|
200 => emoji_packs_response(),
|
||||||
|
500 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emoji Packs"],
|
||||||
|
summary: "Lists local custom emoji packs",
|
||||||
|
operationId: "PleromaAPI.EmojiPackController.index",
|
||||||
|
responses: %{
|
||||||
|
200 => emoji_packs_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emoji Packs"],
|
||||||
|
summary: "Show emoji pack",
|
||||||
|
operationId: "PleromaAPI.EmojiPackController.show",
|
||||||
|
parameters: [name_param()],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Emoji Pack", "application/json", emoji_pack()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def archive_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emoji Packs"],
|
||||||
|
summary: "Requests a local pack archive from the instance",
|
||||||
|
operationId: "PleromaAPI.EmojiPackController.archive",
|
||||||
|
parameters: [name_param()],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Archive file", "application/octet-stream", %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :binary
|
||||||
|
}),
|
||||||
|
403 => Operation.response("Forbidden", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def download_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emoji Packs"],
|
||||||
|
summary: "Download pack from another instance",
|
||||||
|
operationId: "PleromaAPI.EmojiPackController.download",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
requestBody: request_body("Parameters", download_request(), required: true),
|
||||||
|
responses: %{
|
||||||
|
200 => ok_response(),
|
||||||
|
500 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp download_request do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:url, :name],
|
||||||
|
properties: %{
|
||||||
|
url: %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :uri,
|
||||||
|
description: "URL of the instance to download from"
|
||||||
|
},
|
||||||
|
name: %Schema{type: :string, format: :uri, description: "Pack Name"},
|
||||||
|
as: %Schema{type: :string, format: :uri, description: "Save as"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emoji Packs"],
|
||||||
|
summary: "Create an empty pack",
|
||||||
|
operationId: "PleromaAPI.EmojiPackController.create",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
parameters: [name_param()],
|
||||||
|
responses: %{
|
||||||
|
200 => ok_response(),
|
||||||
|
400 => Operation.response("Not Found", "application/json", ApiError),
|
||||||
|
409 => Operation.response("Conflict", "application/json", ApiError),
|
||||||
|
500 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emoji Packs"],
|
||||||
|
summary: "Delete a custom emoji pack",
|
||||||
|
operationId: "PleromaAPI.EmojiPackController.delete",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
parameters: [name_param()],
|
||||||
|
responses: %{
|
||||||
|
200 => ok_response(),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emoji Packs"],
|
||||||
|
summary: "Updates (replaces) pack metadata",
|
||||||
|
operationId: "PleromaAPI.EmojiPackController.update",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
requestBody: request_body("Parameters", update_request(), required: true),
|
||||||
|
parameters: [name_param()],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Metadata", "application/json", metadata()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_file_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emoji Packs"],
|
||||||
|
summary: "Add new file to the pack",
|
||||||
|
operationId: "PleromaAPI.EmojiPackController.add_file",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
requestBody: request_body("Parameters", add_file_request(), required: true),
|
||||||
|
parameters: [name_param()],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Files Object", "application/json", files_object()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||||
|
409 => Operation.response("Conflict", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp add_file_request do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:file],
|
||||||
|
properties: %{
|
||||||
|
file: %Schema{
|
||||||
|
description:
|
||||||
|
"File needs to be uploaded with the multipart request or link to remote file",
|
||||||
|
anyOf: [
|
||||||
|
%Schema{type: :string, format: :binary},
|
||||||
|
%Schema{type: :string, format: :uri}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
shortcode: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"Shortcode for new emoji, must be unique for all emoji. If not sended, shortcode will be taken from original filename."
|
||||||
|
},
|
||||||
|
filename: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description:
|
||||||
|
"New emoji file name. If not specified will be taken from original filename."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_file_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emoji Packs"],
|
||||||
|
summary: "Add new file to the pack",
|
||||||
|
operationId: "PleromaAPI.EmojiPackController.update_file",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
requestBody: request_body("Parameters", update_file_request(), required: true),
|
||||||
|
parameters: [name_param()],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Files Object", "application/json", files_object()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||||
|
409 => Operation.response("Conflict", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_file_request do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:shortcode, :new_shortcode, :new_filename],
|
||||||
|
properties: %{
|
||||||
|
shortcode: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "Emoji file shortcode"
|
||||||
|
},
|
||||||
|
new_shortcode: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "New emoji file shortcode"
|
||||||
|
},
|
||||||
|
new_filename: %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "New filename for emoji file"
|
||||||
|
},
|
||||||
|
force: %Schema{
|
||||||
|
type: :boolean,
|
||||||
|
description: "With true value to overwrite existing emoji with new shortcode",
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_file_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emoji Packs"],
|
||||||
|
summary: "Delete emoji file from pack",
|
||||||
|
operationId: "PleromaAPI.EmojiPackController.delete_file",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
parameters: [
|
||||||
|
name_param(),
|
||||||
|
Operation.parameter(:shortcode, :query, :string, "File shortcode",
|
||||||
|
example: "cofe",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Files Object", "application/json", files_object()),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def import_from_filesystem_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emoji Packs"],
|
||||||
|
summary: "Imports packs from filesystem",
|
||||||
|
operationId: "PleromaAPI.EmojiPackController.import",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Array of imported pack names", "application/json", %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{type: :string}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp name_param do
|
||||||
|
Operation.parameter(:name, :path, :string, "Pack Name", example: "cofe", required: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp url_param do
|
||||||
|
Operation.parameter(
|
||||||
|
:url,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :string, format: :uri},
|
||||||
|
"URL of the instance",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp ok_response do
|
||||||
|
Operation.response("Ok", "application/json", %Schema{type: :string, example: "ok"})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp emoji_packs_response do
|
||||||
|
Operation.response(
|
||||||
|
"Object with pack names as keys and pack contents as values",
|
||||||
|
"application/json",
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
additionalProperties: emoji_pack(),
|
||||||
|
example: %{
|
||||||
|
"emojos" => emoji_pack().example
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp emoji_pack do
|
||||||
|
%Schema{
|
||||||
|
title: "EmojiPack",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
files: files_object(),
|
||||||
|
pack: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
license: %Schema{type: :string},
|
||||||
|
homepage: %Schema{type: :string, format: :uri},
|
||||||
|
description: %Schema{type: :string},
|
||||||
|
"can-download": %Schema{type: :boolean},
|
||||||
|
"share-files": %Schema{type: :boolean},
|
||||||
|
"download-sha256": %Schema{type: :string}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"files" => %{"emacs" => "emacs.png", "guix" => "guix.png"},
|
||||||
|
"pack" => %{
|
||||||
|
"license" => "Test license",
|
||||||
|
"homepage" => "https://pleroma.social",
|
||||||
|
"description" => "Test description",
|
||||||
|
"can-download" => true,
|
||||||
|
"share-files" => true,
|
||||||
|
"download-sha256" => "57482F30674FD3DE821FF48C81C00DA4D4AF1F300209253684ABA7075E5FC238"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp files_object do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
additionalProperties: %Schema{type: :string},
|
||||||
|
description: "Object with emoji names as keys and filenames as values"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_request do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
metadata: %Schema{
|
||||||
|
type: :object,
|
||||||
|
description: "Metadata to replace the old one",
|
||||||
|
properties: %{
|
||||||
|
license: %Schema{type: :string},
|
||||||
|
homepage: %Schema{type: :string, format: :uri},
|
||||||
|
description: %Schema{type: :string},
|
||||||
|
"fallback-src": %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :uri,
|
||||||
|
description: "Fallback url to download pack from"
|
||||||
|
},
|
||||||
|
"fallback-src-sha256": %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "SHA256 encoded for fallback pack archive"
|
||||||
|
},
|
||||||
|
"share-files": %Schema{type: :boolean, description: "Is pack allowed for sharing?"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp metadata do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
license: %Schema{type: :string},
|
||||||
|
homepage: %Schema{type: :string, format: :uri},
|
||||||
|
description: %Schema{type: :string},
|
||||||
|
"fallback-src": %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :uri,
|
||||||
|
description: "Fallback url to download pack from"
|
||||||
|
},
|
||||||
|
"fallback-src-sha256": %Schema{
|
||||||
|
type: :string,
|
||||||
|
description: "SHA256 encoded for fallback pack archive"
|
||||||
|
},
|
||||||
|
"share-files": %Schema{type: :boolean, description: "Is pack allowed for sharing?"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,79 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.PleromaMascotOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Mascot"],
|
||||||
|
summary: "Gets user mascot image",
|
||||||
|
security: [%{"oAuth" => ["read:accounts"]}],
|
||||||
|
operationId: "PleromaAPI.MascotController.show",
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Mascot", "application/json", mascot())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Mascot"],
|
||||||
|
summary: "Set/clear user avatar image",
|
||||||
|
description:
|
||||||
|
"Behaves exactly the same as `POST /api/v1/upload`. Can only accept images - any attempt to upload non-image files will be met with `HTTP 415 Unsupported Media Type`.",
|
||||||
|
operationId: "PleromaAPI.MascotController.update",
|
||||||
|
requestBody:
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
file: %Schema{type: :string, format: :binary}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
),
|
||||||
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Mascot", "application/json", mascot()),
|
||||||
|
415 => Operation.response("Unsupported Media Type", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp mascot do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :string},
|
||||||
|
url: %Schema{type: :string, format: :uri},
|
||||||
|
type: %Schema{type: :string},
|
||||||
|
pleroma: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
mime_type: %Schema{type: :string}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"id" => "abcdefg",
|
||||||
|
"url" => "https://pleroma.example.org/media/abcdefg.png",
|
||||||
|
"type" => "image",
|
||||||
|
"pleroma" => %{
|
||||||
|
"mime_type" => "image/png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
@ -0,0 +1,102 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Reference
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Scrobbles"],
|
||||||
|
summary: "Creates a new Listen activity for an account",
|
||||||
|
security: [%{"oAuth" => ["write"]}],
|
||||||
|
operationId: "PleromaAPI.ScrobbleController.create",
|
||||||
|
requestBody: request_body("Parameters", create_request(), requried: true),
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Scrobble", "application/json", scrobble())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Scrobbles"],
|
||||||
|
summary: "Requests a list of current and recent Listen activities for an account",
|
||||||
|
operationId: "PleromaAPI.ScrobbleController.index",
|
||||||
|
parameters: [
|
||||||
|
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"} | pagination_params()
|
||||||
|
],
|
||||||
|
security: [%{"oAuth" => ["read"]}],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Array of Scrobble", "application/json", %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: scrobble()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp create_request do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:title],
|
||||||
|
properties: %{
|
||||||
|
title: %Schema{type: :string, description: "The title of the media playing"},
|
||||||
|
album: %Schema{type: :string, description: "The album of the media playing"},
|
||||||
|
artist: %Schema{type: :string, description: "The artist of the media playing"},
|
||||||
|
length: %Schema{type: :integer, description: "The length of the media playing"},
|
||||||
|
visibility: %Schema{
|
||||||
|
allOf: [VisibilityScope],
|
||||||
|
default: "public",
|
||||||
|
description: "Scrobble visibility"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"title" => "Some Title",
|
||||||
|
"artist" => "Some Artist",
|
||||||
|
"album" => "Some Album",
|
||||||
|
"length" => 180_000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp scrobble do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :string},
|
||||||
|
account: Account,
|
||||||
|
title: %Schema{type: :string, description: "The title of the media playing"},
|
||||||
|
album: %Schema{type: :string, description: "The album of the media playing"},
|
||||||
|
artist: %Schema{type: :string, description: "The artist of the media playing"},
|
||||||
|
length: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: "The length of the media playing",
|
||||||
|
nullable: true
|
||||||
|
},
|
||||||
|
created_at: %Schema{type: :string, format: :"date-time"}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"id" => "1234",
|
||||||
|
"account" => Account.schema().example,
|
||||||
|
"title" => "Some Title",
|
||||||
|
"artist" => "Some Artist",
|
||||||
|
"album" => "Some Album",
|
||||||
|
"length" => 180_000,
|
||||||
|
"created_at" => "2019-09-28T12:40:45.000Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ApiSpec.ReportOperation do
|
|||||||
alias OpenApiSpex.Schema
|
alias OpenApiSpex.Schema
|
||||||
alias Pleroma.Web.ApiSpec.Helpers
|
alias Pleroma.Web.ApiSpec.Helpers
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||||
|
|
||||||
def open_api_operation(action) do
|
def open_api_operation(action) do
|
||||||
operation = String.to_existing_atom("#{action}_operation")
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
@ -47,7 +48,7 @@ defmodule Pleroma.Web.ApiSpec.ReportOperation do
|
|||||||
description: "Reason for the report"
|
description: "Reason for the report"
|
||||||
},
|
},
|
||||||
forward: %Schema{
|
forward: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
default: false,
|
default: false,
|
||||||
description:
|
description:
|
||||||
|
@ -19,6 +19,7 @@ defmodule Pleroma.Web.ApiSpec.SearchOperation do
|
|||||||
apply(__MODULE__, operation, [])
|
apply(__MODULE__, operation, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Note: `with_relationships` param is not supported (PleromaFE uses this op for autocomplete)
|
||||||
def account_search_operation do
|
def account_search_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Search"],
|
tags: ["Search"],
|
||||||
@ -96,8 +97,8 @@ defmodule Pleroma.Web.ApiSpec.SearchOperation do
|
|||||||
:query,
|
:query,
|
||||||
%Schema{type: :integer},
|
%Schema{type: :integer},
|
||||||
"Offset"
|
"Offset"
|
||||||
)
|
),
|
||||||
| pagination_params()
|
with_relationships_param() | pagination_params()
|
||||||
],
|
],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Results", "application/json", results())
|
200 => Operation.response("Results", "application/json", results())
|
||||||
@ -138,8 +139,8 @@ defmodule Pleroma.Web.ApiSpec.SearchOperation do
|
|||||||
:query,
|
:query,
|
||||||
%Schema{allOf: [BooleanLike], default: false},
|
%Schema{allOf: [BooleanLike], default: false},
|
||||||
"Only include accounts that the user is following"
|
"Only include accounts that the user is following"
|
||||||
)
|
),
|
||||||
| pagination_params()
|
with_relationships_param() | pagination_params()
|
||||||
],
|
],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Results", "application/json", results2())
|
200 => Operation.response("Results", "application/json", results2())
|
||||||
|
@ -349,10 +349,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||||||
summary: "Bookmarked statuses",
|
summary: "Bookmarked statuses",
|
||||||
description: "Statuses the user has bookmarked",
|
description: "Statuses the user has bookmarked",
|
||||||
operationId: "StatusController.bookmarks",
|
operationId: "StatusController.bookmarks",
|
||||||
parameters: [
|
parameters: pagination_params(),
|
||||||
Operation.parameter(:with_relationships, :query, BooleanLike, "Include relationships")
|
|
||||||
| pagination_params()
|
|
||||||
],
|
|
||||||
security: [%{"oAuth" => ["read:bookmarks"]}],
|
security: [%{"oAuth" => ["read:bookmarks"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Array of Statuses", "application/json", array_of_statuses())
|
200 => Operation.response("Array of Statuses", "application/json", array_of_statuses())
|
||||||
@ -398,12 +395,12 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||||||
"Duration the poll should be open, in seconds. Must be provided with `poll[options]`"
|
"Duration the poll should be open, in seconds. Must be provided with `poll[options]`"
|
||||||
},
|
},
|
||||||
multiple: %Schema{
|
multiple: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Allow multiple choices?"
|
description: "Allow multiple choices?"
|
||||||
},
|
},
|
||||||
hide_totals: %Schema{
|
hide_totals: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Hide vote counts until the poll ends?"
|
description: "Hide vote counts until the poll ends?"
|
||||||
}
|
}
|
||||||
@ -415,7 +412,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||||||
description: "ID of the status being replied to, if status is a reply"
|
description: "ID of the status being replied to, if status is a reply"
|
||||||
},
|
},
|
||||||
sensitive: %Schema{
|
sensitive: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Mark status and attached media as sensitive?"
|
description: "Mark status and attached media as sensitive?"
|
||||||
},
|
},
|
||||||
@ -439,7 +436,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
|
|||||||
},
|
},
|
||||||
# Pleroma-specific properties:
|
# Pleroma-specific properties:
|
||||||
preview: %Schema{
|
preview: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description:
|
description:
|
||||||
"If set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example"
|
"If set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example"
|
||||||
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ApiSpec.SubscriptionOperation do
|
|||||||
alias OpenApiSpex.Schema
|
alias OpenApiSpex.Schema
|
||||||
alias Pleroma.Web.ApiSpec.Helpers
|
alias Pleroma.Web.ApiSpec.Helpers
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.PushSubscription
|
alias Pleroma.Web.ApiSpec.Schemas.PushSubscription
|
||||||
|
|
||||||
def open_api_operation(action) do
|
def open_api_operation(action) do
|
||||||
@ -117,27 +118,27 @@ defmodule Pleroma.Web.ApiSpec.SubscriptionOperation do
|
|||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
follow: %Schema{
|
follow: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Receive follow notifications?"
|
description: "Receive follow notifications?"
|
||||||
},
|
},
|
||||||
favourite: %Schema{
|
favourite: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Receive favourite notifications?"
|
description: "Receive favourite notifications?"
|
||||||
},
|
},
|
||||||
reblog: %Schema{
|
reblog: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Receive reblog notifications?"
|
description: "Receive reblog notifications?"
|
||||||
},
|
},
|
||||||
mention: %Schema{
|
mention: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Receive mention notifications?"
|
description: "Receive mention notifications?"
|
||||||
},
|
},
|
||||||
poll: %Schema{
|
poll: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Receive poll notifications?"
|
description: "Receive poll notifications?"
|
||||||
}
|
}
|
||||||
@ -181,27 +182,27 @@ defmodule Pleroma.Web.ApiSpec.SubscriptionOperation do
|
|||||||
type: :object,
|
type: :object,
|
||||||
properties: %{
|
properties: %{
|
||||||
follow: %Schema{
|
follow: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Receive follow notifications?"
|
description: "Receive follow notifications?"
|
||||||
},
|
},
|
||||||
favourite: %Schema{
|
favourite: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Receive favourite notifications?"
|
description: "Receive favourite notifications?"
|
||||||
},
|
},
|
||||||
reblog: %Schema{
|
reblog: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Receive reblog notifications?"
|
description: "Receive reblog notifications?"
|
||||||
},
|
},
|
||||||
mention: %Schema{
|
mention: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Receive mention notifications?"
|
description: "Receive mention notifications?"
|
||||||
},
|
},
|
||||||
poll: %Schema{
|
poll: %Schema{
|
||||||
type: :boolean,
|
allOf: [BooleanLike],
|
||||||
nullable: true,
|
nullable: true,
|
||||||
description: "Receive poll notifications?"
|
description: "Receive poll notifications?"
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,7 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||||||
local_param(),
|
local_param(),
|
||||||
with_muted_param(),
|
with_muted_param(),
|
||||||
exclude_visibilities_param(),
|
exclude_visibilities_param(),
|
||||||
reply_visibility_param(),
|
reply_visibility_param() | pagination_params()
|
||||||
with_relationships_param() | pagination_params()
|
|
||||||
],
|
],
|
||||||
operationId: "TimelineController.home",
|
operationId: "TimelineController.home",
|
||||||
responses: %{
|
responses: %{
|
||||||
@ -44,7 +43,7 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||||||
description:
|
description:
|
||||||
"View statuses with a “direct” privacy, from your account or in your notifications",
|
"View statuses with a “direct” privacy, from your account or in your notifications",
|
||||||
deprecated: true,
|
deprecated: true,
|
||||||
parameters: pagination_params(),
|
parameters: [with_muted_param() | pagination_params()],
|
||||||
security: [%{"oAuth" => ["read:statuses"]}],
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
operationId: "TimelineController.direct",
|
operationId: "TimelineController.direct",
|
||||||
responses: %{
|
responses: %{
|
||||||
@ -63,8 +62,7 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||||||
only_media_param(),
|
only_media_param(),
|
||||||
with_muted_param(),
|
with_muted_param(),
|
||||||
exclude_visibilities_param(),
|
exclude_visibilities_param(),
|
||||||
reply_visibility_param(),
|
reply_visibility_param() | pagination_params()
|
||||||
with_relationships_param() | pagination_params()
|
|
||||||
],
|
],
|
||||||
operationId: "TimelineController.public",
|
operationId: "TimelineController.public",
|
||||||
responses: %{
|
responses: %{
|
||||||
@ -109,8 +107,7 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||||||
local_param(),
|
local_param(),
|
||||||
only_media_param(),
|
only_media_param(),
|
||||||
with_muted_param(),
|
with_muted_param(),
|
||||||
exclude_visibilities_param(),
|
exclude_visibilities_param() | pagination_params()
|
||||||
with_relationships_param() | pagination_params()
|
|
||||||
],
|
],
|
||||||
operationId: "TimelineController.hashtag",
|
operationId: "TimelineController.hashtag",
|
||||||
responses: %{
|
responses: %{
|
||||||
@ -134,8 +131,7 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||||||
required: true
|
required: true
|
||||||
),
|
),
|
||||||
with_muted_param(),
|
with_muted_param(),
|
||||||
exclude_visibilities_param(),
|
exclude_visibilities_param() | pagination_params()
|
||||||
with_relationships_param() | pagination_params()
|
|
||||||
],
|
],
|
||||||
operationId: "TimelineController.list",
|
operationId: "TimelineController.list",
|
||||||
responses: %{
|
responses: %{
|
||||||
@ -153,10 +149,6 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp with_relationships_param do
|
|
||||||
Operation.parameter(:with_relationships, :query, BooleanLike, "Include relationships")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp local_param do
|
defp local_param do
|
||||||
Operation.parameter(
|
Operation.parameter(
|
||||||
:local,
|
:local,
|
||||||
|
@ -13,7 +13,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Attachment do
|
|||||||
type: :object,
|
type: :object,
|
||||||
requried: [:id, :url, :preview_url],
|
requried: [:id, :url, :preview_url],
|
||||||
properties: %{
|
properties: %{
|
||||||
id: %Schema{type: :string},
|
id: %Schema{type: :string, description: "The ID of the attachment in the database."},
|
||||||
url: %Schema{
|
url: %Schema{
|
||||||
type: :string,
|
type: :string,
|
||||||
format: :uri,
|
format: :uri,
|
||||||
|
@ -16,7 +16,8 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
|||||||
def get_user(%Plug.Conn{} = conn) do
|
def get_user(%Plug.Conn{} = conn) do
|
||||||
with {:ok, {name, password}} <- fetch_credentials(conn),
|
with {:ok, {name, password}} <- fetch_credentials(conn),
|
||||||
{_, %User{} = user} <- {:user, fetch_user(name)},
|
{_, %User{} = user} <- {:user, fetch_user(name)},
|
||||||
{_, true} <- {:checkpw, AuthenticationPlug.checkpw(password, user.password_hash)} do
|
{_, true} <- {:checkpw, AuthenticationPlug.checkpw(password, user.password_hash)},
|
||||||
|
{:ok, user} <- AuthenticationPlug.maybe_update_password(user, password) do
|
||||||
{:ok, user}
|
{:ok, user}
|
||||||
else
|
else
|
||||||
{:error, _reason} = error -> error
|
{:error, _reason} = error -> error
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Auth.TOTPAuthenticator do
|
defmodule Pleroma.Web.Auth.TOTPAuthenticator do
|
||||||
|
@ -23,6 +23,7 @@ defmodule Pleroma.Web.ChatChannel do
|
|||||||
if String.length(text) in 1..Pleroma.Config.get([:instance, :chat_limit]) do
|
if String.length(text) in 1..Pleroma.Config.get([:instance, :chat_limit]) do
|
||||||
author = User.get_cached_by_nickname(user_name)
|
author = User.get_cached_by_nickname(user_name)
|
||||||
author = Pleroma.Web.MastodonAPI.AccountView.render("show.json", user: author)
|
author = Pleroma.Web.MastodonAPI.AccountView.render("show.json", user: author)
|
||||||
|
|
||||||
message = ChatChannelState.add_message(%{text: text, author: author})
|
message = ChatChannelState.add_message(%{text: text, author: author})
|
||||||
|
|
||||||
broadcast!(socket, "new_msg", message)
|
broadcast!(socket, "new_msg", message)
|
||||||
|
@ -347,11 +347,14 @@ defmodule Pleroma.Web.CommonAPI do
|
|||||||
|> check_expiry_date()
|
|> check_expiry_date()
|
||||||
end
|
end
|
||||||
|
|
||||||
def listen(user, %{"title" => _} = data) do
|
def listen(user, data) do
|
||||||
with visibility <- data["visibility"] || "public",
|
visibility = Map.get(data, :visibility, "public")
|
||||||
{to, cc} <- get_to_and_cc(user, [], nil, visibility, nil),
|
|
||||||
|
with {to, cc} <- get_to_and_cc(user, [], nil, visibility, nil),
|
||||||
listen_data <-
|
listen_data <-
|
||||||
Map.take(data, ["album", "artist", "title", "length"])
|
data
|
||||||
|
|> Map.take([:album, :artist, :title, :length])
|
||||||
|
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
||||||
|> Map.put("type", "Audio")
|
|> Map.put("type", "Audio")
|
||||||
|> Map.put("to", to)
|
|> Map.put("to", to)
|
||||||
|> Map.put("cc", cc)
|
|> Map.put("cc", cc)
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
defmodule Pleroma.Web.ControllerHelper do
|
defmodule Pleroma.Web.ControllerHelper do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Config
|
|
||||||
|
|
||||||
# As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
|
# As in Mastodon API, per https://api.rubyonrails.org/classes/ActiveModel/Type/Boolean.html
|
||||||
@falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
|
@falsy_param_values [false, 0, "0", "f", "F", "false", "False", "FALSE", "off", "OFF"]
|
||||||
|
|
||||||
@ -106,13 +104,16 @@ defmodule Pleroma.Web.ControllerHelper do
|
|||||||
|
|
||||||
def put_if_exist(map, key, value), do: Map.put(map, key, value)
|
def put_if_exist(map, key, value), do: Map.put(map, key, value)
|
||||||
|
|
||||||
@doc "Whether to skip rendering `[:account][:pleroma][:relationship]`for statuses/notifications"
|
@doc """
|
||||||
def skip_relationships?(params) do
|
Returns true if request specifies to include embedded relationships in account objects.
|
||||||
if Config.get([:extensions, :output_relationships_in_statuses_by_default]) do
|
May only be used in selected account-related endpoints; has no effect for status- or
|
||||||
false
|
notification-related endpoints.
|
||||||
else
|
"""
|
||||||
# BREAKING: older PleromaFE versions do not send this param but _do_ expect relationships.
|
# Intended for PleromaFE: https://git.pleroma.social/pleroma/pleroma-fe/-/issues/838
|
||||||
not truthy_param?(params["with_relationships"])
|
def embed_relationships?(params) do
|
||||||
end
|
# To do once OpenAPI transition mess is over: just `truthy_param?(params[:with_relationships])`
|
||||||
|
params
|
||||||
|
|> Map.get(:with_relationships, params["with_relationships"])
|
||||||
|
|> truthy_param?()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -10,8 +10,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||||||
add_link_headers: 2,
|
add_link_headers: 2,
|
||||||
truthy_param?: 1,
|
truthy_param?: 1,
|
||||||
assign_account_by_id: 2,
|
assign_account_by_id: 2,
|
||||||
json_response: 3,
|
embed_relationships?: 1,
|
||||||
skip_relationships?: 1
|
json_response: 3
|
||||||
]
|
]
|
||||||
|
|
||||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
@ -177,6 +177,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||||||
)
|
)
|
||||||
|> add_if_present(params, :pleroma_settings_store, :pleroma_settings_store)
|
|> add_if_present(params, :pleroma_settings_store, :pleroma_settings_store)
|
||||||
|> add_if_present(params, :default_scope, :default_scope)
|
|> add_if_present(params, :default_scope, :default_scope)
|
||||||
|
|> add_if_present(params["source"], "privacy", :default_scope)
|
||||||
|> add_if_present(params, :actor_type, :actor_type)
|
|> add_if_present(params, :actor_type, :actor_type)
|
||||||
|
|
||||||
changeset = User.update_changeset(user, user_params)
|
changeset = User.update_changeset(user, user_params)
|
||||||
@ -189,7 +190,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defp add_if_present(map, params, params_field, map_field, value_function \\ &{:ok, &1}) do
|
defp add_if_present(map, params, params_field, map_field, value_function \\ &{:ok, &1}) do
|
||||||
with true <- Map.has_key?(params, params_field),
|
with true <- is_map(params),
|
||||||
|
true <- Map.has_key?(params, params_field),
|
||||||
{:ok, new_value} <- value_function.(Map.get(params, params_field)) do
|
{:ok, new_value} <- value_function.(Map.get(params, params_field)) do
|
||||||
Map.put(map, map_field, new_value)
|
Map.put(map, map_field, new_value)
|
||||||
else
|
else
|
||||||
@ -247,8 +249,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||||||
|> render("index.json",
|
|> render("index.json",
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: reading_user,
|
for: reading_user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
_e -> render_error(conn, :not_found, "Can't find user")
|
_e -> render_error(conn, :not_found, "Can't find user")
|
||||||
@ -271,7 +272,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(followers)
|
|> add_link_headers(followers)
|
||||||
|> render("index.json", for: for_user, users: followers, as: :user)
|
# https://git.pleroma.social/pleroma/pleroma-fe/-/issues/838#note_59223
|
||||||
|
|> render("index.json",
|
||||||
|
for: for_user,
|
||||||
|
users: followers,
|
||||||
|
as: :user,
|
||||||
|
embed_relationships: embed_relationships?(params)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/accounts/:id/following"
|
@doc "GET /api/v1/accounts/:id/following"
|
||||||
@ -290,7 +297,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(followers)
|
|> add_link_headers(followers)
|
||||||
|> render("index.json", for: for_user, users: followers, as: :user)
|
# https://git.pleroma.social/pleroma/pleroma-fe/-/issues/838#note_59223
|
||||||
|
|> render("index.json",
|
||||||
|
for: for_user,
|
||||||
|
users: followers,
|
||||||
|
as: :user,
|
||||||
|
embed_relationships: embed_relationships?(params)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/accounts/:id/lists"
|
@doc "GET /api/v1/accounts/:id/lists"
|
||||||
|
@ -20,6 +20,10 @@ defmodule Pleroma.Web.MastodonAPI.FallbackController do
|
|||||||
render_error(conn, :not_found, "Record not found")
|
render_error(conn, :not_found, "Record not found")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def call(conn, {:error, :forbidden}) do
|
||||||
|
render_error(conn, :forbidden, "Access denied")
|
||||||
|
end
|
||||||
|
|
||||||
def call(conn, {:error, error_message}) do
|
def call(conn, {:error, error_message}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|
@ -11,17 +11,21 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do
|
|||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
|
plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:media"]})
|
plug(OAuthScopesPlug, %{scopes: ["read:media"]} when action == :show)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["write:media"]} when action != :show)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.MediaOperation
|
||||||
|
|
||||||
@doc "POST /api/v1/media"
|
@doc "POST /api/v1/media"
|
||||||
def create(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
|
def create(%{assigns: %{user: user}, body_params: %{file: file} = data} = conn, _) do
|
||||||
with {:ok, object} <-
|
with {:ok, object} <-
|
||||||
ActivityPub.upload(
|
ActivityPub.upload(
|
||||||
file,
|
file,
|
||||||
actor: User.ap_id(user),
|
actor: User.ap_id(user),
|
||||||
description: Map.get(data, "description")
|
description: Map.get(data, :description)
|
||||||
) do
|
) do
|
||||||
attachment_data = Map.put(object.data, "id", object.id)
|
attachment_data = Map.put(object.data, "id", object.id)
|
||||||
|
|
||||||
@ -29,11 +33,30 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create(_conn, _data), do: {:error, :bad_request}
|
||||||
|
|
||||||
|
@doc "POST /api/v2/media"
|
||||||
|
def create2(%{assigns: %{user: user}, body_params: %{file: file} = data} = conn, _) do
|
||||||
|
with {:ok, object} <-
|
||||||
|
ActivityPub.upload(
|
||||||
|
file,
|
||||||
|
actor: User.ap_id(user),
|
||||||
|
description: Map.get(data, :description)
|
||||||
|
) do
|
||||||
|
attachment_data = Map.put(object.data, "id", object.id)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_status(202)
|
||||||
|
|> render("attachment.json", %{attachment: attachment_data})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create2(_conn, _data), do: {:error, :bad_request}
|
||||||
|
|
||||||
@doc "PUT /api/v1/media/:id"
|
@doc "PUT /api/v1/media/:id"
|
||||||
def update(%{assigns: %{user: user}} = conn, %{"id" => id, "description" => description})
|
def update(%{assigns: %{user: user}, body_params: %{description: description}} = conn, %{id: id}) do
|
||||||
when is_binary(description) do
|
|
||||||
with %Object{} = object <- Object.get_by_id(id),
|
with %Object{} = object <- Object.get_by_id(id),
|
||||||
true <- Object.authorize_mutation(object, user),
|
:ok <- Object.authorize_access(object, user),
|
||||||
{:ok, %Object{data: data}} <- Object.update_data(object, %{"name" => description}) do
|
{:ok, %Object{data: data}} <- Object.update_data(object, %{"name" => description}) do
|
||||||
attachment_data = Map.put(data, "id", object.id)
|
attachment_data = Map.put(data, "id", object.id)
|
||||||
|
|
||||||
@ -41,5 +64,17 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(_conn, _data), do: {:error, :bad_request}
|
def update(conn, data), do: show(conn, data)
|
||||||
|
|
||||||
|
@doc "GET /api/v1/media/:id"
|
||||||
|
def show(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||||
|
with %Object{data: data, id: object_id} = object <- Object.get_by_id(id),
|
||||||
|
:ok <- Object.authorize_access(object, user) do
|
||||||
|
attachment_data = Map.put(data, "id", object_id)
|
||||||
|
|
||||||
|
render(conn, "attachment.json", %{attachment: attachment_data})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(_conn, _data), do: {:error, :bad_request}
|
||||||
end
|
end
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1]
|
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
|
||||||
|
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
@ -50,8 +50,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
|||||||
|> add_link_headers(notifications)
|
|> add_link_headers(notifications)
|
||||||
|> render("index.json",
|
|> render("index.json",
|
||||||
notifications: notifications,
|
notifications: notifications,
|
||||||
for: user,
|
for: user
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -5,14 +5,13 @@
|
|||||||
defmodule Pleroma.Web.MastodonAPI.SearchController do
|
defmodule Pleroma.Web.MastodonAPI.SearchController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [skip_relationships?: 1]
|
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
|
alias Pleroma.Web.ControllerHelper
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
|
||||||
@ -34,7 +33,11 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
|
|||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_view(AccountView)
|
|> put_view(AccountView)
|
||||||
|> render("index.json", users: accounts, for: user, as: :user)
|
|> render("index.json",
|
||||||
|
users: accounts,
|
||||||
|
for: user,
|
||||||
|
as: :user
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def search2(conn, params), do: do_search(:v2, conn, params)
|
def search2(conn, params), do: do_search(:v2, conn, params)
|
||||||
@ -71,13 +74,13 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
|
|||||||
|
|
||||||
defp search_options(params, user) do
|
defp search_options(params, user) do
|
||||||
[
|
[
|
||||||
skip_relationships: skip_relationships?(params),
|
|
||||||
resolve: params[:resolve],
|
resolve: params[:resolve],
|
||||||
following: params[:following],
|
following: params[:following],
|
||||||
limit: params[:limit],
|
limit: params[:limit],
|
||||||
offset: params[:offset],
|
offset: params[:offset],
|
||||||
type: params[:type],
|
type: params[:type],
|
||||||
author: get_author(params),
|
author: get_author(params),
|
||||||
|
embed_relationships: ControllerHelper.embed_relationships?(params),
|
||||||
for_user: user
|
for_user: user
|
||||||
]
|
]
|
||||||
|> Enum.filter(&elem(&1, 1))
|
|> Enum.filter(&elem(&1, 1))
|
||||||
@ -90,7 +93,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
|
|||||||
users: accounts,
|
users: accounts,
|
||||||
for: options[:for_user],
|
for: options[:for_user],
|
||||||
as: :user,
|
as: :user,
|
||||||
skip_relationships: false
|
embed_relationships: options[:embed_relationships]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -100,8 +103,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
|
|||||||
StatusView.render("index.json",
|
StatusView.render("index.json",
|
||||||
activities: statuses,
|
activities: statuses,
|
||||||
for: options[:for_user],
|
for: options[:for_user],
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: options[:skip_relationships]
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper,
|
import Pleroma.Web.ControllerHelper,
|
||||||
only: [try_render: 3, add_link_headers: 2, skip_relationships?: 1]
|
only: [try_render: 3, add_link_headers: 2]
|
||||||
|
|
||||||
require Ecto.Query
|
require Ecto.Query
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||||||
|
|
||||||
`ids` query param is required
|
`ids` query param is required
|
||||||
"""
|
"""
|
||||||
def index(%{assigns: %{user: user}} = conn, %{ids: ids} = params) do
|
def index(%{assigns: %{user: user}} = conn, %{ids: ids} = _params) do
|
||||||
limit = 100
|
limit = 100
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
@ -117,8 +117,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||||||
render(conn, "index.json",
|
render(conn, "index.json",
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -383,8 +382,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||||||
|> render("index.json",
|
|> render("index.json",
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -406,8 +404,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||||||
|> render("index.json",
|
|> render("index.json",
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper,
|
import Pleroma.Web.ControllerHelper,
|
||||||
only: [add_link_headers: 2, add_link_headers: 3, skip_relationships?: 1]
|
only: [add_link_headers: 2, add_link_headers: 3]
|
||||||
|
|
||||||
alias Pleroma.Pagination
|
alias Pleroma.Pagination
|
||||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
@ -63,8 +63,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||||||
|> render("index.json",
|
|> render("index.json",
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -88,8 +87,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||||||
|> render("index.json",
|
|> render("index.json",
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -125,8 +123,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||||||
|> render("index.json",
|
|> render("index.json",
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -173,8 +170,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||||||
|> render("index.json",
|
|> render("index.json",
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -203,8 +199,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||||||
render(conn, "index.json",
|
render(conn, "index.json",
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
_e -> render_error(conn, :forbidden, "Error.")
|
_e -> render_error(conn, :forbidden, "Error.")
|
||||||
|
@ -15,13 +15,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||||||
def render("index.json", %{users: users} = opts) do
|
def render("index.json", %{users: users} = opts) do
|
||||||
reading_user = opts[:for]
|
reading_user = opts[:for]
|
||||||
|
|
||||||
# Note: :skip_relationships option is currently intentionally not supported for accounts
|
|
||||||
relationships_opt =
|
relationships_opt =
|
||||||
cond do
|
cond do
|
||||||
Map.has_key?(opts, :relationships) ->
|
Map.has_key?(opts, :relationships) ->
|
||||||
opts[:relationships]
|
opts[:relationships]
|
||||||
|
|
||||||
is_nil(reading_user) ->
|
is_nil(reading_user) || !opts[:embed_relationships] ->
|
||||||
UserRelationship.view_relationships_option(nil, [])
|
UserRelationship.view_relationships_option(nil, [])
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
@ -193,14 +192,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
relationship =
|
relationship =
|
||||||
if opts[:skip_relationships] do
|
if opts[:embed_relationships] do
|
||||||
%{}
|
|
||||||
else
|
|
||||||
render("relationship.json", %{
|
render("relationship.json", %{
|
||||||
user: opts[:for],
|
user: opts[:for],
|
||||||
target: user,
|
target: user,
|
||||||
relationships: opts[:relationships]
|
relationships: opts[:relationships]
|
||||||
})
|
})
|
||||||
|
else
|
||||||
|
%{}
|
||||||
end
|
end
|
||||||
|
|
||||||
%{
|
%{
|
||||||
|
@ -33,6 +33,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||||||
avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit),
|
avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit),
|
||||||
background_upload_limit: Keyword.get(instance, :background_upload_limit),
|
background_upload_limit: Keyword.get(instance, :background_upload_limit),
|
||||||
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
|
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
|
||||||
|
background_image: Keyword.get(instance, :background_image),
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
metadata: %{
|
metadata: %{
|
||||||
features: features(),
|
features: features(),
|
||||||
|
@ -51,9 +51,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||||||
|> Enum.filter(& &1)
|
|> Enum.filter(& &1)
|
||||||
|> Kernel.++(move_activities_targets)
|
|> Kernel.++(move_activities_targets)
|
||||||
|
|
||||||
UserRelationship.view_relationships_option(reading_user, actors,
|
UserRelationship.view_relationships_option(reading_user, actors, subset: :source_mutes)
|
||||||
source_mutes_only: opts[:skip_relationships]
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
opts =
|
opts =
|
||||||
@ -83,15 +81,13 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||||||
|
|
||||||
mastodon_type = Activity.mastodon_notification_type(activity)
|
mastodon_type = Activity.mastodon_notification_type(activity)
|
||||||
|
|
||||||
render_opts = %{
|
# Note: :relationships contain user mutes (needed for :muted flag in :status)
|
||||||
relationships: opts[:relationships],
|
status_render_opts = %{relationships: opts[:relationships]}
|
||||||
skip_relationships: opts[:skip_relationships]
|
|
||||||
}
|
|
||||||
|
|
||||||
with %{id: _} = account <-
|
with %{id: _} = account <-
|
||||||
AccountView.render(
|
AccountView.render(
|
||||||
"show.json",
|
"show.json",
|
||||||
Map.merge(render_opts, %{user: actor, for: reading_user})
|
%{user: actor, for: reading_user}
|
||||||
) do
|
) do
|
||||||
response = %{
|
response = %{
|
||||||
id: to_string(notification.id),
|
id: to_string(notification.id),
|
||||||
@ -105,21 +101,20 @@ defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
|||||||
|
|
||||||
case mastodon_type do
|
case mastodon_type do
|
||||||
"mention" ->
|
"mention" ->
|
||||||
put_status(response, activity, reading_user, render_opts)
|
put_status(response, activity, reading_user, status_render_opts)
|
||||||
|
|
||||||
"favourite" ->
|
"favourite" ->
|
||||||
put_status(response, parent_activity_fn.(), reading_user, render_opts)
|
put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
|
||||||
|
|
||||||
"reblog" ->
|
"reblog" ->
|
||||||
put_status(response, parent_activity_fn.(), reading_user, render_opts)
|
put_status(response, parent_activity_fn.(), reading_user, status_render_opts)
|
||||||
|
|
||||||
"move" ->
|
"move" ->
|
||||||
# Note: :skip_relationships option being applied to _account_ rendering (here)
|
put_target(response, activity, reading_user, %{})
|
||||||
put_target(response, activity, reading_user, render_opts)
|
|
||||||
|
|
||||||
"pleroma:emoji_reaction" ->
|
"pleroma:emoji_reaction" ->
|
||||||
response
|
response
|
||||||
|> put_status(parent_activity_fn.(), reading_user, render_opts)
|
|> put_status(parent_activity_fn.(), reading_user, status_render_opts)
|
||||||
|> put_emoji(activity)
|
|> put_emoji(activity)
|
||||||
|
|
||||||
type when type in ["follow", "follow_request"] ->
|
type when type in ["follow", "follow_request"] ->
|
||||||
|
@ -107,9 +107,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||||||
|> Enum.map(&get_user(&1.data["actor"], false))
|
|> Enum.map(&get_user(&1.data["actor"], false))
|
||||||
|> Enum.filter(& &1)
|
|> Enum.filter(& &1)
|
||||||
|
|
||||||
UserRelationship.view_relationships_option(reading_user, actors,
|
UserRelationship.view_relationships_option(reading_user, actors, subset: :source_mutes)
|
||||||
source_mutes_only: opts[:skip_relationships]
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
opts =
|
opts =
|
||||||
@ -162,9 +160,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||||||
account:
|
account:
|
||||||
AccountView.render("show.json", %{
|
AccountView.render("show.json", %{
|
||||||
user: user,
|
user: user,
|
||||||
for: opts[:for],
|
for: opts[:for]
|
||||||
relationships: opts[:relationships],
|
|
||||||
skip_relationships: opts[:skip_relationships]
|
|
||||||
}),
|
}),
|
||||||
in_reply_to_id: nil,
|
in_reply_to_id: nil,
|
||||||
in_reply_to_account_id: nil,
|
in_reply_to_account_id: nil,
|
||||||
@ -330,9 +326,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||||||
account:
|
account:
|
||||||
AccountView.render("show.json", %{
|
AccountView.render("show.json", %{
|
||||||
user: user,
|
user: user,
|
||||||
for: opts[:for],
|
for: opts[:for]
|
||||||
relationships: opts[:relationships],
|
|
||||||
skip_relationships: opts[:skip_relationships]
|
|
||||||
}),
|
}),
|
||||||
in_reply_to_id: reply_to && to_string(reply_to.id),
|
in_reply_to_id: reply_to && to_string(reply_to.id),
|
||||||
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
|
in_reply_to_account_id: reply_to_user && to_string(reply_to_user.id),
|
||||||
@ -443,27 +437,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("listen.json", %{activity: %Activity{data: %{"type" => "Listen"}} = activity} = opts) do
|
|
||||||
object = Object.normalize(activity)
|
|
||||||
|
|
||||||
user = get_user(activity.data["actor"])
|
|
||||||
created_at = Utils.to_masto_date(activity.data["published"])
|
|
||||||
|
|
||||||
%{
|
|
||||||
id: activity.id,
|
|
||||||
account: AccountView.render("show.json", %{user: user, for: opts[:for]}),
|
|
||||||
created_at: created_at,
|
|
||||||
title: object.data["title"] |> HTML.strip_tags(),
|
|
||||||
artist: object.data["artist"] |> HTML.strip_tags(),
|
|
||||||
album: object.data["album"] |> HTML.strip_tags(),
|
|
||||||
length: object.data["length"]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def render("listens.json", opts) do
|
|
||||||
safe_render_many(opts.activities, StatusView, "listen.json", opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def render("context.json", %{activity: activity, activities: activities, user: user}) do
|
def render("context.json", %{activity: activity, activities: activities, user: user}) do
|
||||||
%{ancestors: ancestors, descendants: descendants} =
|
%{ancestors: ancestors, descendants: descendants} =
|
||||||
activities
|
activities
|
||||||
|
26
lib/pleroma/web/media_proxy/invalidation.ex
Normal file
26
lib/pleroma/web/media_proxy/invalidation.ex
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MediaProxy.Invalidation do
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
@callback purge(list(String.t()), map()) :: {:ok, String.t()} | {:error, String.t()}
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
|
||||||
|
@spec purge(list(String.t())) :: {:ok, String.t()} | {:error, String.t()}
|
||||||
|
def purge(urls) do
|
||||||
|
[:media_proxy, :invalidation, :enabled]
|
||||||
|
|> Config.get()
|
||||||
|
|> do_purge(urls)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_purge(true, urls) do
|
||||||
|
provider = Config.get([:media_proxy, :invalidation, :provider])
|
||||||
|
options = Config.get(provider)
|
||||||
|
provider.purge(urls, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_purge(_, _), do: :ok
|
||||||
|
end
|
40
lib/pleroma/web/media_proxy/invalidations/http.ex
Normal file
40
lib/pleroma/web/media_proxy/invalidations/http.ex
Normal file
@ -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.MediaProxy.Invalidation.Http do
|
||||||
|
@moduledoc false
|
||||||
|
@behaviour Pleroma.Web.MediaProxy.Invalidation
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
@impl Pleroma.Web.MediaProxy.Invalidation
|
||||||
|
def purge(urls, opts) do
|
||||||
|
method = Map.get(opts, :method, :purge)
|
||||||
|
headers = Map.get(opts, :headers, [])
|
||||||
|
options = Map.get(opts, :options, [])
|
||||||
|
|
||||||
|
Logger.debug("Running cache purge: #{inspect(urls)}")
|
||||||
|
|
||||||
|
Enum.each(urls, fn url ->
|
||||||
|
with {:error, error} <- do_purge(method, url, headers, options) do
|
||||||
|
Logger.error("Error while cache purge: url - #{url}, error: #{inspect(error)}")
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, "success"}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_purge(method, url, headers, options) do
|
||||||
|
case Pleroma.HTTP.request(method, url, "", headers, options) do
|
||||||
|
{:ok, %{status: status} = env} when 400 <= status and status < 500 ->
|
||||||
|
{:error, env}
|
||||||
|
|
||||||
|
{:error, error} = error ->
|
||||||
|
error
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:ok, "success"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
41
lib/pleroma/web/media_proxy/invalidations/script.ex
Normal file
41
lib/pleroma/web/media_proxy/invalidations/script.ex
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MediaProxy.Invalidation.Script do
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
@behaviour Pleroma.Web.MediaProxy.Invalidation
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
@impl Pleroma.Web.MediaProxy.Invalidation
|
||||||
|
def purge(urls, %{script_path: script_path} = _options) do
|
||||||
|
args =
|
||||||
|
urls
|
||||||
|
|> List.wrap()
|
||||||
|
|> Enum.uniq()
|
||||||
|
|> Enum.join(" ")
|
||||||
|
|
||||||
|
path = Path.expand(script_path)
|
||||||
|
|
||||||
|
Logger.debug("Running cache purge: #{inspect(urls)}, #{path}")
|
||||||
|
|
||||||
|
case do_purge(path, [args]) do
|
||||||
|
{result, exit_status} when exit_status > 0 ->
|
||||||
|
Logger.error("Error while cache purge: #{inspect(result)}")
|
||||||
|
{:error, inspect(result)}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
{:ok, "success"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def purge(_, _), do: {:error, "not found script path"}
|
||||||
|
|
||||||
|
defp do_purge(path, args) do
|
||||||
|
System.cmd(path, args)
|
||||||
|
rescue
|
||||||
|
error -> {inspect(error), 1}
|
||||||
|
end
|
||||||
|
end
|
@ -1,5 +1,5 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.OAuth.MFAController do
|
defmodule Pleroma.Web.OAuth.MFAController do
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.OAuth.MFAView do
|
defmodule Pleroma.Web.OAuth.MFAView do
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.OAuth.Token.CleanWorker do
|
defmodule Pleroma.Web.OAuth.Token.CleanWorker do
|
||||||
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
|||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper,
|
import Pleroma.Web.ControllerHelper,
|
||||||
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2, skip_relationships?: 1]
|
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2]
|
||||||
|
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
@ -149,8 +149,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
|||||||
|> render("index.json",
|
|> render("index.json",
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: for_user,
|
for: for_user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Emoji.Pack
|
alias Pleroma.Emoji.Pack
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
Pleroma.Plugs.OAuthScopesPlug,
|
Pleroma.Plugs.OAuthScopesPlug,
|
||||||
%{scopes: ["write"], admin: true}
|
%{scopes: ["write"], admin: true}
|
||||||
@ -19,39 +21,37 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
@skip_plugs [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug]
|
||||||
:skip_plug,
|
plug(:skip_plug, @skip_plugs when action in [:archive, :show, :list])
|
||||||
[Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug]
|
|
||||||
when action in [:archive, :show, :list]
|
|
||||||
)
|
|
||||||
|
|
||||||
def remote(conn, %{"url" => url}) do
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaEmojiPackOperation
|
||||||
|
|
||||||
|
def remote(conn, %{url: url}) do
|
||||||
with {:ok, packs} <- Pack.list_remote(url) do
|
with {:ok, packs} <- Pack.list_remote(url) do
|
||||||
json(conn, packs)
|
json(conn, packs)
|
||||||
else
|
else
|
||||||
{:shareable, _} ->
|
{:error, :not_shareable} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:internal_server_error)
|
|> put_status(:internal_server_error)
|
||||||
|> json(%{error: "The requested instance does not support sharing emoji packs"})
|
|> json(%{error: "The requested instance does not support sharing emoji packs"})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def list(conn, _params) do
|
def index(conn, _params) do
|
||||||
emoji_path =
|
emoji_path =
|
||||||
Path.join(
|
[:instance, :static_dir]
|
||||||
Pleroma.Config.get!([:instance, :static_dir]),
|
|> Pleroma.Config.get!()
|
||||||
"emoji"
|
|> Path.join("emoji")
|
||||||
)
|
|
||||||
|
|
||||||
with {:ok, packs} <- Pack.list_local() do
|
with {:ok, packs} <- Pack.list_local() do
|
||||||
json(conn, packs)
|
json(conn, packs)
|
||||||
else
|
else
|
||||||
{:create_dir, {:error, e}} ->
|
{:error, :create_dir, e} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:internal_server_error)
|
|> put_status(:internal_server_error)
|
||||||
|> json(%{error: "Failed to create the emoji pack directory at #{emoji_path}: #{e}"})
|
|> json(%{error: "Failed to create the emoji pack directory at #{emoji_path}: #{e}"})
|
||||||
|
|
||||||
{:ls, {:error, e}} ->
|
{:error, :ls, e} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:internal_server_error)
|
|> put_status(:internal_server_error)
|
||||||
|> json(%{
|
|> json(%{
|
||||||
@ -60,13 +60,13 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def show(conn, %{"name" => name}) do
|
def show(conn, %{name: name}) do
|
||||||
name = String.trim(name)
|
name = String.trim(name)
|
||||||
|
|
||||||
with {:ok, pack} <- Pack.show(name) do
|
with {:ok, pack} <- Pack.show(name) do
|
||||||
json(conn, pack)
|
json(conn, pack)
|
||||||
else
|
else
|
||||||
{:loaded, _} ->
|
{:error, :not_found} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:not_found)
|
|> put_status(:not_found)
|
||||||
|> json(%{error: "Pack #{name} does not exist"})
|
|> json(%{error: "Pack #{name} does not exist"})
|
||||||
@ -78,11 +78,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def archive(conn, %{"name" => name}) do
|
def archive(conn, %{name: name}) do
|
||||||
with {:ok, archive} <- Pack.get_archive(name) do
|
with {:ok, archive} <- Pack.get_archive(name) do
|
||||||
send_download(conn, {:binary, archive}, filename: "#{name}.zip")
|
send_download(conn, {:binary, archive}, filename: "#{name}.zip")
|
||||||
else
|
else
|
||||||
{:can_download?, _} ->
|
{:error, :cant_download} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:forbidden)
|
|> put_status(:forbidden)
|
||||||
|> json(%{
|
|> json(%{
|
||||||
@ -90,23 +90,23 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||||||
"Pack #{name} cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing"
|
"Pack #{name} cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing"
|
||||||
})
|
})
|
||||||
|
|
||||||
{:exists?, _} ->
|
{:error, :not_found} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:not_found)
|
|> put_status(:not_found)
|
||||||
|> json(%{error: "Pack #{name} does not exist"})
|
|> json(%{error: "Pack #{name} does not exist"})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def download(conn, %{"url" => url, "name" => name} = params) do
|
def download(%{body_params: %{url: url, name: name} = params} = conn, _) do
|
||||||
with :ok <- Pack.download(name, url, params["as"]) do
|
with {:ok, _pack} <- Pack.download(name, url, params[:as]) do
|
||||||
json(conn, "ok")
|
json(conn, "ok")
|
||||||
else
|
else
|
||||||
{:shareable, _} ->
|
{:error, :not_shareable} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:internal_server_error)
|
|> put_status(:internal_server_error)
|
||||||
|> json(%{error: "The requested instance does not support sharing emoji packs"})
|
|> json(%{error: "The requested instance does not support sharing emoji packs"})
|
||||||
|
|
||||||
{:checksum, _} ->
|
{:error, :imvalid_checksum} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:internal_server_error)
|
|> put_status(:internal_server_error)
|
||||||
|> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"})
|
|> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"})
|
||||||
@ -118,10 +118,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(conn, %{"name" => name}) do
|
def create(conn, %{name: name}) do
|
||||||
name = String.trim(name)
|
name = String.trim(name)
|
||||||
|
|
||||||
with :ok <- Pack.create(name) do
|
with {:ok, _pack} <- Pack.create(name) do
|
||||||
json(conn, "ok")
|
json(conn, "ok")
|
||||||
else
|
else
|
||||||
{:error, :eexist} ->
|
{:error, :eexist} ->
|
||||||
@ -143,7 +143,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(conn, %{"name" => name}) do
|
def delete(conn, %{name: name}) do
|
||||||
name = String.trim(name)
|
name = String.trim(name)
|
||||||
|
|
||||||
with {:ok, deleted} when deleted != [] <- Pack.delete(name) do
|
with {:ok, deleted} when deleted != [] <- Pack.delete(name) do
|
||||||
@ -166,11 +166,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(conn, %{"name" => name, "metadata" => metadata}) do
|
def update(%{body_params: %{metadata: metadata}} = conn, %{name: name}) do
|
||||||
with {:ok, pack} <- Pack.update_metadata(name, metadata) do
|
with {:ok, pack} <- Pack.update_metadata(name, metadata) do
|
||||||
json(conn, pack.pack)
|
json(conn, pack.pack)
|
||||||
else
|
else
|
||||||
{:has_all_files?, _} ->
|
{:error, :incomplete} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|> json(%{error: "The fallback archive does not have all files specified in pack.json"})
|
|> json(%{error: "The fallback archive does not have all files specified in pack.json"})
|
||||||
@ -184,19 +184,19 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_file(conn, %{"name" => name} = params) do
|
def add_file(%{body_params: params} = conn, %{name: name}) do
|
||||||
filename = params["filename"] || get_filename(params["file"])
|
filename = params[:filename] || get_filename(params[:file])
|
||||||
shortcode = params["shortcode"] || Path.basename(filename, Path.extname(filename))
|
shortcode = params[:shortcode] || Path.basename(filename, Path.extname(filename))
|
||||||
|
|
||||||
with {:ok, pack} <- Pack.add_file(name, shortcode, filename, params["file"]) do
|
with {:ok, pack} <- Pack.add_file(name, shortcode, filename, params[:file]) do
|
||||||
json(conn, pack.files)
|
json(conn, pack.files)
|
||||||
else
|
else
|
||||||
{:exists, _} ->
|
{:error, :already_exists} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:conflict)
|
|> put_status(:conflict)
|
||||||
|> json(%{error: "An emoji with the \"#{shortcode}\" shortcode already exists"})
|
|> json(%{error: "An emoji with the \"#{shortcode}\" shortcode already exists"})
|
||||||
|
|
||||||
{:loaded, _} ->
|
{:error, :not_found} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|> json(%{error: "pack \"#{name}\" is not found"})
|
|> json(%{error: "pack \"#{name}\" is not found"})
|
||||||
@ -215,20 +215,20 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_file(conn, %{"name" => name, "shortcode" => shortcode} = params) do
|
def update_file(%{body_params: %{shortcode: shortcode} = params} = conn, %{name: name}) do
|
||||||
new_shortcode = params["new_shortcode"]
|
new_shortcode = params[:new_shortcode]
|
||||||
new_filename = params["new_filename"]
|
new_filename = params[:new_filename]
|
||||||
force = params["force"] == true
|
force = params[:force]
|
||||||
|
|
||||||
with {:ok, pack} <- Pack.update_file(name, shortcode, new_shortcode, new_filename, force) do
|
with {:ok, pack} <- Pack.update_file(name, shortcode, new_shortcode, new_filename, force) do
|
||||||
json(conn, pack.files)
|
json(conn, pack.files)
|
||||||
else
|
else
|
||||||
{:exists, _} ->
|
{:error, :doesnt_exist} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|> json(%{error: "Emoji \"#{shortcode}\" does not exist"})
|
|> json(%{error: "Emoji \"#{shortcode}\" does not exist"})
|
||||||
|
|
||||||
{:not_used, _} ->
|
{:error, :already_exists} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:conflict)
|
|> put_status(:conflict)
|
||||||
|> json(%{
|
|> json(%{
|
||||||
@ -236,7 +236,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||||||
"New shortcode \"#{new_shortcode}\" is already used. If you want to override emoji use 'force' option"
|
"New shortcode \"#{new_shortcode}\" is already used. If you want to override emoji use 'force' option"
|
||||||
})
|
})
|
||||||
|
|
||||||
{:loaded, _} ->
|
{:error, :not_found} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|> json(%{error: "pack \"#{name}\" is not found"})
|
|> json(%{error: "pack \"#{name}\" is not found"})
|
||||||
@ -255,16 +255,16 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_file(conn, %{"name" => name, "shortcode" => shortcode}) do
|
def delete_file(conn, %{name: name, shortcode: shortcode}) do
|
||||||
with {:ok, pack} <- Pack.delete_file(name, shortcode) do
|
with {:ok, pack} <- Pack.delete_file(name, shortcode) do
|
||||||
json(conn, pack.files)
|
json(conn, pack.files)
|
||||||
else
|
else
|
||||||
{:exists, _} ->
|
{:error, :doesnt_exist} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|> json(%{error: "Emoji \"#{shortcode}\" does not exist"})
|
|> json(%{error: "Emoji \"#{shortcode}\" does not exist"})
|
||||||
|
|
||||||
{:loaded, _} ->
|
{:error, :not_found} ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|> json(%{error: "pack \"#{name}\" is not found"})
|
|> json(%{error: "pack \"#{name}\" is not found"})
|
@ -9,16 +9,19 @@ defmodule Pleroma.Web.PleromaAPI.MascotController do
|
|||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action == :show)
|
plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action == :show)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action != :show)
|
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action != :show)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaMascotOperation
|
||||||
|
|
||||||
@doc "GET /api/v1/pleroma/mascot"
|
@doc "GET /api/v1/pleroma/mascot"
|
||||||
def show(%{assigns: %{user: user}} = conn, _params) do
|
def show(%{assigns: %{user: user}} = conn, _params) do
|
||||||
json(conn, User.get_mascot(user))
|
json(conn, User.get_mascot(user))
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "PUT /api/v1/pleroma/mascot"
|
@doc "PUT /api/v1/pleroma/mascot"
|
||||||
def update(%{assigns: %{user: user}} = conn, %{"file" => file}) do
|
def update(%{assigns: %{user: user}, body_params: %{file: file}} = conn, _) do
|
||||||
with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)),
|
with {:ok, object} <- ActivityPub.upload(file, actor: User.ap_id(user)),
|
||||||
# Reject if not an image
|
# Reject if not an image
|
||||||
%{type: "image"} = attachment <- render_attachment(object) do
|
%{type: "image"} = attachment <- render_attachment(object) do
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, skip_relationships?: 1]
|
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
@ -69,7 +69,12 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||||||
%{
|
%{
|
||||||
name: emoji,
|
name: emoji,
|
||||||
count: length(users),
|
count: length(users),
|
||||||
accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}),
|
accounts:
|
||||||
|
AccountView.render("index.json", %{
|
||||||
|
users: users,
|
||||||
|
for: user,
|
||||||
|
as: :user
|
||||||
|
}),
|
||||||
me: !!(user && user.ap_id in user_ap_ids)
|
me: !!(user && user.ap_id in user_ap_ids)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@ -145,8 +150,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||||||
|> render("index.json",
|
|> render("index.json",
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: user,
|
for: user,
|
||||||
as: :activity,
|
as: :activity
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
_error ->
|
_error ->
|
||||||
@ -201,7 +205,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do
|
def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"max_id" => max_id}) do
|
||||||
with notifications <- Notification.set_read_up_to(user, max_id) do
|
with notifications <- Notification.set_read_up_to(user, max_id) do
|
||||||
notifications = Enum.take(notifications, 80)
|
notifications = Enum.take(notifications, 80)
|
||||||
|
|
||||||
@ -209,8 +213,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||||||
|> put_view(NotificationView)
|
|> put_view(NotificationView)
|
||||||
|> render("index.json",
|
|> render("index.json",
|
||||||
notifications: notifications,
|
notifications: notifications,
|
||||||
for: user,
|
for: user
|
||||||
skip_relationships: skip_relationships?(params)
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -5,34 +5,27 @@
|
|||||||
defmodule Pleroma.Web.PleromaAPI.ScrobbleController do
|
defmodule Pleroma.Web.PleromaAPI.ScrobbleController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, fetch_integer_param: 2]
|
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
|
||||||
|
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read"], fallback: :proceed_unauthenticated} when action == :user_scrobbles
|
%{scopes: ["read"], fallback: :proceed_unauthenticated} when action == :index
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write"]} when action != :user_scrobbles)
|
plug(OAuthScopesPlug, %{scopes: ["write"]} when action == :create)
|
||||||
|
|
||||||
def new_scrobble(%{assigns: %{user: user}} = conn, %{"title" => _} = params) do
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaScrobbleOperation
|
||||||
params =
|
|
||||||
if !params["length"] do
|
|
||||||
params
|
|
||||||
else
|
|
||||||
params
|
|
||||||
|> Map.put("length", fetch_integer_param(params, "length"))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
def create(%{assigns: %{user: user}, body_params: params} = conn, _) do
|
||||||
with {:ok, activity} <- CommonAPI.listen(user, params) do
|
with {:ok, activity} <- CommonAPI.listen(user, params) do
|
||||||
conn
|
render(conn, "show.json", activity: activity, for: user)
|
||||||
|> put_view(StatusView)
|
|
||||||
|> render("listen.json", %{activity: activity, for: user})
|
|
||||||
else
|
else
|
||||||
{:error, message} ->
|
{:error, message} ->
|
||||||
conn
|
conn
|
||||||
@ -41,16 +34,18 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_scrobbles(%{assigns: %{user: reading_user}} = conn, params) do
|
def index(%{assigns: %{user: reading_user}} = conn, %{id: id} = params) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user) do
|
with %User{} = user <- User.get_cached_by_nickname_or_id(id, for: reading_user) do
|
||||||
params = Map.put(params, "type", ["Listen"])
|
params =
|
||||||
|
params
|
||||||
|
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
||||||
|
|> Map.put("type", ["Listen"])
|
||||||
|
|
||||||
activities = ActivityPub.fetch_user_abstract_activities(user, reading_user, params)
|
activities = ActivityPub.fetch_user_abstract_activities(user, reading_user, params)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(activities)
|
|> add_link_headers(activities)
|
||||||
|> put_view(StatusView)
|
|> render("index.json", %{
|
||||||
|> render("listens.json", %{
|
|
||||||
activities: activities,
|
activities: activities,
|
||||||
for: reading_user,
|
for: reading_user,
|
||||||
as: :activity
|
as: :activity
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationController do
|
defmodule Pleroma.Web.PleromaAPI.TwoFactorAuthenticationController do
|
||||||
|
37
lib/pleroma/web/pleroma_api/views/scrobble_view.ex
Normal file
37
lib/pleroma/web/pleroma_api/views/scrobble_view.ex
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.ScrobbleView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
|
||||||
|
def render("show.json", %{activity: %Activity{data: %{"type" => "Listen"}} = activity} = opts) do
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
|
||||||
|
user = StatusView.get_user(activity.data["actor"])
|
||||||
|
created_at = Utils.to_masto_date(activity.data["published"])
|
||||||
|
|
||||||
|
%{
|
||||||
|
id: activity.id,
|
||||||
|
account: AccountView.render("show.json", %{user: user, for: opts[:for]}),
|
||||||
|
created_at: created_at,
|
||||||
|
title: object.data["title"] |> HTML.strip_tags(),
|
||||||
|
artist: object.data["artist"] |> HTML.strip_tags(),
|
||||||
|
album: object.data["album"] |> HTML.strip_tags(),
|
||||||
|
length: object.data["length"]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("index.json", opts) do
|
||||||
|
safe_render_many(opts.activities, __MODULE__, "show.json", opts)
|
||||||
|
end
|
||||||
|
end
|
@ -216,24 +216,25 @@ defmodule Pleroma.Web.Router do
|
|||||||
scope "/packs" do
|
scope "/packs" do
|
||||||
pipe_through(:admin_api)
|
pipe_through(:admin_api)
|
||||||
|
|
||||||
get("/import", EmojiAPIController, :import_from_filesystem)
|
get("/import", EmojiPackController, :import_from_filesystem)
|
||||||
get("/remote", EmojiAPIController, :remote)
|
get("/remote", EmojiPackController, :remote)
|
||||||
post("/download", EmojiAPIController, :download)
|
post("/download", EmojiPackController, :download)
|
||||||
|
|
||||||
post("/:name", EmojiAPIController, :create)
|
post("/:name", EmojiPackController, :create)
|
||||||
patch("/:name", EmojiAPIController, :update)
|
patch("/:name", EmojiPackController, :update)
|
||||||
delete("/:name", EmojiAPIController, :delete)
|
delete("/:name", EmojiPackController, :delete)
|
||||||
|
|
||||||
post("/:name/files", EmojiAPIController, :add_file)
|
post("/:name/files", EmojiPackController, :add_file)
|
||||||
patch("/:name/files", EmojiAPIController, :update_file)
|
patch("/:name/files", EmojiPackController, :update_file)
|
||||||
delete("/:name/files", EmojiAPIController, :delete_file)
|
delete("/:name/files", EmojiPackController, :delete_file)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Pack info / downloading
|
# Pack info / downloading
|
||||||
scope "/packs" do
|
scope "/packs" do
|
||||||
get("/", EmojiAPIController, :list)
|
pipe_through(:api)
|
||||||
get("/:name", EmojiAPIController, :show)
|
get("/", EmojiPackController, :index)
|
||||||
get("/:name/archive", EmojiAPIController, :archive)
|
get("/:name", EmojiPackController, :show)
|
||||||
|
get("/:name/archive", EmojiPackController, :archive)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -325,7 +326,7 @@ defmodule Pleroma.Web.Router do
|
|||||||
get("/mascot", MascotController, :show)
|
get("/mascot", MascotController, :show)
|
||||||
put("/mascot", MascotController, :update)
|
put("/mascot", MascotController, :update)
|
||||||
|
|
||||||
post("/scrobble", ScrobbleController, :new_scrobble)
|
post("/scrobble", ScrobbleController, :create)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope [] do
|
scope [] do
|
||||||
@ -345,7 +346,7 @@ defmodule Pleroma.Web.Router do
|
|||||||
|
|
||||||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||||
pipe_through(:api)
|
pipe_through(:api)
|
||||||
get("/accounts/:id/scrobbles", ScrobbleController, :user_scrobbles)
|
get("/accounts/:id/scrobbles", ScrobbleController, :index)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||||
@ -403,6 +404,7 @@ defmodule Pleroma.Web.Router do
|
|||||||
post("/markers", MarkerController, :upsert)
|
post("/markers", MarkerController, :upsert)
|
||||||
|
|
||||||
post("/media", MediaController, :create)
|
post("/media", MediaController, :create)
|
||||||
|
get("/media/:id", MediaController, :show)
|
||||||
put("/media/:id", MediaController, :update)
|
put("/media/:id", MediaController, :update)
|
||||||
|
|
||||||
get("/notifications", NotificationController, :index)
|
get("/notifications", NotificationController, :index)
|
||||||
@ -497,6 +499,8 @@ defmodule Pleroma.Web.Router do
|
|||||||
scope "/api/v2", Pleroma.Web.MastodonAPI do
|
scope "/api/v2", Pleroma.Web.MastodonAPI do
|
||||||
pipe_through(:api)
|
pipe_through(:api)
|
||||||
get("/search", SearchController, :search2)
|
get("/search", SearchController, :search2)
|
||||||
|
|
||||||
|
post("/media", MediaController, :create2)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api", Pleroma.Web do
|
scope "/api", Pleroma.Web do
|
||||||
|
@ -27,8 +27,20 @@ defmodule Pleroma.Workers.AttachmentsCleanupWorker do
|
|||||||
|
|
||||||
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
|
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
|
||||||
|
|
||||||
|
prefix =
|
||||||
|
case Pleroma.Config.get([Pleroma.Upload, :base_url]) do
|
||||||
|
nil -> "media"
|
||||||
|
_ -> ""
|
||||||
|
end
|
||||||
|
|
||||||
|
base_url =
|
||||||
|
String.trim_trailing(
|
||||||
|
Pleroma.Config.get([Pleroma.Upload, :base_url], Pleroma.Web.base_url()),
|
||||||
|
"/"
|
||||||
|
)
|
||||||
|
|
||||||
# find all objects for copies of the attachments, name and actor doesn't matter here
|
# find all objects for copies of the attachments, name and actor doesn't matter here
|
||||||
delete_ids =
|
object_ids_and_hrefs =
|
||||||
from(o in Object,
|
from(o in Object,
|
||||||
where:
|
where:
|
||||||
fragment(
|
fragment(
|
||||||
@ -67,29 +79,28 @@ defmodule Pleroma.Workers.AttachmentsCleanupWorker do
|
|||||||
|> Enum.map(fn {href, %{id: id, count: count}} ->
|
|> Enum.map(fn {href, %{id: id, count: count}} ->
|
||||||
# only delete files that have single instance
|
# only delete files that have single instance
|
||||||
with 1 <- count do
|
with 1 <- count do
|
||||||
prefix =
|
href
|
||||||
case Pleroma.Config.get([Pleroma.Upload, :base_url]) do
|
|> String.trim_leading("#{base_url}/#{prefix}")
|
||||||
nil -> "media"
|
|> uploader.delete_file()
|
||||||
_ -> ""
|
|
||||||
end
|
|
||||||
|
|
||||||
base_url =
|
{id, href}
|
||||||
String.trim_trailing(
|
else
|
||||||
Pleroma.Config.get([Pleroma.Upload, :base_url], Pleroma.Web.base_url()),
|
_ -> {id, nil}
|
||||||
"/"
|
|
||||||
)
|
|
||||||
|
|
||||||
file_path = String.trim_leading(href, "#{base_url}/#{prefix}")
|
|
||||||
|
|
||||||
uploader.delete_file(file_path)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
id
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
from(o in Object, where: o.id in ^delete_ids)
|
object_ids = Enum.map(object_ids_and_hrefs, fn {id, _} -> id end)
|
||||||
|
|
||||||
|
from(o in Object, where: o.id in ^object_ids)
|
||||||
|> Repo.delete_all()
|
|> Repo.delete_all()
|
||||||
|
|
||||||
|
object_ids_and_hrefs
|
||||||
|
|> Enum.filter(fn {_, href} -> not is_nil(href) end)
|
||||||
|
|> Enum.map(&elem(&1, 1))
|
||||||
|
|> Pleroma.Web.MediaProxy.Invalidation.purge()
|
||||||
|
|
||||||
|
{:ok, :success}
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%{"op" => "cleanup_attachments", "object" => _object}, _job), do: :ok
|
def perform(%{"op" => "cleanup_attachments", "object" => _object}, _job), do: {:ok, :skip}
|
||||||
end
|
end
|
||||||
|
2
mix.exs
2
mix.exs
@ -155,7 +155,7 @@ defmodule Pleroma.Mixfile do
|
|||||||
{:credo, "~> 1.1.0", only: [:dev, :test], runtime: false},
|
{:credo, "~> 1.1.0", only: [:dev, :test], runtime: false},
|
||||||
{:mock, "~> 0.3.3", only: :test},
|
{:mock, "~> 0.3.3", only: :test},
|
||||||
{:crypt,
|
{:crypt,
|
||||||
git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"},
|
git: "https://github.com/msantos/crypt", ref: "f63a705f92c26955977ee62a313012e309a4d77a"},
|
||||||
{:cors_plug, "~> 1.5"},
|
{:cors_plug, "~> 1.5"},
|
||||||
{:ex_doc, "~> 0.21", only: :dev, runtime: false},
|
{:ex_doc, "~> 0.21", only: :dev, runtime: false},
|
||||||
{:web_push_encryption, "~> 0.2.1"},
|
{:web_push_encryption, "~> 0.2.1"},
|
||||||
|
6
mix.lock
6
mix.lock
@ -21,13 +21,13 @@
|
|||||||
"cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"},
|
"cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"},
|
||||||
"credo": {:hex, :credo, "1.1.5", "caec7a3cadd2e58609d7ee25b3931b129e739e070539ad1a0cd7efeeb47014f4", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d0bbd3222607ccaaac5c0340f7f525c627ae4d7aee6c8c8c108922620c5b6446"},
|
"credo": {:hex, :credo, "1.1.5", "caec7a3cadd2e58609d7ee25b3931b129e739e070539ad1a0cd7efeeb47014f4", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d0bbd3222607ccaaac5c0340f7f525c627ae4d7aee6c8c8c108922620c5b6446"},
|
||||||
"crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
"crontab": {:hex, :crontab, "1.1.8", "2ce0e74777dfcadb28a1debbea707e58b879e6aa0ffbf9c9bb540887bce43617", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
"crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
|
"crypt": {:git, "https://github.com/msantos/crypt", "f63a705f92c26955977ee62a313012e309a4d77a", [ref: "f63a705f92c26955977ee62a313012e309a4d77a"]},
|
||||||
"custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"},
|
"custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"},
|
||||||
"db_connection": {:hex, :db_connection, "2.2.1", "caee17725495f5129cb7faebde001dc4406796f12a62b8949f4ac69315080566", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "2b02ece62d9f983fcd40954e443b7d9e6589664380e5546b2b9b523cd0fb59e1"},
|
"db_connection": {:hex, :db_connection, "2.2.1", "caee17725495f5129cb7faebde001dc4406796f12a62b8949f4ac69315080566", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "2b02ece62d9f983fcd40954e443b7d9e6589664380e5546b2b9b523cd0fb59e1"},
|
||||||
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
|
"decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"},
|
||||||
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
|
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
|
||||||
"earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"},
|
"earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"},
|
||||||
"ecto": {:hex, :ecto, "3.4.0", "a7a83ab8359bf816ce729e5e65981ce25b9fc5adfc89c2ea3980f4fed0bfd7c1", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "5eed18252f5b5bbadec56a24112b531343507dbe046273133176b12190ce19cc"},
|
"ecto": {:hex, :ecto, "3.4.4", "a2c881e80dc756d648197ae0d936216c0308370332c5e77a2325a10293eef845", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cc4bd3ad62abc3b21fb629f0f7a3dab23a192fca837d257dd08449fba7373561"},
|
||||||
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
|
"ecto_enum": {:hex, :ecto_enum, "1.4.0", "d14b00e04b974afc69c251632d1e49594d899067ee2b376277efd8233027aec8", [:mix], [{:ecto, ">= 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "> 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:mariaex, ">= 0.0.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "8fb55c087181c2b15eee406519dc22578fa60dd82c088be376d0010172764ee4"},
|
||||||
"ecto_sql": {:hex, :ecto_sql, "3.3.4", "aa18af12eb875fbcda2f75e608b3bd534ebf020fc4f6448e4672fcdcbb081244", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5eccbdbf92e3c6f213007a82d5dbba4cd9bb659d1a21331f89f408e4c0efd7a8"},
|
"ecto_sql": {:hex, :ecto_sql, "3.3.4", "aa18af12eb875fbcda2f75e608b3bd534ebf020fc4f6448e4672fcdcbb081244", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5eccbdbf92e3c6f213007a82d5dbba4cd9bb659d1a21331f89f408e4c0efd7a8"},
|
||||||
"eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"},
|
"eimp": {:hex, :eimp, "1.0.14", "fc297f0c7e2700457a95a60c7010a5f1dcb768a083b6d53f49cd94ab95a28f22", [:rebar3], [{:p1_utils, "1.0.18", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "501133f3112079b92d9e22da8b88bf4f0e13d4d67ae9c15c42c30bd25ceb83b6"},
|
||||||
@ -60,7 +60,7 @@
|
|||||||
"httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"},
|
"httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"},
|
||||||
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"},
|
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"},
|
||||||
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"},
|
"inet_cidr": {:hex, :inet_cidr, "1.0.4", "a05744ab7c221ca8e395c926c3919a821eb512e8f36547c062f62c4ca0cf3d6e", [:mix], [], "hexpm", "64a2d30189704ae41ca7dbdd587f5291db5d1dda1414e0774c29ffc81088c1bc"},
|
||||||
"jason": {:hex, :jason, "1.2.0", "10043418c42d2493d0ee212d3fddd25d7ffe484380afad769a0a38795938e448", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "116747dbe057794c3a3e4e143b7c8390b29f634e16c78a7f59ba75bfa6852e7f"},
|
"jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"},
|
||||||
"joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"},
|
"joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"},
|
||||||
"jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"},
|
"jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"},
|
||||||
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
|
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
|
||||||
|
578
priv/gettext/nl/LC_MESSAGES/errors.po
Normal file
578
priv/gettext/nl/LC_MESSAGES/errors.po
Normal file
@ -0,0 +1,578 @@
|
|||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2020-05-15 09:37+0000\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: Automatically generated\n"
|
||||||
|
"Language-Team: none\n"
|
||||||
|
"Language: nl\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Generator: Translate Toolkit 2.5.1\n"
|
||||||
|
|
||||||
|
## This file is a PO Template file.
|
||||||
|
##
|
||||||
|
## `msgid`s here are often extracted from source code.
|
||||||
|
## Add new translations manually only if they're dynamic
|
||||||
|
## translations that can't be statically extracted.
|
||||||
|
##
|
||||||
|
## Run `mix gettext.extract` to bring this file up to
|
||||||
|
## date. Leave `msgstr`s empty as changing them here as no
|
||||||
|
## effect: edit them in PO (`.po`) files instead.
|
||||||
|
## From Ecto.Changeset.cast/4
|
||||||
|
msgid "can't be blank"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.unique_constraint/3
|
||||||
|
msgid "has already been taken"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.put_change/3
|
||||||
|
msgid "is invalid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.validate_format/3
|
||||||
|
msgid "has invalid format"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.validate_subset/3
|
||||||
|
msgid "has an invalid entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.validate_exclusion/3
|
||||||
|
msgid "is reserved"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.validate_confirmation/3
|
||||||
|
msgid "does not match confirmation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.no_assoc_constraint/3
|
||||||
|
msgid "is still associated with this entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "are still associated with this entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.validate_length/3
|
||||||
|
msgid "should be %{count} character(s)"
|
||||||
|
msgid_plural "should be %{count} character(s)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "should have %{count} item(s)"
|
||||||
|
msgid_plural "should have %{count} item(s)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "should be at least %{count} character(s)"
|
||||||
|
msgid_plural "should be at least %{count} character(s)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "should have at least %{count} item(s)"
|
||||||
|
msgid_plural "should have at least %{count} item(s)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "should be at most %{count} character(s)"
|
||||||
|
msgid_plural "should be at most %{count} character(s)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "should have at most %{count} item(s)"
|
||||||
|
msgid_plural "should have at most %{count} item(s)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.validate_number/3
|
||||||
|
msgid "must be less than %{number}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "must be greater than %{number}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "must be less than or equal to %{number}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "must be greater than or equal to %{number}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "must be equal to %{number}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:421
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Account not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:249
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Already voted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:360
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Bad request"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:425
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Can't delete object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:196
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Can't delete this post"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/controller_helper.ex:95
|
||||||
|
#: lib/pleroma/web/controller_helper.ex:101
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Can't display this activity"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:227
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:254
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Can't find user"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:114
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Can't get favorites"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:437
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Can't like object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/utils.ex:556
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Cannot post an empty status without attachments"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/utils.ex:504
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Comment must be up to %{max_size} characters"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/config/config_db.ex:222
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Config with params %{params} not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:95
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not delete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:141
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not favorite"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:370
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not pin"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:112
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not repeat"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:188
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not unfavorite"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:380
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not unpin"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:126
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not unrepeat"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:428
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:437
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not update state"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:202
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Error."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:106
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid CAPTCHA"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:117
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:569
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid credentials"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:38
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid credentials."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:265
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid indices"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/admin_api/admin_api_controller.ex:1147
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid parameters"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/utils.ex:411
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid password."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:187
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid request"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:109
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Kocaptcha service unavailable"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:113
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Missing parameters"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/utils.ex:540
|
||||||
|
#, elixir-format
|
||||||
|
msgid "No such conversation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/admin_api/admin_api_controller.ex:439
|
||||||
|
#: lib/pleroma/web/admin_api/admin_api_controller.ex:465 lib/pleroma/web/admin_api/admin_api_controller.ex:507
|
||||||
|
#, elixir-format
|
||||||
|
msgid "No such permission_group"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/uploaded_media.ex:74
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:485 lib/pleroma/web/admin_api/admin_api_controller.ex:1135
|
||||||
|
#: lib/pleroma/web/feed/user_controller.ex:73 lib/pleroma/web/ostatus/ostatus_controller.ex:143
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:241
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Poll's author can't vote"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:37 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:49
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:50 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:290
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Record not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/admin_api/admin_api_controller.ex:1153
|
||||||
|
#: lib/pleroma/web/feed/user_controller.ex:79 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:32
|
||||||
|
#: lib/pleroma/web/ostatus/ostatus_controller.ex:149
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Something went wrong"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/activity_draft.ex:107
|
||||||
|
#, elixir-format
|
||||||
|
msgid "The message visibility must be direct"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/utils.ex:566
|
||||||
|
#, elixir-format
|
||||||
|
msgid "The status is over the character limit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31
|
||||||
|
#, elixir-format
|
||||||
|
msgid "This resource requires authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Throttled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:266
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Too many choices"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:442
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unhandled activity type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/admin_api/admin_api_controller.ex:536
|
||||||
|
#, elixir-format
|
||||||
|
msgid "You can't revoke your own admin status."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:218
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:309
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Your account is currently disabled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:180
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:332
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Your login is missing a confirmed e-mail address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:389
|
||||||
|
#, elixir-format
|
||||||
|
msgid "can't read inbox of %{nickname} as %{as_nickname}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:472
|
||||||
|
#, elixir-format
|
||||||
|
msgid "can't update outbox of %{nickname} as %{as_nickname}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:388
|
||||||
|
#, elixir-format
|
||||||
|
msgid "conversation is already muted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:316
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:491
|
||||||
|
#, elixir-format
|
||||||
|
msgid "error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:29
|
||||||
|
#, elixir-format
|
||||||
|
msgid "mascots can only be images"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:60
|
||||||
|
#, elixir-format
|
||||||
|
msgid "not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:395
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Bad OAuth request."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:115
|
||||||
|
#, elixir-format
|
||||||
|
msgid "CAPTCHA already used"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:112
|
||||||
|
#, elixir-format
|
||||||
|
msgid "CAPTCHA expired"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/uploaded_media.ex:55
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Failed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:411
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Failed to authenticate: %{message}."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:442
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Failed to set up user account."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/oauth_scopes_plug.ex:38
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Insufficient permissions: %{permissions}."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/uploaded_media.ex:94
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Internal Error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/fallback_controller.ex:22
|
||||||
|
#: lib/pleroma/web/oauth/fallback_controller.ex:29
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid Username/Password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:118
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid answer data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:128
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Nodeinfo schema version not handled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:169
|
||||||
|
#, elixir-format
|
||||||
|
msgid "This action is outside the authorized scopes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/fallback_controller.ex:14
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unknown error, please check the details and try again."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:116
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:155
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unlisted redirect_uri."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:391
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unsupported OAuth provider: %{provider}."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/uploaders/uploader.ex:72
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Uploader callback timeout"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/uploader_controller.ex:23
|
||||||
|
#, elixir-format
|
||||||
|
msgid "bad request"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:103
|
||||||
|
#, elixir-format
|
||||||
|
msgid "CAPTCHA Error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:200
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not add reaction emoji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:211
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not remove reaction emoji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:129
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid CAPTCHA (Missing parameter: %{name})"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:92
|
||||||
|
#, elixir-format
|
||||||
|
msgid "List not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:124
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Missing parameter: %{name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:207
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:322
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Password reset is required"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/tests/auth_test_controller.ex:9
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/admin_api_controller.ex:6
|
||||||
|
#: lib/pleroma/web/controller_helper.ex:6 lib/pleroma/web/fallback_redirect_controller.ex:6
|
||||||
|
#: lib/pleroma/web/feed/tag_controller.ex:6 lib/pleroma/web/feed/user_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mailer/subscription_controller.ex:2 lib/pleroma/web/masto_fe_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/app_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/auth_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/filter_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/instance_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/marker_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex:14 lib/pleroma/web/mastodon_api/controllers/media_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/notification_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/report_controller.ex:8 lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/search_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:7 lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:6 lib/pleroma/web/media_proxy/media_proxy_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mongooseim/mongoose_im_controller.ex:6 lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:6
|
||||||
|
#: lib/pleroma/web/oauth/fallback_controller.ex:6 lib/pleroma/web/oauth/mfa_controller.ex:10
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:6 lib/pleroma/web/ostatus/ostatus_controller.ex:6
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:2
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex:6
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex:6
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex:7 lib/pleroma/web/static_fe/static_fe_controller.ex:6
|
||||||
|
#: lib/pleroma/web/twitter_api/controllers/password_controller.ex:10 lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex:6
|
||||||
|
#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:6 lib/pleroma/web/twitter_api/twitter_api_controller.ex:6
|
||||||
|
#: lib/pleroma/web/uploader_controller.ex:6 lib/pleroma/web/web_finger/web_finger_controller.ex:6
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Security violation: OAuth scopes check was neither handled nor explicitly skipped."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:28
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Two-factor authentication enabled, you must use a access token."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:210
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unexpected error occurred while adding file to pack."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:138
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unexpected error occurred while creating pack."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:278
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unexpected error occurred while removing file from pack."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:250
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unexpected error occurred while updating file in pack."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:179
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unexpected error occurred while updating pack metadata."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/user_is_admin_plug.ex:40
|
||||||
|
#, elixir-format
|
||||||
|
msgid "User is not an admin or OAuth admin scope is not granted."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Web push subscription is disabled on this Pleroma instance"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/admin_api/admin_api_controller.ex:502
|
||||||
|
#, elixir-format
|
||||||
|
msgid "You can't revoke your own admin/moderator status."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:105
|
||||||
|
#, elixir-format
|
||||||
|
msgid "authorization required for timeline view"
|
||||||
|
msgstr ""
|
@ -3,8 +3,8 @@ msgstr ""
|
|||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-05-13 16:37+0000\n"
|
"POT-Creation-Date: 2020-05-13 16:37+0000\n"
|
||||||
"PO-Revision-Date: 2020-05-14 14:37+0000\n"
|
"PO-Revision-Date: 2020-05-16 17:13+0000\n"
|
||||||
"Last-Translator: Michał Sidor <pleromeme@meekchopp.es>\n"
|
"Last-Translator: Jędrzej Tomaszewski <jederow@hotmail.com>\n"
|
||||||
"Language-Team: Polish <https://translate.pleroma.social/projects/pleroma/"
|
"Language-Team: Polish <https://translate.pleroma.social/projects/pleroma/"
|
||||||
"pleroma/pl/>\n"
|
"pleroma/pl/>\n"
|
||||||
"Language: pl\n"
|
"Language: pl\n"
|
||||||
@ -46,7 +46,7 @@ msgstr "ma niepoprawny wpis"
|
|||||||
|
|
||||||
## From Ecto.Changeset.validate_exclusion/3
|
## From Ecto.Changeset.validate_exclusion/3
|
||||||
msgid "is reserved"
|
msgid "is reserved"
|
||||||
msgstr ""
|
msgstr "jest zarezerwowany"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_confirmation/3
|
## From Ecto.Changeset.validate_confirmation/3
|
||||||
msgid "does not match confirmation"
|
msgid "does not match confirmation"
|
||||||
@ -54,17 +54,17 @@ msgstr ""
|
|||||||
|
|
||||||
## From Ecto.Changeset.no_assoc_constraint/3
|
## From Ecto.Changeset.no_assoc_constraint/3
|
||||||
msgid "is still associated with this entry"
|
msgid "is still associated with this entry"
|
||||||
msgstr ""
|
msgstr "jest wciąż powiązane z tym wpisem"
|
||||||
|
|
||||||
msgid "are still associated with this entry"
|
msgid "are still associated with this entry"
|
||||||
msgstr ""
|
msgstr "są wciąż powiązane z tym wpisem"
|
||||||
|
|
||||||
## From Ecto.Changeset.validate_length/3
|
## From Ecto.Changeset.validate_length/3
|
||||||
msgid "should be %{count} character(s)"
|
msgid "should be %{count} character(s)"
|
||||||
msgid_plural "should be %{count} character(s)"
|
msgid_plural "should be %{count} character(s)"
|
||||||
msgstr[0] ""
|
msgstr[0] "powinno mieć %{count} znak"
|
||||||
msgstr[1] ""
|
msgstr[1] "powinno mieć %{count} znaki"
|
||||||
msgstr[2] ""
|
msgstr[2] "powinno mieć %{count} znaków"
|
||||||
|
|
||||||
msgid "should have %{count} item(s)"
|
msgid "should have %{count} item(s)"
|
||||||
msgid_plural "should have %{count} item(s)"
|
msgid_plural "should have %{count} item(s)"
|
||||||
|
@ -1 +1 @@
|
|||||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/vendors~app.18fea621d430000acc27.css rel=stylesheet><link href=/static/css/app.613cef07981cd95ccceb.css rel=stylesheet><link href=/static/fontello.1588947937982.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.561a1c605d1dfb0e6f74.js></script><script type=text/javascript src=/static/js/app.996428ccaaaa7f28cb8d.js></script></body></html>
|
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><title>Pleroma</title><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/vendors~app.18fea621d430000acc27.css rel=stylesheet><link href=/static/css/app.613cef07981cd95ccceb.css rel=stylesheet><link href=/static/fontello.1589385935077.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.561a1c605d1dfb0e6f74.js></script><script type=text/javascript src=/static/js/app.838ffa9aecf210c7d744.js></script></body></html>
|
@ -1,2 +1,2 @@
|
|||||||
(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{722:function(e,t,n){"use strict";n.r(t),n.d(t,"register",(function(){return s})),n.d(t,"setFocusTrapTest",(function(){return d})),n.d(t,"unregister",(function(){return l}));var r="a[href], area[href], input, select, textarea, button, iframe, object, embed, [contenteditable], [tabindex], video[controls], audio[controls], summary",o=["text","search","url","password","tel"],i=["checkbox","radio"],a=void 0;function c(e){for(var t=[],n=(function(e){if(!a)return;var t=e.parentElement;for(;t;){if(a(t))return t;t=t.parentElement}}(e)||document).querySelectorAll(r),o=n.length,i=0;i<o;i++){var c=n[i];c!==e&&(c.disabled||/^-/.test(c.getAttribute("tabindex")||"")||c.hasAttribute("inert")||!(c.offsetWidth>0||c.offsetHeight>0))||t.push(c)}return t}function u(e,t){var n=document.activeElement;if(!function(e,t){var n,r,i,a=e.tagName,c="TEXTAREA"===a,u="INPUT"===a&&-1!==o.indexOf(e.getAttribute("type").toLowerCase()),f=e.hasAttribute("contenteditable");if(!c&&!u&&!f)return!1;if(f){var s=getSelection();n=s.anchorOffset,r=s.focusOffset,i=e.textContent.length}else n=e.selectionStart,r=e.selectionEnd,i=e.value.length;return("ArrowLeft"!==t||n!==r||0!==n)&&("ArrowRight"!==t||n!==r||n!==i)}(n,t)){var r=c(n);if(r.length){var i=r.indexOf(n);("ArrowLeft"===t?r[i-1]||r[0]:r[i+1]||r[r.length-1]).focus(),e.preventDefault()}}}function f(e){if(!(e.altKey||e.metaKey||e.ctrlKey)){var t=e.key;switch(t){case"ArrowLeft":case"ArrowRight":u(e,t);break;case"Enter":!function(e){var t=document.activeElement;"INPUT"===t.tagName&&-1!==i.indexOf(t.getAttribute("type").toLowerCase())&&(t.click(),e.preventDefault())}(e)}}}function s(){addEventListener("keydown",f)}function l(){removeEventListener("keydown",f)}function d(e){a=e}}}]);
|
(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{738:function(e,t,n){"use strict";n.r(t),n.d(t,"register",(function(){return s})),n.d(t,"setFocusTrapTest",(function(){return d})),n.d(t,"unregister",(function(){return l}));var r="a[href], area[href], input, select, textarea, button, iframe, object, embed, [contenteditable], [tabindex], video[controls], audio[controls], summary",o=["text","search","url","password","tel"],i=["checkbox","radio"],a=void 0;function c(e){for(var t=[],n=(function(e){if(!a)return;var t=e.parentElement;for(;t;){if(a(t))return t;t=t.parentElement}}(e)||document).querySelectorAll(r),o=n.length,i=0;i<o;i++){var c=n[i];c!==e&&(c.disabled||/^-/.test(c.getAttribute("tabindex")||"")||c.hasAttribute("inert")||!(c.offsetWidth>0||c.offsetHeight>0))||t.push(c)}return t}function u(e,t){var n=document.activeElement;if(!function(e,t){var n,r,i,a=e.tagName,c="TEXTAREA"===a,u="INPUT"===a&&-1!==o.indexOf(e.getAttribute("type").toLowerCase()),f=e.hasAttribute("contenteditable");if(!c&&!u&&!f)return!1;if(f){var s=getSelection();n=s.anchorOffset,r=s.focusOffset,i=e.textContent.length}else n=e.selectionStart,r=e.selectionEnd,i=e.value.length;return("ArrowLeft"!==t||n!==r||0!==n)&&("ArrowRight"!==t||n!==r||n!==i)}(n,t)){var r=c(n);if(r.length){var i=r.indexOf(n);("ArrowLeft"===t?r[i-1]||r[0]:r[i+1]||r[r.length-1]).focus(),e.preventDefault()}}}function f(e){if(!(e.altKey||e.metaKey||e.ctrlKey)){var t=e.key;switch(t){case"ArrowLeft":case"ArrowRight":u(e,t);break;case"Enter":!function(e){var t=document.activeElement;"INPUT"===t.tagName&&-1!==i.indexOf(t.getAttribute("type").toLowerCase())&&(t.click(),e.preventDefault())}(e)}}}function s(){addEventListener("keydown",f)}function l(){removeEventListener("keydown",f)}function d(e){a=e}}}]);
|
||||||
//# sourceMappingURL=arrow-key-navigation.js.map
|
//# sourceMappingURL=arrow-key-navigation.js.map
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -4,103 +4,69 @@
|
|||||||
http://jedwatson.github.io/classnames
|
http://jedwatson.github.io/classnames
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* escape-html
|
||||||
|
* Copyright(c) 2012-2013 TJ Holowaychuk
|
||||||
|
* Copyright(c) 2015 Andreas Lubbe
|
||||||
|
* Copyright(c) 2015 Tiancheng "Timothy" Gu
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* wavesurfer.js 3.3.1 (2020-01-14)
|
* wavesurfer.js 3.3.1 (2020-01-14)
|
||||||
* https://github.com/katspaugh/wavesurfer.js
|
* https://github.com/katspaugh/wavesurfer.js
|
||||||
* @license BSD-3-Clause
|
* @license BSD-3-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!****************************************!*\
|
/*! ./ajax */
|
||||||
!*** ./node_modules/debounce/index.js ***!
|
|
||||||
\****************************************/
|
|
||||||
|
|
||||||
/*! no static exports found */
|
|
||||||
|
|
||||||
/*!***********************************!*\
|
|
||||||
!*** ./src/drawer.canvasentry.js ***!
|
|
||||||
\***********************************/
|
|
||||||
|
|
||||||
/*! ./util/style */
|
|
||||||
|
|
||||||
/*! ./util/get-id */
|
|
||||||
|
|
||||||
/*!***********************!*\
|
|
||||||
!*** ./src/drawer.js ***!
|
|
||||||
\***********************/
|
|
||||||
|
|
||||||
/*! ./util */
|
|
||||||
|
|
||||||
/*!***********************************!*\
|
|
||||||
!*** ./src/drawer.multicanvas.js ***!
|
|
||||||
\***********************************/
|
|
||||||
|
|
||||||
/*! ./drawer */
|
/*! ./drawer */
|
||||||
|
|
||||||
/*! ./drawer.canvasentry */
|
/*! ./drawer.canvasentry */
|
||||||
|
|
||||||
/*!**************************************!*\
|
/*! ./drawer.multicanvas */
|
||||||
!*** ./src/mediaelement-webaudio.js ***!
|
|
||||||
\**************************************/
|
|
||||||
|
|
||||||
/*! ./mediaelement */
|
/*! ./extend */
|
||||||
|
|
||||||
/*!*****************************!*\
|
/*! ./fetch */
|
||||||
!*** ./src/mediaelement.js ***!
|
|
||||||
\*****************************/
|
|
||||||
|
|
||||||
/*! ./webaudio */
|
/*! ./frame */
|
||||||
|
|
||||||
/*!**************************!*\
|
|
||||||
!*** ./src/peakcache.js ***!
|
|
||||||
\**************************/
|
|
||||||
|
|
||||||
/*!**************************!*\
|
|
||||||
!*** ./src/util/ajax.js ***!
|
|
||||||
\**************************/
|
|
||||||
|
|
||||||
/*! ./observer */
|
|
||||||
|
|
||||||
/*!****************************!*\
|
|
||||||
!*** ./src/util/extend.js ***!
|
|
||||||
\****************************/
|
|
||||||
|
|
||||||
/*!***************************!*\
|
|
||||||
!*** ./src/util/fetch.js ***!
|
|
||||||
\***************************/
|
|
||||||
|
|
||||||
/*!***************************!*\
|
|
||||||
!*** ./src/util/frame.js ***!
|
|
||||||
\***************************/
|
|
||||||
|
|
||||||
/*! ./request-animation-frame */
|
|
||||||
|
|
||||||
/*!****************************!*\
|
|
||||||
!*** ./src/util/get-id.js ***!
|
|
||||||
\****************************/
|
|
||||||
|
|
||||||
/*!***************************!*\
|
|
||||||
!*** ./src/util/index.js ***!
|
|
||||||
\***************************/
|
|
||||||
|
|
||||||
/*! ./ajax */
|
|
||||||
|
|
||||||
/*! ./get-id */
|
/*! ./get-id */
|
||||||
|
|
||||||
/*! ./max */
|
/*! ./max */
|
||||||
|
|
||||||
|
/*! ./mediaelement */
|
||||||
|
|
||||||
|
/*! ./mediaelement-webaudio */
|
||||||
|
|
||||||
/*! ./min */
|
/*! ./min */
|
||||||
|
|
||||||
/*! ./extend */
|
/*! ./observer */
|
||||||
|
|
||||||
/*! ./style */
|
/*! ./peakcache */
|
||||||
|
|
||||||
/*! ./frame */
|
|
||||||
|
|
||||||
/*! debounce */
|
|
||||||
|
|
||||||
/*! ./prevent-click */
|
/*! ./prevent-click */
|
||||||
|
|
||||||
/*! ./fetch */
|
/*! ./request-animation-frame */
|
||||||
|
|
||||||
|
/*! ./style */
|
||||||
|
|
||||||
|
/*! ./util */
|
||||||
|
|
||||||
|
/*! ./util/get-id */
|
||||||
|
|
||||||
|
/*! ./util/style */
|
||||||
|
|
||||||
|
/*! ./webaudio */
|
||||||
|
|
||||||
|
/*! debounce */
|
||||||
|
|
||||||
|
/*! no static exports found */
|
||||||
|
|
||||||
|
/*!***********************!*\
|
||||||
|
!*** ./src/drawer.js ***!
|
||||||
|
\***********************/
|
||||||
|
|
||||||
/*!*************************!*\
|
/*!*************************!*\
|
||||||
!*** ./src/util/max.js ***!
|
!*** ./src/util/max.js ***!
|
||||||
@ -110,17 +76,29 @@
|
|||||||
!*** ./src/util/min.js ***!
|
!*** ./src/util/min.js ***!
|
||||||
\*************************/
|
\*************************/
|
||||||
|
|
||||||
/*!******************************!*\
|
/*!*************************!*\
|
||||||
!*** ./src/util/observer.js ***!
|
!*** ./src/webaudio.js ***!
|
||||||
\******************************/
|
\*************************/
|
||||||
|
|
||||||
/*!***********************************!*\
|
/*!**************************!*\
|
||||||
!*** ./src/util/prevent-click.js ***!
|
!*** ./src/peakcache.js ***!
|
||||||
\***********************************/
|
\**************************/
|
||||||
|
|
||||||
/*!*********************************************!*\
|
/*!**************************!*\
|
||||||
!*** ./src/util/request-animation-frame.js ***!
|
!*** ./src/util/ajax.js ***!
|
||||||
\*********************************************/
|
\**************************/
|
||||||
|
|
||||||
|
/*!***************************!*\
|
||||||
|
!*** ./src/util/fetch.js ***!
|
||||||
|
\***************************/
|
||||||
|
|
||||||
|
/*!***************************!*\
|
||||||
|
!*** ./src/util/frame.js ***!
|
||||||
|
\***************************/
|
||||||
|
|
||||||
|
/*!***************************!*\
|
||||||
|
!*** ./src/util/index.js ***!
|
||||||
|
\***************************/
|
||||||
|
|
||||||
/*!***************************!*\
|
/*!***************************!*\
|
||||||
!*** ./src/util/style.js ***!
|
!*** ./src/util/style.js ***!
|
||||||
@ -130,20 +108,42 @@
|
|||||||
!*** ./src/wavesurfer.js ***!
|
!*** ./src/wavesurfer.js ***!
|
||||||
\***************************/
|
\***************************/
|
||||||
|
|
||||||
/*! ./drawer.multicanvas */
|
/*!****************************!*\
|
||||||
|
!*** ./src/util/extend.js ***!
|
||||||
|
\****************************/
|
||||||
|
|
||||||
/*! ./peakcache */
|
/*!****************************!*\
|
||||||
|
!*** ./src/util/get-id.js ***!
|
||||||
|
\****************************/
|
||||||
|
|
||||||
/*! ./mediaelement-webaudio */
|
/*!*****************************!*\
|
||||||
|
!*** ./src/mediaelement.js ***!
|
||||||
|
\*****************************/
|
||||||
|
|
||||||
/*!*************************!*\
|
/*!******************************!*\
|
||||||
!*** ./src/webaudio.js ***!
|
!*** ./src/util/observer.js ***!
|
||||||
\*************************/
|
\******************************/
|
||||||
|
|
||||||
/*!
|
/*!***********************************!*\
|
||||||
* escape-html
|
!*** ./src/drawer.canvasentry.js ***!
|
||||||
* Copyright(c) 2012-2013 TJ Holowaychuk
|
\***********************************/
|
||||||
* Copyright(c) 2015 Andreas Lubbe
|
|
||||||
* Copyright(c) 2015 Tiancheng "Timothy" Gu
|
/*!***********************************!*\
|
||||||
* MIT Licensed
|
!*** ./src/drawer.multicanvas.js ***!
|
||||||
*/
|
\***********************************/
|
||||||
|
|
||||||
|
/*!***********************************!*\
|
||||||
|
!*** ./src/util/prevent-click.js ***!
|
||||||
|
\***********************************/
|
||||||
|
|
||||||
|
/*!**************************************!*\
|
||||||
|
!*** ./src/mediaelement-webaudio.js ***!
|
||||||
|
\**************************************/
|
||||||
|
|
||||||
|
/*!****************************************!*\
|
||||||
|
!*** ./node_modules/debounce/index.js ***!
|
||||||
|
\****************************************/
|
||||||
|
|
||||||
|
/*!*********************************************!*\
|
||||||
|
!*** ./src/util/request-animation-frame.js ***!
|
||||||
|
\*********************************************/
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,2 +1,2 @@
|
|||||||
(window.webpackJsonp=window.webpackJsonp||[]).push([[8],{918:function(e,t){window.addEventListener("message",(function(e){var t=e.data||{};function n(){window.parent.postMessage({type:"setHeight",id:t.id,height:document.getElementsByTagName("html")[0].scrollHeight},"*")}window.parent&&"setHeight"===t.type&&(["interactive","complete"].includes(document.readyState)?n():document.addEventListener("DOMContentLoaded",n))}))}},[[918,0]]]);
|
(window.webpackJsonp=window.webpackJsonp||[]).push([[8],{944:function(e,t){window.addEventListener("message",(function(e){var t=e.data||{};function n(){window.parent.postMessage({type:"setHeight",id:t.id,height:document.getElementsByTagName("html")[0].scrollHeight},"*")}window.parent&&"setHeight"===t.type&&(["interactive","complete"].includes(document.readyState)?n():document.addEventListener("DOMContentLoaded",n))}))}},[[944,0]]]);
|
||||||
//# sourceMappingURL=embed.js.map
|
//# sourceMappingURL=embed.js.map
|
@ -1,2 +1,2 @@
|
|||||||
(window.webpackJsonp=window.webpackJsonp||[]).push([[9],{919:function(n,o,w){"use strict";w.r(o);w(920)},920:function(n,o,w){}},[[919,0]]]);
|
(window.webpackJsonp=window.webpackJsonp||[]).push([[9],{945:function(n,o,w){"use strict";w.r(o);w(946)},946:function(n,o,w){}},[[945,0]]]);
|
||||||
//# sourceMappingURL=mailer.js.map
|
//# sourceMappingURL=mailer.js.map
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user